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 40a7509286.

* Revert "fix(frontend): 直前のパターンを記録するように"

This reverts commit 5372b25940.

* Revert "Revert "fix(frontend): 直前のパターンを記録するように""

This reverts commit a9bb52e799.

* Revert "Revert "記録するホットキーはレートリミット式にする""

This reverts commit bdac34273e.

* 試験的に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:
かっこかり
2024-07-12 16:25:44 +09:00
committed by GitHub
parent 121af778a0
commit 385969e9f5
61 changed files with 932 additions and 391 deletions

View File

@@ -5,7 +5,7 @@
// TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
import { Component, markRaw, Ref, ref, defineAsyncComponent, nextTick } from 'vue';
import { EventEmitter } from 'eventemitter3';
import * as Misskey from 'misskey-js';
import type { ComponentProps as CP } from 'vue-component-type-helpers';
@@ -24,6 +24,8 @@ import MkContextMenu from '@/components/MkContextMenu.vue';
import { MenuItem } from '@/types/menu.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js';
import { focusParent } from './scripts/focus.js';
export const openingWindowsCount = ref(0);
@@ -622,31 +624,33 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: {
export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: {
align?: string;
width?: number;
viaKeyboard?: boolean;
onClosing?: () => void;
}): Promise<void> {
return new Promise(resolve => {
let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(document.activeElement);
return new Promise(resolve => nextTick(() => {
const { dispose } = popup(MkPopupMenu, {
items,
src,
width: options?.width,
align: options?.align,
viaKeyboard: options?.viaKeyboard,
returnFocusTo,
}, {
closed: () => {
resolve();
dispose();
returnFocusTo = null;
},
closing: () => {
if (options?.onClosing) options.onClosing();
options?.onClosing?.();
},
});
});
}));
}
export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement);
ev.preventDefault();
return new Promise(resolve => {
return new Promise(resolve => nextTick(() => {
const { dispose } = popup(MkContextMenu, {
items,
ev,
@@ -654,14 +658,19 @@ export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise<void> {
closed: () => {
resolve();
dispose();
// MkModalを通していないのでここでフォーカスを戻す処理を行う
if (returnFocusTo != null) {
focusParent(returnFocusTo, true, false);
returnFocusTo = null;
}
},
});
});
}));
}
export function post(props: Record<string, any> = {}): Promise<void> {
showMovedDialog();
return new Promise(resolve => {
// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、