Skip to content

fix(frontend): モーダルが重なっているときに ESC で下のモーダルまで閉じる問題を修正 - #18066

Open
Syogo-Suganoya wants to merge 2 commits into
misskey-dev:developfrom
Syogo-Suganoya:fix-14388-quote-dialog-esc
Open

Syogo-Suganoya wants to merge 2 commits into
misskey-dev:developfrom
Syogo-Suganoya:fix-14388-quote-dialog-esc

Conversation

@Syogo-Suganoya

@Syogo-Suganoya Syogo-Suganoya commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

MkModal で、表示中のモーダルのうち最前面(z-index が最大)のものだけが ESC を受け取るようにした。

Why

Fix #14388

投稿フォームに URL を貼ったときの
「引用として添付しますか?」ダイアログで ESC を押すと、
ダイアログだけを閉じるつもりが、下の投稿フォームまで閉じてしまうため。

原因は MkModal が v-hotkey.global で
ESC を document の keydown として拾っていること。

モーダルが重なっていると、開いているモーダル全部に ESC が届く。

投稿フォームだけで対処せず MkModal 側で直したので、
ほかの重なったモーダル(ポップアップメニューなど)でも ESC で閉じるのは最前面だけになる。

Additional info (optional)

ローカル環境で確認した。

引用ダイアログで ESC を 1 回押す

Before After
pr5-before-2-after-esc pr5-after-2-after-esc
投稿フォームごと閉じる ダイアログだけ閉じ、投稿フォームは残る

もう 1 回 ESC を押すと投稿フォームが閉じる(単独のモーダルの動きは変わらない)。

そのほかの確認

ESC を押す前(引用ダイアログ表示中)

Before After
pr5-before-1-dialog pr5-after-1-dialog

公開範囲メニュー(ポップアップ)で ESC を 1 回押す

Before After
メニュー表示中 pr5-before-3-menu pr5-after-3-menu
ESC を押した後 pr5-before-4-menu-after-esc pr5-after-4-menu-after-esc
投稿フォームごと閉じる メニューだけ閉じる

Checklist

  • Read the contribution guide
  • Test working in a local environment
  • (If needed) Add story of storybook
  • (If needed) Update CHANGELOG.md
  • (If possible) Add tests

🤖 Generated with Claude Code

MkModal は ESC を document で拾っているため、確認ダイアログなどを重ねて
表示していると下のモーダル(投稿フォームなど)にも ESC が届いていた。
表示中のモーダルのうち最前面のものだけが ESC を受け取るようにした。

Fix misskey-dev#14388

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyH5iDXHDUwSpDhjuPGC9W
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VyH5iDXHDUwSpDhjuPGC9W
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between c01c162 and a84ba46.

Files ignored due to path filters: 1
  • CHANGELOG.md is excluded by !CHANGELOG.md
Files selected for processing: 1
  • packages/frontend/src/components/MkModal.vue

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.

Walkthrough

表示中モーダルのz-indexを追跡します。ESCキーを押したとき、最も大きいz-indexを持つモーダルだけがescイベントを発火します。非表示時とアンマウント時には、z-indexを追跡対象から削除します。

Changes: モーダルのESCキー制御

Layer / File(s) Summary
表示状態の追跡とESCイベント制御
packages/frontend/src/components/MkModal.vue
表示中モーダルのz-indexをSetで管理します。非表示時とアンマウント時に登録を解除します。ESCキーでは、登録中の最大z-indexを持つモーダルだけがescイベントを発火します。

Priority: Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested labels: packages/frontend

Suggested reviewers: syuilo

Merge Risk: Minimal · up to a84ba

No actionable merge-blocking risk was identified in the modal ESC handling change.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed タイトルは、重なったモーダルでESCを押すと下のモーダルまで閉じる問題の修正を具体的に示しています。主な変更内容を正確に表しています。
Linked Issues check Passed [#14388] 要件: 「引用として添付しますか?」ダイアログで ESC を押したとき、ダイアログだけを閉じ、投稿フォームは閉じないこと。

実装: PR は MkModal.vue に、表示中モーダルの z-index を登録する Set を追加します。ESC は、登録された z-index の最大値と一致するモーダルだけが esc イベントを発火します。モーダルは表示時に Set へ…
Out of Scope Changes check Passed 変更は MkModal.vue の ESC 処理に限定されます。ESC の振る舞いは共通コンポーネントで決まるため、変更は全モーダルに及びます。ただし、これは #14388 の原因箇所への修正です。全モーダルに及ぶことは、設計上の影響であり、無関係な変更ではありません。

作者の説明では、公開範囲メニューなどのポップアップでも最前面のものだけが ESC で閉じるとされています。これは同じ仕組み…
Description check Passed PRの説明はテンプレートの必須項目である「What」「Why」「Additional info」「Checklist」を含んでいます。変更内容、修正理由、再現状況、確認結果、未実施のテストを具体的に記載しています。
Full details: Linked Issues check

Explanation

[#14388] 要件: 「引用として添付しますか?」ダイアログで ESC を押したとき、ダイアログだけを閉じ、投稿フォームは閉じないこと。

実装: PR は MkModal.vue に、表示中モーダルの z-index を登録する Set を追加します。ESC は、登録された z-index の最大値と一致するモーダルだけが esc イベントを発火します。モーダルは表示時に Set へ登録され、非表示時とアンマウント時に削除されます。これは根本原因(v-hotkey.global が document の keydown として ESC を拾うこと)への対処です。

確認結果: 作者はローカル環境で動作を確認したと報告しています。Before/After のスクリーンショットでは、1 回目の ESC でダイアログだけが閉じ、投稿フォームが残ります。2 回目の ESC で投稿フォームが閉じます。

確認できなかった点:

  • 判断は PR 概要と作者の説明に基づきます。差分の全文は確認していません。
  • 同じ z-index 値を持つモーダルが複数ある場合、それらすべてが ESC を受け取る可能性があります。また、そのうち 1 つが閉じたときに値が Set から削除されると、残りのモーダルが ESC を受け取れなくなる可能性があります。(推測)作者の確認結果から、引用ダイアログと投稿フォームは異なる z-index で表示されていると推定します。(推測)
  • テストは追加されていません。ただし、リンク issue はテストを要求していないため、本判定には影響しません。

判定: 要件の振る舞いは、提示された証拠から満たされると判断します。

Full details: Out of Scope Changes check

Explanation

変更は MkModal.vue の ESC 処理に限定されます。ESC の振る舞いは共通コンポーネントで決まるため、変更は全モーダルに及びます。ただし、これは #14388 の原因箇所への修正です。全モーダルに及ぶことは、設計上の影響であり、無関係な変更ではありません。

作者の説明では、公開範囲メニューなどのポップアップでも最前面のものだけが ESC で閉じるとされています。これは同じ仕組みによる結果です。独立した機能追加は確認されません。

テストと CHANGELOG の更新は PR チェックリストに記載されています。CHANGELOG.md は ignored_files に含まれるため、評価対象外です。テストは、リンク issue の要件ではありません。

判定: 無関係な変更は確認されませんでした。全モーダルへの影響は設計上の結果として留意してください。

Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

モーダルの順を記録して
ESCの行き先をひとつに
表示中ならSetに加え
閉じたら記録を外します
最前面だけが応答します

Comment @coderabbitai help to get the list of available commands.

@Syogo-Suganoya Syogo-Suganoya left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変更箇所の意図メモです。


Generated by Claude Code

Comment on lines +44 to +47
<script lang="ts">
// 表示中のモーダルの z-index。ESC は最前面のモーダルだけが受け取るようにするために使う
const visibleModalZIndexes = new Set<number>();
</script>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

全 MkModal インスタンスで共有する状態なので、
<script setup> ではなく通常の <script> ブロック(モジュールスコープ)に置いています。

「最前面」の判定には z-index を使っています。

os.claimZIndex() は呼ぶたびに +100 した値を返し、
priority(low/middle/high)ごとに帯が分かれているので、
z-index 最大=画面上で一番上のモーダル、と一致します。


Generated by Claude Code

Comment on lines +173 to +177
callback: () => {
// keydown は document で拾っているため、下に重なっているモーダルにも届いてしまう
if (zIndex !== Math.max(...visibleModalZIndexes)) return;
emit('esc');
},

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v-hotkey.global は document に keydown リスナーを登録するので、
同じ要素上のリスナーは stopPropagation() では止まりません
(登録順=下のモーダルから順に呼ばれる)。

そのため各モーダルが「自分が最前面か」を自分で判定して、
違えば何もしないようにしました。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stopImmediatePropagation() で止める案も考えましたが、
登録順が下→上なので上のモーダルが先に処理できず、使えませんでした。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stopImmediatePropagation() で止める案も考えましたが、
登録順が下→上なので上のモーダルが先に処理できず、使えませんでした。

Comment on lines 340 to 351
watch([showing, () => props.manualShowing], ([showing, manualShowing]) => {
if (manualShowing === true || (manualShowing == null && showing === true)) {
visibleModalZIndexes.add(zIndex);
if (modalRootEl.value != null) {
const { release } = focusTrap(modalRootEl.value, props.hasInteractionWithOtherFocusTrappedEls);
releaseFocusTrap = release;
modalRootEl.value.focus();
}
} else {
visibleModalZIndexes.delete(zIndex);
releaseFocusTrap?.();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

既存の「表示/非表示」を判定している watch(focusTrap の付け外しと同じ場所)で
登録・解除しています。

manualShowing で一時的に隠しているモーダル(MkPopupMenu の
子メニュー表示中、MkEmojiPickerDialog の非表示時)も
「表示中」から外れるので、
隠れているモーダルが ESC に反応することはなくなります。

close() で showing が false になった時点で外れるため、
閉じるアニメーション中のモーダルは最前面扱いされず、次の ESC は下のモーダルに届きます。


Generated by Claude Code

});
onUnmounted(() => {
visibleModalZIndexes.delete(zIndex);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

閉じアニメーションを経ずにアンマウントされた場合(親ごと消えた等)、
z-index が Set に残り続けると、
以降どのモーダルも最前面と判定されず ESC が効かなくなるので、
onUnmounted でも必ず外しています。


Generated by Claude Code

@github-project-automation github-project-automation Bot moved this from Todo to Approved in [実験中] 管理用 Oct 29, 2026
@codecov

codecov Bot commented Oct 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 14.28571% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 12.79%. Comparing base (d9b1fc5) to head (a84ba46).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
packages/frontend/src/components/MkModal.vue 14.28% 5 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           develop   #18066       +/-   ##
============================================
- Coverage    26.73%   12.79%   -13.94%     
============================================
  Files         1162      227      -935     
  Lines        39864    11135    -28729     
  Branches     10977     3686     -7291     
============================================
- Hits         10658     1425     -9233     
+ Misses       23432     7631    -15801     
+ Partials      5774     2079     -3695     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

🖥 Frontend Diagnostics Report

(No significant changes)

View details

Requests by resource type
Type Requests Encoded bytes
Base Head Δ Base Head Δ
Document 2 2 0 33 KB 33 KB 0 B
Script 148 148 0 2.1 MB 2.1 MB $\color{orange}{\text{+116 B}}$
Stylesheet 58 58 0 284 KB 284 KB 0 B
Fetch 22 22 0 73 KB 73 KB 0 B
Image 11 11 0 254 KB 254 KB 0 B
Font 2 2 0 118 KB 118 KB 0 B
Other 9 9 0 421 KB 421 KB $\color{green}{\text{-2 B}}$
V8 heap snapshot statistics
Metric @ Base @ Head Δ MAD
$\color{gray}{\rule{8pt}{8pt}}$ Total 11 MB
± 5.2 KB
11 MB
± 2.1 KB
$\text{-16 KB}$
$\text{-0.1\%}$
5.6 KB
$\color{orange}{\rule{8pt}{8pt}}$ Code 2.9 MB 2.8 MB $\text{-12 KB}$ 3.1 KB
$\color{red}{\rule{8pt}{8pt}}$ Strings 1.3 MB 1.3 MB $\text{-356 B}$ 232 B
$\color{cyan}{\rule{8pt}{8pt}}$ JS arrays 119 KB 119 KB $\text{-92 B}$ 36 B
$\color{green}{\rule{8pt}{8pt}}$ Typed arrays 0 B 0 B 0 B 0 B
$\color{yellow}{\rule{8pt}{8pt}}$ System objects 0 B 0 B 0 B 0 B
$\color{violet}{\rule{8pt}{8pt}}$ Other JS objs 2.1 MB 2.1 MB $\text{-156 B}$ 283 B
$\color{pink}{\rule{8pt}{8pt}}$ Other non-JS objs 5 MB 5 MB $\text{-10 KB}$ 14 KB

Download representative heap snapshot: base / head

📦 Bundle Stats

Chunk size diff (0 updated, 0 added, 0 removed)
Chunk Base Head Δ Δ (%)
(total) 5.9 MB 5.9 MB $\text{+58 B}$ $\text{+0\%}$
(other generated chunks) 2 MB 2 MB $\text{+58 B}$ $\text{+0\%}$
Startup chunk size (0 updated, 0 added, 0 removed)
Chunk Base Head Δ Δ (%)
(total) 1 MB 1 MB $\text{+58 B}$ $\text{+0\%}$
(other generated chunks) 759 KB 759 KB $\text{+58 B}$ $\text{+0\%}$
(other) 284 KB 284 KB 0 B 0%

Startup chunks are the Vite entry for src/_boot_.ts and its static imports.

Bundles Modules Entries Imports Size
Static Dynamic Rendered Gzip Brotli
Base 469 2,857 21 10,347 333 10 MB 2.8 MB 2.4 MB
Head 469 2,857 21 10,347 333 10 MB 2.8 MB 2.4 MB
Δ 0 0 0 0 0 $\text{+235 B}$ $\text{+63 B}$ $\text{+35 B}$
Δ (%) 0% 0% 0% 0% 0% $\text{+0\%}$ $\text{+0\%}$ $\text{+0\%}$

Open treemap HTML

@kakkokari-gtyih

Copy link
Copy Markdown
Member

@syuilo z-indexを見る形で問題無さそうかしら

@syuilo

syuilo commented Oct 30, 2026

Copy link
Copy Markdown
Member

うーむ

@syuilo

syuilo commented Oct 30, 2026

Copy link
Copy Markdown
Member

z-indexに依存しない方が良さそうではありますね

@syuilo

syuilo commented Oct 30, 2026

Copy link
Copy Markdown
Member

根本的にはhotkey側をどうにかした方が良い可能性を考えています

@Syogo-Suganoya

Copy link
Copy Markdown
Contributor Author

hotkey 側で直す版を #18090 として作ってみました。

v-hotkey.global の登録をまとめて管理し、document の keydown リスナーを 1 つにしています。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

packages/frontend Client side specific issue/PR

Projects

Status: Approved

4 participants