From 1b4cdff9dfb1c15d4486e2c137e86ed093dc60ce Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Sat, 15 Jun 2024 22:33:05 -1000 Subject: [PATCH] feat: :sparkles: Add mentions autocompleter --- components/composer/composer.vue | 21 ++++- components/composer/emoji-suggestbox.vue | 2 + components/composer/mention-suggestbox.vue | 98 ++++++++++++++++++++++ utils/validators.ts | 6 ++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 components/composer/mention-suggestbox.vue diff --git a/components/composer/composer.vue b/components/composer/composer.vue index 40c9f45..199d1d9 100644 --- a/components/composer/composer.vue +++ b/components/composer/composer.vue @@ -15,8 +15,10 @@ aria-live="polite"> {{ remainingCharacters }} - + +
@@ -82,6 +84,10 @@ const currentlyBeingTypedEmoji = computed(() => { const match = content.value?.match(partiallyTypedEmojiValidator); return match ? match.at(-1)?.replace(":", "") ?? "" : null; }); +const currentlyBeingTypedMention = computed(() => { + const match = content.value?.match(partiallyTypedMentionValidator); + return match ? match.at(-1)?.replace("@", "") ?? "" : null; +}); const openFilePicker = () => { uploader.value?.openFilePicker(); @@ -98,6 +104,17 @@ const autocompleteEmoji = (emoji: string) => { ); }; +const autocompleteMention = (mention: string) => { + // Replace the end of the string with the mention + content.value = content.value?.replace( + createRegExp( + exactly("@"), + exactly(currentlyBeingTypedMention.value ?? "").notBefore(char), + ), + `@${mention} `, + ); +}; + const files = ref< { id: string; diff --git a/components/composer/emoji-suggestbox.vue b/components/composer/emoji-suggestbox.vue index db52d83..7d5b0e0 100644 --- a/components/composer/emoji-suggestbox.vue +++ b/components/composer/emoji-suggestbox.vue @@ -14,10 +14,12 @@ import { distance } from "fastest-levenshtein"; import type { CustomEmoji } from "~/composables/Identities"; const props = defineProps<{ currentlyTypingEmoji: string | null; + textarea: HTMLTextAreaElement | undefined; }>(); const emojiRefs = ref([]); const { Tab, ArrowRight, ArrowLeft, Enter } = useMagicKeys({ + target: props.textarea, passive: false, onEventFired(e) { if ( diff --git a/components/composer/mention-suggestbox.vue b/components/composer/mention-suggestbox.vue new file mode 100644 index 0000000..b694494 --- /dev/null +++ b/components/composer/mention-suggestbox.vue @@ -0,0 +1,98 @@ + + + \ No newline at end of file diff --git a/utils/validators.ts b/utils/validators.ts index faec2d2..0a9da6c 100644 --- a/utils/validators.ts +++ b/utils/validators.ts @@ -23,3 +23,9 @@ export const partiallyTypedEmojiValidator = createRegExp( ), [caseInsensitive, multiline], ); + +export const partiallyTypedMentionValidator = createRegExp( + exactly("@"), + oneOrMore(letter.or(digit).or(exactly("_"))).notBefore(char), + [caseInsensitive, multiline], +);