Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
43 changes: 43 additions & 0 deletions src/BootstrapBlazor/Components/Filters/Filter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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>
/// Filter component
/// </summary>
public class Filter<TFilter> : FilterProvider where TFilter : IComponent
{
/// <summary>
/// 获得/设置 过滤器组件参数集合 Default is null
/// </summary>
[Parameter]
public IDictionary<string, object>? FilterParameters { get; set; }

/// <summary>
/// 渲染自定义过滤器方法
/// </summary>
/// <returns></returns>
protected override RenderFragment RenderFilter() => builder =>
{
var filterType = typeof(TFilter);
builder.OpenComponent<TFilter>(0);
if (filterType.IsSubclassOf(typeof(FilterBase)))
{
builder.AddAttribute(1, nameof(FilterBase.FieldKey), FieldKey);
builder.AddAttribute(2, nameof(FilterBase.IsHeaderRow), IsHeaderRow);
}
if (filterType.IsSubclassOf(typeof(MultipleFilterBase)))
{
builder.AddAttribute(10, nameof(MultipleFilterBase.Count), Count);
}

if (FilterParameters != null)
{
builder.AddMultipleAttributes(100, FilterParameters);
}
builder.CloseComponent();
};
}
20 changes: 20 additions & 0 deletions src/BootstrapBlazor/Components/Filters/FilterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public abstract class FilterBase : BootstrapModuleComponentBase, IFilterAction
[CascadingParameter, NotNull]
protected TableColumnFilter? TableColumnFilter { get; set; }

/// <summary>
/// Gets or sets the <see cref="FilterContext"/> instance from cascading parameter.
/// </summary>
[CascadingParameter]
protected FilterContext? FilterContext { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand All @@ -51,6 +57,20 @@ protected override void OnInitialized()
}
}

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

if (FilterContext != null)
{
FieldKey = FilterContext.FieldKey;
IsHeaderRow = FilterContext.IsHeaderRow;
}
}

/// <summary>
/// 重置按钮回调方法
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions src/BootstrapBlazor/Components/Filters/FilterContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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

using System.Runtime.CompilerServices;

namespace BootstrapBlazor.Components;

/// <summary>
/// FilterContext class
/// </summary>
public class FilterContext
{
/// <summary>
/// Gets or sets whether the filter is header row. Default is false.
/// </summary>
public bool IsHeaderRow { get; set; }

/// <summary>
/// Gets or sets the column field key. Default is null.
/// </summary>
public string? FieldKey { get; set; }

/// <summary>
/// Gets or sets the filter counter. Default is 0.
/// </summary>
public int Count { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
@namespace BootstrapBlazor.Components
@inherits ComponentBase
@typeparam TFilter

@if (_isHeaderRow)
@if (IsHeaderRow)
{
@RenderFilter()
}
Expand All @@ -17,12 +16,13 @@ else
<div class="d-flex flex-fill">
@if (ShowMoreButton)
{
<Button Color="Color.None" OnClick="OnClickPlus" Icon="@PlusIcon" IsDisabled="@(_count == 1)"></Button>
<Button Color="Color.None" OnClick="OnClickMinus" Icon="@MinusIcon" IsDisabled="@(_count == 0)"></Button>
<Button Color="Color.None" OnClick="OnClickPlus" Icon="@PlusIcon" IsDisabled="@(Count == 1)"></Button>
<Button Color="Color.None" OnClick="OnClickMinus" Icon="@MinusIcon" IsDisabled="@(Count == 0)"></Button>
}
</div>
<Button Color="Color.None" class="filter-dismiss" OnClick="OnClickReset" Text="@ClearButtonText"></Button>
<Button Color="Color.None" class="filter-dismiss" OnClick="OnClickConfirm" Text="@FilterButtonText"></Button>
</div>
</div>
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,10 @@
namespace BootstrapBlazor.Components;

/// <summary>
/// Filter 组件
/// FilterProvider component
/// </summary>
public partial class Filter<TFilter> where TFilter : IComponent
public partial class FilterProvider
{
/// <summary>
/// 获得/设置 过滤器组件参数集合 Default is null
/// </summary>
[Parameter]
public IDictionary<string, object>? FilterParameters { get; set; }

/// <summary>
/// 获得/设置 重置按钮文本
/// </summary>
Expand Down Expand Up @@ -53,10 +47,20 @@ public partial class Filter<TFilter> where TFilter : IComponent
/// <summary>
/// Gets or sets the filter title. Default is null.
/// </summary>
[Parameter]
public string? Title { get; set; }

/// <summary>
/// Gets or sets the child content. Default is null.
/// </summary>
[Parameter]
public RenderFragment? ChildContent { get; set; }

/// <summary>
/// Gets or sets the <see cref="TableColumnFilter"/> instance from cascading parameter.
/// </summary>
[CascadingParameter]
private TableColumnFilter? TableColumnFilter { get; set; }
protected TableColumnFilter? TableColumnFilter { get; set; }

[Inject]
[NotNull]
Expand All @@ -66,9 +70,20 @@ public partial class Filter<TFilter> where TFilter : IComponent
[NotNull]
private IIconTheme? IconTheme { get; set; }

private int _count;
private string? _fieldKey;
private bool _isHeaderRow = false;
/// <summary>
/// Gets or sets the filter counter. Default is 0.
/// </summary>
protected int Count { get; set; }

/// <summary>
/// Gets or sets the column field key. Default is null.
/// </summary>
protected string? FieldKey { get; set; }

/// <summary>
/// Gets or sets whether the filter is header row. Default is false.
/// </summary>
protected bool IsHeaderRow { get; set; }

/// <summary>
/// <inheritdoc/>
Expand All @@ -84,9 +99,8 @@ protected override void OnParametersSet()
ClearButtonText ??= Localizer[nameof(ClearButtonText)];

Title ??= TableColumnFilter.GetFilterTitle();

_isHeaderRow = TableColumnFilter.IsHeaderRow();
_fieldKey = TableColumnFilter.GetFieldKey();
FieldKey ??= TableColumnFilter.GetFieldKey();
IsHeaderRow = TableColumnFilter.IsHeaderRow();
}

/// <summary>
Expand All @@ -95,7 +109,7 @@ protected override void OnParametersSet()
/// <returns></returns>
private async Task OnClickReset()
{
_count = 0;
Count = 0;
if (TableColumnFilter != null)
{
await TableColumnFilter.Reset();
Expand All @@ -121,9 +135,9 @@ protected async Task OnClickConfirm()
/// <returns></returns>
private void OnClickPlus()
{
if (_count == 0)
if (Count == 0)
{
_count++;
Count++;
}
}

Expand All @@ -133,9 +147,9 @@ private void OnClickPlus()
/// <returns></returns>
private void OnClickMinus()
{
if (_count == 1)
if (Count == 1)
{
_count--;
Count--;
}
}

Expand All @@ -145,22 +159,14 @@ private void OnClickMinus()
/// <returns></returns>
protected virtual RenderFragment RenderFilter() => builder =>
{
var filterType = typeof(TFilter);
builder.OpenComponent<TFilter>(0);
if (filterType.IsSubclassOf(typeof(FilterBase)))
builder.OpenComponent<CascadingValue<FilterContext>>(0);
builder.AddAttribute(1, nameof(CascadingValue<FilterContext>.Value), new FilterContext()
{
builder.AddAttribute(1, nameof(FilterBase.FieldKey), _fieldKey);
builder.AddAttribute(2, nameof(FilterBase.IsHeaderRow), _isHeaderRow);
}
if (filterType.IsSubclassOf(typeof(MultipleFilterBase)))
{
builder.AddAttribute(10, nameof(MultipleFilterBase.Count), _count);
}

if (FilterParameters != null)
{
builder.AddMultipleAttributes(100, FilterParameters);
}
Count = Count,
FieldKey = FieldKey,
IsHeaderRow = IsHeaderRow
});
builder.AddAttribute(2, nameof(CascadingValue<FilterContext>.ChildContent), ChildContent);
builder.CloseComponent();
};
}
13 changes: 13 additions & 0 deletions src/BootstrapBlazor/Components/Filters/MultipleFilterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,17 @@ public abstract class MultipleFilterBase : FilterBase
/// 获得/设置 多个条件逻辑关系符号
/// </summary>
protected FilterLogic Logic { get; set; }

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

if (FilterContext != null)
{
Count = FilterContext.Count;
}
}
}
36 changes: 36 additions & 0 deletions test/UnitTest/Components/FilterProviderTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 UnitTest.Components;

public class FilterProviderTest : BootstrapBlazorTestBase
{
[Fact]
public async Task FilterProvider_Ok()
{
var cut = Context.RenderComponent<FilterProvider>(pb =>
{
pb.Add(a => a.ShowMoreButton, true);
pb.AddChildContent<MockFilter>();
});

var filter = cut.FindComponent<MockFilter>();
var context = filter.Instance.GetFilterContext();
Assert.NotNull(context);
Assert.Equal(0, context.Count);

// 点击 +
var plus = cut.Find(".card-footer button");
await cut.InvokeAsync(() => plus.Click());
context = filter.Instance.GetFilterContext();
Assert.NotNull(context);
Assert.Equal(1, context.Count);
}

class MockFilter : StringFilter
{
public FilterContext? GetFilterContext() => FilterContext;
}
}