Skip to content

Commit bfa2842

Browse files
authored
[dev-v5] FluentTreeView (#3802)
* Add first classes * Add events * Register the TreeView * Update the registering of TreeDefinition * Add Expanded and Selected events * Add SelecterdId and AsideTemplate * Add Ellipsis * Add Height * Add IconStart, IconEnd and IconAside * Add Items (not completed) * Update the Items * Add SetExpandedAsync * Add doc * Add events * Add new example (to updateà * Update * Add Unlimited example * Add MultiSelection sample * Add Multiple SelectedItems * Add doc * Update Multiple to SelectionMode * Remove the incorrect usage of InternalItem * Update doc * Add MultipleSelectionVisibility * Add pointer-events: none; and Update the samples * Add Migration page * Use the SDK 9.0.203 * Add Unit Tests * Add Unit Tests * Add Unit Tests * Add unit Tests * Add unit Tests * Add unit tests * Update doc * Update sln
1 parent c1977a9 commit bfa2842

File tree

56 files changed

+2614
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2614
-8
lines changed

.github/workflows/build-core-lib.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
- name: Setup .NET 9.0
4747
uses: actions/setup-dotnet@v4
4848
with:
49-
dotnet-version: 9.0.x
49+
dotnet-version: 9.0.203 # Waiting a fix in the 9.0.300: https://github.com/dotnet/sdk/issues/49038
5050
dotnet-quality: ga
5151

5252
# Build
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
@page "/treeview/debug"
2+
3+
<h1>TreeView Debug</h1>
4+
5+
<div>
6+
SelectedId = @SelectedId;
7+
</div>
8+
9+
<div>
10+
Expanded1 = @Expanded1;
11+
Expanded2 = @Expanded2;
12+
Expanded3 = @Expanded3;
13+
Expanded4 = @Expanded4;
14+
</div>
15+
16+
<button onclick="Expanded(true)">Open</button>
17+
<button onclick="Expanded(false)">Close</button>
18+
<button @onclick="@(e => SelectedId = "test-3")">Select #3</button>
19+
20+
<FluentTreeView @bind-SelectedId="@SelectedId"
21+
OnExpandedChanged="@(e => Console.WriteLine($"* Expanded: {e.Id}: {e.Expanded}"))"
22+
OnSelectedChanged="@(e => Console.WriteLine($"* Selected: {e.Id}"))">
23+
24+
<FluentTreeItem Id="test-1"
25+
Text="My item"
26+
Height="70px"
27+
@bind-Expanded="@Expanded1"
28+
IconStart="@(new Icons.Regular.Size16.ShareScreenStart())"
29+
IconEnd="@(new Icons.Regular.Size16.CallEnd())"
30+
IconAside="@(new Icons.Regular.Size16.AddCircle())"
31+
SelectedChanged="@(e => Console.WriteLine($". Selected: test-1: {e}"))">
32+
<FluentTreeItem Height="unset">Sub Item</FluentTreeItem>
33+
</FluentTreeItem>
34+
35+
<FluentTreeItem Id="test-2"
36+
Text="CollapseOnly with really long tree-item content goes here. Lorem ipsum dolor sit amet."
37+
IconStart="@(new Icons.Regular.Size16.ShareScreenStart())"
38+
IconEnd="@(new Icons.Regular.Size16.CallEnd())"
39+
IconAside="@(new Icons.Regular.Size16.AddCircle())"
40+
IconCollapsed="@(new Icons.Regular.Size16.ArrowCircleRight())"
41+
@bind-Expanded="@Expanded2"
42+
SelectedChanged="@(e => Console.WriteLine($". Selected: test-2: {e}"))">
43+
<FluentTreeItem>Sub Item</FluentTreeItem>
44+
</FluentTreeItem>
45+
46+
<FluentTreeItem Id="test-3"
47+
IconExpanded="@(new Icons.Regular.Size16.CopyArrowRight())"
48+
@bind-Expanded="@Expanded3"
49+
SelectedChanged="@(e => Console.WriteLine($". Selected: test-3: {e}"))">
50+
ExpandOnly
51+
<FluentTreeItem>Sub Item</FluentTreeItem>
52+
</FluentTreeItem>
53+
54+
<FluentTreeItem Id="test-4"
55+
Text="CollapseExpand"
56+
IconCollapsed="@(new Icons.Regular.Size16.ArrowCircleRight())"
57+
IconExpanded="@(new Icons.Regular.Size16.CopyArrowRight())"
58+
@bind-Expanded="@Expanded4"
59+
SelectedChanged="@(e => Console.WriteLine($". Selected: test-4: {e}"))">
60+
<FluentTreeItem>Sub Item</FluentTreeItem>
61+
</FluentTreeItem>
62+
</FluentTreeView>
63+
64+
<FluentDivider>Other section</FluentDivider>
65+
66+
<script>
67+
68+
function Expanded(value) {
69+
document.getElementById('test-1').expanded = value;
70+
document.getElementById('test-2').expanded = value;
71+
document.getElementById('test-3').expanded = value;
72+
document.getElementById('test-4').expanded = value;
73+
}
74+
</script>
75+
76+
@code
77+
{
78+
string SelectedId = "test-2";
79+
bool Expanded1 = true;
80+
bool Expanded2 = false;
81+
bool Expanded3 = true;
82+
bool Expanded4 = false;
83+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// ------------------------------------------------------------------------
2+
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
3+
// ------------------------------------------------------------------------
4+
5+
using Microsoft.FluentUI.AspNetCore.Components;
6+
using FluentUI.Demo.SampleData;
7+
using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons;
8+
9+
namespace FluentUI.Demo.Client.Documentation.Components.TreeView.Examples;
10+
11+
public static class SampleDataExtension
12+
{
13+
/// <summary>
14+
/// Converts a collection of <see cref="People.Company"/> objects into a hierarchical collection
15+
/// of <see cref="TreeViewItem"/> objects.
16+
/// </summary>
17+
public static IEnumerable<TreeViewItem> ToTreeViewItems(this IEnumerable<People.Company> organization, bool includeIcons = false)
18+
{
19+
return organization.Select(company => new TreeViewItem
20+
{
21+
IconStart = includeIcons ? new Icons.Regular.Size16.BuildingBank().WithColor(Color.Primary) : null,
22+
Id = company.Id,
23+
Text = company.Name,
24+
Items = company.Departments.Select(dept => new TreeViewItem
25+
{
26+
IconStart = includeIcons ? new Icons.Regular.Size16.ContactCardGroup().WithColor(SystemColors.Palette.DarkOrangeForeground1) : null,
27+
Id = dept.Id,
28+
Text = dept.Name,
29+
Items = dept.Employees.Select(emp => new TreeViewItem
30+
{
31+
IconStart = includeIcons ? new Icons.Regular.Size16.PersonVoice().WithColor(SystemColors.Palette.ForestBorderActive) : null,
32+
Id = emp.Id,
33+
Text = $"{emp.FirstName} {emp.LastName}",
34+
}).ToArray()
35+
}).ToArray()
36+
}).ToArray();
37+
}
38+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<p>Current selected tree item is <b>@SelectedId - @SelectedItem?.Text</b></p>
2+
<p>Most recently expanded/collapsed tree item is <b>@ExpandedItem?.Text</b></p>
3+
4+
<FluentTreeView @bind-SelectedId="@SelectedId"
5+
@bind-CurrentSelected="SelectedItem"
6+
OnExpandedChanged="@(item => ExpandedItem = item)">
7+
<FluentTreeItem Id="Item1" Text="Root item 1">
8+
<FluentTreeItem Id="Item11" Text="Flowers" IconStart="@(new Icons.Regular.Size16.LeafOne())">
9+
<FluentTreeItem Id="Item111" Text="Daisy" />
10+
<FluentTreeItem Id="Item112" Text="Sunflower" />
11+
<FluentTreeItem Id="Item113" Text="Rose" />
12+
</FluentTreeItem>
13+
<FluentTreeItem Id="Item12" Text="Nested item 2" />
14+
<FluentTreeItem Id="Item13" Text="Nested item 3" />
15+
</FluentTreeItem>
16+
</FluentTreeView>
17+
18+
@code {
19+
string? SelectedId;
20+
FluentTreeItem? SelectedItem;
21+
FluentTreeItem? ExpandedItem;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<div class="my-2">
2+
<b>Selected item:</b> @SelectedItem?.Text
3+
</div>
4+
5+
<FluentTreeView Items="@Items"
6+
@bind-SelectedItem="@SelectedItem">
7+
<ItemTemplate>
8+
<FluentBadge Color="BadgeColor.Informative" Content="@context.Id" Style="pointer-events: none;" />
9+
@context.Text
10+
</ItemTemplate>
11+
</FluentTreeView>
12+
13+
@code
14+
{
15+
private ITreeViewItem? SelectedItem;
16+
private IEnumerable<ITreeViewItem>? Items = new List<ITreeViewItem>();
17+
18+
// Read the Tree content and set the selected item
19+
protected override void OnInitialized()
20+
{
21+
Items = GetCompanyOrganization();
22+
SelectedItem = Items?.ElementAt(3);
23+
}
24+
25+
// Example of a tree with a company organization
26+
// (5 companies containing 4 departments with 10 employees)
27+
private TreeViewItem[] GetCompanyOrganization()
28+
{
29+
return SampleData.People
30+
.GetOrganization(companyCount: 5, departmentCount: 4, employeeCount: 10)
31+
.ToTreeViewItems()
32+
.ToArray();
33+
}
34+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<div class="my-2">
2+
<b>Selected item:</b> @SelectedItem?.Text
3+
</div>
4+
5+
<FluentTreeView Items="@Items"
6+
@bind-SelectedItem="@SelectedItem" />
7+
8+
@code
9+
{
10+
private ITreeViewItem? SelectedItem;
11+
private IEnumerable<ITreeViewItem>? Items = new List<ITreeViewItem>();
12+
13+
// Read the Tree content and set the selected item
14+
protected override void OnInitialized()
15+
{
16+
Items = GetCompanyOrganization();
17+
SelectedItem = Items?.ElementAt(3);
18+
}
19+
20+
// Example of a tree with a company organization
21+
// (5 companies containing 4 departments with 10 employees)
22+
private TreeViewItem[] GetCompanyOrganization()
23+
{
24+
return SampleData.People
25+
.GetOrganization(companyCount: 5, departmentCount: 4, employeeCount: 10)
26+
.ToTreeViewItems()
27+
.ToArray();
28+
}
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<div class="my-2">
2+
<b>Selected items:</b> @(string.Join("; ", SelectedItems?.Select(i => i.Text) ?? []))
3+
</div>
4+
5+
<FluentTreeView Items="@Items"
6+
HideSelection="true"
7+
SelectionMode="TreeSelectionMode.Multiple"
8+
@bind-SelectedItems="@SelectedItems">
9+
</FluentTreeView>
10+
11+
@code
12+
{
13+
private IEnumerable<ITreeViewItem>? SelectedItems;
14+
private IEnumerable<ITreeViewItem>? Items = new List<ITreeViewItem>();
15+
16+
// Read the Tree content
17+
protected override void OnInitialized()
18+
{
19+
Items = GetCompanyOrganization();
20+
SelectedItems = Items.Take(2);
21+
}
22+
23+
// Example of a tree with a company organization
24+
// (5 companies containing 4 departments with 10 employees)
25+
private TreeViewItem[] GetCompanyOrganization()
26+
{
27+
return SampleData.People
28+
.GetOrganization(companyCount: 5, departmentCount: 4, employeeCount: 10)
29+
.ToTreeViewItems(includeIcons: false)
30+
.ToArray();
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<div class="my-2">
2+
<b>Selected items:</b> @(string.Join("; ", SelectedItems?.Select(i => i.Text) ?? []))
3+
</div>
4+
5+
<FluentTreeView Items="@Items"
6+
HideSelection="true"
7+
SelectionMode="TreeSelectionMode.Multiple"
8+
MultipleSelectionVisibility="@GetTreeSelectionVisibility"
9+
@bind-SelectedItems="@SelectedItems">
10+
</FluentTreeView>
11+
12+
@code
13+
{
14+
private IEnumerable<ITreeViewItem>? SelectedItems;
15+
private IEnumerable<ITreeViewItem>? Items = new List<ITreeViewItem>();
16+
17+
// Read the Tree content
18+
protected override void OnInitialized()
19+
{
20+
Items = GetCompanyOrganization();
21+
}
22+
23+
// Example of a custom visibility function
24+
private TreeSelectionVisibility GetTreeSelectionVisibility(ITreeViewItem item)
25+
{
26+
return item.Id.First() switch
27+
{
28+
// Company or Department => collapsed checkbox
29+
'C' => TreeSelectionVisibility.Collapse,
30+
'D' => TreeSelectionVisibility.Hidden,
31+
32+
// Employee or others => visible checkbox
33+
'E' => TreeSelectionVisibility.Visible,
34+
_ => TreeSelectionVisibility.Visible
35+
};
36+
}
37+
38+
// Example of a tree with a company organization
39+
// (5 companies containing 4 departments with 10 employees)
40+
private TreeViewItem[] GetCompanyOrganization()
41+
{
42+
return SampleData.People
43+
.GetOrganization(companyCount: 5, departmentCount: 4, employeeCount: 10)
44+
.ToTreeViewItems(includeIcons: false)
45+
.ToArray();
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<div class="my-2">
2+
<b>Selected item:</b> @SelectedItem?.Text
3+
</div>
4+
5+
<FluentTreeView Items="@Items"
6+
LazyLoadItems="true"
7+
@bind-SelectedItem="@SelectedItem" />
8+
9+
@code
10+
{
11+
private ITreeViewItem? SelectedItem;
12+
private IEnumerable<ITreeViewItem>? Items = new List<ITreeViewItem>();
13+
14+
protected override async Task OnInitializedAsync()
15+
{
16+
Items = await GetItemsAsync();
17+
}
18+
19+
// Generate a random number of items
20+
// Including a "Fake" sub-item to simulate the [+]
21+
private async Task<IEnumerable<ITreeViewItem>> GetItemsAsync()
22+
{
23+
await Task.Delay(300); // Simulate a delay for loading items
24+
25+
var nbItems = Random.Shared.Next(3, 9);
26+
27+
return Enumerable.Range(1, nbItems)
28+
.Select(i => new TreeViewItem()
29+
{
30+
Text = $"Item {Random.Shared.Next(1, 9999)}",
31+
OnExpandedAsync = OnExpandedAsync,
32+
Items = TreeViewItem.LoadingTreeViewItems("Loading..."), // "Fake" sub-item to simulate the [+]
33+
}).ToArray();
34+
}
35+
36+
// Handle the expanded event to load items
37+
private async Task OnExpandedAsync(TreeViewItemExpandedEventArgs e)
38+
{
39+
if (e.Expanded)
40+
{
41+
e.CurrentItem.Items = await GetItemsAsync();
42+
}
43+
else
44+
{
45+
// Remove sub-items and add a "Fake" item to simulate the [+]
46+
e.CurrentItem.Items = TreeViewItem.LoadingTreeViewItems("Loading...");
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)