import React, { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo } from 'react'; import { Editor } from 'slate'; import { Box, config, MenuItem, Text } from 'folds'; import { Room } from 'matrix-js-sdk'; import { Command, useCommands } from '../../hooks/useCommands'; import { AutocompleteMenu, AutocompleteQuery, createCommandElement, moveCursor, replaceWithElement, } from '../../components/editor'; import { UseAsyncSearchOptions, useAsyncSearch } from '../../hooks/useAsyncSearch'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useKeyDown } from '../../hooks/useKeyDown'; import { onTabPress } from '../../utils/keyboard'; type CommandAutoCompleteHandler = (commandName: string) => void; type CommandAutocompleteProps = { room: Room; editor: Editor; query: AutocompleteQuery; requestClose: () => void; }; const SEARCH_OPTIONS: UseAsyncSearchOptions = { matchOptions: { contain: true, }, }; export function CommandAutocomplete({ room, editor, query, requestClose, }: CommandAutocompleteProps) { const mx = useMatrixClient(); const commands = useCommands(mx, room); const commandNames = useMemo(() => Object.keys(commands) as Command[], [commands]); const [result, search, resetSearch] = useAsyncSearch( commandNames, useCallback((commandName: string) => commandName, []), SEARCH_OPTIONS ); const autoCompleteNames = result ? result.items : commandNames; useEffect(() => { if (query.text) search(query.text); else resetSearch(); }, [query.text, search, resetSearch]); const handleAutocomplete: CommandAutoCompleteHandler = (commandName) => { const cmdEl = createCommandElement(commandName); replaceWithElement(editor, query.range, cmdEl); moveCursor(editor, true); requestClose(); }; useKeyDown(window, (evt: KeyboardEvent) => { onTabPress(evt, () => { if (autoCompleteNames.length === 0) { return; } const cmdName = autoCompleteNames[0]; handleAutocomplete(cmdName); }); }); return autoCompleteNames.length === 0 ? null : ( Commands } requestClose={requestClose} > {autoCompleteNames.map((commandName) => ( ) => onTabPress(evt, () => handleAutocomplete(commandName)) } onClick={() => handleAutocomplete(commandName)} > {`/${commandName}`} {commands[commandName].description} ))} ); }