Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Input): add Clearable parameter #4991

Merged
merged 5 commits into from
Dec 29, 2024
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
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;
ArgoZhang marked this conversation as resolved.
Show resolved Hide resolved
}

&: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");
ArgoZhang marked this conversation as resolved.
Show resolved Hide resolved

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());
ArgoZhang marked this conversation as resolved.
Show resolved Hide resolved
Assert.True(clicked);
}

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