From 32adc53a0be84052f964b023e07df6e70b76ac22 Mon Sep 17 00:00:00 2001 From: Caio Pizzol Date: Mon, 15 Dec 2025 17:08:19 -0300 Subject: [PATCH] feat: adaptive insert table column width --- .../src/extensions/table/table.js | 21 ++++++- .../table/tableHelpers/createCell.js | 7 ++- .../table/tableHelpers/createTable.js | 10 +++- .../table/tableHelpers/tableHelpers.test.js | 60 +++++++++++++++++++ 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/packages/super-editor/src/extensions/table/table.js b/packages/super-editor/src/extensions/table/table.js index aab070186..c5737e670 100644 --- a/packages/super-editor/src/extensions/table/table.js +++ b/packages/super-editor/src/extensions/table/table.js @@ -540,11 +540,28 @@ export const Table = Node.create({ * @example * editor.commands.insertTable() * editor.commands.insertTable({ rows: 3, cols: 3, withHeaderRow: true }) + * editor.commands.insertTable({ rows: 3, cols: 3, columnWidths: [200, 100, 200] }) */ insertTable: - ({ rows = 3, cols = 3, withHeaderRow = false } = {}) => + ({ rows = 3, cols = 3, withHeaderRow = false, columnWidths = null } = {}) => ({ tr, dispatch, editor }) => { - const node = createTable(editor.schema, rows, cols, withHeaderRow); + let widths = columnWidths; + + // If no widths provided, auto-calculate to fill available page width + if (!widths) { + const { pageSize = {}, pageMargins = {} } = editor.converter?.pageStyles ?? {}; + const { width: pageWidth } = pageSize; + const { left = 0, right = 0 } = pageMargins; + + if (pageWidth) { + // Page dimensions are in inches, convert to pixels (96 PPI) + const availableWidth = (pageWidth - left - right) * 96; + const columnWidth = Math.floor(availableWidth / cols); + widths = Array(cols).fill(columnWidth); + } + } + + const node = createTable(editor.schema, rows, cols, withHeaderRow, null, widths); if (dispatch) { let offset = tr.selection.$from.end() + 1; diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createCell.js b/packages/super-editor/src/extensions/table/tableHelpers/createCell.js index 81a4dce9c..a7613f519 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createCell.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createCell.js @@ -1,7 +1,10 @@ // @ts-check -export const createCell = (cellType, cellContent = null) => { +export const createCell = (cellType, cellContent = null, attrs = null) => { if (cellContent) { - return cellType.createChecked(null, cellContent); + return cellType.createChecked(attrs, cellContent); + } + if (attrs) { + return cellType.createAndFill(attrs); } return cellType.createAndFill(); }; diff --git a/packages/super-editor/src/extensions/table/tableHelpers/createTable.js b/packages/super-editor/src/extensions/table/tableHelpers/createTable.js index 749c9f38f..783522b3e 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/createTable.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/createTable.js @@ -12,13 +12,16 @@ import { createTableBorders } from './createTableBorders.js'; * @param {number} colsCount - Number of columns * @param {boolean} withHeaderRow - Create first row as header * @param {Object} [cellContent=null] - Initial cell content + * @param {number[]} [columnWidths=null] - Array of pixel widths per column * @returns {Object} Complete table node with borders * @example * const table = createTable(schema, 3, 3, true) * @example * const table = createTable(schema, 2, 4, false, paragraphNode) + * @example + * const table = createTable(schema, 3, 3, false, null, [200, 100, 200]) */ -export const createTable = (schema, rowsCount, colsCount, withHeaderRow, cellContent = null) => { +export const createTable = (schema, rowsCount, colsCount, withHeaderRow, cellContent = null, columnWidths = null) => { const types = { table: getNodeType('table', schema), tableRow: getNodeType('tableRow', schema), @@ -30,10 +33,11 @@ export const createTable = (schema, rowsCount, colsCount, withHeaderRow, cellCon const cells = []; for (let index = 0; index < colsCount; index++) { - const cell = createCell(types.tableCell, cellContent); + const cellAttrs = columnWidths ? { colwidth: [columnWidths[index]] } : null; + const cell = createCell(types.tableCell, cellContent, cellAttrs); if (cell) cells.push(cell); if (withHeaderRow) { - const headerCell = createCell(types.tableHeader, cellContent); + const headerCell = createCell(types.tableHeader, cellContent, cellAttrs); if (headerCell) { headerCells.push(headerCell); } diff --git a/packages/super-editor/src/extensions/table/tableHelpers/tableHelpers.test.js b/packages/super-editor/src/extensions/table/tableHelpers/tableHelpers.test.js index 3fe3cde7c..15d3c88d3 100644 --- a/packages/super-editor/src/extensions/table/tableHelpers/tableHelpers.test.js +++ b/packages/super-editor/src/extensions/table/tableHelpers/tableHelpers.test.js @@ -92,6 +92,20 @@ describe('tableHelpers', () => { expect(filledCell.content.firstChild.textContent).toBe('Hello'); }); + it('createCell accepts attrs parameter for setting cell attributes', () => { + const cellType = schema.nodes.tableCell; + + const cellWithWidth = createCell(cellType, null, { colwidth: [200] }); + expect(cellWithWidth.type.name).toBe('tableCell'); + expect(cellWithWidth.attrs.colwidth).toEqual([200]); + + const cellWithContent = createCell(cellType, schema.nodes.paragraph.create(null, schema.text('Test')), { + colwidth: [150], + }); + expect(cellWithContent.attrs.colwidth).toEqual([150]); + expect(cellWithContent.content.firstChild.textContent).toBe('Test'); + }); + const buildRowTable = (widths, overrideCol, overrideValue) => { const cellType = schema.nodes.tableCell; const rowType = schema.nodes.tableRow; @@ -164,6 +178,52 @@ describe('tableHelpers', () => { expect(headerCell.type.name).toBe('tableHeader'); }); + it('createTable applies column widths when provided', () => { + const columnWidths = [200, 100, 200]; + const table = createTable(schema, 2, 3, false, null, columnWidths); + + expect(table.type.name).toBe('table'); + expect(table.childCount).toBe(2); // 2 rows + + // Check first row cells have correct widths + const firstRow = table.firstChild; + expect(firstRow.childCount).toBe(3); + expect(firstRow.child(0).attrs.colwidth).toEqual([200]); + expect(firstRow.child(1).attrs.colwidth).toEqual([100]); + expect(firstRow.child(2).attrs.colwidth).toEqual([200]); + + // Check second row cells also have correct widths + const secondRow = table.child(1); + expect(secondRow.child(0).attrs.colwidth).toEqual([200]); + expect(secondRow.child(1).attrs.colwidth).toEqual([100]); + expect(secondRow.child(2).attrs.colwidth).toEqual([200]); + }); + + it('createTable applies column widths to header row when withHeaderRow is true', () => { + const columnWidths = [150, 150]; + const table = createTable(schema, 2, 2, true, null, columnWidths); + + // First row should be header cells with widths + const headerRow = table.firstChild; + expect(headerRow.child(0).type.name).toBe('tableHeader'); + expect(headerRow.child(0).attrs.colwidth).toEqual([150]); + expect(headerRow.child(1).attrs.colwidth).toEqual([150]); + + // Second row should be regular cells with widths + const bodyRow = table.child(1); + expect(bodyRow.child(0).type.name).toBe('tableCell'); + expect(bodyRow.child(0).attrs.colwidth).toEqual([150]); + }); + + it('createTable uses default widths when columnWidths is null', () => { + const table = createTable(schema, 1, 2, false, null, null); + + const firstRow = table.firstChild; + // Default colwidth from schema is [100] + expect(firstRow.child(0).attrs.colwidth).toEqual([100]); + expect(firstRow.child(1).attrs.colwidth).toEqual([100]); + }); + it('createTableBorders assigns uniform border configuration', () => { const borders = createTableBorders({ size: 2, color: '#ccc' }); expect(borders.top).toEqual({ size: 2, color: '#ccc' });