diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 84cd741f5eb749..6f9c5d06709a8b 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -7,6 +7,7 @@ - `ExternalLink`: Use unicode arrow instead of svg icon ([#60255](https://github.com/WordPress/gutenberg/pull/60255)). - `ProgressBar`: Move the indicator width styles from emotion to a CSS variable ([#60388](https://github.com/WordPress/gutenberg/pull/60388)). - `Text`: Add `text-wrap: pretty;` to improve wrapping. ([#60164](https://github.com/WordPress/gutenberg/pull/60164)). +- `Navigator`: Navigation to the active path doesn't create a new location history. ([#60561](https://github.com/WordPress/gutenberg/pull/60561)) ## 27.3.0 (2024-04-03) diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx index c776b036d39b60..368e78d799d72d 100644 --- a/packages/components/src/navigator/navigator-provider/component.tsx +++ b/packages/components/src/navigator/navigator-provider/component.tsx @@ -146,7 +146,6 @@ function routerReducer( action: RouterAction ): RouterState { let { screens, locationHistory, matchedPath } = state; - switch ( action.type ) { case 'add': screens = addScreen( state, action.screen ); @@ -158,6 +157,13 @@ function routerReducer( locationHistory = goBack( state ); break; case 'goto': + if ( + locationHistory.length && + action.path === + locationHistory[ locationHistory.length - 1 ].path + ) { + break; + } locationHistory = goTo( state, action.path, action.options ); break; case 'gotoparent': diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 26fbc70f1403fe..6fc9ab570e5ac9 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -24,7 +24,7 @@ import { privateApis as blockEditorPrivateApis, store as blockEditorStore, } from '@wordpress/block-editor'; -import { Button, ScrollLock } from '@wordpress/components'; +import { ScrollLock } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; import { __, _x, sprintf } from '@wordpress/i18n'; @@ -139,8 +139,7 @@ function Layout( { initialPost } ) { const isWideViewport = useViewportMatch( 'large' ); const isLargeViewport = useViewportMatch( 'medium' ); - const { openGeneralSidebar, closeGeneralSidebar } = - useDispatch( editPostStore ); + const { closeGeneralSidebar } = useDispatch( editPostStore ); const { createErrorNotice } = useDispatch( noticesStore ); const { setIsInserterOpened } = useDispatch( editorStore ); const { @@ -206,11 +205,6 @@ function Layout( { initialPost } ) { const styles = useEditorStyles(); - const openSidebarPanel = () => - openGeneralSidebar( - hasBlockSelected ? 'edit-post/block' : 'edit-post/document' - ); - // Inserter and Sidebars are mutually exclusive useEffect( () => { if ( sidebarIsOpened && ! isHugeViewport ) { @@ -313,25 +307,8 @@ function Layout( { initialPost } ) { editorNotices={ } secondarySidebar={ secondarySidebar() } sidebar={ - ( ( isMobileViewport && sidebarIsOpened ) || - ( ! isMobileViewport && ! isDistractionFree ) ) && ( - <> - { ! isMobileViewport && ! sidebarIsOpened && ( -
- -
- ) } - - + ! isDistractionFree && ( + ) } notices={ } diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index 6ea62974ebbbe0..27b85cffa08643 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -63,11 +63,7 @@ function onActionPerformed( actionId, items ) { } } -const SidebarContent = ( { - sidebarName, - keyboardShortcut, - isEditingTemplate, -} ) => { +const SidebarContent = ( { tabName, keyboardShortcut, isEditingTemplate } ) => { const tabListRef = useRef( null ); // Because `PluginSidebarEditPost` renders a `ComplementaryArea`, we // need to forward the `Tabs` context so it can be passed through the @@ -86,7 +82,7 @@ const SidebarContent = ( { // We are purposefully using a custom `data-tab-id` attribute here // because we don't want rely on any assumptions about `Tabs` // component internals. - ( element ) => element.getAttribute( 'data-tab-id' ) === sidebarName + ( element ) => element.getAttribute( 'data-tab-id' ) === tabName ); const activeElement = selectedTabElement?.ownerDocument.activeElement; const tabsHasFocus = tabsElements.some( ( element ) => { @@ -99,11 +95,11 @@ const SidebarContent = ( { ) { selectedTabElement?.focus(); } - }, [ sidebarName ] ); + }, [ tabName ] ); return ( @@ -153,41 +149,38 @@ const SidebarContent = ( { }; const SettingsSidebar = () => { - const { - sidebarName, - isSettingsSidebarActive, - keyboardShortcut, - isEditingTemplate, - } = useSelect( ( select ) => { - // The settings sidebar is used by the edit-post/document and edit-post/block sidebars. - // sidebarName represents the sidebar that is active or that should be active when the SettingsSidebar toggle button is pressed. - // If one of the two sidebars is active the component will contain the content of that sidebar. - // When neither of the two sidebars is active we can not simply return null, because the PluginSidebarEditPost - // component, besides being used to render the sidebar, also renders the toggle button. In that case sidebarName - // should contain the sidebar that will be active when the toggle button is pressed. If a block - // is selected, that should be edit-post/block otherwise it's edit-post/document. - let sidebar = select( interfaceStore ).getActiveComplementaryArea( - editPostStore.name - ); - let isSettingsSidebar = true; - if ( ! [ sidebars.document, sidebars.block ].includes( sidebar ) ) { - isSettingsSidebar = false; - if ( select( blockEditorStore ).getBlockSelectionStart() ) { - sidebar = sidebars.block; + const { tabName, keyboardShortcut, isEditingTemplate } = useSelect( + ( select ) => { + const shortcut = select( + keyboardShortcutsStore + ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ); + + const sidebar = select( interfaceStore ).getActiveComplementaryArea( + editPostStore.name + ); + const _isEditorSidebarOpened = [ + sidebars.block, + sidebars.document, + ].includes( sidebar ); + let _tabName = sidebar; + if ( ! _isEditorSidebarOpened ) { + _tabName = !! select( + blockEditorStore + ).getBlockSelectionStart() + ? sidebars.block + : sidebars.document; } - sidebar = sidebars.document; - } - const shortcut = select( - keyboardShortcutsStore - ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ); - return { - sidebarName: sidebar, - isSettingsSidebarActive: isSettingsSidebar, - keyboardShortcut: shortcut, - isEditingTemplate: - select( editorStore ).getCurrentPostType() === 'wp_template', - }; - }, [] ); + + return { + tabName: _tabName, + keyboardShortcut: shortcut, + isEditingTemplate: + select( editorStore ).getCurrentPostType() === + 'wp_template', + }; + }, + [] + ); const { openGeneralSidebar } = useDispatch( editPostStore ); @@ -202,17 +195,12 @@ const SettingsSidebar = () => { return ( diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index be16158bc6b04d..4ba063905d8b2e 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -77,18 +77,3 @@ body.js.block-editor-page { } @include wordpress-admin-schemes(); - -// The edit-site package adds or removes the sidebar when it's opened or closed. -// The edit-post package, however, always has the sidebar in the canvas. -// These edit-post specific rules ensures there isn't a border on the right of -// the canvas when a sidebar is visually closed. -.interface-interface-skeleton__sidebar { - border-left: none; - - .is-sidebar-opened & { - @include break-medium() { - border-left: $border-width solid $gray-200; - overflow: hidden scroll; - } - } -} diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index f5fdd62ba7dc14..ba04333b985bde 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -297,9 +297,7 @@ export default function Editor( { isLoading, onClick } ) { ( shouldShowListView && ) ) } sidebar={ - ! isDistractionFree && isEditMode && - isRightSidebarOpen && ! isDistractionFree && ( ) diff --git a/packages/edit-site/src/components/sidebar-edit-mode/index.js b/packages/edit-site/src/components/sidebar-edit-mode/index.js index f3cb7cc9dae0ed..38b2ca0665cc4b 100644 --- a/packages/edit-site/src/components/sidebar-edit-mode/index.js +++ b/packages/edit-site/src/components/sidebar-edit-mode/index.js @@ -33,11 +33,7 @@ const { Slot: InspectorSlot, Fill: InspectorFill } = createSlotFill( ); export const SidebarInspectorFill = InspectorFill; -const FillContents = ( { - sidebarName, - isEditingPage, - supportsGlobalStyles, -} ) => { +const FillContents = ( { tabName, isEditingPage, supportsGlobalStyles } ) => { const tabListRef = useRef( null ); // Because `DefaultSidebar` renders a `ComplementaryArea`, we // need to forward the `Tabs` context so it can be passed through the @@ -56,7 +52,7 @@ const FillContents = ( { // We are purposefully using a custom `data-tab-id` attribute here // because we don't want rely on any assumptions about `Tabs` // component internals. - ( element ) => element.getAttribute( 'data-tab-id' ) === sidebarName + ( element ) => element.getAttribute( 'data-tab-id' ) === tabName ); const activeElement = selectedTabElement?.ownerDocument.activeElement; const tabsHasFocus = tabsElements.some( ( element ) => { @@ -69,12 +65,12 @@ const FillContents = ( { ) { selectedTabElement?.focus(); } - }, [ sidebarName ] ); + }, [ tabName ] ); return ( <> { - const _sidebar = + const sidebar = select( interfaceStore ).getActiveComplementaryArea( STORE_NAME ); const _isEditorSidebarOpened = [ SIDEBAR_BLOCK, SIDEBAR_TEMPLATE, - ].includes( _sidebar ); - const { getCanvasMode } = unlock( select( editSiteStore ) ); + ].includes( sidebar ); + let _tabName = sidebar; + if ( ! _isEditorSidebarOpened ) { + _tabName = !! select( blockEditorStore ).getBlockSelectionStart() + ? SIDEBAR_BLOCK + : SIDEBAR_TEMPLATE; + } return { - sidebar: _sidebar, + tabName: _tabName, isEditorSidebarOpened: _isEditorSidebarOpened, hasBlockSelection: !! select( blockEditorStore ).getBlockSelectionStart(), supportsGlobalStyles: select( coreStore ).getCurrentTheme()?.is_block_theme, isEditingPage: select( editSiteStore ).isPage(), - isEditorOpen: getCanvasMode() === 'edit', }; }, [] ); const { enableComplementaryArea } = useDispatch( interfaceStore ); @@ -157,11 +156,6 @@ export function SidebarComplementaryAreaFills() { enableComplementaryArea, ] ); - let sidebarName = sidebar; - if ( ! isEditorSidebarOpened ) { - sidebarName = hasBlockSelection ? SIDEBAR_BLOCK : SIDEBAR_TEMPLATE; - } - // `newSelectedTabId` could technically be falsey if no tab is selected (i.e. // the initial render) or when we don't want a tab displayed (i.e. the // sidebar is closed). These cases should both be covered by the `!!` check @@ -177,19 +171,12 @@ export function SidebarComplementaryAreaFills() { return ( diff --git a/packages/edit-widgets/src/components/layout/interface.js b/packages/edit-widgets/src/components/layout/interface.js index 987e3868de1337..ee46251eca2245 100644 --- a/packages/edit-widgets/src/components/layout/interface.js +++ b/packages/edit-widgets/src/components/layout/interface.js @@ -96,11 +96,7 @@ function Interface( { blockEditorSettings } ) { } } header={
} secondarySidebar={ hasSecondarySidebar && } - sidebar={ - hasSidebarEnabled && ( - - ) - } + sidebar={ } content={ <> ; } -function ComplementaryAreaFill( { scope, children, className, id } ) { +const SIDEBAR_WIDTH = 280; +const variants = { + open: { width: SIDEBAR_WIDTH }, + closed: { width: 0 }, + mobileOpen: { width: '100vw' }, +}; + +function ComplementaryAreaFill( { + activeArea, + isActive, + scope, + children, + className, + id, +} ) { + const disableMotion = useReducedMotion(); + const isMobileViewport = useViewportMatch( 'medium', '<' ); + // This is used to delay the exit animation to the next tick. + // The reason this is done is to allow us to apply the right transition properties + // When we switch from an open sidebar to another open sidebar. + // we don't want to animate in this case. + const previousActiveArea = usePrevious( activeArea ); + const previousIsActive = usePrevious( isActive ); + const [ , setState ] = useState( {} ); + useEffect( () => { + setState( {} ); + }, [ isActive ] ); + const transition = { + type: 'tween', + duration: + disableMotion || + isMobileViewport || + ( !! previousActiveArea && + !! activeArea && + activeArea !== previousActiveArea ) + ? 0 + : ANIMATION_DURATION, + ease: [ 0.6, 0, 0.4, 1 ], + }; + return ( -
- { children } -
+ + { ( previousIsActive || isActive ) && ( + +
+ { children } +
+
+ ) } +
); } @@ -110,6 +183,11 @@ function ComplementaryArea( { toggleShortcut, isActiveByDefault, } ) { + // This state is used to delay the rendering of the Fill + // until the initial effect runs. + // This prevents the animation from running on mount if + // the complementary area is active by default. + const [ isReady, setIsReady ] = useState( false ); const { isLoading, isActive, @@ -163,6 +241,7 @@ function ComplementaryArea( { } else if ( activeArea === undefined && isSmall ) { disableComplementaryArea( scope, identifier ); } + setIsReady( true ); }, [ activeArea, isActiveByDefault, @@ -173,6 +252,10 @@ function ComplementaryArea( { disableComplementaryArea, ] ); + if ( ! isReady ) { + return; + } + return ( <> { isPinnable && ( @@ -204,59 +287,57 @@ function ComplementaryArea( { { title } ) } - { isActive && ( - + disableComplementaryArea( scope ) } + smallScreenTitle={ smallScreenTitle } + toggleButtonProps={ { + label: closeLabel, + shortcut: toggleShortcut, + scope, + identifier, + } } > - disableComplementaryArea( scope ) } - smallScreenTitle={ smallScreenTitle } - toggleButtonProps={ { - label: closeLabel, - shortcut: toggleShortcut, - scope, - identifier, - } } - > - { header || ( - <> -

- { title } -

- { isPinnable && ( -