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 && (
-
- { children }
-
- ) }
+ { header || (
+ <>
+
+ { title }
+
+ { isPinnable && (
+