fix(modal): tag-chip kebab + ESC-after-accept + autocomplete scroll-into-view
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 18s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m2s

Two regressions the operator re-flagged (the earlier "fixes" didn't work):

#711 tag-chip kebab: TagPanel's kebab used the #activator + v-bind="props"
v-menu pattern — the exact pattern SuggestionItem's own comment documents as
NEVER toggling inside the teleported ImageViewer modal. Extracted TagChip.vue
using the proven explicit pattern (activator="parent" + :open-on-click="false"
+ a manual v-model), mirroring the working suggestion kebab. Now opens.

#700 ESC-after-accept: the guard suppressed close whenever ANY non-tooltip
overlay was active anywhere in the DOM, so a stray overlay after accepting a
suggestion (focus drops to <body>) blocked Esc. Now key off the event origin —
only defer to an overlay when Esc is pressed from INSIDE its content
(ev.target.closest('.v-overlay__content')); a stray overlay no longer traps the
modal, and dialogs/menus still handle their own Esc.

A1: TagAutocomplete arrow-nav now scrollIntoView's the highlighted row — the
list is capped at 240px and arrowing past the fold left the active item
off-screen (operator-flagged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 23:42:30 -04:00
parent 4c42a15fa1
commit 22dc516dc7
4 changed files with 90 additions and 62 deletions
+14 -14
View File
@@ -97,20 +97,20 @@ let prevBodyOverflow = null
// own keystrokes.
function onKeyDown(ev) {
if (ev.key === 'Escape') {
// Escape closes the modal even from inside a text input — that's
// the universal "get me out of here" expectation, and the
// autofocused tag-entry field would otherwise trap focus with no
// visible escape (operator-flagged 2026-06-01). EXCEPTION: when a
// nested Vuetify overlay is open (v-menu autocomplete dropdown,
// FandomPicker v-dialog, per-suggestion 3-dot menu), let that
// overlay's own Esc handling fire instead of closing the whole
// modal mid-interaction. Vuetify marks open overlays with
// `.v-overlay--active`.
// EXCLUDE tooltips (`.v-tooltip`): they're also `.v-overlay--active` while
// shown, so one lingering after a hover/click (e.g. just after accepting a
// suggested tag) wrongly suppressed the close (#700). Only real interactive
// overlays (menus/dialogs) should keep ESC from closing the modal.
if (document.querySelector('.v-overlay--active:not(.v-tooltip)')) return
// Escape closes the modal even from inside a text input — the universal
// "get me out of here" expectation; the autofocused tag field would
// otherwise trap focus (operator-flagged 2026-06-01). EXCEPTION: when the
// keystroke originates INSIDE an open Vuetify overlay's content (a rename/
// fandom/alias dialog, or a kebab menu), let that overlay handle its own
// Esc and don't close the whole modal.
//
// #700 re-fix (2026-06-07): the prior guard queried for ANY active overlay
// anywhere in the DOM and suppressed the close — so a lingering overlay
// after accepting a suggestion (focus drops to <body>, not into any
// overlay) wrongly blocked Esc. Keying off the event's origin instead means
// a stray overlay no longer traps the modal: only an Esc pressed from
// within overlay content defers to that overlay.
if (ev.target?.closest?.('.v-overlay__content')) return
ev.preventDefault()
emit('close')
} else if (ev.key === 'ArrowLeft') {