Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
352ff17
feat(DateTimePicker): Support custom disable dates
izanhzh Sep 14, 2024
2a0fc34
调整单元测试
izanhzh Sep 14, 2024
679cee5
撤销格式化调整
izanhzh Sep 14, 2024
7311b22
撤销格式化调整
izanhzh Sep 14, 2024
09ebedf
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
izanhzh Sep 14, 2024
8811423
调整单元测试
izanhzh Sep 14, 2024
a62e712
调整ViewMode的赋值逻辑
izanhzh Sep 18, 2024
2740443
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
izanhzh Sep 18, 2024
d9108b9
调整单元测试
izanhzh Sep 18, 2024
90bbda9
调整SelectedValue赋值
izanhzh Sep 18, 2024
a1516b4
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
izanhzh Sep 24, 2024
84aee0e
提高覆盖率
izanhzh Sep 24, 2024
9c3799b
调整MinValueToToday逻辑
izanhzh Sep 24, 2024
562e167
撤销MinValueToToday时对MinValue/MaxValue的校验
izanhzh Sep 24, 2024
e5279d7
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
ArgoZhang Sep 27, 2024
5761d95
refactor: 重命名参数名称
ArgoZhang Sep 27, 2024
777cbd2
调整支持不可为空时间
izanhzh Sep 28, 2024
5e8df2c
补充单元测试
izanhzh Sep 28, 2024
a9ee2b2
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
ArgoZhang Sep 28, 2024
668665a
test: 更新单元测试
ArgoZhang Sep 28, 2024
d720ebb
doc: 移除不使用的命名空间
ArgoZhang Sep 28, 2024
3df5d06
revert: 撤销更改
ArgoZhang Sep 28, 2024
66fbea4
refactor: 更新代码提高代码覆盖率
ArgoZhang Sep 28, 2024
d2c1c1f
refactor: 精简代码提高可读性
ArgoZhang Sep 28, 2024
e71feff
doc: 更新功能示例
ArgoZhang Sep 28, 2024
c6ce0a7
refactor: 更改禁用日期回调方法名称
ArgoZhang Sep 28, 2024
35d8eff
test: 更新单元测试
ArgoZhang Sep 28, 2024
f12707a
refactor: 重新设计禁用日期获得逻辑
ArgoZhang Sep 28, 2024
b5a0bb7
doc: 更新示例
ArgoZhang Sep 28, 2024
98e78a9
test: 更新单元测试
ArgoZhang Sep 28, 2024
84c0012
refactor: 重构代码
ArgoZhang Sep 28, 2024
e7d370c
refactor: 重构代码
ArgoZhang Sep 28, 2024
98dbf89
refactor: 撤销代码更改
ArgoZhang Sep 28, 2024
8059c23
test: 更新单元测试
ArgoZhang Sep 28, 2024
96cafc9
refactor: 重构清除缓存逻辑
ArgoZhang Sep 28, 2024
7abdb98
doc: 更新示例
ArgoZhang Sep 28, 2024
4ec63d8
refactor: 更新缓存清除逻辑
ArgoZhang Sep 28, 2024
ad706da
test: 更新单元测试
ArgoZhang Sep 28, 2024
a9ec98c
refactor: 代码重构
ArgoZhang Sep 28, 2024
6efa64d
doc: 更新注释文档
ArgoZhang Sep 28, 2024
907cb74
doc: 更新示例
ArgoZhang Sep 28, 2024
cd6e51a
refactor: 代码重构
ArgoZhang Sep 28, 2024
27e8b44
perf: 提高性能
ArgoZhang Sep 28, 2024
082b8a5
refactor: 重构代码
ArgoZhang Sep 28, 2024
d679c9e
feat: 视图切换时更新禁用日期缓存
ArgoZhang Sep 28, 2024
7fef08b
refactor: 格式化代码
ArgoZhang Sep 28, 2024
2ce4b7a
wip: 临时提交
ArgoZhang Sep 28, 2024
ea42fa3
refactor: 增加 UI 显示逻辑
ArgoZhang Sep 28, 2024
4379d90
revert: 撤销 UI 更改
ArgoZhang Sep 28, 2024
853f04b
refactor: 重命名回调方法
ArgoZhang Sep 29, 2024
65cba62
refactor: 优化代码
ArgoZhang Sep 29, 2024
e6ed623
refactor: 更新示例
ArgoZhang Sep 29, 2024
efc0a52
feat: 增加缓存清除实例方法
ArgoZhang Sep 29, 2024
324544a
doc: 增加动态切换回调方法示例
ArgoZhang Sep 29, 2024
22bbcb3
refactor: 重构 FormatValueAsString 提高可读性
ArgoZhang Sep 29, 2024
29a2042
doc: 更新文档说明
ArgoZhang Sep 29, 2024
06ce4d7
doc: 更新注意事项
ArgoZhang Sep 29, 2024
1c75cc1
test: 增加清除方法单元测试
ArgoZhang Sep 29, 2024
91fa9b2
doc: 更新介绍文字
ArgoZhang Sep 29, 2024
4de9e89
doc: 更正单词拼写错误
ArgoZhang Sep 29, 2024
7d8f48d
refactor: 调整示例
ArgoZhang Sep 29, 2024
742a6a1
doc: 更新文档
ArgoZhang Sep 29, 2024
371d8dc
doc: 更新示例
ArgoZhang Sep 29, 2024
d631dc4
refactor: 更新文档
ArgoZhang Sep 29, 2024
d34987a
refactor: 防止多线程报错
ArgoZhang Sep 29, 2024
2894626
feat: 增加 DisplayDisabledDayAsEmpty 参数
ArgoZhang Sep 29, 2024
2d96c36
doc: 更新示例
ArgoZhang Sep 29, 2024
3548070
refactor: 更新 FormatValueAsString 逻辑
ArgoZhang Sep 29, 2024
09eab5a
Merge branch 'main' into feat-date-time-picker-support-custom-disable…
ArgoZhang Sep 29, 2024
91da121
doc: 更新 DisplayDisabledDayAsEmpty 参数说明
ArgoZhang Sep 29, 2024
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 @@ -34,7 +34,7 @@
<Pre>builder.Services.AddBootstrapHolidayService();</Pre>
</div>
</section>
<DatePickerBody @bind-Value="Value" OnConfirm="@NormalOnConfirm" ShowFooter="false" ShowLunar="_showLunar" ShowSolarTerm="_showSolarTerm" ShowFestivals="_showFestivals" ShowHolidays="_showHolidays" />
<DatePickerBody @bind-Value="Value" OnConfirm="@NormalOnConfirm" ShowFooter="false" ShowLunar="_showLunar" ShowSolarTerm="_showSolarTerm" ShowFestivals="_showFestivals" ShowHolidays="_showHolidays"/>
<ConsoleLogger @ref="NormalLogger" class="mt-3" />
<section ignore>
<Tips class="mt-3">
Expand Down Expand Up @@ -188,6 +188,29 @@
</DateTimePicker>
</DemoBlock>

<DemoBlock Title="@Localizer["DisableDayPredicateTitle"]" Introduction="@Localizer["DisableDayPredicateIntro"]" Name="DisableDayPredicate">
<section ignore>
<GroupBox Title="@Localizer["DisableOptions"]">
<div class="row g-3 form-inline text-end">
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["DisableSunday"]" ShowLabel="true" @bind-Value="_disableSunday" />
</div>
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["DisableToday"]" ShowLabel="true" @bind-Value="_disableToday" />
</div>
</div>
</GroupBox>
</section>
<DateTimePicker ViewMode="DatePickerViewMode.DateTime" @bind-Value="@BindNullValue" DisableDayPredicate="(day) => _disableSunday && day.DayOfWeek == DayOfWeek.Sunday || (_disableToday && day.Date == DateTime.Today)">
<TimePickerSetting ShowClockScale="true" IsAutoSwitch="false" />
</DateTimePicker>
<section ignore>
<Tips class="mt-3">
<p>@((MarkupString)Localizer["DisableDayPredicateTip"].Value)</p>
</Tips>
</section>
</DemoBlock>

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

<EventTable Items="@GetEvents()" />
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ private string BindValueString
private bool _showSolarTerm = true;
private bool _showFestivals = true;
private bool _showHolidays = true;
private bool _disableSunday = true;
private bool _disableToday = false;

/// <summary>
/// 获得事件方法
Expand Down
8 changes: 7 additions & 1 deletion src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2556,7 +2556,13 @@
"FeatureShowFestivals": "Festivals",
"FeatureShowHolidays": "Holidays",
"FeatureIntro": "<b>Holidays</b> Functional dependency component packages <code>BootstrapBlazor.Holiday</code> <a href=\"holiday\" target=\"_blank\">[Portal]</a>",
"FeatureFestivalIntro": "<b>The festival</b> function is provided by the built-in service <code>ICalendarFestivals</code> in the component library. The built-in default implementation provides 12 Gregorian festivals and 7 lunar festivals, which can be extended through custom festival services. For detailed function introductions, please refer to the <b>Festival Services</b> documentation <a href=\"festival\" target=\"_blank\">[Portal]</a>"
"FeatureFestivalIntro": "<b>The festival</b> function is provided by the built-in service <code>ICalendarFestivals</code> in the component library. The built-in default implementation provides 12 Gregorian festivals and 7 lunar festivals, which can be extended through custom festival services. For detailed function introductions, please refer to the <b>Festival Services</b> documentation <a href=\"festival\" target=\"_blank\">[Portal]</a>",
"DisableOptions": "Disable options",
"DisableSunday": "Disable sunday",
"DisableToday": "Disable today",
"DisableDayPredicateTitle": "Customize the disable date",
"DisableDayPredicateIntro": "Specify which dates are disabled by setting <code>DisableDayPredicate</code>",
"DisableDayPredicateTip": "Please note that you must set the allowed empty time for the custom disable date to take effect. When the binding value is determined to be disabled, the binding value will be reset to empty time!"
},
"BootstrapBlazor.Server.Components.Samples.TimePickers": {
"Title": "TimePicker",
Expand Down
8 changes: 7 additions & 1 deletion src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -2556,7 +2556,13 @@
"FeatureShowFestivals": "节日",
"FeatureShowHolidays": "法定假日",
"FeatureIntro": "<b>假日</b>功能依赖组件包 <code>BootstrapBlazor.Holiday</code> <a href=\"holiday\" target=\"_blank\">[传送门]</a>",
"FeatureFestivalIntro": "<b>节日</b>功能由组件库内置服务 <code>ICalendarFestivals</code> 提供,内置默认实现提供了 12 个公历节日与 7 个农历节日,可以通过自定义节日服务进行扩展,详细功能介绍请参阅 <b>节日服务</b> 文档 <a href=\"festival\" target=\"_blank\">[传送门]</a>"
"FeatureFestivalIntro": "<b>节日</b>功能由组件库内置服务 <code>ICalendarFestivals</code> 提供,内置默认实现提供了 12 个公历节日与 7 个农历节日,可以通过自定义节日服务进行扩展,详细功能介绍请参阅 <b>节日服务</b> 文档 <a href=\"festival\" target=\"_blank\">[传送门]</a>",
"DisableOptions": "禁用选项",
"DisableSunday": "禁用周日",
"DisableToday": "禁用今天",
"DisableDayPredicateTitle": "自定义禁用日期",
"DisableDayPredicateIntro": "通过设置 <code>DisableDayPredicate</code> 自定义哪些日期需要被禁用",
"DisableDayPredicateTip": "请注意,必须要设置允许空时间,自定义禁用日期才会生效,当绑定值被判断为禁用时,绑定值将被重置为空时间!"
},
"BootstrapBlazor.Server.Components.Samples.TimePickers": {
"Title": "TimePicker 时间选择器",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@
else if (IsDisabled(day))
{
<span class="cell">
@if(ShowLunar)
@if (ShowLunar)
{
<span>@text</span>
<span class="bb-picker-body-lunar-text">@GetLunarText(day)</span>
}
else if(DayDisabledTemplate != null)
else if (DayDisabledTemplate != null)
{
@DayDisabledTemplate(day)
}
Expand Down Expand Up @@ -175,7 +175,7 @@
</Button>
}
</div>
@if (MinValue == null && MaxValue == null)
@if (!IsDisabled(DateTime.Today))
{
<Button class="picker-panel-link-btn is-now" Color="Color.None" OnClickWithoutRender="@ClickNowButton">
<span>@NowButtonText</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private DateTime StartDate
.AddClass("is-open", ShowTimePicker)
.Build();

private bool IsDisabled(DateTime day) => (MinValue.HasValue && day < MinValue.Value) || (MaxValue.HasValue && day > MaxValue.Value);
private bool IsDisabled(DateTime day) => (MinValue.HasValue && day < MinValue.Value) || (MaxValue.HasValue && day > MaxValue.Value) || (DisableDayPredicate != null && DisableDayPredicate(day));

/// <summary>
/// 获得 上一月按钮样式
Expand Down Expand Up @@ -375,6 +375,12 @@ public bool AllowNull
[CascadingParameter]
private DateTimeRange? Ranger { get; set; }

/// <summary>
/// 获取/设置 自定义禁用日期判断方法
/// </summary>
[Parameter]
public Func<DateTime, bool>? DisableDayPredicate { get; set; }

[Inject]
[NotNull]
private ICalendarFestivals? CalendarFestivals { get; set; }
Expand Down Expand Up @@ -770,7 +776,7 @@ private void ResetTimePickerPanel()
TimePickerPanel?.Reset();
}

private bool Validate() => (!MinValue.HasValue || SelectValue >= MinValue.Value) && (!MaxValue.HasValue || SelectValue <= MaxValue.Value);
private bool Validate() => !IsDisabled(SelectValue);

/// <summary>
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
DateTimeFormat="@DateTimeFormat" DateFormat="@DateFormat" TimeFormat="@TimeFormat" ShowFooter="true"
ShowLunar="ShowLunar" ShowSolarTerm="ShowSolarTerm" ShowFestivals="ShowFestivals" ShowHolidays="ShowHolidays"
OnConfirm="OnConfirm" OnClear="OnClear" MinValue="MinValue" MaxValue="MaxValue"
AutoClose="AutoClose" ViewMode="ViewMode" DayTemplate="DayTemplate!" DayDisabledTemplate="DayDisabledTemplate!">
AutoClose="AutoClose" ViewMode="ViewMode" DayTemplate="DayTemplate!" DayDisabledTemplate="DayDisabledTemplate!" DisableDayPredicate="ReliableDisableDayPredicate!">
@ChildContent
</DatePickerBody>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ public string? Format
[Parameter]
public bool ShowHolidays { get; set; }

/// <summary>
/// 获取/设置 自定义禁用日期判断方法 (注意:仅当允许空时间时此方法才会生效)
/// </summary>
[Parameter]
public Func<DateTime, bool>? DisableDayPredicate { get; set; }

[Inject]
[NotNull]
private IStringLocalizer<DateTimePicker<DateTime>>? Localizer { get; set; }
Expand All @@ -222,6 +228,8 @@ public string? Format

private DateTime SelectedValue { get; set; }

private Func<DateTime, bool>? ReliableDisableDayPredicate { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand Down Expand Up @@ -249,6 +257,11 @@ protected override void OnParametersSet()

Icon ??= IconTheme.GetIconByKey(ComponentIcons.DateTimePickerIcon);

if (AllowNull && DisableDayPredicate != null)
{
ReliableDisableDayPredicate = DisableDayPredicate;
}

var type = typeof(TValue);

// 判断泛型类型
Expand All @@ -257,25 +270,34 @@ protected override void OnParametersSet()
throw new InvalidOperationException(GenericTypeErrorMessage);
}

// Value 为 MinValue 时 设置 Value 默认值
if (Value == null)
{
SelectedValue = DateTime.MinValue;
}
else if (Value is DateTimeOffset v1)
{
SelectedValue = v1.DateTime;
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? v1.DateTime : v1.DateTime.Date;
}
else
{
SelectedValue = (DateTime)(object)Value;
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? (DateTime)(object)Value : ((DateTime)(object)Value).Date;
}

if (MinValueToEmpty(SelectedValue))
if (ReliableDisableDayPredicate != null && ReliableDisableDayPredicate(SelectedValue))
{
SelectedValue = DateTime.Today;
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? DateTime.Now : DateTime.Today;
Value = default;
}
else if (MinValueToEmpty(SelectedValue))
{
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? DateTime.Now : DateTime.Today;
Value = default;
}
else if (MinValue.HasValue && MinValue > DateTime.Today)
{
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? MinValue.Value : MinValue.Value.Date;
Value = GetValue();
}
else if (MinValueToToday(SelectedValue))
{
SelectedValue = ViewMode == DatePickerViewMode.DateTime ? DateTime.Now : DateTime.Today;
Expand Down Expand Up @@ -377,4 +399,13 @@ protected override bool TryParseValueFromString(string value, [MaybeNullWhen(fal
}

private string? ReadonlyString => IsEditable ? null : "readonly";

private Func<DateTime, bool>? GetSafeDisableDayPredicate()
{
if (!AllowNull || AutoToday)
{
return null;
}
return DisableDayPredicate;
}
}
37 changes: 37 additions & 0 deletions test/UnitTest/Components/DateTimePickerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,43 @@ await cut.InvokeAsync(() =>
Assert.Equal("02/15/2024 01:00:00", cut.Instance.Value.ToString("MM/dd/yyyy HH:mm:ss"));
}

[Fact]
public void DisableDayPredicate_Ok()
{
var cut = Context.RenderComponent<DateTimePicker<DateTime?>>(pb =>
{
pb.Add(a => a.DisableDayPredicate, DisableToday);
pb.Add(a => a.Value, DateTime.Today);
});
Assert.Null(cut.Instance.Value);
Assert.DoesNotContain("btn picker-panel-link-btn is-now", cut.Markup);
Assert.Contains("today disabled", cut.Markup);

cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.Value, DateTime.Today.AddDays(1));
});
Assert.Equal(DateTime.Today.AddDays(1), cut.Instance.Value);

cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.DisableDayPredicate, DisableYesterday);
pb.Add(a => a.Value, DateTime.Today);
});
Assert.Equal(DateTime.Today, cut.Instance.Value);
Assert.Contains("btn picker-panel-link-btn is-now", cut.Markup);
}

private bool DisableToday(DateTime day)
{
return day.Date == DateTime.Today;
}

private bool DisableYesterday(DateTime day)
{
return day.Date == DateTime.Today.AddDays(-1);
}

class MockDateTimePicker : DatePickerBody
{
public static bool GetSafeYearDateTime_Ok()
Expand Down