diff --git a/.changeset/two-rats-watch.md b/.changeset/two-rats-watch.md new file mode 100644 index 000000000..8d772ff8c --- /dev/null +++ b/.changeset/two-rats-watch.md @@ -0,0 +1,5 @@ +--- +"@tiptap/extension-table": patch +--- + +enforce cellMinWidth even on column not resized by the user, fixes #5435 diff --git a/demos/src/Nodes/Table/React/index.spec.js b/demos/src/Nodes/Table/React/index.spec.js index 0b423b975..791e06b6f 100644 --- a/demos/src/Nodes/Table/React/index.spec.js +++ b/demos/src/Nodes/Table/React/index.spec.js @@ -56,6 +56,14 @@ context('/src/Nodes/Table/React/', () => { }) }) + it('sets the minimum width on the colgroups by default (3x1)', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.insertTable({ cols: 3, rows: 1, withHeaderRow: false }) + + cy.get('.tiptap').find('col').invoke('attr', 'style').should('eq', 'min-width: 25px;') + }) + }) + it('generates correct markup for a table (1x1)', () => { cy.get('.tiptap').then(([{ editor }]) => { editor.commands.insertTable({ cols: 1, rows: 1, withHeaderRow: false }) @@ -63,7 +71,7 @@ context('/src/Nodes/Table/React/', () => { const html = editor.getHTML() expect(html).to.equal( - '

', + '

', ) }) }) @@ -75,7 +83,7 @@ context('/src/Nodes/Table/React/', () => { const html = editor.getHTML() expect(html).to.equal( - '

', + '

', ) }) }) diff --git a/demos/src/Nodes/Table/Vue/index.spec.js b/demos/src/Nodes/Table/Vue/index.spec.js index cdfd5e9c2..80c94925e 100644 --- a/demos/src/Nodes/Table/Vue/index.spec.js +++ b/demos/src/Nodes/Table/Vue/index.spec.js @@ -56,13 +56,23 @@ context('/src/Nodes/Table/Vue/', () => { }) }) + it('sets the minimum width on the colgroups by default (3x1)', () => { + cy.get('.tiptap').then(([{ editor }]) => { + editor.commands.insertTable({ cols: 3, rows: 1, withHeaderRow: false }) + + cy.get('.tiptap').find('col').invoke('attr', 'style').should('eq', 'min-width: 25px;') + }) + }) + it('generates correct markup for a table (1x1)', () => { cy.get('.tiptap').then(([{ editor }]) => { editor.commands.insertTable({ cols: 1, rows: 1, withHeaderRow: false }) const html = editor.getHTML() - expect(html).to.equal('

') + expect(html).to.equal( + '

', + ) }) }) @@ -72,7 +82,9 @@ context('/src/Nodes/Table/Vue/', () => { const html = editor.getHTML() - expect(html).to.equal('

') + expect(html).to.equal( + '

', + ) }) }) diff --git a/packages/extension-table/src/TableView.ts b/packages/extension-table/src/TableView.ts index fbb451b23..b7775bed7 100644 --- a/packages/extension-table/src/TableView.ts +++ b/packages/extension-table/src/TableView.ts @@ -1,41 +1,52 @@ -// @ts-nocheck import { Node as ProseMirrorNode } from '@tiptap/pm/model' import { NodeView } from '@tiptap/pm/view' +import { getColStyleDeclaration } from './utilities/colStyle.js' + export function updateColumns( node: ProseMirrorNode, - colgroup: Element, - table: Element, + colgroup: HTMLTableColElement, // has the same prototype as + table: HTMLTableElement, cellMinWidth: number, overrideCol?: number, - overrideValue?: any, + overrideValue?: number, ) { let totalWidth = 0 let fixedWidth = true let nextDOM = colgroup.firstChild const row = node.firstChild - for (let i = 0, col = 0; i < row.childCount; i += 1) { - const { colspan, colwidth } = row.child(i).attrs + 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] - const cssWidth = hasWidth ? `${hasWidth}px` : '' + 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 + 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 + if (!hasWidth) { + fixedWidth = false } - nextDOM = nextDOM.nextSibling + 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 + } } } } @@ -43,7 +54,7 @@ export function updateColumns( while (nextDOM) { const after = nextDOM.nextSibling - nextDOM.parentNode.removeChild(nextDOM) + nextDOM.parentNode?.removeChild(nextDOM) nextDOM = after } @@ -61,13 +72,13 @@ export class TableView implements NodeView { cellMinWidth: number - dom: Element + dom: HTMLDivElement - table: Element + table: HTMLTableElement - colgroup: Element + colgroup: HTMLTableColElement - contentDOM: Element + contentDOM: HTMLTableSectionElement constructor(node: ProseMirrorNode, cellMinWidth: number) { this.node = node diff --git a/packages/extension-table/src/utilities/colStyle.ts b/packages/extension-table/src/utilities/colStyle.ts new file mode 100644 index 000000000..0453d94a4 --- /dev/null +++ b/packages/extension-table/src/utilities/colStyle.ts @@ -0,0 +1,10 @@ +export function getColStyleDeclaration(minWidth: number, width: number | undefined): [string, string] { + if (width) { + // apply the stored width unless it is below the configured minimum cell width + return ['width', `${Math.max(width, minWidth)}px`] + } + + // set the minimum with on the column if it has no stored width + return ['min-width', `${minWidth}px`] + +} diff --git a/packages/extension-table/src/utilities/createColGroup.ts b/packages/extension-table/src/utilities/createColGroup.ts index 2c4d305ab..0e369f713 100644 --- a/packages/extension-table/src/utilities/createColGroup.ts +++ b/packages/extension-table/src/utilities/createColGroup.ts @@ -1,5 +1,13 @@ import { DOMOutputSpec, Node as ProseMirrorNode } from '@tiptap/pm/model' +import { getColStyleDeclaration } from './colStyle.js' + +export type ColGroup = { + colgroup: DOMOutputSpec + tableWidth: string + tableMinWidth: string +} | Record; + /** * Creates a colgroup element for a table node in ProseMirror. * @@ -9,12 +17,22 @@ import { DOMOutputSpec, Node as ProseMirrorNode } from '@tiptap/pm/model' * @param overrideValue - (Optional) The width value to use for the overridden column. * @returns An object containing the colgroup element, the total width of the table, and the minimum width of the table. */ +export function createColGroup( + node: ProseMirrorNode, + cellMinWidth: number, +): ColGroup +export function createColGroup( + node: ProseMirrorNode, + cellMinWidth: number, + overrideCol: number, + overrideValue: number, +): ColGroup export function createColGroup( node: ProseMirrorNode, cellMinWidth: number, overrideCol?: number, - overrideValue?: any, -) { + overrideValue?: number, +): ColGroup { let totalWidth = 0 let fixedWidth = true const cols: DOMOutputSpec[] = [] @@ -28,8 +46,7 @@ export function createColGroup( 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` : '' + const hasWidth = overrideCol === col ? overrideValue : colwidth && colwidth[j] as number | undefined totalWidth += hasWidth || cellMinWidth @@ -37,7 +54,12 @@ export function createColGroup( fixedWidth = false } - cols.push(['col', cssWidth ? { style: `width: ${cssWidth}` } : {}]) + const [property, value] = getColStyleDeclaration(cellMinWidth, hasWidth) + + cols.push([ + 'col', + { style: `${property}: ${value}` }, + ]) } }