FieldHub Date & Date-Time Picker

Current jsapp3 iteration — common/forms/fields/date-input/DateInput.vue and common/forms/fields/date-time-input/DateTimeInput.vue, built on @vuepic/vue-datepicker 8.8.1.

UI/UX Documentation
DateInput single date · format MM/dd/yyyy · typing enabled
DateInput — range modes range · multiCalendars
DateInput — preset ranges proposed addition · not in production jsapp3

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.

DateTimeInput & TimeInput 12-hour time · MM/dd/yyyy hh:mm a · hh:mm a

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.

Input rules & developer notes behaviour contract for implementation
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 strict MM/dd/yyyy (mask input is an open TODO in DateInput.vue).
Accepted time entry
3pm, 3 PM, 3:30pm, 3.30 p, 15:30, bare 9 (→ 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. change fires with null on clear — consumers must not treat every change as 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 (highlightToday proposed)
noToday suppresses the library's dp__today outline ring; it does not stop today being selectable. highlightToday is separate and additive — it applies a fh-today class via the library's dayClass hook that renders today's date number bold, so it works independently of noToday.
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 (MonthYearSelect built on SelectSearchable dropdowns) — 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 from common/settings.ts (DATE_FORMAT_DISPLAY etc.). The year list starts at ±20 years (INITIAL_DATE_YEARS_OFFSET) and extends automatically when month navigation reaches the window edge; minDate/maxDate pin the window instead.
Component mapping (production)
Single/range date → common/forms/fields/date-input; date + time → date-time-input; time only → time-input. All wrap FormRow and model luxon DateTime passed through timeZoneClear / dateTimeTruncatedTime — never construct values from raw new Date() in app code.
Accessibility requirements
Icon triggers must be real buttons with labels; label[for] must match the input id; errors need aria-invalid + aria-describedby + role="alert"; the input's aria-label must 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 custom calculatePosition on open/resize/scroll — budget for it when porting new menu variants.
Known gaps vs. production

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.