UNPKG

@ozen-ui/kit

Version:

React component library

148 lines (147 loc) 8.23 kB
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';