forked from Mirrors/gomuks
web/composer: refactor autocompleter to be generic
This commit is contained in:
parent
d31b0905ed
commit
0696a43208
2 changed files with 34 additions and 14 deletions
|
@ -13,9 +13,9 @@
|
|||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
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<T> extends AutocompleterProps {
|
||||
items: T[]
|
||||
getText: (item: T) => string
|
||||
getKey: (item: T) => string
|
||||
render: (item: T) => JSX.Element
|
||||
}
|
||||
|
||||
function useAutocompleter<T>({
|
||||
params, state, setState, setAutocomplete,
|
||||
items, getText, getKey, render,
|
||||
}: InnerAutocompleterProps<T>) {
|
||||
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 <div className="autocompletions">
|
||||
{emojis.map((emoji, i) => <div
|
||||
{items.map((item, i) => <div
|
||||
onClick={onClick}
|
||||
data-index={i}
|
||||
className={`autocompletion-item ${selected === i ? "selected" : ""}`}
|
||||
key={emoji.u}
|
||||
>{emoji.u} :{emoji.n}:</div>)}
|
||||
key={getKey(item)}
|
||||
>{render(item)}</div>)}
|
||||
</div>
|
||||
}
|
||||
|
||||
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 <div className="autocompletions">
|
||||
Autocomplete {params.type} {params.query}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue