Skip to content

Commit

Permalink
feat(editor): add toolbar registry extension
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Jan 31, 2025
1 parent 7579570 commit 8baafe0
Show file tree
Hide file tree
Showing 51 changed files with 2,267 additions and 70 deletions.
20 changes: 16 additions & 4 deletions blocksuite/affine/block-attachment/src/attachment-spec.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import {
BlockFlavourIdentifier,
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 { builtinToolbarConfig } from './configs/toolbar';
import {
AttachmentEmbedConfigExtension,
AttachmentEmbedService,
} from './embed.js';
} from './embed';

const flavour = 'affine:attachment';

export const AttachmentBlockSpec: ExtensionType[] = [
FlavourExtension('affine:attachment'),
BlockViewExtension('affine:attachment', model => {
FlavourExtension(flavour),
BlockViewExtension(flavour, model => {
return model.parent?.flavour === 'affine:surface'
? literal`affine-edgeless-attachment`
: literal`affine-attachment`;
Expand All @@ -20,4 +28,8 @@ export const AttachmentBlockSpec: ExtensionType[] = [
AttachmentEmbedConfigExtension(),
AttachmentEmbedService,
AttachmentBlockNotionHtmlAdapterExtension,
ToolbarModuleExtension({
id: BlockFlavourIdentifier(flavour),
config: builtinToolbarConfig,
}),
];
166 changes: 166 additions & 0 deletions blocksuite/affine/block-attachment/src/configs/toolbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import {
type AttachmentBlockModel,
AttachmentBlockSchema,
defaultAttachmentProps,
} from '@blocksuite/affine-model';
import {
EMBED_CARD_HEIGHT,
EMBED_CARD_WIDTH,
} from '@blocksuite/affine-shared/consts';
import {
ActionPlacement,
type ToolbarAction,
type ToolbarActionGroup,
type ToolbarModuleConfig,
} from '@blocksuite/affine-shared/services';
import { BlockSelection } from '@blocksuite/block-std';
import { Bound } from '@blocksuite/global/utils';
import {
CaptionIcon,
CopyIcon,
DeleteIcon,
DownloadIcon,
DuplicateIcon,
EditIcon,
ResetIcon,
} from '@blocksuite/icons/lit';
import { computed } from '@preact/signals-core';
import { html } from 'lit';

import { AttachmentEmbedProvider } from '../embed';

export const builtinToolbarConfig = {
actions: [
{
id: 'rename',
tooltip: 'Rename',
icon: EditIcon(),
run(_cx) {},
},
{
id: 'conversions',
actions: [
{
id: 'card-view',
label: 'Card view',
run(cx) {
const model = cx.getCurrentBlockModelBy(
BlockSelection,
AttachmentBlockSchema
);
if (!model) return;

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;

cx.store.updateBlock(model, {
style,
embed: false,
xywh: bound.serialize(),
});
},
},
{
id: 'embed-view',
label: 'Embed view',
run(cx) {
const model = cx.getCurrentBlockModelBy(
BlockSelection,
AttachmentBlockSchema
);
if (!model) return;

cx.std
.get(AttachmentEmbedProvider)
.convertTo(model as AttachmentBlockModel);
},
},
],
content(cx) {
const model = cx.getCurrentBlockModelBy(
BlockSelection,
AttachmentBlockSchema
);
if (!model) return null;

const actions = this.actions.map(action => ({ ...action }));

return html`<affine-view-dropdown
.actions=${actions}
.context=${cx}
.viewType$=${computed(() => {
const [cardAction, embedAction] = actions;
const embed = model.embed$.value ?? false;
cardAction.disabled = !embed;
embedAction.disabled =
embed &&
cx.std
.get(AttachmentEmbedProvider)
.embedded(model as AttachmentBlockModel);
return embed ? embedAction.label : cardAction.label;
})}
></affine-view-dropdown>`;
},
} satisfies ToolbarActionGroup<ToolbarAction>,
{
id: 'download',
tooltip: 'Download',
icon: DownloadIcon(),
run(_cx) {},
},
{
id: 'caption',
tooltip: 'Caption',
icon: CaptionIcon(),
run(_cx) {},
},
{
id: 'clipboard',
placement: ActionPlacement.More,
actions: [
{
id: 'copy',
label: 'Copy',
icon: CopyIcon(),
run(_cx) {},
},
{
id: 'duplicate',
label: 'Duplicate',
icon: DuplicateIcon(),
run(_cx) {},
},
],
},
{
id: 'refresh',
placement: ActionPlacement.More,
actions: [
{
id: 'reload',
label: 'Reload',
icon: ResetIcon(),
run(_cx) {},
},
],
},
{
id: 'delete',
placement: ActionPlacement.More,
actions: [
{
id: 'delete',
label: 'Delete',
icon: DeleteIcon(),
run(_cx) {},
},
],
},
],
} as const satisfies ToolbarModuleConfig;
2 changes: 1 addition & 1 deletion blocksuite/affine/block-attachment/src/embed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class AttachmentEmbedService extends Extension {
// Converts to embed view.
convertTo(model: AttachmentBlockModel, maxFileSize = this._maxFileSize) {
const config = this.values.find(config => config.check(model, maxFileSize));
if (!config || !config.action) {
if (!config?.action) {
model.doc.updateBlock(model, { embed: true });
return;
}
Expand Down
20 changes: 16 additions & 4 deletions blocksuite/affine/block-bookmark/src/bookmark-spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
import {
BlockFlavourIdentifier,
BlockViewExtension,
FlavourExtension,
} from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';

import { BookmarkBlockAdapterExtensions } from './adapters/extension.js';
import { BookmarkBlockAdapterExtensions } from './adapters/extension';
import { builtinToolbarConfig } from './configs/toolbar';

const flavour = 'affine:bookmark';

export const BookmarkBlockSpec: ExtensionType[] = [
FlavourExtension('affine:bookmark'),
BlockViewExtension('affine:bookmark', model => {
FlavourExtension(flavour),
BlockViewExtension(flavour, model => {
return model.parent?.flavour === 'affine:surface'
? literal`affine-edgeless-bookmark`
: literal`affine-bookmark`;
}),
BookmarkBlockAdapterExtensions,
ToolbarModuleExtension({
id: BlockFlavourIdentifier(flavour),
config: builtinToolbarConfig,
}),
].flat();
118 changes: 118 additions & 0 deletions blocksuite/affine/block-bookmark/src/configs/toolbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { BookmarkBlockSchema } from '@blocksuite/affine-model';
import {
ActionPlacement,
type ToolbarActionGroup,
type ToolbarModuleConfig,
} from '@blocksuite/affine-shared/services';
import { getHostName } from '@blocksuite/affine-shared/utils';
import { BlockSelection } from '@blocksuite/block-std';
import {
CaptionIcon,
CopyIcon,
DeleteIcon,
DuplicateIcon,
PaletteIcon,
ResetIcon,
} from '@blocksuite/icons/lit';
import { html } from 'lit';

export const builtinToolbarConfig = {
actions: [
{
id: 'preview',
content(cx) {
const model = cx.getCurrentBlockModelBy(
BlockSelection,
BookmarkBlockSchema
);
if (!model) return null;

const { url } = model;

return html`
<a
class="affine-link-preview"
rel="noopener noreferrer"
target="_blank"
href=${url}
>
<span>${getHostName(url)}</span>
</a>
`;
},
},
{
id: 'conversions',
actions: [
{
id: 'inline-view',
label: 'Inline view',
run(_cx) {},
},
{
id: 'card-view',
label: 'Card view',
run(_cx) {},
},
],
content(_cx) {
this.actions;
return null;
},
} satisfies ToolbarActionGroup,
{
id: 'style',
tooltip: 'Card style',
icon: PaletteIcon(),
run(_cx) {},
},
{
id: 'caption',
tooltip: 'Caption',
icon: CaptionIcon(),
run(_cx) {},
},
{
id: 'clipboard',
placement: ActionPlacement.More,
actions: [
{
id: 'copy',
label: 'Copy',
icon: CopyIcon(),
run(_cx) {},
},
{
id: 'duplicate',
label: 'Duplicate',
icon: DuplicateIcon(),
run(_cx) {},
},
],
},
{
id: 'refresh',
placement: ActionPlacement.More,
actions: [
{
id: 'reload',
label: 'Reload',
icon: ResetIcon(),
run(_cx) {},
},
],
},
{
id: 'delete',
placement: ActionPlacement.More,
actions: [
{
id: 'delete',
label: 'Delete',
icon: DeleteIcon(),
run(_cx) {},
},
],
},
],
} as const satisfies ToolbarModuleConfig;
Loading

0 comments on commit 8baafe0

Please sign in to comment.