Skip to content

Commit ece4efc

Browse files
fix(frontend): mfmFunctionPickerを使用して挿入する際のハンドリングを改善 (#17018)
* fix(frontend): mfmFunctionPickerを使用して絵文字を挿入する際のハンドリングを改善 * fix * Update MkPostForm.vue * Update Changelog --------- Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
1 parent cd973b2 commit ece4efc

File tree

4 files changed

+40
-41
lines changed

4 files changed

+40
-41
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Fix: ドライブクリーナーでファイルを削除しても画面に反映されない問題を修正 #16061
1818
- Fix: 非ログイン時にログインを求めるダイアログが表示された後にダイアログのぼかしが解除されず操作不能になることがある問題を修正
1919
- Fix: ドライブのソートが「登録日(昇順)」の場合に正しく動作しない問題を修正
20+
- Fix: 高度なMFMのピッカーを使用する際の挙動を改善
2021
- Fix: 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正
2122
- Fix: ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正
2223
- Fix: 2月29日を誕生日に設定している場合、閏年以外は3月1日を誕生日として扱うように修正

packages/frontend/src/components/MkPostForm.vue

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,17 +1166,41 @@ async function insertEmoji(ev: MouseEvent) {
11661166
},
11671167
() => {
11681168
textAreaReadOnly.value = false;
1169-
nextTick(() => focus());
1169+
nextTick(() => {
1170+
if (textareaEl.value) {
1171+
textareaEl.value.focus();
1172+
textareaEl.value.setSelectionRange(pos, posEnd);
1173+
}
1174+
});
11701175
},
11711176
);
11721177
}
11731178
11741179
async function insertMfmFunction(ev: MouseEvent) {
11751180
if (textareaEl.value == null) return;
1181+
let pos = textareaEl.value.selectionStart ?? 0;
1182+
let posEnd = textareaEl.value.selectionEnd ?? text.value.length;
11761183
mfmFunctionPicker(
11771184
ev.currentTarget ?? ev.target,
1178-
textareaEl.value,
1179-
text,
1185+
(tag) => {
1186+
if (pos === posEnd) {
1187+
text.value = `${text.value.substring(0, pos)}$[${tag} ]${text.value.substring(pos)}`;
1188+
pos += tag.length + 3;
1189+
posEnd = pos;
1190+
} else {
1191+
text.value = `${text.value.substring(0, pos)}$[${tag} ${text.value.substring(pos, posEnd)}]${text.value.substring(posEnd)}`;
1192+
pos += tag.length + 3;
1193+
posEnd = pos;
1194+
}
1195+
},
1196+
() => {
1197+
nextTick(() => {
1198+
if (textareaEl.value) {
1199+
textareaEl.value.focus();
1200+
textareaEl.value.setSelectionRange(pos, posEnd);
1201+
}
1202+
});
1203+
},
11801204
);
11811205
}
11821206

packages/frontend/src/os.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen
654654
align?: string;
655655
width?: number;
656656
onClosing?: () => void;
657+
onClosed?: () => void;
657658
}): Promise<void> {
658659
if (!(anchorElement instanceof HTMLElement)) {
659660
anchorElement = null;
@@ -672,6 +673,7 @@ export function popupMenu(items: (MenuItem | null)[], anchorElement?: HTMLElemen
672673
resolve();
673674
dispose();
674675
returnFocusTo = null;
676+
options?.onClosed?.();
675677
},
676678
closing: () => {
677679
options?.onClosing?.();

packages/frontend/src/utility/mfm-function-picker.ts

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,27 @@
33
* SPDX-License-Identifier: AGPL-3.0-only
44
*/
55

6-
import { nextTick } from 'vue';
76
import { MFM_TAGS } from '@@/js/const.js';
8-
import type { Ref } from 'vue';
9-
import type { MenuItem } from '@/types/menu.js';
107
import * as os from '@/os.js';
118
import { i18n } from '@/i18n.js';
129

1310
/**
1411
* MFMの装飾のリストを表示する
1512
*/
16-
export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
13+
export function mfmFunctionPicker(anchorElement: HTMLElement | EventTarget | null, onChosen: (tag: string) => void, onClosed?: () => void) {
1714
os.popupMenu([{
1815
text: i18n.ts.addMfmFunction,
1916
type: 'label',
20-
}, ...getFunctionList(textArea, textRef)], anchorElement);
21-
}
22-
23-
function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>): MenuItem[] {
24-
return MFM_TAGS.map(tag => ({
17+
}, ...MFM_TAGS.map(tag => ({
2518
text: tag,
2619
icon: 'ti ti-icons',
27-
action: () => add(textArea, textRef, tag),
28-
}));
29-
}
30-
31-
function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) {
32-
const caretStart: number = textArea.selectionStart as number;
33-
const caretEnd: number = textArea.selectionEnd as number;
34-
35-
MFM_TAGS.forEach(tag => {
36-
if (type === tag) {
37-
if (caretStart === caretEnd) {
38-
// 単純にFunctionを追加
39-
const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ]${textRef.value.substring(caretEnd)}`;
40-
textRef.value = trimmedText;
41-
} else {
42-
// 選択範囲を囲むようにFunctionを追加
43-
const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ${textRef.value.substring(caretStart, caretEnd)}]${textRef.value.substring(caretEnd)}`;
44-
textRef.value = trimmedText;
45-
}
46-
}
47-
});
48-
49-
const nextCaretStart: number = caretStart + 3 + type.length;
50-
const nextCaretEnd: number = caretEnd + 3 + type.length;
51-
52-
// キャレットを戻す
53-
nextTick(() => {
54-
textArea.focus();
55-
textArea.setSelectionRange(nextCaretStart, nextCaretEnd);
20+
action: () => {
21+
onChosen(tag);
22+
},
23+
}))], anchorElement, {
24+
onClosed: () => {
25+
if (onClosed) onClosed();
26+
},
5627
});
5728
}
29+

0 commit comments

Comments
 (0)