mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-23 20:18:42 +08:00
refactor(core): refactor left sidebar to use di (#8385)
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import { assertExists } from '@blocksuite/affine/global/utils';
|
|
||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {
|
import {
|
||||||
@@ -60,48 +59,53 @@ const ResizeHandle = ({
|
|||||||
...rest
|
...rest
|
||||||
}: ResizeHandleProps) => {
|
}: ResizeHandleProps) => {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const onResizeStart = useCallback(() => {
|
const onResizeStart = useCallback(
|
||||||
let resized = false;
|
(event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
const panelContainer = ref.current?.parentElement;
|
event.preventDefault();
|
||||||
assertExists(
|
let resized = false;
|
||||||
panelContainer,
|
const panelContainer = ref.current?.parentElement;
|
||||||
'parent element not found for resize indicator'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { left: anchorLeft, right: anchorRight } =
|
|
||||||
panelContainer.getBoundingClientRect();
|
|
||||||
|
|
||||||
function onMouseMove(e: MouseEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!panelContainer) return;
|
if (!panelContainer) return;
|
||||||
const newWidth = Math.min(
|
|
||||||
maxWidth,
|
|
||||||
Math.max(
|
|
||||||
resizeHandlePos === 'right'
|
|
||||||
? e.clientX - anchorLeft
|
|
||||||
: anchorRight - e.clientX,
|
|
||||||
minWidth
|
|
||||||
)
|
|
||||||
);
|
|
||||||
onWidthChange(newWidth);
|
|
||||||
onResizing(true);
|
|
||||||
resized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', onMouseMove);
|
// add cursor style to body
|
||||||
document.addEventListener(
|
document.body.style.cursor = 'col-resize';
|
||||||
'mouseup',
|
|
||||||
() => {
|
const { left: anchorLeft, right: anchorRight } =
|
||||||
// if not resized, toggle sidebar
|
panelContainer.getBoundingClientRect();
|
||||||
if (!resized) {
|
|
||||||
onOpen(false);
|
function onMouseMove(e: MouseEvent) {
|
||||||
}
|
e.preventDefault();
|
||||||
onResizing(false);
|
if (!panelContainer) return;
|
||||||
document.removeEventListener('mousemove', onMouseMove);
|
const newWidth = Math.min(
|
||||||
},
|
maxWidth,
|
||||||
{ once: true }
|
Math.max(
|
||||||
);
|
resizeHandlePos === 'right'
|
||||||
}, [maxWidth, resizeHandlePos, minWidth, onWidthChange, onResizing, onOpen]);
|
? e.clientX - anchorLeft
|
||||||
|
: anchorRight - e.clientX,
|
||||||
|
minWidth
|
||||||
|
)
|
||||||
|
);
|
||||||
|
onWidthChange(newWidth);
|
||||||
|
onResizing(true);
|
||||||
|
resized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMouseMove);
|
||||||
|
document.addEventListener(
|
||||||
|
'mouseup',
|
||||||
|
() => {
|
||||||
|
// if not resized, toggle sidebar
|
||||||
|
if (!resized) {
|
||||||
|
onOpen(false);
|
||||||
|
}
|
||||||
|
onResizing(false);
|
||||||
|
document.removeEventListener('mousemove', onMouseMove);
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[maxWidth, resizeHandlePos, minWidth, onWidthChange, onResizing, onOpen]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import type { useI18n } from '@affine/i18n';
|
import type { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import { SidebarIcon } from '@blocksuite/icons/rc';
|
import { SidebarIcon } from '@blocksuite/icons/rc';
|
||||||
import type { createStore } from 'jotai';
|
|
||||||
|
|
||||||
import { appSidebarOpenAtom } from '../components/app-sidebar';
|
import type { AppSidebarService } from '../modules/app-sidebar';
|
||||||
import { registerAffineCommand } from './registry';
|
import { registerAffineCommand } from './registry';
|
||||||
|
|
||||||
export function registerAffineLayoutCommands({
|
export function registerAffineLayoutCommands({
|
||||||
t,
|
t,
|
||||||
store,
|
appSidebarService,
|
||||||
}: {
|
}: {
|
||||||
t: ReturnType<typeof useI18n>;
|
t: ReturnType<typeof useI18n>;
|
||||||
store: ReturnType<typeof createStore>;
|
appSidebarService: AppSidebarService;
|
||||||
}) {
|
}) {
|
||||||
const unsubs: Array<() => void> = [];
|
const unsubs: Array<() => void> = [];
|
||||||
unsubs.push(
|
unsubs.push(
|
||||||
@@ -20,7 +19,7 @@ export function registerAffineLayoutCommands({
|
|||||||
category: 'affine:layout',
|
category: 'affine:layout',
|
||||||
icon: <SidebarIcon />,
|
icon: <SidebarIcon />,
|
||||||
label: () =>
|
label: () =>
|
||||||
store.get(appSidebarOpenAtom)
|
appSidebarService.sidebar.open$.value
|
||||||
? t['com.affine.cmdk.affine.left-sidebar.collapse']()
|
? t['com.affine.cmdk.affine.left-sidebar.collapse']()
|
||||||
: t['com.affine.cmdk.affine.left-sidebar.expand'](),
|
: t['com.affine.cmdk.affine.left-sidebar.expand'](),
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@ export function registerAffineLayoutCommands({
|
|||||||
},
|
},
|
||||||
run() {
|
run() {
|
||||||
track.$.navigationPanel.$.toggle();
|
track.$.navigationPanel.$.toggle();
|
||||||
|
appSidebarService.sidebar.toggleSidebar();
|
||||||
store.set(appSidebarOpenAtom, v => !v);
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import {
|
||||||
|
AppSidebarFallback,
|
||||||
|
ShellAppSidebarFallback,
|
||||||
|
} from '@affine/core/modules/app-sidebar/views';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
|
|
||||||
import { useAppSettingHelper } from '../../components/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '../../components/hooks/affine/use-app-setting-helper';
|
||||||
import { AppSidebarFallback, ShellAppSidebarFallback } from '../app-sidebar';
|
|
||||||
import type { WorkspaceRootProps } from '../workspace';
|
import type { WorkspaceRootProps } from '../workspace';
|
||||||
import {
|
import {
|
||||||
AppContainer as AppContainerWithoutSettings,
|
AppContainer as AppContainerWithoutSettings,
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { atom } from 'jotai';
|
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
|
||||||
|
|
||||||
export const APP_SIDEBAR_OPEN = 'app-sidebar-open';
|
|
||||||
export const isMobile = !BUILD_CONFIG.isElectron && window.innerWidth < 768;
|
|
||||||
|
|
||||||
export const appSidebarOpenAtom = atomWithStorage(APP_SIDEBAR_OPEN, !isMobile);
|
|
||||||
export const appSidebarFloatingAtom = atom(isMobile);
|
|
||||||
|
|
||||||
export const appSidebarResizingAtom = atom(false);
|
|
||||||
export const appSidebarWidthAtom = atomWithStorage(
|
|
||||||
'app-sidebar-width',
|
|
||||||
248 /* px */
|
|
||||||
);
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { useAtom } from 'jotai';
|
|
||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { appSidebarOpenAtom } from '../../../components/app-sidebar';
|
|
||||||
|
|
||||||
export function useSwitchSidebarStatus() {
|
|
||||||
const [isOpened, setOpened] = useAtom(appSidebarOpenAtom);
|
|
||||||
|
|
||||||
const onOpenChange = useCallback(() => {
|
|
||||||
setOpened(open => !open);
|
|
||||||
}, [setOpened]);
|
|
||||||
|
|
||||||
return useMemo(
|
|
||||||
() => ({
|
|
||||||
onOpenChange,
|
|
||||||
isOpened,
|
|
||||||
}),
|
|
||||||
[isOpened, onOpenChange]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import type { AffineEditorContainer } from '@blocksuite/affine/presets';
|
import type { AffineEditorContainer } from '@blocksuite/affine/presets';
|
||||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||||
@@ -71,6 +72,7 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
const cmdkQuickSearchService = useService(CMDKQuickSearchService);
|
const cmdkQuickSearchService = useService(CMDKQuickSearchService);
|
||||||
const editorSettingService = useService(EditorSettingService);
|
const editorSettingService = useService(EditorSettingService);
|
||||||
const createWorkspaceDialogService = useService(CreateWorkspaceDialogService);
|
const createWorkspaceDialogService = useService(CreateWorkspaceDialogService);
|
||||||
|
const appSidebarService = useService(AppSidebarService);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = registerCMDKCommand(cmdkQuickSearchService, editor);
|
const unsub = registerCMDKCommand(cmdkQuickSearchService, editor);
|
||||||
@@ -123,12 +125,12 @@ export function useRegisterWorkspaceCommands() {
|
|||||||
|
|
||||||
// register AffineLayoutCommands
|
// register AffineLayoutCommands
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = registerAffineLayoutCommands({ t, store });
|
const unsub = registerAffineLayoutCommands({ t, appSidebarService });
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsub();
|
unsub();
|
||||||
};
|
};
|
||||||
}, [store, t]);
|
}, [appSidebarService, store, t]);
|
||||||
|
|
||||||
// register AffineCreationCommands
|
// register AffineCreationCommands
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
pushGlobalLoadingEventAtom,
|
pushGlobalLoadingEventAtom,
|
||||||
resolveGlobalLoadingEventAtom,
|
resolveGlobalLoadingEventAtom,
|
||||||
} from '@affine/component/global-loading';
|
} from '@affine/component/global-loading';
|
||||||
|
import { SidebarSwitch } from '@affine/core/modules/app-sidebar/views';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { type DocMode, ZipTransformer } from '@blocksuite/affine/blocks';
|
import { type DocMode, ZipTransformer } from '@blocksuite/affine/blocks';
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +18,7 @@ import {
|
|||||||
useServices,
|
useServices,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useSetAtom } from 'jotai';
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
@@ -40,7 +41,6 @@ import { WorkbenchService } from '../../modules/workbench';
|
|||||||
import { WorkspaceAIOnboarding } from '../affine/ai-onboarding';
|
import { WorkspaceAIOnboarding } from '../affine/ai-onboarding';
|
||||||
import { AppContainer } from '../affine/app-container';
|
import { AppContainer } from '../affine/app-container';
|
||||||
import { SyncAwareness } from '../affine/awareness';
|
import { SyncAwareness } from '../affine/awareness';
|
||||||
import { appSidebarResizingAtom, SidebarSwitch } from '../app-sidebar';
|
|
||||||
import { useRegisterFindInPageCommands } from '../hooks/affine/use-register-find-in-page-commands';
|
import { useRegisterFindInPageCommands } from '../hooks/affine/use-register-find-in-page-commands';
|
||||||
import { useSubscriptionNotifyReader } from '../hooks/affine/use-subscription-notify';
|
import { useSubscriptionNotifyReader } from '../hooks/affine/use-subscription-notify';
|
||||||
import { useRegisterWorkspaceCommands } from '../hooks/use-register-workspace-commands';
|
import { useRegisterWorkspaceCommands } from '../hooks/use-register-workspace-commands';
|
||||||
@@ -221,10 +221,8 @@ const WorkspaceLayoutUIContainer = ({ children }: PropsWithChildren) => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const resizing = useAtomValue(appSidebarResizingAtom);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContainer data-current-path={currentPath} resizing={resizing}>
|
<AppContainer data-current-path={currentPath}>
|
||||||
<LayoutComponent>{children}</LayoutComponent>
|
<LayoutComponent>{children}</LayoutComponent>
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
import { appSidebarFloatingAtom, appSidebarOpenAtom } from '../../app-sidebar';
|
|
||||||
import * as style from './style.css';
|
import * as style from './style.css';
|
||||||
|
|
||||||
interface HeaderPros {
|
interface HeaderPros {
|
||||||
@@ -16,8 +16,9 @@ interface HeaderPros {
|
|||||||
// 1. Manage layout issues independently of page or business logic
|
// 1. Manage layout issues independently of page or business logic
|
||||||
// 2. Dynamic centered middle element (relative to the main-container), when the middle element is detected to collide with the two elements, the line wrapping process is performed
|
// 2. Dynamic centered middle element (relative to the main-container), when the middle element is detected to collide with the two elements, the line wrapping process is performed
|
||||||
export const Header = ({ left, center, right }: HeaderPros) => {
|
export const Header = ({ left, center, right }: HeaderPros) => {
|
||||||
const open = useAtomValue(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
const appSidebarFloating = useAtomValue(appSidebarFloatingAtom);
|
const open = useLiveData(appSidebarService.open$);
|
||||||
|
const appSidebarFloating = useLiveData(appSidebarService.responsiveFloating$);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(style.header)}
|
className={clsx(style.header)}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
|
import { MenuItem } from '@affine/core/modules/app-sidebar/views';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
import type { DocCollection } from '@blocksuite/affine/store';
|
import type { DocCollection } from '@blocksuite/affine/store';
|
||||||
import { ImportIcon } from '@blocksuite/icons/rc';
|
import { ImportIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
import { MenuItem } from '../app-sidebar';
|
|
||||||
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
||||||
|
|
||||||
const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
const ImportPage = ({ docCollection }: { docCollection: DocCollection }) => {
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
import { openSettingModalAtom } from '@affine/core/components/atoms';
|
import { openSettingModalAtom } from '@affine/core/components/atoms';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
|
import {
|
||||||
|
AddPageButton,
|
||||||
|
AppDownloadButton,
|
||||||
|
AppSidebar,
|
||||||
|
CategoryDivider,
|
||||||
|
MenuItem,
|
||||||
|
MenuLinkItem,
|
||||||
|
QuickSearchInput,
|
||||||
|
SidebarContainer,
|
||||||
|
SidebarScrollableContainer,
|
||||||
|
} from '@affine/core/modules/app-sidebar/views';
|
||||||
|
import { ExternalMenuLinkItem } from '@affine/core/modules/app-sidebar/views/menu-item/external-menu-link-item';
|
||||||
import {
|
import {
|
||||||
ExplorerCollections,
|
ExplorerCollections,
|
||||||
ExplorerFavorites,
|
ExplorerFavorites,
|
||||||
@@ -30,18 +42,6 @@ import type { MouseEvent, ReactElement } from 'react';
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import { WorkbenchService } from '../../modules/workbench';
|
import { WorkbenchService } from '../../modules/workbench';
|
||||||
import {
|
|
||||||
AddPageButton,
|
|
||||||
AppDownloadButton,
|
|
||||||
AppSidebar,
|
|
||||||
CategoryDivider,
|
|
||||||
MenuItem,
|
|
||||||
MenuLinkItem,
|
|
||||||
QuickSearchInput,
|
|
||||||
SidebarContainer,
|
|
||||||
SidebarScrollableContainer,
|
|
||||||
} from '../app-sidebar';
|
|
||||||
import { ExternalMenuLinkItem } from '../app-sidebar/menu-item/external-menu-link-item';
|
|
||||||
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
||||||
import { WorkspaceNavigator } from '../workspace-selector';
|
import { WorkspaceNavigator } from '../workspace-selector';
|
||||||
import ImportPage from './import-page';
|
import ImportPage from './import-page';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
useJournalInfoHelper,
|
useJournalInfoHelper,
|
||||||
useJournalRouteHelper,
|
useJournalRouteHelper,
|
||||||
} from '@affine/core/components/hooks/use-journal';
|
} from '@affine/core/components/hooks/use-journal';
|
||||||
|
import { MenuItem } from '@affine/core/modules/app-sidebar/views';
|
||||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { isNewTabTrigger } from '@affine/core/utils';
|
import { isNewTabTrigger } from '@affine/core/utils';
|
||||||
@@ -12,8 +13,6 @@ import { TodayIcon } from '@blocksuite/icons/rc';
|
|||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { type MouseEvent } from 'react';
|
import { type MouseEvent } from 'react';
|
||||||
|
|
||||||
import { MenuItem } from '../app-sidebar';
|
|
||||||
|
|
||||||
interface AppSidebarJournalButtonProps {
|
interface AppSidebarJournalButtonProps {
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
useConfirmModal,
|
useConfirmModal,
|
||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { MenuLinkItem } from '@affine/core/modules/app-sidebar/views';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import {
|
import {
|
||||||
@@ -12,8 +13,6 @@ import {
|
|||||||
useService,
|
useService,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
import { MenuLinkItem } from '../app-sidebar';
|
|
||||||
|
|
||||||
export const TrashButton = () => {
|
export const TrashButton = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const docsService = useService(DocsService);
|
const docsService = useService(DocsService);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useAppUpdater } from '@affine/core/components/hooks/use-app-updater';
|
import { useAppUpdater } from '@affine/core/components/hooks/use-app-updater';
|
||||||
|
import { AppUpdaterButton } from '@affine/core/modules/app-sidebar/views';
|
||||||
import { Suspense } from 'react';
|
import { Suspense } from 'react';
|
||||||
|
|
||||||
import { AppUpdaterButton } from '../app-sidebar';
|
|
||||||
|
|
||||||
const UpdaterButtonInner = () => {
|
const UpdaterButtonInner = () => {
|
||||||
const appUpdater = useAppUpdater();
|
const appUpdater = useAppUpdater();
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ export const appStyle = style({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
backgroundColor: cssVar('backgroundPrimaryColor'),
|
backgroundColor: cssVar('backgroundPrimaryColor'),
|
||||||
selectors: {
|
selectors: {
|
||||||
'&[data-is-resizing="true"]': {
|
|
||||||
cursor: 'col-resize',
|
|
||||||
},
|
|
||||||
'&.blur-background': {
|
'&.blur-background': {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper';
|
||||||
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
import {
|
import {
|
||||||
DocsService,
|
DocsService,
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
@@ -6,22 +7,18 @@ import {
|
|||||||
useService,
|
useService,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { clsx } from 'clsx';
|
import { clsx } from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import type { HTMLAttributes, PropsWithChildren, ReactElement } from 'react';
|
import type { HTMLAttributes, PropsWithChildren, ReactElement } from 'react';
|
||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
import { appSidebarOpenAtom } from '../app-sidebar';
|
|
||||||
import { appStyle, mainContainerStyle, toolStyle } from './index.css';
|
import { appStyle, mainContainerStyle, toolStyle } from './index.css';
|
||||||
|
|
||||||
export type WorkspaceRootProps = PropsWithChildren<{
|
export type WorkspaceRootProps = PropsWithChildren<{
|
||||||
resizing?: boolean;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
useNoisyBackground?: boolean;
|
useNoisyBackground?: boolean;
|
||||||
useBlurBackground?: boolean;
|
useBlurBackground?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const AppContainer = ({
|
export const AppContainer = ({
|
||||||
resizing,
|
|
||||||
useNoisyBackground,
|
useNoisyBackground,
|
||||||
useBlurBackground,
|
useBlurBackground,
|
||||||
children,
|
children,
|
||||||
@@ -39,7 +36,6 @@ export const AppContainer = ({
|
|||||||
'blur-background': blurBackground,
|
'blur-background': blurBackground,
|
||||||
})}
|
})}
|
||||||
data-noise-background={noisyBackground}
|
data-noise-background={noisyBackground}
|
||||||
data-is-resizing={resizing}
|
|
||||||
data-blur-background={blurBackground}
|
data-blur-background={blurBackground}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -53,7 +49,8 @@ export const MainContainer = forwardRef<
|
|||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
PropsWithChildren<MainContainerProps>
|
PropsWithChildren<MainContainerProps>
|
||||||
>(function MainContainer({ className, children, ...props }, ref): ReactElement {
|
>(function MainContainer({ className, children, ...props }, ref): ReactElement {
|
||||||
const appSideBarOpen = useAtomValue(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const appSideBarOpen = useLiveData(appSidebarService.open$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+6
-6
@@ -3,27 +3,26 @@ import {
|
|||||||
type InlineEditHandle,
|
type InlineEditHandle,
|
||||||
observeResize,
|
observeResize,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { SharePageButton } from '@affine/core/components/affine/share-page-modal';
|
||||||
import { FavoriteButton } from '@affine/core/components/blocksuite/block-suite-header/favorite';
|
import { FavoriteButton } from '@affine/core/components/blocksuite/block-suite-header/favorite';
|
||||||
import { InfoButton } from '@affine/core/components/blocksuite/block-suite-header/info';
|
import { InfoButton } from '@affine/core/components/blocksuite/block-suite-header/info';
|
||||||
import { JournalWeekDatePicker } from '@affine/core/components/blocksuite/block-suite-header/journal/date-picker';
|
import { JournalWeekDatePicker } from '@affine/core/components/blocksuite/block-suite-header/journal/date-picker';
|
||||||
import { JournalTodayButton } from '@affine/core/components/blocksuite/block-suite-header/journal/today-button';
|
import { JournalTodayButton } from '@affine/core/components/blocksuite/block-suite-header/journal/today-button';
|
||||||
import { PageHeaderMenuButton } from '@affine/core/components/blocksuite/block-suite-header/menu';
|
import { PageHeaderMenuButton } from '@affine/core/components/blocksuite/block-suite-header/menu';
|
||||||
import { DetailPageHeaderPresentButton } from '@affine/core/components/blocksuite/block-suite-header/present/detail-header-present-button';
|
import { DetailPageHeaderPresentButton } from '@affine/core/components/blocksuite/block-suite-header/present/detail-header-present-button';
|
||||||
|
import { BlocksuiteHeaderTitle } from '@affine/core/components/blocksuite/block-suite-header/title';
|
||||||
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||||
import { useRegisterCopyLinkCommands } from '@affine/core/components/hooks/affine/use-register-copy-link-commands';
|
import { useRegisterCopyLinkCommands } from '@affine/core/components/hooks/affine/use-register-copy-link-commands';
|
||||||
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
|
import { useDocCollectionPageTitle } from '@affine/core/components/hooks/use-block-suite-workspace-page-title';
|
||||||
import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal';
|
import { useJournalInfoHelper } from '@affine/core/components/hooks/use-journal';
|
||||||
|
import { HeaderDivider } from '@affine/core/components/pure/header';
|
||||||
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
import { ViewIcon, ViewTitle } from '@affine/core/modules/workbench';
|
||||||
import type { Doc } from '@blocksuite/affine/store';
|
import type { Doc } from '@blocksuite/affine/store';
|
||||||
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
import { useLiveData, useService, type Workspace } from '@toeverything/infra';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { SharePageButton } from '../../../../components/affine/share-page-modal';
|
|
||||||
import { appSidebarFloatingAtom } from '../../../../components/app-sidebar';
|
|
||||||
import { BlocksuiteHeaderTitle } from '../../../../components/blocksuite/block-suite-header/title/index';
|
|
||||||
import { HeaderDivider } from '../../../../components/pure/header';
|
|
||||||
import * as styles from './detail-page-header.css';
|
import * as styles from './detail-page-header.css';
|
||||||
import { useDetailPageHeaderResponsive } from './use-header-responsive';
|
import { useDetailPageHeaderResponsive } from './use-header-responsive';
|
||||||
|
|
||||||
@@ -35,7 +34,8 @@ const Header = forwardRef<
|
|||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
>(({ children, style, className }, ref) => {
|
>(({ children, style, className }, ref) => {
|
||||||
const appSidebarFloating = useAtomValue(appSidebarFloatingAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const appSidebarFloating = useLiveData(appSidebarService.responsiveFloating$);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="header"
|
data-testid="header"
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { Entity, LiveData } from '@toeverything/infra';
|
||||||
|
import { map } from 'rxjs';
|
||||||
|
|
||||||
|
import type { AppSidebarState } from '../providers/storage';
|
||||||
|
|
||||||
|
const isMobile = !BUILD_CONFIG.isElectron && window.innerWidth < 768;
|
||||||
|
|
||||||
|
enum APP_SIDEBAR_STATE {
|
||||||
|
OPEN = 'open',
|
||||||
|
WIDTH = 'width',
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppSidebar extends Entity {
|
||||||
|
constructor(private readonly appSidebarState: AppSidebarState) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
open$ = LiveData.from(
|
||||||
|
this.appSidebarState
|
||||||
|
.watch<boolean>(APP_SIDEBAR_STATE.OPEN)
|
||||||
|
.pipe(map(value => value ?? !isMobile)),
|
||||||
|
!isMobile
|
||||||
|
);
|
||||||
|
|
||||||
|
width$ = LiveData.from(
|
||||||
|
this.appSidebarState
|
||||||
|
.watch<number>(APP_SIDEBAR_STATE.WIDTH)
|
||||||
|
.pipe(map(value => value ?? 248)),
|
||||||
|
248
|
||||||
|
);
|
||||||
|
responsiveFloating$ = new LiveData<boolean>(isMobile);
|
||||||
|
hoverFloating$ = new LiveData<boolean>(false);
|
||||||
|
resizing$ = new LiveData<boolean>(false);
|
||||||
|
|
||||||
|
getCachedAppSidebarOpenState = () => {
|
||||||
|
return this.appSidebarState.get<boolean>(APP_SIDEBAR_STATE.OPEN);
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleSidebar = () => {
|
||||||
|
this.setOpen(!this.open$.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
setOpen = (open: boolean) => {
|
||||||
|
this.appSidebarState.set(APP_SIDEBAR_STATE.OPEN, open);
|
||||||
|
if (!open && this.hoverFloating$.value) {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
this.setHoverFloating(false);
|
||||||
|
}, 500);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
setResponsiveFloating = (floating: boolean) => {
|
||||||
|
this.responsiveFloating$.next(floating);
|
||||||
|
};
|
||||||
|
|
||||||
|
setHoverFloating = (hoverFloating: boolean) => {
|
||||||
|
this.hoverFloating$.next(hoverFloating);
|
||||||
|
};
|
||||||
|
|
||||||
|
setResizing = (resizing: boolean) => {
|
||||||
|
this.resizing$.next(resizing);
|
||||||
|
};
|
||||||
|
|
||||||
|
setWidth = (width: number) => {
|
||||||
|
this.appSidebarState.set(APP_SIDEBAR_STATE.WIDTH, width);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
type GlobalState,
|
||||||
|
type Memento,
|
||||||
|
wrapMemento,
|
||||||
|
} from '@toeverything/infra';
|
||||||
|
|
||||||
|
import type { AppSidebarState } from '../providers/storage';
|
||||||
|
|
||||||
|
export class AppSidebarStateImpl implements AppSidebarState {
|
||||||
|
wrapped: Memento;
|
||||||
|
constructor(globalState: GlobalState) {
|
||||||
|
this.wrapped = wrapMemento(globalState, `app-sidebar-state:`);
|
||||||
|
}
|
||||||
|
|
||||||
|
keys(): string[] {
|
||||||
|
return this.wrapped.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
get<T>(key: string): T | undefined {
|
||||||
|
return this.wrapped.get<T>(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch<T>(key: string) {
|
||||||
|
return this.wrapped.watch<T>(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
set<T>(key: string, value: T): void {
|
||||||
|
return this.wrapped.set<T>(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
del(key: string): void {
|
||||||
|
return this.wrapped.del(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
return this.wrapped.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { type Framework, GlobalState } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { AppSidebar } from './entities/app-sidebar';
|
||||||
|
import { AppSidebarStateImpl } from './impls/storage';
|
||||||
|
import { AppSidebarState } from './providers/storage';
|
||||||
|
import { AppSidebarService } from './services/app-sidebar';
|
||||||
|
|
||||||
|
export * from './services/app-sidebar';
|
||||||
|
|
||||||
|
export function configureAppSidebarModule(framework: Framework) {
|
||||||
|
framework
|
||||||
|
.service(AppSidebarService)
|
||||||
|
.entity(AppSidebar, [AppSidebarState])
|
||||||
|
.impl(AppSidebarState, AppSidebarStateImpl, [GlobalState]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { createIdentifier, type Memento } from '@toeverything/infra';
|
||||||
|
|
||||||
|
export interface AppSidebarState extends Memento {}
|
||||||
|
|
||||||
|
export const AppSidebarState =
|
||||||
|
createIdentifier<AppSidebarState>('AppSidebarState');
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { GlobalState, Service } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { AppSidebar } from '../entities/app-sidebar';
|
||||||
|
|
||||||
|
export class AppSidebarService extends Service {
|
||||||
|
sidebar = this.framework.createEntity(AppSidebar, [GlobalState]);
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
+52
-26
@@ -2,13 +2,18 @@ import { Skeleton } from '@affine/component';
|
|||||||
import { ResizePanel } from '@affine/component/resize-panel';
|
import { ResizePanel } from '@affine/component/resize-panel';
|
||||||
import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/components/hooks/affine/use-app-setting-helper';
|
||||||
import { NavigateContext } from '@affine/core/components/hooks/use-navigate-helper';
|
import { NavigateContext } from '@affine/core/components/hooks/use-navigate-helper';
|
||||||
import { useServiceOptional, WorkspaceService } from '@toeverything/infra';
|
import { WorkspaceNavigator } from '@affine/core/components/workspace-selector';
|
||||||
import { useAtom, useAtomValue } from 'jotai';
|
import {
|
||||||
|
useLiveData,
|
||||||
|
useService,
|
||||||
|
useServiceOptional,
|
||||||
|
WorkspaceService,
|
||||||
|
} from '@toeverything/infra';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
import type { PropsWithChildren, ReactElement } from 'react';
|
import type { PropsWithChildren, ReactElement } from 'react';
|
||||||
import { useContext, useEffect, useMemo } from 'react';
|
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { WorkspaceNavigator } from '../workspace-selector';
|
import { AppSidebarService } from '../services/app-sidebar';
|
||||||
import * as styles from './fallback.css';
|
import * as styles from './fallback.css';
|
||||||
import {
|
import {
|
||||||
floatingMaxWidth,
|
floatingMaxWidth,
|
||||||
@@ -18,13 +23,6 @@ import {
|
|||||||
navWrapperStyle,
|
navWrapperStyle,
|
||||||
sidebarFloatMaskStyle,
|
sidebarFloatMaskStyle,
|
||||||
} from './index.css';
|
} from './index.css';
|
||||||
import {
|
|
||||||
APP_SIDEBAR_OPEN,
|
|
||||||
appSidebarFloatingAtom,
|
|
||||||
appSidebarOpenAtom,
|
|
||||||
appSidebarResizingAtom,
|
|
||||||
appSidebarWidthAtom,
|
|
||||||
} from './index.jotai';
|
|
||||||
import { SidebarHeader } from './sidebar-header';
|
import { SidebarHeader } from './sidebar-header';
|
||||||
|
|
||||||
export type History = {
|
export type History = {
|
||||||
@@ -40,10 +38,12 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
|
|
||||||
const clientBorder = appSettings.clientBorder;
|
const clientBorder = appSettings.clientBorder;
|
||||||
|
|
||||||
const [open, setOpen] = useAtom(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
const [width, setWidth] = useAtom(appSidebarWidthAtom);
|
|
||||||
const [floating, setFloating] = useAtom(appSidebarFloatingAtom);
|
const open = useLiveData(appSidebarService.open$);
|
||||||
const [resizing, setResizing] = useAtom(appSidebarResizingAtom);
|
const width = useLiveData(appSidebarService.width$);
|
||||||
|
const floating = useLiveData(appSidebarService.responsiveFloating$);
|
||||||
|
const resizing = useLiveData(appSidebarService.resizing$);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// do not float app sidebar on desktop
|
// do not float app sidebar on desktop
|
||||||
@@ -61,13 +61,13 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
const isFloating = isFloatingMaxWidth || isOverflowWidth;
|
const isFloating = isFloatingMaxWidth || isOverflowWidth;
|
||||||
if (
|
if (
|
||||||
open === undefined &&
|
open === undefined &&
|
||||||
localStorage.getItem(APP_SIDEBAR_OPEN) === null
|
appSidebarService.getCachedAppSidebarOpenState() === undefined
|
||||||
) {
|
) {
|
||||||
// give the initial value,
|
// give the initial value,
|
||||||
// so that the sidebar can be closed on mobile by default
|
// so that the sidebar can be closed on mobile by default
|
||||||
setOpen(!isFloating);
|
appSidebarService.setOpen(!isFloating);
|
||||||
}
|
}
|
||||||
setFloating(isFloating);
|
appSidebarService.setResponsiveFloating(isFloating);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dOnResize = debounce(onResize, 50);
|
const dOnResize = debounce(onResize, 50);
|
||||||
@@ -75,11 +75,36 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('resize', dOnResize);
|
window.removeEventListener('resize', dOnResize);
|
||||||
};
|
};
|
||||||
}, [open, setFloating, setOpen, width]);
|
}, [appSidebarService, open, width]);
|
||||||
|
|
||||||
const hasRightBorder = !BUILD_CONFIG.isElectron && !clientBorder;
|
const hasRightBorder = !BUILD_CONFIG.isElectron && !clientBorder;
|
||||||
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
||||||
|
|
||||||
|
const handleOpenChange = useCallback(
|
||||||
|
(open: boolean) => {
|
||||||
|
appSidebarService.setOpen(open);
|
||||||
|
},
|
||||||
|
[appSidebarService]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleResizing = useCallback(
|
||||||
|
(resizing: boolean) => {
|
||||||
|
appSidebarService.setResizing(resizing);
|
||||||
|
},
|
||||||
|
[appSidebarService]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleWidthChange = useCallback(
|
||||||
|
(width: number) => {
|
||||||
|
appSidebarService.setWidth(width);
|
||||||
|
},
|
||||||
|
[appSidebarService]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
appSidebarService.setOpen(false);
|
||||||
|
}, [appSidebarService]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResizePanel
|
<ResizePanel
|
||||||
@@ -90,9 +115,9 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
minWidth={MIN_WIDTH}
|
minWidth={MIN_WIDTH}
|
||||||
width={width}
|
width={width}
|
||||||
resizeHandlePos="right"
|
resizeHandlePos="right"
|
||||||
onOpen={setOpen}
|
onOpen={handleOpenChange}
|
||||||
onResizing={setResizing}
|
onResizing={handleResizing}
|
||||||
onWidthChange={setWidth}
|
onWidthChange={handleWidthChange}
|
||||||
className={navWrapperStyle}
|
className={navWrapperStyle}
|
||||||
resizeHandleOffset={0}
|
resizeHandleOffset={0}
|
||||||
resizeHandleVerticalPadding={clientBorder ? 16 : 0}
|
resizeHandleVerticalPadding={clientBorder ? 16 : 0}
|
||||||
@@ -115,7 +140,7 @@ export function AppSidebar({ children }: PropsWithChildren) {
|
|||||||
data-open={open}
|
data-open={open}
|
||||||
data-is-floating={floating}
|
data-is-floating={floating}
|
||||||
className={sidebarFloatMaskStyle}
|
className={sidebarFloatMaskStyle}
|
||||||
onClick={() => setOpen(false)}
|
onClick={handleClose}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -207,7 +232,8 @@ const FallbackBody = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const AppSidebarFallback = (): ReactElement | null => {
|
export const AppSidebarFallback = (): ReactElement | null => {
|
||||||
const width = useAtomValue(appSidebarWidthAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const width = useLiveData(appSidebarService.width$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const clientBorder = appSettings.clientBorder;
|
const clientBorder = appSettings.clientBorder;
|
||||||
|
|
||||||
@@ -235,7 +261,8 @@ export const AppSidebarFallback = (): ReactElement | null => {
|
|||||||
* NOTE(@forehalo): this is a copy of [AppSidebarFallback] without [WorkspaceNavigator] which will introduce a lot useless dependencies for shell(tab bar)
|
* NOTE(@forehalo): this is a copy of [AppSidebarFallback] without [WorkspaceNavigator] which will introduce a lot useless dependencies for shell(tab bar)
|
||||||
*/
|
*/
|
||||||
export const ShellAppSidebarFallback = () => {
|
export const ShellAppSidebarFallback = () => {
|
||||||
const width = useAtomValue(appSidebarWidthAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const width = useLiveData(appSidebarService.width$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
const clientBorder = appSettings.clientBorder;
|
const clientBorder = appSettings.clientBorder;
|
||||||
|
|
||||||
@@ -268,4 +295,3 @@ export * from './menu-item';
|
|||||||
export * from './quick-search-input';
|
export * from './quick-search-input';
|
||||||
export * from './sidebar-containers';
|
export * from './sidebar-containers';
|
||||||
export * from './sidebar-header';
|
export * from './sidebar-header';
|
||||||
export { appSidebarFloatingAtom, appSidebarOpenAtom, appSidebarResizingAtom };
|
|
||||||
+1
-1
@@ -3,7 +3,7 @@ import { cssVarV2 } from '@toeverything/theme/v2';
|
|||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import type { To } from 'react-router-dom';
|
import type { To } from 'react-router-dom';
|
||||||
|
|
||||||
import { MenuLinkItem } from '.';
|
import { MenuLinkItem } from './index';
|
||||||
|
|
||||||
const RawLink = ({
|
const RawLink = ({
|
||||||
children,
|
children,
|
||||||
+4
-3
@@ -1,11 +1,12 @@
|
|||||||
import { useAtomValue } from 'jotai';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { AppSidebarService } from '../../services/app-sidebar';
|
||||||
import { navHeaderStyle } from '../index.css';
|
import { navHeaderStyle } from '../index.css';
|
||||||
import { appSidebarOpenAtom } from '../index.jotai';
|
|
||||||
import { SidebarSwitch } from './sidebar-switch';
|
import { SidebarSwitch } from './sidebar-switch';
|
||||||
|
|
||||||
export const SidebarHeader = () => {
|
export const SidebarHeader = () => {
|
||||||
const open = useAtomValue(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const open = useLiveData(appSidebarService.open$);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={navHeaderStyle} data-open={open}>
|
<div className={navHeaderStyle} data-open={open}>
|
||||||
+11
-4
@@ -1,9 +1,10 @@
|
|||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { SidebarIcon } from '@blocksuite/icons/rc';
|
import { SidebarIcon } from '@blocksuite/icons/rc';
|
||||||
import { useAtom } from 'jotai';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { appSidebarOpenAtom } from '../index.jotai';
|
import { AppSidebarService } from '../../services/app-sidebar';
|
||||||
import * as styles from './sidebar-switch.css';
|
import * as styles from './sidebar-switch.css';
|
||||||
|
|
||||||
export const SidebarSwitch = ({
|
export const SidebarSwitch = ({
|
||||||
@@ -13,12 +14,18 @@ export const SidebarSwitch = ({
|
|||||||
show: boolean;
|
show: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useAtom(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const open = useLiveData(appSidebarService.open$);
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const tooltipContent = open
|
const tooltipContent = open
|
||||||
? t['com.affine.sidebarSwitch.collapse']()
|
? t['com.affine.sidebarSwitch.collapse']()
|
||||||
: t['com.affine.sidebarSwitch.expand']();
|
: t['com.affine.sidebarSwitch.expand']();
|
||||||
|
|
||||||
|
const toggleSidebar = useCallback(() => {
|
||||||
|
appSidebarService.toggleSidebar();
|
||||||
|
}, [appSidebarService]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-show={show}
|
data-show={show}
|
||||||
@@ -34,7 +41,7 @@ export const SidebarSwitch = ({
|
|||||||
style={{
|
style={{
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
}}
|
}}
|
||||||
onClick={() => setOpen(open => !open)}
|
onClick={toggleSidebar}
|
||||||
>
|
>
|
||||||
<SidebarIcon />
|
<SidebarIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -7,11 +7,6 @@ import {
|
|||||||
useDraggable,
|
useDraggable,
|
||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import {
|
|
||||||
appSidebarOpenAtom,
|
|
||||||
appSidebarResizingAtom,
|
|
||||||
} from '@affine/core/components/app-sidebar';
|
|
||||||
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
|
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
@@ -25,7 +20,6 @@ import {
|
|||||||
useServiceOptional,
|
useServiceOptional,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import { partition } from 'lodash-es';
|
import { partition } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
Fragment,
|
Fragment,
|
||||||
@@ -35,6 +29,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
|
import { AppSidebarService } from '../../app-sidebar';
|
||||||
import { iconNameToIcon } from '../../workbench/constants';
|
import { iconNameToIcon } from '../../workbench/constants';
|
||||||
import { DesktopStateSynchronizer } from '../../workbench/services/desktop-state-synchronizer';
|
import { DesktopStateSynchronizer } from '../../workbench/services/desktop-state-synchronizer';
|
||||||
import {
|
import {
|
||||||
@@ -298,9 +293,11 @@ export const AppTabsHeader = ({
|
|||||||
left?: ReactNode;
|
left?: ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const sidebarWidth = useAtomValue(appSidebarWidthAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
|
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||||
const sidebarResizing = useAtomValue(appSidebarResizingAtom);
|
const sidebarOpen = useLiveData(appSidebarService.open$);
|
||||||
|
const sidebarResizing = useLiveData(appSidebarService.resizing$);
|
||||||
|
|
||||||
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
const isMacosDesktop = BUILD_CONFIG.isElectron && environment.isMacOs;
|
||||||
const isWindowsDesktop = BUILD_CONFIG.isElectron && environment.isWindows;
|
const isWindowsDesktop = BUILD_CONFIG.isElectron && environment.isWindows;
|
||||||
const fullScreen = useIsFullScreen();
|
const fullScreen = useIsFullScreen();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CategoryDivider } from '@affine/core/components/app-sidebar';
|
import { CategoryDivider } from '@affine/core/modules/app-sidebar/views';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { RenameModal } from '@affine/component/rename-modal';
|
import { RenameModal } from '@affine/component/rename-modal';
|
||||||
import { appSidebarWidthAtom } from '@affine/core/components/app-sidebar/index.jotai';
|
import { AppSidebarService } from '@affine/core/modules/app-sidebar';
|
||||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { extractEmojiIcon } from '@affine/core/utils';
|
import { extractEmojiIcon } from '@affine/core/utils';
|
||||||
@@ -21,10 +21,10 @@ import {
|
|||||||
MoreHorizontalIcon,
|
MoreHorizontalIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import type { To } from 'history';
|
import type { To } from 'history';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import {
|
import {
|
||||||
Fragment,
|
Fragment,
|
||||||
type RefAttributes,
|
type RefAttributes,
|
||||||
@@ -117,10 +117,13 @@ export const ExplorerTreeNode = ({
|
|||||||
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
||||||
const clickForCollapse = !onClick && !to && !disabled;
|
const clickForCollapse = !onClick && !to && !disabled;
|
||||||
const [childCount, setChildCount] = useState(0);
|
const [childCount, setChildCount] = useState(0);
|
||||||
const sidebarWidth = useAtomValue(appSidebarWidthAtom);
|
|
||||||
const [renaming, setRenaming] = useState(defaultRenaming);
|
const [renaming, setRenaming] = useState(defaultRenaming);
|
||||||
const [lastInGroup, setLastInGroup] = useState(false);
|
const [lastInGroup, setLastInGroup] = useState(false);
|
||||||
const rootRef = useRef<HTMLDivElement>(null);
|
const rootRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||||
|
|
||||||
const { emoji, name } = useMemo(() => {
|
const { emoji, name } = useMemo(() => {
|
||||||
if (!extractEmojiAsIcon || !rawName) {
|
if (!extractEmojiAsIcon || !rawName) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { configureQuotaModule } from '@affine/core/modules/quota';
|
import { configureQuotaModule } from '@affine/core/modules/quota';
|
||||||
import { configureInfraModules, type Framework } from '@toeverything/infra';
|
import { configureInfraModules, type Framework } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import { configureAppSidebarModule } from './app-sidebar';
|
||||||
import { configureCloudModule } from './cloud';
|
import { configureCloudModule } from './cloud';
|
||||||
import { configureCollectionModule } from './collection';
|
import { configureCollectionModule } from './collection';
|
||||||
import { configureCreateWorkspaceModule } from './create-workspace';
|
import { configureCreateWorkspaceModule } from './create-workspace';
|
||||||
@@ -57,4 +58,5 @@ export function configureCommonModules(framework: Framework) {
|
|||||||
configureCreateWorkspaceModule(framework);
|
configureCreateWorkspaceModule(framework);
|
||||||
configureUserspaceModule(framework);
|
configureUserspaceModule(framework);
|
||||||
configureDocInfoModule(framework);
|
configureDocInfoModule(framework);
|
||||||
|
configureAppSidebarModule(framework);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
|
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
|
||||||
import { RightSidebarIcon } from '@blocksuite/icons/rc';
|
import { RightSidebarIcon } from '@blocksuite/icons/rc';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import { Suspense, useCallback } from 'react';
|
import { Suspense, useCallback } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary';
|
import { AppSidebarService } from '../../app-sidebar';
|
||||||
import { appSidebarOpenAtom } from '../../../components/app-sidebar/index.jotai';
|
import { SidebarSwitch } from '../../app-sidebar/views';
|
||||||
import { SidebarSwitch } from '../../../components/app-sidebar/sidebar-header/sidebar-switch';
|
|
||||||
import { ViewService } from '../services/view';
|
import { ViewService } from '../services/view';
|
||||||
import { WorkbenchService } from '../services/workbench';
|
import { WorkbenchService } from '../services/workbench';
|
||||||
import * as styles from './route-container.css';
|
import * as styles from './route-container.css';
|
||||||
@@ -43,7 +42,8 @@ const ToggleButton = ({
|
|||||||
|
|
||||||
export const RouteContainer = () => {
|
export const RouteContainer = () => {
|
||||||
const viewPosition = useViewPosition();
|
const viewPosition = useViewPosition();
|
||||||
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||||
|
const leftSidebarOpen = useLiveData(appSidebarService.open$);
|
||||||
const workbench = useService(WorkbenchService).workbench;
|
const workbench = useService(WorkbenchService).workbench;
|
||||||
const view = useService(ViewService).view;
|
const view = useService(ViewService).view;
|
||||||
const sidebarOpen = useLiveData(workbench.sidebarOpen$);
|
const sidebarOpen = useLiveData(workbench.sidebarOpen$);
|
||||||
|
|||||||
Reference in New Issue
Block a user