UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

124 lines (113 loc) 3.12 kB
function getChildIndex(node: Element) { return Array.prototype.indexOf.call(node.parentElement!.childNodes, node); } function isWhitespaceNode(node: Node) { return node.nodeType === 3 && !/\S/.test(node.nodeValue || ""); } /** * Step 1, Turns: * * <ul> * <li>item</li> * <li> * <ul> * <li>...</li> * <li>...</li> * </ul> * </li> * * Into: * <ul> * <li>item</li> * <ul> * <li>...</li> * <li>...</li> * </ul> * </ul> * */ function liftNestedListsToParent(element: HTMLElement) { element.querySelectorAll("li > ul, li > ol").forEach((list) => { const index = getChildIndex(list); const parentListItem = list.parentElement!; const siblingsAfter = Array.from(parentListItem.childNodes).slice( index + 1, ); list.remove(); siblingsAfter.forEach((sibling) => { sibling.remove(); }); parentListItem.insertAdjacentElement("afterend", list); siblingsAfter.reverse().forEach((sibling) => { if (isWhitespaceNode(sibling)) { return; } const siblingContainer = document.createElement("li"); siblingContainer.append(sibling); list.insertAdjacentElement("afterend", siblingContainer); }); if (parentListItem.childNodes.length === 0) { parentListItem.remove(); } }); } /** * Step 2, Turns (output of liftNestedListsToParent): * * <li>item</li> * <ul> * <li>...</li> * <li>...</li> * </ul> * * Into: * <div> * <li>item</li> * <div data-node-type="blockGroup"> * <ul> * <li>...</li> * <li>...</li> * </ul> * </div> * </div> * * This resulting format is parsed */ function createGroups(element: HTMLElement) { element.querySelectorAll("li + ul, li + ol").forEach((list) => { const listItem = list.previousElementSibling as HTMLElement; const blockContainer = document.createElement("div"); listItem.insertAdjacentElement("afterend", blockContainer); blockContainer.append(listItem); const blockGroup = document.createElement("div"); blockGroup.setAttribute("data-node-type", "blockGroup"); blockContainer.append(blockGroup); while ( blockContainer.nextElementSibling?.nodeName === "UL" || blockContainer.nextElementSibling?.nodeName === "OL" ) { blockGroup.append(blockContainer.nextElementSibling); } }); } // prevent XSS, similar to https://github.com/ProseMirror/prosemirror-view/blob/1251b2b412656a2a06263e4187574beb43651273/src/clipboard.ts#L204 // https://github.com/TypeCellOS/BlockNote/issues/601 let _detachedDoc: Document | null = null; function detachedDoc() { return ( _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title")) ); } export function nestedListsToBlockNoteStructure( elementOrHTML: HTMLElement | string, ) { if (typeof elementOrHTML === "string") { const element = detachedDoc().createElement("div"); element.innerHTML = elementOrHTML; elementOrHTML = element; } liftNestedListsToParent(elementOrHTML); createGroups(elementOrHTML); return elementOrHTML; }