mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
ef87210b3d
* feat: new image menu * switch to resizable side handles * use pixels * refactor excalidraw and drawio menu * support image resize undo * video resize * callout menu refresh * refresh table menus * fix color scheme * fix: patch @tiptap/core ResizableNodeView to prevent resize sticking after mouseup * feat: columns * notes callout * focus on first column * capture tab key in column * fix print * hide columns menu when some nodes are focused * fix print * fix columns * selective placeholder * fix blockquote * quote * fix callout in columns
128 lines
2.8 KiB
TypeScript
128 lines
2.8 KiB
TypeScript
import { Node, mergeAttributes, findParentNode } from "@tiptap/core";
|
|
import { TextSelection } from "prosemirror-state";
|
|
|
|
export interface ColumnOptions {
|
|
HTMLAttributes: Record<string, any>;
|
|
}
|
|
|
|
export interface ColumnAttributes {
|
|
width?: number | null;
|
|
}
|
|
|
|
declare module "@tiptap/core" {
|
|
interface Commands<ReturnType> {
|
|
column: {
|
|
setColumnWidth: (width: number | null) => ReturnType;
|
|
};
|
|
}
|
|
}
|
|
|
|
export const Column = Node.create<ColumnOptions>({
|
|
name: "column",
|
|
group: "block",
|
|
content: "block+",
|
|
defining: true,
|
|
isolating: true,
|
|
selectable: false,
|
|
|
|
addOptions() {
|
|
return {
|
|
HTMLAttributes: {},
|
|
};
|
|
},
|
|
|
|
addAttributes() {
|
|
return {
|
|
width: {
|
|
default: null,
|
|
parseHTML: (element) => {
|
|
const value = element.getAttribute("data-width");
|
|
return value ? parseFloat(value) : null;
|
|
},
|
|
renderHTML: (attributes: ColumnAttributes) => {
|
|
if (!attributes.width) return {};
|
|
return {
|
|
"data-width": attributes.width,
|
|
style: `flex: ${attributes.width}`,
|
|
};
|
|
},
|
|
},
|
|
};
|
|
},
|
|
|
|
parseHTML() {
|
|
return [
|
|
{
|
|
tag: `div[data-type="${this.name}"]`,
|
|
},
|
|
];
|
|
},
|
|
|
|
renderHTML({ HTMLAttributes }) {
|
|
return [
|
|
"div",
|
|
mergeAttributes(
|
|
{ "data-type": this.name },
|
|
this.options.HTMLAttributes,
|
|
HTMLAttributes,
|
|
),
|
|
0,
|
|
];
|
|
},
|
|
|
|
addKeyboardShortcuts() {
|
|
const jumpToColumn = (direction: 1 | -1) => () => {
|
|
const { state, dispatch } = this.editor.view;
|
|
|
|
const columns = findParentNode(
|
|
(node) => node.type.name === "columns",
|
|
)(state.selection);
|
|
if (!columns) return false;
|
|
|
|
const column = findParentNode(
|
|
(node) => node.type.name === "column",
|
|
)(state.selection);
|
|
if (!column) return false;
|
|
|
|
let currentIndex = -1;
|
|
columns.node.forEach((_child, offset, index) => {
|
|
if (columns.pos + 1 + offset === column.pos) {
|
|
currentIndex = index;
|
|
}
|
|
});
|
|
|
|
const targetIndex = currentIndex + direction;
|
|
if (targetIndex < 0 || targetIndex >= columns.node.childCount) {
|
|
return true;
|
|
}
|
|
|
|
let offset = 0;
|
|
for (let j = 0; j < targetIndex; j++) {
|
|
offset += columns.node.child(j).nodeSize;
|
|
}
|
|
|
|
const targetPos = columns.pos + 1 + offset + 1 + 1;
|
|
if (dispatch) {
|
|
dispatch(
|
|
state.tr.setSelection(TextSelection.create(state.doc, targetPos)),
|
|
);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
return {
|
|
Tab: jumpToColumn(1),
|
|
"Shift-Tab": jumpToColumn(-1),
|
|
};
|
|
},
|
|
|
|
addCommands() {
|
|
return {
|
|
setColumnWidth:
|
|
(width) =>
|
|
({ commands }) =>
|
|
commands.updateAttributes("column", { width }),
|
|
};
|
|
},
|
|
});
|