From c850dbb2b77d573bc019d768f4cb87c5106d2b3e Mon Sep 17 00:00:00 2001 From: CatsJuice Date: Thu, 11 Jul 2024 06:24:44 +0000 Subject: [PATCH] refactor(core): optimize abstraction of sidebar doc tree structure (#7455) --- .../collections/collections-list.tsx | 159 ++++++++-------- .../workspace-slider-bar/collections/doc.tsx | 156 ---------------- .../collections/index.tsx | 1 - .../collections/styles.css.ts | 53 +----- .../components/postfix-item.tsx | 2 +- .../components/reference-page.tsx | 127 ------------- .../workspace-slider-bar/doc-tree/doc.css.ts | 61 ++++++ .../workspace-slider-bar/doc-tree/doc.tsx | 174 ++++++++++++++++++ .../workspace-slider-bar/doc-tree/node.css.ts | 6 + .../workspace-slider-bar/doc-tree/node.tsx | 103 +++++++++++ .../favorite/favorite-list.tsx | 2 +- .../favorite/favourite-nav-item.tsx | 133 ++----------- .../favorite/styles.css.ts | 14 +- 13 files changed, 454 insertions(+), 537 deletions(-) delete mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/collections/doc.tsx delete mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx create mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.css.ts create mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.tsx create mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.css.ts create mode 100644 packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.tsx diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx index 22d695f635..cc4be9cf56 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/collections-list.tsx @@ -29,7 +29,6 @@ import { import type { DocCollection } from '@blocksuite/store'; import { type AnimateLayoutChanges, useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import * as Collapsible from '@radix-ui/react-collapsible'; import { useLiveData, useService } from '@toeverything/infra'; import { useCallback, useMemo, useState } from 'react'; @@ -37,11 +36,11 @@ import { useAllPageListConfig } from '../../../../hooks/affine/use-all-page-list import { useBlockSuiteDocMeta } from '../../../../hooks/use-block-suite-page-meta'; import { WorkbenchService } from '../../../../modules/workbench'; import { WorkbenchLink } from '../../../../modules/workbench/view/workbench-link'; -import { MenuLinkItem as SidebarMenuLinkItem } from '../../../app-sidebar'; import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay'; import * as draggableMenuItemStyles from '../components/draggable-menu-item.css'; +import { SidebarDocItem } from '../doc-tree/doc'; +import { SidebarDocTreeNode } from '../doc-tree/node'; import type { CollectionsListProps } from '../index'; -import { Doc } from './doc'; import * as styles from './styles.css'; const animateLayoutChanges: AnimateLayoutChanges = ({ @@ -60,7 +59,6 @@ export const CollectionSidebarNavItem = ({ dndId: DNDIdentifier; className?: string; }) => { - const [collapsed, setCollapsed] = useState(true); const [open, setOpen] = useState(false); const collectionService = useService(CollectionService); const { createPage } = usePageHelper(docCollection); @@ -139,79 +137,78 @@ export const CollectionSidebarNavItem = ({ }); }, [createAndAddDocument, openConfirmModal, t]); - return ( - { + // prevent drag + e.stopPropagation(); + }} + style={{ display: 'flex', alignItems: 'center' }} > - } - to={path} - linkComponent={WorkbenchLink} - postfix={ -
{ - // prevent drag - e.stopPropagation(); - }} - style={{ display: 'flex', alignItems: 'center' }} - > - - - - - - - - - -
- } - collapsed={collapsed} + + + + - {collection.name} -
- - {!collapsed && ( - - )} - -
+ + + + + + + ); + + return ( + + } + rootProps={{ + className, + style, + ...attributes, + }} + menuItemProps={{ + ...listeners, + 'data-draggable': true, + 'data-dragging': isDragging, + 'data-testid': 'collection-item', + 'data-collection-id': collection.id, + 'data-type': 'collection-list-item', + className: draggableMenuItemStyles.draggableMenuItem, + active: isOver || currentPath === path, + icon: , + postfix, + }} + > + {collection.name} + ); }; -export const CollectionSidebarNavItemContent = ({ +const CollectionSidebarNavItemContent = ({ collection, docCollection, dndId, @@ -254,12 +251,20 @@ export const CollectionSidebarNavItemContent = ({ {filtered.length > 0 ? ( filtered.map(page => { return ( - ); }) diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/doc.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/doc.tsx deleted file mode 100644 index 592b285301..0000000000 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/doc.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { Loading, Tooltip } from '@affine/component'; -import { DocsSearchService } from '@affine/core/modules/docs-search'; -import { - WorkbenchLink, - WorkbenchService, -} from '@affine/core/modules/workbench'; -import { useI18n } from '@affine/i18n'; -import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc'; -import { useDraggable } from '@dnd-kit/core'; -import * as Collapsible from '@radix-ui/react-collapsible'; -import { - DocsService, - LiveData, - useLiveData, - useService, - useServices, -} from '@toeverything/infra'; -import React, { useEffect, useMemo, useState } from 'react'; - -import { - type DNDIdentifier, - getDNDId, -} from '../../../../hooks/affine/use-global-dnd-helper'; -import { MenuLinkItem } from '../../../app-sidebar'; -import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay'; -import { PostfixItem } from '../components/postfix-item'; -import { ReferencePage } from '../components/reference-page'; -import * as styles from './styles.css'; - -export const Doc = ({ - docId, - parentId, - inAllowList, - removeFromAllowList, -}: { - parentId: DNDIdentifier; - docId: string; - inAllowList: boolean; - removeFromAllowList: (id: string) => void; -}) => { - const { docsSearchService, workbenchService } = useServices({ - DocsSearchService, - WorkbenchService, - DocsService, - }); - const t = useI18n(); - const location = useLiveData(workbenchService.workbench.location$); - const active = location.pathname === '/' + docId; - - const [collapsed, setCollapsed] = React.useState(true); - const docRecord = useLiveData(useService(DocsService).list.doc$(docId)); - const docMode = useLiveData(docRecord?.mode$); - const docTitle = useLiveData(docRecord?.title$); - const icon = useMemo(() => { - return docMode === 'edgeless' ? : ; - }, [docMode]); - const references = useLiveData( - useMemo( - () => LiveData.from(docsSearchService.watchRefsFrom(docId), null), - [docsSearchService, docId] - ) - ); - const indexerLoading = useLiveData( - docsSearchService.indexer.status$.map( - v => v.remaining === undefined || v.remaining > 0 - ) - ); - const [referencesLoading, setReferencesLoading] = useState(true); - useEffect(() => { - setReferencesLoading( - prev => - prev && - indexerLoading /* after loading becomes false, it never becomes true */ - ); - }, [indexerLoading]); - const untitled = !docTitle; - - const dragItemId = getDNDId('collection-list', 'doc', docId, parentId); - - const title = docTitle || t['Untitled'](); - const docTitleElement = useMemo(() => { - return ; - }, [icon, docTitle]); - - const { setNodeRef, attributes, listeners, isDragging } = useDraggable({ - id: dragItemId, - data: { - preview: docTitleElement, - }, - }); - - return ( - - - } - ref={setNodeRef} - {...attributes} - {...listeners} - > -
- - {title || t['Untitled']()} - - {!collapsed && referencesLoading && ( - -
- -
-
- )} -
-
- - {references ? ( - references.length > 0 ? ( - references.map(({ docId: childDocId }) => { - return ( - - ); - }) - ) : ( -
- {t['com.affine.rootAppSidebar.docs.no-subdoc']()} -
- ) - ) : null} -
-
- ); -}; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/index.tsx index 0d1440e8ac..ec946faa2a 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/index.tsx @@ -1,2 +1 @@ export * from './collections-list'; -export { Doc } from './doc'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts index 6c9e8bdd61..08da5372eb 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/collections/styles.css.ts @@ -3,7 +3,7 @@ import { globalStyle, style } from '@vanilla-extract/css'; export const wrapper = style({ display: 'flex', flexDirection: 'column', - gap: '4px', + gap: 2, userSelect: 'none', // marginLeft:8, }); @@ -23,37 +23,6 @@ export const viewTitle = style({ display: 'flex', alignItems: 'center', }); -export const title = style({ - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', -}); -globalStyle(`[data-draggable=true] ${title}:before`, { - content: '""', - position: 'absolute', - top: '50%', - transform: 'translateY(-50%)', - left: 0, - width: 4, - height: 4, - transition: 'height 0.2s, opacity 0.2s', - backgroundColor: cssVar('placeholderColor'), - borderRadius: '2px', - opacity: 0, - willChange: 'height, opacity', -}); -globalStyle(`[data-draggable=true] ${title}:hover:before`, { - height: 12, - opacity: 1, -}); -globalStyle(`[data-draggable=true][data-dragging=true] ${title}`, { - opacity: 0.5, -}); -globalStyle(`[data-draggable=true][data-dragging=true] ${title}:before`, { - height: 32, - width: 2, - opacity: 1, -}); export const more = style({ display: 'flex', alignItems: 'center', @@ -91,23 +60,6 @@ export const collapsibleContent = style({ }, }, }); -export const label = style({ - selectors: { - '&[data-untitled="true"]': { - opacity: 0.6, - }, - }, -}); -export const labelContainer = style({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', -}); -export const labelTooltipContainer = style({ - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', -}); export const emptyCollectionWrapper = style({ padding: '9px 0', display: 'flex', @@ -146,10 +98,9 @@ export const emptyCollectionNewButton = style({ fontSize: cssVar('fontXs'), }); export const docsListContainer = style({ - marginLeft: 20, display: 'flex', flexDirection: 'column', - gap: 4, + gap: 2, }); export const noReferences = style({ fontSize: cssVar('fontSm'), diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx index 594bd32dc1..a6e9638e4a 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/postfix-item.tsx @@ -9,7 +9,7 @@ import { AddFavouriteButton } from '../favorite/add-favourite-button'; import * as styles from '../favorite/styles.css'; import { OperationMenuButton } from './operation-menu-button'; -type PostfixItemProps = { +export type PostfixItemProps = { pageId: string; pageTitle: string; inFavorites?: boolean; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx deleted file mode 100644 index cd5ea8f830..0000000000 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { Loading, Tooltip } from '@affine/component'; -import { DocsSearchService } from '@affine/core/modules/docs-search'; -import { - WorkbenchLink, - WorkbenchService, -} from '@affine/core/modules/workbench'; -import { useI18n } from '@affine/i18n'; -import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc'; -import * as Collapsible from '@radix-ui/react-collapsible'; -import { - DocsService, - LiveData, - useLiveData, - useServices, -} from '@toeverything/infra'; -import { useEffect, useMemo, useState } from 'react'; - -import { MenuLinkItem } from '../../../app-sidebar'; -import * as styles from '../favorite/styles.css'; -import { PostfixItem } from './postfix-item'; -export interface ReferencePageProps { - pageId: string; - parentIds?: Set; -} - -export const ReferencePage = ({ pageId, parentIds }: ReferencePageProps) => { - const t = useI18n(); - const { docsSearchService, workbenchService, docsService } = useServices({ - DocsSearchService, - WorkbenchService, - DocsService, - }); - const workbench = workbenchService.workbench; - const location = useLiveData(workbench.location$); - const linkActive = location.pathname === '/' + pageId; - const docRecord = useLiveData(docsService.list.doc$(pageId)); - const docMode = useLiveData(docRecord?.mode$); - const docTitle = useLiveData(docRecord?.title$); - const icon = useMemo(() => { - return docMode === 'edgeless' ? : ; - }, [docMode]); - const [collapsed, setCollapsed] = useState(true); - const references = useLiveData( - useMemo( - () => LiveData.from(docsSearchService.watchRefsFrom(pageId), null), - [docsSearchService, pageId] - ) - ); - const indexerLoading = useLiveData( - docsSearchService.indexer.status$.map( - v => v.remaining === undefined || v.remaining > 0 - ) - ); - const [referencesLoading, setReferencesLoading] = useState(true); - useEffect(() => { - setReferencesLoading( - prev => - prev && - indexerLoading /* after loading becomes false, it never becomes true */ - ); - }, [indexerLoading]); - const nestedItem = parentIds && parentIds.size > 0; - const untitled = !docTitle; - const pageTitle = docTitle || t['Untitled'](); - - return ( - - - } - > -
- - {pageTitle} - - {!collapsed && referencesLoading && ( - -
- -
-
- )} -
-
- -
- {references ? ( - references.length > 0 ? ( - references.map(({ docId }) => { - return ( - - ); - }) - ) : ( -
- {t['com.affine.rootAppSidebar.docs.no-subdoc']()} -
- ) - ) : null} -
-
-
- ); -}; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.css.ts new file mode 100644 index 0000000000..3c58024de5 --- /dev/null +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.css.ts @@ -0,0 +1,61 @@ +import { cssVar } from '@toeverything/theme'; +import { globalStyle, style } from '@vanilla-extract/css'; + +export const title = style({ + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', +}); + +globalStyle(`[data-draggable=true] ${title}:before`, { + content: '""', + position: 'absolute', + top: '50%', + transform: 'translateY(-50%)', + left: 0, + width: 4, + height: 4, + transition: 'height 0.2s, opacity 0.2s', + backgroundColor: cssVar('placeholderColor'), + borderRadius: '2px', + opacity: 0, + willChange: 'height, opacity', +}); +globalStyle(`[data-draggable=true] ${title}:hover:before`, { + height: 12, + opacity: 1, +}); +globalStyle(`[data-draggable=true][data-dragging=true] ${title}`, { + opacity: 0.5, +}); +globalStyle(`[data-draggable=true][data-dragging=true] ${title}:before`, { + height: 32, + width: 2, + opacity: 1, +}); + +export const label = style({ + selectors: { + '&[data-untitled="true"]': { + opacity: 0.6, + }, + }, +}); +export const labelContainer = style({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +}); +export const labelTooltipContainer = style({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +}); + +export const noReferences = style({ + fontSize: cssVar('fontSm'), + textAlign: 'left', + paddingLeft: '32px', + color: cssVar('black30'), + userSelect: 'none', +}); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.tsx new file mode 100644 index 0000000000..90bac69be2 --- /dev/null +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/doc.tsx @@ -0,0 +1,174 @@ +import { Loading, Tooltip } from '@affine/component'; +import type { MenuItemProps } from '@affine/core/components/app-sidebar'; +import { + type DNDIdentifier, + type DndWhere, + getDNDId, +} from '@affine/core/hooks/affine/use-global-dnd-helper'; +import { DocsSearchService } from '@affine/core/modules/docs-search'; +import { + WorkbenchLink, + WorkbenchService, +} from '@affine/core/modules/workbench'; +import { useI18n } from '@affine/i18n'; +import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc'; +import { useDraggable } from '@dnd-kit/core'; +import { + DocsService, + LiveData, + useLiveData, + useServices, +} from '@toeverything/infra'; +import { nanoid } from 'nanoid'; +import { useEffect, useMemo, useState } from 'react'; + +import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay'; +import { PostfixItem, type PostfixItemProps } from '../components/postfix-item'; +import * as styles from './doc.css'; +import { SidebarDocTreeNode } from './node'; + +export type SidebarDocItemProps = { + docId: string; + postfixConfig?: Omit< + PostfixItemProps, + 'pageId' | 'pageTitle' | 'isReferencePage' + >; + isReference?: boolean; + dragConfig?: { + parentId?: DNDIdentifier; + where: DndWhere; + }; + menuItemProps?: Partial & Record<`data-${string}`, string>; +}; + +export const SidebarDocItem = function SidebarDocItem({ + docId, + postfixConfig, + isReference, + dragConfig, + menuItemProps, +}: SidebarDocItemProps) { + const { docsSearchService, workbenchService, docsService } = useServices({ + DocsSearchService, + WorkbenchService, + DocsService, + }); + const t = useI18n(); + const location = useLiveData(workbenchService.workbench.location$); + const active = location.pathname === '/' + docId; + + const docRecord = useLiveData(docsService.list.doc$(docId)); + const docMode = useLiveData(docRecord?.mode$); + const docTitle = useLiveData(docRecord?.title$); + const icon = useMemo(() => { + return docMode === 'edgeless' ? : ; + }, [docMode]); + + const references = useLiveData( + useMemo( + () => LiveData.from(docsSearchService.watchRefsFrom(docId), null), + [docsSearchService, docId] + ) + ); + const indexerLoading = useLiveData( + docsSearchService.indexer.status$.map( + v => v.remaining === undefined || v.remaining > 0 + ) + ); + const [referencesLoading, setReferencesLoading] = useState(true); + useEffect(() => { + setReferencesLoading( + prev => + prev && + indexerLoading /* after loading becomes false, it never becomes true */ + ); + }, [indexerLoading]); + const untitled = !docTitle; + const title = docTitle || t['Untitled'](); + + // drag (not available for sub-docs) + const dragItemId = dragConfig + ? getDNDId(dragConfig.where, 'doc', docId, dragConfig.parentId) + : nanoid(); + const docTitleElement = useMemo(() => { + return ; + }, [icon, docTitle]); + const { setNodeRef, attributes, listeners, isDragging } = useDraggable({ + id: dragItemId, + data: { preview: docTitleElement }, + disabled: !dragConfig || isReference, + }); + + const dragAttrs: Partial = isReference + ? { + // prevent dragging parent node + onMouseDown: e => e.stopPropagation(), + } + : { ...attributes, ...listeners }; + + // workaround to avoid invisible in playwright caused by nested drag + delete dragAttrs['aria-disabled']; + + return ( + + ), + ...dragAttrs, + ...menuItemProps, + }} + subTree={ + references ? ( + references.length > 0 ? ( + references.map(({ docId: childDocId }) => { + return ( + + ); + }) + ) : ( +
+ {t['com.affine.rootAppSidebar.docs.no-subdoc']()} +
+ ) + ) : null + } + > +
+ + {title || t['Untitled']()} + + {referencesLoading && ( + +
+ +
+
+ )} +
+
+ ); +}; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.css.ts new file mode 100644 index 0000000000..45d6262911 --- /dev/null +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.css.ts @@ -0,0 +1,6 @@ +import { style } from '@vanilla-extract/css'; + +export const collapseContent = style({ + paddingTop: 2, + paddingLeft: 20, +}); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.tsx new file mode 100644 index 0000000000..df0177c1d6 --- /dev/null +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/doc-tree/node.tsx @@ -0,0 +1,103 @@ +import { + MenuItem, + type MenuItemProps, + MenuLinkItem, +} from '@affine/core/components/app-sidebar'; +import type { Collection } from '@affine/env/filter'; +import * as Collapsible from '@radix-ui/react-collapsible'; +import { + createContext, + forwardRef, + type PropsWithChildren, + type ReactNode, + useContext, + useState, +} from 'react'; +import { Link, type To } from 'react-router-dom'; + +import * as styles from './node.css'; + +type SidebarDocTreeNode = + | { + type: 'collection'; + data: Collection; + } + // | { type: 'tag' } + // | { type: 'folder' } + | { + type: 'doc'; + data: string; + }; + +export type SidebarDocTreeNodeProps = PropsWithChildren<{ + node: SidebarDocTreeNode; + subTree?: ReactNode; + to?: To; + linkComponent?: React.ComponentType<{ to: To; className?: string }>; + + menuItemProps?: MenuItemProps & Record<`data-${string}`, unknown>; + rootProps?: Collapsible.CollapsibleProps & Record<`data-${string}`, unknown>; +}>; + +type SidebarDocTreeNodeContext = { + ancestors: SidebarDocTreeNode[]; +}; + +export const sidebarDocTreeContext = + createContext(null); + +/** + * Tree node for the sidebar doc/folder/tag/collection tree. + * This component is used to manage: + * - Collapsing state + * - Ancestors context + * - Link/Menu item rendering + * - Subtree indentation (left/top) + */ +export const SidebarDocTreeNode = forwardRef(function SidebarDocTreeNode( + { + node, + children, + subTree, + to, + linkComponent: LinkComponent = Link, + menuItemProps, + rootProps, + }: SidebarDocTreeNodeProps, + ref: React.Ref +) { + const [collapsed, setCollapsed] = useState(true); + const { ancestors } = useContext(sidebarDocTreeContext) ?? { ancestors: [] }; + + const finalMenuItemProps: SidebarDocTreeNodeProps['menuItemProps'] = { + ...menuItemProps, + collapsed, + onCollapsedChange: setCollapsed, + }; + + return ( + + + {to ? ( + + {children} + + ) : ( + {children} + )} + + {collapsed ? null : subTree} + + + + ); +}); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx index 8ba9c4c328..4c6685502b 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx @@ -84,7 +84,7 @@ const FavoriteListInner = ({ docCollection: workspace }: FavoriteListProps) => { } else if (item.type === 'doc') { return ( ); diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-nav-item.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-nav-item.tsx index eba6c4ad61..d9ea55e879 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-nav-item.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-nav-item.tsx @@ -1,32 +1,16 @@ -import { Loading, Tooltip } from '@affine/component'; import { getDNDId, parseDNDId, } from '@affine/core/hooks/affine/use-global-dnd-helper'; -import { DocsSearchService } from '@affine/core/modules/docs-search'; -import { - WorkbenchLink, - WorkbenchService, -} from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc'; import { type AnimateLayoutChanges, useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import * as Collapsible from '@radix-ui/react-collapsible'; -import { - DocsService, - LiveData, - useLiveData, - useServices, -} from '@toeverything/infra'; -import { useEffect, useMemo, useState } from 'react'; +import { DocsService, useLiveData, useService } from '@toeverything/infra'; +import { useMemo } from 'react'; -import { MenuLinkItem } from '../../../app-sidebar'; import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay'; -import * as draggableMenuItemStyles from '../components/draggable-menu-item.css'; -import { PostfixItem } from '../components/postfix-item'; -import type { ReferencePageProps } from '../components/reference-page'; -import { ReferencePage } from '../components/reference-page'; +import { SidebarDocItem } from '../doc-tree/doc'; import * as styles from './styles.css'; const animateLayoutChanges: AnimateLayoutChanges = ({ @@ -34,44 +18,12 @@ const animateLayoutChanges: AnimateLayoutChanges = ({ wasDragging, }) => (isSorting || wasDragging ? false : true); -export const FavouriteDocSidebarNavItem = ({ - pageId, -}: ReferencePageProps & { - sortable?: boolean; -}) => { +export const FavouriteDocSidebarNavItem = ({ docId }: { docId: string }) => { const t = useI18n(); - const { docsSearchService, workbenchService, docsService } = useServices({ - DocsSearchService, - WorkbenchService, - DocsService, - }); - const workbench = workbenchService.workbench; - const location = useLiveData(workbench.location$); - const linkActive = location.pathname === '/' + pageId; - const docRecord = useLiveData(docsService.list.doc$(pageId)); + const docsService = useService(DocsService); + const docRecord = useLiveData(docsService.list.doc$(docId)); const docMode = useLiveData(docRecord?.mode$); const docTitle = useLiveData(docRecord?.title$); - const references = useLiveData( - useMemo( - () => LiveData.from(docsSearchService.watchRefsFrom(pageId), null), - [docsSearchService, pageId] - ) - ); - const indexerLoading = useLiveData( - docsSearchService.indexer.status$.map( - v => v.remaining === undefined || v.remaining > 0 - ) - ); - const [referencesLoading, setReferencesLoading] = useState(true); - useEffect(() => { - setReferencesLoading( - prev => - prev && - indexerLoading /* after loading becomes false, it never becomes true */ - ); - }, [indexerLoading]); - const [collapsed, setCollapsed] = useState(true); - const untitled = !docTitle; const pageTitle = docTitle || t['Untitled'](); const icon = useMemo(() => { @@ -82,7 +34,7 @@ export const FavouriteDocSidebarNavItem = ({ return ; }, [icon, pageTitle]); - const dragItemId = getDNDId('sidebar-pin', 'doc', pageId); + const dragItemId = getDNDId('sidebar-pin', 'doc', docId); const { setNodeRef, @@ -107,68 +59,23 @@ export const FavouriteDocSidebarNavItem = ({ }; return ( - - - } - > -
- - {pageTitle} - - {!collapsed && referencesLoading && ( - -
- -
-
- )} -
-
- - {references ? ( - references.length > 0 ? ( - references.map(({ docId }) => { - return ( - - ); - }) - ) : ( -
- {t['com.affine.rootAppSidebar.docs.no-subdoc']()} -
- ) - ) : null} -
-
+ + ); }; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts index 131ea402fb..e526119599 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/styles.css.ts @@ -22,15 +22,6 @@ export const favItemWrapper = style({ flexDirection: 'column', flexShrink: 0, userSelect: 'none', - selectors: { - '&[data-nested="true"]': { - marginLeft: '20px', - width: 'calc(100% - 20px)', - }, - '&:not(:first-of-type)': { - marginTop: '4px', - }, - }, }); export const collapsibleContent = style({ overflow: 'hidden', @@ -71,10 +62,13 @@ globalStyle(`${dragPageItemOverlay} span`, { }); export const favoriteList = style({ overflow: 'hidden', + borderRadius: '4px', + display: 'flex', + flexDirection: 'column', + gap: 2, selectors: { '&[data-over="true"]': { background: cssVar('hoverColorFilled'), - borderRadius: '4px', }, }, });