Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions packages/super-editor/src/extensions/table/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment on lines +557 to +558
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use pixelsToInches helper

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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
};
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend changing function signature to take an object with named options, rather than an ordered list of positional arguments; with a long list of arguments, it's hard to look at a createTable call and understand what each argument is.

const types = {
table: getNodeType('table', schema),
tableRow: getNodeType('tableRow', schema),
Expand All @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if columnWidths.length != colsCount? In particular, if columnWidths[index] is undefined, should cellAttrs be set to null rather than { colwidth: [undefined] }?

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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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' });
Expand Down
Loading