refactor(editor): surface-ref slash menu config extension (#10681)

This commit is contained in:
L-Sun
2025-03-07 09:05:03 +00:00
parent d37868d97a
commit 83b05aca4d
11 changed files with 293 additions and 293 deletions

View File

@@ -17,9 +17,10 @@
"@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-model": "workspace:*",
"@blocksuite/affine-shared": "workspace:*", "@blocksuite/affine-shared": "workspace:*",
"@blocksuite/affine-widget-slash-menu": "workspace:*",
"@blocksuite/block-std": "workspace:*", "@blocksuite/block-std": "workspace:*",
"@blocksuite/global": "workspace:*", "@blocksuite/global": "workspace:*",
"@blocksuite/icons": "^2.2.3", "@blocksuite/icons": "^2.2.4",
"@blocksuite/inline": "workspace:*", "@blocksuite/inline": "workspace:*",
"@blocksuite/store": "workspace:*", "@blocksuite/store": "workspace:*",
"@lit/context": "^1.1.2", "@lit/context": "^1.1.2",

View File

@@ -0,0 +1,75 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import type { FrameBlockModel } from '@blocksuite/affine-model';
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
import {
type SlashMenuActionItem,
type SlashMenuConfig,
SlashMenuConfigExtension,
} from '@blocksuite/affine-widget-slash-menu';
import { FrameIcon, GroupingIcon } from '@blocksuite/icons/lit';
import { insertSurfaceRefBlockCommand } from '../commands';
import { EdgelessTooltip } from './tooltips';
const surfaceRefSlashMenuConfig: SlashMenuConfig = {
items: ({ std }) => {
let index = 0;
const surfaceModel = getSurfaceBlock(std.store);
if (!surfaceModel) return [];
const frameModels = std.store
.getBlocksByFlavour('affine:frame')
.map(block => block.model as FrameBlockModel);
const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({
name: 'Frame: ' + frameModel.title,
icon: FrameIcon(),
group: `5_Document Group & Frame@${index++}`,
tooltip: {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: frameModel.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
const groupElements = surfaceModel.getElementsByType('group');
const groupItems = groupElements.map<SlashMenuActionItem>(group => ({
name: 'Group: ' + group.title.toString(),
icon: GroupingIcon(),
group: `5_Document Group & Frame@${index++}`,
tooltip: {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: group.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
return [...frameItems, ...groupItems];
},
};
export const SurfaceRefSlashMenuConfigExtension = SlashMenuConfigExtension(
'affine:surface-ref',
surfaceRefSlashMenuConfig
);

View File

@@ -6,6 +6,8 @@ import {
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
import { SurfaceRefSlashMenuConfigExtension } from './configs/slash-menu';
export const surfaceRefToolbarWidget = WidgetViewExtension( export const surfaceRefToolbarWidget = WidgetViewExtension(
'affine:surface-ref', 'affine:surface-ref',
'surfaceToolbar', 'surfaceToolbar',
@@ -16,6 +18,7 @@ export const PageSurfaceRefBlockSpec: ExtensionType[] = [
FlavourExtension('affine:surface-ref'), FlavourExtension('affine:surface-ref'),
BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`), BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`),
surfaceRefToolbarWidget, surfaceRefToolbarWidget,
SurfaceRefSlashMenuConfigExtension,
]; ];
export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [ export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [
@@ -24,4 +27,5 @@ export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [
'affine:surface-ref', 'affine:surface-ref',
literal`affine-edgeless-surface-ref` literal`affine-edgeless-surface-ref`
), ),
SurfaceRefSlashMenuConfigExtension,
]; ];

View File

@@ -11,6 +11,7 @@
{ "path": "../components" }, { "path": "../components" },
{ "path": "../model" }, { "path": "../model" },
{ "path": "../shared" }, { "path": "../shared" },
{ "path": "../widget-slash-menu" },
{ "path": "../../framework/block-std" }, { "path": "../../framework/block-std" },
{ "path": "../../framework/global" }, { "path": "../../framework/global" },
{ "path": "../../framework/inline" }, { "path": "../../framework/inline" },

View File

@@ -13,8 +13,6 @@
"author": "toeverything", "author": "toeverything",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@blocksuite/affine-block-surface": "workspace:*",
"@blocksuite/affine-block-surface-ref": "workspace:*",
"@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-components": "workspace:*",
"@blocksuite/affine-rich-text": "workspace:*", "@blocksuite/affine-rich-text": "workspace:*",
"@blocksuite/affine-shared": "workspace:*", "@blocksuite/affine-shared": "workspace:*",

View File

@@ -1,15 +1,9 @@
import { getSurfaceBlock } from '@blocksuite/affine-block-surface';
import { insertSurfaceRefBlockCommand } from '@blocksuite/affine-block-surface-ref';
import { toast } from '@blocksuite/affine-components/toast'; import { toast } from '@blocksuite/affine-components/toast';
import type { import type { ParagraphBlockModel } from '@blocksuite/affine-model';
FrameBlockModel,
ParagraphBlockModel,
} from '@blocksuite/affine-model';
import { import {
getInlineEditorByModel, getInlineEditorByModel,
insertContent, insertContent,
} from '@blocksuite/affine-rich-text'; } from '@blocksuite/affine-rich-text';
import { getSelectedModelsCommand } from '@blocksuite/affine-shared/commands';
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts'; import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
import { createDefaultDoc } from '@blocksuite/affine-shared/utils'; import { createDefaultDoc } from '@blocksuite/affine-shared/utils';
import { import {
@@ -18,8 +12,6 @@ import {
CopyIcon, CopyIcon,
DeleteIcon, DeleteIcon,
DualLinkIcon, DualLinkIcon,
FrameIcon,
GroupingIcon,
LinkedPageIcon, LinkedPageIcon,
NowIcon, NowIcon,
PlusIcon, PlusIcon,
@@ -31,23 +23,27 @@ import type { DeltaInsert } from '@blocksuite/inline';
import { Slice, Text } from '@blocksuite/store'; import { Slice, Text } from '@blocksuite/store';
import { slashMenuToolTips } from './tooltips'; import { slashMenuToolTips } from './tooltips';
import type { SlashMenuActionItem, SlashMenuConfig } from './types'; import type { SlashMenuConfig } from './types';
import { formatDate, formatTime } from './utils'; import { formatDate, formatTime } from './utils';
// TODO(@L-Sun): This counter temporarily added variables for refactoring.
let index = 0;
export const defaultSlashMenuConfig: SlashMenuConfig = { export const defaultSlashMenuConfig: SlashMenuConfig = {
disableWhen: ({ model }) => { disableWhen: ({ model }) => {
return model.flavour === 'affine:code'; return model.flavour === 'affine:code';
}, },
items: ({ std, model }) => [ items: () => {
const now = new Date();
const tomorrow = new Date();
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
tomorrow.setDate(tomorrow.getDate() + 1);
return [
{ {
name: 'New Doc', name: 'New Doc',
description: 'Start a new document.', description: 'Start a new document.',
icon: PlusIcon(), icon: PlusIcon(),
tooltip: slashMenuToolTips['New Doc'], tooltip: slashMenuToolTips['New Doc'],
group: `3_Page@${index++}`, group: '3_Page@0',
when: ({ model }) => when: ({ model }) =>
model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'), model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'),
action: ({ std, model }) => { action: ({ std, model }) => {
@@ -66,7 +62,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: LinkedPageIcon(), icon: LinkedPageIcon(),
tooltip: slashMenuToolTips['Linked Doc'], tooltip: slashMenuToolTips['Linked Doc'],
searchAlias: ['dual link'], searchAlias: ['dual link'],
group: `3_Page@${index++}`, group: '3_Page@1',
when: ({ std, model }) => { when: ({ std, model }) => {
const root = model.doc.root; const root = model.doc.root;
if (!root) return false; if (!root) return false;
@@ -76,7 +72,9 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
); );
if (!linkedDocWidget) return false; if (!linkedDocWidget) return false;
return model.doc.schema.flavourSchemaMap.has('affine:embed-linked-doc'); return model.doc.schema.flavourSchemaMap.has(
'affine:embed-linked-doc'
);
}, },
action: ({ model, std }) => { action: ({ model, std }) => {
const root = model.doc.root; const root = model.doc.root;
@@ -101,75 +99,12 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
}); });
}, },
}, },
// ---------------------------------------------------------
...(() => {
const { host } = std;
const surfaceModel = getSurfaceBlock(host.doc);
if (!surfaceModel) return [];
const parent = host.doc.getParent(model);
if (!parent) return [];
const frameModels = host.doc
.getBlocksByFlavour('affine:frame')
.map(block => block.model as FrameBlockModel);
const frameItems = frameModels.map<SlashMenuActionItem>(frameModel => ({
name: 'Frame: ' + frameModel.title,
icon: FrameIcon(),
group: `5_Document Group & Frame@${index++}`,
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: frameModel.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
const groupElements = surfaceModel.getElementsByType('group');
const groupItems = groupElements.map<SlashMenuActionItem>(group => ({
name: 'Group: ' + group.title.toString(),
icon: GroupingIcon(),
group: `5_Document Group & Frame@${index++}`,
action: ({ std }) => {
std.command
.chain()
.pipe(getSelectedModelsCommand)
.pipe(insertSurfaceRefBlockCommand, {
reference: group.id,
place: 'after',
removeEmptyLine: true,
})
.run();
},
}));
return [...frameItems, ...groupItems];
})(),
// ---------------------------------------------------------
...((): SlashMenuActionItem[] => {
const now = new Date();
const tomorrow = new Date();
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
tomorrow.setDate(tomorrow.getDate() + 1);
return [
{ {
name: 'Today', name: 'Today',
icon: TodayIcon(), icon: TodayIcon(),
tooltip: slashMenuToolTips['Today'], tooltip: slashMenuToolTips['Today'],
description: formatDate(now), description: formatDate(now),
group: `6_Date@${index++}`, group: '6_Date@0',
action: ({ std, model }) => { action: ({ std, model }) => {
insertContent(std.host, model, formatDate(now)); insertContent(std.host, model, formatDate(now));
}, },
@@ -179,7 +114,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: TomorrowIcon(), icon: TomorrowIcon(),
tooltip: slashMenuToolTips['Tomorrow'], tooltip: slashMenuToolTips['Tomorrow'],
description: formatDate(tomorrow), description: formatDate(tomorrow),
group: `6_Date@${index++}`, group: '6_Date@1',
action: ({ std, model }) => { action: ({ std, model }) => {
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
@@ -191,7 +126,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: YesterdayIcon(), icon: YesterdayIcon(),
tooltip: slashMenuToolTips['Yesterday'], tooltip: slashMenuToolTips['Yesterday'],
description: formatDate(yesterday), description: formatDate(yesterday),
group: `6_Date@${index++}`, group: '6_Date@2',
action: ({ std, model }) => { action: ({ std, model }) => {
const yesterday = new Date(); const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
@@ -203,22 +138,17 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
icon: NowIcon(), icon: NowIcon(),
tooltip: slashMenuToolTips['Now'], tooltip: slashMenuToolTips['Now'],
description: formatTime(now), description: formatTime(now),
group: `6_Date@${index++}`, group: '6_Date@3',
action: ({ std, model }) => { action: ({ std, model }) => {
insertContent(std.host, model, formatTime(now)); insertContent(std.host, model, formatTime(now));
}, },
}, },
];
})(),
// ---------------------------------------------------------
// { groupName: 'Actions' },
{ {
name: 'Move Up', name: 'Move Up',
description: 'Shift this line up.', description: 'Shift this line up.',
icon: ArrowUpBigIcon(), icon: ArrowUpBigIcon(),
tooltip: slashMenuToolTips['Move Up'], tooltip: slashMenuToolTips['Move Up'],
group: `8_Actions@${index++}`, group: '8_Actions@0',
action: ({ std, model }) => { action: ({ std, model }) => {
const { host } = std; const { host } = std;
const previousSiblingModel = host.doc.getPrev(model); const previousSiblingModel = host.doc.getPrev(model);
@@ -235,7 +165,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Shift this line down.', description: 'Shift this line down.',
icon: ArrowDownBigIcon(), icon: ArrowDownBigIcon(),
tooltip: slashMenuToolTips['Move Down'], tooltip: slashMenuToolTips['Move Down'],
group: `8_Actions@${index++}`, group: '8_Actions@1',
action: ({ std, model }) => { action: ({ std, model }) => {
const { host } = std; const { host } = std;
const nextSiblingModel = host.doc.getNext(model); const nextSiblingModel = host.doc.getNext(model);
@@ -252,7 +182,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Copy this line to clipboard.', description: 'Copy this line to clipboard.',
icon: CopyIcon(), icon: CopyIcon(),
tooltip: slashMenuToolTips['Copy'], tooltip: slashMenuToolTips['Copy'],
group: `8_Actions@${index++}`, group: '8_Actions@2',
action: ({ std, model }) => { action: ({ std, model }) => {
const slice = Slice.fromModels(std.store, [model]); const slice = Slice.fromModels(std.store, [model]);
@@ -271,7 +201,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
description: 'Create a duplicate of this line.', description: 'Create a duplicate of this line.',
icon: DualLinkIcon(), icon: DualLinkIcon(),
tooltip: slashMenuToolTips['Copy'], tooltip: slashMenuToolTips['Copy'],
group: `8_Actions@${index++}`, group: '8_Actions@3',
action: ({ std, model }) => { action: ({ std, model }) => {
if (!model.text || !(model.text instanceof Text)) { if (!model.text || !(model.text instanceof Text)) {
console.error("Can't duplicate a block without text"); console.error("Can't duplicate a block without text");
@@ -310,10 +240,11 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
searchAlias: ['remove'], searchAlias: ['remove'],
icon: DeleteIcon(), icon: DeleteIcon(),
tooltip: slashMenuToolTips['Delete'], tooltip: slashMenuToolTips['Delete'],
group: `8_Actions@${index++}`, group: '8_Actions@4',
action: ({ std, model }) => { action: ({ std, model }) => {
std.host.doc.deleteBlock(model); std.host.doc.deleteBlock(model);
}, },
}, },
], ];
},
}; };

View File

@@ -1,7 +1,6 @@
import type { SlashMenuTooltip } from '../types'; import type { SlashMenuTooltip } from '../types';
import { CopyTooltip } from './copy'; import { CopyTooltip } from './copy';
import { DeleteTooltip } from './delete'; import { DeleteTooltip } from './delete';
import { EdgelessTooltip } from './edgeless';
import { LinkDocTooltip } from './link-doc'; import { LinkDocTooltip } from './link-doc';
import { MoveDownTooltip } from './move-down'; import { MoveDownTooltip } from './move-down';
import { MoveUpTooltip } from './move-up'; import { MoveUpTooltip } from './move-up';
@@ -61,9 +60,4 @@ export const slashMenuToolTips: Record<string, SlashMenuTooltip> = {
figure: DeleteTooltip, figure: DeleteTooltip,
caption: 'Delete', caption: 'Delete',
}, },
'Group & Frame': {
figure: EdgelessTooltip,
caption: 'Edgeless',
},
}; };

View File

@@ -7,8 +7,6 @@
}, },
"include": ["./src"], "include": ["./src"],
"references": [ "references": [
{ "path": "../block-surface" },
{ "path": "../block-surface-ref" },
{ "path": "../components" }, { "path": "../components" },
{ "path": "../rich-text" }, { "path": "../rich-text" },
{ "path": "../shared" }, { "path": "../shared" },

View File

@@ -301,6 +301,7 @@ export const PackageList = [
'blocksuite/affine/components', 'blocksuite/affine/components',
'blocksuite/affine/model', 'blocksuite/affine/model',
'blocksuite/affine/shared', 'blocksuite/affine/shared',
'blocksuite/affine/widget-slash-menu',
'blocksuite/framework/block-std', 'blocksuite/framework/block-std',
'blocksuite/framework/global', 'blocksuite/framework/global',
'blocksuite/framework/inline', 'blocksuite/framework/inline',
@@ -495,8 +496,6 @@ export const PackageList = [
location: 'blocksuite/affine/widget-slash-menu', location: 'blocksuite/affine/widget-slash-menu',
name: '@blocksuite/affine-widget-slash-menu', name: '@blocksuite/affine-widget-slash-menu',
workspaceDependencies: [ workspaceDependencies: [
'blocksuite/affine/block-surface',
'blocksuite/affine/block-surface-ref',
'blocksuite/affine/components', 'blocksuite/affine/components',
'blocksuite/affine/rich-text', 'blocksuite/affine/rich-text',
'blocksuite/affine/shared', 'blocksuite/affine/shared',

View File

@@ -2665,9 +2665,10 @@ __metadata:
"@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-model": "workspace:*" "@blocksuite/affine-model": "workspace:*"
"@blocksuite/affine-shared": "workspace:*" "@blocksuite/affine-shared": "workspace:*"
"@blocksuite/affine-widget-slash-menu": "workspace:*"
"@blocksuite/block-std": "workspace:*" "@blocksuite/block-std": "workspace:*"
"@blocksuite/global": "workspace:*" "@blocksuite/global": "workspace:*"
"@blocksuite/icons": "npm:^2.2.3" "@blocksuite/icons": "npm:^2.2.4"
"@blocksuite/inline": "workspace:*" "@blocksuite/inline": "workspace:*"
"@blocksuite/store": "workspace:*" "@blocksuite/store": "workspace:*"
"@lit/context": "npm:^1.1.2" "@lit/context": "npm:^1.1.2"
@@ -3040,8 +3041,6 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu" resolution: "@blocksuite/affine-widget-slash-menu@workspace:blocksuite/affine/widget-slash-menu"
dependencies: dependencies:
"@blocksuite/affine-block-surface": "workspace:*"
"@blocksuite/affine-block-surface-ref": "workspace:*"
"@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-components": "workspace:*"
"@blocksuite/affine-rich-text": "workspace:*" "@blocksuite/affine-rich-text": "workspace:*"
"@blocksuite/affine-shared": "workspace:*" "@blocksuite/affine-shared": "workspace:*"