diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml index d00d32ec..152a43e8 100644 --- a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.xml @@ -1289,6 +1289,408 @@ + + + Represents a base class for designing date picker components. + + + + + Is set to true to scroll to the actual year after the next render + + + + + Represents the currently selected date + + + This date is highlighted in the UI + + + + + Represents the current view state of the object. + + + + + Initializes a new instance of the MudBaseDatePickerX class. + + + + + Gets or sets the scroll manager used to control scrolling behavior within the component. + + + + + Gets or sets the JavaScript interop service used to invoke JavaScript APIs from .NET code. + + This property is typically provided by dependency injection in Blazor applications to enable + communication between .NET and JavaScript. It should be set by the framework and not manually assigned in most + scenarios. + + + + Gets or sets the time provider used to obtain the current time within the component. + + This property allows for abstraction of time-related operations, enabling easier testing and + customization of time sources. When overriding the default time behavior, provide a suitable implementation of + the TimeProvider. + + + + Time zone information used to convert the selected date and time to a specific time zone. If not set, the component will use the local time zone of the user's device. This property is particularly useful when you want to display or store the selected date and time in a different time zone than the user's local time zone. + + + + + The maximum selectable date. + + + + + The minimum selectable date. + + + + + The initial view to display. + + + Defaults to . + + + + + The format for selected dates. + + + + + The current month shown in the date picker. + + + Defaults to the current month.
+ When bound via @bind-PickerMonth, controls the initial month displayed. This value is always the first day of a month. +
+
+ + + Occurs when has changed. + + + + + The delay, in milliseconds, before closing the picker after a value is selected. + + + Defaults to 100.
+ This delay helps the user see that a date has been selected before the popover disappears. +
+
+ + + The number of months to display in the calendar. + + + Defaults to 1. + + + + + The maximum number of months allowed in one row. + + + Defaults to null.
+ When null, the is used. +
+
+ + + The start month when opening the picker. + + + + + Shows week numbers at the start of each week. + + + Defaults to false. + + + + + The format of the selected date in the title. + + + Defaults to ddd, dd MMM.
+ Supported date formats can be found here: . +
+
+ + + Closes this picker when a value is selected. + + + Defaults to false. + + + + + The function used to disable one or more dates. + + + Defaults to null.
+ When set, a date will be disabled if the function returns true. +
+
+ + + The function which returns CSS classes for a date. + + + Multiple classes must be separated by spaces. + + + + + The icon for the button that navigates to the previous month or year. + + + Defaults to . + + + + + The icon for the button which navigates to the next month or year. + + + Defaults to . + + + + + The year to use, which cannot be changed. + + + Defaults to null. + + + + + The month to use, which cannot be changed. + + + Defaults to null. + + + + + The day to use, which cannot be changed. + + + Defaults to null. + + + + + True if the generic type T is DateOnly or nullable, false otherwise. + + + + + Generic conversion method to convert the generic type T to DateTime. Supports DateTime and DateTimeOffset. + + The value to convert. + The converted DateTime value. + Thrown when the type T is not supported. + + + + Converts a nullable value to an instance of type , if supported. + + If is , the local time zone offset is + applied to the converted value. + The nullable value to convert. If , the method returns the default + value for . + An instance of type representing the specified date, or the default value for + if is . + Thrown if is not or . + + + + + + + + + + True if the current view is either hours or minutes, false otherwise. + + + + + True if the current view is either date, month or year, false otherwise. + + + + + Returns the date and time format string to use for formatting operations. + + The returned format string is suitable for use with date and time formatting methods. If no + custom format is specified, the method combines the current culture's short date pattern with a 24-hour time + component. + A format string representing the date and time pattern. If a custom format is set, that value is returned; + otherwise, a default pattern based on the current culture's short date pattern and a 24-hour time format is + used. + + + + Scrolls to the current year. + + + + + Provides date and time selection in a single component. The date and time can be submitted together or separately. The time selection is done through an interactive clock interface where the user can select hours and minutes by clicking or dragging a pointer. + + + + + + + + + + + + + + + The currently selected value. + + + When this value changes, occurs. + + + + + Occurs when has changed. + + + + + Shows a 12-hour selection clock. + + + Defaults to false.
+ When true, hours 1-12 are displayed with an AM or PM marker.
+ When false, hours 0-23 are displayed.
+
+
+ + + The step interval when selecting minutes. + + + Defaults to 1. For example: a value of 15 would allow minutes 0, 15, + 30, and 45 be selected. + + + + + Controls which values can be edited. + + + Defaults to . + + + + + Gets or sets the text displayed for the AM period in a time picker or similar control. + + + + + Gets or sets the text displayed for the post-meridiem (PM) indicator. + + + + + Handles the event when a day is clicked in the calendar view. This method updates the working value with the selected date, and if appropriate based on the component's configuration, submits the new value and closes the picker. + + The date that was clicked. + A task that represents the asynchronous operation. + + + + Submits the current value asynchronously, triggering value change notifications and validation as appropriate. + + The method does not perform any action if the control is in a read-only state or if the + working value is null. Upon successful submission, the method updates the value, invokes change notifications, + updates the displayed text, and initiates validation. + A task that represents the asynchronous submit operation. + + + + Clears the selected date and time, resetting the component to its initial state. If is true, the picker will also close after clearing the value. + + Indicates whether the picker should close after clearing the value. + A task that represents the asynchronous operation. + + + + Gets the formatted date string for the title of the picker, based on the current working value, the component's value, or the current local date if neither is set. The date is formatted according to the culture settings and the specified format for the title. + + The formatted date string for the title of the picker. + + + + Calculates the first day of the month for the current calendar context. + + The returned date is determined using the culture-specific calendar, which may affect the + calculation of the month's start depending on the culture in use. + A representing the first day of the month, based on the current value, highlighted date, + or the current local date if neither is set. + + + + Calculates the calendar year corresponding to the specified date, adjusted according to the current value and + culture settings. + + The result is determined using the calendar of the current culture. If the current value is + not set, the calculation uses the current local date. + The date for which to determine the calendar year. The calculation is based on the calendar associated with the + current culture. + The calendar year as an integer, adjusted based on the current value and the specified date. + + + + Gets the hour portion of the selected time. + + A two-character string depending on whether is set, or -- if no value is set. + + + + Gets the minute portion of the selected time. + + A two-digit string for minutes, or -- if no value is set. + + + + Scrolls to the current year. + + + + + Sets the current view of the picker to the specified value. + + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. + + @@ -5985,8 +6387,13 @@ - + + + + + + The timezone of the watch. If null, DateTime.Now will be used. diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/DateTimePickerPage.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/DateTimePickerPage.razor new file mode 100644 index 00000000..a2daa74d --- /dev/null +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/DateTimePickerPage.razor @@ -0,0 +1,14 @@ +@page "/muddatetimepicker" +@namespace MudExtensions.Docs.Pages + + + + + + + + + + + + diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample1.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample1.razor new file mode 100644 index 00000000..a4ce5a8e --- /dev/null +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample1.razor @@ -0,0 +1,43 @@ +@namespace MudExtensions.Docs.Examples +@using MudBlazor.Extensions + + + + + + + + + + @foreach (Variant item in Enum.GetValues()) + { + @item.ToDescriptionString() + } + + + + + + + @foreach (Color item in Enum.GetValues()) + { + @item.ToDescriptionString() + } + + + Set Today + + + + +@code { + private DateTime? _date = DateTime.Now; + private bool _editable = false; + private bool _showToolbar = true; + private Color _color = Color.Primary; + private string? _dateFormat; + private bool _clearable = false; + private Variant _variant = Variant.Outlined; + private bool _amPm = false; +} diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample2.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample2.razor new file mode 100644 index 00000000..e599abe7 --- /dev/null +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample2.razor @@ -0,0 +1,27 @@ +@namespace MudExtensions.Docs.Examples +@using MudBlazor.Extensions + + + + + + + + + + + + + + + + + +@code { + private DateTime? _date = DateTime.Now; + private DateTimeOffset _date2 = DateTimeOffset.UtcNow; + private DateOnly _date3 = DateOnly.FromDateTime(DateTime.Now); + private bool _clearable = false; + private bool _editable = false; +} diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample3.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample3.razor new file mode 100644 index 00000000..2e0ca6d7 --- /dev/null +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/DateTimePicker/Examples/DateTimePickerExample3.razor @@ -0,0 +1,52 @@ +@namespace MudExtensions.Docs.Examples +@using MudBlazor.Extensions + + + + + + Clear + Cancel + OK + + + + + + + + @foreach (Variant item in Enum.GetValues()) + { + @item.ToDescriptionString() + } + + + + + + + @foreach (Color item in Enum.GetValues()) + { + @item.ToDescriptionString() + } + + + Set Today + + + + +@code { + private MudDateTimePicker _picker = null!; + + private DateTime? _date = DateTime.Now; + private bool _editable = false; + private bool _showToolbar = true; + private bool _submitOnClose = true; + private Color _color = Color.Primary; + private string? _dateFormat; + private bool _isAdornmentEnd = true; + private bool _clearable = false; + private Variant _variant = Variant.Outlined; +} diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Services/MudExtensionsDocsService.cs b/docs/CodeBeam.MudBlazor.Extensions.Docs/Services/MudExtensionsDocsService.cs index 223769ee..ca3254ab 100644 --- a/docs/CodeBeam.MudBlazor.Extensions.Docs/Services/MudExtensionsDocsService.cs +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Services/MudExtensionsDocsService.cs @@ -1,49 +1,49 @@ using MudExtensions.Utilities; -namespace MudExtensions.Docs.Services +namespace MudExtensions.Docs.Services; + +public class MudExtensionsDocsService { - public class MudExtensionsDocsService + List _components = new() { - List _components = new() - { - new MudExtensionComponentInfo() {Title = "MudAnimate", Component = typeof(MudAnimate), Usage = ComponentUsage.Utility, IsUnique = true, Description = "A revolutionary next generation animate component."}, - new MudExtensionComponentInfo() {Title = "MudBarcode", Component = typeof(MudBarcode), Usage = ComponentUsage.Display, IsUnique = true, Description = "A QR and barcode viewer with defined value."}, - new MudExtensionComponentInfo() {Title = "MudChipField", Component = typeof(MudChipField<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A textfield which transform a text to a chip when pressed pre-defined key."}, - new MudExtensionComponentInfo() {Title = "MudCodeInput", Component = typeof(MudCodeInput<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A group of textfields that each one contains a char."}, - new MudExtensionComponentInfo() {Title = "MudCodeViewer", Component = typeof(MudCodeViewer), Usage = ComponentUsage.Display, IsUnique = true, Description = "A viewer for code blocks with basic editable options."}, - new MudExtensionComponentInfo() {Title = "MudColorProvider", Component = typeof(MudColorProvider), Usage = ComponentUsage.Utility, IsUnique = true, Description = "An extension for primary, secondary and tertiary colors to support Material 3.", IsMaterial3 = true}, - new MudExtensionComponentInfo() {Title = "MudComboBox", Component = typeof(MudComboBox<>), RelatedComponents = new List() {typeof(MudComboBoxItem)}, Usage = ComponentUsage.Input, IsUnique = true, Description = "Unites MudSelect and MudAutocomplete features."}, - 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."}, - 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."}, - new MudExtensionComponentInfo() {Title = "MudDateWheelPicker", Component = typeof(MudDateWheelPicker), Usage = ComponentUsage.Input, IsUnique = true, Description = "A date time picker with MudWheels."}, - new MudExtensionComponentInfo() {Title = "MudGallery", Component = typeof(MudGallery), Usage = ComponentUsage.Display, IsUnique = true, Description = "Mobile friendly image gallery component."}, - 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."}, - new MudExtensionComponentInfo() {Title = "MudJsonTreeView", Component = typeof(MudJsonTreeView), Usage = ComponentUsage.Display, RelatedComponents = new List() {typeof(MudJsonTreeViewNode)}, IsUnique = true, Description = "A treeview for display json data."}, - new MudExtensionComponentInfo() {Title = "MudListExtended", Component = typeof(MudListExtended<>), Usage = ComponentUsage.Input, RelatedComponents = new List() {typeof(MudListItemExtended)}, IsUnique = false, Description = "The extended MudList component with richer features."}, - new MudExtensionComponentInfo() {Title = "MudLoading", Component = typeof(MudLoading), Usage = ComponentUsage.Display, IsUnique = true, Description = "Loading container for a whole page or a specific section."}, - new MudExtensionComponentInfo() {Title = "MudLoadingButton", Component = typeof(MudLoadingButton), Usage = ComponentUsage.Button, IsUnique = true, Description = "A classic MudButton with loading improvements."}, - new MudExtensionComponentInfo() {Title = "MudPage", Component = typeof(MudPage), Usage = ComponentUsage.Layout, RelatedComponents = new List() {typeof(MudSection)}, IsUnique = true, Description = "A CSS grid layout component that builds columns and rows, supports ColSpan & RowSpan."}, - new MudExtensionComponentInfo() {Title = "MudPasswordField", Component = typeof(MudPasswordField<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A specialized textfield that designed for working easily with passwords."}, - new MudExtensionComponentInfo() {Title = "MudPopup", Component = typeof(MudPopup), Usage = ComponentUsage.Display, IsUnique = true, Description = "A mobile friendly multi-functional popup content."}, - new MudExtensionComponentInfo() {Title = "MudRangeSlider", Component = typeof(MudRangeSlider), Usage = ComponentUsage.Input, IsUnique = true, Description = "A slider with range capabilities, set upper and lower values."}, - new MudExtensionComponentInfo() {Title = "MudScrollbar", Component = typeof(MudScrollbar), Usage = ComponentUsage.Utility, IsUnique = true, Description = "Customize all or defined scrollbars."}, - new MudExtensionComponentInfo() {Title = "MudSelectExtended", Component = typeof(MudSelectExtended<>), Usage = ComponentUsage.Input, RelatedComponents = new List() {typeof(MudSelectItemExtended)}, IsUnique = false, Description = "The extended MudSelect component with richer features."}, - new MudExtensionComponentInfo() {Title = "MudSignaturePad", Component = typeof(MudSignaturePad), Usage = ComponentUsage.Display, IsUnique = true, Description = "Draw and export a signature on a canvas."}, - new MudExtensionComponentInfo() {Title = "MudSpeedDial", Component = typeof(MudSpeedDial), Usage = ComponentUsage.Button, IsUnique = true, Description = "One button that stack other buttons in a popover."}, - new MudExtensionComponentInfo() {Title = "MudSplitter", Component = typeof(MudSplitter), Usage = ComponentUsage.Layout, IsUnique = true, Description = "A resizeable content splitter."}, - new MudExtensionComponentInfo() {Title = "MudStepperExtended", Component = typeof(MudStepperExtended), Usage = ComponentUsage.Display, RelatedComponents = new List() {typeof(MudStepExtended)}, IsUnique = false, Description = "A wizard-like steps to control the flow with rich options."}, - new MudExtensionComponentInfo() {Title = "MudSwitchM3", Component = typeof(MudSwitchM3), Usage = ComponentUsage.Input, IsUnique = true, IsMaterial3 = true, Description = "Material 3 switch component that has all MudSwitch features."}, - new MudExtensionComponentInfo() {Title = "MudTeleport", Component = typeof(MudTeleport), Usage = ComponentUsage.Layout, IsUnique = true, Description = "Teleport the content to the specified parent and redesign the DOM hierarchy."}, - new MudExtensionComponentInfo() {Title = "MudTextFieldExtended", Component = typeof(MudTextFieldExtended<>), Usage = ComponentUsage.Input, IsUnique = false, Description = "The extended MudTextField component with richer features."}, - new MudExtensionComponentInfo() {Title = "MudTextM3", Component = typeof(MudTextM3), Usage = ComponentUsage.Display, IsUnique = true, IsMaterial3 = true, Description = "Material 3 typography."}, - new MudExtensionComponentInfo() {Title = "MudTransferList", Component = typeof(MudTransferList<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A component that has 2 lists that transfer items to another."}, - new MudExtensionComponentInfo() {Title = "MudWatch", Component = typeof(MudWatch), Usage = ComponentUsage.Display, IsUnique = true, Description = "A performance optimized watch to show current time or show stopwatch or countdown."}, - new MudExtensionComponentInfo() {Title = "MudWheel", Component = typeof(MudWheel<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "Smoothly changes values in a wheel within defined ItemCollection."}, - }; + new MudExtensionComponentInfo() {Title = "MudAnimate", Component = typeof(MudAnimate), Usage = ComponentUsage.Utility, IsUnique = true, Description = "A revolutionary next generation animate component."}, + new MudExtensionComponentInfo() {Title = "MudBarcode", Component = typeof(MudBarcode), Usage = ComponentUsage.Display, IsUnique = true, Description = "A QR and barcode viewer with defined value."}, + new MudExtensionComponentInfo() {Title = "MudChipField", Component = typeof(MudChipField<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A textfield which transform a text to a chip when pressed pre-defined key."}, + new MudExtensionComponentInfo() {Title = "MudCodeInput", Component = typeof(MudCodeInput<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A group of textfields that each one contains a char."}, + new MudExtensionComponentInfo() {Title = "MudCodeViewer", Component = typeof(MudCodeViewer), Usage = ComponentUsage.Display, IsUnique = true, Description = "A viewer for code blocks with basic editable options."}, + new MudExtensionComponentInfo() {Title = "MudColorProvider", Component = typeof(MudColorProvider), Usage = ComponentUsage.Utility, IsUnique = true, Description = "An extension for primary, secondary and tertiary colors to support Material 3.", IsMaterial3 = true}, + new MudExtensionComponentInfo() {Title = "MudComboBox", Component = typeof(MudComboBox<>), RelatedComponents = new List() {typeof(MudComboBoxItem)}, Usage = ComponentUsage.Input, IsUnique = true, Description = "Unites MudSelect and MudAutocomplete features."}, + 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."}, + 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."}, + new MudExtensionComponentInfo() {Title = "MudDateTimePicker", Component = typeof(MudDateTimePicker<>), Usage = ComponentUsage.Input, IsUnique = false, Description = "Unified generic date and time picker component."}, + new MudExtensionComponentInfo() {Title = "MudDateWheelPicker", Component = typeof(MudDateWheelPicker), Usage = ComponentUsage.Input, IsUnique = true, Description = "A date time picker with MudWheels."}, + new MudExtensionComponentInfo() {Title = "MudGallery", Component = typeof(MudGallery), Usage = ComponentUsage.Display, IsUnique = true, Description = "Mobile friendly image gallery component."}, + 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."}, + new MudExtensionComponentInfo() {Title = "MudJsonTreeView", Component = typeof(MudJsonTreeView), Usage = ComponentUsage.Display, RelatedComponents = new List() {typeof(MudJsonTreeViewNode)}, IsUnique = true, Description = "A treeview for display json data."}, + new MudExtensionComponentInfo() {Title = "MudListExtended", Component = typeof(MudListExtended<>), Usage = ComponentUsage.Input, RelatedComponents = new List() {typeof(MudListItemExtended)}, IsUnique = false, Description = "The extended MudList component with richer features."}, + new MudExtensionComponentInfo() {Title = "MudLoading", Component = typeof(MudLoading), Usage = ComponentUsage.Display, IsUnique = true, Description = "Loading container for a whole page or a specific section."}, + new MudExtensionComponentInfo() {Title = "MudLoadingButton", Component = typeof(MudLoadingButton), Usage = ComponentUsage.Button, IsUnique = true, Description = "A classic MudButton with loading improvements."}, + new MudExtensionComponentInfo() {Title = "MudPage", Component = typeof(MudPage), Usage = ComponentUsage.Layout, RelatedComponents = new List() {typeof(MudSection)}, IsUnique = true, Description = "A CSS grid layout component that builds columns and rows, supports ColSpan & RowSpan."}, + new MudExtensionComponentInfo() {Title = "MudPasswordField", Component = typeof(MudPasswordField<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A specialized textfield that designed for working easily with passwords."}, + new MudExtensionComponentInfo() {Title = "MudPopup", Component = typeof(MudPopup), Usage = ComponentUsage.Display, IsUnique = true, Description = "A mobile friendly multi-functional popup content."}, + new MudExtensionComponentInfo() {Title = "MudRangeSlider", Component = typeof(MudRangeSlider), Usage = ComponentUsage.Input, IsUnique = true, Description = "A slider with range capabilities, set upper and lower values."}, + new MudExtensionComponentInfo() {Title = "MudScrollbar", Component = typeof(MudScrollbar), Usage = ComponentUsage.Utility, IsUnique = true, Description = "Customize all or defined scrollbars."}, + new MudExtensionComponentInfo() {Title = "MudSelectExtended", Component = typeof(MudSelectExtended<>), Usage = ComponentUsage.Input, RelatedComponents = new List() {typeof(MudSelectItemExtended)}, IsUnique = false, Description = "The extended MudSelect component with richer features."}, + new MudExtensionComponentInfo() {Title = "MudSignaturePad", Component = typeof(MudSignaturePad), Usage = ComponentUsage.Display, IsUnique = true, Description = "Draw and export a signature on a canvas."}, + new MudExtensionComponentInfo() {Title = "MudSpeedDial", Component = typeof(MudSpeedDial), Usage = ComponentUsage.Button, IsUnique = true, Description = "One button that stack other buttons in a popover."}, + new MudExtensionComponentInfo() {Title = "MudSplitter", Component = typeof(MudSplitter), Usage = ComponentUsage.Layout, IsUnique = true, Description = "A resizeable content splitter."}, + new MudExtensionComponentInfo() {Title = "MudStepperExtended", Component = typeof(MudStepperExtended), Usage = ComponentUsage.Display, RelatedComponents = new List() {typeof(MudStepExtended)}, IsUnique = false, Description = "A wizard-like steps to control the flow with rich options."}, + new MudExtensionComponentInfo() {Title = "MudSwitchM3", Component = typeof(MudSwitchM3), Usage = ComponentUsage.Input, IsUnique = true, IsMaterial3 = true, Description = "Material 3 switch component that has all MudSwitch features."}, + new MudExtensionComponentInfo() {Title = "MudTeleport", Component = typeof(MudTeleport), Usage = ComponentUsage.Layout, IsUnique = true, Description = "Teleport the content to the specified parent and redesign the DOM hierarchy."}, + new MudExtensionComponentInfo() {Title = "MudTextFieldExtended", Component = typeof(MudTextFieldExtended<>), Usage = ComponentUsage.Input, IsUnique = false, Description = "The extended MudTextField component with richer features."}, + new MudExtensionComponentInfo() {Title = "MudTextM3", Component = typeof(MudTextM3), Usage = ComponentUsage.Display, IsUnique = true, IsMaterial3 = true, Description = "Material 3 typography."}, + new MudExtensionComponentInfo() {Title = "MudTransferList", Component = typeof(MudTransferList<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "A component that has 2 lists that transfer items to another."}, + new MudExtensionComponentInfo() {Title = "MudWatch", Component = typeof(MudWatch), Usage = ComponentUsage.Display, IsUnique = true, Description = "A performance optimized watch to show current time or show stopwatch or countdown."}, + new MudExtensionComponentInfo() {Title = "MudWheel", Component = typeof(MudWheel<>), Usage = ComponentUsage.Input, IsUnique = true, Description = "Smoothly changes values in a wheel within defined ItemCollection."}, + }; - public List GetAllComponentInfo() - { - return _components; - } + public List GetAllComponentInfo() + { + return _components; } } diff --git a/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudBaseDatePickerX.cs b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudBaseDatePickerX.cs new file mode 100644 index 00000000..f6b9c008 --- /dev/null +++ b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudBaseDatePickerX.cs @@ -0,0 +1,506 @@ +using Microsoft.AspNetCore.Components; +using MudBlazor; +using MudBlazor.Extensions; +using MudBlazor.State; +using System.Globalization; + +namespace MudExtensions; + +/// +/// Represents a base class for designing date picker components. +/// +public abstract partial class MudBaseDatePickerX : MudPicker +{ + internal readonly string _mudPickerCalendarContentElementId; + private readonly ParameterState _formatState; + protected readonly string _componentId = Identifier.Create(); + + internal DateTime? _picker_month; + + /// + /// Is set to true to scroll to the actual year after the next render + /// + protected bool _scrollToYearAfterRender = false; + + /// + /// Represents the currently selected date + /// + /// + /// This date is highlighted in the UI + /// + protected internal DateTime? HighlightedDate { get; set; } + + /// + /// Represents the current view state of the object. + /// + protected OpenTo CurrentView; + + /// + /// Initializes a new instance of the MudBaseDatePickerX class. + /// + protected MudBaseDatePickerX() + { + _mudPickerCalendarContentElementId = Identifier.Create(); + Culture = CultureInfo.CurrentCulture; + + using var registerScope = CreateRegisterScope(); + _formatState = registerScope.RegisterParameter(nameof(DateFormat)) + .WithParameter(() => DateFormat) + .WithChangeHandler(DateFormatChangedAsync); + } + + /// + /// Gets or sets the scroll manager used to control scrolling behavior within the component. + /// + [Inject] protected IScrollManager ScrollManager { get; set; } = null!; + + /// + /// Gets or sets the JavaScript interop service used to invoke JavaScript APIs from .NET code. + /// + /// This property is typically provided by dependency injection in Blazor applications to enable + /// communication between .NET and JavaScript. It should be set by the framework and not manually assigned in most + /// scenarios. + [Inject] private IJsApiService JsApiService { get; set; } = null!; + + /// + /// Gets or sets the time provider used to obtain the current time within the component. + /// + /// This property allows for abstraction of time-related operations, enabling easier testing and + /// customization of time sources. When overriding the default time behavior, provide a suitable implementation of + /// the TimeProvider. + [Inject] protected TimeProvider TimeProvider { get; set; } = null!; + + + /// + /// Time zone information used to convert the selected date and time to a specific time zone. If not set, the component will use the local time zone of the user's device. This property is particularly useful when you want to display or store the selected date and time in a different time zone than the user's local time zone. + /// + [Parameter] + public TimeZoneInfo? TimeZone { get; set; } + + /// + /// The maximum selectable date. + /// + [Parameter] public DateTime? MaxDate { get; set; } + + /// + /// The minimum selectable date. + /// + [Parameter] public DateTime? MinDate { get; set; } + + /// + /// The initial view to display. + /// + /// + /// Defaults to . + /// + [Parameter] public OpenTo OpenTo { get; set; } = OpenTo.Date; + + /// + /// The format for selected dates. + /// + [Parameter, ParameterState] + public string? DateFormat { get; set; } + + [Parameter] public DayOfWeek? FirstDayOfWeek { get; set; } + + /// + /// The current month shown in the date picker. + /// + /// + /// Defaults to the current month.
+ /// When bound via @bind-PickerMonth, controls the initial month displayed. This value is always the first day of a month. + ///
+ [Parameter] + public DateTime? PickerMonth + { + get => _picker_month; + set + { + if (value == _picker_month) + return; + _picker_month = value; + InvokeAsync(StateHasChanged); + PickerMonthChanged.InvokeAsync(value); + } + } + + /// + /// Occurs when has changed. + /// + [Parameter] public EventCallback PickerMonthChanged { get; set; } + + /// + /// The delay, in milliseconds, before closing the picker after a value is selected. + /// + /// + /// Defaults to 100.
+ /// This delay helps the user see that a date has been selected before the popover disappears. + ///
+ [Parameter] public int ClosingDelay { get; set; } = 100; + + /// + /// The number of months to display in the calendar. + /// + /// + /// Defaults to 1. + /// + [Parameter] public int DisplayMonths { get; set; } = 1; + + /// + /// The maximum number of months allowed in one row. + /// + /// + /// Defaults to null.
+ /// When null, the is used. + ///
+ [Parameter] public int? MaxMonthColumns { get; set; } + + /// + /// The start month when opening the picker. + /// + [Parameter] public DateTime? StartMonth { get; set; } + + /// + /// Shows week numbers at the start of each week. + /// + /// + /// Defaults to false. + /// + [Parameter] public bool ShowWeekNumbers { get; set; } + + /// + /// The format of the selected date in the title. + /// + /// + /// Defaults to ddd, dd MMM.
+ /// Supported date formats can be found here: . + ///
+ [Parameter] public string TitleDateFormat { get; set; } = "ddd, dd MMM"; + + /// + /// Closes this picker when a value is selected. + /// + /// + /// Defaults to false. + /// + [Parameter] public bool AutoClose { get; set; } + + /// + /// The function used to disable one or more dates. + /// + /// + /// Defaults to null.
+ /// When set, a date will be disabled if the function returns true. + ///
+ [Parameter] public Func IsDateDisabledFunc { get; set; } = _ => false; + + /// + /// The function which returns CSS classes for a date. + /// + /// + /// Multiple classes must be separated by spaces. + /// + [Parameter] public Func? AdditionalDateClassesFunc { get; set; } + + /// + /// The icon for the button that navigates to the previous month or year. + /// + /// + /// Defaults to . + /// + [Parameter] public string PreviousIcon { get; set; } = Icons.Material.Filled.ChevronLeft; + + /// + /// The icon for the button which navigates to the next month or year. + /// + /// + /// Defaults to . + /// + [Parameter] public string NextIcon { get; set; } = Icons.Material.Filled.ChevronRight; + + /// + /// The year to use, which cannot be changed. + /// + /// + /// Defaults to null. + /// + [Parameter] public int? FixYear { get; set; } + + /// + /// The month to use, which cannot be changed. + /// + /// + /// Defaults to null. + /// + [Parameter] public int? FixMonth { get; set; } + + /// + /// The day to use, which cannot be changed. + /// + /// + /// Defaults to null. + /// + [Parameter] public int? FixDay { get; set; } + + /// + /// True if the generic type T is DateOnly or nullable, false otherwise. + /// + protected internal bool IsDateOnly => (Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T)) == typeof(DateOnly); + + /// + /// Generic conversion method to convert the generic type T to DateTime. Supports DateTime and DateTimeOffset. + /// + /// The value to convert. + /// The converted DateTime value. + /// Thrown when the type T is not supported. + protected internal DateTime? ToDateTime(T? value) + { + if (value == null) + return null; + + var tz = TimeZone ?? TimeZoneInfo.Local; + + if (value is DateTime dt) + return dt; + + if (value is DateTimeOffset dto) + return TimeZoneInfo.ConvertTime(dto, tz).DateTime; + + if (value is DateOnly d) + return d.ToDateTime(TimeOnly.MinValue); + + throw new NotSupportedException($"Type {typeof(T)} not supported"); + } + + /// + /// Converts a nullable value to an instance of type , if supported. + /// + /// If is , the local time zone offset is + /// applied to the converted value. + /// The nullable value to convert. If , the method returns the default + /// value for . + /// An instance of type representing the specified date, or the default value for + /// if is . + /// Thrown if is not or . + protected T? FromDateTime(DateTime? date) + { + if (date == null) + return default; + + var tz = TimeZone ?? TimeZoneInfo.Local; + + var t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); + + if (t == typeof(DateTime)) + return (T)(object)date.Value; + + if (t == typeof(DateTimeOffset)) + { + var offset = tz.GetUtcOffset(date.Value); + return (T)(object)new DateTimeOffset(date.Value, offset); + } + + if (t == typeof(DateOnly)) + return (T)(object)DateOnly.FromDateTime(date.Value); + + throw new NotSupportedException($"Type {typeof(T)} not supported"); + } + + protected void ValidateType() + { + var t = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); + + if (t != typeof(DateTime) && t != typeof(DateTimeOffset) && t != typeof(DateOnly)) + throw new NotSupportedException($"Type {typeof(T)} not supported."); + } + + /// + protected override void OnInitialized() + { + ValidateType(); + base.OnInitialized(); + } + + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (firstRender) + { + _picker_month ??= GetCalendarStartOfMonth(); + } + + if (firstRender && CurrentView == OpenTo.Year) + { + ScrollToYearAsync().CatchAndLog(); + return; + } + + if (_scrollToYearAfterRender) + ScrollToYearAsync().CatchAndLog(); + } + + protected virtual Task DateFormatChangedAsync(string? newFormat) => Task.CompletedTask; + + private Task DateFormatChangedAsync(ParameterChangedEventArgs args) => DateFormatChangedAsync(args.Value); + + /// + /// True if the current view is either hours or minutes, false otherwise. + /// + public bool IsTimeView => CurrentView == OpenTo.Hours || CurrentView == OpenTo.Minutes; + + /// + /// True if the current view is either date, month or year, false otherwise. + /// + public bool IsDateView => + CurrentView == OpenTo.Date + || CurrentView == OpenTo.Month + || CurrentView == OpenTo.Year; + + protected override async Task OnPickerOpenedAsync() + { + await base.OnPickerOpenedAsync(); + + var dateTime = ToDateTime(_value); + + if (dateTime.HasValue) + { + var culture = GetCulture(); + var calendar = culture.Calendar; + PickerMonth = new DateTime( + calendar.GetYear(dateTime.Value), + calendar.GetMonth(dateTime.Value), + 1, + calendar); + } + + CurrentView = OpenTo; + } + + protected DateTime GetMonthStart(int month) + { + var culture = GetCulture(); + var calendar = culture.Calendar; + var baseDate = _picker_month ?? DateTime.Today; + + return calendar.AddMonths(new DateTime(baseDate.Year, baseDate.Month, 1), month); + } + + protected IEnumerable GetWeek(int month, int index) + { + if (index is < 0 or > 5) + throw new ArgumentException("Index must be between 0 and 5", nameof(index)); + + var culture = GetCulture(); + var monthFirst = GetMonthStart(month); + + var weekFirst = monthFirst + .AddDays(index * 7) + .StartOfWeek(GetFirstDayOfWeek(), culture); + + for (var i = 0; i < 7; i++) + yield return weekFirst.AddDays(i); + } + + protected virtual bool IsDayDisabled(DateTime date) + { + return date < MinDate || + date > MaxDate || + IsDateDisabledFunc(date); + } + + protected abstract string GetDayClasses(int month, DateTime day); + protected abstract Task OnDayClickedAsync(DateTime dateTime); + + protected string FormatTitleDate(DateTime? date) + { + return date?.ToString(TitleDateFormat, GetCulture()) ?? ""; + } + + protected IEnumerable GetAbbreviatedDayNames() + { + var culture = GetCulture(); + var names = culture.DateTimeFormat.AbbreviatedDayNames; + + var firstDay = (int)GetFirstDayOfWeek(); + + return Enumerable.Range(0, 7).Select(i => names[(i + firstDay) % 7]); + } + + protected override IConverter GetDefaultConverter() + { + return new DefaultConverter + { + Culture = GetCulture, + Format = GetFormat + }; + } + + protected override string? ConvertSet(T? value) + { + var dt = ToDateTime(value); + + if (dt == null) + return null; + + return dt.Value.ToString(GetFormat(), GetCulture()); + } + + protected internal string? ConvertSetInternal(T? value) + { + return ConvertSet(value); + } + + /// + /// Returns the date and time format string to use for formatting operations. + /// + /// The returned format string is suitable for use with date and time formatting methods. If no + /// custom format is specified, the method combines the current culture's short date pattern with a 24-hour time + /// component. + /// A format string representing the date and time pattern. If a custom format is set, that value is returned; + /// otherwise, a default pattern based on the current culture's short date pattern and a 24-hour time format is + /// used. + protected override string GetFormat() + { + if (!string.IsNullOrWhiteSpace(_formatState.Value)) + return _formatState.Value; + + if (IsDateOnly) + return CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; + + return $"{CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern} HH:mm"; + } + + protected abstract DateTime GetCalendarStartOfMonth(); + protected abstract int GetCalendarYear(DateTime yearDate); + + protected DayOfWeek GetFirstDayOfWeek() + { + return FirstDayOfWeek ?? GetCulture().DateTimeFormat.FirstDayOfWeek; + } + + protected DateTime GetMonthEnd(int month) + { + var culture = GetCulture(); + var calendar = culture.Calendar; + var monthStartDate = PickerMonth ?? DateTime.Today.StartOfMonth(culture); + + return calendar + .AddMonths(monthStartDate, month) + .EndOfMonth(culture); + } + + /// + /// Scrolls to the current year. + /// + public virtual Task ScrollToYearAsync(DateTime? date = null) + { + return Task.CompletedTask; + } + + //private ValueTask HandleMouseoverOnPickerCalendarDayButton(int tempId) + //{ + // return JsApiService.UpdateStyleProperty(_mudPickerCalendarContentElementId, "--selected-day", tempId); + //} +} \ No newline at end of file diff --git a/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor new file mode 100644 index 00000000..6e0aecc3 --- /dev/null +++ b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor @@ -0,0 +1,348 @@ +@typeparam T +@namespace MudExtensions +@inherits MudBaseDatePickerX + +@Render + +@code { + + protected override RenderFragment PickerContent => + @ + + + @GetFormattedYearString() + + @if (_mode == PickerMode.Time) + { + @GetTitleDateString() + } + + @if (!IsDateOnly) + { + + + + + + + + + } + + + + @if (_mode == PickerMode.Date) + { + @GetTitleDateString() + } + else + { +
+ @GetHourString() + : + @GetMinuteString() +
+ + @if (AmPm) + { + + + + + } + } +
+
+ + + @if (_mode == PickerMode.Date || IsDateOnly) + { +
+ @{ + int dayId = 0; + var culture = GetCulture(); + var calendar = culture.Calendar; + + if (_picker_month.HasValue && calendar.GetYear(_picker_month.Value) == 1 && calendar.GetMonth(_picker_month.Value) == 1) + { + dayId = -1; + } + } + + @for (int displayMonth = 0; displayMonth < DisplayMonths; ++displayMonth) + { + int tempMonth = displayMonth; + +
+ + @* 🔥 YEAR VIEW *@ + @if (tempMonth == 0 && CurrentView == OpenTo.Year) + { +
+ @for (int i = GetMinYear(); i <= GetMaxYear(); i++) + { + var year = i; + +
+ @year +
+ } +
+ } + + @* 🔥 MONTH VIEW *@ + else if (tempMonth == 0 && CurrentView == OpenTo.Month) + { + var calendarYear = GetCalendarYear(PickerMonth ?? DateTime.Today); + +
+
+ + @if (!FixYear.HasValue) + { + + + + + + } + else + { + + @calendarYear + + } +
+
+ +
+ @foreach (var month in GetAllMonths()) + { + + } +
+ } + + @* 🔥 DATE VIEW *@ + else if (CurrentView == OpenTo.Date || tempMonth > 0) + { +
+
+ + @if (!FixMonth.HasValue) + { + + + + + + } + else + { + + @GetMonthName(tempMonth) + + } +
+ +
+ + @if (ShowWeekNumbers) + { +
+ +
+ } + + @foreach (var dayName in GetAbbreviatedDayNames()) + { + + @dayName + + } +
+
+ +
+
+ + @for (int week = 0; week < 6; week++) + { + int tempWeek = week; + + var firstMonthFirstYear = + _picker_month.HasValue && + calendar.GetYear(_picker_month.Value) == 1 && + calendar.GetMonth(_picker_month.Value) == 1; + + @if (ShowWeekNumbers) + { +
+ + @GetWeekNumber(tempMonth, tempWeek) + +
+ } + + var wasMaxValue = false; + + @foreach (var day in GetWeek(tempMonth, tempWeek)) + { + var tempId = ++dayId; + + @if ((tempId != 0 || !firstMonthFirstYear) && !wasMaxValue) + { + var selectedDay = !firstMonthFirstYear ? day : day.AddDays(-1); + + // onpointerover = "@(async () => await HandleMouseoverOnPickerCalendarDayButton(tempId))" + + } + else + { + + } + wasMaxValue = day == calendar.MaxSupportedDateTime; + } + } +
+
+ } +
+ } +
+ } + else + { +
+
+ +
+
+ } +
+
; +} diff --git a/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor.cs b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor.cs new file mode 100644 index 00000000..2cf7af50 --- /dev/null +++ b/src/CodeBeam.MudBlazor.Extensions/Components/DateTimePicker/MudDateTimePicker.razor.cs @@ -0,0 +1,966 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using MudBlazor; +using MudBlazor.Extensions; +using MudBlazor.Utilities; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace MudExtensions; + +/// +/// Provides date and time selection in a single component. The date and time can be submitted together or separately. The time selection is done through an interactive clock interface where the user can select hours and minutes by clicking or dragging a pointer. +/// +/// +public partial class MudDateTimePicker : MudBaseDatePickerX +{ + [Inject] private IJSRuntime JsRuntime { get; set; } = null!; + + [DynamicDependency(nameof(OnStickClick))] + [DynamicDependency(nameof(SelectTimeFromStick))] + public MudDateTimePicker() + { + _dotNetReferenceLazy = new Lazy>>(CreateDotNetObjectReference); + } + + private string? _clockElementReferenceId; + private readonly Lazy>> _dotNetReferenceLazy; + + private DotNetObjectReference> CreateDotNetObjectReference() => DotNetObjectReference.Create(this); + + private DateTime? _workingValue; + private readonly SetTime _timeSet = new(); + private string _timeHourFormat; + + private record SetTime + { + public int Hour { get; set; } + public int Minute { get; set; } + } + + public bool PointerMoving { get; set; } + + protected ElementReference ClockElementReference { get; private set; } + private bool _amPm = false; + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + _workingValue = ToDateTime(Value); + } + + /// + protected override void OnParametersSet() + { + base.OnParametersSet(); + _workingValue = ToDateTime(Value); + SyncTimeFromValue(); + } + + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + // Initialize the pointer events for the clock every time it's created (ex: popover opening and closing). + if (ClockElementReference.Id != _clockElementReferenceId) + { + _clockElementReferenceId = ClockElementReference.Id; + + await JsRuntime.InvokeVoidAsyncWithErrorHandling("mudTimePicker.initPointerEvents", ClockElementReference, _dotNetReferenceLazy.Value); + } + } + + private void SyncTimeFromValue() + { + if (_workingValue == null) + { + _timeSet.Hour = 0; + _timeSet.Minute = 0; + return; + } + + _timeSet.Hour = _workingValue.Value.Hour; + _timeSet.Minute = _workingValue.Value.Minute; + } + + protected PickerMode _mode = PickerMode.Date; + + /// + /// The currently selected value. + /// + /// + /// When this value changes, occurs. + /// + [Parameter] + public T? Value + { + get => _value; + set => SetDateAsync(ToDateTime(value), true).CatchAndLog(); + } + + /// + /// Occurs when has changed. + /// + [Parameter] + public EventCallback ValueChanged { get; set; } + + /// + /// Shows a 12-hour selection clock. + /// + /// + /// Defaults to false.
+ /// When true, hours 1-12 are displayed with an AM or PM marker.
+ /// When false, hours 0-23 are displayed.
+ ///
+ [Parameter] + public bool AmPm + { + get => _amPm; + set + { + if (_amPm == value) + return; + + _amPm = value; + + Touched = true; + _ = SetTextAsync(ConvertSet(_value), false); + } + } + + /// + /// The step interval when selecting minutes. + /// + /// + /// Defaults to 1. For example: a value of 15 would allow minutes 0, 15, + /// 30, and 45 be selected. + /// + [Parameter] + public int MinuteSelectionStep { get; set; } = 1; + + /// + /// Controls which values can be edited. + /// + /// + /// Defaults to . + /// + [Parameter] + [Category(CategoryTypes.FormComponent.PickerBehavior)] + public TimeEditMode TimeEditMode { get; set; } = TimeEditMode.Normal; + + private int RoundToStepInterval(int value) + { + if (MinuteSelectionStep > 1) + { + var interval = MinuteSelectionStep % 60; + value = (value + (interval / 2)) / interval * interval; + + if (value == 60) + value = 0; + } + + return value; + } + + /// + /// Gets or sets the text displayed for the AM period in a time picker or similar control. + /// + [Parameter] + public string AmText { get; set; } = "AM"; + + /// + /// Gets or sets the text displayed for the post-meridiem (PM) indicator. + /// + [Parameter] + public string PmText { get; set; } = "PM"; + + protected override async Task WriteTextAsync(string? text) + { + if (string.IsNullOrWhiteSpace(text)) + { + _workingValue = null; + _value = default; + + await ValueChanged.InvokeAsync(_value); + return; + } + + var culture = GetCulture(); + + if (DateTime.TryParseExact(text, GetFormat(), culture, DateTimeStyles.None, out var parsed)) + { + _workingValue = parsed; + _value = FromDateTime(parsed); + + SyncTimeFromValue(); + + PickerMonth = new DateTime(parsed.Year, parsed.Month, 1); + + await ValueChanged.InvokeAsync(_value); + await BeginValidateAsync(); + FieldChanged(_value); + } + else + { + await SetTextAsync(ConvertSet(_value), false); + } + } + + private async Task OnAmClickedAsync() + { + _timeSet.Hour %= 12; + await UpdateTimeAsync(); + await FocusAsync(); + } + + private async Task OnPmClickedAsync() + { + if (_timeSet.Hour <= 12) + { + _timeSet.Hour += 12; + } + + _timeSet.Hour %= 24; + await UpdateTimeAsync(); + await FocusAsync(); + } + + private DateTimeOffset _lastSetTime = DateTimeOffset.MinValue; + private const int DebounceTimeoutMs = 100; + + protected internal async Task SetDateAsync(DateTime? date, bool updateValue) + { + var current = ToDateTime(_value); + + if (current != null && date != null && date.Value.Kind == DateTimeKind.Unspecified) + { + date = DateTime.SpecifyKind(date.Value, current.Value.Kind); + } + + var now = TimeProvider.GetUtcNow(); + + if (current == date && (now - _lastSetTime).TotalMilliseconds < DebounceTimeoutMs) + return; + + _lastSetTime = now; + + if (current != date || (date is null && Text != null)) + { + Touched = true; + + HighlightedDate = date; + + if (date is not null && IsDateDisabledFunc(date.Value.Date)) + { + await SetTextAsync(null, false); + return; + } + + if (date is not null) + { + var culture = GetCulture(); + PickerMonth = new DateTime( + culture.Calendar.GetYear(date.Value), + culture.Calendar.GetMonth(date.Value), + 1, + culture.Calendar); + } + + var converted = FromDateTime(date); + _value = converted; + + if (updateValue) + { + ResetConverterErrors(); + await SetTextAsync(ConvertSet(_value), false); + } + + await ValueChanged.InvokeAsync(_value); + await BeginValidateAsync(); + FieldChanged(_value); + } + } + + private async Task UpdateTimeAsync() + { + if (_workingValue == null) + _workingValue = TimeProvider.GetLocalNow().Date; + + _workingValue = new DateTime( + _workingValue.Value.Year, + _workingValue.Value.Month, + _workingValue.Value.Day, + _timeSet.Hour, + _timeSet.Minute, + 0 + ); + } + + private void SetDatePart(DateTime date) + { + var current = _workingValue ?? TimeProvider.GetLocalNow().Date; + + _workingValue = new DateTime( + date.Year, + date.Month, + date.Day, + current.Hour, + current.Minute, + current.Second + ); + } + + private void SetTimePart(int hour, int minute) + { + var current = _workingValue ?? TimeProvider.GetLocalNow().Date; + + _workingValue = new DateTime( + current.Year, + current.Month, + current.Day, + hour, + minute, + 0 + ); + } + + protected override string GetDayClasses(int month, DateTime day) + { + var b = new CssBuilder("mud-day"); + + b.AddClass(AdditionalDateClassesFunc?.Invoke(day) ?? string.Empty); + + if (day < GetMonthStart(month) || day > GetMonthEnd(month)) + return b.AddClass("mud-hidden").Build(); + + var current = _workingValue ?? ToDateTime(Value); + + if (current?.Date == day.Date) + return b.AddClass("mud-selected") + .AddClass($"mud-theme-{Color.ToStringFast(true)}") + .Build(); + + if (day.Date == TimeProvider.GetLocalNow().Date) + return b.AddClass("mud-current mud-button-outlined") + .AddClass($"mud-button-outlined-{Color.ToStringFast(true)} mud-{Color.ToStringFast(true)}-text") + .Build(); + + return b.Build(); + } + + /// + /// Handles the event when a day is clicked in the calendar view. This method updates the working value with the selected date, and if appropriate based on the component's configuration, submits the new value and closes the picker. + /// + /// The date that was clicked. + /// A task that represents the asynchronous operation. + protected override async Task OnDayClickedAsync(DateTime dateTime) + { + await FocusAsync(); + + SetDatePart(dateTime); + + if (PickerActions == null || AutoClose || PickerVariant == PickerVariant.Static) + { + await Task.Run(() => InvokeAsync(SubmitAsync)); + + if (PickerVariant != PickerVariant.Static) + { + await Task.Delay(TimeSpan.FromMilliseconds(ClosingDelay), TimeProvider); + await CloseAsync(false); + } + } + } + + /// + /// Submits the current value asynchronously, triggering value change notifications and validation as appropriate. + /// + /// The method does not perform any action if the control is in a read-only state or if the + /// working value is null. Upon successful submission, the method updates the value, invokes change notifications, + /// updates the displayed text, and initiates validation. + /// A task that represents the asynchronous submit operation. + protected override async Task SubmitAsync() + { + if (GetReadOnlyState()) + return; + + if (_workingValue == null) + return; + + var converted = FromDateTime(_workingValue); + + _value = converted; + + await ValueChanged.InvokeAsync(_value); + await SetTextAsync(ConvertSet(_value), false); + await BeginValidateAsync(); + FieldChanged(_value); + } + + /// + /// Clears the selected date and time, resetting the component to its initial state. If is true, the picker will also close after clearing the value. + /// + /// Indicates whether the picker should close after clearing the value. + /// A task that represents the asynchronous operation. + public override async Task ClearAsync(bool close = true) + { + await SetDateAsync(null, true); + + if (AutoClose) + await CloseAsync(false); + } + + /// + /// Gets the formatted date string for the title of the picker, based on the current working value, the component's value, or the current local date if neither is set. The date is formatted according to the culture settings and the specified format for the title. + /// + /// The formatted date string for the title of the picker. + protected string GetTitleDateString() + { + var date = _workingValue + ?? ToDateTime(Value) + ?? TimeProvider.GetLocalNow().Date; + + return FormatTitleDate(date); + } + + /// + /// Calculates the first day of the month for the current calendar context. + /// + /// The returned date is determined using the culture-specific calendar, which may affect the + /// calculation of the month's start depending on the culture in use. + /// A representing the first day of the month, based on the current value, highlighted date, + /// or the current local date if neither is set. + protected override DateTime GetCalendarStartOfMonth() + { + var date = ToDateTime(Value) ?? HighlightedDate ?? TimeProvider.GetLocalNow().Date; + return date.StartOfMonth(GetCulture()); + } + + /// + /// Calculates the calendar year corresponding to the specified date, adjusted according to the current value and + /// culture settings. + /// + /// The result is determined using the calendar of the current culture. If the current value is + /// not set, the calculation uses the current local date. + /// The date for which to determine the calendar year. The calculation is based on the calendar associated with the + /// current culture. + /// The calendar year as an integer, adjusted based on the current value and the specified date. + protected override int GetCalendarYear(DateTime yearDate) + { + var date = ToDateTime(Value) ?? TimeProvider.GetLocalNow().Date; + var diff = GetCulture().Calendar.GetYear(date) - GetCulture().Calendar.GetYear(yearDate); + + return GetCulture().Calendar.GetYear(date) - diff; + } + + protected string GetMonthName(int month) + { + var date = GetMonthStart(month); + return date.ToString("MMMM yyyy", GetCulture()); + } + + protected Task OnPreviousMonthClick() + { + PickerMonth = GetMonthStart(0).AddMonths(-1); + return Task.CompletedTask; + } + + protected Task OnNextMonthClick() + { + PickerMonth = GetMonthStart(0).AddMonths(1); + return Task.CompletedTask; + } + + private void GoToSelectedYear() + { + PickerMonth = HighlightedDate; + OnYearClick(); + } + + private void OnYearClick() + { + if (!FixYear.HasValue) + { + _mode = PickerMode.Date; + CurrentView = OpenTo.Year; + StateHasChanged(); + _scrollToYearAfterRender = true; + } + } + + protected int GetMinYear() + { + return MinDate?.Year ?? 1900; + } + + protected int GetMaxYear() + { + return MaxDate?.Year ?? 2100; + } + + protected Task OnYearClickedAsync(int year) + { + var current = ToDateTime(Value) ?? TimeProvider.GetLocalNow().Date; + PickerMonth = new DateTime(year, current.Month, 1); + + _workingValue = new DateTime( + year, + current.Month, + current.Day, + current.Hour, + current.Minute, + current.Second + ); + + CurrentView = OpenTo.Month; + return Task.CompletedTask; + } + + protected Typo GetYearTypo(int year) + { + var current = ToDateTime(Value); + return current?.Year == year ? Typo.h5 : Typo.body1; + } + + protected string GetYearClasses(int year) + { + var current = ToDateTime(Value); + + return new CssBuilder("mud-picker-year-text") + .AddClass("mud-selected", current?.Year == year) + .Build(); + } + + protected Task OnPreviousYearClick() + { + PickerMonth = (PickerMonth ?? DateTime.Today).AddYears(-1); + return Task.CompletedTask; + } + + protected Task OnNextYearClick() + { + PickerMonth = (PickerMonth ?? DateTime.Today).AddYears(1); + return Task.CompletedTask; + } + + protected IEnumerable GetAllMonths() + { + return Enumerable.Range(1, 12); + } + + protected Task OnMonthSelectedAsync(int month) + { + var current = _workingValue ?? ToDateTime(Value) ?? TimeProvider.GetLocalNow().Date; + PickerMonth = new DateTime(current.Year, month, 1); + + _workingValue = new DateTime( + current.Year, + month, + current.Day, + current.Hour, + current.Minute, + current.Second + ); + + CurrentView = OpenTo.Date; + return Task.CompletedTask; + } + + protected bool IsMonthDisabled(int month) + { + if (!MinDate.HasValue && !MaxDate.HasValue) + return false; + + var year = (PickerMonth ?? DateTime.Today).Year; + + var start = new DateTime(year, month, 1); + var end = start.AddMonths(1).AddDays(-1); + + return (MinDate.HasValue && end < MinDate.Value) + || (MaxDate.HasValue && start > MaxDate.Value); + } + + protected Typo GetMonthTypo(int month) + { + var current = ToDateTime(Value); + return current?.Month == month ? Typo.h6 : Typo.body2; + } + + protected string GetMonthClasses(int month) + { + var current = ToDateTime(Value); + + return new CssBuilder() + .AddClass("mud-selected", current?.Month == month) + .Build(); + } + + protected string GetAbbreviatedMonthName(int month) + { + return GetCulture().DateTimeFormat.AbbreviatedMonthNames[month - 1]; + } + + protected int GetWeekNumber(int month, int week) + { + var firstDay = GetWeek(month, week).First(); + + return GetCulture().Calendar.GetWeekOfYear( + firstDay, + CalendarWeekRule.FirstFourDayWeek, + GetFirstDayOfWeek()); + } + + protected string GetCalendarDayOfMonth(DateTime date) + { + return date.Day.ToString(GetCulture()); + } + + protected void OnFormattedDateClick() + { + CurrentView = OpenTo.Month; + } + + protected void OnMonthClicked(int month) + { + CurrentView = OpenTo.Month; + } + + private string GetCalendarHeaderClasses(int month) + { + return new CssBuilder("mud-picker-calendar-header") + .AddClass($"mud-picker-calendar-header-{month + 1}") + .AddClass($"mud-picker-calendar-header-last", month == DisplayMonths - 1) + .Build(); + } + + private string HourDialClassname => + new CssBuilder("mud-time-picker-dial") + .AddClass("mud-time-picker-hour") + .AddClass("mud-time-picker-dial-out", CurrentView != OpenTo.Hours) + .AddClass("mud-time-picker-dial-hidden", CurrentView != OpenTo.Hours) + .Build(); + + private string MinuteDialClassname => + new CssBuilder("mud-time-picker-dial") + .AddClass("mud-time-picker-minute") + .AddClass("mud-time-picker-dial-out", CurrentView != OpenTo.Minutes) + .AddClass("mud-time-picker-dial-hidden", CurrentView != OpenTo.Minutes) + .Build(); + + private string GetPointerRotation() + { + return $"rotateZ({GetDeg()}deg);"; + } + + private double GetDeg() + { + double deg = 0; + + if (CurrentView == OpenTo.Hours) + { + deg = _timeSet.Hour * 30 % 360; + } + + if (CurrentView == OpenTo.Minutes) + { + deg = _timeSet.Minute * 6 % 360; + } + + return deg; + } + + private string GetPointerHeight() + { + var height = 40; + + if (CurrentView == OpenTo.Minutes) + { + height = 40; + } + + if (CurrentView == OpenTo.Hours) + { + if (!AmPm && _timeSet.Hour > 0 && _timeSet.Hour < 13) + { + height = 26; + } + else + { + height = 40; + } + } + + return $"{height}%;"; + } + + private string GetNumberColor(int value) + { + if (CurrentView == OpenTo.Hours) + { + var h = _timeSet.Hour; + + if (AmPm) + { + h = _timeSet.Hour % 12; + if (_timeSet.Hour % 12 == 0) + { + h = 12; + } + } + + if (h == value) + { + return $"mud-clock-number mud-theme-{Color.ToStringFast(true)}"; + } + } + else if (CurrentView == OpenTo.Minutes && _timeSet.Minute == value) + { + return $"mud-clock-number mud-theme-{Color.ToStringFast(true)}"; + } + + return "mud-clock-number"; + } + + private string GetClockPointerColor() + { + return PointerMoving + ? $"mud-picker-time-clock-pointer mud-{Color.ToStringFast(true)}" + : $"mud-picker-time-clock-pointer mud-picker-time-clock-pointer-animation mud-{Color.ToStringFast(true)}"; + } + + private string GetClockPinColor() + { + return $"mud-picker-time-clock-pin mud-{Color.ToStringFast(true)}"; + } + + private string GetClockPointerThumbColor() + { + var deg = GetDeg(); + return deg % 30 == 0 + ? $"mud-picker-time-clock-pointer-thumb mud-onclock-text mud-onclock-primary mud-{Color.ToStringFast(true)}" + : $"mud-picker-time-clock-pointer-thumb mud-onclock-minute mud-{Color.ToStringFast(true)}-text"; + } + + private static string GetTransform(double angle, double radius, double offsetX, double offsetY) + { + angle = angle / 180 * Math.PI; + var x = ((Math.Sin(angle) * radius) + offsetX).ToString("F3", CultureInfo.InvariantCulture); + var y = (((Math.Cos(angle) + 1) * radius) + offsetY).ToString("F3", CultureInfo.InvariantCulture); + return $"transform: translate({x}px, {y}px);"; + } + + [JSInvokable] + public async Task SelectTimeFromStick(int value, bool pointerMoving) + { + PointerMoving = pointerMoving; + + if (CurrentView == OpenTo.Minutes) + _timeSet.Minute = RoundToStepInterval(value); + else + _timeSet.Hour = value; + + await UpdateTimeAsync(); + + StateHasChanged(); + } + + [JSInvokable] + public async Task OnStickClick(int value) + { + // The pointer is up and not moving so animations can be enabled again. + PointerMoving = false; + + // Clicking a stick will submit the time. + if (CurrentView == OpenTo.Minutes) + { + await SubmitAndCloseAsync(); + } + else if (CurrentView == OpenTo.Hours) + { + if (TimeEditMode == TimeEditMode.Normal) + { + CurrentView = OpenTo.Minutes; + } + else if (TimeEditMode == TimeEditMode.OnlyHours) + { + await SubmitAndCloseAsync(); + } + } + + // Manually update because the event won't do it from JavaScript. + StateHasChanged(); + } + + protected async Task SubmitAndCloseAsync() + { + if (PickerActions == null || AutoClose) + { + await SubmitAsync(); + + if (PickerVariant != PickerVariant.Static) + { + await Task.Delay(TimeSpan.FromMilliseconds(ClosingDelay), TimeProvider); + await CloseAsync(false); + } + } + } + + /// + /// Gets the hour portion of the selected time. + /// + /// A two-character string depending on whether is set, or -- if no value is set. + private string GetHourString() + { + if (_workingValue?.Hour == null) + { + return "--"; + } + + return _workingValue.Value.Hour.ToString("D2"); + } + + /// + /// Gets the minute portion of the selected time. + /// + /// A two-digit string for minutes, or -- if no value is set. + private string GetMinuteString() + { + if (_workingValue?.Minute == null) + { + return "--"; + } + + return _workingValue.Value.Minute.ToString("D2"); + } + + private async Task OnHourClickAsync() + { + CurrentView = OpenTo.Hours; + await FocusAsync(); + } + + private async Task OnMinutesClick() + { + CurrentView = OpenTo.Minutes; + await FocusAsync(); + } + + private async Task HourFormatChanged(string value) + { + if (value == "am") + { + await OnAmClickedAsync(); + } + else if (value == "pm") + { + await OnPmClickedAsync(); + } + StateHasChanged(); + } + + private void HandleModeChange(PickerMode mode) + { + if (mode == PickerMode.Date) + { + CurrentView = OpenTo.Date; + } + else if (mode == PickerMode.Time) + { + CurrentView = OpenTo.Hours; + } + } + + protected string GetFormattedYearString() + { + var date = _workingValue + ?? ToDateTime(Value) + ?? TimeProvider.GetLocalNow().Date; + + return date.Year.ToString(); + } + + /// + /// Scrolls to the current year. + /// + public override async Task ScrollToYearAsync(DateTime? date = null) + { + var culture = GetCulture(); + var calendar = culture.Calendar; + + _scrollToYearAfterRender = false; + + var dateTime = + date + ?? _workingValue + ?? ToDateTime(Value) + ?? TimeProvider.GetLocalNow().Date; + + var id = $"{_componentId}{calendar.GetYear(dateTime)}"; + + await ScrollManager.ScrollToYearAsync(id); + + StateHasChanged(); + } + + protected override async Task OnOpenedAsync() + { + _mode = PickerMode.Date; + CurrentView = OpenTo.Hours; + await base.OnOpenedAsync(); + } + + /// + /// Sets the current view of the picker to the specified value. + /// + public void SetView(OpenTo view) + { + switch (view) + { + case OpenTo.Date: + _mode = PickerMode.Date; + CurrentView = OpenTo.Date; + break; + + case OpenTo.Month: + _mode = PickerMode.Date; + CurrentView = OpenTo.Month; + break; + + case OpenTo.Year: + _mode = PickerMode.Date; + CurrentView = OpenTo.Year; + break; + + case OpenTo.Hours: + _mode = PickerMode.Time; + CurrentView = OpenTo.Hours; + break; + + case OpenTo.Minutes: + _mode = PickerMode.Time; + CurrentView = OpenTo.Minutes; + break; + } + + StateHasChanged(); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources asynchronously. + /// + protected override async ValueTask DisposeAsyncCore() + { + await base.DisposeAsyncCore(); + + if (IsJSRuntimeAvailable) + { + await JsRuntime.InvokeVoidAsyncWithErrorHandling("mudTimePicker.destroyPointerEvents", ClockElementReference); + } + + if (_dotNetReferenceLazy.IsValueCreated) + { + _dotNetReferenceLazy.Value.Dispose(); + } + } +} diff --git a/src/CodeBeam.MudBlazor.Extensions/Enums/PickerMode.cs b/src/CodeBeam.MudBlazor.Extensions/Enums/PickerMode.cs new file mode 100644 index 00000000..b95df001 --- /dev/null +++ b/src/CodeBeam.MudBlazor.Extensions/Enums/PickerMode.cs @@ -0,0 +1,8 @@ +namespace MudExtensions; + +#pragma warning disable CS1591 +public enum PickerMode +{ + Date, + Time +} diff --git a/src/CodeBeam.MudBlazor.Extensions/Styles/Components/_datetimepicker.scss b/src/CodeBeam.MudBlazor.Extensions/Styles/Components/_datetimepicker.scss new file mode 100644 index 00000000..fd1557fa --- /dev/null +++ b/src/CodeBeam.MudBlazor.Extensions/Styles/Components/_datetimepicker.scss @@ -0,0 +1,13 @@ +.mud-date-time-mode-selected { + background: rgba(0, 0, 0, 0.25) !important; +} + +.mud-date-time-toggle { + color: inherit !important; + border-color: inherit !important; +} + +.mud-date-time-toggle-item { + color: inherit !important; + border-color: inherit !important; +} diff --git a/src/CodeBeam.MudBlazor.Extensions/Styles/MudExtensions.scss b/src/CodeBeam.MudBlazor.Extensions/Styles/MudExtensions.scss index 720c56ec..f6b1dbe9 100644 --- a/src/CodeBeam.MudBlazor.Extensions/Styles/MudExtensions.scss +++ b/src/CodeBeam.MudBlazor.Extensions/Styles/MudExtensions.scss @@ -11,6 +11,7 @@ @import 'components/_typographym3'; @import 'components/_transferlist'; @import 'components/_codeinput'; +@import 'components/_datetimepicker'; @import 'components/_listextended'; @import 'components/_selectextended'; diff --git a/src/CodeBeam.MudBlazor.Extensions/wwwroot/MudExtensions.min.css b/src/CodeBeam.MudBlazor.Extensions/wwwroot/MudExtensions.min.css index 81fa6a8e..12832f06 100644 --- a/src/CodeBeam.MudBlazor.Extensions/wwwroot/MudExtensions.min.css +++ b/src/CodeBeam.MudBlazor.Extensions/wwwroot/MudExtensions.min.css @@ -1 +1 @@ -.mud-combobox{margin:0;padding:0;position:relative;list-style:none}.mud-combobox.mud-combobox-padding{padding-top:8px;padding-bottom:8px}.mud-combobox-item{width:100%;display:flex;position:relative;box-sizing:border-box;text-align:start;align-items:center;padding-top:8px;padding-bottom:8px;justify-content:flex-start;text-decoration:none;outline:none;min-height:48px}.mud-combobox-item.mud-combobox-item-comfort{padding-top:4px;padding-bottom:4px;min-height:40px}.mud-combobox-item.mud-combobox-item-slim{padding-top:2px;padding-bottom:2px;min-height:32px}.mud-combobox-item.mud-combobox-item-superslim{padding-top:0;padding-bottom:0;min-height:24px}.mud-combobox-item.mud-combobox-item-disabled{color:var(--mud-palette-action-disabled) !important;cursor:default !important;pointer-events:none !important}.mud-combobox-item.mud-combobox-item-disabled .mud-combobox-item-icon{color:var(--mud-palette-action-disabled) !important}.mud-combobox-item-clickable{color:inherit;border:0;cursor:pointer;margin:0;outline:0;user-select:none;border-radius:0;vertical-align:middle;background-color:rgba(0,0,0,0);-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);transition:background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-combobox-item-clickable:hover{background-color:var(--mud-palette-action-default-hover)}.mud-combobox-item-gutters{padding-left:16px;padding-right:16px}.mud-combobox-item-text{flex:1 1 auto;min-width:0;margin-top:4px;margin-bottom:4px;padding-inline-start:8px;padding-inline-end:8px}.mud-combobox-item-text-inset{padding-left:56px;padding-inline-start:56px;padding-inline-end:unset}.mud-combobox-item-icon{color:var(--mud-palette-action-default);display:inline-flex;flex-shrink:0;padding-inline-start:8px;padding-inline-end:8px;margin-inline-start:-4px;margin-inline-end:4px}.mud-combobox-item-multiselect{max-height:32px}.mud-combobox-item-multiselect.mud-combobox-item-multiselect-checkbox{padding-inline-end:16px}.mud-combobox-subheader{color:var(--mud-palette-action-default);background-color:var(--mud-palette-background);font-size:.875rem;box-sizing:border-box;list-style:none;font-weight:500;padding-top:16px;padding-bottom:16px}.mud-combobox-subheader-secondary-background{background-color:var(--mud-palette-background-gray)}.mud-combobox-subheader-gutters{padding-left:16px;padding-right:16px}.mud-combobox-subheader-inset{padding-left:72px;padding-inline-start:72px;padding-inline-end:unset}.mud-combobox-subheader-sticky{top:-8px;z-index:1;position:sticky}.mud-combobox-subheader-sticky.mud-combobox-subheader-sticky-dense{top:0}.mud-combobox-item-hilight{background-color:var(--mud-palette-background-gray)}.mud-combobox-item-hilight{background-color:var(--mud-palette-lines-default) !important}.mud-combobox-item-bordered{border-left:4px solid var(--mud-palette-lines-default);padding-inline-start:12px}.mud-combobox-item-bordered-primary{border-left:4px solid var(--mud-palette-primary);padding-inline-start:12px}.mud-combobox-item-bordered-secondary{border-left:4px solid var(--mud-palette-secondary);padding-inline-start:12px}.mud-combobox-item-bordered-tertiary{border-left:4px solid var(--mud-palette-tertiary);padding-inline-start:12px}.mud-combobox-item-bordered-info{border-left:4px solid var(--mud-palette-info);padding-inline-start:12px}.mud-combobox-item-bordered-success{border-left:4px solid var(--mud-palette-success);padding-inline-start:12px}.mud-combobox-item-bordered-warning{border-left:4px solid var(--mud-palette-warning);padding-inline-start:12px}.mud-combobox-item-bordered-error{border-left:4px solid var(--mud-palette-error);padding-inline-start:12px}.mud-combobox-item-bordered-dark{border-left:4px solid var(--mud-palette-dark);padding-inline-start:12px}.mud-combobox-item-nested-background{background-color:var(--mud-palette-background-gray)}.mud-combobox-item-avatar{min-width:56px;flex-shrink:0}.mud-combobox-highlighter{background-color:rgba(0,0,0,0);font-weight:bold;text-decoration:underline}.mud-gallery-selected-toolbox{left:0;right:0;height:56px;width:100%;background-color:rgba(0,0,0,.6)}.mud-gallery-selected-toolbox.gallery-toolbox-top{top:0}.mud-gallery-selected-toolbox.gallery-toolbox-bottom{bottom:0}.mud-page{display:grid;box-sizing:border-box;width:100%}.mud-page.mud-page-height-full{min-height:100vh}.mud-page.mud-page-height-full-without-appbar{min-height:calc(100vh - var(--mud-appbar-height))}.mud-page.mud-page-column-2{grid-template-columns:repeat(2, 50%)}.mud-page.mud-page-column-3{grid-template-columns:repeat(3, 33.333333%)}.mud-page.mud-page-column-4{grid-template-columns:repeat(4, 25%)}.mud-page.mud-page-column-5{grid-template-columns:repeat(5, 20%)}.mud-page.mud-page-column-6{grid-template-columns:repeat(6, 16.666666%)}.mud-page.mud-page-column-7{grid-template-columns:repeat(7, 14.285714%)}.mud-page.mud-page-column-8{grid-template-columns:repeat(8, 12.5%)}.mud-page.mud-page-column-9{grid-template-columns:repeat(9, 11.111111%)}.mud-page.mud-page-column-10{grid-template-columns:repeat(10, 10%)}.mud-page.mud-page-column-11{grid-template-columns:repeat(11, 9.090909%)}.mud-page.mud-page-column-12{grid-template-columns:repeat(12, 8.333333%)}.mud-page.mud-page-row-2{grid-template-rows:repeat(2, 50%)}.mud-page.mud-page-row-3{grid-template-rows:repeat(3, 33.333333%)}.mud-page.mud-page-row-4{grid-template-rows:repeat(4, 25%)}.mud-page.mud-page-row-5{grid-template-rows:repeat(5, 20%)}.mud-page.mud-page-row-6{grid-template-rows:repeat(6, 16.666666%)}.mud-page.mud-page-row-7{grid-template-rows:repeat(7, 14.285714%)}.mud-page.mud-page-row-8{grid-template-rows:repeat(8, 12.5%)}.mud-page.mud-page-row-9{grid-template-rows:repeat(9, 11.111111%)}.mud-page.mud-page-row-10{grid-template-rows:repeat(10, 10%)}.mud-page.mud-page-row-11{grid-template-rows:repeat(11, 9.090909%)}.mud-page.mud-page-row-12{grid-template-rows:repeat(12, 8.333333%)}.mud-section{display:inline-grid;overflow:auto}.mud-section.mud-section-col-start-1{grid-column-start:1}.mud-section.mud-section-col-start-2{grid-column-start:2}.mud-section.mud-section-col-start-3{grid-column-start:3}.mud-section.mud-section-col-start-4{grid-column-start:4}.mud-section.mud-section-col-start-5{grid-column-start:5}.mud-section.mud-section-col-start-6{grid-column-start:6}.mud-section.mud-section-col-start-7{grid-column-start:7}.mud-section.mud-section-col-start-8{grid-column-start:8}.mud-section.mud-section-col-start-9{grid-column-start:9}.mud-section.mud-section-col-start-10{grid-column-start:10}.mud-section.mud-section-col-start-11{grid-column-start:11}.mud-section.mud-section-col-start-12{grid-column-start:12}.mud-section.mud-section-col-end-1{grid-column-end:1}.mud-section.mud-section-col-end-2{grid-column-end:2}.mud-section.mud-section-col-end-3{grid-column-end:3}.mud-section.mud-section-col-end-4{grid-column-end:4}.mud-section.mud-section-col-end-5{grid-column-end:5}.mud-section.mud-section-col-end-6{grid-column-end:6}.mud-section.mud-section-col-end-7{grid-column-end:7}.mud-section.mud-section-col-end-8{grid-column-end:8}.mud-section.mud-section-col-end-9{grid-column-end:9}.mud-section.mud-section-col-end-10{grid-column-end:10}.mud-section.mud-section-col-end-11{grid-column-end:11}.mud-section.mud-section-col-end-12{grid-column-end:12}.mud-section.mud-section-col-end-13{grid-column-end:13}.mud-section.mud-section-row-start-1{grid-row-start:1}.mud-section.mud-section-row-start-2{grid-row-start:2}.mud-section.mud-section-row-start-3{grid-row-start:3}.mud-section.mud-section-row-start-4{grid-row-start:4}.mud-section.mud-section-row-start-5{grid-row-start:5}.mud-section.mud-section-row-start-6{grid-row-start:6}.mud-section.mud-section-row-start-7{grid-row-start:7}.mud-section.mud-section-row-start-8{grid-row-start:8}.mud-section.mud-section-row-start-9{grid-row-start:9}.mud-section.mud-section-row-start-10{grid-row-start:10}.mud-section.mud-section-row-start-11{grid-row-start:11}.mud-section.mud-section-row-start-12{grid-row-start:12}.mud-section.mud-section-row-end-1{grid-row-end:1}.mud-section.mud-section-row-end-2{grid-row-end:2}.mud-section.mud-section-row-end-3{grid-row-end:3}.mud-section.mud-section-row-end-4{grid-row-end:4}.mud-section.mud-section-row-end-5{grid-row-end:5}.mud-section.mud-section-row-end-6{grid-row-end:6}.mud-section.mud-section-row-end-7{grid-row-end:7}.mud-section.mud-section-row-end-8{grid-row-end:8}.mud-section.mud-section-row-end-9{grid-row-end:9}.mud-section.mud-section-row-end-10{grid-row-end:10}.mud-section.mud-section-row-end-11{grid-row-end:11}.mud-section.mud-section-row-end-12{grid-row-end:12}.mud-section.mud-section-row-end-13{grid-row-end:13}.mud-popup{z-index:2000;overflow:auto;background-color:var(--mud-palette-background);min-height:var(--mud-appbar-height)}.mud-popup.mud-popup-center{height:300px;left:50%;top:50%;transform:translate(-50%, -50%);width:320px;aspect-ratio:1/1}.mud-range-container{align-items:center;margin:20px 0}.mud-range-container input::-webkit-slider-thumb{pointer-events:all;position:relative;z-index:1}.mud-range-container input::-moz-range-thumb{pointer-events:all;position:relative;z-index:10}.mud-range-container input::-moz-range-track{position:relative;z-index:-1}.mud-range-container input:last-of-type::-moz-range-track{-moz-appearance:none}.mud-range-container .mud-slider-input:last-of-type{position:absolute;pointer-events:none;top:0}.mud-range-container input[type=range]::-webkit-slider-thumb{pointer-events:all}.mud-range-display{text-align:center}.mud-signature-pad-container{touch-action:none}.mud-splitter{display:grid;position:relative;width:100%}.mud-splitter-content{overflow:auto}.mud-splitter-thumb ::-webkit-slider-runnable-track{visibility:hidden !important;height:100% !important}.mud-splitter-thumb ::-moz-range-track{visibility:hidden !important;height:100% !important}.mud-splitter-track{position:absolute;top:50%;transform:translateY(-50%);height:100%}.mud-splitter-track.mud-slider{visibility:hidden !important}.mud-splitter-track.mud-slider .mud-slider-container{height:100% !important}.mud-splitter-track.mud-slider .mud-slider-input{top:50%}.mud-splitter-thumb ::-webkit-slider-thumb{visibility:visible !important;appearance:none !important;-webkit-appearance:none !important;top:50% !important;transform:translateY(-50%) !important;height:100% !important;width:8px !important;border:none !important;border-radius:0 !important;cursor:ew-resize !important}.mud-splitter-thumb-disabled ::-webkit-slider-thumb{cursor:default !important}.mud-splitter-thumb ::-moz-range-thumb{visibility:visible !important;appearance:none !important;-moz-appearance:none !important;top:50% !important;transform:translateY(-50%) !important;height:100% !important;width:8px !important;border:none !important;border-radius:0 !important;cursor:ew-resize !important}.mud-splitter-thumb-disabled ::-moz-range-thumb{cursor:default !important}.mud-stepper-header-extended{min-height:62px;border-radius:var(--mud-default-borderradius)}.mud-stepper-header-extended.mud-stepper-header-non-linear-extended:hover{background-color:var(--mud-palette-action-default-hover)}.mud-stepper-badge-extended{z-index:21}.mud-stepper-avatar-extended{z-index:20}.mud-stepper-avatar-bg-extended{background-color:var(--mud-palette-background)}.mud-stepper-sub-inner-header-extended{grid-column-start:1;grid-column-end:-1;flex-direction:row;grid-row:1;list-style:none;display:flex}.mud-stepper-sub-inner-header-vertical-extended{grid-row-start:1;grid-row-end:-1;flex-direction:column;grid-column:1;list-style:none;display:flex}.mud-stepper-text-mobile-extended{grid-row:1;margin-top:22px}.mud-stepper-text-mobile-vertical-extended{grid-column:1;margin-inline-start:22px}.mud-stepper-step-1.horizontal{width:calc(100%/1)}.mud-stepper-step-1.vertical{height:calc(100%/1)}.mud-stepper-step-2.horizontal{width:calc(100%/2)}.mud-stepper-step-2.vertical{height:calc(100%/2)}.mud-stepper-step-3.horizontal{width:calc(100%/3)}.mud-stepper-step-3.vertical{height:calc(100%/3)}.mud-stepper-step-4.horizontal{width:calc(100%/4)}.mud-stepper-step-4.vertical{height:calc(100%/4)}.mud-stepper-step-5.horizontal{width:calc(100%/5)}.mud-stepper-step-5.vertical{height:calc(100%/5)}.mud-stepper-step-6.horizontal{width:calc(100%/6)}.mud-stepper-step-6.vertical{height:calc(100%/6)}.mud-stepper-step-7.horizontal{width:calc(100%/7)}.mud-stepper-step-7.vertical{height:calc(100%/7)}.mud-stepper-step-8.horizontal{width:calc(100%/8)}.mud-stepper-step-8.vertical{height:calc(100%/8)}.mud-stepper-step-9.horizontal{width:calc(100%/9)}.mud-stepper-step-9.vertical{height:calc(100%/9)}.mud-stepper-step-10.horizontal{width:calc(100%/10)}.mud-stepper-step-10.vertical{height:calc(100%/10)}.mud-stepper-step-11.horizontal{width:calc(100%/11)}.mud-stepper-step-11.vertical{height:calc(100%/11)}.mud-stepper-step-12.horizontal{width:calc(100%/12)}.mud-stepper-step-12.vertical{height:calc(100%/12)}.mud-stepper-grid-1.horizontal{display:grid;grid-template-columns:repeat(2, 1fr)}.mud-stepper-grid-1.vertical{display:grid;grid-template-rows:repeat(2, 1fr)}.mud-stepper-grid-2.horizontal{display:grid;grid-template-columns:repeat(4, 1fr)}.mud-stepper-grid-2.vertical{display:grid;grid-template-rows:repeat(4, 1fr)}.mud-stepper-grid-3.horizontal{display:grid;grid-template-columns:repeat(6, 1fr)}.mud-stepper-grid-3.vertical{display:grid;grid-template-rows:repeat(6, 1fr)}.mud-stepper-grid-4.horizontal{display:grid;grid-template-columns:repeat(8, 1fr)}.mud-stepper-grid-4.vertical{display:grid;grid-template-rows:repeat(8, 1fr)}.mud-stepper-grid-5.horizontal{display:grid;grid-template-columns:repeat(10, 1fr)}.mud-stepper-grid-5.vertical{display:grid;grid-template-rows:repeat(10, 1fr)}.mud-stepper-grid-6.horizontal{display:grid;grid-template-columns:repeat(12, 1fr)}.mud-stepper-grid-6.vertical{display:grid;grid-template-rows:repeat(12, 1fr)}.mud-stepper-grid-7.horizontal{display:grid;grid-template-columns:repeat(14, 1fr)}.mud-stepper-grid-7.vertical{display:grid;grid-template-rows:repeat(14, 1fr)}.mud-stepper-grid-8.horizontal{display:grid;grid-template-columns:repeat(16, 1fr)}.mud-stepper-grid-8.vertical{display:grid;grid-template-rows:repeat(16, 1fr)}.mud-stepper-grid-9.horizontal{display:grid;grid-template-columns:repeat(18, 1fr)}.mud-stepper-grid-9.vertical{display:grid;grid-template-rows:repeat(18, 1fr)}.mud-stepper-grid-10.horizontal{display:grid;grid-template-columns:repeat(20, 1fr)}.mud-stepper-grid-10.vertical{display:grid;grid-template-rows:repeat(20, 1fr)}.mud-stepper-grid-11.horizontal{display:grid;grid-template-columns:repeat(22, 1fr)}.mud-stepper-grid-11.vertical{display:grid;grid-template-rows:repeat(22, 1fr)}.mud-stepper-grid-12.horizontal{display:grid;grid-template-columns:repeat(24, 1fr)}.mud-stepper-grid-12.vertical{display:grid;grid-template-rows:repeat(24, 1fr)}.mud-stepper-grid-13.horizontal{display:grid;grid-template-columns:repeat(26, 1fr)}.mud-stepper-grid-13.vertical{display:grid;grid-template-rows:repeat(26, 1fr)}.mud-stepper-grid-14.horizontal{display:grid;grid-template-columns:repeat(28, 1fr)}.mud-stepper-grid-14.vertical{display:grid;grid-template-rows:repeat(28, 1fr)}.mud-stepper-grid-15.horizontal{display:grid;grid-template-columns:repeat(30, 1fr)}.mud-stepper-grid-15.vertical{display:grid;grid-template-rows:repeat(30, 1fr)}.mud-stepper-grid-16.horizontal{display:grid;grid-template-columns:repeat(32, 1fr)}.mud-stepper-grid-16.vertical{display:grid;grid-template-rows:repeat(32, 1fr)}.mud-stepper-grid-17.horizontal{display:grid;grid-template-columns:repeat(34, 1fr)}.mud-stepper-grid-17.vertical{display:grid;grid-template-rows:repeat(34, 1fr)}.mud-stepper-grid-18.horizontal{display:grid;grid-template-columns:repeat(36, 1fr)}.mud-stepper-grid-18.vertical{display:grid;grid-template-rows:repeat(36, 1fr)}.mud-stepper-grid-19.horizontal{display:grid;grid-template-columns:repeat(38, 1fr)}.mud-stepper-grid-19.vertical{display:grid;grid-template-rows:repeat(38, 1fr)}.mud-stepper-grid-20.horizontal{display:grid;grid-template-columns:repeat(40, 1fr)}.mud-stepper-grid-20.vertical{display:grid;grid-template-rows:repeat(40, 1fr)}.mud-stepper-grid-21.horizontal{display:grid;grid-template-columns:repeat(42, 1fr)}.mud-stepper-grid-21.vertical{display:grid;grid-template-rows:repeat(42, 1fr)}.mud-stepper-grid-22.horizontal{display:grid;grid-template-columns:repeat(44, 1fr)}.mud-stepper-grid-22.vertical{display:grid;grid-template-rows:repeat(44, 1fr)}.mud-stepper-grid-23.horizontal{display:grid;grid-template-columns:repeat(46, 1fr)}.mud-stepper-grid-23.vertical{display:grid;grid-template-rows:repeat(46, 1fr)}.mud-stepper-grid-24.horizontal{display:grid;grid-template-columns:repeat(48, 1fr)}.mud-stepper-grid-24.vertical{display:grid;grid-template-rows:repeat(48, 1fr)}.steps-1.horizontal{grid-column-start:2;grid-column-end:2;grid-row:1/-1}.steps-1.vertical{grid-row-start:2;grid-row-end:2;grid-column:1/-1}.steps-2.horizontal{grid-column-start:2;grid-column-end:4;grid-row:1/-1}.steps-2.vertical{grid-row-start:2;grid-row-end:4;grid-column:1/-1}.steps-3.horizontal{grid-column-start:2;grid-column-end:6;grid-row:1/-1}.steps-3.vertical{grid-row-start:2;grid-row-end:6;grid-column:1/-1}.steps-4.horizontal{grid-column-start:2;grid-column-end:8;grid-row:1/-1}.steps-4.vertical{grid-row-start:2;grid-row-end:8;grid-column:1/-1}.steps-5.horizontal{grid-column-start:2;grid-column-end:10;grid-row:1/-1}.steps-5.vertical{grid-row-start:2;grid-row-end:10;grid-column:1/-1}.steps-6.horizontal{grid-column-start:2;grid-column-end:12;grid-row:1/-1}.steps-6.vertical{grid-row-start:2;grid-row-end:12;grid-column:1/-1}.steps-7.horizontal{grid-column-start:2;grid-column-end:14;grid-row:1/-1}.steps-7.vertical{grid-row-start:2;grid-row-end:14;grid-column:1/-1}.steps-8.horizontal{grid-column-start:2;grid-column-end:16;grid-row:1/-1}.steps-8.vertical{grid-row-start:2;grid-row-end:16;grid-column:1/-1}.steps-9.horizontal{grid-column-start:2;grid-column-end:18;grid-row:1/-1}.steps-9.vertical{grid-row-start:2;grid-row-end:18;grid-column:1/-1}.steps-10.horizontal{grid-column-start:2;grid-column-end:20;grid-row:1/-1}.steps-10.vertical{grid-row-start:2;grid-row-end:20;grid-column:1/-1}.steps-11.horizontal{grid-column-start:2;grid-column-end:22;grid-row:1/-1}.steps-11.vertical{grid-row-start:2;grid-row-end:22;grid-column:1/-1}.steps-12.horizontal{grid-column-start:2;grid-column-end:24;grid-row:1/-1}.steps-12.vertical{grid-row-start:2;grid-row-end:24;grid-column:1/-1}.mud-stepper-progress-extended.horizontal.header-size-small{top:22px;height:2px}.mud-stepper-progress-extended.horizontal.header-size-medium{top:30px;height:3px}.mud-stepper-progress-extended.horizontal.header-size-large{top:38px;height:4px}.mud-stepper-progress-extended.vertical.header-size-small{left:22px}.mud-stepper-progress-extended.vertical.header-size-medium{left:30px}.mud-stepper-progress-extended.vertical.header-size-large{left:38px}.mobile.horizontal .mud-stepper-progress-extended{margin-inline-start:40px}.mud-stepper-progress-extended{display:inline-grid;z-index:10}.mud-stepper-progress-extended.vertical{transform:rotateX(180deg)}.mud-switch-m3{cursor:pointer;display:inline-flex;align-items:center;vertical-align:middle;margin-top:4px;margin-bottom:4px;-webkit-tap-highlight-color:rgba(0,0,0,0)}.mud-switch-m3.mud-disabled{color:var(--mud-palette-text-disabled) !important;cursor:default}.mud-switch-m3.mud-readonly,.mud-switch-m3 .mud-readonly:hover{cursor:default;background-color:rgba(0,0,0,0) !important}.mud-switch-span-m3{width:52px;height:32px;display:inline-flex;z-index:0;position:relative;box-sizing:border-box;flex-shrink:0;vertical-align:middle}.mud-switch-span-m3.mud-switch-child-content-m3{margin-inline-end:12px}.mud-switch-span-m3 .mud-switch-track-m3{width:52px;height:32px;z-index:-1;transition:opacity 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;border-radius:30px;background-color:var(--mud-palette-background);border:2px solid}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-default-m3{border-color:var(--mud-palette-text-primary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-primary-m3{border-color:var(--mud-palette-primary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-secondary-m3{border-color:var(--mud-palette-secondary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-tertiary-m3{border-color:var(--mud-palette-tertiary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-info-m3{border-color:var(--mud-palette-info)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-success-m3{border-color:var(--mud-palette-success)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-warning-m3{border-color:var(--mud-palette-warning)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-error-m3{border-color:var(--mud-palette-error)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-dark-m3{border-color:var(--mud-palette-dark)}.mud-switch-base-m3{padding-top:4px;padding-bottom:4px;padding-inline-start:8px;top:0;left:0;bottom:0;color:#fafafa;z-index:1;position:absolute;transition:left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-switch-base-m3.mud-switch-base-dense-m3{padding-inline-start:4px}.mud-switch-base-m3.mud-checked{transform:translateX(20px);padding:4px}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3{opacity:1}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-default{background-color:var(--mud-palette-text-primary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-primary{background-color:var(--mud-palette-primary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-secondary{background-color:var(--mud-palette-secondary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-tertiary{background-color:var(--mud-palette-tertiary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-info{background-color:var(--mud-palette-info)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-success{background-color:var(--mud-palette-success)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-warning{background-color:var(--mud-palette-warning)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-error{background-color:var(--mud-palette-error)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-dark{background-color:var(--mud-palette-dark)}.mud-switch-base-m3.mud-checked .mud-switch-thumb-m3{width:24px;height:24px;box-shadow:0px 2px 1px -1px rgba(0,0,0,.2),0px 1px 1px 0px rgba(0,0,0,.14),0px 1px 3px 0px rgba(0,0,0,.12);border-radius:50%;background-color:var(--mud-palette-background)}.mud-switch-base-m3:hover{background-color:var(--mud-palette-action-default-hover)}.mud-switch-base-m3.mud-switch-disabled{color:var(--mud-palette-gray-default) !important}.mud-switch-base-m3.mud-switch-disabled+.mud-switch-track-m3{opacity:.12 !important}.mud-switch-base-m3.mud-switch-disabled:hover,.mud-switch-base-m3.mud-switch-disabled:focus-visible{cursor:default;background-color:rgba(0,0,0,0) !important}.mud-switch-button-m3{display:flex;align-items:inherit;justify-content:inherit}.mud-switch-button-m3 .mud-switch-input-m3{top:0;left:0;width:100%;cursor:inherit;height:100%;margin:0;opacity:0;padding:0;z-index:1;position:absolute}.mud-switch-button-m3 .mud-switch-thumb-m3{width:16px;height:16px;box-shadow:0px 2px 1px -1px rgba(0,0,0,.2),0px 1px 1px 0px rgba(0,0,0,.14),0px 1px 3px 0px rgba(0,0,0,.12);border-radius:50%}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-default-m3{background-color:var(--mud-palette-text-primary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-primary-m3{background-color:var(--mud-palette-primary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-secondary-m3{background-color:var(--mud-palette-secondary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-tertiary-m3{background-color:var(--mud-palette-tertiary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-info-m3{background-color:var(--mud-palette-info)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-success-m3{background-color:var(--mud-palette-success)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-warning-m3{background-color:var(--mud-palette-warning)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-error-m3{background-color:var(--mud-palette-error)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-dark-m3{background-color:var(--mud-palette-dark)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-off-icon-m3{width:24px;height:24px}.mud-wheel{overflow:hidden;min-width:0;flex-grow:1;user-select:none;-webkit-user-select:none}.mud-wheel-item{width:100%;display:flex;align-content:center;justify-content:center;color:var(--mud-palette-text-secondary);border-radius:var(--mud-default-borderradius)}.mud-wheel-item.mud-wheel-item:hover:not(.mud-disabled){background-color:var(--mud-palette-action-default-hover)}.mud-wheel-item.wheel-item-closest{color:var(--mud-palette-text)}.mud-wheel-item.wheel-item-empty{min-height:32px !important}.mud-wheel-item.wheel-item-empty.wheel-item-empty-dense{min-height:24px !important}.mud-wheel-item.wheel-item-empty.wheel-item-empty:hover{background-color:unset}.mud-wheel-item.mud-disabled{color:var(--mud-palette-text-disabled)}.middle-item{transform:scale(1.2)}.middle-item.mud-disabled{color:var(--mud-palette-text-disabled)}.mud-wheel-border{min-height:2px !important}.mud-wheel-border.mud-wheel-border-default{background-color:var(--mud-palette-text-primary)}.mud-wheel-border.mud-wheel-border-primary{background-color:var(--mud-palette-primary)}.mud-wheel-border.wheel-border-gradient-primary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-primary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-secondary{background-color:var(--mud-palette-secondary)}.mud-wheel-border.wheel-border-gradient-secondary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-secondary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-tertiary{background-color:var(--mud-palette-tertiary)}.mud-wheel-border.wheel-border-gradient-tertiary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-tertiary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-info{background-color:var(--mud-palette-info)}.mud-wheel-border.wheel-border-gradient-info{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-info), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-success{background-color:var(--mud-palette-success)}.mud-wheel-border.wheel-border-gradient-success{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-success), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-warning{background-color:var(--mud-palette-warning)}.mud-wheel-border.wheel-border-gradient-warning{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-warning), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-error{background-color:var(--mud-palette-error)}.mud-wheel-border.wheel-border-gradient-error{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-error), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-dark{background-color:var(--mud-palette-dark)}.mud-wheel-border.wheel-border-gradient-dark{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-dark), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.wheel-border-gradient-default{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-text-primary), rgba(255, 0, 0, 0));background-color:unset}.mud-typographym3-display-large{font-family:var(--mud-typographym3-display-large-font);line-height:var(--mud-typographym3-display-large-line-height);font-size:var(--mud-typographym3-display-large-size);letter-spacing:var(--mud-typographym3-display-large-tracking);font-weight:var(--mud-typographym3-display-large-weight)}.mud-typographym3-display-medium{font-family:var(--mud-typographym3-display-medium-font);line-height:var(--mud-typographym3-display-medium-line-height);font-size:var(--mud-typographym3-display-medium-size);letter-spacing:var(--mud-typographym3-display-medium-tracking);font-weight:var(--mud-typographym3-display-medium-weight)}.mud-typographym3-display-small{font-family:var(--mud-typographym3-display-small-font);line-height:var(--mud-typographym3-display-small-line-height);font-size:var(--mud-typographym3-display-small-size);letter-spacing:var(--mud-typographym3-display-small-tracking);font-weight:var(--mud-typographym3-display-small-weight)}.mud-typographym3-headline-large{font-family:var(--mud-typographym3-headline-large-font);line-height:var(--mud-typographym3-headline-large-line-height);font-size:var(--mud-typographym3-headline-large-size);letter-spacing:var(--mud-typographym3-headline-large-tracking);font-weight:var(--mud-typographym3-headline-large-weight)}.mud-typographym3-headline-medium{font-family:var(--mud-typographym3-headline-medium-font);line-height:var(--mud-typographym3-headline-medium-line-height);font-size:var(--mud-typographym3-headline-medium-size);letter-spacing:var(--mud-typographym3-headline-medium-tracking);font-weight:var(--mud-typographym3-headline-medium-weight)}.mud-typographym3-headline-small{font-family:var(--mud-typographym3-headline-small-font);line-height:var(--mud-typographym3-headline-small-line-height);font-size:var(--mud-typographym3-headline-small-size);letter-spacing:var(--mud-typographym3-headline-small-tracking);font-weight:var(--mud-typographym3-headline-small-weight)}.mud-typographym3-title-large{font-family:var(--mud-typographym3-title-large-font);line-height:var(--mud-typographym3-title-large-line-height);font-size:var(--mud-typographym3-title-large-size);letter-spacing:var(--mud-typographym3-title-large-tracking);font-weight:var(--mud-typographym3-title-large-weight)}.mud-typographym3-title-medium{font-family:var(--mud-typographym3-title-medium-font);line-height:var(--mud-typographym3-title-medium-line-height);font-size:var(--mud-typographym3-title-medium-size);letter-spacing:var(--mud-typographym3-title-medium-tracking);font-weight:var(--mud-typographym3-title-medium-weight)}.mud-typographym3-title-small{font-family:var(--mud-typographym3-title-small-font);line-height:var(--mud-typographym3-title-small-line-height);font-size:var(--mud-typographym3-title-small-size);letter-spacing:var(--mud-typographym3-title-small-tracking);font-weight:var(--mud-typographym3-title-small-weight)}.mud-typographym3-body-large{font-family:var(--mud-typographym3-body-large-font);line-height:var(--mud-typographym3-body-large-line-height);font-size:var(--mud-typographym3-body-large-size);letter-spacing:var(--mud-typographym3-body-large-tracking);font-weight:var(--mud-typographym3-body-large-weight)}.mud-typographym3-body-medium{font-family:var(--mud-typographym3-body-medium-font);line-height:var(--mud-typographym3-body-medium-line-height);font-size:var(--mud-typographym3-body-medium-size);letter-spacing:var(--mud-typographym3-body-medium-tracking);font-weight:var(--mud-typographym3-body-medium-weight)}.mud-typographym3-body-small{font-family:var(--mud-typographym3-body-small-font);line-height:var(--mud-typographym3-body-small-line-height);font-size:var(--mud-typographym3-body-small-size);letter-spacing:var(--mud-typographym3-body-small-tracking);font-weight:var(--mud-typographym3-body-small-weight)}.mud-typographym3-label-large{font-family:var(--mud-typographym3-label-large-font);line-height:var(--mud-typographym3-label-large-line-height);font-size:var(--mud-typographym3-label-large-size);letter-spacing:var(--mud-typographym3-label-large-tracking);font-weight:var(--mud-typographym3-label-large-weight)}.mud-typographym3-label-medium{font-family:var(--mud-typographym3-label-medium-font);line-height:var(--mud-typographym3-label-medium-line-height);font-size:var(--mud-typographym3-label-medium-size);letter-spacing:var(--mud-typographym3-label-medium-tracking);font-weight:var(--mud-typographym3-label-medium-weight)}.mud-typographym3-label-small{font-family:var(--mud-typographym3-label-small-font);line-height:var(--mud-typographym3-label-small-line-height);font-size:var(--mud-typographym3-label-small-size);letter-spacing:var(--mud-typographym3-label-small-tracking);font-weight:var(--mud-typographym3-label-small-weight)}.mud-typography-display-inline{display:inline}.mud-transfer-list-common{height:fill-available;height:-webkit-fill-available}.mud-transfer-list-container{display:flex;flex-direction:column;flex:1 1 0%}.justify-text-center input{text-align:center}.mud-code input{padding:12px 0px !important}.mud-list-extended{margin:0;padding:0;position:relative;list-style:none}.mud-list-extended.mud-list-padding-extended{padding-top:8px;padding-bottom:8px}.mud-list-item-extended{width:100%;display:flex;position:relative;box-sizing:border-box;text-align:start;align-items:center;padding-top:8px;padding-bottom:8px;justify-content:flex-start;text-decoration:none;outline:none}.mud-list-item-extended.mud-list-item-dense-extended{padding-top:4px;padding-bottom:4px}.mud-list-item-extended.mud-list-item-disabled-extended{color:var(--mud-palette-action-disabled) !important;cursor:default !important;pointer-events:none !important}.mud-list-item-extended.mud-list-item-disabled-extended .mud-list-item-icon-extended{color:var(--mud-palette-action-disabled) !important}.mud-list-item-clickable-extended{color:inherit;border:0;cursor:pointer;margin:0;outline:0;user-select:none;border-radius:0;vertical-align:middle;background-color:rgba(0,0,0,0);-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);transition:background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-list-item-clickable-extended:hover:not(.mud-list-item-functional){background-color:var(--mud-palette-action-default-hover)}.mud-list-item-gutters-extended{padding-left:16px;padding-right:16px}.mud-list-item-text-extended{flex:1 1 auto;min-width:0;margin-top:4px;margin-bottom:4px;padding-inline-start:8px;padding-inline-end:8px}.mud-list-item-text-inset-extended{padding-left:56px;padding-inline-start:56px;padding-inline-end:unset}.mud-list-item-icon-extended{color:var(--mud-palette-action-default);display:inline-flex;flex-shrink:0;padding-inline-start:8px;padding-inline-end:8px;margin-inline-start:-4px;margin-inline-end:4px}.mud-list-item-multiselect-extended{max-height:32px}.mud-list-item-multiselect-extended.mud-list-item-multiselect-checkbox-extended{padding-inline-end:16px}.mud-list-subheader-extended{color:var(--mud-palette-action-default);background-color:var(--mud-palette-background);font-size:.875rem;box-sizing:border-box;list-style:none;font-weight:500;padding-top:16px;padding-bottom:16px}.mud-list-subheader-secondary-background-extended{background-color:var(--mud-palette-background-gray)}.mud-list-subheader-gutters-extended{padding-left:16px;padding-right:16px}.mud-list-subheader-inset-extended{padding-left:72px;padding-inline-start:72px;padding-inline-end:unset}.mud-list-subheader-sticky-extended{top:-8px;z-index:1;position:sticky}.mud-list-subheader-sticky-extended.mud-list-subheader-sticky-dense-extended{top:0}.mud-list-item-hilight-extended{background-color:var(--mud-palette-background-gray)}.mud-list-item-hilight-selected{background-color:var(--mud-palette-lines-default) !important}.mud-list-item-nested-background-extended{background-color:var(--mud-palette-background-gray)}.mud-list-item-avatar-extended{min-width:56px;flex-shrink:0}.mud-nested-list-extended>.mud-list-item-extended{padding-left:32px;padding-inline-start:32px;padding-inline-end:unset}.mud-select-extended{display:flex;flex-grow:1;flex-basis:0;min-width:0;position:relative;height:fit-content}.mud-select-extended.mud-autocomplete{display:block}.mud-select-extended.mud-autocomplete .mud-select-input-extended{cursor:text}.mud-select-extended.mud-autocomplete .mud-input-adornment-extended{cursor:pointer}.mud-select-extended.mud-autocomplete--with-progress .mud-select-input-extended input{padding-right:3.5rem !important}.mud-select-extended.mud-autocomplete--with-progress .mud-input-adorned-end input{padding-right:4.5rem !important}.mud-select-extended.mud-autocomplete--with-progress .mud-select-input-extended .mud-icon-button{display:none !important}.mud-select-extended.mud-autocomplete--with-progress .progress-indicator-circular{position:absolute;width:100%;top:0;bottom:0;display:flex;align-items:center;justify-content:flex-end;padding-top:.25rem;padding-bottom:.25rem;padding-right:1rem}.mud-select-extended.mud-autocomplete--with-progress .progress-indicator-circular--with-adornment{padding-right:3rem}.mud-select-extended.mud-autocomplete--with-progress .mud-progress-linear{position:absolute;bottom:-1px;height:2px}.mud-select-extended .mud-select-input-extended{cursor:pointer}.mud-select-extended .mud-select-input-extended .mud-select-extended-nowrap{white-space:nowrap}.mud-select-extended .mud-select-input-extended .mud-input-slot{overflow:hidden;text-overflow:ellipsis}.mud-select-extended .mud-select-input-extended .mud-input-adornment-end{margin-left:0}.mud-select-extended .mud-select-input-extended:disabled{cursor:default}.mud-select-extended .mud-disabled .mud-select-input{cursor:default}.mud-select-extended>.mud-form-helpertext{margin-top:-21px}.mud-select-all-extended{margin-top:10px;border-bottom:1px solid #d3d3d3;padding-bottom:18px}.mud-select-input-chip-extended{display:flex;flex-wrap:wrap;max-width:100%;row-gap:4px}.mud-select-input-chip-extended.mud-select-extended-nowrap{flex-wrap:nowrap;overflow-x:hidden}.mud-select-input-chip-extended .mud-chip{flex:0 0 auto;white-space:nowrap}.mud-placeholder-extended{line-height:unset}.mud-input-adornment-start-extended:not(.mud-input-text-extended){margin-inline-start:12px}.mud-input-adornment-start-extended.mud-input-filled-extended{margin-top:16px}.mud-input-adornment-end-extended:not(.mud-input-text-extended){margin-inline-end:12px}.mud-no-start-adornment .mud-input-adornment-start-extended{margin-inline-start:0 !important} +.mud-combobox{margin:0;padding:0;position:relative;list-style:none}.mud-combobox.mud-combobox-padding{padding-top:8px;padding-bottom:8px}.mud-combobox-item{width:100%;display:flex;position:relative;box-sizing:border-box;text-align:start;align-items:center;padding-top:8px;padding-bottom:8px;justify-content:flex-start;text-decoration:none;outline:none;min-height:48px}.mud-combobox-item.mud-combobox-item-comfort{padding-top:4px;padding-bottom:4px;min-height:40px}.mud-combobox-item.mud-combobox-item-slim{padding-top:2px;padding-bottom:2px;min-height:32px}.mud-combobox-item.mud-combobox-item-superslim{padding-top:0;padding-bottom:0;min-height:24px}.mud-combobox-item.mud-combobox-item-disabled{color:var(--mud-palette-action-disabled) !important;cursor:default !important;pointer-events:none !important}.mud-combobox-item.mud-combobox-item-disabled .mud-combobox-item-icon{color:var(--mud-palette-action-disabled) !important}.mud-combobox-item-clickable{color:inherit;border:0;cursor:pointer;margin:0;outline:0;user-select:none;border-radius:0;vertical-align:middle;background-color:rgba(0,0,0,0);-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);transition:background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-combobox-item-clickable:hover{background-color:var(--mud-palette-action-default-hover)}.mud-combobox-item-gutters{padding-left:16px;padding-right:16px}.mud-combobox-item-text{flex:1 1 auto;min-width:0;margin-top:4px;margin-bottom:4px;padding-inline-start:8px;padding-inline-end:8px}.mud-combobox-item-text-inset{padding-left:56px;padding-inline-start:56px;padding-inline-end:unset}.mud-combobox-item-icon{color:var(--mud-palette-action-default);display:inline-flex;flex-shrink:0;padding-inline-start:8px;padding-inline-end:8px;margin-inline-start:-4px;margin-inline-end:4px}.mud-combobox-item-multiselect{max-height:32px}.mud-combobox-item-multiselect.mud-combobox-item-multiselect-checkbox{padding-inline-end:16px}.mud-combobox-subheader{color:var(--mud-palette-action-default);background-color:var(--mud-palette-background);font-size:.875rem;box-sizing:border-box;list-style:none;font-weight:500;padding-top:16px;padding-bottom:16px}.mud-combobox-subheader-secondary-background{background-color:var(--mud-palette-background-gray)}.mud-combobox-subheader-gutters{padding-left:16px;padding-right:16px}.mud-combobox-subheader-inset{padding-left:72px;padding-inline-start:72px;padding-inline-end:unset}.mud-combobox-subheader-sticky{top:-8px;z-index:1;position:sticky}.mud-combobox-subheader-sticky.mud-combobox-subheader-sticky-dense{top:0}.mud-combobox-item-hilight{background-color:var(--mud-palette-background-gray)}.mud-combobox-item-hilight{background-color:var(--mud-palette-lines-default) !important}.mud-combobox-item-bordered{border-left:4px solid var(--mud-palette-lines-default);padding-inline-start:12px}.mud-combobox-item-bordered-primary{border-left:4px solid var(--mud-palette-primary);padding-inline-start:12px}.mud-combobox-item-bordered-secondary{border-left:4px solid var(--mud-palette-secondary);padding-inline-start:12px}.mud-combobox-item-bordered-tertiary{border-left:4px solid var(--mud-palette-tertiary);padding-inline-start:12px}.mud-combobox-item-bordered-info{border-left:4px solid var(--mud-palette-info);padding-inline-start:12px}.mud-combobox-item-bordered-success{border-left:4px solid var(--mud-palette-success);padding-inline-start:12px}.mud-combobox-item-bordered-warning{border-left:4px solid var(--mud-palette-warning);padding-inline-start:12px}.mud-combobox-item-bordered-error{border-left:4px solid var(--mud-palette-error);padding-inline-start:12px}.mud-combobox-item-bordered-dark{border-left:4px solid var(--mud-palette-dark);padding-inline-start:12px}.mud-combobox-item-nested-background{background-color:var(--mud-palette-background-gray)}.mud-combobox-item-avatar{min-width:56px;flex-shrink:0}.mud-combobox-highlighter{background-color:rgba(0,0,0,0);font-weight:bold;text-decoration:underline}.mud-gallery-selected-toolbox{left:0;right:0;height:56px;width:100%;background-color:rgba(0,0,0,.6)}.mud-gallery-selected-toolbox.gallery-toolbox-top{top:0}.mud-gallery-selected-toolbox.gallery-toolbox-bottom{bottom:0}.mud-page{display:grid;box-sizing:border-box;width:100%}.mud-page.mud-page-height-full{min-height:100vh}.mud-page.mud-page-height-full-without-appbar{min-height:calc(100vh - var(--mud-appbar-height))}.mud-page.mud-page-column-2{grid-template-columns:repeat(2, 50%)}.mud-page.mud-page-column-3{grid-template-columns:repeat(3, 33.333333%)}.mud-page.mud-page-column-4{grid-template-columns:repeat(4, 25%)}.mud-page.mud-page-column-5{grid-template-columns:repeat(5, 20%)}.mud-page.mud-page-column-6{grid-template-columns:repeat(6, 16.666666%)}.mud-page.mud-page-column-7{grid-template-columns:repeat(7, 14.285714%)}.mud-page.mud-page-column-8{grid-template-columns:repeat(8, 12.5%)}.mud-page.mud-page-column-9{grid-template-columns:repeat(9, 11.111111%)}.mud-page.mud-page-column-10{grid-template-columns:repeat(10, 10%)}.mud-page.mud-page-column-11{grid-template-columns:repeat(11, 9.090909%)}.mud-page.mud-page-column-12{grid-template-columns:repeat(12, 8.333333%)}.mud-page.mud-page-row-2{grid-template-rows:repeat(2, 50%)}.mud-page.mud-page-row-3{grid-template-rows:repeat(3, 33.333333%)}.mud-page.mud-page-row-4{grid-template-rows:repeat(4, 25%)}.mud-page.mud-page-row-5{grid-template-rows:repeat(5, 20%)}.mud-page.mud-page-row-6{grid-template-rows:repeat(6, 16.666666%)}.mud-page.mud-page-row-7{grid-template-rows:repeat(7, 14.285714%)}.mud-page.mud-page-row-8{grid-template-rows:repeat(8, 12.5%)}.mud-page.mud-page-row-9{grid-template-rows:repeat(9, 11.111111%)}.mud-page.mud-page-row-10{grid-template-rows:repeat(10, 10%)}.mud-page.mud-page-row-11{grid-template-rows:repeat(11, 9.090909%)}.mud-page.mud-page-row-12{grid-template-rows:repeat(12, 8.333333%)}.mud-section{display:inline-grid;overflow:auto}.mud-section.mud-section-col-start-1{grid-column-start:1}.mud-section.mud-section-col-start-2{grid-column-start:2}.mud-section.mud-section-col-start-3{grid-column-start:3}.mud-section.mud-section-col-start-4{grid-column-start:4}.mud-section.mud-section-col-start-5{grid-column-start:5}.mud-section.mud-section-col-start-6{grid-column-start:6}.mud-section.mud-section-col-start-7{grid-column-start:7}.mud-section.mud-section-col-start-8{grid-column-start:8}.mud-section.mud-section-col-start-9{grid-column-start:9}.mud-section.mud-section-col-start-10{grid-column-start:10}.mud-section.mud-section-col-start-11{grid-column-start:11}.mud-section.mud-section-col-start-12{grid-column-start:12}.mud-section.mud-section-col-end-1{grid-column-end:1}.mud-section.mud-section-col-end-2{grid-column-end:2}.mud-section.mud-section-col-end-3{grid-column-end:3}.mud-section.mud-section-col-end-4{grid-column-end:4}.mud-section.mud-section-col-end-5{grid-column-end:5}.mud-section.mud-section-col-end-6{grid-column-end:6}.mud-section.mud-section-col-end-7{grid-column-end:7}.mud-section.mud-section-col-end-8{grid-column-end:8}.mud-section.mud-section-col-end-9{grid-column-end:9}.mud-section.mud-section-col-end-10{grid-column-end:10}.mud-section.mud-section-col-end-11{grid-column-end:11}.mud-section.mud-section-col-end-12{grid-column-end:12}.mud-section.mud-section-col-end-13{grid-column-end:13}.mud-section.mud-section-row-start-1{grid-row-start:1}.mud-section.mud-section-row-start-2{grid-row-start:2}.mud-section.mud-section-row-start-3{grid-row-start:3}.mud-section.mud-section-row-start-4{grid-row-start:4}.mud-section.mud-section-row-start-5{grid-row-start:5}.mud-section.mud-section-row-start-6{grid-row-start:6}.mud-section.mud-section-row-start-7{grid-row-start:7}.mud-section.mud-section-row-start-8{grid-row-start:8}.mud-section.mud-section-row-start-9{grid-row-start:9}.mud-section.mud-section-row-start-10{grid-row-start:10}.mud-section.mud-section-row-start-11{grid-row-start:11}.mud-section.mud-section-row-start-12{grid-row-start:12}.mud-section.mud-section-row-end-1{grid-row-end:1}.mud-section.mud-section-row-end-2{grid-row-end:2}.mud-section.mud-section-row-end-3{grid-row-end:3}.mud-section.mud-section-row-end-4{grid-row-end:4}.mud-section.mud-section-row-end-5{grid-row-end:5}.mud-section.mud-section-row-end-6{grid-row-end:6}.mud-section.mud-section-row-end-7{grid-row-end:7}.mud-section.mud-section-row-end-8{grid-row-end:8}.mud-section.mud-section-row-end-9{grid-row-end:9}.mud-section.mud-section-row-end-10{grid-row-end:10}.mud-section.mud-section-row-end-11{grid-row-end:11}.mud-section.mud-section-row-end-12{grid-row-end:12}.mud-section.mud-section-row-end-13{grid-row-end:13}.mud-popup{z-index:2000;overflow:auto;background-color:var(--mud-palette-background);min-height:var(--mud-appbar-height)}.mud-popup.mud-popup-center{height:300px;left:50%;top:50%;transform:translate(-50%, -50%);width:320px;aspect-ratio:1/1}.mud-range-container{align-items:center;margin:20px 0}.mud-range-container input::-webkit-slider-thumb{pointer-events:all;position:relative;z-index:1}.mud-range-container input::-moz-range-thumb{pointer-events:all;position:relative;z-index:10}.mud-range-container input::-moz-range-track{position:relative;z-index:-1}.mud-range-container input:last-of-type::-moz-range-track{-moz-appearance:none}.mud-range-container .mud-slider-input:last-of-type{position:absolute;pointer-events:none;top:0}.mud-range-container input[type=range]::-webkit-slider-thumb{pointer-events:all}.mud-range-display{text-align:center}.mud-signature-pad-container{touch-action:none}.mud-splitter{display:grid;position:relative;width:100%}.mud-splitter-content{overflow:auto}.mud-splitter-thumb ::-webkit-slider-runnable-track{visibility:hidden !important;height:100% !important}.mud-splitter-thumb ::-moz-range-track{visibility:hidden !important;height:100% !important}.mud-splitter-track{position:absolute;top:50%;transform:translateY(-50%);height:100%}.mud-splitter-track.mud-slider{visibility:hidden !important}.mud-splitter-track.mud-slider .mud-slider-container{height:100% !important}.mud-splitter-track.mud-slider .mud-slider-input{top:50%}.mud-splitter-thumb ::-webkit-slider-thumb{visibility:visible !important;appearance:none !important;-webkit-appearance:none !important;top:50% !important;transform:translateY(-50%) !important;height:100% !important;width:8px !important;border:none !important;border-radius:0 !important;cursor:ew-resize !important}.mud-splitter-thumb-disabled ::-webkit-slider-thumb{cursor:default !important}.mud-splitter-thumb ::-moz-range-thumb{visibility:visible !important;appearance:none !important;-moz-appearance:none !important;top:50% !important;transform:translateY(-50%) !important;height:100% !important;width:8px !important;border:none !important;border-radius:0 !important;cursor:ew-resize !important}.mud-splitter-thumb-disabled ::-moz-range-thumb{cursor:default !important}.mud-stepper-header-extended{min-height:62px;border-radius:var(--mud-default-borderradius)}.mud-stepper-header-extended.mud-stepper-header-non-linear-extended:hover{background-color:var(--mud-palette-action-default-hover)}.mud-stepper-badge-extended{z-index:21}.mud-stepper-avatar-extended{z-index:20}.mud-stepper-avatar-bg-extended{background-color:var(--mud-palette-background)}.mud-stepper-sub-inner-header-extended{grid-column-start:1;grid-column-end:-1;flex-direction:row;grid-row:1;list-style:none;display:flex}.mud-stepper-sub-inner-header-vertical-extended{grid-row-start:1;grid-row-end:-1;flex-direction:column;grid-column:1;list-style:none;display:flex}.mud-stepper-text-mobile-extended{grid-row:1;margin-top:22px}.mud-stepper-text-mobile-vertical-extended{grid-column:1;margin-inline-start:22px}.mud-stepper-step-1.horizontal{width:calc(100%/1)}.mud-stepper-step-1.vertical{height:calc(100%/1)}.mud-stepper-step-2.horizontal{width:calc(100%/2)}.mud-stepper-step-2.vertical{height:calc(100%/2)}.mud-stepper-step-3.horizontal{width:calc(100%/3)}.mud-stepper-step-3.vertical{height:calc(100%/3)}.mud-stepper-step-4.horizontal{width:calc(100%/4)}.mud-stepper-step-4.vertical{height:calc(100%/4)}.mud-stepper-step-5.horizontal{width:calc(100%/5)}.mud-stepper-step-5.vertical{height:calc(100%/5)}.mud-stepper-step-6.horizontal{width:calc(100%/6)}.mud-stepper-step-6.vertical{height:calc(100%/6)}.mud-stepper-step-7.horizontal{width:calc(100%/7)}.mud-stepper-step-7.vertical{height:calc(100%/7)}.mud-stepper-step-8.horizontal{width:calc(100%/8)}.mud-stepper-step-8.vertical{height:calc(100%/8)}.mud-stepper-step-9.horizontal{width:calc(100%/9)}.mud-stepper-step-9.vertical{height:calc(100%/9)}.mud-stepper-step-10.horizontal{width:calc(100%/10)}.mud-stepper-step-10.vertical{height:calc(100%/10)}.mud-stepper-step-11.horizontal{width:calc(100%/11)}.mud-stepper-step-11.vertical{height:calc(100%/11)}.mud-stepper-step-12.horizontal{width:calc(100%/12)}.mud-stepper-step-12.vertical{height:calc(100%/12)}.mud-stepper-grid-1.horizontal{display:grid;grid-template-columns:repeat(2, 1fr)}.mud-stepper-grid-1.vertical{display:grid;grid-template-rows:repeat(2, 1fr)}.mud-stepper-grid-2.horizontal{display:grid;grid-template-columns:repeat(4, 1fr)}.mud-stepper-grid-2.vertical{display:grid;grid-template-rows:repeat(4, 1fr)}.mud-stepper-grid-3.horizontal{display:grid;grid-template-columns:repeat(6, 1fr)}.mud-stepper-grid-3.vertical{display:grid;grid-template-rows:repeat(6, 1fr)}.mud-stepper-grid-4.horizontal{display:grid;grid-template-columns:repeat(8, 1fr)}.mud-stepper-grid-4.vertical{display:grid;grid-template-rows:repeat(8, 1fr)}.mud-stepper-grid-5.horizontal{display:grid;grid-template-columns:repeat(10, 1fr)}.mud-stepper-grid-5.vertical{display:grid;grid-template-rows:repeat(10, 1fr)}.mud-stepper-grid-6.horizontal{display:grid;grid-template-columns:repeat(12, 1fr)}.mud-stepper-grid-6.vertical{display:grid;grid-template-rows:repeat(12, 1fr)}.mud-stepper-grid-7.horizontal{display:grid;grid-template-columns:repeat(14, 1fr)}.mud-stepper-grid-7.vertical{display:grid;grid-template-rows:repeat(14, 1fr)}.mud-stepper-grid-8.horizontal{display:grid;grid-template-columns:repeat(16, 1fr)}.mud-stepper-grid-8.vertical{display:grid;grid-template-rows:repeat(16, 1fr)}.mud-stepper-grid-9.horizontal{display:grid;grid-template-columns:repeat(18, 1fr)}.mud-stepper-grid-9.vertical{display:grid;grid-template-rows:repeat(18, 1fr)}.mud-stepper-grid-10.horizontal{display:grid;grid-template-columns:repeat(20, 1fr)}.mud-stepper-grid-10.vertical{display:grid;grid-template-rows:repeat(20, 1fr)}.mud-stepper-grid-11.horizontal{display:grid;grid-template-columns:repeat(22, 1fr)}.mud-stepper-grid-11.vertical{display:grid;grid-template-rows:repeat(22, 1fr)}.mud-stepper-grid-12.horizontal{display:grid;grid-template-columns:repeat(24, 1fr)}.mud-stepper-grid-12.vertical{display:grid;grid-template-rows:repeat(24, 1fr)}.mud-stepper-grid-13.horizontal{display:grid;grid-template-columns:repeat(26, 1fr)}.mud-stepper-grid-13.vertical{display:grid;grid-template-rows:repeat(26, 1fr)}.mud-stepper-grid-14.horizontal{display:grid;grid-template-columns:repeat(28, 1fr)}.mud-stepper-grid-14.vertical{display:grid;grid-template-rows:repeat(28, 1fr)}.mud-stepper-grid-15.horizontal{display:grid;grid-template-columns:repeat(30, 1fr)}.mud-stepper-grid-15.vertical{display:grid;grid-template-rows:repeat(30, 1fr)}.mud-stepper-grid-16.horizontal{display:grid;grid-template-columns:repeat(32, 1fr)}.mud-stepper-grid-16.vertical{display:grid;grid-template-rows:repeat(32, 1fr)}.mud-stepper-grid-17.horizontal{display:grid;grid-template-columns:repeat(34, 1fr)}.mud-stepper-grid-17.vertical{display:grid;grid-template-rows:repeat(34, 1fr)}.mud-stepper-grid-18.horizontal{display:grid;grid-template-columns:repeat(36, 1fr)}.mud-stepper-grid-18.vertical{display:grid;grid-template-rows:repeat(36, 1fr)}.mud-stepper-grid-19.horizontal{display:grid;grid-template-columns:repeat(38, 1fr)}.mud-stepper-grid-19.vertical{display:grid;grid-template-rows:repeat(38, 1fr)}.mud-stepper-grid-20.horizontal{display:grid;grid-template-columns:repeat(40, 1fr)}.mud-stepper-grid-20.vertical{display:grid;grid-template-rows:repeat(40, 1fr)}.mud-stepper-grid-21.horizontal{display:grid;grid-template-columns:repeat(42, 1fr)}.mud-stepper-grid-21.vertical{display:grid;grid-template-rows:repeat(42, 1fr)}.mud-stepper-grid-22.horizontal{display:grid;grid-template-columns:repeat(44, 1fr)}.mud-stepper-grid-22.vertical{display:grid;grid-template-rows:repeat(44, 1fr)}.mud-stepper-grid-23.horizontal{display:grid;grid-template-columns:repeat(46, 1fr)}.mud-stepper-grid-23.vertical{display:grid;grid-template-rows:repeat(46, 1fr)}.mud-stepper-grid-24.horizontal{display:grid;grid-template-columns:repeat(48, 1fr)}.mud-stepper-grid-24.vertical{display:grid;grid-template-rows:repeat(48, 1fr)}.steps-1.horizontal{grid-column-start:2;grid-column-end:2;grid-row:1/-1}.steps-1.vertical{grid-row-start:2;grid-row-end:2;grid-column:1/-1}.steps-2.horizontal{grid-column-start:2;grid-column-end:4;grid-row:1/-1}.steps-2.vertical{grid-row-start:2;grid-row-end:4;grid-column:1/-1}.steps-3.horizontal{grid-column-start:2;grid-column-end:6;grid-row:1/-1}.steps-3.vertical{grid-row-start:2;grid-row-end:6;grid-column:1/-1}.steps-4.horizontal{grid-column-start:2;grid-column-end:8;grid-row:1/-1}.steps-4.vertical{grid-row-start:2;grid-row-end:8;grid-column:1/-1}.steps-5.horizontal{grid-column-start:2;grid-column-end:10;grid-row:1/-1}.steps-5.vertical{grid-row-start:2;grid-row-end:10;grid-column:1/-1}.steps-6.horizontal{grid-column-start:2;grid-column-end:12;grid-row:1/-1}.steps-6.vertical{grid-row-start:2;grid-row-end:12;grid-column:1/-1}.steps-7.horizontal{grid-column-start:2;grid-column-end:14;grid-row:1/-1}.steps-7.vertical{grid-row-start:2;grid-row-end:14;grid-column:1/-1}.steps-8.horizontal{grid-column-start:2;grid-column-end:16;grid-row:1/-1}.steps-8.vertical{grid-row-start:2;grid-row-end:16;grid-column:1/-1}.steps-9.horizontal{grid-column-start:2;grid-column-end:18;grid-row:1/-1}.steps-9.vertical{grid-row-start:2;grid-row-end:18;grid-column:1/-1}.steps-10.horizontal{grid-column-start:2;grid-column-end:20;grid-row:1/-1}.steps-10.vertical{grid-row-start:2;grid-row-end:20;grid-column:1/-1}.steps-11.horizontal{grid-column-start:2;grid-column-end:22;grid-row:1/-1}.steps-11.vertical{grid-row-start:2;grid-row-end:22;grid-column:1/-1}.steps-12.horizontal{grid-column-start:2;grid-column-end:24;grid-row:1/-1}.steps-12.vertical{grid-row-start:2;grid-row-end:24;grid-column:1/-1}.mud-stepper-progress-extended.horizontal.header-size-small{top:22px;height:2px}.mud-stepper-progress-extended.horizontal.header-size-medium{top:30px;height:3px}.mud-stepper-progress-extended.horizontal.header-size-large{top:38px;height:4px}.mud-stepper-progress-extended.vertical.header-size-small{left:22px}.mud-stepper-progress-extended.vertical.header-size-medium{left:30px}.mud-stepper-progress-extended.vertical.header-size-large{left:38px}.mobile.horizontal .mud-stepper-progress-extended{margin-inline-start:40px}.mud-stepper-progress-extended{display:inline-grid;z-index:10}.mud-stepper-progress-extended.vertical{transform:rotateX(180deg)}.mud-switch-m3{cursor:pointer;display:inline-flex;align-items:center;vertical-align:middle;margin-top:4px;margin-bottom:4px;-webkit-tap-highlight-color:rgba(0,0,0,0)}.mud-switch-m3.mud-disabled{color:var(--mud-palette-text-disabled) !important;cursor:default}.mud-switch-m3.mud-readonly,.mud-switch-m3 .mud-readonly:hover{cursor:default;background-color:rgba(0,0,0,0) !important}.mud-switch-span-m3{width:52px;height:32px;display:inline-flex;z-index:0;position:relative;box-sizing:border-box;flex-shrink:0;vertical-align:middle}.mud-switch-span-m3.mud-switch-child-content-m3{margin-inline-end:12px}.mud-switch-span-m3 .mud-switch-track-m3{width:52px;height:32px;z-index:-1;transition:opacity 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;border-radius:30px;background-color:var(--mud-palette-background);border:2px solid}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-default-m3{border-color:var(--mud-palette-text-primary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-primary-m3{border-color:var(--mud-palette-primary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-secondary-m3{border-color:var(--mud-palette-secondary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-tertiary-m3{border-color:var(--mud-palette-tertiary)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-info-m3{border-color:var(--mud-palette-info)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-success-m3{border-color:var(--mud-palette-success)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-warning-m3{border-color:var(--mud-palette-warning)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-error-m3{border-color:var(--mud-palette-error)}.mud-switch-span-m3 .mud-switch-track-m3.mud-switch-track-dark-m3{border-color:var(--mud-palette-dark)}.mud-switch-base-m3{padding-top:4px;padding-bottom:4px;padding-inline-start:8px;top:0;left:0;bottom:0;color:#fafafa;z-index:1;position:absolute;transition:left 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-switch-base-m3.mud-switch-base-dense-m3{padding-inline-start:4px}.mud-switch-base-m3.mud-checked{transform:translateX(20px);padding:4px}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3{opacity:1}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-default{background-color:var(--mud-palette-text-primary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-primary{background-color:var(--mud-palette-primary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-secondary{background-color:var(--mud-palette-secondary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-tertiary{background-color:var(--mud-palette-tertiary)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-info{background-color:var(--mud-palette-info)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-success{background-color:var(--mud-palette-success)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-warning{background-color:var(--mud-palette-warning)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-error{background-color:var(--mud-palette-error)}.mud-switch-base-m3.mud-checked+.mud-switch-track-m3.mud-dark{background-color:var(--mud-palette-dark)}.mud-switch-base-m3.mud-checked .mud-switch-thumb-m3{width:24px;height:24px;box-shadow:0px 2px 1px -1px rgba(0,0,0,.2),0px 1px 1px 0px rgba(0,0,0,.14),0px 1px 3px 0px rgba(0,0,0,.12);border-radius:50%;background-color:var(--mud-palette-background)}.mud-switch-base-m3:hover{background-color:var(--mud-palette-action-default-hover)}.mud-switch-base-m3.mud-switch-disabled{color:var(--mud-palette-gray-default) !important}.mud-switch-base-m3.mud-switch-disabled+.mud-switch-track-m3{opacity:.12 !important}.mud-switch-base-m3.mud-switch-disabled:hover,.mud-switch-base-m3.mud-switch-disabled:focus-visible{cursor:default;background-color:rgba(0,0,0,0) !important}.mud-switch-button-m3{display:flex;align-items:inherit;justify-content:inherit}.mud-switch-button-m3 .mud-switch-input-m3{top:0;left:0;width:100%;cursor:inherit;height:100%;margin:0;opacity:0;padding:0;z-index:1;position:absolute}.mud-switch-button-m3 .mud-switch-thumb-m3{width:16px;height:16px;box-shadow:0px 2px 1px -1px rgba(0,0,0,.2),0px 1px 1px 0px rgba(0,0,0,.14),0px 1px 3px 0px rgba(0,0,0,.12);border-radius:50%}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-default-m3{background-color:var(--mud-palette-text-primary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-primary-m3{background-color:var(--mud-palette-primary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-secondary-m3{background-color:var(--mud-palette-secondary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-tertiary-m3{background-color:var(--mud-palette-tertiary)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-info-m3{background-color:var(--mud-palette-info)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-success-m3{background-color:var(--mud-palette-success)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-warning-m3{background-color:var(--mud-palette-warning)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-error-m3{background-color:var(--mud-palette-error)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-dark-m3{background-color:var(--mud-palette-dark)}.mud-switch-button-m3 .mud-switch-thumb-m3.mud-switch-thumb-off-icon-m3{width:24px;height:24px}.mud-wheel{overflow:hidden;min-width:0;flex-grow:1;user-select:none;-webkit-user-select:none}.mud-wheel-item{width:100%;display:flex;align-content:center;justify-content:center;color:var(--mud-palette-text-secondary);border-radius:var(--mud-default-borderradius)}.mud-wheel-item.mud-wheel-item:hover:not(.mud-disabled){background-color:var(--mud-palette-action-default-hover)}.mud-wheel-item.wheel-item-closest{color:var(--mud-palette-text)}.mud-wheel-item.wheel-item-empty{min-height:32px !important}.mud-wheel-item.wheel-item-empty.wheel-item-empty-dense{min-height:24px !important}.mud-wheel-item.wheel-item-empty.wheel-item-empty:hover{background-color:unset}.mud-wheel-item.mud-disabled{color:var(--mud-palette-text-disabled)}.middle-item{transform:scale(1.2)}.middle-item.mud-disabled{color:var(--mud-palette-text-disabled)}.mud-wheel-border{min-height:2px !important}.mud-wheel-border.mud-wheel-border-default{background-color:var(--mud-palette-text-primary)}.mud-wheel-border.mud-wheel-border-primary{background-color:var(--mud-palette-primary)}.mud-wheel-border.wheel-border-gradient-primary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-primary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-secondary{background-color:var(--mud-palette-secondary)}.mud-wheel-border.wheel-border-gradient-secondary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-secondary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-tertiary{background-color:var(--mud-palette-tertiary)}.mud-wheel-border.wheel-border-gradient-tertiary{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-tertiary), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-info{background-color:var(--mud-palette-info)}.mud-wheel-border.wheel-border-gradient-info{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-info), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-success{background-color:var(--mud-palette-success)}.mud-wheel-border.wheel-border-gradient-success{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-success), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-warning{background-color:var(--mud-palette-warning)}.mud-wheel-border.wheel-border-gradient-warning{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-warning), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-error{background-color:var(--mud-palette-error)}.mud-wheel-border.wheel-border-gradient-error{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-error), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.mud-wheel-border-dark{background-color:var(--mud-palette-dark)}.mud-wheel-border.wheel-border-gradient-dark{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-dark), rgba(255, 0, 0, 0));background-color:unset}.mud-wheel-border.wheel-border-gradient-default{background-image:linear-gradient(to right, rgba(255, 0, 0, 0), var(--mud-palette-text-primary), rgba(255, 0, 0, 0));background-color:unset}.mud-typographym3-display-large{font-family:var(--mud-typographym3-display-large-font);line-height:var(--mud-typographym3-display-large-line-height);font-size:var(--mud-typographym3-display-large-size);letter-spacing:var(--mud-typographym3-display-large-tracking);font-weight:var(--mud-typographym3-display-large-weight)}.mud-typographym3-display-medium{font-family:var(--mud-typographym3-display-medium-font);line-height:var(--mud-typographym3-display-medium-line-height);font-size:var(--mud-typographym3-display-medium-size);letter-spacing:var(--mud-typographym3-display-medium-tracking);font-weight:var(--mud-typographym3-display-medium-weight)}.mud-typographym3-display-small{font-family:var(--mud-typographym3-display-small-font);line-height:var(--mud-typographym3-display-small-line-height);font-size:var(--mud-typographym3-display-small-size);letter-spacing:var(--mud-typographym3-display-small-tracking);font-weight:var(--mud-typographym3-display-small-weight)}.mud-typographym3-headline-large{font-family:var(--mud-typographym3-headline-large-font);line-height:var(--mud-typographym3-headline-large-line-height);font-size:var(--mud-typographym3-headline-large-size);letter-spacing:var(--mud-typographym3-headline-large-tracking);font-weight:var(--mud-typographym3-headline-large-weight)}.mud-typographym3-headline-medium{font-family:var(--mud-typographym3-headline-medium-font);line-height:var(--mud-typographym3-headline-medium-line-height);font-size:var(--mud-typographym3-headline-medium-size);letter-spacing:var(--mud-typographym3-headline-medium-tracking);font-weight:var(--mud-typographym3-headline-medium-weight)}.mud-typographym3-headline-small{font-family:var(--mud-typographym3-headline-small-font);line-height:var(--mud-typographym3-headline-small-line-height);font-size:var(--mud-typographym3-headline-small-size);letter-spacing:var(--mud-typographym3-headline-small-tracking);font-weight:var(--mud-typographym3-headline-small-weight)}.mud-typographym3-title-large{font-family:var(--mud-typographym3-title-large-font);line-height:var(--mud-typographym3-title-large-line-height);font-size:var(--mud-typographym3-title-large-size);letter-spacing:var(--mud-typographym3-title-large-tracking);font-weight:var(--mud-typographym3-title-large-weight)}.mud-typographym3-title-medium{font-family:var(--mud-typographym3-title-medium-font);line-height:var(--mud-typographym3-title-medium-line-height);font-size:var(--mud-typographym3-title-medium-size);letter-spacing:var(--mud-typographym3-title-medium-tracking);font-weight:var(--mud-typographym3-title-medium-weight)}.mud-typographym3-title-small{font-family:var(--mud-typographym3-title-small-font);line-height:var(--mud-typographym3-title-small-line-height);font-size:var(--mud-typographym3-title-small-size);letter-spacing:var(--mud-typographym3-title-small-tracking);font-weight:var(--mud-typographym3-title-small-weight)}.mud-typographym3-body-large{font-family:var(--mud-typographym3-body-large-font);line-height:var(--mud-typographym3-body-large-line-height);font-size:var(--mud-typographym3-body-large-size);letter-spacing:var(--mud-typographym3-body-large-tracking);font-weight:var(--mud-typographym3-body-large-weight)}.mud-typographym3-body-medium{font-family:var(--mud-typographym3-body-medium-font);line-height:var(--mud-typographym3-body-medium-line-height);font-size:var(--mud-typographym3-body-medium-size);letter-spacing:var(--mud-typographym3-body-medium-tracking);font-weight:var(--mud-typographym3-body-medium-weight)}.mud-typographym3-body-small{font-family:var(--mud-typographym3-body-small-font);line-height:var(--mud-typographym3-body-small-line-height);font-size:var(--mud-typographym3-body-small-size);letter-spacing:var(--mud-typographym3-body-small-tracking);font-weight:var(--mud-typographym3-body-small-weight)}.mud-typographym3-label-large{font-family:var(--mud-typographym3-label-large-font);line-height:var(--mud-typographym3-label-large-line-height);font-size:var(--mud-typographym3-label-large-size);letter-spacing:var(--mud-typographym3-label-large-tracking);font-weight:var(--mud-typographym3-label-large-weight)}.mud-typographym3-label-medium{font-family:var(--mud-typographym3-label-medium-font);line-height:var(--mud-typographym3-label-medium-line-height);font-size:var(--mud-typographym3-label-medium-size);letter-spacing:var(--mud-typographym3-label-medium-tracking);font-weight:var(--mud-typographym3-label-medium-weight)}.mud-typographym3-label-small{font-family:var(--mud-typographym3-label-small-font);line-height:var(--mud-typographym3-label-small-line-height);font-size:var(--mud-typographym3-label-small-size);letter-spacing:var(--mud-typographym3-label-small-tracking);font-weight:var(--mud-typographym3-label-small-weight)}.mud-typography-display-inline{display:inline}.mud-transfer-list-common{height:fill-available;height:-webkit-fill-available}.mud-transfer-list-container{display:flex;flex-direction:column;flex:1 1 0%}.justify-text-center input{text-align:center}.mud-code input{padding:12px 0px !important}.mud-date-time-mode-selected{background:rgba(0,0,0,.25) !important;color:var(--mud-palette-primary-contrast-text)}.mud-date-time-toggle{color:inherit !important;border-color:inherit !important}.mud-date-time-toggle-item{color:inherit !important;border-color:inherit !important}.mud-list-extended{margin:0;padding:0;position:relative;list-style:none}.mud-list-extended.mud-list-padding-extended{padding-top:8px;padding-bottom:8px}.mud-list-item-extended{width:100%;display:flex;position:relative;box-sizing:border-box;text-align:start;align-items:center;padding-top:8px;padding-bottom:8px;justify-content:flex-start;text-decoration:none;outline:none}.mud-list-item-extended.mud-list-item-dense-extended{padding-top:4px;padding-bottom:4px}.mud-list-item-extended.mud-list-item-disabled-extended{color:var(--mud-palette-action-disabled) !important;cursor:default !important;pointer-events:none !important}.mud-list-item-extended.mud-list-item-disabled-extended .mud-list-item-icon-extended{color:var(--mud-palette-action-disabled) !important}.mud-list-item-clickable-extended{color:inherit;border:0;cursor:pointer;margin:0;outline:0;user-select:none;border-radius:0;vertical-align:middle;background-color:rgba(0,0,0,0);-webkit-appearance:none;-webkit-tap-highlight-color:rgba(0,0,0,0);transition:background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms}.mud-list-item-clickable-extended:hover:not(.mud-list-item-functional){background-color:var(--mud-palette-action-default-hover)}.mud-list-item-gutters-extended{padding-left:16px;padding-right:16px}.mud-list-item-text-extended{flex:1 1 auto;min-width:0;margin-top:4px;margin-bottom:4px;padding-inline-start:8px;padding-inline-end:8px}.mud-list-item-text-inset-extended{padding-left:56px;padding-inline-start:56px;padding-inline-end:unset}.mud-list-item-icon-extended{color:var(--mud-palette-action-default);display:inline-flex;flex-shrink:0;padding-inline-start:8px;padding-inline-end:8px;margin-inline-start:-4px;margin-inline-end:4px}.mud-list-item-multiselect-extended{max-height:32px}.mud-list-item-multiselect-extended.mud-list-item-multiselect-checkbox-extended{padding-inline-end:16px}.mud-list-subheader-extended{color:var(--mud-palette-action-default);background-color:var(--mud-palette-background);font-size:.875rem;box-sizing:border-box;list-style:none;font-weight:500;padding-top:16px;padding-bottom:16px}.mud-list-subheader-secondary-background-extended{background-color:var(--mud-palette-background-gray)}.mud-list-subheader-gutters-extended{padding-left:16px;padding-right:16px}.mud-list-subheader-inset-extended{padding-left:72px;padding-inline-start:72px;padding-inline-end:unset}.mud-list-subheader-sticky-extended{top:-8px;z-index:1;position:sticky}.mud-list-subheader-sticky-extended.mud-list-subheader-sticky-dense-extended{top:0}.mud-list-item-hilight-extended{background-color:var(--mud-palette-background-gray)}.mud-list-item-hilight-selected{background-color:var(--mud-palette-lines-default) !important}.mud-list-item-nested-background-extended{background-color:var(--mud-palette-background-gray)}.mud-list-item-avatar-extended{min-width:56px;flex-shrink:0}.mud-nested-list-extended>.mud-list-item-extended{padding-left:32px;padding-inline-start:32px;padding-inline-end:unset}.mud-select-extended{display:flex;flex-grow:1;flex-basis:0;min-width:0;position:relative;height:fit-content}.mud-select-extended.mud-autocomplete{display:block}.mud-select-extended.mud-autocomplete .mud-select-input-extended{cursor:text}.mud-select-extended.mud-autocomplete .mud-input-adornment-extended{cursor:pointer}.mud-select-extended.mud-autocomplete--with-progress .mud-select-input-extended input{padding-right:3.5rem !important}.mud-select-extended.mud-autocomplete--with-progress .mud-input-adorned-end input{padding-right:4.5rem !important}.mud-select-extended.mud-autocomplete--with-progress .mud-select-input-extended .mud-icon-button{display:none !important}.mud-select-extended.mud-autocomplete--with-progress .progress-indicator-circular{position:absolute;width:100%;top:0;bottom:0;display:flex;align-items:center;justify-content:flex-end;padding-top:.25rem;padding-bottom:.25rem;padding-right:1rem}.mud-select-extended.mud-autocomplete--with-progress .progress-indicator-circular--with-adornment{padding-right:3rem}.mud-select-extended.mud-autocomplete--with-progress .mud-progress-linear{position:absolute;bottom:-1px;height:2px}.mud-select-extended .mud-select-input-extended{cursor:pointer}.mud-select-extended .mud-select-input-extended .mud-select-extended-nowrap{white-space:nowrap}.mud-select-extended .mud-select-input-extended .mud-input-slot{overflow:hidden;text-overflow:ellipsis}.mud-select-extended .mud-select-input-extended .mud-input-adornment-end{margin-left:0}.mud-select-extended .mud-select-input-extended:disabled{cursor:default}.mud-select-extended .mud-disabled .mud-select-input{cursor:default}.mud-select-extended>.mud-form-helpertext{margin-top:-21px}.mud-select-all-extended{margin-top:10px;border-bottom:1px solid #d3d3d3;padding-bottom:18px}.mud-select-input-chip-extended{display:flex;flex-wrap:wrap;max-width:100%;row-gap:4px}.mud-select-input-chip-extended.mud-select-extended-nowrap{flex-wrap:nowrap;overflow-x:hidden}.mud-select-input-chip-extended .mud-chip{flex:0 0 auto;white-space:nowrap}.mud-placeholder-extended{line-height:unset}.mud-input-adornment-start-extended:not(.mud-input-text-extended){margin-inline-start:12px}.mud-input-adornment-start-extended.mud-input-filled-extended{margin-top:16px}.mud-input-adornment-end-extended:not(.mud-input-text-extended){margin-inline-end:12px}.mud-no-start-adornment .mud-input-adornment-start-extended{margin-inline-start:0 !important} diff --git a/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/DateTimePickerTests.cs b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/DateTimePickerTests.cs new file mode 100644 index 00000000..0fec0d9d --- /dev/null +++ b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/DateTimePickerTests.cs @@ -0,0 +1,101 @@ +using AwesomeAssertions; +using Bunit; +using Microsoft.AspNetCore.Components; + +namespace MudExtensions.UnitTests.Components; + +[TestFixture] +public class DateTimePickerTests : BunitTest +{ + private IRenderedComponent> RenderPicker( + T? value = default, + EventCallback? valueChanged = null, + string? format = null) + { + return Context.Render>(parameters => + { + parameters.Add(p => p.Value, value); + + if (valueChanged.HasValue) + parameters.Add(p => p.ValueChanged, valueChanged.Value); + + if (format is not null) + parameters.Add(p => p.DateFormat, format); + }); + } + + [Test] + public void DateTimePicker_DefaultRender_Should_Render_Input() + { + var comp = Context.Render>(); + comp.Find("input").Should().NotBeNull(); + } + + [Test] + public void DateTimePicker_Should_Format_Correctly() + { + var comp = RenderPicker( + value: new DateTime(2026, 5, 3, 14, 30, 0), + format: "dd.MM.yyyy HH:mm"); + + var text = comp.Instance.ConvertSetInternal(comp.Instance.Value); + + text.Should().Be("03.05.2026 14:30"); + } + + [Test] + public void DateTimePicker_DateOnly_Should_Not_Render_Time() + { + var comp = Context.Render>(p => p + .Add(x => x.Value, new DateOnly(2026, 5, 3)) + ); + + comp.Markup.Should().NotContain("mud-picker-time"); + } + + [Test] + public void DateTimePicker_DateTimeOffset_Should_Convert_Correctly() + { + DateTimeOffset? value = new DateTimeOffset(2026, 5, 3, 10, 0, 0, TimeSpan.Zero); + + var comp = Context.Render>(p => p + .Add(x => x.Value, value) + .Add(x => x.TimeZone, TimeZoneInfo.Utc) + ); + + var dt = comp.Instance.ToDateTime(value); + + dt.Should().Be(new DateTime(2026, 5, 3, 10, 0, 0)); + } + + [Test] + public async Task DateTimePicker_Input_Change_Should_Update_Value() + { + DateTime? value = null; + + var callback = EventCallback.Factory.Create( + this, + v => value = v); + + var comp = RenderPicker( + value: default, + valueChanged: callback, + format: "dd.MM.yyyy HH:mm"); + + var input = comp.Find("input"); + + await input.ChangeAsync(new ChangeEventArgs + { + Value = "03.05.2026 14:30" + }); + + value.Should().Be(new DateTime(2026, 5, 3, 14, 30, 0)); + } + + [Test] + public void DateTimePicker_Null_Value_Should_Render_Empty() + { + var comp = RenderPicker(); + comp.Find("input").GetAttribute("value").Should().BeNullOrEmpty(); + } +}