# CalendarRange A date-range picker. The user clicks once to set a start date and clicks again to set an end date. While hovering, the range between start and the cursor is shaded as a preview. Great for booking forms, report filters, or anything that needs a "from / to" date pair. --- ## Quick example ```csharp new CalendarRange(id: "vacation", name: "vacation") ``` This renders an empty picker. The user clicks two dates to form a range. Both dates are submitted with the form as `vacation-start` and `vacation-end`. --- ## All the options ```csharp public CalendarRange( string id, string name = "date", DateOnly? selectedStart = null, DateOnly? selectedEnd = null) ``` | Parameter | What it does | |---|---| | `id` | A unique identifier. The root element gets `id="calr-{id}"`. | | `name` | Base form field name. The two hidden inputs become `{name}-start` and `{name}-end`. | | `selectedStart` | Pre-selected start date. | | `selectedEnd` | Pre-selected end date. | --- ## Real-world examples ### Vacation request form ```html
``` **Reading the submitted values on the server:** ```csharp public record Command( [property: FromForm] string VacationStart, // "yyyy-MM-dd" [property: FromForm] string VacationEnd // "yyyy-MM-dd" ); // Validate they are not empty before parsing if (string.IsNullOrEmpty(command.VacationStart) || string.IsNullOrEmpty(command.VacationEnd)) return; // user did not complete the selection var start = DateOnly.ParseExact(command.VacationStart, "yyyy-MM-dd"); var end = DateOnly.ParseExact(command.VacationEnd, "yyyy-MM-dd"); ``` ### Pre-selected range (e.g. editing an existing request) ```csharp new CalendarRange( id: "vacation", name: "vacation", selectedStart: existingRequest.StartDate, selectedEnd: existingRequest.EndDate) ``` ### Reacting to selection changes in JavaScript ```js document.getElementById('calr-vacation').addEventListener('rangeChange', e => { console.log(e.detail.start, e.detail.end); // Update a price estimate, nights count, etc. }); ``` The `.calr-label` element inside the calendar automatically updates to show `start → end` (or `start → pick end date` while mid-selection). You do not need custom JS for the label. --- ## How it works The click logic follows three states: 1. **Nothing selected** — first click sets the start date, clears the end 2. **Start selected, no end** — next click after start sets the end and fires `rangeChange`; clicking before start moves the start; clicking on start again clears both 3. **Both selected** — any click resets and starts again from step 1 The hover preview does not rebuild the grid. It only toggles CSS classes on the day buttons, so it is fast even for long ranges. All state is stored in `data-start` and `data-end` attributes on the root element, not in closures, so HTMX-swapped calendars re-initialise correctly. ## Complete page example **`Templates/ReportRangePage.htmx`** ```html