mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
feat(editor): add WidgetViewExtension (#10180)
Closes: [BS-2282](https://linear.app/affine-design/issue/BS-2282/replace-widgetviewmapextension-with-widgetextension)
This commit is contained in:
@@ -1,32 +1,40 @@
|
||||
import type { ExtensionType } from '@blocksuite/store';
|
||||
|
||||
import { WidgetViewMapIdentifier } from '../identifier.js';
|
||||
import type { WidgetViewMapType } from '../spec/type.js';
|
||||
import { WidgetViewIdentifier } from '../identifier.js';
|
||||
import type { WidgetViewType } from '../spec/type.js';
|
||||
|
||||
/**
|
||||
* Create a widget view map extension.
|
||||
* Create a widget view extension.
|
||||
*
|
||||
* @param flavour The flavour of the block that the widget view map is for.
|
||||
* @param widgetViewMap A map of widget names to widget view lit literal.
|
||||
* @param flavour The flavour of the block that the widget view is for.
|
||||
* @param id The id of the widget view.
|
||||
* @param view The widget view lit literal.
|
||||
*
|
||||
* A widget view map is to provide a map of widgets to a block.
|
||||
* For every target block, it's view will be rendered with the widget views.
|
||||
* A widget view is to provide a widget view for a block.
|
||||
* For every target block, it's view will be rendered with the widget view.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { WidgetViewMapExtension } from '@blocksuite/block-std';
|
||||
* import { WidgetViewExtension } from '@blocksuite/block-std';
|
||||
*
|
||||
* const MyWidgetViewMapExtension = WidgetViewMapExtension('my-flavour', {
|
||||
* 'my-widget': literal`my-widget-view`
|
||||
* });
|
||||
* const MyWidgetViewExtension = WidgetViewExtension('my-flavour', 'my-widget', literal`my-widget-view`);
|
||||
*/
|
||||
export function WidgetViewMapExtension(
|
||||
export function WidgetViewExtension(
|
||||
flavour: string,
|
||||
widgetViewMap: WidgetViewMapType
|
||||
id: string,
|
||||
view: WidgetViewType
|
||||
): ExtensionType {
|
||||
return {
|
||||
setup: di => {
|
||||
di.addImpl(WidgetViewMapIdentifier(flavour), () => widgetViewMap);
|
||||
if (flavour.includes('|') || id.includes('|')) {
|
||||
console.error(`Register view failed:`);
|
||||
console.error(
|
||||
`flavour or id cannot include '|', flavour: ${flavour}, id: ${id}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
const key = `${flavour}|${id}`;
|
||||
di.addImpl(WidgetViewIdentifier(key), view);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Command } from './command/index.js';
|
||||
import type { EventOptions, UIEventHandler } from './event/index.js';
|
||||
import type { BlockService, LifeCycleWatcher } from './extension/index.js';
|
||||
import type { BlockStdScope } from './scope/index.js';
|
||||
import type { BlockViewType, WidgetViewMapType } from './spec/type.js';
|
||||
import type { BlockViewType, WidgetViewType } from './spec/type.js';
|
||||
|
||||
export const BlockServiceIdentifier =
|
||||
createIdentifier<BlockService>('BlockService');
|
||||
@@ -20,8 +20,8 @@ export const ConfigIdentifier =
|
||||
|
||||
export const BlockViewIdentifier = createIdentifier<BlockViewType>('BlockView');
|
||||
|
||||
export const WidgetViewMapIdentifier =
|
||||
createIdentifier<WidgetViewMapType>('WidgetViewMap');
|
||||
export const WidgetViewIdentifier =
|
||||
createIdentifier<WidgetViewType>('WidgetView');
|
||||
|
||||
export const LifeCycleWatcherIdentifier =
|
||||
createIdentifier<LifeCycleWatcher>('LifeCycleWatcher');
|
||||
|
||||
@@ -2,4 +2,4 @@ import type { BlockModel } from '@blocksuite/store';
|
||||
import type { StaticValue } from 'lit/static-html.js';
|
||||
|
||||
export type BlockViewType = StaticValue | ((model: BlockModel) => StaticValue);
|
||||
export type WidgetViewMapType = Record<string, StaticValue>;
|
||||
export type WidgetViewType = StaticValue;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { html, type StaticValue, unsafeStatic } from 'lit/static-html.js';
|
||||
|
||||
import type { CommandManager } from '../../command/index.js';
|
||||
import type { UIEventDispatcher } from '../../event/index.js';
|
||||
import { WidgetViewMapIdentifier } from '../../identifier.js';
|
||||
import { WidgetViewIdentifier } from '../../identifier.js';
|
||||
import type { RangeManager } from '../../range/index.js';
|
||||
import type { BlockStdScope } from '../../scope/block-std-scope.js';
|
||||
import { PropTypes, requiredProperties } from '../decorators/index.js';
|
||||
@@ -56,22 +56,21 @@ export class EditorHost extends SignalWatcher(
|
||||
console.warn(`Cannot find render flavour ${flavour}.`);
|
||||
return html`${nothing}`;
|
||||
}
|
||||
const widgetViewMap = this.std.getOptional(
|
||||
WidgetViewMapIdentifier(flavour)
|
||||
|
||||
const widgetViews = this.std.provider.getAll(WidgetViewIdentifier);
|
||||
const widgets = widgetViews.entries().reduce(
|
||||
(mapping, [key, tag]) => {
|
||||
const [widgetFlavour, id] = key.split('|');
|
||||
if (widgetFlavour === flavour) {
|
||||
const template = html`<${tag} ${unsafeStatic(WIDGET_ID_ATTR)}=${id}></${tag}>`;
|
||||
mapping[id] = template;
|
||||
}
|
||||
return mapping;
|
||||
},
|
||||
{} as Record<string, TemplateResult>
|
||||
);
|
||||
|
||||
const tag = typeof view === 'function' ? view(model) : view;
|
||||
const widgets: Record<string, TemplateResult> = widgetViewMap
|
||||
? Object.entries(widgetViewMap).reduce((mapping, [key, tag]) => {
|
||||
const template = html`<${tag} ${unsafeStatic(WIDGET_ID_ATTR)}=${key}></${tag}>`;
|
||||
|
||||
return {
|
||||
...mapping,
|
||||
[key]: template,
|
||||
};
|
||||
}, {})
|
||||
: {};
|
||||
|
||||
return html`<${tag}
|
||||
${unsafeStatic(BLOCK_ID_ATTR)}=${model.id}
|
||||
.widgets=${widgets}
|
||||
@@ -144,13 +143,22 @@ export class EditorHost extends SignalWatcher(
|
||||
const view = this.std.getView(rootModel.flavour);
|
||||
if (!view) return result;
|
||||
|
||||
const widgetViewMap = this.std.getOptional(
|
||||
WidgetViewMapIdentifier(rootModel.flavour)
|
||||
const widgetViews = this.std.provider.getAll(
|
||||
WidgetViewIdentifier(rootModel.flavour)
|
||||
);
|
||||
const widgetTags = Object.entries(widgetViews).reduce(
|
||||
(mapping, [key, tag]) => {
|
||||
const [widgetFlavour, id] = key.split('|');
|
||||
if (widgetFlavour === rootModel.flavour) {
|
||||
mapping[id] = tag;
|
||||
}
|
||||
return mapping;
|
||||
},
|
||||
{} as Record<string, StaticValue>
|
||||
);
|
||||
const widgetTags = Object.values(widgetViewMap ?? {});
|
||||
const elementsTags: StaticValue[] = [
|
||||
typeof view === 'function' ? view(rootModel) : view,
|
||||
...widgetTags,
|
||||
...Object.values(widgetTags),
|
||||
];
|
||||
await Promise.all(
|
||||
elementsTags.map(tag => {
|
||||
|
||||
Reference in New Issue
Block a user