diff options
| author | misskey-release-bot[bot] <157398866+misskey-release-bot[bot]@users.noreply.github.com> | 2026-03-05 10:56:50 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-05 10:56:50 +0000 |
| commit | fe3dd8edb5f30104cd0a7ed755eb254feda2922d (patch) | |
| tree | af6cf5fa4ca75302ac2de5db742cead00bc13d21 /packages/frontend/src/utility/autocomplete.ts | |
| parent | Merge pull request #16998 from misskey-dev/develop (diff) | |
| parent | Release: 2026.3.0 (diff) | |
| download | misskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.tar.gz misskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.tar.bz2 misskey-fe3dd8edb5f30104cd0a7ed755eb254feda2922d.zip | |
Merge pull request #17217 from misskey-dev/develop
Release: 2026.3.0
Diffstat (limited to 'packages/frontend/src/utility/autocomplete.ts')
| -rw-r--r-- | packages/frontend/src/utility/autocomplete.ts | 50 |
1 files changed, 30 insertions, 20 deletions
diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts index 82109af1a0..a44bf7c1ae 100644 --- a/packages/frontend/src/utility/autocomplete.ts +++ b/packages/frontend/src/utility/autocomplete.ts @@ -12,6 +12,15 @@ import { popup } from '@/os.js'; export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam'; +type CompleteProps<T extends keyof CompleteInfo> = { + type: T; + value: CompleteInfo[T]['payload']; +}; + +function isCompleteType<T extends keyof CompleteInfo>(expectedType: T, props: CompleteProps<keyof CompleteInfo>): props is CompleteProps<T> { + return props.type === expectedType; +} + export class Autocomplete { private suggestion: { x: Ref<number>; @@ -194,7 +203,7 @@ export class Autocomplete { this.currentType = type; //#region サジェストを表示すべき位置を計算 - const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart); + const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart ?? 0); const rect = this.textarea.getBoundingClientRect(); @@ -213,10 +222,11 @@ export class Autocomplete { const _y = ref(y); const _q = ref(q); - const { dispose } = await popup(defineAsyncComponent(() => import('@/components/MkAutocomplete.vue')), { + const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkAutocomplete.vue')), { textarea: this.textarea, close: this.close, type: type, + //@ts-expect-error popupは今のところジェネリック型のコンポーネントに対応していない q: _q, x: _x, y: _y, @@ -252,19 +262,19 @@ export class Autocomplete { /** * オートコンプリートする */ - private complete<T extends keyof CompleteInfo>({ type, value }: { type: T; value: CompleteInfo[T]['payload'] }) { + private complete<T extends keyof CompleteInfo>(props: CompleteProps<T>) { this.close(); const caret = Number(this.textarea.selectionStart); - if (type === 'user') { + if (isCompleteType('user', props)) { const source = this.text; const before = source.substring(0, caret); const trimmedBefore = before.substring(0, before.lastIndexOf('@')); const after = source.substring(caret); - const acct = value.host === null ? value.username : `${value.username}@${toASCII(value.host)}`; + const acct = props.value.host === null ? props.value.username : `${props.value.username}@${toASCII(props.value.host)}`; // 挿入 this.text = `${trimmedBefore}@${acct} ${after}`; @@ -275,7 +285,7 @@ export class Autocomplete { const pos = trimmedBefore.length + (acct.length + 2); this.textarea.setSelectionRange(pos, pos); }); - } else if (type === 'hashtag') { + } else if (isCompleteType('hashtag', props)) { const source = this.text; const before = source.substring(0, caret); @@ -283,15 +293,15 @@ export class Autocomplete { const after = source.substring(caret); // 挿入 - this.text = `${trimmedBefore}#${value} ${after}`; + this.text = `${trimmedBefore}#${props.value} ${after}`; // キャレットを戻す nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + (value.length + 2); + const pos = trimmedBefore.length + (props.value.length + 2); this.textarea.setSelectionRange(pos, pos); }); - } else if (type === 'emoji') { + } else if (isCompleteType('emoji', props)) { const source = this.text; const before = source.substring(0, caret); @@ -299,15 +309,15 @@ export class Autocomplete { const after = source.substring(caret); // 挿入 - this.text = trimmedBefore + value + after; + this.text = trimmedBefore + props.value + after; // キャレットを戻す nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + value.length; + const pos = trimmedBefore.length + props.value.length; this.textarea.setSelectionRange(pos, pos); }); - } else if (type === 'emojiComplete') { + } else if (isCompleteType('emojiComplete', props)) { const source = this.text; const before = source.substring(0, caret); @@ -315,15 +325,15 @@ export class Autocomplete { const after = source.substring(caret); // 挿入 - this.text = trimmedBefore + value + after; + this.text = trimmedBefore + props.value + after; // キャレットを戻す nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + value.length; + const pos = trimmedBefore.length + props.value.length; this.textarea.setSelectionRange(pos, pos); }); - } else if (type === 'mfmTag') { + } else if (isCompleteType('mfmTag', props)) { const source = this.text; const before = source.substring(0, caret); @@ -331,15 +341,15 @@ export class Autocomplete { const after = source.substring(caret); // 挿入 - this.text = `${trimmedBefore}$[${value} ]${after}`; + this.text = `${trimmedBefore}$[${props.value} ]${after}`; // キャレットを戻す nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + (value.length + 3); + const pos = trimmedBefore.length + (props.value.length + 3); this.textarea.setSelectionRange(pos, pos); }); - } else if (type === 'mfmParam') { + } else if (isCompleteType('mfmParam', props)) { const source = this.text; const before = source.substring(0, caret); @@ -347,12 +357,12 @@ export class Autocomplete { const after = source.substring(caret); // 挿入 - this.text = `${trimmedBefore}.${value}${after}`; + this.text = `${trimmedBefore}.${props.value}${after}`; // キャレットを戻す nextTick(() => { this.textarea.focus(); - const pos = trimmedBefore.length + (value.length + 1); + const pos = trimmedBefore.length + (props.value.length + 1); this.textarea.setSelectionRange(pos, pos); }); } |