-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathBlockManager.ts
More file actions
219 lines (195 loc) · 5.55 KB
/
BlockManager.ts
File metadata and controls
219 lines (195 loc) · 5.55 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
import {
type BlockNodeInit,
type EditorDocumentSerialized,
EditorJSModel
} from '@editorjs/model';
import 'reflect-metadata';
import { inject, injectable } from 'inversify';
import { TOKENS } from '../tokens.js';
import ToolsManager from '../tools/ToolsManager.js';
import { BlockToolData } from '@editorjs/editorjs';
import {
CoreConfigValidated,
EventBus
} from '@editorjs/sdk';
/**
* Parameters for the BlocksManager.insert() method
*/
interface InsertBlockParameters {
/**
* Block ID
*/
id?: string;
/**
* Block tool name to insert
*/
type?: string;
/**
* Block's initial data
*/
data?: BlockToolData;
/**
* Index to insert block at
*/
index?: number;
// needToFocus?: boolean;
/**
* Flag indicates if block at index should be replaced
*/
replace?: boolean;
/**
* If true, moves caret to the new block
*/
focus?: boolean;
// tunes?: {[name: string]: BlockTuneData};
}
/**
* BlocksManager is responsible for block lifecycle operations:
* - insert, delete, move, render, clear
*
* Model event handling (BlockAddedEvent / BlockRemovedEvent) and rendering
* are intentionally delegated to BlockRenderer, keeping this class free of
* any Adapter dependency and avoiding the circular dependency:
* BlocksManager → Adapter → EditorAPI → BlocksAPI → BlocksManager
*/
@injectable()
export class BlocksManager {
/**
* Editor's Document Model instance to get and update blocks data
*/
#model: EditorJSModel;
/**
* Editor's EventBus instance to exchange events between components
*/
#eventBus: EventBus;
/**
* Tools manager instance to get block tools
*/
#toolsManager: ToolsManager;
/**
* Editor's validated user configuration
*/
#config: CoreConfigValidated;
/**
* Returns Blocks count
*/
public get blocksCount(): number {
return this.#model.length;
}
/**
* BlocksManager 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 config - Editor validated configuration
*/
constructor(
model: EditorJSModel,
eventBus: EventBus,
toolsManager: ToolsManager,
@inject(TOKENS.EditorConfig) config: CoreConfigValidated
) {
this.#model = model;
this.#eventBus = eventBus;
this.#toolsManager = toolsManager;
this.#config = config;
}
/**
* Inserts a new block to the editor at the specified index
* @param parameters - method parameters object
* @param parameters.type - block tool name to insert
* @param parameters.data - block's initial data
* @param parameters.index - index to insert block at
// * @param parameters.needToFocus - flag indicates if caret should be set to block after insert
* @param parameters.replace - flag indicates if block at index should be replaced
*/
public insert({
id = undefined,
type = this.#config.defaultBlock,
data = {},
index,
focus = false,
replace = false,
// tunes = {},
}: InsertBlockParameters = {}): void {
let newIndex = index;
if (newIndex === undefined) {
newIndex = this.#model.length + (replace ? -1 : 0);
}
if (replace) {
this.#model.removeBlock(this.#config.userId, newIndex);
}
this.#model.addBlock(this.#config.userId, {
...data,
id,
name: type,
}, newIndex);
if (focus) {
/**
* @todo think of how to set the focus without knowing the data key
*/
}
}
/**
* Inserts several Blocks to specified index
* @param blocks - array of blocks to insert
* @param [index] - index to insert blocks at. If undefined, inserts at the end
*/
public insertMany(blocks: BlockNodeInit[], index: number = this.#model.length): void {
blocks.forEach((block, i) => this.#model.addBlock(this.#config.userId, block, index + i));
}
/**
* Re-initialize document
* @param document - serialized document data
*/
public render(document: EditorDocumentSerialized): void {
this.#model.initializeDocument(document);
}
/**
* Remove all blocks from Document
*/
public clear(): void {
this.#model.clearBlocks();
}
/**
* Removes Block by index, or current block if index is not passed
* @param index - index of a block to delete
*/
public deleteBlock(index: number | undefined = this.#getCurrentBlockIndex()): void {
if (index === undefined) {
/**
* @todo see what happens in legacy
*/
throw new Error('No block selected to delete');
}
this.#model.removeBlock(this.#config.userId, index);
}
/**
* Moves a block to a new index
* @param toIndex - index where the block is moved to
* @param [fromIndex] - block to move. Current block if not passed
*/
public move(toIndex: number, fromIndex: number | undefined = this.#getCurrentBlockIndex()): void {
if (fromIndex === undefined) {
throw new Error('No block selected to move');
}
/**
* Do nothing if fromIndex and toIndex are the same
*/
if (fromIndex === toIndex) {
return;
}
const block = this.#model.getBlockSerialized(fromIndex);
this.#model.removeBlock(this.#config.userId, fromIndex);
this.#model.addBlock(this.#config.userId, block, toIndex);
}
/**
* Returns block index where user caret is placed
*/
#getCurrentBlockIndex(): number | undefined {
const userCaret = this.#model.getCaret(this.#config.userId);
const caretIndex = userCaret?.index;
return caretIndex?.blockIndex;
}
}