summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkModal.vue
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2024-07-12 16:25:44 +0900
committerGitHub <noreply@github.com>2024-07-12 16:25:44 +0900
commit385969e9f56a39a1e5547b94901d155e1e811263 (patch)
treec5c4082d99d97f3220efd4dfd0926b4e809cfd3b /packages/frontend/src/components/MkModal.vue
parentenhance(frontend): 未使用のサウンド設定を削除 (#14116) (diff)
downloadsharkey-385969e9f56a39a1e5547b94901d155e1e811263.tar.gz
sharkey-385969e9f56a39a1e5547b94901d155e1e811263.tar.bz2
sharkey-385969e9f56a39a1e5547b94901d155e1e811263.zip
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 commit 40a7509286a87911ad4cc06d9482e8a2e5d0e7e8. * Revert "fix(frontend): 直前のパターンを記録するように" This reverts commit 5372b2594023952cff34aa62253ed4efef15b5dd. * Revert "Revert "fix(frontend): 直前のパターンを記録するように"" This reverts commit a9bb52e799e110927ad92cd8f26af980819334e1. * Revert "Revert "記録するホットキーはレートリミット式にする"" This reverts commit bdac34273e0bc5f13604c7e2f9fa6b1321a0df3d. * 試験的にCypressでのFocustrapを無効化 * fix * fix focus-trap * Update Changelog * :v: * fix focustrap invocation logic * スクロールがsticky headerを考慮するように * :art: * スタイルの微調整 * :art: * 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>
Diffstat (limited to 'packages/frontend/src/components/MkModal.vue')
-rw-r--r--packages/frontend/src/components/MkModal.vue28
1 files changed, 26 insertions, 2 deletions
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 264d8b6c9c..a5fbf8d365 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -30,9 +30,9 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.transition_modal_leaveTo]: transitionName === 'modal',
[$style.transition_send_leaveTo]: transitionName === 'send',
})"
- :duration="transitionDuration" appear @afterLeave="emit('closed')" @enter="emit('opening')" @afterEnter="onOpened"
+ :duration="transitionDuration" appear @afterLeave="onClosed" @enter="emit('opening')" @afterEnter="onOpened"
>
- <div v-show="manualShowing != null ? manualShowing : showing" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
+ <div v-show="manualShowing != null ? manualShowing : showing" ref="modalRootEl" v-hotkey.global="keymap" :class="[$style.root, { [$style.drawer]: type === 'drawer', [$style.dialog]: type === 'dialog', [$style.popup]: type === 'popup' }]" :style="{ zIndex, pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
<div data-cy-bg :data-cy-transparent="isEnableBgTransparent" class="_modalBg" :class="[$style.bg, { [$style.bgTransparent]: isEnableBgTransparent }]" :style="{ zIndex }" @click="onBgClick" @mousedown="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
<div ref="content" :class="[$style.content, { [$style.fixed]: fixed }]" :style="{ zIndex }" @click.self="onBgClick">
<slot :max-height="maxHeight" :type="type"></slot>
@@ -48,6 +48,8 @@ import { isTouchUsing } from '@/scripts/touch.js';
import { defaultStore } from '@/store.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { type Keymap } from '@/scripts/hotkey.js';
+import { focusTrap } from '@/scripts/focus-trap.js';
+import { focusParent } from '@/scripts/focus.js';
function getFixedContainer(el: Element | null): Element | null {
if (el == null || el.tagName === 'BODY') return null;
@@ -69,6 +71,7 @@ const props = withDefaults(defineProps<{
zPriority?: 'low' | 'middle' | 'high';
noOverlap?: boolean;
transparentBg?: boolean;
+ returnFocusTo?: HTMLElement | null;
}>(), {
manualShowing: null,
src: null,
@@ -77,6 +80,7 @@ const props = withDefaults(defineProps<{
zPriority: 'low',
noOverlap: true,
transparentBg: false,
+ returnFocusTo: null,
});
const emit = defineEmits<{
@@ -94,6 +98,7 @@ const maxHeight = ref<number>();
const fixed = ref(false);
const transformOrigin = ref('center');
const showing = ref(true);
+const modalRootEl = shallowRef<HTMLElement>();
const content = shallowRef<HTMLElement>();
const zIndex = os.claimZIndex(props.zPriority);
const useSendAnime = ref(false);
@@ -132,6 +137,7 @@ const transitionDuration = computed((() =>
: 0
));
+let releaseFocusTrap: (() => void) | null = null;
let contentClicking = false;
function close(opts: { useSendAnimation?: boolean } = {}) {
@@ -296,6 +302,10 @@ const onOpened = () => {
}, { passive: true });
};
+const onClosed = () => {
+ emit('closed');
+};
+
const alignObserver = new ResizeObserver((entries, observer) => {
align();
});
@@ -313,6 +323,20 @@ onMounted(() => {
align();
}, { immediate: true });
+ watch([showing, () => props.manualShowing], ([showing, manualShowing]) => {
+ if (manualShowing === true || (manualShowing == null && showing === true)) {
+ if (modalRootEl.value != null) {
+ const { release } = focusTrap(modalRootEl.value);
+
+ releaseFocusTrap = release;
+ modalRootEl.value.focus();
+ }
+ } else {
+ releaseFocusTrap?.();
+ focusParent(props.returnFocusTo ?? props.src, true, false);
+ }
+ }, { immediate: true });
+
nextTick(() => {
alignObserver.observe(content.value!);
});