From 0ec196058fade3f36f8f26558873bc04fe8bc807 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Wed, 19 Jun 2024 20:12:11 +0800 Subject: [PATCH 01/18] feat: Migrate to typescript --- package.json | 6 +- src/{index.js => index.ts} | 507 +++++++++++++++++++++++-------- src/types/icons.d.ts | 5 + src/types/index.ts | 11 + src/utils/{caret.js => caret.ts} | 93 ++++-- src/utils/{dom.js => dom.ts} | 26 +- src/utils/type-guards.ts | 9 + tsconfig.json | 18 ++ vite.config.js | 5 +- yarn.lock | 439 +++++++++++++++++++++++++- 10 files changed, 951 insertions(+), 168 deletions(-) rename src/{index.js => index.ts} (61%) create mode 100644 src/types/icons.d.ts create mode 100644 src/types/index.ts rename src/utils/{caret.js => caret.ts} (67%) rename src/utils/{dom.js => dom.ts} (63%) create mode 100644 src/utils/type-guards.ts create mode 100644 tsconfig.json diff --git a/package.json b/package.json index 80e73cbf..e8b1035d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "require": "./dist/nested-list.umd.js" } }, + "types": "./dist/index.d.ts", "scripts": { "dev": "vite", "build": "vite build", @@ -30,12 +31,15 @@ "lint:fix": "eslint src/ --fix" }, "devDependencies": { + "@editorjs/editorjs": "^2.29.1", "eslint": "^7.22.0", "eslint-loader": "^4.0.2", "postcss-nested": "^5.0.3", "postcss-nested-ancestors": "^2.0.0", + "typescript": "^5.4.5", "vite": "^4.5.0", - "vite-plugin-css-injected-by-js": "^3.3.0" + "vite-plugin-css-injected-by-js": "^3.3.0", + "vite-plugin-dts": "^3.9.1" }, "dependencies": { "@codexteam/icons": "^0.0.2" diff --git a/src/index.js b/src/index.ts similarity index 61% rename from src/index.js rename to src/index.ts index aba7b72c..ca9a11a6 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,23 +1,95 @@ -import * as Dom from './utils/dom'; -import Caret from './utils/caret'; -import { IconListBulleted, IconListNumbered } from '@codexteam/icons'; +import type { API, PasteConfig, ToolboxConfig } from "@editorjs/editorjs"; +import type { PasteEvent } from "./types"; +import type { TunesMenuConfig } from "@editorjs/editorjs/types/tools"; + +import { isHtmlElement } from "./utils/type-guards"; + +import * as Dom from "./utils/dom"; +import Caret from "./utils/caret"; +import { IconListBulleted, IconListNumbered } from "@codexteam/icons"; /** * Build styles */ -import './../styles/index.pcss'; +import "./../styles/index.pcss"; + +/** + * Output data + */ +interface ListData { + /** + * list type 'ordered' or 'unordered' + */ + style: string; + /** + * list of first-level elements + */ + items: ListItem[]; +} /** - * @typedef {object} ListData - * @property {string} style - list type 'ordered' or 'unordered' - * @property {ListItem[]} items - list of first-level elements + * List item within the output data */ +interface ListItem { + /** + * list item text content + */ + content: string; + /** + * sublist items + */ + items: ListItem[]; +} /** - * @typedef {object} ListItem - * @property {string} content - list item text content - * @property {ListItem[]} items - sublist items + * Tool's configuration */ +interface NestedListConfig { + /** + * default list style: ordered or unordered + * default is unordered + */ + defaultStyle: string; +} + +/** + * Constructor Params for Nested List Tool, use to pass initial data and settings + */ +interface NestedListParams { + /** + * Preload data for the tool + */ + data: ListData; + /** + * Tool's configuration + */ + config: NestedListConfig; + /** + * Editor.js API + */ + api: API; + /** + * Is Nested List Tool read-only + */ + readOnly: boolean; +} + +/** + * CSS classes for the Nested List Tool + */ +interface NestedListCssClasses { + baseBlock: string; + wrapper: string; + wrapperOrdered: string; + wrapperUnordered: string; + item: string; + itemBody: string; + itemContent: string; + itemChildren: string; + settingsWrapper: string; + settingsButton: string; + settingsButtonActive: string; +} /** * NestedList Tool for EditorJS @@ -28,7 +100,7 @@ export default class NestedList { * * @returns {boolean} */ - static get isReadOnlySupported() { + static get isReadOnlySupported(): boolean { return true; } @@ -38,7 +110,7 @@ export default class NestedList { * @returns {boolean} * @public */ - static get enableLineBreaks() { + static get enableLineBreaks(): boolean { return true; } @@ -47,15 +119,50 @@ export default class NestedList { * icon - Tool icon's SVG * title - title to show in toolbox * - * @returns {{icon: string, title: string}} + * @returns {ToolboxConfig} */ - static get toolbox() { + static get toolbox(): ToolboxConfig { return { icon: IconListNumbered, - title: 'List', + title: "List", }; } + /** + * The Editor.js API + */ + api: API; + + /** + * Is NestedList Tool read-only + */ + readOnly: boolean; + + /** + * Tool's configuration + */ + config: NestedListConfig; + + /** + * Default list style + */ + defaultListStyle: NestedListConfig["defaultStyle"]; + + /** + * Corresponds to UiNodes type from Editor.js but with wrapper being nullable + */ + nodes: { wrapper: HTMLElement | null }; + + /** + * Tool's data + */ + data: ListData; + + /** + * Caret helper + */ + caret: Caret; + /** * Render plugin`s main Element and fill it with saved data * @@ -65,7 +172,7 @@ export default class NestedList { * @param {object} params.api - Editor.js API * @param {boolean} params.readOnly - read-only mode flag */ - constructor({ data, config, api, readOnly }) { + constructor({ data, config, api, readOnly }: NestedListParams) { /** * HTML nodes used in tool */ @@ -80,8 +187,8 @@ export default class NestedList { /** * Set the default list style from the config. */ - this.defaultListStyle = this.config.defaultStyle === 'ordered' ? 'ordered' : 'unordered'; - + this.defaultListStyle = + this.config.defaultStyle === "ordered" ? "ordered" : "unordered"; const initialData = { style: this.defaultListStyle, @@ -101,38 +208,49 @@ export default class NestedList { * @returns {Element} * @public */ - render() { - this.nodes.wrapper = this.makeListWrapper(this.data.style, [ this.CSS.baseBlock ]); + render(): Element { + this.nodes.wrapper = this.makeListWrapper(this.data.style, [ + this.CSS.baseBlock, + ]); // fill with data if (this.data.items.length) { this.appendItems(this.data.items, this.nodes.wrapper); } else { - this.appendItems([ { - content: '', - items: [], - } ], this.nodes.wrapper); + this.appendItems( + [ + { + content: "", + items: [], + }, + ], + this.nodes.wrapper + ); } if (!this.readOnly) { // detect keydown on the last item to escape List - this.nodes.wrapper.addEventListener('keydown', (event) => { - switch (event.key) { - case 'Enter': - this.enterPressed(event); - break; - case 'Backspace': - this.backspace(event); - break; - case 'Tab': - if (event.shiftKey) { - this.shiftTab(event); - } else { - this.addTab(event); - } - break; - } - }, false); + this.nodes.wrapper.addEventListener( + "keydown", + (event) => { + switch (event.key) { + case "Enter": + this.enterPressed(event); + break; + case "Backspace": + this.backspace(event); + break; + case "Tab": + if (event.shiftKey) { + this.shiftTab(event); + } else { + this.addTab(event); + } + break; + } + }, + false + ); } return this.nodes.wrapper; @@ -144,21 +262,21 @@ export default class NestedList { * @public * @returns {Array} */ - renderSettings() { + renderSettings(): TunesMenuConfig { const tunes = [ { - name: 'unordered', - label: this.api.i18n.t('Unordered'), + name: "unordered", + label: this.api.i18n.t("Unordered"), icon: IconListBulleted, }, { - name: 'ordered', - label: this.api.i18n.t('Ordered'), + name: "ordered", + label: this.api.i18n.t("Ordered"), icon: IconListNumbered, }, ]; - return tunes.map(tune => ({ + return tunes.map((tune) => ({ name: tune.name, icon: tune.icon, label: tune.label, @@ -175,9 +293,9 @@ export default class NestedList { * * @returns {PasteConfig} - paste config. */ - static get pasteConfig() { + static get pasteConfig(): PasteConfig { return { - tags: ['OL', 'UL', 'LI'], + tags: ["OL", "UL", "LI"], }; } @@ -186,7 +304,7 @@ export default class NestedList { * * @param {PasteEvent} event - event with pasted data */ - onPaste(event) { + onPaste(event: PasteEvent): void { const list = event.detail.data; this.data = this.pasteHandler(list); @@ -194,7 +312,7 @@ export default class NestedList { // render new list const oldView = this.nodes.wrapper; - if (oldView) { + if (oldView && oldView.parentNode) { oldView.parentNode.replaceChild(this.render(), oldView); } } @@ -205,30 +323,30 @@ export default class NestedList { * @param {HTMLUListElement|HTMLOListElement|HTMLLIElement} element * @returns {ListData} */ - pasteHandler(element) { + pasteHandler(element: PasteEvent["detail"]["data"]): ListData { const { tagName: tag } = element; - let style; - let tagToSearch; + let style: string = ""; + let tagToSearch: string; // set list style and tag to search. switch (tag) { - case 'OL': - style = 'ordered'; - tagToSearch = 'ol'; + case "OL": + style = "ordered"; + tagToSearch = "ol"; break; - case 'UL': - case 'LI': - style = 'unordered'; - tagToSearch = 'ul'; + case "UL": + case "LI": + style = "unordered"; + tagToSearch = "ul"; } - const data = { + const data: ListData = { style, items: [], }; // get pasted items from the html. - const getPastedItems = (parent) => { + const getPastedItems = (parent: Element): ListItem[] => { // get first level li elements. const children = Array.from(parent.querySelectorAll(`:scope > li`)); @@ -238,7 +356,7 @@ export default class NestedList { // get subitems. const subItems = subItemsWrapper ? getPastedItems(subItemsWrapper) : []; // get text content of the li element. - const content = child?.firstChild?.textContent || ''; + const content = child?.firstChild?.textContent || ""; return { content, @@ -260,7 +378,7 @@ export default class NestedList { * @param {Element} parentItem - where to append * @returns {void} */ - appendItems(items, parentItem) { + appendItems(items: ListItem[], parentItem: Element): void { items.forEach((item) => { const itemEl = this.createItem(item.content, item.items); @@ -275,12 +393,12 @@ export default class NestedList { * @param {ListItem[]} [items] - children * @returns {Element} */ - createItem(content, items = []) { - const itemWrapper = Dom.make('li', this.CSS.item); - const itemBody = Dom.make('div', this.CSS.itemBody); - const itemContent = Dom.make('div', this.CSS.itemContent, { + createItem(content: string, items: ListItem[] = []): Element { + const itemWrapper = Dom.make("li", this.CSS.item); + const itemBody = Dom.make("div", this.CSS.itemBody); + const itemContent = Dom.make("div", this.CSS.itemContent, { innerHTML: content, - contentEditable: !this.readOnly, + contentEditable: (!this.readOnly).toString(), }); itemBody.appendChild(itemContent); @@ -301,17 +419,19 @@ export default class NestedList { * * @returns {ListData} */ - save() { + save(): ListData { /** * The method for recursive collecting of the child items * * @param {Element} parent - where to find items * @returns {ListItem[]} */ - const getItems = (parent) => { - const children = Array.from(parent.querySelectorAll(`:scope > .${this.CSS.item}`)); + const getItems = (parent: Element): ListItem[] => { + const children = Array.from( + parent.querySelectorAll(`:scope > .${this.CSS.item}`) + ); - return children.map(el => { + return children.map((el) => { const subItemsWrapper = el.querySelector(`.${this.CSS.itemChildren}`); const content = this.getItemContent(el); const subItems = subItemsWrapper ? getItems(subItemsWrapper) : []; @@ -325,7 +445,7 @@ export default class NestedList { return { style: this.data.style, - items: getItems(this.nodes.wrapper), + items: this.nodes.wrapper ? getItems(this.nodes.wrapper) : [], }; } @@ -335,12 +455,18 @@ export default class NestedList { * @param {Element} parentItem - item that should contain passed sub-items * @param {ListItem[]} items - sub items to append */ - addChildrenList(parentItem, items) { + addChildrenList(parentItem: Element, items: ListItem[]): void { const itemBody = parentItem.querySelector(`.${this.CSS.itemBody}`); - const sublistWrapper = this.makeListWrapper(undefined, [ this.CSS.itemChildren ]); + const sublistWrapper = this.makeListWrapper(undefined, [ + this.CSS.itemChildren, + ]); this.appendItems(items, sublistWrapper); + if (!itemBody) { + return; + } + itemBody.appendChild(sublistWrapper); } @@ -351,32 +477,39 @@ export default class NestedList { * @param {string[]} [classes] - additional classes to append * @returns {HTMLOListElement|HTMLUListElement} */ - makeListWrapper(style = this.listStyle, classes = []) { - const tag = style === 'ordered' ? 'ol' : 'ul'; - const styleClass = style === 'ordered' ? this.CSS.wrapperOrdered : this.CSS.wrapperUnordered; + makeListWrapper( + style: string = this.listStyle, + classes: string[] = [] + ): HTMLOListElement | HTMLUListElement { + const tag = style === "ordered" ? "ol" : "ul"; + const styleClass = + style === "ordered" ? this.CSS.wrapperOrdered : this.CSS.wrapperUnordered; classes.push(styleClass); - return Dom.make(tag, [this.CSS.wrapper, ...classes]); + // since tag is either 'ol' or 'ul' we can safely cast it to HTMLOListElement | HTMLUListElement + return Dom.make(tag, [this.CSS.wrapper, ...classes]) as + | HTMLOListElement + | HTMLUListElement; } /** * Styles * - * @returns {object} - CSS classes names by keys + * @returns {NestedListCssClasses} - CSS classes names by keys * @private */ - get CSS() { + get CSS(): NestedListCssClasses { return { baseBlock: this.api.styles.block, - wrapper: 'cdx-nested-list', - wrapperOrdered: 'cdx-nested-list--ordered', - wrapperUnordered: 'cdx-nested-list--unordered', - item: 'cdx-nested-list__item', - itemBody: 'cdx-nested-list__item-body', - itemContent: 'cdx-nested-list__item-content', - itemChildren: 'cdx-nested-list__item-children', - settingsWrapper: 'cdx-nested-list__settings', + wrapper: "cdx-nested-list", + wrapperOrdered: "cdx-nested-list--ordered", + wrapperUnordered: "cdx-nested-list--unordered", + item: "cdx-nested-list__item", + itemBody: "cdx-nested-list__item-body", + itemContent: "cdx-nested-list__item-content", + itemChildren: "cdx-nested-list__item-children", + settingsWrapper: "cdx-nested-list__settings", settingsButton: this.api.styles.settingsButton, settingsButtonActive: this.api.styles.settingsButtonActive, }; @@ -387,7 +520,7 @@ export default class NestedList { * * @returns {string} */ - get listStyle() { + get listStyle(): string { return this.data.style || this.defaultListStyle; } @@ -396,13 +529,21 @@ export default class NestedList { * * @param {string} style - new style to set */ - set listStyle(style) { + set listStyle(style: string) { + if (!this.nodes) { + return; + } + if (!this.nodes.wrapper) { + return; + } /** * Get lists elements * - * @type {any[]} + * @type {Element[]} */ - const lists = Array.from(this.nodes.wrapper.querySelectorAll(`.${this.CSS.wrapper}`)); + const lists: Element[] = Array.from( + this.nodes.wrapper.querySelectorAll(`.${this.CSS.wrapper}`) + ); /** * Add main wrapper to the list @@ -412,9 +553,9 @@ export default class NestedList { /** * For each list we need to update classes */ - lists.forEach(list => { - list.classList.toggle(this.CSS.wrapperUnordered, style === 'unordered'); - list.classList.toggle(this.CSS.wrapperOrdered, style === 'ordered'); + lists.forEach((list) => { + list.classList.toggle(this.CSS.wrapperUnordered, style === "unordered"); + list.classList.toggle(this.CSS.wrapperOrdered, style === "ordered"); }); /** @@ -430,11 +571,20 @@ export default class NestedList { * * @returns {Element} */ - get currentItem() { - let currentNode = window.getSelection().anchorNode; + get currentItem(): Element | null { + const selection = window.getSelection(); + + if (!selection) { + return null; + } + let currentNode = selection.anchorNode; - if (currentNode.nodeType !== Node.ELEMENT_NODE) { - currentNode = currentNode.parentNode; + if (!currentNode) { + return null; + } + + if (!isHtmlElement(currentNode)) { + return null; } return currentNode.closest(`.${this.CSS.item}`); @@ -446,8 +596,11 @@ export default class NestedList { * @param {KeyboardEvent} event - keydown * @returns {void} */ - enterPressed(event) { + enterPressed(event: KeyboardEvent): void { const currentItem = this.currentItem; + if (!currentItem) { + return; + } /** * Prevent editor.js behaviour @@ -488,6 +641,9 @@ export default class NestedList { * And move it to the new item */ const endingFragment = Caret.extractFragmentFromCaretPositionTillTheEnd(); + if (!endingFragment) { + return; + } const endingHTML = Dom.fragmentToString(endingFragment); const itemChildren = currentItem.querySelector(`.${this.CSS.itemChildren}`); @@ -501,7 +657,9 @@ export default class NestedList { * * @type {boolean} */ - const childrenExist = itemChildren && Array.from(itemChildren.querySelectorAll(`.${this.CSS.item}`)).length > 0; + const childrenExist = + itemChildren && + Array.from(itemChildren.querySelectorAll(`.${this.CSS.item}`)).length > 0; /** * If item has children, prepend to them @@ -521,8 +679,18 @@ export default class NestedList { * * @returns {void} */ - unshiftItem() { + unshiftItem(): void { const currentItem = this.currentItem; + if (!currentItem) { + return; + } + if (!currentItem.parentNode) { + return; + } + if (!isHtmlElement(currentItem.parentNode)) { + return; + } + const parentItem = currentItem.parentNode.closest(`.${this.CSS.item}`); /** @@ -541,8 +709,14 @@ export default class NestedList { /** * If previous parent's children list is now empty, remove it. */ - const prevParentChildrenList = parentItem.querySelector(`.${this.CSS.itemChildren}`); - const isPrevParentChildrenEmpty = prevParentChildrenList.children.length === 0; + const prevParentChildrenList = parentItem.querySelector( + `.${this.CSS.itemChildren}` + ); + if (!prevParentChildrenList) { + return; + } + const isPrevParentChildrenEmpty = + prevParentChildrenList.children.length === 0; if (isPrevParentChildrenEmpty) { prevParentChildrenList.remove(); @@ -555,11 +729,14 @@ export default class NestedList { * @param {Element} item - item wrapper (
  • ) * @returns {string} */ - getItemContent(item) { + getItemContent(item: Element): string { const contentNode = item.querySelector(`.${this.CSS.itemContent}`); + if (!contentNode) { + return ""; + } if (Dom.isEmpty(contentNode)) { - return ''; + return ""; } return contentNode.innerHTML; @@ -572,8 +749,13 @@ export default class NestedList { * @param {boolean} atStart - where to set focus: at the start or at the end * @returns {void} */ - focusItem(item, atStart = true) { - const itemContent = item.querySelector(`.${this.CSS.itemContent}`); + focusItem(item: Element, atStart: boolean = true): void { + const itemContent = item.querySelector( + `.${this.CSS.itemContent}` + ); + if (!itemContent) { + return; + } Caret.focus(itemContent, atStart); } @@ -583,8 +765,8 @@ export default class NestedList { * * @returns {void} */ - getOutOfList() { - this.currentItem.remove(); + getOutOfList(): void { + this.currentItem?.remove(); this.api.blocks.insert(); this.api.caret.setToBlock(this.api.blocks.getCurrentBlockIndex()); @@ -595,7 +777,7 @@ export default class NestedList { * * @param {KeyboardEvent} event - keydown */ - backspace(event) { + backspace(event: KeyboardEvent): void { /** * Caret is not at start of the item * Then backspace button should remove letter as usual @@ -610,7 +792,16 @@ export default class NestedList { event.preventDefault(); const currentItem = this.currentItem; + if (!currentItem) { + return; + } const previousItem = currentItem.previousSibling; + if (!currentItem.parentNode) { + return; + } + if (!isHtmlElement(currentItem.parentNode)) { + return; + } const parentItem = currentItem.parentNode.closest(`.${this.CSS.item}`); /** @@ -639,6 +830,11 @@ export default class NestedList { return; } + // make sure previousItem is an HTMLElement + if (previousItem && !isHtmlElement(previousItem)) { + return; + } + /** * Prevent editor.js behaviour */ @@ -647,7 +843,7 @@ export default class NestedList { /** * Lets compute the item which will be merged with current item text */ - let targetItem; + let targetItem: Element | null; /** * If there is a previous item then we get a deepest item in its sublists @@ -655,7 +851,9 @@ export default class NestedList { * Otherwise we will use the parent item */ if (previousItem) { - const childrenOfPreviousItem = previousItem.querySelectorAll(`.${this.CSS.item}`); + const childrenOfPreviousItem = previousItem.querySelectorAll( + `.${this.CSS.item}` + ); targetItem = Array.from(childrenOfPreviousItem).pop() || previousItem; } else { @@ -666,16 +864,27 @@ export default class NestedList { * Get content from caret till the end of the block to move it to the new item */ const endingFragment = Caret.extractFragmentFromCaretPositionTillTheEnd(); + if (!endingFragment) { + return; + } const endingHTML = Dom.fragmentToString(endingFragment); /** * Get the target item content element */ - const targetItemContent = targetItem.querySelector(`.${this.CSS.itemContent}`); + if (!targetItem) { + return; + } + const targetItemContent = targetItem.querySelector( + `.${this.CSS.itemContent}` + ); /** * Set a new place for caret */ + if (!targetItemContent) { + return; + } Caret.focus(targetItemContent, false); /** @@ -686,12 +895,15 @@ export default class NestedList { /** * Update target item content by merging with current item html content */ - targetItemContent.insertAdjacentHTML('beforeend', endingHTML); + targetItemContent.insertAdjacentHTML("beforeend", endingHTML); /** * Get the sublist first-level items for current item */ - let currentItemSublistItems = currentItem.querySelectorAll(`.${this.CSS.itemChildren} > .${this.CSS.item}`); + let currentItemSublistItems: NodeListOf | Element[] = + currentItem.querySelectorAll( + `.${this.CSS.itemChildren} > .${this.CSS.item}` + ); /** * Create an array from current item sublist items @@ -702,12 +914,21 @@ export default class NestedList { * Filter items for sublist first-level * No need to move deeper items */ - currentItemSublistItems = currentItemSublistItems.filter(node => node.parentNode.closest(`.${this.CSS.item}`) === currentItem); + currentItemSublistItems = currentItemSublistItems.filter((node) => { + // make sure node.parentNode is an HTMLElement + if (!node.parentNode) { + return false; + } + if (!isHtmlElement(node.parentNode)) { + return false; + } + return node.parentNode.closest(`.${this.CSS.item}`) === currentItem; + }); /** * Reverse the array to insert items */ - currentItemSublistItems.reverse().forEach(item => { + currentItemSublistItems.reverse().forEach((item) => { /** * Check if we need to save the indent for current item children * @@ -769,7 +990,7 @@ export default class NestedList { * * @param {KeyboardEvent} event - keydown */ - addTab(event) { + addTab(event: KeyboardEvent): void { /** * Prevent editor.js behaviour */ @@ -781,7 +1002,16 @@ export default class NestedList { event.preventDefault(); const currentItem = this.currentItem; + if (!currentItem) { + return; + } const prevItem = currentItem.previousSibling; + if (!prevItem) { + return; + } + if (!isHtmlElement(prevItem)) { + return; + } const isFirstChild = !prevItem; /** @@ -791,7 +1021,9 @@ export default class NestedList { return; } - const prevItemChildrenList = prevItem.querySelector(`.${this.CSS.itemChildren}`); + const prevItemChildrenList = prevItem.querySelector( + `.${this.CSS.itemChildren}` + ); this.caret.save(); @@ -806,11 +1038,13 @@ export default class NestedList { * - Create and append children wrapper to the previous item * - Append current item to it */ - const sublistWrapper = this.makeListWrapper(undefined, [ this.CSS.itemChildren ]); + const sublistWrapper = this.makeListWrapper(undefined, [ + this.CSS.itemChildren, + ]); const prevItemBody = prevItem.querySelector(`.${this.CSS.itemBody}`); sublistWrapper.appendChild(currentItem); - prevItemBody.appendChild(sublistWrapper); + prevItemBody?.appendChild(sublistWrapper); } this.caret.restore(); @@ -822,7 +1056,7 @@ export default class NestedList { * @param {KeyboardEvent} event - keydown * @returns {void} */ - shiftTab(event) { + shiftTab(event: KeyboardEvent): void { /** * Prevent editor.js behaviour */ @@ -845,27 +1079,32 @@ export default class NestedList { * @param {ListData} data * @returns {string} */ - static joinRecursive(data) { + static joinRecursive(data: ListData | ListItem): string { return data.items .map((item) => `${item.content} ${NestedList.joinRecursive(item)}`) - .join(''); + .join(""); } /** * Convert from text to list with import and export list to text */ - static get conversionConfig() { + static get conversionConfig(): { + export: (data: ListData) => string; + import: (content: string) => ListData; + } { return { export: (data) => { return NestedList.joinRecursive(data); }, import: (content) => { return { - items: [ { - content, - items: [], - } ], - style: 'unordered', + items: [ + { + content, + items: [], + }, + ], + style: "unordered", }; }, }; diff --git a/src/types/icons.d.ts b/src/types/icons.d.ts new file mode 100644 index 00000000..5a1febb5 --- /dev/null +++ b/src/types/icons.d.ts @@ -0,0 +1,5 @@ +// temporary fix for the missing types +declare module "@codexteam/icons" { + export const IconListBulleted: string; + export const IconListNumbered: string; +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 00000000..db3eb420 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,11 @@ +/** + * Paste event for tag substitution, similar to editor.js PasteEvent but with a different data type + */ +export interface PasteEvent extends CustomEvent { + /** + * Pasted element + */ + detail: { + data: HTMLUListElement | HTMLOListElement | HTMLLIElement; + }; +} diff --git a/src/utils/caret.js b/src/utils/caret.ts similarity index 67% rename from src/utils/caret.js rename to src/utils/caret.ts index dfba7bc3..8c57f3cb 100644 --- a/src/utils/caret.js +++ b/src/utils/caret.ts @@ -1,9 +1,12 @@ -import * as dom from './dom'; +import * as dom from "./dom"; +import { isHtmlElement } from "./type-guards"; /** * Helper for working with caret */ export default class Caret { + savedFakeCaret: HTMLElement | undefined; + /** * Store internal properties */ @@ -19,12 +22,15 @@ export default class Caret { * * @returns {void} */ - save() { + save(): void { const range = Caret.range; - const cursor = dom.make('span'); + const cursor = dom.make("span"); cursor.hidden = true; + if (!range) { + return; + } range.insertNode(cursor); this.savedFakeCaret = cursor; @@ -35,12 +41,16 @@ export default class Caret { * * @returns {void} */ - restore() { + restore(): void { if (!this.savedFakeCaret) { return; } const sel = window.getSelection(); + if (!sel) { + return; + } + const range = new Range(); range.setStartAfter(this.savedFakeCaret); @@ -53,7 +63,7 @@ export default class Caret { * A little timeout uses to allow browser to set caret after element before we remove it. */ setTimeout(() => { - this.savedFakeCaret.remove(); + this.savedFakeCaret?.remove(); }, 150); } @@ -62,7 +72,7 @@ export default class Caret { * * @returns {Range|null} */ - static get range() { + static get range(): Range | null { const selection = window.getSelection(); return selection && selection.rangeCount ? selection.getRangeAt(0) : null; @@ -73,9 +83,13 @@ export default class Caret { * * @returns {DocumentFragment|void} */ - static extractFragmentFromCaretPositionTillTheEnd() { + static extractFragmentFromCaretPositionTillTheEnd(): DocumentFragment | void { const selection = window.getSelection(); + if (!selection) { + return; + } + if (!selection.rangeCount) { return; } @@ -87,10 +101,22 @@ export default class Caret { * selectRange.startContainer can point to the Text node which has no .closest() method */ if (startNode.nodeType !== Node.ELEMENT_NODE) { + if (!startNode.parentNode) { + return; + } startNode = startNode.parentNode; } - const currentBlockInput = startNode.closest('[contenteditable]'); + // if startNode is not htmlelement return + if (!isHtmlElement(startNode)) { + return; + } + + const currentBlockInput = startNode.closest("[contenteditable]"); + + if (!currentBlockInput) { + return; + } selectRange.deleteContents(); @@ -109,9 +135,12 @@ export default class Caret { * @param {boolean} atStart - where to set focus: at the start or at the end * @returns {void} */ - static focus(element, atStart = true) { + static focus(element: HTMLElement, atStart: boolean = true): void { const range = document.createRange(); const selection = window.getSelection(); + if (!selection) { + return; + } range.selectNodeContents(element); range.collapse(atStart); @@ -123,17 +152,30 @@ export default class Caret { /** * Check if the caret placed at the start of the contenteditable element * - * @returns {void} + * @returns {boolean} */ - static isAtStart() { + static isAtStart(): boolean { const selection = window.getSelection(); + if (!selection) { + return false; + } + if (selection.focusOffset > 0) { return false; } const focusNode = selection.focusNode; + if (!focusNode) { + return false; + } + + // if focusNode is not htmlelement return false + if (!isHtmlElement(focusNode)) { + return false; + } + /** * In case of *
    @@ -141,7 +183,7 @@ export default class Caret { * |adaddad <-- focus node *
    */ - const leftSiblings = Caret.getHigherLevelSiblings(focusNode, 'left'); + const leftSiblings = Caret.getHigherLevelSiblings(focusNode, "left"); const nothingAtLeft = leftSiblings.every((node) => { return dom.isEmpty(node); @@ -168,25 +210,38 @@ export default class Caret { * @param {'left' | 'right'} direction - direction of search * @returns {HTMLElement[]} */ - static getHigherLevelSiblings(from, direction = 'left') { + static getHigherLevelSiblings( + from: HTMLElement, + direction: "left" | "right" = "left" + ): HTMLElement[] { let current = from; - const siblings = []; + const siblings: HTMLElement[] = []; + + // type guard to make sure that current.parentNode is an HTMLElement + let currentParentNode: HTMLElement | null = null; + if (current.parentNode && isHtmlElement(current.parentNode)) { + // Now we are sure parentNode is an HTMLElement + currentParentNode = current.parentNode as HTMLElement; + } /** * Find passed node's firs-level parent (in example - blockquote) */ - while (current.parentNode && (current.parentNode).contentEditable !== 'true') { - current = current.parentNode; + while (currentParentNode && currentParentNode.contentEditable !== "true") { + current = currentParentNode; } - const sibling = direction === 'left' ? 'previousSibling' : 'nextSibling'; + const sibling = direction === "left" ? "previousSibling" : "nextSibling"; /** * Find all left/right siblings */ while (current[sibling]) { - current = current[sibling]; - siblings.push(current); + const siblingNode = current[sibling]; + if (siblingNode && isHtmlElement(siblingNode)) { + current = siblingNode; + siblings.push(current); + } } return siblings; diff --git a/src/utils/dom.js b/src/utils/dom.ts similarity index 63% rename from src/utils/dom.js rename to src/utils/dom.ts index c9d00887..43226e2d 100644 --- a/src/utils/dom.js +++ b/src/utils/dom.ts @@ -1,3 +1,8 @@ +/** + * HtmlElement's attribute that can be set + */ +type HtmlElementAttributes = Pick; + /** * Helper for making Elements with attributes * @@ -6,7 +11,11 @@ * @param {object} attributes - any attributes * @returns {Element} */ -export function make(tagName, classNames = null, attributes = {}) { +export function make( + tagName: string, + classNames: string[] | string | null = null, + attributes?: HtmlElementAttributes +): HTMLElement { const el = document.createElement(tagName); if (Array.isArray(classNames)) { @@ -16,7 +25,8 @@ export function make(tagName, classNames = null, attributes = {}) { } for (const attrName in attributes) { - el[attrName] = attributes[attrName]; + el[attrName as keyof HtmlElementAttributes] = + attributes[attrName as keyof HtmlElementAttributes]; } return el; @@ -28,8 +38,8 @@ export function make(tagName, classNames = null, attributes = {}) { * @param {DocumentFragment} fragment - document fragment to process * @returns {string} */ -export function fragmentToString(fragment) { - const div = make('div'); +export function fragmentToString(fragment: DocumentFragment): string { + const div = make("div"); div.appendChild(fragment); @@ -44,8 +54,8 @@ export function fragmentToString(fragment) { * @param {Node} node - node to check * @returns {boolean} */ -export function isEmpty(node) { - let content; +export function isEmpty(node: Element): boolean { + let content: string | null; if (node.nodeType !== Node.ELEMENT_NODE) { content = node.textContent; @@ -55,8 +65,8 @@ export function isEmpty(node) { /** * Don't count
    s as content */ - content = content.replaceAll('
    ', ''); + content = content.replaceAll("
    ", ""); } - return content.trim().length === 0; + return content?.trim().length === 0; } diff --git a/src/utils/type-guards.ts b/src/utils/type-guards.ts new file mode 100644 index 00000000..a7968fa9 --- /dev/null +++ b/src/utils/type-guards.ts @@ -0,0 +1,9 @@ +/** + * Type guard to check if a node is an HTMLElement, then we can safely use it as an HTMLElement + * @param node + * @returns {node is HTMLElement} + */ +export function isHtmlElement(node: Node): node is HTMLElement { + // node is an HTMLElement if it is an element node + return node.nodeType === Node.ELEMENT_NODE; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..3075aac1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "es2016", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["dom", "ESNext"], + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "outDir": "./dist", + "declaration": true, + "declarationDir": "./dist/types", + "isolatedModules": true + }, + "include": ["src"], + "exclude": ["node_modules", "**/*.spec.ts"] +} diff --git a/vite.config.js b/vite.config.js index 87dbad30..64b79224 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,7 @@ import path from "path"; import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js"; import * as pkg from "./package.json"; +import dts from "vite-plugin-dts"; const NODE_ENV = process.argv.mode || "development"; const VERSION = pkg.version; @@ -9,7 +10,7 @@ export default { build: { copyPublicDir: false, lib: { - entry: path.resolve(__dirname, "src", "index.js"), + entry: path.resolve(__dirname, "src", "index.ts"), name: "NestedList", fileName: "nested-list", }, @@ -19,5 +20,5 @@ export default { VERSION: JSON.stringify(VERSION), }, - plugins: [cssInjectedByJsPlugin()], + plugins: [cssInjectedByJsPlugin(), dts()], }; diff --git a/yarn.lock b/yarn.lock index fb85d735..2137222e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,11 +20,21 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.24.4": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" + integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== + "@codexteam/icons@^0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@codexteam/icons/-/icons-0.0.2.tgz#9183996a38b75a93506890373a015e3a2a369264" integrity sha512-KdeKj3TwaTHqM3IXd5YjeJP39PBUZTb+dtHjGlf5+b0VgsxYD4qzsZkb11lzopZbAuDsHaZJmAYQ8LFligIT6Q== +"@editorjs/editorjs@^2.29.1": + version "2.29.1" + resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.29.1.tgz#d7c644c5c3fc1ea1022373cbf2c8c3ac9f990b7b" + integrity sha512-WRT2pCfikMsvySQJqpCU21LfTZaPuxUWsDO8aFGrPx4MKzOR9D+Ur4mNb3jq0FXx2EMqvIWfTyFixJxtjGHTyQ== + "@esbuild/android-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" @@ -161,10 +171,176 @@ version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" +"@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@microsoft/api-extractor-model@7.28.13": + version "7.28.13" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz#96fbc52155e0d07e0eabbd9699065b77702fe33a" + integrity sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "4.0.2" + +"@microsoft/api-extractor@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz#41c42677bc71cd8e0f23c63c56802d85044e65cd" + integrity sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w== + dependencies: + "@microsoft/api-extractor-model" "7.28.13" + "@microsoft/tsdoc" "0.14.2" + "@microsoft/tsdoc-config" "~0.16.1" + "@rushstack/node-core-library" "4.0.2" + "@rushstack/rig-package" "0.5.2" + "@rushstack/terminal" "0.10.0" + "@rushstack/ts-command-line" "4.19.1" + lodash "~4.17.15" + minimatch "~3.0.3" + resolve "~1.22.1" + semver "~7.5.4" + source-map "~0.6.1" + typescript "5.4.2" + +"@microsoft/tsdoc-config@~0.16.1": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz#b786bb4ead00d54f53839a458ce626c8548d3adf" + integrity sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw== + dependencies: + "@microsoft/tsdoc" "0.14.2" + ajv "~6.12.6" + jju "~1.4.0" + resolve "~1.19.0" + +"@microsoft/tsdoc@0.14.2": + version "0.14.2" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" + integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== + +"@rollup/pluginutils@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rushstack/node-core-library@4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz#e26854a3314b279d57e8abdb4acce7797d02f554" + integrity sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg== + dependencies: + fs-extra "~7.0.1" + import-lazy "~4.0.0" + jju "~1.4.0" + resolve "~1.22.1" + semver "~7.5.4" + z-schema "~5.0.2" + +"@rushstack/rig-package@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@rushstack/rig-package/-/rig-package-0.5.2.tgz#0e23a115904678717a74049661931c0b37dd5495" + integrity sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA== + dependencies: + resolve "~1.22.1" + strip-json-comments "~3.1.1" + +"@rushstack/terminal@0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@rushstack/terminal/-/terminal-0.10.0.tgz#e81909fa0e5c8016b6df4739f0f381f44358269f" + integrity sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw== + dependencies: + "@rushstack/node-core-library" "4.0.2" + supports-color "~8.1.1" + +"@rushstack/ts-command-line@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz#288ee54dd607e558a8be07705869c16c31b5c3ef" + integrity sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg== + dependencies: + "@rushstack/terminal" "0.10.0" + "@types/argparse" "1.0.38" + argparse "~1.0.9" + string-argv "~0.3.1" + +"@types/argparse@1.0.38": + version "1.0.38" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" + integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== + +"@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + "@types/json-schema@^7.0.5": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" +"@volar/language-core@1.11.1", "@volar/language-core@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-1.11.1.tgz#ecdf12ea8dc35fb8549e517991abcbf449a5ad4f" + integrity sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw== + dependencies: + "@volar/source-map" "1.11.1" + +"@volar/source-map@1.11.1", "@volar/source-map@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-1.11.1.tgz#535b0328d9e2b7a91dff846cab4058e191f4452f" + integrity sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg== + dependencies: + muggle-string "^0.3.1" + +"@volar/typescript@~1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-1.11.1.tgz#ba86c6f326d88e249c7f5cfe4b765be3946fd627" + integrity sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ== + dependencies: + "@volar/language-core" "1.11.1" + path-browserify "^1.0.1" + +"@vue/compiler-core@3.4.27": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.27.tgz#e69060f4b61429fe57976aa5872cfa21389e4d91" + integrity sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg== + dependencies: + "@babel/parser" "^7.24.4" + "@vue/shared" "3.4.27" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" + +"@vue/compiler-dom@^3.3.0": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz#d51d35f40d00ce235d7afc6ad8b09dfd92b1cc1c" + integrity sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw== + dependencies: + "@vue/compiler-core" "3.4.27" + "@vue/shared" "3.4.27" + +"@vue/language-core@1.8.27", "@vue/language-core@^1.8.27": + version "1.8.27" + resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.27.tgz#2ca6892cb524e024a44e554e4c55d7a23e72263f" + integrity sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA== + dependencies: + "@volar/language-core" "~1.11.1" + "@volar/source-map" "~1.11.1" + "@vue/compiler-dom" "^3.3.0" + "@vue/shared" "^3.3.0" + computeds "^0.0.1" + minimatch "^9.0.3" + muggle-string "^0.3.1" + path-browserify "^1.0.1" + vue-template-compiler "^2.7.14" + +"@vue/shared@3.4.27", "@vue/shared@^3.3.0": + version "3.4.27" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.27.tgz#f05e3cd107d157354bb4ae7a7b5fc9cf73c63b50" + integrity sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA== + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -177,7 +353,7 @@ ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" -ajv@^6.10.0, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.4, ajv@~6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" dependencies: @@ -215,7 +391,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -argparse@^1.0.7: +argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" dependencies: @@ -240,6 +416,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -279,10 +462,20 @@ color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +computeds@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/computeds/-/computeds-0.0.1.tgz#215b08a4ba3e08a11ff6eee5d6d8d7166a97ce2e" + integrity sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -299,12 +492,24 @@ cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== + debug@^4.0.1, debug@^4.1.1: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" dependencies: ms "2.1.2" +debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -329,6 +534,11 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + esbuild@^0.18.10: version "0.18.20" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" @@ -473,6 +683,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -529,6 +744,15 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@~7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -538,6 +762,11 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -565,6 +794,11 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" +graceful-fs@^4.1.2: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -577,6 +811,18 @@ has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -588,6 +834,11 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-lazy@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" + integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -603,6 +854,13 @@ inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" +is-core-module@^2.1.0, is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -621,6 +879,11 @@ isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -656,6 +919,11 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -677,6 +945,16 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -685,12 +963,24 @@ lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" +lodash@~4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" dependencies: yallist "^4.0.0" +magic-string@^0.30.8: + version "0.30.10" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" + integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -703,6 +993,20 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimatch@~3.0.3: + version "3.0.8" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -711,6 +1015,11 @@ ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" +muggle-string@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.3.1.tgz#e524312eb1728c63dd0b2ac49e3282e6ed85963a" + integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg== + nanoid@^3.3.6: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -763,6 +1072,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -775,11 +1089,21 @@ path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" +path-parse@^1.0.6, path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pkg-dir@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -852,6 +1176,23 @@ resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" +resolve@~1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +resolve@~1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -883,6 +1224,18 @@ semver@^7.2.1: dependencies: lru-cache "^6.0.0" +semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +semver@~7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -906,7 +1259,12 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map@^0.6.1: +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -914,6 +1272,11 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +string-argv@~0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -928,7 +1291,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -944,6 +1307,18 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@~8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + table@^6.0.9: version "6.7.3" resolved "https://registry.yarnpkg.com/table/-/table-6.7.3.tgz#255388439715a738391bd2ee4cbca89a4d05a9b7" @@ -968,6 +1343,16 @@ type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" +typescript@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.2.tgz#0ae9cebcfae970718474fe0da2c090cad6577372" + integrity sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ== + +typescript@^5.4.5: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -986,11 +1371,29 @@ v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" +validator@^13.7.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + vite-plugin-css-injected-by-js@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.0.tgz#c19480a9e42a95c5bced976a9dde1446f9bd91ff" integrity sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew== +vite-plugin-dts@^3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-3.9.1.tgz#625ad388ec3956708ccec7960550a7b0a8e8909e" + integrity sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg== + dependencies: + "@microsoft/api-extractor" "7.43.0" + "@rollup/pluginutils" "^5.1.0" + "@vue/language-core" "^1.8.27" + debug "^4.3.4" + kolorist "^1.8.0" + magic-string "^0.30.8" + vue-tsc "^1.8.27" + vite@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26" @@ -1002,6 +1405,23 @@ vite@^4.5.0: optionalDependencies: fsevents "~2.3.2" +vue-template-compiler@^2.7.14: + version "2.7.16" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz#c81b2d47753264c77ac03b9966a46637482bb03b" + integrity sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ== + dependencies: + de-indent "^1.0.2" + he "^1.2.0" + +vue-tsc@^1.8.27: + version "1.8.27" + resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-1.8.27.tgz#feb2bb1eef9be28017bb9e95e2bbd1ebdd48481c" + integrity sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg== + dependencies: + "@volar/typescript" "~1.11.1" + "@vue/language-core" "1.8.27" + semver "^7.5.4" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -1019,3 +1439,14 @@ wrappy@1: yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + +z-schema@~5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0" From 54fe652bb484098a43a571a8bea276693f35b0f6 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Wed, 19 Jun 2024 20:58:19 +0800 Subject: [PATCH 02/18] style: add editorconfig from @editorjs/paragraph --- .editorconfig | 10 +++++ src/index.ts | 102 +++++++++++++++++++++---------------------- src/types/icons.d.ts | 2 +- src/utils/caret.ts | 16 +++---- src/utils/dom.ts | 6 +-- 5 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..aaeb769a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = false + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +quote_type = single diff --git a/src/index.ts b/src/index.ts index ca9a11a6..8775d970 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,17 @@ -import type { API, PasteConfig, ToolboxConfig } from "@editorjs/editorjs"; -import type { PasteEvent } from "./types"; -import type { TunesMenuConfig } from "@editorjs/editorjs/types/tools"; +import type { API, PasteConfig, ToolboxConfig } from '@editorjs/editorjs'; +import type { PasteEvent } from './types'; +import type { TunesMenuConfig } from '@editorjs/editorjs/types/tools'; -import { isHtmlElement } from "./utils/type-guards"; +import { isHtmlElement } from './utils/type-guards'; -import * as Dom from "./utils/dom"; -import Caret from "./utils/caret"; -import { IconListBulleted, IconListNumbered } from "@codexteam/icons"; +import * as Dom from './utils/dom'; +import Caret from './utils/caret'; +import { IconListBulleted, IconListNumbered } from '@codexteam/icons'; /** * Build styles */ -import "./../styles/index.pcss"; +import './../styles/index.pcss'; /** * Output data @@ -124,7 +124,7 @@ export default class NestedList { static get toolbox(): ToolboxConfig { return { icon: IconListNumbered, - title: "List", + title: 'List', }; } @@ -146,7 +146,7 @@ export default class NestedList { /** * Default list style */ - defaultListStyle: NestedListConfig["defaultStyle"]; + defaultListStyle: NestedListConfig['defaultStyle']; /** * Corresponds to UiNodes type from Editor.js but with wrapper being nullable @@ -188,7 +188,7 @@ export default class NestedList { * Set the default list style from the config. */ this.defaultListStyle = - this.config.defaultStyle === "ordered" ? "ordered" : "unordered"; + this.config.defaultStyle === 'ordered' ? 'ordered' : 'unordered'; const initialData = { style: this.defaultListStyle, @@ -220,7 +220,7 @@ export default class NestedList { this.appendItems( [ { - content: "", + content: '', items: [], }, ], @@ -231,16 +231,16 @@ export default class NestedList { if (!this.readOnly) { // detect keydown on the last item to escape List this.nodes.wrapper.addEventListener( - "keydown", + 'keydown', (event) => { switch (event.key) { - case "Enter": + case 'Enter': this.enterPressed(event); break; - case "Backspace": + case 'Backspace': this.backspace(event); break; - case "Tab": + case 'Tab': if (event.shiftKey) { this.shiftTab(event); } else { @@ -265,13 +265,13 @@ export default class NestedList { renderSettings(): TunesMenuConfig { const tunes = [ { - name: "unordered", - label: this.api.i18n.t("Unordered"), + name: 'unordered', + label: this.api.i18n.t('Unordered'), icon: IconListBulleted, }, { - name: "ordered", - label: this.api.i18n.t("Ordered"), + name: 'ordered', + label: this.api.i18n.t('Ordered'), icon: IconListNumbered, }, ]; @@ -295,7 +295,7 @@ export default class NestedList { */ static get pasteConfig(): PasteConfig { return { - tags: ["OL", "UL", "LI"], + tags: ['OL', 'UL', 'LI'], }; } @@ -323,21 +323,21 @@ export default class NestedList { * @param {HTMLUListElement|HTMLOListElement|HTMLLIElement} element * @returns {ListData} */ - pasteHandler(element: PasteEvent["detail"]["data"]): ListData { + pasteHandler(element: PasteEvent['detail']['data']): ListData { const { tagName: tag } = element; - let style: string = ""; + let style: string = ''; let tagToSearch: string; // set list style and tag to search. switch (tag) { - case "OL": - style = "ordered"; - tagToSearch = "ol"; + case 'OL': + style = 'ordered'; + tagToSearch = 'ol'; break; - case "UL": - case "LI": - style = "unordered"; - tagToSearch = "ul"; + case 'UL': + case 'LI': + style = 'unordered'; + tagToSearch = 'ul'; } const data: ListData = { @@ -356,7 +356,7 @@ export default class NestedList { // get subitems. const subItems = subItemsWrapper ? getPastedItems(subItemsWrapper) : []; // get text content of the li element. - const content = child?.firstChild?.textContent || ""; + const content = child?.firstChild?.textContent || ''; return { content, @@ -394,9 +394,9 @@ export default class NestedList { * @returns {Element} */ createItem(content: string, items: ListItem[] = []): Element { - const itemWrapper = Dom.make("li", this.CSS.item); - const itemBody = Dom.make("div", this.CSS.itemBody); - const itemContent = Dom.make("div", this.CSS.itemContent, { + const itemWrapper = Dom.make('li', this.CSS.item); + const itemBody = Dom.make('div', this.CSS.itemBody); + const itemContent = Dom.make('div', this.CSS.itemContent, { innerHTML: content, contentEditable: (!this.readOnly).toString(), }); @@ -481,9 +481,9 @@ export default class NestedList { style: string = this.listStyle, classes: string[] = [] ): HTMLOListElement | HTMLUListElement { - const tag = style === "ordered" ? "ol" : "ul"; + const tag = style === 'ordered' ? 'ol' : 'ul'; const styleClass = - style === "ordered" ? this.CSS.wrapperOrdered : this.CSS.wrapperUnordered; + style === 'ordered' ? this.CSS.wrapperOrdered : this.CSS.wrapperUnordered; classes.push(styleClass); @@ -502,14 +502,14 @@ export default class NestedList { get CSS(): NestedListCssClasses { return { baseBlock: this.api.styles.block, - wrapper: "cdx-nested-list", - wrapperOrdered: "cdx-nested-list--ordered", - wrapperUnordered: "cdx-nested-list--unordered", - item: "cdx-nested-list__item", - itemBody: "cdx-nested-list__item-body", - itemContent: "cdx-nested-list__item-content", - itemChildren: "cdx-nested-list__item-children", - settingsWrapper: "cdx-nested-list__settings", + wrapper: 'cdx-nested-list', + wrapperOrdered: 'cdx-nested-list--ordered', + wrapperUnordered: 'cdx-nested-list--unordered', + item: 'cdx-nested-list__item', + itemBody: 'cdx-nested-list__item-body', + itemContent: 'cdx-nested-list__item-content', + itemChildren: 'cdx-nested-list__item-children', + settingsWrapper: 'cdx-nested-list__settings', settingsButton: this.api.styles.settingsButton, settingsButtonActive: this.api.styles.settingsButtonActive, }; @@ -554,8 +554,8 @@ export default class NestedList { * For each list we need to update classes */ lists.forEach((list) => { - list.classList.toggle(this.CSS.wrapperUnordered, style === "unordered"); - list.classList.toggle(this.CSS.wrapperOrdered, style === "ordered"); + list.classList.toggle(this.CSS.wrapperUnordered, style === 'unordered'); + list.classList.toggle(this.CSS.wrapperOrdered, style === 'ordered'); }); /** @@ -732,11 +732,11 @@ export default class NestedList { getItemContent(item: Element): string { const contentNode = item.querySelector(`.${this.CSS.itemContent}`); if (!contentNode) { - return ""; + return ''; } if (Dom.isEmpty(contentNode)) { - return ""; + return ''; } return contentNode.innerHTML; @@ -895,7 +895,7 @@ export default class NestedList { /** * Update target item content by merging with current item html content */ - targetItemContent.insertAdjacentHTML("beforeend", endingHTML); + targetItemContent.insertAdjacentHTML('beforeend', endingHTML); /** * Get the sublist first-level items for current item @@ -1082,7 +1082,7 @@ export default class NestedList { static joinRecursive(data: ListData | ListItem): string { return data.items .map((item) => `${item.content} ${NestedList.joinRecursive(item)}`) - .join(""); + .join(''); } /** @@ -1104,7 +1104,7 @@ export default class NestedList { items: [], }, ], - style: "unordered", + style: 'unordered', }; }, }; diff --git a/src/types/icons.d.ts b/src/types/icons.d.ts index 5a1febb5..79bafd56 100644 --- a/src/types/icons.d.ts +++ b/src/types/icons.d.ts @@ -1,5 +1,5 @@ // temporary fix for the missing types -declare module "@codexteam/icons" { +declare module '@codexteam/icons' { export const IconListBulleted: string; export const IconListNumbered: string; } diff --git a/src/utils/caret.ts b/src/utils/caret.ts index 8c57f3cb..469c575f 100644 --- a/src/utils/caret.ts +++ b/src/utils/caret.ts @@ -1,5 +1,5 @@ -import * as dom from "./dom"; -import { isHtmlElement } from "./type-guards"; +import * as dom from './dom'; +import { isHtmlElement } from './type-guards'; /** * Helper for working with caret @@ -24,7 +24,7 @@ export default class Caret { */ save(): void { const range = Caret.range; - const cursor = dom.make("span"); + const cursor = dom.make('span'); cursor.hidden = true; @@ -112,7 +112,7 @@ export default class Caret { return; } - const currentBlockInput = startNode.closest("[contenteditable]"); + const currentBlockInput = startNode.closest('[contenteditable]'); if (!currentBlockInput) { return; @@ -183,7 +183,7 @@ export default class Caret { * |adaddad <-- focus node * */ - const leftSiblings = Caret.getHigherLevelSiblings(focusNode, "left"); + const leftSiblings = Caret.getHigherLevelSiblings(focusNode, 'left'); const nothingAtLeft = leftSiblings.every((node) => { return dom.isEmpty(node); @@ -212,7 +212,7 @@ export default class Caret { */ static getHigherLevelSiblings( from: HTMLElement, - direction: "left" | "right" = "left" + direction: 'left' | 'right' = 'left' ): HTMLElement[] { let current = from; const siblings: HTMLElement[] = []; @@ -227,11 +227,11 @@ export default class Caret { /** * Find passed node's firs-level parent (in example - blockquote) */ - while (currentParentNode && currentParentNode.contentEditable !== "true") { + while (currentParentNode && currentParentNode.contentEditable !== 'true') { current = currentParentNode; } - const sibling = direction === "left" ? "previousSibling" : "nextSibling"; + const sibling = direction === 'left' ? 'previousSibling' : 'nextSibling'; /** * Find all left/right siblings diff --git a/src/utils/dom.ts b/src/utils/dom.ts index 43226e2d..c23e689f 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -1,7 +1,7 @@ /** * HtmlElement's attribute that can be set */ -type HtmlElementAttributes = Pick; +type HtmlElementAttributes = Pick; /** * Helper for making Elements with attributes @@ -39,7 +39,7 @@ export function make( * @returns {string} */ export function fragmentToString(fragment: DocumentFragment): string { - const div = make("div"); + const div = make('div'); div.appendChild(fragment); @@ -65,7 +65,7 @@ export function isEmpty(node: Element): boolean { /** * Don't count
    s as content */ - content = content.replaceAll("
    ", ""); + content = content.replaceAll('
    ', ''); } return content?.trim().length === 0; From 1d8796b297f4ef169b86b813800749ba5db87881 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Wed, 19 Jun 2024 22:18:14 +0800 Subject: [PATCH 03/18] chore: Update tsconfig.json module and moduleResolution options --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 3075aac1..ad953fbb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "es2016", - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "ESNext", + "moduleResolution": "Node", "lib": ["dom", "ESNext"], "esModuleInterop": true, "forceConsistentCasingInFileNames": true, From dc30fa0a38e5abcce1867c7d5edc045a49fe08ab Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Wed, 19 Jun 2024 22:34:51 +0800 Subject: [PATCH 04/18] chore: move types to exports in package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e8b1035d..b6125839 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,10 @@ "exports": { ".": { "import": "./dist/nested-list.mjs", - "require": "./dist/nested-list.umd.js" + "require": "./dist/nested-list.umd.js", + "types": "./dist/index.d.ts" } }, - "types": "./dist/index.d.ts", "scripts": { "dev": "vite", "build": "vite build", From 272130c68089ae778ae64a1d962fa205c8a22bb8 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Wed, 19 Jun 2024 22:50:07 +0800 Subject: [PATCH 05/18] chore: Update tsconfig.json module to CommonJS --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index ad953fbb..d378e671 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2016", - "module": "ESNext", + "module": "CommonJS", "moduleResolution": "Node", "lib": ["dom", "ESNext"], "esModuleInterop": true, From 545e14f2ad622442265a2900d9ae1adeb6659fa8 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 12:49:40 +0800 Subject: [PATCH 06/18] fix: enter press not return --- src/index.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8775d970..0d153217 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,7 +124,7 @@ export default class NestedList { static get toolbox(): ToolboxConfig { return { icon: IconListNumbered, - title: 'List', + title: 'test-List', }; } @@ -583,6 +583,12 @@ export default class NestedList { return null; } + if (!isHtmlElement(currentNode)) { + currentNode = currentNode.parentNode; + } + if (!currentNode) { + return null; + } if (!isHtmlElement(currentNode)) { return null; } @@ -598,9 +604,6 @@ export default class NestedList { */ enterPressed(event: KeyboardEvent): void { const currentItem = this.currentItem; - if (!currentItem) { - return; - } /** * Prevent editor.js behaviour @@ -622,9 +625,11 @@ export default class NestedList { /** * On Enter in the last empty item, get out of list */ - const isEmpty = this.getItemContent(currentItem).trim().length === 0; - const isFirstLevelItem = currentItem.parentNode === this.nodes.wrapper; - const isLastItem = currentItem.nextElementSibling === null; + const isEmpty = currentItem + ? this.getItemContent(currentItem).trim().length === 0 + : true; + const isFirstLevelItem = currentItem?.parentNode === this.nodes.wrapper; + const isLastItem = currentItem?.nextElementSibling === null; if (isFirstLevelItem && isLastItem && isEmpty) { this.getOutOfList(); @@ -645,7 +650,9 @@ export default class NestedList { return; } const endingHTML = Dom.fragmentToString(endingFragment); - const itemChildren = currentItem.querySelector(`.${this.CSS.itemChildren}`); + const itemChildren = currentItem?.querySelector( + `.${this.CSS.itemChildren}` + ); /** * Create the new list item @@ -668,7 +675,7 @@ export default class NestedList { if (childrenExist) { itemChildren.prepend(itemEl); } else { - currentItem.after(itemEl); + currentItem?.after(itemEl); } this.focusItem(itemEl); From 096506effef5371e10cf3f703850c7593bab42e8 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 13:27:25 +0800 Subject: [PATCH 07/18] fix: backspace not working --- src/utils/caret.ts | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/utils/caret.ts b/src/utils/caret.ts index 469c575f..feded369 100644 --- a/src/utils/caret.ts +++ b/src/utils/caret.ts @@ -217,18 +217,14 @@ export default class Caret { let current = from; const siblings: HTMLElement[] = []; - // type guard to make sure that current.parentNode is an HTMLElement - let currentParentNode: HTMLElement | null = null; - if (current.parentNode && isHtmlElement(current.parentNode)) { - // Now we are sure parentNode is an HTMLElement - currentParentNode = current.parentNode as HTMLElement; - } - /** * Find passed node's firs-level parent (in example - blockquote) */ - while (currentParentNode && currentParentNode.contentEditable !== 'true') { - current = currentParentNode; + while ( + current.parentNode && + (current.parentNode as HTMLElement).contentEditable !== 'true' + ) { + current = current.parentNode as HTMLElement; } const sibling = direction === 'left' ? 'previousSibling' : 'nextSibling'; @@ -237,11 +233,8 @@ export default class Caret { * Find all left/right siblings */ while (current[sibling]) { - const siblingNode = current[sibling]; - if (siblingNode && isHtmlElement(siblingNode)) { - current = siblingNode; - siblings.push(current); - } + current = current[sibling] as HTMLElement; + siblings.push(current); } return siblings; From b782dabf470d708a2f07a3e3d026cfcb2a20a153 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 13:29:55 +0800 Subject: [PATCH 08/18] chore: rename tool name back to List --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 0d153217..b22994ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -124,7 +124,7 @@ export default class NestedList { static get toolbox(): ToolboxConfig { return { icon: IconListNumbered, - title: 'test-List', + title: 'List', }; } From 89db87ee4ae61822782724209baf05cf82cbebab Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 13:37:45 +0800 Subject: [PATCH 09/18] chore: add types path to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index b6125839..75977a13 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "types": "./dist/index.d.ts" } }, + "types": "./dist/index.d.ts", "scripts": { "dev": "vite", "build": "vite build", From 0587f696a9140377cf897535f8c229811573c2a1 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 13:37:56 +0800 Subject: [PATCH 10/18] chore: Update tsconfig.json module to ESNext --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index d378e671..ad953fbb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2016", - "module": "CommonJS", + "module": "ESNext", "moduleResolution": "Node", "lib": ["dom", "ESNext"], "esModuleInterop": true, From e579d630efc3eed3a0aed49b9c5670c8e6336efc Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 15:44:14 +0800 Subject: [PATCH 11/18] chore: Update eslint configuration to use @typescript-eslint/parser --- .eslintrc | 3 +- package.json | 4 +- yarn.lock | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 250 insertions(+), 5 deletions(-) diff --git a/.eslintrc b/.eslintrc index fb3aef4b..137de7ae 100644 --- a/.eslintrc +++ b/.eslintrc @@ -6,5 +6,6 @@ "parserOptions": { "sourceType": "module", "ecmaVersion": 2020 - } + }, + "parser": "@typescript-eslint/parser" } diff --git a/package.json b/package.json index 75977a13..7e10769f 100644 --- a/package.json +++ b/package.json @@ -27,12 +27,14 @@ "scripts": { "dev": "vite", "build": "vite build", - "lint": "eslint src/ --quiet", + "lint": "eslint src/ --quiet --ext .ts,.tsx", "lint:errors": "eslint src/ --quiet", "lint:fix": "eslint src/ --fix" }, "devDependencies": { "@editorjs/editorjs": "^2.29.1", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", "eslint": "^7.22.0", "eslint-loader": "^4.0.2", "postcss-nested": "^5.0.3", diff --git a/yarn.lock b/yarn.lock index 2137222e..7d6a7796 100644 --- a/yarn.lock +++ b/yarn.lock @@ -145,6 +145,18 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" + integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== + "@eslint/eslintrc@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" @@ -219,6 +231,27 @@ resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz#c3ec604a0b54b9a9b87e9735dfc59e1a5da6a5fb" integrity sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@rollup/pluginutils@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" @@ -280,6 +313,87 @@ version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" +"@typescript-eslint/eslint-plugin@^7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz#cdc521c8bca38b55585cf30db787fb2abad3f9fd" + integrity sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/type-utils" "7.13.1" + "@typescript-eslint/utils" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@^7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.13.1.tgz#fac57811b3e519185f7259bac312291f7b9c4e72" + integrity sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A== + dependencies: + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/typescript-estree" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz#c08041206904bf36f0e6997efdb0ca775e0c452e" + integrity sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg== + dependencies: + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + +"@typescript-eslint/type-utils@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz#63bec3f1fb43cf0bc409cbdb88ef96d118ca8632" + integrity sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg== + dependencies: + "@typescript-eslint/typescript-estree" "7.13.1" + "@typescript-eslint/utils" "7.13.1" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.13.1.tgz#787db283bd0b58751094c90d5b58bbf5e9fc9bd8" + integrity sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw== + +"@typescript-eslint/typescript-estree@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz#3412841b130e070db2f675e3d9b8cb1ae49e1c3f" + integrity sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw== + dependencies: + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/visitor-keys" "7.13.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.13.1.tgz#611083379caa0d3a2c09d126c65065a3e4337ba2" + integrity sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.13.1" + "@typescript-eslint/types" "7.13.1" + "@typescript-eslint/typescript-estree" "7.13.1" + +"@typescript-eslint/visitor-keys@7.13.1": + version "7.13.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz#9c229a795a919db61f2d7f2337ef584ac05fbe96" + integrity sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA== + dependencies: + "@typescript-eslint/types" "7.13.1" + eslint-visitor-keys "^3.4.3" + "@volar/language-core@1.11.1", "@volar/language-core@~1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-1.11.1.tgz#ecdf12ea8dc35fb8549e517991abcbf449a5ad4f" @@ -397,6 +511,11 @@ argparse@^1.0.7, argparse@~1.0.9: dependencies: sprintf-js "~1.0.2" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -423,6 +542,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -514,6 +640,13 @@ deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -606,6 +739,11 @@ eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + eslint@^7.22.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -696,6 +834,17 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -704,12 +853,26 @@ fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" dependencies: flat-cache "^3.0.4" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" @@ -794,6 +957,18 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + graceful-fs@^4.1.2: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -803,6 +978,11 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -827,6 +1007,11 @@ ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -869,12 +1054,17 @@ is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" dependencies: is-extglob "^2.1.1" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -987,13 +1177,26 @@ make-dir@^3.0.2: dependencies: semver "^6.0.0" +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.3: +minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== @@ -1094,6 +1297,11 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -1164,6 +1372,11 @@ punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + regexpp@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -1193,6 +1406,11 @@ resolve@~1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -1206,6 +1424,13 @@ rollup@^3.27.1: optionalDependencies: fsevents "~2.3.2" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" @@ -1224,7 +1449,7 @@ semver@^7.2.1: dependencies: lru-cache "^6.0.0" -semver@^7.5.4: +semver@^7.5.4, semver@^7.6.0: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -1246,6 +1471,11 @@ shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" @@ -1333,6 +1563,18 @@ text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" From e6dc1d50781e8bf017dfd1591f058c954e8cb6b3 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 16:08:47 +0800 Subject: [PATCH 12/18] chore: Add build step to GitHub Actions workflow --- .github/workflows/eslint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index e6d21205..294f067d 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -10,3 +10,4 @@ jobs: - uses: actions/checkout@v2 - run: yarn install - run: yarn lint + - run: yarn build From 67094620d1b1832f1eb38348421d6486ee8144e8 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 16:09:55 +0800 Subject: [PATCH 13/18] chore: Bump @editorjs/nested-list version to 1.4.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e10769f..8a4d26e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/nested-list", - "version": "1.4.2", + "version": "1.4.3", "keywords": [ "codex editor", "nested-list", From 825413c22cad33b005771c60caa6ea4ffb0c9723 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Thu, 20 Jun 2024 16:25:29 +0800 Subject: [PATCH 14/18] chore: Update lint to target .ts files only --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a4d26e0..cb9559b7 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "scripts": { "dev": "vite", "build": "vite build", - "lint": "eslint src/ --quiet --ext .ts,.tsx", + "lint": "eslint src/ --quiet --ext .ts", "lint:errors": "eslint src/ --quiet", "lint:fix": "eslint src/ --fix" }, From 96a547605c62c1670daee6396e649c6c2740f4a1 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Sun, 7 Jul 2024 09:39:44 +0800 Subject: [PATCH 15/18] refactor: update types and doc --- src/index.ts | 54 +++++++++++++++++++++------------------------- src/utils/caret.ts | 5 ++++- src/utils/dom.ts | 10 +++++---- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/index.ts b/src/index.ts index b22994ef..ebef23ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,9 @@ import type { API, PasteConfig, ToolboxConfig } from '@editorjs/editorjs'; import type { PasteEvent } from './types'; -import type { TunesMenuConfig } from '@editorjs/editorjs/types/tools'; +import type { + BlockToolConstructorOptions, + TunesMenuConfig, +} from '@editorjs/editorjs/types/tools'; import { isHtmlElement } from './utils/type-guards'; @@ -13,6 +16,11 @@ import { IconListBulleted, IconListNumbered } from '@codexteam/icons'; */ import './../styles/index.pcss'; +/** + * list style to make list as ordered or unordered + */ +type ListDataStyle = 'ordered' | 'unordered'; + /** * Output data */ @@ -20,7 +28,7 @@ interface ListData { /** * list type 'ordered' or 'unordered' */ - style: string; + style: ListDataStyle; /** * list of first-level elements */ @@ -49,30 +57,16 @@ interface NestedListConfig { * default list style: ordered or unordered * default is unordered */ - defaultStyle: string; + defaultStyle?: ListDataStyle; } /** * Constructor Params for Nested List Tool, use to pass initial data and settings */ -interface NestedListParams { - /** - * Preload data for the tool - */ - data: ListData; - /** - * Tool's configuration - */ - config: NestedListConfig; - /** - * Editor.js API - */ - api: API; - /** - * Is Nested List Tool read-only - */ - readOnly: boolean; -} +export type NestedListParams = BlockToolConstructorOptions< + ListData, + NestedListConfig +>; /** * CSS classes for the Nested List Tool @@ -141,12 +135,12 @@ export default class NestedList { /** * Tool's configuration */ - config: NestedListConfig; + config?: NestedListConfig; /** * Default list style */ - defaultListStyle: NestedListConfig['defaultStyle']; + defaultListStyle?: NestedListConfig['defaultStyle']; /** * Corresponds to UiNodes type from Editor.js but with wrapper being nullable @@ -188,7 +182,7 @@ export default class NestedList { * Set the default list style from the config. */ this.defaultListStyle = - this.config.defaultStyle === 'ordered' ? 'ordered' : 'unordered'; + this.config?.defaultStyle === 'ordered' ? 'ordered' : 'unordered'; const initialData = { style: this.defaultListStyle, @@ -265,12 +259,12 @@ export default class NestedList { renderSettings(): TunesMenuConfig { const tunes = [ { - name: 'unordered', + name: 'unordered' as const, label: this.api.i18n.t('Unordered'), icon: IconListBulleted, }, { - name: 'ordered', + name: 'ordered' as const, label: this.api.i18n.t('Ordered'), icon: IconListNumbered, }, @@ -325,7 +319,7 @@ export default class NestedList { */ pasteHandler(element: PasteEvent['detail']['data']): ListData { const { tagName: tag } = element; - let style: string = ''; + let style: ListDataStyle = 'unordered'; let tagToSearch: string; // set list style and tag to search. @@ -527,9 +521,9 @@ export default class NestedList { /** * Set list style * - * @param {string} style - new style to set + * @param {ListDataStyle} style - new style to set */ - set listStyle(style: string) { + set listStyle(style: ListDataStyle) { if (!this.nodes) { return; } @@ -561,7 +555,7 @@ export default class NestedList { /** * Update the style in data * - * @type {string} + * @type {ListDataStyle} */ this.data.style = style; } diff --git a/src/utils/caret.ts b/src/utils/caret.ts index feded369..f26d1a33 100644 --- a/src/utils/caret.ts +++ b/src/utils/caret.ts @@ -5,6 +5,9 @@ import { isHtmlElement } from './type-guards'; * Helper for working with caret */ export default class Caret { + /** + * The for caret saving/restoring + */ savedFakeCaret: HTMLElement | undefined; /** @@ -12,7 +15,7 @@ export default class Caret { */ constructor() { /** - * The for caret saving/restoring + * The hidden for caret saving/restoring */ this.savedFakeCaret = undefined; } diff --git a/src/utils/dom.ts b/src/utils/dom.ts index c23e689f..e1acbd4f 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -1,7 +1,7 @@ /** * HtmlElement's attribute that can be set */ -type HtmlElementAttributes = Pick; +type HtmlElementAttributes = Partial; /** * Helper for making Elements with attributes @@ -24,9 +24,11 @@ export function make( el.classList.add(classNames); } - for (const attrName in attributes) { - el[attrName as keyof HtmlElementAttributes] = - attributes[attrName as keyof HtmlElementAttributes]; + if (attributes) { + Object.entries(attributes).forEach(([key, value]) => { + if (!value) return; + el.setAttribute(key, value.toString()); + }); } return el; From 7861ddc0a33feb6ef0b473abc2359fd7bd0c58d5 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Sun, 7 Jul 2024 13:02:36 +0800 Subject: [PATCH 16/18] chore: Remove build step in eslint workflow --- .github/workflows/eslint.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 294f067d..e6d21205 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -10,4 +10,3 @@ jobs: - uses: actions/checkout@v2 - run: yarn install - run: yarn lint - - run: yarn build From 6f8acd87d5bde5c294bc0b581558708dc60592a9 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Sat, 27 Jul 2024 19:18:11 +0800 Subject: [PATCH 17/18] refactor: add private modifier --- src/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index ebef23ea..2a15f7ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -125,37 +125,37 @@ export default class NestedList { /** * The Editor.js API */ - api: API; + private api: API; /** * Is NestedList Tool read-only */ - readOnly: boolean; + private readOnly: boolean; /** * Tool's configuration */ - config?: NestedListConfig; + private config?: NestedListConfig; /** * Default list style */ - defaultListStyle?: NestedListConfig['defaultStyle']; + private defaultListStyle?: NestedListConfig['defaultStyle']; /** * Corresponds to UiNodes type from Editor.js but with wrapper being nullable */ - nodes: { wrapper: HTMLElement | null }; + private nodes: { wrapper: HTMLElement | null }; /** * Tool's data */ - data: ListData; + private data: ListData; /** * Caret helper */ - caret: Caret; + private caret: Caret; /** * Render plugin`s main Element and fill it with saved data From 449be27d2ea8e26e5892273e3e47996aba87b4a0 Mon Sep 17 00:00:00 2001 From: Cong Yuan Date: Sun, 28 Jul 2024 11:23:37 +0800 Subject: [PATCH 18/18] fix: make sure html element attributes are set --- src/utils/dom.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/dom.ts b/src/utils/dom.ts index e1acbd4f..f15911df 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -24,11 +24,10 @@ export function make( el.classList.add(classNames); } - if (attributes) { - Object.entries(attributes).forEach(([key, value]) => { - if (!value) return; - el.setAttribute(key, value.toString()); - }); + for (const attrName in attributes) { + // as any is used to avoid TS error that read-only properties of HTMLElement are not assignable + (el[attrName as keyof HtmlElementAttributes] as any) = + attributes[attrName as keyof HtmlElementAttributes]; } return el;