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:
Saul-Mirone
2025-02-14 11:00:01 +00:00
parent 9dc81ecb99
commit d111f8ac88
19 changed files with 324 additions and 313 deletions
@@ -1,7 +1,7 @@
import {
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
WidgetViewExtension,
} from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js';
@@ -14,13 +14,17 @@ import {
import { CodeBlockService } from './code-block-service.js';
import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js';
export const codeToolbarWidget = WidgetViewExtension(
'affine:code',
AFFINE_CODE_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`
);
export const CodeBlockSpec: ExtensionType[] = [
FlavourExtension('affine:code'),
CodeBlockService,
BlockViewExtension('affine:code', literal`affine-code`),
WidgetViewMapExtension('affine:code', {
codeToolbar: literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`,
}),
codeToolbarWidget,
CodeBlockInlineManagerExtension,
CodeBlockUnitSpecExtension,
CodeBlockAdapterExtensions,
@@ -1,7 +1,7 @@
import {
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
WidgetViewExtension,
} from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
@@ -10,6 +10,12 @@ import { ImageBlockAdapterExtensions } from './adapters/extension.js';
import { ImageProxyService } from './image-proxy-service.js';
import { ImageBlockService, ImageDropOption } from './image-service.js';
export const imageToolbarWidget = WidgetViewExtension(
'affine:image',
'imageToolbar',
literal`affine-image-toolbar-widget`
);
export const ImageBlockSpec: ExtensionType[] = [
FlavourExtension('affine:image'),
ImageBlockService,
@@ -22,9 +28,7 @@ export const ImageBlockSpec: ExtensionType[] = [
return literal`affine-image`;
}),
WidgetViewMapExtension('affine:image', {
imageToolbar: literal`affine-image-toolbar-widget`,
}),
imageToolbarWidget,
ImageDropOption,
ImageBlockAdapterExtensions,
].flat();
@@ -1,9 +1,6 @@
export * from './commands.js';
export * from './surface-ref-block.js';
export * from './surface-ref-block-edgeless.js';
export {
EdgelessSurfaceRefBlockSpec,
PageSurfaceRefBlockSpec,
} from './surface-ref-spec.js';
export * from './surface-ref-spec.js';
export * from './types.js';
export * from './utils.js';
@@ -1,17 +1,21 @@
import {
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
WidgetViewExtension,
} from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js';
export const surfaceRefToolbarWidget = WidgetViewExtension(
'affine:surface-ref',
'surfaceToolbar',
literal`affine-surface-ref-toolbar`
);
export const PageSurfaceRefBlockSpec: ExtensionType[] = [
FlavourExtension('affine:surface-ref'),
BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`),
WidgetViewMapExtension('affine:surface-ref', {
surfaceToolbar: literal`affine-surface-ref-toolbar`,
}),
surfaceRefToolbarWidget,
];
export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [
@@ -9,15 +9,9 @@ import {
type BlockStdScope,
ConfigIdentifier,
LifeCycleWatcher,
WidgetViewMapIdentifier,
type WidgetViewMapType,
} from '@blocksuite/block-std';
import type { Container } from '@blocksuite/global/di';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../../root-block/widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../../root-block/widgets/format-bar/format-bar.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../../root-block/widgets/slash-menu/index.js';
export class MobileSpecsPatches extends LifeCycleWatcher {
static override key = 'mobile-patches';
@@ -54,48 +48,6 @@ export class MobileSpecsPatches extends LifeCycleWatcher {
} satisfies CodeBlockConfig;
});
}
// Disable root level widgets for mobile.
{
const rootWidgetViewMapIdentifier =
WidgetViewMapIdentifier('affine:page');
const prev = di.getFactory(rootWidgetViewMapIdentifier);
di.override(rootWidgetViewMapIdentifier, provider => {
const ignoreWidgets = [
AFFINE_FORMAT_BAR_WIDGET,
AFFINE_EMBED_CARD_TOOLBAR_WIDGET,
AFFINE_SLASH_MENU_WIDGET,
];
const newMap = { ...prev?.(provider) };
ignoreWidgets.forEach(widget => {
if (widget in newMap) delete newMap[widget];
});
return newMap;
});
}
// Disable block level toolbar widgets for mobile.
{
di.override(
WidgetViewMapIdentifier('affine:code'),
(): WidgetViewMapType => ({})
);
di.override(
WidgetViewMapIdentifier('affine:image'),
(): WidgetViewMapType => ({})
);
di.override(
WidgetViewMapIdentifier('affine:surface-ref'),
(): WidgetViewMapType => ({})
);
}
}
override mounted() {
@@ -0,0 +1,50 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager';
import { RootBlockAdapterExtensions } from '../adapters/extension';
import {
docRemoteSelectionWidget,
dragHandleWidget,
embedCardToolbarWidget,
formatBarWidget,
innerModalWidget,
linkedDocWidget,
modalWidget,
scrollAnchoringWidget,
slashMenuWidget,
viewportOverlayWidget,
} from './widgets';
export const CommonSpecs: ExtensionType[] = [
FlavourExtension('affine:page'),
DocModeService,
ThemeService,
EmbedOptionService,
ExportManagerExtension,
PageViewportServiceExtension,
DNDAPIExtension,
FileDropExtension,
...RootBlockAdapterExtensions,
modalWidget,
innerModalWidget,
slashMenuWidget,
linkedDocWidget,
dragHandleWidget,
embedCardToolbarWidget,
formatBarWidget,
docRemoteSelectionWidget,
viewportOverlayWidget,
scrollAnchoringWidget,
];
export * from './widgets';
@@ -0,0 +1,64 @@
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import { WidgetViewExtension } from '@blocksuite/block-std';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
export const modalWidget = WidgetViewExtension(
'affine:page',
AFFINE_MODAL_WIDGET,
literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`
);
export const innerModalWidget = WidgetViewExtension(
'affine:page',
AFFINE_INNER_MODAL_WIDGET,
literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`
);
export const slashMenuWidget = WidgetViewExtension(
'affine:page',
AFFINE_SLASH_MENU_WIDGET,
literal`${unsafeStatic(AFFINE_SLASH_MENU_WIDGET)}`
);
export const linkedDocWidget = WidgetViewExtension(
'affine:page',
AFFINE_LINKED_DOC_WIDGET,
literal`${unsafeStatic(AFFINE_LINKED_DOC_WIDGET)}`
);
export const dragHandleWidget = WidgetViewExtension(
'affine:page',
AFFINE_DRAG_HANDLE_WIDGET,
literal`${unsafeStatic(AFFINE_DRAG_HANDLE_WIDGET)}`
);
export const embedCardToolbarWidget = WidgetViewExtension(
'affine:page',
AFFINE_EMBED_CARD_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_EMBED_CARD_TOOLBAR_WIDGET)}`
);
export const formatBarWidget = WidgetViewExtension(
'affine:page',
AFFINE_FORMAT_BAR_WIDGET,
literal`${unsafeStatic(AFFINE_FORMAT_BAR_WIDGET)}`
);
export const docRemoteSelectionWidget = WidgetViewExtension(
'affine:page',
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
literal`${unsafeStatic(AFFINE_DOC_REMOTE_SELECTION_WIDGET)}`
);
export const viewportOverlayWidget = WidgetViewExtension(
'affine:page',
AFFINE_VIEWPORT_OVERLAY_WIDGET,
literal`${unsafeStatic(AFFINE_VIEWPORT_OVERLAY_WIDGET)}`
);
export const scrollAnchoringWidget = WidgetViewExtension(
'affine:page',
AFFINE_SCROLL_ANCHORING_WIDGET,
literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`
);
@@ -1,40 +1,18 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_EDGELESS_AUTO_CONNECT_WIDGET } from '@blocksuite/affine-widget-edgeless-auto-connect';
import { AFFINE_FRAME_TITLE_WIDGET } from '@blocksuite/affine-widget-frame-title';
import {
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
} from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import { AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
import {
BlockServiceWatcher,
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
WidgetViewExtension,
} from '@blocksuite/block-std';
import { ToolController } from '@blocksuite/block-std/gfx';
import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js';
import { RootBlockAdapterExtensions } from '../adapters/extension.js';
import { CommonSpecs } from '../common-specs/index.js';
import { AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET } from '../widgets/edgeless-zoom-toolbar/index.js';
import { EDGELESS_ELEMENT_TOOLBAR_WIDGET } from '../widgets/element-toolbar/index.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
import { NOTE_SLICER_WIDGET } from './components/note-slicer/index.js';
import { EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET } from './components/presentation/edgeless-navigator-black-background.js';
import { EDGELESS_DRAGGING_AREA_WIDGET } from './components/rects/edgeless-dragging-area-rect.js';
@@ -42,68 +20,56 @@ import { EDGELESS_SELECTED_RECT_WIDGET } from './components/rects/edgeless-selec
import { EDGELESS_TOOLBAR_WIDGET } from './components/toolbar/edgeless-toolbar.js';
import { EdgelessRootService } from './edgeless-root-service.js';
export const edgelessRootWidgetViewMap = {
[AFFINE_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`,
[AFFINE_INNER_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`,
[AFFINE_SLASH_MENU_WIDGET]: literal`${unsafeStatic(
AFFINE_SLASH_MENU_WIDGET
)}`,
[AFFINE_LINKED_DOC_WIDGET]: literal`${unsafeStatic(
AFFINE_LINKED_DOC_WIDGET
)}`,
[AFFINE_DRAG_HANDLE_WIDGET]: literal`${unsafeStatic(
AFFINE_DRAG_HANDLE_WIDGET
)}`,
[AFFINE_EMBED_CARD_TOOLBAR_WIDGET]: literal`${unsafeStatic(
AFFINE_EMBED_CARD_TOOLBAR_WIDGET
)}`,
[AFFINE_FORMAT_BAR_WIDGET]: literal`${unsafeStatic(
AFFINE_FORMAT_BAR_WIDGET
)}`,
[AFFINE_DOC_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic(
AFFINE_DOC_REMOTE_SELECTION_WIDGET
)}`,
[AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic(
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET
)}`,
[AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET]: literal`${unsafeStatic(
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET
)}`,
[AFFINE_FRAME_TITLE_WIDGET]: literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`,
[EDGELESS_ELEMENT_TOOLBAR_WIDGET]: literal`${unsafeStatic(EDGELESS_ELEMENT_TOOLBAR_WIDGET)}`,
[AFFINE_VIEWPORT_OVERLAY_WIDGET]: literal`${unsafeStatic(
AFFINE_VIEWPORT_OVERLAY_WIDGET
)}`,
[AFFINE_EDGELESS_AUTO_CONNECT_WIDGET]: literal`${unsafeStatic(
AFFINE_EDGELESS_AUTO_CONNECT_WIDGET
)}`,
[AFFINE_SCROLL_ANCHORING_WIDGET]: literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`,
[EDGELESS_DRAGGING_AREA_WIDGET]: literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`,
[NOTE_SLICER_WIDGET]: literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`,
[EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET]: literal`${unsafeStatic(EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET)}`,
[EDGELESS_SELECTED_RECT_WIDGET]: literal`${unsafeStatic(EDGELESS_SELECTED_RECT_WIDGET)}`,
[EDGELESS_TOOLBAR_WIDGET]: literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}`,
};
const EdgelessCommonExtension: ExtensionType[] = [
FlavourExtension('affine:page'),
EdgelessRootService,
DocModeService,
ThemeService,
EmbedOptionService,
ExportManagerExtension,
ToolController,
DNDAPIExtension,
PageViewportServiceExtension,
RootBlockAdapterExtensions,
FileDropExtension,
].flat();
export const EdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root`),
WidgetViewMapExtension('affine:page', edgelessRootWidgetViewMap),
];
export const edgelessRemoteSelectionWidget = WidgetViewExtension(
'affine:page',
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
literal`${unsafeStatic(AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET)}`
);
export const edgelessZoomToolbarWidget = WidgetViewExtension(
'affine:page',
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET)}`
);
export const frameTitleWidget = WidgetViewExtension(
'affine:page',
AFFINE_FRAME_TITLE_WIDGET,
literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`
);
export const elementToolbarWidget = WidgetViewExtension(
'affine:page',
EDGELESS_ELEMENT_TOOLBAR_WIDGET,
literal`${unsafeStatic(EDGELESS_ELEMENT_TOOLBAR_WIDGET)}`
);
export const autoConnectWidget = WidgetViewExtension(
'affine:page',
AFFINE_EDGELESS_AUTO_CONNECT_WIDGET,
literal`${unsafeStatic(AFFINE_EDGELESS_AUTO_CONNECT_WIDGET)}`
);
export const edgelessDraggingAreaWidget = WidgetViewExtension(
'affine:page',
EDGELESS_DRAGGING_AREA_WIDGET,
literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`
);
export const noteSlicerWidget = WidgetViewExtension(
'affine:page',
NOTE_SLICER_WIDGET,
literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`
);
export const edgelessNavigatorBlackBackgroundWidget = WidgetViewExtension(
'affine:page',
EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET,
literal`${unsafeStatic(EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET)}`
);
export const edgelessSelectedRectWidget = WidgetViewExtension(
'affine:page',
EDGELESS_SELECTED_RECT_WIDGET,
literal`${unsafeStatic(EDGELESS_SELECTED_RECT_WIDGET)}`
);
export const edgelessToolbarWidget = WidgetViewExtension(
'affine:page',
EDGELESS_TOOLBAR_WIDGET,
literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}`
);
class EdgelessLocker extends BlockServiceWatcher {
static override readonly flavour = 'affine:page';
@@ -119,6 +85,27 @@ class EdgelessLocker extends BlockServiceWatcher {
}
}
const EdgelessCommonExtension: ExtensionType[] = [
CommonSpecs,
ToolController,
EdgelessRootService,
].flat();
export const EdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root`),
edgelessRemoteSelectionWidget,
edgelessZoomToolbarWidget,
frameTitleWidget,
elementToolbarWidget,
autoConnectWidget,
edgelessDraggingAreaWidget,
noteSlicerWidget,
edgelessNavigatorBlackBackgroundWidget,
edgelessSelectedRectWidget,
edgelessToolbarWidget,
];
export const PreviewEdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root-preview`),
@@ -1,5 +1,6 @@
export * from './adapters/markdown.js';
export * from './clipboard/index.js';
export * from './common-specs/index.js';
export * from './edgeless/edgeless-root-spec.js';
export * from './edgeless/index.js';
export { TemplateJob } from './edgeless/services/template.js';
@@ -1,83 +1,34 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import {
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
} from '@blocksuite/block-std';
import { BlockViewExtension, WidgetViewExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js';
import { RootBlockAdapterExtensions } from '../adapters/extension.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { CommonSpecs } from '../common-specs/index.js';
import { AFFINE_KEYBOARD_TOOLBAR_WIDGET } from '../widgets/keyboard-toolbar/index.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_PAGE_DRAGGING_AREA_WIDGET } from '../widgets/page-dragging-area/page-dragging-area.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
import { PageRootService } from './page-root-service.js';
export const pageRootWidgetViewMap = {
[AFFINE_KEYBOARD_TOOLBAR_WIDGET]: literal`${unsafeStatic(AFFINE_KEYBOARD_TOOLBAR_WIDGET)}`,
[AFFINE_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`,
[AFFINE_INNER_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`,
[AFFINE_SLASH_MENU_WIDGET]: literal`${unsafeStatic(
AFFINE_SLASH_MENU_WIDGET
)}`,
[AFFINE_LINKED_DOC_WIDGET]: literal`${unsafeStatic(
AFFINE_LINKED_DOC_WIDGET
)}`,
[AFFINE_DRAG_HANDLE_WIDGET]: literal`${unsafeStatic(
AFFINE_DRAG_HANDLE_WIDGET
)}`,
[AFFINE_EMBED_CARD_TOOLBAR_WIDGET]: literal`${unsafeStatic(
AFFINE_EMBED_CARD_TOOLBAR_WIDGET
)}`,
[AFFINE_FORMAT_BAR_WIDGET]: literal`${unsafeStatic(
AFFINE_FORMAT_BAR_WIDGET
)}`,
[AFFINE_DOC_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic(
AFFINE_DOC_REMOTE_SELECTION_WIDGET
)}`,
[AFFINE_PAGE_DRAGGING_AREA_WIDGET]: literal`${unsafeStatic(
AFFINE_PAGE_DRAGGING_AREA_WIDGET
)}`,
[AFFINE_VIEWPORT_OVERLAY_WIDGET]: literal`${unsafeStatic(
AFFINE_VIEWPORT_OVERLAY_WIDGET
)}`,
[AFFINE_SCROLL_ANCHORING_WIDGET]: literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`,
};
export const keyboardToolbarWidget = WidgetViewExtension(
'affine:page',
AFFINE_KEYBOARD_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_KEYBOARD_TOOLBAR_WIDGET)}`
);
export const pageDraggingAreaWidget = WidgetViewExtension(
'affine:page',
AFFINE_PAGE_DRAGGING_AREA_WIDGET,
literal`${unsafeStatic(AFFINE_PAGE_DRAGGING_AREA_WIDGET)}`
);
const PageCommonExtension: ExtensionType[] = [
FlavourExtension('affine:page'),
CommonSpecs,
PageRootService,
DocModeService,
ThemeService,
EmbedOptionService,
PageViewportServiceExtension,
];
pageDraggingAreaWidget,
].flat();
export const PageRootBlockSpec: ExtensionType[] = [
...PageCommonExtension,
BlockViewExtension('affine:page', literal`affine-page-root`),
WidgetViewMapExtension('affine:page', pageRootWidgetViewMap),
ExportManagerExtension,
DNDAPIExtension,
RootBlockAdapterExtensions,
FileDropExtension,
keyboardToolbarWidget,
].flat();
export const PreviewPageRootBlockSpec: ExtensionType[] = [
@@ -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 => {
+2 -6
View File
@@ -1,9 +1,6 @@
import '../../style.css';
import {
WidgetViewMapExtension,
WidgetViewMapIdentifier,
} from '@blocksuite/block-std';
import * as blockStd from '@blocksuite/block-std';
import * as blocks from '@blocksuite/blocks';
import {
CommunityCanvasTextFonts,
@@ -52,8 +49,8 @@ async function main() {
blocks,
global: { utils: globalUtils },
editor,
blockStd: blockStd,
identifiers: {
WidgetViewMapIdentifier,
QuickSearchProvider,
DocModeProvider,
RefNodeSlotsProvider,
@@ -65,7 +62,6 @@ async function main() {
],
extensions: {
FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts),
WidgetViewMapExtension,
},
mockServices: {
mockDocModeService,
+6 -10
View File
@@ -794,13 +794,11 @@ test.describe('slash menu with customize menu', () => {
{
setup: di => {
di.override(
window.$blocksuite.identifiers.WidgetViewMapIdentifier(
'affine:page'
window.$blocksuite.blockStd.WidgetViewIdentifier(
'affine:page|affine-slash-menu-widget'
),
// @ts-ignore
() => ({
'affine-slash-menu-widget': fakeLiteral`affine-custom-slash-menu`,
})
fakeLiteral`affine-custom-slash-menu`
);
},
},
@@ -869,13 +867,11 @@ test.describe('slash menu with customize menu', () => {
{
setup: di =>
di.override(
window.$blocksuite.identifiers.WidgetViewMapIdentifier(
'affine:page'
window.$blocksuite.blockStd.WidgetViewIdentifier(
'affine:page|affine-slash-menu-widget'
),
// @ts-ignore
() => ({
'affine-slash-menu-widget': fakeLiteral`affine-custom-slash-menu`,
})
fakeLiteral`affine-custom-slash-menu`
),
},
];
@@ -5,11 +5,7 @@ import type {
QuickSearchProvider,
ThemeProvider,
} from '@blocksuite/affine-shared/services';
import type {
EditorHost,
WidgetViewMapExtension,
WidgetViewMapIdentifier,
} from '@blocksuite/block-std';
import type { EditorHost } from '@blocksuite/block-std';
import type { RefNodeSlotsProvider, TestUtils } from '@blocksuite/blocks';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type {
@@ -32,8 +28,8 @@ declare global {
utils: typeof import('@blocksuite/global/utils');
};
editor: typeof import('@blocksuite/presets');
blockStd: typeof import('@blocksuite/block-std');
identifiers: {
WidgetViewMapIdentifier: typeof WidgetViewMapIdentifier;
QuickSearchProvider: typeof QuickSearchProvider;
DocModeProvider: typeof DocModeProvider;
ThemeProvider: typeof ThemeProvider;
@@ -41,9 +37,6 @@ declare global {
ParseDocUrlService: typeof ParseDocUrlProvider;
};
defaultExtensions: () => ExtensionType[];
extensions: {
WidgetViewMapExtension: typeof WidgetViewMapExtension;
};
mockServices: {
mockDocModeService: typeof DocModeService;
};
@@ -1,6 +1,6 @@
import {
BlockServiceWatcher,
WidgetViewMapIdentifier,
WidgetViewExtension,
} from '@blocksuite/affine/block-std';
import {
AFFINE_AI_PANEL_WIDGET,
@@ -14,10 +14,8 @@ import {
EdgelessCopilotWidget,
EdgelessElementToolbarWidget,
EdgelessRootBlockSpec,
edgelessRootWidgetViewMap,
ImageBlockSpec,
PageRootBlockSpec,
pageRootWidgetViewMap,
ParagraphBlockService,
ParagraphBlockSpec,
} from '@blocksuite/affine/blocks';
@@ -63,25 +61,22 @@ function getAIPageRootWatcher(framework: FrameworkProvider) {
return AIPageRootWatcher;
}
const aiPanelWidget = WidgetViewExtension(
'affine:page',
AFFINE_AI_PANEL_WIDGET,
literal`${unsafeStatic(AFFINE_AI_PANEL_WIDGET)}`
);
const edgelessCopilotWidget = WidgetViewExtension(
'affine:page',
AFFINE_EDGELESS_COPILOT_WIDGET,
literal`${unsafeStatic(AFFINE_EDGELESS_COPILOT_WIDGET)}`
);
export function createAIPageRootBlockSpec(
framework: FrameworkProvider
): ExtensionType[] {
return [
...PageRootBlockSpec,
getAIPageRootWatcher(framework),
{
setup: di => {
di.override(WidgetViewMapIdentifier('affine:page'), () => {
return {
...pageRootWidgetViewMap,
[AFFINE_AI_PANEL_WIDGET]: literal`${unsafeStatic(
AFFINE_AI_PANEL_WIDGET
)}`,
};
});
},
},
];
return [...PageRootBlockSpec, aiPanelWidget, getAIPageRootWatcher(framework)];
}
function getAIEdgelessRootWatcher(framework: FrameworkProvider) {
@@ -123,22 +118,9 @@ export function createAIEdgelessRootBlockSpec(
): ExtensionType[] {
return [
...EdgelessRootBlockSpec,
aiPanelWidget,
edgelessCopilotWidget,
getAIEdgelessRootWatcher(framework),
{
setup: di => {
di.override(WidgetViewMapIdentifier('affine:page'), () => {
return {
...edgelessRootWidgetViewMap,
[AFFINE_EDGELESS_COPILOT_WIDGET]: literal`${unsafeStatic(
AFFINE_EDGELESS_COPILOT_WIDGET
)}`,
[AFFINE_AI_PANEL_WIDGET]: literal`${unsafeStatic(
AFFINE_AI_PANEL_WIDGET
)}`,
};
});
},
},
];
}
@@ -17,7 +17,15 @@ import { toURLSearchParams } from '@affine/core/modules/navigation';
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
import { WorkspaceService } from '@affine/core/modules/workspace';
import track from '@affine/track';
import type { DocMode } from '@blocksuite/affine/blocks';
import {
codeToolbarWidget,
type DocMode,
embedCardToolbarWidget,
formatBarWidget,
imageToolbarWidget,
slashMenuWidget,
surfaceRefToolbarWidget,
} from '@blocksuite/affine/blocks';
import {
DocTitle,
EdgelessEditor,
@@ -173,6 +181,12 @@ const usePatchSpecs = (mode: DocMode) => {
builder.extend([patchForAttachmentEmbedViews(reactToLit)]);
}
if (BUILD_CONFIG.isMobileEdition) {
builder.omit(formatBarWidget);
builder.omit(embedCardToolbarWidget);
builder.omit(slashMenuWidget);
builder.omit(codeToolbarWidget);
builder.omit(imageToolbarWidget);
builder.omit(surfaceRefToolbarWidget);
builder.extend([patchForMobile()].flat());
}
if (BUILD_CONFIG.isElectron) {