Skip to content

Commit

Permalink
Styles Navigation Screen: Add Style Book (#50566)
Browse files Browse the repository at this point in the history
* Styles Navigation Screen: Add button to open Style Book

* Remove state and useEffect, use async / await instead

* Try exposing Style Book in browse mode

* Try hiding Style Book tabs, add keyboard behaviour

* Revert background color removal

* Clear the editor canvas container view when accessing the main navigation screen

* Fix enableResizing, move prevent default to the Style Book component for simplicity

* Tidy code a little

Co-authored-by: ramon <ramonjd@gmail.com>

---------

Co-authored-by: ramon <ramonjd@gmail.com>
  • Loading branch information
andrewserong and ramonjd authored May 17, 2023
1 parent 6bfafd0 commit c34280b
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 65 deletions.
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,74 @@ 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 ) =>
'style-book' ===
unlock( select( editSiteStore ) ).getEditorCanvasContainerView(),
[]
);

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={ () =>
setEditorCanvasContainerView(
! isStyleBookOpened
? 'style-book'
: undefined
)
}
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

0 comments on commit c34280b

Please sign in to comment.