import { Node, mergeAttributes } from "@tiptap/core"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { StackView } from "../StackView"; declare module "@tiptap/core" { interface Commands { stack: { insertStack: (columns?: number) => ReturnType; setStackColumns: (pos: number, columns: number) => ReturnType; }; } } export const StackColumn = Node.create({ name: "stackColumn", group: "stackColumn", content: "block+", defining: true, isolating: true, parseHTML() { return [{ tag: 'div[data-type="stack-column"]' }]; }, renderHTML({ HTMLAttributes }) { return ["div", mergeAttributes(HTMLAttributes, { "data-type": "stack-column" }), 0]; }, }); export const Stack = Node.create({ name: "stack", group: "block", content: "stackColumn{2,4}", defining: true, isolating: true, addAttributes() { return { layout: { default: "2-column" }, gap: { default: "medium" }, }; }, parseHTML() { return [{ tag: 'div[data-component="stack"]' }]; }, renderHTML({ HTMLAttributes }) { return [ "div", mergeAttributes(HTMLAttributes, { "data-component": "stack" }), 0, ]; }, addCommands() { return { insertStack: (columns = 2) => ({ commands }) => { const cols = Array.from({ length: Math.min(Math.max(columns, 2), 4) }, () => ({ type: "stackColumn", content: [{ type: "paragraph" }], })); return commands.insertContent({ type: "stack", attrs: { layout: `${columns}-column` }, content: cols, }); }, setStackColumns: (pos, columns) => ({ tr, state, dispatch }) => { const target = Math.min(Math.max(columns, 2), 4); const node = state.doc.nodeAt(pos); if (!node || node.type.name !== "stack") return false; const current = node.childCount; if (current === target) { tr.setNodeMarkup(pos, undefined, { ...node.attrs, layout: `${target}-column` }); dispatch?.(tr); return true; } tr.setNodeMarkup(pos, undefined, { ...node.attrs, layout: `${target}-column` }); if (target > current) { const colType = state.schema.nodes.stackColumn; const pType = state.schema.nodes.paragraph; const insertPos = pos + node.nodeSize - 1; for (let i = 0; i < target - current; i++) { tr.insert(insertPos + i, colType.create(null, pType.create())); } } else { let removeFrom = pos + 1; for (let i = 0; i < target; i++) { removeFrom += node.child(i).nodeSize; } const removeTo = pos + node.nodeSize - 1; tr.delete(removeFrom, removeTo); } dispatch?.(tr); return true; }, }; }, addNodeView() { return ReactNodeViewRenderer(StackView); }, });