Merge pull request #26 from toeverything/bugfix/command-menu

fix command menu don's vanish on scorlling
This commit is contained in:
Diamond
2022-08-02 16:20:20 +08:00
committed by GitHub
2 changed files with 42 additions and 35 deletions
@@ -23,6 +23,7 @@ import type { DragDropManager } from './drag-drop';
import { MouseManager } from './mouse'; import { MouseManager } from './mouse';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Point } from '@toeverything/utils'; import { Point } from '@toeverything/utils';
import { ScrollManager } from './scroll';
// import { BrowserClipboard } from './clipboard/browser-clipboard'; // import { BrowserClipboard } from './clipboard/browser-clipboard';
@@ -63,6 +64,7 @@ export interface VirgoSelection {
// Editor's external API // Editor's external API
export interface Virgo { export interface Virgo {
selectionManager: SelectionManager; selectionManager: SelectionManager;
scrollManager: ScrollManager;
createBlock: ( createBlock: (
type: keyof BlockFlavors, type: keyof BlockFlavors,
parentId?: string parentId?: string
@@ -40,26 +40,30 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
bottom: 0, bottom: 0,
}); });
const [search_text, set_search_text] = useState<string>(''); const [searchText, setSearchText] = useState<string>('');
const [search_blocks, set_search_blocks] = useState<QueryResult>([]); const [searchBlocks, setSearchBlocks] = useState<QueryResult>([]);
const commandMenuContentRef = useRef(); const commandMenuContentRef = useRef();
// TODO: Two-way link to be developed // TODO: Two-way link to be developed
// useEffect(() => { // useEffect(() => {
// QueryBlocks(editor, search_text, result => set_search_blocks(result)); // QueryBlocks(editor, searchText, result => set_searchBlocks(result));
// }, [editor, search_text]); // }, [editor, searchText]);
const hideMenu = () => {
setShow(false);
editor.scrollManager.unLock();
};
const [types, categories] = useMemo(() => { const [types, categories] = useMemo(() => {
const types: Array<BlockFlavorKeys | string> = []; const types: Array<BlockFlavorKeys | string> = [];
const categories: Array<CommandMenuCategories> = []; const categories: Array<CommandMenuCategories> = [];
if (search_blocks.length) { if (searchBlocks.length) {
Object.values(search_blocks).forEach(({ id }) => types.push(id)); Object.values(searchBlocks).forEach(({ id }) => types.push(id));
categories.push(CommandMenuCategories.pages); categories.push(CommandMenuCategories.pages);
} }
Object.entries(menuItemsMap).forEach(([category, itemInfoList]) => { Object.entries(menuItemsMap).forEach(([category, itemInfoList]) => {
itemInfoList.forEach(info => { itemInfoList.forEach(info => {
if ( if (
!search_text || !searchText ||
info.text.toLowerCase().includes(search_text.toLowerCase()) info.text.toLowerCase().includes(searchText.toLowerCase())
) { ) {
types.push(info.type); types.push(info.type);
} }
@@ -73,9 +77,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
}); });
}); });
return [types, categories]; return [types, categories];
}, [search_blocks, search_text]); }, [searchBlocks, searchText]);
const check_if_show_command_menu = useCallback( const checkIfShowCommandMenu = useCallback(
async (event: React.KeyboardEvent<HTMLDivElement>) => { async (event: React.KeyboardEvent<HTMLDivElement>) => {
const { type, anchorNode } = editor.selection.currentSelectInfo; const { type, anchorNode } = editor.selection.currentSelectInfo;
if (event.key === '/' && type === 'Range') { if (event.key === '/' && type === 'Range') {
@@ -101,8 +105,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
); );
} }
}); });
set_search_text(''); setSearchText('');
setShow(true); setShow(true);
editor.scrollManager.lock();
const rect = const rect =
editor.selection.currentSelectInfo?.browserSelection editor.selection.currentSelectInfo?.browserSelection
?.getRangeAt(0) ?.getRangeAt(0)
@@ -137,12 +142,12 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
[editor, blockId] [editor, blockId]
); );
const handle_click_others = useCallback( const handleClickOthers = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
if (show) { if (show) {
const { anchorNode } = editor.selection.currentSelectInfo; const { anchorNode } = editor.selection.currentSelectInfo;
if (anchorNode.id !== blockId) { if (anchorNode.id !== blockId) {
setShow(false); hideMenu();
return; return;
} }
setTimeout(() => { setTimeout(() => {
@@ -150,12 +155,12 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
editor.blockHelper.getSearchSlashText(blockId); editor.blockHelper.getSearchSlashText(blockId);
// check if has search text // check if has search text
if (searchText && searchText.startsWith('/')) { if (searchText && searchText.startsWith('/')) {
set_search_text(searchText.slice(1)); setSearchText(searchText.slice(1));
} else { } else {
setShow(false); hideMenu();
} }
if (searchText.length > 6 && !types.length) { if (searchText.length > 6 && !types.length) {
setShow(false); hideMenu();
} }
}); });
} }
@@ -163,18 +168,18 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
[editor, show, blockId, types] [editor, show, blockId, types]
); );
const handle_keyup = useCallback( const handleKeyup = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
check_if_show_command_menu(event); checkIfShowCommandMenu(event);
handle_click_others(event); handleClickOthers(event);
}, },
[check_if_show_command_menu, handle_click_others] [checkIfShowCommandMenu, handleClickOthers]
); );
const handle_key_down = useCallback( const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => { (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.code === 'Escape') { if (event.code === 'Escape') {
setShow(false); hideMenu();
} }
}, },
[] []
@@ -183,23 +188,23 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
useEffect(() => { useEffect(() => {
const sub = hooks const sub = hooks
.get(HookType.ON_ROOT_NODE_KEYUP) .get(HookType.ON_ROOT_NODE_KEYUP)
.subscribe(handle_keyup); .subscribe(handleKeyup);
sub.add( sub.add(
hooks hooks
.get(HookType.ON_ROOT_NODE_KEYDOWN_CAPTURE) .get(HookType.ON_ROOT_NODE_KEYDOWN_CAPTURE)
.subscribe(handle_key_down) .subscribe(handleKeyDown)
); );
return () => { return () => {
sub.unsubscribe(); sub.unsubscribe();
}; };
}, [handle_keyup, handle_key_down, hooks]); }, [handleKeyup, handleKeyDown, hooks]);
const handle_click_away = () => { const handleClickAway = () => {
setShow(false); hideMenu();
}; };
const handle_selected = async (type: BlockFlavorKeys | string) => { const handleSelected = async (type: BlockFlavorKeys | string) => {
const text = await editor.commands.textCommands.getBlockText(blockId); const text = await editor.commands.textCommands.getBlockText(blockId);
editor.blockHelper.removeSearchSlash(blockId, true); editor.blockHelper.removeSearchSlash(blockId, true);
if (type.startsWith('Virgo')) { if (type.startsWith('Virgo')) {
@@ -224,20 +229,20 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
block.firstCreateFlag = true; block.firstCreateFlag = true;
} }
} }
setShow(false); hideMenu();
}; };
const handle_close = () => { const handleClose = () => {
editor.blockHelper.removeSearchSlash(blockId); editor.blockHelper.removeSearchSlash(blockId);
}; };
return ( return (
<div <div
style={{ zIndex: 1 }} style={{ zIndex: 1 }}
onKeyUpCapture={handle_keyup} onKeyUpCapture={handleKeyup}
ref={commandMenuContentRef} ref={commandMenuContentRef}
> >
<MuiClickAwayListener onClickAway={handle_click_away}> <MuiClickAwayListener onClickAway={handleClickAway}>
<div> <div>
<CommandMenuContainer <CommandMenuContainer
editor={editor} editor={editor}
@@ -248,9 +253,9 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
}} }}
isShow={show} isShow={show}
blockId={blockId} blockId={blockId}
onSelected={handle_selected} onSelected={handleSelected}
onclose={handle_close} onclose={handleClose}
searchBlocks={search_blocks} searchBlocks={searchBlocks}
types={types} types={types}
categories={categories} categories={categories}
/> />