@ozen-ui/kit
Version:
React component library
182 lines (181 loc) • 10.9 kB
JavaScript
import { __assign, __read, __rest, __spreadArray } from "tslib";
import './Select.css';
import React, { Children, forwardRef, isValidElement, useEffect, useRef, } from 'react';
import { logger } from '@ozen-ui/logger';
import { isFragment } from 'react-is';
import { useControlled } from '../../hooks/useControlled';
import { useDevEffect } from '../../hooks/useDevEffect';
import { useMultiRef } from '../../hooks/useMultiRef';
import { useThemeProps } from '../../hooks/useThemeProps';
import { isArray, isNotNil } from '../../utils';
import { isKeys } from '../../utils/isKeys';
import { isString } from '../../utils/isString';
import { DataList, DataListOption } from '../DataList';
import { SelectInput } from './components';
import { SELECT_DEFAULT_AUTO_FOCUS, SELECT_DEFAULT_DEFAULT_OPEN, SELECT_DEFAULT_DISABLED, SELECT_DEFAULT_FULL_WIDTH, SELECT_DEFAULT_RENDER_RIGHT_LAYOUT, SELECT_DEFAULT_MULTILINE, SELECT_DEFAULT_REQUIRED, SELECT_DEFAULT_SIZE, SELECT_DEFAULT_SHOW_CLEAR_BUTTON, SELECT_DEFAULT_ERROR, } from './constants';
import { isMultipleLabel, isMultipleParams, isNotMultipleLabel, isNotMultipleParams, } from './helpers';
import { SelectContext } from './SelectContext';
var SelectRender = function (inProps, ref) {
var props = useThemeProps({
props: inProps,
name: 'Select',
});
var _a = props.size, size = _a === void 0 ? SELECT_DEFAULT_SIZE : _a, _b = props.autoFocus, autoFocus = _b === void 0 ? SELECT_DEFAULT_AUTO_FOCUS : _b, _c = props.fullWidth, fullWidth = _c === void 0 ? SELECT_DEFAULT_FULL_WIDTH : _c, _d = props.disabled, disabled = _d === void 0 ? SELECT_DEFAULT_DISABLED : _d, _e = props.required, required = _e === void 0 ? SELECT_DEFAULT_REQUIRED : _e, _f = props.multiline, multiline = _f === void 0 ? SELECT_DEFAULT_MULTILINE : _f, _g = props.defaultOpen, defaultOpen = _g === void 0 ? SELECT_DEFAULT_DEFAULT_OPEN : _g, _h = props.renderRightLayout, renderRightLayout = _h === void 0 ? SELECT_DEFAULT_RENDER_RIGHT_LAYOUT : _h, valueProp = props.value, defaultValue = props.defaultValue, onChange = props.onChange, renderValueProp = props.renderValue, children = props.children, onClick = props.onClick, onKeyDown = props.onKeyDown, menuProps = props.menuProps, dataListPropsProp = props.dataListProps, bodyProps = props.bodyProps, bodyRefProp = props.bodyRef, onCloseProp = props.onClose, onOpenProp = props.onOpen, openProp = props.open, multiple = props.multiple, _j = props.showClearButton, showClearButton = _j === void 0 ? SELECT_DEFAULT_SHOW_CLEAR_BUTTON : _j, renderLeft = props.renderLeft, renderRight = props.renderRight, onClearProp = props.onClear, clearText = props.clearText, _k = props.error, error = _k === void 0 ? SELECT_DEFAULT_ERROR : _k, other = __rest(props, ["size", "autoFocus", "fullWidth", "disabled", "required", "multiline", "defaultOpen", "renderRightLayout", "value", "defaultValue", "onChange", "renderValue", "children", "onClick", "onKeyDown", "menuProps", "dataListProps", "bodyProps", "bodyRef", "onClose", "onOpen", "open", "multiple", "showClearButton", "renderLeft", "renderRight", "onClear", "clearText", "error"]);
useDevEffect(function () {
if (renderRightLayout.length !== new Set(renderRightLayout).size) {
logger.error("\u0412 renderRightLayout [".concat(renderRightLayout.map(function (element) { return "\"".concat(element, "\""); }).join(', '), "] \u0435\u0441\u0442\u044C \u0434\u0443\u0431\u043B\u0438\u0440\u0443\u044E\u0449\u0438\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u044B, \u043F\u0435\u0440\u0435\u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0435\u043C\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435."));
}
}, [renderRightLayout]);
var bodyInnerRef = useRef(null);
var bodyRef = useMultiRef([(bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.ref) || bodyRefProp, bodyInnerRef]);
var dataListProps = dataListPropsProp || menuProps;
var _l = __read(useControlled({
value: valueProp,
defaultValue: defaultValue,
name: 'Select',
state: 'value',
}), 2), valueState = _l[0], setValueState = _l[1];
var _m = __read(useControlled({
value: openProp,
defaultValue: defaultOpen,
name: 'Select',
state: 'open',
}), 2), open = _m[0], setOpen = _m[1];
var currentLabel;
var isNotSelectOption = function (child) { return !isValidElement(child) || child.type !== DataListOption; };
var resolvedChildren = isFragment(children)
? children.props.children
: children;
Children.forEach(resolvedChildren, function (child) {
var _a, _b;
if (!isNotSelectOption(child)) {
var label = isString(child.props.children)
? (_a = child.props.label) !== null && _a !== void 0 ? _a : child.props.children
: child.props.label;
var params = __assign(__assign({}, inProps), { multiple: multiple, value: valueState });
if (isMultipleParams(params) &&
isMultipleLabel(currentLabel, multiple)) {
var selected = (_b = params.value) === null || _b === void 0 ? void 0 : _b.includes(child.props.value);
if (selected)
currentLabel = __spreadArray(__spreadArray([], __read((currentLabel || [])), false), [
label,
], false);
}
if (isNotMultipleParams(params) &&
isNotMultipleLabel(currentLabel, multiple)) {
var selected = params.value === child.props.value;
if (selected)
currentLabel = label;
}
}
});
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 handleClick = function (e) {
onClick === null || onClick === void 0 ? void 0 : onClick(e);
handleToggle();
};
/** Управление элементом контроля через клавиатуру */
var handleKeyDown = function (e) {
if (isKeys(e, ['Space', 'ArrowDown', 'ArrowUp']) && !open) {
e.preventDefault();
handleToggle();
}
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
};
/** Событие выбора значения из раскрывающегося списка */
var handleChange = function (e, _a) {
var value = _a.value;
setValueState(value);
onChange === null || onChange === void 0 ? void 0 : onChange(value, e);
if (!multiple)
handleClose();
};
/** Представление значение элемента контроля по умолчанию */
var renderDefaultValue = function (option) {
var _a = option.label, label = _a === void 0 ? '' : _a, _b = option.value, value = _b === void 0 ? '' : _b;
if (!value && value !== 0)
return null;
if (isMultipleLabel(label, multiple))
return React.createElement("span", null, __spreadArray([], __read(label), false).join(', '));
if (isNotMultipleLabel(label, multiple))
return React.createElement("span", null, label);
return null;
};
/** Значение для текстового поля компонента */
var inputValue = function () {
var _a;
var params = {
multiple: multiple,
value: valueState,
};
if (isMultipleParams(params))
return ((_a = params.value) === null || _a === void 0 ? void 0 : _a.join(',')) || '';
if (isNotMultipleParams(params))
return (params === null || params === void 0 ? void 0 : params.value) || '';
return '';
};
var onClear = function (event) {
setValueState(undefined);
onChange === null || onChange === void 0 ? void 0 : onChange(undefined, event);
onClearProp === null || onClearProp === void 0 ? void 0 : onClearProp(event);
};
/** Представление значение элемента контроля */
var renderValue = renderValueProp || renderDefaultValue;
var hasValue = isArray(valueState)
? valueState.length > 0
: isNotNil(valueState);
useEffect(function () {
var _a;
/** Устанавливает фокус на элементе контроля
* если компонент по умолчанию открыт — defaultOpen={true}
* и является неконтролируемым */
if (defaultOpen)
(_a = bodyInnerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
}, []);
useEffect(function () {
var _a;
/** Автофокус на элементе контроля */
if (autoFocus)
(_a = bodyInnerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
}, [autoFocus]);
return (React.createElement(SelectContext.Provider, { value: {
open: open !== null && open !== void 0 ? open : false,
hasValue: hasValue,
size: size,
disabled: disabled,
renderRightLayout: renderRightLayout,
showClearButton: showClearButton,
fullWidth: fullWidth,
required: required,
multiline: multiline,
onClear: onClear,
clearText: clearText !== null && clearText !== void 0 ? clearText : '',
renderRight: renderRight,
renderLeft: renderLeft,
error: error,
} },
React.createElement(SelectInput, __assign({}, other, { value: inputValue(), onClick: handleClick, onKeyDown: handleKeyDown, renderedValue: renderValue({
label: currentLabel,
value: valueState,
}), bodyProps: __assign(__assign({}, bodyProps), { ref: bodyRef }), ref: ref })),
React.createElement(DataList, __assign({ equalAnchorWidth: true }, dataListProps, { listProps: __assign({ size: size, role: 'listbox' }, dataListProps === null || dataListProps === void 0 ? void 0 : dataListProps.listProps), open: open, multiple: multiple, onClose: handleClose, onSelect: handleChange, anchorRef: bodyInnerRef,
// TODO: add `null` to `DataListSelected`
selected: (valueState || null) }), children)));
};
export var Select = forwardRef(SelectRender);
Select.displayName = 'Select';