diff options
| author | Hazelnoot <acomputerdog@gmail.com> | 2025-04-24 14:23:45 -0400 |
|---|---|---|
| committer | Hazelnoot <acomputerdog@gmail.com> | 2025-04-24 14:23:45 -0400 |
| commit | a4dd19fdd427a5adc8fa80871d1c742aa9708730 (patch) | |
| tree | 3983a0e772922042043026b4af168e8dd3525fb2 /packages/frontend/src/utility/autocomplete.ts | |
| parent | Merge branch 'develop' into merge/2025-03-24 (diff) | |
| parent | enhance(backend): DB note (userId) インデクス -> (userId, id) 複合イ... (diff) | |
| download | sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.tar.gz sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.tar.bz2 sharkey-a4dd19fdd427a5adc8fa80871d1c742aa9708730.zip | |
merge upstream again
Diffstat (limited to 'packages/frontend/src/utility/autocomplete.ts')
| -rw-r--r-- | packages/frontend/src/utility/autocomplete.ts | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/packages/frontend/src/utility/autocomplete.ts b/packages/frontend/src/utility/autocomplete.ts index 0276cc3c20..885c57caa9 100644 --- a/packages/frontend/src/utility/autocomplete.ts +++ b/packages/frontend/src/utility/autocomplete.ts @@ -7,6 +7,7 @@ import { nextTick, ref, defineAsyncComponent } from 'vue'; import getCaretCoordinates from 'textarea-caret'; import { toASCII } from 'punycode.js'; import type { Ref } from 'vue'; +import type { CompleteInfo } from '@/components/MkAutocomplete.vue'; import { popup } from '@/os.js'; export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag' | 'mfmParam'; @@ -19,7 +20,7 @@ export class Autocomplete { close: () => void; } | null; private textarea: HTMLInputElement | HTMLTextAreaElement; - private currentType: string; + private currentType: keyof CompleteInfo | undefined; private textRef: Ref<string | number | null>; private opening: boolean; private onlyType: SuggestionType[]; @@ -74,7 +75,7 @@ export class Autocomplete { * テキスト入力時 */ private onInput() { - const caretPos = this.textarea.selectionStart; + const caretPos = Number(this.textarea.selectionStart); const text = this.text.substring(0, caretPos).split('\n').pop()!; const mentionIndex = text.lastIndexOf('@'); @@ -101,6 +102,8 @@ export class Autocomplete { const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam.includes(' '); const isMfmTag = mfmTagIndex !== -1 && !isMfmParam; const isEmoji = emojiIndex !== -1 && text.split(/:[\p{Letter}\p{Number}\p{Mark}_+-]+:/u).pop()!.includes(':'); + // :ok:などを🆗にするたいおぷ + const isEmojiCompleteToUnicode = !isEmoji && emojiIndex === text.length - 1; let opened = false; @@ -137,6 +140,14 @@ export class Autocomplete { } } + if (isEmojiCompleteToUnicode && !opened && this.onlyType.includes('emoji')) { + const emoji = text.substring(text.lastIndexOf(':', text.length - 2) + 1, text.length - 1); + if (!emoji.includes(' ')) { + this.open('emojiComplete', emoji); + opened = true; + } + } + if (isMfmTag && !opened && this.onlyType.includes('mfmTag')) { const mfmTag = text.substring(mfmTagIndex + 1); if (!mfmTag.includes(' ')) { @@ -164,7 +175,7 @@ export class Autocomplete { /** * サジェストを提示します。 */ - private async open(type: string, q: any) { + private async open<T extends keyof CompleteInfo>(type: T, q: CompleteInfo[T]['query']) { if (type !== this.currentType) { this.close(); } @@ -231,10 +242,10 @@ export class Autocomplete { /** * オートコンプリートする */ - private complete({ type, value }) { + private complete<T extends keyof CompleteInfo>({ type, value }: { type: T; value: CompleteInfo[T]['payload'] }) { this.close(); - const caret = this.textarea.selectionStart; + const caret = Number(this.textarea.selectionStart); if (type === 'user') { const source = this.text; @@ -286,6 +297,22 @@ export class Autocomplete { const pos = trimmedBefore.length + value.length; this.textarea.setSelectionRange(pos, pos); }); + } else if (type === 'emojiComplete') { + const source = this.text; + + const before = source.substring(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf(':', before.length - 2)); + const after = source.substring(caret); + + // 挿入 + this.text = trimmedBefore + value + after; + + // キャレットを戻す + nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + value.length; + this.textarea.setSelectionRange(pos, pos); + }); } else if (type === 'mfmTag') { const source = this.text; |