Skip to content

Commit dff971a

Browse files
Copilotvnbaaij
andauthored
Simplify pinned-column wording, preserve demo page metadata, and remove tracked generated props file (#4673)
* feat: rename DataGridColumnPin Left/Right to Start/End and PinOffsetPx to PinOffset (string) with logical CSS properties Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/705a5b7c-1768-42a0-ba5f-faa99daf78d4 Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * chore: remove accidentally committed build artifact and add obj/ to .gitignore Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/705a5b7c-1768-42a0-ba5f-faa99daf78d4 Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * chore: plan wording updates for start/end docs and comments Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/e324508b-d7f0-40e2-bfec-a2707d42806c Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * docs: simplify DataGrid start/end wording Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/e324508b-d7f0-40e2-bfec-a2707d42806c Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * chore: remove tracked generated props file Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/9d4563a7-5f41-4b62-ab85-0d78d50060b1 Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * docs: restore pinned columns page metadata Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/bbc46172-3499-4123-a119-cc10c435de7f Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * chore: remove regenerated props file Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/bbc46172-3499-4123-a119-cc10c435de7f Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * Fix formatting in DataGridPinnedColumnsPage.md * fix: allow non-pixel pinned column widths Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/4c32bc45-2c71-462f-8fad-b3b8abd4a55a Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> * docs: clarify pinned offset recomputation comments Agent-Logs-Url: https://github.com/microsoft/fluentui-blazor/sessions/4c32bc45-2c71-462f-8fad-b3b8abd4a55a Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: vnbaaij <1761079+vnbaaij@users.noreply.github.com> Co-authored-by: Vincent Baaij <vnbaaij@outlook.com>
1 parent 3787a2a commit dff971a

11 files changed

Lines changed: 195 additions & 193 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,4 @@ all-icons.json
424424
all-emojis.json
425425
/global.json
426426
/src/Core.Scripts/src/BuildConstants.ts
427+
/src/Core.Scripts/obj/

examples/Demo/FluentUI.Demo.Client/Documentation/Components/DataGrid/Examples/DataGridPinnedColumns.razor

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
StripedRows="true"
55
ResizableColumns="true"
66
DisplayMode="DataGridDisplayMode.Grid">
7-
<PropertyColumn Title="ID" Property="@(e => e.Id)" Width="100px" Pin="DataGridColumnPin.Left" Sortable="true" />
8-
<PropertyColumn Title="Full Name" Property="@(e => e.FullName)" Width="160px" Pin="DataGridColumnPin.Left" Sortable="true" />
7+
<PropertyColumn Title="ID" Property="@(e => e.Id)" Width="100px" Pin="DataGridColumnPin.Start" Sortable="true" />
8+
<PropertyColumn Title="Full Name" Property="@(e => e.FullName)" Width="160px" Pin="DataGridColumnPin.Start" Sortable="true" />
99
<PropertyColumn Title="Department" Property="@(e => e.Department)" Sortable="true" />
1010
<PropertyColumn Title="Location" Property="@(e => e.Location)" Sortable="true" />
1111
<PropertyColumn Title="Start Date" Property="@(e => e.StartDate)" Sortable="true" />
1212
<PropertyColumn Title="Salary" Property="@(e => e.Salary)" Width="120px" Sortable="true" Align="DataGridCellAlignment.End" />
13-
<TemplateColumn Title="Actions" Width="120px" Pin="DataGridColumnPin.Right">
13+
<TemplateColumn Title="Actions" Width="120px" Pin="DataGridColumnPin.End">
1414
<FluentButton IconStart="@(new Icons.Regular.Size16.Edit())" Appearance="ButtonAppearance.Subtle" Title="Edit" @onclick="@(() => selectedName = context.FullName + " (edit)")" />
1515
<FluentButton IconStart="@(new Icons.Regular.Size16.Delete())" Appearance="ButtonAppearance.Subtle" Title="Delete" @onclick="@(() => selectedName = context.FullName + " (delete)")" />
1616
</TemplateColumn>
Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
---
22
title: Pinned columns
3-
order: 0095
43
route: /DataGrid/PinnedColumns
54
---
65

76
# Pinned columns
87

9-
Columns can be pinned (frozen) to the left or right edge of the grid so that they remain visible
10-
while the user scrolls horizontally through wider datasets.
8+
Columns can be pinned (frozen) to the start or end edge of the grid so that they remain visible
9+
while the user scrolls horizontally through wider datasets. Using `Start`/`End` instead of
10+
`Left`/`Right` means pinned columns automatically work correctly in both LTR and RTL layouts.
1111

1212
## Parameters
1313

@@ -16,19 +16,19 @@ Set the `Pin` parameter on any `PropertyColumn` or `TemplateColumn`:
1616
| Value | Behavior |
1717
|---|---|
1818
| `DataGridColumnPin.None` | Default — column scrolls normally |
19-
| `DataGridColumnPin.Left` | Column stays anchored to the left edge |
20-
| `DataGridColumnPin.Right` | Column stays anchored to the right edge |
19+
| `DataGridColumnPin.Start` | Column stays anchored to the start edge |
20+
| `DataGridColumnPin.End` | Column stays anchored to the end edge |
2121

2222
## Rules
2323

24-
* **Explicit pixel width required.** Every pinned column must declare a `Width` in pixels
25-
(e.g. `Width="150px"`). Relative units (`fr`, `%`) are not supported because the browser cannot
26-
determine a fixed sticky offset from them at render time.
27-
* **Left-pinned columns must be contiguous at the start.** Each left-pinned column must
28-
immediately follow another left-pinned column, or be the very first column.
29-
* **Right-pinned columns must be contiguous at the end.** Each right-pinned column must
30-
immediately precede another right-pinned column, or be the very last column.
31-
* Violating any of these rules throws an `ArgumentException` with a descriptive message.
24+
* **Explicit width required.** Every pinned column must declare a `Width`.
25+
Pixel and non-pixel CSS units are supported. After the grid renders, sticky offsets are
26+
recomputed from the rendered header widths so pinned columns stay aligned.
27+
* **Start-pinned columns must be contiguous at the start.** Each start-pinned column must
28+
immediately follow another start-pinned column, or be the very first column.
29+
* **End-pinned columns must be contiguous at the end.** Each end-pinned column must
30+
immediately precede another end-pinned column, or be the very last column.
31+
* Violating the missing-width or ordering rules throws an `ArgumentException` with a descriptive message.
3232

3333
## Scrollable container
3434

@@ -39,10 +39,10 @@ bar appears when columns overflow the container:
3939
```razor
4040
<div style="overflow-x: auto;">
4141
<FluentDataGrid Items="@employees" Style="min-width: max-content;">
42-
<PropertyColumn Title="ID" Property="@(e => e.Id)" Width="60px" Pin="DataGridColumnPin.Left" />
43-
<PropertyColumn Title="Name" Property="@(e => e.Name)" Width="160px" Pin="DataGridColumnPin.Left" />
42+
<PropertyColumn Title="ID" Property="@(e => e.Id)" Width="60px" Pin="DataGridColumnPin.Start" />
43+
<PropertyColumn Title="Name" Property="@(e => e.Name)" Width="160px" Pin="DataGridColumnPin.Start" />
4444
<PropertyColumn Title="City" Property="@(e => e.City)" />
45-
<TemplateColumn Title="Actions" Width="120px" Pin="DataGridColumnPin.Right">
45+
<TemplateColumn Title="Actions" Width="120px" Pin="DataGridColumnPin.End">
4646
...
4747
</TemplateColumn>
4848
</FluentDataGrid>
@@ -63,22 +63,20 @@ property `--fluent-data-grid-pinned-background`:
6363

6464
## Notes
6565

66-
* Column resizing interacts correctly with sticky offsets — the JavaScript in
67-
`FluentDataGrid.razor.ts` recalculates `left` / `right` values after every resize step via
68-
`UpdatePinnedColumnOffsets`.
66+
* Column resizing keeps pinned columns aligned as widths change.
6967
* Virtualization and paging are fully compatible because each rendered row's cells carry the
7068
same `position: sticky` styling regardless of which page or scroll position is active.
71-
* In RTL layouts the browser interprets `left` / `right` according to the document direction, so
72-
pinned columns behave correctly without additional configuration.
69+
* RTL layouts are fully supported: start and end automatically map to the correct physical
70+
direction based on the document's writing mode.
7371

7472
## Example
7573

76-
Demonstrates pinned (frozen) columns using `Pin="DataGridColumnPin.Left"` and `Pin="DataGridColumnPin.Right"`.
74+
Demonstrates pinned (frozen) columns using `Pin="DataGridColumnPin.Start"` and `Pin="DataGridColumnPin.End"`.
7775
The two leftmost columns and the Actions column remain visible while the rest scroll horizontally.
7876

7977
Wrap the grid in a `<div style="overflow-x: auto;">` container and give the grid a `Style="min-width: max-content;"`
8078
so that the horizontal scroll bar appears.
8179

82-
Pinned columns require an explicit pixel `Width`.
80+
Pinned columns require an explicit `Width`.
8381

8482
{{ DataGridPinnedColumns }}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,21 +188,23 @@ public abstract partial class ColumnBase<TGridItem>
188188
public string? Width { get; set; }
189189

190190
/// <summary>
191-
/// Gets or sets whether this column is pinned (frozen) to the left or right edge of the grid,
191+
/// Gets or sets whether this column is pinned (frozen) to the start or end edge of the grid,
192192
/// so it remains visible when the user scrolls horizontally.
193-
/// Pinned columns require an explicit <see cref="Width"/> in pixels (e.g., <c>"150px"</c>).
194-
/// Left-pinned columns must be contiguous at the start of the column list;
195-
/// right-pinned columns must be contiguous at the end.
193+
/// Pinned columns require an explicit <see cref="Width"/>.
194+
/// Sticky offsets are recomputed from rendered header widths after the grid is rendered.
195+
/// Start-pinned columns must be contiguous at the start of the column list;
196+
/// end-pinned columns must be contiguous at the end.
196197
/// </summary>
197198
[Parameter]
198199
public DataGridColumnPin Pin { get; set; } = DataGridColumnPin.None;
199200

200201
/// <summary>
201-
/// The sticky <c>left</c> or <c>right</c> CSS offset (in pixels) computed by
202-
/// <see cref="FluentDataGrid{TGridItem}"/> when columns are collected.
202+
/// The sticky start or end CSS offset seeded by
203+
/// <see cref="FluentDataGrid{TGridItem}"/> when columns are collected and later updated from
204+
/// rendered widths by JavaScript.
203205
/// Not intended for direct use by consumers.
204206
/// </summary>
205-
internal double PinOffsetPx { get; set; }
207+
internal string PinOffset { get; set; } = "0px";
206208

207209
/// <summary>
208210
/// Gets or sets the minimal width of the column.

src/Core/Components/DataGrid/FluentDataGrid.razor.cs

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ private void FinishCollectingColumns()
600600
throw new ArgumentException("The 'HierarchicalToggle' parameter can only be set on the first column of the grid.");
601601
}
602602

603-
// Validate and compute offsets for pinned columns.
603+
// Validate pinned columns and seed their initial sticky offsets.
604604
ValidateAndComputePinnedColumns();
605605

606606
// Always re-evaluate after collecting columns when using displaymode grid. A column might be added or hidden and the _internalGridTemplateColumns needs to reflect that.
@@ -624,12 +624,13 @@ private void FinishCollectingColumns()
624624
}
625625

626626
/// <summary>
627-
/// Validates the pinned-column configuration and computes the sticky pixel offsets for each
628-
/// pinned column. Rules enforced:
627+
/// Validates the pinned-column configuration and seeds initial sticky offsets for each
628+
/// pinned column before JavaScript recomputes them from rendered widths after first render.
629+
/// Rules enforced:
629630
/// <list type="bullet">
630-
/// <item>Pinned columns must specify an explicit pixel <c>Width</c> (e.g., <c>"150px"</c>).</item>
631-
/// <item>Left-pinned columns must be contiguous at the beginning of the column list.</item>
632-
/// <item>Right-pinned columns must be contiguous at the end of the column list.</item>
631+
/// <item>Pinned columns must specify an explicit <c>Width</c>.</item>
632+
/// <item>Start-pinned columns must be contiguous at the beginning of the column list.</item>
633+
/// <item>End-pinned columns must be contiguous at the end of the column list.</item>
633634
/// </list>
634635
/// </summary>
635636
private void ValidateAndComputePinnedColumns()
@@ -642,20 +643,20 @@ private void ValidateAndComputePinnedColumns()
642643

643644
ValidatePinnedColumnConstraints();
644645

645-
// Compute left-pin sticky offsets (cumulative left-to-right).
646-
var leftOffset = 0.0;
647-
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Left))
646+
// Compute start-pin sticky offsets in display order.
647+
var startOffset = 0.0;
648+
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Start))
648649
{
649-
col.PinOffsetPx = leftOffset;
650-
leftOffset += ParsePixelWidth(col.Width);
650+
col.PinOffset = $"{startOffset.ToString(CultureInfo.InvariantCulture)}px";
651+
startOffset += ParsePixelWidth(col.Width);
651652
}
652653

653-
// Compute right-pin sticky offsets (cumulative right-to-left).
654-
var rightOffset = 0.0;
655-
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.Right).Reverse())
654+
// Compute end-pin sticky offsets in reverse display order.
655+
var endOffset = 0.0;
656+
foreach (var col in _columns.Where(c => c.Pin == DataGridColumnPin.End).Reverse())
656657
{
657-
col.PinOffsetPx = rightOffset;
658-
rightOffset += ParsePixelWidth(col.Width);
658+
col.PinOffset = $"{endOffset.ToString(CultureInfo.InvariantCulture)}px";
659+
endOffset += ParsePixelWidth(col.Width);
659660
}
660661
}
661662

@@ -665,52 +666,46 @@ private void ValidateAndComputePinnedColumns()
665666
/// </summary>
666667
private void ValidatePinnedColumnConstraints()
667668
{
668-
// Width must be an explicit pixel value.
669+
// Width must be explicitly provided for pinned columns.
669670
foreach (var col in _columns.Where(c => c.Pin != DataGridColumnPin.None))
670671
{
671672
if (string.IsNullOrWhiteSpace(col.Width))
672673
{
673674
throw new ArgumentException(
674675
$"Column '{col.Title ?? col.Index.ToString(CultureInfo.InvariantCulture)}' has Pin set but no Width. " +
675-
"Pinned columns require an explicit Width in pixels (e.g., '150px').");
676-
}
677-
678-
if (!col.Width!.Trim().EndsWith("px", StringComparison.OrdinalIgnoreCase))
679-
{
680-
throw new ArgumentException(
681-
$"Column '{col.Title ?? col.Index.ToString(CultureInfo.InvariantCulture)}' has Pin set but Width '{col.Width}' is not in pixels. " +
682-
"Pinned columns require an explicit Width in pixels (e.g., '150px').");
676+
"Pinned columns require an explicit Width.");
683677
}
684678
}
685679

686-
// Left-pinned columns must be contiguous at the start: each one must be preceded by
687-
// another left-pinned column (or be the very first column).
680+
// Start-pinned columns must be contiguous at the start: each one must be preceded by
681+
// another start-pinned column (or be the very first column).
688682
for (var i = 0; i < _columns.Count; i++)
689683
{
690-
if (_columns[i].Pin == DataGridColumnPin.Left && i > 0 && _columns[i - 1].Pin != DataGridColumnPin.Left)
684+
if (_columns[i].Pin == DataGridColumnPin.Start && i > 0 && _columns[i - 1].Pin != DataGridColumnPin.Start)
691685
{
692686
throw new ArgumentException(
693-
$"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is left-pinned but the preceding column is not. " +
694-
"Left-pinned columns must be contiguous at the start of the column list.");
687+
$"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is start-pinned but the preceding column is not. " +
688+
"Start-pinned columns must be contiguous at the start of the column list.");
695689
}
696690
}
697691

698-
// Right-pinned columns must be contiguous at the end: each one must be followed by
699-
// another right-pinned column (or be the very last column).
692+
// End-pinned columns must be contiguous at the end: each one must be followed by
693+
// another end-pinned column (or be the very last column).
700694
for (var i = 0; i < _columns.Count; i++)
701695
{
702-
if (_columns[i].Pin == DataGridColumnPin.Right && i < _columns.Count - 1 && _columns[i + 1].Pin != DataGridColumnPin.Right)
696+
if (_columns[i].Pin == DataGridColumnPin.End && i < _columns.Count - 1 && _columns[i + 1].Pin != DataGridColumnPin.End)
703697
{
704698
throw new ArgumentException(
705-
$"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is right-pinned but the following column is not. " +
706-
"Right-pinned columns must be contiguous at the end of the column list.");
699+
$"Column '{_columns[i].Title ?? _columns[i].Index.ToString(CultureInfo.InvariantCulture)}' is end-pinned but the following column is not. " +
700+
"End-pinned columns must be contiguous at the end of the column list.");
707701
}
708702
}
709703
}
710704

711705
/// <summary>
712706
/// Parses a CSS pixel value string such as <c>"150px"</c> and returns the numeric value.
713-
/// Returns <c>0</c> if the string is null, empty, or not a valid pixel value.
707+
/// Returns <c>0</c> if the string is null, empty, or not a valid pixel value so JavaScript
708+
/// can recompute the final sticky offsets from rendered widths after first render.
714709
/// </summary>
715710
private static double ParsePixelWidth(string? width)
716711
{
@@ -1445,4 +1440,3 @@ private async Task ToggleExpandedAsync(TGridItem item)
14451440
}
14461441
}
14471442
}
1448-

src/Core/Components/DataGrid/FluentDataGrid.razor.css

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -100,37 +100,37 @@
100100
/* ---- Pinned (sticky) columns ---- */
101101

102102
/* Background: keeps content from showing through when scrolling. Override --fluent-data-grid-pinned-background to theme. */
103-
.fluent-data-grid th.col-pinned-left,
104-
.fluent-data-grid th.col-pinned-right {
103+
.fluent-data-grid th.col-pinned-start,
104+
.fluent-data-grid th.col-pinned-end {
105105
background-color: var(--colorNeutralBackground1);
106106
}
107107

108-
.fluent-data-grid td.col-pinned-left,
109-
.fluent-data-grid td.col-pinned-right {
108+
.fluent-data-grid td.col-pinned-start,
109+
.fluent-data-grid td.col-pinned-end {
110110
background-color: var(--fluent-data-grid-pinned-background);
111111
}
112112

113-
/* Visual separator on the trailing edge of the last left-pinned column */
114-
.fluent-data-grid td:nth-last-child(1 of .col-pinned-left) {
113+
/* Visual separator on the trailing edge of the last start-pinned column */
114+
.fluent-data-grid td:nth-last-child(1 of .col-pinned-start) {
115115
border-inline-end: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
116116
}
117117

118-
/* Visual separator on the leading edge of the first right-pinned column */
119-
.fluent-data-grid td:nth-child(1 of .col-pinned-right) {
118+
/* Visual separator on the leading edge of the first end-pinned column */
119+
.fluent-data-grid td:nth-child(1 of .col-pinned-end) {
120120
border-inline-start: var(--strokeWidthThin) solid var(--colorNeutralStroke1);
121121
}
122122

123123
/* Pinned header cells must stack above pinned data cells (z-index: 1 applied inline by C#).
124124
For UseMenuService=false the inline z-index: 5 is absent, so we provide a floor here.
125125
The sticky-header row needs the highest value to beat both the sticky-header row z-index (2)
126126
and the pinned data cell z-index (1). */
127-
.fluent-data-grid th.col-pinned-left,
128-
.fluent-data-grid th.col-pinned-right {
127+
.fluent-data-grid th.col-pinned-start,
128+
.fluent-data-grid th.col-pinned-end {
129129
z-index: 2;
130130
}
131131

132-
.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-left,
133-
.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-right {
132+
.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-start,
133+
.fluent-data-grid tr[row-type='sticky-header'] > th.col-pinned-end {
134134
background-color: var(--colorNeutralBackground4);
135135
z-index: 4;
136136
}

0 commit comments

Comments
 (0)