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

Editor: Animate opening and closing editor right sidebar #60561

Merged
merged 10 commits into from
Apr 11, 2024
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ function routerReducer(
action: RouterAction
): RouterState {
let { screens, locationHistory, matchedPath } = state;

switch ( action.type ) {
case 'add':
screens = addScreen( state, action.screen );
Expand All @@ -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':
Expand Down
31 changes: 4 additions & 27 deletions packages/edit-post/src/components/layout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -313,25 +307,8 @@ function Layout( { initialPost } ) {
editorNotices={ <EditorNotices /> }
secondarySidebar={ secondarySidebar() }
sidebar={
( ( isMobileViewport && sidebarIsOpened ) ||
( ! isMobileViewport && ! isDistractionFree ) ) && (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sidebar animations can't work if the sidebar is conditionally rendered, it means that we have to "always" render the sidebar and let the "complementary areas" handle the showing/hiding of the panels.

This means that I removed the "toggle sidebar" button that is present in the post editor (this also aligns behavior with the site editor).

<>
{ ! isMobileViewport && ! sidebarIsOpened && (
<div className="edit-post-layout__toggle-sidebar-panel">
<Button
variant="secondary"
className="edit-post-layout__toggle-sidebar-panel-button"
onClick={ openSidebarPanel }
aria-expanded={ false }
>
{ hasBlockSelected
? __( 'Open block settings' )
: __( 'Open document settings' ) }
</Button>
</div>
) }
<ComplementaryArea.Slot scope="core/edit-post" />
</>
! isDistractionFree && (
<ComplementaryArea.Slot scope="core/edit-post" />
)
}
notices={ <EditorSnackbars /> }
Expand Down
86 changes: 37 additions & 49 deletions packages/edit-post/src/components/sidebar/settings-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 ) => {
Expand All @@ -99,11 +95,11 @@ const SidebarContent = ( {
) {
selectedTabElement?.focus();
}
}, [ sidebarName ] );
}, [ tabName ] );

return (
<PluginSidebarEditPost
identifier={ sidebarName }
identifier={ tabName }
header={
<Tabs.Context.Provider value={ tabsContextValue }>
<SettingsHeader ref={ tabListRef } />
Expand Down Expand Up @@ -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 );

Expand All @@ -202,17 +195,12 @@ const SettingsSidebar = () => {

return (
<Tabs
// Due to how this component is controlled (via a value from the
// `interfaceStore`), when the sidebar closes the currently selected
// tab can't be found. This causes the component to continuously reset
// the selection to `null` in an infinite loop.Proactively setting
// the selected tab to `null` avoids that.
selectedTabId={ isSettingsSidebarActive ? sidebarName : null }
selectedTabId={ tabName }
onSelect={ onTabSelect }
selectOnMove={ false }
>
<SidebarContent
sidebarName={ sidebarName }
tabName={ tabName }
keyboardShortcut={ keyboardShortcut }
isEditingTemplate={ isEditingTemplate }
/>
Expand Down
15 changes: 0 additions & 15 deletions packages/edit-post/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
2 changes: 0 additions & 2 deletions packages/edit-site/src/components/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,7 @@ export default function Editor( { isLoading, onClick } ) {
( shouldShowListView && <ListViewSidebar /> ) )
}
sidebar={
! isDistractionFree &&
isEditMode &&
isRightSidebarOpen &&
! isDistractionFree && (
<ComplementaryArea.Slot scope="core/edit-site" />
)
Expand Down
45 changes: 16 additions & 29 deletions packages/edit-site/src/components/sidebar-edit-mode/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 ) => {
Expand All @@ -69,12 +65,12 @@ const FillContents = ( {
) {
selectedTabElement?.focus();
}
}, [ sidebarName ] );
}, [ tabName ] );

return (
<>
<DefaultSidebar
identifier={ sidebarName }
identifier={ tabName }
title={ __( 'Settings' ) }
icon={ isRTL() ? drawerLeft : drawerRight }
closeLabel={ __( 'Close Settings' ) }
Expand Down Expand Up @@ -108,31 +104,34 @@ const FillContents = ( {

export function SidebarComplementaryAreaFills() {
const {
sidebar,
tabName,
isEditorSidebarOpened,
hasBlockSelection,
supportsGlobalStyles,
isEditingPage,
isEditorOpen,
} = useSelect( ( select ) => {
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 );
Expand All @@ -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
Expand All @@ -177,19 +171,12 @@ export function SidebarComplementaryAreaFills() {

return (
<Tabs
// Due to how this component is controlled (via a value from the
// edit-site store), when the sidebar closes the currently selected
// tab can't be found. This causes the component to continuously reset
// the selection to `null` in an infinite loop. Proactively setting
// the selected tab to `null` avoids that.
selectedTabId={
isEditorOpen && isEditorSidebarOpened ? sidebarName : null
}
selectedTabId={ tabName }
onSelect={ onTabSelect }
selectOnMove={ false }
>
<FillContents
sidebarName={ sidebarName }
tabName={ tabName }
isEditingPage={ isEditingPage }
supportsGlobalStyles={ supportsGlobalStyles }
/>
Expand Down
6 changes: 1 addition & 5 deletions packages/edit-widgets/src/components/layout/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,7 @@ function Interface( { blockEditorSettings } ) {
} }
header={ <Header /> }
secondarySidebar={ hasSecondarySidebar && <SecondarySidebar /> }
sidebar={
hasSidebarEnabled && (
<ComplementaryArea.Slot scope="core/edit-widgets" />
)
}
sidebar={ <ComplementaryArea.Slot scope="core/edit-widgets" /> }
content={
<>
<WidgetAreasBlockEditorContent
Expand Down
Loading
Loading