diff --git a/package-lock.json b/package-lock.json index c01f19ad33228..7425d75403ad1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55359,7 +55359,8 @@ "@wordpress/reusable-blocks": "file:../reusable-blocks", "@wordpress/url": "file:../url", "@wordpress/widgets": "file:../widgets", - "classnames": "^2.3.1" + "classnames": "^2.3.1", + "rememo": "^4.0.2" }, "engines": { "node": ">=12" @@ -70610,7 +70611,8 @@ "@wordpress/reusable-blocks": "file:../reusable-blocks", "@wordpress/url": "file:../url", "@wordpress/widgets": "file:../widgets", - "classnames": "^2.3.1" + "classnames": "^2.3.1", + "rememo": "^4.0.2" } }, "@wordpress/editor": { diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index d585102c06db9..c524a8842ad4c 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -32,7 +32,7 @@ const preventDefault = ( event ) => { event.preventDefault(); }; -function HeaderToolbar( { hasFixedToolbar, setListViewToggleElement } ) { +function HeaderToolbar( { hasFixedToolbar } ) { const inserterButton = useRef(); const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editorStore ); @@ -43,10 +43,12 @@ function HeaderToolbar( { hasFixedToolbar, setListViewToggleElement } ) { showIconLabels, isListViewOpen, listViewShortcut, + listViewToggleRef, } = useSelect( ( select ) => { const { hasInserterItems, getBlockRootClientId, getBlockSelectionEnd } = select( blockEditorStore ); - const { getEditorSettings, isListViewOpened } = select( editorStore ); + const { getEditorSettings, isListViewOpened, getListViewToggleRef } = + unlock( select( editorStore ) ); const { getEditorMode, isFeatureActive } = select( editPostStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); @@ -65,6 +67,7 @@ function HeaderToolbar( { hasFixedToolbar, setListViewToggleElement } ) { listViewShortcut: getShortcutRepresentation( 'core/edit-post/toggle-list-view' ), + listViewToggleRef: getListViewToggleRef(), }; }, [] ); @@ -103,7 +106,7 @@ function HeaderToolbar( { hasFixedToolbar, setListViewToggleElement } ) { showTooltip={ ! showIconLabels } variant={ showIconLabels ? 'tertiary' : undefined } aria-expanded={ isListViewOpen } - ref={ setListViewToggleElement } + ref={ listViewToggleRef } size="compact" /> diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index 67349bd640403..1e0a1f3195613 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -55,10 +55,7 @@ const slideX = { hover: { x: 0, transition: { type: 'tween', delay: 0.2 } }, }; -function Header( { - setEntitiesSavedStatesCallback, - setListViewToggleElement, -} ) { +function Header( { setEntitiesSavedStatesCallback } ) { const isWideViewport = useViewportMatch( 'large' ); const isLargeViewport = useViewportMatch( 'medium' ); const blockToolbarRef = useRef(); @@ -111,10 +108,7 @@ function Header( { transition={ { type: 'tween', delay: 0.8 } } className="edit-post-header__toolbar" > - + { hasFixedToolbar && isLargeViewport && ( <>
{ if ( typeof entitiesSavedStatesCallback === 'function' ) { @@ -268,11 +265,7 @@ function Layout() { return ; } if ( mode === 'visual' && isListViewOpened ) { - return ( - - ); + return ; } return null; @@ -313,7 +306,6 @@ function Layout() { setEntitiesSavedStatesCallback={ setEntitiesSavedStatesCallback } - setListViewToggleElement={ setListViewToggleElement } /> } editorNotices={ } diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index fa692d4869004..02690d9115d7a 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -4,7 +4,7 @@ import { __experimentalListView as ListView } from '@wordpress/block-editor'; import { Button, TabPanel } from '@wordpress/components'; import { useFocusOnMount, useMergeRefs } from '@wordpress/compose'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { focus } from '@wordpress/dom'; import { useCallback, useRef, useState } from '@wordpress/element'; import { __, _x } from '@wordpress/i18n'; @@ -17,9 +17,11 @@ import { store as editorStore } from '@wordpress/editor'; * Internal dependencies */ import ListViewOutline from './list-view-outline'; +import { unlock } from '../../lock-unlock'; -export default function ListViewSidebar( { listViewToggleElement } ) { +export default function ListViewSidebar() { const { setIsListViewOpened } = useDispatch( editorStore ); + const { getListViewToggleRef } = unlock( useSelect( editorStore ) ); // This hook handles focus when the sidebar first renders. const focusOnMountRef = useFocusOnMount( 'firstElement' ); @@ -27,8 +29,8 @@ export default function ListViewSidebar( { listViewToggleElement } ) { // When closing the list view, focus should return to the toggle button. const closeListView = useCallback( () => { setIsListViewOpened( false ); - listViewToggleElement?.focus(); - }, [ listViewToggleElement, setIsListViewOpened ] ); + getListViewToggleRef().current?.focus(); + }, [ getListViewToggleRef, setIsListViewOpened ] ); const closeOnEscape = useCallback( ( event ) => { diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index db92c36d75af7..9a1931b2ede39 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -84,7 +84,7 @@ const blockRemovalRules = { ), }; -export default function Editor( { listViewToggleElement, isLoading } ) { +export default function Editor( { isLoading } ) { const { record: editedPost, getTitle, @@ -251,13 +251,7 @@ export default function Editor( { listViewToggleElement, isLoading } ) { secondarySidebar={ isEditMode && ( ( shouldShowInserter && ) || - ( shouldShowListView && ( - - ) ) ) + ( shouldShowListView && ) ) } sidebar={ isEditMode && diff --git a/packages/edit-site/src/components/header-edit-mode/document-tools/index.js b/packages/edit-site/src/components/header-edit-mode/document-tools/index.js index b67f842128e26..9db8e091265e2 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-tools/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-tools/index.js @@ -35,27 +35,30 @@ export default function DocumentTools( { hasFixedToolbar, isDistractionFree, showIconLabels, - setListViewToggleElement, } ) { const inserterButton = useRef(); - const { isInserterOpen, isListViewOpen, listViewShortcut, isVisualMode } = - useSelect( ( select ) => { - const { getEditorMode } = select( editSiteStore ); - const { getShortcutRepresentation } = select( - keyboardShortcutsStore - ); - const { isInserterOpened, isListViewOpened } = - select( editorStore ); + const { + isInserterOpen, + isListViewOpen, + listViewShortcut, + isVisualMode, + listViewToggleRef, + } = useSelect( ( select ) => { + const { getEditorMode } = select( editSiteStore ); + const { getShortcutRepresentation } = select( keyboardShortcutsStore ); + const { isInserterOpened, isListViewOpened, getListViewToggleRef } = + unlock( select( editorStore ) ); - return { - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - listViewShortcut: getShortcutRepresentation( - 'core/edit-site/toggle-list-view' - ), - isVisualMode: getEditorMode() === 'visual', - }; - }, [] ); + return { + isInserterOpen: isInserterOpened(), + isListViewOpen: isListViewOpened(), + listViewShortcut: getShortcutRepresentation( + 'core/edit-site/toggle-list-view' + ), + isVisualMode: getEditorMode() === 'visual', + listViewToggleRef: getListViewToggleRef(), + }; + }, [] ); const { __unstableSetEditorMode } = useDispatch( blockEditorStore ); const { setDeviceType, setIsInserterOpened, setIsListViewOpened } = useDispatch( editorStore ); @@ -161,7 +164,7 @@ export default function DocumentTools( { /* translators: button label text should, if possible, be under 16 characters. */ label={ __( 'List View' ) } onClick={ toggleListView } - ref={ setListViewToggleElement } + ref={ listViewToggleRef } shortcut={ listViewShortcut } showTooltip={ ! showIconLabels } variant={ diff --git a/packages/edit-site/src/components/header-edit-mode/index.js b/packages/edit-site/src/components/header-edit-mode/index.js index 4b9d48d797952..0d24865e74bf6 100644 --- a/packages/edit-site/src/components/header-edit-mode/index.js +++ b/packages/edit-site/src/components/header-edit-mode/index.js @@ -44,7 +44,7 @@ import { FOCUSABLE_ENTITIES } from '../../utils/constants'; const { PostViewLink, PreviewDropdown } = unlock( editorPrivateApis ); -export default function HeaderEditMode( { setListViewToggleElement } ) { +export default function HeaderEditMode() { const { templateType, isDistractionFree, @@ -137,7 +137,6 @@ export default function HeaderEditMode( { setListViewToggleElement } ) { blockEditorMode={ blockEditorMode } isDistractionFree={ isDistractionFree } showIconLabels={ showIconLabels } - setListViewToggleElement={ setListViewToggleElement } /> { isTopToolbar && ( <> diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 6d62ba1df07f4..c6ad3cf565b12 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -129,8 +129,6 @@ export default function Layout() { const isEditorLoading = useIsSiteEditorLoading(); const [ isResizableFrameOversized, setIsResizableFrameOversized ] = useState( false ); - const [ listViewToggleElement, setListViewToggleElement ] = - useState( null ); // This determines which animation variant should apply to the header. // There is also a `isDistractionFreeHovering` state that gets priority @@ -258,11 +256,7 @@ export default function Layout() { ease: 'easeOut', } } > -
+
) } @@ -367,9 +361,6 @@ export default function Layout() { } } > { setIsListViewOpened( false ); - listViewToggleElement?.focus(); - }, [ listViewToggleElement, setIsListViewOpened ] ); + getListViewToggleRef().current?.focus(); + }, [ getListViewToggleRef, setIsListViewOpened ] ); const closeOnEscape = useCallback( ( event ) => { diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 1ef98df31269f..974a0ba02905f 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -53,7 +53,8 @@ "@wordpress/reusable-blocks": "file:../reusable-blocks", "@wordpress/url": "file:../url", "@wordpress/widgets": "file:../widgets", - "classnames": "^2.3.1" + "classnames": "^2.3.1", + "rememo": "^4.0.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/edit-widgets/src/components/header/document-tools/index.js b/packages/edit-widgets/src/components/header/document-tools/index.js index 8cac7590be5e8..06376bbd76291 100644 --- a/packages/edit-widgets/src/components/header/document-tools/index.js +++ b/packages/edit-widgets/src/components/header/document-tools/index.js @@ -24,7 +24,7 @@ import { unlock } from '../../../lock-unlock'; const { useShouldContextualToolbarShow } = unlock( blockEditorPrivateApis ); -function DocumentTools( { setListViewToggleElement } ) { +function DocumentTools() { const isMediumViewport = useViewportMatch( 'medium' ); const inserterButton = useRef(); const widgetAreaClientId = useLastSelectedWidgetArea(); @@ -35,14 +35,18 @@ function DocumentTools( { setListViewToggleElement } ) { ), [ widgetAreaClientId ] ); - const { isInserterOpen, isListViewOpen } = useSelect( ( select ) => { - const { isInserterOpened, isListViewOpened } = - select( editWidgetsStore ); - return { - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - }; - }, [] ); + const { isInserterOpen, isListViewOpen, listViewToggleRef } = useSelect( + ( select ) => { + const { isInserterOpened, isListViewOpened, getListViewToggleRef } = + unlock( select( editWidgetsStore ) ); + return { + isInserterOpen: isInserterOpened(), + isListViewOpen: isListViewOpened(), + listViewToggleRef: getListViewToggleRef(), + }; + }, + [] + ); const { setIsWidgetAreaOpen, setIsInserterOpened, setIsListViewOpened } = useDispatch( editWidgetsStore ); const { selectBlock } = useDispatch( blockEditorStore ); @@ -119,7 +123,7 @@ function DocumentTools( { setListViewToggleElement } ) { /* translators: button label text should, if possible, be under 16 characters. */ label={ __( 'List View' ) } onClick={ toggleListView } - ref={ setListViewToggleElement } + ref={ listViewToggleRef } /> ) } diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index 9d4cb4cb60103..0aadec83d5d2f 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -17,7 +17,7 @@ import DocumentTools from './document-tools'; import SaveButton from '../save-button'; import MoreMenu from '../more-menu'; -function Header( { setListViewToggleElement } ) { +function Header() { const isLargeViewport = useViewportMatch( 'medium' ); const blockToolbarRef = useRef(); const { hasFixedToolbar } = useSelect( @@ -47,9 +47,7 @@ function Header( { setListViewToggleElement } ) { { __( 'Widgets' ) } ) } - + { hasFixedToolbar && isLargeViewport && ( <>
diff --git a/packages/edit-widgets/src/components/layout/interface.js b/packages/edit-widgets/src/components/layout/interface.js index 2cb1eebcfab73..987e3868de133 100644 --- a/packages/edit-widgets/src/components/layout/interface.js +++ b/packages/edit-widgets/src/components/layout/interface.js @@ -3,7 +3,7 @@ */ import { useViewportMatch } from '@wordpress/compose'; import { BlockBreadcrumb } from '@wordpress/block-editor'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { InterfaceSkeleton, @@ -68,9 +68,6 @@ function Interface( { blockEditorSettings } ) { [] ); - const [ listViewToggleElement, setListViewToggleElement ] = - useState( null ); - // Inserter and Sidebars are mutually exclusive useEffect( () => { if ( hasSidebarEnabled && ! isHugeViewport ) { @@ -97,16 +94,8 @@ function Interface( { blockEditorSettings } ) { ...interfaceLabels, secondarySidebar: secondarySidebarLabel, } } - header={ -
- } - secondarySidebar={ - hasSecondarySidebar && ( - - ) - } + header={
} + secondarySidebar={ hasSecondarySidebar && } sidebar={ hasSidebarEnabled && ( diff --git a/packages/edit-widgets/src/components/secondary-sidebar/index.js b/packages/edit-widgets/src/components/secondary-sidebar/index.js index 20488e4478b98..49e240bd147cb 100644 --- a/packages/edit-widgets/src/components/secondary-sidebar/index.js +++ b/packages/edit-widgets/src/components/secondary-sidebar/index.js @@ -13,7 +13,7 @@ import { store as editWidgetsStore } from '../../store'; import InserterSidebar from './inserter-sidebar'; import ListViewSidebar from './list-view-sidebar'; -export default function SecondarySidebar( { listViewToggleElement } ) { +export default function SecondarySidebar() { const { isInserterOpen, isListViewOpen } = useSelect( ( select ) => { const { isInserterOpened, isListViewOpened } = select( editWidgetsStore ); @@ -27,9 +27,7 @@ export default function SecondarySidebar( { listViewToggleElement } ) { return ; } if ( isListViewOpen ) { - return ( - - ); + return ; } return null; } diff --git a/packages/edit-widgets/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-widgets/src/components/secondary-sidebar/list-view-sidebar.js index 5104a587d9e2c..6aa2123a6774a 100644 --- a/packages/edit-widgets/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-widgets/src/components/secondary-sidebar/list-view-sidebar.js @@ -4,7 +4,7 @@ import { __experimentalListView as ListView } from '@wordpress/block-editor'; import { Button } from '@wordpress/components'; import { useFocusOnMount, useMergeRefs } from '@wordpress/compose'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { useCallback, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; @@ -14,9 +14,11 @@ import { ESCAPE } from '@wordpress/keycodes'; * Internal dependencies */ import { store as editWidgetsStore } from '../../store'; +import { unlock } from '../../lock-unlock'; -export default function ListViewSidebar( { listViewToggleElement } ) { +export default function ListViewSidebar() { const { setIsListViewOpened } = useDispatch( editWidgetsStore ); + const { getListViewToggleRef } = unlock( useSelect( editWidgetsStore ) ); // Use internal state instead of a ref to make sure that the component // re-renders when the dropZoneElement updates. @@ -27,8 +29,8 @@ export default function ListViewSidebar( { listViewToggleElement } ) { // When closing the list view, focus should return to the toggle button. const closeListView = useCallback( () => { setIsListViewOpened( false ); - listViewToggleElement?.focus(); - }, [ listViewToggleElement, setIsListViewOpened ] ); + getListViewToggleRef().current?.focus(); + }, [ getListViewToggleRef, setIsListViewOpened ] ); const closeOnEscape = useCallback( ( event ) => { diff --git a/packages/edit-widgets/src/store/index.js b/packages/edit-widgets/src/store/index.js index b8542b2fa5cf1..8768b45954b93 100644 --- a/packages/edit-widgets/src/store/index.js +++ b/packages/edit-widgets/src/store/index.js @@ -11,7 +11,9 @@ import reducer from './reducer'; import * as resolvers from './resolvers'; import * as selectors from './selectors'; import * as actions from './actions'; +import * as privateSelectors from './private-selectors'; import { STORE_NAME } from './constants'; +import { unlock } from '../lock-unlock'; /** * Block editor data store configuration. @@ -47,3 +49,5 @@ apiFetch.use( function ( options, next ) { return next( options ); } ); + +unlock( store ).registerPrivateSelectors( privateSelectors ); diff --git a/packages/edit-widgets/src/store/private-selectors.js b/packages/edit-widgets/src/store/private-selectors.js new file mode 100644 index 0000000000000..29911091622fb --- /dev/null +++ b/packages/edit-widgets/src/store/private-selectors.js @@ -0,0 +1,16 @@ +/** + * External dependencies + */ +import createSelector from 'rememo'; + +/** + * WordPress dependencies + */ +import { createRef } from '@wordpress/element'; + +export const getListViewToggleRef = createSelector( + () => { + return createRef(); + }, + () => [] +); diff --git a/packages/editor/src/store/private-selectors.js b/packages/editor/src/store/private-selectors.js index 0ab97dea67ce5..b05dcae93c947 100644 --- a/packages/editor/src/store/private-selectors.js +++ b/packages/editor/src/store/private-selectors.js @@ -1,8 +1,14 @@ +/** + * External dependencies + */ +import createSelector from 'rememo'; + /** * WordPress dependencies */ import { store as blockEditorStore } from '@wordpress/block-editor'; import { createRegistrySelector } from '@wordpress/data'; +import { createRef } from '@wordpress/element'; /** * Internal dependencies @@ -45,3 +51,10 @@ export const getInsertionPoint = createRegistrySelector( return EMPTY_INSERTION_POINT; } ); + +export const getListViewToggleRef = createSelector( + () => { + return createRef(); + }, + () => [] +);