diff --git a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
index 9d717562c4..02aa1d20ff 100644
--- a/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
+++ b/examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml
@@ -1245,6 +1245,36 @@
Gets a reference to the enclosing .
+
+
+ Event callback for when the row is clicked.
+
+
+
+
+
+
+ Event callback for when the key is pressed on a row.
+
+
+
+
+
+
+
+ Event callback for when the cell is clicked.
+
+
+
+
+
+
+ Event callback for when the key is pressed on a cell.
+
+
+
+
+
Overridden by derived components to provide rendering logic for the column's cells.
@@ -1558,6 +1588,36 @@
Allows to clear the selection.
+
+
+ Select on Unselect an item when the row is clicked.
+
+
+
+
+
+
+ Select on Unselect an item when the navigation keys are pressed.
+
+
+
+
+
+
+
+ Select on Unselect an item when the cell is clicked.
+
+
+
+
+
+
+ Select on Unselect an item when the navigation keys are pressed.
+
+
+
+
+
@@ -1585,6 +1645,9 @@
+
+
+
Holds the name of a property and the direction to sort by.
@@ -1773,11 +1836,6 @@
A default fragment is used if loading content is not specified.
-
-
- Gets the first (optional) SelectColumn
-
-
Constructs an instance of .
@@ -5450,6 +5508,9 @@
+
+
+
diff --git a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
index 40da078af5..c2796d96b7 100644
--- a/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
+++ b/src/Core/Components/DataGrid/Columns/ColumnBase.razor.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
+using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.FluentUI.AspNetCore.Components.DataGrid.Infrastructure;
@@ -11,48 +12,56 @@ namespace Microsoft.FluentUI.AspNetCore.Components;
/// The type of data represented by each row in the grid.
public abstract partial class ColumnBase
{
- [CascadingParameter] internal InternalGridContext InternalGridContext { get; set; } = default!;
+ [CascadingParameter]
+ internal InternalGridContext InternalGridContext { get; set; } = default!;
///
/// Gets or sets the title text for the column.
/// This is rendered automatically if is not used.
///
- [Parameter] public string? Title { get; set; }
+ [Parameter]
+ public string? Title { get; set; }
///
/// Gets or sets the an optional CSS class name.
/// If specified, this is included in the class attribute of header and grid cells
/// for this column.
///
- [Parameter] public string? Class { get; set; }
+ [Parameter]
+ public string? Class { get; set; }
///
/// Gets or sets an optional CSS style specification.
/// If specified, this is included in the style attribute of header and grid cells
/// for this column.
///
- [Parameter] public string? Style { get; set; }
+ [Parameter]
+ public string? Style { get; set; }
///
/// If specified, controls the justification of header and grid cells for this column.
///
- [Parameter] public Align Align { get; set; }
+ [Parameter]
+ public Align Align { get; set; }
///
/// If true, generates a title and aria-label attribute for the cell contents
///
- [Parameter] public bool Tooltip { get; set; } = false;
+ [Parameter]
+ public bool Tooltip { get; set; } = false;
///
/// Gets or sets the value to be used as the tooltip and aria-label in this column's cells
///
- [Parameter] public Func? TooltipText { get; set; }
+ [Parameter]
+ public Func? TooltipText { get; set; }
///
/// Gets or sets an optional template for this column's header cell.
/// If not specified, the default header template includes the along with any applicable sort indicators and options buttons.
///
- [Parameter] public RenderFragment>? HeaderCellItemTemplate { get; set; }
+ [Parameter]
+ public RenderFragment>? HeaderCellItemTemplate { get; set; }
///
/// If specified, indicates that this column has this associated options UI. A button to display this
@@ -61,7 +70,8 @@ public abstract partial class ColumnBase
/// If is used, it is left up to that template to render any relevant
/// "show options" UI and invoke the grid's ).
///
- [Parameter] public RenderFragment? ColumnOptions { get; set; }
+ [Parameter]
+ public RenderFragment? ColumnOptions { get; set; }
///
/// Gets or sets a value indicating whether the data should be sortable by this column.
@@ -70,14 +80,16 @@ public abstract partial class ColumnBase
/// or is sortable by default if any
/// or parameter is specified).
///
- [Parameter] public bool? Sortable { get; set; }
+ [Parameter]
+ public bool? Sortable { get; set; }
///
/// Gets or sets a value indicating whether the data is currently filtered by this column.
///
/// The default value is false.
///
- [Parameter] public bool? Filtered { get; set; }
+ [Parameter]
+ public bool? Filtered { get; set; }
///
/// Gets or sets the sorting rules for a column.
@@ -88,29 +100,75 @@ public abstract partial class ColumnBase
/// Gets or sets the initial sort direction.
/// if is true.
///
- [Parameter] public SortDirection InitialSortDirection { get; set; } = default;
+ [Parameter]
+ public SortDirection InitialSortDirection { get; set; } = default;
///
/// Gets or sets a value indicating whether this column should be sorted by default.
///
- [Parameter] public bool IsDefaultSortColumn { get; set; } = false;
+ [Parameter]
+ public bool IsDefaultSortColumn { get; set; } = false;
///
/// If specified, virtualized grids will use this template to render cells whose data has not yet been loaded.
///
- [Parameter] public RenderFragment? PlaceholderTemplate { get; set; }
+ [Parameter]
+ public RenderFragment? PlaceholderTemplate { get; set; }
///
/// Gets or sets the width of the column.
/// Use either this or the GridTemplateColumns parameter but not both.
/// Needs to be a valid CSS width value like '100px', '10%' or '0.5fr'.
///
- [Parameter] public string? Width { get; set; }
+ [Parameter]
+ public string? Width { get; set; }
///
/// Gets a reference to the enclosing .
///
- public FluentDataGrid Grid => InternalGridContext.Grid;
+ internal FluentDataGrid Grid => InternalGridContext.Grid;
+
+ ///
+ /// Event callback for when the row is clicked.
+ ///
+ ///
+ ///
+ protected internal virtual Task OnRowClickAsync(FluentDataGridRow row)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Event callback for when the key is pressed on a row.
+ ///
+ ///
+ ///
+ ///
+ protected internal virtual Task OnRowKeyDownAsync(FluentDataGridRow row, KeyboardEventArgs args)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Event callback for when the cell is clicked.
+ ///
+ ///
+ ///
+ protected internal virtual Task OnCellClickAsync(FluentDataGridCell cell)
+ {
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Event callback for when the key is pressed on a cell.
+ ///
+ ///
+ ///
+ ///
+ protected internal virtual Task OnCellKeyDownAsync(FluentDataGridCell cell, KeyboardEventArgs args)
+ {
+ return Task.CompletedTask;
+ }
///
/// Overridden by derived components to provide rendering logic for the column's cells.
diff --git a/src/Core/Components/DataGrid/Columns/SelectColumn.cs b/src/Core/Components/DataGrid/Columns/SelectColumn.cs
index 24882ac088..2535a7c73d 100644
--- a/src/Core/Components/DataGrid/Columns/SelectColumn.cs
+++ b/src/Core/Components/DataGrid/Columns/SelectColumn.cs
@@ -200,13 +200,76 @@ public void ClearSelection()
///
public async Task ClearSelectionAsync()
{
- _selectedItems.Clear();
- RefreshHeaderContent();
+ ClearSelection();
await Task.CompletedTask;
}
+ ///
+ /// Select on Unselect an item when the row is clicked.
+ ///
+ ///
+ ///
+ protected internal override Task OnRowClickAsync(FluentDataGridRow row)
+ {
+ if (SelectFromEntireRow == true && row.RowType == DataGridRowType.Default)
+ {
+ return AddOrRemoveSelectedItemAsync(row.Item);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Select on Unselect an item when the navigation keys are pressed.
+ ///
+ ///
+ ///
+ ///
+ protected internal override Task OnRowKeyDownAsync(FluentDataGridRow row, KeyboardEventArgs args)
+ {
+ if (SelectFromEntireRow == true && row.RowType == DataGridRowType.Default)
+ {
+ return AddOrRemoveSelectedItemAsync(row.Item);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Select on Unselect an item when the cell is clicked.
+ ///
+ ///
+ ///
+ protected internal override Task OnCellClickAsync(FluentDataGridCell cell)
+ {
+ // If the cell is a checkbox cell, add or remove the item from the selected items list.
+ if (SelectFromEntireRow == false && cell.CellType == DataGridCellType.Default)
+ {
+ return AddOrRemoveSelectedItemAsync(cell.Item);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Select on Unselect an item when the navigation keys are pressed.
+ ///
+ ///
+ ///
+ ///
+ protected internal override Task OnCellKeyDownAsync(FluentDataGridCell cell, KeyboardEventArgs args)
+ {
+ // If the cell is a checkbox cell, add or remove the item from the selected items list.
+ if (SelectFromEntireRow == false && cell.CellType == DataGridCellType.Default)
+ {
+ return AddOrRemoveSelectedItemAsync(cell.Item);
+ }
+
+ return Task.CompletedTask;
+ }
+
///
- internal async Task AddOrRemoveSelectedItemAsync(TGridItem? item)
+ private async Task AddOrRemoveSelectedItemAsync(TGridItem? item)
{
if (item != null)
{
@@ -348,9 +411,10 @@ private RenderFragment GetHeaderContent()
if (!SelectAllDisabled)
{
builder.AddAttribute(3, "OnClick", EventCallback.Factory.Create(this, OnClickAllAsync));
+ builder.AddAttribute(4, "onkeydown", EventCallback.Factory.Create(this, OnKeyAllAsync));
}
- builder.AddAttribute(4, "Style", "margin-left: 12px;");
- builder.AddAttribute(5, "Title", iconAllChecked == IconIndeterminate
+ builder.AddAttribute(5, "Style", "margin-left: 12px;");
+ builder.AddAttribute(6, "Title", iconAllChecked == IconIndeterminate
? TitleAllIndeterminate
: (iconAllChecked == GetIcon(true) ? TitleAllChecked : TitleAllUnchecked));
builder.CloseComponent();
@@ -377,8 +441,9 @@ private void RefreshHeaderContent()
{
builder.AddAttribute(1, "style", "cursor: pointer; margin-left: 12px;");
builder.AddAttribute(2, "onclick", EventCallback.Factory.Create(this, OnClickAllAsync));
+ builder.AddAttribute(3, "onkeydown", EventCallback.Factory.Create(this, OnKeyAllAsync));
}
- builder.AddContent(3, SelectAllTemplate.Invoke(new SelectAllTemplateArgs(GetSelectAll())));
+ builder.AddContent(4, SelectAllTemplate.Invoke(new SelectAllTemplateArgs(GetSelectAll())));
builder.CloseElement();
});
}
@@ -451,6 +516,15 @@ internal async Task OnClickAllAsync(MouseEventArgs e)
RefreshHeaderContent();
}
+
+ ///
+ internal async Task OnKeyAllAsync(KeyboardEventArgs e)
+ {
+ if (KEYBOARD_SELECT_KEYS.Contains(e.Code))
+ {
+ await OnClickAllAsync(new MouseEventArgs());
+ }
+ }
}
public record SelectAllTemplateArgs(bool? AllSelected) { }
diff --git a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
index 04917057be..1a58f58228 100644
--- a/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGrid.razor.cs
@@ -178,11 +178,6 @@ public partial class FluentDataGrid : FluentComponentBase, IHandleEve
[Inject] private IJSRuntime JSRuntime { get; set; } = default!;
[Inject] private IKeyCodeService KeyCodeService { get; set; } = default!;
- ///
- /// Gets the first (optional) SelectColumn
- ///
- internal IEnumerable> SelectColumns => _columns.Where(col => col is SelectColumn).Cast< SelectColumn>();
-
private ElementReference? _gridReference;
private Virtualize<(int, TGridItem)>? _virtualizeComponent;
diff --git a/src/Core/Components/DataGrid/FluentDataGridCell.razor b/src/Core/Components/DataGrid/FluentDataGridCell.razor
index e94605961d..a05de6cc31 100644
--- a/src/Core/Components/DataGrid/FluentDataGridCell.razor
+++ b/src/Core/Components/DataGrid/FluentDataGridCell.razor
@@ -6,6 +6,7 @@
grid-column=@GridColumn
class="@Class"
style="@StyleValue"
+ @onkeydown="@HandleOnCellKeyDownAsync"
@onclick="@HandleOnCellClickAsync"
@attributes="AdditionalAttributes">
@ChildContent
diff --git a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
index ad3fa3cada..c81423a886 100644
--- a/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGridCell.razor.cs
@@ -3,6 +3,7 @@
// ------------------------------------------------------------------------
using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
using Microsoft.FluentUI.AspNetCore.Components.DataGrid.Infrastructure;
using Microsoft.FluentUI.AspNetCore.Components.Utilities;
@@ -72,11 +73,22 @@ internal async Task HandleOnCellClickAsync()
await GridContext.Grid.OnCellClick.InvokeAsync(this);
}
- // If the cell is a checkbox cell, add or remove the item from the selected items list.
- var selectColumn = Column as SelectColumn;
- if (CellType == DataGridCellType.Default && selectColumn != null && selectColumn.SelectFromEntireRow == false)
+ if (Column != null)
{
- await selectColumn.AddOrRemoveSelectedItemAsync(Item);
+ await Column.OnCellClickAsync(this);
+ }
+ }
+
+ internal async Task HandleOnCellKeyDownAsync(KeyboardEventArgs e)
+ {
+ if (!SelectColumn.KEYBOARD_SELECT_KEYS.Contains(e.Code))
+ {
+ return;
+ }
+
+ if (Column != null)
+ {
+ await Column.OnCellKeyDownAsync(this, e);
}
}
diff --git a/src/Core/Components/DataGrid/FluentDataGridRow.razor.cs b/src/Core/Components/DataGrid/FluentDataGridRow.razor.cs
index 3051b6f0f3..bfcd3de9e6 100644
--- a/src/Core/Components/DataGrid/FluentDataGridRow.razor.cs
+++ b/src/Core/Components/DataGrid/FluentDataGridRow.razor.cs
@@ -103,19 +103,18 @@ private async Task HandleOnCellFocusAsync(DataGridCellFocusEventArgs args)
///
internal async Task HandleOnRowClickAsync(string rowId)
{
- if (Owner.Rows.TryGetValue(rowId, out var row))
+ var row = GetRow(rowId);
+
+ if (row != null && Owner.Grid.OnRowClick.HasDelegate)
{
- if (Owner.Grid.OnRowClick.HasDelegate)
- {
- await Owner.Grid.OnRowClick.InvokeAsync(row);
- }
+ await Owner.Grid.OnRowClick.InvokeAsync(row);
+ }
- if (row != null && row.RowType == DataGridRowType.Default)
+ if (row != null && row.RowType == DataGridRowType.Default)
+ {
+ foreach (var column in Owner.Grid._columns)
{
- foreach (var selColumn in Owner.Grid.SelectColumns.Where(i => i.SelectFromEntireRow))
- {
- await selColumn.AddOrRemoveSelectedItemAsync(Item);
- }
+ await column.OnRowClickAsync(row);
}
}
}
@@ -123,34 +122,43 @@ internal async Task HandleOnRowClickAsync(string rowId)
///
internal async Task HandleOnRowDoubleClickAsync(string rowId)
{
- if (Owner.Rows.TryGetValue(rowId, out var row))
+ var row = GetRow(rowId);
+ if (row != null && Owner.Grid.OnRowDoubleClick.HasDelegate)
{
- if (Owner.Grid.OnRowDoubleClick.HasDelegate)
- {
- await Owner.Grid.OnRowDoubleClick.InvokeAsync(row);
- }
+ await Owner.Grid.OnRowDoubleClick.InvokeAsync(row);
}
}
///
internal async Task HandleOnRowKeyDownAsync(string rowId, KeyboardEventArgs e)
{
- // Enter when a SelectColumn is defined.
- if (SelectColumn.KEYBOARD_SELECT_KEYS.Contains(e.Code))
+ if (!SelectColumn.KEYBOARD_SELECT_KEYS.Contains(e.Code))
+ {
+ return;
+ }
+
+ var row = GetRow(rowId, r => r.RowType == DataGridRowType.Default);
+ if (row != null)
{
- if (Owner.Rows.TryGetValue(rowId, out var row))
+ foreach (var column in Owner.Grid._columns)
{
- if (row != null && row.RowType == DataGridRowType.Default)
- {
- foreach (var selColumn in Owner.Grid.SelectColumns)
- {
- await selColumn.AddOrRemoveSelectedItemAsync(Item);
- }
- }
+ await column.OnRowKeyDownAsync(row, e);
}
}
}
+ private FluentDataGridRow? GetRow(string rowId, Func, bool>? where = null)
+ {
+ if (!string.IsNullOrEmpty(rowId) && Owner.Rows.TryGetValue(rowId, out var row))
+ {
+ return where == null
+ ? row
+ : row is not null && where(row) ? row : null;
+ }
+
+ return null;
+ }
+
///
Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
=> callback.InvokeAsync(arg);