@ebl-vue/editor-full 2.31.10 → 2.31.12

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 (39) hide show
  1. package/dist/index.mjs +560 -405
  2. package/dist/index.mjs.map +1 -1
  3. package/package.json +2 -4
  4. package/src/components/Editor/Editor.vue +22 -6
  5. package/src/plugins/code/index.css +31 -2
  6. package/src/plugins/code/index.ts +7 -0
  7. package/src/plugins/list/ListRenderer/ChecklistRenderer.ts +1 -1
  8. package/src/plugins/list/ListTabulator/index.ts +1 -1
  9. package/src/plugins/list/index.ts +118 -123
  10. package/src/plugins/list/styles/.cdx-list .css +168 -0
  11. package/src/plugins/list/utils/normalizeData.ts +0 -1
  12. package/src/plugins/list-bak/ListRenderer/ChecklistRenderer.ts +208 -0
  13. package/src/plugins/list-bak/ListRenderer/ListRenderer.ts +73 -0
  14. package/src/plugins/list-bak/ListRenderer/OrderedListRenderer.ts +123 -0
  15. package/src/plugins/list-bak/ListRenderer/UnorderedListRenderer.ts +123 -0
  16. package/src/plugins/list-bak/ListRenderer/index.ts +6 -0
  17. package/src/plugins/list-bak/ListTabulator/index.ts +1179 -0
  18. package/src/plugins/list-bak/index.ts +485 -0
  19. package/src/plugins/list-bak/styles/CssPrefix.ts +4 -0
  20. package/src/plugins/list-bak/styles/input.css +36 -0
  21. package/src/plugins/list-bak/styles/list.css +165 -0
  22. package/src/plugins/list-bak/types/Elements.ts +14 -0
  23. package/src/plugins/list-bak/types/ItemMeta.ts +40 -0
  24. package/src/plugins/list-bak/types/ListParams.ts +102 -0
  25. package/src/plugins/list-bak/types/ListRenderer.ts +6 -0
  26. package/src/plugins/list-bak/types/OlCounterType.ts +63 -0
  27. package/src/plugins/list-bak/types/index.ts +14 -0
  28. package/src/plugins/list-bak/utils/focusItem.ts +18 -0
  29. package/src/plugins/list-bak/utils/getChildItems.ts +40 -0
  30. package/src/plugins/list-bak/utils/getItemChildWrapper.ts +10 -0
  31. package/src/plugins/list-bak/utils/getItemContentElement.ts +10 -0
  32. package/src/plugins/list-bak/utils/getSiblings.ts +52 -0
  33. package/src/plugins/list-bak/utils/isLastItem.ts +9 -0
  34. package/src/plugins/list-bak/utils/itemHasSublist.ts +10 -0
  35. package/src/plugins/list-bak/utils/normalizeData.ts +84 -0
  36. package/src/plugins/list-bak/utils/removeChildWrapperIfEmpty.ts +31 -0
  37. package/src/plugins/list-bak/utils/renderToolboxInput.ts +105 -0
  38. package/src/plugins/list-bak/utils/stripNumbers.ts +7 -0
  39. package/src/plugins/list-bak/utils/type-guards.ts +8 -0
@@ -0,0 +1,40 @@
1
+ import type { OlCounterType } from './OlCounterType';
2
+
3
+ /**
4
+ * Meta information of each list item
5
+ */
6
+ export interface ItemMetaBase {}
7
+
8
+ /**
9
+ * Meta information of checklist item
10
+ */
11
+ export interface ChecklistItemMeta extends ItemMetaBase {
12
+ /**
13
+ * State of the checkbox of the item
14
+ */
15
+ checked: boolean;
16
+ }
17
+
18
+ /**
19
+ * Meta information of ordered list item
20
+ */
21
+ export interface OrderedListItemMeta extends ItemMetaBase {
22
+ /**
23
+ * If passed, ordered list counters will start with this index
24
+ */
25
+ start?: number;
26
+ /**
27
+ * Counters type used only in ordered list
28
+ */
29
+ counterType?: OlCounterType;
30
+ }
31
+
32
+ /**
33
+ * Meta information of unordered list item
34
+ */
35
+ export interface UnorderedListItemMeta extends ItemMetaBase {}
36
+
37
+ /**
38
+ * Type that represents all available meta objects for list item
39
+ */
40
+ export type ItemMeta = ChecklistItemMeta | OrderedListItemMeta | UnorderedListItemMeta;
@@ -0,0 +1,102 @@
1
+ import type { ItemMeta } from './ItemMeta';
2
+ import type { OlCounterType } from './OlCounterType';
3
+
4
+ /**
5
+ * list style to make list as ordered or unordered
6
+ */
7
+ export type ListDataStyle = 'ordered' | 'unordered' | 'checklist';
8
+
9
+ /**
10
+ * Interface that represents data of the List tool
11
+ */
12
+ export type ListData = Omit<ListItem, 'content'> & {
13
+ /**
14
+ * Style of the list tool
15
+ */
16
+ style: ListDataStyle;
17
+ };
18
+
19
+ /**
20
+ * Interface that represents data of the List tool
21
+ */
22
+ export interface OldListData {
23
+ /**
24
+ * Style of the List tool
25
+ */
26
+ style: 'ordered' | 'unordered';
27
+ /**
28
+ * Array of items of the List tool
29
+ */
30
+ items: string[];
31
+ }
32
+
33
+ /**
34
+ * Type that represents data of the List tool
35
+ */
36
+ export type OldNestedListData = Omit<ListData, 'meta'>;
37
+
38
+ /**
39
+ * Interface that represents old checklist data format
40
+ */
41
+ export interface OldChecklistData {
42
+ /**
43
+ * Checklist items
44
+ */
45
+ items: OldChecklistItem[];
46
+ }
47
+
48
+ /**
49
+ * Interface that represents old checklist item format
50
+ */
51
+ interface OldChecklistItem {
52
+ /**
53
+ * Text of the checklist item
54
+ */
55
+ text: string;
56
+ /**
57
+ * Checked state of the checklist item
58
+ */
59
+ checked: boolean;
60
+ }
61
+
62
+ /**
63
+ * List item within the output data
64
+ */
65
+ export interface ListItem {
66
+ /**
67
+ * list item text content
68
+ */
69
+ content: string;
70
+
71
+ /**
72
+ * Meta information of each list item
73
+ */
74
+ meta: ItemMeta;
75
+
76
+ /**
77
+ * sublist items
78
+ */
79
+ items: ListItem[];
80
+ }
81
+
82
+ /**
83
+ * Tool's configuration
84
+ */
85
+ export interface ListConfig {
86
+ /**
87
+ * default list style: ordered or unordered
88
+ * default is unordered
89
+ */
90
+ defaultStyle?: ListDataStyle;
91
+ /**
92
+ * Max level of the nesting in list
93
+ * If nesting is not needed, it could be set to 1
94
+ */
95
+ maxLevel?: number;
96
+ /**
97
+ * Specifies which counter types should be shown in the ordered list style selector.
98
+ * @example ['numeric', 'upper-roman'] // Shows selector with these two options
99
+ * @default undefined // All counter types are available when not specified
100
+ */
101
+ counterTypes?: OlCounterType[];
102
+ }
@@ -0,0 +1,6 @@
1
+ import type { CheckListRenderer, OrderedListRenderer, UnorderedListRenderer } from '../ListRenderer';
2
+
3
+ /**
4
+ * Type that represents all possible list renderer types
5
+ */
6
+ export type ListRenderer = CheckListRenderer | OrderedListRenderer | UnorderedListRenderer;
@@ -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,84 @@
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
+
43
+ const normalizedDataItems: ListItem[] = [];
44
+
45
+ if (instanceOfOldListData(data)) {
46
+ data.items.forEach((item) => {
47
+ normalizedDataItems.push({
48
+ content: item,
49
+ meta: {},
50
+ items: [],
51
+ });
52
+ });
53
+
54
+ return {
55
+ style: data.style,
56
+ meta: {},
57
+ items: normalizedDataItems,
58
+ };
59
+ } else if (instanceOfChecklistData(data)) {
60
+ data.items.forEach((item) => {
61
+ normalizedDataItems.push({
62
+ content: item.text,
63
+ meta: {
64
+ checked: item.checked,
65
+ },
66
+ items: [],
67
+ });
68
+ });
69
+
70
+ return {
71
+ style: 'checklist',
72
+ meta: {},
73
+ items: normalizedDataItems,
74
+ };
75
+ } else if (instanceOfOldNestedListData(data)) {
76
+ return {
77
+ style: data.style,
78
+ meta: {},
79
+ items: data.items,
80
+ };
81
+ } else {
82
+ return structuredClone(data);
83
+ }
84
+ };
@@ -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,105 @@
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(inputCallback: (index: string) => void,
44
+ { value, placeholder, attributes, sanitize }: InputOptions): HTMLElement {
45
+ const startWithElementWrapper = Dom.make('div', css.wrapper);
46
+
47
+ const input = Dom.make('input', css.input, {
48
+ placeholder,
49
+ /**
50
+ * Used to prevent focusing on the input by Tab key
51
+ * (Popover in the Toolbar lays below the blocks,
52
+ * so Tab in the last block will focus this hidden input if this property is not set)
53
+ */
54
+ tabIndex: -1,
55
+ /**
56
+ * Value of the start property, if it is not specified, then it is set to one
57
+ */
58
+ value,
59
+ }) as HTMLInputElement;
60
+
61
+ /**
62
+ * Add passed attributes to the input
63
+ */
64
+ for (const attribute in attributes) {
65
+ input.setAttribute(attribute, attributes[attribute]);
66
+ }
67
+
68
+ startWithElementWrapper.appendChild(input);
69
+
70
+ input.addEventListener('input', () => {
71
+ /**
72
+ * If input sanitizer specified, then sanitize input value
73
+ */
74
+ if (sanitize !== undefined) {
75
+ input.value = sanitize(input.value);
76
+ }
77
+
78
+ const validInput = input.checkValidity();
79
+
80
+ /**
81
+ * If input is invalid and classlist does not contain invalid class, add it
82
+ */
83
+ if (!validInput && !startWithElementWrapper.classList.contains(css.startWithElementWrapperInvalid)) {
84
+ startWithElementWrapper.classList.add(css.startWithElementWrapperInvalid);
85
+ }
86
+
87
+ /**
88
+ * If input is valid and classlist contains invalid class, remove it
89
+ */
90
+ if (validInput && startWithElementWrapper.classList.contains(css.startWithElementWrapperInvalid)) {
91
+ startWithElementWrapper.classList.remove(css.startWithElementWrapperInvalid);
92
+ }
93
+
94
+ /**
95
+ * If input is invalid, than do not change start with attribute
96
+ */
97
+ if (!validInput) {
98
+ return;
99
+ }
100
+
101
+ inputCallback(input.value);
102
+ });
103
+
104
+ return startWithElementWrapper;
105
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Removes everything except numbers in passed string
3
+ * @param input - string to be striped
4
+ */
5
+ export default function stripNumbers(input: string): string {
6
+ return input.replace(/\D+/g, '');
7
+ }
@@ -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
+ }