-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathBlockRenderer.ts
More file actions
160 lines (140 loc) · 4.82 KB
/
BlockRenderer.ts
File metadata and controls
160 lines (140 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import {
BlockAddedEvent,
BlockRemovedEvent,
EditorJSModel,
EventType,
ModelEvents
} from '@editorjs/model';
import 'reflect-metadata';
import { inject, injectable } from 'inversify';
import { TOKENS } from '../tokens.js';
import ToolsManager from '../tools/ToolsManager.js';
import { BlockAPI } from '@editorjs/editorjs';
import {
EventBus,
BlockAddedCoreEvent,
BlockRemovedCoreEvent,
EditorJSAdapterPlugin
} from '@editorjs/sdk';
/**
* BlockRenderer subscribes to model block events and is responsible for:
* - creating a BlockToolAdapter for each added block
* - instantiating and rendering the BlockTool
* - dispatching BlockAddedCoreEvent / BlockRemovedCoreEvent to the UI layer
*
* It is intentionally separate from BlocksManager so that BlocksManager
* has no dependency on the Adapter, breaking the circular dependency:
* BlocksManager → Adapter → EditorAPI → BlocksAPI → BlocksManager
*
* BlockRenderer is resolved in Core.initialize() *after* #initializeAdapter()
* has registered the Adapter, so the @Inject('Adapter') here is always safe.
*/
@injectable()
export class BlockRenderer {
/**
* Editor's Document Model instance to listen for block events
*/
#model: EditorJSModel;
/**
* Editor's EventBus instance to dispatch core events
*/
#eventBus: EventBus;
/**
* Tools manager instance to look up block tools by name
*/
#toolsManager: ToolsManager;
/**
* Adapter plugin instance used to create per-block BlockToolAdapters
*/
#adapter: EditorJSAdapterPlugin;
/**
* BlockRenderer constructor.
* All parameters are injected through the IoC container.
* @param model - Editor's Document Model instance
* @param eventBus - Editor's EventBus instance
* @param toolsManager - Tools manager instance
* @param adapter - Adapter plugin instance
*/
constructor(
model: EditorJSModel,
eventBus: EventBus,
toolsManager: ToolsManager,
@inject(TOKENS.Adapter) adapter: EditorJSAdapterPlugin
) {
this.#model = model;
this.#eventBus = eventBus;
this.#toolsManager = toolsManager;
this.#adapter = adapter;
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- Need to bubble the promise up in case of errors
this.#model.addEventListener(EventType.Changed, event => this.#handleModelUpdate(event));
}
/**
* Handles model update events.
* Filters only BlockAddedEvent and BlockRemovedEvent.
* @param event - Model update event
*/
#handleModelUpdate(event: ModelEvents): Promise<void> | void {
switch (true) {
case event instanceof BlockAddedEvent:
return this.#handleBlockAddedEvent(event);
case event instanceof BlockRemovedEvent:
return this.#handleBlockRemovedEvent(event);
default:
}
}
/**
* Handles BlockAddedEvent:
* - creates a BlockToolAdapter for the block
* - instantiates and renders the BlockTool
* - dispatches BlockAddedCoreEvent with the rendered UI element
* @param event - BlockAddedEvent
*/
async #handleBlockAddedEvent(event: BlockAddedEvent): Promise<void> {
const { index, data } = event.detail;
if (index.blockIndex === undefined) {
throw new Error('[BlockRenderer] Block index should be defined. Probably something wrong with the Editor Model. Please, report this issue');
}
const tool = this.#toolsManager.blockTools.get(data.name);
if (tool === undefined) {
throw new Error(`[BlockRenderer] Block Tool ${data.name} not found`);
}
const blockToolAdapter = this.#adapter.createBlockToolAdapter(index.blockIndex, tool.name);
const block = tool.create({
adapter: blockToolAdapter,
data: data.data,
block: {} as BlockAPI,
readOnly: false,
});
try {
const blockElement = await block.render();
this.#eventBus.dispatchEvent(new BlockAddedCoreEvent({
tool: tool.name,
data: data.data,
ui: blockElement,
index: index.blockIndex,
}));
} catch (error) {
console.error(`[BlockRenderer] Block Tool ${data.name} failed to render`, error);
}
}
/**
* Handles BlockRemovedEvent:
* - cleans up the block's adapter and its registered inputs
* - dispatches BlockRemovedCoreEvent so the UI layer removes the block
* @param event - BlockRemovedEvent
*/
#handleBlockRemovedEvent(event: BlockRemovedEvent): void {
const { data, index } = event.detail;
if (index.blockIndex === undefined) {
throw new Error('[BlockRenderer] Block index should be defined. Probably something wrong with the Editor Model. Please, report this issue');
}
this.#adapter.destroyBlockToolAdapter(index.blockIndex);
this.#eventBus.dispatchEvent(new BlockRemovedCoreEvent({
tool: data.name,
index: index.blockIndex,
}));
/**
* @todo clear block tool adapter memory
*/
}
}