@ozen-ui/kit
Version:
React component library
148 lines (147 loc) • 8.23 kB
JavaScript
import { __assign, __read, __rest, __spreadArray } from "tslib";
import './DataList.css';
import React, { useMemo, Children, isValidElement, cloneElement, useRef, useEffect, } from 'react';
import { isFragment } from 'react-is';
import { useControlled } from '../../hooks/useControlled';
import { useEventListener } from '../../hooks/useEventListener';
import { useMultiRef } from '../../hooks/useMultiRef';
import { usePrevious } from '../../hooks/usePrevious';
import { useThemeProps } from '../../hooks/useThemeProps';
import { cn } from '../../utils/classname';
import { getPaperSizeToFormElement } from '../../utils/getPaperSizeToFormElement';
import { isKey } from '../../utils/isKey';
import { polymorphicComponentWithRef } from '../../utils/polymorphicComponentWithRef';
import { scrollContainerToElement } from '../../utils/scrollContainerToElement';
import { List } from '../List';
import { Popover } from '../Popover';
import { DATA_LIST_DEFAULT_SIZE, DATA_LIST_DEFAULT_OPEN, DATA_LIST_DEFAULT_TAG, } from './constants';
import { isMultipleParams, isNotMultipleParams } from './helpers';
import { DataListOption } from './index';
import { lastSelectedValue, useDataListNavigation } from './utils';
export var cnDataList = cn('DataList');
var DataListRender = function (inProps, ref) {
var _a = useThemeProps({ props: inProps, name: 'DataList' }), _b = _a.as, as = _b === void 0 ? DATA_LIST_DEFAULT_TAG : _b, _c = _a.open, open = _c === void 0 ? DATA_LIST_DEFAULT_OPEN : _c, _d = _a.size, size = _d === void 0 ? DATA_LIST_DEFAULT_SIZE : _d, name = _a.name, onClose = _a.onClose, children = _a.children, listProps = _a.listProps, anchorRef = _a.anchorRef, className = _a.className, defaultSelected = _a.defaultSelected, onSelectProp = _a.onSelect, selectedProp = _a.selected, other = __rest(_a, ["as", "open", "size", "name", "onClose", "children", "listProps", "anchorRef", "className", "defaultSelected", "onSelect", "selected"]);
var dataListRef = useRef(null);
var listRef = useRef(null);
var radius = getPaperSizeToFormElement(size);
var _e = __read(useControlled({
name: 'DataList',
defaultValue: defaultSelected,
value: selectedProp,
state: 'selected',
}), 2), selectedState = _e[0], setSelected = _e[1];
var nodes = useMemo(function () { return new Map(); }, [children]);
var items = useMemo(function () { return new Array(); }, [children]);
var resolvedChildren = isFragment(children)
? children.props.children
: children;
var isNotSelectOption = function (child) {
return !isValidElement(child) ||
child.type !== DataListOption ||
!!child.props.disabled;
};
useEffect(function () {
Children.forEach(resolvedChildren, function (child, idx) {
if (!isNotSelectOption(child)) {
items.push(child.props.value);
nodes.set(child.props.value, idx);
}
});
}, [children]);
var handleSelect = function (event, value) {
var onSelect = function (e, payload) {
setSelected(payload.value);
onSelectProp === null || onSelectProp === void 0 ? void 0 : onSelectProp(e, payload);
};
var params = __assign(__assign({}, inProps), { onSelect: onSelect, selected: selectedState });
if (isNotMultipleParams(params)) {
var onSelect_1 = params.onSelect;
onSelect_1 === null || onSelect_1 === void 0 ? void 0 : onSelect_1(event, { name: name, value: value });
}
if (isMultipleParams(params)) {
var selected = params.selected, onSelect_2 = params.onSelect;
var res = (selected === null || selected === void 0 ? void 0 : selected.includes(value || ''))
? selected.filter(function (item) { return item !== value; })
: __spreadArray(__spreadArray([], __read((selected || [])), false), [value], false);
onSelect_2 === null || onSelect_2 === void 0 ? void 0 : onSelect_2(event, { name: name, value: res });
}
};
var handleOnMouseDownList = function (e) {
var _a;
e.preventDefault();
(_a = listProps === null || listProps === void 0 ? void 0 : listProps.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(listProps, e);
};
// Навигация по списку
var _f = useDataListNavigation({
items: items,
active: open,
selected: lastSelectedValue(selectedState),
onSelect: function (event, item) {
handleSelect(event, item !== null && item !== void 0 ? item : '');
},
}), current = _f.current, focused = _f.focused, onKeyDown = _f.onKeyDown, onClick = _f.onClick;
var previousCurrent = usePrevious(current);
useEffect(function () {
var _a, _b;
var firstCurrent = (previousCurrent === null || previousCurrent === undefined) &&
current !== null &&
current !== undefined
? current
: undefined;
var selected = focused !== null && focused !== void 0 ? focused : firstCurrent;
// Находит элемент по ключу
var idx = selected !== undefined && selected !== null
? nodes.get(selected)
: undefined;
if (idx !== undefined) {
// Прокрутка списка
scrollContainerToElement({
container: dataListRef.current,
element: (_b = (_a = listRef.current) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b[idx],
behavior: focused !== undefined && focused !== null ? 'smooth' : 'instant',
});
}
}, [focused, current, previousCurrent]);
// Назначает элементу контроля событие управления списком с клавиатуры
useEventListener({
eventName: 'keydown',
element: anchorRef,
handler: function (event) { return onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event); },
});
// Закрывает список по нажатию на клавишу {Tab}
useEventListener({
active: open,
eventName: 'keydown',
element: anchorRef,
handler: function (event) {
if (isKey(event, 'Tab'))
onClose === null || onClose === void 0 ? void 0 : onClose();
},
});
// Представление раскрывающегося списка
var renderChildren = useMemo(function () {
return Children.map(resolvedChildren, function (child) {
if (isNotSelectOption(child)) {
return child;
}
var key = child.props.value;
var elementChild = child;
var props = {
onClick: function (event) {
var _a, _b;
onClick === null || onClick === void 0 ? void 0 : onClick(event, key);
(_b = (_a = child.props).onClick) === null || _b === void 0 ? void 0 : _b.call(_a, event);
},
focused: key === focused,
selected: Array.isArray(selectedState)
? selectedState.includes(key)
: selectedState === key,
};
return cloneElement(elementChild, props);
});
}, [resolvedChildren, selectedState, focused, onClick]);
return (React.createElement(Popover, __assign({ open: open, offset: [0, 4], radius: radius, onClose: onClose, strategy: "absolute", anchorRef: anchorRef, as: as, placement: "bottom-start", transitionProps: { classNames: 'DataList-animation' }, className: cnDataList('', [className]), disableReturnFocus: true, disableEnforceFocus: true }, other, { ref: useMultiRef([ref, dataListRef]) }),
React.createElement(List, __assign({ as: "ul", size: size }, listProps, { onMouseDown: handleOnMouseDownList, ref: useMultiRef([listRef, listProps === null || listProps === void 0 ? void 0 : listProps.ref]) }), renderChildren)));
};
export var DataList = polymorphicComponentWithRef(DataListRender);
DataList.displayName = 'DataList';