From f72e54f5a3ece2fde69913aed8e934da175e84c4 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Wed, 25 Sep 2024 22:26:27 +0300 Subject: [PATCH] suggestions --- src/ListRenderer/ChecklistRenderer.ts | 8 ++ src/ListRenderer/ListRenderer.ts | 7 ++ src/ListRenderer/OrderedListRenderer.ts | 10 +- src/ListRenderer/UnorderedListRenderer.ts | 9 ++ src/ListTabulator/index.ts | 145 ++++++++++++---------- src/types/ListParams.ts | 5 + src/types/ListRenderer.ts | 3 +- src/utils/getSiblings.ts | 45 +++++++ 8 files changed, 160 insertions(+), 72 deletions(-) create mode 100644 src/utils/getSiblings.ts diff --git a/src/ListRenderer/ChecklistRenderer.ts b/src/ListRenderer/ChecklistRenderer.ts index c0c6d03a..1f855596 100644 --- a/src/ListRenderer/ChecklistRenderer.ts +++ b/src/ListRenderer/ChecklistRenderer.ts @@ -136,6 +136,14 @@ export class CheckListRenderer implements ListRendererInterface { * @returns {ItemMeta} Item meta object */ getItemMeta: (item: Element) => ItemMeta; + + + /** + * Returns default item meta used on creation of the new item + * @returns item meta object + */ + composeDefaultMeta: () => ItemMeta; }; diff --git a/src/ListRenderer/OrderedListRenderer.ts b/src/ListRenderer/OrderedListRenderer.ts index edba0bae..fcf51189 100644 --- a/src/ListRenderer/OrderedListRenderer.ts +++ b/src/ListRenderer/OrderedListRenderer.ts @@ -94,9 +94,17 @@ export class OrderedListRenderer implements ListRendererInterface { /** * Get all child items of the current list item */ - private getChildItems(element: HTMLElement): Element[] | null { + private getChildItems(element: HTMLElement): ListItemElement[] | null { let itemChildWrapper: HTMLElement = element; /** @@ -179,22 +180,12 @@ export default class ListTabulator { * @param {Element} parentItem - where to append * @returns {void} */ - appendItems(items: ListItem[], parentItem: Element): void { + appendItems(items: ListItem[], parentElement: Element): void { if (this.renderer !== undefined) { items.forEach((item) => { - let itemEl: Element; + const itemEl = this.renderItem(item.content, item.meta); - if (this.renderer instanceof OrderedListRenderer) { - itemEl = this.renderer!.renderItem(item.content, item.meta as OrderedListItemMeta); - } - else if (this.renderer instanceof UnorderedListRenderer) { - itemEl = this.renderer!.renderItem(item.content, item.meta as UnorderedListItemMeta); - } - else { - itemEl = this.renderer!.renderItem(item.content, item.meta as ChecklistItemMeta); - } - - parentItem.appendChild(itemEl); + parentElement.appendChild(itemEl); /** * Check if there are child items @@ -518,12 +509,7 @@ export default class ListTabulator { */ event.preventDefault(); - /** - * Prevent editor.js behaviour - */ - event.stopPropagation(); - - this.mergeCurrentItemWithPrevious(currentItem); + this.mergeItemWithPrevious(currentItem); } @@ -562,7 +548,7 @@ export default class ListTabulator { * * @returns {void} */ - unshiftItem(item: Element): void { + unshiftItem(item: ListItemElement): void { if (!item.parentNode) { return; } @@ -585,12 +571,7 @@ export default class ListTabulator { return; } - const siblings = this.getChildItems(item.parentElement); - - if (siblings === null) { - console.log('siblings are null') - return; - } + const siblings = getSiblings(item as HTMLElement); /** * If current item has no childs, than render child wrapper @@ -600,16 +581,8 @@ export default class ListTabulator { currentItemWrapper = this.renderer!.renderWrapper(false); } - let currentItemPassed = false; - - siblings.forEach((sibling) => { - if (currentItemPassed) { - currentItemWrapper.appendChild(sibling); - } - - if (sibling === item) { - currentItemPassed = true; - } + siblings?.forEach((sibling) => { + currentItemWrapper.appendChild(sibling); }) /** @@ -647,10 +620,10 @@ export default class ListTabulator { /** * Method that is used for list splitting and moving trailing items to the new separated list - * @param currentItem - current item html element + * @param item - current item html element */ - splitList(currentItem: HTMLElement): void { - const currentItemChildrenList = this.getChildItems(currentItem); + splitList(item: ListItemElement): void { + const currentItemChildrenList = this.getChildItems(item); /** * First child item should be unshifted because separated list should start @@ -663,22 +636,18 @@ export default class ListTabulator { } /** - * Render new wrapper for list that would be separated + * Get trailing siblings of the current item */ - const newListWrapper = this.renderer!.renderWrapper(true); - - let trailingElement: Element | null = currentItem.nextElementSibling; + const newListItems = getSiblings(item); - const newListItems: Element[] = []; + if (newListItems === null) { + return; + } /** - * Form array of trailing elements to be moved to separate list + * Render new wrapper for list that would be separated */ - while (trailingElement !== null) { - newListItems.push(trailingElement); - - trailingElement = trailingElement.nextElementSibling; - } + const newListWrapper = this.renderer!.renderWrapper(true); /** * Append new list wrapper with trailing elements @@ -718,7 +687,7 @@ export default class ListTabulator { * Method that is used for splitting item content and moving trailing content to the new sibling item * @param currentItem - current item html element */ - splitItem(currentItem: HTMLElement): void { + splitItem(currentItem: ListItemElement): void { const [ currentNode, offset ] = getCaretNodeAndOffset(); if ( currentNode === null ) { @@ -749,7 +718,7 @@ export default class ListTabulator { /** * Create the new list item */ - const itemEl = this.renderer!.renderItem(endingHTML, { checked: false }); + const itemEl = this.renderItem(endingHTML); /** * Move new item after current @@ -772,10 +741,22 @@ export default class ListTabulator { * Current item children would not change nesting level * @param currentItem - current item html element */ - mergeCurrentItemWithPrevious(currentItem: HTMLElement): void { - const previousItem = currentItem.previousElementSibling; + mergeItemWithPrevious(item: ListItemElement): void { + const previousItem = item.previousElementSibling; + + const currentItemParentNode = item.parentNode; + + /** + * Check that parent node of the current element exists + */ + if (currentItemParentNode === null) { + return; + } + if (!isHtmlElement(currentItemParentNode)) { + return; + } - const parentItem = currentItem.closest(`.${DefaultListCssClasses.item}`); + const parentItem = currentItemParentNode.closest(`.${DefaultListCssClasses.item}`); /** * Check that current item has any previous siblings to be merged with @@ -794,7 +775,7 @@ export default class ListTabulator { /** * Lets compute the item which will be merged with current item text */ - let targetItem: Element | null; + let targetItem: ListItemElement | null; /** * If there is a previous item then we get a deepest item in its sublists @@ -802,8 +783,14 @@ export default class ListTabulator { * Otherwise we will use the parent item */ if (previousItem) { - const childrenOfPreviousItem = previousItem.querySelectorAll(`.${DefaultListCssClasses.item}`); + /** + * Get list of all levels children of the previous item + */ + const childrenOfPreviousItem = previousItem.querySelectorAll(`.${DefaultListCssClasses.item}`); + /** + * Target item would be deepest child of the previous item or previous item itself + */ targetItem = childrenOfPreviousItem[childrenOfPreviousItem.length - 1] || previousItem; } else { targetItem = parentItem; @@ -812,7 +799,7 @@ export default class ListTabulator { /** * Get current item content */ - const currentItemContent = currentItem.querySelector(`.${DefaultListCssClasses.itemContent}`)?.innerHTML ?? ''; + const currentItemContent = this.renderer.getItemContent(item); /** * Get the target item content element @@ -824,15 +811,15 @@ export default class ListTabulator { /** * Get target item content element */ - const targetItemContent = targetItem.querySelector(`.${DefaultListCssClasses.itemContent}`); + const targetItemContentElement = targetItem.querySelector(`.${DefaultListCssClasses.itemContent}`); /** * Set a new place for caret */ - if (!targetItemContent) { + if (!targetItemContentElement) { return; } - focus(targetItemContent, false); + focus(targetItemContentElement, false); /** * Save the caret position @@ -842,12 +829,12 @@ export default class ListTabulator { /** * Update target item content by merging with current item html content */ - targetItemContent.insertAdjacentHTML('beforeend', currentItemContent); + targetItemContentElement.insertAdjacentHTML('beforeend', currentItemContent); /** * Get child list of the currentItem */ - const currentItemChildrenList = this.getChildItems(currentItem); + const currentItemChildrenList = this.getChildItems(item); /** * Check that current item has any children @@ -856,7 +843,7 @@ export default class ListTabulator { /** * Remove current item element */ - currentItem.remove(); + item.remove(); /** * Restore the caret position @@ -882,7 +869,7 @@ export default class ListTabulator { /** * Remove current item element */ - currentItem.remove(); + item.remove(); /** * Restore the caret position @@ -992,7 +979,7 @@ export default class ListTabulator { * @param {boolean} atStart - where to set focus: at the start or at the end * @returns {void} */ - focusItem(item: Element, atStart: boolean = true): void { + focusItem(item: ListItemElement, atStart: boolean = true): void { const itemContent = item.querySelector( `.${DefaultListCssClasses.itemContent}` ); @@ -1005,7 +992,7 @@ export default class ListTabulator { /** * Get out from List Tool by Enter on the empty last item - * + * @param index - optional parameter represents index, where would be inseted default block * @returns {void} */ getOutOfList(index?: number): void { @@ -1023,4 +1010,24 @@ export default class ListTabulator { this.currentItem?.remove(); this.api.caret.setToBlock(newBlock); } + + /** + * Method that calls render function of the renderer with a necessary item meta cast + * @param item - item to be rendered + * @returns html element of the rendered item + */ + renderItem(itemContent: ListItem['content'], meta?: ListItem['meta']): HTMLElement { + const itemMeta = meta ?? this.renderer.composeDefaultMeta(); + + switch (true) { + case this.renderer instanceof OrderedListRenderer: + return this.renderer.renderItem(itemContent, itemMeta as OrderedListItemMeta); + + case this.renderer instanceof UnorderedListRenderer: + return this.renderer.renderItem(itemContent, itemMeta as UnorderedListItemMeta); + + default: + return this.renderer.renderItem(itemContent, itemMeta as ChecklistItemMeta); + } + } } diff --git a/src/types/ListParams.ts b/src/types/ListParams.ts index 39f5502d..135c5773 100644 --- a/src/types/ListParams.ts +++ b/src/types/ListParams.ts @@ -49,3 +49,8 @@ export interface NestedListConfig { */ defaultStyle?: ListDataStyle; } + +/** + * Type that represents element of the list item + */ +export type ListItemElement = HTMLElement; diff --git a/src/types/ListRenderer.ts b/src/types/ListRenderer.ts index c48e2ac8..c41c5fb5 100644 --- a/src/types/ListRenderer.ts +++ b/src/types/ListRenderer.ts @@ -1,7 +1,6 @@ import { CheckListRenderer, OrderedListRenderer, UnorderedListRenderer } from '../ListRenderer'; /** - * @todo move to types * Type that represents all possible list renderer types */ -export type ListRenderer = OrderedListRenderer | UnorderedListRenderer | CheckListRenderer; +export type ListRenderer = CheckListRenderer | OrderedListRenderer | UnorderedListRenderer; diff --git a/src/utils/getSiblings.ts b/src/utils/getSiblings.ts new file mode 100644 index 00000000..4db38c6a --- /dev/null +++ b/src/utils/getSiblings.ts @@ -0,0 +1,45 @@ +/** + * Get all siblings before passed element, or after it + */ +export function getSiblings(element: HTMLElement, afterCurrentElement: boolean = true): Element[] | null { + const siblings: Element[] = []; + + let nextElementSibling: HTMLElement; + + function getNextElementSibling(element: HTMLElement): HTMLElement{ + /** + * Get first sibling element respectfully to passed afterCurrentElement + */ + switch (afterCurrentElement) { + case true: + return element.nextElementSibling as HTMLElement; + + case false: + return element.previousElementSibling as HTMLElement; + } + } + + nextElementSibling = getNextElementSibling(element); + + /** + * Iterate by all siblings elements + */ + while (nextElementSibling !== null) { + siblings.push(nextElementSibling); + + /** + * Get next element sibling + */ + nextElementSibling = getNextElementSibling(nextElementSibling); + } + + /** + * Check that formed siblings array is not empty + * If it is emtpy, return null + */ + if (siblings.length !== 0) { + return siblings; + } + + return null; +}