mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-15 03:30:29 +03:00
Editor Commands (#1450)
* add commands hook * add commands in editor * add command auto complete menu * add commands in room input * remove old reply code from room input * fix video component css * do not auto focus input on android or ios * fix crash on enable block after selection * fix circular deps in editor * fix autocomplete return focus move editor cursor * remove unwanted keydown from room input * fix emoji alignment in editor * test ipad user agent * refactor isAndroidOrIOS to mobileOrTablet * update slate & slate-react * downgrade slate-react to 0.98.4 0.99.0 has breaking changes with ReactEditor.focus * add sql to readable ext mimetype * fix empty editor formatting gets saved as draft * add option to use enter for newline * remove empty msg draft from atom family * prevent msg ctx menu from open on text selection
This commit is contained in:
parent
4d0b6b93bc
commit
613e6d6503
34 changed files with 620 additions and 131 deletions
109
src/app/organisms/room/CommandAutocomplete.tsx
Normal file
109
src/app/organisms/room/CommandAutocomplete.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import React, { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo } from 'react';
|
||||
import { Editor } from 'slate';
|
||||
import { Box, 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<string>;
|
||||
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 : (
|
||||
<AutocompleteMenu
|
||||
headerContent={
|
||||
<Box grow="Yes" direction="Row" gap="200" justifyContent="SpaceBetween">
|
||||
<Text size="L400">Commands</Text>
|
||||
<Text size="T200" priority="300" truncate>
|
||||
Begin your message with command
|
||||
</Text>
|
||||
</Box>
|
||||
}
|
||||
requestClose={requestClose}
|
||||
>
|
||||
{autoCompleteNames.map((commandName) => (
|
||||
<MenuItem
|
||||
key={commandName}
|
||||
as="button"
|
||||
radii="300"
|
||||
onKeyDown={(evt: ReactKeyboardEvent<HTMLButtonElement>) =>
|
||||
onTabPress(evt, () => handleAutocomplete(commandName))
|
||||
}
|
||||
onClick={() => handleAutocomplete(commandName)}
|
||||
>
|
||||
<Box grow="Yes" direction="Row" gap="200" justifyContent="SpaceBetween">
|
||||
<Box shrink="No">
|
||||
<Text style={{ flexGrow: 1 }} size="B400" truncate>
|
||||
{`/${commandName}`}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text truncate priority="300" size="T200">
|
||||
{commands[commandName].description}
|
||||
</Text>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
))}
|
||||
</AutocompleteMenu>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue