refactor(core): refactor left sidebar to use di (#8385)
This commit is contained in:
54 files changed
+316
-180
No files matched your search
@@ -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]);
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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,
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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}>
|
||||||
File renamed without changes.
+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>
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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