diff --git a/web/src/ui/composer/Autocompleter.tsx b/web/src/ui/composer/Autocompleter.tsx index 2747e89..a702cc4 100644 --- a/web/src/ui/composer/Autocompleter.tsx +++ b/web/src/ui/composer/Autocompleter.tsx @@ -13,9 +13,9 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { useEffect } from "react" +import { JSX, useEffect } from "react" import { RoomStateStore } from "@/api/statestore" -import { useFilteredEmojis } from "@/util/emoji" +import { Emoji, useFilteredEmojis } from "@/util/emoji" import useEvent from "@/util/useEvent.ts" import type { ComposerState } from "./MessageComposer.tsx" import "./Autocompleter.css" @@ -39,20 +39,29 @@ export interface AutocompleterProps { const positiveMod = (val: number, div: number) => (val % div + div) % div -export const EmojiAutocompleter = ({ params, state, setState, setAutocomplete }: AutocompleterProps) => { - const emojis = useFilteredEmojis((params.frozenQuery ?? params.query).slice(1), true) +interface InnerAutocompleterProps extends AutocompleterProps { + items: T[] + getText: (item: T) => string + getKey: (item: T) => string + render: (item: T) => JSX.Element +} + +function useAutocompleter({ + params, state, setState, setAutocomplete, + items, getText, getKey, render, +}: InnerAutocompleterProps) { const onSelect = useEvent((index: number) => { - if (emojis.length === 0) { + if (items.length === 0) { return } - index = positiveMod(index, emojis.length) - const emoji = emojis[index] + index = positiveMod(index, items.length) + const replacementText = getText(items[index]) setState({ - text: state.text.slice(0, params.startPos) + emoji.u + state.text.slice(params.endPos), + text: state.text.slice(0, params.startPos) + replacementText + state.text.slice(params.endPos), }) setAutocomplete({ ...params, - endPos: params.startPos + emoji.u.length, + endPos: params.startPos + replacementText.length, frozenQuery: params.frozenQuery ?? params.query, }) document.querySelector(`div.autocompletion-item[data-index='${index}']`)?.scrollIntoView({ block: "nearest" }) @@ -69,17 +78,28 @@ export const EmojiAutocompleter = ({ params, state, setState, setAutocomplete }: onSelect(params.selected) } }, [onSelect, params.selected]) - const selected = params.selected !== undefined ? positiveMod(params.selected, emojis.length) : -1 + const selected = params.selected !== undefined ? positiveMod(params.selected, items.length) : -1 return
- {emojis.map((emoji, i) =>
{emoji.u} :{emoji.n}:
)} + key={getKey(item)} + >{render(item)}
)}
} +const emojiFuncs = { + getText: (emoji: Emoji) => emoji.u, + getKey: (emoji: Emoji) => emoji.u, + render: (emoji: Emoji) => <>{emoji.u} :{emoji.n}:, +} + +export const EmojiAutocompleter = ({ params, ...rest }: AutocompleterProps) => { + const items = useFilteredEmojis((params.frozenQuery ?? params.query).slice(1), true) + return useAutocompleter({ params, ...rest, items, ...emojiFuncs }) +} + export const UserAutocompleter = ({ params }: AutocompleterProps) => { return
Autocomplete {params.type} {params.query} diff --git a/web/src/util/emoji/index.ts b/web/src/util/emoji/index.ts index a209091..13468b5 100644 --- a/web/src/util/emoji/index.ts +++ b/web/src/util/emoji/index.ts @@ -16,7 +16,7 @@ import { useRef } from "react" import data from "./data.json" -interface Emoji { +export interface Emoji { u: string // Unicode codepoint c: number // Category number t: string // Emoji title