diff --git a/docs-app/public/docs/2-plugins/column-resizing.md b/docs-app/public/docs/2-plugins/column-resizing.md index c3d5f724..976d6469 100644 --- a/docs-app/public/docs/2-plugins/column-resizing.md +++ b/docs-app/public/docs/2-plugins/column-resizing.md @@ -151,6 +151,130 @@ table = headlessTable(this, { See the API Documentation [here][api-docs] for the full list of options and descriptions. +#### Fixed table layout + +With fixed table layout you can set `tableLayout: fixed` for a simpler calculation of column widths where the resize handle only resizes the column that is being resized. + +```js +table = headlessTable(this, { + columns: () => [ + /* ... */ + ], + plugins: [ColumnResizing.with(() => ({ tableLayout: "fixed" }))], +}); +``` + + + ### Preferences The width will be stored in preferences, per column. diff --git a/table/src/plugins/column-resizing/plugin.ts b/table/src/plugins/column-resizing/plugin.ts index 847e22fd..45b7e791 100644 --- a/table/src/plugins/column-resizing/plugin.ts +++ b/table/src/plugins/column-resizing/plugin.ts @@ -67,6 +67,19 @@ export interface TableOptions { * Valid values are 'left' or 'right' */ handlePosition?: string; + + /** + * Specify the table layout strategy for column resizing. + * + * - 'auto': Uses complex redistribution logic where resizing one column + * affects neighboring columns (default, preserves existing behavior) + * - 'fixed': Simple per-column resizing suitable for CSS table-layout: fixed + * + * Valid values are 'auto' or 'fixed' + * + * default: 'auto' + */ + tableLayout?: string; } interface Signature { @@ -378,6 +391,39 @@ export class TableMeta { resizeColumn(column: Column, delta: number) { if (delta === 0) return; + const tableLayout = this.options?.tableLayout ?? 'auto'; + + if (tableLayout === 'fixed') { + this.#resizeColumnFixed(column, delta); + } else { + this.#resizeColumnAuto(column, delta); + } + } + + /** + * Simple column resizing for table-layout: fixed + * Only affects the target column and respects minimum width + */ + #resizeColumnFixed( + column: Column, + delta: number, + ) { + const columnMeta = meta.forColumn(column, ColumnResizing); + const newWidth = columnMeta.width + delta; + + if (newWidth >= columnMeta.minWidth) { + columnMeta.width = newWidth; + } + } + + /** + * Complex column resizing with redistribution logic + * Preserves existing behavior for table-layout: auto + */ + #resizeColumnAuto( + column: Column, + delta: number, + ) { /** * When the delta is negative, we are dragging to the next * when positive, we are dragging to the right diff --git a/test-app/tests/plugins/column-resizing/fixed-layout-test.gts b/test-app/tests/plugins/column-resizing/fixed-layout-test.gts new file mode 100644 index 00000000..8a28271c --- /dev/null +++ b/test-app/tests/plugins/column-resizing/fixed-layout-test.gts @@ -0,0 +1,101 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { htmlSafe } from "@ember/template"; +import { render } from "@ember/test-helpers"; +import { module, test } from "qunit"; +import { setupRenderingTest } from "ember-qunit"; +import { setOwner } from "@ember/owner"; + +import { headlessTable, type ColumnConfig } from "@universal-ember/table"; +import { + ColumnResizing, + resizeHandle, +} from "@universal-ember/table/plugins/column-resizing"; +import { createHelpers } from "@universal-ember/table/test-support"; + +import { TestStyles, getColumns, assertChanges, width } from "./utils.gts"; + +module("Plugins | resizing | fixed layout", function (hooks) { + setupRenderingTest(hooks); + + let ctx: Context; + let { dragLeft, dragRight } = createHelpers({ + resizeHandle: "[data-handle]", + }); + + hooks.beforeEach(function () { + ctx = new Context(); + setOwner(ctx, this.owner); + }); + + class Context { + @tracked containerWidth = 1000; + + columns: ColumnConfig[] = [ + { + name: "A", + key: "A", + pluginOptions: [ColumnResizing.forColumn(() => ({ minWidth: 128 }))], + }, + { + name: "B", + key: "B", + pluginOptions: [ColumnResizing.forColumn(() => ({ minWidth: 128 }))], + }, + { + name: "C", + key: "C", + pluginOptions: [ColumnResizing.forColumn(() => ({ minWidth: 128 }))], + }, + { + name: "D", + key: "D", + pluginOptions: [ColumnResizing.forColumn(() => ({ minWidth: 128 }))], + }, + ]; + + table = headlessTable(this, { + columns: () => this.columns, + data: () => [] as unknown[], + plugins: [ColumnResizing.with(() => ({ tableLayout: "fixed" }))], + }); + } + + class FixedLayoutTestComponent extends Component<{ ctx: Context }> { + get table() { + return this.args.ctx.table; + } + + get modifiers() { + return this.table.modifiers; + } + + get testContainerStyle() { + return htmlSafe(`width: ${this.args.ctx.containerWidth}px`); + } + + + } +});