Skip to content

Timeline plugin: date selector - #241

Open
sandesh-sp wants to merge 3 commits into
feat/timeline-viewfrom
feat/timeline-date-selector
Open

Timeline plugin: date selector#241
sandesh-sp wants to merge 3 commits into
feat/timeline-viewfrom
feat/timeline-date-selector

Conversation

@sandesh-sp

@sandesh-sp sandesh-sp commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Replaces the date selector placeholder with the real control: the current date in the active time mode's format, opening a popover picker whose fields match that mode — year stepper, month grid, day calendar, or datetime field. Picks are validated and clamped to the timeline range; out-of-range months and days are disabled.

Stack — merge in order, starting from the base:

  1. Timeline plugin: shell and component placeholders #239 shell and component placeholders → development
  2. Timeline plugin: timeline view #240 timeline view
  3. Timeline plugin: date selector #241 date selector
  4. Timeline plugin: playback controls #242 playback controls
  5. Timeline plugin: time mode control #243 time mode control

Replaces the date selector placeholder with the real control: the current
date rendered in the active time mode's format, opening a popover picker
whose fields match that mode — a year stepper, a month grid, a day
calendar, or a datetime field. Picks are validated and clamped to the
timeline range, and out-of-range months and days are disabled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgqjkZhQvfoBgdJtt9SSKS
MMGIS core stores and formats time as UTC ISO strings, so the selector now
matches: all 32 date call sites across DateSelector and DayCalendar run through
moment.utc. Previously the header rendered the viewer's local calendar day and a
day click emitted an instant offset by the local UTC offset, landing on a
different UTC day east and west of Greenwich.

- Derive the HOUR-mode date from the selectedDate prop instead of a value
  seeded once on open, so a time change arriving while the picker is open is no
  longer overwritten with a stale date.
- Rewrite the month field's text only when an arrow or a snapped year moves the
  view, not on every viewMonth change, so typing a two-digit month reaches
  October through December instead of being replaced mid-keystroke.
- Give the picker popover a title element and aria-labelledby in place of a
  label pointing at an id that only exists in YEAR and MONTH modes, and set
  aria-haspopup/aria-expanded/aria-controls on the trigger.
- Move focus into the popover on open and restore it to the trigger on close.
- Name each day cell with its full date rather than the bare day number.
- Split :focus-visible out of the calendar hover rule, which was both invisible
  and overriding the selected cell's fill, and keep the ringless reset for
  pointer focus only.
@sandesh-sp

Copy link
Copy Markdown
Collaborator Author

Review pass over the stack (#239#243). Seven issues in this PR, pushed as 0cd3c48. Rebased on the fixes in #239 and #240.

The big one: local time vs UTC

MMGIS core is UTC end to end — TimeControl.globalTimeFormat = utcFormat(...), times stored as toISOString() Z-strings, TimeUI on moment.utc throughout. This selector was entirely browser-local: a grep for moment.utc|utcFormat|getUTC across the plugin returned zero hits.

Two consequences, with currentTime = 2025-03-10T00:00:00Z:

  • At America/New_York the header rendered Mar 9 while core's own TimeUI showed 2025-03-10 on the same screen.
  • Clicking "10" in the day grid emitted 2025-03-10T05:00:00.000Z, five hours off the day boundary the data is keyed to. At UTC+10 the same click emitted 2025-03-09T14:00:00.000Z — time-enabled tile URLs built for the wrong day.

All 32 date call sites across DateSelector and DayCalendar now go through moment.utc. Everything else about the date handling was already careful — every path into onDateChange goes through commit, which validates isValid() and hard-clamps into range, so no invalid or out-of-range Date can reach toISOString(); the day grid walks real dates rather than adding 24h, so leap years and DST are correct. Getting all that right and then reading the viewer's zone was the one gap, and the fix was mechanical.

Also fixed here

  • Stale date half in HOUR mode. inputValue was seeded only on popover open and never re-synced, and the HOUR branch read it in preference to the prop. Open the picker at 2025-03-10T20:00, let playback cross midnight, then edit the time field → it wrote the stale date back and rewound the map a full day. The date half now derives from selectedDate; the inputValue state is gone.
  • Two-digit months were unreachable. A [viewMonth] effect rewrote monthText on the same keystroke handleMonthInput accepted: typing "1" for "10" strict-parsed to January and the field was immediately rewritten to "Jan", so the next keystroke gave "Jan0". October through December could not be entered by number, contradicting the component's own comment. Text is now rewritten only when an arrow or a snapped year moves the view.
  • Popover had no dialog semantics. It portals to the end of document.body with no role, no Escape, no focus management — so opening it left focus on the trigger and Tab walked the entire MMGIS UI before reaching the calendar, with no way to dismiss in place (Enter can't submit either: 2+ text inputs block implicit submission). Now role="dialog", Escape-to-close, focus moved in on open and restored on close, and aria-haspopup/aria-expanded/aria-controls on the trigger.
  • Dangling htmlFor. <label htmlFor="date-input"> rendered in all four modes but that id only exists on the year input, which renders in YEAR and MONTH. Replaced with a title element and aria-labelledby on the form.
  • Day cells now carry a full-date aria-label instead of announcing a bare day number with no month or year.
  • Calendar focus indicator was folded into the hover rule: outline: none plus a #e6f4f6 tint at ~1.15:1 on white, indistinguishable from hover — and :focus-visible (0,2,0) outranked --selected (0,1,0), so focusing the selected day stripped its fill and the grid looked unselected. Split out with a real ring; the ringless reset now applies to pointer focus only.

tsc --noEmit clean, 898 unit tests pass.

One thing left alone: the "Compare date" button still has no onClick. Looked like intended future work rather than a defect to fix here — say the word if it should be wired or removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant