From f4f68aa356f2b43613e67042c1622b9652b3dfde Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Sun, 1 Aug 2021 16:00:19 +0800 Subject: [PATCH 1/3] Fix insertion point in edit-widgets --- .../src/hooks/use-widget-library-insertion-point.js | 11 +++++++++++ packages/edit-widgets/src/store/selectors.js | 12 ++++++++++++ 2 files changed, 23 insertions(+) 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 index 014c139dbe707..a3af4f5d0abab 100644 --- a/packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js +++ b/packages/edit-widgets/src/hooks/use-widget-library-insertion-point.js @@ -8,6 +8,7 @@ import { store as coreStore } from '@wordpress/core-data'; /** * Internal dependencies */ +import { store as editWidgetsStore } from '../store'; import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../store/utils'; const useWidgetLibraryInsertionPoint = () => { @@ -31,6 +32,16 @@ const useWidgetLibraryInsertionPoint = () => { getBlockIndex, } = select( blockEditorStore ); + const insertionPoint = select( + editWidgetsStore + ).__experimentalGetInsertionPoint(); + + // "Browse all" in the quick inserter will set the rootClientId to the current block. + // Otherwise, it will just be undefined, and we'll have to handle it differently below. + if ( insertionPoint.rootClientId ) { + return insertionPoint; + } + const clientId = getBlockSelectionEnd() || firstRootId; const rootClientId = getBlockRootClientId( clientId ); diff --git a/packages/edit-widgets/src/store/selectors.js b/packages/edit-widgets/src/store/selectors.js index 3c72988e2157c..9526ee6811a1f 100644 --- a/packages/edit-widgets/src/store/selectors.js +++ b/packages/edit-widgets/src/store/selectors.js @@ -222,6 +222,18 @@ export function isInserterOpened( state ) { return !! state.blockInserterPanel; } +/** + * Get the insertion point for the inserter. + * + * @param {Object} state Global application state. + * + * @return {Object} The root client ID and index to insert at. + */ +export function __experimentalGetInsertionPoint( state ) { + const { rootClientId, insertionIndex } = state.blockInserterPanel; + return { rootClientId, insertionIndex }; +} + /** * Returns true if a block can be inserted into a widget area. * From 2d20b79c0c420fbed5fd66492bfcaaaea3a3a605 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Sun, 1 Aug 2021 16:01:22 +0800 Subject: [PATCH 2/3] Fix insertion point in customize-widgets --- .../src/components/inserter/index.js | 13 ++++++++ .../src/components/inserter/use-inserter.js | 33 ++++++++++++------- .../components/sidebar-block-editor/index.js | 1 + .../src/controls/inserter-outer-section.js | 23 ++++++------- .../src/controls/sidebar-control.js | 10 +++++- .../customize-widgets/src/store/actions.js | 19 +++++++++++ .../customize-widgets/src/store/reducer.js | 15 +++++++++ .../customize-widgets/src/store/selectors.js | 23 +++++++++++++ 8 files changed, 113 insertions(+), 24 deletions(-) diff --git a/packages/customize-widgets/src/components/inserter/index.js b/packages/customize-widgets/src/components/inserter/index.js index 09ef6bdcb5611..01516947f6dab 100644 --- a/packages/customize-widgets/src/components/inserter/index.js +++ b/packages/customize-widgets/src/components/inserter/index.js @@ -5,13 +5,22 @@ import { __ } from '@wordpress/i18n'; import { __experimentalLibrary as Library } from '@wordpress/block-editor'; import { Button } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; import { closeSmall } from '@wordpress/icons'; +/** + * Internal dependencies + */ +import { store as customizeWidgetsStore } from '../../store'; + function Inserter( { setIsOpened } ) { const inserterTitleId = useInstanceId( Inserter, 'customize-widget-layout__inserter-panel-title' ); + const insertionPoint = useSelect( ( select ) => + select( customizeWidgetsStore ).__experimentalGetInsertionPoint() + ); return (
setIsOpened( false ) } /> diff --git a/packages/customize-widgets/src/components/inserter/use-inserter.js b/packages/customize-widgets/src/components/inserter/use-inserter.js index e4668dfbd5622..002f76c3b72cc 100644 --- a/packages/customize-widgets/src/components/inserter/use-inserter.js +++ b/packages/customize-widgets/src/components/inserter/use-inserter.js @@ -1,16 +1,27 @@ /** * WordPress dependencies */ -import { useState, useEffect, useCallback } from '@wordpress/element'; +import { useEffect, useCallback } from '@wordpress/element'; +import { useSelect, useDispatch, select as selectStore } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as customizeWidgetsStore } from '../../store'; export default function useInserter( inserter ) { - const [ isInserterOpened, setIsInserterOpened ] = useState( - () => inserter.isOpen + const isInserterOpened = useSelect( ( select ) => + select( customizeWidgetsStore ).isInserterOpened() ); + const { setIsInserterOpened } = useDispatch( customizeWidgetsStore ); useEffect( () => { - return inserter.subscribe( setIsInserterOpened ); - }, [ inserter ] ); + if ( isInserterOpened ) { + inserter.open(); + } else { + inserter.close(); + } + }, [ inserter, isInserterOpened ] ); return [ isInserterOpened, @@ -18,16 +29,14 @@ export default function useInserter( inserter ) { ( updater ) => { let isOpen = updater; if ( typeof updater === 'function' ) { - isOpen = updater( inserter.isOpen ); + isOpen = updater( + selectStore( customizeWidgetsStore ).isInserterOpened() + ); } - if ( isOpen ) { - inserter.open(); - } else { - inserter.close(); - } + setIsInserterOpened( isOpen ); }, - [ inserter ] + [ setIsInserterOpened ] ), ]; } diff --git a/packages/customize-widgets/src/components/sidebar-block-editor/index.js b/packages/customize-widgets/src/components/sidebar-block-editor/index.js index 911b24c119e76..f2459f195a0e8 100644 --- a/packages/customize-widgets/src/components/sidebar-block-editor/index.js +++ b/packages/customize-widgets/src/components/sidebar-block-editor/index.js @@ -88,6 +88,7 @@ export default function SidebarBlockEditor( { blockEditorSettings, isFixedToolbarActive, keepCaretInsideBlock, + setIsInserterOpened, ] ); if ( isWelcomeGuideActive ) { diff --git a/packages/customize-widgets/src/controls/inserter-outer-section.js b/packages/customize-widgets/src/controls/inserter-outer-section.js index d668d0c0cb857..1f881a52f0d51 100644 --- a/packages/customize-widgets/src/controls/inserter-outer-section.js +++ b/packages/customize-widgets/src/controls/inserter-outer-section.js @@ -3,6 +3,12 @@ */ import { ESCAPE } from '@wordpress/keycodes'; import { focus } from '@wordpress/dom'; +import { dispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as customizeWidgetsStore } from '../store'; export default function getInserterOuterSection() { const { @@ -52,14 +58,16 @@ export default function getInserterOuterSection() { 'keydown', ( event ) => { if ( - this.isOpen && + this.expanded() && ( event.keyCode === ESCAPE || event.code === 'Escape' ) && ! event.defaultPrevented ) { event.preventDefault(); event.stopPropagation(); - this.close(); + dispatch( customizeWidgetsStore ).setIsInserterOpened( + false + ); } }, // Use capture mode to make this run before other event listeners. @@ -68,15 +76,8 @@ export default function getInserterOuterSection() { this.contentContainer.addClass( 'widgets-inserter' ); } - get isOpen() { - return this.expanded(); - } - subscribe( handler ) { - this.expanded.bind( handler ); - return () => this.expanded.unbind( handler ); - } open() { - if ( ! this.isOpen ) { + if ( ! this.expanded() ) { const contentContainer = this.contentContainer[ 0 ]; this.activeElementBeforeExpanded = contentContainer.ownerDocument.activeElement; @@ -97,7 +98,7 @@ export default function getInserterOuterSection() { } } close() { - if ( this.isOpen ) { + if ( this.expanded() ) { const contentContainer = this.contentContainer[ 0 ]; const activeElement = contentContainer.ownerDocument.activeElement; diff --git a/packages/customize-widgets/src/controls/sidebar-control.js b/packages/customize-widgets/src/controls/sidebar-control.js index d980832f44d05..d45a7abd44cd2 100644 --- a/packages/customize-widgets/src/controls/sidebar-control.js +++ b/packages/customize-widgets/src/controls/sidebar-control.js @@ -1,8 +1,14 @@ +/** + * WordPress dependencies + */ +import { dispatch } from '@wordpress/data'; + /** * Internal dependencies */ import SidebarAdapter from '../components/sidebar-block-editor/sidebar-adapter'; import getInserterOuterSection from './inserter-outer-section'; +import { store as customizeWidgetsStore } from '../store'; const getInserterId = ( controlId ) => `widgets-inserter-${ controlId }`; @@ -45,7 +51,9 @@ export default function getSidebarControl() { if ( ! args.unchanged ) { // Close the inserter when the section collapses. if ( ! expanded ) { - this.inserter.close(); + dispatch( customizeWidgetsStore ).setIsInserterOpened( + false + ); } this.subscribers.forEach( ( subscriber ) => diff --git a/packages/customize-widgets/src/store/actions.js b/packages/customize-widgets/src/store/actions.js index b156e1e3bbead..e1ee14b4682d7 100644 --- a/packages/customize-widgets/src/store/actions.js +++ b/packages/customize-widgets/src/store/actions.js @@ -15,3 +15,22 @@ export function __unstableToggleFeature( feature ) { feature, }; } + +/** + * Returns an action object used to open/close the inserter. + * + * @param {boolean|Object} value Whether the inserter should be + * opened (true) or closed (false). + * To specify an insertion point, + * use an object. + * @param {string} value.rootClientId The root client ID to insert at. + * @param {number} value.insertionIndex The index to insert at. + * + * @return {Object} Action object. + */ +export function setIsInserterOpened( value ) { + return { + type: 'SET_IS_INSERTER_OPENED', + value, + }; +} diff --git a/packages/customize-widgets/src/store/reducer.js b/packages/customize-widgets/src/store/reducer.js index 8076cc26df943..bfc2cbfdcc436 100644 --- a/packages/customize-widgets/src/store/reducer.js +++ b/packages/customize-widgets/src/store/reducer.js @@ -25,6 +25,20 @@ const createWithInitialState = ( initialState ) => ( reducer ) => { return ( state = initialState, action ) => reducer( state, action ); }; +/** + * Reducer tracking whether the inserter is open. + * + * @param {boolean|Object} state + * @param {Object} action + */ +function blockInserterPanel( state = false, action ) { + switch ( action.type ) { + case 'SET_IS_INSERTER_OPENED': + return action.value; + } + return state; +} + /** * Reducer returning the user preferences. * @@ -50,5 +64,6 @@ export const preferences = flow( [ } ); export default combineReducers( { + blockInserterPanel, preferences, } ); diff --git a/packages/customize-widgets/src/store/selectors.js b/packages/customize-widgets/src/store/selectors.js index cc546ec6e5a7e..855bb17780b62 100644 --- a/packages/customize-widgets/src/store/selectors.js +++ b/packages/customize-widgets/src/store/selectors.js @@ -18,3 +18,26 @@ import { get } from 'lodash'; export function __unstableIsFeatureActive( state, feature ) { return get( state.preferences.features, [ feature ], false ); } + +/** + * Returns true if the inserter is opened. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether the inserter is opened. + */ +export function isInserterOpened( state ) { + return !! state.blockInserterPanel; +} + +/** + * Get the insertion point for the inserter. + * + * @param {Object} state Global application state. + * + * @return {Object} The root client ID and index to insert at. + */ +export function __experimentalGetInsertionPoint( state ) { + const { rootClientId, insertionIndex } = state.blockInserterPanel; + return { rootClientId, insertionIndex }; +} From 534a7bba5bbbb47a0cf5e71dfbc2141f87ba8622 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Sun, 1 Aug 2021 17:59:55 +0800 Subject: [PATCH 3/3] Fix internal action --- .../src/controls/inserter-outer-section.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/customize-widgets/src/controls/inserter-outer-section.js b/packages/customize-widgets/src/controls/inserter-outer-section.js index 1f881a52f0d51..b90e24c4dad53 100644 --- a/packages/customize-widgets/src/controls/inserter-outer-section.js +++ b/packages/customize-widgets/src/controls/inserter-outer-section.js @@ -75,6 +75,19 @@ export default function getInserterOuterSection() { ); this.contentContainer.addClass( 'widgets-inserter' ); + + // Set a flag if the state is being changed from open() or close(). + // Don't propagate the event if it's an internal action to prevent infinite loop. + this.isFromInternalAction = false; + this.expanded.bind( () => { + if ( ! this.isFromInternalAction ) { + // Propagate the event to React to sync the state. + dispatch( customizeWidgetsStore ).setIsInserterOpened( + this.expanded() + ); + } + this.isFromInternalAction = false; + } ); } open() { if ( ! this.expanded() ) { @@ -82,6 +95,8 @@ export default function getInserterOuterSection() { this.activeElementBeforeExpanded = contentContainer.ownerDocument.activeElement; + this.isFromInternalAction = true; + this.expand( { completeCallback() { // We have to do this in a "completeCallback" or else the elements will not yet be visible/tabbable. @@ -103,6 +118,8 @@ export default function getInserterOuterSection() { const activeElement = contentContainer.ownerDocument.activeElement; + this.isFromInternalAction = true; + this.collapse( { completeCallback() { // Return back the focus when closing the inserter.