Components
Time picker
A time picked from a list, for scheduling events or setting appointments. The type it binds says how many: a TimeOnly? for one or none, a collection of TimeOnly for several.
Drawn and behaving as Flux UI's time picker does, measured against its live docs.
Time picker
A button over a list of times: the button keeps the focus, the arrows move through the list and Enter picks.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerExample : Component
{
private TimeOnly? _time;
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.TimePicker.Value(_time).OnChange(time => { _time = time; }).Id("ui-time"),
Ui.Text.Data("testid", "ui-time-picker-state")[
_time is { } at ? "Chosen: " + at.ToString("HH:mm", CultureInfo.InvariantCulture) : "No time chosen."
]
];
}No time chosen.
Basic usage
Bound to a TimeOnly? that starts at 11:30, as Flux's value does.
using System.Globalization;
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerBoundExample : Component
{
private readonly Meeting _meeting = new();
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.TimePicker.Bind(() => _meeting.StartsAt).Live().Label("Starts at"),
Ui.Text[_meeting.StartsAt is { } at ? "Starts at " + at.ToString("HH:mm", CultureInfo.InvariantCulture) + "." : "No time yet."]
];
public sealed class Meeting
{
public TimeOnly? StartsAt { get; set; } = new TimeOnly(11, 30);
}
}Starts at 11:30.
Input trigger
Hour, minute and the half of the day typed as one field: 9 can only be nine o'clock, so it moves on, and p is the afternoon.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerInputExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Type(Ui.TimePickerType.Input).Id("ui-time-input")
];
}Without dropdown
The typed trigger with no list under it.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerNoDropdownExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Type(Ui.TimePickerType.Input).Dropdown(false).Id("ui-time-typed")
];
}Multiple times
Opened on a collection: the list stays open while times are picked.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerMultipleExample : Component
{
private List<TimeOnly> _times = [];
protected override Component? Render() =>
Div.Class("max-w-xs space-y-3")[
Ui.TimePicker.Value(_times).OnChange(times => { _times = [.. times]; }).Id("ui-time-multiple"),
Ui.Text.Data("testid", "ui-time-picker-slots")[$"{_times.Count} times"]
];
}0 times
Time format
The culture's own clock unless TwelveHour or TwentyFourHour says otherwise.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerFormatExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs space-y-4")[
Ui.TimePicker.Of<TimeOnly?>().TwelveHour.Id("ui-time-twelve"),
Ui.TimePicker.Of<TimeOnly?>().TwentyFourHour.Id("ui-time-twenty-four")
];
}Interval
Minutes between the times on offer: 30 unless set.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerIntervalExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Interval(60).Id("ui-time-interval")
];
}Min/max times
Nine to five. Flux's "now" shorthand is the time it is: TimeOnly.FromDateTime(…).
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerMinMaxExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Min(new TimeOnly(9, 0)).Max(new TimeOnly(17, 0)).Id("ui-time-min-max")
];
}Unavailable times
Times, and stretches of time as a UiTimeRange, listed and disabled: booked slots.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerUnavailableExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Id("ui-time-unavailable").Unavailable([
new TimeOnly(3, 0), new TimeOnly(4, 0), new UiTimeRange(new TimeOnly(5, 30), new TimeOnly(7, 29)),
])
];
}Open to
Where the list opens while nothing is chosen. Otherwise it opens on the chosen time, or the time it is now.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerOpenToExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().OpenTo(new TimeOnly(10, 0)).Id("ui-time-open")
];
}Localization
The times are written in the app's culture unless Locale names another.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class TimePickerLocaleExample : Component
{
protected override Component? Render() =>
Div.Class("max-w-xs")[
Ui.TimePicker.Of<TimeOnly?>().Locale("ja-JP").Id("ui-time-locale")
];
}Behaviour
What it binds
TimeOnly? picks one time or none, TimeOnly always one, and a collection of TimeOnly several, Flux's multiple. A bound time is written with the reader's next action, at once with .Live(). Unavailable takes times and UiTimeRange stretches, listed but disabled; Clearable() puts a cross in the button while a time is chosen.
The keyboard
The button is the combobox and keeps the focus. The open list holds the page still (data-rask-lock), and scrolls itself to keep the active time in view while the page stays.
Key | What happens |
|---|---|
| Space, ArrowDown, ArrowUp | Open the list. Enter on the closed button does nothing, as on Flux. |
| ArrowDown, ArrowUp | Move the cursor through the list. |
| Enter | Picks the lit time and closes; with several it switches the time and the list stays open. |
| Escape | Closes. |
Typed times
The typed trigger is one field in parts, as the date picker's is: 9 is nine o'clock and moves on, a and p set the half of the day, and a whole time reaches the binding through one hidden field as HH:mm.
The same departure from Flux as the date picker's, by the same decision: an hour with no minute is no time, "Enter a valid time." shows under the field and the save is refused, and every part emptied is no time chosen.
Reference
Ui.TimePicker
A button over a list of times. A TimeOnly? picks one or none, a collection of TimeOnly several.
Step | Takes | What it does |
|---|---|---|
.Bind(…) | () => a T | The member it reads and writes: () => m.StartsAt. Or Value with OnChange. |
.Value(…) | T: a TimeOnly?, a TimeOnly, or times | What it holds, for a page that holds it itself. |
.OnChange(…) | a handler given the new value | Runs with the new value when a time is picked. |
.AfterBind(…) | a handler given the new value | Runs after Bind has written the model. |
.Validate(…) | a validator | A rule the value is checked by when it is written. |
.Live() | bool | Writes the model as soon as a time is picked, not with the next action. |
.Type(…) | Ui.TimePickerTypeButton Input Each is a step of its own too, unless an HTML tag has its name. | A button, or fields to type the time into. The button when unset. |
.Dropdown() | bool | False leaves the typed trigger without its list. |
.TimeFormat(…) | Ui.TimePickerTimeFormatAuto TwelveHour TwentyFourHour Each is a step of its own too, unless an HTML tag has its name. | 12-hour or 24-hour, each a step too (.TwelveHour). The culture's own when unset. |
.Interval(…) | int | Minutes between the times on offer. 30 when unset. |
.Min(…) | a TimeOnly | The earliest time on offer. |
.Max(…) | a TimeOnly | The latest time on offer. |
.Unavailable(…) | TimeOnly values and UiTimeRange stretches | Times, and stretches of time, listed and disabled. |
.OpenTo(…) | a TimeOnly | Where the list opens while nothing is chosen. |
.Label(…) | string | Draws a field around it, with this label and the error. |
.Description(…) | string | Help text between the label and the picker. |
.DescriptionTrailing(…) | string | Help text under the picker. |
.Badge(…) | string | A word beside the label: "Optional". |
.Placeholder(…) | string | What the button shows while no time is chosen. "Select a time" when unset. |
.Size(…) | Ui.TimePickerSizeBase Sm Xs Each is a step of its own too, unless an HTML tag has its name. | How tall it is. 40px when unset. |
.Clearable() | bool | A button that empties the picker, while a time is chosen. |
.Disabled() | bool | Takes no input and is skipped by the keyboard. |
.Invalid() | bool | Error styling the form did not ask for. |
.Locale(…) | string | The culture the times are written in: "ja-JP". The app's culture when unset. |
.ShowValidation() | bool | False leaves the member's message to a Ui.Error placed elsewhere. |