mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-02 14:49:44 +08:00
feat: center peek view (#7073)
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/T2klNLEk0wxLh4NRDzhk/7380c06e-880e-424a-9204-8cfb5e924978.mp4">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/T2klNLEk0wxLh4NRDzhk/7380c06e-880e-424a-9204-8cfb5e924978.mp4">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/7380c06e-880e-424a-9204-8cfb5e924978.mp4">Kapture 2024-05-30 at 19.42.46.mp4</video>
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||
import { useJournalHelper } from '@affine/core/hooks/use-journal';
|
||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { LinkedPageIcon, TodayIcon } from '@blocksuite/icons';
|
||||
import type { DocCollection } from '@blocksuite/store';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { type PropsWithChildren, useCallback, useRef } from 'react';
|
||||
|
||||
import * as styles from './styles.css';
|
||||
|
||||
@@ -64,8 +66,30 @@ export function AffinePageReference({
|
||||
t,
|
||||
});
|
||||
|
||||
const ref = useRef<HTMLAnchorElement>(null);
|
||||
|
||||
const peekView = useService(PeekViewService).peekView;
|
||||
|
||||
const onClick = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (e.shiftKey && ref.current) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
peekView.open(ref.current);
|
||||
return true; // means this click is handled
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[peekView]
|
||||
);
|
||||
|
||||
return (
|
||||
<WorkbenchLink to={`/${pageId}`} className={styles.pageReferenceLink}>
|
||||
<WorkbenchLink
|
||||
ref={ref}
|
||||
to={`/${pageId}`}
|
||||
onClick={onClick}
|
||||
className={styles.pageReferenceLink}
|
||||
>
|
||||
{Wrapper ? <Wrapper>{el}</Wrapper> : el}
|
||||
</WorkbenchLink>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import { cssVar } from '@toeverything/theme';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export const linkItemRoot = style({
|
||||
color: 'inherit',
|
||||
display: 'contents',
|
||||
});
|
||||
export const root = style({
|
||||
display: 'inline-flex',
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface MenuItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
|
||||
export interface MenuLinkItemProps extends MenuItemProps {
|
||||
to: To;
|
||||
linkComponent?: React.ComponentType<{ to: To; className: string }>;
|
||||
linkComponent?: React.ComponentType<{ to: To; className?: string }>;
|
||||
}
|
||||
|
||||
const stopPropagation: React.MouseEventHandler = e => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
useLitPortalFactory,
|
||||
} from '@affine/component';
|
||||
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import type { BlockSpec } from '@blocksuite/block-std';
|
||||
import {
|
||||
@@ -30,6 +31,7 @@ import { AffinePageReference } from '../../affine/reference-link';
|
||||
import { BlocksuiteEditorJournalDocTitle } from './journal-doc-title';
|
||||
import {
|
||||
patchNotificationService,
|
||||
patchPeekViewService,
|
||||
patchReferenceRenderer,
|
||||
type ReferenceReactRenderer,
|
||||
} from './specs/custom/spec-patchers';
|
||||
@@ -66,6 +68,7 @@ interface BlocksuiteEditorProps {
|
||||
|
||||
const usePatchSpecs = (page: Doc, specs: BlockSpec[]) => {
|
||||
const [reactToLit, portals] = useLitPortalFactory();
|
||||
const peekViewService = useService(PeekViewService);
|
||||
const referenceRenderer: ReferenceReactRenderer = useMemo(() => {
|
||||
return function customReference(reference) {
|
||||
const pageId = reference.delta.attributes?.reference?.pageId;
|
||||
@@ -83,8 +86,9 @@ const usePatchSpecs = (page: Doc, specs: BlockSpec[]) => {
|
||||
patchReferenceRenderer(patched, reactToLit, referenceRenderer),
|
||||
confirmModal
|
||||
);
|
||||
patched = patchPeekViewService(patched, peekViewService);
|
||||
return patched;
|
||||
}, [confirmModal, reactToLit, referenceRenderer, specs]);
|
||||
}, [confirmModal, peekViewService, reactToLit, referenceRenderer, specs]);
|
||||
|
||||
return [
|
||||
patchedSpecs,
|
||||
|
||||
+29
@@ -6,6 +6,9 @@ import {
|
||||
type ToastOptions,
|
||||
type useConfirmModal,
|
||||
} from '@affine/component';
|
||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
||||
import type { ActivePeekView } from '@affine/core/modules/peek-view/entities/peek-view';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import type { BlockSpec } from '@blocksuite/block-std';
|
||||
import type {
|
||||
AffineReference,
|
||||
@@ -15,6 +18,8 @@ import type {
|
||||
import { LitElement, type TemplateResult } from 'lit';
|
||||
import React, { createElement, type ReactNode } from 'react';
|
||||
|
||||
const logger = new DebugLogger('affine::spec-patchers');
|
||||
|
||||
export type ReferenceReactRenderer = (
|
||||
reference: AffineReference
|
||||
) => React.ReactElement;
|
||||
@@ -190,3 +195,27 @@ export function patchNotificationService(
|
||||
});
|
||||
return specs;
|
||||
}
|
||||
|
||||
export function patchPeekViewService(
|
||||
specs: BlockSpec[],
|
||||
service: PeekViewService
|
||||
) {
|
||||
const rootSpec = specs.find(
|
||||
spec => spec.schema.model.flavour === 'affine:page'
|
||||
) as BlockSpec<string, RootService>;
|
||||
|
||||
if (!rootSpec) {
|
||||
return specs;
|
||||
}
|
||||
|
||||
patchSpecService(rootSpec, pageService => {
|
||||
pageService.peekViewService = {
|
||||
peek: (target: ActivePeekView['target']) => {
|
||||
logger.debug('center peek', target);
|
||||
service.peekView.open(target);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return specs;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
DeleteIcon,
|
||||
DeletePermanentlyIcon,
|
||||
DualLinkIcon,
|
||||
DuplicateIcon,
|
||||
EditIcon,
|
||||
FavoritedIcon,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
FilterIcon,
|
||||
FilterMinusIcon,
|
||||
MoreVerticalIcon,
|
||||
OpenInNewIcon,
|
||||
PlusIcon,
|
||||
ResetIcon,
|
||||
SplitViewIcon,
|
||||
@@ -170,7 +170,7 @@ export const PageOperationCell = ({
|
||||
style={{ marginBottom: 4 }}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<OpenInNewIcon />
|
||||
<DualLinkIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ export const CollectionSidebarNavItem = ({
|
||||
<span>{collection.name}</span>
|
||||
</SidebarMenuLinkItem>
|
||||
<Collapsible.Content className={styles.collapsibleContent}>
|
||||
<div style={{ marginLeft: 20, marginTop: -4 }}>
|
||||
<div className={styles.docsListContainer}>
|
||||
{pagesToRender.map(page => {
|
||||
return (
|
||||
<Doc
|
||||
|
||||
+10
-12
@@ -1,19 +1,21 @@
|
||||
import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import {
|
||||
WorkbenchLink,
|
||||
WorkbenchService,
|
||||
} from '@affine/core/modules/workbench';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||
import type { DocCollection, DocMeta } from '@blocksuite/store';
|
||||
import { useDraggable } from '@dnd-kit/core';
|
||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||
import { DocsService, useLiveData, useService } from '@toeverything/infra';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import {
|
||||
type DNDIdentifier,
|
||||
getDNDId,
|
||||
} from '../../../../hooks/affine/use-global-dnd-helper';
|
||||
import { useNavigateHelper } from '../../../../hooks/use-navigate-helper';
|
||||
import { MenuItem as CollectionItem } from '../../../app-sidebar';
|
||||
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';
|
||||
@@ -50,11 +52,6 @@ export const Doc = ({
|
||||
return docMode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
|
||||
}, [docMode]);
|
||||
|
||||
const { jumpToPage } = useNavigateHelper();
|
||||
const clickDoc = useCallback(() => {
|
||||
jumpToPage(docCollection.id, doc.id);
|
||||
}, [jumpToPage, doc.id, docCollection.id]);
|
||||
|
||||
const references = useBlockSuitePageReferences(docCollection, docId);
|
||||
const referencesToRender = references.filter(
|
||||
id => allPageMeta[id] && !allPageMeta[id]?.trash
|
||||
@@ -78,11 +75,12 @@ export const Doc = ({
|
||||
data-draggable={true}
|
||||
data-dragging={isDragging}
|
||||
>
|
||||
<CollectionItem
|
||||
<MenuLinkItem
|
||||
data-testid="collection-page"
|
||||
data-type="collection-list-item"
|
||||
icon={icon}
|
||||
onClick={clickDoc}
|
||||
to={`/${docId}`}
|
||||
linkComponent={WorkbenchLink}
|
||||
className={styles.title}
|
||||
active={active}
|
||||
collapsed={referencesToRender.length > 0 ? collapsed : undefined}
|
||||
@@ -101,7 +99,7 @@ export const Doc = ({
|
||||
{...listeners}
|
||||
>
|
||||
{doc.title || t['Untitled']()}
|
||||
</CollectionItem>
|
||||
</MenuLinkItem>
|
||||
<Collapsible.Content className={styles.collapsibleContent}>
|
||||
{referencesToRender.map(id => {
|
||||
return (
|
||||
|
||||
+6
@@ -150,3 +150,9 @@ export const emptyCollectionNewButton = style({
|
||||
height: '28px',
|
||||
fontSize: cssVar('fontXs'),
|
||||
});
|
||||
export const docsListContainer = style({
|
||||
marginLeft: 20,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
});
|
||||
|
||||
+6
-2
@@ -1,5 +1,8 @@
|
||||
import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import {
|
||||
WorkbenchLink,
|
||||
WorkbenchService,
|
||||
} from '@affine/core/modules/workbench';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||
import type { DocCollection, DocMeta } from '@blocksuite/store';
|
||||
@@ -60,10 +63,11 @@ export const ReferencePage = ({
|
||||
data-type="reference-page"
|
||||
data-testid={`reference-page-${pageId}`}
|
||||
active={active}
|
||||
to={`/workspace/${docCollection.id}/${pageId}`}
|
||||
to={`/${pageId}`}
|
||||
icon={icon}
|
||||
collapsed={collapsible ? collapsed : undefined}
|
||||
onCollapsedChange={setCollapsed}
|
||||
linkComponent={WorkbenchLink}
|
||||
postfix={
|
||||
<PostfixItem
|
||||
docCollection={docCollection}
|
||||
|
||||
+6
-2
@@ -3,7 +3,10 @@ import {
|
||||
parseDNDId,
|
||||
} from '@affine/core/hooks/affine/use-global-dnd-helper';
|
||||
import { useBlockSuitePageReferences } from '@affine/core/hooks/use-block-suite-page-references';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import {
|
||||
WorkbenchLink,
|
||||
WorkbenchService,
|
||||
} from '@affine/core/modules/workbench';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||
import { type AnimateLayoutChanges, useSortable } from '@dnd-kit/sortable';
|
||||
@@ -102,7 +105,8 @@ export const FavouriteDocSidebarNavItem = ({
|
||||
data-dragging={isDragging}
|
||||
className={draggableMenuItemStyles.draggableMenuItem}
|
||||
active={linkActive}
|
||||
to={`/workspace/${workspace.id}/${pageId}`}
|
||||
to={`/${pageId}`}
|
||||
linkComponent={WorkbenchLink}
|
||||
collapsed={collapsible ? collapsed : undefined}
|
||||
onCollapsedChange={setCollapsed}
|
||||
postfix={
|
||||
|
||||
Reference in New Issue
Block a user