Skip to content

Commit

Permalink
refactor(TreeView): improve TreeView script (#4728)
Browse files Browse the repository at this point in the history
* refactor: 精简代码移除不使用的参数

* refactor: 精简 scroll 代码提高性能

* refactor: 更新 GetParentsState 方法

* refactor: 重构 toggleLoading 方法

* refactor: 精简 setChildrenState 方法

* refactor: 精简 dispose 方法

* refactor: 移除 Data 方法

* test: 更新单元测试
  • Loading branch information
ArgoZhang authored Nov 24, 2024
1 parent 227fa5e commit 333dd4d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 104 deletions.
16 changes: 5 additions & 11 deletions src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,15 +391,15 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
if (_keyboardArrowUpDownTrigger)
{
_keyboardArrowUpDownTrigger = false;
await InvokeVoidAsync("scroll", Id, ScrollIntoViewOptions ?? new() { Behavior = ScrollIntoViewBehavior.Smooth, Block = ScrollIntoViewBlock.Center, Inline = ScrollIntoViewInline.Nearest });
await InvokeVoidAsync("scroll", Id, ScrollIntoViewOptions);
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, new { Invoke = Interop, Method = nameof(TriggerKeyDown), IsVirtualize, AutoCheckParent, AutoCheckChildren });
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, new { Invoke = Interop, Method = nameof(TriggerKeyDown) });

private bool _keyboardArrowUpDownTrigger;

Expand Down Expand Up @@ -431,17 +431,11 @@ public async ValueTask TriggerKeyDown(string key)
/// 客户端查询指定行选择框状态方法 由 JavaScript 调用
/// </summary>
/// <param name="items"></param>
/// <param name="index"></param>
/// <param name="state"></param>
/// <returns></returns>
[JSInvokable]
public ValueTask<List<CheckboxState>> GetParentsState(List<int> items, int index, CheckboxState? state)
public ValueTask<List<CheckboxState>> GetParentsState(List<int> items)
{
var rows = Rows;
if (state.HasValue)
{
rows[index].CheckedState = state.Value;
}
var result = items.Select(i =>
{
var item = rows[i];
Expand Down Expand Up @@ -719,16 +713,16 @@ private async Task OnCheckStateChanged(TreeViewItem<TItem> item, CheckboxState s
// 向下级联操作
if (item.CheckedState != CheckboxState.Indeterminate)
{
_ = InvokeVoidAsync("setChildrenState", Id, Rows.IndexOf(item), item.CheckedState);
item.SetChildrenCheck(TreeNodeStateCache);
_ = InvokeVoidAsync("setChildrenState", Id, Rows.IndexOf(item), item.CheckedState);
}
}

if (AutoCheckParent)
{
// 向上级联操作
item.SetParentCheck(TreeNodeStateCache);
_ = InvokeVoidAsync("setParentState", Id, Rows.IndexOf(item));
_ = InvokeVoidAsync("setParentState", Id, Interop, nameof(GetParentsState), Rows.IndexOf(item));
}

if (OnTreeItemChecked != null)
Expand Down
140 changes: 61 additions & 79 deletions src/BootstrapBlazor/Components/TreeView/TreeView.razor.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import Data from "../../modules/data.js"
import EventHandler from "../../modules/event-handler.js"
import EventHandler from "../../modules/event-handler.js"

export function init(id, options) {
const { invoke, method, isVirtualize } = options
const el = document.getElementById(id)
if (el === null) {
return
}

const tree = { el, invoke, isVirtualize };
Data.set(id, tree)

const { invoke, method } = options
EventHandler.on(el, 'keydown', '.tree-root', e => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
const v = el.getAttribute('data-bb-keyboard');
Expand All @@ -33,103 +29,89 @@ export function init(id, options) {
}

export function scroll(id, options) {
const tree = Data.get(id)
if (tree) {
const { el } = tree;
const item = el.querySelector(".active .tree-node");
if (item) {
item.scrollIntoView(options);
}
const el = document.getElementById(id);
const item = el.querySelector(".active .tree-node");
if (item) {
item.scrollIntoView(options ?? { behavior: 'smooth', block: 'start', inline: 'nearest' });
}
}

export function toggleLoading(id, index, state) {
const tree = Data.get(id)
if (tree) {
const { el } = tree;
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
if (node) {
if (state) {
node.classList.add('loading');
}
else {
node.classList.remove('loading');
}
const el = document.getElementById(id);
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
if (node) {
if (state) {
node.classList.add('loading');
}
else {
node.classList.remove('loading');
}
}
}

export function setChildrenState(id, index, state) {
const tree = Data.get(id)
if (tree) {
const { el } = tree;
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
const level = parseInt(node.style.getPropertyValue('--bb-tree-view-level'));
if (node) {
let next = node.nextElementSibling;
while (next) {
const currentLevel = parseInt(next.style.getPropertyValue('--bb-tree-view-level'));
if (currentLevel <= level) {
break;
}
const checkbox = next.querySelector('.form-check-input');
if (checkbox) {
checkbox.indeterminate = false;
checkbox.checked = state === 1;
EventHandler.trigger(checkbox, "statechange.bb.checkbox", { state });
}
next = next.nextElementSibling;
const el = document.getElementById(id);
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
const level = parseInt(node.style.getPropertyValue('--bb-tree-view-level'));
if (node) {
let next = node.nextElementSibling;
while (next) {
const currentLevel = parseInt(next.style.getPropertyValue('--bb-tree-view-level'));
if (currentLevel <= level) {
break;
}
const checkbox = next.querySelector('.form-check-input');
if (checkbox) {
checkbox.indeterminate = false;
checkbox.checked = state === 1;
EventHandler.trigger(checkbox, "statechange.bb.checkbox", { state });
}
next = next.nextElementSibling;
}
}
}

export async function setParentState(id, index, state) {
const tree = Data.get(id)
if (tree) {
const { el, invoke } = tree;
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
let level = parseInt(node.style.getPropertyValue('--bb-tree-view-level'));
if (node) {
const parents = [];
let prev = node.previousElementSibling;
while (prev) {
const currentLevel = parseInt(prev.style.getPropertyValue('--bb-tree-view-level'));
if (currentLevel < level) {
level = currentLevel;
parents.push(prev);
}
prev = prev.previousElementSibling;
export async function setParentState(id, invoke, method, index) {
const el = document.getElementById(id);
const node = el.querySelector(`[data-bb-tree-view-index="${index}"]`);
let level = parseInt(node.style.getPropertyValue('--bb-tree-view-level'));
if (node) {
const parents = [];
let prev = node.previousElementSibling;
while (prev) {
const currentLevel = parseInt(prev.style.getPropertyValue('--bb-tree-view-level'));
if (currentLevel < level) {
level = currentLevel;
parents.push(prev);
}
prev = prev.previousElementSibling;
}

if (parents.length > 0) {
const results = await invoke.invokeMethodAsync('GetParentsState', parents.map(p => parseInt(p.getAttribute('data-bb-tree-view-index'))), index, state);
for (let index = 0; index < parents.length; index++) {
const checkbox = parents[index].querySelector('.form-check-input');
const result = results[index];
checkbox.indeterminate = false;
if (result === 0) {
checkbox.checked = false;
}
else if (result === 1) {
checkbox.checked = true;
}
else {
checkbox.indeterminate = true;
}
EventHandler.trigger(checkbox, "statechange.bb.checkbox", { state: result });
if (parents.length > 0) {
const results = await invoke.invokeMethodAsync(method, parents.map(p => parseInt(p.getAttribute('data-bb-tree-view-index'))));
for (let index = 0; index < parents.length; index++) {
const checkbox = parents[index].querySelector('.form-check-input');
const result = results[index];
checkbox.indeterminate = false;
if (result === 0) {
checkbox.checked = false;
}
else if (result === 1) {
checkbox.checked = true;
}
else {
checkbox.indeterminate = true;
}
EventHandler.trigger(checkbox, "statechange.bb.checkbox", { state: result });
}
}
}
}

export function dispose(id) {
const tree = Data.get(id)
Data.remove(id);
const el = document.getElementById(id);

if (tree) {
const { el } = tree;
if (el) {
EventHandler.off(el, 'keyup', '.tree-root');
}
}
17 changes: 3 additions & 14 deletions test/UnitTest/Components/TreeViewTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,6 @@ public async Task GetParentsState_Ok()
Assert.Equal(CheckboxState.Checked, checkboxes[1].Instance.State);

await cut.InvokeAsync(() => cut.Find(".fa-caret-right.visible").Click());

cut.WaitForState(() => cut.Instance.Items[0].Items.Count > 0);
// 101 unchecked
// -> 101-101 unchecked
Expand All @@ -516,21 +515,11 @@ public async Task GetParentsState_Ok()

checkboxes = cut.FindComponents<Checkbox<TreeViewItem<TreeFoo>>>();
var parents = new List<int>() { 0 };
List<CheckboxState> results = await cut.Instance.GetParentsState(parents, 1, CheckboxState.Checked);
Assert.NotNull(results);
Assert.Equal(CheckboxState.Checked, checkboxes[1].Instance.Value.CheckedState);
Assert.Equal(CheckboxState.Checked, checkboxes[0].Instance.Value.CheckedState);

Assert.Single(results);
Assert.Equal(CheckboxState.Checked, results[0]);

// 更改第二个子节点状态
checkboxes = cut.FindComponents<Checkbox<TreeViewItem<TreeFoo>>>();
results = await cut.Instance.GetParentsState(parents, 2, CheckboxState.UnChecked);
List<CheckboxState> results = await cut.Instance.GetParentsState(parents);
Assert.NotNull(results);
Assert.Equal(CheckboxState.Indeterminate, checkboxes[0].Instance.Value.CheckedState);
Assert.Equal(CheckboxState.Checked, checkboxes[1].Instance.Value.CheckedState);
Assert.Equal(CheckboxState.UnChecked, checkboxes[2].Instance.Value.CheckedState);
Assert.Equal(CheckboxState.UnChecked, checkboxes[1].Instance.Value.CheckedState);
Assert.Equal(CheckboxState.Checked, checkboxes[2].Instance.Value.CheckedState);

Assert.Single(results);
Assert.Equal(CheckboxState.Indeterminate, results[0]);
Expand Down

0 comments on commit 333dd4d

Please sign in to comment.