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
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.1.9-beta01</Version>
<Version>9.1.9-beta02</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
82 changes: 56 additions & 26 deletions src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// Tree 组件
/// </summary>
#if NET6_0_OR_GREATER
[CascadingTypeParameter(nameof(TItem))]
#endif
public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
{
/// <summary>
Expand Down Expand Up @@ -58,7 +56,7 @@ public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
.Build();

private string? GetContentClassString(TreeViewItem<TItem> item) => CssBuilder.Default("tree-content")
.AddClass("active", ActiveItem == item)
.AddClass("active", _activeItem == item)
.Build();

private string? GetNodeClassString(TreeViewItem<TItem> item) => CssBuilder.Default("tree-node")
Expand All @@ -74,7 +72,7 @@ public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
/// <summary>
/// 获得/设置 选中节点 默认 null
/// </summary>
private TreeViewItem<TItem>? ActiveItem { get; set; }
private TreeViewItem<TItem>? _activeItem;

/// <summary>
/// 获得/设置 是否禁用整个组件 默认 false
Expand Down Expand Up @@ -168,11 +166,11 @@ public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
[NotNull]
public List<TreeViewItem<TItem>>? Items { get; set; }

/// <summary>
/// 获得/设置 扁平化数据集合注意 <see cref="TreeViewItem{TItem}.Parent"/> 参数一定要赋值,不然无法呈现层次结构
/// </summary>
[Parameter]
public List<TreeViewItem<TItem>>? FlatItems { get; set; }
///// <summary>
///// 获得/设置 扁平化数据集合注意 <see cref="TreeViewItem{TItem}.Parent"/> 参数一定要赋值,不然无法呈现层次结构
///// </summary>
//[Parameter]
//public List<TreeViewItem<TItem>>? FlatItems { get; set; }

/// <summary>
/// 获得/设置 是否显示 CheckBox 默认 false 不显示
Expand Down Expand Up @@ -347,6 +345,11 @@ protected override void OnParametersSet()
SearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewSearchIcon);
ClearSearchIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewResetSearchIcon);
LoadingIcon ??= IconTheme.GetIconByKey(ComponentIcons.TreeViewLoadingIcon);

if (IsReset)
{
_rows = null;
}
}

/// <summary>
Expand All @@ -355,8 +358,12 @@ protected override void OnParametersSet()
/// <returns></returns>
protected override async Task OnParametersSetAsync()
{
_rows = null;
Items ??= [];
if (Items == null)
{
// 未提供数据显示 loading
return;
}

if (Items.Count > 0)
{
await CheckExpand(Items);
Expand All @@ -369,16 +376,16 @@ protected override async Task OnParametersSetAsync()
}

// 从数据源中恢复当前 active 节点
if (ActiveItem != null)
if (_activeItem != null)
{
ActiveItem = TreeNodeStateCache.Find(Items, ActiveItem.Value, out _);
_activeItem = TreeNodeStateCache.Find(Items, _activeItem.Value, out _);
}

if (_init == false)
{
// 设置 ActiveItem 默认值
ActiveItem ??= Items.FirstOrDefaultActiveItem();
ActiveItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
_activeItem ??= Items.FirstOrDefaultActiveItem();
_activeItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
_init = true;
}
}
Expand Down Expand Up @@ -417,16 +424,16 @@ public async ValueTask TriggerKeyDown(string key)
{
// 通过 ActiveItem 找到兄弟节点
// 如果兄弟节点没有时,找到父亲节点
if (ActiveItem != null)
if (_activeItem != null)
{
if (key == "ArrowUp" || key == "ArrowDown")
{
_keyboardArrowUpDownTrigger = true;
await ActiveTreeViewItem(key, ActiveItem);
await ActiveTreeViewItem(key, _activeItem);
}
else if (key == "ArrowLeft" || key == "ArrowRight")
{
await OnToggleNodeAsync(ActiveItem, true);
await OnToggleNodeAsync(_activeItem, true);
}
}
}
Expand Down Expand Up @@ -574,7 +581,7 @@ private async Task<IEnumerable<IExpandableNode<TItem>>> GetChildrenRowAsync(Tree
/// <returns></returns>
private async Task OnClick(TreeViewItem<TItem> item)
{
ActiveItem = item;
_activeItem = item;
if (ClickToggleNode && CanTriggerClickNode(item))
{
await OnToggleNodeAsync(item);
Expand Down Expand Up @@ -622,21 +629,43 @@ private async Task OnClickResetSearch()
/// <summary>
/// 设置选中节点
/// </summary>
public void SetActiveItem(TreeViewItem<TItem> item)
public void SetActiveItem(TreeViewItem<TItem>? item)
{
_activeItem = item;
_activeItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
StateHasChanged();
}

/// <summary>
/// 重新设置 <see cref="Items"/> 数据源方法
/// </summary>
public void SetItems(List<TreeViewItem<TItem>> items)
{
ActiveItem = item;
ActiveItem.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
//FlatItems = null;
Items = items;
_rows = null;
StateHasChanged();
}

///// <summary>
///// 重新设置 <see cref="FlatItems"/> 数据源方法
///// </summary>
///// <param name="flatItems"></param>
//public void SetFlatItems(List<TreeViewItem<TItem>> flatItems)
//{
// Items = null;
// FlatItems = flatItems;
// _rows = null;
// StateHasChanged();
//}

/// <summary>
/// 设置选中节点
/// </summary>
public void SetActiveItem(TItem item)
{
ActiveItem = Items.GetAllItems().FirstOrDefault(i => Equals(i.Value, item));
ActiveItem?.SetParentExpand<TreeViewItem<TItem>, TItem>(true);
StateHasChanged();
var val = Items.GetAllItems().FirstOrDefault(i => Equals(i.Value, item));
SetActiveItem(val);
}

private static CheckboxState ToggleCheckState(CheckboxState state) => state switch
Expand Down Expand Up @@ -832,7 +861,8 @@ private List<TreeViewItem<TItem>> Rows
{
get
{
_rows ??= FlatItems ?? Items.ToFlat<TItem>().ToList();
// 扁平化数据集合
_rows ??= Items.ToFlat<TItem>().ToList();
return _rows;
}
}
Expand Down
42 changes: 40 additions & 2 deletions test/UnitTest/Components/TreeViewTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace UnitTest.Components;
public class TreeViewTest : BootstrapBlazorTestBase
{
[Fact]
public void Items_Ok()
public async Task Items_Ok()
{
var cut = Context.RenderComponent<TreeView<TreeFoo>>();
cut.DoesNotContain("tree-root");
Expand All @@ -25,16 +25,54 @@ public void Items_Ok()
{
pb.Add(a => a.Items, TreeFoo.GetTreeItems());
});
cut.Contains("li");
var items = cut.FindAll(".tree-content");
Assert.Equal(9, items.Count);

cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.Items, null);
pb.Add(a => a.ShowSkeleton, false);
});
Assert.Equal("", cut.Markup);

// SetItems
await cut.InvokeAsync(() => cut.Instance.SetItems(
[
new TreeViewItem<TreeFoo>(new TreeFoo() { Text = "Test1" }) { Text = "Test1" },
new TreeViewItem<TreeFoo>(new TreeFoo() { Text = "Test2" }) { Text = "Test2" }
]));

items = cut.FindAll(".tree-content");
Assert.Equal(2, items.Count);
}

//[Fact]
//public void FlatItems_Ok()
//{
// var cut = Context.RenderComponent<TreeView<TreeFoo>>(pb =>
// {
// pb.Add(a => a.FlatItems, TreeFoo.GetFlatItems());
// });
// cut.WaitForElement(".tree-view");

// // 验证树形结构正确生成
// var nodes = cut.FindAll(".tree-content");
// Assert.Equal(3, nodes.Count);

// // 验证父子关系
// var parentNode = cut.Find("[data-item-id='1']");
// Assert.NotNull(parentNode);
// var childNode = cut.Find("[data-item-id='2']");
// Assert.NotNull(childNode);
// Assert.Contains("tree-children", childNode.ParentElement?.ClassName);

// cut.SetParametersAndRender(pb =>
// {
// pb.Add(a => a.FlatItems, null);
// });
// Assert.Equal("", cut.Markup);
//}

[Fact]
public void Items_Disabled()
{
Expand Down
Loading