UNPKG

@pinuts/bsr-uikit-relaunch

Version:

BSR UI-KIT Relaunch

290 lines (284 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireWildcard(require("react")); var _formBuilder = require("@pinuts/form-builder"); var _reactI18next = require("react-i18next"); var _react2 = require("@ariakit/react"); var _withFieldGroup = _interopRequireDefault(require("../../hooks/withFieldGroup.jsx")); var _FormFieldWrapper = _interopRequireDefault(require("../FormFieldWrapper/FormFieldWrapper.jsx")); var _index = require("../../utils/index.js"); var _BSRAddressSelectorModule = _interopRequireDefault(require("./BSRAddressSelector.module.scss")); var _BSRAddressContext = require("./BSRAddressContext.js"); var _useGetPlaceHolderText = _interopRequireDefault(require("./useGetPlaceHolderText.js")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } //FIXME this component is too complicated and probably over-engineered. // simplify it :) const { useFormikContext, useField } = _formBuilder.formik; let debounceTimeout = null; /** * Fetches options from the given URL * * @param url * @return {({label: string, value: string}|{label: string, value: string}|{label: string, value: string}|{label: string, value: string}|{label: string, value: string})[]} */ const fetchOptions = async url => { try { const response = await fetch(url); const data = await response.json(); return data; } catch (error) { console.error(error); } }; const BSRAutocompleteField = _ref => { let { config } = _ref; const [t] = (0, _reactI18next.useTranslation)(); const { scope, additionalQuery, labelTemplate, inputProps = {}, key, viewMode, labelText, disabled, readOnly, suggestionsAmount = undefined } = config; const { prevSelection, setPrevSelection, selectedOptionId, setSelectedOptionId, triggerCancelCombox, setTriggerCancelCombox, getControllerPath, dontShowSelector, fieldKeys } = (0, _react.useContext)(_BSRAddressContext.BSRAddressContext); const { setFieldValue } = useFormikContext(); const [field,, { setValue }] = useField(key); const groupKey = fieldKeys?.groupKey; const labelFieldKey = `${key}Label`; const [labelField] = useField(labelFieldKey); const usedValueLabel = labelField?.value; const [fetchedOptions, setFetchedOptions] = (0, _react.useState)([]); const [searchInput, setSearchInput] = (0, _react.useState)(''); const [selectedOption, setSelectedOption] = (0, _react.useState)({}); const [isFocused, setIsFocused] = (0, _react.useState)(false); const preselectedItemRef = (0, _react.useRef)(null); const inputRef = (0, _react.useRef)(null); const mountedRef = (0, _react.useRef)(true); const cancelButtonRef = (0, _react.useRef)(null); const isInvalid = false; const controllerPath = getControllerPath(); const placeHolderI18nKey = 'bsrAddressSelector.labels.streetPlaceholder'; const placeHolderI18n = (0, _useGetPlaceHolderText.default)(placeHolderI18nKey); const placeHolderConfig = config.placeholder || placeHolderI18nKey; const placeholder = placeHolderConfig && placeHolderI18nKey !== placeHolderConfig ? placeHolderConfig : placeHolderI18n; // need this to reset the combobox from outside (0, _react.useEffect)(() => { if (triggerCancelCombox) { cancelButtonRef.current?.click(); setSearchInput(''); setTriggerCancelCombox(false); } }, [cancelButtonRef, triggerCancelCombox, setTriggerCancelCombox]); (0, _react.useEffect)(() => { if (mountedRef) { const loadData = async () => { const searchQuery = selectedOptionId || searchInput; if (searchQuery.length > 2) { const url = `${controllerPath}streetNames/?searchQuery=${encodeURIComponent(searchQuery)}`; try { const dataArr = await fetchOptions(url); // Nur State verändern, wenn Komponente gemounted ist if (mountedRef && dataArr) { if (suggestionsAmount && dataArr.length > suggestionsAmount) { dataArr.length = suggestionsAmount; } setFetchedOptions(dataArr); } } catch (error) { console.error(error); } } }; if (debounceTimeout) { clearTimeout(debounceTimeout); } // set time out for debouncing debounceTimeout = setTimeout(() => { loadData(); }, 300); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [selectedOptionId, scope, additionalQuery, controllerPath, labelTemplate, searchInput]); (0, _react.useEffect)(() => { return () => { mountedRef.current = false; }; }, []); const filteredOptions = fetchedOptions; // no filter for now (0, _react.useEffect)(() => { const newSelectedOption = fetchedOptions.find(option => option.value === field.value); if (newSelectedOption) { if (newSelectedOption.value === '') { setSearchInput(searchInput); setSelectedOption(newSelectedOption); return; } setSearchInput(newSelectedOption.label); setSelectedOption(newSelectedOption); } }, [field.value, searchInput, fetchedOptions]); const handleOnSelectChange = (0, _react.useCallback)(userInput => { setSearchInput(userInput); const newSelectedOption = filteredOptions.find(option => option.value === userInput); setValue(''); // reset value // set few states to the new selected option, if it exists const [value, label] = newSelectedOption ? [newSelectedOption.value, newSelectedOption.label] : ['', '']; setFieldValue(key, value); setFieldValue(labelFieldKey, label); setSelectedOption(newSelectedOption || {}); setSelectedOptionId(newSelectedOption?.value || ''); // keep previous selection, if new selection is empty if (newSelectedOption) { setPrevSelection(newSelectedOption); } }, [setValue, filteredOptions, key, setSearchInput, setSelectedOptionId, setFieldValue, labelFieldKey, setPrevSelection]); const combobox = (0, _react2.useComboboxStore)({ value: selectedOption?.value === '' && (searchInput === selectedOption.label || searchInput === '') ? '' : searchInput || usedValueLabel, setValue: handleOnSelectChange, setSelectedValue: handleOnSelectChange }); const handleBlur = (0, _react.useCallback)(() => { setSelectedOptionId(''); setIsFocused(false); }, [setSelectedOptionId, setIsFocused]); const resetValues = (0, _react.useCallback)(() => { setSelectedOptionId(' '); setPrevSelection({}); setFieldValue(key, ''); setFieldValue(groupKey, ''); setFieldValue(labelFieldKey, undefined); setTimeout(() => { try { inputRef.current.select(); } catch (e) { // ignore } }, 50); }, [key, setFieldValue, labelFieldKey, groupKey, setSelectedOptionId, setPrevSelection]); const className = (0, _react.useMemo)(() => [_BSRAddressSelectorModule.default.comboboxWrapper, _BSRAddressSelectorModule.default.formControl, _BSRAddressSelectorModule.default.ariakitCombobox, 'ariakit-combobox', 'form-control py-1 px-0', isFocused && 'focused', isInvalid && 'is-invalid'].filter(Boolean).join(' '), [isFocused, isInvalid]); const formControlClassNames = (0, _index.getFormControlClassNames)(disabled, readOnly, disabled, ['input', field.className].filter(Boolean)); const getPreviousSelectedItem = (0, _react.useCallback)(() => { return prevSelection?.value ? prevSelection.value : field.value; }, [prevSelection, field.value]); if (dontShowSelector) { return null; } if (viewMode) { return usedValueLabel?.label || ''; } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_react2.ComboboxProvider, null, /*#__PURE__*/_react.default.createElement("div", { className: className }, /*#__PURE__*/_react.default.createElement(_react2.Combobox, _extends({ store: combobox, autoSelect: true, getAutoSelectId: getPreviousSelectedItem, className: formControlClassNames, focusable: true, placeholder: placeholder, onBlur: handleBlur, onFocus: event => { setIsFocused(true); event.target.select(); }, ref: inputRef }, inputProps)), selectedOption.value && /*#__PURE__*/_react.default.createElement(_react2.ComboboxCancel, { className: `btn ${_BSRAddressSelectorModule.default.comboboxCancel || ''}`, store: combobox, render: /*#__PURE__*/_react.default.createElement("button", { type: "button", onClick: resetValues, ref: cancelButtonRef, className: `form-control btn ${_BSRAddressSelectorModule.default.comboboxCancel || ''}`, "aria-label": `${t('aria.deleteField', { label: t(labelText) })}` }) }), filteredOptions.length > 0 && /*#__PURE__*/_react.default.createElement(_react2.ComboboxDisclosure, { className: `btn ${_BSRAddressSelectorModule.default.comboboxArrow}`, store: combobox })), filteredOptions.length > 0 && /*#__PURE__*/_react.default.createElement("div", { className: `${_BSRAddressSelectorModule.default.comboboxPopoverWrapper} comboboxPopoverWrapper` }, /*#__PURE__*/_react.default.createElement(_react2.ComboboxPopover, { store: combobox, gutter: 4, focusable: true, className: `${_BSRAddressSelectorModule.default.popover}`, sameWidth: true, autoFocusOnShow: false, initialFocus: preselectedItemRef }, filteredOptions.map((_ref2, index) => { let { value, label } = _ref2; return /*#__PURE__*/_react.default.createElement(_react2.ComboboxItem, { key: `k${value}-${index}`, id: value, value: value, className: `${_BSRAddressSelectorModule.default.comboboxItem} dropdown-item ${value === prevSelection.value ? 'selected' : ''}`, ref: value === prevSelection.value ? preselectedItemRef : undefined }, label); }))))); }; BSRAutocompleteField.propTypes = { config: _propTypes.default.shape({ controllerPath: _propTypes.default.string, key: _propTypes.default.string.isRequired, groupKey: _propTypes.default.string, labelTemplate: _propTypes.default.string, inputProps: _propTypes.default.instanceOf(Object), scope: _propTypes.default.string, additionalQuery: _propTypes.default.string, doSubmitOnChange: _propTypes.default.bool, readOnly: _propTypes.default.bool, labelText: _propTypes.default.string, viewMode: _propTypes.default.bool, disabled: _propTypes.default.bool, suggestionsAmount: _propTypes.default.number, placeholder: _propTypes.default.string }).isRequired }; const HigherOrderAutocompleteField = (0, _withFieldGroup.default)(BSRAutocompleteField); const WrappedAutocompleteField = props => { const { dontShowSelector } = (0, _react.useContext)(_BSRAddressContext.BSRAddressContext); if (dontShowSelector) { return null; } return /*#__PURE__*/_react.default.createElement(_FormFieldWrapper.default, props, /*#__PURE__*/_react.default.createElement(HigherOrderAutocompleteField, props)); }; (0, _formBuilder.registerComponent)('BSRAutocompleteField', WrappedAutocompleteField); (0, _formBuilder.registerComponent)('BSRAutocompleteFieldBasic', HigherOrderAutocompleteField); var _default = exports.default = BSRAutocompleteField;