Skip to content

Commit

Permalink
Fix insertion point in Widgets editors (#33802)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin940726 authored and desrosj committed Aug 30, 2021
1 parent b4c5c94 commit 466c9e3
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 25 deletions.
13 changes: 13 additions & 0 deletions packages/customize-widgets/src/components/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div
Expand All @@ -34,6 +43,10 @@ function Inserter( { setIsOpened } ) {
</div>
<div className="customize-widgets-layout__inserter-panel-content">
<Library
rootClientId={ insertionPoint.rootClientId }
__experimentalInsertionIndex={
insertionPoint.insertionIndex
}
showInserterHelpPanel
onSelect={ () => setIsOpened( false ) }
/>
Expand Down
33 changes: 21 additions & 12 deletions packages/customize-widgets/src/components/inserter/use-inserter.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
/**
* 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,
useCallback(
( 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 ]
),
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default function SidebarBlockEditor( {
blockEditorSettings,
isFixedToolbarActive,
keepCaretInsideBlock,
setIsInserterOpened,
] );

if ( isWelcomeGuideActive ) {
Expand Down
41 changes: 29 additions & 12 deletions packages/customize-widgets/src/controls/inserter-outer-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -52,33 +58,42 @@ export default function getInserterOuterSection() {
'keydown',
( event ) => {
if (
this.isOpen &&
this.expanded() &&
( event.keyCode === ESCAPE || event.code === 'Escape' )
) {
event.stopPropagation();

this.close();
dispatch( customizeWidgetsStore ).setIsInserterOpened(
false
);
}
},
// Use capture mode to make this run before other event listeners.
true
);

this.contentContainer.addClass( 'widgets-inserter' );
}
get isOpen() {
return this.expanded();
}
subscribe( handler ) {
this.expanded.bind( handler );
return () => this.expanded.unbind( handler );

// 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.isOpen ) {
if ( ! this.expanded() ) {
const contentContainer = this.contentContainer[ 0 ];
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.
Expand All @@ -95,11 +110,13 @@ export default function getInserterOuterSection() {
}
}
close() {
if ( this.isOpen ) {
if ( this.expanded() ) {
const contentContainer = this.contentContainer[ 0 ];
const activeElement =
contentContainer.ownerDocument.activeElement;

this.isFromInternalAction = true;

this.collapse( {
completeCallback() {
// Return back the focus when closing the inserter.
Expand Down
10 changes: 9 additions & 1 deletion packages/customize-widgets/src/controls/sidebar-control.js
Original file line number Diff line number Diff line change
@@ -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 }`;

Expand Down Expand Up @@ -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 ) =>
Expand Down
19 changes: 19 additions & 0 deletions packages/customize-widgets/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}
15 changes: 15 additions & 0 deletions packages/customize-widgets/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -50,5 +64,6 @@ export const preferences = flow( [
} );

export default combineReducers( {
blockInserterPanel,
preferences,
} );
23 changes: 23 additions & 0 deletions packages/customize-widgets/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import { store as editWidgetsStore } from '../store';
import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../store/utils';

const useWidgetLibraryInsertionPoint = () => {
Expand All @@ -30,6 +31,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 );

Expand Down
12 changes: 12 additions & 0 deletions packages/edit-widgets/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,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.
*
Expand Down

0 comments on commit 466c9e3

Please sign in to comment.