@ -8,223 +8,100 @@ namespace Connected.Components;
public abstract partial class DatePickerBase : Picker < DateTime ? >
public abstract partial class DatePickerBase : Picker < DateTime ? >
{
{
#region Variables
private bool _dateFormatTouched ;
private bool _dateFormatTouched ;
private DateTime ? _picker_month ;
protected DatePickerBase ( ) : base ( new DefaultConverter < DateTime ? >
protected virtual bool IsRange { get ; } = false ;
{
protected OpenTo CurrentView ;
Format = CultureInfo . CurrentCulture . DateTimeFormat . ShortDatePattern ,
Culture = CultureInfo . CurrentCulture
} )
{
AdornmentAriaLabel = "Open Date Picker" ;
}
[Inject] protected IScrollManager ScrollManager { get ; set ; }
/// <summary>
/// <summary>
/// Max selectable date .
/// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker.
/// </summary>
/// </summary>
[Parameter]
private string _componentId = Guid . NewGuid ( ) . ToString ( ) ;
[Category(CategoryTypes.FormComponent.Validation)]
public DateTime ? MaxDate { get ; set ; }
/// <summary>
/// <summary>
/// Min selectable date.
/// Is set to true to scroll to the actual year after the next render
/// </summary>
/// </summary>
[Parameter]
private bool _scrollToYearAfterRender = false ;
[Category(CategoryTypes.FormComponent.Validation)]
# endregion
public DateTime ? MinDate { get ; set ; }
/// <summary>
#region Events
/// First view to show in the MudDatePicker.
private Typo GetMonthTypo ( DateTime month )
/// </summary>
{
[Parameter]
if ( GetMonthStart ( 0 ) = = month )
[Category(CategoryTypes.FormComponent.PickerBehavior)]
return Typo . h5 ;
public OpenTo OpenTo { get ; set ; } = OpenTo . Date ;
return Typo . subtitle1 ;
}
/// <summary>
protected abstract DateTime GetCalendarStartOfMonth ( ) ;
/// String Format for selected date view
/// </summary>
private int GetCalendarDayOfMonth ( DateTime date )
[Parameter]
[Category(CategoryTypes.FormComponent.Behavior)]
public string DateFormat
{
{
get
return Culture . Calendar . GetDayOfMonth ( date ) ;
{
return ( Converter as DefaultConverter < DateTime ? > ) ? . Format ;
}
set
{
if ( Converter is DefaultConverter < DateTime ? > defaultConverter )
{
defaultConverter . Format = value ;
_dateFormatTouched = true ;
}
DateFormatChanged ( value ) ;
}
}
}
/// <summary>
/// <summary>
/// Date format value change hook for descendants.
/// Converts gregorian year into whatever year it is in the provided culture
/// </summary>
/// </summary>
protected virtual Task DateFormatChanged ( string newFormat )
/// <param name="year">Gregorian year</param>
/// <returns>Year according to culture</returns>
protected abstract int GetCalendarYear ( int year ) ;
public async void ScrollToYear ( )
{
{
return Task . CompletedTask ;
_scrollToYearAfterRender = false ;
var id = $"{_componentId}{GetMonthStart(0).Year}" ;
await ScrollManager . ScrollToYearAsync ( id ) ;
StateHasChanged ( ) ;
}
}
protected override bool SetCulture ( CultureInfo value )
pr ivate int GetMinYear ( )
{
{
if ( ! base . SetCulture ( value ) )
if ( MinDate . HasValue )
return false ;
return MinDate . Value . Year ;
return DateTime . Today . Year - 100 ;
if ( ! _dateFormatTouched & & Converter is DefaultConverter < DateTime ? > defaultConverter )
defaultConverter . Format = value . DateTimeFormat . ShortDatePattern ;
return true ;
}
}
/// <summary>
private int GetMaxYear ( )
/// Defines on which day the week starts. Depends on the value of Culture.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DayOfWeek ? FirstDayOfWeek { get ; set ; } = null ;
/// <summary>
/// The current month of the date picker (two-way bindable). This changes when the user browses through the calender.
/// The month is represented as a DateTime which is always the first day of that month. You can also set this to define which month is initially shown. If not set, the current month is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DateTime ? PickerMonth
{
{
get = > _picker_month ;
if ( MaxDate . HasValue )
set
return MaxDate . Value . Year ;
{
return DateTime . Today . Year + 100 ;
if ( value = = _picker_month )
return ;
_picker_month = value ;
InvokeAsync ( StateHasChanged ) ;
PickerMonthChanged . InvokeAsync ( value ) ;
}
}
}
private DateTime ? _picker_month ;
/// <summary>
/// Fired when the date changes.
/// </summary>
[Parameter] public EventCallback < DateTime ? > PickerMonthChanged { get ; set ; }
/// <summary>
private Typo GetYearTypo ( int year )
/// Sets the amount of time in milliseconds to wait before closing the picker. This helps the user see that the date was selected before the popover disappears.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int ClosingDelay { get ; set ; } = 100 ;
/// <summary>
/// Number of months to display in the calendar
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int DisplayMonths { get ; set ; } = 1 ;
/// <summary>
/// Maximum number of months in one row
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerAppearance)]
public int? MaxMonthColumns { get ; set ; }
/// <summary>
/// Start month when opening the picker.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DateTime ? StartMonth { get ; set ; }
/// <summary>
/// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public bool ShowWeekNumbers { get ; set ; }
/// <summary>
/// Format of the selected date in the title. By default, this is "ddd, dd MMM" which abbreviates day and month names.
/// For instance, display the long names like this "dddd, dd. MMMM".
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public string TitleDateFormat { get ; set ; } = "ddd, dd MMM" ;
/// <summary>
/// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public bool AutoClose { get ; set ; }
/// <summary>
/// Function to determine whether a date is disabled
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public Func < DateTime , bool > IsDateDisabledFunc
{
{
get = > _isDateDisabledFunc ;
if ( year = = GetMonthStart ( 0 ) . Year )
set
return Typo . h5 ;
{
return Typo . subtitle1 ;
_isDateDisabledFunc = value ? ? ( _ = > false ) ;
}
}
}
private Func < DateTime , bool > _isDateDisabledFunc = _ = > false ;
/// <summary>
private void OnFormattedDateClick ( )
/// Function to conditionally apply new classes to specific days
{
/// </summary>
// todo: raise an event the user can handle
[Parameter]
}
[Category(CategoryTypes.FormComponent.Appearance)]
public Func < DateTime , string > AdditionalDateClassesFunc { get ; set ; }
/// <summary>
/// Custom previous icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerAppearance)]
public string PreviousIcon { get ; set ; } = Icons . Material . Filled . ChevronLeft ;
/// <summary>
/// Custom next icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerAppearance)]
public string NextIcon { get ; set ; } = Icons . Material . Filled . ChevronRight ;
/// <summary>
/// Set a predefined fix year - no year can be selected
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixYear { get ; set ; }
/// <summary>
/// Set a predefined fix month - no month can be selected
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixMonth { get ; set ; }
/// <summary>
/// Set a predefined fix day - no day can be selected
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixDay { get ; set ; }
protected virtual bool IsRange { get ; } = false ;
private IEnumerable < DateTime > GetAllMonths ( )
{
var current = GetMonthStart ( 0 ) ;
var calendarYear = Culture . Calendar . GetYear ( current ) ;
var firstOfCalendarYear = Culture . Calendar . ToDateTime ( calendarYear , 1 , 1 , 0 , 0 , 0 , 0 ) ;
for ( var i = 0 ; i < Culture . Calendar . GetMonthsInYear ( calendarYear ) ; i + + )
yield return Culture . Calendar . AddMonths ( firstOfCalendarYear , i ) ;
}
protected OpenTo CurrentView ;
private string GetAbbreviatedMonthName ( DateTime month )
{
var calendarMonth = Culture . Calendar . GetMonth ( month ) ;
return Culture . DateTimeFormat . AbbreviatedMonthNames [ calendarMonth - 1 ] ;
}
private string GetMonthName ( DateTime month )
{
var calendarMonth = Culture . Calendar . GetMonth ( month ) ;
return Culture . DateTimeFormat . MonthNames [ calendarMonth - 1 ] ;
}
protected override void OnPickerOpened ( )
protected override void OnPickerOpened ( )
{
{
base . OnPickerOpened ( ) ;
base . OnPickerOpened ( ) ;
@ -461,39 +338,73 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
_scrollToYearAfterRender = true ;
_scrollToYearAfterRender = true ;
}
}
}
}
/// <summary>
/// <summary>
/// We need a random id for the year items in the year list so we can scroll to the item safely in every DatePicker .
/// Date format value change hook for descendants .
/// </summary>
/// </summary>
private string _componentId = Guid . NewGuid ( ) . ToString ( ) ;
protected virtual Task DateFormatChanged ( string newFormat )
{
return Task . CompletedTask ;
}
protected override bool SetCulture ( CultureInfo value )
{
if ( ! base . SetCulture ( value ) )
return false ;
if ( ! _dateFormatTouched & & Converter is DefaultConverter < DateTime ? > defaultConverter )
defaultConverter . Format = value . DateTimeFormat . ShortDatePattern ;
return true ;
}
/// <summary>
/// <summary>
/// Is set to true to scroll to the actual year after the next render
/// Fired when the date changes.
/// </summary>
/// </summary>
private bool _scrollToYearAfterRender = false ;
[Parameter] public EventCallback < DateTime ? > PickerMonthChanged { get ; set ; }
public async void ScrollToYear ( )
# endregion
{
_scrollToYearAfterRender = false ;
var id = $"{_componentId}{GetMonthStart(0).Year}" ;
await ScrollManager . ScrollToYearAsync ( id ) ;
StateHasChanged ( ) ;
}
private int GetMinYear ( )
#region Content
[Inject] protected IScrollManager ScrollManager { get ; set ; }
/// <summary>
/// Defines on which day the week starts. Depends on the value of Culture.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DayOfWeek ? FirstDayOfWeek { get ; set ; } = null ;
/// <summary>
/// The current month of the date picker (two-way bindable). This changes when the user browses through the calender.
/// The month is represented as a DateTime which is always the first day of that month. You can also set this to define which month is initially shown. If not set, the current month is shown.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DateTime ? PickerMonth
{
{
if ( MinDate . HasValue )
get = > _picker_month ;
return MinDate . Value . Year ;
set
return DateTime . Today . Year - 100 ;
{
if ( value = = _picker_month )
return ;
_picker_month = value ;
InvokeAsync ( StateHasChanged ) ;
PickerMonthChanged . InvokeAsync ( value ) ;
}
}
}
# endregion
private int GetMaxYear ( )
#region Styling
private string GetMonthClasses ( DateTime month )
{
{
if ( MaxDate . HasValue )
if ( GetMonthStart( 0 ) = = month )
return MaxDate . Value . Year ;
return $"picker-month-selected mud-{Color.ToDescription()}-text" ;
return DateTime . Today . Year + 100 ;
return null ;
}
}
/// <summary>
/// Function to conditionally apply new classes to specific days
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Appearance)]
public Func < DateTime , string > AdditionalDateClassesFunc { get ; set ; }
private string GetYearClasses ( int year )
private string GetYearClasses ( int year )
{
{
if ( year = = GetMonthStart ( 0 ) . Year )
if ( year = = GetMonthStart ( 0 ) . Year )
@ -508,56 +419,161 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
. AddClass ( $"picker-calendar-header-last" , month = = DisplayMonths - 1 )
. AddClass ( $"picker-calendar-header-last" , month = = DisplayMonths - 1 )
. Build ( ) ;
. Build ( ) ;
}
}
/// <summary>
/// Custom previous icon.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerAppearance)]
public string PreviousIcon { get ; set ; } = Icons . Material . Filled . ChevronLeft ;
private Typo GetYearTypo ( int year )
/// <summary>
{
/// Custom next icon.
if ( year = = GetMonthStart ( 0 ) . Year )
/// </summary>
return Typo . h5 ;
[Parameter]
return Typo . subtitle1 ;
[Category(CategoryTypes.FormComponent.PickerAppearance)]
}
public string NextIcon { get ; set ; } = Icons . Material . Filled . ChevronRight ;
private void OnFormattedDateClick ( )
/// <summary>
/// String Format for selected date view
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Behavior)]
public string DateFormat
{
{
// todo: raise an event the user can handle
get
{
return ( Converter as DefaultConverter < DateTime ? > ) ? . Format ;
}
set
{
if ( Converter is DefaultConverter < DateTime ? > defaultConverter )
{
defaultConverter . Format = value ;
_dateFormatTouched = true ;
}
DateFormatChanged ( value ) ;
}
}
}
# endregion
#region Behavior
/// <summary>
/// Max selectable date.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public DateTime ? MaxDate { get ; set ; }
private IEnumerable < DateTime > GetAllMonths ( )
/// <summary>
{
/// Min selectable date.
var current = GetMonthStart ( 0 ) ;
/// </summary>
var calendarYear = Culture . Calendar . GetYear ( current ) ;
[Parameter]
var firstOfCalendarYear = Culture . Calendar . ToDateTime ( calendarYear , 1 , 1 , 0 , 0 , 0 , 0 ) ;
[Category(CategoryTypes.FormComponent.Validation)]
for ( var i = 0 ; i < Culture . Calendar . GetMonthsInYear ( calendarYear ) ; i + + )
public DateTime ? MinDate { get ; set ; }
yield return Culture . Calendar . AddMonths ( firstOfCalendarYear , i ) ;
}
private string GetAbbreviatedMonthName ( DateTime month )
/// <summary>
{
/// First view to show in the DatePicker.
var calendarMonth = Culture . Calendar . GetMonth ( month ) ;
/// </summary>
return Culture . DateTimeFormat . AbbreviatedMonthNames [ calendarMonth - 1 ] ;
[Parameter]
}
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public OpenTo OpenTo { get ; set ; } = OpenTo . Date ;
private string GetMonthName ( DateTime month )
/// <summary>
{
/// Set a predefined fix year - no year can be selected
var calendarMonth = Culture . Calendar . GetMonth ( month ) ;
/// </summary>
return Culture . DateTimeFormat . MonthNames [ calendarMonth - 1 ] ;
[Parameter]
}
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixYear { get ; set ; }
/// <summary>
/// Set a predefined fix month - no month can be selected
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixMonth { get ; set ; }
/// <summary>
/// Set a predefined fix day - no day can be selected
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int? FixDay { get ; set ; }
/// <summary>
/// Sets the amount of time in milliseconds to wait before closing the picker. This helps the user see that the date was selected before the popover disappears.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public int ClosingDelay { get ; set ; } = 100 ;
private string GetMonthClasses ( DateTime month )
/// <summary>
{
/// Number of months to display in the calendar
if ( GetMonthStart ( 0 ) = = month )
/// </summary>
return $"picker-month-selected mud-{Color.ToDescription()}-text" ;
[Parameter]
return null ;
[Category(CategoryTypes.FormComponent.PickerBehavior)]
}
public int DisplayMonths { get ; set ; } = 1 ;
private Typo GetMonthTypo ( DateTime month )
/// <summary>
/// Maximum number of months in one row
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerAppearance)]
public int? MaxMonthColumns { get ; set ; }
/// <summary>
/// Start month when opening the picker.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public DateTime ? StartMonth { get ; set ; }
/// <summary>
/// Display week numbers according to the Culture parameter. If no culture is defined, CultureInfo.CurrentCulture will be used.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public bool ShowWeekNumbers { get ; set ; }
/// <summary>
/// Format of the selected date in the title. By default, this is "ddd, dd MMM" which abbreviates day and month names.
/// For instance, display the long names like this "dddd, dd. MMMM".
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public string TitleDateFormat { get ; set ; } = "ddd, dd MMM" ;
/// <summary>
/// If AutoClose is set to true and PickerActions are defined, selecting a day will close the MudDatePicker.
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.PickerBehavior)]
public bool AutoClose { get ; set ; }
/// <summary>
/// Function to determine whether a date is disabled
/// </summary>
[Parameter]
[Category(CategoryTypes.FormComponent.Validation)]
public Func < DateTime , bool > IsDateDisabledFunc
{
{
if ( GetMonthStart ( 0 ) = = month )
get = > _isDateDisabledFunc ;
return Typo . h5 ;
set
return Typo . subtitle1 ;
{
_isDateDisabledFunc = value ? ? ( _ = > false ) ;
}
}
}
private Func < DateTime , bool > _isDateDisabledFunc = _ = > false ;
# endregion
#region Lifecycle
protected DatePickerBase ( ) : base ( new DefaultConverter < DateTime ? >
{
Format = CultureInfo . CurrentCulture . DateTimeFormat . ShortDatePattern ,
Culture = CultureInfo . CurrentCulture
} )
{
AdornmentAriaLabel = "Open Date Picker" ;
}
protected override void OnInitialized ( )
protected override void OnInitialized ( )
{
{
@ -584,17 +600,6 @@ public abstract partial class DatePickerBase : Picker<DateTime?>
ScrollToYear ( ) ;
ScrollToYear ( ) ;
}
}
protected abstract DateTime GetCalendarStartOfMonth ( ) ;
# endregion
private int GetCalendarDayOfMonth ( DateTime date )
{
return Culture . Calendar . GetDayOfMonth ( date ) ;
}
/// <summary>
/// Converts gregorian year into whatever year it is in the provided culture
/// </summary>
/// <param name="year">Gregorian year</param>
/// <returns>Year according to culture</returns>
protected abstract int GetCalendarYear ( int year ) ;
}
}