Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Widgets] Auto expand the last selected widget area when opening the inserter #25669

Merged
merged 4 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 ];
};