From b1c7e6ae2092e930574dc00b5fe314bd4510a4db Mon Sep 17 00:00:00 2001 From: "M. Palanikannan" <73993394+Palanikannan1437@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:36:19 +0530 Subject: [PATCH] [WEB-1526] feat: add auto merge behaviour to task lists and fix infinite backspace case (#4703) * feat: add auto merge behaviour to task lists * fix: unhandled cases for taskItem and taskList * fix: css task list such that toggling task list doesn't shift things * fix: task list jumps around while trying create/delete things in between two task lists * fix: remove filtering for generic transactions i.e. transactions with some meta data while tying to join things --- packages/editor/core/src/styles/editor.css | 9 ++++++ .../custom-list-keymap/list-helpers.ts | 16 +++++++--- .../editor/core/src/ui/extensions/keymap.tsx | 30 ++++++------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/packages/editor/core/src/styles/editor.css b/packages/editor/core/src/styles/editor.css index 00312cb00..f76b6b1bf 100644 --- a/packages/editor/core/src/styles/editor.css +++ b/packages/editor/core/src/styles/editor.css @@ -110,6 +110,11 @@ ul[data-type="taskList"] li > label input[type="checkbox"]:checked:hover { } } +/* the p tag just after the ul tag */ +ul[data-type="taskList"] + p { + margin-top: 0.4rem !important; +} + ul[data-type="taskList"] li > label input[type="checkbox"] { position: relative; -webkit-appearance: none; @@ -152,6 +157,10 @@ ul[data-type="taskList"] li > label input[type="checkbox"] { } } +ul[data-type="taskList"] li > div > p { + margin-top: 10px; +} + ul[data-type="taskList"] li[data-checked="true"] > div > p { color: rgb(var(--color-text-400)); text-decoration: line-through; diff --git a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers.ts b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers.ts index 330ebbc12..d6ddb8ce0 100644 --- a/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers.ts +++ b/packages/editor/core/src/ui/extensions/custom-list-keymap/list-helpers.ts @@ -72,7 +72,7 @@ const getPrevListDepth = (typeOrName: string, state: EditorState) => { // Traverse up the document structure from the adjusted position for (let d = resolvedPos.depth; d > 0; d--) { const node = resolvedPos.node(d); - if (node.type.name === "bulletList" || node.type.name === "orderedList") { + if (node.type.name === "bulletList" || node.type.name === "orderedList" || node.type.name === "taskList") { // Increment depth for each list ancestor found depth++; } @@ -146,6 +146,8 @@ export const handleBackspace = (editor: Editor, name: string, parentListTypes: s if (!isAtStartOfNode(editor.state)) { return false; } + + // is the paragraph node inside of the current list item (maybe with a hard break) const isParaSibling = isCurrentParagraphASibling(editor.state); const isCurrentListItemSublist = prevListIsHigher(name, editor.state); const listItemPos = findListItemPos(name, editor.state); @@ -306,7 +308,10 @@ const isCurrentParagraphASibling = (state: EditorState): boolean => { const currentParagraphNode = $from.parent; // Get the current node where the selection is. // Ensure we're in a paragraph and the parent is a list item. - if (currentParagraphNode.type.name === "paragraph" && listItemNode.type.name === "listItem") { + if ( + currentParagraphNode.type.name === "paragraph" && + (listItemNode.type.name === "listItem" || listItemNode.type.name === "taskItem") + ) { let paragraphNodesCount = 0; listItemNode.forEach((child) => { if (child.type.name === "paragraph") { @@ -327,16 +332,19 @@ export function isCursorInSubList(editor: Editor) { // Check if the current node is a list item const listItem = editor.schema.nodes.listItem; + const taskItem = editor.schema.nodes.taskItem; // Traverse up the document tree from the current position for (let depth = $from.depth; depth > 0; depth--) { const node = $from.node(depth); - if (node.type === listItem) { + if (node.type === listItem || node.type === taskItem) { // If the parent of the list item is also a list, it's a sub-list const parent = $from.node(depth - 1); if ( parent && - (parent.type === editor.schema.nodes.bulletList || parent.type === editor.schema.nodes.orderedList) + (parent.type === editor.schema.nodes.bulletList || + parent.type === editor.schema.nodes.orderedList || + parent.type === editor.schema.nodes.taskList) ) { return true; } diff --git a/packages/editor/core/src/ui/extensions/keymap.tsx b/packages/editor/core/src/ui/extensions/keymap.tsx index 2e0bdd1fe..43b4e3435 100644 --- a/packages/editor/core/src/ui/extensions/keymap.tsx +++ b/packages/editor/core/src/ui/extensions/keymap.tsx @@ -15,9 +15,7 @@ declare module "@tiptap/core" { } } -function autoJoin(tr: Transaction, newTr: Transaction, nodeType: NodeType) { - if (!tr.isGeneric) return false; - +function autoJoin(tr: Transaction, newTr: Transaction, nodeTypes: NodeType[]) { // Find all ranges where we might want to join. const ranges: Array = []; for (let i = 0; i < tr.mapping.maps.length; i++) { @@ -28,7 +26,7 @@ function autoJoin(tr: Transaction, newTr: Transaction, nodeType: NodeType) { // Figure out which joinable points exist inside those ranges, // by checking all node boundaries in their parent nodes. - const joinable = []; + const joinable: number[] = []; for (let i = 0; i < ranges.length; i += 2) { const from = ranges[i], to = ranges[i + 1]; @@ -40,7 +38,7 @@ function autoJoin(tr: Transaction, newTr: Transaction, nodeType: NodeType) { if (!after) break; if (index && joinable.indexOf(pos) == -1) { const before = parent.child(index - 1); - if (before.type == after.type && before.type === nodeType) joinable.push(pos); + if (before.type == after.type && nodeTypes.includes(before.type)) joinable.push(pos); } pos += after.nodeSize; } @@ -88,25 +86,15 @@ export const CustomKeymap = Extension.create({ // Create a new transaction. const newTr = newState.tr; - let joined = false; - for (const transaction of transactions) { - const anotherJoin = autoJoin(transaction, newTr, newState.schema.nodes["orderedList"]); - joined = anotherJoin || joined; - } - if (joined) { - return newTr; - } - }, - }), - new Plugin({ - key: new PluginKey("unordered-list-merging"), - appendTransaction(transactions, oldState, newState) { - // Create a new transaction. - const newTr = newState.tr; + const joinableNodes = [ + newState.schema.nodes["orderedList"], + newState.schema.nodes["taskList"], + newState.schema.nodes["bulletList"], + ]; let joined = false; for (const transaction of transactions) { - const anotherJoin = autoJoin(transaction, newTr, newState.schema.nodes["bulletList"]); + const anotherJoin = autoJoin(transaction, newTr, joinableNodes); joined = anotherJoin || joined; } if (joined) {