feat(editor): update embed iframe toolbar config (#11221)

part of [BS-2843](https://linear.app/affine-design/issue/BS-2843/iframe-embed-block-占位态)
This commit is contained in:
donteatfriedrice
2025-03-27 04:17:30 +00:00
parent e763061bd6
commit 0c73fde44a
3 changed files with 104 additions and 94 deletions
@@ -102,7 +102,7 @@ export class EmbedIframeLinkInputPopup extends EmbedIframeLinkInputBase {
outline: none;
}
.link-input::placeholder {
color: var(--affine-placeholder-color);
color: ${unsafeCSSVarV2('text/placeholder')};
}
}
@@ -24,30 +24,77 @@ import {
CopyIcon,
DeleteIcon,
DuplicateIcon,
LinkedPageIcon,
OpenInNewIcon,
ResetIcon,
} from '@blocksuite/icons/lit';
import { type ExtensionType, Slice, Text } from '@blocksuite/store';
import {
type ExtensionType,
Slice,
Text,
toDraftModel,
} from '@blocksuite/store';
import { computed, signal } from '@preact/signals-core';
import { html } from 'lit';
import { keyed } from 'lit/directives/keyed.js';
import * as Y from 'yjs';
import {
convertSelectedBlocksToLinkedDoc,
getTitleFromSelectedModels,
notifyDocCreated,
promptDocTitle,
} from '../../common/render-linked-doc';
import { EmbedIframeBlockComponent } from '../embed-iframe-block';
const trackBaseProps = {
category: 'embed iframe block',
};
const showWhenUrlExists = (ctx: ToolbarContext) => {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return false;
return !!model.props.url;
};
const openLinkAction = (id: string): ToolbarAction => {
return {
id,
when: showWhenUrlExists,
tooltip: 'Original',
icon: OpenInNewIcon(),
run(ctx) {
const component = ctx.getCurrentBlockByType(EmbedIframeBlockComponent);
component?.open();
},
};
};
const captionAction = (id: string): ToolbarAction => {
return {
id,
when: showWhenUrlExists,
tooltip: 'Caption',
icon: CaptionIcon(),
run(ctx) {
const component = ctx.getCurrentBlockByType(EmbedIframeBlockComponent);
component?.captionEditor?.show();
ctx.track('OpenedCaptionEditor', {
...trackBaseProps,
control: 'add caption',
});
},
};
};
export const builtinToolbarConfig = {
actions: [
openLinkAction('a.open-link'),
{
id: 'b.conversions',
when: (ctx: ToolbarContext) => {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return false;
return !!model.props.url;
},
id: 'c.conversions',
when: showWhenUrlExists,
actions: [
{
id: 'inline',
@@ -155,24 +202,48 @@ export const builtinToolbarConfig = {
)}`;
},
} satisfies ToolbarActionGroup<ToolbarAction>,
captionAction('d.caption'),
{
id: 'c.caption',
when: (ctx: ToolbarContext) => {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return false;
return !!model.props.url;
},
tooltip: 'Caption',
icon: CaptionIcon(),
id: 'e.convert-to-linked-doc',
tooltip: 'Create Linked Doc',
icon: LinkedPageIcon(),
run(ctx) {
const component = ctx.getCurrentBlockByType(EmbedIframeBlockComponent);
component?.captionEditor?.show();
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return;
ctx.track('OpenedCaptionEditor', {
...trackBaseProps,
control: 'add caption',
});
const { store, std, selection, track } = ctx;
selection.clear();
const draftedModels = [model].map(toDraftModel);
const autofill = getTitleFromSelectedModels(draftedModels);
promptDocTitle(std, autofill)
.then(async title => {
if (title === null) return;
await convertSelectedBlocksToLinkedDoc(
std,
store,
draftedModels,
title
);
notifyDocCreated(std, store);
track('DocCreated', {
segment: 'doc',
page: 'doc editor',
module: 'toolbar',
control: 'create linked doc',
type: 'embed-linked-doc',
});
track('LinkedDocCreated', {
segment: 'doc',
page: 'doc editor',
module: 'toolbar',
control: 'create linked doc',
type: 'embed-linked-doc',
});
})
.catch(console.error);
},
},
{
@@ -243,14 +314,10 @@ export const builtinToolbarConfig = {
export const builtinSurfaceToolbarConfig = {
actions: [
openLinkAction('a.open-link'),
{
id: 'b.conversions',
when: (ctx: ToolbarContext) => {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return false;
return !!model.props.url;
},
id: 'c.conversions',
when: showWhenUrlExists,
actions: [
{
id: 'card',
@@ -324,28 +391,9 @@ export const builtinSurfaceToolbarConfig = {
)}`;
},
} satisfies ToolbarActionGroup<ToolbarAction>,
captionAction('d.caption'),
{
id: 'c.caption',
when: (ctx: ToolbarContext) => {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return false;
return !!model.props.url;
},
tooltip: 'Caption',
icon: CaptionIcon(),
run(ctx) {
const component = ctx.getCurrentBlockByType(EmbedIframeBlockComponent);
component?.captionEditor?.show();
ctx.track('OpenedCaptionEditor', {
...trackBaseProps,
control: 'add caption',
});
},
},
{
id: 'd.scale',
id: 'e.scale',
content(ctx) {
const model = ctx.getCurrentModelByType(EmbedIframeBlockModel);
if (!model) return null;
@@ -877,11 +877,11 @@ const embedIframeToolbarConfig = {
},
actions: [
{
id: 'a.copy-link-and-edit',
id: 'b.copy-link',
actions: [
{
id: 'copy-link',
tooltip: 'Copy link',
tooltip: 'Copy original link',
icon: CopyIcon(),
run(ctx) {
const model = ctx.getCurrentBlockByType(
@@ -895,52 +895,14 @@ const embedIframeToolbarConfig = {
toast(ctx.host, 'Copied link to clipboard');
ctx.track('CopiedLink', {
category: matchModels(model, [BookmarkBlockModel])
? 'bookmark'
category: matchModels(model, [EmbedIframeBlockModel])
? 'embed iframe block'
: 'link',
type: 'card view',
control: 'copy link',
});
},
},
{
id: 'edit',
tooltip: 'Edit',
icon: EditIcon(),
run(ctx) {
const component = ctx.getCurrentBlockByType(
EmbedIframeBlockComponent
);
if (!component) return;
ctx.hide();
const model = component.model;
const abortController = new AbortController();
abortController.signal.onabort = () => ctx.show();
toggleEmbedCardEditModal(
ctx.host,
model,
'card',
undefined,
undefined,
(_std, _component, props) => {
ctx.store.updateBlock(model, props);
component.requestUpdate();
},
abortController
);
ctx.track('OpenedAliasPopup', {
category: matchModels(model, [BookmarkBlockModel])
? 'bookmark'
: 'link',
type: 'card view',
control: 'edit',
});
},
},
],
},
],