@ebl-vue/editor-full 2.31.35 → 2.31.37
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.
- package/.postcssrc.yml +33 -0
- package/dist/index.d.ts +3 -7
- package/dist/index.mjs +184 -183
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/postcss.config.js +15 -0
- package/src/components/Editor/Editor.vue +293 -0
- package/src/components/index.ts +27 -0
- package/src/constants/index.ts +1 -0
- package/src/i18n/zh-cn.ts +160 -0
- package/src/icons/index.ts +93 -0
- package/src/index.ts +21 -0
- package/src/installer.ts +21 -0
- package/src/plugins/alert/index.ts +455 -0
- package/src/plugins/block-alignment/index.ts +117 -0
- package/src/plugins/block-alignment/readme.md +1 -0
- package/src/plugins/code/LICENSE +21 -0
- package/src/plugins/code/index.ts +619 -0
- package/src/plugins/code/utils/string.ts +34 -0
- package/src/plugins/color-picker/index.ts +132 -0
- package/src/plugins/delimiter/index.ts +121 -0
- package/src/plugins/drag-drop/index.css +19 -0
- package/src/plugins/drag-drop/index.ts +151 -0
- package/src/plugins/drag-drop/readme.md +1 -0
- package/src/plugins/header/H1.ts +404 -0
- package/src/plugins/header/H2.ts +403 -0
- package/src/plugins/header/H3.ts +404 -0
- package/src/plugins/header/H4.ts +404 -0
- package/src/plugins/header/H5.ts +403 -0
- package/src/plugins/header/H6.ts +404 -0
- package/src/plugins/header/index.ts +15 -0
- package/src/plugins/header/types.d.ts +46 -0
- package/src/plugins/imageResizeCrop/ImageTune.ts +635 -0
- package/src/plugins/imageResizeCrop/index.css +230 -0
- package/src/plugins/imageResizeCrop/index.ts +5 -0
- package/src/plugins/imageResizeCrop/types.d.ts +23 -0
- package/src/plugins/imageTool/index.ts +510 -0
- package/src/plugins/imageTool/types/codexteam__ajax.d.ts +89 -0
- package/src/plugins/imageTool/types/types.ts +236 -0
- package/src/plugins/imageTool/ui.ts +313 -0
- package/src/plugins/imageTool/uploader.ts +293 -0
- package/src/plugins/imageTool/utils/dom.ts +24 -0
- package/src/plugins/imageTool/utils/index.ts +73 -0
- package/src/plugins/imageTool/utils/isPromise.ts +10 -0
- package/src/plugins/indent/index.ts +695 -0
- package/src/plugins/inline-code/index.ts +203 -0
- package/src/plugins/list/ListRenderer/ChecklistRenderer.ts +208 -0
- package/src/plugins/list/ListRenderer/ListRenderer.ts +73 -0
- package/src/plugins/list/ListRenderer/OrderedListRenderer.ts +123 -0
- package/src/plugins/list/ListRenderer/UnorderedListRenderer.ts +123 -0
- package/src/plugins/list/ListRenderer/index.ts +6 -0
- package/src/plugins/list/ListTabulator/index.ts +1179 -0
- package/src/plugins/list/index.ts +488 -0
- package/src/plugins/list/styles/CssPrefix.ts +4 -0
- package/src/plugins/list/types/Elements.ts +14 -0
- package/src/plugins/list/types/ItemMeta.ts +40 -0
- package/src/plugins/list/types/ListParams.ts +102 -0
- package/src/plugins/list/types/ListRenderer.ts +6 -0
- package/src/plugins/list/types/OlCounterType.ts +63 -0
- package/src/plugins/list/types/index.ts +14 -0
- package/src/plugins/list/utils/focusItem.ts +18 -0
- package/src/plugins/list/utils/getChildItems.ts +40 -0
- package/src/plugins/list/utils/getItemChildWrapper.ts +10 -0
- package/src/plugins/list/utils/getItemContentElement.ts +10 -0
- package/src/plugins/list/utils/getSiblings.ts +52 -0
- package/src/plugins/list/utils/isLastItem.ts +9 -0
- package/src/plugins/list/utils/itemHasSublist.ts +10 -0
- package/src/plugins/list/utils/normalizeData.ts +83 -0
- package/src/plugins/list/utils/removeChildWrapperIfEmpty.ts +31 -0
- package/src/plugins/list/utils/renderToolboxInput.ts +113 -0
- package/src/plugins/list/utils/stripNumbers.ts +7 -0
- package/src/plugins/list/utils/type-guards.ts +8 -0
- package/src/plugins/marker/index.ts +199 -0
- package/src/plugins/outline/index.ts +62 -0
- package/src/plugins/outline/outline.css +52 -0
- package/src/plugins/paragraph/index.ts +384 -0
- package/src/plugins/paragraph/types/icons.d.ts +4 -0
- package/src/plugins/paragraph/utils/makeFragment.ts +17 -0
- package/src/plugins/quote/index.ts +203 -0
- package/src/plugins/table/index.ts +4 -0
- package/src/plugins/table/plugin.ts +255 -0
- package/src/plugins/table/table.ts +1202 -0
- package/src/plugins/table/toolbox.ts +166 -0
- package/src/plugins/table/utils/dom.ts +130 -0
- package/src/plugins/table/utils/popover.ts +185 -0
- package/src/plugins/table/utils/throttled.ts +22 -0
- package/src/plugins/underline/index.ts +214 -0
- package/src/plugins/undo/index.ts +526 -0
- package/src/plugins/undo/observer.ts +101 -0
- package/src/plugins/undo/vanilla-caret-js.ts +102 -0
- package/src/style.css +139 -0
- package/src/types.ts +3 -0
- package/src/utils/AxiosService.ts +87 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/install.ts +19 -0
- package/tsconfig.json +37 -0
- package/vite.config.ts +81 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { IconNumber, IconLowerRoman, IconUpperRoman, IconLowerAlpha, IconUpperAlpha } from '../../../icons';
|
|
2
|
+
|
|
3
|
+
export type OlCounterType = 'numeric' | 'upper-roman' | 'lower-roman' | 'upper-alpha' | 'lower-alpha';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Map that represents all of the supported styles of the counters for ordered list
|
|
7
|
+
*/
|
|
8
|
+
export const OlCounterTypesMap = new Map<string, string>([
|
|
9
|
+
/**
|
|
10
|
+
* Value that represents default arabic numbers for counters
|
|
11
|
+
*/
|
|
12
|
+
['Numeric', 'numeric'],
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Value that represents lower roman numbers for counteres
|
|
16
|
+
*/
|
|
17
|
+
['Lower Roman', 'lower-roman'],
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Value that represents upper roman numbers for counters
|
|
21
|
+
*/
|
|
22
|
+
['Upper Roman', 'upper-roman'],
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Value that represents lower alpha characters for counters
|
|
26
|
+
*/
|
|
27
|
+
['Lower Alpha', 'lower-alpha'],
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Value that represents upper alpha characters for counters
|
|
31
|
+
*/
|
|
32
|
+
['Upper Alpha', 'upper-alpha'],
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Map that represents relation between supported counter types and theirs icons to be displayed in toolbox
|
|
37
|
+
*/
|
|
38
|
+
export const OlCounterIconsMap = new Map<string, string>([
|
|
39
|
+
/**
|
|
40
|
+
* Value that represents Icon for Numeric counter type
|
|
41
|
+
*/
|
|
42
|
+
['numeric', IconNumber],
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Value that represents Icon for Lower Roman counter type
|
|
46
|
+
*/
|
|
47
|
+
['lower-roman', IconLowerRoman],
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Value that represents Icon for Upper Roman counter type
|
|
51
|
+
*/
|
|
52
|
+
['upper-roman', IconUpperRoman],
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Value that represents Icon for Lower Alpha counter type
|
|
56
|
+
*/
|
|
57
|
+
['lower-alpha', IconLowerAlpha],
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Value that represents Icon for Upper Alpha counter type
|
|
61
|
+
*/
|
|
62
|
+
['upper-alpha', IconUpperAlpha],
|
|
63
|
+
]);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paste event for tag substitution, similar to editor.js PasteEvent but with a different data type
|
|
3
|
+
*/
|
|
4
|
+
export interface PasteEvent extends CustomEvent {
|
|
5
|
+
/**
|
|
6
|
+
* Pasted element
|
|
7
|
+
*/
|
|
8
|
+
detail: {
|
|
9
|
+
/**
|
|
10
|
+
* Supported elements fir the paste event
|
|
11
|
+
*/
|
|
12
|
+
data: HTMLUListElement | HTMLOListElement | HTMLLIElement;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { focus } from '@editorjs/caret';
|
|
2
|
+
import type { ItemElement } from '../types/Elements';
|
|
3
|
+
import { getItemContentElement } from './getItemContentElement';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets focus to the item's content
|
|
7
|
+
* @param item - item (<li>) to select
|
|
8
|
+
* @param atStart - where to set focus: at the start or at the end
|
|
9
|
+
*/
|
|
10
|
+
export function focusItem(item: ItemElement, atStart: boolean = true): void {
|
|
11
|
+
const itemContent = getItemContentElement(item);
|
|
12
|
+
|
|
13
|
+
if (!itemContent) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
focus(itemContent, atStart);
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DefaultListCssClasses } from '../ListRenderer';
|
|
2
|
+
import type { ItemChildWrapperElement, ItemElement } from '../types/Elements';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get child items of the passed element
|
|
6
|
+
* @param element - child items would be got from this element
|
|
7
|
+
* @param firstLevelChildren - if method should return all level child items or only first level ones
|
|
8
|
+
*/
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
|
|
10
|
+
export function getChildItems(element: ItemElement | ItemChildWrapperElement, firstLevelChildren: boolean = true): ItemElement[] {
|
|
11
|
+
let itemChildWrapper: HTMLElement = element;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* If passed element is list item than get item's child wrapper
|
|
15
|
+
*/
|
|
16
|
+
if (element.classList.contains(DefaultListCssClasses.item)) {
|
|
17
|
+
itemChildWrapper = element.querySelector(`.${DefaultListCssClasses.itemChildren}`) as HTMLElement;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if itemChildWrapper is not null
|
|
22
|
+
*/
|
|
23
|
+
if (itemChildWrapper === null) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (firstLevelChildren) {
|
|
28
|
+
/**
|
|
29
|
+
* Filter first level child items of the curret child item wrapper
|
|
30
|
+
* In case that child could be not only list item
|
|
31
|
+
*/
|
|
32
|
+
return Array.from(itemChildWrapper.querySelectorAll(`:scope > .${DefaultListCssClasses.item}`));
|
|
33
|
+
} else {
|
|
34
|
+
/**
|
|
35
|
+
* Filter all levels child items of the current child item wrapper
|
|
36
|
+
* In case that child could be not only list item
|
|
37
|
+
*/
|
|
38
|
+
return Array.from(itemChildWrapper.querySelectorAll(`.${DefaultListCssClasses.item}`));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ItemChildWrapperElement, ItemElement } from '../types/Elements';
|
|
2
|
+
import { DefaultListCssClasses } from '../ListRenderer';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns child wrapper element of the passed item
|
|
6
|
+
* @param item - wrapper element would be got from this item
|
|
7
|
+
*/
|
|
8
|
+
export function getItemChildWrapper(item: ItemElement): ItemChildWrapperElement | null {
|
|
9
|
+
return item.querySelector(`.${DefaultListCssClasses.itemChildren}`);
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ItemContentElement, ItemElement } from '../types/Elements';
|
|
2
|
+
import { DefaultListCssClasses } from '../ListRenderer';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns content element of the passed item
|
|
6
|
+
* @param item - content element would be got from this item
|
|
7
|
+
*/
|
|
8
|
+
export function getItemContentElement(item: ItemElement): ItemContentElement | null {
|
|
9
|
+
return item.querySelector(`.${DefaultListCssClasses.itemContent}`);
|
|
10
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get all siblings before passed element, or after it
|
|
3
|
+
* @param element - html element whose siblings would be returned
|
|
4
|
+
* @param direction - wherever siblings would be returned, after element of before it
|
|
5
|
+
*/
|
|
6
|
+
export function getSiblings(element: HTMLElement, direction: 'after' | 'before' = 'after'): Element[] | null {
|
|
7
|
+
const siblings: Element[] = [];
|
|
8
|
+
|
|
9
|
+
let nextElementSibling: HTMLElement;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Method that is responsible for getting next element sibling responsible to the direction variable
|
|
13
|
+
* @param el - current element
|
|
14
|
+
* @returns HTML element of the sibling
|
|
15
|
+
*/
|
|
16
|
+
function getNextElementSibling(el: HTMLElement): HTMLElement {
|
|
17
|
+
/**
|
|
18
|
+
* Get first sibling element respectfully to passed direction
|
|
19
|
+
*/
|
|
20
|
+
switch (direction) {
|
|
21
|
+
case 'after':
|
|
22
|
+
return el.nextElementSibling as HTMLElement;
|
|
23
|
+
|
|
24
|
+
case 'before':
|
|
25
|
+
return el.previousElementSibling as HTMLElement;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
nextElementSibling = getNextElementSibling(element);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Iterate by all siblings elements
|
|
33
|
+
*/
|
|
34
|
+
while (nextElementSibling !== null) {
|
|
35
|
+
siblings.push(nextElementSibling);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get next element sibling
|
|
39
|
+
*/
|
|
40
|
+
nextElementSibling = getNextElementSibling(nextElementSibling);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check that formed siblings array is not empty
|
|
45
|
+
* If it is emtpy, return null
|
|
46
|
+
*/
|
|
47
|
+
if (siblings.length !== 0) {
|
|
48
|
+
return siblings;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ItemElement } from '../types/Elements';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check that passed item element is last item of the list
|
|
5
|
+
* @param item - item to be checked, wherever it has next element sibling
|
|
6
|
+
*/
|
|
7
|
+
export function isLastItem(item: ItemElement): boolean {
|
|
8
|
+
return item.nextElementSibling === null;
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ItemElement } from '../types/Elements';
|
|
2
|
+
import { DefaultListCssClasses } from '../ListRenderer';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if passed item has the sublist
|
|
6
|
+
* @param item - item to be checked wherever it has sublist
|
|
7
|
+
*/
|
|
8
|
+
export function itemHasSublist(item: ItemElement): boolean {
|
|
9
|
+
return item.querySelector(`.${DefaultListCssClasses.itemChildren}`) !== null;
|
|
10
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { OldListData, ListData, ListItem, OldChecklistData, OldNestedListData } from '../types/ListParams';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Method that checks if data is result of the Old list tool save mtehod
|
|
5
|
+
* @param data - data of the OldList, Checklist, OldNestedList or Editorjs List tool
|
|
6
|
+
* @returns true if data related to the List tool, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
function instanceOfOldListData(data: ListData | OldListData | OldChecklistData | OldNestedListData): data is OldListData {
|
|
9
|
+
return (typeof data.items[0] === 'string');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Method that checks if data is result of the Old nested list tool save method
|
|
14
|
+
* @param data - data of the OldList, Checklist, OldNestedList or Editorjs List tool
|
|
15
|
+
* @returns true if data is related to the Nested List tool, false otherwise
|
|
16
|
+
*/
|
|
17
|
+
function instanceOfOldNestedListData(data: ListData | OldListData | OldChecklistData | OldNestedListData): data is OldNestedListData {
|
|
18
|
+
return !('meta' in data);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Method that checks if data is result of the Old checklist tool save method
|
|
23
|
+
* @param data - data of the Checklist, OldList, OldNestedList or Editorjs List tool
|
|
24
|
+
* @returns true if data is related to the Checklist tool, false otherwise
|
|
25
|
+
*/
|
|
26
|
+
function instanceOfChecklistData(data: ListData | OldListData | OldChecklistData | OldNestedListData): data is OldChecklistData {
|
|
27
|
+
return (
|
|
28
|
+
typeof data.items[0] !== 'string'
|
|
29
|
+
&& 'text' in data.items[0]
|
|
30
|
+
&& 'checked' in data.items[0]
|
|
31
|
+
&& typeof data.items[0].text === 'string'
|
|
32
|
+
&& typeof data.items[0].checked === 'boolean'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Method that checks if passed data is related to the legacy format and normalizes it
|
|
38
|
+
* @param data - data to be checked
|
|
39
|
+
* @returns - normalized data, ready to be used by Editorjs List tool
|
|
40
|
+
*/
|
|
41
|
+
export default function normalizeData(data: ListData | OldListData | OldChecklistData): ListData {
|
|
42
|
+
const normalizedDataItems: ListItem[] = [];
|
|
43
|
+
|
|
44
|
+
if (instanceOfOldListData(data)) {
|
|
45
|
+
data.items.forEach((item) => {
|
|
46
|
+
normalizedDataItems.push({
|
|
47
|
+
content: item,
|
|
48
|
+
meta: {},
|
|
49
|
+
items: [],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
style: data.style,
|
|
55
|
+
meta: {},
|
|
56
|
+
items: normalizedDataItems,
|
|
57
|
+
};
|
|
58
|
+
} else if (instanceOfChecklistData(data)) {
|
|
59
|
+
data.items.forEach((item) => {
|
|
60
|
+
normalizedDataItems.push({
|
|
61
|
+
content: item.text,
|
|
62
|
+
meta: {
|
|
63
|
+
checked: item.checked,
|
|
64
|
+
},
|
|
65
|
+
items: [],
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
style: 'checklist',
|
|
71
|
+
meta: {},
|
|
72
|
+
items: normalizedDataItems,
|
|
73
|
+
};
|
|
74
|
+
} else if (instanceOfOldNestedListData(data)) {
|
|
75
|
+
return {
|
|
76
|
+
style: data.style,
|
|
77
|
+
meta: {},
|
|
78
|
+
items: data.items,
|
|
79
|
+
};
|
|
80
|
+
} else {
|
|
81
|
+
return structuredClone(data);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DefaultListCssClasses } from '../ListRenderer';
|
|
2
|
+
import type { ItemChildWrapperElement, ItemElement } from '../types/Elements';
|
|
3
|
+
import { getChildItems } from './getChildItems';
|
|
4
|
+
import { getItemChildWrapper } from './getItemChildWrapper';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Method that will remove passed child wrapper if it has no child items
|
|
8
|
+
* @param element - child wrapper or actual item, from where we get child wrapper
|
|
9
|
+
*/
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
|
|
11
|
+
export function removeChildWrapperIfEmpty(element: ItemChildWrapperElement | ItemElement): void {
|
|
12
|
+
let itemChildWrapper: HTMLElement | null = element;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* If passed element is list item than get item's child wrapper
|
|
16
|
+
*/
|
|
17
|
+
if (element.classList.contains(DefaultListCssClasses.item)) {
|
|
18
|
+
itemChildWrapper = getItemChildWrapper(element);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (itemChildWrapper === null) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check that there is at least one item
|
|
27
|
+
*/
|
|
28
|
+
if (getChildItems(itemChildWrapper).length === 0) {
|
|
29
|
+
itemChildWrapper.remove();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as Dom from '@editorjs/dom';
|
|
2
|
+
import { CssPrefix } from '../styles/CssPrefix';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options used in input rendering
|
|
6
|
+
*/
|
|
7
|
+
interface InputOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Placeholder, that will be displayed in input
|
|
10
|
+
*/
|
|
11
|
+
placeholder: string;
|
|
12
|
+
/**
|
|
13
|
+
* Input will be rendered with this value inside
|
|
14
|
+
*/
|
|
15
|
+
value?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Html attributes, that would be added to the input element
|
|
18
|
+
*/
|
|
19
|
+
attributes?: {
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Flag that represents special behavior that prevents you from entering anything other than numbers
|
|
24
|
+
*/
|
|
25
|
+
sanitize?: (value: string) => string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const css = {
|
|
29
|
+
wrapper: `${CssPrefix}-start-with-field`,
|
|
30
|
+
input: `${CssPrefix}-start-with-field__input`,
|
|
31
|
+
startWithElementWrapperInvalid: `${CssPrefix}-start-with-field--invalid`,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Method that renders html element for popover start with item
|
|
36
|
+
* @param inputCallback - callback method that could change list attributes on input
|
|
37
|
+
* @param inputOptions - options used in input rendering
|
|
38
|
+
* @param inputOptions.value - input will be rendered with this value inside
|
|
39
|
+
* @param inputOptions.placeholder - placeholder, that will be displayed in input
|
|
40
|
+
* @param inputOptions.attributes - html attributes, that would be added to the input element
|
|
41
|
+
* @returns - rendered html element
|
|
42
|
+
*/
|
|
43
|
+
export function renderToolboxInput(startLabel:string,inputCallback: (index: string) => void,
|
|
44
|
+
{ value, placeholder, attributes, sanitize }: InputOptions): HTMLElement {
|
|
45
|
+
const startWithElementWrapper = Dom.make('div', css.wrapper);
|
|
46
|
+
const startLabelTag = Dom.make('div', 'cdx-list-start-with-field__startlabel', {
|
|
47
|
+
innerText:startLabel
|
|
48
|
+
})
|
|
49
|
+
const inpuWrapper=Dom.make('div', 'cdx-list-start-with-field__input-wrapper');
|
|
50
|
+
|
|
51
|
+
const input = Dom.make('input', css.input, {
|
|
52
|
+
placeholder,
|
|
53
|
+
/**
|
|
54
|
+
* Used to prevent focusing on the input by Tab key
|
|
55
|
+
* (Popover in the Toolbar lays below the blocks,
|
|
56
|
+
* so Tab in the last block will focus this hidden input if this property is not set)
|
|
57
|
+
*/
|
|
58
|
+
tabIndex: -1,
|
|
59
|
+
/**
|
|
60
|
+
* Value of the start property, if it is not specified, then it is set to one
|
|
61
|
+
*/
|
|
62
|
+
value,
|
|
63
|
+
type: 'number'
|
|
64
|
+
}) as HTMLInputElement;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Add passed attributes to the input
|
|
68
|
+
*/
|
|
69
|
+
for (const attribute in attributes) {
|
|
70
|
+
input.setAttribute(attribute, attributes[attribute]);
|
|
71
|
+
}
|
|
72
|
+
startWithElementWrapper.appendChild(startLabelTag);
|
|
73
|
+
inpuWrapper.appendChild(input);
|
|
74
|
+
startWithElementWrapper.appendChild(inpuWrapper);
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
input.addEventListener('input', () => {
|
|
79
|
+
/**
|
|
80
|
+
* If input sanitizer specified, then sanitize input value
|
|
81
|
+
*/
|
|
82
|
+
if (sanitize !== undefined) {
|
|
83
|
+
input.value = sanitize(input.value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const validInput = input.checkValidity();
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* If input is invalid and classlist does not contain invalid class, add it
|
|
90
|
+
*/
|
|
91
|
+
if (!validInput && !startWithElementWrapper.classList.contains(css.startWithElementWrapperInvalid)) {
|
|
92
|
+
startWithElementWrapper.classList.add(css.startWithElementWrapperInvalid);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* If input is valid and classlist contains invalid class, remove it
|
|
97
|
+
*/
|
|
98
|
+
if (validInput && startWithElementWrapper.classList.contains(css.startWithElementWrapperInvalid)) {
|
|
99
|
+
startWithElementWrapper.classList.remove(css.startWithElementWrapperInvalid);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* If input is invalid, than do not change start with attribute
|
|
104
|
+
*/
|
|
105
|
+
if (!validInput) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
inputCallback(input.value);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return startWithElementWrapper;
|
|
113
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if a node is an HTMLElement, then we can safely use it as an HTMLElement
|
|
3
|
+
* @param node - Node to be checked, wherever it is an HTMLElement
|
|
4
|
+
*/
|
|
5
|
+
export function isHtmlElement(node: Node): node is HTMLElement {
|
|
6
|
+
// node is an HTMLElement if it is an element node
|
|
7
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
8
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build styles
|
|
3
|
+
*/
|
|
4
|
+
import '@ebl-vue/editor-render/styles/marker.css';
|
|
5
|
+
import type { API } from '@ebl-vue/editorjs';
|
|
6
|
+
import { IconMarker } from '../../icons';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Marker Tool for the Editor.js
|
|
10
|
+
*
|
|
11
|
+
* Allows to wrap inline fragment and style it somehow.
|
|
12
|
+
*/
|
|
13
|
+
export default class Marker {
|
|
14
|
+
private api: API;
|
|
15
|
+
private button: HTMLElement | null;
|
|
16
|
+
private tag: string;
|
|
17
|
+
private iconClasses: {base: string; active: string};
|
|
18
|
+
|
|
19
|
+
static get toolboxIcon() {
|
|
20
|
+
return IconMarker;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Class name for term-tag
|
|
24
|
+
*
|
|
25
|
+
* @type {string}
|
|
26
|
+
*/
|
|
27
|
+
static get CSS() {
|
|
28
|
+
return 'cdx-marker';
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {{api: object}} - Editor.js API
|
|
33
|
+
*/
|
|
34
|
+
constructor({api}: {api: any}) {
|
|
35
|
+
this.api = api;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Toolbar Button
|
|
39
|
+
*
|
|
40
|
+
* @type {HTMLElement|null}
|
|
41
|
+
*/
|
|
42
|
+
this.button = null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Tag represented the term
|
|
46
|
+
*
|
|
47
|
+
* @type {string}
|
|
48
|
+
*/
|
|
49
|
+
this.tag = 'MARK';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* CSS classes
|
|
53
|
+
*/
|
|
54
|
+
this.iconClasses = {
|
|
55
|
+
base: this.api.styles.inlineToolButton,
|
|
56
|
+
active: this.api.styles.inlineToolButtonActive
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Specifies Tool as Inline Toolbar Tool
|
|
62
|
+
*
|
|
63
|
+
* @return {boolean}
|
|
64
|
+
*/
|
|
65
|
+
static get isInline() {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create button element for Toolbar
|
|
71
|
+
*
|
|
72
|
+
* @return {HTMLElement}
|
|
73
|
+
*/
|
|
74
|
+
render() {
|
|
75
|
+
this.button = document.createElement('button');
|
|
76
|
+
//this.button.type = 'button';
|
|
77
|
+
this.button.setAttribute("type", "button");
|
|
78
|
+
this.button.classList.add(this.iconClasses.base);
|
|
79
|
+
this.button.innerHTML = this.toolboxIcon;
|
|
80
|
+
|
|
81
|
+
return this.button;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Wrap/Unwrap selected fragment
|
|
86
|
+
*
|
|
87
|
+
* @param {Range} range - selected fragment
|
|
88
|
+
*/
|
|
89
|
+
surround(range:Range) {
|
|
90
|
+
if (!range) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let termWrapper = this.api.selection.findParentTag(this.tag, Marker.CSS);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* If start or end of selection is in the highlighted block
|
|
98
|
+
*/
|
|
99
|
+
if (termWrapper) {
|
|
100
|
+
this.unwrap(termWrapper);
|
|
101
|
+
} else {
|
|
102
|
+
this.wrap(range);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Wrap selection with term-tag
|
|
108
|
+
*
|
|
109
|
+
* @param {Range} range - selected fragment
|
|
110
|
+
*/
|
|
111
|
+
wrap(range:Range) {
|
|
112
|
+
/**
|
|
113
|
+
* Create a wrapper for highlighting
|
|
114
|
+
*/
|
|
115
|
+
let marker = document.createElement(this.tag);
|
|
116
|
+
|
|
117
|
+
marker.classList.add(Marker.CSS);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* SurroundContent throws an error if the Range splits a non-Text node with only one of its boundary points
|
|
121
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents}
|
|
122
|
+
*
|
|
123
|
+
* // range.surroundContents(span);
|
|
124
|
+
*/
|
|
125
|
+
marker.appendChild(range.extractContents());
|
|
126
|
+
range.insertNode(marker);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Expand (add) selection to highlighted block
|
|
130
|
+
*/
|
|
131
|
+
this.api.selection.expandToTag(marker);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Unwrap term-tag
|
|
136
|
+
*
|
|
137
|
+
* @param {HTMLElement} termWrapper - term wrapper tag
|
|
138
|
+
*/
|
|
139
|
+
unwrap(termWrapper: HTMLElement) {
|
|
140
|
+
/**
|
|
141
|
+
* Expand selection to all term-tag
|
|
142
|
+
*/
|
|
143
|
+
this.api.selection.expandToTag(termWrapper);
|
|
144
|
+
|
|
145
|
+
let sel = window.getSelection();
|
|
146
|
+
let range = sel?.getRangeAt(0);
|
|
147
|
+
|
|
148
|
+
let unwrappedContent = range?.extractContents();
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Remove empty term-tag
|
|
152
|
+
*/
|
|
153
|
+
termWrapper?.parentNode?.removeChild(termWrapper);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Insert extracted content
|
|
157
|
+
*/
|
|
158
|
+
if (unwrappedContent) {
|
|
159
|
+
range?.insertNode(unwrappedContent);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Restore selection
|
|
163
|
+
*/
|
|
164
|
+
sel?.removeAllRanges();
|
|
165
|
+
if (range) {
|
|
166
|
+
sel?.addRange(range);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check and change Term's state for current selection
|
|
172
|
+
*/
|
|
173
|
+
checkState() {
|
|
174
|
+
const termTag = this.api.selection.findParentTag(this.tag, Marker.CSS);
|
|
175
|
+
|
|
176
|
+
this.button?.classList.toggle(this.iconClasses.active, !!termTag);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get Tool icon's SVG
|
|
181
|
+
* @return {string}
|
|
182
|
+
*/
|
|
183
|
+
get toolboxIcon() {
|
|
184
|
+
return IconMarker;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Sanitizer rule
|
|
189
|
+
* @return {{mark: {class: string}}}
|
|
190
|
+
*/
|
|
191
|
+
static get sanitize() {
|
|
192
|
+
return {
|
|
193
|
+
mark: {
|
|
194
|
+
class: Marker.CSS
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|