refactor(editor): rename block-std to std (#11250)

Closes: BS-2946
This commit is contained in:
Saul-Mirone
2025-03-28 07:20:34 +00:00
parent 4498676a96
commit 205cd7a86d
1029 changed files with 1580 additions and 1698 deletions
@@ -0,0 +1,33 @@
import type { ExtensionType } from '@blocksuite/store';
import { BlockViewIdentifier } from '../identifier.js';
import type { BlockViewType } from '../spec/type.js';
/**
* Create a block view extension.
*
* @param flavour The flavour of the block that the view is for.
* @param view Lit literal template for the view. Example: `my-list-block`
*
* The view is a lit template that is used to render the block.
*
* @example
* ```ts
* import { BlockViewExtension } from '@blocksuite/std';
*
* const MyListBlockViewExtension = BlockViewExtension(
* 'affine:list',
* literal`my-list-block`
* );
* ```
*/
export function BlockViewExtension(
flavour: string,
view: BlockViewType
): ExtensionType {
return {
setup: di => {
di.addImpl(BlockViewIdentifier(flavour), () => view);
},
};
}
@@ -0,0 +1,44 @@
import type { ServiceIdentifier } from '@blocksuite/global/di';
import type { ExtensionType } from '@blocksuite/store';
import { ConfigIdentifier } from '../identifier.js';
export interface ConfigFactory<Config extends Record<string, any>> {
(config: Config): ExtensionType;
identifier: ServiceIdentifier<Config>;
}
/**
* Create a config extension.
* A config extension provides a configuration object for a block flavour.
* The configuration object can be used like:
* ```ts
* const config = std.provider.getOptional(ConfigIdentifier('my-flavour'));
* ```
*
* @param configId The id of the config. Should be unique for each config.
*
* @example
* ```ts
* import { ConfigExtensionFactory } from '@blocksuite/std';
* const MyConfigExtensionFactory = ConfigExtensionFactory<ConfigType>('my-flavour');
* const MyConfigExtension = MyConfigExtensionFactory({
* option1: 'value1',
* option2: 'value2',
* });
* ```
*/
export function ConfigExtensionFactory<Config extends Record<string, any>>(
configId: string
): ConfigFactory<Config> {
const identifier = ConfigIdentifier(configId) as ServiceIdentifier<Config>;
const extensionFactory = (config: Config): ExtensionType => ({
setup: di => {
di.override(ConfigIdentifier(configId), () => {
return config;
});
},
});
extensionFactory.identifier = identifier;
return extensionFactory;
}
@@ -0,0 +1,373 @@
import {
draggable,
dropTargetForElements,
type ElementGetFeedbackArgs,
monitorForElements,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { centerUnderPointer } from '@atlaskit/pragmatic-drag-and-drop/element/center-under-pointer';
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/disable-native-drag-preview';
import { pointerOutsideOfPreview } from '@atlaskit/pragmatic-drag-and-drop/element/pointer-outside-of-preview';
import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
import type { DropTargetRecord } from '@atlaskit/pragmatic-drag-and-drop/types';
import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element';
import {
attachClosestEdge,
type Edge,
extractClosestEdge,
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
import type { ServiceIdentifier } from '@blocksuite/global/di';
import { LifeCycleWatcherIdentifier } from '../../identifier.js';
import { LifeCycleWatcher } from '../lifecycle-watcher.js';
import type {
ElementDragEventBaseArgs,
ElementDragEventMap,
ElementDropEventMap,
ElementDropTargetFeedbackArgs,
ElementMonitorFeedbackArgs,
OriginalAutoScrollOption,
OriginalDraggableOption,
OriginalDropTargetOption,
OriginalMonitorOption,
} from './types.js';
export type DragEntity = { type: string };
export type DragFrom = { at: string };
export type DragFromBlockSuite = {
at: 'blocksuite-editor';
docId: string;
};
export type DragPayload<
E extends DragEntity = DragEntity,
F extends DragFrom = DragFromBlockSuite,
> = {
bsEntity?: E;
from?: F;
};
export type DropPayload<T extends {} = {}> = {
edge?: Edge;
} & T;
export type DropEdge = Edge;
export interface DNDEntity {
basic: DragEntity;
}
export type DraggableOption<
PayloadEntity extends DragEntity,
PayloadFrom extends DragFrom,
DropPayload extends {},
> = Pick<OriginalDraggableOption, 'element' | 'dragHandle' | 'canDrag'> & {
/**
* Set drag data for the draggable element.
* @see {@link ElementGetFeedbackArgs} for callback arguments
* @param callback - callback to set drag
*/
setDragData?: (args: ElementGetFeedbackArgs) => PayloadEntity;
/**
* Set external drag data for the draggable element.
* @param callback - callback to set external drag data
* @see {@link ElementGetFeedbackArgs} for callback arguments
*/
setExternalDragData?: (
args: ElementGetFeedbackArgs
) => ReturnType<
Required<OriginalDraggableOption>['getInitialDataForExternal']
>;
/**
* Set custom drag preview for the draggable element.
*
* `setDragPreview` is a function that will be called with a `container` element and other drag data as parameter when the drag preview is generating.
* Append the custom element to the `container` which will be used to generate the preview. Once the drag preview is generated, the
* `container` element and its children will be removed automatically.
*
* If you want to completely disable the drag preview, just set `setDragPreview` to `false`.
*
* @example
* dnd.draggable({
* // ...
* setDragPreview: ({ container }) => {
* const preview = document.createElement('div');
* preview.style.width = '100px';
* preview.style.height = '100px';
* preview.style.backgroundColor = 'red';
* preview.innerText = 'Custom Drag Preview';
* container.appendChild(preview);
*
* return () => {
* // do some cleanup
* }
* }
* })
*
* @param callback - callback to set custom drag preview
* @returns
*/
setDragPreview?:
| false
| ((
options: ElementDragEventBaseArgs<
DragPayload<PayloadEntity, PayloadFrom>
> & {
/**
* Allows you to use the native `setDragImage` function if you want
* Although, we recommend using alternative techniques (see element adapter docs)
*/
nativeSetDragImage: DataTransfer['setDragImage'] | null;
container: HTMLElement;
setOffset: (
offset: 'preserve' | 'center' | { x: number; y: number }
) => void;
}
) => void | (() => void));
} & ElementDragEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
export type DropTargetOption<
PayloadEntity extends DragEntity,
PayloadFrom extends DragFrom,
DropPayload extends {},
> = {
/**
* {@link OriginalDropTargetOption.element}
*/
element: HTMLElement;
/**
* Allow drop position for the drop target.
*/
allowDropPosition?: Edge[];
/**
* {@link OriginalDropTargetOption.getDropEffect}
*/
getDropEffect?: (
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => DropTargetRecord['dropEffect'];
/**
* {@link OriginalDropTargetOption.canDrop}
*/
canDrop?: (
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => boolean;
/**
* {@link OriginalDropTargetOption.getData}
*/
setDropData?: (
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => DropPayload;
/**
* {@link OriginalDropTargetOption.getIsSticky}
*/
getIsSticky?: (
args: ElementDropTargetFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => boolean;
} & ElementDropEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
export type MonitorOption<
PayloadEntity extends DragEntity,
PayloadFrom extends DragFrom,
DropPayload extends {},
> = {
/**
* {@link OriginalMonitorOption.canMonitor}
*/
canMonitor?: (
args: ElementMonitorFeedbackArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => boolean;
} & ElementDragEventMap<DragPayload<PayloadEntity, PayloadFrom>, DropPayload>;
export type AutoScroll<
PayloadEntity extends DragEntity,
PayloadFrom extends DragFrom,
> = {
element: HTMLElement;
canScroll?: (
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => void;
getAllowedAxis?: (
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => ReturnType<Required<OriginalAutoScrollOption>['getAllowedAxis']>;
getConfiguration?: (
args: ElementDragEventBaseArgs<DragPayload<PayloadEntity, PayloadFrom>>
) => ReturnType<Required<OriginalAutoScrollOption>['getConfiguration']>;
};
export const DndExtensionIdentifier = LifeCycleWatcherIdentifier(
'DndController'
) as ServiceIdentifier<DndController>;
export class DndController extends LifeCycleWatcher {
static override key = 'DndController';
/**
* Make an element draggable.
*/
draggable<
PayloadEntity extends DragEntity = DragEntity,
DropData extends {} = {},
>(
args: DraggableOption<
PayloadEntity,
DragFromBlockSuite,
DropPayload<DropData>
>
) {
const {
setDragData,
setExternalDragData,
setDragPreview,
element,
dragHandle,
...rest
} = args;
return draggable({
...(rest as Partial<OriginalDraggableOption>),
element,
dragHandle,
onGenerateDragPreview: options => {
if (setDragPreview) {
let state: typeof centerUnderPointer | { x: number; y: number };
const setOffset = (
offset: 'preserve' | 'center' | { x: number; y: number }
) => {
if (offset === 'center') {
state = centerUnderPointer;
} else if (offset === 'preserve') {
state = preserveOffsetOnSource({
element: options.source.element,
input: options.location.current.input,
});
} else if (typeof offset === 'object') {
if (
offset.x < 0 ||
offset.y < 0 ||
typeof offset.x === 'string' ||
typeof offset.y === 'string'
) {
state = pointerOutsideOfPreview({
x:
typeof offset.x === 'number'
? `${Math.abs(offset.x)}px`
: offset.x,
y:
typeof offset.y === 'number'
? `${Math.abs(offset.y)}px`
: offset.y,
});
}
state = offset;
}
};
setCustomNativeDragPreview({
getOffset: (...args) => {
if (!state) {
setOffset('center');
}
if (typeof state === 'function') {
return state(...args);
}
return state;
},
render: renderOption => {
setDragPreview({
setOffset,
...options,
...renderOption,
});
},
nativeSetDragImage: options.nativeSetDragImage,
});
} else if (setDragPreview === false) {
disableNativeDragPreview({
nativeSetDragImage: options.nativeSetDragImage,
});
}
},
getInitialData: options => {
const bsEntity = setDragData?.(options) ?? {};
return {
bsEntity,
from: {
at: 'blocksuite-editor',
docId: this.std.store.doc.id,
},
};
},
getInitialDataForExternal: setExternalDragData
? options => {
return setExternalDragData?.(options);
}
: undefined,
});
}
/**
* Make an element a drop target.
*/
dropTarget<
PayloadEntity extends DragEntity = DragEntity,
DropData extends {} = {},
PayloadFrom extends DragFrom = DragFromBlockSuite,
>(args: DropTargetOption<PayloadEntity, PayloadFrom, DropPayload<DropData>>) {
const {
element,
setDropData,
allowDropPosition = ['bottom', 'left', 'top', 'right'],
...rest
} = args;
return dropTargetForElements({
element,
getData: options => {
const data = setDropData?.(options) ?? {};
const edge = extractClosestEdge(
attachClosestEdge(data, {
element: options.element,
input: options.input,
allowedEdges: allowDropPosition,
})
);
return edge
? {
...data,
edge,
}
: data;
},
...(rest as Partial<OriginalDropTargetOption>),
});
}
monitor<
PayloadEntity extends DragEntity = DragEntity,
DropData extends {} = {},
PayloadFrom extends DragFrom = DragFromBlockSuite,
>(args: MonitorOption<PayloadEntity, PayloadFrom, DropPayload<DropData>>) {
return monitorForElements(args as OriginalMonitorOption);
}
autoScroll<
PayloadEntity extends DragEntity = DragEntity,
PayloadFrom extends DragFrom = DragFromBlockSuite,
>(options: AutoScroll<PayloadEntity, PayloadFrom>) {
return autoScrollForElements(options as OriginalAutoScrollOption);
}
}
@@ -0,0 +1,118 @@
import type {
draggable,
dropTargetForElements,
ElementDropTargetGetFeedbackArgs,
ElementMonitorGetFeedbackArgs,
monitorForElements,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type {
DragLocation,
// oxlint-disable-next-line no-unused-vars
DragLocationHistory,
DropTargetRecord,
ElementDragType,
} from '@atlaskit/pragmatic-drag-and-drop/types';
import type { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-scroll/element';
export type ElementDragEventBaseArgs<Payload, DropPayload = {}> = {
/**
* {@link DragLocationHistory} of the drag
*/
location: {
/**
* {@link DragLocationHistory.initial}
*/
initial: DragLocationWithPayload<DropPayload>;
/**
* {@link DragLocationHistory.current}
*/
current: DragLocationWithPayload<DropPayload>;
/**
* {@link DragLocationHistory.previous}
*/
previous: Pick<DragLocationWithPayload<DropPayload>, 'dropTargets'>;
};
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
};
export type DragLocationWithPayload<Payload> = Omit<
DragLocation,
'dropTargets'
> & {
dropTargets: DropTargetRecordWithPayload<Payload>[];
};
export type DropTargetRecordWithPayload<Payload> = Omit<
DropTargetRecord,
'data'
> & {
data: Payload;
};
export type ElementDragEventMap<DragPayload, DropPayload> = {
onDragStart?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload>
) => void;
onDrag?: (data: ElementDragEventBaseArgs<DragPayload, DropPayload>) => void;
onDrop?: (data: ElementDragEventBaseArgs<DragPayload, DropPayload>) => void;
onDropTargetChange?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload>
) => void;
};
type DropTargetLocalizedData = {
self: DropTargetRecord;
};
export type ElementDropTargetFeedbackArgs<Payload> = Omit<
ElementDropTargetGetFeedbackArgs,
'source'
> & {
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
};
export type ElementDropEventMap<DragPayload, DropPayload> = {
onDragStart?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
onDrag?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
onDrop?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
onDropTargetChange?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
onDragEnter?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
onDragLeave?: (
data: ElementDragEventBaseArgs<DragPayload, DropPayload> &
DropTargetLocalizedData
) => void;
};
export type ElementMonitorFeedbackArgs<Payload> = Omit<
ElementMonitorGetFeedbackArgs,
'source'
> & {
source: Omit<ElementDragType['payload'], 'data'> & { data: Payload };
};
export type OriginalDraggableOption = Parameters<typeof draggable>[0];
export type OriginalDropTargetOption = Parameters<
typeof dropTargetForElements
>[0];
export type OriginalMonitorOption = Parameters<typeof monitorForElements>[0];
export type OriginalAutoScrollOption = Parameters<
typeof autoScrollForElements
>[0];
@@ -0,0 +1,49 @@
import { DisposableGroup } from '@blocksuite/global/disposable';
import { Subject } from 'rxjs';
import type { BlockStdScope } from '../scope/std-scope';
import { LifeCycleWatcher } from './lifecycle-watcher';
export class EditorLifeCycleExtension extends LifeCycleWatcher {
static override key = 'editor-life-cycle';
disposables = new DisposableGroup();
readonly slots = {
created: new Subject<void>(),
mounted: new Subject<void>(),
rendered: new Subject<void>(),
unmounted: new Subject<void>(),
};
constructor(override readonly std: BlockStdScope) {
super(std);
this.disposables.add(this.slots.created);
this.disposables.add(this.slots.mounted);
this.disposables.add(this.slots.rendered);
this.disposables.add(this.slots.unmounted);
}
override created() {
super.created();
this.slots.created.next();
}
override mounted() {
super.mounted();
this.slots.mounted.next();
}
override rendered() {
super.rendered();
this.slots.rendered.next();
}
override unmounted() {
super.unmounted();
this.slots.unmounted.next();
this.disposables.dispose();
}
}
@@ -0,0 +1,26 @@
import type { ExtensionType } from '@blocksuite/store';
import { BlockFlavourIdentifier } from '../identifier.js';
/**
* Create a flavour extension.
*
* @param flavour
* The flavour of the block that the extension is for.
*
* @example
* ```ts
* import { FlavourExtension } from '@blocksuite/std';
*
* const MyFlavourExtension = FlavourExtension('my-flavour');
* ```
*/
export function FlavourExtension(flavour: string): ExtensionType {
return {
setup: di => {
di.addImpl(BlockFlavourIdentifier(flavour), () => ({
flavour,
}));
},
};
}
@@ -0,0 +1,10 @@
export * from './block-view.js';
export * from './config.js';
export * from './dnd/index.js';
export * from './editor-life-cycle.js';
export * from './flavour.js';
export * from './keymap.js';
export * from './lifecycle-watcher.js';
export * from './service.js';
export * from './service-manager.js';
export * from './widget-view-map.js';
@@ -0,0 +1,49 @@
import type { ExtensionType } from '@blocksuite/store';
import type { EventOptions, UIEventHandler } from '../event/index.js';
import { KeymapIdentifier } from '../identifier.js';
import type { BlockStdScope } from '../scope/index.js';
let id = 1;
/**
* Create a keymap extension.
*
* @param keymapFactory
* Create keymap of the extension.
* It should return an object with `keymap` and `options`.
*
* `keymap` is a record of keymap.
*
* @param options
* `options` is an optional object that restricts the event to be handled.
*
* @example
* ```ts
* import { KeymapExtension } from '@blocksuite/std';
*
* const MyKeymapExtension = KeymapExtension(std => {
* return {
* keymap: {
* 'mod-a': SelectAll
* }
* options: {
* flavour: 'affine:paragraph'
* }
* }
* });
* ```
*/
export function KeymapExtension(
keymapFactory: (std: BlockStdScope) => Record<string, UIEventHandler>,
options?: EventOptions
): ExtensionType {
return {
setup: di => {
di.addImpl(KeymapIdentifier(`Keymap-${id++}`), {
getter: keymapFactory,
options,
});
},
};
}
@@ -0,0 +1,70 @@
import type { Container } from '@blocksuite/global/di';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { Extension } from '@blocksuite/store';
import { LifeCycleWatcherIdentifier, StdIdentifier } from '../identifier.js';
import type { BlockStdScope } from '../scope/index.js';
/**
* A life cycle watcher is an extension that watches the life cycle of the editor.
* It is used to perform actions when the editor is created, mounted, rendered, or unmounted.
*
* When creating a life cycle watcher, you must define a key that is unique to the watcher.
* The key is used to identify the watcher in the dependency injection container.
* ```ts
* class MyLifeCycleWatcher extends LifeCycleWatcher {
* static override readonly key = 'my-life-cycle-watcher';
* ```
*
* In the life cycle watcher, the methods will be called in the following order:
* 1. `created`: Called when the std is created.
* 2. `rendered`: Called when `std.render` is called.
* 3. `mounted`: Called when the editor host is mounted.
* 4. `unmounted`: Called when the editor host is unmounted.
*/
export abstract class LifeCycleWatcher extends Extension {
static key: string;
constructor(readonly std: BlockStdScope) {
super();
}
static override setup(di: Container) {
if (!this.key) {
throw new BlockSuiteError(
ErrorCode.ValueNotExists,
'Key is not defined in the LifeCycleWatcher'
);
}
di.add(this as unknown as { new (std: BlockStdScope): LifeCycleWatcher }, [
StdIdentifier,
]);
di.addImpl(LifeCycleWatcherIdentifier(this.key), provider =>
provider.get(this)
);
}
/**
* Called when std is created.
*/
created() {}
/**
* Called when editor host is mounted.
* Which means the editor host emit the `connectedCallback` lifecycle event.
*/
mounted() {}
/**
* Called when `std.render` is called.
*/
rendered() {}
/**
* Called when editor host is unmounted.
* Which means the editor host emit the `disconnectedCallback` lifecycle event.
*/
unmounted() {}
}
@@ -0,0 +1,22 @@
import { LifeCycleWatcher } from '../extension/index.js';
import { BlockServiceIdentifier } from '../identifier.js';
export class ServiceManager extends LifeCycleWatcher {
static override readonly key = 'serviceManager';
override mounted() {
super.mounted();
this.std.provider.getAll(BlockServiceIdentifier).forEach(service => {
service.mounted();
});
}
override unmounted() {
super.unmounted();
this.std.provider.getAll(BlockServiceIdentifier).forEach(service => {
service.unmounted();
});
}
}
@@ -0,0 +1,113 @@
import type { Container } from '@blocksuite/global/di';
import { DisposableGroup } from '@blocksuite/global/disposable';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { Extension } from '@blocksuite/store';
import type { EventName, UIEventHandler } from '../event/index.js';
import {
BlockFlavourIdentifier,
BlockServiceIdentifier,
StdIdentifier,
} from '../identifier.js';
import type { BlockStdScope } from '../scope/index.js';
/**
* @deprecated
* BlockService is deprecated. You should reconsider where to put your feature.
*
* BlockService is a legacy extension that is used to provide services to the block.
* In the previous version of BlockSuite, block service provides a way to extend the block.
* However, in the new version, we recommend using the new extension system.
*/
export abstract class BlockService extends Extension {
static flavour: string;
readonly disposables = new DisposableGroup();
readonly flavour: string;
get collection() {
return this.std.workspace;
}
get doc() {
return this.std.store;
}
get host() {
return this.std.host;
}
get selectionManager() {
return this.std.selection;
}
get uiEventDispatcher() {
return this.std.event;
}
constructor(
readonly std: BlockStdScope,
readonly flavourProvider: { flavour: string }
) {
super();
this.flavour = flavourProvider.flavour;
}
static override setup(di: Container) {
if (!this.flavour) {
throw new BlockSuiteError(
ErrorCode.ValueNotExists,
'Flavour is not defined in the BlockService'
);
}
di.add(
this as unknown as {
new (
std: BlockStdScope,
flavourProvider: { flavour: string }
): BlockService;
},
[StdIdentifier, BlockFlavourIdentifier(this.flavour)]
);
di.addImpl(BlockServiceIdentifier(this.flavour), provider =>
provider.get(this)
);
}
bindHotKey(
keymap: Record<string, UIEventHandler>,
options?: { global: boolean }
) {
this.disposables.add(
this.uiEventDispatcher.bindHotkey(keymap, {
flavour: options?.global ? undefined : this.flavour,
})
);
}
// life cycle start
dispose() {
this.disposables.dispose();
}
// event handlers start
handleEvent(
name: EventName,
fn: UIEventHandler,
options?: { global: boolean }
) {
this.disposables.add(
this.uiEventDispatcher.add(name, fn, {
flavour: options?.global ? undefined : this.flavour,
})
);
}
// life cycle end
mounted() {}
unmounted() {
this.disposables.dispose();
}
}
@@ -0,0 +1,40 @@
import type { ExtensionType } from '@blocksuite/store';
import { WidgetViewIdentifier } from '../identifier.js';
import type { WidgetViewType } from '../spec/type.js';
/**
* Create a widget view extension.
*
* @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 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 { WidgetViewExtension } from '@blocksuite/std';
*
* const MyWidgetViewExtension = WidgetViewExtension('my-flavour', 'my-widget', literal`my-widget-view`);
*/
export function WidgetViewExtension(
flavour: string,
id: string,
view: WidgetViewType
): ExtensionType {
return {
setup: di => {
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);
},
};
}