@ebl-vue/editor-full 1.0.12 → 1.1.1

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 (52) hide show
  1. package/dist/index.d.ts +6 -0
  2. package/dist/index.mjs +860 -445
  3. package/dist/index.mjs.map +1 -1
  4. package/package.json +2 -1
  5. package/src/components/Editor/Editor.vue +47 -12
  6. package/src/i18n/zh-cn.ts +6 -1
  7. package/src/icons/index.ts +15 -0
  8. package/src/installer.ts +4 -3
  9. package/src/plugins/alert/index.ts +19 -27
  10. package/src/plugins/block-alignment/index.ts +4 -3
  11. package/src/plugins/code/index.ts +3 -2
  12. package/src/plugins/color-picker/index.ts +3 -11
  13. package/src/plugins/delimiter/index.ts +5 -6
  14. package/src/plugins/header/H1.ts +1 -1
  15. package/src/plugins/header/H2.ts +1 -1
  16. package/src/plugins/header/H3.ts +1 -1
  17. package/src/plugins/header/H4.ts +1 -2
  18. package/src/plugins/header/H5.ts +1 -3
  19. package/src/plugins/header/H6.ts +1 -3
  20. package/src/plugins/imageResizeCrop/ImageTune.ts +900 -0
  21. package/src/plugins/imageResizeCrop/index.css +234 -0
  22. package/src/plugins/imageResizeCrop/index.ts +5 -0
  23. package/src/plugins/imageResizeCrop/types.d.ts +23 -0
  24. package/src/plugins/imageTool/index.css +145 -0
  25. package/src/plugins/imageTool/index.ts +538 -0
  26. package/src/plugins/imageTool/types/codexteam__ajax.d.ts +89 -0
  27. package/src/plugins/imageTool/types/types.ts +236 -0
  28. package/src/plugins/imageTool/ui.ts +313 -0
  29. package/src/plugins/imageTool/uploader.ts +268 -0
  30. package/src/plugins/imageTool/utils/dom.ts +24 -0
  31. package/src/plugins/imageTool/utils/index.ts +73 -0
  32. package/src/plugins/imageTool/utils/isPromise.ts +10 -0
  33. package/src/plugins/indent/index.ts +5 -7
  34. package/src/plugins/inline-code/index.ts +2 -5
  35. package/src/plugins/list/ListRenderer/ChecklistRenderer.ts +1 -4
  36. package/src/plugins/list/index.ts +20 -37
  37. package/src/plugins/list/types/OlCounterType.ts +1 -1
  38. package/src/plugins/marker/index.ts +28 -16
  39. package/src/plugins/paragraph/index.ts +3 -2
  40. package/src/plugins/quote/index.ts +1 -4
  41. package/src/plugins/table/plugin.ts +1 -1
  42. package/src/plugins/table/table.ts +40 -38
  43. package/src/plugins/table/toolbox.ts +5 -4
  44. package/src/plugins/table/utils/dom.ts +15 -14
  45. package/src/plugins/table/utils/popover.ts +28 -15
  46. package/src/plugins/underline/index.ts +2 -4
  47. package/src/plugins/undo/index.ts +48 -33
  48. package/src/plugins/undo/observer.ts +3 -3
  49. package/src/utils/AxiosService.ts +87 -0
  50. package/types/index.d.ts +6 -0
  51. package/vite.config.ts +3 -1
  52. package/src/plugins/list/styles/icons/index.ts +0 -10
@@ -0,0 +1,538 @@
1
+ /**
2
+ * Image Tool for the Editor.js
3
+ * @author CodeX <team@codex.so>
4
+ * @license MIT
5
+ * @see {@link https://github.com/editor-js/image}
6
+ *
7
+ * To developers.
8
+ * To simplify Tool structure, we split it to 4 parts:
9
+ * 1) index.ts — main Tool's interface, public API and methods for working with data
10
+ * 2) uploader.ts — module that has methods for sending files via AJAX: from device, by URL or File pasting
11
+ * 3) ui.ts — module for UI manipulations: render, showing preloader, etc
12
+ *
13
+ * For debug purposes there is a testing server
14
+ * that can save uploaded files and return a Response {@link UploadResponseFormat}
15
+ *
16
+ * $ node dev/server.js
17
+ *
18
+ * It will expose 8008 port, so you can pass http://localhost:8008 with the Tools config:
19
+ *
20
+ * image: {
21
+ * class: ImageTool,
22
+ * config: {
23
+ * endpoints: {
24
+ * byFile: 'http://localhost:8008/uploadFile',
25
+ * byUrl: 'http://localhost:8008/fetchUrl',
26
+ * }
27
+ * },
28
+ * },
29
+ */
30
+
31
+ import type { TunesMenuConfig } from '@ebl-vue/editorjs/types/tools';
32
+ import type { API, ToolboxConfig, PasteConfig, BlockToolConstructorOptions, BlockTool, BlockAPI, PasteEvent, PatternPasteEventDetail, FilePasteEventDetail } from '@editorjs/editorjs';
33
+ import './index.css';
34
+
35
+ import Ui from './ui';
36
+ import Uploader from './uploader';
37
+
38
+ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture, IconText } from '../../icons';
39
+ import type { ActionConfig, UploadResponseFormat, ImageToolData, ImageConfig, HTMLPasteEventDetailExtended, ImageSetterParam, FeaturesConfig } from './types/types';
40
+
41
+ type ImageToolConstructorOptions = BlockToolConstructorOptions<ImageToolData, ImageConfig>;
42
+
43
+ /**
44
+ * Implementation of ImageTool class
45
+ */
46
+ export default class ImageTool implements BlockTool {
47
+ /**
48
+ * Editor.js API instance
49
+ */
50
+ private api: API;
51
+
52
+ /**
53
+ * Current Block API instance
54
+ */
55
+ private block: BlockAPI;
56
+
57
+ /**
58
+ * Configuration for the ImageTool
59
+ */
60
+ private config: ImageConfig;
61
+
62
+ /**
63
+ * Uploader module instance
64
+ */
65
+ private uploader: any;
66
+
67
+ /**
68
+ * UI module instance
69
+ */
70
+ private ui: Ui;
71
+
72
+ /**
73
+ * Stores current block data internally
74
+ */
75
+ private _data: ImageToolData;
76
+
77
+ private userStore:any;
78
+
79
+
80
+ /**
81
+ * Caption enabled state
82
+ * Null when user has not toggled the caption tune
83
+ * True when user has toggled the caption tune
84
+ * False when user has toggled the caption tune
85
+ */
86
+ private isCaptionEnabled: boolean | null = null;
87
+
88
+ /**
89
+ * @param tool - tool properties got from editor.js
90
+ * @param tool.data - previously saved data
91
+ * @param tool.config - user config for Tool
92
+ * @param tool.api - Editor.js API
93
+ * @param tool.readOnly - read-only mode flag
94
+ * @param tool.block - current Block API
95
+ */
96
+ constructor({ data, config, api, readOnly, block }: ImageToolConstructorOptions) {
97
+ this.api = api;
98
+ this.block = block;
99
+ this.userStore=config?.userStore;
100
+
101
+ /**
102
+ * Tool's initial config
103
+ */
104
+ this.config = {
105
+ endpoints: config.endpoints,
106
+ additionalRequestData: config.additionalRequestData,
107
+ additionalRequestHeaders: config.additionalRequestHeaders,
108
+ field: config.field,
109
+ types: config.types,
110
+ captionPlaceholder: this.api.i18n.t(config.captionPlaceholder ?? 'Caption'),
111
+ buttonContent: config.buttonContent,
112
+ uploader: config.uploader,
113
+ actions: config.actions,
114
+ features: config.features || {},
115
+ userStore: config.userStore,
116
+ };
117
+
118
+
119
+ /**
120
+ * Module for file uploading
121
+ */
122
+ this.uploader = new Uploader({
123
+ config: this.config,
124
+ onUpload: (response: UploadResponseFormat) => this.onUpload(response),
125
+ onError: (error: string) => this.uploadingFailed(error),
126
+ });
127
+
128
+ /**
129
+ * Module for working with UI
130
+ */
131
+ this.ui = new Ui({
132
+ api,
133
+ config: this.config,
134
+ onSelectFile: () => {
135
+ this.uploader.uploadSelectedFile({
136
+ onPreview: (src: string) => {
137
+ this.ui.showPreloader(src);
138
+ },
139
+ noSelectedFile:()=>{
140
+ this.noSelectedFile();
141
+ }
142
+ });
143
+ },
144
+ readOnly,
145
+ });
146
+
147
+ /**
148
+ * Set saved state
149
+ */
150
+ this._data = {
151
+ caption: '',
152
+ withBorder: false,
153
+ withBackground: false,
154
+ stretched: false,
155
+ file: {
156
+ url: '',
157
+ },
158
+ };
159
+ this.data = data;
160
+ }
161
+
162
+ private noSelectedFile(): void {
163
+ this.api.blocks.delete(this.api.blocks.getCurrentBlockIndex());
164
+ }
165
+ private deleteCurrentBlock() {
166
+ this.api.blocks.delete(this.api.blocks.getCurrentBlockIndex());
167
+ }
168
+ /**
169
+ * Notify core that read-only mode is supported
170
+ */
171
+ public static get isReadOnlySupported(): boolean {
172
+ return true;
173
+ }
174
+
175
+ /**
176
+ * Get Tool toolbox settings
177
+ * icon - Tool icon's SVG
178
+ * title - title to show in toolbox
179
+ */
180
+ public static get toolbox(): ToolboxConfig {
181
+ return {
182
+ icon: IconPicture,
183
+ title: 'Image',
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Available image tools
189
+ */
190
+ public static get tunes(): Array<ActionConfig> {
191
+ return [
192
+ {
193
+ name: 'withBorder',
194
+ icon: IconAddBorder,
195
+ title: 'With border',
196
+ toggle: true,
197
+ },
198
+ {
199
+ name: 'stretched',
200
+ icon: IconStretch,
201
+ title: 'Stretch image',
202
+ toggle: true,
203
+ },
204
+ {
205
+ name: 'withBackground',
206
+ icon: IconAddBackground,
207
+ title: 'With background',
208
+ toggle: true,
209
+ },
210
+ ];
211
+ }
212
+
213
+ /**
214
+ * Renders Block content
215
+ */
216
+ public render(): HTMLDivElement {
217
+ if (this.config.features?.caption === true || this.config.features?.caption === undefined || (this.config.features?.caption === 'optional' && this.data.caption)) {
218
+ this.isCaptionEnabled = true;
219
+ this.ui.applyTune('caption', true);
220
+ }
221
+
222
+ return this.ui.render() as HTMLDivElement;
223
+ }
224
+
225
+ /**
226
+ * Validate data: check if Image exists
227
+ * @param savedData — data received after saving
228
+ * @returns false if saved data is not correct, otherwise true
229
+ */
230
+ public validate(savedData: ImageToolData): boolean {
231
+ return !!savedData.file.url;
232
+ }
233
+
234
+ /**
235
+ * Return Block data
236
+ */
237
+ public save(): ImageToolData {
238
+ const caption = this.ui.nodes.caption;
239
+
240
+ this._data.caption = caption.innerHTML;
241
+
242
+ return this.data;
243
+ }
244
+
245
+ /**
246
+ * Returns configuration for block tunes: add background, add border, stretch image
247
+ * @returns TunesMenuConfig
248
+ */
249
+ public renderSettings(): TunesMenuConfig {
250
+ // Merge default tunes with the ones that might be added by user
251
+ // @see https://github.com/editor-js/image/pull/49
252
+ const tunes = ImageTool.tunes.concat(this.config.actions || []);
253
+ const featureTuneMap: Record<string, string> = {
254
+ border: 'withBorder',
255
+ background: 'withBackground',
256
+ stretch: 'stretched',
257
+ caption: 'caption',
258
+ };
259
+
260
+ if (this.config.features?.caption === 'optional') {
261
+ tunes.push({
262
+ name: 'caption',
263
+ icon: IconText,
264
+ title: 'With caption',
265
+ toggle: true,
266
+ });
267
+ }
268
+
269
+ const availableTunes = tunes.filter((tune) => {
270
+ const featureKey = Object.keys(featureTuneMap).find(key => featureTuneMap[key] === tune.name);
271
+
272
+ if (featureKey === 'caption') {
273
+ return this.config.features?.caption !== false;
274
+ }
275
+
276
+ return featureKey == null || this.config.features?.[featureKey as keyof FeaturesConfig] !== false;
277
+ });
278
+
279
+ /**
280
+ * Check if the tune is active
281
+ * @param tune - tune to check
282
+ */
283
+ const isActive = (tune: ActionConfig): boolean => {
284
+ let currentState = this.data[tune.name as keyof ImageToolData] as boolean;
285
+
286
+ if (tune.name === 'caption') {
287
+ currentState = this.isCaptionEnabled ?? currentState;
288
+ }
289
+
290
+ return currentState;
291
+ };
292
+
293
+ return availableTunes.map(tune => ({
294
+ icon: tune.icon,
295
+ label: this.api.i18n.t(tune.title),
296
+ name: tune.name,
297
+ toggle: tune.toggle,
298
+ isActive: isActive(tune),
299
+ onActivate: () => {
300
+ /** If it'a user defined tune, execute it's callback stored in action property */
301
+ if (typeof tune.action === 'function') {
302
+ tune.action(tune.name);
303
+
304
+ return;
305
+ }
306
+ let newState = !isActive(tune);
307
+
308
+ /**
309
+ * For the caption tune, we can't rely on the this._data
310
+ * because it can be manualy toggled by user
311
+ */
312
+ if (tune.name === 'caption') {
313
+ this.isCaptionEnabled = !(this.isCaptionEnabled ?? false);
314
+ newState = this.isCaptionEnabled;
315
+ }
316
+
317
+ this.tuneToggled(tune.name as keyof ImageToolData, newState);
318
+ },
319
+ }));
320
+ }
321
+
322
+ /**
323
+ * Fires after clicks on the Toolbox Image Icon
324
+ * Initiates click on the Select File button
325
+ */
326
+ public appendCallback(): void {
327
+ this.ui.nodes.fileButton.click();
328
+ }
329
+
330
+ /**
331
+ * Specify paste substitutes
332
+ * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
333
+ */
334
+ public static get pasteConfig(): PasteConfig {
335
+ return {
336
+ /**
337
+ * Paste HTML into Editor
338
+ */
339
+ tags: [
340
+ {
341
+ img: { src: true },
342
+ },
343
+ ],
344
+ /**
345
+ * Paste URL of image into the Editor
346
+ */
347
+ // patterns: {
348
+ // image: /https?:\/\/\S+\.(gif|jpe?g|png|webp)(\?[a-z0-9=]*)?$/i,
349
+ // },
350
+
351
+ /**
352
+ * Drag n drop file from into the Editor
353
+ */
354
+ files: {
355
+ mimeTypes: ['image/*'],
356
+ },
357
+ };
358
+ }
359
+
360
+ /**
361
+ * Specify paste handlers
362
+ * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
363
+ * @param event - editor.js custom paste event
364
+ * {@link https://github.com/codex-team/editor.js/blob/master/types/tools/paste-events.d.ts}
365
+ */
366
+ public async onPaste(event: PasteEvent): Promise<void> {
367
+ switch (event.type) {
368
+ case 'tag': {
369
+ const image = (event.detail as HTMLPasteEventDetailExtended).data;
370
+
371
+ /** Images from PDF */
372
+ if (/^blob:/.test(image.src)) {
373
+ const response = await fetch(image.src);
374
+
375
+ const file = await response.blob();
376
+
377
+ this.uploadFile(file);
378
+ break;
379
+ }
380
+
381
+ this.uploadUrl(image.src);
382
+ break;
383
+ }
384
+ // case 'pattern': {
385
+ // const url = (event.detail as PatternPasteEventDetail).data;
386
+
387
+ // this.uploadUrl(url);
388
+ // break;
389
+ // }
390
+ case 'file': {
391
+ const file = (event.detail as FilePasteEventDetail).file;
392
+
393
+ this.uploadFile(file);
394
+ break;
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Private methods
401
+ */
402
+
403
+ /**
404
+ * Stores all Tool's data
405
+ * @param data - data in Image Tool format
406
+ */
407
+ private set data(data: ImageToolData) {
408
+ this.image = data.file;
409
+
410
+ this._data.caption = data.caption || '';
411
+ this.ui.fillCaption(this._data.caption);
412
+
413
+ ImageTool.tunes.forEach(({ name: tune }) => {
414
+ const value = typeof data[tune as keyof ImageToolData] !== 'undefined' ? data[tune as keyof ImageToolData] === true || data[tune as keyof ImageToolData] === 'true' : false;
415
+
416
+ this.setTune(tune as keyof ImageToolData, value);
417
+ });
418
+
419
+ if (data.caption) {
420
+ this.setTune('caption', true);
421
+ } else if (this.config.features?.caption === true) {
422
+ this.setTune('caption', true);
423
+ }
424
+ }
425
+
426
+ /**
427
+ * Return Tool data
428
+ */
429
+ private get data(): ImageToolData {
430
+ return this._data;
431
+ }
432
+
433
+ /**
434
+ * Set new image file
435
+ * @param file - uploaded file data
436
+ */
437
+ private set image(file: ImageSetterParam | undefined) {
438
+ this._data.file = file || { url: '' };
439
+
440
+ if (file && file.url) {
441
+ this.ui.fillImage(file.url);
442
+ }
443
+ }
444
+
445
+ /**
446
+ * File uploading callback
447
+ * @param response - uploading server response
448
+ */
449
+ private onUpload(response: UploadResponseFormat): void {
450
+ if (response.success && Boolean(response.file)) {
451
+ this.image = response.file;
452
+ } else {
453
+ this.uploadingFailed('incorrect response: ' + JSON.stringify(response));
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Handle uploader errors
459
+ * @param errorText - uploading error info
460
+ */
461
+ private uploadingFailed(errorText: string): void {
462
+ console.log('Image Tool: uploading failed because of', errorText);
463
+ let errorMessage = this.api.i18n.t('Couldn’t upload image. Please try another.');
464
+ if (errorText) {
465
+ errorMessage = this.api.i18n.t(errorText);
466
+ }
467
+ this.api.notifier.show({
468
+ message: errorMessage,
469
+ style: 'error',
470
+ });
471
+ this.ui.hidePreloader();
472
+ this.deleteCurrentBlock();
473
+ }
474
+
475
+ /**
476
+ * Callback fired when Block Tune is activated
477
+ * @param tuneName - tune that has been clicked
478
+ * @param state - new state
479
+ */
480
+ private tuneToggled(tuneName: keyof ImageToolData, state: boolean): void {
481
+ if (tuneName === 'caption') {
482
+ this.ui.applyTune(tuneName, state);
483
+
484
+ if (state == false) {
485
+ this._data.caption = '';
486
+ this.ui.fillCaption('');
487
+ }
488
+ } else {
489
+ /**
490
+ * Inverse tune state
491
+ */
492
+ this.setTune(tuneName, state);
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Set one tune
498
+ * @param tuneName - {@link Tunes.tunes}
499
+ * @param value - tune state
500
+ */
501
+ private setTune(tuneName: keyof ImageToolData, value: boolean): void {
502
+ (this._data[tuneName] as boolean) = value;
503
+
504
+ this.ui.applyTune(tuneName, value);
505
+ if (tuneName === 'stretched') {
506
+ /**
507
+ * Wait until the API is ready
508
+ */
509
+ Promise.resolve().then(() => {
510
+ this.block.stretched = value;
511
+ })
512
+ .catch((err) => {
513
+ console.error(err);
514
+ });
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Show preloader and upload image file
520
+ * @param file - file that is currently uploading (from paste)
521
+ */
522
+ private uploadFile(file: Blob): void {
523
+ this.uploader.uploadByFile(file, {
524
+ onPreview: (src: string) => {
525
+ this.ui.showPreloader(src);
526
+ },
527
+ });
528
+ }
529
+
530
+ /**
531
+ * Show preloader and upload image by target url
532
+ * @param url - url pasted
533
+ */
534
+ private uploadUrl(url: string): void {
535
+ this.ui.showPreloader(url);
536
+ this.uploader.uploadByUrl(url);
537
+ }
538
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Module declaration for '@codexteam/ajax'.
3
+ */
4
+ declare module '@codexteam/ajax' {
5
+ /**
6
+ * Options for configuring an Ajax request.
7
+ */
8
+ export interface AjaxOptions {
9
+ /**
10
+ * The URL to which the request is sent.
11
+ */
12
+ url?: string;
13
+ /**
14
+ * The data to send with the request.
15
+ */
16
+ data?: object;
17
+ /**
18
+ * The MIME type of the request.
19
+ */
20
+ accept?: string;
21
+ /**
22
+ * The headers to send with the request.
23
+ */
24
+ headers?: object;
25
+ /**
26
+ * A function to call before the request is sent, with the files to be sent.
27
+ */
28
+ beforeSend?: (files: File[]) => void;
29
+ /**
30
+ * The name of the field in the form data to which the file should be assigned.
31
+ */
32
+ fieldName?: string;
33
+ /**
34
+ * The type of the request (e.g., 'POST', 'GET').
35
+ */
36
+ type?: string;
37
+ }
38
+
39
+ /**
40
+ * Parameter type of selectFiles function in AjaxOptions interface
41
+ */
42
+ export type AjaxFileOptionsParam = {
43
+ /**
44
+ * the accepted file types.
45
+ */
46
+ accept: string;
47
+ };
48
+
49
+ /**
50
+ * Represents the response from an Ajax request.
51
+ * @template T - The type of the response body.
52
+ */
53
+ export interface AjaxResponse<T = object> {
54
+ /** The body of the response. */
55
+ body: T;
56
+ }
57
+
58
+ /**
59
+ * Prompts the user to select files and returns a promise that resolves with the selected files.
60
+ * @param options - Options for file selection.
61
+ * @param options.accept - The accepted file types.
62
+ * @returns A promise that resolves with the selected files.
63
+ */
64
+ export function selectFiles(options: AjaxFileOptionsParam): Promise<File[]>;
65
+
66
+ /**
67
+ * Sends an Ajax request with the specified options.
68
+ * @param options - Options for the Ajax request.
69
+ * @returns A promise that resolves with the Ajax response.
70
+ */
71
+ export function transport(options: AjaxOptions): Promise<AjaxResponse>;
72
+
73
+ /**
74
+ * Sends a POST request with the specified options.
75
+ * @param options - Options for the POST request.
76
+ * @returns A promise that resolves with the Ajax response.
77
+ */
78
+ export function post(options: AjaxOptions): Promise<AjaxResponse>;
79
+
80
+ /**
81
+ * Represents common content types.
82
+ */
83
+ export const contentType: {
84
+ /**
85
+ * The MIME type for JSON content.
86
+ */
87
+ JSON: string;
88
+ };
89
+ }