feat(DateTimePicker): add IsButton parameter (#6999)

* feat: 增加日期选择按钮

* refactor: 增加逻辑

* refactor: 移除 DateTimePickerButton 组件

* refactor: 重构脚本支持 Button 模式切换

* feat: 增加按钮元素

* doc: 增加多语言

* doc: 更新示例

* doc: 文档格式化

* test: 更新单元测试

* chore: bump version 9.11.5-beta05
This commit is contained in:
Argo Zhang
2025-10-24 21:13:33 +08:00
committed by GitHub
parent 7239ed0f1a
commit 37ab016e2b
17 changed files with 327 additions and 197 deletions

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "AutoScroll"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "wählen",
"DatePlaceHolder": "Datum auswählen",
"TimePlaceHolder": "Uhrzeit auswählen",
"DateTimePlaceHolderText": "Bitte auswählen ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "AutoScroll"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Seleccione",
"DatePlaceHolder": "Seleccione fecha",
"TimePlaceHolder": "Seleccione hora",
"DateTimePlaceHolderText": "Por favor seleccione ...",

View File

@@ -45,6 +45,7 @@
"AutoScrollText": "Scorrimento automatico"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Seleziona",
"DatePlaceHolder": "Seleziona la data",
"TimePlaceHolder": "Seleziona l'ora",
"DateTimePlaceHolderText": "Per favore seleziona ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "Rolagem automática"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Selecione",
"DatePlaceHolder": "Selecione a data",
"TimePlaceHolder": "Selecione o horário",
"DateTimePlaceHolderText": "Por favor, selecione ...",

View File

@@ -1,4 +1,4 @@
{
{
"BootstrapBlazor.Components.AutoComplete": {
"NoDataTip": "Нет данных",
"PlaceHolder": "Пожалуйста, введите"
@@ -34,6 +34,7 @@
"AutoScrollText": "Автопрокрутка"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Выберите",
"DatePlaceHolder": "Выберите дату",
"TimePlaceHolder": "Выберите время",
"DateTimePlaceHolderText": "Пожалуйста, выберите ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "เลื่อนอัตโนมัติ"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "เลือก",
"DatePlaceHolder": "เลือกวันที่",
"TimePlaceHolder": "เลือกเวลา",
"DateTimePlaceHolderText": "กรุณาเลือก ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "Автопрокрутка"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Виберіть",
"DatePlaceHolder": "Виберіть дату",
"TimePlaceHolder": "Виберіть час",
"DateTimePlaceHolderText": "Будь ласка, виберіть ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "自動滾屏"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "選擇",
"DatePlaceHolder": "選擇日期",
"TimePlaceHolder": "選擇時間",
"DateTimePlaceHolderText": "請選擇日期時間",

View File

@@ -5,7 +5,15 @@
<h4>@Localizer["Description"]</h4>
<DemoBlock Title="@Localizer["DateTimePickerTitle"]" Introduction="@Localizer["DateTimePickerIntro"]" Name="DateTimePicker">
<DateTimePicker ViewMode="DatePickerViewMode.DateTime" TimeFormat="hh\:mm"
<section ignore class="row g-3 mb-3">
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="IsButton"></BootstrapInputGroupLabel>
<Switch @bind-Value="_isButton"></Switch>
</BootstrapInputGroup>
</div>
</section>
<DateTimePicker ViewMode="DatePickerViewMode.DateTime" TimeFormat="hh\:mm" IsButton="_isButton"
Value="@DateTimePickerValue" OnValueChanged="@TimePickerValueChanged">
<TimePickerSetting ShowClockScale="true" IsAutoSwitch="false" />
</DateTimePicker>
@@ -16,17 +24,29 @@
<section ignore>
<GroupBox Title="@Localizer["Feature"]">
<div class="row g-3 form-inline text-end">
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["FeatureShowLunar"]" ShowLabel="true" @bind-Value="_showLunar" />
<div class="col-12 col-sm-6 col-lg-3">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="@Localizer["FeatureShowLunar"]"></BootstrapInputGroupLabel>
<Switch @bind-Value="_showLunar" />
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["FeatureShowSolarTerm"]" ShowLabel="true" @bind-Value="_showSolarTerm" />
<div class="col-12 col-sm-6 col-lg-3">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="@Localizer["FeatureShowSolarTerm"]"></BootstrapInputGroupLabel>
<Switch @bind-Value="_showSolarTerm" />
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["FeatureShowFestivals"]" ShowLabel="true" @bind-Value="_showFestivals" />
<div class="col-12 col-sm-6 col-lg-3">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="@Localizer["FeatureShowFestivals"]"></BootstrapInputGroupLabel>
<Switch @bind-Value="_showFestivals" />
</BootstrapInputGroup>
</div>
<div class="col-12 col-sm-3">
<Switch DisplayText="@Localizer["FeatureShowHolidays"]" ShowLabel="true" @bind-Value="_showHolidays" />
<div class="col-12 col-sm-6 col-lg-3">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="@Localizer["FeatureShowHolidays"]"></BootstrapInputGroupLabel>
<Switch @bind-Value="_showHolidays" />
</BootstrapInputGroup>
</div>
</div>
</GroupBox>
@@ -64,7 +84,7 @@
</DemoBlock>
<DemoBlock Title="@Localizer["ShowIconTitle"]" Introduction="@Localizer["ShowIconIntro"]" Name="ShowIcon">
<DateTimePicker TValue="DateTimeOffset?" ShowIcon="false" />
<DateTimePicker TValue="DateTimeOffset ?" ShowIcon="false" />
</DemoBlock>
<DemoBlock Title="@Localizer["DateTimeOffsetTitle"]" Introduction="@Localizer["DateTimeOffsetIntro"]" Name="DateTimeOffset">
@@ -180,7 +200,7 @@
</DemoBlock>
<DemoBlock Title="@Localizer["DayTemplateTitle"]" Introduction="@Localizer["DayTemplateIntro"]" Name="DayTemplate">
<DateTimePicker TValue="DateTimeOffset?" CustomClass="custom-picker">
<DateTimePicker TValue="DateTimeOffset ?" CustomClass="custom-picker">
<DayTemplate>
<span class="custom-cell">
<span>@context.Day</span>

View File

@@ -95,6 +95,7 @@ public sealed partial class DateTimePickers
private bool _disableToday = true;
private DateTime? _disabledNullValue = DateTime.Today;
private DateTime _disabledValue = DateTime.Today;
private bool _isButton = false;
private async Task<List<DateTime>> OnGetDisabledDaysCallback(DateTime start, DateTime end)
{

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup Condition="'$(VisualStudioVersion)' == '17.0'">
<Version>9.11.5-beta04</Version>
<Version>9.11.5-beta05</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(VisualStudioVersion)' == '18.0'">
<Version>10.0.0-rc.2.1.3</Version>
<Version>10.0.0-rc.2.1.4</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -8,6 +8,14 @@
<BootstrapLabel required="@Required" for="@Id" ShowLabelTooltip="ShowLabelTooltip" Value="@DisplayText" />
}
<div @attributes="@AdditionalAttributes" tabindex="@TabIndexString" id="@Id" class="@ClassString" data-bb-dropdown=".picker-panel">
@if(IsButton)
{
<button type="button" class="@ButtonClassString" disabled="@Disabled"
data-bs-toggle="@Constants.DropdownToggleString" data-bs-placement="@PlacementString"
data-bs-custom-class="@CustomClassString">@PickerButtonText</button>
}
else
{
<input readonly="@ReadonlyString" class="@InputClassName" @bind="@CurrentValueAsString" placeholder="@PlaceholderString"
disabled="@Disabled" data-bs-toggle="@Constants.DropdownToggleString" data-bs-placement="@PlacementString"
data-bs-custom-class="@CustomClassString" @onblur="OnBlur" />
@@ -15,6 +23,7 @@
{
<i class="@DateTimePickerIconClassString"></i>
}
}
<DatePickerBody @bind-Value="SelectedValue" @ref="_pickerBody" FirstDayOfWeek="FirstDayOfWeek"
ShowClearButton="AllowNull" ShowSidebar="ShowSidebar" SidebarTemplate="SidebarTemplate"
DateTimeFormat="@DateTimeFormat" DateFormat="@DateFormat" TimeFormat="@TimeFormat" ShowFooter="true"

View File

@@ -31,6 +31,10 @@ public partial class DateTimePicker<TValue>
.AddClass(ValidCss)
.Build();
private string? ButtonClassString => CssBuilder.Default("btn dropdown-toggle")
.AddClass($"btn-{ButtonColor.ToDescriptionString()}", ButtonColor != Color.None)
.Build();
/// <summary>
/// 获得 组件小图标样式
/// </summary>
@@ -54,6 +58,24 @@ public partial class DateTimePicker<TValue>
/// </summary>
private bool AllowNull { get; set; }
/// <summary>
/// 获得/设置 是否显示为按钮样式 默认 false
/// </summary>
[Parameter]
public bool IsButton { get; set; }
/// <summary>
/// 获得/设置 选择按钮文本 默认 null 读取资源文件
/// </summary>
[Parameter]
public string? PickerButtonText { get; set; }
/// <summary>
/// 获得/设置 选择按钮颜色 默认 <see cref="Color.Primary"/>
/// </summary>
[Parameter]
public Color ButtonColor { get; set; } = Color.Primary;
/// <summary>
/// 获得/设置 时间格式化字符串 默认值为 null
/// </summary>
@@ -292,6 +314,7 @@ public partial class DateTimePicker<TValue>
DateTimeFormat ??= Localizer[nameof(DateTimeFormat)];
DateFormat ??= Localizer[nameof(DateFormat)];
TimeFormat ??= Localizer[nameof(TimeFormat)];
PickerButtonText ??= Localizer[nameof(PickerButtonText)];
Icon ??= IconTheme.GetIconByKey(ComponentIcons.DateTimePickerIcon);
@@ -371,6 +394,29 @@ public partial class DateTimePicker<TValue>
/// <returns></returns>
protected override bool ShouldRender() => _render;
private bool _isButton = false;
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
/// <returns></returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender)
{
// 首次渲染时同步 IsButton 参数值
_isButton = IsButton;
}
if (_isButton != IsButton)
{
_isButton = IsButton;
await InvokeVoidAsync("reset", Id);
}
}
/// <summary>
/// 格式化数值方法
/// </summary>

View File

@@ -8,6 +8,12 @@ export function init(id, invoke, options) {
return
}
const popover = createPopover(el, invoke, options);
const input = handlerInput(el, popover);
Data.set(id, { el, input, invoke, options, popover });
}
const createPopover = (el, invoke, options) => {
const popover = Popover.init(el, {
dropdownSelector: el.getAttribute('data-bb-dropdown'),
isDisabled: () => {
@@ -19,13 +25,12 @@ export function init(id, invoke, options) {
}
}
});
const input = el.querySelector('.datetime-picker-input');
const dateTimePicker = {
input,
popover
}
Data.set(id, dateTimePicker);
return popover;
}
const handlerInput = (el, popover) => {
const input = el.querySelector('.datetime-picker-input');
if (input) {
EventHandler.on(input, 'keydown', e => {
if (e.key === 'Tab' && popover.isShown()) {
popover.hide();
@@ -40,6 +45,27 @@ export function init(id, invoke, options) {
popover.show();
}
});
}
return input;
}
const disposeInput = input => {
if (input) {
EventHandler.off(input, 'keydown');
EventHandler.off(input, 'keyup');
}
}
export function reset(id) {
const picker = Data.get(id);
if (picker) {
const { el, input, popover, invoke, options } = picker;
disposeInput(input);
Popover.dispose(popover);
picker.popover = createPopover(el, invoke, options);
picker.input = handlerInput(picker.el, picker.popover);
}
}
export function hide(id) {
@@ -55,8 +81,7 @@ export function dispose(id) {
if (data) {
const { input, popover } = data;
EventHandler.off(input, 'keydown');
EventHandler.off(input, 'keyup');
disposeInput(input);
Popover.dispose(popover)
}
}

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "AutoScroll"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "Picker ...",
"DatePlaceHolder": "Select date",
"TimePlaceHolder": "Select time",
"DateTimePlaceHolderText": "Please select ...",

View File

@@ -34,6 +34,7 @@
"AutoScrollText": "自动滚屏"
},
"BootstrapBlazor.Components.DateTimePicker": {
"PickerButtonText": "选择",
"DatePlaceHolder": "选择日期",
"TimePlaceHolder": "选择时间",
"DateTimePlaceHolderText": "请选择日期时间",

View File

@@ -388,6 +388,25 @@ public class DateTimePickerTest : BootstrapBlazorTestBase
pb.Add(a => a.TimeFormat, null);
});
}
[Fact]
public void IsButton_Ok()
{
var cut = Context.RenderComponent<DateTimePicker<DateTime>>(pb =>
{
pb.Add(a => a.IsButton, true);
pb.Add(a => a.ButtonColor, Color.Danger);
pb.Add(a => a.PickerButtonText, "Pick DateTime");
});
cut.Contains("btn dropdown-toggle btn-danger");
cut.SetParametersAndRender(pb =>
{
pb.Add(a => a.IsButton, false);
});
cut.DoesNotContain("btn dropdown-toggle btn-danger");
cut.Contains("dropdown-toggle form-control datetime-picker-input");
}
#endregion
#region DatePicker