From 8b261ad47261be1733bf319e40d81f67ad95c2af Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 28 Sep 2020 14:04:29 +0800 Subject: [PATCH 1/4] Auto expand the first widget area if none is opened and the inserter is opened --- .../src/widget-area/edit/index.js | 26 ++++++++++++++--- .../src/components/header/index.js | 12 ++++++++ packages/edit-widgets/src/store/actions.js | 28 +++++++++++++++++++ packages/edit-widgets/src/store/reducer.js | 26 +++++++++++++++++ packages/edit-widgets/src/store/resolvers.js | 5 ++++ packages/edit-widgets/src/store/selectors.js | 25 +++++++++++++++++ 6 files changed, 118 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/widget-area/edit/index.js b/packages/block-library/src/widget-area/edit/index.js index 4db09556424756..5c1e2755069adf 100644 --- a/packages/block-library/src/widget-area/edit/index.js +++ b/packages/block-library/src/widget-area/edit/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { EntityProvider } from '@wordpress/core-data'; import { Panel, PanelBody } from '@wordpress/components'; @@ -15,14 +15,32 @@ export default function WidgetAreaEdit( { className, attributes: { id, name }, } ) { - const index = useSelect( - ( select ) => select( 'core/block-editor' ).getBlockIndex( clientId ), + const { index, isOpen } = useSelect( + ( select ) => { + const blockIndex = select( 'core/block-editor' ).getBlockIndex( + clientId + ); + + return { + index: blockIndex, + isOpen: select( 'core/edit-widgets' ).getIsWidgetAreaOpen( + blockIndex + ), + }; + }, [ clientId ] ); + const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' ); return ( - + { + setIsWidgetAreaOpen( index, opened ); + } } + > + select( 'core/edit-widgets' ).getIsAllWidgetAreasClosed() + ); + const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' ); + + function handleInserterOpen( isOpen ) { + if ( isOpen && isAllWidgetAreasClosed ) { + setIsWidgetAreaOpen( 0, isOpen ); + } + } return ( <> @@ -43,6 +54,7 @@ function Header( { isCustomizer } ) { ...toolbarItemProps, } } rootClientId={ rootClientId } + onToggle={ handleInserterOpen } /> ) } diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index fab994e763c1ce..6af5be0dd1a521 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -147,3 +147,31 @@ export function setWidgetIdForClientId( clientId, widgetId ) { widgetId, }; } + +/** + * Sets the open state of all the widget areas. + * + * @param {Array} widgetAreasOpenState An array of the open states of all the widget areas. + * @return {Object} Action. + */ +export function setWidgetAreasOpenState( widgetAreasOpenState ) { + return { + type: 'SET_WIDGET_AREAS_OPEN_STATE', + widgetAreasOpenState, + }; +} + +/** + * Sets the open state of the widget area. + * + * @param {number} index The index of the widget area. + * @param {boolean} isOpen Whether the widget area should be opened. + * @return {Object} Action. + */ +export function setIsWidgetAreaOpen( index, isOpen ) { + return { + type: 'SET_IS_WIDGET_AREA_OPEN', + index, + isOpen, + }; +} diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js index f24805d59d41dd..20b9c22d08305b 100644 --- a/packages/edit-widgets/src/store/reducer.js +++ b/packages/edit-widgets/src/store/reducer.js @@ -28,6 +28,32 @@ export function mapping( state, action ) { return state || {}; } +/** + * Controls the open state of the widget areas. + * + * @param {Array} state Redux state + * @param {Object} action Redux action + * @return {Array} Updated state + */ +export function widgetAreasOpenState( state = [], action ) { + const { type } = action; + switch ( type ) { + case 'SET_WIDGET_AREAS_OPEN_STATE': { + return action.widgetAreasOpenState; + } + case 'SET_IS_WIDGET_AREA_OPEN': { + const { index, isOpen } = action; + const list = state.slice(); + list[ index ] = isOpen; + return list; + } + default: { + return state; + } + } +} + export default combineReducers( { mapping, + widgetAreasOpenState, } ); diff --git a/packages/edit-widgets/src/store/resolvers.js b/packages/edit-widgets/src/store/resolvers.js index 49ab28864dd6e8..7fd5f3519ed1ea 100644 --- a/packages/edit-widgets/src/store/resolvers.js +++ b/packages/edit-widgets/src/store/resolvers.js @@ -7,6 +7,7 @@ import { createBlock } from '@wordpress/blocks'; * Internal dependencies */ import { resolveWidgetAreas, select, dispatch } from './controls'; +import { setWidgetAreasOpenState } from './actions'; import { KIND, POST_TYPE, @@ -61,6 +62,10 @@ export function* getWidgetAreas() { ); } + yield setWidgetAreasOpenState( + widgetAreaBlocks.map( ( _, index ) => index === 0 ) + ); + yield persistStubPost( buildWidgetAreasPostId(), widgetAreaBlocks ); yield { diff --git a/packages/edit-widgets/src/store/selectors.js b/packages/edit-widgets/src/store/selectors.js index 3941524ca54626..1a74ebbdf47405 100644 --- a/packages/edit-widgets/src/store/selectors.js +++ b/packages/edit-widgets/src/store/selectors.js @@ -160,3 +160,28 @@ export const hasResolvedWidgetAreas = createRegistrySelector( return true; } ); + +/** + * Gets whether the widget area is opened. + * + * @param {Array} state The open state of the widget areas. + * @param {number} index The index of the widget area. + * @return {boolean} True if the widget area is open. + */ +export const getIsWidgetAreaOpen = ( state, index ) => { + const { widgetAreasOpenState } = state; + return !! widgetAreasOpenState[ index ]; +}; + +/** + * Returns true if all the widget areas is closed. + * + * @param {Array} state The open state of the widget areas. + * @return {boolean} True if all the widget area is closed. + */ +export const getIsAllWidgetAreasClosed = ( state ) => { + const { widgetAreasOpenState } = state; + return Object.values( widgetAreasOpenState ).every( + ( isOpen ) => ! isOpen + ); +}; From 6ace8e7000a501882df3c7878b0ac6f521096ac9 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 28 Sep 2020 16:03:20 +0800 Subject: [PATCH 2/4] Refactor to use clientId as keys and handle last selected widget areas --- .../src/widget-area/edit/index.js | 18 ++++----------- .../src/components/header/index.js | 11 +++++---- .../src/hooks/use-last-selected-root-id.js | 22 ++++++------------ packages/edit-widgets/src/store/actions.js | 14 +++++------ packages/edit-widgets/src/store/reducer.js | 17 +++++++------- packages/edit-widgets/src/store/resolvers.js | 9 +++++--- packages/edit-widgets/src/store/selectors.js | 23 ++++--------------- 7 files changed, 45 insertions(+), 69 deletions(-) diff --git a/packages/block-library/src/widget-area/edit/index.js b/packages/block-library/src/widget-area/edit/index.js index 5c1e2755069adf..272956acd1809e 100644 --- a/packages/block-library/src/widget-area/edit/index.js +++ b/packages/block-library/src/widget-area/edit/index.js @@ -15,19 +15,9 @@ export default function WidgetAreaEdit( { className, attributes: { id, name }, } ) { - const { index, isOpen } = useSelect( - ( select ) => { - const blockIndex = select( 'core/block-editor' ).getBlockIndex( - clientId - ); - - return { - index: blockIndex, - isOpen: select( 'core/edit-widgets' ).getIsWidgetAreaOpen( - blockIndex - ), - }; - }, + const isOpen = useSelect( + ( select ) => + select( 'core/edit-widgets' ).getIsWidgetAreaOpen( clientId ), [ clientId ] ); const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' ); @@ -38,7 +28,7 @@ export default function WidgetAreaEdit( { title={ name } opened={ isOpen } onToggle={ ( opened ) => { - setIsWidgetAreaOpen( index, opened ); + setIsWidgetAreaOpen( clientId, opened ); } } > - select( 'core/edit-widgets' ).getIsAllWidgetAreasClosed() + const isLastSelectedWidgetAreaOpen = useSelect( + ( select ) => + select( 'core/edit-widgets' ).getIsWidgetAreaOpen( rootClientId ), + [ rootClientId ] ); const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' ); function handleInserterOpen( isOpen ) { - if ( isOpen && isAllWidgetAreasClosed ) { - setIsWidgetAreaOpen( 0, isOpen ); + // Open the last selected widget area when opening the inserter. + if ( isOpen && ! isLastSelectedWidgetAreaOpen ) { + setIsWidgetAreaOpen( rootClientId, isOpen ); } } 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 index 0db9a7da57a568..692e9cb1b21412 100644 --- a/packages/edit-widgets/src/hooks/use-last-selected-root-id.js +++ b/packages/edit-widgets/src/hooks/use-last-selected-root-id.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { useSelect } from '@wordpress/data'; -import { useRef } from '@wordpress/element'; /** * Internal dependencies @@ -18,28 +17,21 @@ const useLastSelectedRootId = () => { POST_TYPE, buildWidgetAreasPostId() ); - if ( widgetAreasPost ) { - return widgetAreasPost?.blocks[ 0 ]?.clientId; - } + return widgetAreasPost?.blocks[ 0 ]?.clientId; }, [] ); - const lastSelectedRootIdRef = useRef(); - if ( ! lastSelectedRootIdRef.current && firstRootId ) { - lastSelectedRootIdRef.current = firstRootId; - } - const selectedRootId = useSelect( ( select ) => { const { getBlockRootClientId, getBlockSelectionEnd } = select( 'core/block-editor' ); - return getBlockRootClientId( getBlockSelectionEnd() ); + 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; }, [] ); - if ( selectedRootId ) { - lastSelectedRootIdRef.current = selectedRootId; - } - - return lastSelectedRootIdRef.current; + // Fallbacks to the first widget area. + return selectedRootId || firstRootId; }; export default useLastSelectedRootId; diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index 6af5be0dd1a521..ee39613b6ce06b 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -151,8 +151,8 @@ export function setWidgetIdForClientId( clientId, widgetId ) { /** * Sets the open state of all the widget areas. * - * @param {Array} widgetAreasOpenState An array of the open states of all the widget areas. - * @return {Object} Action. + * @param {Object} widgetAreasOpenState The open states of all the widget areas. + * @return {Object} Action. */ export function setWidgetAreasOpenState( widgetAreasOpenState ) { return { @@ -164,14 +164,14 @@ export function setWidgetAreasOpenState( widgetAreasOpenState ) { /** * Sets the open state of the widget area. * - * @param {number} index The index of the widget area. - * @param {boolean} isOpen Whether the widget area should be opened. - * @return {Object} Action. + * @param {number} clientId The clientId of the widget area. + * @param {boolean} isOpen Whether the widget area should be opened. + * @return {Object} Action. */ -export function setIsWidgetAreaOpen( index, isOpen ) { +export function setIsWidgetAreaOpen( clientId, isOpen ) { return { type: 'SET_IS_WIDGET_AREA_OPEN', - index, + clientId, isOpen, }; } diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js index 20b9c22d08305b..aa414d56f8d744 100644 --- a/packages/edit-widgets/src/store/reducer.js +++ b/packages/edit-widgets/src/store/reducer.js @@ -31,21 +31,22 @@ export function mapping( state, action ) { /** * Controls the open state of the widget areas. * - * @param {Array} state Redux state - * @param {Object} action Redux action - * @return {Array} Updated state + * @param {Object} state Redux state + * @param {Object} action Redux action + * @return {Array} Updated state */ -export function widgetAreasOpenState( state = [], action ) { +export function widgetAreasOpenState( state = {}, action ) { const { type } = action; switch ( type ) { case 'SET_WIDGET_AREAS_OPEN_STATE': { return action.widgetAreasOpenState; } case 'SET_IS_WIDGET_AREA_OPEN': { - const { index, isOpen } = action; - const list = state.slice(); - list[ index ] = isOpen; - return list; + const { clientId, isOpen } = action; + return { + ...state, + [ clientId ]: isOpen, + }; } default: { return state; diff --git a/packages/edit-widgets/src/store/resolvers.js b/packages/edit-widgets/src/store/resolvers.js index 7fd5f3519ed1ea..e8af992ab9be1d 100644 --- a/packages/edit-widgets/src/store/resolvers.js +++ b/packages/edit-widgets/src/store/resolvers.js @@ -62,9 +62,12 @@ export function* getWidgetAreas() { ); } - yield setWidgetAreasOpenState( - widgetAreaBlocks.map( ( _, index ) => index === 0 ) - ); + const widgetAreasOpenState = {}; + widgetAreaBlocks.forEach( ( widgetAreaBlock, index ) => { + // Defaults to open the first widget area. + widgetAreasOpenState[ widgetAreaBlock.clientId ] = index === 0; + } ); + yield setWidgetAreasOpenState( widgetAreasOpenState ); yield persistStubPost( buildWidgetAreasPostId(), widgetAreaBlocks ); diff --git a/packages/edit-widgets/src/store/selectors.js b/packages/edit-widgets/src/store/selectors.js index 1a74ebbdf47405..03286dad378e89 100644 --- a/packages/edit-widgets/src/store/selectors.js +++ b/packages/edit-widgets/src/store/selectors.js @@ -164,24 +164,11 @@ export const hasResolvedWidgetAreas = createRegistrySelector( /** * Gets whether the widget area is opened. * - * @param {Array} state The open state of the widget areas. - * @param {number} index The index of the widget area. - * @return {boolean} True if the widget area is open. + * @param {Array} state The open state of the widget areas. + * @param {number} clientId The clientId of the widget area. + * @return {boolean} True if the widget area is open. */ -export const getIsWidgetAreaOpen = ( state, index ) => { +export const getIsWidgetAreaOpen = ( state, clientId ) => { const { widgetAreasOpenState } = state; - return !! widgetAreasOpenState[ index ]; -}; - -/** - * Returns true if all the widget areas is closed. - * - * @param {Array} state The open state of the widget areas. - * @return {boolean} True if all the widget area is closed. - */ -export const getIsAllWidgetAreasClosed = ( state ) => { - const { widgetAreasOpenState } = state; - return Object.values( widgetAreasOpenState ).every( - ( isOpen ) => ! isOpen - ); + return !! widgetAreasOpenState[ clientId ]; }; From b4e5a35728b18ebf6734cdc698e7731049c77814 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 28 Sep 2020 16:24:15 +0800 Subject: [PATCH 3/4] Select the last selected block when opening the inserter --- packages/edit-widgets/src/components/header/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index 419936c6bedfbc..49fe8e34581245 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -32,10 +32,13 @@ function Header( { isCustomizer } ) { [ rootClientId ] ); const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' ); + const { selectBlock } = useDispatch( 'core/block-editor' ); function handleInserterOpen( isOpen ) { - // Open the last selected widget area when opening the inserter. if ( isOpen && ! isLastSelectedWidgetAreaOpen ) { + // Select the last selected block if hasn't already. + selectBlock( rootClientId ); + // Open the last selected widget area when opening the inserter. setIsWidgetAreaOpen( rootClientId, isOpen ); } } From 70a85794f9225d4715532c62aaa17f0c132d6db4 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 28 Sep 2020 16:35:05 +0800 Subject: [PATCH 4/4] Fix JSDOC --- packages/edit-widgets/src/store/actions.js | 2 +- packages/edit-widgets/src/store/reducer.js | 2 +- packages/edit-widgets/src/store/selectors.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index ee39613b6ce06b..2452367b1381b9 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -164,7 +164,7 @@ export function setWidgetAreasOpenState( widgetAreasOpenState ) { /** * Sets the open state of the widget area. * - * @param {number} clientId The clientId of the widget area. + * @param {string} clientId The clientId of the widget area. * @param {boolean} isOpen Whether the widget area should be opened. * @return {Object} Action. */ diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js index aa414d56f8d744..0bc9e16f20df20 100644 --- a/packages/edit-widgets/src/store/reducer.js +++ b/packages/edit-widgets/src/store/reducer.js @@ -31,7 +31,7 @@ export function mapping( state, action ) { /** * Controls the open state of the widget areas. * - * @param {Object} state Redux state + * @param {Object} state Redux state * @param {Object} action Redux action * @return {Array} Updated state */ diff --git a/packages/edit-widgets/src/store/selectors.js b/packages/edit-widgets/src/store/selectors.js index 03286dad378e89..fc98c3e3e6b368 100644 --- a/packages/edit-widgets/src/store/selectors.js +++ b/packages/edit-widgets/src/store/selectors.js @@ -165,7 +165,7 @@ export const hasResolvedWidgetAreas = createRegistrySelector( * Gets whether the widget area is opened. * * @param {Array} state The open state of the widget areas. - * @param {number} clientId The clientId of the widget area. + * @param {string} clientId The clientId of the widget area. * @return {boolean} True if the widget area is open. */ export const getIsWidgetAreaOpen = ( state, clientId ) => {