diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2026-01-08 21:08:27 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-08 21:08:27 +0900 |
| commit | ece4efcefedc3ef095f8683db868d64645811893 (patch) | |
| tree | a5b5fe0493a714e8e0b6ccaafcf47fa22676d36a | |
| parent | fix(frontend): 2月29日を誕生日に設定している場合、平年は3... (diff) | |
| download | misskey-ece4efcefedc3ef095f8683db868d64645811893.tar.gz misskey-ece4efcefedc3ef095f8683db868d64645811893.tar.bz2 misskey-ece4efcefedc3ef095f8683db868d64645811893.zip | |
fix(frontend): mfmFunctionPickerを使用して挿入する際のハンドリングを改善 (#17018)
* fix(frontend): mfmFunctionPickerを使用して絵文字を挿入する際のハンドリングを改善
* fix
* Update MkPostForm.vue
* Update Changelog
---------
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | packages/frontend/src/components/MkPostForm.vue | 30 | ||||
| -rw-r--r-- | packages/frontend/src/os.ts | 2 | ||||
| -rw-r--r-- | packages/frontend/src/utility/mfm-function-picker.ts | 48 |
4 files changed, 40 insertions, 41 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index db90bb74b7..8cd333372a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061 - Fix: 非ログイン時にログインを求めるダイアログが表示された後にダイアログのぼかしが解除されず操作不能になることがある問題を修正 - Fix: ドライブのソートが「登録日(昇順)」の場合に正しく動作しない問題を修正 +- Fix: 高度なMFMのピッカーを使用する際の挙動を改善 - Fix: 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正 - Fix: ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正 - Fix: 2月29日を誕生日に設定している場合、閏年以外は3月1日を誕生日として扱うように修正 diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 4b027cf105..9734b51927 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1166,17 +1166,41 @@ async function insertEmoji(ev: MouseEvent) { }, () => { textAreaReadOnly.value = false; - nextTick(() => focus()); + nextTick(() => { + if (textareaEl.value) { + textareaEl.value.focus(); + textareaEl.value.setSelectionRange(pos, posEnd); + } + }); }, ); } async function insertMfmFunction(ev: MouseEvent) { if (textareaEl.value == null) return; + let pos = textareaEl.value.selectionStart ?? 0; + let posEnd = textareaEl.value.selectionEnd ?? text.value.length; mfmFunctionPicker( ev.currentTarget ?? ev.target, - textareaEl.value, - text, + (tag) => { + if (pos === posEnd) { + text.value = `${text.value.substring(0, pos)}$[${tag} ]${text.value.substring(pos)}`; + pos += tag.length + 3; + posEnd = pos; + } else { + text.value = `${text.value.substring(0, pos)}$[${tag} ${text.value.substring(pos, posEnd)}]${text.value.substring(posEnd)}`; + pos += tag.length + 3; + posEnd = pos; + } + }, + () => { + nextTick(() => { + if (textareaEl.value) { + textareaEl.value.focus(); + textareaEl.value.setSelectionRange(pos, posEnd); + } + }); + }, ); } diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index c1390e8274..3fb204c2b2 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -654,6 +654,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen align?: string; width?: number; onClosing?: () => void; + onClosed?: () => void; }): Promise<void> { if (!(anchorElement instanceof HTMLElement)) { anchorElement = null; @@ -672,6 +673,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen resolve(); dispose(); returnFocusTo = null; + options?.onClosed?.(); }, closing: () => { options?.onClosing?.(); diff --git a/packages/frontend/src/utility/mfm-function-picker.ts b/packages/frontend/src/utility/mfm-function-picker.ts index 09802d580b..5580435db1 100644 --- a/packages/frontend/src/utility/mfm-function-picker.ts +++ b/packages/frontend/src/utility/mfm-function-picker.ts @@ -3,55 +3,27 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { nextTick } from 'vue'; import { MFM_TAGS } from '@@/js/const.js'; -import type { Ref } from 'vue'; -import type { MenuItem } from '@/types/menu.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; /** * MFMの装飾のリストを表示する */ -export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) { +export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, onChosen: (tag: string) => void, onClosed?: () => void) { os.popupMenu([{ text: i18n.ts.addMfmFunction, type: 'label', - }, ...getFunctionList(textArea, textRef)], anchorElement); -} - -function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>): MenuItem[] { - return MFM_TAGS.map(tag => ({ + }, ...MFM_TAGS.map(tag => ({ text: tag, icon: 'ti ti-icons', - action: () => add(textArea, textRef, tag), - })); -} - -function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) { - const caretStart: number = textArea.selectionStart as number; - const caretEnd: number = textArea.selectionEnd as number; - - MFM_TAGS.forEach(tag => { - if (type === tag) { - if (caretStart === caretEnd) { - // 単純にFunctionを追加 - const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ]${textRef.value.substring(caretEnd)}`; - textRef.value = trimmedText; - } else { - // 選択範囲を囲むようにFunctionを追加 - const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ${textRef.value.substring(caretStart, caretEnd)}]${textRef.value.substring(caretEnd)}`; - textRef.value = trimmedText; - } - } - }); - - const nextCaretStart: number = caretStart + 3 + type.length; - const nextCaretEnd: number = caretEnd + 3 + type.length; - - // キャレットを戻す - nextTick(() => { - textArea.focus(); - textArea.setSelectionRange(nextCaretStart, nextCaretEnd); + action: () => { + onChosen(tag); + }, + }))], anchorElement, { + onClosed: () => { + if (onClosed) onClosed(); + }, }); } + |