mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
feat(editor): header of edgeless embed doc (#12029)
Close [BS-3268](https://linear.app/affine-design/issue/BS-3268/edgeless-下,-dark-mode-embed的配色应该更加清晰) Close [BS-3067](https://linear.app/affine-design/issue/BS-3067/在embed上,添加split-view等相关的操作入口,基本接近page-block(见设计稿)) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced an interactive header for embedded synced documents with fold/unfold toggle, document opening, and multiple view options. - Added info and copy link buttons for embedded synced documents and notes to improve document management and sharing. - **Enhancements** - Updated styles for embedded synced document blocks and headers for better visual consistency. - Added new localization entries for header actions: "Fold", "Unfold", and "Open". - Disabled redundant open document actions in toolbars, centralizing controls in the header. - **Refactor** - Unified header button components for notes and embedded synced documents into reusable components. - Simplified header components by delegating button behaviors to shared components. - **Bug Fixes** - Fixed conditional rendering of editor content in embedded synced documents when folded. - **Chores** - Upgraded theme dependency version from "^1.1.12" to "^1.1.14" across multiple packages. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -39,7 +39,7 @@
|
||||
"@sentry/react": "^9.2.0",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.14",
|
||||
"cmdk": "^1.0.4",
|
||||
"embla-carousel-react": "^8.5.1",
|
||||
"input-otp": "^1.4.1",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@sentry/react": "^9.2.0",
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.14",
|
||||
"@vanilla-extract/css": "^1.17.0",
|
||||
"async-call-rpc": "^6.4.2",
|
||||
"next-themes": "^0.4.4",
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"@radix-ui/react-toast": "^1.2.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.5",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.1",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.14",
|
||||
"@vanilla-extract/dynamic": "^2.1.2",
|
||||
"bytes": "^3.1.2",
|
||||
"check-password-strength": "^3.0.0",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@sentry/react": "^9.2.0",
|
||||
"@toeverything/infra": "workspace:*",
|
||||
"@toeverything/pdf-viewer": "^0.1.1",
|
||||
"@toeverything/theme": "^1.1.12",
|
||||
"@toeverything/theme": "^1.1.14",
|
||||
"@vanilla-extract/dynamic": "^2.1.2",
|
||||
"animejs": "^4.0.0",
|
||||
"bytes": "^3.1.2",
|
||||
|
||||
@@ -58,11 +58,14 @@ import {
|
||||
import { patchDatabaseBlockConfigService } from '../extensions/database-block-config-service';
|
||||
import { patchDocModeService } from '../extensions/doc-mode-service';
|
||||
import { patchDocUrlExtensions } from '../extensions/doc-url';
|
||||
import {
|
||||
patchForEdgelessNoteConfig,
|
||||
patchForEmbedSyncedDocConfig,
|
||||
} from '../extensions/edgeless-block-header';
|
||||
import { EdgelessClipboardAIChatConfig } from '../extensions/edgeless-clipboard';
|
||||
import { patchForClipboardInElectron } from '../extensions/electron-clipboard';
|
||||
import { enableEditorExtension } from '../extensions/entry/enable-editor';
|
||||
import { enableMobileExtension } from '../extensions/entry/enable-mobile';
|
||||
import { patchForEdgelessNoteConfig } from '../extensions/note-config';
|
||||
import { patchNotificationService } from '../extensions/notification-service';
|
||||
import { patchOpenDocExtension } from '../extensions/open-doc';
|
||||
import { patchPeekViewService } from '../extensions/peek-view-service';
|
||||
@@ -162,6 +165,7 @@ const usePatchSpecs = (mode: DocMode) => {
|
||||
[
|
||||
patchReferenceRenderer(reactToLit, referenceRenderer),
|
||||
patchForEdgelessNoteConfig(framework, reactToLit, insidePeekView),
|
||||
patchForEmbedSyncedDocConfig(reactToLit),
|
||||
patchNotificationService(confirmModal),
|
||||
patchPeekViewService(peekViewService),
|
||||
patchOpenDocExtension(),
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { useSharingUrl } from '@affine/core/components/hooks/affine/use-share-url';
|
||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { type DocMode } from '@blocksuite/affine/model';
|
||||
import { InformationIcon, LinkIcon } from '@blocksuite/icons/rc';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import * as styles from './edgeless-block-header.css';
|
||||
|
||||
export const DocInfoButton = ({
|
||||
docId,
|
||||
trackFn,
|
||||
'data-testid': dataTestId,
|
||||
}: {
|
||||
docId: string;
|
||||
trackFn?: () => void;
|
||||
'data-testid'?: string;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const workspaceDialogService = useService(WorkspaceDialogService);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
trackFn?.();
|
||||
workspaceDialogService.open('doc-info', { docId });
|
||||
}, [docId, trackFn, workspaceDialogService]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
tooltip={t['com.affine.page-properties.page-info.view']()}
|
||||
data-testid={dataTestId}
|
||||
onClick={onClick}
|
||||
>
|
||||
<InformationIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const CopyLinkButton = ({
|
||||
pageId,
|
||||
blockId,
|
||||
mode,
|
||||
trackFn,
|
||||
'data-testid': dataTestId,
|
||||
}: {
|
||||
pageId: string;
|
||||
blockId?: string;
|
||||
mode?: DocMode;
|
||||
trackFn?: () => void;
|
||||
'data-testid'?: string;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const workspace = useService(WorkspaceService).workspace;
|
||||
|
||||
const { onClickCopyLink } = useSharingUrl({
|
||||
workspaceId: workspace.id,
|
||||
pageId,
|
||||
});
|
||||
|
||||
const copyLink = useCallback(() => {
|
||||
trackFn?.();
|
||||
onClickCopyLink(mode, blockId ? [blockId] : undefined);
|
||||
}, [blockId, mode, onClickCopyLink, trackFn]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
tooltip={t['com.affine.share-menu.copy']()}
|
||||
data-testid={dataTestId}
|
||||
onClick={copyLink}
|
||||
>
|
||||
<LinkIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
@@ -17,18 +17,50 @@ export const title = style({
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
flex: 1,
|
||||
color: cssVarV2('text/primary'),
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: 600,
|
||||
lineHeight: '30px',
|
||||
});
|
||||
|
||||
export const noteTitle = style([
|
||||
title,
|
||||
{
|
||||
color: cssVarV2('text/primary'),
|
||||
fontWeight: 600,
|
||||
lineHeight: '30px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const embedSyncedDocTitle = style([
|
||||
title,
|
||||
{
|
||||
color: cssVarV2('text/secondary'),
|
||||
fontWeight: 400,
|
||||
lineHeight: '24px',
|
||||
fontSize: '15px',
|
||||
selectors: {
|
||||
'&[data-collapsed="true"]': {
|
||||
color: cssVarV2('text/primary'),
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export const iconSize = 24;
|
||||
const buttonPadding = 4;
|
||||
export const button = style({
|
||||
padding: buttonPadding,
|
||||
pointerEvents: 'auto',
|
||||
color: cssVarV2('icon/transparentBlack'),
|
||||
borderRadius: 4,
|
||||
});
|
||||
|
||||
export const buttonText = style([
|
||||
embedSyncedDocTitle,
|
||||
{
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
fontWeight: 500,
|
||||
},
|
||||
]);
|
||||
|
||||
export const headerHeight = 2 * headerPadding + iconSize + 2 * buttonPadding;
|
||||
@@ -0,0 +1,265 @@
|
||||
import { Button, IconButton, Menu, MenuItem } from '@affine/component';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||
import { stopPropagation } from '@affine/core/utils';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { EmbedSyncedDocBlockComponent } from '@blocksuite/affine/blocks/embed';
|
||||
import { isPeekable, peek } from '@blocksuite/affine/components/peek';
|
||||
import { DisposableGroup } from '@blocksuite/affine/global/disposable';
|
||||
import { Bound } from '@blocksuite/affine/global/gfx';
|
||||
import type { EmbedSyncedDocModel } from '@blocksuite/affine-model';
|
||||
import {
|
||||
ArrowDownSmallIcon,
|
||||
CenterPeekIcon,
|
||||
ExpandFullIcon,
|
||||
LinkedPageIcon,
|
||||
OpenInNewIcon,
|
||||
SplitViewIcon,
|
||||
ToggleDownIcon,
|
||||
ToggleRightIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import type { BlockStdScope } from '@blocksuite/std';
|
||||
import { batch } from '@preact/signals-core';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { CopyLinkButton, DocInfoButton } from './common';
|
||||
import * as styles from './edgeless-block-header.css';
|
||||
|
||||
const ToggleButton = ({ model }: { model: EmbedSyncedDocModel }) => {
|
||||
const [isFolded, setIsFolded] = useState(model.isFolded);
|
||||
const t = useI18n();
|
||||
|
||||
useEffect(() => {
|
||||
const disposables = new DisposableGroup();
|
||||
disposables.add(
|
||||
model.props.preFoldHeight$.subscribe(value => setIsFolded(!!value))
|
||||
);
|
||||
// the height may be changed by dragging selected rect
|
||||
disposables.add(
|
||||
model.xywh$.subscribe(value => {
|
||||
const bound = Bound.deserialize(value);
|
||||
const preFoldHeight = model.props.preFoldHeight$.peek();
|
||||
if (
|
||||
bound.h !== styles.headerHeight &&
|
||||
preFoldHeight !== undefined &&
|
||||
bound.h !== preFoldHeight
|
||||
) {
|
||||
model.props.preFoldHeight$.value = 0;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return () => disposables.dispose();
|
||||
}, [model.props.preFoldHeight$, model.xywh$]);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
model.doc.captureSync();
|
||||
|
||||
batch(() => {
|
||||
const { x, y, w, h } = model.elementBound;
|
||||
if (model.isFolded) {
|
||||
model.props.xywh$.value = `[${x},${y},${w},${model.props.preFoldHeight$.peek() ?? 1}]`;
|
||||
model.props.preFoldHeight$.value = 0;
|
||||
} else {
|
||||
model.props.preFoldHeight$.value = h;
|
||||
model.props.xywh$.value = `[${x},${y},${w},${styles.headerHeight}]`;
|
||||
}
|
||||
});
|
||||
}, [model]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
onClick={toggle}
|
||||
tooltip={
|
||||
isFolded
|
||||
? t['com.affine.editor.edgeless-embed-synced-doc-header.unfold']()
|
||||
: t['com.affine.editor.edgeless-embed-synced-doc-header.fold']()
|
||||
}
|
||||
icon={isFolded ? <ToggleRightIcon /> : <ToggleDownIcon />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Title = ({ model }: { model: EmbedSyncedDocModel }) => {
|
||||
const docDisplayMetaService = useService(DocDisplayMetaService);
|
||||
const title = useLiveData(
|
||||
docDisplayMetaService.title$(model.props.pageId, {
|
||||
title: model.props.title,
|
||||
reference: true,
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.embedSyncedDocTitle}
|
||||
data-collapsed={!!model.props.preFoldHeight}
|
||||
data-testid="edgeless-embed-synced-doc-title"
|
||||
>
|
||||
<LinkedPageIcon />
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const EmbedSyncedDocInfoButton = ({
|
||||
model,
|
||||
}: {
|
||||
model: EmbedSyncedDocModel;
|
||||
}) => {
|
||||
return (
|
||||
<DocInfoButton
|
||||
docId={model.props.pageId}
|
||||
data-testid="edgeless-embed-synced-doc-info-button"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const EmbedSyncedDocCopyLinkButton = ({
|
||||
model,
|
||||
}: {
|
||||
model: EmbedSyncedDocModel;
|
||||
}) => {
|
||||
return (
|
||||
<CopyLinkButton
|
||||
pageId={model.props.pageId}
|
||||
data-testid="edgeless-embed-synced-doc-copy-link-button"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const OpenButton = ({ model }: { model: EmbedSyncedDocModel }) => {
|
||||
const t = useI18n();
|
||||
const workbench = useService(WorkbenchService).workbench;
|
||||
|
||||
const open = useCallback(() => {
|
||||
workbench.openDoc({
|
||||
docId: model.props.pageId,
|
||||
});
|
||||
}, [model.props.pageId, workbench]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={styles.button}
|
||||
variant="plain"
|
||||
size="custom"
|
||||
onClick={open}
|
||||
prefixStyle={{
|
||||
width: `${styles.iconSize}px`,
|
||||
height: `${styles.iconSize}px`,
|
||||
}}
|
||||
prefix={<ExpandFullIcon />}
|
||||
>
|
||||
<span className={styles.buttonText}>
|
||||
{t['com.affine.editor.edgeless-embed-synced-doc-header.open']()}
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const MoreMenu = ({
|
||||
model,
|
||||
std,
|
||||
}: {
|
||||
model: EmbedSyncedDocModel;
|
||||
std: BlockStdScope;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const workbench = useService(WorkbenchService).workbench;
|
||||
|
||||
const controls = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
type: 'open-in-active-view',
|
||||
label: t['com.affine.peek-view-controls.open-doc'](),
|
||||
icon: <ExpandFullIcon />,
|
||||
onClick: () => {
|
||||
workbench.openDoc(model.props.pageId);
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
type: 'open-in-center-peek',
|
||||
label: t['com.affine.peek-view-controls.open-doc-in-center-peek'](),
|
||||
icon: <CenterPeekIcon />,
|
||||
onClick: () => {
|
||||
const block = std.view.getBlock(model.id);
|
||||
if (
|
||||
!(
|
||||
block instanceof EmbedSyncedDocBlockComponent && isPeekable(block)
|
||||
)
|
||||
)
|
||||
return;
|
||||
peek(block);
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
type: 'open-in-split-view',
|
||||
label: t['com.affine.peek-view-controls.open-doc-in-split-view'](),
|
||||
icon: <SplitViewIcon />,
|
||||
onClick: () => {
|
||||
workbench.openDoc(model.props.pageId, { at: 'beside' });
|
||||
},
|
||||
enabled: BUILD_CONFIG.isElectron,
|
||||
},
|
||||
{
|
||||
type: 'open-in-new-tab',
|
||||
label: t['com.affine.peek-view-controls.open-doc-in-new-tab'](),
|
||||
icon: <OpenInNewIcon />,
|
||||
onClick: () => {
|
||||
workbench.openDoc(model.props.pageId, {
|
||||
at: 'new-tab',
|
||||
});
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
].filter(({ enabled }) => enabled);
|
||||
}, [model.id, model.props.pageId, std.view, t, workbench]);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
items={controls.map(option => (
|
||||
<MenuItem
|
||||
key={option.type}
|
||||
type="default"
|
||||
prefixIcon={option.icon}
|
||||
onClick={option.onClick}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
contentOptions={{
|
||||
align: 'center',
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
icon={<ArrowDownSmallIcon />}
|
||||
onDoubleClickCapture={stopPropagation}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export const EdgelessEmbedSyncedDocHeader = ({
|
||||
model,
|
||||
std,
|
||||
}: {
|
||||
model: EmbedSyncedDocModel;
|
||||
std: BlockStdScope;
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.header} onPointerDown={stopPropagation}>
|
||||
<ToggleButton model={model} />
|
||||
<Title model={model} />
|
||||
<OpenButton model={model} />
|
||||
<MoreMenu model={model} std={std} />
|
||||
<EmbedSyncedDocInfoButton model={model} />
|
||||
<EmbedSyncedDocCopyLinkButton model={model} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,7 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { useSharingUrl } from '@affine/core/components/hooks/affine/use-share-url';
|
||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import { DocService } from '@affine/core/modules/doc';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { useInsidePeekView } from '@affine/core/modules/peek-view/view/modal-container';
|
||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||
import { extractEmojiIcon } from '@affine/core/utils';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
@@ -12,16 +9,15 @@ import { Bound } from '@blocksuite/affine/global/gfx';
|
||||
import { type NoteBlockModel } from '@blocksuite/affine/model';
|
||||
import { GfxControllerIdentifier } from '@blocksuite/affine/std/gfx';
|
||||
import {
|
||||
InformationIcon,
|
||||
LinkedPageIcon,
|
||||
LinkIcon,
|
||||
ToggleDownIcon,
|
||||
ToggleRightIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService, useServices } from '@toeverything/infra';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import * as styles from './edgeless-note-header.css';
|
||||
import { CopyLinkButton, DocInfoButton } from './common';
|
||||
import * as styles from './edgeless-block-header.css';
|
||||
|
||||
const EdgelessNoteToggleButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
const t = useI18n();
|
||||
@@ -97,7 +93,7 @@ const EdgelessNoteToggleButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
>
|
||||
{collapsed ? <ToggleRightIcon /> : <ToggleDownIcon />}
|
||||
</IconButton>
|
||||
<div className={styles.title} data-testid="edgeless-note-title">
|
||||
<div className={styles.noteTitle} data-testid="edgeless-note-title">
|
||||
{collapsed && (
|
||||
<>
|
||||
{emoji && <span>{emoji}</span>}
|
||||
@@ -131,55 +127,33 @@ const ViewInPageButton = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const InfoButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
const t = useI18n();
|
||||
const workspaceDialogService = useService(WorkspaceDialogService);
|
||||
|
||||
const onOpenInfoModal = useCallback(() => {
|
||||
const PageBlockInfoButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
const trackFn = useCallback(() => {
|
||||
track.edgeless.pageBlock.headerToolbar.openDocInfo();
|
||||
workspaceDialogService.open('doc-info', { docId: note.doc.id });
|
||||
}, [note.doc.id, workspaceDialogService]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
tooltip={t['com.affine.page-properties.page-info.view']()}
|
||||
<DocInfoButton
|
||||
docId={note.doc.id}
|
||||
trackFn={trackFn}
|
||||
data-testid="edgeless-note-info-button"
|
||||
onClick={onOpenInfoModal}
|
||||
>
|
||||
<InformationIcon />
|
||||
</IconButton>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const LinkButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
const t = useI18n();
|
||||
const { workspaceService, editorService } = useServices({
|
||||
WorkspaceService,
|
||||
EditorService,
|
||||
});
|
||||
|
||||
const { onClickCopyLink } = useSharingUrl({
|
||||
workspaceId: workspaceService.workspace.id,
|
||||
pageId: editorService.editor.doc.id,
|
||||
});
|
||||
|
||||
const copyLink = useCallback(() => {
|
||||
const NoteCopyLinkButton = ({ note }: { note: NoteBlockModel }) => {
|
||||
const trackFn = useCallback(() => {
|
||||
track.edgeless.pageBlock.headerToolbar.copyBlockToLink();
|
||||
onClickCopyLink('edgeless', [note.id]);
|
||||
}, [note.id, onClickCopyLink]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.button}
|
||||
size={styles.iconSize}
|
||||
tooltip={t['com.affine.share-menu.copy']()}
|
||||
<CopyLinkButton
|
||||
pageId={note.doc.id}
|
||||
blockId={note.id}
|
||||
mode="edgeless"
|
||||
trackFn={trackFn}
|
||||
data-testid="edgeless-note-link-button"
|
||||
onClick={copyLink}
|
||||
>
|
||||
<LinkIcon />
|
||||
</IconButton>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -192,8 +166,8 @@ export const EdgelessNoteHeader = ({ note }: { note: NoteBlockModel }) => {
|
||||
<div className={styles.header} data-testid="edgeless-page-block-header">
|
||||
<EdgelessNoteToggleButton note={note} />
|
||||
<ViewInPageButton />
|
||||
{!insidePeekView && <InfoButton note={note} />}
|
||||
<LinkButton note={note} />
|
||||
{!insidePeekView && <PageBlockInfoButton note={note} />}
|
||||
<NoteCopyLinkButton note={note} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ElementOrFactory } from '@affine/component';
|
||||
import { JournalService } from '@affine/core/modules/journal';
|
||||
import { EmbedSyncedDocConfigExtension } from '@blocksuite/affine/blocks/embed';
|
||||
import { NoteConfigExtension } from '@blocksuite/affine/blocks/note';
|
||||
import { EDGELESS_BLOCK_CHILD_PADDING } from '@blocksuite/affine/blocks/root';
|
||||
import { Bound, Vec } from '@blocksuite/affine/global/gfx';
|
||||
@@ -12,6 +13,7 @@ import type { FrameworkProvider } from '@toeverything/infra';
|
||||
import { html, type TemplateResult } from 'lit';
|
||||
|
||||
import { BlocksuiteEditorJournalDocTitle } from '../../block-suite-editor/journal-doc-title';
|
||||
import { EdgelessEmbedSyncedDocHeader } from './edgeless-embed-synced-doc-header';
|
||||
import { EdgelessNoteHeader } from './edgeless-note-header';
|
||||
|
||||
export function patchForEdgelessNoteConfig(
|
||||
@@ -97,3 +99,12 @@ export function patchForEdgelessNoteConfig(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function patchForEmbedSyncedDocConfig(
|
||||
reactToLit: (element: ElementOrFactory) => TemplateResult
|
||||
) {
|
||||
return EmbedSyncedDocConfigExtension({
|
||||
edgelessHeader: ({ model, std }) =>
|
||||
reactToLit(<EdgelessEmbedSyncedDocHeader model={model} std={std} />),
|
||||
});
|
||||
}
|
||||
@@ -1094,7 +1094,19 @@ export const createCustomToolbarExtension = (
|
||||
actions: [
|
||||
embedSyncedDocToolbarConfig.actions,
|
||||
createOpenDocActionGroup(EmbedSyncedDocBlockComponent, settings),
|
||||
createEdgelessOpenDocActionGroup(EmbedSyncedDocBlockComponent),
|
||||
].flat(),
|
||||
},
|
||||
}),
|
||||
|
||||
ToolbarModuleExtension({
|
||||
id: BlockFlavourIdentifier('custom:affine:surface:embed-synced-doc'),
|
||||
config: {
|
||||
actions: [
|
||||
// the open actions are provided by the header of embed-edgeless-synced-doc-block
|
||||
{
|
||||
id: 'A.open-doc',
|
||||
when: () => false,
|
||||
},
|
||||
].flat(),
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{
|
||||
"ar": 99,
|
||||
"ar": 98,
|
||||
"ca": 4,
|
||||
"da": 4,
|
||||
"de": 99,
|
||||
"el-GR": 99,
|
||||
"de": 98,
|
||||
"el-GR": 98,
|
||||
"en": 100,
|
||||
"es-AR": 99,
|
||||
"es-CL": 100,
|
||||
"es": 99,
|
||||
"fa": 99,
|
||||
"fr": 99,
|
||||
"es": 98,
|
||||
"fa": 98,
|
||||
"fr": 98,
|
||||
"hi": 2,
|
||||
"it-IT": 99,
|
||||
"it-IT": 98,
|
||||
"it": 1,
|
||||
"ja": 99,
|
||||
"ja": 98,
|
||||
"ko": 57,
|
||||
"pl": 99,
|
||||
"pt-BR": 99,
|
||||
"ru": 99,
|
||||
"sv-SE": 99,
|
||||
"uk": 99,
|
||||
"pl": 98,
|
||||
"pt-BR": 98,
|
||||
"ru": 98,
|
||||
"sv-SE": 98,
|
||||
"uk": 98,
|
||||
"ur": 2,
|
||||
"zh-Hans": 99,
|
||||
"zh-Hant": 99
|
||||
"zh-Hans": 98,
|
||||
"zh-Hant": 98
|
||||
}
|
||||
|
||||
@@ -7143,6 +7143,18 @@ export function useAFFiNEI18N(): {
|
||||
* `View in page`
|
||||
*/
|
||||
["com.affine.editor.edgeless-note-header.view-in-page"](): string;
|
||||
/**
|
||||
* `Fold`
|
||||
*/
|
||||
["com.affine.editor.edgeless-embed-synced-doc-header.fold"](): string;
|
||||
/**
|
||||
* `Unfold`
|
||||
*/
|
||||
["com.affine.editor.edgeless-embed-synced-doc-header.unfold"](): string;
|
||||
/**
|
||||
* `Open`
|
||||
*/
|
||||
["com.affine.editor.edgeless-embed-synced-doc-header.open"](): string;
|
||||
/**
|
||||
* `Empower Your Team with Seamless Collaboration`
|
||||
*/
|
||||
|
||||
@@ -1775,6 +1775,9 @@
|
||||
"com.affine.editor.bi-directional-link-panel.hide": "Hide",
|
||||
"com.affine.editor.edgeless-note-header.fold-page-block": "Fold page block",
|
||||
"com.affine.editor.edgeless-note-header.view-in-page": "View in page",
|
||||
"com.affine.editor.edgeless-embed-synced-doc-header.fold": "Fold",
|
||||
"com.affine.editor.edgeless-embed-synced-doc-header.unfold": "Unfold",
|
||||
"com.affine.editor.edgeless-embed-synced-doc-header.open": "Open",
|
||||
"com.affine.upgrade-to-team-page.title": "Empower Your Team with Seamless Collaboration",
|
||||
"com.affine.upgrade-to-team-page.workspace-selector.placeholder": "Select an existing workspace or create a new one",
|
||||
"com.affine.upgrade-to-team-page.workspace-selector.create-workspace": "Create Workspace",
|
||||
|
||||
Reference in New Issue
Block a user