fix(frontend): フォーカスの挙動を修正 (#14158)
* fix(frontend): 直前のパターンを記録するように * fix(frontend): フォーカス/タブ移動に関する挙動を調整 (#226) Cherry-pick commit e8c030673326871edf3623cf2b8675d68f9e1b13 Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com> * focusのデザイン修正 * move scripts * Modalにfocus trapを追加 * 記録するホットキーはレートリミット式にする * escキーのハンドリングをMkModalに統一 * fix * enterで子メニューを開けるように * lint * fix focus trap * improve switch accessibility * 一部のmodalのフォーカストラップが外れない問題を修正 * fix * fix * Revert "記録するホットキーはレートリミット式にする" This reverts commit40a7509286
. * Revert "fix(frontend): 直前のパターンを記録するように" This reverts commit5372b25940
. * Revert "Revert "fix(frontend): 直前のパターンを記録するように"" This reverts commita9bb52e799
. * Revert "Revert "記録するホットキーはレートリミット式にする"" This reverts commitbdac34273e
. * 試験的にCypressでのFocustrapを無効化 * fix * fix focus-trap * Update Changelog * ✌️ * fix focustrap invocation logic * スクロールがsticky headerを考慮するように * 🎨 * スタイルの微調整 * 🎨 * remove deprecated key aliases * focusElementが足りなかったので修正 * preview系にfocus時スタイルが足りなかったので修正 * `returnFocusElement` -> `returnFocusTo` * lint * Update packages/frontend/src/components/MkModalWindow.vue * Apply suggestions from code review Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com> * keydownイベントをまとめる * use correct pesudo-element selector * fix * rename --------- Co-authored-by: taiyme <53635909+taiyme@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
@@ -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/focus.js';
|
||||
|
||||
const props = defineProps<{
|
||||
mediaList: Misskey.entities.DriveFile[];
|
||||
@@ -49,7 +50,9 @@ const gallery = shallowRef<HTMLDivElement>();
|
||||
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,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
@@ -298,7 +320,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;
|
||||
@@ -312,7 +334,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;
|
||||
|
Reference in New Issue
Block a user