UNPKG

@heycar-uikit/core

Version:
102 lines (98 loc) 5.23 kB
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 };