Skip to content

Commit 9d21ce8

Browse files
authored
[DataGrid] Provide new way to render column actions (#2586)
* Store progress * Use records for string parameter collections * Tabbing error 🤦‍♂️ * Move and change column 'label resources' records * Process PR review comments
1 parent 139c1c1 commit 9d21ce8

15 files changed

+442
-28
lines changed

examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,56 @@
13321332
Constructs an instance of <see cref="T:Microsoft.FluentUI.AspNetCore.Components.ColumnBase`1" />.
13331333
</summary>
13341334
</member>
1335+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnOptionsLabels.OptionsMenu">
1336+
<summary>
1337+
Gets or sets the text shown in the column menu
1338+
</summary>
1339+
</member>
1340+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnOptionsLabels.Default">
1341+
<summary>
1342+
Gets the default labels for the options UI.
1343+
</summary>
1344+
</member>
1345+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.ResizeMenu">
1346+
<summary>
1347+
Gets or sets the text shown in the column menu
1348+
</summary>
1349+
</member>
1350+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.DiscreteLabel">
1351+
<summary>
1352+
Gets or sets the label in the discrete mode resize UI
1353+
</summary>
1354+
</member>
1355+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.ExactLabel">
1356+
<summary>
1357+
Gets or sets the label in the exact mode resize UI
1358+
</summary>
1359+
</member>
1360+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.GrowAriaLabel">
1361+
<summary>
1362+
Gets or sets the aria label for the grow button in the discrete resize UI
1363+
</summary>
1364+
</member>
1365+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.ShrinkAriaLabel">
1366+
<summary>
1367+
Gets or sets the aria label for the shrink button in the discrete resize UI
1368+
</summary>
1369+
</member>
1370+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.ResetAriaLabel">
1371+
<summary>
1372+
Gets or sets the aria label for the reset button in the resize UI
1373+
</summary>
1374+
</member>
1375+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.SubmitAriaLabel">
1376+
<summary>
1377+
Gets or sets the aria label for the submit button in the resize UI
1378+
</summary>
1379+
</member>
1380+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeLabels.Default">
1381+
<summary>
1382+
Gets the default labels for the resize UI.
1383+
</summary>
1384+
</member>
13351385
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnResizeOptions`1.Grid">
13361386
<summary>
13371387
Gets a reference to the enclosing <see cref="T:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1" />.
@@ -1350,6 +1400,26 @@
13501400
The display of this component is dependant on a ResizeType being set
13511401
</summary>
13521402
</member>
1403+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnSortLabels.SortMenu">
1404+
<summary>
1405+
Gets or sets the text shown in the column menu
1406+
</summary>
1407+
</member>
1408+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnSortLabels.SortMenuAscendingLabel">
1409+
<summary>
1410+
Gets or sets the text shown in the column menu when in ascending order
1411+
</summary>
1412+
</member>
1413+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnSortLabels.SortMenuDescendingLabel">
1414+
<summary>
1415+
Gets or sets the text shown in the column menu when in descending order
1416+
</summary>
1417+
</member>
1418+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.ColumnSortLabels.Default">
1419+
<summary>
1420+
Gets the default labels for the sort UI.
1421+
</summary>
1422+
</member>
13531423
<member name="T:Microsoft.FluentUI.AspNetCore.Components.GridSort`1">
13541424
<summary>
13551425
Represents a sort order specification used within <see cref="T:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1"/>.
@@ -1794,6 +1864,21 @@
17941864
(Aria) Labels used in the column resize UI.
17951865
</summary>
17961866
</member>
1867+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.ColumnSortLabels">
1868+
<summary>
1869+
Labels used in the column sort UI.
1870+
</summary>
1871+
</member>
1872+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.ColumnOptionsLabels">
1873+
<summary>
1874+
Labels used in the column options UI.
1875+
</summary>
1876+
</member>
1877+
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.HeaderCellAsButtonWithMenu">
1878+
<summary>
1879+
If true, enables the new style of header cell that includes a button to display all column options through a menu.
1880+
</summary>
1881+
</member>
17971882
<member name="P:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.ItemKey">
17981883
<summary>
17991884
Optionally defines a value for @key on each rendered row. Typically this should be used to specify a
@@ -1948,6 +2033,13 @@
19482033
</summary>
19492034
<param name="column">The column whose options are to be displayed, if any are available.</param>
19502035
</member>
2036+
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.ShowColumnResizeAsync(Microsoft.FluentUI.AspNetCore.Components.ColumnBase{`0})">
2037+
<summary>
2038+
Displays the column resize UI for the specified column, closing any other column
2039+
resize UI that was previously displayed.
2040+
</summary>
2041+
<param name="column">The column whose resize UI is to be displayed.</param>
2042+
</member>
19512043
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentDataGrid`1.RefreshDataAsync">
19522044
<summary>
19532045
Instructs the grid to re-fetch and render the current data from the supplied data source

examples/Demo/Shared/Pages/DataGrid/Examples/DataGridTypical.razor

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
GridTemplateColumns="0.2fr 1fr 0.2fr 0.2fr 0.2fr 0.2fr"
1212
RowClass="@rowClass"
1313
RowStyle="@rowStyle"
14+
HeaderCellAsButtonWithMenu="true"
1415
Style="height: 405px;overflow:auto;"
1516
ColumnResizeLabels="@customLabels">
1617
<TemplateColumn Tooltip="true" TooltipText="@(c => "Flag of " + c.Name)" Title="Rank" SortBy="@rankSort" Align="Align.Center" InitialSortDirection="SortDirection.Ascending" IsDefaultSortColumn=true>
@@ -25,8 +26,8 @@
2526
</PropertyColumn>
2627
<PropertyColumn Property="@(c => c.Medals.Gold)" Sortable="true" Align="Align.Start" Tooltip="true" TooltipText="@(c => "That is " + c.Medals.Gold + " x GOLD!!")" />
2728
<PropertyColumn Property="@(c => c.Medals.Silver)" Sortable="true" Align="Align.Center" Tooltip="true" />
28-
<PropertyColumn Property="@(c => c.Medals.Bronze)" Sortable="true" Align="Align.End" />
29-
<PropertyColumn Property="@(c => c.Medals.Total)" Filtered="@(minMedals != 0 || maxMedals != 130)" Align="Align.End" Tooltip="true">
29+
<PropertyColumn Property="@(c => c.Medals.Bronze)" Sortable="false" Align="Align.End" />
30+
<PropertyColumn Property="@(c => c.Medals.Total)" Sortable="true" Filtered="@(minMedals != 0 || maxMedals != 130)" Align="Align.End" Tooltip="true">
3031
<ColumnOptions>
3132
<div style="width: 100%; height: 150px;">
3233
<FluentSlider Label="@($"Min ({minMedals})")" Min="0" Max="150" Step="1" Orientation="Orientation.Horizontal" @bind-Value=minMedals Immediate="true" Style="width: 100%;">
@@ -64,7 +65,7 @@
6465
int minMedals;
6566
int maxMedals = 130;
6667

67-
ColumnResizeLabels customLabels = new ColumnResizeLabels(DiscreteLabel: "Width (+/- 10px)", ResetAriaLabel: "restore");
68+
ColumnResizeLabels customLabels = ColumnResizeLabels.Default with { DiscreteLabel = "Width (+/- 10px)", ResetAriaLabel = "Restore" };
6869

6970
GridSort<Country> rankSort = GridSort<Country>
7071
.ByDescending(x => x.Medals.Gold)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
@page "/issue-tester"
2+

src/Core/Components/DataGrid/Columns/ColumnBase.razor

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,53 @@
1313
{
1414
@HeaderCellItemTemplate(this)
1515
}
16+
else if (Grid.HeaderCellAsButtonWithMenu)
17+
{
18+
string? tooltip = Tooltip ? Title : null;
19+
20+
<FluentKeyCode Only="new [] { KeyCode.Ctrl, KeyCode.Enter}" OnKeyDown="HandleKeyDown" class="keycapture">
21+
<span class="col-sort-container" @oncontextmenu="@(() => Grid.RemoveSortByColumnAsync(this))" @oncontextmenu:preventDefault>
22+
<FluentButton Disabled="@(!AnyColumnActionEnabled)" Id="@_columnId" Appearance="Appearance.Stealth" class="col-sort-button" @onclick="@HandleColumnHeaderClickedAsync" aria-label="@tooltip" title="@tooltip">
23+
<div class="col-title-text" title="@tooltip">@Title</div>
24+
25+
@if (Grid.SortByAscending.HasValue && ShowSortIcon)
26+
{
27+
if (Grid.SortByAscending == true)
28+
{
29+
<FluentIcon Value="@(new CoreIcons.Regular.Size24.ArrowSortUp())" Slot="@(Align == Align.End ? "start" : "end")" Style="opacity: 0.5;" />
30+
}
31+
else
32+
{
33+
<FluentIcon Value="@(new CoreIcons.Regular.Size24.ArrowSortDown())" Slot="@(Align == Align.End ? "start" : "end")" Style="opacity: 0.5;" />
34+
}
35+
}
36+
@if (Grid.ResizeType is not null && ColumnOptions is not null)
37+
{
38+
@if (Filtered.GetValueOrDefault())
39+
{
40+
<FluentIcon Value="@(new CoreIcons.Regular.Size24.Filter())" Slot="@(Align == Align.End ? "start" : "end")" Style="opacity: 0.5;" />
41+
}
42+
}
43+
</FluentButton>
44+
<FluentMenu Anchor="@_columnId" @bind-Open="@_isMenuOpen" HorizontalViewportLock="false" HorizontalPosition="HorizontalPosition.End">
45+
@if (Sortable.HasValue ? Sortable.Value : IsSortableByDefault())
46+
{
47+
<FluentMenuItem OnClick="@(async () => await Grid.SortByColumnAsync(this))" @onkeydown="HandleSortMenuKeyDownAsync">
48+
@GetSortOptionText()
49+
</FluentMenuItem>
50+
}
51+
@if (Grid.ResizeType is not null && Grid.ResizableColumns)
52+
{
53+
<FluentMenuItem OnClick="@(async () => await Grid.ShowColumnResizeAsync(this))" @onkeydown="HandleResizeMenuKeyDownAsync">@Grid.ColumnResizeLabels.ResizeMenu</FluentMenuItem>
54+
}
55+
@if (ColumnOptions is not null)
56+
{
57+
<FluentMenuItem OnClick="@(async () => await Grid.ShowColumnOptionsAsync(this))" @onkeydown="HandleOptionsMenuKeyDownAsync">@Grid.ColumnOptionsLabels.OptionsMenu</FluentMenuItem>
58+
}
59+
</FluentMenu>
60+
</span>
61+
</FluentKeyCode>
62+
}
1663
else
1764
{
1865
string? tooltip = Tooltip ? Title : null;

src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ namespace Microsoft.FluentUI.AspNetCore.Components;
1212
/// <typeparam name="TGridItem">The type of data represented by each row in the grid.</typeparam>
1313
public abstract partial class ColumnBase<TGridItem>
1414
{
15+
private bool _isMenuOpen;
16+
private static readonly string[] KEYBOARD_MENU_SELECT_KEYS = ["Enter", "NumpadEnter"];
17+
private readonly string _columnId = $"column-header{Identifier.NewId()}";
18+
1519
[CascadingParameter]
1620
internal InternalGridContext<TGridItem> InternalGridContext { get; set; } = default!;
1721

@@ -128,6 +132,8 @@ public abstract partial class ColumnBase<TGridItem>
128132
/// </summary>
129133
protected FluentDataGrid<TGridItem> Grid => InternalGridContext.Grid;
130134

135+
protected bool AnyColumnActionEnabled => Sortable is true || IsDefaultSortColumn || ColumnOptions != null || Grid.ResizableColumns;
136+
131137
/// <summary>
132138
/// Event callback for when the row is clicked.
133139
/// </summary>
@@ -218,4 +224,65 @@ public ColumnBase()
218224
{
219225
HeaderContent = RenderDefaultHeaderContent;
220226
}
227+
228+
private async Task HandleColumnHeaderClickedAsync()
229+
{
230+
if ((Sortable is true || IsDefaultSortColumn) && (Grid.ResizableColumns || ColumnOptions is not null))
231+
{
232+
_isMenuOpen = !_isMenuOpen;
233+
}
234+
else if ((Sortable is true || IsDefaultSortColumn) && !Grid.ResizableColumns && ColumnOptions is null)
235+
{
236+
await Grid.SortByColumnAsync(this);
237+
}
238+
else if (Sortable is not true && !IsDefaultSortColumn && ColumnOptions is null && Grid.ResizableColumns)
239+
{
240+
await Grid.ShowColumnResizeAsync(this);
241+
}
242+
}
243+
244+
private async Task HandleSortMenuKeyDownAsync(KeyboardEventArgs args)
245+
{
246+
if (KEYBOARD_MENU_SELECT_KEYS.Contains(args.Key))
247+
{
248+
await Grid.SortByColumnAsync(this);
249+
StateHasChanged();
250+
_isMenuOpen = false;
251+
}
252+
}
253+
254+
private async Task HandleResizeMenuKeyDownAsync(KeyboardEventArgs args)
255+
{
256+
if (KEYBOARD_MENU_SELECT_KEYS.Contains(args.Key))
257+
{
258+
await Grid.ShowColumnResizeAsync(this);
259+
_isMenuOpen = false;
260+
}
261+
}
262+
263+
private async Task HandleOptionsMenuKeyDownAsync(KeyboardEventArgs args)
264+
{
265+
if (KEYBOARD_MENU_SELECT_KEYS.Contains(args.Key))
266+
{
267+
await Grid.ShowColumnOptionsAsync(this);
268+
_isMenuOpen = false;
269+
}
270+
}
271+
272+
private string GetSortOptionText()
273+
{
274+
if (Grid.SortByAscending.HasValue && ShowSortIcon)
275+
{
276+
if (Grid.SortByAscending is true)
277+
{
278+
return Grid.ColumnSortLabels.SortMenuAscendingLabel;
279+
}
280+
else
281+
{
282+
return Grid.ColumnSortLabels.SortMenuDescendingLabel;
283+
}
284+
}
285+
286+
return Grid.ColumnSortLabels.SortMenu;
287+
}
221288
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
namespace Microsoft.FluentUI.AspNetCore.Components;
5+
6+
public record ColumnOptionsLabels
7+
{
8+
/// <summary>
9+
/// Gets or sets the text shown in the column menu
10+
/// </summary>
11+
public string OptionsMenu { get; set; } = "Filter";
12+
13+
/// <summary>
14+
/// Gets the default labels for the options UI.
15+
/// </summary>
16+
public static ColumnOptionsLabels Default => new();
17+
18+
}
19+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
namespace Microsoft.FluentUI.AspNetCore.Components;
6+
7+
public record ColumnResizeLabels
8+
{
9+
/// <summary>
10+
/// Gets or sets the text shown in the column menu
11+
/// </summary>
12+
public string ResizeMenu { get; set; } = "Resize";
13+
14+
/// <summary>
15+
/// Gets or sets the label in the discrete mode resize UI
16+
/// </summary>
17+
public string DiscreteLabel { get; set; } = "Column width";
18+
19+
/// <summary>
20+
/// Gets or sets the label in the exact mode resize UI
21+
/// </summary>
22+
public string ExactLabel { get; set; } = "Column width (in pixels)";
23+
24+
/// <summary>
25+
/// Gets or sets the aria label for the grow button in the discrete resize UI
26+
/// </summary>
27+
public string? GrowAriaLabel { get; set; } = "Grow column width";
28+
29+
/// <summary>
30+
/// Gets or sets the aria label for the shrink button in the discrete resize UI
31+
/// </summary>
32+
public string? ShrinkAriaLabel { get; set; } = "Shrink column width";
33+
34+
/// <summary>
35+
/// Gets or sets the aria label for the reset button in the resize UI
36+
/// </summary>
37+
public string? ResetAriaLabel { get; set; } = "Reset column widths";
38+
39+
/// <summary>
40+
/// Gets or sets the aria label for the submit button in the resize UI
41+
/// </summary>
42+
public string? SubmitAriaLabel { get; set; } = "Set column widths";
43+
44+
/// <summary>
45+
/// Gets the default labels for the resize UI.
46+
/// </summary>
47+
public static ColumnResizeLabels Default => new();
48+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
namespace Microsoft.FluentUI.AspNetCore.Components;
5+
6+
public record ColumnSortLabels
7+
{
8+
/// <summary>
9+
/// Gets or sets the text shown in the column menu
10+
/// </summary>
11+
public string SortMenu { get; set; } = "Sort";
12+
13+
/// <summary>
14+
/// Gets or sets the text shown in the column menu when in ascending order
15+
/// </summary>
16+
public string SortMenuAscendingLabel { get; set; } = "Sort (ascending)";
17+
18+
/// <summary>
19+
/// Gets or sets the text shown in the column menu when in descending order
20+
/// </summary>
21+
public string SortMenuDescendingLabel { get; set; } = "Sort (descending)";
22+
23+
/// <summary>
24+
/// Gets the default labels for the sort UI.
25+
/// </summary>
26+
public static ColumnSortLabels Default => new();
27+
28+
}
29+

0 commit comments

Comments
 (0)