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

Bootstrap the sections exploded view #40314

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/block-editor/README.md
Expand Up @@ -173,6 +173,10 @@ _Related_

Undocumented declaration.

### BlockLockToolbar

Undocumented declaration.

### BlockMover

_Related_
Expand Down
15 changes: 9 additions & 6 deletions packages/block-editor/src/components/block-tools/style.scss
Expand Up @@ -52,7 +52,8 @@
.block-editor-block-list__empty-block-inserter,
.block-editor-default-block-appender,
.block-editor-block-list__insertion-point-inserter,
.block-editor-block-list__block-popover-inserter {
.block-editor-block-list__block-popover-inserter,
.edit-site-block-list-exploded__inserter {
.block-editor-inserter__toggle.components-button.has-icon {
// Basic look
background: $gray-900;
Expand All @@ -70,11 +71,13 @@
}
}
}

.block-editor-block-list__insertion-point-inserter .block-editor-inserter__toggle.components-button.has-icon {
background: var(--wp-admin-theme-color);
&:hover {
background: $gray-900;
.edit-site-block-list-exploded__inserter,
.block-editor-block-list__insertion-point-inserter {
.block-editor-inserter__toggle.components-button.has-icon {
background: var(--wp-admin-theme-color);
&:hover {
background: $gray-900;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Expand Up @@ -108,6 +108,7 @@ export { default as BlockInspector } from './block-inspector';
export { default as BlockList } from './block-list';
export { useBlockProps } from './block-list/use-block-props';
export { LayoutStyle as __experimentalLayoutStyle } from './block-list/layout';
export { default as BlockLockToolbar } from './block-lock/toolbar';
export { default as BlockMover } from './block-mover';
export {
default as BlockPreview,
Expand Down
98 changes: 53 additions & 45 deletions packages/edit-site/src/components/block-editor/index.js
Expand Up @@ -40,6 +40,7 @@ import BlockInspectorButton from './block-inspector-button';
import EditTemplatePartMenuButton from '../edit-template-part-menu-button';
import BackButton from './back-button';
import ResizableEditor from './resizable-editor';
import BlockListExploded from '../block-list-exploded';

const LAYOUT = {
type: 'default',
Expand All @@ -48,7 +49,7 @@ const LAYOUT = {
};

export default function BlockEditor( { setIsInserterOpen } ) {
const { settings } = useSelect(
const { settings, editorMode } = useSelect(
( select ) => {
let storedSettings = select( editSiteStore ).getSettings(
setIsInserterOpen
Expand All @@ -74,6 +75,7 @@ export default function BlockEditor( { setIsInserterOpen } ) {

return {
settings: storedSettings,
editorMode: select( editSiteStore ).getEditorMode(),
};
},
[ setIsInserterOpen ]
Expand Down Expand Up @@ -158,52 +160,58 @@ export default function BlockEditor( { setIsInserterOpen } ) {
<SidebarInspectorFill>
<BlockInspector />
</SidebarInspectorFill>
<BlockTools
className={ classnames( 'edit-site-visual-editor', {
'is-focus-mode': isTemplatePart,
} ) }
__unstableContentRef={ contentRef }
onClick={ ( event ) => {
// Clear selected block when clicking on the gray background.
if ( event.target === event.currentTarget ) {
clearSelectedBlock();
}
} }
>
<BlockEditorKeyboardShortcuts.Register />
<BackButton />
<ResizableEditor
// Reinitialize the editor and reset the states when the template changes.
key={ templateId }
enableResizing={
isTemplatePart &&
// Disable resizing in mobile viewport.
! isMobileViewport
}
settings={ settings }
contentRef={ mergedRefs }
{ editorMode === 'exploded' && <BlockListExploded /> }

{ editorMode === 'visual' && (
<BlockTools
className={ classnames( 'edit-site-visual-editor', {
'is-focus-mode': isTemplatePart,
} ) }
__unstableContentRef={ contentRef }
onClick={ ( event ) => {
// Clear selected block when clicking on the gray background.
if ( event.target === event.currentTarget ) {
clearSelectedBlock();
}
} }
>
<BlockList
className="edit-site-block-editor__block-list wp-site-blocks"
__experimentalLayout={ LAYOUT }
renderAppender={ isTemplatePart ? false : undefined }
/>
</ResizableEditor>
<__unstableBlockSettingsMenuFirstItem>
{ ( { onClose } ) => (
<BlockInspectorButton onClick={ onClose } />
) }
</__unstableBlockSettingsMenuFirstItem>
<__unstableBlockToolbarLastItem>
<__unstableBlockNameContext.Consumer>
{ ( blockName ) =>
blockName === 'core/navigation' && (
<MaybeNavMenuSidebarToggle />
)
<BlockEditorKeyboardShortcuts.Register />
<BackButton />
<ResizableEditor
// Reinitialize the editor and reset the states when the template changes.
key={ templateId }
enableResizing={
isTemplatePart &&
// Disable resizing in mobile viewport.
! isMobileViewport
}
</__unstableBlockNameContext.Consumer>
</__unstableBlockToolbarLastItem>
</BlockTools>
settings={ settings }
contentRef={ mergedRefs }
>
<BlockList
className="edit-site-block-editor__block-list wp-site-blocks"
__experimentalLayout={ LAYOUT }
renderAppender={
isTemplatePart ? false : undefined
}
/>
</ResizableEditor>
<__unstableBlockSettingsMenuFirstItem>
{ ( { onClose } ) => (
<BlockInspectorButton onClick={ onClose } />
) }
</__unstableBlockSettingsMenuFirstItem>
<__unstableBlockToolbarLastItem>
<__unstableBlockNameContext.Consumer>
{ ( blockName ) =>
blockName === 'core/navigation' && (
<MaybeNavMenuSidebarToggle />
)
}
</__unstableBlockNameContext.Consumer>
</__unstableBlockToolbarLastItem>
</BlockTools>
) }
<ReusableBlocksMenuItems />
</BlockEditorProvider>
);
Expand Down
@@ -0,0 +1,4 @@
## BlockListExploded Component

This is an experimental component being built to implement the exploded view in the site editor.
The potential goal for this component is to be a component that can be used by any block editor, so it's important to keep its dependencies minimal. (block-editor dependencies). Eventually, it should be moved to the `@wordpress/block-editor` package.
37 changes: 37 additions & 0 deletions packages/edit-site/src/components/block-list-exploded/index.js
@@ -0,0 +1,37 @@
/**
* WordPress dependencies
*/
import { store as blockEditorStore, Inserter } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { pure } from '@wordpress/compose';

/**
* Internal dependencies
*/
import BlockListExplodedItem from './item';

const InserterBeforeBlock = pure( ( { clientId } ) => (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know we had this HoC. Should I prefer using this over React.memo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest at this point, I don't know. What's certain is that we need to make a decision :P. This HoC was here before React introduced React.memo and if I'm not wrong also works for Class component contrary to React.memo which I think is only suited for function components.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, React.memo works only with functional components.

What's certain is that we need to make a decision

Got it. Use React.memo with function components use pure with Class components 😄

<div className="edit-site-block-list-exploded__inserter" key={ clientId }>
<Inserter clientId={ clientId } __experimentalIsQuick isPrimary />
</div>
) );

function BlockListExploded() {
const blockOrder = useSelect( ( select ) => {
return select( blockEditorStore ).getBlockOrder();
}, [] );

return (
<div className="edit-site-block-list-exploded">
{ blockOrder.map( ( clientId ) => (
<div key={ clientId }>
<InserterBeforeBlock clientId={ clientId } />
<BlockListExplodedItem clientId={ clientId } />
</div>
) ) }
<InserterBeforeBlock />
</div>
);
}

export default BlockListExploded;
84 changes: 84 additions & 0 deletions packages/edit-site/src/components/block-list-exploded/item.js
@@ -0,0 +1,84 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import {
store as blockEditorStore,
BlockPreview,
useBlockDisplayInformation,
} from '@wordpress/block-editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { pure } from '@wordpress/compose';
import { sprintf, __ } from '@wordpress/i18n';
import { useMemo, useRef, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import BlockListExplodedTopToolbar from './top-toolbar';
import { store as editSiteStore } from '../../store';

function BlockListExplodedItem( { clientId } ) {
const blockWrapper = useRef();
const { block, isSelected } = useSelect(
( select ) => {
const { getBlock, isBlockSelected } = select( blockEditorStore );
return {
block: getBlock( clientId ),
isSelected: isBlockSelected( clientId ),
};
},
[ clientId ]
);
const { title } = useBlockDisplayInformation( clientId );
const { selectBlock } = useDispatch( blockEditorStore );
// If the exploded list becomes part of block-editor
// This mode also need to move into the block-editor store.
const { switchEditorMode } = useDispatch( editSiteStore );

// translators: %s: Type of block (i.e. Text, Image etc)
const blockLabel = sprintf( __( 'Block: %s' ), title );
const blocksToPreview = useMemo( () => [ block ], [ block ] );

useEffect( () => {
if ( isSelected ) {
blockWrapper.current.focus();
}
}, [ isSelected ] );

return (
<div
className={ classnames(
'edit-site-block-list-exploded__item-container',
{ 'is-selected': isSelected }
) }
>
{ isSelected && (
<BlockListExplodedTopToolbar clientId={ clientId } />
) }
<div
ref={ blockWrapper }
role="button"
onClick={ ( event ) => {
if ( event.detail === 1 ) {
selectBlock( clientId );
} else if ( event.detail === 2 ) {
switchEditorMode( 'visual' );
}
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
} }
onKeyPress={ () => selectBlock( clientId ) }
onFocus={ () => selectBlock( clientId ) }
aria-label={ blockLabel }
tabIndex={ 0 }
>
<BlockPreview blocks={ blocksToPreview } />
</div>
</div>
);
}

export default pure( BlockListExplodedItem );
66 changes: 66 additions & 0 deletions packages/edit-site/src/components/block-list-exploded/style.scss
@@ -0,0 +1,66 @@
.edit-site-block-list-exploded {
display: flex;
flex-direction: column;
width: $content-width;
margin: auto;
}

.edit-site-block-list-exploded__inserter > div {
display: flex;
margin: 30px auto;
width: fit-content;
}

.edit-site-block-list-exploded__item-container {
position: relative;

&::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
}

&:hover::after {
border: 1px solid var(--wp-admin-theme-color);
}

&.is-selected::after {
border: 2px solid var(--wp-admin-theme-color);
}
}

.edit-site-block-list-exploded__item-top-toolbar {
position: absolute;
top: -$block-toolbar-height - $grid-unit-15;
height: $block-toolbar-height;
background: var(--wp-admin-theme-color);
color: $white;
display: flex;
align-items: center;
gap: $grid-unit-20;
padding-left: $grid-unit-20;
border-radius: $radius-block-ui;

.block-editor-block-lock-toolbar,
.block-editor-block-mover__move-button-container {
background: transparent;
color: inherit;
border: none;

.components-button {
color: $white;
}
}

// TODO: Absorb these in the BlockMover component (outside of mobile).
.block-editor-block-mover-button.is-down-button svg {
bottom: 5px;
}
.block-editor-block-mover-button.is-up-button svg {
top: 5px;
}
}
@@ -0,0 +1,27 @@
/**
* WordPress dependencies
*/
import {
BlockTitle,
BlockMover,
BlockLockToolbar,
BlockIcon,
useBlockDisplayInformation,
} from '@wordpress/block-editor';

function BlockListExplodedTopToolbar( { clientId } ) {
const { icon } = useBlockDisplayInformation( clientId );

return (
<div className="edit-site-block-list-exploded__item-top-toolbar">
<BlockIcon icon={ icon } showColors />
<span>
<BlockTitle clientId={ clientId } />
</span>
<BlockLockToolbar clientId={ clientId } />
<BlockMover clientIds={ [ clientId ] } hideDragHandle />
</div>
);
}

export default BlockListExplodedTopToolbar;