import { notifyLinkedDocSwitchedToEmbed } from '@blocksuite/affine-components/notification'; import { ActionPlacement, type ToolbarAction, type ToolbarActionGroup, type ToolbarModuleConfig, } from '@blocksuite/affine-shared/services'; import { cloneReferenceInfoWithoutAliases, isInsideBlockByFlavour, } from '@blocksuite/affine-shared/utils'; import { BlockSelection } from '@blocksuite/block-std'; import { DeleteIcon } from '@blocksuite/icons/lit'; import { signal } from '@preact/signals-core'; import { html } from 'lit-html'; import { keyed } from 'lit-html/directives/keyed.js'; import { AffineReference } from '../reference-node'; const trackBaseProps = { segment: 'doc', page: 'doc editor', module: 'toolbar', category: 'linked doc', type: 'inline view', }; export const builtinInlineReferenceToolbarConfig = { actions: [ { id: 'a.doc-title', content(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return null; if (!target.referenceInfo.title) return null; return html` target.open({ event })} >`; }, }, { id: 'c.conversions', actions: [ { id: 'inline', label: 'Inline view', disabled: true, }, { id: 'card', label: 'Card view', run(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return; if (!target.block) return; const { block: { model }, referenceInfo, inlineEditor, selfInlineRange, } = target; const { parent } = model; if (!inlineEditor || !selfInlineRange || !parent) return; // Clears ctx.reset(); const index = parent.children.indexOf(model); const blockId = ctx.store.addBlock( 'affine:embed-linked-doc', referenceInfo, parent, index + 1 ); const totalTextLength = inlineEditor.yTextLength; const inlineTextLength = selfInlineRange.length; if (totalTextLength === inlineTextLength) { ctx.store.deleteBlock(model); } else { inlineEditor.insertText(selfInlineRange, target.docTitle); } ctx.select('note', [ ctx.selection.create(BlockSelection, { blockId }), ]); ctx.track('SelectedView', { ...trackBaseProps, control: 'select view', type: 'card view', }); }, }, { id: 'embed', label: 'Embed view', disabled(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return true; if (!target.block) return true; if ( isInsideBlockByFlavour( ctx.store, target.block.model, 'affine:edgeless-text' ) ) return true; // nesting is not supported if (target.closest('affine-embed-synced-doc-block')) return true; // same doc if (target.referenceInfo.pageId === ctx.store.id) return true; // linking to block if (target.referenceToNode()) return true; return false; }, run(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return; if (!target.block) return; const { block: { model }, referenceInfo, inlineEditor, selfInlineRange, } = target; const { parent } = model; if (!inlineEditor || !selfInlineRange || !parent) return; // Clears ctx.reset(); const index = parent.children.indexOf(model); const blockId = ctx.store.addBlock( 'affine:embed-synced-doc', cloneReferenceInfoWithoutAliases(referenceInfo), parent, index + 1 ); const totalTextLength = inlineEditor.yTextLength; const inlineTextLength = selfInlineRange.length; if (totalTextLength === inlineTextLength) { ctx.store.deleteBlock(model); } else { inlineEditor.insertText(selfInlineRange, target.docTitle); } const hasTitleAlias = Boolean(referenceInfo.title); if (hasTitleAlias) { notifyLinkedDocSwitchedToEmbed(ctx.std); } ctx.select('note', [ ctx.selection.create(BlockSelection, { blockId }), ]); ctx.track('SelectedView', { ...trackBaseProps, control: 'select view', type: 'embed view', }); }, }, ], content(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return null; const actions = this.actions.map(action => ({ ...action })); const viewType$ = signal(actions[0].label); const onToggle = (e: CustomEvent) => { const opened = e.detail; if (!opened) return; ctx.track('OpenedViewSelector', { ...trackBaseProps, control: 'switch view', }); }; return html`${keyed( target, html`` )}`; }, when(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return false; if (!target.block) return false; if (ctx.flags.isNative()) return false; if ( target.block.closest('affine-database') || target.block.closest('affine-table') ) return false; return true; }, } satisfies ToolbarActionGroup, { placement: ActionPlacement.More, id: 'c.delete', label: 'Delete', icon: DeleteIcon(), variant: 'destructive', run(ctx) { const target = ctx.message$.peek()?.element; if (!(target instanceof AffineReference)) return; const { inlineEditor, selfInlineRange } = target; if (!inlineEditor || !selfInlineRange) return; if (!inlineEditor.isValidInlineRange(selfInlineRange)) return; inlineEditor.deleteText(selfInlineRange); }, }, ], } as const satisfies ToolbarModuleConfig;