Skip to content

Commit

Permalink
fix(frontend): フォーカス/タブ移動に関する挙動を調整 (misskey-dev#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
taiyme authored Jun 5, 2024
1 parent b714ba3 commit e8c0306
Show file tree
Hide file tree
Showing 30 changed files with 598 additions and 332 deletions.
1 change: 0 additions & 1 deletion packages/frontend/src/components/MkButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ function onMousedown(evt: MouseEvent): void {
}

&:focus-visible {
outline: solid 2px var(--focus);
outline-offset: 2px;
}

Expand Down
12 changes: 1 addition & 11 deletions packages/frontend/src/components/MkChannelFollowButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,7 @@ async function onClick() {
}

&:focus-visible {
&:after {
content: "";
pointer-events: none;
position: absolute;
top: -5px;
right: -5px;
bottom: -5px;
left: -5px;
border: 2px solid var(--focus);
border-radius: 32px;
}
outline-offset: 2px;
}

&:hover {
Expand Down
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
5 changes: 0 additions & 5 deletions packages/frontend/src/components/MkEmojiPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -700,11 +700,6 @@ defineExpose({
border-radius: 4px;
font-size: 24px;

&:focus-visible {
outline: solid 2px var(--focus);
z-index: 1;
}

&:hover {
background: rgba(0, 0, 0, 0.05);
}
Expand Down
12 changes: 1 addition & 11 deletions packages/frontend/src/components/MkFollowButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,7 @@ onBeforeUnmount(() => {
}

&:focus-visible {
&:after {
content: "";
pointer-events: none;
position: absolute;
top: -5px;
right: -5px;
bottom: -5px;
left: -5px;
border: 2px solid var(--focus);
border-radius: 32px;
}
outline-offset: 2px;
}

&:hover {
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
25 changes: 21 additions & 4 deletions packages/frontend/src/components/MkMediaAudio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,32 @@ SPDX-License-Identifier: AGPL-3.0-only
<source :src="audioRef.url">
</audio>
<div :class="[$style.controlsChild, $style.controlsLeft]">
<button class="_button" :class="$style.controlButton" @click.stop="togglePlayPause">
<button
:class="['_button', $style.controlButton]"
tabindex="-1"
@click.stop="togglePlayPause"
>
<i v-if="isPlaying" class="ti ti-player-pause-filled"></i>
<i v-else class="ti ti-player-play-filled"></i>
</button>
</div>
<div :class="[$style.controlsChild, $style.controlsRight]">
<button class="_button" :class="$style.controlButton" @click.stop="showAudioMenu">
<button
:class="['_button', $style.controlButton]"
tabindex="-1"
@click.stop="() => {}"
@mousedown.prevent.stop="showAudioMenu"
>
<i class="ti ti-settings"></i>
</button>
</div>
<div :class="[$style.controlsChild, $style.controlsTime]">{{ hms(elapsedTimeMs) }}</div>
<div :class="[$style.controlsChild, $style.controlsVolume]">
<button class="_button" :class="$style.controlButton" @click.stop="toggleMute">
<button
:class="['_button', $style.controlButton]"
tabindex="-1"
@click.stop="toggleMute"
>
<i v-if="volume === 0" class="ti ti-volume-3"></i>
<i v-else class="ti ti-volume"></i>
</button>
Expand Down Expand Up @@ -366,7 +379,7 @@ onDeactivated(() => {
border: 0.5px solid var(--divider);
border-radius: var(--mediaList-radius, 8px);

&:focus {
&:focus-visible {
outline: none;
}
}
Expand Down Expand Up @@ -445,6 +458,10 @@ onDeactivated(() => {
color: var(--accent);
background-color: var(--accentedBg);
}

&:focus-visible {
outline: none;
}
}
}

Expand Down
13 changes: 11 additions & 2 deletions packages/frontend/src/components/MkMediaBanner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
<div :class="$style.downloadText">{{ mediaRef.name }}</div>
</a>
<button :class="['_button', $style.downloadMenu]" @click.stop="showMediaMenu">
<button
:class="['_button', $style.downloadMenu]"
tabindex="-1"
@click.stop="() => {}"
@mousedown.prevent.stop="showMediaMenu"
>
<i class="ti ti-settings"></i>
</button>
</div>
Expand Down Expand Up @@ -112,7 +117,7 @@ const showMediaMenu = (ev: MouseEvent) => {
border: 0.5px solid var(--divider);
border-radius: var(--mediaList-radius, 8px);

&:focus {
&:focus-visible {
outline: none;
}
}
Expand Down Expand Up @@ -215,6 +220,10 @@ const showMediaMenu = (ev: MouseEvent) => {
color: var(--accent);
background-color: var(--accentedBg);
}

&:focus-visible {
outline: none;
}
}

@container mediaBanner (max-width: 250px) {
Expand Down
25 changes: 21 additions & 4 deletions 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 @@ -66,8 +67,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template v-else-if="props.controls">
<div :class="$style.controlsUpperRight">
<button
:class="['_button', $style.controlItem]"
v-tooltip="i18n.ts.hide"
:class="['_button', $style.controlItem]"
tabindex="-1"
@click.stop="hideRef = true"
>
<div :class="$style.controlButton"><i class="ti ti-eye-off"></i></div>
Expand All @@ -77,8 +79,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.controlsLowerRight">
<button
:class="['_button', $style.controlItem]"
v-tooltip="i18n.ts.menu"
@click.stop="showImageMenu"
tabindex="-1"
@click.stop="() => {}"
@mousedown.prevent.stop="showImageMenu"
>
<div :class="$style.controlButton"><i class="ti ti-dots"></i></div>
</button>
Expand All @@ -89,6 +92,8 @@ SPDX-License-Identifier: AGPL-3.0-only
v-if="imageRef.comment"
v-tooltip:dialog="imageRef.comment"
:class="['_button', $style.controlItem]"
tabindex="-1"
@click.stop="() => {}"
>
<div :class="$style.controlButton"><span>ALT</span></div>
</button>
Expand All @@ -99,13 +104,17 @@ SPDX-License-Identifier: AGPL-3.0-only
v-if="['image/gif', 'image/apng'].includes(imageRef.type)"
v-tooltip:dialog="i18n.ts._tms.displayingGifFiles"
:class="['_button', $style.controlItem]"
tabindex="-1"
@click.stop="() => {}"
>
<div :class="$style.controlButton"><span>GIF</span></div>
</button>
<button
v-if="imageRef.isSensitive"
v-tooltip:dialog="i18n.ts._tms.displayingSensitiveFiles"
:class="['_button', $style.controlItem]"
tabindex="-1"
@click.stop="() => {}"
>
<div :class="$style.controlButton"><span>NSFW</span></div>
</button>
Expand Down Expand Up @@ -196,6 +205,10 @@ const showImageMenu = (ev: MouseEvent) => {
overflow: hidden; // fallback (overflow: clip)
overflow: clip;
border-radius: var(--mediaList-radius, 8px);

&:focus-visible {
outline: none;
}
}

.rootVisible {
Expand Down Expand Up @@ -296,6 +309,10 @@ const showImageMenu = (ev: MouseEvent) => {
&:last-child {
padding-right: clamp(4px, calc(8px * var(--mediaImage-scale)), 8px);
}

&:focus-visible {
outline: none;
}
}

.controlsUpperRight {
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
Loading

0 comments on commit e8c0306

Please sign in to comment.