From 85addad18f7d1865bfcb2b7afe498cc41b22801f Mon Sep 17 00:00:00 2001 From: CatsJuice Date: Thu, 13 Feb 2025 05:25:09 +0000 Subject: [PATCH] feat(core): persist right-sidebar open state and resize width (#10120) --- .../components/resize-panel/resize-panel.tsx | 18 +++++++++++- .../core/src/components/atoms/index.ts | 1 - .../modules/workbench/entities/workbench.ts | 28 +++++++++++++++---- .../core/src/modules/workbench/index.ts | 8 ++++-- .../modules/workbench/view/workbench-root.tsx | 16 ++++++++--- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/packages/frontend/component/src/components/resize-panel/resize-panel.tsx b/packages/frontend/component/src/components/resize-panel/resize-panel.tsx index ebc6d6aac..c3af43d3a 100644 --- a/packages/frontend/component/src/components/resize-panel/resize-panel.tsx +++ b/packages/frontend/component/src/components/resize-panel/resize-panel.tsx @@ -19,6 +19,7 @@ export interface ResizeHandleProps onOpen: (open: boolean) => void; onResizing: (resizing: boolean) => void; onWidthChange: (width: number) => void; + onWidthChanged?: (width: number) => void; tooltip?: TooltipProps['content']; tooltipShortcut?: TooltipProps['shortcut']; tooltipOptions?: Partial>; @@ -49,6 +50,7 @@ export interface ResizePanelProps onOpen: (open: boolean) => void; onResizing: (resizing: boolean) => void; onWidthChange: (width: number) => void; + onWidthChanged?: (width: number) => void; } const ResizeHandle = ({ @@ -64,6 +66,7 @@ const ResizeHandle = ({ onOpen, onResizing, onWidthChange, + onWidthChanged, tooltip, tooltipShortcut, tooltipOptions, @@ -84,6 +87,7 @@ const ResizeHandle = ({ const { left: anchorLeft, right: anchorRight } = panelContainer.getBoundingClientRect(); + let lastWidth: number; function onMouseMove(e: MouseEvent) { e.preventDefault(); if (!panelContainer) return; @@ -96,6 +100,7 @@ const ResizeHandle = ({ minWidth ) ); + lastWidth = newWidth; onWidthChange(newWidth); onResizing(true); resized = true; @@ -110,13 +115,22 @@ const ResizeHandle = ({ onOpen(false); } onResizing(false); + lastWidth && onWidthChanged?.(lastWidth); document.removeEventListener('mousemove', onMouseMove); document.body.style.cursor = ''; }, { once: true } ); }, - [maxWidth, resizeHandlePos, minWidth, onWidthChange, onResizing, onOpen] + [ + maxWidth, + resizeHandlePos, + minWidth, + onWidthChange, + onResizing, + onWidthChanged, + onOpen, + ] ); const { dropTargetRef } = useDropTarget(dropTargetOptions, [ @@ -173,6 +187,7 @@ export const ResizePanel = forwardRef( onOpen, onResizing, onWidthChange, + onWidthChanged, resizeHandlePos, resizeHandleOffset, resizeHandleVerticalPadding, @@ -223,6 +238,7 @@ export const ResizePanel = forwardRef( onOpen={onOpen} onResizing={onResizing} onWidthChange={onWidthChange} + onWidthChanged={onWidthChanged} open={open} resizing={resizing} dropTargetOptions={resizeHandleDropTargetOptions} diff --git a/packages/frontend/core/src/components/atoms/index.ts b/packages/frontend/core/src/components/atoms/index.ts index 8cd17bc60..60612a224 100644 --- a/packages/frontend/core/src/components/atoms/index.ts +++ b/packages/frontend/core/src/components/atoms/index.ts @@ -4,7 +4,6 @@ import { atom } from 'jotai'; * @deprecated use `useSignOut` hook instated */ export const openQuotaModalAtom = atom(false); -export const rightSidebarWidthAtom = atom(320); export type AllPageFilterOption = 'docs' | 'collections' | 'tags'; export const allPageFilterSelectAtom = atom('docs'); diff --git a/packages/frontend/core/src/modules/workbench/entities/workbench.ts b/packages/frontend/core/src/modules/workbench/entities/workbench.ts index 05d2be99c..b5266ecd1 100644 --- a/packages/frontend/core/src/modules/workbench/entities/workbench.ts +++ b/packages/frontend/core/src/modules/workbench/entities/workbench.ts @@ -6,6 +6,7 @@ import { type To } from 'history'; import { omit } from 'lodash-es'; import { nanoid } from 'nanoid'; +import type { GlobalState } from '../../storage'; import type { WorkbenchNewTabHandler } from '../services/workbench-new-tab-handler'; import type { WorkbenchDefaultState } from '../services/workbench-view-state'; import { View } from './view'; @@ -18,10 +19,14 @@ export type WorkbenchOpenOptions = { show?: boolean; // only for new tab }; +const sidebarOpenKey = 'workbenchSidebarOpen'; +const sidebarWidthKey = 'workbenchSidebarWidth'; + export class Workbench extends Entity { constructor( private readonly defaultState: WorkbenchDefaultState, - private readonly newTabHandler: WorkbenchNewTabHandler + private readonly newTabHandler: WorkbenchNewTabHandler, + private readonly globalState: GlobalState ) { super(); } @@ -50,7 +55,20 @@ export class Workbench extends Entity { location$ = LiveData.computed(get => { return get(get(this.activeView$).location$); }); - sidebarOpen$ = new LiveData(false); + sidebarOpen$ = LiveData.from( + this.globalState.watch(sidebarOpenKey), + false + ); + setSidebarOpen(open: boolean) { + this.globalState.set(sidebarOpenKey, open); + } + sidebarWidth$ = LiveData.from( + this.globalState.watch(sidebarWidthKey), + 320 + ); + setSidebarWidth(width: number) { + this.globalState.set(sidebarWidthKey, width); + } active(index: number | View) { if (typeof index === 'number') { @@ -85,15 +103,15 @@ export class Workbench extends Entity { } openSidebar() { - this.sidebarOpen$.next(true); + this.setSidebarOpen(true); } closeSidebar() { - this.sidebarOpen$.next(false); + this.setSidebarOpen(false); } toggleSidebar() { - this.sidebarOpen$.next(!this.sidebarOpen$.value); + this.setSidebarOpen(!this.sidebarOpen$.value); } open(to: To, option: WorkbenchOpenOptions = {}) { diff --git a/packages/frontend/core/src/modules/workbench/index.ts b/packages/frontend/core/src/modules/workbench/index.ts index 9415274fb..54a638a85 100644 --- a/packages/frontend/core/src/modules/workbench/index.ts +++ b/packages/frontend/core/src/modules/workbench/index.ts @@ -15,7 +15,7 @@ import { type Framework } from '@toeverything/infra'; import { DesktopApiService } from '../desktop-api'; import { PeekViewService } from '../peek-view'; -import { GlobalStateService } from '../storage'; +import { GlobalState, GlobalStateService } from '../storage'; import { WorkspaceScope } from '../workspace'; import { SidebarTab } from './entities/sidebar-tab'; import { View } from './entities/view'; @@ -39,7 +39,11 @@ export function configureWorkbenchCommonModule(services: Framework) { services .scope(WorkspaceScope) .service(WorkbenchService) - .entity(Workbench, [WorkbenchDefaultState, WorkbenchNewTabHandler]) + .entity(Workbench, [ + WorkbenchDefaultState, + WorkbenchNewTabHandler, + GlobalState, + ]) .entity(View) .scope(ViewScope) .service(ViewService, [ViewScope]) diff --git a/packages/frontend/core/src/modules/workbench/view/workbench-root.tsx b/packages/frontend/core/src/modules/workbench/view/workbench-root.tsx index 1e5d3e66b..fcce6d126 100644 --- a/packages/frontend/core/src/modules/workbench/view/workbench-root.tsx +++ b/packages/frontend/core/src/modules/workbench/view/workbench-root.tsx @@ -1,6 +1,5 @@ import { ResizePanel } from '@affine/component/resize-panel'; import { AffineErrorComponent } from '@affine/core/components/affine/affine-error-boundary/affine-error-fallback'; -import { rightSidebarWidthAtom } from '@affine/core/components/atoms'; import { workbenchRoutes } from '@affine/core/desktop/workbench-router'; import { appSettingAtom, @@ -8,7 +7,7 @@ import { useLiveData, useService, } from '@toeverything/infra'; -import { useAtom, useAtomValue } from 'jotai'; +import { useAtomValue } from 'jotai'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; import { type RouteObject, useLocation } from 'react-router-dom'; @@ -115,16 +114,24 @@ const MAX_SIDEBAR_WIDTH = 800; const WorkbenchSidebar = () => { const { clientBorder } = useAtomValue(appSettingAtom); - const [width, setWidth] = useAtom(rightSidebarWidthAtom); const [resizing, setResizing] = useState(false); const workbench = useService(WorkbenchService).workbench; + const [width, setWidth] = useState(workbench.sidebarWidth$.value ?? 0); const views = useLiveData(workbench.views$); const activeView = useLiveData(workbench.activeView$); const sidebarOpen = useLiveData(workbench.sidebarOpen$); const [floating, setFloating] = useState(false); + const onWidthChanged = useCallback( + (width: number) => { + workbench.setSidebarWidth(width); + setWidth(width); + }, + [workbench] + ); + const handleOpenChange = useCallback( (open: boolean) => { if (open) { @@ -155,9 +162,10 @@ const WorkbenchSidebar = () => { onResizing={setResizing} className={styles.workbenchSidebar} data-client-border={clientBorder && sidebarOpen} - open={sidebarOpen} + open={sidebarOpen ?? false} onOpen={handleOpenChange} onWidthChange={setWidth} + onWidthChanged={onWidthChanged} minWidth={MIN_SIDEBAR_WIDTH} maxWidth={MAX_SIDEBAR_WIDTH} unmountOnExit={false}