From 251d1d87820034a08d6eb32fc9d0965b067dc960 Mon Sep 17 00:00:00 2001 From: fundon Date: Wed, 19 Mar 2025 00:52:22 +0000 Subject: [PATCH] refactor(editor): edgeless attacment toolbar config extension (#10710) --- .../block-attachment/src/attachment-spec.ts | 16 +- .../block-attachment/src/configs/toolbar.ts | 175 ++++++++++++++---- .../block-bookmark/src/configs/toolbar.ts | 8 +- .../blocks/block-embed/src/configs/toolbar.ts | 9 +- .../src/embed-html-block/configs/toolbar.ts | 5 +- .../embed-linked-doc-block/configs/toolbar.ts | 8 +- .../embed-synced-doc-block/configs/toolbar.ts | 5 +- .../edgeless/configs/toolbar/attachment.ts | 11 -- .../src/edgeless/configs/toolbar/index.ts | 6 - .../card-style-dropdown-menu/dropdown-menu.ts | 14 +- .../src/view-dropdown-menu/dropdown-menu.ts | 7 +- .../nodes/link-node/configs/toolbar.ts | 4 +- .../nodes/reference-node/configs/toolbar.ts | 4 +- .../src/services/toolbar-service/context.ts | 12 +- .../widgets/widget-toolbar/package.json | 1 + .../widgets/widget-toolbar/src/toolbar.ts | 22 ++- .../widgets/widget-toolbar/tsconfig.json | 1 + tools/utils/src/workspace.gen.ts | 1 + yarn.lock | 1 + 19 files changed, 196 insertions(+), 114 deletions(-) delete mode 100644 blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/attachment.ts diff --git a/blocksuite/affine/blocks/block-attachment/src/attachment-spec.ts b/blocksuite/affine/blocks/block-attachment/src/attachment-spec.ts index 9cbeb5333e..961f26d130 100644 --- a/blocksuite/affine/blocks/block-attachment/src/attachment-spec.ts +++ b/blocksuite/affine/blocks/block-attachment/src/attachment-spec.ts @@ -1,18 +1,13 @@ import { AttachmentBlockSchema } from '@blocksuite/affine-model'; -import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services'; import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu'; -import { - BlockFlavourIdentifier, - BlockViewExtension, - FlavourExtension, -} from '@blocksuite/block-std'; +import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; import { AttachmentBlockNotionHtmlAdapterExtension } from './adapters/notion-html.js'; import { AttachmentDropOption } from './attachment-service.js'; import { attachmentSlashMenuConfig } from './configs/slash-menu.js'; -import { builtinToolbarConfig } from './configs/toolbar'; +import { createBuiltinToolbarConfigExtension } from './configs/toolbar'; import { AttachmentEmbedConfigExtension, AttachmentEmbedService, @@ -31,9 +26,6 @@ export const AttachmentBlockSpec: ExtensionType[] = [ AttachmentEmbedConfigExtension(), AttachmentEmbedService, AttachmentBlockNotionHtmlAdapterExtension, - ToolbarModuleExtension({ - id: BlockFlavourIdentifier(flavour), - config: builtinToolbarConfig, - }), + createBuiltinToolbarConfigExtension(flavour), SlashMenuConfigExtension(flavour, attachmentSlashMenuConfig), -]; +].flat(); diff --git a/blocksuite/affine/blocks/block-attachment/src/configs/toolbar.ts b/blocksuite/affine/blocks/block-attachment/src/configs/toolbar.ts index a412a5bea6..55eb883829 100644 --- a/blocksuite/affine/blocks/block-attachment/src/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-attachment/src/configs/toolbar.ts @@ -2,6 +2,7 @@ import { createLitPortal } from '@blocksuite/affine-components/portal'; import { AttachmentBlockModel, defaultAttachmentProps, + type EmbedCardStyle, } from '@blocksuite/affine-model'; import { EMBED_CARD_HEIGHT, @@ -12,7 +13,9 @@ import { type ToolbarAction, type ToolbarActionGroup, type ToolbarModuleConfig, + ToolbarModuleExtension, } from '@blocksuite/affine-shared/services'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import { Bound } from '@blocksuite/global/gfx'; import { CaptionIcon, @@ -23,6 +26,7 @@ import { EditIcon, ResetIcon, } from '@blocksuite/icons/lit'; +import type { ExtensionType } from '@blocksuite/store'; import { flip, offset } from '@floating-ui/dom'; import { computed } from '@preact/signals-core'; import { html } from 'lit'; @@ -34,9 +38,6 @@ import { AttachmentEmbedProvider } from '../embed'; import { cloneAttachmentProperties } from '../utils'; const trackBaseProps = { - segment: 'doc', - page: 'doc editor', - module: 'toolbar', category: 'attachment', type: 'card view', }; @@ -54,14 +55,14 @@ export const attachmentViewDropdownMenu = { const style = defaultAttachmentProps.style!; const width = EMBED_CARD_WIDTH[style]; const height = EMBED_CARD_HEIGHT[style]; - const bound = Bound.deserialize(model.xywh); - bound.w = width; - bound.h = height; + const bounds = Bound.deserialize(model.xywh); + bounds.w = width; + bounds.h = height; ctx.store.updateBlock(model, { style, embed: false, - xywh: bound.serialize(), + xywh: bounds.serialize(), }); }, }, @@ -72,9 +73,11 @@ export const attachmentViewDropdownMenu = { const model = ctx.getCurrentModelByType(AttachmentBlockModel); if (!model) return; - // Clears - ctx.reset(); - ctx.select('note'); + if (!ctx.hasSelectedSurfaceModels) { + // Clears + ctx.reset(); + ctx.select('note'); + } ctx.std.get(AttachmentEmbedProvider).convertTo(model); @@ -101,7 +104,8 @@ export const attachmentViewDropdownMenu = { return embed ? embedAction.label : cardAction.label; }); - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { + e.stopPropagation(); const opened = e.detail; if (!opened) return; @@ -114,16 +118,41 @@ export const attachmentViewDropdownMenu = { return html`${keyed( model, html`` )}`; }, -} satisfies ToolbarActionGroup; +} as const satisfies ToolbarActionGroup; -export const builtinToolbarConfig = { +const downloadAction = { + id: 'c.download', + tooltip: 'Download', + icon: DownloadIcon(), + run(ctx) { + const block = ctx.getCurrentBlockByType(AttachmentBlockComponent); + block?.download(); + }, +} as const satisfies ToolbarAction; + +const captionAction = { + id: 'd.caption', + tooltip: 'Caption', + icon: CaptionIcon(), + run(ctx) { + const block = ctx.getCurrentBlockByType(AttachmentBlockComponent); + block?.captionEditor?.show(); + + ctx.track('OpenedCaptionEditor', { + ...trackBaseProps, + control: 'add caption', + }); + }, +} as const satisfies ToolbarAction; + +const builtinToolbarConfig = { actions: [ { id: 'a.rename', @@ -162,29 +191,8 @@ export const builtinToolbarConfig = { }, }, attachmentViewDropdownMenu, - { - id: 'c.download', - tooltip: 'Download', - icon: DownloadIcon(), - run(ctx) { - const block = ctx.getCurrentBlockByType(AttachmentBlockComponent); - block?.download(); - }, - }, - { - id: 'd.caption', - tooltip: 'Caption', - icon: CaptionIcon(), - run(ctx) { - const block = ctx.getCurrentBlockByType(AttachmentBlockComponent); - block?.captionEditor?.show(); - - ctx.track('OpenedCaptionEditor', { - ...trackBaseProps, - control: 'add caption', - }); - }, - }, + downloadAction, + captionAction, { placement: ActionPlacement.More, id: 'a.clipboard', @@ -247,3 +255,96 @@ export const builtinToolbarConfig = { }, ], } as const satisfies ToolbarModuleConfig; + +const builtinSurfaceToolbarConfig = { + actions: [ + attachmentViewDropdownMenu, + { + id: 'c.style', + actions: [ + { + id: 'horizontalThin', + label: 'Horizontal style', + }, + { + id: 'cubeThick', + label: 'Vertical style', + }, + ], + content(ctx) { + const model = ctx.getCurrentModelByType(AttachmentBlockModel); + if (!model) return null; + + const actions = this.actions.map(action => ({ + ...action, + run: ({ store }) => { + const style = action.id as EmbedCardStyle; + const bounds = Bound.deserialize(model.xywh); + bounds.w = EMBED_CARD_WIDTH[style]; + bounds.h = EMBED_CARD_HEIGHT[style]; + const xywh = bounds.serialize(); + + store.updateBlock(model, { style, xywh }); + + ctx.track('SelectedCardStyle', { + ...trackBaseProps, + page: 'whiteboard editor', + control: 'select card style', + type: style, + }); + }, + })) satisfies ToolbarAction[]; + const style$ = model.props.style$; + const onToggle = (e: CustomEvent) => { + e.stopPropagation(); + const opened = e.detail; + if (!opened) return; + + ctx.track('OpenedCardStyleSelector', { + ...trackBaseProps, + page: 'whiteboard editor', + control: 'switch card style', + }); + }; + + return html`${keyed( + model, + html`` + )}`; + }, + } satisfies ToolbarActionGroup, + { + ...downloadAction, + id: 'd.download', + }, + { + ...captionAction, + id: 'e.caption', + }, + ], + + when: ctx => ctx.getSurfaceModelsByType(AttachmentBlockModel).length === 1, +} as const satisfies ToolbarModuleConfig; + +export const createBuiltinToolbarConfigExtension = ( + flavour: string +): ExtensionType[] => { + const name = flavour.split(':').pop(); + + return [ + ToolbarModuleExtension({ + id: BlockFlavourIdentifier(flavour), + config: builtinToolbarConfig, + }), + + ToolbarModuleExtension({ + id: BlockFlavourIdentifier(`affine:surface:${name}`), + config: builtinSurfaceToolbarConfig, + }), + ]; +}; diff --git a/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts b/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts index 94710baf82..d616b7a23c 100644 --- a/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts @@ -181,7 +181,7 @@ export const builtinToolbarConfig = { if (!model) return null; const actions = this.actions.map(action => ({ ...action })); - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -194,9 +194,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; @@ -231,7 +231,7 @@ export const builtinToolbarConfig = { }, })) satisfies ToolbarAction[]; - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -244,9 +244,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; diff --git a/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts b/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts index c08e671fb5..4292519f32 100644 --- a/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-embed/src/configs/toolbar.ts @@ -242,8 +242,7 @@ export function createBuiltinToolbarConfigForExternal( const viewType$ = signal( `${viewType === 'card' ? 'Card' : 'Embed'} view` ); - - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -256,9 +255,9 @@ export function createBuiltinToolbarConfigForExternal( return html`${keyed( model, html`` )}`; @@ -293,7 +292,7 @@ export function createBuiltinToolbarConfigForExternal( }, })) satisfies ToolbarAction[]; - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -306,9 +305,9 @@ export function createBuiltinToolbarConfigForExternal( return html`${keyed( model, html`` )}`; diff --git a/blocksuite/affine/blocks/block-embed/src/embed-html-block/configs/toolbar.ts b/blocksuite/affine/blocks/block-embed/src/embed-html-block/configs/toolbar.ts index d6114d29b3..352341afdd 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-html-block/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-html-block/configs/toolbar.ts @@ -67,8 +67,7 @@ export const builtinToolbarConfig = { }); }, })); - - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -81,9 +80,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; diff --git a/blocksuite/affine/blocks/block-embed/src/embed-linked-doc-block/configs/toolbar.ts b/blocksuite/affine/blocks/block-embed/src/embed-linked-doc-block/configs/toolbar.ts index 3aa1fc52dc..47a76751de 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-linked-doc-block/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-linked-doc-block/configs/toolbar.ts @@ -119,7 +119,7 @@ export const builtinToolbarConfig = { if (!model) return null; const actions = this.actions.map(action => ({ ...action })); - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -132,9 +132,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; @@ -168,7 +168,7 @@ export const builtinToolbarConfig = { }); }, })) satisfies ToolbarAction[]; - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -181,9 +181,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; diff --git a/blocksuite/affine/blocks/block-embed/src/embed-synced-doc-block/configs/toolbar.ts b/blocksuite/affine/blocks/block-embed/src/embed-synced-doc-block/configs/toolbar.ts index fe1251e458..7ab2a51be0 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-synced-doc-block/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-synced-doc-block/configs/toolbar.ts @@ -155,8 +155,7 @@ export const builtinToolbarConfig = { if (!model) return null; const actions = this.actions.map(action => ({ ...action })); - - const toggle = (e: CustomEvent) => { + const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; @@ -169,9 +168,9 @@ export const builtinToolbarConfig = { return html`${keyed( model, html`` )}`; diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/attachment.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/attachment.ts deleted file mode 100644 index 6f23da1c5a..0000000000 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/attachment.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { ToolbarModuleConfig } from '@blocksuite/affine-shared/services'; - -export const builtinAttachmentToolbarConfig = { - actions: [ - { - id: 'a.test', - label: 'Attachment', - run() {}, - }, - ], -} as const satisfies ToolbarModuleConfig; diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts index 453cdeede8..07e72b4197 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts +++ b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts @@ -2,7 +2,6 @@ import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services'; import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; -import { builtinAttachmentToolbarConfig } from './attachment'; import { builtinBookmarkToolbarConfig } from './bookmark'; import { builtinBrushToolbarConfig } from './brush'; import { builtinConnectorToolbarConfig } from './connector'; @@ -17,11 +16,6 @@ import { builtinShapeToolbarConfig } from './shape'; import { builtinTextToolbarConfig } from './text'; export const EdgelessElementToolbarExtension: ExtensionType[] = [ - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:attachment'), - config: builtinAttachmentToolbarConfig, - }), - ToolbarModuleExtension({ id: BlockFlavourIdentifier('affine:surface:bookmark'), config: builtinBookmarkToolbarConfig, diff --git a/blocksuite/affine/components/src/card-style-dropdown-menu/dropdown-menu.ts b/blocksuite/affine/components/src/card-style-dropdown-menu/dropdown-menu.ts index 35b3d5986b..7be5297340 100644 --- a/blocksuite/affine/components/src/card-style-dropdown-menu/dropdown-menu.ts +++ b/blocksuite/affine/components/src/card-style-dropdown-menu/dropdown-menu.ts @@ -21,8 +21,10 @@ import { ifDefined } from 'lit-html/directives/if-defined.js'; import { repeat } from 'lit-html/directives/repeat.js'; import { + EmbedCardDarkCubeIcon, EmbedCardDarkHorizontalIcon, EmbedCardDarkListIcon, + EmbedCardLightCubeIcon, EmbedCardLightHorizontalIcon, EmbedCardLightListIcon, } from '../icons'; @@ -30,11 +32,15 @@ import { const cardStyleMap: Record> = { light: { horizontal: EmbedCardLightHorizontalIcon, + horizontalThin: EmbedCardLightListIcon, list: EmbedCardLightListIcon, + cubeThick: EmbedCardLightCubeIcon, }, dark: { horizontal: EmbedCardDarkHorizontalIcon, + horizontalThin: EmbedCardDarkListIcon, list: EmbedCardDarkListIcon, + cubeThick: EmbedCardDarkCubeIcon, }, }; @@ -53,9 +59,6 @@ export class CardStyleDropdownMenu extends SignalWatcher(ShadowlessElement) { @property({ attribute: false }) accessor style$!: Signal | ReadonlySignal; - @property({ attribute: false }) - accessor toggle: ((e: CustomEvent) => void) | undefined; - icons$ = computed( () => cardStyleMap[this.context.themeProvider.theme$.value] ); @@ -64,14 +67,12 @@ export class CardStyleDropdownMenu extends SignalWatcher(ShadowlessElement) { const { actions, context, - toggle, style$: { value: style }, icons$: { value: icons }, } = this; return html` action.id, ({ id, label, icon, disabled, run }) => html` run?.(context)} diff --git a/blocksuite/affine/components/src/view-dropdown-menu/dropdown-menu.ts b/blocksuite/affine/components/src/view-dropdown-menu/dropdown-menu.ts index 01352826a4..38115ee909 100644 --- a/blocksuite/affine/components/src/view-dropdown-menu/dropdown-menu.ts +++ b/blocksuite/affine/components/src/view-dropdown-menu/dropdown-menu.ts @@ -30,20 +30,15 @@ export class ViewDropdownMenu extends SignalWatcher(ShadowlessElement) { @property({ attribute: false }) accessor viewType$!: Signal | ReadonlySignal; - @property({ attribute: false }) - accessor toggle: ((e: CustomEvent) => void) | undefined; - override render() { const { actions, context, - toggle, viewType$: { value: viewType }, } = this; return html` action.id, ({ id, label, disabled, run }) => html` { dragStart(); - host.addEventListener('pointerup', dragEnd, { once: true }); + document.addEventListener('pointerup', dragEnd, { once: true }); }); this.handleEvent('nativeDrop', dragEnd); disposables.addFromEvent(host, 'dragenter', dragStart, eventOptions); @@ -445,7 +446,7 @@ export class AffineToolbarWidget extends WidgetComponent { host, 'dragleave', throttle( - event => { + (event: DragEvent) => { const { x, y, target } = event; if (target === this) return; const rect = host.getBoundingClientRect(); @@ -464,7 +465,12 @@ export class AffineToolbarWidget extends WidgetComponent { eventOptions ); - // Handles element when hovering + // Handles elements when resizing + const edgelessSlots = std.get(EdgelessLegacySlotIdentifier); + disposables.add(edgelessSlots.elementResizeStart.subscribe(dragStart)); + disposables.add(edgelessSlots.elementResizeEnd.subscribe(dragEnd)); + + // Handles elements when hovering disposables.add( message$.subscribe(data => { if ( @@ -501,7 +507,7 @@ export class AffineToolbarWidget extends WidgetComponent { const value = flags.value$.value; // Hides toolbar - if (value === Flag.None || flags.check(Flag.Hiding, value)) { + if (Flag.None === value || flags.check(Flag.Hiding, value)) { if (toolbar.dataset.open) delete toolbar.dataset.open; return; } @@ -530,12 +536,8 @@ export class AffineToolbarWidget extends WidgetComponent { const value = flags.value$.value; - if ( - !context.activated || - Flag.None === value || - flags.contains(Flag.Hiding, value) - ) - return; + if (!context.activated) return; + if (Flag.None === value || flags.contains(Flag.Hiding, value)) return; const build = referenceElement$.value; const referenceElement = build?.(); diff --git a/blocksuite/affine/widgets/widget-toolbar/tsconfig.json b/blocksuite/affine/widgets/widget-toolbar/tsconfig.json index fa0767b24d..1fafca22dc 100644 --- a/blocksuite/affine/widgets/widget-toolbar/tsconfig.json +++ b/blocksuite/affine/widgets/widget-toolbar/tsconfig.json @@ -8,6 +8,7 @@ "include": ["./src"], "references": [ { "path": "../../blocks/block-database" }, + { "path": "../../blocks/block-surface" }, { "path": "../../blocks/block-table" }, { "path": "../../components" }, { "path": "../../model" }, diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index 5b6df13d07..4134927477 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -575,6 +575,7 @@ export const PackageList = [ name: '@blocksuite/affine-widget-toolbar', workspaceDependencies: [ 'blocksuite/affine/blocks/block-database', + 'blocksuite/affine/blocks/block-surface', 'blocksuite/affine/blocks/block-table', 'blocksuite/affine/components', 'blocksuite/affine/model', diff --git a/yarn.lock b/yarn.lock index 3ceb22dbee..9f33ea4311 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3234,6 +3234,7 @@ __metadata: resolution: "@blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widgets/widget-toolbar" dependencies: "@blocksuite/affine-block-database": "workspace:*" + "@blocksuite/affine-block-surface": "workspace:*" "@blocksuite/affine-block-table": "workspace:*" "@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-model": "workspace:*"