From d8cf64a2ca489b380be6606c2f31c3d3cccb5e26 Mon Sep 17 00:00:00 2001
From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Tue, 9 Jul 2024 18:55:22 +0900
Subject: [PATCH] =?UTF-8?q?fix(frontend):=20=E3=83=95=E3=82=A9=E3=83=BC?=
=?UTF-8?q?=E3=82=AB=E3=82=B9/=E3=82=BF=E3=83=96=E7=A7=BB=E5=8B=95?=
=?UTF-8?q?=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E6=8C=99=E5=8B=95=E3=82=92?=
=?UTF-8?q?=E8=AA=BF=E6=95=B4=20(#226)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cherry-pick commit e8c030673326871edf3623cf2b8675d68f9e1b13
Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com>
---
packages/frontend/src/components/MkButton.vue | 1 -
.../src/components/MkChannelFollowButton.vue | 12 +-
.../frontend/src/components/MkContextMenu.vue | 3 +-
.../frontend/src/components/MkEmojiPicker.vue | 5 -
.../src/components/MkFollowButton.vue | 12 +-
.../src/components/MkImgWithBlurhash.vue | 4 +-
.../frontend/src/components/MkMediaAudio.vue | 30 +-
.../frontend/src/components/MkMediaList.vue | 54 +++-
.../frontend/src/components/MkMediaVideo.vue | 10 +-
.../frontend/src/components/MkMenu.child.vue | 5 +-
packages/frontend/src/components/MkMenu.vue | 296 ++++++++++--------
packages/frontend/src/components/MkNote.vue | 46 ++-
.../src/components/MkNoteDetailed.vue | 64 ++--
.../frontend/src/components/MkPopupMenu.vue | 4 +-
packages/frontend/src/components/MkSelect.vue | 27 +-
packages/frontend/src/os.ts | 26 +-
.../frontend/src/pages/drive.file.info.vue | 1 +
packages/frontend/src/pages/page.vue | 1 +
.../frontend/src/pages/settings/profile.vue | 1 +
packages/frontend/src/scripts/focus.ts | 32 --
packages/frontend/src/scripts/tms/focus.ts | 70 +++++
.../frontend/src/scripts/tms/get-or-null.ts | 19 ++
packages/frontend/src/style.scss | 23 +-
23 files changed, 450 insertions(+), 296 deletions(-)
delete mode 100644 packages/frontend/src/scripts/focus.ts
create mode 100644 packages/frontend/src/scripts/tms/focus.ts
create mode 100644 packages/frontend/src/scripts/tms/get-or-null.ts
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 25b003ba5a..9560efb7d9 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -250,7 +250,6 @@ function onMousedown(evt: MouseEvent): void {
}
&:focus-visible {
- outline: solid 2px var(--focus);
outline-offset: 2px;
}
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 841d37a568..35dc3ad4bf 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -87,17 +87,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 {
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index a807742bb9..b0f6bc9d81 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveToClass="defaultStore.state.animation ? $style.transition_fade_leaveTo : ''"
>
{}">
-
+
@@ -28,6 +28,7 @@ import * as os from '@/os.js';
const props = defineProps<{
items: MenuItem[];
ev: MouseEvent;
+ returnFocusElement?: HTMLElement | null;
}>();
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 4bd4bee1e5..1f7892fadb 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -702,11 +702,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);
}
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 6a4081079c..ea76950c0d 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -185,17 +185,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 {
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 4e3fafe845..617404f5c4 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/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index e8dfcc7768..d392d1f3e9 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -39,23 +39,37 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
+ {}"
+ @mousedown.prevent.stop="showMenu"
+ >
{{ hms(elapsedTimeMs) }}
-
+
@@ -64,6 +78,10 @@ SPDX-License-Identifier: AGPL-3.0-only
:class="$style.volumeSeekbar"
/>
+
{
border-radius: var(--radius);
overflow: clip;
- &:focus {
+ &:focus-visible {
outline: none;
}
}
@@ -437,6 +455,10 @@ onDeactivated(() => {
color: var(--accent);
background-color: var(--accentedBg);
}
+
+ &:focus-visible {
+ outline: none;
+ }
}
}
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index b1321a8ef9..d890c5c869 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -39,6 +39,7 @@ import XVideo from '@/components/MkMediaVideo.vue';
import * as os from '@/os.js';
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
import { defaultStore } from '@/store.js';
+import { focusParent } from '@/scripts/tms/focus.js';
const props = defineProps<{
mediaList: Misskey.entities.DriveFile[];
@@ -49,7 +50,9 @@ const gallery = shallowRef();
const pswpZIndex = os.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) {
@@ -60,7 +63,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 = '';
@@ -131,6 +134,7 @@ onMounted(() => {
bgOpacity: 1,
showAnimationDuration: 100,
hideAnimationDuration: 100,
+ returnFocus: false,
pswpModule: PhotoSwipe,
});
@@ -159,39 +163,47 @@ 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 => {
@@ -199,6 +211,16 @@ const previewable = (file: Misskey.entities.DriveFile): boolean => {
// 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,
+});