@ozen-ui/kit
Version:
React component library
202 lines (201 loc) • 14.6 kB
JavaScript
"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';