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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<DemoBlock Title="@Localizer["BasicUsageTitle"]" Introduction="@Localizer["BasicUsageIntro"]" Name="Normal">
<div class="widget-demo">
<DropdownWidget>
<DropdownWidget OnItemCloseAsync="OnItemCloseAsync">
<DropdownWidgetItem Icon="fa-regular fa-envelope" BadgeNumber="4">
<HeaderTemplate>
<span>@Localizer["BasicUsageMessage"]</span>
Expand Down Expand Up @@ -81,7 +81,9 @@
</DropdownWidgetItem>
</DropdownWidget>
</div>

<section ignore>
<ConsoleLogger @ref="_logger"></ConsoleLogger>
</section>
</DemoBlock>

<AttributeTable Items="@GetAttributes()" Title="@Localizer["AttributeTitle"]" />
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ namespace BootstrapBlazor.Server.Components.Samples;
/// </summary>
public partial class DropdownWidgets
{
private ConsoleLogger _logger = default!;

private Task OnItemCloseAsync(DropdownWidgetItem item)
{
_logger.Log($"Item {item.BadgeNumber} closed");
return Task.CompletedTask;
}

private AttributeItem[] GetAttributes() =>
[
new()
Expand Down Expand Up @@ -82,6 +90,14 @@ private AttributeItem[] GetAttributes() =>
Type = "RenderFragment",
ValueList = " — ",
DefaultValue = " — "
},
new()
{
Name = "OnItemCloseAsync",
Description = Localizer["OnItemCloseAsync"],
Type = "Func<DropdownWidgetItem, Task>",
ValueList = " — ",
DefaultValue = " — "
}
];
}
3 changes: 2 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3980,7 +3980,8 @@
"BasicUsageNotify": "You have 10 unread notifications",
"BasicUsageViewNotify": "View all notifications",
"BasicUsageTasks": "You have 3 tasks",
"BasicUsageViewTasks": "View all tasks"
"BasicUsageViewTasks": "View all tasks",
"OnItemCloseAsync": "Close dropdown widget item callback method"
},
"BootstrapBlazor.Server.Components.Samples.Empties": {
"Title": "Empty",
Expand Down
3 changes: 2 additions & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -3980,7 +3980,8 @@
"BasicUsageNotify": "您有 10 个未读通知",
"BasicUsageViewNotify": "查看所有通知",
"BasicUsageTasks": "您有 3 个任务",
"BasicUsageViewTasks": "查看所有任务"
"BasicUsageViewTasks": "查看所有任务",
"OnItemCloseAsync": "关闭菜单项回调方法"
},
"BootstrapBlazor.Server.Components.Samples.Empties": {
"Title": "Empty 空状态",
Expand Down
25 changes: 1 addition & 24 deletions src/BootstrapBlazor.Server/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
},
"profiles": {
"WebConsole": {
"console": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
Expand All @@ -17,15 +17,6 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Watch": {
"commandName": "Executable",
"executablePath": "dotnet.exe",
"workingDirectory": "$(ProjectDir)",
"commandLineArgs": "watch run",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
Expand All @@ -35,26 +26,12 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"LanConsole": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://0.0.0.0/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}"
}
}
}
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>8.10.2-beta04</Version>
<Version>8.10.2-beta05</Version>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@namespace BootstrapBlazor.Components
@inherits BootstrapComponentBase
@inherits BootstrapModuleComponentBase
@attribute [BootstrapModuleAutoLoader(JSObjectReference = true)]

<div @attributes="@AdditionalAttributes" class="@ClassString">
<div @attributes="@AdditionalAttributes" class="@ClassString" id="@Id">
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,26 @@ public sealed partial class DropdownWidget
[Parameter]
public IEnumerable<DropdownWidgetItem>? Items { get; set; }

/// <summary>
/// 获得/设置 下拉项关闭回调方法
/// </summary>
[Parameter]
public Func<DropdownWidgetItem, Task>? OnItemCloseAsync { get; set; }

/// <summary>
/// 获得/设置 下拉项关闭回调方法
/// </summary>
[Parameter]
public Func<DropdownWidgetItem, Task>? OnItemShownAsync { get; set; }

private List<DropdownWidgetItem> Childs { get; } = new List<DropdownWidgetItem>(20);

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

/// <summary>
/// 添加 DropdownWidgetItem 方法
/// </summary>
Expand All @@ -37,4 +55,28 @@ internal void Add(DropdownWidgetItem item)
}

private IEnumerable<DropdownWidgetItem> GetItems() => Items == null ? Childs : Childs.Concat(Items);

/// <summary>
/// Widget 下拉项关闭回调方法 由 JavaScript 调用
/// </summary>
/// <param name="index"></param>
/// <param name="shown"></param>
/// <returns></returns>
[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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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');
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace UnitTest.Components;

public class DropdownWigetTest : BootstrapBlazorTestBase
public class DropdownWidgetTest : BootstrapBlazorTestBase
{
[Fact]
public void Items_OK()
Expand Down Expand Up @@ -54,12 +54,12 @@ public void Title_OK()
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent<DropdownWidgetItem>(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]
Expand Down Expand Up @@ -181,10 +181,60 @@ public void HeaderColor_OK()
Assert.NotNull(ele);
}

private static IEnumerable<DropdownWidgetItem> GetItems()
[Fact]
public async Task OnItemAsync_OK()
{
var shown = false;
var closed = false;
var cut = Context.RenderComponent<DropdownWidget>(builder =>
{
builder.Add(a => a.OnItemShownAsync, item =>
{
shown = true;
return Task.CompletedTask;
});
builder.Add(s => s.ChildContent, new RenderFragment(builder =>
{
builder.OpenComponent<DropdownWidgetItem>(0);
builder.AddAttribute(1, nameof(DropdownWidgetItem.HeaderColor), Color.Success);
builder.AddAttribute(2, nameof(DropdownWidgetItem.Title), "Test1");
builder.CloseComponent();

builder.OpenComponent<DropdownWidgetItem>(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<DropdownWidgetItem> GetItems()
{
var ret = new List<DropdownWidgetItem>();
var wiget = new DropdownWidgetItem();
var widget = new DropdownWidgetItem();
var parameters = new Dictionary<string, object?>()
{
["Icon"] = "fa-regular fa-bell",
Expand All @@ -207,8 +257,8 @@ private static IEnumerable<DropdownWidgetItem> GetItems()
}),

};
wiget.SetParametersAsync(ParameterView.FromDictionary(parameters!));
ret.Add(wiget);
widget.SetParametersAsync(ParameterView.FromDictionary(parameters!));
ret.Add(widget);
return ret;
}
}