@ebl-vue/editor-full 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.postcssrc.yml +33 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1 -0
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.mjs +2565 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +55 -0
  8. package/postcss.config.js +15 -0
  9. package/src/components/Editor/Editor.vue +209 -0
  10. package/src/components/index.ts +27 -0
  11. package/src/constants/index.ts +1 -0
  12. package/src/i18n/zh-cn.ts +151 -0
  13. package/src/icons/index.ts +78 -0
  14. package/src/index.ts +11 -0
  15. package/src/installer.ts +22 -0
  16. package/src/plugins/alert/index.css +150 -0
  17. package/src/plugins/alert/index.ts +463 -0
  18. package/src/plugins/block-alignment/index.css +9 -0
  19. package/src/plugins/block-alignment/index.ts +116 -0
  20. package/src/plugins/block-alignment/readme.md +1 -0
  21. package/src/plugins/code/LICENSE +21 -0
  22. package/src/plugins/code/index.css +120 -0
  23. package/src/plugins/code/index.ts +530 -0
  24. package/src/plugins/code/utils/string.ts +34 -0
  25. package/src/plugins/color-picker/index.ts +138 -0
  26. package/src/plugins/color-picker/styles.css +27 -0
  27. package/src/plugins/delimiter/index.css +14 -0
  28. package/src/plugins/delimiter/index.ts +122 -0
  29. package/src/plugins/drag-drop/index.css +19 -0
  30. package/src/plugins/drag-drop/index.ts +151 -0
  31. package/src/plugins/drag-drop/readme.md +1 -0
  32. package/src/plugins/header/H1.ts +405 -0
  33. package/src/plugins/header/H2.ts +403 -0
  34. package/src/plugins/header/H3.ts +404 -0
  35. package/src/plugins/header/H4.ts +405 -0
  36. package/src/plugins/header/H5.ts +405 -0
  37. package/src/plugins/header/H6.ts +406 -0
  38. package/src/plugins/header/index.css +20 -0
  39. package/src/plugins/header/index.ts +15 -0
  40. package/src/plugins/header/types.d.ts +46 -0
  41. package/src/plugins/indent/index.css +86 -0
  42. package/src/plugins/indent/index.ts +697 -0
  43. package/src/plugins/inline-code/index.css +11 -0
  44. package/src/plugins/inline-code/index.ts +205 -0
  45. package/src/plugins/list/ListRenderer/ChecklistRenderer.ts +211 -0
  46. package/src/plugins/list/ListRenderer/ListRenderer.ts +73 -0
  47. package/src/plugins/list/ListRenderer/OrderedListRenderer.ts +123 -0
  48. package/src/plugins/list/ListRenderer/UnorderedListRenderer.ts +123 -0
  49. package/src/plugins/list/ListRenderer/index.ts +6 -0
  50. package/src/plugins/list/ListTabulator/index.ts +1179 -0
  51. package/src/plugins/list/index.ts +502 -0
  52. package/src/plugins/list/styles/CssPrefix.ts +4 -0
  53. package/src/plugins/list/styles/icons/index.ts +10 -0
  54. package/src/plugins/list/styles/input.css +36 -0
  55. package/src/plugins/list/styles/list.css +165 -0
  56. package/src/plugins/list/types/Elements.ts +14 -0
  57. package/src/plugins/list/types/ItemMeta.ts +40 -0
  58. package/src/plugins/list/types/ListParams.ts +102 -0
  59. package/src/plugins/list/types/ListRenderer.ts +6 -0
  60. package/src/plugins/list/types/OlCounterType.ts +63 -0
  61. package/src/plugins/list/types/index.ts +14 -0
  62. package/src/plugins/list/utils/focusItem.ts +18 -0
  63. package/src/plugins/list/utils/getChildItems.ts +40 -0
  64. package/src/plugins/list/utils/getItemChildWrapper.ts +10 -0
  65. package/src/plugins/list/utils/getItemContentElement.ts +10 -0
  66. package/src/plugins/list/utils/getSiblings.ts +52 -0
  67. package/src/plugins/list/utils/isLastItem.ts +9 -0
  68. package/src/plugins/list/utils/itemHasSublist.ts +10 -0
  69. package/src/plugins/list/utils/normalizeData.ts +84 -0
  70. package/src/plugins/list/utils/removeChildWrapperIfEmpty.ts +31 -0
  71. package/src/plugins/list/utils/renderToolboxInput.ts +105 -0
  72. package/src/plugins/list/utils/stripNumbers.ts +7 -0
  73. package/src/plugins/list/utils/type-guards.ts +8 -0
  74. package/src/plugins/list.md +15 -0
  75. package/src/plugins/marker/index.css +4 -0
  76. package/src/plugins/marker/index.ts +187 -0
  77. package/src/plugins/paragraph/index.css +23 -0
  78. package/src/plugins/paragraph/index.ts +380 -0
  79. package/src/plugins/paragraph/types/icons.d.ts +4 -0
  80. package/src/plugins/paragraph/utils/makeFragment.ts +17 -0
  81. package/src/plugins/quote/index.css +26 -0
  82. package/src/plugins/quote/index.ts +206 -0
  83. package/src/plugins/table/index.ts +4 -0
  84. package/src/plugins/table/plugin.ts +254 -0
  85. package/src/plugins/table/style.css +388 -0
  86. package/src/plugins/table/table.ts +1192 -0
  87. package/src/plugins/table/toolbox.ts +165 -0
  88. package/src/plugins/table/utils/dom.ts +128 -0
  89. package/src/plugins/table/utils/popover.ts +172 -0
  90. package/src/plugins/table/utils/throttled.ts +22 -0
  91. package/src/plugins/underline/index.css +3 -0
  92. package/src/plugins/underline/index.ts +216 -0
  93. package/src/plugins/undo/index.ts +509 -0
  94. package/src/plugins/undo/observer.ts +101 -0
  95. package/src/style.css +89 -0
  96. package/src/utils/index.ts +15 -0
  97. package/src/utils/install.ts +19 -0
  98. package/tsconfig.json +37 -0
  99. package/types/index.d.ts +13 -0
  100. package/types/plugins/index.d.ts +0 -0
  101. package/vite.config.ts +79 -0
@@ -0,0 +1,509 @@
1
+ import VanillaCaret from "vanilla-caret-js";
2
+ import Observer from "./observer";
3
+
4
+ /**
5
+ * https://github.com/kommitters/editorjs-undo/tree/main
6
+ * Undo/Redo feature for Editor.js.
7
+ *
8
+ * @typedef {Object} Undo
9
+ * @description Feature's initialization class.
10
+ * @property {Object} editor — Editor.js instance object.
11
+ * @property {Number} maxLength - Max amount of changes recorded by the history stack.
12
+ * @property {Function} onUpdate - Callback called when the user performs an undo or redo action.
13
+ * @property {Boolean} shouldSaveHistory - Defines if the plugin should save the change in the stack
14
+ * @property {Object} initialItem - Initial data object.
15
+ */
16
+ export default class Undo {
17
+ /**
18
+ * @param options — Plugin custom options.
19
+ */
20
+ constructor({ editor, config = {}, onUpdate, maxLength }) {
21
+ const defaultOptions = {
22
+ maxLength: 30,
23
+ onUpdate() {},
24
+ config: {
25
+ debounceTimer: 200,
26
+ shortcuts: {
27
+ undo: ["CMD+Z"],
28
+ redo: ["CMD+Y", "CMD+SHIFT+Z"],
29
+ },
30
+ },
31
+ };
32
+
33
+ const { blocks, caret } = editor;
34
+ const { configuration } = editor;
35
+ const { holder, defaultBlock } = configuration;
36
+ const defaultShortcuts = defaultOptions.config.shortcuts;
37
+ const { shortcuts: configShortcuts } = config;
38
+ const shortcuts = { ...defaultShortcuts, ...configShortcuts };
39
+ const undo = Array.isArray(shortcuts.undo) ? shortcuts.undo : [shortcuts.undo];
40
+ const redo = Array.isArray(shortcuts.redo) ? shortcuts.redo : [shortcuts.redo];
41
+ const defaultDebounceTimer = defaultOptions.config.debounceTimer;
42
+ const { debounceTimer = defaultDebounceTimer } = config;
43
+
44
+ this.holder =
45
+ typeof holder === "string" ? document.getElementById(holder) : holder;
46
+ this.editor = editor;
47
+ this.defaultBlock = defaultBlock;
48
+ this.blocks = blocks;
49
+ this.caret = caret;
50
+ this.shouldSaveHistory = true;
51
+ this.readOnly = configuration.readOnly;
52
+ this.maxLength = maxLength || defaultOptions.maxLength;
53
+ this.onUpdate = onUpdate || defaultOptions.onUpdate;
54
+ this.config = { debounceTimer, shortcuts: { undo, redo } };
55
+
56
+ const observer = new Observer(
57
+ () => this.registerChange(),
58
+ this.holder,
59
+ this.config.debounceTimer
60
+ );
61
+ observer.setMutationObserver();
62
+
63
+ this.setEventListeners();
64
+ this.initialItem = null;
65
+ this.clear();
66
+ }
67
+
68
+ /**
69
+ * Notify core that read-only mode is suppoorted
70
+ *
71
+ * @returns {boolean}
72
+ */
73
+ static get isReadOnlySupported() {
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Truncates the history stack when it excedes the limit of changes.
79
+ *
80
+ * @param {Object} stack Changes history stack.
81
+ * @param {Number} stack Limit of changes recorded by the history stack.
82
+ */
83
+ truncate(stack, limit) {
84
+ while (stack.length > limit) {
85
+ stack.shift();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Initializes the stack when the user provides initial data.
91
+ *
92
+ * @param {Object} initialItem Initial data provided by the user.
93
+ */
94
+ initialize(initialItem) {
95
+ const initialData =
96
+ "blocks" in initialItem ? initialItem.blocks : initialItem;
97
+ const initialIndex = initialData.length - 1;
98
+ const firstElement = { index: initialIndex, state: initialData };
99
+ this.stack[0] = firstElement;
100
+ this.initialItem = firstElement;
101
+ }
102
+
103
+ /**
104
+ * Clears the history stack.
105
+ */
106
+ clear() {
107
+ this.stack = this.initialItem
108
+ ? [this.initialItem]
109
+ : [{ index: 0, state: [{ type: this.defaultBlock, data: {} }] }];
110
+ this.position = 0;
111
+ this.onUpdate();
112
+ }
113
+
114
+ /**
115
+ * Returns true if readOnly was toggled to true
116
+ * @returns {Node} Indirectly shows if readOnly was set to true or false
117
+ */
118
+ setReadOnly() {
119
+ const toolbox = this.holder.querySelector(".ce-toolbox");
120
+ this.readOnly = !toolbox;
121
+ }
122
+
123
+ /**
124
+ * Registers the data returned by API's save method into the history stack.
125
+ */
126
+ registerChange() {
127
+ this.setReadOnly();
128
+ if (!this.readOnly) {
129
+ if (this.editor && this.editor.save && this.shouldSaveHistory) {
130
+ this.editor.save().then((savedData) => {
131
+ if (this.editorDidUpdate(savedData.blocks))
132
+ this.save(savedData.blocks);
133
+ });
134
+ }
135
+ this.shouldSaveHistory = true;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Checks if the saved data has to be added to the history stack.
141
+ *
142
+ * @param {Object} newData New data to be saved in the history stack.
143
+ * @returns {Boolean}
144
+ */
145
+ editorDidUpdate(newData) {
146
+ const { state } = this.stack[this.position];
147
+ if (!newData.length) return false;
148
+ if (newData.length !== state.length) return true;
149
+
150
+ return JSON.stringify(state) !== JSON.stringify(newData);
151
+ }
152
+
153
+ /**
154
+ * Adds the saved data in the history stack and updates current position.
155
+ */
156
+ save(state) {
157
+ if (this.position >= this.maxLength) {
158
+ this.truncate(this.stack, this.maxLength);
159
+ }
160
+ this.position = Math.min(this.position, this.stack.length - 1);
161
+
162
+ this.stack = this.stack.slice(0, this.position + 1);
163
+
164
+ const index = this.blocks.getCurrentBlockIndex();
165
+ const blockCount = this.blocks.getBlocksCount();
166
+ let indexInState = index;
167
+
168
+ if (!state[index]) indexInState -= blockCount - state.length;
169
+ const caretIndex =
170
+ state[indexInState] && (
171
+ state[indexInState].type === "paragraph" ||
172
+ state[indexInState].type === "header")
173
+ ? this.getCaretIndex(index)
174
+ : null;
175
+ this.stack.push({ index: indexInState, state, caretIndex });
176
+ this.position += 1;
177
+ this.onUpdate();
178
+ }
179
+
180
+ /**
181
+ * Gets the caret position.
182
+ * @param {Number} index is the block index
183
+ * @returns The caret position
184
+ */
185
+ getCaretIndex(index) {
186
+ const blocks = this.holder.getElementsByClassName("ce-block__content");
187
+ const caretBlock = new VanillaCaret(blocks[index].firstChild);
188
+
189
+ return caretBlock.getPos();
190
+ }
191
+
192
+ /**
193
+ * Inserts a block deleted previously
194
+ * @param {Array} state is the current state according to this.position.
195
+ * @param {Array} compState is the state to compare and know the deleted block.
196
+ * @param {Number} index is the block index in state.
197
+ */
198
+ insertDeletedBlock(state, compState, index) {
199
+ for (let i = 0; i < state.length; i += 1) {
200
+ if (!compState[i] || state[i].id !== compState[i].id) {
201
+ this.blocks.insert(state[i].type, state[i].data, {}, i, true);
202
+ this.caret.setToBlock(index, "end");
203
+ break;
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Returns true if a block was dropped previously
210
+ * @param {Array} state is the current state according to this.position.
211
+ * @param {Array} compState is the state to compare and know the dropped block.
212
+ * @returns {Boolean} true if the block was dropped
213
+ */
214
+ blockWasDropped(state, compState) {
215
+ if (state.length === compState.length) {
216
+ return state.some((block, i) => block.id !== compState[i].id);
217
+ }
218
+ return false;
219
+ }
220
+
221
+ /**
222
+ * Returns true if the block has to be deleted because it was skipped previously.
223
+ * @param {Array} state is the current state according to this.position.
224
+ * @param {Array} compState is the state to compare if there was a deleted block.
225
+ * @returns {Boolean} true if a block was inserted previously.
226
+ */
227
+ blockWasSkipped(state, compState) {
228
+ return state.length !== compState.length;
229
+ }
230
+
231
+ /**
232
+ * Returns true if the content in a block without the focus was modified.
233
+ * @param {Number} index is the block index in state.
234
+ * @param {Number} compIndex is the index to compare and know if the block was inserted previously
235
+ * @returns true if the content in a block without the focus was modified.
236
+ */
237
+ contentChangedInNoFocusBlock(index, compIndex) {
238
+ return index !== compIndex;
239
+ }
240
+
241
+ /**
242
+ * Returns true if a block was deleted previously.
243
+ * @param {Array} state is the current state according to this.position.
244
+ * @param {Array} compState is the state to compare and know if a block was deleted.
245
+ * @returns {Boolean} true if a block was deleted previously.
246
+ */
247
+ blockWasDeleted(state, compState) {
248
+ return state.length > compState.length;
249
+ }
250
+
251
+ /**
252
+ * Returns true if the content was copied.
253
+ * @param {Array} state is the current state according to this.position.
254
+ * @param {Array} compState is the state to compare and know if the content was copied.
255
+ * @param {Number} index is the block index in state.
256
+ * @returns {Boolean} true if a block was deleted previously.
257
+ */
258
+ contentWasCopied(state, compState, index) {
259
+ return Object.keys(state[index].data).length === 0
260
+ && JSON.stringify(compState[index + 1]) !== JSON.stringify(state[index + 1]);
261
+ }
262
+
263
+ /**
264
+ * Decreases the current position and update the respective block in the editor.
265
+ */
266
+ async undo() {
267
+ if (this.canUndo()) {
268
+ const { index: nextIndex, state: nextState } = this.stack[this.position];
269
+
270
+ this.position -= 1;
271
+ this.shouldSaveHistory = false;
272
+ let { index } = this.stack[this.position];
273
+ const { state, caretIndex } = this.stack[this.position];
274
+
275
+ this.onUpdate();
276
+ const blockCount = this.blocks.getBlocksCount();
277
+
278
+ if (!state[index]) {
279
+ index -= 1;
280
+ this.stack[this.position].index = index;
281
+ }
282
+
283
+ if (this.blockWasDeleted(state, nextState)) {
284
+ this.insertDeletedBlock(state, nextState, index);
285
+ } else if (this.contentWasCopied(state, nextState, index)) {
286
+ await this.blocks.render({ blocks: state });
287
+ this.caret.setToBlock(index, 'end');
288
+ } else if (index < nextIndex && this.blockWasSkipped(state, nextState)) {
289
+ await this.blocks.delete(nextIndex);
290
+ this.caret.setToBlock(index, "end");
291
+ } else if (blockCount > state.length) {
292
+ await this.blocks.render({ blocks: state });
293
+ this.setCaretIndex(index, caretIndex);
294
+ } else if (this.blockWasDropped(state, nextState)) {
295
+ await this.blocks.render({ blocks: state });
296
+ this.caret.setToBlock(index, 'end');
297
+ } else if (this.contentChangedInNoFocusBlock(index, nextIndex)) {
298
+ const { id } = this.blocks.getBlockByIndex(nextIndex);
299
+
300
+ await this.blocks.update(id, state[nextIndex].data);
301
+ this.setCaretIndex(index, caretIndex);
302
+ }
303
+
304
+ const block = this.blocks.getBlockByIndex(index);
305
+ if (block) {
306
+ await this.blocks.update(block.id, state[index].data);
307
+ this.setCaretIndex(index, caretIndex);
308
+ }
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Sets the caret position.
314
+ * @param {Number} index is the block index
315
+ * @param {Number} caretIndex is the caret position
316
+ * @param {Array} state is the current state according to this.position.
317
+ */
318
+ setCaretIndex(index, caretIndex) {
319
+ if (caretIndex && caretIndex !== -1) {
320
+ const blocks = this.holder.getElementsByClassName("ce-block__content");
321
+ const caretBlock = new VanillaCaret(blocks[index].firstChild);
322
+ setTimeout(() => caretBlock.setPos(caretIndex), 50);
323
+ } else {
324
+ this.caret.setToBlock(index, "end");
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Inserts new block
330
+ * @param {Array} state is the current state according to this.position.
331
+ * @param {Number} index is the block index
332
+ */
333
+ async insertBlock(state, index) {
334
+ await this.blocks.insert(
335
+ state[index].type,
336
+ state[index].data,
337
+ {},
338
+ index,
339
+ true,
340
+ );
341
+ }
342
+
343
+ /**
344
+ * Inserts a block when is skipped and update the previous one if it changed.
345
+ * @param {Array} prevState is the previous state according to this.position.
346
+ * @param {Array} state is the current state according to this.position.
347
+ * @param {Number} index is the block index.
348
+ */
349
+ async insertSkippedBlocks(prevState, state, index) {
350
+ for (let i = prevState.length; i < state.length; i += 1) {
351
+ this.insertBlock(state, i);
352
+ }
353
+
354
+ if (JSON.stringify(prevState[index - 1]) !== JSON.stringify(state[index - 1])) {
355
+ await this.updateModifiedBlock(state, index);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Updates the passed block or render the state when the content was copied.
361
+ * @param {Array} state is the current state according to this.position.
362
+ * @param {Number} index is the block index.
363
+ */
364
+ async updateModifiedBlock(state, index) {
365
+ const block = state[index - 1];
366
+ if (this.editor.blocks.getById(block.id)) return this.blocks.update(block.id, block.data);
367
+ return this.blocks.render({ blocks: state });
368
+ }
369
+
370
+ /**
371
+ * Increases the current position and update the respective block in the editor.
372
+ */
373
+ async redo() {
374
+ if (this.canRedo()) {
375
+ this.position += 1;
376
+ this.shouldSaveHistory = false;
377
+ const { index, state, caretIndex } = this.stack[(this.position)];
378
+ const { index: prevIndex, state: prevState } =
379
+ this.stack[this.position - 1];
380
+
381
+ if (this.blockWasDeleted(prevState, state)) {
382
+ await this.blocks.delete();
383
+ this.caret.setToBlock(index, "end");
384
+ } else if (this.blockWasSkipped(state, prevState)) {
385
+ await this.insertSkippedBlocks(prevState, state, index);
386
+ this.caret.setToBlock(index, 'end');
387
+ } else if (this.blockWasDropped(state, prevState) && this.position !== 1) {
388
+ await this.blocks.render({ blocks: state });
389
+ this.caret.setToBlock(index, "end");
390
+ }
391
+
392
+ this.onUpdate();
393
+ const block = this.blocks.getBlockByIndex(index);
394
+ if (block) {
395
+ await this.blocks.update(block.id, state[index].data);
396
+ this.setCaretIndex(index, caretIndex);
397
+ }
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Checks if the history stack can perform an undo action.
403
+ *
404
+ * @returns {Boolean}
405
+ */
406
+ canUndo() {
407
+ return !this.readOnly && this.position > 0;
408
+ }
409
+
410
+ /**
411
+ * Checks if the history stack can perform a redo action.
412
+ *
413
+ * @returns {Boolean}
414
+ */
415
+ canRedo() {
416
+ return !this.readOnly && this.position < this.count();
417
+ }
418
+
419
+ /**
420
+ * Returns the number of changes recorded in the history stack.
421
+ *
422
+ * @returns {Number}
423
+ */
424
+ count() {
425
+ return this.stack.length - 1; // -1 because of initial item
426
+ }
427
+
428
+ /**
429
+ * Parses the keys passed in the shortcut property to accept CMD,ALT and SHIFT
430
+ *
431
+ * @param {Array} keys are the keys passed in shortcuts in config
432
+ * @returns {Array}
433
+ */
434
+
435
+ parseKeys(keys) {
436
+ const specialKeys = {
437
+ CMD: /(Mac)/i.test(navigator.platform) ? "metaKey" : "ctrlKey",
438
+ ALT: "altKey",
439
+ SHIFT: "shiftKey",
440
+ };
441
+ const parsedKeys = keys.slice(0, -1).map((key) => specialKeys[key]);
442
+
443
+ const letterKey =
444
+ parsedKeys.includes("shiftKey") && keys.length === 2
445
+ ? keys[keys.length - 1].toUpperCase()
446
+ : keys[keys.length - 1].toLowerCase();
447
+
448
+ parsedKeys.push(letterKey);
449
+ return parsedKeys;
450
+ }
451
+
452
+ /**
453
+ * Sets events listeners to allow keyboard actions support
454
+ */
455
+
456
+ setEventListeners() {
457
+ const { holder } = this;
458
+ const { shortcuts } = this.config;
459
+ const { undo, redo } = shortcuts;
460
+ const keysUndo = undo.map((undoShortcut) => undoShortcut.replace(/ /g, "").split("+"));
461
+ const keysRedo = redo.map((redoShortcut) => redoShortcut.replace(/ /g, "").split("+"));
462
+
463
+ const keysUndoParsed = keysUndo.map((keys: string[]) => this.parseKeys(keys));
464
+ const keysRedoParsed = keysRedo.map((keys: string[]) => this.parseKeys(keys));
465
+
466
+ const twoKeysPressed = (e: KeyboardEvent, keys: string[]) =>
467
+ keys.length === 2 && e[keys[0]] && (e.key.toLowerCase() === keys[1]);
468
+ const threeKeysPressed = (e: KeyboardEvent, keys: string[]) =>
469
+ keys.length === 3 && e[keys[0]] && e[keys[1]] && (e.key.toLowerCase() === keys[2]);
470
+
471
+ const verifyListTwoKeysPressed = (e: KeyboardEvent, keysList) =>
472
+ keysList.reduce((result, keys) => result || twoKeysPressed(e, keys), false);
473
+ const verifyListThreeKeysPressed = (e: KeyboardEvent, keysList) =>
474
+ keysList.reduce((result, keys) => result || threeKeysPressed(e, keys), false);
475
+
476
+ const pressedKeys = (e: KeyboardEvent, keys, compKeys) => {
477
+ if (verifyListTwoKeysPressed(e, keys) && !verifyListThreeKeysPressed(e, compKeys)) {
478
+ return true;
479
+ }
480
+ if (verifyListThreeKeysPressed(e, keys)) {
481
+ return true;
482
+ }
483
+ return false;
484
+ };
485
+
486
+ const handleUndo = (e) => {
487
+ if (pressedKeys(e, keysUndoParsed, keysRedoParsed)) {
488
+ e.preventDefault();
489
+ this.undo();
490
+ }
491
+ };
492
+
493
+ const handleRedo = (e: KeyboardEvent) => {
494
+ if (pressedKeys(e, keysRedoParsed, keysUndoParsed)) {
495
+ e.preventDefault();
496
+ this.redo();
497
+ }
498
+ };
499
+
500
+ const handleDestroy = () => {
501
+ holder.removeEventListener("keydown", handleUndo);
502
+ holder.removeEventListener("keydown", handleRedo);
503
+ };
504
+
505
+ holder.addEventListener("keydown", handleUndo);
506
+ holder.addEventListener("keydown", handleRedo);
507
+ holder.addEventListener("destroy", handleDestroy);
508
+ }
509
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @typedef {Object} Observer
3
+ * @description Custom MutationObserver to detect changes in the editor.
4
+ * @property {String} holder — Editor.js holder id.
5
+ * @property {Object} observer - MutationObserver object that detects changes in the editor.
6
+ * @property {Number} debounceTimer - Delay time for the debouncer.
7
+ * @property {Function} mutationDebouncer - Debouncer to delay the changes registration.
8
+ */
9
+ export default class Observer {
10
+ private observer: MutationObserver | null;
11
+ private debounceTimer: number;
12
+ private mutationDebouncer: Function;
13
+ private holder: string;
14
+ /**
15
+ * Creates a new instance of the Observer object.
16
+ * @param {Function} registerChange - Function that register a change in the history stack.
17
+ * @param {String} holder - Editor.js holder id.
18
+ * @param {Number} debounceTimer Delay time for the debouncer.
19
+ */
20
+ constructor(registerChange: Function, holder: string, debounceTimer: number) {
21
+
22
+ this.holder = holder;
23
+ this.observer = null;
24
+ this.debounceTimer = debounceTimer;
25
+ this.mutationDebouncer = this.debounce(() => {
26
+ registerChange();
27
+ }, this.debounceTimer);
28
+ }
29
+
30
+ /**
31
+ * Sets a mutation observer to catch every change in the editor.
32
+ */
33
+ setMutationObserver() {
34
+ const observerOptions = {
35
+ childList: true,
36
+ attributes: true,
37
+ subtree: true,
38
+ characterData: true,
39
+ characterDataOldValue: true,
40
+ };
41
+
42
+ const target = this.holder.querySelector('.codex-editor__redactor');
43
+
44
+ this.observer = new MutationObserver((mutationList) => {
45
+ this.mutationHandler(mutationList);
46
+ });
47
+ this.observer.observe(target, observerOptions);
48
+ }
49
+
50
+ /**
51
+ * Handles the mutations and checks if a new mutation has been produced.
52
+ * @param {Object} mutationList The registered mutations
53
+ */
54
+ mutationHandler(mutationList: any[]) {
55
+ let contentMutated = false;
56
+
57
+ mutationList.forEach((mutation:any) => {
58
+ switch (mutation.type) {
59
+ case 'childList':
60
+ if (mutation.target === this.holder) {
61
+ this.onDestroy();
62
+ } else {
63
+ contentMutated = true;
64
+ }
65
+ break;
66
+ case 'characterData':
67
+ contentMutated = true;
68
+ break;
69
+ case 'attributes':
70
+ if (!mutation.target.classList.contains('ce-block') && !mutation.target.classList.contains('tc-toolbox')) {
71
+ contentMutated = true;
72
+ }
73
+ break;
74
+ default:
75
+ break;
76
+ }
77
+ });
78
+
79
+ if (contentMutated) this.mutationDebouncer();
80
+ }
81
+
82
+ /**
83
+ * Delays invoking a function until after wait millis have elapsed.
84
+ * @param {Function} callback The function to be delayed.
85
+ * @param {Number} wait The deplay time in millis.
86
+ */
87
+ debounce(callback: Function, wait: number) {
88
+ let timeout: any;
89
+ return (...args:any) => {
90
+ const context = this;
91
+ clearTimeout(timeout);
92
+ timeout = setTimeout(() => callback.apply(context, args), wait);
93
+ };
94
+ }
95
+
96
+ onDestroy() {
97
+ const destroyEvent = new CustomEvent('destroy');
98
+ document.dispatchEvent(destroyEvent);
99
+ this.observer?.disconnect();
100
+ }
101
+ }
package/src/style.css ADDED
@@ -0,0 +1,89 @@
1
+ :root{
2
+ --color-border: #EFF0F1;
3
+ }
4
+ .ebl-editor-title{
5
+ min-height: 55px;
6
+ }
7
+ .ebl-editor-title-wrap{
8
+ min-height: 55px;
9
+ margin: 0 auto;
10
+ }
11
+ .ebl-editor-title-input{
12
+ min-height: 55px;
13
+ width: 100%;
14
+ font-weight: 600;
15
+ border: none;
16
+ background-color: transparent;
17
+ font-size:34px;
18
+ }
19
+ .ebl-editor-title-input:focus{
20
+ outline: none;
21
+ }
22
+ .ebl-editor-title-input:active{
23
+ outline: none;
24
+ }
25
+ .ebl-editor-time-wrap{
26
+ white-space: nowrap;
27
+ font-size: 14px;
28
+ font-weight: 400;
29
+ line-height: 22px;
30
+ padding-top :12px ;
31
+ padding-bottom: 8px;
32
+ color: #646a73;
33
+ }
34
+ .ce-block__content{
35
+ max-width: 650px;
36
+ }
37
+ @media (min-width: 1050px) {
38
+ .ce-block__content {
39
+ max-width: 700px;
40
+
41
+ }
42
+ .ce-toolbar__content {
43
+ max-width: 700px;
44
+ }
45
+
46
+ }
47
+
48
+ .ce-toolbar,.ce-inline-toolbar{
49
+ z-index: 1000;
50
+ }
51
+ .ce-toolbar__plus{
52
+ border: 1px solid var(--color-border);
53
+ }
54
+ .ce-toolbar {
55
+ .ce-popover__items{
56
+ display: flex;
57
+ flex-direction: row;
58
+ flex-wrap: wrap
59
+ }
60
+
61
+
62
+ .ce-popover__items .ce-popover-item{
63
+ position: relative;
64
+ }
65
+
66
+ .ce-popover__items .ce-popover-item__title{
67
+ display: none;
68
+ }
69
+
70
+ .ce-popover-item-html,
71
+ .ce-popover-item-separator,
72
+ .ce-popover-item[data-item-name="convert-to"]
73
+ {
74
+ width: 100%;
75
+ }
76
+ .ce-popover-item[data-item-name="convert-to"] .ce-popover-item__title{
77
+ display: block;
78
+ }
79
+ }
80
+
81
+ .ce-inline-toolbar .ce-popover__items{
82
+ display: flex!important;
83
+ flex-direction: row;
84
+ flex-wrap: wrap;
85
+ }
86
+
87
+ .ce-inline-toolbar .ce-popover__items .ce-popover-item__title{
88
+ display: none
89
+ }
@@ -0,0 +1,15 @@
1
+
2
+
3
+ export default {
4
+ copy: (o: any) => {
5
+ return JSON.parse(JSON.stringify(o));
6
+ },
7
+ createUniqueString: () => {
8
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
9
+ const r = Math.random() * 16 | 0;
10
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
11
+ console.log(v)
12
+ return v.toString(16);
13
+ });
14
+ }
15
+ }