refactor(editor): extensionalize surface canvas renderer (#11456)
This commit is contained in:
@@ -2,8 +2,6 @@ import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame';
|
|||||||
import {
|
import {
|
||||||
EdgelessCRUDIdentifier,
|
EdgelessCRUDIdentifier,
|
||||||
EdgelessLegacySlotIdentifier,
|
EdgelessLegacySlotIdentifier,
|
||||||
type ElementRenderer,
|
|
||||||
elementRenderers,
|
|
||||||
getSurfaceBlock,
|
getSurfaceBlock,
|
||||||
type SurfaceBlockModel,
|
type SurfaceBlockModel,
|
||||||
type SurfaceContext,
|
type SurfaceContext,
|
||||||
@@ -48,8 +46,6 @@ export class EdgelessRootService extends RootService implements SurfaceContext {
|
|||||||
|
|
||||||
private readonly _surface: SurfaceBlockModel;
|
private readonly _surface: SurfaceBlockModel;
|
||||||
|
|
||||||
elementRenderers: Record<string, ElementRenderer> = elementRenderers;
|
|
||||||
|
|
||||||
TemplateJob = TemplateJob;
|
TemplateJob = TemplateJob;
|
||||||
|
|
||||||
get blocks(): GfxBlockElementModel[] {
|
get blocks(): GfxBlockElementModel[] {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
createIdentifier,
|
||||||
|
type ServiceIdentifier,
|
||||||
|
} from '@blocksuite/global/di';
|
||||||
|
import type {
|
||||||
|
GfxLocalElementModel,
|
||||||
|
GfxPrimitiveElementModel,
|
||||||
|
} from '@blocksuite/std/gfx';
|
||||||
|
import type { ExtensionType } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import type { ElementRenderer } from '../renderer/elements';
|
||||||
|
|
||||||
|
export const ElementRendererIdentifier =
|
||||||
|
createIdentifier<unknown>('element-renderer');
|
||||||
|
|
||||||
|
export const ElementRendererExtension = <
|
||||||
|
T extends GfxPrimitiveElementModel | GfxLocalElementModel,
|
||||||
|
>(
|
||||||
|
id: string,
|
||||||
|
renderer: ElementRenderer<T>
|
||||||
|
): ExtensionType & {
|
||||||
|
identifier: ServiceIdentifier<ElementRenderer<T>>;
|
||||||
|
} => {
|
||||||
|
const identifier = ElementRendererIdentifier<ElementRenderer<T>>(id);
|
||||||
|
return {
|
||||||
|
setup: di => {
|
||||||
|
di.addImpl(identifier, () => renderer);
|
||||||
|
},
|
||||||
|
identifier,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from './clipboard-config';
|
export * from './clipboard-config';
|
||||||
export * from './crud-extension';
|
export * from './crud-extension';
|
||||||
|
export * from './element-renderer';
|
||||||
export * from './export-manager';
|
export * from './export-manager';
|
||||||
export * from './legacy-slot-extension';
|
export * from './legacy-slot-extension';
|
||||||
export * from './query';
|
export * from './query';
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ export {
|
|||||||
export { CanvasRenderer } from './renderer/canvas-renderer.js';
|
export { CanvasRenderer } from './renderer/canvas-renderer.js';
|
||||||
export * from './renderer/elements/group/consts.js';
|
export * from './renderer/elements/group/consts.js';
|
||||||
export type { ElementRenderer } from './renderer/elements/index.js';
|
export type { ElementRenderer } from './renderer/elements/index.js';
|
||||||
export {
|
export { normalizeShapeBound } from './renderer/elements/index.js';
|
||||||
elementRenderers,
|
|
||||||
normalizeShapeBound,
|
|
||||||
} from './renderer/elements/index.js';
|
|
||||||
export { fitContent } from './renderer/elements/shape/utils.js';
|
export { fitContent } from './renderer/elements/shape/utils.js';
|
||||||
export * from './renderer/elements/type.js';
|
export * from './renderer/elements/type.js';
|
||||||
export { Overlay, OverlayIdentifier } from './renderer/overlay.js';
|
export { Overlay, OverlayIdentifier } from './renderer/overlay.js';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { requestConnectedFrame } from '@blocksuite/affine-shared/utils';
|
|||||||
import { DisposableGroup } from '@blocksuite/global/disposable';
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
import type { IBound } from '@blocksuite/global/gfx';
|
import type { IBound } from '@blocksuite/global/gfx';
|
||||||
import { getBoundWithRotation, intersects } from '@blocksuite/global/gfx';
|
import { getBoundWithRotation, intersects } from '@blocksuite/global/gfx';
|
||||||
|
import type { BlockStdScope } from '@blocksuite/std';
|
||||||
import type {
|
import type {
|
||||||
GridManager,
|
GridManager,
|
||||||
LayerManager,
|
LayerManager,
|
||||||
@@ -13,6 +14,7 @@ import last from 'lodash-es/last';
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
import type { SurfaceElementModel } from '../element-model/base.js';
|
import type { SurfaceElementModel } from '../element-model/base.js';
|
||||||
|
import { ElementRendererIdentifier } from '../extensions/element-renderer.js';
|
||||||
import { RoughCanvas } from '../utils/rough/canvas.js';
|
import { RoughCanvas } from '../utils/rough/canvas.js';
|
||||||
import type { ElementRenderer } from './elements/index.js';
|
import type { ElementRenderer } from './elements/index.js';
|
||||||
import type { Overlay } from './overlay.js';
|
import type { Overlay } from './overlay.js';
|
||||||
@@ -26,12 +28,12 @@ type EnvProvider = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type RendererOptions = {
|
type RendererOptions = {
|
||||||
|
std: BlockStdScope;
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
layerManager: LayerManager;
|
layerManager: LayerManager;
|
||||||
provider?: Partial<EnvProvider>;
|
provider?: Partial<EnvProvider>;
|
||||||
enableStackingCanvas?: boolean;
|
enableStackingCanvas?: boolean;
|
||||||
onStackingCanvasCreated?: (canvas: HTMLCanvasElement) => void;
|
onStackingCanvasCreated?: (canvas: HTMLCanvasElement) => void;
|
||||||
elementRenderers: Record<string, ElementRenderer>;
|
|
||||||
gridManager: GridManager;
|
gridManager: GridManager;
|
||||||
surfaceModel: SurfaceBlockModel;
|
surfaceModel: SurfaceBlockModel;
|
||||||
};
|
};
|
||||||
@@ -51,7 +53,7 @@ export class CanvasRenderer {
|
|||||||
|
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
elementRenderers: Record<string, ElementRenderer>;
|
std: BlockStdScope;
|
||||||
|
|
||||||
grid: GridManager;
|
grid: GridManager;
|
||||||
|
|
||||||
@@ -76,11 +78,11 @@ export class CanvasRenderer {
|
|||||||
|
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||||
|
this.std = options.std;
|
||||||
this.viewport = options.viewport;
|
this.viewport = options.viewport;
|
||||||
this.layerManager = options.layerManager;
|
this.layerManager = options.layerManager;
|
||||||
this.grid = options.gridManager;
|
this.grid = options.gridManager;
|
||||||
this.provider = options.provider ?? {};
|
this.provider = options.provider ?? {};
|
||||||
this.elementRenderers = options.elementRenderers;
|
|
||||||
this._initViewport();
|
this._initViewport();
|
||||||
|
|
||||||
options.enableStackingCanvas = options.enableStackingCanvas ?? false;
|
options.enableStackingCanvas = options.enableStackingCanvas ?? false;
|
||||||
@@ -277,10 +279,9 @@ export class CanvasRenderer {
|
|||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
const display = (element.display ?? true) && !element.hidden;
|
const display = (element.display ?? true) && !element.hidden;
|
||||||
if (display && intersects(getBoundWithRotation(element), bound)) {
|
if (display && intersects(getBoundWithRotation(element), bound)) {
|
||||||
const renderFn =
|
const renderFn = this.std.getOptional<ElementRenderer>(
|
||||||
this.elementRenderers[
|
ElementRendererIdentifier(element.type)
|
||||||
element.type as keyof typeof this.elementRenderers
|
);
|
||||||
];
|
|
||||||
|
|
||||||
if (!renderFn) {
|
if (!renderFn) {
|
||||||
console.warn(`Cannot find renderer for ${element.type}`);
|
console.warn(`Cannot find renderer for ${element.type}`);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import type { IBound } from '@blocksuite/global/gfx';
|
import type { IBound } from '@blocksuite/global/gfx';
|
||||||
import type { GfxPrimitiveElementModel } from '@blocksuite/std/gfx';
|
import type {
|
||||||
|
GfxLocalElementModel,
|
||||||
|
GfxPrimitiveElementModel,
|
||||||
|
} from '@blocksuite/std/gfx';
|
||||||
|
|
||||||
|
import { ElementRendererExtension } from '../../extensions/element-renderer.js';
|
||||||
import type { RoughCanvas } from '../../index.js';
|
import type { RoughCanvas } from '../../index.js';
|
||||||
import type { CanvasRenderer } from '../canvas-renderer.js';
|
import type { CanvasRenderer } from '../canvas-renderer.js';
|
||||||
import { brush } from './brush/index.js';
|
import { brush } from './brush/index.js';
|
||||||
@@ -13,7 +17,9 @@ import { text } from './text/index.js';
|
|||||||
export { normalizeShapeBound } from './shape/utils.js';
|
export { normalizeShapeBound } from './shape/utils.js';
|
||||||
|
|
||||||
export type ElementRenderer<
|
export type ElementRenderer<
|
||||||
T extends GfxPrimitiveElementModel = GfxPrimitiveElementModel,
|
T extends
|
||||||
|
| GfxPrimitiveElementModel
|
||||||
|
| GfxLocalElementModel = GfxPrimitiveElementModel,
|
||||||
> = (
|
> = (
|
||||||
model: T,
|
model: T,
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
@@ -23,12 +29,47 @@ export type ElementRenderer<
|
|||||||
viewportBound: IBound
|
viewportBound: IBound
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
export const elementRenderers = {
|
export const BrushElementRendererExtension = ElementRendererExtension(
|
||||||
brush,
|
'brush',
|
||||||
highlighter,
|
brush
|
||||||
connector,
|
);
|
||||||
group,
|
|
||||||
shape,
|
export const HighlighterElementRendererExtension = ElementRendererExtension(
|
||||||
text,
|
'highlighter',
|
||||||
mindmap,
|
highlighter
|
||||||
} as Record<string, ElementRenderer<any>>;
|
);
|
||||||
|
|
||||||
|
export const ConnectorElementRendererExtension = ElementRendererExtension(
|
||||||
|
'connector',
|
||||||
|
connector
|
||||||
|
);
|
||||||
|
|
||||||
|
export const GroupElementRendererExtension = ElementRendererExtension(
|
||||||
|
'group',
|
||||||
|
group
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ShapeElementRendererExtension = ElementRendererExtension(
|
||||||
|
'shape',
|
||||||
|
shape
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TextElementRendererExtension = ElementRendererExtension(
|
||||||
|
'text',
|
||||||
|
text
|
||||||
|
);
|
||||||
|
|
||||||
|
export const MindmapElementRendererExtension = ElementRendererExtension(
|
||||||
|
'mindmap',
|
||||||
|
mindmap
|
||||||
|
);
|
||||||
|
|
||||||
|
export const elementRendererExtensions = [
|
||||||
|
BrushElementRendererExtension,
|
||||||
|
HighlighterElementRendererExtension,
|
||||||
|
ConnectorElementRendererExtension,
|
||||||
|
GroupElementRendererExtension,
|
||||||
|
ShapeElementRendererExtension,
|
||||||
|
TextElementRendererExtension,
|
||||||
|
MindmapElementRendererExtension,
|
||||||
|
];
|
||||||
|
|||||||
@@ -11,17 +11,12 @@ import type { Subject } from 'rxjs';
|
|||||||
|
|
||||||
import { ConnectorElementModel } from './element-model/index.js';
|
import { ConnectorElementModel } from './element-model/index.js';
|
||||||
import { CanvasRenderer } from './renderer/canvas-renderer.js';
|
import { CanvasRenderer } from './renderer/canvas-renderer.js';
|
||||||
import {
|
|
||||||
type ElementRenderer,
|
|
||||||
elementRenderers,
|
|
||||||
} from './renderer/elements/index.js';
|
|
||||||
import { OverlayIdentifier } from './renderer/overlay.js';
|
import { OverlayIdentifier } from './renderer/overlay.js';
|
||||||
import type { SurfaceBlockModel } from './surface-model.js';
|
import type { SurfaceBlockModel } from './surface-model.js';
|
||||||
|
|
||||||
export interface SurfaceContext {
|
export interface SurfaceContext {
|
||||||
viewport: Viewport;
|
viewport: Viewport;
|
||||||
host: EditorHost;
|
host: EditorHost;
|
||||||
elementRenderers: Record<string, ElementRenderer>;
|
|
||||||
selection: {
|
selection: {
|
||||||
selectedIds: string[];
|
selectedIds: string[];
|
||||||
slots: {
|
slots: {
|
||||||
@@ -148,6 +143,7 @@ export class SurfaceBlockComponent extends BlockComponent<SurfaceBlockModel> {
|
|||||||
const themeService = this.std.get(ThemeProvider);
|
const themeService = this.std.get(ThemeProvider);
|
||||||
|
|
||||||
this._renderer = new CanvasRenderer({
|
this._renderer = new CanvasRenderer({
|
||||||
|
std: this.std,
|
||||||
viewport: gfx.viewport,
|
viewport: gfx.viewport,
|
||||||
layerManager: gfx.layer,
|
layerManager: gfx.layer,
|
||||||
gridManager: gfx.grid,
|
gridManager: gfx.grid,
|
||||||
@@ -177,7 +173,6 @@ export class SurfaceBlockComponent extends BlockComponent<SurfaceBlockModel> {
|
|||||||
onStackingCanvasCreated(canvas) {
|
onStackingCanvasCreated(canvas) {
|
||||||
canvas.className = 'indexable-canvas';
|
canvas.className = 'indexable-canvas';
|
||||||
},
|
},
|
||||||
elementRenderers,
|
|
||||||
surfaceModel: this.model,
|
surfaceModel: this.model,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ import {
|
|||||||
EdgelessLegacySlotExtension,
|
EdgelessLegacySlotExtension,
|
||||||
} from './extensions';
|
} from './extensions';
|
||||||
import { ExportManagerExtension } from './extensions/export-manager/export-manager';
|
import { ExportManagerExtension } from './extensions/export-manager/export-manager';
|
||||||
|
import { elementRendererExtensions } from './renderer/elements';
|
||||||
|
|
||||||
const CommonSurfaceBlockSpec: ExtensionType[] = [
|
const CommonSurfaceBlockSpec: ExtensionType[] = [
|
||||||
FlavourExtension('affine:surface'),
|
FlavourExtension('affine:surface'),
|
||||||
EdgelessCRUDExtension,
|
EdgelessCRUDExtension,
|
||||||
EdgelessLegacySlotExtension,
|
EdgelessLegacySlotExtension,
|
||||||
ExportManagerExtension,
|
ExportManagerExtension,
|
||||||
|
...elementRendererExtensions,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PageSurfaceBlockSpec: ExtensionType[] = [
|
export const PageSurfaceBlockSpec: ExtensionType[] = [
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* oxlint-disable @typescript-eslint/no-non-null-assertion */
|
/* oxlint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import {
|
import {
|
||||||
CanvasRenderer,
|
CanvasRenderer,
|
||||||
elementRenderers,
|
|
||||||
fitContent,
|
fitContent,
|
||||||
type SurfaceBlockModel,
|
type SurfaceBlockModel,
|
||||||
} from '@blocksuite/affine/blocks/surface';
|
} from '@blocksuite/affine/blocks/surface';
|
||||||
@@ -98,6 +97,7 @@ export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
|
|||||||
|
|
||||||
const themeService = this.std.get(ThemeProvider);
|
const themeService = this.std.get(ThemeProvider);
|
||||||
this.renderer = new CanvasRenderer({
|
this.renderer = new CanvasRenderer({
|
||||||
|
std: this.std,
|
||||||
viewport: this.viewport,
|
viewport: this.viewport,
|
||||||
layerManager: this._layer,
|
layerManager: this._layer,
|
||||||
gridManager: this._grid,
|
gridManager: this._grid,
|
||||||
@@ -124,7 +124,6 @@ export class MindmapSurfaceBlock extends BlockComponent<SurfaceBlockModel> {
|
|||||||
themeService.edgelessTheme
|
themeService.edgelessTheme
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
elementRenderers,
|
|
||||||
surfaceModel: this.model,
|
surfaceModel: this.model,
|
||||||
});
|
});
|
||||||
this._disposables.add(this.renderer);
|
this._disposables.add(this.renderer);
|
||||||
|
|||||||
Reference in New Issue
Block a user