Spaces:
Sleeping
Sleeping
| import { Node, mergeAttributes } from "@tiptap/core"; | |
| import { ReactNodeViewRenderer } from "@tiptap/react"; | |
| import { StackView } from "../StackView"; | |
| declare module "@tiptap/core" { | |
| interface Commands<ReturnType> { | |
| 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); | |
| }, | |
| }); | |