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
4 changes: 4 additions & 0 deletions src/BootstrapBlazor.Server/Components/Samples/Buttons.razor
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@
</div>
</DemoBlock>

<DemoBlock Title="@Localizer["ToggleButton"]" Introduction="@Localizer["ToggleIntroduction"]" Name="Toggle">
<ToggleButton Text="Toggle" Color="Color.Danger"></ToggleButton>
</DemoBlock>

<AttributeTable Items="@GetAttributes()" />

<EventTable Items="@GetEvents()" />
Expand Down
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,9 @@
"TooltipText": "Button",
"TooltipTitle": "Tooltip",
"TooltipIntro": "Set <code>TooltipText</code> <code>TooltipPlacement</code> <code>TooltipTrigger</code> shortcut button prompt bar information, position, and triggering method. For more functions, please use the <code>Tooltip</code> component to implement. In this example, the second button is in the <b>disabled</b> state, and the prompt bar is still available",
"TooltipDisabledText": "Disabled"
"TooltipDisabledText": "Disabled",
"ToggleButton": "Toggle",
"ToggleIntroduction": "Get the current button <code>Toggle</code> state by setting the <code>IsActiveChanged</code> or <code>OnToggleAsync</code> callback method"
},
"BootstrapBlazor.Server.Components.Samples.PulseButtons": {
"Block1Title": "Basic usage",
Expand Down
4 changes: 3 additions & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,9 @@
"TooltipText": "按钮",
"TooltipTitle": "按钮提示栏",
"TooltipIntro": "通过设置 <code>TooltipText</code> <code>TooltipPlacement</code> <code>TooltipTrigger</code> 快捷设置按钮提示栏信息、位置、触发方式,更多功能请使用 <code>Tooltip</code> 组件实现,本例中第二个按钮是 <b>禁用</b> 状态,提示栏仍然可用",
"TooltipDisabledText": "禁用按钮"
"TooltipDisabledText": "禁用按钮",
"ToggleButton": "状态切换按钮",
"ToggleIntroduction": "通过设置 <code>IsActiveChanged</code> 或者 <code>OnToggleAsync</code> 回调方法获得当前按钮 <code>Toggle</code> 状态"
},
"BootstrapBlazor.Server.Components.Samples.PulseButtons": {
"Block1Title": "基础用法",
Expand Down
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.9.1-beta04</Version>
<Version>9.9.1-beta05</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions src/BootstrapBlazor/Components/Button/ToggleButton.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@namespace BootstrapBlazor.Components
@inherits ButtonBase

<button @attributes="@AdditionalAttributes" @onclick="@OnClickButton" id="@Id" class="@ToggleClassName" disabled="@Disabled"
role="button" data-bs-placement="@PlacementString" data-bs-trigger="@TriggerString"
aria-disabled="@DisabledString" tabindex="@Tab" data-bs-toggle="button"
@onclick:stopPropagation="@StopPropagation">
@if(IsAsyncLoading)
{
<i class="@LoadingIcon"></i>
}
else if (!string.IsNullOrEmpty(Icon))
{
<i class="@Icon"></i>
}
@if (!string.IsNullOrEmpty(Text))
{
<span>@Text</span>
}
<CascadingValue Value="this" IsFixed="true">
@ChildContent
</CascadingValue>
</button>
81 changes: 81 additions & 0 deletions src/BootstrapBlazor/Components/Button/ToggleButton.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

namespace BootstrapBlazor.Components;

/// <summary>
/// Toggle Button 按钮组件
/// </summary>
public partial class ToggleButton
{
/// <summary>
/// 获得/设置 状态切换回调方法
/// </summary>
[Parameter]
public Func<bool, Task>? OnToggleAsync { get; set; }

/// <summary>
/// 获得/设置 当前状态是否为激活状态 默认 false
/// </summary>
[Parameter]
public bool IsActive { get; set; }

/// <summary>
/// 获得/设置 激活状态回调方法
/// </summary>
[Parameter]
public EventCallback<bool> IsActiveChanged { get; set; }

private string? ToggleClassName => CssBuilder.Default(ClassName)
.AddClass("active", IsActive)
.Build();

private async Task OnClickButton()
{
if (IsAsync)
{
IsAsyncLoading = true;
IsDisabled = true;
}

await HandlerClick();

// 恢复按钮
if (IsAsync)
{
IsDisabled = IsKeepDisabled;
IsAsyncLoading = false;
}
}

private async Task HandlerClick()
{
IsActive = !IsActive;
if (OnClickWithoutRender != null)
{
if (!IsAsync)
{
IsNotRender = true;
}

await OnClickWithoutRender();
}

if (OnClick.HasDelegate)
{
await OnClick.InvokeAsync();
}

if (IsActiveChanged.HasDelegate)
{
await IsActiveChanged.InvokeAsync(IsActive);
}

if (OnToggleAsync != null)
{
await OnToggleAsync(IsActive);
}
}
}
41 changes: 41 additions & 0 deletions test/UnitTest/Components/ButtonTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,4 +452,45 @@
Assert.Equal("test-share-title", cut.Instance.ShareContext?.Title);
Assert.Equal("www.blazor.zone", cut.Instance.ShareContext?.Url);
}

[Fact]
public async Task ToogleButton_Ok()
{
var active = false;
var bindActive = false;

Check warning on line 460 in test/UnitTest/Components/ButtonTest.cs

View workflow job for this annotation

GitHub Actions / run test

The variable 'bindActive' is assigned but its value is never used
var clickWithoutRender = false;
var clicked = false;
var tcs = new TaskCompletionSource();
var cut = Context.RenderComponent<ToggleButton>(pb =>
{
pb.Add(a => a.IsActive, false);
pb.Add(a => a.IsActiveChanged, EventCallback.Factory.Create<bool>(this, b =>
{
active = b;
bindActive = true;
}));
pb.Add(a => a.OnClickWithoutRender, () =>
{
clickWithoutRender = true;
return Task.CompletedTask;
});
pb.Add(a => a.OnClick, () =>
{
clicked = true;
return Task.CompletedTask;
});
pb.Add(a => a.OnToggleAsync, async isActive =>
{
await Task.Delay(10);
active = isActive;
tcs.TrySetResult();
});
});
var button = cut.Find("button");
await cut.InvokeAsync(() => button.Click());
await tcs.Task;
Assert.True(active);
Assert.True(clickWithoutRender);
Assert.True(clicked);
}
}
21 changes: 21 additions & 0 deletions test/UnitTest/Components/TableTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8720,6 +8720,27 @@ public void Modify_Ok()
Assert.True(ProhibitDelete(cut.Instance));
}

[Fact]
public void Table_Sortable()
{
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
var cut = Context.RenderComponent<Table<Foo>>(pb =>
{
pb.AddCascadingValue<ISortableList>(new SortableList());
pb.Add(a => a.TableColumns, foo => builder =>
{
builder.OpenComponent<TableColumn<Foo, string>>(0);
builder.AddAttribute(1, "Field", "Name");
builder.AddAttribute(2, "FieldExpression", Utility.GenerateValueExpression(foo, "Name", typeof(string)));
builder.CloseComponent();
});
pb.Add(a => a.RenderMode, TableRenderMode.Table);
pb.Add(a => a.Items, Foo.GenerateFoo(localizer));
});
}

class SortableList : ISortableList { }

static bool ProhibitEdit(Table<Foo> @this)
{
var ret = false;
Expand Down
Loading