Production has no Vue3 preset picker yet — the legacy widget
(uam/htdocs/v3/js/date_picker_preset_widget.js) only offered
“+N days” buttons on server-rendered pages. This field shows the proposed
equivalent using the library's preset-dates prop: Today, Yesterday,
This/Last Week (Sunday-based, matching week-start="0"), This/Last Month,
This/Last Quarter, This/Last Year.
Production's TimeInput.vue uses the same clock icon
(ScheduleIcon) but structures the menu differently: a 15-minute preset
list sidebar plus an external AM/PM toggle button. This mockup uses the library's
time-picker columns instead — with the clearer stepper chevrons proposed below.
- Accepted date entry (forgiving parser — proposed)
-
Separators
/,-,.or spaces, with any surrounding whitespace:7/5/26,07-05-2026,7 . 5 . 2026,7 5 26. Separator-less digit runs parse as MMddyyyy / MMddyy:07052026,070526. Two-digit years:70–99 → 19xx,00–69 → 20xx. Production currently requires strictMM/dd/yyyy(mask input is an open TODO inDateInput.vue). - Accepted time entry
-
3pm,3 PM,3:30pm,3.30 p,15:30, bare9(→ 9:00). 12-hour requires 1–12 with meridiem; 24-hour accepted without one. In DateTimeInput a missing time commits midnight; the time part needs a meridiem or colon (7/5/26 3pm,07-05-2026 15:30). - Commit semantics
-
Typed text commits on Enter, Tab or blur; calendar clicks commit immediately
(
autoApply). Unparseable text leaves the model unchanged.changefires withnullon clear — consumers must not treat everychangeas a picked date. DateInput values are truncated to midnight; TimeInput values are pinned to the 1970-01-01 epoch day (TRUNCATED_DATE). - Range entry
-
Two dates around a spaced dash:
07/01/2026 - 09/30/2026. The input sanitizer auto-inserts spaces around a bare dash; because-is also the range separator, prefer/or.separators inside dates when typing ranges.
- Today marking (
highlightTodayproposed) -
noTodaysuppresses the library'sdp__todayoutline ring; it does not stop today being selectable.highlightTodayis separate and additive — it applies afh-todayclass via the library'sdayClasshook that renders today's date number bold, so it works independently ofnoToday. - Month/year header (proposed change)
-
All pickers use the library's original header: clicking the month opens an
all-months grid overlay, clicking the year opens the original year picker.
Production instead renders a custom blue header bar
(
MonthYearSelectbuilt onSelectSearchabledropdowns) — adopting the stock header is part of this proposal and would retire that component. - Calendar rules
-
Weeks start Sunday (
week-start="0"); format constants come fromcommon/settings.ts(DATE_FORMAT_DISPLAYetc.). The year list starts at ±20 years (INITIAL_DATE_YEARS_OFFSET) and extends automatically when month navigation reaches the window edge;minDate/maxDatepin the window instead. - Component mapping (production)
-
Single/range date →
common/forms/fields/date-input; date + time →date-time-input; time only →time-input. All wrapFormRowand model luxonDateTimepassed throughtimeZoneClear/dateTimeTruncatedTime— never construct values from rawnew Date()in app code. - Accessibility requirements
-
Icon triggers must be real buttons with labels;
label[for]must match the input id; errors needaria-invalid+aria-describedby+role="alert"; the input'saria-labelmust be the field label, not a generic string (production hardcodes"Datepicker input"— fix when porting). - Styling hooks
-
Menu classes:
date-input-menu,date-time-input-menu,time-input-menu,datepicker-multi-calendars,datepicker-with-presets(proposed). Source SCSS:uam/htdocs/v3/scss/jsapp3/jsapp3-datepicker.scss. Production also repositions the menu with a customcalculatePositionon open/resize/scroll — budget for it when porting new menu variants.
This mockup is faithful for look and interaction, but two things are deliberately
simplified. First, the model is a native Date rather than a luxon
DateTime, so the timezone-clearing helpers
(timeZoneClear, dateTimeTruncatedTime) are not exercised.
Second, menu positioning uses the library's own logic
instead of the ~100-line calculatePosition the real component runs on open,
resize and scroll — so overflow behaviour near a viewport edge will differ. Beyond
that, several things are intentional improvements over production, not current
behaviour: the stock month/year overlays (production renders a custom
MonthYearSelect header bar), the preset-ranges field, forgiving text
entry, and the accessibility fixes (per-field input labels, label/input wiring,
error association, focus rings, real buttons for the icon controls).
Worth a look during review: the #calendar-header slot in
DateInput.vue applies the red-color class to columns
index === 5 || index === 6. With week-start="0" the columns run
Sun–Sat, so that highlights Friday and Saturday, not the weekend. The
weekend columns would be 0 and 6. This mockup reproduces the
current behaviour as-is so you can see it.