@heycar-uikit/core
Version:
The React UI library from HeyCar
102 lines (98 loc) • 5.23 kB
JavaScript
import React, { useState, useRef, useCallback, useEffect } from 'react';
import cn from 'classnames';
import { ChevronTop, ChevronDown } from '../../icons/modern';
import Input from '../../input/modern';
import DropdownOption from './components/DropdownOption.js';
var styles = {"container":"dropdown__container_5a833","disabled":"dropdown__disabled_5a833","value":"dropdown__value_5a833","options":"dropdown__options_5a833","show":"dropdown__show_5a833","option":"dropdown__option_5a833","selected":"dropdown__selected_5a833","highlighted":"dropdown__highlighted_5a833","fullWidth":"dropdown__fullWidth_5a833"};
require('./styles/default.css');
/* eslint-disable prettier/prettier */
function Dropdown({ Component = 'div', value, onChange, options, disabled, dataTestId, onBlur, onClick, label, hint, inputRef, fullWidth, placeholder, error, onKeyDown, ...restProps }) {
const [stateValue, setStateValue] = useState(value);
const [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(0);
const containerRef = useRef(null);
const isValueIncludedInOptions = useCallback((option) => !!options.find((o) => Object.entries(o).every(([key, val]) => {
return val === option[key];
})), [options]);
useEffect(() => {
if (options.length === 0)
setStateValue(undefined);
if (value && !isValueIncludedInOptions(value))
setStateValue(undefined);
if (stateValue && !isValueIncludedInOptions(stateValue))
setStateValue(undefined);
if (!stateValue && value && isValueIncludedInOptions(value))
setStateValue(value);
}, [value, options, stateValue, isValueIncludedInOptions]);
const selectOption = useCallback((option) => {
if (onChange) {
if (option !== stateValue)
onChange(option);
}
setStateValue(option);
}, [onChange, stateValue]);
const onClickHandler = useCallback(() => {
onClick?.();
if (!disabled)
setIsOpen((nextState) => !nextState);
}, [onClick, disabled, setIsOpen]);
useEffect(() => {
if (isOpen)
setHighlightedIndex(0);
}, [isOpen]);
useEffect(() => {
const handler = (e) => {
if (e.target != containerRef.current)
return;
switch (e.code) {
case 'Enter':
case 'Space':
setIsOpen(prev => !prev);
if (isOpen)
selectOption(options[highlightedIndex]);
break;
case 'ArrowUp':
case 'ArrowDown': {
if (!isOpen) {
setIsOpen(true);
break;
}
const newValue = highlightedIndex + (e.code === 'ArrowDown' ? 1 : -1);
if (newValue >= 0 && newValue < options.length) {
setHighlightedIndex(newValue);
}
break;
}
case 'Escape':
setIsOpen(false);
break;
}
};
containerRef.current?.addEventListener('keydown', handler);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
containerRef.current?.removeEventListener('keydown', handler);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [highlightedIndex, options, selectOption]);
const isOptionSelection = (option) => {
return option?.value === stateValue?.value;
};
const onBlurHandler = useCallback(() => {
onBlur?.();
setIsOpen(false);
}, [onBlur, setIsOpen]);
const classNames = cn(styles.container, disabled && styles.disabled, fullWidth && styles.fullWidth);
const valueClassNames = cn(disabled && 'disabled', styles.value);
return (React.createElement(Component, { className: classNames, onBlur: onBlurHandler, onClick: onClickHandler, ref: containerRef, tabIndex: 0 },
React.createElement(Input, { "aria-disabled": true, className: valueClassNames, disabled: disabled, error: error, fullWidth: true, hint: hint, label: label, onChange: () => {
setStateValue(options[highlightedIndex]);
}, onKeyDown: onKeyDown, placeholder: placeholder, ref: inputRef, rightIcon: isOpen ? React.createElement(ChevronTop, null) : React.createElement(ChevronDown, null), value: stateValue?.label, ...restProps }),
React.createElement("ul", { className: `${styles.options} ${isOpen ? styles.show : ''}`, "data-test-id": dataTestId }, options.map((option, index) => (React.createElement("li", { className: `${styles.option} ${isOptionSelection(option) ? styles.selected : ''} ${index === highlightedIndex ? styles.highlighted : ''}`, key: option.value, onMouseDown: e => {
selectOption(option);
e.stopPropagation();
setIsOpen(false);
}, onMouseEnter: () => setHighlightedIndex(index) },
React.createElement(DropdownOption, { ...option })))))));
}
export { Dropdown as default };