fix(ui): normalize TreeView expanded keys to strings for sessionStorage compatibility
Keys from KeySelector (e.g. boxed int) were compared against string keys restored from sessionStorage, causing expansion state to be lost on navigation. All keys are now normalized to strings internally.
This commit is contained in:
@@ -32,7 +32,7 @@ else
|
|||||||
var key = KeySelector(item);
|
var key = KeySelector(item);
|
||||||
var children = ChildrenSelector(item);
|
var children = ChildrenSelector(item);
|
||||||
var isBranch = HasChildrenSelector(item);
|
var isBranch = HasChildrenSelector(item);
|
||||||
var isExpanded = _expandedKeys.Contains(key);
|
var isExpanded = _expandedKeys.Contains(KeyStr(key));
|
||||||
|
|
||||||
<li role="treeitem" @key="key"
|
<li role="treeitem" @key="key"
|
||||||
aria-expanded="@(isBranch ? (isExpanded ? "true" : "false") : null)"
|
aria-expanded="@(isBranch ? (isExpanded ? "true" : "false") : null)"
|
||||||
@@ -66,7 +66,10 @@ else
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
private IReadOnlyList<TItem>? _items;
|
private IReadOnlyList<TItem>? _items;
|
||||||
private HashSet<object> _expandedKeys = new();
|
private HashSet<string> _expandedKeys = new();
|
||||||
|
|
||||||
|
/// <summary>Normalize any key object to a string for consistent comparison with sessionStorage.</summary>
|
||||||
|
private string KeyStr(object key) => key.ToString()!;
|
||||||
private bool _initialExpansionApplied;
|
private bool _initialExpansionApplied;
|
||||||
private bool _storageLoaded;
|
private bool _storageLoaded;
|
||||||
private TItem? _contextMenuItem;
|
private TItem? _contextMenuItem;
|
||||||
@@ -124,7 +127,7 @@ else
|
|||||||
var keys = System.Text.Json.JsonSerializer.Deserialize<List<string>>(json);
|
var keys = System.Text.Json.JsonSerializer.Deserialize<List<string>>(json);
|
||||||
if (keys != null)
|
if (keys != null)
|
||||||
{
|
{
|
||||||
_expandedKeys = new HashSet<object>(keys);
|
_expandedKeys = new HashSet<string>(keys);
|
||||||
_initialExpansionApplied = true;
|
_initialExpansionApplied = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +162,7 @@ else
|
|||||||
{
|
{
|
||||||
if (InitiallyExpanded!(item))
|
if (InitiallyExpanded!(item))
|
||||||
{
|
{
|
||||||
_expandedKeys.Add(KeySelector(item));
|
_expandedKeys.Add(KeyStr(KeySelector(item)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = ChildrenSelector(item);
|
var children = ChildrenSelector(item);
|
||||||
@@ -172,9 +175,10 @@ else
|
|||||||
|
|
||||||
private void ToggleExpand(object key)
|
private void ToggleExpand(object key)
|
||||||
{
|
{
|
||||||
if (!_expandedKeys.Remove(key))
|
var k = KeyStr(key);
|
||||||
|
if (!_expandedKeys.Remove(k))
|
||||||
{
|
{
|
||||||
_expandedKeys.Add(key);
|
_expandedKeys.Add(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
PersistExpandedState();
|
PersistExpandedState();
|
||||||
@@ -184,8 +188,7 @@ else
|
|||||||
{
|
{
|
||||||
if (StorageKey != null)
|
if (StorageKey != null)
|
||||||
{
|
{
|
||||||
var keys = _expandedKeys.Select(k => k.ToString()!).ToList();
|
var json = System.Text.Json.JsonSerializer.Serialize(_expandedKeys.ToList());
|
||||||
var json = System.Text.Json.JsonSerializer.Serialize(keys);
|
|
||||||
_ = JSRuntime.InvokeVoidAsync("treeviewStorage.save", StorageKey, json);
|
_ = JSRuntime.InvokeVoidAsync("treeviewStorage.save", StorageKey, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +235,7 @@ else
|
|||||||
{
|
{
|
||||||
if (HasChildrenSelector(item))
|
if (HasChildrenSelector(item))
|
||||||
{
|
{
|
||||||
_expandedKeys.Add(KeySelector(item));
|
_expandedKeys.Add(KeyStr(KeySelector(item)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = ChildrenSelector(item);
|
var children = ChildrenSelector(item);
|
||||||
@@ -258,13 +261,14 @@ else
|
|||||||
public async Task RevealNode(object key, bool select = false)
|
public async Task RevealNode(object key, bool select = false)
|
||||||
{
|
{
|
||||||
var parentLookup = BuildParentLookup();
|
var parentLookup = BuildParentLookup();
|
||||||
|
var k = KeyStr(key);
|
||||||
|
|
||||||
// If key is not in the tree at all, no-op
|
// If key is not in the tree at all, no-op
|
||||||
if (!parentLookup.ContainsKey(key))
|
if (!parentLookup.ContainsKey(k))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Walk up through ancestors
|
// Walk up through ancestors
|
||||||
var current = key;
|
var current = k;
|
||||||
while (parentLookup.TryGetValue(current, out var parentKey) && parentKey != null)
|
while (parentLookup.TryGetValue(current, out var parentKey) && parentKey != null)
|
||||||
{
|
{
|
||||||
_expandedKeys.Add(parentKey);
|
_expandedKeys.Add(parentKey);
|
||||||
@@ -280,9 +284,9 @@ else
|
|||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<object, object?> BuildParentLookup()
|
private Dictionary<string, string?> BuildParentLookup()
|
||||||
{
|
{
|
||||||
var lookup = new Dictionary<object, object?>();
|
var lookup = new Dictionary<string, string?>();
|
||||||
if (_items is { Count: > 0 })
|
if (_items is { Count: > 0 })
|
||||||
{
|
{
|
||||||
BuildParentLookupRecursive(_items, null, lookup);
|
BuildParentLookupRecursive(_items, null, lookup);
|
||||||
@@ -290,11 +294,11 @@ else
|
|||||||
return lookup;
|
return lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildParentLookupRecursive(IReadOnlyList<TItem> items, object? parentKey, Dictionary<object, object?> lookup)
|
private void BuildParentLookupRecursive(IReadOnlyList<TItem> items, string? parentKey, Dictionary<string, string?> lookup)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
var key = KeySelector(item);
|
var key = KeyStr(KeySelector(item));
|
||||||
lookup[key] = parentKey;
|
lookup[key] = parentKey;
|
||||||
|
|
||||||
var children = ChildrenSelector(item);
|
var children = ChildrenSelector(item);
|
||||||
|
|||||||
Reference in New Issue
Block a user