Skip to content

Commit 722c959

Browse files
committed
New Component: MudDateTimePicker
1 parent 6a7efdc commit 722c959

9 files changed

Lines changed: 1453 additions & 1 deletion

File tree

docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@page "/muddatetimepicker"
2+
@namespace MudExtensions.Docs.Pages
3+
4+
<ExamplePage Component="typeof(MudDateTimePicker<>)">
5+
<ExampleCard ComponentName="DateTimePicker" ExampleName="DateTimePickerExample1" Title="Usage" Description="MudDateTimePicker automatically converts wheel values to DateTime?.">
6+
<DateTimePickerExample1 />
7+
</ExampleCard>
8+
<ExampleCard ComponentName="DateTimePicker" ExampleName="DateTimePickerExample2" Title="MinDate & MaxDate" Description="You can restrict available date with MinDate and MaxDate parameters.">
9+
<DateTimePickerExample2 />
10+
</ExampleCard>
11+
</ExamplePage>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
@namespace MudExtensions.Docs.Examples
2+
@using MudBlazor.Extensions
3+
4+
<MudGrid>
5+
<MudItem xs="12" sm="8">
6+
<MudDateTimePicker @bind-Value="_date" Editable="_editable" Label="Date" Clearable="_clearable"
7+
ShowToolbar="_showToolbar" Variant="_variant" Color="_color"
8+
Format="@_dateFormat"
9+
TransformOrigin="Origin.TopCenter" AnchorOrigin="Origin.BottomCenter" />
10+
</MudItem>
11+
12+
<MudItem xs="12" sm="4">
13+
<MudStack Spacing="4">
14+
<MudSelect @bind-Value="_dateView" Variant="Variant.Outlined" Label="Date View">
15+
@foreach (DateView item in Enum.GetValues<DateView>())
16+
{
17+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
18+
}
19+
</MudSelect>
20+
<MudSelect @bind-Value="_variant" Variant="Variant.Outlined" Label="Variant">
21+
@foreach (Variant item in Enum.GetValues<Variant>())
22+
{
23+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
24+
}
25+
</MudSelect>
26+
<MudSwitchM3 @bind-Value="_editable" Color="Color.Secondary" Label="Editable" />
27+
<MudSwitchM3 @bind-Value="_showToolbar" Color="Color.Secondary" Label="Show Toolbar" />
28+
<MudSwitchM3 @bind-Value="_showHeader" Color="Color.Secondary" Label="Show Header" />
29+
<MudSwitchM3 @bind-Value="_submitOnClose" Color="Color.Secondary" Label="Submit On Close" />
30+
<MudSwitchM3 @bind-Value="_isAdornmentEnd" Label="Adornment End" Color="Color.Secondary" />
31+
<MudSelect @bind-Value="_color" Variant="Variant.Outlined" Label="Color">
32+
@foreach (Color item in Enum.GetValues<Color>())
33+
{
34+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
35+
}
36+
</MudSelect>
37+
<MudTextField @bind-Value="_dateFormat" Variant="Variant.Outlined" Label="Date Format" />
38+
<MudButton OnClick="@(() => _date = DateTime.Now)">Set Today</MudButton>
39+
</MudStack>
40+
</MudItem>
41+
</MudGrid>
42+
43+
@code {
44+
private DateTime? _date = DateTime.Now;
45+
private DateView _dateView = DateView.Date;
46+
private bool _editable = false;
47+
private bool _showToolbar = true;
48+
private bool _showHeader = true;
49+
private bool _submitOnClose = true;
50+
private Color _color = Color.Primary;
51+
private string? _dateFormat;
52+
private bool _isAdornmentEnd = true;
53+
private bool _clearable = false;
54+
private Variant _variant = Variant.Outlined;
55+
}

docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/SelectExtended/Examples/SelectExtendedExample1.razor

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<MudSelectExtended T="string" Label="Data Based" ItemCollection="_collection" Disabled="_disabled" Modal="@_modal" />
1313
<MudSelectExtended T="string" Label="Data Based" ItemCollection="_collection" Disabled="_disabled" Modal="@_modal" AddNullItem="true" AddedNullItemText="@_nullItemText" />
1414
</MudStack>
15+
16+
<MudDateTimePicker T="DateTimeOffset" @bind-Value="_selectedDate" />
1517
</MudItem>
1618

1719
<MudItem xs="12" sm="4">
@@ -28,4 +30,6 @@
2830
private bool _modal = true;
2931
private string[] _collection = new string[] { "Foo", "Bar", "Fizz", "Buzz" };
3032
private string? _nullItemText = "None";
33+
34+
private DateTimeOffset _selectedDate = DateTimeOffset.Now;
3135
}

docs/CodeBeam.MudBlazor.Extensions.Docs/Services/MudExtensionsDocsService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class MudExtensionsDocsService
1515
new MudExtensionComponentInfo() {Title = "MudComboBox", Component = typeof(MudComboBox<>), RelatedComponents = new List<Type>() {typeof(MudComboBoxItem<string>)}, Usage = ComponentUsage.Input, IsUnique = true, Description = "Unites MudSelect and MudAutocomplete features."},
1616
new MudExtensionComponentInfo() {Title = "MudCssManager", Component = typeof(MudCssManager), Usage = ComponentUsage.Utility, IsUnique = true, IsUtility = true, Description = "Directly and dynamically get or set component's css property."},
1717
new MudExtensionComponentInfo() {Title = "MudCsvMapper", Component = typeof(MudCsvMapper), Usage = ComponentUsage.Display, IsUnique = true, Description = "A .csv file uploader that matches the .csv file headers to supplied / expected headers."},
18+
new MudExtensionComponentInfo() {Title = "MudDateTimePicker", Component = typeof(MudDateTimePicker<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "Unified generic date and time picker component."},
1819
new MudExtensionComponentInfo() {Title = "MudDateWheelPicker", Component = typeof(MudDateWheelPicker), Usage = ComponentUsage.Input, IsUnique = true, Description = "A date time picker with MudWheels."},
1920
new MudExtensionComponentInfo() {Title = "MudGallery", Component = typeof(MudGallery), Usage = ComponentUsage.Display, IsUnique = true, Description = "Mobile friendly image gallery component."},
2021
new MudExtensionComponentInfo() {Title = "MudInputStyler", Component = typeof(MudInputStyler), Usage = ComponentUsage.Utility, IsUnique = true, Description = "Applies colors or other CSS styles easily for mud inputs like MudTextField and MudSelect."},
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
using Microsoft.AspNetCore.Components;
2+
using MudBlazor;
3+
using MudBlazor.Extensions;
4+
using MudBlazor.State;
5+
using MudBlazor.Utilities;
6+
using System.Globalization;
7+
using System.Runtime.Serialization;
8+
9+
namespace MudExtensions
10+
{
11+
public abstract partial class MudBaseDatePickerX<T> : MudPicker<T>
12+
{
13+
internal readonly string _mudPickerCalendarContentElementId;
14+
private readonly ParameterState<string?> _formatState;
15+
16+
protected MudBaseDatePickerX()
17+
{
18+
_mudPickerCalendarContentElementId = Identifier.Create();
19+
Culture = CultureInfo.CurrentCulture;
20+
21+
using var registerScope = CreateRegisterScope();
22+
_formatState = registerScope.RegisterParameter<string?>(nameof(Format))
23+
.WithParameter(() => Format)
24+
.WithChangeHandler(FormatChangedAsync);
25+
}
26+
27+
// 🔥 GENERIC CONVERSION LAYER
28+
protected DateTime? ToDateTime(T? value)
29+
{
30+
if (value == null)
31+
return null;
32+
33+
if (value is DateTime dt)
34+
return dt;
35+
36+
if (value is DateTimeOffset dto)
37+
return dto.LocalDateTime;
38+
39+
throw new NotSupportedException($"Type {typeof(T)} not supported");
40+
}
41+
42+
protected T? FromDateTime(DateTime? date)
43+
{
44+
if (date == null)
45+
return default;
46+
47+
var t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
48+
49+
if (t == typeof(DateTime))
50+
return (T)(object)date.Value;
51+
52+
if (t == typeof(DateTimeOffset))
53+
{
54+
var offset = TimeZoneInfo.Local.GetUtcOffset(date.Value);
55+
return (T)(object)new DateTimeOffset(date.Value, offset);
56+
}
57+
58+
throw new NotSupportedException($"Type {typeof(T)} not supported");
59+
}
60+
61+
protected void ValidateType()
62+
{
63+
var t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
64+
65+
if (t != typeof(DateTime) && t != typeof(DateTimeOffset))
66+
throw new NotSupportedException($"Type {typeof(T)} not supported.");
67+
}
68+
69+
protected override void OnInitialized()
70+
{
71+
ValidateType();
72+
base.OnInitialized();
73+
}
74+
75+
[Inject] protected IScrollManager ScrollManager { get; set; } = null!;
76+
[Inject] private IJsApiService JsApiService { get; set; } = null!;
77+
[Inject] protected TimeProvider TimeProvider { get; set; } = null!;
78+
79+
[Parameter] public DateTime? MaxDate { get; set; }
80+
[Parameter] public DateTime? MinDate { get; set; }
81+
[Parameter] public OpenTo OpenTo { get; set; } = OpenTo.Date;
82+
83+
[Parameter, ParameterState]
84+
public string? Format { get; set; }
85+
86+
protected virtual Task FormatChangedAsync(string? newFormat) => Task.CompletedTask;
87+
88+
private Task FormatChangedAsync(ParameterChangedEventArgs<string?> args)
89+
=> FormatChangedAsync(args.Value);
90+
91+
[Parameter] public DayOfWeek? FirstDayOfWeek { get; set; }
92+
93+
internal DateTime? _picker_month;
94+
95+
[Parameter]
96+
public DateTime? PickerMonth
97+
{
98+
get => _picker_month;
99+
set
100+
{
101+
if (value == _picker_month)
102+
return;
103+
_picker_month = value;
104+
InvokeAsync(StateHasChanged);
105+
PickerMonthChanged.InvokeAsync(value);
106+
}
107+
}
108+
109+
protected internal DateTime? HighlightedDate { get; set; }
110+
111+
[Parameter] public EventCallback<DateTime?> PickerMonthChanged { get; set; }
112+
113+
[Parameter] public int ClosingDelay { get; set; } = 100;
114+
[Parameter] public int DisplayMonths { get; set; } = 1;
115+
[Parameter] public int? MaxMonthColumns { get; set; }
116+
[Parameter] public DateTime? StartMonth { get; set; }
117+
[Parameter] public bool ShowWeekNumbers { get; set; }
118+
[Parameter] public string TitleDateFormat { get; set; } = "ddd, dd MMM";
119+
[Parameter] public bool AutoClose { get; set; }
120+
121+
[Parameter]
122+
public Func<DateTime, bool> IsDateDisabledFunc { get; set; } = _ => false;
123+
124+
[Parameter] public Func<DateTime, string>? AdditionalDateClassesFunc { get; set; }
125+
[Parameter] public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft;
126+
[Parameter] public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight;
127+
128+
[Parameter] public int? FixYear { get; set; }
129+
[Parameter] public int? FixMonth { get; set; }
130+
[Parameter] public int? FixDay { get; set; }
131+
132+
protected OpenTo CurrentView;
133+
134+
protected override async Task OnPickerOpenedAsync()
135+
{
136+
await base.OnPickerOpenedAsync();
137+
138+
var dateTime = ToDateTime(_value);
139+
140+
if (dateTime.HasValue)
141+
{
142+
var culture = GetCulture();
143+
var calendar = culture.Calendar;
144+
PickerMonth = new DateTime(
145+
calendar.GetYear(dateTime.Value),
146+
calendar.GetMonth(dateTime.Value),
147+
1,
148+
calendar);
149+
}
150+
151+
CurrentView = OpenTo;
152+
}
153+
154+
protected DateTime GetMonthStart(int month)
155+
{
156+
var culture = GetCulture();
157+
var calendar = culture.Calendar;
158+
var baseDate = _picker_month ?? DateTime.Today;
159+
160+
return calendar.AddMonths(new DateTime(baseDate.Year, baseDate.Month, 1), month);
161+
}
162+
163+
protected IEnumerable<DateTime> GetWeek(int month, int index)
164+
{
165+
if (index is < 0 or > 5)
166+
throw new ArgumentException("Index must be between 0 and 5", nameof(index));
167+
168+
var culture = GetCulture();
169+
var monthFirst = GetMonthStart(month);
170+
171+
var weekFirst = monthFirst
172+
.AddDays(index * 7)
173+
.StartOfWeek(GetFirstDayOfWeek(), culture);
174+
175+
for (var i = 0; i < 7; i++)
176+
yield return weekFirst.AddDays(i);
177+
}
178+
179+
protected virtual bool IsDayDisabled(DateTime date)
180+
{
181+
return date < MinDate ||
182+
date > MaxDate ||
183+
IsDateDisabledFunc(date);
184+
}
185+
186+
protected abstract string GetDayClasses(int month, DateTime day);
187+
protected abstract Task OnDayClickedAsync(DateTime dateTime);
188+
189+
protected string FormatTitleDate(DateTime? date)
190+
{
191+
return date?.ToString(TitleDateFormat, GetCulture()) ?? "";
192+
}
193+
194+
protected string GetFormattedYearString()
195+
{
196+
var selectedYear = HighlightedDate ?? GetMonthStart(0);
197+
return selectedYear.Year.ToString();
198+
}
199+
200+
protected IEnumerable<string> GetAbbreviatedDayNames()
201+
{
202+
var culture = GetCulture();
203+
var names = culture.DateTimeFormat.AbbreviatedDayNames;
204+
205+
var firstDay = (int)GetFirstDayOfWeek();
206+
207+
return Enumerable.Range(0, 7).Select(i => names[(i + firstDay) % 7]);
208+
}
209+
210+
protected override IConverter<T?, string?> GetDefaultConverter()
211+
{
212+
return new DefaultConverter<T?>
213+
{
214+
Culture = GetCulture,
215+
Format = GetFormat
216+
};
217+
}
218+
219+
protected override string? ConvertSet(T? value)
220+
{
221+
var dt = ToDateTime(value);
222+
223+
if (dt == null)
224+
return null;
225+
226+
return dt.Value.ToString(GetFormat(), GetCulture());
227+
}
228+
229+
protected override string GetFormat()
230+
{
231+
if (!string.IsNullOrWhiteSpace(_formatState.Value))
232+
return _formatState.Value;
233+
234+
return $"{CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern} HH:mm";
235+
}
236+
237+
protected abstract DateTime GetCalendarStartOfMonth();
238+
protected abstract int GetCalendarYear(DateTime yearDate);
239+
240+
protected DayOfWeek GetFirstDayOfWeek()
241+
{
242+
return FirstDayOfWeek ?? GetCulture().DateTimeFormat.FirstDayOfWeek;
243+
}
244+
245+
protected DateTime GetMonthEnd(int month)
246+
{
247+
var culture = GetCulture();
248+
var calendar = culture.Calendar;
249+
var monthStartDate = PickerMonth ?? DateTime.Today.StartOfMonth(culture);
250+
251+
return calendar
252+
.AddMonths(monthStartDate, month)
253+
.EndOfMonth(culture);
254+
}
255+
256+
//private ValueTask HandleMouseoverOnPickerCalendarDayButton(int tempId)
257+
//{
258+
// return JsApiService.UpdateStyleProperty(_mudPickerCalendarContentElementId, "--selected-day", tempId);
259+
//}
260+
}
261+
}

0 commit comments

Comments
 (0)