Skip to content

Commit

Permalink
fix(table): set min-width for cols #5435 (#5464)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ragnar-Oock authored Oct 30, 2024
1 parent d6e4caf commit 1523901
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-rats-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tiptap/extension-table": patch
---

enforce cellMinWidth even on column not resized by the user, fixes #5435
12 changes: 10 additions & 2 deletions demos/src/Nodes/Table/React/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,22 @@ 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 })

const html = editor.getHTML()

expect(html).to.equal(
'<table style="min-width: 25px"><colgroup><col></colgroup><tbody><tr><td colspan="1" rowspan="1"><p></p></td></tr></tbody></table>',
'<table style="min-width: 25px"><colgroup><col style="min-width: 25px"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p></p></td></tr></tbody></table>',
)
})
})
Expand All @@ -75,7 +83,7 @@ context('/src/Nodes/Table/React/', () => {
const html = editor.getHTML()

expect(html).to.equal(
'<table style="min-width: 25px"><colgroup><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th></tr></tbody></table>',
'<table style="min-width: 25px"><colgroup><col style="min-width: 25px"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th></tr></tbody></table>',
)
})
})
Expand Down
16 changes: 14 additions & 2 deletions demos/src/Nodes/Table/Vue/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<table style="min-width: 25px"><colgroup><col></colgroup><tbody><tr><td colspan="1" rowspan="1"><p></p></td></tr></tbody></table>')
expect(html).to.equal(
'<table style="min-width: 25px"><colgroup><col style="min-width: 25px"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p></p></td></tr></tbody></table>',
)
})
})

Expand All @@ -72,7 +82,9 @@ context('/src/Nodes/Table/Vue/', () => {

const html = editor.getHTML()

expect(html).to.equal('<table style="min-width: 25px"><colgroup><col></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th></tr></tbody></table>')
expect(html).to.equal(
'<table style="min-width: 25px"><colgroup><col style="min-width: 25px"></colgroup><tbody><tr><th colspan="1" rowspan="1"><p></p></th></tr></tbody></table>',
)
})
})

Expand Down
61 changes: 36 additions & 25 deletions packages/extension-table/src/TableView.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,60 @@
// @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, // <colgroup> has the same prototype as <col>
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

for (let j = 0; j < colspan; j += 1, col += 1) {
const hasWidth = overrideCol === col ? overrideValue : colwidth && colwidth[j]
const cssWidth = hasWidth ? `${hasWidth}px` : ''
if (row !== null) {
for (let i = 0, col = 0; i < row.childCount; i += 1) {
const { colspan, colwidth } = row.child(i).attrs

totalWidth += hasWidth || cellMinWidth
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` : ''

if (!hasWidth) {
fixedWidth = false
}
totalWidth += hasWidth || cellMinWidth

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
}
}
}
}

while (nextDOM) {
const after = nextDOM.nextSibling

nextDOM.parentNode.removeChild(nextDOM)
nextDOM.parentNode?.removeChild(nextDOM)
nextDOM = after
}

Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions packages/extension-table/src/utilities/colStyle.ts
Original file line number Diff line number Diff line change
@@ -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`]

}
32 changes: 27 additions & 5 deletions packages/extension-table/src/utilities/createColGroup.ts
Original file line number Diff line number Diff line change
@@ -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<string, never>;

/**
* Creates a colgroup element for a table node in ProseMirror.
*
Expand All @@ -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[] = []
Expand All @@ -28,16 +46,20 @@ 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

if (!hasWidth) {
fixedWidth = false
}

cols.push(['col', cssWidth ? { style: `width: ${cssWidth}` } : {}])
const [property, value] = getColStyleDeclaration(cellMinWidth, hasWidth)

cols.push([
'col',
{ style: `${property}: ${value}` },
])
}
}

Expand Down

0 comments on commit 1523901

Please sign in to comment.