This commit is contained in:
fantasticit 2022-11-28 16:41:23 +08:00
parent ab6affa3cc
commit 8c58090a41
4 changed files with 119 additions and 58 deletions

View File

@ -46,7 +46,7 @@ export const TableCell = Node.create<TableCellOptions>({
}, },
}, },
colwidth: { colwidth: {
default: null, default: [150],
parseHTML: (element) => { parseHTML: (element) => {
const colwidth = element.getAttribute('colwidth'); const colwidth = element.getAttribute('colwidth');
const value = colwidth ? [parseInt(colwidth, 10)] : null; const value = colwidth ? [parseInt(colwidth, 10)] : null;

View File

@ -13,7 +13,7 @@ export const TableHeader = BuiltInTableHeader.extend({
default: 1, default: 1,
}, },
colwidth: { colwidth: {
default: null, default: [150],
parseHTML: (element) => { parseHTML: (element) => {
const colwidth = element.getAttribute('colwidth'); const colwidth = element.getAttribute('colwidth');
const value = colwidth ? colwidth.split(',').map((item) => parseInt(item, 10)) : null; const value = colwidth ? colwidth.split(',').map((item) => parseInt(item, 10)) : null;

View File

@ -1,60 +1,121 @@
import BuiltInTable from '@tiptap/extension-table'; import BuiltInTable from '@tiptap/extension-table';
import { Plugin } from 'prosemirror-state'; import { Node as ProseMirrorNode } from 'prosemirror-model';
import { tableEditing } from 'prosemirror-tables'; import { NodeView } from 'prosemirror-view';
import { Decoration, DecorationSet } from 'prosemirror-view';
function updateColumns(
node: ProseMirrorNode,
colgroup: Element,
table: HTMLElement,
cellMinWidth: number,
overrideCol?: number,
overrideValue?: any
) {
let totalWidth = 0;
let fixedWidth = true;
let nextDOM = colgroup.firstChild as HTMLElement;
const row = node.firstChild;
for (let i = 0, col = 0; i < row.childCount; i += 1) {
const { colspan, colwidth } = row.child(i).attrs;
for (let j = 0; j < colspan; j += 1, col += 1) {
const hasWidth = overrideCol === col ? overrideValue : colwidth && colwidth[j];
const cssWidth = hasWidth ? `${hasWidth}px` : '';
totalWidth += hasWidth || cellMinWidth;
if (!hasWidth) {
fixedWidth = false;
}
if (!nextDOM) {
colgroup.appendChild(document.createElement('col')).style.width = cssWidth;
} else {
if (nextDOM.style.width !== cssWidth) {
nextDOM.style.width = cssWidth;
}
nextDOM = nextDOM.nextSibling as HTMLElement;
}
}
}
while (nextDOM) {
const after = nextDOM.nextSibling as HTMLElement;
nextDOM.parentNode.removeChild(nextDOM);
nextDOM = after;
}
if (fixedWidth) {
table.style.width = `${totalWidth}px`;
table.style.minWidth = '';
} else {
table.style.width = '';
table.style.minWidth = `${totalWidth}px`;
}
}
class TableView implements NodeView {
node: ProseMirrorNode;
cellMinWidth: number;
dom: HTMLElement;
scrollDom: HTMLElement;
table: HTMLElement;
colgroup: HTMLElement;
contentDOM: HTMLElement;
constructor(node: ProseMirrorNode, cellMinWidth: number) {
this.node = node;
this.cellMinWidth = cellMinWidth;
this.dom = document.createElement('div');
this.dom.className = 'tableWrapper';
this.scrollDom = document.createElement('div');
this.scrollDom.className = 'scrollWrapper';
this.dom.appendChild(this.scrollDom);
this.table = this.scrollDom.appendChild(document.createElement('table'));
this.colgroup = this.table.appendChild(document.createElement('colgroup'));
updateColumns(node, this.colgroup, this.table, cellMinWidth);
this.contentDOM = this.table.appendChild(document.createElement('tbody'));
}
update(node: ProseMirrorNode) {
if (node.type !== this.node.type) {
return false;
}
this.node = node;
updateColumns(node, this.colgroup, this.table, this.cellMinWidth);
return true;
}
ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) {
return (
mutation.type === 'attributes' && (mutation.target === this.table || this.colgroup.contains(mutation.target))
);
}
}
export const Table = BuiltInTable.extend({ export const Table = BuiltInTable.extend({
renderHTML() { // @ts-ignore
return [ addOptions() {
'div', return {
{ class: 'node-table' }, HTMLAttributes: {},
['div', { class: `scrollable` }, ['table', { class: `think-table render-wrapper` }, ['tbody', 0]]], resizable: true,
]; handleWidth: 5,
}, cellMinWidth: 25,
View: TableView,
addProseMirrorPlugins() { lastColumnResizable: true,
const { isEditable } = this.editor; allowTableNodeSelection: false,
};
return [
tableEditing(),
new Plugin({
props: {
decorations: (state) => {
const { doc } = state;
const decorations: Decoration[] = [];
let index = 0;
doc.descendants((node, pos) => {
if (node.type.name !== this.name) return;
const elements = document.getElementsByClassName('think-table');
const table = elements[index];
if (!table) return;
if (!isEditable) {
table.classList.add('is-readonly');
}
const element = table.parentElement;
const shadowRight = !!(element && element.scrollWidth > element.clientWidth);
if (shadowRight) {
decorations.push(
Decoration.widget(pos + 1, () => {
const shadow = document.createElement('div');
shadow.className = `scrollable-shadow right ${isEditable ? 'is-editable' : ''}`;
return shadow;
})
);
}
index++;
});
return DecorationSet.create(doc, decorations);
},
},
}),
];
}, },
}).configure({ resizable: true }); }).configure({ resizable: true });

View File

@ -34,7 +34,7 @@ export const TableBubbleMenu = ({ editor }) => {
while (container && container.tagName !== 'TABLE') { while (container && container.tagName !== 'TABLE') {
container = container.parentElement; container = container.parentElement;
} }
return container.parentElement; return container;
}, []); }, []);
const copyMe = useCallback(() => copyNode(Table.name, editor), [editor]); const copyMe = useCallback(() => copyNode(Table.name, editor), [editor]);
const deleteMe = useCallback(() => { const deleteMe = useCallback(() => {