import type { Node as ProseMirrorNode } from '@tiptap/pm/model'
import type { NodeView, ViewMutationRecord } from '@tiptap/pm/view'
import { getColStyleDeclaration } from './utilities/colStyle.js'
export function updateColumns(
node: ProseMirrorNode,
colgroup: HTMLTableColElement, //
has the same prototype as
table: HTMLTableElement,
cellMinWidth: number,
overrideCol?: number,
overrideValue?: number,
) {
let totalWidth = 0
let fixedWidth = true
let nextDOM = colgroup.firstChild
const row = node.firstChild
if (row !== null) {
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]) as number | undefined)
const cssWidth = hasWidth ? `${hasWidth}px` : ''
totalWidth += hasWidth || cellMinWidth
if (!hasWidth) {
fixedWidth = false
}
if (!nextDOM) {
const colElement = document.createElement('col')
const [propertyKey, propertyValue] = getColStyleDeclaration(cellMinWidth, hasWidth)
colElement.style.setProperty(propertyKey, propertyValue)
colgroup.appendChild(colElement)
} else {
if ((nextDOM as HTMLTableColElement).style.width !== cssWidth) {
const [propertyKey, propertyValue] = getColStyleDeclaration(cellMinWidth, hasWidth)
;(nextDOM as HTMLTableColElement).style.setProperty(propertyKey, propertyValue)
}
nextDOM = nextDOM.nextSibling
}
}
}
}
while (nextDOM) {
const after = nextDOM.nextSibling
nextDOM.parentNode?.removeChild(nextDOM)
nextDOM = after
}
// Check if user has set a width style on the table node
const hasUserWidth = node.attrs.style && typeof node.attrs.style === 'string' && /\bwidth\s*:/i.test(node.attrs.style)
if (fixedWidth && !hasUserWidth) {
table.style.width = `${totalWidth}px`
table.style.minWidth = ''
} else {
table.style.width = ''
table.style.minWidth = `${totalWidth}px`
}
}
export class TableView implements NodeView {
node: ProseMirrorNode
cellMinWidth: number
dom: HTMLDivElement
table: HTMLTableElement
colgroup: HTMLTableColElement
contentDOM: HTMLTableSectionElement
constructor(node: ProseMirrorNode, cellMinWidth: number) {
this.node = node
this.cellMinWidth = cellMinWidth
this.dom = document.createElement('div')
this.dom.className = 'tableWrapper'
this.table = this.dom.appendChild(document.createElement('table'))
// Apply user styles to the table element
if (node.attrs.style) {
this.table.style.cssText = node.attrs.style
}
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: ViewMutationRecord) {
const target = mutation.target as Node
const isInsideWrapper = this.dom.contains(target)
const isInsideContent = this.contentDOM.contains(target)
if (isInsideWrapper && !isInsideContent) {
if (mutation.type === 'attributes' || mutation.type === 'childList' || mutation.type === 'characterData') {
return true
}
}
return false
}
}