mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
@@ -9,7 +9,7 @@ import { useLiveData, useService } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import { selectAtom } from 'jotai/utils';
|
||||
import type { MouseEventHandler } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { CollectionListItem } from './collections/collection-list-item';
|
||||
import { PageListItem } from './docs/page-list-item';
|
||||
@@ -240,7 +240,7 @@ export const PageListItemRenderer = (item: ListItem) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const CollectionListItemRenderer = (item: ListItem) => {
|
||||
export const CollectionListItemRenderer = memo((item: ListItem) => {
|
||||
const props = useAtomValue(listsPropsAtom);
|
||||
const { selectionActive } = useAtomValue(selectionStateAtom);
|
||||
const collection = item as CollectionMeta;
|
||||
@@ -252,7 +252,9 @@ export const CollectionListItemRenderer = (item: ListItem) => {
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
CollectionListItemRenderer.displayName = 'CollectionListItemRenderer';
|
||||
|
||||
export const TagListItemRenderer = (item: ListItem) => {
|
||||
const props = useAtomValue(listsPropsAtom);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { RadioGroup, type RadioItem } from '@affine/component';
|
||||
import type { AllPageFilterOption } from '@affine/core/components/atoms';
|
||||
import { allPageFilterSelectAtom } from '@affine/core/components/atoms';
|
||||
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
@@ -14,26 +14,25 @@ export const WorkspaceModeFilterTab = ({
|
||||
}: {
|
||||
activeFilter: AllPageFilterOption;
|
||||
}) => {
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
const t = useI18n();
|
||||
const [value, setValue] = useState(activeFilter);
|
||||
const [filterMode, setFilterMode] = useAtom(allPageFilterSelectAtom);
|
||||
const { jumpToCollections, jumpToTags, jumpToPage } = useNavigateHelper();
|
||||
const workbenchService = useService(WorkbenchService);
|
||||
const handleValueChange = useCallback(
|
||||
(value: AllPageFilterOption) => {
|
||||
switch (value) {
|
||||
case 'collections':
|
||||
jumpToCollections(workspace.id);
|
||||
workbenchService.workbench.openCollections();
|
||||
break;
|
||||
case 'tags':
|
||||
jumpToTags(workspace.id);
|
||||
workbenchService.workbench.openTags();
|
||||
break;
|
||||
case 'docs':
|
||||
jumpToPage(workspace.id, 'all');
|
||||
workbenchService.workbench.openAll();
|
||||
break;
|
||||
}
|
||||
},
|
||||
[jumpToCollections, jumpToPage, jumpToTags, workspace]
|
||||
[workbenchService.workbench]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||
import {
|
||||
AddPageButton,
|
||||
AppDownloadButton,
|
||||
@@ -23,7 +22,6 @@ import {
|
||||
} from '@affine/core/modules/explorer';
|
||||
import { ExplorerTags } from '@affine/core/modules/explorer/views/sections/tags';
|
||||
import { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk';
|
||||
import { isNewTabTrigger } from '@affine/core/utils';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import type { Doc } from '@blocksuite/affine/store';
|
||||
@@ -35,17 +33,11 @@ import {
|
||||
SettingsIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import type { Workspace } from '@toeverything/infra';
|
||||
import {
|
||||
useLiveData,
|
||||
useService,
|
||||
useServices,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
import type { MouseEvent, ReactElement } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useLiveData, useService, useServices } from '@toeverything/infra';
|
||||
import type { ReactElement } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
|
||||
import { WorkbenchService } from '../../modules/workbench';
|
||||
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
||||
import { WorkspaceNavigator } from '../workspace-selector';
|
||||
import {
|
||||
quickSearch,
|
||||
@@ -72,42 +64,43 @@ export type RootAppSidebarProps = {
|
||||
};
|
||||
};
|
||||
|
||||
const AllDocsButton = () => {
|
||||
const t = useI18n();
|
||||
const { workbenchService } = useServices({
|
||||
WorkbenchService,
|
||||
});
|
||||
const workbench = workbenchService.workbench;
|
||||
const allPageActive = useLiveData(
|
||||
workbench.location$.selector(location => location.pathname === '/all')
|
||||
);
|
||||
|
||||
return (
|
||||
<MenuLinkItem icon={<AllDocsIcon />} active={allPageActive} to={'/all'}>
|
||||
<span data-testid="all-pages">
|
||||
{t['com.affine.workspaceSubPath.all']()}
|
||||
</span>
|
||||
</MenuLinkItem>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is for the whole affine app sidebar.
|
||||
* This component wraps the app sidebar in `@affine/component` with logic and data.
|
||||
*
|
||||
*/
|
||||
export const RootAppSidebar = (): ReactElement => {
|
||||
const { workbenchService, workspaceService, cMDKQuickSearchService } =
|
||||
useServices({
|
||||
WorkspaceService,
|
||||
WorkbenchService,
|
||||
CMDKQuickSearchService,
|
||||
});
|
||||
const currentWorkspace = workspaceService.workspace;
|
||||
export const RootAppSidebar = memo((): ReactElement => {
|
||||
const { workbenchService, cMDKQuickSearchService } = useServices({
|
||||
WorkbenchService,
|
||||
CMDKQuickSearchService,
|
||||
});
|
||||
const t = useI18n();
|
||||
const globalDialogService = useService(GlobalDialogService);
|
||||
const workspaceDialogService = useService(WorkspaceDialogService);
|
||||
const workbench = workbenchService.workbench;
|
||||
const currentPath = useLiveData(
|
||||
workbench.location$.map(location => location.pathname)
|
||||
);
|
||||
const onOpenQuickSearchModal = useCallback(() => {
|
||||
cMDKQuickSearchService.toggle();
|
||||
}, [cMDKQuickSearchService]);
|
||||
|
||||
const allPageActive = currentPath === '/all';
|
||||
|
||||
const pageHelper = usePageHelper(currentWorkspace.docCollection);
|
||||
|
||||
const onClickNewPage = useAsyncCallback(
|
||||
async (e?: MouseEvent) => {
|
||||
pageHelper.createPage(undefined, isNewTabTrigger(e) ? 'new-tab' : true);
|
||||
track.$.navigationPanel.$.createDoc();
|
||||
},
|
||||
[pageHelper]
|
||||
);
|
||||
|
||||
const onOpenSettingModal = useCallback(() => {
|
||||
globalDialogService.open('setting', {
|
||||
activeTab: 'appearance',
|
||||
@@ -169,13 +162,9 @@ export const RootAppSidebar = (): ReactElement => {
|
||||
data-event-props="$.navigationPanel.$.quickSearch"
|
||||
onClick={onOpenQuickSearchModal}
|
||||
/>
|
||||
<AddPageButton onClick={onClickNewPage} />
|
||||
<AddPageButton />
|
||||
</div>
|
||||
<MenuLinkItem icon={<AllDocsIcon />} active={allPageActive} to={'/all'}>
|
||||
<span data-testid="all-pages">
|
||||
{t['com.affine.workspaceSubPath.all']()}
|
||||
</span>
|
||||
</MenuLinkItem>
|
||||
<AllDocsButton />
|
||||
<AppSidebarJournalButton />
|
||||
<MenuItem
|
||||
data-testid="slider-bar-workspace-setting-button"
|
||||
@@ -220,6 +209,6 @@ export const RootAppSidebar = (): ReactElement => {
|
||||
</SidebarContainer>
|
||||
</AppSidebar>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
RootAppSidebar.displayName = 'memo(RootAppSidebar)';
|
||||
|
||||
@@ -1,24 +1,35 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-page-list/utils';
|
||||
import { isNewTabTrigger } from '@affine/core/utils';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import track from '@affine/track';
|
||||
import { PlusIcon } from '@blocksuite/icons/rc';
|
||||
import { useService, WorkspaceService } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import type React from 'react';
|
||||
import type { MouseEventHandler } from 'react';
|
||||
import { type MouseEvent, useCallback } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
|
||||
interface AddPageButtonProps {
|
||||
onClick?: MouseEventHandler;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const sideBottom = { side: 'bottom' as const };
|
||||
export function AddPageButton({
|
||||
onClick,
|
||||
className,
|
||||
style,
|
||||
}: AddPageButtonProps) {
|
||||
export function AddPageButton({ className, style }: AddPageButtonProps) {
|
||||
const workspaceService = useService(WorkspaceService);
|
||||
const currentWorkspace = workspaceService.workspace;
|
||||
const pageHelper = usePageHelper(currentWorkspace.docCollection);
|
||||
|
||||
const onClickNewPage = useCallback(
|
||||
(e?: MouseEvent) => {
|
||||
pageHelper.createPage(undefined, isNewTabTrigger(e) ? 'new-tab' : true);
|
||||
track.$.navigationPanel.$.createDoc();
|
||||
},
|
||||
[pageHelper]
|
||||
);
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
return (
|
||||
@@ -28,8 +39,8 @@ export function AddPageButton({
|
||||
data-testid="sidebar-new-page-button"
|
||||
style={style}
|
||||
className={clsx([styles.root, className])}
|
||||
onClick={onClick}
|
||||
onAuxClick={onClick}
|
||||
onClick={onClickNewPage}
|
||||
onAuxClick={onClickNewPage}
|
||||
>
|
||||
<PlusIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -89,6 +89,34 @@ interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {
|
||||
dropEffect?: ExplorerTreeNodeDropEffect;
|
||||
}
|
||||
|
||||
/**
|
||||
* specific rename modal for explorer tree node,
|
||||
* Separate it into a separate component to prevent re-rendering the entire component when width changes.
|
||||
*/
|
||||
const ExplorerTreeNodeRenameModal = ({
|
||||
setRenaming,
|
||||
handleRename,
|
||||
rawName,
|
||||
}: {
|
||||
setRenaming: (renaming: boolean) => void;
|
||||
handleRename: (newName: string) => void;
|
||||
rawName: string | undefined;
|
||||
}) => {
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||
return (
|
||||
<RenameModal
|
||||
open
|
||||
width={sidebarWidth - 32}
|
||||
onOpenChange={setRenaming}
|
||||
onRename={handleRename}
|
||||
currentName={rawName ?? ''}
|
||||
>
|
||||
<div className={styles.itemRenameAnchor} />
|
||||
</RenameModal>
|
||||
);
|
||||
};
|
||||
|
||||
export const ExplorerTreeNode = ({
|
||||
children,
|
||||
icon: Icon,
|
||||
@@ -126,9 +154,6 @@ export const ExplorerTreeNode = ({
|
||||
const [lastInGroup, setLastInGroup] = useState(false);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const appSidebarService = useService(AppSidebarService).sidebar;
|
||||
const sidebarWidth = useLiveData(appSidebarService.width$);
|
||||
|
||||
const { emoji, name } = useMemo(() => {
|
||||
if (!extractEmojiAsIcon || !rawName) {
|
||||
return {
|
||||
@@ -379,16 +404,12 @@ export const ExplorerTreeNode = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{renameable && (
|
||||
<RenameModal
|
||||
open={!!renaming}
|
||||
width={sidebarWidth - 32}
|
||||
onOpenChange={setRenaming}
|
||||
onRename={handleRename}
|
||||
currentName={rawName ?? ''}
|
||||
>
|
||||
<div className={styles.itemRenameAnchor} />
|
||||
</RenameModal>
|
||||
{renameable && renaming && (
|
||||
<ExplorerTreeNodeRenameModal
|
||||
setRenaming={setRenaming}
|
||||
handleRename={handleRename}
|
||||
rawName={rawName}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export { View as WorkbenchView } from './entities/view';
|
||||
export { Workbench } from './entities/workbench';
|
||||
export { ViewScope } from './scopes/view';
|
||||
export { ViewService } from './services/view';
|
||||
export { WorkbenchService } from './services/workbench';
|
||||
export { useBindWorkbenchToBrowserRouter } from './view/browser-adapter';
|
||||
export { useIsActiveView } from './view/use-is-active-view';
|
||||
|
||||
Reference in New Issue
Block a user