@spaced-out/ui-design-system
Version:
Sense UI components library
165 lines (141 loc) • 4.04 kB
Flow
// @flow strict
import * as React from 'react';
import type {MenuClassNames, MenuLabelTooltip} from '../../types/menu';
import type {ClickAwayRefType} from '../../utils/click-away';
import {getTextLabelFromSelectedKeys} from '../../utils/menu';
import type {InputProps} from '../Input';
import type {MenuOption, Virtualization} from '../Menu';
import type {ElevationType} from '../Tooltip';
import {Typeahead} from './Typeahead';
type ClassNames = $ReadOnly<{
wrapper?: string,
box?: string,
}>;
export type SimpleTypeaheadRef = {
selectedKeys?: Array<string>,
};
export type SimpleTypeaheadProps = {
// Input props
...InputProps,
isLoading?: boolean,
menuOpenOffset?: number,
classNames?: ClassNames,
// Menu props
options?: Array<MenuOption>,
selectedKeys?: Array<string>,
menuVirtualization?: Virtualization,
header?: React.Node,
footer?: React.Node,
menuClassNames?: MenuClassNames,
showLabelTooltip?: MenuLabelTooltip,
allowWrap?: boolean,
elevation?: ElevationType,
// events
onSelect?: (option: MenuOption, ?SyntheticEvent<HTMLElement>) => mixed,
onSearch?: (evt: SyntheticInputEvent<HTMLInputElement>) => mixed,
onMenuOpen?: () => mixed,
onMenuClose?: () => mixed,
onClear?: () => void,
// Resolvers
resolveLabel?: (option: MenuOption) => string | React.Node,
resolveSecondaryLabel?: (option: MenuOption) => string | React.Node,
clickAwayRef?: ClickAwayRefType,
allowInternalFilter?: boolean,
...
};
const SimpleTypeaheadBase = (props: SimpleTypeaheadProps, ref) => {
const {
size = 'medium',
classNames,
placeholder = 'Select...',
options,
selectedKeys,
onSelect,
onMenuOpen,
onMenuClose,
resolveLabel,
resolveSecondaryLabel,
onClear,
onSearch,
menuVirtualization,
header,
footer,
menuClassNames,
clickAwayRef,
showLabelTooltip,
allowInternalFilter = true,
allowWrap = false,
elevation = 'modal',
...inputProps
} = props;
const [typeaheadInputText, setTypeaheadInputText] = React.useState('');
const [typeaheadSelectedKeys, setTypeaheadSelectedKeys] =
React.useState(selectedKeys);
React.useEffect(() => {
const newTypeaheadText = getTextLabelFromSelectedKeys(
selectedKeys,
options,
);
setTypeaheadSelectedKeys(selectedKeys);
setTypeaheadInputText(newTypeaheadText);
}, [selectedKeys]);
const handleOptionChange = (selectedOption: MenuOption, e) => {
e?.stopPropagation();
const newSelectedKeys = [selectedOption.key];
const newTypeaheadText = getTextLabelFromSelectedKeys(
newSelectedKeys,
options,
);
setTypeaheadSelectedKeys(newSelectedKeys);
setTypeaheadInputText(newTypeaheadText);
setTimeout(() => {
onSelect?.(selectedOption, e);
});
};
React.useImperativeHandle(ref, () => ({
selectedKeys: typeaheadSelectedKeys,
}));
return (
<Typeahead
{...inputProps}
allowInternalFilter={allowInternalFilter}
classNames={classNames}
size={size}
placeholder={placeholder}
onSelect={handleOptionChange}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
typeaheadInputText={typeaheadInputText}
onSearch={(e) => {
setTypeaheadInputText(e.target.value);
onSearch?.(e);
}}
onClear={() => {
setTypeaheadInputText('');
setTypeaheadSelectedKeys([]);
onClear?.();
}}
menu={{
options,
selectedKeys: typeaheadSelectedKeys,
resolveLabel,
resolveSecondaryLabel,
size,
virtualization: menuVirtualization,
header,
footer,
classNames: menuClassNames,
showLabelTooltip,
allowWrap,
}}
clickAwayRef={clickAwayRef}
elevation={elevation}
/>
);
};
export const SimpleTypeahead: React.AbstractComponent<
SimpleTypeaheadProps,
SimpleTypeaheadRef,
> = React.forwardRef<SimpleTypeaheadProps, SimpleTypeaheadRef>(
SimpleTypeaheadBase,
);