hensei-web/src/lib/components/edra/extensions/table/table-cell.ts
Justin Edmund 2792279f9a add edra tiptap editor component
copied from edra library with svelte 5 fix for onTransaction callback
2025-12-21 15:12:51 -08:00

124 lines
2.6 KiB
TypeScript

import { mergeAttributes, Node } from '@tiptap/core';
import { Plugin } from '@tiptap/pm/state';
import { Decoration, DecorationSet } from '@tiptap/pm/view';
import { getCellsInColumn, isRowSelected, selectRow } from './utils.js';
export interface TableCellOptions {
HTMLAttributes: Record<string, unknown>;
}
export const TableCell = Node.create<TableCellOptions>({
name: 'tableCell',
content: 'block+',
tableRole: 'cell',
isolating: true,
addOptions() {
return {
HTMLAttributes: {}
};
},
parseHTML() {
return [{ tag: 'td' }];
},
renderHTML({ HTMLAttributes }) {
return ['td', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
},
addAttributes() {
return {
colspan: {
default: 1,
parseHTML: (element) => {
const colspan = element.getAttribute('colspan');
const value = colspan ? parseInt(colspan, 10) : 1;
return value;
}
},
rowspan: {
default: 1,
parseHTML: (element) => {
const rowspan = element.getAttribute('rowspan');
const value = rowspan ? parseInt(rowspan, 10) : 1;
return value;
}
},
colwidth: {
default: null,
parseHTML: (element) => {
const colwidth = element.getAttribute('colwidth');
const value = colwidth ? [parseInt(colwidth, 10)] : null;
return value;
}
},
style: {
default: null
}
};
},
addProseMirrorPlugins() {
const { isEditable } = this.editor;
return [
new Plugin({
props: {
decorations: (state) => {
if (!isEditable) {
return DecorationSet.empty;
}
const { doc, selection } = state;
const decorations: Decoration[] = [];
const cells = getCellsInColumn(0)(selection);
if (cells) {
cells.forEach(({ pos }: { pos: number }, index: number) => {
decorations.push(
Decoration.widget(pos + 1, () => {
const rowSelected = isRowSelected(index)(selection);
let className = 'grip-row';
if (rowSelected) {
className += ' selected';
}
if (index === 0) {
className += ' first';
}
if (index === cells.length - 1) {
className += ' last';
}
const grip = document.createElement('a');
grip.className = className;
grip.addEventListener('mousedown', (event) => {
event.preventDefault();
event.stopImmediatePropagation();
this.editor.view.dispatch(selectRow(index)(this.editor.state.tr));
});
return grip;
})
);
});
}
return DecorationSet.create(doc, decorations);
}
}
})
];
}
});