Enhance(frontend): MFMの属性にオートコンプリートが利用できるように (#12803)

* MFMのパラメータでオートコンプリートできるように

* tweak conditions & refactor

* ファイル末尾の改行忘れ

* remove console.log & refactor

* 型付けに敗北

* fix

* update CHANGELOG.md

* tweak conditions

* CHANGELOGの様式ミス

* CHANGELOGを書く場所を間違えていたので修正

* move changelog

* move changelog

* typeof MFM_TAGS[number]

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

* $[border.noclip ]対応

* Update const.ts

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
1Step621
2024-01-19 18:50:26 +09:00
committed by GitHub
parent b17eb8e537
commit 678dba9245
4 changed files with 75 additions and 8 deletions

View File

@@ -8,13 +8,13 @@ import getCaretCoordinates from 'textarea-caret';
import { toASCII } from 'punycode/';
import { popup } from '@/os.js';
export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag';
export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam';
export class Autocomplete {
private suggestion: {
x: Ref<number>;
y: Ref<number>;
q: Ref<string | null>;
q: Ref<any>;
close: () => void;
} | null;
private textarea: HTMLInputElement | HTMLTextAreaElement;
@@ -49,7 +49,7 @@ export class Autocomplete {
this.textarea = textarea;
this.textRef = textRef;
this.opening = false;
this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag'];
this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag', 'mfmParam'];
this.attach();
}
@@ -80,6 +80,7 @@ export class Autocomplete {
const hashtagIndex = text.lastIndexOf('#');
const emojiIndex = text.lastIndexOf(':');
const mfmTagIndex = text.lastIndexOf('$');
const mfmParamIndex = text.lastIndexOf('.');
const max = Math.max(
mentionIndex,
@@ -94,7 +95,8 @@ export class Autocomplete {
const isMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
const isMfmTag = mfmTagIndex !== -1;
const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
let opened = false;
@@ -134,6 +136,17 @@ export class Autocomplete {
}
}
if (isMfmParam && !opened && this.onlyType.includes('mfmParam')) {
const mfmParam = text.substring(mfmParamIndex + 1);
if (!mfmParam.includes(' ')) {
this.open('mfmParam', {
tag: text.substring(mfmTagIndex + 2, mfmParamIndex),
params: mfmParam.split(','),
});
opened = true;
}
}
if (!opened) {
this.close();
}
@@ -142,7 +155,7 @@ export class Autocomplete {
/**
* サジェストを提示します。
*/
private async open(type: string, q: string | null) {
private async open(type: string, q: any) {
if (type !== this.currentType) {
this.close();
}
@@ -280,6 +293,22 @@ export class Autocomplete {
const pos = trimmedBefore.length + (value.length + 3);
this.textarea.setSelectionRange(pos, pos);
});
} else if (type === 'mfmParam') {
const source = this.text;
const before = source.substring(0, caret);
const trimmedBefore = before.substring(0, before.lastIndexOf('.'));
const after = source.substring(caret);
// 挿入
this.text = `${trimmedBefore}.${value}${after}`;
// キャレットを戻す
nextTick(() => {
this.textarea.focus();
const pos = trimmedBefore.length + (value.length + 1);
this.textarea.setSelectionRange(pos, pos);
});
}
}
}