From f36b5d929096419e3002a2dfc4d9d7e3d0125e0e Mon Sep 17 00:00:00 2001 From: AApuci Date: Thu, 29 Oct 2026 22:36:00 +0800 Subject: [PATCH 1/6] fix(Table): Add fitAllColumnWidth to auto-fit all columns(#8644) auto-fit every .col-resizer in the table. It calls autoFitColumnWidth with notify=false for each column to avoid multiple callbacks, then persists the column state and invokes the resizeColumnCallback once using the first column name. --- .../Components/Table/Table.razor.js | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index a257c1fd4d4..6ddcf6e13f6 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -120,6 +120,31 @@ export async function reset(id) { } } +export async function fitAllColumnWidth(id) { + const table = Data.get(id); + if (!table?.tables?.length) { + return; + } + + const columns = [...table.tables[0].querySelectorAll('.col-resizer')]; + + if (columns.length > 0) { + for (const col of columns) { + await autoFitColumnWidth(table, col, false); + } + + const state = getColumnStateObject(table); + saveColumnStateToLocalstorage(table, state); + + // use first column name to notify + await table.invoke.invokeMethodAsync( + table.options.resizeColumnCallback, + getColumnName(columns[0]), + state + ); + } +} + export async function switchCardView(id) { const table = Data.get(id); @@ -616,13 +641,13 @@ const indexOfCol = col => { return [...row.children].indexOf(th); } -const autoFitColumnWidth = async (table, col) => { +const autoFitColumnWidth = async (table, col, notify = true) => { stopAutoColumnWidth(table); const field = getColumnName(col); const index = indexOfCol(col); let rows = null; let maxWidth = getColumnMaxCellWidth(table, index); - + if (table.options.fitColumnWidthIncludeHeader) { const th = getColumnHeader(col); maxWidth = Math.max(maxWidth, getCellWidth(th)); @@ -647,6 +672,11 @@ const autoFitColumnWidth = async (table, col) => { resetColumnWidthTips(table, col); + // notify or not + if (!notify) { + return; + } + const state = getColumnStateObject(table); saveColumnStateToLocalstorage(table, state); From b47cada9be7dca9cd0ac24593ef96a5fcd9da5cb Mon Sep 17 00:00:00 2001 From: AApuci Date: Fri, 30 Oct 2026 09:02:38 +0800 Subject: [PATCH 2/6] fix(Table): Send wildcard when notifying column resize Change Table.fitAllColumnWidth to pass "*" to the resizeColumnCallback instead of the first column's name. This signals that all columns were adjusted (rather than a specific column) and avoids relying on the first column identifier in the notification. --- src/BootstrapBlazor/Components/Table/Table.razor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index 6ddcf6e13f6..b81e6ce7361 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -139,7 +139,7 @@ export async function fitAllColumnWidth(id) { // use first column name to notify await table.invoke.invokeMethodAsync( table.options.resizeColumnCallback, - getColumnName(columns[0]), + "*", state ); } From f0f178cf839a87c9e197e94915bde567a3ada4e9 Mon Sep 17 00:00:00 2001 From: AApuci Date: Fri, 30 Oct 2026 09:07:19 +0800 Subject: [PATCH 3/6] fix(Table): Clarify resize callback uses wildcard Update comment in src/BootstrapBlazor/Components/Table/Table.razor.js to indicate the resize callback is invoked with a wildcard ('*') to notify that all columns have been resized, rather than using the first column name. This is a comment-only clarification; no functional code changes were made. --- src/BootstrapBlazor/Components/Table/Table.razor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index b81e6ce7361..ddc48a0d5df 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -136,7 +136,7 @@ export async function fitAllColumnWidth(id) { const state = getColumnStateObject(table); saveColumnStateToLocalstorage(table, state); - // use first column name to notify + // use wildcard to notify all columns have been resized await table.invoke.invokeMethodAsync( table.options.resizeColumnCallback, "*", From 480af8b491f5972f08f80d7934cc2e3c86eb8da8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 30 Oct 2026 11:35:52 +0800 Subject: [PATCH 4/6] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Table/Table.razor | 2 +- .../Components/Table/Table.razor.cs | 4 ++ .../Components/Table/Table.razor.js | 49 +++++++++---------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/BootstrapBlazor/Components/Table/Table.razor b/src/BootstrapBlazor/Components/Table/Table.razor index 5b6ef0c9ba9..24bbaefb827 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor +++ b/src/BootstrapBlazor/Components/Table/Table.razor @@ -194,7 +194,7 @@ if (IsShowEmpty) { - + diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.cs b/src/BootstrapBlazor/Components/Table/Table.razor.cs index 7ca935f78d1..da00d1f2626 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.cs +++ b/src/BootstrapBlazor/Components/Table/Table.razor.cs @@ -2049,6 +2049,8 @@ private async Task OnContextMenu(MouseEventArgs e, TItem item) /// /// 获得/设置 表格客户端状态调整回调方法 /// Gets or sets Table Column Client Status Changed Callback + /// 第一个参数为发生变化的列名称;调用 批量调整时为 "*",第二个参数包含完整列状态。 + /// The first argument is the changed column name, or "*" for a batch update by . The second argument contains the full column state. /// v10.6.1 /// [Parameter] @@ -2072,6 +2074,8 @@ private async Task OnContextMenu(MouseEventArgs e, TItem item) /// /// 列宽自适应方法 /// Fit All Column Width Method + /// 启用 时,按当前已渲染内容调整所有可见数据列的宽度;发生调整后统一保存列状态、触发一次回调。 + /// When is enabled, fits all visible data columns to their currently rendered content, then saves the column state and invokes the callback once if widths were fitted. /// public async Task FitAllColumnWidth() { diff --git a/src/BootstrapBlazor/Components/Table/Table.razor.js b/src/BootstrapBlazor/Components/Table/Table.razor.js index ddc48a0d5df..a77960841cc 100644 --- a/src/BootstrapBlazor/Components/Table/Table.razor.js +++ b/src/BootstrapBlazor/Components/Table/Table.razor.js @@ -126,13 +126,14 @@ export async function fitAllColumnWidth(id) { return; } - const columns = [...table.tables[0].querySelectorAll('.col-resizer')]; - - if (columns.length > 0) { - for (const col of columns) { - await autoFitColumnWidth(table, col, false); + let resized = false; + for (const col of getResizableColumns(table)) { + if (await autoFitColumnWidth(table, col, false)) { + resized = true; } + } + if (resized) { const state = getColumnStateObject(table); saveColumnStateToLocalstorage(table, state); @@ -496,7 +497,7 @@ const setResizeListener = table => { let colIndex = 0 let originalX = 0 - const columns = [...table.tables[0].querySelectorAll('.col-resizer')] + const columns = getResizableColumns(table); columns.forEach(col => { table.columns.push(col) EventHandler.on(col, 'click', e => e.stopPropagation()); @@ -635,6 +636,8 @@ const getColumnName = col => getColumnHeader(col).getAttribute('data-bb-field'); const getResizableColumnWidth = col => getWidth(getColumnHeader(col)) | 0; +const getResizableColumns = table => [...table.tables[0].querySelectorAll(':scope > thead > tr > th > .col-resizer')]; + const indexOfCol = col => { const th = getColumnHeader(col); const row = th.parentElement; @@ -645,7 +648,6 @@ const autoFitColumnWidth = async (table, col, notify = true) => { stopAutoColumnWidth(table); const field = getColumnName(col); const index = indexOfCol(col); - let rows = null; let maxWidth = getColumnMaxCellWidth(table, index); if (table.options.fitColumnWidthIncludeHeader) { @@ -655,15 +657,15 @@ const autoFitColumnWidth = async (table, col, notify = true) => { if (maxWidth > 0) { table.tables.forEach(table => { - const colEl = table.querySelectorAll('colgroup col')[index]; + const colEl = table.querySelectorAll(':scope > colgroup > col')[index]; if (colEl) { colEl.style.setProperty('width', `${maxWidth}px`); } - const th = table.querySelectorAll('thead > tr > th')[index]; + const th = table.querySelectorAll(':scope > thead > tr > th')[index]; if (th) { const span = th.querySelector('.table-text'); - span.style.removeProperty('width'); + span?.style.removeProperty('width'); } const tableWidth = getTableWidth(table); @@ -673,30 +675,27 @@ const autoFitColumnWidth = async (table, col, notify = true) => { resetColumnWidthTips(table, col); // notify or not - if (!notify) { - return; - } - - const state = getColumnStateObject(table); - saveColumnStateToLocalstorage(table, state); + if (notify) { + const state = getColumnStateObject(table); + saveColumnStateToLocalstorage(table, state); - await table.invoke.invokeMethodAsync(table.options.resizeColumnCallback, field, state); + await table.invoke.invokeMethodAsync(table.options.resizeColumnCallback, field, state); + } + return true; } + return false; } const getColumnMaxCellWidth = (table, index) => { - let rows = null; + const body = table.thead ? table.tables[1] : table.tables[0]; + const rows = body.querySelectorAll(':scope > tbody > tr:not(.is-detail):not(.is-empty)'); let maxWidth = 0; - if (table.thead) { - rows = [...table.tables[1].tBodies[0].rows].filter(x => !x.classList.contains('is-detail')); - } - else { - rows = [...table.tables[0].tBodies[0].rows].filter(x => !x.classList.contains('is-detail')); - } rows.forEach(row => { const cell = row.cells[index]; - maxWidth = Math.max(maxWidth, calcCellWidth(cell)); + if (cell && cell.colSpan === 1) { + maxWidth = Math.max(maxWidth, calcCellWidth(cell)); + } }); return maxWidth; From 82442d5aba78a0bce19f48375d4051864de461a4 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 30 Oct 2026 11:35:59 +0800 Subject: [PATCH 5/6] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableTest.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/test/UnitTest/Components/TableTest.cs b/test/UnitTest/Components/TableTest.cs index 395953bbebc..e885cdb0616 100644 --- a/test/UnitTest/Components/TableTest.cs +++ b/test/UnitTest/Components/TableTest.cs @@ -3153,6 +3153,7 @@ public void ShowEmpty_Ok() }); cut.Contains("test-empty"); cut.Contains("colspan=\"1\""); + Assert.Single(cut.FindAll("tbody > tr.is-empty")); var table = cut.FindComponent>(); table.Render(pb => @@ -9034,8 +9035,10 @@ public async Task AllowDragColumn_FixedColumn_Ok() await cut.InvokeAsync(() => table.Instance.DragColumnCallback(1, 0)); } - [Fact] - public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok() + [Theory] + [InlineData(nameof(Foo.Address))] + [InlineData("*")] + public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok(string columnName) { var state = new TableColumnClientStatus(); state.TableWidth = 500; @@ -9045,6 +9048,7 @@ public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok() Context.JSInterop.Setup("getColumnStates", "test").SetResult(state); var name = ""; + var callbackCount = 0; TableColumnClientStatus? clientState = null; var localizer = Context.Services.GetRequiredService>(); var cut = Context.Render(pb => @@ -9057,6 +9061,7 @@ public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok() pb.Add(a => a.FitColumnWidthIncludeHeader, true); pb.Add(a => a.OnTableColumnClientStatusChanged, (field, state) => { + callbackCount++; name = field; clientState = state; return Task.CompletedTask; @@ -9082,9 +9087,15 @@ public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok() newState.TableWidth = 100; newState.Columns.Add(new TableColumnState() { Name = "Name", Width = 50 }); newState.Columns.Add(new TableColumnState() { Name = "Address", Width = 50 }); - await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(nameof(Foo.Address), newState)); - Assert.Equal("Address", name); + await cut.InvokeAsync(() => table.Instance.ResizeColumnCallback(columnName, newState)); + Assert.Equal(columnName, name); + Assert.Equal(1, callbackCount); Assert.NotNull(clientState); + Assert.Equal(100, clientState.TableWidth); + Assert.Equal(50, clientState.Columns.Single(c => c.Name == nameof(Foo.Name)).Width); + Assert.Equal(50, clientState.Columns.Single(c => c.Name == nameof(Foo.Address)).Width); + Assert.False(clientState.Columns.Single(c => c.Name == nameof(Foo.Name)).Visible); + Assert.True(clientState.Columns.Single(c => c.Name == nameof(Foo.Address)).Visible); } [Fact] From 7ba8225342c6244cee679c9a1f3e4a873722e359 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 30 Oct 2026 11:36:23 +0800 Subject: [PATCH 6/6] chore: bump version 11.1.0-rc.3.4 --- src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 5362856e581..3be134664f5 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 11.1.0-rc.3.3 + 11.1.0-rc.3.4