# TimePicker A styled time picker. The user selects hours, minutes, and optionally AM/PM. The component always writes the selected time as `HH:MM` (24-hour) to the hidden input, regardless of whether 12-hour display mode is used. Optionally renders a visible label and description. --- ## HTML structure ``` div.flex.flex-col.gap-1.5 label.text-sm.font-medium ← omitted when empty {label} div.flex.items-center.gap-1.rounded-md.border.border-input.bg-background.px-3.py-2 select.timepicker-h[name={name}-h] ← hour select (1–12 or 0–23) span.text-muted-foreground : select.timepicker-m[name={name}-m] ← minute select (00–59) select.timepicker-ampm[name={name}-ampm] ← AM/PM (12h mode only) input.sr-only[type=hidden, name={name}] ← hidden input holding HH:MM p.text-sm.text-muted-foreground ← omitted when empty {description} ``` --- ## CSS mechanics | Class | Effect | |---|---| | `rounded-md border border-input bg-background` | Consistent styling with other form fields | | `sr-only` on hidden input | Hidden visually but included in form submission | | `appearance-none` on `""".ToUtf8Bytes(); _calendar = new Components.Calendar(name: "date", selectedDate: selectedDate); _timePicker = new Components.TimePicker(name: "time", value: selectedTime, placeholder: "09:00"); _title = new Components.Input(id: "title", name: "title", label: "Meeting title", placeholder: "Sync call"); _submit = new Components.Button("Book meeting", type: "submit"); _success = booked ? new Components.Alert(title: "Meeting booked!", description: $"Scheduled for {selectedDate:d} at {selectedTime}.") : HtmxEmpty.Instance; } protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken); protected override void RenderDatePicker(HtmxRenderContext ctx) => _calendar.Render(ctx.Next()); protected override void RenderTimePicker(HtmxRenderContext ctx) => _timePicker.Render(ctx.Next()); protected override void RenderTitleInput(HtmxRenderContext ctx) => _title.Render(ctx.Next()); protected override void RenderSubmitBtn(HtmxRenderContext ctx) => _submit.Render(ctx.Next()); protected override void RenderSuccessAlert(HtmxRenderContext ctx) => _success.Render(ctx.Next()); } ``` **POST handler** ```csharp [Handler] [MapPost("/meetings/new")] public static partial class PostScheduleMeetingHandler { public record Command( [property: FromForm] DateOnly Date, [property: FromForm] string Time, [property: FromForm] string Title); private static Task HandleAsync( [AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct) { // Persist meeting… return ctx.WriteHtmxPage( new ScheduleMeetingPage(af, ctx, cmd.Date, cmd.Time, booked: true), title: "Schedule meeting"); } } ``` **`AppJsonSerializerContext.cs`** ```csharp [JsonSerializable(typeof(PostScheduleMeetingHandler.Command), TypeInfoPropertyName = "ScheduleMeetingCommand")] ```