fix: drop migration backup-gate (FC-3h supersedes) + modal Escape via document-level listener so video focus can't swallow it
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,6 @@
|
||||
<Teleport to="body">
|
||||
<div
|
||||
class="fc-viewer" role="dialog" aria-modal="true"
|
||||
@keydown.esc="$emit('close')"
|
||||
@keydown.left="onArrowLeft"
|
||||
@keydown.right="onArrowRight"
|
||||
tabindex="-1" ref="rootEl"
|
||||
>
|
||||
<button class="fc-viewer__close" @click="$emit('close')" aria-label="Close">
|
||||
@@ -68,7 +65,7 @@ import VideoCanvas from './VideoCanvas.vue'
|
||||
import TagPanel from './TagPanel.vue'
|
||||
import ProvenancePanel from './ProvenancePanel.vue'
|
||||
|
||||
defineEmits(['close'])
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const modal = useModalStore()
|
||||
const rootEl = ref(null)
|
||||
@@ -86,14 +83,39 @@ const integrityBadge = computed(() => {
|
||||
})
|
||||
|
||||
let prevBodyOverflow = null
|
||||
|
||||
// Document-level keyboard handler. Bound on document (not on the modal
|
||||
// root) because <video> elements capture focus when the user interacts
|
||||
// with their controls — when video has focus, Escape and arrow keys
|
||||
// don't bubble reliably to ancestors. Listening on document side-steps
|
||||
// that. Filtered via isTextEntry so tag/comment inputs still get their
|
||||
// own keystrokes.
|
||||
function onKeyDown(ev) {
|
||||
if (ev.key === 'Escape') {
|
||||
if (isTextEntry(ev.target)) return
|
||||
ev.preventDefault()
|
||||
emit('close')
|
||||
} else if (ev.key === 'ArrowLeft') {
|
||||
if (isTextEntry(ev.target)) return
|
||||
ev.preventDefault()
|
||||
modal.goPrev()
|
||||
} else if (ev.key === 'ArrowRight') {
|
||||
if (isTextEntry(ev.target)) return
|
||||
ev.preventDefault()
|
||||
modal.goNext()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
prevBodyOverflow = document.body.style.overflow
|
||||
document.body.style.overflow = 'hidden'
|
||||
document.addEventListener('keydown', onKeyDown, true)
|
||||
await nextFrame()
|
||||
rootEl.value?.focus()
|
||||
})
|
||||
onUnmounted(() => {
|
||||
document.body.style.overflow = prevBodyOverflow
|
||||
document.removeEventListener('keydown', onKeyDown, true)
|
||||
})
|
||||
|
||||
watch(() => modal.currentImageId, async () => {
|
||||
@@ -105,16 +127,6 @@ function nextFrame() {
|
||||
return new Promise(resolve => requestAnimationFrame(resolve))
|
||||
}
|
||||
|
||||
function onArrowLeft(ev) {
|
||||
if (isTextEntry(ev.target)) return
|
||||
ev.preventDefault()
|
||||
modal.goPrev()
|
||||
}
|
||||
function onArrowRight(ev) {
|
||||
if (isTextEntry(ev.target)) return
|
||||
ev.preventDefault()
|
||||
modal.goNext()
|
||||
}
|
||||
function isTextEntry(el) {
|
||||
if (!el) return false
|
||||
const tag = el.tagName
|
||||
|
||||
Reference in New Issue
Block a user