Skip to content

Commit

Permalink
feat(Input): add Clearable parameter (#4991)
Browse files Browse the repository at this point in the history
* feat: 增加 InputClearIcon 定义

* feat: 增加 Clearable 属性

* refactor: 禁用模式下不显示 Clear 按钮

* test: 增加单元测试

* chore: bump version 9.2.0-beta01

Co-Authored-By: zph19970424 <[email protected]>

---------

Co-authored-by: zph19970424 <[email protected]>
  • Loading branch information
ArgoZhang and zph19970424 authored Dec 29, 2024
1 parent 70ebace commit f83730d
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 4 deletions.
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.0-beta03</Version>
<Version>9.2.0-beta01</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
23 changes: 22 additions & 1 deletion src/BootstrapBlazor/Components/Input/BootstrapInput.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,25 @@
<BootstrapLabel required="@Required" for="@Id" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
}

<input @attributes="@AdditionalAttributes" type="@Type" placeholder="@PlaceHolder" id="@Id" readonly="@ReadonlyString" class="@ClassName" disabled="@Disabled" @bind-value="CurrentValueAsString" @bind-value:event="@EventString" @onblur="@OnBlur" @ref="FocusElement" />
@if (Clearable)
{
<div class="bb-clearable-input">
@RenderInput
@if (!IsDisabled && !Readonly)
{
<i class="@ClearableIconString" @onclick="OnClickClear"></i>
}
</div>
}
else
{
@RenderInput
}

@code {
RenderFragment RenderInput =>
@<input @attributes="@AdditionalAttributes" type="@Type" id="@Id" class="@ClassName"
readonly="@ReadonlyString" disabled="@Disabled"
placeholder="@PlaceHolder"
@bind-value="CurrentValueAsString" @bind-value:event="@EventString" @onblur="@OnBlur" @ref="FocusElement" />;
}
48 changes: 48 additions & 0 deletions src/BootstrapBlazor/Components/Input/BootstrapInput.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,47 @@ public partial class BootstrapInput<TValue>
[Parameter]
public bool AutoSetDefaultWhenNull { get; set; }

/// <summary>
/// 获得/设置 是否显示清空小按钮 默认 false
/// </summary>
[Parameter]
public bool Clearable { get; set; }

/// <summary>
/// 获得/设置 清空文本框时回调方法 默认 null
/// </summary>
[Parameter]
public Func<TValue, Task>? OnClear { get; set; }

/// <summary>
/// 获得/设置 清空小按钮图标 默认 null
/// </summary>
[Parameter]
public string? ClearableIcon { get; set; }

/// <summary>
/// 图标主题服务
/// </summary>
[Inject]
[NotNull]
private IIconTheme? IconTheme { get; set; }

private string? ReadonlyString => Readonly ? "true" : null;

private string? ClearableIconString => CssBuilder.Default("form-control-clear-icon")
.AddClass(ClearableIcon)
.Build();

/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();

ClearableIcon ??= IconTheme.GetIconByKey(ComponentIcons.InputClearIcon);
}

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand All @@ -47,4 +86,13 @@ protected override bool TryParseValueFromString(string value, [MaybeNullWhen(fal
}
return ret;
}

private async Task OnClickClear()
{
if (OnClear != null)
{
await OnClear(Value);
}
CurrentValueAsString = "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function handleKeyUp(id, invoke, enter, enterCallbackMethod, esc, escCall
const el = document.getElementById(id)
if (el) {
EventHandler.on(el, 'keyup', e => {
if (enter && e.key === 'Enter') {
if (enter && (e.key === 'Enter' || e.key === 'NumpadEnter')) {
invoke.invokeMethodAsync(enterCallbackMethod, el.value)
}
else if (esc && e.key === 'Escape') {
Expand Down
29 changes: 29 additions & 0 deletions src/BootstrapBlazor/Components/Input/BootstrapInput.razor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.bb-clearable-input {
display: inline-flex;
align-items: center;
flex-grow: 1;
width: 100%;
position: relative;

> input {
width: 100%;
flex-grow: 1;
padding-right: 2rem;

+ .form-control-clear-icon {
color: var(--bb-border-hover-color);
cursor: pointer;
position: absolute;
right: 11px;
display: none;
}

&:focus + .form-control-clear-icon {
display: block;
}
}

&:hover .form-control-clear-icon {
display: block;
}
}
7 changes: 6 additions & 1 deletion src/BootstrapBlazor/Enums/ComponentIcons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -818,5 +818,10 @@ public enum ComponentIcons
/// <summary>
/// ThemeProvider 组件 明亮模式图标
/// </summary>
ThemeProviderActiveModeIcon
ThemeProviderActiveModeIcon,

/// <summary>
/// Input 组件 ClearIcon 图标
/// </summary>
InputClearIcon
}
2 changes: 2 additions & 0 deletions src/BootstrapBlazor/Options/IconThemeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ public IconThemeOptions()

{ ComponentIcons.ImageViewerFileIcon, "fa-regular fa-file-image" },

{ ComponentIcons.InputClearIcon, "fa-regular fa-circle-xmark" },

{ ComponentIcons.InputNumberMinusIcon, "fa-solid fa-circle-minus" },
{ ComponentIcons.InputNumberPlusIcon, "fa-solid fa-circle-plus" },

Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor/wwwroot/scss/components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
@import "../../Components/IFrame/IFrame.razor.scss";
@import "../../Components/ImagePreviewer/ImagePreviewer.razor.scss";
@import "../../Components/ImageViewer/ImageViewer.razor.scss";
@import "../../Components/Input/BootstrapInput.razor.scss";
@import "../../Components/Input/BootstrapInputGroup.razor.scss";
@import "../../Components/Input/FloatingLabel.razor.scss";
@import "../../Components/IpAddress/IpAddress.razor.scss";
Expand Down
36 changes: 36 additions & 0 deletions test/UnitTest/Components/InputTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,42 @@ public void Readonly_Ok()
cut.Contains("readonly=\"true\"");
}

[Fact]
public void Clearable_Ok()
{
var cut = Context.RenderComponent<BootstrapInput<string>>(builder => builder.Add(a => a.Clearable, false));
cut.DoesNotContain("bb-clearable-input");

cut.SetParametersAndRender(pb => pb.Add(a => a.Clearable, true));
cut.Contains("bb-clearable-input");
cut.Contains("form-control-clear-icon");

cut.SetParametersAndRender(pb => pb.Add(a => a.Readonly, true));
cut.DoesNotContain("form-control-clear-icon");

cut.SetParametersAndRender(pb => pb.Add(a => a.Readonly, false));
cut.SetParametersAndRender(pb => pb.Add(a => a.IsDisabled, true));
cut.DoesNotContain("form-control-clear-icon");
}

[Fact]
public async Task OnClear_Ok()
{
var clicked = false;
var cut = Context.RenderComponent<BootstrapInput<string>>(builder =>
{
builder.Add(a => a.Clearable, true);
builder.Add(a => a.OnClear, v =>
{
clicked = true;
return Task.CompletedTask;
});
});
var icon = cut.Find(".form-control-clear-icon");
await cut.InvokeAsync(() => icon.Click());
Assert.True(clicked);
}

[Fact]
public async Task OnInput_Ok()
{
Expand Down

0 comments on commit f83730d

Please sign in to comment.