Skip to content

Commit

Permalink
[Widgets] Auto expand the last selected widget area when opening the …
Browse files Browse the repository at this point in the history
…inserter (#25669)

* Auto expand the first widget area if none is opened and the inserter is opened

* Refactor to use clientId as keys and handle last selected widget areas

* Select the last selected block when opening the inserter

* Fix JSDOC
  • Loading branch information
kevin940726 authored Sep 28, 2020
1 parent 2d5e6f9 commit b390873
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 19 deletions.
16 changes: 12 additions & 4 deletions packages/block-library/src/widget-area/edit/index.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -15,14 +15,22 @@ export default function WidgetAreaEdit( {
className,
attributes: { id, name },
} ) {
const index = useSelect(
( select ) => select( 'core/block-editor' ).getBlockIndex( clientId ),
const isOpen = useSelect(
( select ) =>
select( 'core/edit-widgets' ).getIsWidgetAreaOpen( clientId ),
[ clientId ]
);
const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' );

return (
<Panel className={ className }>
<PanelBody title={ name } initialOpen={ index === 0 }>
<PanelBody
title={ name }
opened={ isOpen }
onToggle={ ( opened ) => {
setIsWidgetAreaOpen( clientId, opened );
} }
>
<EntityProvider
kind="root"
type="postType"
Expand Down
18 changes: 18 additions & 0 deletions packages/edit-widgets/src/components/header/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { ToolbarItem } from '@wordpress/components';
import {
Expand All @@ -25,6 +26,22 @@ const inserterToggleProps = { isPrimary: true };
function Header( { isCustomizer } ) {
const isLargeViewport = useViewportMatch( 'medium' );
const rootClientId = useLastSelectedRootId();
const isLastSelectedWidgetAreaOpen = useSelect(
( select ) =>
select( 'core/edit-widgets' ).getIsWidgetAreaOpen( rootClientId ),
[ rootClientId ]
);
const { setIsWidgetAreaOpen } = useDispatch( 'core/edit-widgets' );
const { selectBlock } = useDispatch( 'core/block-editor' );

function handleInserterOpen( isOpen ) {
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 );
}
}

return (
<>
Expand All @@ -43,6 +60,7 @@ function Header( { isCustomizer } ) {
...toolbarItemProps,
} }
rootClientId={ rootClientId }
onToggle={ handleInserterOpen }
/>
) }
</ToolbarItem>
Expand Down
22 changes: 7 additions & 15 deletions packages/edit-widgets/src/hooks/use-last-selected-root-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useRef } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -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;
28 changes: 28 additions & 0 deletions packages/edit-widgets/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,31 @@ export function setWidgetIdForClientId( clientId, widgetId ) {
widgetId,
};
}

/**
* Sets the open state of all the widget areas.
*
* @param {Object} widgetAreasOpenState 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 {string} clientId The clientId of the widget area.
* @param {boolean} isOpen Whether the widget area should be opened.
* @return {Object} Action.
*/
export function setIsWidgetAreaOpen( clientId, isOpen ) {
return {
type: 'SET_IS_WIDGET_AREA_OPEN',
clientId,
isOpen,
};
}
27 changes: 27 additions & 0 deletions packages/edit-widgets/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,33 @@ export function mapping( state, action ) {
return state || {};
}

/**
* Controls the open state of the widget areas.
*
* @param {Object} 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 { clientId, isOpen } = action;
return {
...state,
[ clientId ]: isOpen,
};
}
default: {
return state;
}
}
}

export default combineReducers( {
mapping,
widgetAreasOpenState,
} );
8 changes: 8 additions & 0 deletions packages/edit-widgets/src/store/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -61,6 +62,13 @@ export function* getWidgetAreas() {
);
}

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 );

yield {
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 @@ -160,3 +160,15 @@ export const hasResolvedWidgetAreas = createRegistrySelector(
return true;
}
);

/**
* Gets whether the widget area is opened.
*
* @param {Array} state The open state of the widget areas.
* @param {string} clientId The clientId of the widget area.
* @return {boolean} True if the widget area is open.
*/
export const getIsWidgetAreaOpen = ( state, clientId ) => {
const { widgetAreasOpenState } = state;
return !! widgetAreasOpenState[ clientId ];
};

0 comments on commit b390873

Please sign in to comment.