refactor(editor): edgeless attacment toolbar config extension (#10710)
This commit is contained in:
@@ -1,18 +1,13 @@
|
|||||||
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
import { AttachmentBlockSchema } from '@blocksuite/affine-model';
|
||||||
import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
|
|
||||||
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
import { SlashMenuConfigExtension } from '@blocksuite/affine-widget-slash-menu';
|
||||||
import {
|
import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std';
|
||||||
BlockFlavourIdentifier,
|
|
||||||
BlockViewExtension,
|
|
||||||
FlavourExtension,
|
|
||||||
} from '@blocksuite/block-std';
|
|
||||||
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 { AttachmentBlockNotionHtmlAdapterExtension } from './adapters/notion-html.js';
|
import { AttachmentBlockNotionHtmlAdapterExtension } from './adapters/notion-html.js';
|
||||||
import { AttachmentDropOption } from './attachment-service.js';
|
import { AttachmentDropOption } from './attachment-service.js';
|
||||||
import { attachmentSlashMenuConfig } from './configs/slash-menu.js';
|
import { attachmentSlashMenuConfig } from './configs/slash-menu.js';
|
||||||
import { builtinToolbarConfig } from './configs/toolbar';
|
import { createBuiltinToolbarConfigExtension } from './configs/toolbar';
|
||||||
import {
|
import {
|
||||||
AttachmentEmbedConfigExtension,
|
AttachmentEmbedConfigExtension,
|
||||||
AttachmentEmbedService,
|
AttachmentEmbedService,
|
||||||
@@ -31,9 +26,6 @@ export const AttachmentBlockSpec: ExtensionType[] = [
|
|||||||
AttachmentEmbedConfigExtension(),
|
AttachmentEmbedConfigExtension(),
|
||||||
AttachmentEmbedService,
|
AttachmentEmbedService,
|
||||||
AttachmentBlockNotionHtmlAdapterExtension,
|
AttachmentBlockNotionHtmlAdapterExtension,
|
||||||
ToolbarModuleExtension({
|
createBuiltinToolbarConfigExtension(flavour),
|
||||||
id: BlockFlavourIdentifier(flavour),
|
|
||||||
config: builtinToolbarConfig,
|
|
||||||
}),
|
|
||||||
SlashMenuConfigExtension(flavour, attachmentSlashMenuConfig),
|
SlashMenuConfigExtension(flavour, attachmentSlashMenuConfig),
|
||||||
];
|
].flat();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createLitPortal } from '@blocksuite/affine-components/portal';
|
|||||||
import {
|
import {
|
||||||
AttachmentBlockModel,
|
AttachmentBlockModel,
|
||||||
defaultAttachmentProps,
|
defaultAttachmentProps,
|
||||||
|
type EmbedCardStyle,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
@@ -12,7 +13,9 @@ import {
|
|||||||
type ToolbarAction,
|
type ToolbarAction,
|
||||||
type ToolbarActionGroup,
|
type ToolbarActionGroup,
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
|
ToolbarModuleExtension,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
|
import { BlockFlavourIdentifier } from '@blocksuite/block-std';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
import {
|
import {
|
||||||
CaptionIcon,
|
CaptionIcon,
|
||||||
@@ -23,6 +26,7 @@ import {
|
|||||||
EditIcon,
|
EditIcon,
|
||||||
ResetIcon,
|
ResetIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
import { flip, offset } from '@floating-ui/dom';
|
import { flip, offset } from '@floating-ui/dom';
|
||||||
import { computed } from '@preact/signals-core';
|
import { computed } from '@preact/signals-core';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
@@ -34,9 +38,6 @@ import { AttachmentEmbedProvider } from '../embed';
|
|||||||
import { cloneAttachmentProperties } from '../utils';
|
import { cloneAttachmentProperties } from '../utils';
|
||||||
|
|
||||||
const trackBaseProps = {
|
const trackBaseProps = {
|
||||||
segment: 'doc',
|
|
||||||
page: 'doc editor',
|
|
||||||
module: 'toolbar',
|
|
||||||
category: 'attachment',
|
category: 'attachment',
|
||||||
type: 'card view',
|
type: 'card view',
|
||||||
};
|
};
|
||||||
@@ -54,14 +55,14 @@ export const attachmentViewDropdownMenu = {
|
|||||||
const style = defaultAttachmentProps.style!;
|
const style = defaultAttachmentProps.style!;
|
||||||
const width = EMBED_CARD_WIDTH[style];
|
const width = EMBED_CARD_WIDTH[style];
|
||||||
const height = EMBED_CARD_HEIGHT[style];
|
const height = EMBED_CARD_HEIGHT[style];
|
||||||
const bound = Bound.deserialize(model.xywh);
|
const bounds = Bound.deserialize(model.xywh);
|
||||||
bound.w = width;
|
bounds.w = width;
|
||||||
bound.h = height;
|
bounds.h = height;
|
||||||
|
|
||||||
ctx.store.updateBlock(model, {
|
ctx.store.updateBlock(model, {
|
||||||
style,
|
style,
|
||||||
embed: false,
|
embed: false,
|
||||||
xywh: bound.serialize(),
|
xywh: bounds.serialize(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -72,9 +73,11 @@ export const attachmentViewDropdownMenu = {
|
|||||||
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
|
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
|
|
||||||
// Clears
|
if (!ctx.hasSelectedSurfaceModels) {
|
||||||
ctx.reset();
|
// Clears
|
||||||
ctx.select('note');
|
ctx.reset();
|
||||||
|
ctx.select('note');
|
||||||
|
}
|
||||||
|
|
||||||
ctx.std.get(AttachmentEmbedProvider).convertTo(model);
|
ctx.std.get(AttachmentEmbedProvider).convertTo(model);
|
||||||
|
|
||||||
@@ -101,7 +104,8 @@ export const attachmentViewDropdownMenu = {
|
|||||||
|
|
||||||
return embed ? embedAction.label : cardAction.label;
|
return embed ? embedAction.label : cardAction.label;
|
||||||
});
|
});
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
|
e.stopPropagation();
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -114,16 +118,41 @@ export const attachmentViewDropdownMenu = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.viewType$=${viewType$}
|
.viewType$=${viewType$}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
},
|
},
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>;
|
} as const satisfies ToolbarActionGroup<ToolbarAction>;
|
||||||
|
|
||||||
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: [
|
actions: [
|
||||||
{
|
{
|
||||||
id: 'a.rename',
|
id: 'a.rename',
|
||||||
@@ -162,29 +191,8 @@ export const builtinToolbarConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
attachmentViewDropdownMenu,
|
attachmentViewDropdownMenu,
|
||||||
{
|
downloadAction,
|
||||||
id: 'c.download',
|
captionAction,
|
||||||
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',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
@@ -247,3 +255,96 @@ export const builtinToolbarConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as const satisfies ToolbarModuleConfig;
|
} 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<boolean>) => {
|
||||||
|
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`<affine-card-style-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
|
.actions=${actions}
|
||||||
|
.context=${ctx}
|
||||||
|
.style$=${style$}
|
||||||
|
></affine-card-style-dropdown-menu>`
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
} satisfies ToolbarActionGroup<ToolbarAction>,
|
||||||
|
{
|
||||||
|
...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,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export const builtinToolbarConfig = {
|
|||||||
if (!model) return null;
|
if (!model) return null;
|
||||||
|
|
||||||
const actions = this.actions.map(action => ({ ...action }));
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -194,9 +194,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.viewType$=${signal(actions[1].label)}
|
.viewType$=${signal(actions[1].label)}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
@@ -231,7 +231,7 @@ export const builtinToolbarConfig = {
|
|||||||
},
|
},
|
||||||
})) satisfies ToolbarAction[];
|
})) satisfies ToolbarAction[];
|
||||||
|
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -244,9 +244,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-card-style-dropdown-menu
|
html`<affine-card-style-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.style$=${model.props.style$}
|
.style$=${model.props.style$}
|
||||||
></affine-card-style-dropdown-menu>`
|
></affine-card-style-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -242,8 +242,7 @@ export function createBuiltinToolbarConfigForExternal(
|
|||||||
const viewType$ = signal(
|
const viewType$ = signal(
|
||||||
`${viewType === 'card' ? 'Card' : 'Embed'} view`
|
`${viewType === 'card' ? 'Card' : 'Embed'} view`
|
||||||
);
|
);
|
||||||
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -256,9 +255,9 @@ export function createBuiltinToolbarConfigForExternal(
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.viewType$=${viewType$}
|
.viewType$=${viewType$}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
@@ -293,7 +292,7 @@ export function createBuiltinToolbarConfigForExternal(
|
|||||||
},
|
},
|
||||||
})) satisfies ToolbarAction[];
|
})) satisfies ToolbarAction[];
|
||||||
|
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -306,9 +305,9 @@ export function createBuiltinToolbarConfigForExternal(
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-card-style-dropdown-menu
|
html`<affine-card-style-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.style$=${model.props.style$}
|
.style$=${model.props.style$}
|
||||||
></affine-card-style-dropdown-menu>`
|
></affine-card-style-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ export const builtinToolbarConfig = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -81,9 +80,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-card-style-dropdown-menu
|
html`<affine-card-style-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.style=${model.props.style$}
|
.style=${model.props.style$}
|
||||||
></affine-card-style-dropdown-menu>`
|
></affine-card-style-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export const builtinToolbarConfig = {
|
|||||||
if (!model) return null;
|
if (!model) return null;
|
||||||
|
|
||||||
const actions = this.actions.map(action => ({ ...action }));
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -132,9 +132,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.viewType$=${signal(actions[1].label)}
|
.viewType$=${signal(actions[1].label)}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
@@ -168,7 +168,7 @@ export const builtinToolbarConfig = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
})) satisfies ToolbarAction[];
|
})) satisfies ToolbarAction[];
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -181,9 +181,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-card-style-dropdown-menu
|
html`<affine-card-style-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.style$=${model.props.style$}
|
.style$=${model.props.style$}
|
||||||
></affine-card-style-dropdown-menu>`
|
></affine-card-style-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -155,8 +155,7 @@ export const builtinToolbarConfig = {
|
|||||||
if (!model) return null;
|
if (!model) return null;
|
||||||
|
|
||||||
const actions = this.actions.map(action => ({ ...action }));
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -169,9 +168,9 @@ export const builtinToolbarConfig = {
|
|||||||
return html`${keyed(
|
return html`${keyed(
|
||||||
model,
|
model,
|
||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
|
@toggle=${onToggle}
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
|
||||||
.viewType$=${signal(actions[2].label)}
|
.viewType$=${signal(actions[2].label)}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -2,7 +2,6 @@ import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services';
|
|||||||
import { BlockFlavourIdentifier } from '@blocksuite/block-std';
|
import { BlockFlavourIdentifier } from '@blocksuite/block-std';
|
||||||
import type { ExtensionType } from '@blocksuite/store';
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
import { builtinAttachmentToolbarConfig } from './attachment';
|
|
||||||
import { builtinBookmarkToolbarConfig } from './bookmark';
|
import { builtinBookmarkToolbarConfig } from './bookmark';
|
||||||
import { builtinBrushToolbarConfig } from './brush';
|
import { builtinBrushToolbarConfig } from './brush';
|
||||||
import { builtinConnectorToolbarConfig } from './connector';
|
import { builtinConnectorToolbarConfig } from './connector';
|
||||||
@@ -17,11 +16,6 @@ import { builtinShapeToolbarConfig } from './shape';
|
|||||||
import { builtinTextToolbarConfig } from './text';
|
import { builtinTextToolbarConfig } from './text';
|
||||||
|
|
||||||
export const EdgelessElementToolbarExtension: ExtensionType[] = [
|
export const EdgelessElementToolbarExtension: ExtensionType[] = [
|
||||||
ToolbarModuleExtension({
|
|
||||||
id: BlockFlavourIdentifier('affine:surface:attachment'),
|
|
||||||
config: builtinAttachmentToolbarConfig,
|
|
||||||
}),
|
|
||||||
|
|
||||||
ToolbarModuleExtension({
|
ToolbarModuleExtension({
|
||||||
id: BlockFlavourIdentifier('affine:surface:bookmark'),
|
id: BlockFlavourIdentifier('affine:surface:bookmark'),
|
||||||
config: builtinBookmarkToolbarConfig,
|
config: builtinBookmarkToolbarConfig,
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import { ifDefined } from 'lit-html/directives/if-defined.js';
|
|||||||
import { repeat } from 'lit-html/directives/repeat.js';
|
import { repeat } from 'lit-html/directives/repeat.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
EmbedCardDarkCubeIcon,
|
||||||
EmbedCardDarkHorizontalIcon,
|
EmbedCardDarkHorizontalIcon,
|
||||||
EmbedCardDarkListIcon,
|
EmbedCardDarkListIcon,
|
||||||
|
EmbedCardLightCubeIcon,
|
||||||
EmbedCardLightHorizontalIcon,
|
EmbedCardLightHorizontalIcon,
|
||||||
EmbedCardLightListIcon,
|
EmbedCardLightListIcon,
|
||||||
} from '../icons';
|
} from '../icons';
|
||||||
@@ -30,11 +32,15 @@ import {
|
|||||||
const cardStyleMap: Record<ColorScheme, Record<string, TemplateResult>> = {
|
const cardStyleMap: Record<ColorScheme, Record<string, TemplateResult>> = {
|
||||||
light: {
|
light: {
|
||||||
horizontal: EmbedCardLightHorizontalIcon,
|
horizontal: EmbedCardLightHorizontalIcon,
|
||||||
|
horizontalThin: EmbedCardLightListIcon,
|
||||||
list: EmbedCardLightListIcon,
|
list: EmbedCardLightListIcon,
|
||||||
|
cubeThick: EmbedCardLightCubeIcon,
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
horizontal: EmbedCardDarkHorizontalIcon,
|
horizontal: EmbedCardDarkHorizontalIcon,
|
||||||
|
horizontalThin: EmbedCardDarkListIcon,
|
||||||
list: EmbedCardDarkListIcon,
|
list: EmbedCardDarkListIcon,
|
||||||
|
cubeThick: EmbedCardDarkCubeIcon,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,9 +59,6 @@ export class CardStyleDropdownMenu extends SignalWatcher(ShadowlessElement) {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor style$!: Signal<string> | ReadonlySignal<string>;
|
accessor style$!: Signal<string> | ReadonlySignal<string>;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
accessor toggle: ((e: CustomEvent<boolean>) => void) | undefined;
|
|
||||||
|
|
||||||
icons$ = computed(
|
icons$ = computed(
|
||||||
() => cardStyleMap[this.context.themeProvider.theme$.value]
|
() => cardStyleMap[this.context.themeProvider.theme$.value]
|
||||||
);
|
);
|
||||||
@@ -64,14 +67,12 @@ export class CardStyleDropdownMenu extends SignalWatcher(ShadowlessElement) {
|
|||||||
const {
|
const {
|
||||||
actions,
|
actions,
|
||||||
context,
|
context,
|
||||||
toggle,
|
|
||||||
style$: { value: style },
|
style$: { value: style },
|
||||||
icons$: { value: icons },
|
icons$: { value: icons },
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<editor-menu-button
|
<editor-menu-button
|
||||||
@toggle=${toggle}
|
|
||||||
.contentPadding="${'8px'}"
|
.contentPadding="${'8px'}"
|
||||||
.button=${html`
|
.button=${html`
|
||||||
<editor-icon-button
|
<editor-icon-button
|
||||||
@@ -88,12 +89,13 @@ export class CardStyleDropdownMenu extends SignalWatcher(ShadowlessElement) {
|
|||||||
action => action.id,
|
action => action.id,
|
||||||
({ id, label, icon, disabled, run }) => html`
|
({ id, label, icon, disabled, run }) => html`
|
||||||
<editor-icon-button
|
<editor-icon-button
|
||||||
aria-label="${label}"
|
aria-label="${ifDefined(label)}"
|
||||||
data-testid="${id}"
|
data-testid="${id}"
|
||||||
.tooltip="${label}"
|
.tooltip="${label}"
|
||||||
.activeMode="${'border'}"
|
.activeMode="${'border'}"
|
||||||
.iconContainerWidth="${'76px'}"
|
.iconContainerWidth="${'76px'}"
|
||||||
.iconContainerHeight="${'76px'}"
|
.iconContainerHeight="${'76px'}"
|
||||||
|
.justify="${'center'}"
|
||||||
?active="${id === style}"
|
?active="${id === style}"
|
||||||
?disabled="${ifDefined(disabled)}"
|
?disabled="${ifDefined(disabled)}"
|
||||||
@click=${() => run?.(context)}
|
@click=${() => run?.(context)}
|
||||||
|
|||||||
@@ -30,20 +30,15 @@ export class ViewDropdownMenu extends SignalWatcher(ShadowlessElement) {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
accessor viewType$!: Signal<string> | ReadonlySignal<string>;
|
accessor viewType$!: Signal<string> | ReadonlySignal<string>;
|
||||||
|
|
||||||
@property({ attribute: false })
|
|
||||||
accessor toggle: ((e: CustomEvent<boolean>) => void) | undefined;
|
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
const {
|
const {
|
||||||
actions,
|
actions,
|
||||||
context,
|
context,
|
||||||
toggle,
|
|
||||||
viewType$: { value: viewType },
|
viewType$: { value: viewType },
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<editor-menu-button
|
<editor-menu-button
|
||||||
@toggle=${toggle}
|
|
||||||
.contentPadding="${'8px'}"
|
.contentPadding="${'8px'}"
|
||||||
.button=${html`
|
.button=${html`
|
||||||
<editor-icon-button
|
<editor-icon-button
|
||||||
@@ -67,7 +62,7 @@ export class ViewDropdownMenu extends SignalWatcher(ShadowlessElement) {
|
|||||||
action => action.id,
|
action => action.id,
|
||||||
({ id, label, disabled, run }) => html`
|
({ id, label, disabled, run }) => html`
|
||||||
<editor-menu-action
|
<editor-menu-action
|
||||||
aria-label="${label}"
|
aria-label="${ifDefined(label)}"
|
||||||
data-testid="${`link-to-${id}`}"
|
data-testid="${`link-to-${id}`}"
|
||||||
?data-selected="${label === viewType}"
|
?data-selected="${label === viewType}"
|
||||||
?disabled="${ifDefined(
|
?disabled="${ifDefined(
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ export const builtinInlineLinkToolbarConfig = {
|
|||||||
|
|
||||||
const actions = this.actions.map(action => ({ ...action }));
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
const viewType$ = signal(actions[0].label);
|
const viewType$ = signal(actions[0].label);
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ export const builtinInlineLinkToolbarConfig = {
|
|||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
.onToggle=${onToggle}
|
||||||
.viewType$=${viewType$}
|
.viewType$=${viewType$}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export const builtinInlineReferenceToolbarConfig = {
|
|||||||
|
|
||||||
const actions = this.actions.map(action => ({ ...action }));
|
const actions = this.actions.map(action => ({ ...action }));
|
||||||
const viewType$ = signal(actions[0].label);
|
const viewType$ = signal(actions[0].label);
|
||||||
const toggle = (e: CustomEvent<boolean>) => {
|
const onToggle = (e: CustomEvent<boolean>) => {
|
||||||
const opened = e.detail;
|
const opened = e.detail;
|
||||||
if (!opened) return;
|
if (!opened) return;
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ export const builtinInlineReferenceToolbarConfig = {
|
|||||||
html`<affine-view-dropdown-menu
|
html`<affine-view-dropdown-menu
|
||||||
.actions=${actions}
|
.actions=${actions}
|
||||||
.context=${ctx}
|
.context=${ctx}
|
||||||
.toggle=${toggle}
|
.onToggle=${onToggle}
|
||||||
.viewType$=${viewType$}
|
.viewType$=${viewType$}
|
||||||
></affine-view-dropdown-menu>`
|
></affine-view-dropdown-menu>`
|
||||||
)}`;
|
)}`;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ abstract class ToolbarContextBase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSurfaceElementsByType<M extends abstract new (...args: any) => any>(
|
getSurfaceModelsByType<M extends abstract new (...args: any) => any>(
|
||||||
klass: M
|
klass: M
|
||||||
) {
|
) {
|
||||||
if (this.hasSelectedSurfaceModels) {
|
if (this.hasSelectedSurfaceModels) {
|
||||||
@@ -227,8 +227,14 @@ abstract class ToolbarContextBase {
|
|||||||
return this.std.getOptional(TelemetryProvider);
|
return this.std.getOptional(TelemetryProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
track = (...args: Parameters<TelemetryService['track']>) => {
|
track = (...[name, props]: Parameters<TelemetryService['track']>) => {
|
||||||
this.telemetryProvider?.track(...args);
|
const segment = this.hasSelectedSurfaceModels ? 'whiteboard' : 'doc';
|
||||||
|
this.telemetryProvider?.track(name, {
|
||||||
|
segment,
|
||||||
|
page: `${segment} editor`,
|
||||||
|
module: 'toolbar',
|
||||||
|
...props,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blocksuite/affine-block-database": "workspace:*",
|
"@blocksuite/affine-block-database": "workspace:*",
|
||||||
|
"@blocksuite/affine-block-surface": "workspace:*",
|
||||||
"@blocksuite/affine-block-table": "workspace:*",
|
"@blocksuite/affine-block-table": "workspace:*",
|
||||||
"@blocksuite/affine-components": "workspace:*",
|
"@blocksuite/affine-components": "workspace:*",
|
||||||
"@blocksuite/affine-model": "workspace:*",
|
"@blocksuite/affine-model": "workspace:*",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { DatabaseSelection } from '@blocksuite/affine-block-database';
|
import { DatabaseSelection } from '@blocksuite/affine-block-database';
|
||||||
|
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
import { TableSelection } from '@blocksuite/affine-block-table';
|
import { TableSelection } from '@blocksuite/affine-block-table';
|
||||||
import { EditorToolbar } from '@blocksuite/affine-components/toolbar';
|
import { EditorToolbar } from '@blocksuite/affine-components/toolbar';
|
||||||
import {
|
import {
|
||||||
@@ -437,7 +438,7 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
const eventOptions = { passive: false };
|
const eventOptions = { passive: false };
|
||||||
this.handleEvent('dragStart', () => {
|
this.handleEvent('dragStart', () => {
|
||||||
dragStart();
|
dragStart();
|
||||||
host.addEventListener('pointerup', dragEnd, { once: true });
|
document.addEventListener('pointerup', dragEnd, { once: true });
|
||||||
});
|
});
|
||||||
this.handleEvent('nativeDrop', dragEnd);
|
this.handleEvent('nativeDrop', dragEnd);
|
||||||
disposables.addFromEvent(host, 'dragenter', dragStart, eventOptions);
|
disposables.addFromEvent(host, 'dragenter', dragStart, eventOptions);
|
||||||
@@ -445,7 +446,7 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
host,
|
host,
|
||||||
'dragleave',
|
'dragleave',
|
||||||
throttle(
|
throttle(
|
||||||
event => {
|
(event: DragEvent) => {
|
||||||
const { x, y, target } = event;
|
const { x, y, target } = event;
|
||||||
if (target === this) return;
|
if (target === this) return;
|
||||||
const rect = host.getBoundingClientRect();
|
const rect = host.getBoundingClientRect();
|
||||||
@@ -464,7 +465,12 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
eventOptions
|
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(
|
disposables.add(
|
||||||
message$.subscribe(data => {
|
message$.subscribe(data => {
|
||||||
if (
|
if (
|
||||||
@@ -501,7 +507,7 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
const value = flags.value$.value;
|
const value = flags.value$.value;
|
||||||
|
|
||||||
// Hides toolbar
|
// 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;
|
if (toolbar.dataset.open) delete toolbar.dataset.open;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -530,12 +536,8 @@ export class AffineToolbarWidget extends WidgetComponent {
|
|||||||
|
|
||||||
const value = flags.value$.value;
|
const value = flags.value$.value;
|
||||||
|
|
||||||
if (
|
if (!context.activated) return;
|
||||||
!context.activated ||
|
if (Flag.None === value || flags.contains(Flag.Hiding, value)) return;
|
||||||
Flag.None === value ||
|
|
||||||
flags.contains(Flag.Hiding, value)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const build = referenceElement$.value;
|
const build = referenceElement$.value;
|
||||||
const referenceElement = build?.();
|
const referenceElement = build?.();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "../../blocks/block-database" },
|
{ "path": "../../blocks/block-database" },
|
||||||
|
{ "path": "../../blocks/block-surface" },
|
||||||
{ "path": "../../blocks/block-table" },
|
{ "path": "../../blocks/block-table" },
|
||||||
{ "path": "../../components" },
|
{ "path": "../../components" },
|
||||||
{ "path": "../../model" },
|
{ "path": "../../model" },
|
||||||
|
|||||||
@@ -575,6 +575,7 @@ export const PackageList = [
|
|||||||
name: '@blocksuite/affine-widget-toolbar',
|
name: '@blocksuite/affine-widget-toolbar',
|
||||||
workspaceDependencies: [
|
workspaceDependencies: [
|
||||||
'blocksuite/affine/blocks/block-database',
|
'blocksuite/affine/blocks/block-database',
|
||||||
|
'blocksuite/affine/blocks/block-surface',
|
||||||
'blocksuite/affine/blocks/block-table',
|
'blocksuite/affine/blocks/block-table',
|
||||||
'blocksuite/affine/components',
|
'blocksuite/affine/components',
|
||||||
'blocksuite/affine/model',
|
'blocksuite/affine/model',
|
||||||
|
|||||||
@@ -3234,6 +3234,7 @@ __metadata:
|
|||||||
resolution: "@blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widgets/widget-toolbar"
|
resolution: "@blocksuite/affine-widget-toolbar@workspace:blocksuite/affine/widgets/widget-toolbar"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@blocksuite/affine-block-database": "workspace:*"
|
"@blocksuite/affine-block-database": "workspace:*"
|
||||||
|
"@blocksuite/affine-block-surface": "workspace:*"
|
||||||
"@blocksuite/affine-block-table": "workspace:*"
|
"@blocksuite/affine-block-table": "workspace:*"
|
||||||
"@blocksuite/affine-components": "workspace:*"
|
"@blocksuite/affine-components": "workspace:*"
|
||||||
"@blocksuite/affine-model": "workspace:*"
|
"@blocksuite/affine-model": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user