@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,530 @@
1
+ //https://github.com/yudaapratama/editorjs-shiki
2
+ import './index.css';
3
+ import type { API, BlockTool, BlockToolConstructorOptions, BlockToolData, PasteConfig, PasteEvent, SanitizerConfig, ToolboxConfig } from '@ebl-vue/editorjs';
4
+ import { codeToHtml, bundledLanguagesInfo as languages } from 'shiki'
5
+ import { getLineStartPosition } from './utils/string';
6
+
7
+ /**
8
+ * CodeTool for Editor.js
9
+ * @version 1.0.0
10
+ * @license MIT
11
+ */
12
+
13
+ /**
14
+ * Data structure for CodeTool's data
15
+ */
16
+ export type CodeData = BlockToolData<{
17
+ /**
18
+ * The code content input by the user
19
+ */
20
+ code: string;
21
+ lang: string;
22
+ theme: string;
23
+ }>;
24
+
25
+ /**
26
+ * Configuration options for the CodeTool provided by the user
27
+ */
28
+ export interface CodeConfig {
29
+ /**
30
+ * Placeholder text to display in the input field when it's empty
31
+ */
32
+ placeholder: string;
33
+ }
34
+
35
+ /**
36
+ * Defines the CSS class names used by CodeTool for styling its elements
37
+ */
38
+ interface CodeToolCSS {
39
+ /** Block Styling from Editor.js API */
40
+ baseClass: string;
41
+ /** Input Styling from Editor.js API */
42
+ input: string;
43
+ /** Wrapper styling */
44
+ wrapper: string;
45
+ /** Textarea styling */
46
+ textarea: string;
47
+ /** Span styling */
48
+ span: string;
49
+ /** Selector Language styling */
50
+ selectorLanguage: string;
51
+ /** Selector Theme styling */
52
+ selectorTheme: string;
53
+ }
54
+
55
+ /**
56
+ * Holds references to the DOM elements used by CodeTool
57
+ */
58
+ interface CodeToolNodes {
59
+ /** Main container or Wrapper for CodeTool */
60
+ holder: HTMLDivElement | null;
61
+ /** Textarea where user inputs their code */
62
+ textarea: HTMLTextAreaElement | null;
63
+ }
64
+
65
+ /**
66
+ * Options passed to the constructor of a block tool
67
+ */
68
+ export type CodeToolConstructorOptions = BlockToolConstructorOptions<CodeData>;
69
+
70
+ /**
71
+ * Code Tool for the Editor.js allows to include code examples in your articles.
72
+ */
73
+ export default class CodeTool implements BlockTool {
74
+ /**
75
+ * API provided by Editor.js for interacting with the editor's core functionality
76
+ */
77
+ private api: API;
78
+ /**
79
+ * Indicates whether the editor is in read-only mode, preventing modifications
80
+ */
81
+ private readOnly: boolean;
82
+ /**
83
+ * Placeholder text displayed when there is no code content
84
+ */
85
+ private placeholder: string;
86
+ /**
87
+ * Collection of CSS class names used by CodeTool for styling its elements
88
+ */
89
+ private CSS: CodeToolCSS;
90
+ /**
91
+ * DOM nodes related to the CodeTool, including containers and other elements
92
+ */
93
+ private nodes: CodeToolNodes;
94
+ /**
95
+ * Stores the current data (code and other related properties) for the CodeTool
96
+ */
97
+ private _data!: CodeData;
98
+ /**
99
+ * Default language for the CodeTool
100
+ */
101
+ private _selectorLanguage: string = '';
102
+ /**
103
+ * Default theme for the CodeTool
104
+ */
105
+ private _selectorTheme: string = '';
106
+ /**
107
+ * Notify core that read-only mode is supported
108
+ * @returns true if read-only mode is supported
109
+ */
110
+ public static get isReadOnlySupported(): boolean {
111
+ return true;
112
+ }
113
+
114
+ /**
115
+ * Allows pressing Enter key to create line breaks inside the CodeTool textarea
116
+ * This enables multi-line input within the code editor.
117
+ * @returns true if line breaks are allowed in the textarea
118
+ */
119
+ public static get enableLineBreaks(): boolean {
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Render plugin`s main Element and fill it with saved data
125
+ * @param options - tool constricting options
126
+ * @param options.data — previously saved plugin code
127
+ * @param options.config - user config for Tool
128
+ * @param options.api - Editor.js API
129
+ * @param options.readOnly - read only mode flag
130
+ */
131
+ constructor({ data, config, api, readOnly }: CodeToolConstructorOptions) {
132
+ this.api = api;
133
+ this.readOnly = readOnly;
134
+
135
+ this.placeholder = this.api.i18n.t(config.placeholder as string || CodeTool.DEFAULT_PLACEHOLDER);
136
+
137
+ this._selectorLanguage = data.lang || config.lang || CodeTool.DEFAULT_LANGUAGE
138
+ this._selectorTheme = data.theme || config.theme || CodeTool.DEFAULT_THEME
139
+
140
+ this.CSS = {
141
+ baseClass: this.api.styles.block,
142
+ input: this.api.styles.input,
143
+ wrapper: 'ce-editorjs-x-shiki',
144
+ textarea: 'ce-editorjs-x-shiki__textarea',
145
+ span: 'ce-editorjs-x-shiki__span',
146
+ selectorLanguage: 'ce-editorjs-x-shiki__selector-language',
147
+ selectorTheme: 'ce-editorjs-x-shiki__selector-theme',
148
+ };
149
+
150
+ this.nodes = {
151
+ holder: null,
152
+ textarea: null,
153
+ };
154
+
155
+ this.data = {
156
+ code: data.code ?? '',
157
+ lang: this._selectorLanguage,
158
+ theme: this._selectorTheme,
159
+ };
160
+
161
+ this.nodes.holder = this.drawView()
162
+ }
163
+
164
+ /**
165
+ * Return Tool's view
166
+ * @returns this.nodes.holder - Code's wrapper
167
+ */
168
+ public render(): HTMLDivElement {
169
+ return this.nodes.holder!;
170
+ }
171
+
172
+ /**
173
+ * Extract Tool's data from the view
174
+ * @param codeWrapper - CodeTool's wrapper, containing textarea with code
175
+ * @returns - saved plugin code
176
+ */
177
+ public save(codeWrapper: HTMLDivElement): CodeData {
178
+ return {
179
+ code: codeWrapper.querySelector('textarea')!.value,
180
+ lang: this._selectorLanguage,
181
+ theme: this._selectorTheme
182
+ };
183
+ }
184
+
185
+ /**
186
+ * onPaste callback fired from Editor`s core
187
+ * @param event - event with pasted content
188
+ */
189
+ public onPaste(event: PasteEvent): void {
190
+ const detail = event.detail;
191
+
192
+ if ('data' in detail) {
193
+ const content = detail.data as string;
194
+
195
+ this.data = {
196
+ code: content || '',
197
+ lang: this._selectorLanguage,
198
+ theme: this._selectorTheme
199
+ };
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Returns Tool`s data from private property
205
+ * @returns
206
+ */
207
+ public get data(): CodeData {
208
+ return this._data;
209
+ }
210
+
211
+ /**
212
+ * Set Tool`s data to private property and update view
213
+ * @param data - saved tool data
214
+ */
215
+ public set data(data: CodeData) {
216
+ this._data = data;
217
+
218
+ if (this.nodes.textarea) {
219
+ this.nodes.textarea.value = data.code;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Get Tool toolbox settings.
225
+ * Provides the icon and title to display in the toolbox for the CodeTool.
226
+ * @returns An object containing:
227
+ * - icon: SVG representation of the Tool's icon
228
+ * - title: Title to show in the toolbox
229
+ */
230
+ public static get toolbox(): ToolboxConfig {
231
+ return {
232
+ icon: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17C7 17 7 15.2536 7 13.5L5.5 12L7 10.5C7 8.74644 7 7 9 7"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17C17 17 17 15.2536 17 13.5L18.5 12L17 10.5C17 8.74644 17 7 15 7"/></svg>',
233
+ title: 'Code',
234
+ };
235
+ }
236
+
237
+ /**
238
+ * Default placeholder for CodeTool's textarea
239
+ * @returns
240
+ */
241
+ public static get DEFAULT_PLACEHOLDER(): string {
242
+ return 'Enter your code';
243
+ }
244
+
245
+ /**
246
+ * Default language for CodeTool's textarea
247
+ * @returns
248
+ */
249
+ public static get DEFAULT_LANGUAGE(): string {
250
+ return 'javascript';
251
+ }
252
+
253
+ /**
254
+ * Default theme for CodeTool's textarea
255
+ * @returns
256
+ */
257
+ public static get DEFAULT_THEME(): string {
258
+ return 'vitesse-dark';
259
+ }
260
+
261
+ /**
262
+ * Used by Editor.js paste handling API.
263
+ * Provides configuration to handle CODE tag.
264
+ * @returns
265
+ */
266
+ public static get pasteConfig(): PasteConfig {
267
+ return {
268
+ tags: ['pre'],
269
+ };
270
+ }
271
+
272
+ /**
273
+ * Automatic sanitize config
274
+ * @returns
275
+ */
276
+ public static get sanitize(): SanitizerConfig {
277
+ return {
278
+ code: true, // Allow HTML tags
279
+ };
280
+ }
281
+
282
+ /**
283
+ * Handles Tab key pressing (adds/removes indentations)
284
+ * @param event - keydown
285
+ */
286
+ private tabHandler(event: KeyboardEvent): void {
287
+ /**
288
+ * Prevent editor.js tab handler
289
+ */
290
+ event.stopPropagation();
291
+
292
+ /**
293
+ * Prevent native tab behaviour
294
+ */
295
+ event.preventDefault();
296
+
297
+ const textarea = event.target as HTMLTextAreaElement;
298
+ const isShiftPressed = event.shiftKey;
299
+ const caretPosition = textarea.selectionStart;
300
+ const value = textarea.value;
301
+ const indentation = ' ';
302
+
303
+ let newCaretPosition;
304
+
305
+ /**
306
+ * For Tab pressing, just add an indentation to the caret position
307
+ */
308
+ if (!isShiftPressed) {
309
+ newCaretPosition = caretPosition + indentation.length;
310
+
311
+ textarea.value = value.substring(0, caretPosition) + indentation + value.substring(caretPosition);
312
+ } else {
313
+ /**
314
+ * For Shift+Tab pressing, remove an indentation from the start of line
315
+ */
316
+ const currentLineStart = getLineStartPosition(value, caretPosition);
317
+ const firstLineChars = value.substr(currentLineStart, indentation.length);
318
+
319
+ if (firstLineChars !== indentation) {
320
+ return;
321
+ }
322
+
323
+ /**
324
+ * Trim the first two chars from the start of line
325
+ */
326
+ textarea.value = value.substring(0, currentLineStart) + value.substring(currentLineStart + indentation.length);
327
+ newCaretPosition = caretPosition - indentation.length;
328
+ }
329
+
330
+ /**
331
+ * Restore the caret
332
+ */
333
+ textarea.setSelectionRange(newCaretPosition, newCaretPosition);
334
+ }
335
+
336
+ /**
337
+ * Create Tool's view
338
+ * @returns
339
+ */
340
+ private drawView(): HTMLDivElement {
341
+ const wrapper = document.createElement('div');
342
+ const wrapperHolder = document.createElement('div');
343
+ const wrapperSelectorHolder = document.createElement('div');
344
+ const wrapperLang = document.createElement('div');
345
+ const wrapperCopy = document.createElement('div');
346
+ const wrapperCopyTip = document.createElement('div');
347
+
348
+
349
+ const selectorLanguage = document.createElement('select');
350
+ const selectorTheme = document.createElement('select');
351
+ const span = document.createElement('span');
352
+ const textarea = document.createElement('textarea');
353
+
354
+ wrapper.classList.add(this.CSS.baseClass, this.CSS.wrapper);
355
+ wrapperHolder.classList.add('ce-editorjs-x-shiki__code');
356
+ wrapperSelectorHolder.classList.add('ce-editorjs-x-shiki__selector');
357
+ wrapperLang.classList.add('ce-editorjs-x-shiki__lang');
358
+ wrapperCopy.classList.add('ce-editorjs-x-shiki__copy');
359
+ wrapperCopyTip.classList.add('ce-editorjs-x-shiki__copy_tip');
360
+
361
+ selectorLanguage.classList.add(this.CSS.selectorLanguage);
362
+ //selectorTheme.classList.add(this.CSS.selectorTheme);
363
+ textarea.classList.add(this.CSS.textarea, this.CSS.input);
364
+
365
+ wrapperLang.innerHTML = this.data.lang
366
+ wrapperCopy.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.25 8.5H10.25C9.2835 8.5 8.5 9.2835 8.5 10.25V17.25C8.5 18.2165 9.2835 19 10.25 19H17.25C18.2165 19 19 18.2165 19 17.25V10.25C19 9.2835 18.2165 8.5 17.25 8.5Z"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.5 8.5V6.75C15.5 6.28587 15.3156 5.84075 14.9874 5.51256C14.6592 5.18437 14.2141 5 13.75 5H6.75C6.28587 5 5.84075 5.18437 5.51256 5.51256C5.18437 5.84075 5 6.28587 5 6.75V13.75C5 14.2141 5.18437 14.6592 5.51256 14.9874C5.84075 15.3156 6.28587 15.5 6.75 15.5H8.5"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 12L15.5 12"/><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15.5L15.5 15.5"/></svg>`;
367
+ wrapperCopyTip.innerText=this.api.i18n.t('Copied');
368
+
369
+
370
+
371
+ languages.forEach((lang) => {
372
+ const option = document.createElement('option');
373
+ option.value = lang.id;
374
+ option.text = lang.name;
375
+ selectorLanguage.appendChild(option);
376
+ });
377
+ selectorLanguage.value = this.data.lang
378
+
379
+ // themes.forEach((theme) => {
380
+ // const option = document.createElement('option');
381
+ // option.value = theme.id;
382
+ // option.text = theme.displayName;
383
+ // selectorTheme.appendChild(option);
384
+ // });
385
+ // selectorTheme.value = this.data.theme
386
+
387
+ textarea.value = this.data.code;
388
+ textarea.placeholder = this.placeholder;
389
+ textarea.spellcheck = false;
390
+ textarea.autocomplete = "off"
391
+ textarea.autocapitalize = "off"
392
+
393
+ if (this.readOnly) {
394
+ textarea.disabled = true;
395
+ }
396
+
397
+ wrapperHolder.appendChild(span);
398
+ wrapperHolder.appendChild(textarea);
399
+
400
+ if(!this.readOnly) {
401
+ wrapperSelectorHolder.appendChild(selectorLanguage);
402
+ //wrapperSelectorHolder.appendChild(selectorTheme);
403
+
404
+ } else {
405
+ wrapperSelectorHolder.appendChild(wrapperLang);
406
+
407
+ }
408
+ wrapperCopy.appendChild(wrapperCopyTip);
409
+
410
+ wrapperSelectorHolder.appendChild(wrapperCopy);
411
+ wrapper.appendChild(wrapperSelectorHolder);
412
+
413
+ wrapper.appendChild(wrapperHolder);
414
+ //wrapper.appendChild(wrapperLang);
415
+
416
+ this.runShiki().then(({ html, preStyle }) => {
417
+
418
+ span.innerHTML = html
419
+ wrapper?.setAttribute('style', preStyle)
420
+ selectorLanguage.setAttribute('style', preStyle)
421
+ selectorTheme.setAttribute('style', preStyle)
422
+ })
423
+
424
+ selectorLanguage.addEventListener('change', (event: Event) => {
425
+ const lang = (event.target as HTMLSelectElement).value
426
+ this._selectorLanguage = lang
427
+ this.runShiki().then(({ html, preStyle }) => {
428
+ span.innerHTML = html
429
+ wrapperLang.innerHTML = lang
430
+ })
431
+ })
432
+
433
+ selectorTheme.addEventListener('change', (event: Event) => {
434
+ const theme = (event.target as HTMLSelectElement).value
435
+ this._selectorTheme = theme
436
+ this.runShiki().then(({ html, preStyle }) => {
437
+ span.innerHTML = html
438
+ wrapper?.setAttribute('style', preStyle)
439
+ selectorLanguage.setAttribute('style', preStyle)
440
+ selectorTheme.setAttribute('style', preStyle)
441
+ })
442
+ })
443
+
444
+ textarea.addEventListener('input', () => {
445
+ this.data.code = textarea.value
446
+ this.runShiki().then(({ html, preStyle }) => {
447
+ span.innerHTML = html
448
+ wrapper?.setAttribute('style', preStyle)
449
+ selectorLanguage.setAttribute('style', preStyle)
450
+ selectorTheme.setAttribute('style', preStyle)
451
+ })
452
+ })
453
+
454
+ /**
455
+ * Enable keydown handlers
456
+ */
457
+ textarea.addEventListener('keydown', (event) => {
458
+ switch (event.code) {
459
+ case 'Tab':
460
+ this.tabHandler(event);
461
+ this.data.code = textarea.value
462
+ this.runShiki().then(({ html, preStyle }) => {
463
+ span.innerHTML = html
464
+ })
465
+ break;
466
+ }
467
+ });
468
+ wrapperCopy.addEventListener('click', (event: MouseEvent) => {
469
+ this.copyCode(this.data.code,event);
470
+ });
471
+
472
+ this.nodes.textarea = textarea;
473
+
474
+ return wrapper;
475
+ }
476
+
477
+ private async runShiki(): Promise<{ html: string, preStyle: string }> {
478
+ let preStyle = ''
479
+
480
+ const html = await codeToHtml(this.data.code, {
481
+ lang: this._selectorLanguage,
482
+ theme: this._selectorTheme,
483
+ transformers: [
484
+ {
485
+ preprocess(code) {
486
+ // if (code.endsWith('\n'))
487
+ return `${code}\n`
488
+ },
489
+ pre(node) {
490
+ this.addClassToHast(node, 'ce-editorjs-x-shiki__span')
491
+ preStyle = node.properties?.style as string || ''
492
+ },
493
+ }
494
+ ]
495
+ })
496
+
497
+ return {
498
+ html,
499
+ preStyle
500
+ }
501
+
502
+ }
503
+
504
+ private copyCode(code: string, event: MouseEvent) {
505
+ if (this.data.code) {
506
+ navigator.clipboard.writeText(code)
507
+ .then(() => {
508
+ if (event.target) {
509
+ const parentEle = (event.target as HTMLElement).parentElement;
510
+ if (parentEle) {
511
+ const tooltip = parentEle.lastChild as HTMLElement;
512
+ if (tooltip) {
513
+ tooltip.classList.add('visible');
514
+ setTimeout(() => {
515
+ tooltip.classList.remove('visible');
516
+
517
+ }, 2000);
518
+ }
519
+ }
520
+ }
521
+
522
+
523
+ })
524
+ .catch((err) => {
525
+ console.error(err);
526
+ alert("Unable to copy");
527
+ });
528
+ }
529
+ }
530
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Return the position of line starting from passed point
3
+ *
4
+ * ┌───────────────┐
5
+ * │1234\n │
6
+ * │2eda | dadd\n │ <-- returns 5
7
+ * └───────────────┘
8
+ * @param string - string to process
9
+ * @param position - search starting position
10
+ * @returns
11
+ */
12
+ export function getLineStartPosition(string: string, position: number): number {
13
+ const charLength = 1;
14
+ let char = '';
15
+
16
+ /**
17
+ * Iterate through all the chars before the position till the
18
+ * - end of line (\n)
19
+ * - or start of string (position === 0)
20
+ */
21
+ while (char !== '\n' && position > 0) {
22
+ position = position - charLength;
23
+ char = string.substr(position, charLength);
24
+ }
25
+
26
+ /**
27
+ * Do not count the linebreak symbol because it is related to the previous line
28
+ */
29
+ if (char === '\n') {
30
+ position += 1;
31
+ }
32
+
33
+ return position;
34
+ }
@@ -0,0 +1,138 @@
1
+ import { API } from '@ebl-vue/editorjs';
2
+ import type { InlineTool } from "@ebl-vue/editorjs/types/inline-tool";
3
+ const IconColor = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.24296 11.4075C5.23167 10.6253 5.52446 9.8395 6.12132 9.24264L9.65686 5.70711C10.0474 5.31658 10.6809 5.31693 11.0714 5.70745L16.0205 10.6565C16.2268 10.8629 16.3243 11.1371 16.3126 11.4075M5.24296 11.4075C5.25382 12.1607 5.54661 12.9106 6.12132 13.4853L8 15.364M5.24296 11.4075H11.9565M16.3126 11.4075C16.3022 11.6487 16.205 11.8869 16.0208 12.0711L12.4853 15.6066C11.3137 16.7782 9.41421 16.7782 8.24264 15.6066L8 15.364M16.3126 11.4075H11.9565M8 15.364L11.9565 11.4075" stroke="black" stroke-width="2"/>
4
+ <path d="M20 17.4615C20 18.3112 19.3284 19 18.5 19C17.6716 19 17 18.3112 17 17.4615C17 16.6119 17.9 15.6154 18.5 15C19.1 15.6154 20 16.6119 20 17.4615Z" stroke="black" stroke-width="2"/></svg>`;
5
+ import './styles.css';
6
+
7
+ import {type InlineToolConstructorOptions} from "@ebl-vue/editorjs/types/tools/inline-tool";
8
+
9
+ type ColorPickerConfig = {
10
+ colors: string[];
11
+ columns: number;
12
+ };
13
+
14
+ // interface ConstructorArgs {
15
+ // api: API;
16
+ // config: ColorPickerConfig;
17
+ // }
18
+
19
+ export default class ColorPicker {
20
+ private api: API;
21
+
22
+ tag = 'SPAN';
23
+ class = 'cdx-text-color';
24
+ defaultColor = '#2644FF';
25
+
26
+ lastRange: Range | null = null;
27
+
28
+ colors: string[] = [
29
+ '#182a4e',
30
+ '#0055cc',
31
+ '#1f6a83',
32
+ '#206e4e',
33
+ '#e56910',
34
+ '#ae2e24',
35
+ '#5e4db2',
36
+ '#758195',
37
+ '#1e7afd',
38
+ '#2998bd',
39
+ '#23a06b',
40
+ '#fea363',
41
+ '#c9372c',
42
+ '#8270db',
43
+ ];
44
+ columns = 7;
45
+
46
+ static get title() {
47
+ return 'Color';
48
+ }
49
+
50
+ static get isInline() {
51
+ return true;
52
+ }
53
+
54
+ constructor(args:InlineToolConstructorOptions) {
55
+ const { api, config } = args;
56
+ this.api = api;
57
+
58
+ if (config.colors) {
59
+ this.colors = config.colors;
60
+ }
61
+ if (config.columns) {
62
+ this.columns = config.columns;
63
+ }
64
+ }
65
+
66
+ render() {
67
+ const button = document.createElement('button');
68
+
69
+ button.type = 'button';
70
+ button.innerHTML = IconColor;
71
+ button.classList.add(this.api.styles.inlineToolButton);
72
+
73
+ button.addEventListener('mousedown', (e) => {
74
+ // prevent text deselection when clicking the button
75
+ e.preventDefault();
76
+ });
77
+
78
+ return button;
79
+ }
80
+
81
+ surround(range: Range | null) {
82
+ this.lastRange = range;
83
+ }
84
+
85
+ wrapAndColor(range: Range | null, color: string) {
86
+ if (!range) {
87
+ return;
88
+ }
89
+ const selectedText = range.extractContents();
90
+ const span = document.createElement(this.tag);
91
+ span.classList.add(this.class);
92
+ span.appendChild(selectedText);
93
+ span.style.color = color;
94
+ span.innerHTML = span.textContent || '';
95
+ range.insertNode(span);
96
+
97
+ this.api.selection.expandToTag(span);
98
+ }
99
+
100
+ renderActions() {
101
+ const container = document.createElement('div');
102
+ container.classList.add('editorjs__color-selector-container');
103
+ container.style.gridTemplateColumns = `repeat(${this.columns}, 1fr)`;
104
+
105
+ this.colors.forEach((colorValue) => {
106
+ const color = document.createElement('div');
107
+ color.classList.add('editorjs__color-selector__container-item');
108
+ color.style.backgroundColor = colorValue;
109
+ color.onclick = () => {
110
+ this.wrapAndColor(this.lastRange, colorValue);
111
+ };
112
+ container.append(color);
113
+ });
114
+
115
+ return container;
116
+ }
117
+
118
+ /**
119
+ * Sanitizer rules
120
+ *
121
+ * @returns {object}
122
+ */
123
+ static get sanitize(): any {
124
+ return {
125
+ span: {
126
+ style: {
127
+ color: true,
128
+ },
129
+ },
130
+ };
131
+ }
132
+ }
133
+
134
+ export class ColorPickerWithoutSanitize extends ColorPicker {
135
+ static override get sanitize() {
136
+ return undefined;
137
+ }
138
+ }