import { Node } from "prosemirror-model"; import { NodeView } from "prosemirror-view"; import { CssClassName } from "../constants"; import { el } from "../util/dom"; import { CellAttrs } from "../schema"; export class TableView implements NodeView { public readonly dom: HTMLDivElement; public readonly table: HTMLTableElement; public readonly colgroup: HTMLElement; public readonly contentDOM: HTMLTableSectionElement; constructor(public node: Node, public cellMinWidth: number) { const dom = el("div", CssClassName.TABLE_WRAPPER); this.dom = dom; this.table = dom.appendChild(el("table")); this.colgroup = this.table.appendChild(el("colgroup")); updateColumns(node, this.colgroup, this.table, cellMinWidth); this.contentDOM = this.table.appendChild(el("tbody")); } public update(node: Node) { if (node.type !== this.node.type) { return false; } this.node = node; updateColumns(node, this.colgroup, this.table, this.cellMinWidth); return true; } public ignoreMutation(record: MutationRecord) { // This is done to prevent decoration changes to the table and colgroups // from being parsed as content changes. This is necessary because // `updateColumns` modifies the DOM directly. return record.type == "attributes" && (record.target == this.table || this.colgroup.contains(record.target)); } } export function updateColumns( table: Node, colgroup: HTMLElement, tableElement: HTMLTableElement, cellMinWidth: number, overrideCol?: number, overrideValue?: number ) { let totalWidth = 0; // Note for future refactoring: `fixedWidth` is set to false if one of the // columns doesn't have an explicit width set, as this is interpreted as being // a "fill remaining space" column. In this case an explicit `width` is not // set on the table. let fixedWidth = true; let nextDOM = colgroup.firstChild as HTMLElement | null; const row = table.firstChild!; for (let i = 0, col = 0; i < row.childCount; i++) { const { colspan, colwidth } = row.child(i).attrs as CellAttrs; for (let j = 0; j < colspan; j++, col++) { const hasWidth = overrideCol == col ? overrideValue : colwidth != null ? colwidth[j] : null; const cssWidth = hasWidth != null ? hasWidth + "px" : ""; totalWidth += hasWidth != null ? hasWidth : cellMinWidth; if (hasWidth == null) { fixedWidth = false; } if (nextDOM === null) { colgroup.appendChild(el("col")).style.width = cssWidth; } else { if (nextDOM.style.width != cssWidth) { nextDOM.style.width = cssWidth; } nextDOM = nextDOM.nextSibling as HTMLElement | null; } } } // Remove any superfluous colgroup elements (this is necessary when deleting // columns). while (nextDOM !== null) { const after = nextDOM.nextSibling as HTMLElement; nextDOM.parentNode!.removeChild(nextDOM); nextDOM = after; } tableElement.style.width = fixedWidth ? `${totalWidth}px` : ""; }