UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.

402 lines (401 loc) 14.9 kB
"use strict"; "use client"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; exports.__esModule = true; exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _react = _interopRequireDefault(require("react")); var _styledComponents = _interopRequireWildcard(require("styled-components")); var _helpers = require("./helpers"); var _InputSelectOption = _interopRequireDefault(require("./InputSelectOption")); var _InputSelect = require("./InputSelect.styled"); var _CloseCircle = _interopRequireDefault(require("../icons/CloseCircle")); var _InputField = _interopRequireDefault(require("../InputField")); var _useRandomId = require("../hooks/useRandomId"); var _useClickOutside = _interopRequireDefault(require("../hooks/useClickOutside")); var _keyMaps = _interopRequireDefault(require("../common/keyMaps")); var _Box = _interopRequireDefault(require("../Box")); var _Text = _interopRequireDefault(require("../Text")); var _Stack = _interopRequireDefault(require("../Stack")); var _useMediaQuery = _interopRequireDefault(require("../hooks/useMediaQuery")); var _Modal = _interopRequireWildcard(require("../Modal")); var _ModalCloseButton = _interopRequireDefault(require("../Modal/ModalCloseButton")); var _Button = _interopRequireDefault(require("../Button")); var _Heading = _interopRequireDefault(require("../Heading")); var _ModalSection = require("../Modal/ModalSection"); var _ModalHeader = require("../Modal/ModalHeader"); const StyledModalSection = (0, _styledComponents.default)(_ModalSection.ModalSectionWrapper).withConfig({ displayName: "InputSelect__StyledModalSection", componentId: "sc-13l56eg-0" })(["padding:0;"]); const StyledModalHeader = (0, _styledComponents.default)(_ModalHeader.ModalHeaderWrapper).withConfig({ displayName: "InputSelect__StyledModalHeader", componentId: "sc-13l56eg-1" })(["", ";"], ({ theme }) => (0, _styledComponents.css)(["padding:", " !important;margin-bottom:0 !important;"], theme.orbit.spaceMedium)); const InputSelect = /*#__PURE__*/_react.default.forwardRef(({ onChange, options, defaultSelected, prevSelected, prevSelectedLabel = "Previously selected", id, onFocus, label, showAll = true, showAllLabel = showAll ? "All options" : "Other options", help, error, onBlur, placeholder, labelClose = "Close", emptyState = "No results found.", onOptionSelect, onClose, disabled, maxHeight = "400px", maxWidth, onKeyDown, spaceAfter, ...props }, ref) => { const randomId = (0, _useRandomId.useRandomIdSeed)(); const labelRef = _react.default.useRef(null); const inputId = id || randomId("input"); const dropdownId = randomId("dropdown"); const dropdownRef = _react.default.useRef(null); const [isOpened, setIsOpened] = _react.default.useState(false); const [inputValue, setInputValue] = _react.default.useState(defaultSelected ? options.find(opt => opt.value === defaultSelected.value)?.title : ""); const [selectedOption, setSelectedOption] = _react.default.useState(defaultSelected || null); const [activeIdx, setActiveIdx] = _react.default.useState(0); const [activeDescendant, setActiveDescendant] = _react.default.useState(""); const [isScrolled, setIsScrolled] = _react.default.useState(false); const [topOffset, setTopOffset] = _react.default.useState(0); const refs = {}; const { isLargeMobile } = (0, _useMediaQuery.default)(); const groupedOptions = _react.default.useMemo(() => (0, _helpers.groupOptions)(options, showAll, prevSelected), [options, prevSelected, showAll]); const [results, setResults] = _react.default.useState(groupedOptions); const handleClose = selection => { if (!selection) { if (inputValue === "") { if (onOptionSelect) onOptionSelect(null); setSelectedOption(null); } else if (inputValue !== selectedOption?.title) { setInputValue(selectedOption?.title || ""); } setResults(groupedOptions); setActiveIdx(0); } if (onClose && isOpened) onClose(selection || (inputValue === "" ? null : selectedOption)); setIsOpened(false); }; const handleCloseClick = () => { handleClose(); }; (0, _useClickOutside.default)(labelRef, handleCloseClick); const handleFocus = ev => { if (onFocus) onFocus(ev); setIsOpened(true); setResults(results || groupedOptions); }; const handleBlur = ev => { if (onBlur) onBlur(ev); }; const handleInputChange = ev => { const { value } = ev.currentTarget; if (onChange) onChange(ev); if (value.length === 0) { setResults(groupedOptions); } else { const filtered = options.filter(({ title }) => { return title.toLowerCase().includes(value.toLowerCase()); }); setResults({ groups: [], all: filtered, flattened: filtered }); } if (!isOpened) setIsOpened(true); setInputValue(value); setActiveIdx(0); }; const handleDropdownKey = ev => { if (!isOpened && (ev.keyCode === _keyMaps.default.ENTER || ev.keyCode === _keyMaps.default.ARROW_DOWN || ev.keyCode === _keyMaps.default.ARROW_UP)) { setIsOpened(true); return; } if (isOpened && ev.keyCode === _keyMaps.default.ESC) handleClose(); if (isOpened && ev.keyCode === _keyMaps.default.ENTER) { ev.preventDefault(); if (results.all.length !== 0) { const option = results.flattened[activeIdx]; setSelectedOption(option); setInputValue(option.title); if (onOptionSelect) onOptionSelect(option); handleClose(option); } } if (ev.keyCode === _keyMaps.default.ARROW_DOWN) { if (results.flattened.length - 1 > activeIdx) { const nextIdx = activeIdx + 1; setActiveIdx(nextIdx); setActiveDescendant(refs[nextIdx].current?.id); if (dropdownRef && dropdownRef.current) { dropdownRef.current.scrollTop = refs[nextIdx].current?.offsetTop; } } } if (ev.keyCode === _keyMaps.default.ARROW_UP) { if (activeIdx > 0) { const prevIdx = activeIdx - 1; setActiveIdx(prevIdx); setActiveDescendant(refs[prevIdx].current?.id); if (dropdownRef && dropdownRef.current) { dropdownRef.current.scrollTop = refs[prevIdx].current?.offsetTop; } } } }; const input = /*#__PURE__*/_react.default.createElement(_InputField.default, (0, _extends2.default)({ help: isLargeMobile && help, error: isLargeMobile && error, label: isLargeMobile && label, disabled: disabled, onFocus: handleFocus, onBlur: handleBlur, onChange: handleInputChange, id: inputId, placeholder: placeholder, autoFocus: !isLargeMobile, role: "combobox", value: inputValue, onKeyDown: ev => { if (onKeyDown) onKeyDown(ev); handleDropdownKey(ev); }, ariaHasPopup: isOpened, ariaExpanded: isOpened, ariaAutocomplete: "list", ariaActiveDescendant: activeDescendant, ariaControls: isOpened ? dropdownId : undefined, autoComplete: "off", ref: ref, prefix: selectedOption && selectedOption.prefix, suffix: String(inputValue).length > 1 && /*#__PURE__*/_react.default.createElement(_InputSelect.StyledCloseButton, { onClick: ev => { ev.preventDefault(); if (onOptionSelect) onOptionSelect(null); setInputValue(""); setResults(groupedOptions); setSelectedOption(null); setActiveIdx(0); }, $disabled: disabled }, /*#__PURE__*/_react.default.createElement(_CloseCircle.default, { color: "primary", ariaLabel: "Clear" })) }, props)); const renderOptions = () => { if (results.groups.length === 0) { return results.all.map((option, idx) => { const { title, description, prefix, value: optValue } = option; const optionId = randomId(title); const isSelected = optValue === selectedOption?.value; const optionRef = /*#__PURE__*/_react.default.createRef(); refs[idx] = optionRef; return /*#__PURE__*/_react.default.createElement(_InputSelectOption.default, { key: optionId, id: optionId, active: activeIdx === idx, isSelected: isSelected, ref: optionRef, title: title, description: description, prefix: prefix, onClick: ev => { ev.preventDefault(); setActiveIdx(idx); setResults(groupedOptions); if (onOptionSelect) onOptionSelect(option); if (isLargeMobile) setIsOpened(false); if (!isSelected) { setInputValue(title); setSelectedOption(option); handleClose(option); } } }); }); } let idx = -1; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, results.groups.map((group, groupIdx) => { const prevSelectedOption = prevSelected && groupIdx === 0; const { group: groupTitle } = group[0]; const groupId = randomId(prevSelectedOption ? "prevSelected" : `${groupTitle}`); return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, { key: groupId }, /*#__PURE__*/_react.default.createElement(_Box.default, { padding: "small" }, /*#__PURE__*/_react.default.createElement(_Text.default, { type: "secondary" }, prevSelectedOption ? prevSelectedLabel : groupTitle)), group.map(option => { idx += 1; const optionIdx = idx; const optionRef = /*#__PURE__*/_react.default.createRef(); refs[optionIdx] = optionRef; const { title, description, prefix, value: optValue } = option; const optionId = randomId(title); const isSelected = optValue === selectedOption?.value; return /*#__PURE__*/_react.default.createElement(_InputSelectOption.default, { key: optionId, id: optionId, active: !!isLargeMobile && activeIdx === optionIdx, isSelected: isSelected, ref: optionRef, title: title, description: description, prefix: prefix, onClick: ev => { ev.preventDefault(); if (onOptionSelect) onOptionSelect(option); setActiveIdx(optionIdx); setResults(groupedOptions); if (isLargeMobile) setIsOpened(false); if (!isSelected) { setInputValue(title); setSelectedOption(option); handleClose(option); } } }); })); }), /*#__PURE__*/_react.default.createElement(_Box.default, { padding: "small" }, /*#__PURE__*/_react.default.createElement(_Text.default, { type: "secondary" }, showAllLabel)), results.all.map(option => { const { title, description, prefix, value: optValue, group } = option; if (group && !showAll) return null; idx += 1; const optionRef = /*#__PURE__*/_react.default.createRef(); const optionIdx = idx; refs[optionIdx] = optionRef; const optionId = randomId(`all_${title}`); const isSelected = optValue === selectedOption?.value; return /*#__PURE__*/_react.default.createElement(_InputSelectOption.default, { key: optionId, id: optionId, active: activeIdx === optionIdx, isSelected: isSelected, ref: optionRef, title: title, description: description, prefix: prefix, onClick: ev => { ev.preventDefault(); if (onOptionSelect) onOptionSelect(option); setActiveIdx(optionIdx); setResults(groupedOptions); if (isLargeMobile) setIsOpened(false); if (!isSelected) { setInputValue(title); setSelectedOption(option); handleClose(option); } } }); })); }; const noResults = typeof emptyState === "string" ? /*#__PURE__*/_react.default.createElement(_Box.default, { padding: "medium" }, /*#__PURE__*/_react.default.createElement(_Text.default, null, emptyState)) : emptyState; const dropdown = isOpened && /*#__PURE__*/_react.default.createElement(_InputSelect.StyledDropdown, { role: "listbox", id: dropdownId, "aria-labelledby": inputId, $hasLabel: !!label, $maxHeight: maxHeight, $maxWidth: maxWidth, ref: dropdownRef }, results.all.length === 0 ? noResults : renderOptions()); return isLargeMobile ? /*#__PURE__*/_react.default.createElement(_InputSelect.StyledLabel, { htmlFor: inputId, ref: labelRef, spaceAfter: spaceAfter }, input, dropdown) : /*#__PURE__*/_react.default.createElement(_InputSelect.StyledLabel, { htmlFor: inputId, ref: labelRef, spaceAfter: spaceAfter }, /*#__PURE__*/_react.default.createElement(_InputField.default, { label: label, help: help, error: error, onFocus: () => setIsOpened(true), readOnly: true, role: "textbox", placeholder: placeholder, value: inputValue, prefix: selectedOption && selectedOption.prefix }), isOpened && /*#__PURE__*/_react.default.createElement(_InputSelect.StyledModalWrapper, { $maxHeight: maxHeight, isScrolled: isScrolled && topOffset > 50 }, /*#__PURE__*/_react.default.createElement(_Modal.default, { labelClose: labelClose, onClose: handleCloseClick, fixedFooter: true, onScroll: ev => { if (!isLargeMobile) { ev.preventDefault(); setIsScrolled(true); setTopOffset(ev.currentTarget.scrollTop); } }, mobileHeader: false, autoFocus: true }, /*#__PURE__*/_react.default.createElement(StyledModalHeader, null, label && /*#__PURE__*/_react.default.createElement(_Stack.default, { align: "center", justify: "between" }, /*#__PURE__*/_react.default.createElement(_Box.default, null, /*#__PURE__*/_react.default.createElement(_Heading.default, { type: "title2" }, label)), /*#__PURE__*/_react.default.createElement(_ModalCloseButton.default, { onClick: handleCloseClick, title: labelClose })), input), /*#__PURE__*/_react.default.createElement(StyledModalSection, null, dropdown), /*#__PURE__*/_react.default.createElement(_Modal.ModalFooter, { flex: "100%" }, /*#__PURE__*/_react.default.createElement(_Button.default, { type: "secondary", fullWidth: true, onClick: handleCloseClick }, labelClose))))); }); InputSelect.displayName = "InputSelect"; var _default = InputSelect; exports.default = _default;