summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkModal.vue
diff options
context:
space:
mode:
authormisskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com>2024-07-31 11:20:31 +0000
committerGitHub <noreply@github.com>2024-07-31 11:20:31 +0000
commite98f66db51aabef925ea1a8faee6c37f67071107 (patch)
tree168fcc9219f7511bbf9bc198568406bd49fc31bb /packages/frontend/src/components/MkModal.vue
parentfix: remove unreleased section (#14246) (diff)
parentRelease: 2024.7.0 (diff)
downloadsharkey-e98f66db51aabef925ea1a8faee6c37f67071107.tar.gz
sharkey-e98f66db51aabef925ea1a8faee6c37f67071107.tar.bz2
sharkey-e98f66db51aabef925ea1a8faee6c37f67071107.zip
Merge pull request #14233 from misskey-dev/develop
Release: 2024.7.0
Diffstat (limited to 'packages/frontend/src/components/MkModal.vue')
-rw-r--r--packages/frontend/src/components/MkModal.vue38
1 files changed, 34 insertions, 4 deletions
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 9e69ab2207..f8032f9b43 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>
@@ -47,6 +47,9 @@ import * as os from '@/os.js';
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;
@@ -68,6 +71,8 @@ const props = withDefaults(defineProps<{
zPriority?: 'low' | 'middle' | 'high';
noOverlap?: boolean;
transparentBg?: boolean;
+ hasInteractionWithOtherFocusTrappedEls?: boolean;
+ returnFocusTo?: HTMLElement | null;
}>(), {
manualShowing: null,
src: null,
@@ -76,6 +81,8 @@ const props = withDefaults(defineProps<{
zPriority: 'low',
noOverlap: true,
transparentBg: false,
+ hasInteractionWithOtherFocusTrappedEls: false,
+ returnFocusTo: null,
});
const emit = defineEmits<{
@@ -93,6 +100,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);
@@ -131,6 +139,7 @@ const transitionDuration = computed((() =>
: 0
));
+let releaseFocusTrap: (() => void) | null = null;
let contentClicking = false;
function close(opts: { useSendAnimation?: boolean } = {}) {
@@ -154,8 +163,11 @@ if (type.value === 'drawer') {
}
const keymap = {
- 'esc': () => emit('esc'),
-};
+ 'esc': {
+ allowRepeat: true,
+ callback: () => emit('esc'),
+ },
+} as const satisfies Keymap;
const MARGIN = 16;
const SCROLLBAR_THICKNESS = 16;
@@ -292,6 +304,10 @@ const onOpened = () => {
}, { passive: true });
};
+const onClosed = () => {
+ emit('closed');
+};
+
const alignObserver = new ResizeObserver((entries, observer) => {
align();
});
@@ -309,6 +325,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, props.hasInteractionWithOtherFocusTrappedEls);
+
+ releaseFocusTrap = release;
+ modalRootEl.value.focus();
+ }
+ } else {
+ releaseFocusTrap?.();
+ focusParent(props.returnFocusTo ?? props.src, true, false);
+ }
+ }, { immediate: true });
+
nextTick(() => {
alignObserver.observe(content.value!);
});