From 0f2a601a882dda37a7f69f6fadbfc9e369d052dd Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Wed, 30 Sep 2020 12:54:18 +0800 Subject: [PATCH 1/3] Move hooks inside the newly created Interface component --- .../src/components/header/index.js | 2 +- .../src/components/layout/index.js | 84 +--------------- .../src/components/layout/interface.js | 95 +++++++++++++++++++ 3 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 packages/edit-widgets/src/components/layout/interface.js diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index 949c3817199762..19d0ff120f47d7 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -25,6 +25,7 @@ import useLastSelectedRootId from '../../hooks/use-last-selected-root-id'; function Header() { const inserterButton = useRef(); const isLargeViewport = useViewportMatch( 'medium' ); + const rootClientId = useLastSelectedRootId(); const isLastSelectedWidgetAreaOpen = useSelect( ( select ) => select( 'core/edit-widgets' ).getIsWidgetAreaOpen( rootClientId ), @@ -37,7 +38,6 @@ function Header() { 'core/edit-widgets' ); const { selectBlock } = useDispatch( 'core/block-editor' ); - const rootClientId = useLastSelectedRootId(); const handleClick = () => { if ( isInserterOpened ) { // Focusing the inserter button closes the inserter popover diff --git a/packages/edit-widgets/src/components/layout/index.js b/packages/edit-widgets/src/components/layout/index.js index ddfc49a3abdf70..bc2e77870abe49 100644 --- a/packages/edit-widgets/src/components/layout/index.js +++ b/packages/edit-widgets/src/components/layout/index.js @@ -1,100 +1,22 @@ /** * WordPress dependencies */ -import { Popover, Button } from '@wordpress/components'; -import { useViewportMatch } from '@wordpress/compose'; -import { close } from '@wordpress/icons'; -import { __experimentalLibrary as Library } from '@wordpress/block-editor'; -import { useEffect } from '@wordpress/element'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface'; +import { Popover } from '@wordpress/components'; import { PluginArea } from '@wordpress/plugins'; /** * Internal dependencies */ import WidgetAreasBlockEditorProvider from '../widget-areas-block-editor-provider'; -import Header from '../header'; import Sidebar from '../sidebar'; -import WidgetAreasBlockEditorContent from '../widget-areas-block-editor-content'; -import PopoverWrapper from './popover-wrapper'; -import useLastSelectedRootId from '../../hooks/use-last-selected-root-id'; +import Interface from './interface'; function Layout( { blockEditorSettings } ) { - const isMobileViewport = useViewportMatch( 'medium', '<' ); - const isHugeViewport = useViewportMatch( 'huge', '>=' ); - const { setIsInserterOpened, closeGeneralSidebar } = useDispatch( - 'core/edit-widgets' - ); - const rootClientId = useLastSelectedRootId(); - - const { hasSidebarEnabled, isInserterOpened } = useSelect( ( select ) => ( { - hasSidebarEnabled: !! select( - 'core/interface' - ).getActiveComplementaryArea( 'core/edit-widgets' ), - isInserterOpened: !! select( 'core/edit-widgets' ).isInserterOpened(), - } ) ); - - // Inserter and Sidebars are mutually exclusive - useEffect( () => { - if ( hasSidebarEnabled && ! isHugeViewport ) { - setIsInserterOpened( false ); - } - }, [ hasSidebarEnabled, isHugeViewport ] ); - - useEffect( () => { - if ( isInserterOpened && ! isHugeViewport ) { - closeGeneralSidebar(); - } - }, [ isInserterOpened, isHugeViewport ] ); - return ( - } - leftSidebar={ - isInserterOpened && ( - setIsInserterOpened( false ) } - > -
-
-
-
- { - if ( isMobileViewport ) { - setIsInserterOpened( false ); - } - } } - rootClientId={ rootClientId } - /> -
-
-
- ) - } - sidebar={ - hasSidebarEnabled && ( - - ) - } - content={ - - } - /> + diff --git a/packages/edit-widgets/src/components/layout/interface.js b/packages/edit-widgets/src/components/layout/interface.js new file mode 100644 index 00000000000000..3376e1f4aa37f0 --- /dev/null +++ b/packages/edit-widgets/src/components/layout/interface.js @@ -0,0 +1,95 @@ +/** + * WordPress dependencies + */ +import { Button } from '@wordpress/components'; +import { useViewportMatch } from '@wordpress/compose'; +import { close } from '@wordpress/icons'; +import { __experimentalLibrary as Library } from '@wordpress/block-editor'; +import { useEffect } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface'; + +/** + * Internal dependencies + */ +import Header from '../header'; +import WidgetAreasBlockEditorContent from '../widget-areas-block-editor-content'; +import PopoverWrapper from './popover-wrapper'; +import useLastSelectedRootId from '../../hooks/use-last-selected-root-id'; + +function Interface( { blockEditorSettings } ) { + const isMobileViewport = useViewportMatch( 'medium', '<' ); + const isHugeViewport = useViewportMatch( 'huge', '>=' ); + const { setIsInserterOpened, closeGeneralSidebar } = useDispatch( + 'core/edit-widgets' + ); + const rootClientId = useLastSelectedRootId( 'layout' ); + + const { hasSidebarEnabled, isInserterOpened } = useSelect( ( select ) => ( { + hasSidebarEnabled: !! select( + 'core/interface' + ).getActiveComplementaryArea( 'core/edit-widgets' ), + isInserterOpened: !! select( 'core/edit-widgets' ).isInserterOpened(), + } ) ); + + // Inserter and Sidebars are mutually exclusive + useEffect( () => { + if ( hasSidebarEnabled && ! isHugeViewport ) { + setIsInserterOpened( false ); + } + }, [ hasSidebarEnabled, isHugeViewport ] ); + + useEffect( () => { + if ( isInserterOpened && ! isHugeViewport ) { + closeGeneralSidebar(); + } + }, [ isInserterOpened, isHugeViewport ] ); + + return ( + } + leftSidebar={ + isInserterOpened && ( + setIsInserterOpened( false ) } + > +
+
+
+
+ { + if ( isMobileViewport ) { + setIsInserterOpened( false ); + } + } } + rootClientId={ rootClientId } + /> +
+
+
+ ) + } + sidebar={ + hasSidebarEnabled && ( + + ) + } + content={ + + } + /> + ); +} + +export default Interface; From 97b40373aa103c8b6635665a1ead3a3d264b273d Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Wed, 30 Sep 2020 12:54:32 +0800 Subject: [PATCH 2/3] Fix insertion at the top of the widget area --- .../src/components/inserter/hooks/use-insertion-point.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index 2ee224d96a4d77..e9ce83652fe20e 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -81,9 +81,9 @@ function useInsertionPoint( { return getBlockIndex( clientId, destinationRootClientId ); } - // If there a selected block, we insert after the selected block. + // If there's a selected block, and the selected block is not the destination root block, we insert after the selected block. const end = getBlockSelectionEnd(); - if ( ! isAppender && end ) { + if ( ! isAppender && end && end !== destinationRootClientId ) { return getBlockIndex( end, destinationRootClientId ) + 1; } From ff74b47b0a3f0c838ec3989cfdf4eaccfd61f171 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 5 Oct 2020 12:05:57 +0800 Subject: [PATCH 3/3] Fix widget insertion from sidebar block library by using a declared insertionPoint prop (#25763) * Fix widget insertion from sidebar block library by using a declared insertionPoint prop * Add comments * Optimize by avoiding `getEntityRecord` call unless needed --- .../inserter/hooks/use-insertion-point.js | 7 ++- .../src/components/inserter/library.js | 8 ++- .../src/components/inserter/menu.js | 2 + .../src/components/header/index.js | 14 ++--- .../src/components/layout/interface.js | 7 ++- .../src/hooks/use-last-selected-root-id.js | 37 ------------- .../hooks/use-last-selected-widget-area.js | 53 ++++++++++++++++++ .../use-widget-library-insertion-point.js | 54 +++++++++++++++++++ 8 files changed, 134 insertions(+), 48 deletions(-) delete mode 100644 packages/edit-widgets/src/hooks/use-last-selected-root-id.js create mode 100644 packages/edit-widgets/src/hooks/use-last-selected-widget-area.js create mode 100644 packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index e9ce83652fe20e..bb240e67859d43 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -32,6 +32,7 @@ function useInsertionPoint( { clientId, isAppender, selectBlockOnInsert, + insertionIndex, } ) { const { destinationRootClientId, @@ -76,6 +77,10 @@ function useInsertionPoint( { } = useDispatch( 'core/block-editor' ); function getInsertionIndex() { + if ( insertionIndex !== undefined ) { + return insertionIndex; + } + // If the clientId is defined, we insert at the position of the block. if ( clientId ) { return getBlockIndex( clientId, destinationRootClientId ); @@ -83,7 +88,7 @@ function useInsertionPoint( { // If there's a selected block, and the selected block is not the destination root block, we insert after the selected block. const end = getBlockSelectionEnd(); - if ( ! isAppender && end && end !== destinationRootClientId ) { + if ( ! isAppender && end ) { return getBlockIndex( end, destinationRootClientId ) + 1; } diff --git a/packages/block-editor/src/components/inserter/library.js b/packages/block-editor/src/components/inserter/library.js index d22966dff2f67c..74e72702042d1f 100644 --- a/packages/block-editor/src/components/inserter/library.js +++ b/packages/block-editor/src/components/inserter/library.js @@ -19,7 +19,8 @@ function InserterLibrary( { isAppender, showInserterHelpPanel, showMostUsedBlocks = false, - __experimentalSelectBlockOnInsert: selectBlockOnInsert, + __experimentalSelectBlockOnInsert, + __experimentalInsertionIndex, onSelect = noop, } ) { const destinationRootClientId = useSelect( @@ -41,7 +42,10 @@ function InserterLibrary( { isAppender={ isAppender } showInserterHelpPanel={ showInserterHelpPanel } showMostUsedBlocks={ showMostUsedBlocks } - __experimentalSelectBlockOnInsert={ selectBlockOnInsert } + __experimentalSelectBlockOnInsert={ + __experimentalSelectBlockOnInsert + } + __experimentalInsertionIndex={ __experimentalInsertionIndex } /> ); } diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js index 3d59359799397a..8669d52b51a6a0 100644 --- a/packages/block-editor/src/components/inserter/menu.js +++ b/packages/block-editor/src/components/inserter/menu.js @@ -26,6 +26,7 @@ function InserterMenu( { clientId, isAppender, __experimentalSelectBlockOnInsert, + __experimentalInsertionIndex, onSelect, showInserterHelpPanel, showMostUsedBlocks, @@ -46,6 +47,7 @@ function InserterMenu( { clientId, isAppender, selectBlockOnInsert: __experimentalSelectBlockOnInsert, + insertionIndex: __experimentalInsertionIndex, } ); const { hasPatterns, hasReusableBlocks } = useSelect( ( select ) => { const { diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index 19d0ff120f47d7..d489e81c31065b 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -20,16 +20,18 @@ import { useRef } from '@wordpress/element'; import SaveButton from '../save-button'; import UndoButton from './undo-redo/undo'; import RedoButton from './undo-redo/redo'; -import useLastSelectedRootId from '../../hooks/use-last-selected-root-id'; +import useLastSelectedWidgetArea from '../../hooks/use-last-selected-widget-area'; function Header() { const inserterButton = useRef(); const isLargeViewport = useViewportMatch( 'medium' ); - const rootClientId = useLastSelectedRootId(); + const widgetAreaClientId = useLastSelectedWidgetArea(); const isLastSelectedWidgetAreaOpen = useSelect( ( select ) => - select( 'core/edit-widgets' ).getIsWidgetAreaOpen( rootClientId ), - [ rootClientId ] + select( 'core/edit-widgets' ).getIsWidgetAreaOpen( + widgetAreaClientId + ), + [ widgetAreaClientId ] ); const isInserterOpened = useSelect( ( select ) => select( 'core/edit-widgets' ).isInserterOpened() @@ -45,9 +47,9 @@ function Header() { } else { if ( ! isLastSelectedWidgetAreaOpen ) { // Select the last selected block if hasn't already. - selectBlock( rootClientId ); + selectBlock( widgetAreaClientId ); // Open the last selected widget area when opening the inserter. - setIsWidgetAreaOpen( rootClientId, true ); + setIsWidgetAreaOpen( widgetAreaClientId, true ); } // The DOM updates resulting from selectBlock() and setIsInserterOpened() calls are applied the // same tick and pretty much in a random order. The inserter is closed if any other part of the diff --git a/packages/edit-widgets/src/components/layout/interface.js b/packages/edit-widgets/src/components/layout/interface.js index 3376e1f4aa37f0..ddccb34206fd6e 100644 --- a/packages/edit-widgets/src/components/layout/interface.js +++ b/packages/edit-widgets/src/components/layout/interface.js @@ -15,7 +15,7 @@ import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface'; import Header from '../header'; import WidgetAreasBlockEditorContent from '../widget-areas-block-editor-content'; import PopoverWrapper from './popover-wrapper'; -import useLastSelectedRootId from '../../hooks/use-last-selected-root-id'; +import useWidgetLibraryInsertionPoint from '../../hooks/use-widget-library-insertion-point'; function Interface( { blockEditorSettings } ) { const isMobileViewport = useViewportMatch( 'medium', '<' ); @@ -23,7 +23,7 @@ function Interface( { blockEditorSettings } ) { const { setIsInserterOpened, closeGeneralSidebar } = useDispatch( 'core/edit-widgets' ); - const rootClientId = useLastSelectedRootId( 'layout' ); + const { rootClientId, insertionIndex } = useWidgetLibraryInsertionPoint(); const { hasSidebarEnabled, isInserterOpened } = useSelect( ( select ) => ( { hasSidebarEnabled: !! select( @@ -72,6 +72,9 @@ function Interface( { blockEditorSettings } ) { } } } rootClientId={ rootClientId } + __experimentalInsertionIndex={ + insertionIndex + } /> diff --git a/packages/edit-widgets/src/hooks/use-last-selected-root-id.js b/packages/edit-widgets/src/hooks/use-last-selected-root-id.js deleted file mode 100644 index 692e9cb1b21412..00000000000000 --- a/packages/edit-widgets/src/hooks/use-last-selected-root-id.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../store/utils'; - -const useLastSelectedRootId = () => { - const firstRootId = useSelect( ( select ) => { - // Default to the first widget area - const { getEntityRecord } = select( 'core' ); - const widgetAreasPost = getEntityRecord( - KIND, - POST_TYPE, - buildWidgetAreasPostId() - ); - return widgetAreasPost?.blocks[ 0 ]?.clientId; - }, [] ); - - const selectedRootId = useSelect( ( select ) => { - const { getBlockRootClientId, getBlockSelectionEnd } = select( - 'core/block-editor' - ); - const blockSelectionEnd = getBlockSelectionEnd(); - const blockRootClientId = getBlockRootClientId( blockSelectionEnd ); - // getBlockRootClientId returns an empty string for top-level blocks, in which case just return the block id. - return blockRootClientId === '' ? blockSelectionEnd : blockRootClientId; - }, [] ); - - // Fallbacks to the first widget area. - return selectedRootId || firstRootId; -}; - -export default useLastSelectedRootId; diff --git a/packages/edit-widgets/src/hooks/use-last-selected-widget-area.js b/packages/edit-widgets/src/hooks/use-last-selected-widget-area.js new file mode 100644 index 00000000000000..4a33e2b3d6097f --- /dev/null +++ b/packages/edit-widgets/src/hooks/use-last-selected-widget-area.js @@ -0,0 +1,53 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../store/utils'; + +/** + * A react hook that returns the client id of the last widget area to have + * been selected, or to have a selected block within it. + * + * @return {string} clientId of the widget area last selected. + */ +const useLastSelectedWidgetArea = () => + useSelect( ( select ) => { + const { getBlockSelectionEnd, getBlockParents, getBlockName } = select( + 'core/block-editor' + ); + const blockSelectionEndClientId = getBlockSelectionEnd(); + + // If the selected block is a widget area, return its clientId. + if ( + getBlockName( blockSelectionEndClientId ) === 'core/widget-area' + ) { + return blockSelectionEndClientId; + } + + // Otherwise, find the clientId of the top-level widget area by looking + // through the selected block's parents. + const blockParents = getBlockParents( blockSelectionEndClientId ); + const rootWidgetAreaClientId = blockParents.find( + ( clientId ) => getBlockName( clientId ) === 'core/widget-area' + ); + + if ( rootWidgetAreaClientId ) { + return rootWidgetAreaClientId; + } + + // If no widget area has been selected, return the clientId of the first + // area. + const { getEntityRecord } = select( 'core' ); + const widgetAreasPost = getEntityRecord( + KIND, + POST_TYPE, + buildWidgetAreasPostId() + ); + return widgetAreasPost?.blocks[ 0 ]?.clientId; + }, [] ); + +export default useLastSelectedWidgetArea; diff --git a/packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js b/packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js new file mode 100644 index 00000000000000..445272f05d0d56 --- /dev/null +++ b/packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js @@ -0,0 +1,54 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../store/utils'; + +const useWidgetLibraryInsertionPoint = () => { + const firstRootId = useSelect( ( select ) => { + // Default to the first widget area + const { getEntityRecord } = select( 'core' ); + const widgetAreasPost = getEntityRecord( + KIND, + POST_TYPE, + buildWidgetAreasPostId() + ); + return widgetAreasPost?.blocks[ 0 ]?.clientId; + }, [] ); + + return useSelect( + ( select ) => { + const { + getBlockRootClientId, + getBlockSelectionEnd, + getBlockOrder, + getBlockIndex, + } = select( 'core/block-editor' ); + + const clientId = getBlockSelectionEnd() || firstRootId; + const rootClientId = getBlockRootClientId( clientId ); + + // If the selected block is at the root level, it's a widget area and + // blocks can't be inserted here. Return this block as the root and the + // last child clientId indicating insertion at the end. + if ( clientId && rootClientId === '' ) { + return { + rootClientId: clientId, + insertionIndex: getBlockOrder( clientId ).length, + }; + } + + return { + rootClientId, + insertionIndex: getBlockIndex( clientId, rootClientId ) + 1, + }; + }, + [ firstRootId ] + ); +}; + +export default useWidgetLibraryInsertionPoint;