| 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); |
| }, |
| }); |
|
|