@ChildContent
diff --git a/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.cs b/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.cs
index 8f7874ef3b7..ae84d07ae3a 100644
--- a/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.cs
+++ b/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.cs
@@ -25,8 +25,26 @@ public sealed partial class DropdownWidget
[Parameter]
public IEnumerable
? Items { get; set; }
+ ///
+ /// 获得/设置 下拉项关闭回调方法
+ ///
+ [Parameter]
+ public Func? OnItemCloseAsync { get; set; }
+
+ ///
+ /// 获得/设置 下拉项关闭回调方法
+ ///
+ [Parameter]
+ public Func? OnItemShownAsync { get; set; }
+
private List Childs { get; } = new List(20);
+ ///
+ ///
+ ///
+ ///
+ protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, new { Method = nameof(TriggerStateChanged) });
+
///
/// 添加 DropdownWidgetItem 方法
///
@@ -37,4 +55,28 @@ internal void Add(DropdownWidgetItem item)
}
private IEnumerable GetItems() => Items == null ? Childs : Childs.Concat(Items);
+
+ ///
+ /// Widget 下拉项关闭回调方法 由 JavaScript 调用
+ ///
+ ///
+ ///
+ ///
+ [JSInvokable]
+ public async Task TriggerStateChanged(int index, bool shown)
+ {
+ var items = GetItems().ToList();
+ var item = index < items.Count ? items[index] : null;
+ if (item != null)
+ {
+ if (OnItemCloseAsync != null && !shown)
+ {
+ await OnItemCloseAsync(item);
+ }
+ else if (OnItemShownAsync != null && shown)
+ {
+ await OnItemShownAsync(item);
+ }
+ }
+ }
}
diff --git a/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.js b/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.js
new file mode 100644
index 00000000000..f23422284bc
--- /dev/null
+++ b/src/BootstrapBlazor/Components/DropdownWidget/DropdownWidget.razor.js
@@ -0,0 +1,23 @@
+import EventHandler from "../../modules/event-handler.js"
+
+export function init(id, invoke, options) {
+ const el = document.getElementById(id);
+ if (el === null) {
+ return;
+ }
+
+ const invokeMethod = e => {
+ const item = e.target;
+ const items = [...el.querySelectorAll("[data-bs-toggle=\"dropdown\"]")];
+ const index = items.indexOf(item);
+ invoke.invokeMethodAsync(method, index, e.type === 'shown.bs.dropdown');
+ }
+
+ const { method } = options;
+ EventHandler.on(el, 'shown.bs.dropdown', invokeMethod);
+ EventHandler.on(el, 'hidden.bs.dropdown', invokeMethod);
+}
+
+export function dispose(id) {
+ EventHandler.off(el, 'hidden.bs.dropdown');
+}
diff --git a/test/UnitTest/Components/DropdownWigetTest.cs b/test/UnitTest/Components/DropdownWidgetTest.cs
similarity index 75%
rename from test/UnitTest/Components/DropdownWigetTest.cs
rename to test/UnitTest/Components/DropdownWidgetTest.cs
index 8dfe7ff9e62..9bc26dccbf2 100644
--- a/test/UnitTest/Components/DropdownWigetTest.cs
+++ b/test/UnitTest/Components/DropdownWidgetTest.cs
@@ -4,7 +4,7 @@
namespace UnitTest.Components;
-public class DropdownWigetTest : BootstrapBlazorTestBase
+public class DropdownWidgetTest : BootstrapBlazorTestBase
{
[Fact]
public void Items_OK()
@@ -54,12 +54,12 @@ public void Title_OK()
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent(0);
- builder.AddAttribute(1, nameof(DropdownWidgetItem.Title), "Wiget Title");
+ builder.AddAttribute(1, nameof(DropdownWidgetItem.Title), "Widget Title");
builder.CloseComponent();
}));
});
- Assert.Contains("Wiget Title", cut.Markup);
+ Assert.Contains("Widget Title", cut.Markup);
}
[Fact]
@@ -181,10 +181,60 @@ public void HeaderColor_OK()
Assert.NotNull(ele);
}
- private static IEnumerable GetItems()
+ [Fact]
+ public async Task OnItemAsync_OK()
+ {
+ var shown = false;
+ var closed = false;
+ var cut = Context.RenderComponent(builder =>
+ {
+ builder.Add(a => a.OnItemShownAsync, item =>
+ {
+ shown = true;
+ return Task.CompletedTask;
+ });
+ builder.Add(s => s.ChildContent, new RenderFragment(builder =>
+ {
+ builder.OpenComponent(0);
+ builder.AddAttribute(1, nameof(DropdownWidgetItem.HeaderColor), Color.Success);
+ builder.AddAttribute(2, nameof(DropdownWidgetItem.Title), "Test1");
+ builder.CloseComponent();
+
+ builder.OpenComponent(0);
+ builder.AddAttribute(10, nameof(DropdownWidgetItem.HeaderColor), Color.Success);
+ builder.AddAttribute(11, nameof(DropdownWidgetItem.Title), "Test2");
+ builder.CloseComponent();
+ }));
+ });
+
+ // 索引越界
+ await cut.InvokeAsync(() => cut.Instance.TriggerStateChanged(2, false));
+ Assert.False(closed);
+
+ // 未注册 OnItemCloseAsync 回调
+ await cut.InvokeAsync(() => cut.Instance.TriggerStateChanged(1, false));
+ Assert.False(closed);
+
+ // 触发 OnItemShownAsync 回调
+ await cut.InvokeAsync(() => cut.Instance.TriggerStateChanged(0, true));
+ Assert.True(shown);
+
+ cut.SetParametersAndRender(pb =>
+ {
+ pb.Add(a => a.OnItemCloseAsync, item =>
+ {
+ closed = true;
+ return Task.CompletedTask;
+ });
+ });
+ await cut.InvokeAsync(() => cut.Instance.TriggerStateChanged(1, false));
+ Assert.True(closed);
+ }
+
+ private static List GetItems()
{
var ret = new List();
- var wiget = new DropdownWidgetItem();
+ var widget = new DropdownWidgetItem();
var parameters = new Dictionary()
{
["Icon"] = "fa-regular fa-bell",
@@ -207,8 +257,8 @@ private static IEnumerable GetItems()
}),
};
- wiget.SetParametersAsync(ParameterView.FromDictionary(parameters!));
- ret.Add(wiget);
+ widget.SetParametersAsync(ParameterView.FromDictionary(parameters!));
+ ret.Add(widget);
return ret;
}
}