Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

<h4>@Localizer["TablesColumnDescription"]</h4>


<DemoBlock Title="@Localizer["AllowDragOrderTitle"]" Introduction="@Localizer["AllowDragOrderIntro"]" Name="AllowDragColumn">
<section ignore>
<p>@((MarkupString)Localizer["AllowDragOrderDesc"].Value)</p>
Expand All @@ -22,10 +21,12 @@
IsMultipleSelect="true" ShowExtendButtons="false"
OnQueryAsync="@OnQueryAsync">
<TableColumns>
<TableColumn @bind-Field="@context.DateTime" Width="120" FormatString="yyyy-MM-dd" />
<TableColumn @bind-Field="@context.Name" Width="100" />
<TableColumn @bind-Field="@context.DateTime" Width="120" FormatString="yyyy-MM-dd" Fixed="true" />
<TableColumn @bind-Field="@context.Name" Width="100" Fixed="true" />
<TableColumn @bind-Field="@context.Address" />
<TableColumn @bind-Field="@context.Count" />
<TableColumn @bind-Field="@context.Complete" />
<TableColumn @bind-Field="@context.Education" />
<TableColumn @bind-Field="@context.Count" Fixed="true" />
</TableColumns>
<TableToolbarTemplate>
<TableToolbarButton Text="@Localizer["ResetButtonText"]" OnClickWithoutRender="Reset"></TableToolbarButton>
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -4639,7 +4639,7 @@
},
"BootstrapBlazor.Server.Components.Samples.Table.TablesColumnDrag": {
"AllowDragOrderDesc": "<p>Pressing the mouse over a column heading and dragging it to another column heading position can adjust the column to be in front of the target column, but the built-in columns in the <code>Table</code> component, such as detail row, row number, selection, and operation columns, cannot be adjusted</p><p>This example enables local storage by setting the <code>ClientTableName</code> parameter. After dragging and adjusting the order, the page can be refreshed, and the column order remains in the previous state</p><p>After version <code>10.6.1</code>, use <code>OnTableColumnClientStatusChanged</code> to uniformly handle column dragging, column resizing, and auto-fit column width callbacks, replacing the original <code>OnDragColumnEndAsync</code>, <code>OnResizeColumnAsync</code>, and <code>OnAutoFitContentAsync</code> callbacks.</p><p><b>Notes:</b></p><p>Table state persistence has two modes. If the <code>ClientTableName</code> property is set, browser local storage is used to persist information. If it is not set, server-side storage persistence is implemented through callbacks. The persistence structure is <code>TableColumnClientStatus</code>, which consists of the column state collection and the table width parameter.</p><p>The table width parameter defaults to 0. When all columns have width values, the table width parameter is the actual width rendered on the client. A value of 0 means the table width is adaptive.</p><p>When using browser local storage for persistence, no additional settings are required. The table automatically loads the client state.</p><p>When using server-side storage for persistence, handle table state loading and saving logic in callbacks. In actual use, the state can be stored in a database or other storage as needed. Use the <code>OnTableColumnClientStatusChanged</code> callback to handle server-side state changes, and use the <code>OnLoadTableColumnClientStatus</code> callback to restore the table persistence state.</p>",
"AllowDragOrderIntro": "By specifying <code>AllowDragColumn</code>, set the table columns to allow dragging column headings to adjust the table column order",
"AllowDragOrderIntro": "By specifying <code>AllowDragColumn</code>, set the table columns to allow dragging column headings to adjust the table column order; The current design only supports dragging non-fixed columns.",
"AllowDragOrderTitle": "Allow dragging column headings to adjust table column order",
"ClearTableColumnClientStatusDesc": "By calling the <code>ClearTableColumnClientStatus</code> method of the table component instance, the current persistent settings are cleared, restoring the table to its default settings.",
"ResetButtonText": "Reset",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4639,7 +4639,7 @@
},
"BootstrapBlazor.Server.Components.Samples.Table.TablesColumnDrag": {
"AllowDragOrderDesc": "<p>在列标题上按下鼠标拖动到其他列标题位置可将该列调整至目标列之前,但 <code>Table</code> 组件内置的列如明细行列、行号列、选择列、操作列等不可被调整</p><p>本示例通过设置 <code>ClientTableName</code> 参数开启了本地化存储,拖动调整顺序后,可刷新页面,列顺序是保持上次状态的</p><p><code>10.6.1</code> 版本后使用 <code>OnTableColumnClientStatusChanged</code> 统一处理列拖拽、列宽调整与自适应列宽回调,移除原 <code>OnDragColumnEndAsync</code> <code>OnResizeColumnAsync</code> 和 <code>OnAutoFitContentAsync</code> 回调。</p><p><b>注意事项:</b></p><p>表格状态持久化功能分两种情况,设置 <code>ClientTableName</code> 属性即使用浏览器本地存储持久化信息,如果未设置则通过回调使用服务器端存储持久化信息。持久化信息结构体为 <code>TableColumnClientStatus</code> 由列状态集合以及表格宽度参数组成。</p><p>表格宽度参数默认为 0 当所有列宽度均有值时,表格宽度参数值为表格在客户端渲染的实际宽度;为 0 表示表格宽度自适应</p><p>使用浏览器本地存储持久化信息时,无需任何设置,表格会自动加载客户端状态</p><p>使用服务器端存储持久化信息时,需在回调中处理表格状态加载与保存逻辑,实际使用中可根据需要使用数据库或其他方式进行存储;通过 <code>OnTableColumnClientStatusChanged</code> 回调处理服务器端状态,通过 <code>OnLoadTableColumnClientStatus</code> 回调方法对表格进行持久化状态恢复操作</p>",
"AllowDragOrderIntro": "通过指定 <code>AllowDragColumn</code> 设置表格列允许拖动列标题调整表格列顺序",
"AllowDragOrderIntro": "通过指定 <code>AllowDragColumn</code> 设置表格列允许拖动列标题调整表格列顺序;目前设计上仅支持非固定列拖动",
"AllowDragOrderTitle": "允许拖动列标题调整表格列顺序",
"ClearTableColumnClientStatusDesc": "通过调用表格组件实例方法 <code>ClearTableColumnClientStatus</code> 清除当前持久化设置恢复表格为默认设置",
"ResetButtonText": "重置",
Expand Down
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Components/Table/Table.razor
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@
style="@GetFixedCellStyleString(col, ActualScrollWidth)"
data-bb-field="@fieldName"
TriggerClick="col.GetSortable()" OnClick="@OnClickHeader(col)"
draggable="@DraggableString">
draggable="@GetDraggableString(col)">
<div class="@GetHeaderWrapperClassString(col)">
@if (col.GetShowCopyColumn())
{
Expand Down
33 changes: 23 additions & 10 deletions src/BootstrapBlazor/Components/Table/Table.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1870,7 +1870,7 @@ private async Task OnContextMenu(MouseEventArgs e, TItem item)
[Parameter]
public bool AllowDragColumn { get; set; }

private string? DraggableString => AllowDragColumn ? "true" : null;
private string? GetDraggableString(ITableColumn col) => AllowDragColumn && !col.Fixed ? "true" : null;

/// <summary>
/// <para lang="zh">获得/设置 拖动列结束回调方法,默认 null 可存储数据库用于服务器端保持列顺序</para>
Expand Down Expand Up @@ -1959,27 +1959,40 @@ public async Task ClearTableColumnClientStatus()
[JSInvokable]
public async Task DragColumnCallback(int originIndex, int currentIndex)
{
if (!AllowDragColumn || originIndex == currentIndex)
{
return;
}

var draggableColumnNames = GetVisibleColumns().Where(i => !i.Fixed).Select(i => i.GetFieldName()).ToList();
var sourceName = draggableColumnNames.ElementAtOrDefault(originIndex);
var targetName = draggableColumnNames.ElementAtOrDefault(currentIndex);
if (string.IsNullOrEmpty(sourceName) || string.IsNullOrEmpty(targetName))
{
return;
}

// 更新缓存数据中列顺序
var visibleColumns = _tableColumnStates.Where(i => i.Visible);
var firstColumn = visibleColumns.ElementAtOrDefault(originIndex);
var firstColumn = _tableColumnStates.Find(i => i.Name == sourceName);
if (firstColumn != null)
{
var targetColumn = visibleColumns.ElementAtOrDefault(currentIndex);
if (targetColumn != null)
var targetState = _tableColumnStates.Find(i => i.Name == targetName);
if (targetState != null)
{
_tableColumnStates.Remove(firstColumn);
var pos = _tableColumnStates.IndexOf(targetColumn);
var pos = _tableColumnStates.IndexOf(targetState);
_tableColumnStates.Insert(pos, firstColumn);

if (OnTableColumnClientStatusChanged != null)
{
await OnTableColumnClientStatusChanged(firstColumn.Name, _tableColumnStateCache);
}
}
_resetColumns = true;
_invoke = true;

StateHasChanged();
_resetColumns = true;
_invoke = true;

StateHasChanged();
}
}
}

Expand Down
69 changes: 69 additions & 0 deletions test/UnitTest/Components/TableTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8870,6 +8870,75 @@ await cut.InvokeAsync(async () =>
});
}

[Fact]
public async Task AllowDragColumn_FixedColumn_Ok()
{
string? name = null;
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
var cut = Context.Render<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<Table<Foo>>(pb =>
{
pb.Add(a => a.RenderMode, TableRenderMode.Table);
pb.Add(a => a.AllowDragColumn, true);
pb.Add(a => a.Items, Foo.GenerateFoo(localizer, 2));
pb.Add(a => a.OnTableColumnClientStatusChanged, (fieldName, state) =>
{
name = fieldName;
return Task.CompletedTask;
});
pb.Add(a => a.TableColumns, foo => builder =>
{
builder.OpenComponent<TableColumn<Foo, string>>(0);
builder.AddAttribute(1, nameof(TableColumn<Foo, string>.Field), foo.Name);
builder.AddAttribute(2, nameof(TableColumn<Foo, string>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Name), typeof(string)));
builder.AddAttribute(3, nameof(TableColumn<Foo, string>.Fixed), true);
builder.CloseComponent();

builder.OpenComponent<TableColumn<Foo, string>>(4);
builder.AddAttribute(5, nameof(TableColumn<Foo, string>.Field), foo.Address);
builder.AddAttribute(6, nameof(TableColumn<Foo, string>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Address), typeof(string)));
builder.CloseComponent();

builder.OpenComponent<TableColumn<Foo, int>>(7);
builder.AddAttribute(8, nameof(TableColumn<Foo, int>.Field), foo.Count);
builder.AddAttribute(9, nameof(TableColumn<Foo, int>.FieldExpression), Utility.GenerateValueExpression(foo, nameof(Foo.Count), typeof(int)));
builder.CloseComponent();
});
});
});

var table = cut.FindComponent<Table<Foo>>();
var columns = cut.FindAll("th");
Assert.False(columns[0].HasAttribute("draggable"));
Assert.Equal("true", columns[1].GetAttribute("draggable"));
Assert.Equal("true", columns[2].GetAttribute("draggable"));

await cut.InvokeAsync(() => table.Instance.DragColumnCallback(1, 0));
Assert.Equal(nameof(Foo.Count), name);
columns = cut.FindAll("th");
Assert.Equal(nameof(Foo.Name), columns[0].GetAttribute("data-bb-field"));
Assert.Equal(nameof(Foo.Count), columns[1].GetAttribute("data-bb-field"));
Assert.Equal(nameof(Foo.Address), columns[2].GetAttribute("data-bb-field"));

await cut.InvokeAsync(() => table.Instance.DragColumnCallback(1, 0));
Assert.Equal(nameof(Foo.Address), name);
columns = cut.FindAll("th");
Assert.Equal(nameof(Foo.Name), columns[0].GetAttribute("data-bb-field"));
Assert.Equal(nameof(Foo.Address), columns[1].GetAttribute("data-bb-field"));
Assert.Equal(nameof(Foo.Count), columns[2].GetAttribute("data-bb-field"));

// 测试相同列拖动
await cut.InvokeAsync(() => table.Instance.DragColumnCallback(0, 0));

// 更改为不允许拖动
table.Render(pb =>
{
pb.Add(a => a.AllowDragColumn, false);
});
await cut.InvokeAsync(() => table.Instance.DragColumnCallback(1, 0));
}

[Fact]
public async Task OnTableColumnClientStatusChanged_ResizeColumn_Ok()
{
Expand Down
Loading