UNPKG

@s-ui/react-molecule-select

Version:

`MoleculeSelect` is a customized `select` created from a combination of `AtomInput`, `MoleculeInputTags`, `MoleculeDropdownList` and `MoleculeDropdownOption`

315 lines 11.9 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; var _excluded = ["className", "onBlur", "onFocus", "isOpen", "onToggle", "children", "errorState", "state", "disabled", "keysSelection", "multiselection", "hasSearch", "iconCloseTag", "tagSize", "searchIcon", "searchPlaceholder", "noResults", "refMoleculeSelect", "aria-label", "onSearch", "readOnly", "onChange", "selectSize", "size", "isBorderless", "value", "defaultValue"]; import { Children, cloneElement, createRef, forwardRef, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { getTarget } from '@s-ui/js/lib/react'; import { useControlledState } from '@s-ui/react-hooks'; import useMergeRefs from '@s-ui/react-hooks/lib/useMergeRefs'; import MoleculeSelectMultipleSelection from './components/MultipleSelection.js'; import MoleculeSelectSingleSelection from './components/SingleSelection.js'; import { DropdownContext, ENABLED_KEYS, getClassName, getOptionData, SELECT_DROPDOWN_LIST_SIZES, SELECT_INPUT_SIZES, SELECT_STATES, SELECT_TAG_SIZES, SELECTION_KEYS } from './config.js'; import { jsx as _jsx } from "react/jsx-runtime"; var useFunctionalRef = function useFunctionalRef() { return useReducer(function (_s, node) { return node; }, null); }; var MoleculeSelect = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) { var classNameFromProps = _ref.className, onBlur = _ref.onBlur, onFocus = _ref.onFocus, isOpen = _ref.isOpen, onToggle = _ref.onToggle, children = _ref.children, errorState = _ref.errorState, state = _ref.state, _ref$disabled = _ref.disabled, disabled = _ref$disabled === void 0 ? false : _ref$disabled, _ref$keysSelection = _ref.keysSelection, keysSelection = _ref$keysSelection === void 0 ? SELECTION_KEYS : _ref$keysSelection, _ref$multiselection = _ref.multiselection, multiselection = _ref$multiselection === void 0 ? false : _ref$multiselection, _ref$hasSearch = _ref.hasSearch, hasSearch = _ref$hasSearch === void 0 ? false : _ref$hasSearch, iconCloseTag = _ref.iconCloseTag, tagSize = _ref.tagSize, searchIcon = _ref.searchIcon, searchPlaceholder = _ref.searchPlaceholder, noResults = _ref.noResults, refMoleculeSelectFromProps = _ref.refMoleculeSelect, ariaLabel = _ref['aria-label'], onSearch = _ref.onSearch, _ref$readOnly = _ref.readOnly, readOnly = _ref$readOnly === void 0 ? false : _ref$readOnly, _ref$onChange = _ref.onChange, onChange = _ref$onChange === void 0 ? function () {} : _ref$onChange, _ref$selectSize = _ref.selectSize, selectSize = _ref$selectSize === void 0 ? SELECT_INPUT_SIZES.MEDIUM : _ref$selectSize, dropdownListSize = _ref.size, isBorderless = _ref.isBorderless, value = _ref.value, defaultValue = _ref.defaultValue, props = _objectWithoutPropertiesLoose(_ref, _excluded); var _useState = useState(false), focusedFirstOption = _useState[0], setFocusedFirstOption = _useState[1]; var refOptions = useRef({}); var _useControlledState = useControlledState(value, defaultValue), innerValue = _useControlledState[0], setInnerValue = _useControlledState[1]; var innerRef = useRef(); var triggerRef = useRef(); var searchRef = useRef(); var refMoleculeSelect = useRef(refMoleculeSelectFromProps); var refsMoleculeSelectOptions = useRef([]); var ref = useMergeRefs(forwardedRef, refMoleculeSelect, innerRef); var _useFunctionalRef = useFunctionalRef(), inputSearch = _useFunctionalRef[0], setInputRef = _useFunctionalRef[1]; var _useState2 = useState(isOpen || false), isOpenState = _useState2[0], setIsOpenState = _useState2[1]; useEffect(function () { return setIsOpenState(isOpen); }, [isOpen, setIsOpenState]); Object.assign(refOptions.current, getOptionData(children)); var isFocused = function isFocused() { var el = innerRef == null ? void 0 : innerRef.current; return el && (el.matches(':focus-within') || el.contains(document.activeElement)); }; var isTriggerFocused = function isTriggerFocused() { var el = triggerRef == null ? void 0 : triggerRef.current; return el && el.matches(':focus'); }; var isSearchFocused = function isSearchFocused() { var el = searchRef == null ? void 0 : searchRef.current; return el && el.matches(':focus'); }; var extendedChildren = useMemo(function () { return Children.toArray(children).filter(Boolean).map(function (child, index) { refsMoleculeSelectOptions.current[index] = /*#__PURE__*/createRef(); return /*#__PURE__*/cloneElement(child, { ref: refsMoleculeSelectOptions.current[index], selectKey: keysSelection }); }); }, [children, keysSelection]); var numOptions = Children.toArray(extendedChildren).length; var className = getClassName({ state: state, errorState: errorState, disabled: disabled, className: classNameFromProps, isBorderless: isBorderless }); var handleToggle = useCallback(function (ev, _temp) { var _ref2 = _temp === void 0 ? { isOutsideEvent: false } : _temp, isOpen = _ref2.isOpen, isOutsideEvent = _ref2.isOutsideEvent; setIsOpenState(isOpen !== undefined ? isOpen : !isOpenState); typeof onToggle === 'function' && onToggle(ev, { isOpen: isOpen }); if (!isOutsideEvent) { ev.preventDefault(); ev.stopPropagation(); } }, [isOpenState, onToggle]); var closeList = useCallback(function (ev, _ref3) { var _ref3$isOutsideEvent = _ref3.isOutsideEvent, isOutsideEvent = _ref3$isOutsideEvent === void 0 ? false : _ref3$isOutsideEvent; handleToggle(ev, { isOpen: false, isOutsideEvent: isOutsideEvent }); }, [handleToggle]); var handleTriggerClick = function handleTriggerClick(event) { handleToggle(event, { isOpen: true }); if (!isOpenState) { setTimeout(function () { if (hasSearch) { focusSearchInput(event); } else { focusFirstOption(event); } }); } }; var handleOutsideClick = useCallback(function (ev) { if (disabled) return; if (refMoleculeSelect.current && !refMoleculeSelect.current.contains(ev.target)) { // outside click closeList(ev, { isOutsideEvent: true }); } }, [closeList, disabled]); var focusFirstOption = useCallback(function (ev) { var options = refsMoleculeSelectOptions.current.map(function (option) { return getTarget(option.current); }); options[0] && options[0].focus(); ev.preventDefault(); ev.stopPropagation(); }, [refsMoleculeSelectOptions]); var focusSearchInput = useCallback(function (ev) { var isEnabledKey = ENABLED_KEYS.includes(ev == null ? void 0 : ev.key); isEnabledKey ? focusFirstOption(ev) : inputSearch == null ? void 0 : inputSearch.focus(); ev == null ? void 0 : ev.preventDefault(); ev == null ? void 0 : ev.stopPropagation(); }, [inputSearch, focusFirstOption]); var isFirstOptionFocused = useCallback(function () { var options = refsMoleculeSelectOptions.current.map(function (option) { return getTarget(option.current); }); return document.activeElement === options[0]; }, [refsMoleculeSelectOptions]); useEffect(function () { Object.assign(refOptions.current, getOptionData(children)); document.addEventListener('touchend', handleOutsideClick); document.addEventListener('mousedown', handleOutsideClick); return function () { document.removeEventListener('touchend', handleOutsideClick); document.removeEventListener('mousedown', handleOutsideClick); }; }, [children, handleOutsideClick]); useEffect(function () { isOpenState && setTimeout(function () { return focusSearchInput(); }); }, [isOpenState, focusSearchInput]); var handleKeyDown = function handleKeyDown(ev) { var _triggerRef$current; ev.persist(); switch (ev.key) { case 'Tab': if (isOpenState) { handleToggle(ev, { isOpen: !isOpenState }); } break; case 'Escape': closeList(ev, { isOutsideEvent: true }); triggerRef == null ? void 0 : (_triggerRef$current = triggerRef.current) == null ? void 0 : _triggerRef$current.focus(); break; case 'ArrowDown': case 'ArrowUp': if (!isOpenState) { handleToggle(ev, { isOpen: !isOpenState }); } if (isTriggerFocused()) { isOpenState && setTimeout(function () { return focusSearchInput(ev); }); !hasSearch && setTimeout(function () { return focusFirstOption(ev); }); } if (isSearchFocused() && ev.key === 'ArrowDown') { focusFirstOption(ev); } if (focusedFirstOption && ev.key === 'ArrowUp') { inputSearch == null ? void 0 : inputSearch.focus(); } break; default: // isOpenState && setTimeout(() => focusSearchInput(ev)) !hasSearch && setTimeout(function () { return focusFirstOption(ev); }); break; } }; var handleFocusOut = function handleFocusOut(event) { typeof onBlur === 'function' && onBlur(event); }; var handleFocusIn = function handleFocusIn() { return !disabled && !hasSearch && onFocus && onFocus(); }; var handleClick = function handleClick(ev) { ev.persist(); if (isFocused()) { if (hasSearch) { setTimeout(function () { return focusSearchInput(ev); }); } else { setTimeout(function () { return focusFirstOption(ev); }); } } }; var handleChange = function handleChange(ev, _ref4) { var value = _ref4.value; setInnerValue(value); onChange && onChange(ev, { value: value }); }; var Select = multiselection ? MoleculeSelectMultipleSelection : MoleculeSelectSingleSelection; var context = { hasSearch: hasSearch, setInputRef: setInputRef, isOpen: isOpenState, inputSearch: inputSearch, onSearch: onSearch, isFirstOptionFocused: isFirstOptionFocused, searchPlaceholder: searchPlaceholder, searchIcon: searchIcon, focusedFirstOption: focusedFirstOption, setFocusedFirstOption: setFocusedFirstOption }; return /*#__PURE__*/_jsx(DropdownContext.Provider, { value: context, children: /*#__PURE__*/_jsx("div", { ref: ref, className: className, onKeyDown: handleKeyDown, onFocus: handleFocusIn, onBlur: handleFocusOut, onClick: handleClick, "aria-label": ariaLabel, children: /*#__PURE__*/_jsx(Select, _extends({ ref: triggerRef, refMoleculeSelect: refMoleculeSelect, refSearch: searchRef, optionsData: refOptions.current, isOpen: isOpenState }, props, { value: innerValue, state: state, disabled: disabled, readOnly: readOnly, onTriggerClick: handleTriggerClick, onToggle: handleToggle, onChange: handleChange, size: dropdownListSize, selectSize: selectSize, tagSize: tagSize, multiselection: multiselection }, multiselection && { iconCloseTag: iconCloseTag, keysSelection: keysSelection }, { children: numOptions > 0 ? extendedChildren : noResults })) }) }); }); MoleculeSelect.displayName = 'MoleculeSelect'; export default MoleculeSelect; export { SELECT_DROPDOWN_LIST_SIZES as moleculeSelectDropdownListSizes }; export { SELECT_INPUT_SIZES as moleculeSelectSizes }; export { SELECT_TAG_SIZES as moleculeSelectTagSizes }; export { SELECT_STATES as moleculeSelectStates };