Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Styles Navigation Screen: Add Style Book #50566

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { edit } from '@wordpress/icons';
import { edit, seen } from '@wordpress/icons';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { __experimentalNavigatorButton as NavigatorButton } from '@wordpress/components';
import { useViewportMatch } from '@wordpress/compose';

/**
* Internal dependencies
Expand All @@ -16,6 +17,7 @@ import { unlock } from '../../private-apis';
import { store as editSiteStore } from '../../store';
import SidebarButton from '../sidebar-button';
import SidebarNavigationItem from '../sidebar-navigation-item';
import StyleBook from '../style-book';

export function SidebarNavigationItemGlobalStyles( props ) {
const { openGeneralSidebar } = useDispatch( editSiteStore );
Expand Down Expand Up @@ -51,26 +53,80 @@ export function SidebarNavigationItemGlobalStyles( props ) {

export default function SidebarNavigationScreenGlobalStyles() {
const { openGeneralSidebar } = useDispatch( editSiteStore );
const { setCanvasMode } = unlock( useDispatch( editSiteStore ) );
const isMobileViewport = useViewportMatch( 'medium', '<' );
const { setCanvasMode, setEditorCanvasContainerView } = unlock(
useDispatch( editSiteStore )
);

const { isStyleBookOpened } = useSelect( ( select ) => {
const { getEditorCanvasContainerView } = unlock(
select( editSiteStore )
);
return {
isStyleBookOpened: 'style-book' === getEditorCanvasContainerView(),
};
}, [] );
andrewserong marked this conversation as resolved.
Show resolved Hide resolved

const openGlobalStyles = async () =>
Promise.all( [
setCanvasMode( 'edit' ),
openGeneralSidebar( 'edit-site/global-styles' ),
] );

const openStyleBook = async () => {
await openGlobalStyles();
// Open the Style Book once the canvas mode is set to edit,
// and the global styles sidebar is open. This ensures that
// the Style Book is not prematurely closed.
setEditorCanvasContainerView( 'style-book' );
};

return (
<SidebarNavigationScreen
title={ __( 'Styles' ) }
description={ __(
'Choose a different style combination for the theme styles.'
) }
content={ <StyleVariationsContainer /> }
actions={
<SidebarButton
icon={ edit }
label={ __( 'Edit styles' ) }
onClick={ () => {
// switch to edit mode.
setCanvasMode( 'edit' );
// open global styles sidebar.
openGeneralSidebar( 'edit-site/global-styles' );
} }
<>
<SidebarNavigationScreen
title={ __( 'Styles' ) }
description={ __(
'Choose a different style combination for the theme styles.'
) }
content={ <StyleVariationsContainer /> }
actions={
<div>
{ ! isMobileViewport && (
<SidebarButton
icon={ seen }
label={ __( 'Style Book' ) }
onClick={ async () => {
if ( ! isStyleBookOpened ) {
setEditorCanvasContainerView(
'style-book'
);
} else {
setEditorCanvasContainerView(
undefined
);
}
} }
andrewserong marked this conversation as resolved.
Show resolved Hide resolved
isPressed={ isStyleBookOpened }
/>
) }
<SidebarButton
icon={ edit }
label={ __( 'Edit styles' ) }
onClick={ async () => await openGlobalStyles() }
/>
</div>
}
/>
{ isStyleBookOpened && ! isMobileViewport && (
<StyleBook
enableResizing={ false }
isSelected={ () => false }
onClick={ openStyleBook }
onSelect={ openStyleBook }
showCloseButton={ false }
showTabs={ false }
/>
}
/>
) }
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import {
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { layout, symbol, navigation, styles, page } from '@wordpress/icons';
import { useSelect } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import SidebarNavigationScreen from '../sidebar-navigation-screen';
import SidebarNavigationItem from '../sidebar-navigation-item';
import { SidebarNavigationItemGlobalStyles } from '../sidebar-navigation-screen-global-styles';
import { unlock } from '../../private-apis';
import { store as editSiteStore } from '../../store';

export default function SidebarNavigationScreenMain() {
const hasNavigationMenus = useSelect( ( select ) => {
Expand All @@ -36,6 +39,22 @@ export default function SidebarNavigationScreenMain() {
const showNavigationScreen = process.env.IS_GUTENBERG_PLUGIN
? hasNavigationMenus
: false;

const editorCanvasContainerView = useSelect( ( select ) => {
return unlock( select( editSiteStore ) ).getEditorCanvasContainerView();
}, [] );

const { setEditorCanvasContainerView } = unlock(
useDispatch( editSiteStore )
);

// Clear the editor canvas container view when accessing the main navigation screen.
useEffect( () => {
if ( editorCanvasContainerView ) {
setEditorCanvasContainerView( undefined );
}
}, [ editorCanvasContainerView, setEditorCanvasContainerView ] );

return (
<SidebarNavigationScreen
isRoot
Expand Down
174 changes: 130 additions & 44 deletions packages/edit-site/src/components/style-book/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Disabled,
TabPanel,
} from '@wordpress/components';

import { __, sprintf } from '@wordpress/i18n';
import {
getCategories,
Expand All @@ -29,7 +30,8 @@ import {
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useResizeObserver } from '@wordpress/compose';
import { useMemo, memo } from '@wordpress/element';
import { useMemo, useState, memo } from '@wordpress/element';
import { ENTER, SPACE } from '@wordpress/keycodes';

/**
* Internal dependencies
Expand Down Expand Up @@ -161,7 +163,14 @@ function getExamples() {
return [ headingsExample, ...otherExamples ];
}

function StyleBook( { isSelected, onSelect } ) {
function StyleBook( {
enableResizing = true,
isSelected,
onClick,
onSelect,
showCloseButton = true,
showTabs = true,
} ) {
const [ resizeObserver, sizes ] = useResizeObserver();
const [ textColor ] = useGlobalStyle( 'color.text' );
const [ backgroundColor ] = useGlobalStyle( 'color.background' );
Expand Down Expand Up @@ -193,66 +202,141 @@ function StyleBook( { isSelected, onSelect } ) {

return (
<EditorCanvasContainer
enableResizing={ true }
closeButtonLabel={ __( 'Close Style Book' ) }
enableResizing={ enableResizing }
closeButtonLabel={
showCloseButton ? __( 'Close Style Book' ) : null
}
>
<div
className={ classnames( 'edit-site-style-book', {
'is-wide': sizes.width > 600,
'is-button': !! onClick,
} ) }
style={ {
color: textColor,
background: backgroundColor,
} }
>
{ resizeObserver }
<TabPanel
className="edit-site-style-book__tab-panel"
tabs={ tabs }
>
{ ( tab ) => (
<Iframe
className="edit-site-style-book__iframe"
name="style-book-canvas"
tabIndex={ 0 }
>
<EditorStyles styles={ settings.styles } />
<style>
{
// Forming a "block formatting context" to prevent margin collapsing.
// @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
`.is-root-container { display: flow-root; }
body { position: relative; padding: 32px !important; }` +
STYLE_BOOK_IFRAME_STYLES
}
</style>
<Examples
className={ classnames(
'edit-site-style-book__examples',
{
'is-wide': sizes.width > 600,
}
) }
examples={ examples }
{ showTabs ? (
<TabPanel
className="edit-site-style-book__tab-panel"
tabs={ tabs }
>
{ ( tab ) => (
<StyleBookBody
category={ tab.name }
label={ sprintf(
// translators: %s: Category of blocks, e.g. Text.
__(
'Examples of blocks in the %s category'
),
tab.title
) }
examples={ examples }
isSelected={ isSelected }
onSelect={ onSelect }
settings={ settings }
sizes={ sizes }
title={ tab.title }
/>
</Iframe>
) }
</TabPanel>
) }
</TabPanel>
) : (
<StyleBookBody
examples={ examples }
isSelected={ isSelected }
onClick={ onClick }
onSelect={ onSelect }
settings={ settings }
sizes={ sizes }
/>
) }
</div>
</EditorCanvasContainer>
);
}

const StyleBookBody = ( {
category,
examples,
isSelected,
onClick,
onSelect,
settings,
sizes,
title,
} ) => {
const [ isFocused, setIsFocused ] = useState( false );

// The presence of an `onClick` prop indicates that the Style Book is being used as a button.
// In this case, add additional props to the iframe to make it behave like a button.
const buttonModeProps = {
role: 'button',
onFocus: () => setIsFocused( true ),
onBlur: () => setIsFocused( false ),
onKeyDown: ( event ) => {
if ( event.defaultPrevented ) {
return;
}
const { keyCode } = event;
if ( onClick && ( keyCode === ENTER || keyCode === SPACE ) ) {
event.preventDefault();
onClick( event );
}
},
onClick: ( event ) => {
if ( event.defaultPrevented ) {
return;
}
if ( onClick ) {
event.preventDefault();
onClick( event );
}
},
readonly: true,
};

const buttonModeStyles = onClick
? 'body { cursor: pointer; } body * { pointer-events: none; }'
: '';

return (
<Iframe
className={ classnames( 'edit-site-style-book__iframe', {
'is-focused': isFocused && !! onClick,
'is-button': !! onClick,
} ) }
name="style-book-canvas"
tabIndex={ 0 }
{ ...( onClick ? buttonModeProps : {} ) }
>
<EditorStyles styles={ settings.styles } />
<style>
{
// Forming a "block formatting context" to prevent margin collapsing.
// @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
`.is-root-container { display: flow-root; }
body { position: relative; padding: 32px !important; }` +
STYLE_BOOK_IFRAME_STYLES +
buttonModeStyles
}
</style>
<Examples
className={ classnames( 'edit-site-style-book__examples', {
'is-wide': sizes.width > 600,
} ) }
examples={ examples }
category={ category }
label={
title
? sprintf(
// translators: %s: Category of blocks, e.g. Text.
__( 'Examples of blocks in the %s category' ),
title
)
: __( 'Examples of blocks' )
}
isSelected={ isSelected }
onSelect={ onSelect }
/>
</Iframe>
);
};

const Examples = memo(
( { className, examples, category, label, isSelected, onSelect } ) => {
const composite = useCompositeState( { orientation: 'vertical' } );
Expand All @@ -263,7 +347,9 @@ const Examples = memo(
aria-label={ label }
>
{ examples
.filter( ( example ) => example.category === category )
.filter( ( example ) =>
category ? example.category === category : true
)
.map( ( example ) => (
<Example
key={ example.name }
Expand All @@ -273,7 +359,7 @@ const Examples = memo(
blocks={ example.blocks }
isSelected={ isSelected( example.name ) }
onClick={ () => {
onSelect( example.name );
onSelect?.( example.name );
} }
/>
) ) }
Expand Down
Loading