0d303169f2
FocusTrap monitors focusin events and can redirect focus into the menu container (blurring the editor) before individual MenuItem onMouseDown handlers fire. Adding preventDefault at the container level ensures no click anywhere inside the menu can steal focus from the editor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import React, { ReactNode } from 'react';
|
|
import FocusTrap from 'focus-trap-react';
|
|
import { isKeyHotkey } from 'is-hotkey';
|
|
import { Header, Menu, Scroll, config } from 'folds';
|
|
|
|
import * as css from './AutocompleteMenu.css';
|
|
import { preventScrollWithArrowKey, stopPropagation } from '../../../utils/keyboard';
|
|
import { useAlive } from '../../../hooks/useAlive';
|
|
|
|
type AutocompleteMenuProps = {
|
|
requestClose: () => void;
|
|
headerContent: ReactNode;
|
|
children: ReactNode;
|
|
};
|
|
export function AutocompleteMenu({ headerContent, requestClose, children }: AutocompleteMenuProps) {
|
|
const alive = useAlive();
|
|
|
|
const handleDeactivate = () => {
|
|
if (alive()) {
|
|
// The component is unmounted so we will not call for `requestClose`
|
|
requestClose();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={css.AutocompleteMenuBase} onMouseDown={(e) => e.preventDefault()}>
|
|
<div className={css.AutocompleteMenuContainer}>
|
|
<FocusTrap
|
|
focusTrapOptions={{
|
|
initialFocus: false,
|
|
onPostDeactivate: handleDeactivate,
|
|
returnFocusOnDeactivate: false,
|
|
clickOutsideDeactivates: true,
|
|
allowOutsideClick: true,
|
|
isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
|
|
isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
|
|
escapeDeactivates: stopPropagation,
|
|
}}
|
|
>
|
|
<Menu className={css.AutocompleteMenu}>
|
|
<Header className={css.AutocompleteMenuHeader} size="400">
|
|
{headerContent}
|
|
</Header>
|
|
<Scroll style={{ flexGrow: 1 }} onKeyDown={preventScrollWithArrowKey}>
|
|
<div style={{ padding: config.space.S200 }}>{children}</div>
|
|
</Scroll>
|
|
</Menu>
|
|
</FocusTrap>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|