diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue index d40bf19f0dda..8ba48ae3fbf4 100644 --- a/packages/frontend/src/components/MkImgWithBlurhash.vue +++ b/packages/frontend/src/components/MkImgWithBlurhash.vue @@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only :enterToClass="defaultStore.state.animation && props.transition?.enterToClass || undefined" :leaveFromClass="defaultStore.state.animation && props.transition?.leaveFromClass || undefined" > - - + + diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 2e91e823126e..007180b16fa8 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -24,8 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only title: imageRef.name, class: $style.imageContainer, href: imageRef.url, - style: 'cursor: zoom-in;' + style: 'cursor: zoom-in;', }" + tabindex="-1" > { overflow: hidden; // fallback (overflow: clip) overflow: clip; border-radius: var(--mediaList-radius, 8px); + + &:focus { + outline: none; + } } .rootVisible { diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index d9d2541fe21c..3637497063a7 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -43,6 +43,7 @@ import XAudio from '@/components/MkMediaAudio.vue'; import XBanner from '@/components/MkMediaBanner.vue'; import XImage from '@/components/MkMediaImage.vue'; import XVideo from '@/components/MkMediaVideo.vue'; +import { focusParent } from '@/scripts/focus.js'; const EXPANDED_MIN_HEIGHT = 80 as const; @@ -55,7 +56,9 @@ const gallery = shallowRef(); const pswpZIndex = claimZIndex('middle'); document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString()); const count = computed(() => props.mediaList.filter(media => previewable(media)).length); -let lightbox: PhotoSwipeLightbox | null; +let lightbox: PhotoSwipeLightbox | null = null; + +let activeEl: HTMLElement | null = null; const popstateHandler = (): void => { if (lightbox?.pswp && lightbox.pswp.isOpen === true) { @@ -66,7 +69,7 @@ const popstateHandler = (): void => { async function calcAspectRatio() { if (!gallery.value) return; - let img = props.mediaList[0]; + const img = props.mediaList[0]; if (props.mediaList.length !== 1 || !(img.properties.width && img.properties.height)) { gallery.value.style.aspectRatio = ''; @@ -141,6 +144,7 @@ onMounted(() => { bgOpacity: 1, showAnimationDuration: 100, hideAnimationDuration: 100, + returnFocus: false, pswpModule: PhotoSwipe, }); @@ -169,39 +173,46 @@ onMounted(() => { lightbox.on('uiRegister', () => { lightbox?.pswp?.ui?.registerElement({ name: 'altText', - className: 'pwsp__alt-text-container', + className: 'pswp__alt-text-container', appendTo: 'wrapper', - onInit: (el, pwsp) => { - let textBox = document.createElement('p'); - textBox.className = 'pwsp__alt-text _acrylic'; + onInit: (el, pswp) => { + const textBox = document.createElement('p'); + textBox.className = 'pswp__alt-text _acrylic'; el.appendChild(textBox); - pwsp.on('change', () => { - textBox.textContent = pwsp.currSlide?.data.comment; + pswp.on('change', () => { + textBox.textContent = pswp.currSlide?.data.comment; }); }, }); }); - lightbox.init(); - - window.addEventListener('popstate', popstateHandler); - - lightbox.on('beforeOpen', () => { + lightbox.on('afterInit', () => { + activeEl = document.activeElement instanceof HTMLElement ? document.activeElement : null; + lightbox?.pswp?.element?.focus({ + preventScroll: true, + }); history.pushState(null, '', '#pswp'); }); - lightbox.on('close', () => { + lightbox.on('destroy', () => { + focusParent(activeEl, true, false); + activeEl = null; if (window.location.hash === '#pswp') { history.back(); } }); + + window.addEventListener('popstate', popstateHandler); + + lightbox.init(); }); onUnmounted(() => { window.removeEventListener('popstate', popstateHandler); lightbox?.destroy(); lightbox = null; + activeEl = null; }); const previewable = (file: Misskey.entities.DriveFile): boolean => { @@ -331,7 +342,7 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => { backdrop-filter: var(--modalBgFilter); } -.pwsp__alt-text-container { +.pswp__alt-text-container { display: flex; flex-direction: row; align-items: center; @@ -345,7 +356,7 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => { max-width: 800px; } -.pwsp__alt-text { +.pswp__alt-text { color: var(--fg); margin: 0 auto; text-align: center; diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue index dfb6d346182e..cc2de91d4e73 100644 --- a/packages/frontend/src/components/MkMenu.child.vue +++ b/packages/frontend/src/components/MkMenu.child.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only