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
14 changes: 14 additions & 0 deletions .github/workflows/build-core-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ jobs:
dotnet-version: 10.0.x
dotnet-quality: preview

- name: Setup .NET 11.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: 11.0.100-preview.7.26381.103

# Build

- name: Restore
Expand Down Expand Up @@ -92,6 +97,10 @@ jobs:
done
working-directory: ${{ github.workspace }}

- name: Tests .NET 11.0
run: dotnet test ./tests/Core/Components.Tests.csproj --report-trx --results-directory ./TestResults --verbosity normal --configuration Release /p:ExampleNetVersion=net11.0
working-directory: ${{ github.workspace }}

- name: Publish Unit Tests
if: success() || failure()
continue-on-error: true
Expand Down Expand Up @@ -229,6 +238,11 @@ jobs:
dotnet-version: 10.0.x
dotnet-quality: preview

- name: Setup .NET 11.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: 11.0.100-preview.7.26381.103

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
Expand Down
5 changes: 4 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
<!-- Target .NET versions for multi-targeting -->
<!-- Used with the published libraries: Core; McpServer; Charts; DataGrid adapters -->
<!-- Debug: minimum version for faster builds; Release: full multi-targeting -->
<TargetNetVersions Condition="'$(Configuration)' == 'Release'">net8.0;net9.0;net10.0</TargetNetVersions>
<TargetNetVersions Condition="'$(Configuration)' == 'Release'">net8.0;net9.0;net10.0;net11.0</TargetNetVersions>
<TargetNetVersions Condition="'$(Configuration)' != 'Release'">$(NetVersion)</TargetNetVersions>

<!-- .NET 11 Preview 7 reports BL0016 across the existing JS interop architecture. -->
<NoWarn Condition="'$(TargetFramework)' == 'net11.0' Or '$(ExampleNetVersion)' == 'net11.0'">$(NoWarn);BL0016</NoWarn>

<!-- Versioning -->
<VersionFile>5.0.0</VersionFile>
<VersionPrefix>5.0.0</VersionPrefix>
Expand Down
2 changes: 0 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,8 @@
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="$(EfCoreVersion11)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EfCoreVersion11)" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="$(EfCoreVersion11)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(RuntimeVersion11)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(RuntimeVersion11)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(RuntimeVersion11)" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(RuntimeVersion11)" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(RuntimeVersion11)" />
<PackageVersion Include="System.Text.Encodings.Web" Version="$(RuntimeVersion11)" />
<PackageVersion Include="System.Text.Json" Version="$(RuntimeVersion11)" />
Expand Down
4 changes: 2 additions & 2 deletions _ListOutdatedDependencies.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

.DESCRIPTION
Temporarily switches the <NetVersion> property in Directory.Build.props to each of
net8.0, net9.0 and net10.0, runs `dotnet outdated` for the solution, and
net8.0, net9.0, net10.0 and net11.0, runs `dotnet outdated` for the solution, and
aggregates the results into a single de-duplicated summary list.
Also lists outdated npm packages (via npm-check-updates) for the Core.Scripts and
Charts.Scripts projects, without changing any package.json or lock file.
Expand All @@ -24,7 +24,7 @@ $ErrorActionPreference = 'Stop'
$repoRoot = $PSScriptRoot
$propsPath = Join-Path $repoRoot 'Directory.Build.props'
$slnPath = Join-Path $repoRoot 'Microsoft.FluentUI-v5.slnx'
$netVersions = @('net8.0', 'net9.0', 'net10.0')
$netVersions = @('net8.0', 'net9.0', 'net10.0', 'net11.0')
$netVersionPattern = '<NetVersion>net\d+\.\d+</NetVersion>'
$exampleVersionPattern = '<ExampleNetVersion>net\d+\.\d+</ExampleNetVersion>'
# Demo/Samples/Tests projects require net9.0+ (see ExampleNetVersion comment in Directory.Build.props).
Expand Down
6 changes: 6 additions & 0 deletions eng/pipelines/build-all-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ extends:
version: 10.0.x
includePreviewVersions: true

- task: UseDotNet@2
displayName: 'Install .NET 11.0'
inputs:
version: 11.0.100-preview.7.26381.103
includePreviewVersions: true

# Set version number (exclude some folders)
- task: PowerShell@2
displayName: 'Versioning $(Build.BuildNumber)'
Expand Down
6 changes: 6 additions & 0 deletions eng/pipelines/build-core-lib.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ extends:
version: 10.0.x
includePreviewVersions: true

- task: UseDotNet@2
displayName: 'Install .NET 11.0'
inputs:
version: 11.0.100-preview.7.26381.103
includePreviewVersions: true

# Set version number (exclude some folders)
- task: PowerShell@2
displayName: 'Versioning $(Build.BuildNumber)'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,11 @@

void ValidateForm()
{
#if NET11_0_OR_GREATER
_ = form.EditContext?.ValidateAsync();
#else
form.EditContext?.Validate();
#endif
}

void SetFormToValid()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,55 @@ Enabling virtualization is just a matter of passing `Virtualize="true"`. For it
properly and reliably, every row rendered must have the same known height. **This is handled by the `FluentDataGrid` code**

{{ DataGridVirtualize }}

## Positioning and anchoring in .NET 11

When targeting .NET 11, a virtualized grid can open at a specific zero-based row index, scroll to a row
programmatically, and keep the viewport anchored as provider data changes. Out-of-range indexes are clamped by
the underlying `Virtualize` component.

An `ItemComparer` should compare stable item identity rather than object references when an items provider
returns new instances across requests.

```razor
@using Microsoft.AspNetCore.Components.Web.Virtualization

<FluentButton OnClick="ScrollToTopAsync">Back to top</FluentButton>

<div style="height: 400px; overflow-y: auto;">
<FluentDataGrid @ref="_grid"
TGridItem="Product"
ItemsProvider="LoadProductsAsync"
Virtualize="true"
InitialItemIndex="500"
AnchorMode="VirtualizeAnchorMode.End"
ItemComparer="ProductIdComparer.Instance"
ItemSize="32"
DisplayMode="DataGridDisplayMode.Table">
<PropertyColumn Property="@(product => product.Name)" />
</FluentDataGrid>
</div>

@code {
private FluentDataGrid<Product>? _grid;

private Task ScrollToTopAsync()
=> _grid!.ScrollToItemAsync(0);

private sealed class ProductIdComparer : IEqualityComparer<Product>
{
public static ProductIdComparer Instance { get; } = new();

public bool Equals(Product? first, Product? second)
=> first?.Id == second?.Id;

public int GetHashCode(Product product)
=> product.Id.GetHashCode();
}
}
```

`InitialItemIndex` is applied only on the first interactive render. Use `ScrollToItemAsync` after the grid has
rendered for later navigation. Calling it before rendering or while `Virtualize` is disabled throws an
`InvalidOperationException`. Cancellation and repeated-call behavior are delegated to the underlying virtualizer;
when calls overlap, the last call wins.
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,6 @@
targetRow.Columns.Add(source);
}
}

StateHasChanged();
}

private void OnDropElement(FluentDragEventArgs<FormElement> e)
Expand Down Expand Up @@ -213,8 +211,6 @@
targetColumn.Elements.Add(source);
}
}

StateHasChanged();
}

public class Form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,5 @@ private async Task ResetThemeAsync()
await ThemeService.SetThemeToElementAsync(_themePreviewElement, settings);
await ThemeService.SetThemeAsync(settings);
await ThemeService.ClearStoredThemeSettingsAsync();

StateHasChanged();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@
private async Task DownloadAllAsync()
{
_downloadingAll = true;
StateHasChanged();

try
{
Expand All @@ -161,7 +160,6 @@
finally
{
_downloadingAll = false;
StateHasChanged();
}
}

Expand Down
2 changes: 2 additions & 0 deletions examples/Demo/FluentUI.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
#if !NET11_0_OR_GREATER
app.UseWebAssemblyDebugging();
#endif
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ protected override async Task OnInitializedAsync()
{
HttpClient.BaseAddress ??= new Uri(NavigationManager.BaseUri);
await McpDocumentationService.LoadAsync(HttpClient, "/mcp-documentation.json").ConfigureAwait(true);
StateHasChanged();
}
}

Expand Down
32 changes: 24 additions & 8 deletions src/Core/Components/DataGrid/FluentDataGrid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,7 @@
}
else
{
<Virtualize @ref="@_virtualizeComponent"
TItem="(int RowIndex, TGridItem Data)"
ItemSize="@ItemSize"
OverscanCount="@OverscanCount"
ItemsProvider="@ProvideVirtualizedItemsAsync"
ItemContent="@(item => builder => RenderRow(builder, item.RowIndex, item.Data))"
Placeholder="@(placeholderContext => builder => RenderPlaceholderRow(builder, placeholderContext))"
SpacerElement="tr" />
@((RenderFragment)RenderVirtualize)
}
}
else
Expand All @@ -91,6 +84,29 @@
</CascadingValue>

@code {
// RenderTreeBuilder is required while Virtualize parameters are target-framework conditional.
// Replace this with Razor markup when older targets no longer require these #if directives.
private void RenderVirtualize(RenderTreeBuilder builder)
{
builder.OpenComponent<Virtualize<(int RowIndex, TGridItem Data)>>(0);
builder.AddAttribute(1, nameof(Virtualize<(int, TGridItem)>.ItemSize), ItemSize);
builder.AddAttribute(2, nameof(Virtualize<(int, TGridItem)>.OverscanCount), OverscanCount);
#if NET9_0_OR_GREATER
builder.AddAttribute(3, nameof(Virtualize<(int, TGridItem)>.MaxItemCount), MaxItemCount);
#endif
#if NET11_0_OR_GREATER
builder.AddAttribute(4, nameof(Virtualize<(int, TGridItem)>.InitialItemIndex), InitialItemIndex);
builder.AddAttribute(5, nameof(Virtualize<(int, TGridItem)>.AnchorMode), AnchorMode);
builder.AddAttribute(6, nameof(Virtualize<(int, TGridItem)>.ItemComparer), _virtualizeItemComparer);
#endif
builder.AddAttribute(7, nameof(Virtualize<(int, TGridItem)>.ItemsProvider), (ItemsProviderDelegate<(int, TGridItem)>)ProvideVirtualizedItemsAsync);
builder.AddAttribute(8, nameof(Virtualize<(int, TGridItem)>.ItemContent), (RenderFragment<(int RowIndex, TGridItem Data)>)(item => contentBuilder => RenderRow(contentBuilder, item.RowIndex, item.Data)));
builder.AddAttribute(9, nameof(Virtualize<(int, TGridItem)>.Placeholder), (RenderFragment<PlaceholderContext>)(placeholderContext => contentBuilder => RenderPlaceholderRow(contentBuilder, placeholderContext)));
builder.AddAttribute(10, nameof(Virtualize<(int, TGridItem)>.SpacerElement), "tr");
builder.AddComponentReferenceCapture(11, value => _virtualizeComponent = (Virtualize<(int, TGridItem)>)value);
builder.CloseComponent();
}

private void RenderNonVirtualizedRows(RenderTreeBuilder __builder)
{
var initialRowIndex = (GenerateHeader != DataGridGeneratedHeaderType.None) ? 2 : 1; // aria-rowindex is 1-based, plus 1 if there is a header
Expand Down
Loading
Loading