UNPKG

@ozen-ui/kit

Version:

React component library

202 lines (201 loc) 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Autocomplete = void 0; var tslib_1 = require("tslib"); require("./Autocomplete.css"); var react_1 = tslib_1.__importStar(require("react")); var useControlled_1 = require("../../hooks/useControlled"); var useDeprecated_1 = require("../../hooks/useDeprecated"); var useMultiRef_1 = require("../../hooks/useMultiRef"); var useMutableRef_1 = require("../../hooks/useMutableRef"); var useThemeProps_1 = require("../../hooks/useThemeProps"); var isKeys_1 = require("../../utils/isKeys"); var DataList_1 = require("../DataList"); var Input_1 = require("../Input"); var classNames_1 = require("./classNames"); var components_1 = require("./components"); var constants_1 = require("./constants"); var helper_1 = require("./helper"); function AutocompleteRender(inProps, ref) { var props = (0, useThemeProps_1.useThemeProps)({ props: inProps, name: 'Autocomplete', }); var _a = (0, helper_1.withDefaultGetters)(props), _b = _a.disabled, disabled = _b === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_DISABLED : _b, _c = _a.required, required = _c === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_REQUIRED : _c, _d = _a.autoFocus, autoFocus = _d === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_AUTOFOCUS : _d, _e = _a.fullWidth, fullWidth = _e === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_FULLWIDTH : _e, _f = _a.size, size = _f === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_SIZE : _f, _g = _a.allowCustomValue, allowCustomValue = _g === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_ALLOW_CUSTOM_VALUE : _g, _h = _a.disableShowChevron, disableShowChevron = _h === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_DISABLE_SHOW_CHEVRON : _h, _j = _a.disableClearButton, disableClearButton = _j === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_DISABLE_CLEAR_BUTTON : _j, _k = _a.disableShowEmptyOptionsList, disableShowEmptyOptionsList = _k === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_DISABLE_SHOW_EMPTY_OPTIONS_LIST : _k, _l = _a.disableCloseOnSelect, disableCloseOnSelect = _l === void 0 ? constants_1.AUTOCOMPLETE_DEFAULT_DISABLE_CLOSE_ON_SELECT : _l, _m = _a.renderInput, renderInput = _m === void 0 ? function (props) { return react_1.default.createElement(Input_1.Input, tslib_1.__assign({}, props, { ref: ref })); } : _m, searchFunctionProp = _a.searchFunction, renderOptionProp = _a.renderOption, inputValueProp = _a.inputValue, className = _a.className, valueProp = _a.value, options = _a.options, defaultValue = _a.defaultValue, error = _a.error, onChange = _a.onChange, onInputChange = _a.onInputChange, label = _a.label, placeholder = _a.placeholder, renderLeft = _a.renderLeft, renderRight = _a.renderRight, hint = _a.hint, getOptionKey = _a.getOptionKey, getOptionLabel = _a.getOptionLabel, getOptionDisabled = _a.getOptionDisabled, dataListProps = _a.dataListProps, onCloseProp = _a.onClose, onOpenProp = _a.onOpen, openProp = _a.open, defaultOpen = _a.defaultOpen, loading = _a.loading, inputProps = _a.inputProps, bodyProps = _a.bodyProps, onKeyDown = _a.onKeyDown, noOptionsText = _a.noOptionsText, clearText = _a.clearText, openText = _a.openText, loadingText = _a.loadingText, closeText = _a.closeText, other = tslib_1.__rest(_a, ["disabled", "required", "autoFocus", "fullWidth", "size", "allowCustomValue", "disableShowChevron", "disableClearButton", "disableShowEmptyOptionsList", "disableCloseOnSelect", "renderInput", "searchFunction", "renderOption", "inputValue", "className", "value", "options", "defaultValue", "error", "onChange", "onInputChange", "label", "placeholder", "renderLeft", "renderRight", "hint", "getOptionKey", "getOptionLabel", "getOptionDisabled", "dataListProps", "onClose", "onOpen", "open", "defaultOpen", "loading", "inputProps", "bodyProps", "onKeyDown", "noOptionsText", "clearText", "openText", "loadingText", "closeText"]); (0, useDeprecated_1.useDeprecatedComponent)('Autocomplete'); var anchorRef = (0, react_1.useRef)(null); var lastInputValue = (0, react_1.useRef)(''); var inputBodyRef = (0, useMultiRef_1.useMultiRef)([anchorRef, bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.ref]); var _o = tslib_1.__read((0, useControlled_1.useControlled)({ value: inputValueProp, defaultValue: '', name: 'Autocomplete', state: 'inputValue', }), 2), inputValue = _o[0], setInputValue = _o[1]; var _p = tslib_1.__read((0, useControlled_1.useControlled)({ value: valueProp, defaultValue: defaultValue, name: 'Autocomplete', state: 'value', }), 2), valueState = _p[0], setValueState = _p[1]; var _q = tslib_1.__read((0, useControlled_1.useControlled)({ value: openProp, defaultValue: defaultOpen, name: 'Autocomplete', state: 'open', }), 2), open = _q[0], setOpen = _q[1]; var searchFunctionDefault = function (options, searchValue) { return options === null || options === void 0 ? void 0 : options.filter(function (option) { return getOptionLabel(option).toLowerCase().includes(searchValue.toLowerCase()); }); }; var savedOnInputChange = (0, useMutableRef_1.useMutableRef)(onInputChange); var savedGetOptionLabel = (0, useMutableRef_1.useMutableRef)(getOptionLabel); var _r = tslib_1.__read((0, react_1.useState)(tslib_1.__spreadArray([], tslib_1.__read(options), false)), 2), filteredOptions = _r[0], setFilteredOptions = _r[1]; var searchFunction = searchFunctionProp || searchFunctionDefault; var dataListValue = valueState ? getOptionKey(valueState) : ''; var hasOptions = !!(filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.length); var showNoOptions = !hasOptions && !loading; var showLoading = !hasOptions && !!loading; var _s = tslib_1.__read((0, react_1.useState)(undefined), 2), search = _s[0], setSearch = _s[1]; var changeInputValue = function (e, value) { var _a; setInputValue(value); (_a = savedOnInputChange.current) === null || _a === void 0 ? void 0 : _a.call(savedOnInputChange, e, value); }; var callOnChange = function (e, value) { var inputValue = value ? getOptionLabel(value) : ''; setValueState(value); onChange === null || onChange === void 0 ? void 0 : onChange(e, value); changeInputValue(null, inputValue); }; /** Эффект — фильтрация списка при изменении опций */ (0, react_1.useEffect)(function () { if (!open) return; setFilteredOptions(search ? search(options) : tslib_1.__spreadArray([], tslib_1.__read(options), false)); }, [search, options, open, searchFunctionProp]); /** Эффект — синхронизируем значение текстового поля со значением в списке */ (0, react_1.useEffect)(function () { var _a; if (allowCustomValue) return; lastInputValue.current = valueState ? (_a = savedGetOptionLabel.current) === null || _a === void 0 ? void 0 : _a.call(savedGetOptionLabel, valueState) : ''; if (lastInputValue.current !== inputValue) { changeInputValue(null, lastInputValue.current); } }, [valueState, allowCustomValue]); /** Эффект — автофокусировка в текстовом поле */ (0, react_1.useEffect)(function () { var _a; if (autoFocus) (_a = anchorRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, [autoFocus]); /** Ждём выполнения анимации на закрытие и делаем сброс функции фильтрации */ var handleExited = function () { var _a; setSearch(undefined); (_a = dataListProps === null || dataListProps === void 0 ? void 0 : dataListProps.onExited) === null || _a === void 0 ? void 0 : _a.call(dataListProps); }; /** Закрытие */ var handleClose = function () { setOpen(false); onCloseProp === null || onCloseProp === void 0 ? void 0 : onCloseProp(); }; /** Открытие */ var handleOpen = function () { setOpen(true); onOpenProp === null || onOpenProp === void 0 ? void 0 : onOpenProp(); }; /** Переключатель открытия и закрытия */ var handleToggle = function () { if (disabled) return; if (open) handleClose(); else handleOpen(); }; /** Очистка поля */ var handleClear = function (e) { callOnChange(e, null); setSearch(undefined); }; /** Актуализация значения текстового поля после его покидания */ var handleBlur = function (e) { var _a; (_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onBlur) === null || _a === void 0 ? void 0 : _a.call(inputProps, e); if (allowCustomValue) return; if (inputValue !== lastInputValue.current) { changeInputValue(null, lastInputValue.current); } }; /** Открытие списка по клику на текстовом поле */ var handleClickOnInput = function (e) { var _a; handleToggle(); (_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onClick) === null || _a === void 0 ? void 0 : _a.call(inputProps, e); }; /** Управление элементом контроля через клавиатуру */ var handleKeyDown = function (e) { if ((0, isKeys_1.isKeys)(e, ['ArrowDown', 'ArrowUp']) && !open) { e.preventDefault(); handleToggle(); } onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e); }; /** Событие ввода значения в текстовом поле */ var handleChangeInput = function (e) { var value = e.target.value; // Открываем список при вводе первого символа (при условии, что список еще не открыт) if (value.length && !open) handleOpen(); if (value) { changeInputValue(e, value); } else { callOnChange(e, null); } // Задаем функцию фильтрации setSearch(function () { return function (options) { return searchFunction(tslib_1.__spreadArray([], tslib_1.__read(options), false), value); }; }); }; /** Событие выбора значения из раскрывающегося списка */ var handleChangeDataList = function (e, _a) { var value = _a.value; var selectedOption = filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.find(function (option) { return (getOptionKey === null || getOptionKey === void 0 ? void 0 : getOptionKey(option)) === value; }); callOnChange(e, selectedOption || null); // Закрываем список после выбора if (!disableCloseOnSelect) handleClose(); }; /** Отображение текстового поля */ var input = renderInput(tslib_1.__assign(tslib_1.__assign({ size: size, hint: hint, disabled: disabled, label: label, required: required, error: error, fullWidth: fullWidth, placeholder: placeholder, renderLeft: renderLeft }, other), { renderRight: (react_1.default.createElement(components_1.AutocompleteRenderRight, { open: open, size: size, disabled: disabled, clearText: clearText, closeText: closeText, openText: openText, hasValue: !!inputValue, onClear: handleClear, onOpen: handleToggle, renderRight: renderRight, disableShowChevron: disableShowChevron, disableClearButton: disableClearButton })), onChange: handleChangeInput, onKeyDown: handleKeyDown, value: inputValue || '', inputProps: tslib_1.__assign(tslib_1.__assign({ autoComplete: 'off' }, inputProps), { onBlur: handleBlur, onClick: handleClickOnInput }), bodyProps: tslib_1.__assign(tslib_1.__assign({ 'aria-expanded': open }, bodyProps), { ref: inputBodyRef }), className: (0, classNames_1.cnAutocomplete)({ size: size, hasChevron: !disableShowChevron }, [ className, ]), ref: ref })); /** Отображение опций */ var renderOptions = filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.map(function (option) { var selected = valueState ? getOptionKey(valueState) === getOptionKey(option) : false; var renderOptionDefault = function (_a) { var option = _a.option; return (react_1.default.createElement(DataList_1.DataListOption, { key: getOptionKey === null || getOptionKey === void 0 ? void 0 : getOptionKey(option), label: getOptionLabel === null || getOptionLabel === void 0 ? void 0 : getOptionLabel(option), value: getOptionKey === null || getOptionKey === void 0 ? void 0 : getOptionKey(option), disabled: getOptionDisabled === null || getOptionDisabled === void 0 ? void 0 : getOptionDisabled(option) })); }; var renderOption = renderOptionProp || renderOptionDefault; return renderOption({ option: option, selected: selected }); }); return (react_1.default.createElement(react_1.default.Fragment, null, input, react_1.default.createElement(DataList_1.DataList, tslib_1.__assign({ size: size, equalAnchorWidth: true, offset: [0, 4], placement: "bottom-start" }, dataListProps, { open: open, onClose: handleClose, anchorRef: anchorRef, onExited: handleExited, selected: dataListValue, onSelect: handleChangeDataList, listProps: tslib_1.__assign({ role: 'listbox' }, dataListProps === null || dataListProps === void 0 ? void 0 : dataListProps.listProps) }), renderOptions, !disableShowEmptyOptionsList && (react_1.default.createElement(components_1.AutocompleteNoOptions, { showNoOptions: showNoOptions, noOptionsText: noOptionsText })), react_1.default.createElement(components_1.AutocompleteLoading, { showLoading: showLoading, loadingText: loadingText, size: size })))); } /** * @deprecated Компонент устарел. Для замены используйте компонент AutocompleteNext */ exports.Autocomplete = (0, react_1.forwardRef)(AutocompleteRender); exports.Autocomplete.displayName = 'Autocomplete';