UNPKG

rsuite

Version:

A suite of react components

385 lines (382 loc) 17.5 kB
'use client'; import _extends from "@babel/runtime/helpers/esm/extends"; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; var _excluded = ["as", "appearance", "classPrefix", "defaultValue", "columnHeight", "columnWidth", "childrenKey", "cleanable", "data", "disabled", "disabledItemValues", "value", "valueKey", "labelKey", "locale", "toggleAs", "style", "countable", "cascade", "placeholder", "placement", "popupClassName", "popupStyle", "searchable", "uncheckableItemValues", "id", "getChildren", "renderValue", "renderExtraFooter", "renderColumn", "renderTreeNode", "onEntered", "onExited", "onClean", "onSearch", "onSelect", "onChange", "onCheck", "menuClassName", "menuStyle", "menuWidth", "menuHeight", "renderMenu", "renderMenuItem"]; import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import pick from 'lodash/pick'; import omit from 'lodash/omit'; import isFunction from 'lodash/isFunction'; import isNil from 'lodash/isNil'; import { findNodeOfTree } from "../internals/Tree/utils/index.js"; import { useClassNames, useControlled, useEventCallback } from "../internals/hooks/index.js"; import { getColumnsAndPaths } from "../CascadeTree/utils.js"; import { createChainedFunction, mergeRefs } from "../internals/utils/index.js"; import { PickerToggle, PickerPopup, SelectedElement, PickerToggleTrigger, usePickerClassName, usePickerRef, useToggleKeyDownEvent, useFocusItemValue, pickTriggerPropKeys, omitTriggerPropKeys, listPickerPropTypes } from "../internals/Picker/index.js"; import { deprecatePropTypeNew } from "../internals/propTypes/index.js"; import { useCascadeValue, useSearch, useSelect } from "../MultiCascadeTree/hooks/index.js"; import TreeView from "../MultiCascadeTree/TreeView.js"; import SearchView from "../MultiCascadeTree/SearchView.js"; import useActive from "../Cascader/useActive.js"; import { oneOf } from "../internals/propTypes/index.js"; import { useCustom } from "../CustomProvider/index.js"; var emptyArray = []; /** * The `MultiCascader` component is used to select multiple values from cascading options. * @see https://rsuitejs.com/components/multi-cascader/ */ var MultiCascader = /*#__PURE__*/React.forwardRef(function (props, ref) { var _selectedPaths; var _useCustom = useCustom('MultiCascader', props), propsWithDefaults = _useCustom.propsWithDefaults, rtl = _useCustom.rtl; var _propsWithDefaults$as = propsWithDefaults.as, Component = _propsWithDefaults$as === void 0 ? 'div' : _propsWithDefaults$as, _propsWithDefaults$ap = propsWithDefaults.appearance, appearance = _propsWithDefaults$ap === void 0 ? 'default' : _propsWithDefaults$ap, _propsWithDefaults$cl = propsWithDefaults.classPrefix, classPrefix = _propsWithDefaults$cl === void 0 ? 'picker' : _propsWithDefaults$cl, defaultValue = propsWithDefaults.defaultValue, columnHeight = propsWithDefaults.columnHeight, columnWidth = propsWithDefaults.columnWidth, _propsWithDefaults$ch = propsWithDefaults.childrenKey, childrenKey = _propsWithDefaults$ch === void 0 ? 'children' : _propsWithDefaults$ch, _propsWithDefaults$cl2 = propsWithDefaults.cleanable, cleanable = _propsWithDefaults$cl2 === void 0 ? true : _propsWithDefaults$cl2, _propsWithDefaults$da = propsWithDefaults.data, data = _propsWithDefaults$da === void 0 ? emptyArray : _propsWithDefaults$da, disabled = propsWithDefaults.disabled, _propsWithDefaults$di = propsWithDefaults.disabledItemValues, disabledItemValues = _propsWithDefaults$di === void 0 ? emptyArray : _propsWithDefaults$di, valueProp = propsWithDefaults.value, _propsWithDefaults$va = propsWithDefaults.valueKey, valueKey = _propsWithDefaults$va === void 0 ? 'value' : _propsWithDefaults$va, _propsWithDefaults$la = propsWithDefaults.labelKey, labelKey = _propsWithDefaults$la === void 0 ? 'label' : _propsWithDefaults$la, locale = propsWithDefaults.locale, toggleAs = propsWithDefaults.toggleAs, style = propsWithDefaults.style, _propsWithDefaults$co = propsWithDefaults.countable, countable = _propsWithDefaults$co === void 0 ? true : _propsWithDefaults$co, _propsWithDefaults$ca = propsWithDefaults.cascade, cascade = _propsWithDefaults$ca === void 0 ? true : _propsWithDefaults$ca, placeholder = propsWithDefaults.placeholder, _propsWithDefaults$pl = propsWithDefaults.placement, placement = _propsWithDefaults$pl === void 0 ? 'bottomStart' : _propsWithDefaults$pl, popupClassName = propsWithDefaults.popupClassName, popupStyle = propsWithDefaults.popupStyle, _propsWithDefaults$se = propsWithDefaults.searchable, searchable = _propsWithDefaults$se === void 0 ? true : _propsWithDefaults$se, _propsWithDefaults$un = propsWithDefaults.uncheckableItemValues, uncheckableItemValues = _propsWithDefaults$un === void 0 ? emptyArray : _propsWithDefaults$un, id = propsWithDefaults.id, getChildren = propsWithDefaults.getChildren, renderValue = propsWithDefaults.renderValue, renderExtraFooter = propsWithDefaults.renderExtraFooter, renderColumn = propsWithDefaults.renderColumn, renderTreeNode = propsWithDefaults.renderTreeNode, onEntered = propsWithDefaults.onEntered, onExited = propsWithDefaults.onExited, onClean = propsWithDefaults.onClean, onSearch = propsWithDefaults.onSearch, onSelect = propsWithDefaults.onSelect, onChange = propsWithDefaults.onChange, onCheck = propsWithDefaults.onCheck, DEPRECATED_menuClassName = propsWithDefaults.menuClassName, DEPRECATED_menuStyle = propsWithDefaults.menuStyle, DEPRECATED_menuWidth = propsWithDefaults.menuWidth, DEPRECATED_menuHeight = propsWithDefaults.menuHeight, DEPRECATED_renderMenu = propsWithDefaults.renderMenu, DEPRECATED_renderMenuItem = propsWithDefaults.renderMenuItem, rest = _objectWithoutPropertiesLoose(propsWithDefaults, _excluded); var _usePickerRef = usePickerRef(ref), trigger = _usePickerRef.trigger, root = _usePickerRef.root, target = _usePickerRef.target, overlay = _usePickerRef.overlay, searchInput = _usePickerRef.searchInput; var _useClassNames = useClassNames(classPrefix), prefix = _useClassNames.prefix, merge = _useClassNames.merge; var onSelectCallback = useCallback(function (node, cascadePaths, event) { var _trigger$current, _trigger$current$upda; onSelect === null || onSelect === void 0 || onSelect(node, cascadePaths, event); (_trigger$current = trigger.current) === null || _trigger$current === void 0 || (_trigger$current$upda = _trigger$current.updatePosition) === null || _trigger$current$upda === void 0 || _trigger$current$upda.call(_trigger$current); }, [onSelect, trigger]); var _useSelect = useSelect({ data: data, childrenKey: childrenKey, labelKey: labelKey, valueKey: valueKey, onSelect: onSelectCallback, getChildren: getChildren }), selectedPaths = _useSelect.selectedPaths, flattenData = _useSelect.flattenData, columnData = _useSelect.columnData, setColumnData = _useSelect.setColumnData, setSelectedPaths = _useSelect.setSelectedPaths, handleSelect = _useSelect.handleSelect; var _useControlled = useControlled(valueProp, defaultValue), controlledValue = _useControlled[0]; var itemKeys = { childrenKey: childrenKey, labelKey: labelKey, valueKey: valueKey }; var cascadeValueProps = _extends({}, itemKeys, { uncheckableItemValues: uncheckableItemValues, cascade: cascade, value: controlledValue, onCheck: onCheck, onChange: onChange }); var _useCascadeValue = useCascadeValue(cascadeValueProps, flattenData), value = _useCascadeValue.value, setValue = _useCascadeValue.setValue, handleCheck = _useCascadeValue.handleCheck; var selectedItems = flattenData.filter(function (item) { return value.some(function (v) { return v === item[valueKey]; }); }) || []; var onFocusItemCallback = useCallback(function (value) { var _getColumnsAndPaths = getColumnsAndPaths(data, flattenData.find(function (item) { return item[valueKey] === value; }), { getParent: function getParent() { return undefined; }, getChildren: function getChildren(item) { return item[childrenKey]; } }), columns = _getColumnsAndPaths.columns, path = _getColumnsAndPaths.path; setColumnData(columns); setSelectedPaths(path); }, [childrenKey, data, flattenData, setColumnData, setSelectedPaths, valueKey]); // Used to hover the focuse item when trigger `onKeydown` var _useFocusItemValue = useFocusItemValue(selectedPaths === null || selectedPaths === void 0 || (_selectedPaths = selectedPaths[selectedPaths.length - 1]) === null || _selectedPaths === void 0 ? void 0 : _selectedPaths[valueKey], { rtl: rtl, data: flattenData, valueKey: valueKey, defaultLayer: selectedPaths !== null && selectedPaths !== void 0 && selectedPaths.length ? selectedPaths.length - 1 : 0, target: function target() { return overlay.current; }, callback: onFocusItemCallback }), focusItemValue = _useFocusItemValue.focusItemValue, setLayer = _useFocusItemValue.setLayer, setKeys = _useFocusItemValue.setKeys, onFocusItem = _useFocusItemValue.onKeyDown; var onSearchCallback = function onSearchCallback(value, event) { if (value) { setLayer(0); } else if (selectedPaths !== null && selectedPaths !== void 0 && selectedPaths.length) { setLayer(selectedPaths.length - 1); } setKeys([]); onSearch === null || onSearch === void 0 || onSearch(value, event); }; var _useSearch = useSearch({ labelKey: labelKey, valueKey: valueKey, childrenKey: childrenKey, flattenedData: flattenData, uncheckableItemValues: uncheckableItemValues, onSearch: onSearchCallback }), items = _useSearch.items, searchKeyword = _useSearch.searchKeyword, setSearchKeyword = _useSearch.setSearchKeyword, handleSearch = _useSearch.handleSearch; var _useActive = useActive({ onEntered: onEntered, onExited: onExited, target: target, setSearchKeyword: setSearchKeyword }), active = _useActive.active, handleEntered = _useActive.handleEntered, handleExited = _useActive.handleExited; var handleClean = useEventCallback(function (event) { if (disabled || !target.current) { return; } setSelectedPaths([]); setValue([]); setColumnData([data]); onChange === null || onChange === void 0 || onChange([], event); }); var handleMenuPressEnter = useEventCallback(function (event) { var _overlay$current; var focusItem = findNodeOfTree(data, function (item) { return item[valueKey] === focusItemValue; }); var checkbox = (_overlay$current = overlay.current) === null || _overlay$current === void 0 ? void 0 : _overlay$current.querySelector("[data-key=\"" + focusItemValue + "\"] [type=\"checkbox\"]"); if (checkbox) { handleCheck(focusItem, event, (checkbox === null || checkbox === void 0 ? void 0 : checkbox.getAttribute('aria-checked')) !== 'true'); } }); var onPickerKeyDown = useToggleKeyDownEvent(_extends({ toggle: !focusItemValue || !active, trigger: trigger, target: target, overlay: overlay, searchInput: searchInput, active: active, onExit: handleClean, onMenuKeyDown: onFocusItem, onMenuPressEnter: handleMenuPressEnter }, rest)); var renderCascadeColumn = function renderCascadeColumn(childNodes, column) { var items = column.items, parentItem = column.parentItem, layer = column.layer; if (typeof renderColumn === 'function') { return renderColumn(childNodes, column); } else if (typeof DEPRECATED_renderMenu === 'function') { return DEPRECATED_renderMenu(items, childNodes, parentItem, layer); } return childNodes; }; var renderCascadeTreeNode = function renderCascadeTreeNode(node, itemData) { var render = typeof renderTreeNode === 'function' ? renderTreeNode : DEPRECATED_renderMenuItem; if (typeof render === 'function') { return render(node, itemData); } return node; }; var renderTreeView = function renderTreeView(positionProps, speakerRef) { var _ref = positionProps || {}, left = _ref.left, top = _ref.top, className = _ref.className; var styles = _extends({}, DEPRECATED_menuStyle, popupStyle, { left: left, top: top }); var classes = merge(className, DEPRECATED_menuClassName, popupClassName, prefix('popup-multi-cascader')); return /*#__PURE__*/React.createElement(PickerPopup, { ref: mergeRefs(overlay, speakerRef), className: classes, style: styles, target: trigger, onKeyDown: onPickerKeyDown }, searchable && /*#__PURE__*/React.createElement(SearchView, { locale: locale, cascade: cascade, data: items, value: value, searchKeyword: searchKeyword, valueKey: valueKey, labelKey: labelKey, childrenKey: childrenKey, disabledItemValues: disabledItemValues, inputRef: searchInput, onCheck: handleCheck, onSearch: handleSearch }), !searchKeyword && /*#__PURE__*/React.createElement(TreeView, { cascade: cascade, columnWidth: columnWidth !== null && columnWidth !== void 0 ? columnWidth : DEPRECATED_menuWidth, columnHeight: columnHeight !== null && columnHeight !== void 0 ? columnHeight : DEPRECATED_menuHeight, classPrefix: "cascade-tree", uncheckableItemValues: uncheckableItemValues, disabledItemValues: disabledItemValues, valueKey: valueKey, labelKey: labelKey, childrenKey: childrenKey, cascadeData: columnData, cascadePaths: selectedPaths, value: value, onSelect: handleSelect, onCheck: handleCheck, renderColumn: renderCascadeColumn, renderTreeNode: renderCascadeTreeNode }), renderExtraFooter === null || renderExtraFooter === void 0 ? void 0 : renderExtraFooter()); }; var selectedElement = placeholder; if (selectedItems.length > 0) { selectedElement = /*#__PURE__*/React.createElement(SelectedElement, { selectedItems: selectedItems, countable: countable, valueKey: valueKey, labelKey: labelKey, childrenKey: childrenKey, prefix: prefix, cascade: cascade, locale: locale }); } /** * 1.Have a value and the value is valid. * 2.Regardless of whether the value is valid, as long as renderValue is set, it is judged to have a value. */ var hasValue = selectedItems.length > 0 || Number(valueProp === null || valueProp === void 0 ? void 0 : valueProp.length) > 0 && isFunction(renderValue); if (hasValue && isFunction(renderValue)) { selectedElement = renderValue(value.length ? value : valueProp !== null && valueProp !== void 0 ? valueProp : [], selectedItems, selectedElement); // If renderValue returns null or undefined, hasValue is false. if (isNil(selectedElement)) { hasValue = false; } } var _usePickerClassName = usePickerClassName(_extends({}, props, { classPrefix: classPrefix, hasValue: hasValue, countable: countable, name: 'cascader', appearance: appearance, cleanable: cleanable })), classes = _usePickerClassName[0], usedClassNamePropKeys = _usePickerClassName[1]; return /*#__PURE__*/React.createElement(PickerToggleTrigger, { id: id, popupType: "tree", multiple: true, pickerProps: pick(props, pickTriggerPropKeys), ref: trigger, placement: placement, onEnter: handleEntered, onExited: handleExited, speaker: renderTreeView }, /*#__PURE__*/React.createElement(Component, { className: classes, style: style, ref: root }, /*#__PURE__*/React.createElement(PickerToggle, _extends({}, omit(rest, [].concat(omitTriggerPropKeys, usedClassNamePropKeys)), { as: toggleAs, appearance: appearance, disabled: disabled, ref: target, onClean: createChainedFunction(handleClean, onClean), onKeyDown: onPickerKeyDown, cleanable: cleanable && !disabled, hasValue: hasValue, active: active, placement: placement, inputValue: value }), selectedElement || (locale === null || locale === void 0 ? void 0 : locale.placeholder)))); }); MultiCascader.displayName = 'MultiCascader'; MultiCascader.propTypes = _extends({}, listPickerPropTypes, { value: PropTypes.array, disabledItemValues: PropTypes.array, locale: PropTypes.any, appearance: oneOf(['default', 'subtle']), cascade: PropTypes.bool, countable: PropTypes.bool, uncheckableItemValues: PropTypes.array, searchable: PropTypes.bool, onSearch: PropTypes.func, onSelect: PropTypes.func, onCheck: PropTypes.func, inline: deprecatePropTypeNew(PropTypes.bool, 'Use `<MultiCascadeTree>` instead.'), renderMenu: deprecatePropTypeNew(PropTypes.func, 'Use "renderColumn" property instead.'), renderMenuItem: deprecatePropTypeNew(PropTypes.func, 'Use "renderTreeNode" property instead.'), menuWidth: deprecatePropTypeNew(PropTypes.number, 'Use "columnWidth" property instead.'), menuHeight: deprecatePropTypeNew(PropTypes.number, 'Use "columnHeight" property instead.') }); export default MultiCascader;