diff --git a/src/BootstrapBlazor.Server/Components/Samples/ClockPickers.razor b/src/BootstrapBlazor.Server/Components/Samples/ClockPickers.razor
index 16ea62038cb..b8f95155b93 100644
--- a/src/BootstrapBlazor.Server/Components/Samples/ClockPickers.razor
+++ b/src/BootstrapBlazor.Server/Components/Samples/ClockPickers.razor
@@ -4,4 +4,35 @@
? Localizer { get; set; }
+ private TimeSpan Value { get; set; } = DateTime.Now - DateTime.Today;
+
+ private TimeSpan SecondValue { get; set; } = TimeSpan.FromHours(12.5);
+
+ private TimeSpan MinuteValue { get; set; } = TimeSpan.FromHours(12);
+
+ private TimeSpan ScaleValue { get; set; } = TimeSpan.FromHours(12.5);
+
+ private bool _autoSwitch = false;
+
+ private AttributeItem[] GetAttributes() =>
+ [
+ new()
+ {
+ Name = nameof(ClockPicker.IsAutoSwitch),
+ Description = Localizer["IsAutoSwitchAttr"],
+ Type = "bool",
+ ValueList = "true / false",
+ DefaultValue = "true"
+ },
+ new()
+ {
+ Name = nameof(ClockPicker.ShowClockScale),
+ Description = Localizer["ShowClockScaleAttr"],
+ Type = "bool",
+ ValueList = "true / false",
+ DefaultValue = "false"
+ },
+ new()
+ {
+ Name = nameof(ClockPicker.ShowMinute),
+ Description = Localizer["ShowMinuteAttr"],
+ Type = "bool",
+ ValueList = "true / false",
+ DefaultValue = "true"
+ },
+ new()
+ {
+ Name = nameof(ClockPicker.ShowSecond),
+ Description = Localizer["ShowSecondAttr"],
+ Type = "bool",
+ ValueList = "true / false",
+ DefaultValue = "true"
+ }
+ ];
}
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json
index 2492159f160..05e7d4bc90c 100644
--- a/src/BootstrapBlazor.Server/Locales/en-US.json
+++ b/src/BootstrapBlazor.Server/Locales/en-US.json
@@ -2469,6 +2469,23 @@
"TimeTitle": "Data is bound in both directions",
"TimeIntro": "Click the confirm button to select the box value consistent with the text box value"
},
+ "BootstrapBlazor.Server.Components.Samples.ClockPickers": {
+ "Title": "ClockPicker",
+ "Description": "Select a time by dragging the watch needle",
+ "BindValueTitle": "bind",
+ "BindValueIntro": "By settingIsAutoSwitch=\"false\"
to disable automatic switching of hour, minute, and stopwatch dial functions",
+ "AutoSwitchText": "Whether to automatically switch the dial",
+ "HasSecondsTitle": "Do not set the number of seconds",
+ "HasSecondsIntro": "By settingShowSecond=\"false\"
to not display the second hand dial",
+ "ShowMinuteTitle": "Do not set minutes",
+ "ShowMinuteIntro": "By settingShowMinute=\"false\"
to not display the minute dial",
+ "ShowClockScaleTitle": "Display dial scale",
+ "ShowClockScaleIntro": "By settingShowClockScale=\"true\"
to display the dial scale",
+ "IsAutoSwitchAttr": "Does it automatically switch between hours, minutes, and seconds",
+ "ShowClockScaleAttr": "Is the dial scale displayed",
+ "ShowMinuteAttr": "Is the minute displayed",
+ "ShowSecondAttr": "Is seconds displayed"
+ },
"BootstrapBlazor.Server.Components.Samples.Editors": {
"EditorsTitle": "Editor",
"EditorsDescription": "Convert the entered text into html
code snippets",
diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json
index 4b4b04e5361..80c5a7ce7ed 100644
--- a/src/BootstrapBlazor.Server/Locales/zh-CN.json
+++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json
@@ -2469,6 +2469,23 @@
"TimeTitle": "数据双向绑定",
"TimeIntro": "点击确认按钮时间选择框值与文本框值一致"
},
+ "BootstrapBlazor.Server.Components.Samples.ClockPickers": {
+ "Title": "ClockPicker 时间选择器",
+ "Description": "通过拖动表针选择时间",
+ "BindValueTitle": "数据双向绑定",
+ "BindValueIntro": "通过设置 IsAutoSwitch=\"false\"
禁止小时、分钟、秒表盘自动切换功能",
+ "AutoSwitchText": "是否自动切换表盘",
+ "HasSecondsTitle": "不设置秒数",
+ "HasSecondsIntro": "通过设置 ShowSecond=\"false\"
不显示秒针表盘",
+ "ShowMinuteTitle": "不设置分钟",
+ "ShowMinuteIntro": "通过设置 ShowMinute=\"false\"
不显示分针表盘",
+ "ShowClockScaleTitle": "显示表盘刻度",
+ "ShowClockScaleIntro": "通过设置 ShowClockScale=\"true\"
显示表盘刻度",
+ "IsAutoSwitchAttr": "是否自动切换 小时、分钟、秒 自动切换",
+ "ShowClockScaleAttr": "是否显示表盘刻度",
+ "ShowMinuteAttr": "是否显示分钟",
+ "ShowSecondAttr": "是否显示秒"
+ },
"BootstrapBlazor.Server.Components.Samples.Editors": {
"EditorsTitle": "Editor 富文本框",
"EditorsDescription": "将输入的文字转化为 html
代码片段",
diff --git a/src/BootstrapBlazor.Server/wwwroot/css/site.css b/src/BootstrapBlazor.Server/wwwroot/css/site.css
index 10bc68fdea1..e0329986d9a 100644
--- a/src/BootstrapBlazor.Server/wwwroot/css/site.css
+++ b/src/BootstrapBlazor.Server/wwwroot/css/site.css
@@ -253,6 +253,13 @@ code {
margin-bottom: 1rem;
}
+.custom-clock {
+ border: 1px solid var(--bs-border-color);
+ padding: .5rem;
+ border-radius: var(--bs-border-radius);
+ width: 320px;
+}
+
@media (min-width: 768px) {
:root {
--bs-header-height: 50px;
diff --git a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor
index b198ed9162d..b97a01bd042 100644
--- a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor
+++ b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor
@@ -10,9 +10,23 @@
}
SetMode(TimeMode.Hour)">@HourValue
:
- SetMode(TimeMode.Minute)">@Value.Minutes.ToString("D2")
+ @if(ShowMinute)
+ {
+ SetMode(TimeMode.Minute)">@Value.Minutes.ToString("D2")
+ }
+ else
+ {
+ @Value.Minutes.ToString("D2")
+ }
:
- SetMode(TimeMode.Second)">@Value.Seconds.ToString("D2")
+ @if(ShowMinute && ShowSecond)
+ {
+ SetMode(TimeMode.Second)">@Value.Seconds.ToString("D2")
+ }
+ else
+ {
+ @Value.Seconds.ToString("D2")
+ }
@if (ShowClockScale)
diff --git a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.cs b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.cs
index b24209fb77d..1c8dc81bf54 100644
--- a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.cs
+++ b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.cs
@@ -19,37 +19,38 @@ public partial class ClockPicker
.Build();
///
- /// 是否显示表盘刻度 默认 false
+ /// 获得/设置 是否显示表盘刻度 默认 false
///
[Parameter]
public bool ShowClockScale { get; set; }
///
- /// 是否显示秒 默认 true
+ /// 获得/设置 是否显示秒 默认 true
///
[Parameter]
public bool ShowSecond { get; set; } = true;
///
- /// 是否显示分钟 默认 true
+ /// 获得/设置 是否显示分钟 默认 true
///
[Parameter]
public bool ShowMinute { get; set; } = true;
///
- /// 是否自动切换 小时、分钟、秒 自动切换 默认 true
+ /// 获得/设置 是否自动切换 小时、分钟、秒 自动切换 默认 true
///
[Parameter]
public bool IsAutoSwitch { get; set; } = true;
[CascadingParameter]
+ [NotNull]
private DatePickerBody? DatePicker { get; set; }
[Inject]
[NotNull]
private IStringLocalizer? Localizer { get; set; }
- private string? CurrentDateString => DatePicker?.Value.ToString(DatePicker.DateFormat);
+ private string? CurrentDateString => DatePicker.Value.ToString(DatePicker.DateFormat);
///
/// is hour or min or sec mode
@@ -118,24 +119,20 @@ private void SetTimePeriod(int hour)
}
///
- /// 设置小时调用此方法
+ /// JSInvoke 调用此方法
///
[JSInvokable]
public void SetTime(int hour, int minute, int second)
{
if (IsAutoSwitch)
{
- switch (Mode)
+ if (Mode == TimeMode.Hour && ShowMinute)
+ {
+ Mode = TimeMode.Minute;
+ }
+ else if (Mode == TimeMode.Minute && ShowSecond)
{
- case TimeMode.Hour:
- Mode = TimeMode.Minute;
- break;
- case TimeMode.Minute:
- Mode = TimeMode.Second;
- break;
- case TimeMode.Second:
- default:
- break;
+ Mode = TimeMode.Second;
}
}
@@ -167,7 +164,7 @@ private static int GetSafeHour(int val)
private void SwitchView()
{
Mode = TimeMode.Hour;
- DatePicker?.SwitchDateView();
+ DatePicker.SwitchDateView();
}
private enum TimeMode
diff --git a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.scss b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.scss
index 041f903bab8..897481faed2 100644
--- a/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.scss
+++ b/src/BootstrapBlazor/Components/ClockPicker/ClockPicker.razor.scss
@@ -18,7 +18,6 @@
--bb-time-footer-btn-hover-bg-color: #409eff;
--bb-time-footer-btn-active-color: #409eff;
--bb-time-footer-btn-active-border-color: #409eff;
- width: var(--bb-picker-panel-body-width);
&.dragging {
user-select: none;
diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor b/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor
index 3b9eb302af7..b760aa7c447 100644
--- a/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor
+++ b/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor
@@ -147,7 +147,7 @@
@ChildContent
-
diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor.scss b/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor.scss
index 30ff785182d..547f9385a0e 100644
--- a/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor.scss
+++ b/src/BootstrapBlazor/Components/DateTimePicker/DatePickerBody.razor.scss
@@ -214,6 +214,10 @@
}
}
}
+
+ .clock-panel-body {
+ width: var(--bb-picker-panel-body-width);
+ }
}
.popover-body {
diff --git a/src/BootstrapBlazor/Locales/en.json b/src/BootstrapBlazor/Locales/en.json
index 990fbf8383c..80f7b19df8f 100644
--- a/src/BootstrapBlazor/Locales/en.json
+++ b/src/BootstrapBlazor/Locales/en.json
@@ -352,7 +352,7 @@
"AscText": "Ascending",
"DescText": "Descending"
},
- "BootstrapBlazor.Components.TimePickerPanel": {
+ "BootstrapBlazor.Components.ClockPicker": {
"AMText": "AM",
"PMText": "PM"
}
diff --git a/src/BootstrapBlazor/Locales/zh.json b/src/BootstrapBlazor/Locales/zh.json
index bac545d9090..77c64ea4d93 100644
--- a/src/BootstrapBlazor/Locales/zh.json
+++ b/src/BootstrapBlazor/Locales/zh.json
@@ -352,7 +352,7 @@
"AscText": "升序",
"DescText": "降序"
},
- "BootstrapBlazor.Components.TimePickerPanel": {
+ "BootstrapBlazor.Components.ClockPicker": {
"AMText": "上午",
"PMText": "下午"
}
diff --git a/test/UnitTest/Components/ClockPickerTest.cs b/test/UnitTest/Components/ClockPickerTest.cs
new file mode 100644
index 00000000000..14d87cdf375
--- /dev/null
+++ b/test/UnitTest/Components/ClockPickerTest.cs
@@ -0,0 +1,200 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+namespace UnitTest.Components;
+
+public class ClockPickerTest : BootstrapBlazorTestBase
+{
+ [Fact]
+ public async Task IsAutoSwitch_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ pb.Add(a => a.IsAutoSwitch, true);
+ });
+
+ // 当前表盘时小时
+ cut.Contains("data-bb-mode=\"Hour\"");
+
+ // 模拟 JSInvoke 调用 SetTime 方法
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(11, 0, 0);
+ });
+ cut.Contains("data-bb-mode=\"Minute\"");
+
+ // 模拟 JSInvoke 调用 SetTime 方法
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(11, 0, 0);
+ });
+ cut.Contains("data-bb-mode=\"Second\"");
+ }
+
+ [Fact]
+ public async Task ShowMinute_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ pb.Add(a => a.IsAutoSwitch, true);
+ pb.Add(a => a.ShowMinute, false);
+ });
+
+ // 当前表盘时小时
+ cut.Contains("data-bb-mode=\"Hour\"");
+
+ // 模拟 JSInvoke 调用 SetTime 方法
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(11, 0, 0);
+ });
+ cut.Contains("data-bb-mode=\"Hour\"");
+ }
+
+ [Fact]
+ public async Task ShowSecond_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ pb.Add(a => a.IsAutoSwitch, true);
+ pb.Add(a => a.ShowSecond, false);
+ });
+
+ // 当前表盘时小时
+ cut.Contains("data-bb-mode=\"Hour\"");
+
+ // 模拟 JSInvoke 调用 SetTime 方法
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(11, 0, 0);
+ });
+ cut.Contains("data-bb-mode=\"Minute\"");
+
+ // 模拟 JSInvoke 调用 SetTime 方法
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(11, 0, 0);
+ });
+ cut.Contains("data-bb-mode=\"Minute\"");
+ }
+
+ [Fact]
+ public async Task SwitchView_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ });
+ var span = cut.Find(".hour");
+ await cut.InvokeAsync(() =>
+ {
+ span.Click();
+ });
+ cut.Contains("data-bb-mode=\"Hour\"");
+
+ span = cut.Find(".minute");
+ await cut.InvokeAsync(() =>
+ {
+ span.Click();
+ });
+ cut.Contains("data-bb-mode=\"Minute\"");
+
+ span = cut.Find(".second");
+ await cut.InvokeAsync(() =>
+ {
+ span.Click();
+ });
+ cut.Contains("data-bb-mode=\"Second\"");
+ }
+
+ [Fact]
+ public async Task SetTime_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(11));
+ });
+ await cut.InvokeAsync(() =>
+ {
+ cut.Instance.SetTime(12, 0, 0);
+ });
+ Assert.Equal(TimeSpan.FromHours(0), cut.Instance.Value);
+
+ cut.SetParametersAndRender(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(11));
+ });
+ var button = cut.Find(".btn-am");
+ await cut.InvokeAsync(() =>
+ {
+ button.Click();
+ });
+ Assert.Equal(TimeSpan.FromHours(11), cut.Instance.Value);
+
+ cut.SetParametersAndRender(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(20));
+ });
+ button = cut.Find(".btn-pm");
+ await cut.InvokeAsync(() =>
+ {
+ button.Click();
+ });
+ Assert.Equal(TimeSpan.FromHours(20), cut.Instance.Value);
+ }
+
+ [Fact]
+ public async Task SetTimePeriod_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ });
+ cut.Contains("btn-pm active");
+
+ var button = cut.Find(".btn-am");
+ await cut.InvokeAsync(() =>
+ {
+ button.Click();
+ });
+ cut.Contains("btn-am active");
+
+ button = cut.Find(".btn-pm");
+ await cut.InvokeAsync(() =>
+ {
+ button.Click();
+ });
+ cut.Contains("btn-pm active");
+ }
+
+ [Fact]
+ public void ShowClockScale_Ok()
+ {
+ var cut = Context.RenderComponent(pb =>
+ {
+ pb.Add(a => a.Value, TimeSpan.FromHours(12.5));
+ pb.Add(a => a.ShowClockScale, true);
+ });
+ cut.Contains("bb-clock-panel bb-clock-panel-scale");
+ }
+
+ [Fact]
+ public async Task DatePicker_Ok()
+ {
+ var cut = Context.RenderComponent>(pb =>
+ {
+ pb.Add(a => a.Value, DateTime.Now);
+ pb.Add(a => a.ViewMode, DatePickerViewMode.DateTime);
+ });
+ var span = cut.Find(".bb-time-text");
+ await cut.InvokeAsync(() =>
+ {
+ span.Click();
+ });
+ cut.Contains("data-bb-mode=\"Hour\"");
+ }
+}