Global support for enter tab and escape #71
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
gtfs.zone/coloring-book#71
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Extend
showModalinmodal-utils.tsto accept explicitenterActionandescapeActionindices that wire up keyboard shortcuts directly in the utility — replacing the ad-hocdismissableflag and the one-off Enter handler inshowFromURLModal. Every call site is updated to declare its intent explicitly. The bespokeshowAtlasSearchModalis migrated intoshowModalusing aclosecallback passed toonMount. Modals with no sensible keyboard shortcut (DB error, schema migration) are the documented exception.Relevant context
src/modules/modal-utils.ts— core utility, 85 lines. Currentdismissable: trueadds X button + Escape-to-close. No Enter support.onMount?: () => voidhas no way to close the modal programmatically.src/modules/about-modal.ts— 1 call,dismissable: true, 1 action (Close).src/modules/ui.ts— 2 calls:showURLErrorModal(dismissable: true, 1 action) andshowFromURLModal(dismissable: true, 2 actions; has a manualonMountEnter handler that queries.modal-action .btn-primary— this is the hack to remove).src/modules/interaction-handler.ts— 1 call: New Stop modal (dismissable: true, 2 actions). Currently Escape only closes the modal but does NOT callsetMapMode(NAVIGATE). The new escapeAction wiring fixes this bug as a side effect, since escapeAction triggers the Cancel onClick which callssetMapMode(NAVIGATE).src/modules/page-content-renderer.ts— 1 call: Stop-has-visits confirmation (nodismissable, 2 actions).src/modules/database-fallback-manager.ts— 2 calls: DB Error (nodismissable, dynamically-built actions — exception); DB Reset (dismissable: true, 2 actions).src/modules/gtfs-database.ts— 1 call: schema migration (nodismissable, 2 actions — exception, user must act).src/modules/atlas-search.ts— custom modal, not usingshowModal. ReturnsPromise<string | null>. Has Cancel button + backdrop click, no Escape. Needs migration.Phase 1 — Overhaul
modal-utils.tsAPIGoal: Replace
dismissablewith explicitenterAction/escapeAction, addclosetoonMount, and consolidate the button-triggering logic into a shared helper.dismissable?: booleanfrom the options interfaceenterAction?: number— index of the action triggered by Enter (skipped when focused element is<button>or<textarea>)escapeAction?: number— index of the action triggered by Escape; also controls whether the X button is renderedonMount?: () => voidtoonMount?: (close: () => void) => void— existing callers that ignore the argument continue to work in TypeScript (functions may ignore extra parameters)triggerAction(idx: number): Promise<void>inner helper that replicates the current button-click flow: disable all buttons → awaitactions[idx].onClick()→ ifkeepOpen=truere-enable, else callclose()button[data-idx]click listeners with calls totriggerAction(idx)<button data-dismiss>✕</button>whenescapeAction !== undefined; on click, calltriggerAction(escapeAction)document, cleaned up inclose()):Escape→ ifescapeAction !== undefined,e.preventDefault(), calltriggerAction(escapeAction)Enter→ ifenterAction !== undefinedande.targetis notHTMLButtonElementand notHTMLTextAreaElement,e.preventDefault(), calltriggerAction(enterAction)closetoonMountcall:options.onMount?.(close)close()called directly fromonMount(e.g. atlas search selecting a result) must remove the keydown listener too — ensureclose()always does full cleanup regardless of how it's invokedPhase 2 — Update all
showModalcall sitesGoal: Wire up Enter/Escape at every call site, remove
dismissable, remove the manual Enter hack.about-modal.ts: replacedismissable: truewithenterAction: 0, escapeAction: 0(Close at index 0)ui.ts—showURLErrorModal: replacedismissable: truewithenterAction: 0, escapeAction: 0(Close at index 0)ui.ts—showFromURLModal: replacedismissable: truewithenterAction: 1, escapeAction: 0(Load=1, Cancel=0); delete theinput.addEventListener('keydown', ...)block insideonMountthat manually clicks.modal-action .btn-primaryinteraction-handler.ts— New Stop: replacedismissable: truewithenterAction: 1, escapeAction: 0(Create Stop=1, Cancel=0)page-content-renderer.ts— Stop-has-visits: addenterAction: 1, escapeAction: 0(Delete=1, Cancel=0); nodismissablewas set beforedatabase-fallback-manager.ts— DB Error: no change (exception — noenterAction, noescapeAction)database-fallback-manager.ts— DB Reset: replacedismissable: truewithenterAction: 0, escapeAction: 1(Reset=0, Cancel=1)gtfs-database.ts— schema migration: addenterAction: 0(Export & Continue=0), noescapeAction(exception — user must act)Phase 3 — Migrate
showAtlasSearchModaltoshowModalGoal: Replace the bespoke DOM modal in
atlas-search.tswith ashowModalcall, getting Escape for free viaescapeActionand result-selection viaclose.asyncand introducelet selectedUrl: string | null = nullin the closureshowModalwith:title: 'From TransitLand Atlas'body: the search input + results container HTML (identical markup to current)actions: [{ label: 'Cancel', onClick: () => {} }]escapeAction: 0(Escape = Cancel)enterAction— Enter in the search input should continue filtering, not confirm; selection is click-onlyonMount: (close) => { ... }— move all current post-appendChildlogic here; changeconst onSelect = (url) => cleanup(url)toconst onSelect = (url: string) => { selectedUrl = url; close(); }selectedUrlafterawait showModal(...)modal.addEventListener('click', ...)) — showModal doesn't add one, and Escape covers keyboard dismisscleanuphelper andresolvecall — replaced byclosefromonMountshowModalappends todocument.bodyand removes on close — remove the manualdocument.body.appendChild(modal)anddocument.body.removeChild(modal)calls from the rewritten functionOriginal Issue
Right now, when I hit tab, because of the way we update (basically refresh) the focus gets lost so it doesn't work.
Also, in modals, escape and enter should work universally for the "go ahead" and "go back" actions.