Skip to content

Commit

Permalink
fix(frontend): フォーカス/タブ移動に関する挙動を調整
Browse files Browse the repository at this point in the history
  • Loading branch information
taiyme committed Jun 4, 2024
1 parent 8724f72 commit e1e4185
Show file tree
Hide file tree
Showing 16 changed files with 464 additions and 279 deletions.
3 changes: 2 additions & 1 deletion packages/frontend/src/components/MkContextMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="defaultStore.state.animation ? $style.transition_fade_leaveTo : ''"
>
<div ref="rootEl" :class="$style.root" :style="{ zIndex }" @contextmenu.prevent.stop="() => {}">
<MkMenu :items="items" :align="'left'" @close="emit('closed')"/>
<MkMenu :items="items" :align="'left'" :returnFocusElement="returnFocusElement" @close="emit('closed')"/>
</div>
</Transition>
</template>
Expand All @@ -28,6 +28,7 @@ import * as os from '@/os.js';
const props = defineProps<{
items: MenuItem[];
ev: MouseEvent;
returnFocusElement?: HTMLElement | null;
}>();

const emit = defineEmits<{
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/components/MkImgWithBlurhash.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined"/>
<img v-show="!hide" key="img" ref="img" :height="imgHeight ?? undefined" :width="imgWidth ?? undefined" :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined" loading="eager" decoding="async"/>
<canvas v-show="hide" key="canvas" ref="canvas" :class="$style.canvas" :width="canvasWidth" :height="canvasHeight" :title="title ?? undefined" tabindex="-1"/>
<img v-show="!hide" key="img" ref="img" :height="imgHeight ?? undefined" :width="imgWidth ?? undefined" :class="$style.img" :src="src ?? undefined" :title="title ?? undefined" :alt="alt ?? undefined" loading="eager" decoding="async" tabindex="-1"/>
</TransitionGroup>
</div>
</template>
Expand Down
7 changes: 6 additions & 1 deletion packages/frontend/src/components/MkMediaImage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<MkImgWithBlurhash
:hash="imageRef.blurhash"
Expand Down Expand Up @@ -196,6 +197,10 @@ const showImageMenu = (ev: MouseEvent) => {
overflow: hidden; // fallback (overflow: clip)
overflow: clip;
border-radius: var(--mediaList-radius, 8px);

&:focus {
outline: none;
}
}

.rootVisible {
Expand Down
54 changes: 38 additions & 16 deletions packages/frontend/src/components/MkMediaList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import 'photoswipe/style.css';
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
import { claimZIndex } from '@/os.js';
import { defaultStore } from '@/store.js';
import { focusParent } from '@/scripts/tms/focus.js';
import XAudio from '@/components/MkMediaAudio.vue';
import XBanner from '@/components/MkMediaBanner.vue';
import XImage from '@/components/MkMediaImage.vue';
Expand All @@ -55,7 +56,9 @@ const gallery = shallowRef<HTMLDivElement>();
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) {
Expand All @@ -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 = '';
Expand Down Expand Up @@ -141,6 +144,7 @@ onMounted(() => {
bgOpacity: 1,
showAnimationDuration: 100,
hideAnimationDuration: 100,
returnFocus: false,
pswpModule: PhotoSwipe,
});

Expand Down Expand Up @@ -169,46 +173,64 @@ 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;
focusParent(activeEl, true, true);
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 => {
if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
};

const openGallery = () => {
if (props.mediaList.filter(media => previewable(media)).length > 0) {
lightbox?.loadAndOpen(0);
}
};

defineExpose({
openGallery,
});
</script>

<style lang="scss" module>
Expand Down Expand Up @@ -331,7 +353,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;
Expand All @@ -345,7 +367,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;
Expand Down
5 changes: 3 additions & 2 deletions packages/frontend/src/components/MkMenu.child.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>

<script lang="ts" setup>
import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
import { nextTick, onMounted, onUnmounted, provide, shallowRef, watch } from 'vue';
import MkMenu from './MkMenu.vue';
import { MenuItem } from '@/types/menu.js';

Expand All @@ -19,14 +19,15 @@ const props = defineProps<{
targetElement: HTMLElement;
rootElement: HTMLElement;
width?: number;
viaKeyboard?: boolean;
}>();

const emit = defineEmits<{
(ev: 'closed'): void;
(ev: 'actioned'): void;
}>();

provide('isNestingMenu', true);

const el = shallowRef<HTMLElement>();
const align = 'left';

Expand Down
Loading

0 comments on commit e1e4185

Please sign in to comment.