diff --git a/code/websites/pokedex.online/src/components/shared/BaseModal.vue b/code/websites/pokedex.online/src/components/shared/BaseModal.vue index a9440c5..037a79a 100644 --- a/code/websites/pokedex.online/src/components/shared/BaseModal.vue +++ b/code/websites/pokedex.online/src/components/shared/BaseModal.vue @@ -60,7 +60,7 @@ const props = defineProps({ type: Boolean, required: true }, - + /** * Modal title (alternative to header slot) */ @@ -68,7 +68,7 @@ const props = defineProps({ type: String, default: null }, - + /** * Modal size * @type {'small' | 'medium' | 'large' | 'full'} @@ -76,9 +76,9 @@ const props = defineProps({ size: { type: String, default: 'medium', - validator: (value) => ['small', 'medium', 'large', 'full'].includes(value) + validator: value => ['small', 'medium', 'large', 'full'].includes(value) }, - + /** * Whether clicking overlay closes the modal */ @@ -86,7 +86,7 @@ const props = defineProps({ type: Boolean, default: true }, - + /** * Whether modal can be closed (hides X button, prevents ESC/overlay close) */ @@ -94,7 +94,7 @@ const props = defineProps({ type: Boolean, default: false }, - + /** * Whether to show the close X button */ @@ -123,7 +123,7 @@ const handleOverlayClick = () => { } }; -const handleEscapeKey = (event) => { +const handleEscapeKey = event => { if (event.key === 'Escape' && props.modelValue && !props.persistent) { handleClose(); } @@ -132,7 +132,7 @@ const handleEscapeKey = (event) => { // Focus management const getFocusableElements = () => { if (!modalRef.value) return []; - + const focusableSelectors = [ 'a[href]', 'area[href]', @@ -143,21 +143,21 @@ const getFocusableElements = () => { '[tabindex]:not([tabindex="-1"])', '[contenteditable="true"]' ]; - + return Array.from( modalRef.value.querySelectorAll(focusableSelectors.join(',')) ); }; -const handleTabKey = (event) => { +const handleTabKey = event => { if (!props.modelValue) return; - + const focusableElements = getFocusableElements(); if (focusableElements.length === 0) return; - + const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; - + if (event.shiftKey) { // Shift + Tab: move focus backwards if (document.activeElement === firstElement) { @@ -173,7 +173,7 @@ const handleTabKey = (event) => { } }; -const handleKeyDown = (event) => { +const handleKeyDown = event => { if (event.key === 'Escape') { handleEscapeKey(event); } else if (event.key === 'Tab') { @@ -182,32 +182,35 @@ const handleKeyDown = (event) => { }; // Lifecycle hooks -watch(() => props.modelValue, async (newValue) => { - if (newValue) { - // Modal opened - previousActiveElement.value = document.activeElement; - document.body.style.overflow = 'hidden'; - emit('open'); - - // Focus first focusable element after render - await nextTick(); - const focusableElements = getFocusableElements(); - if (focusableElements.length > 0) { - focusableElements[0].focus(); - } else if (modalRef.value) { - modalRef.value.focus(); - } - } else { - // Modal closed - document.body.style.overflow = ''; - - // Restore focus to previous element - if (previousActiveElement.value) { - previousActiveElement.value.focus(); - previousActiveElement.value = null; +watch( + () => props.modelValue, + async newValue => { + if (newValue) { + // Modal opened + previousActiveElement.value = document.activeElement; + document.body.style.overflow = 'hidden'; + emit('open'); + + // Focus first focusable element after render + await nextTick(); + const focusableElements = getFocusableElements(); + if (focusableElements.length > 0) { + focusableElements[0].focus(); + } else if (modalRef.value) { + modalRef.value.focus(); + } + } else { + // Modal closed + document.body.style.overflow = ''; + + // Restore focus to previous element + if (previousActiveElement.value) { + previousActiveElement.value.focus(); + previousActiveElement.value = null; + } } } -}); +); onMounted(() => { document.addEventListener('keydown', handleKeyDown); @@ -244,7 +247,9 @@ onBeforeUnmount(() => { .modal-container { background: white; border-radius: 0.5rem; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + box-shadow: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 10px 10px -5px rgba(0, 0, 0, 0.04); max-height: 90vh; display: flex; flex-direction: column;