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 @@ -6,7 +6,10 @@
<h4>@Localizer["Intro"]</h4>

<DemoBlock Title="@Localizer["NormalTitle"]" Introduction="@Localizer["NormalIntro"]" Name="Normal">
<SelectTable TItem="Foo" @bind-Value="@_foo" OnQueryAsync="OnQueryAsync" GetTextCallback="@GetTextCallback" TableMinWidth="300">
<section ignore>
@((MarkupString)Localizer["NormalDesc"].Value)
</section>
<SelectTable TItem="Foo" @bind-Value="@_foo" OnQueryAsync="OnQueryAsync" GetTextCallback="@GetTextCallback" TableMinWidth="300" IsClearable>
<TableColumns>
<TableColumn @bind-Field="@context.Name"></TableColumn>
<TableColumn @bind-Field="@context.Address"></TableColumn>
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -6567,6 +6567,7 @@
"Intro": "The dropdown box is a table used to display the selection requirements for complex types",
"NormalTitle": "Basic usage",
"NormalIntro": "Suitable for candidates with a relatively large amount of information, presenting information using <code>Table</code>",
"NormalDesc": "You can use <code>IsClearable</code> to control whether to display the clear button. The default value is <code>false</code>",
"ColorTitle": "Color",
"ColorIntro": "Change component border color by setting <code>Color</code>",
"IsDisabledTitle": "Disabled",
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -6567,6 +6567,7 @@
"Intro": "下拉框为表格用于展示复杂类型的选择需求",
"NormalTitle": "基本功能",
"NormalIntro": "适用于候选项信息量比较大,用 <code>Table</code> 呈现信息量",
"NormalDesc": "可通过 <code>IsClearable</code> 控制是否显示清除小按钮,默认值 <code>false</code>",
"ColorTitle": "颜色",
"ColorIntro": "通过设置 <code>Color</code> 改变组件边框颜色",
"IsDisabledTitle": "禁用",
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.2.6</Version>
<Version>9.2.7-beta01</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
36 changes: 19 additions & 17 deletions src/BootstrapBlazor/Components/Select/Select.razor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -147,25 +147,27 @@
transform: rotate(0);
}

.select .clear-icon {
position: absolute;
height: 100%;
width: var(--bb-select-append-width);
right: 0;
top: 0;
color: var(--bb-select-append-color);
align-items: center;
justify-content: center;
cursor: pointer;
display: none;
}
.select {
.clear-icon {
position: absolute;
height: 100%;
width: var(--bb-select-append-width);
right: 0;
top: 0;
color: var(--bb-select-append-color);
align-items: center;
justify-content: center;
cursor: pointer;
display: none;
}

.select:hover .clear-icon {
display: flex;
}
&:hover .clear-icon {
display: flex;
}

.select.cls:hover .form-select-append {
display: none;
&.cls:hover .form-select-append {
display: none;
}
}

.form-select.is-valid:focus,
Expand Down
4 changes: 4 additions & 0 deletions src/BootstrapBlazor/Components/Select/SelectTable.razor
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
<span class="@AppendClassString"><i class="@DropdownIcon"></i></span>
}
</div>
@if (GetClearable())
{
<span class="@ClearClassString" @onclick="OnClearValue"><i class="@ClearIcon"></i></span>
}
<CascadingValue Value="this" IsFixed="true">
@TableColumns?.Invoke(new TItem())
</CascadingValue>
Expand Down
39 changes: 39 additions & 0 deletions src/BootstrapBlazor/Components/Select/SelectTable.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ namespace BootstrapBlazor.Components;
[NotNull]
public string? DropdownIcon { get; set; }

/// <summary>
/// 获得/设置 是否可清除 默认 false
/// </summary>
[Parameter]
public bool IsClearable { get; set; }

/// <summary>
/// 获得/设置 IIconTheme 服务实例
/// </summary>
Expand All @@ -78,6 +84,7 @@ namespace BootstrapBlazor.Components;
/// </summary>
private string? ClassName => CssBuilder.Default("select select-table dropdown")
.AddClass("disabled", IsDisabled)
.AddClass("cls", IsClearable)
.AddClassFromAttributes(AdditionalAttributes)
.Build();

Expand All @@ -101,6 +108,15 @@ namespace BootstrapBlazor.Components;
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
.Build();

private bool GetClearable() => IsClearable && !IsDisabled;

/// <summary>
/// 获得/设置 右侧清除图标 默认 fa-solid fa-angle-up
/// </summary>
[Parameter]
[NotNull]
public string? ClearIcon { get; set; }

/// <summary>
/// 获得 PlaceHolder 属性
/// </summary>
Expand Down Expand Up @@ -174,6 +190,12 @@ namespace BootstrapBlazor.Components;
[Parameter]
public bool AutoGenerateColumns { get; set; }

/// <summary>
/// 获得/设置 清除文本内容 OnClear 回调方法 默认 null
/// </summary>
[Parameter]
public Func<Task>? OnClearAsync { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<Select<TItem>>? Localizer { get; set; }
Expand All @@ -191,6 +213,12 @@ namespace BootstrapBlazor.Components;

private string GetStyleString => $"height: {Height}px;";

private string? ClearClassString => CssBuilder.Default("clear-icon")
.AddClass($"text-{Color.ToDescriptionString()}", Color != Color.None)
.AddClass($"text-success", IsValid.HasValue && IsValid.Value)
.AddClass($"text-danger", IsValid.HasValue && !IsValid.Value)
.Build();

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand Down Expand Up @@ -220,6 +248,7 @@ protected override void OnParametersSet()

PlaceHolder ??= Localizer[nameof(PlaceHolder)];
DropdownIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectDropdownIcon);
ClearIcon ??= IconTheme.GetIconByKey(ComponentIcons.SelectClearIcon);
}

/// <summary>
Expand All @@ -239,4 +268,14 @@ private async Task OnClickRowCallback(TItem item)
CurrentValue = item;
await InvokeVoidAsync("close", Id);
}

private async Task OnClearValue()
{
if (OnClearAsync != null)
{
await OnClearAsync();
}

await OnClickRowCallback(default!);
}
}
49 changes: 49 additions & 0 deletions test/UnitTest/Components/SelectTableTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,44 @@ public void Items_Ok()
});
}

[Fact]
public async Task IsClearable_Ok()
{
var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>();
var items = Foo.GenerateFoo(localizer, 4);
var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb =>
{
pb.AddChildContent<SelectTable<Foo>>(pb =>
{
pb.Add(a => a.OnQueryAsync, options => OnFilterQueryAsync(options, items));
pb.Add(a => a.GetTextCallback, foo => foo.Name);
});
});
var table = cut.FindComponent<SelectTable<Foo>>();
Assert.DoesNotContain("clear-icon", table.Markup);

var isClear = false;
table.SetParametersAndRender(pb =>
{
pb.Add(a => a.IsClearable, true);
pb.Add(a => a.Value, items[0]);
pb.Add(a => a.OnClearAsync, () =>
{
isClear = true;
return Task.CompletedTask;
});
});
Assert.Contains("clear-icon", table.Markup);
var input = table.Find(".form-select");
Assert.Equal("张三 0001", input.GetAttribute("value"));

var span = table.Find(".clear-icon");
await table.InvokeAsync(() => span.Click());
input = table.Find(".form-select");
Assert.Null(input.GetAttribute("value"));
Assert.True(isClear);
}

[Fact]
public void TableMinWidth_Ok()
{
Expand Down Expand Up @@ -65,9 +103,13 @@ public void Color_Ok()
pb.Add(a => a.Color, Color.Danger);
pb.Add(a => a.GetTextCallback, foo => foo.Name);
pb.Add(a => a.OnQueryAsync, options => OnFilterQueryAsync(options, items));
pb.Add(a => a.IsClearable, true);
});
});
cut.Contains("border-danger");

var span = cut.Find(".clear-icon");
Assert.True(span.ClassList.Contains("text-danger"));
}

[Fact]
Expand Down Expand Up @@ -271,6 +313,7 @@ public async Task Validate_Ok()
builder.Add(a => a.Model, model);
builder.AddChildContent<SelectTable<Foo>>(pb =>
{
pb.Add(a => a.IsClearable, true);
pb.Add(a => a.Value, model.Foo);
pb.Add(a => a.ValueExpression, Utility.GenerateValueExpression(model, "Foo", typeof(Foo)));
pb.Add(a => a.OnValueChanged, v =>
Expand Down Expand Up @@ -302,6 +345,9 @@ await cut.InvokeAsync(() =>
});
Assert.True(valid);

var span = cut.Find(".clear-icon");
Assert.True(span.ClassList.Contains("text-success"));

model.Foo = null;
var table = cut.FindComponent<SelectTable<Foo>>();
table.SetParametersAndRender();
Expand All @@ -311,6 +357,9 @@ await cut.InvokeAsync(() =>
form.Submit();
});
Assert.True(invalid);

span = cut.Find(".clear-icon");
Assert.True(span.ClassList.Contains("text-danger"));
}

[Fact]
Expand Down
Loading