UNPKG

rsuite

Version:

A suite of react components

544 lines (470 loc) 21 kB
"use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _extends3 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _pick = _interopRequireDefault(require("lodash/pick")); var _omit = _interopRequireDefault(require("lodash/omit")); var _isFunction = _interopRequireDefault(require("lodash/isFunction")); var _isNil = _interopRequireDefault(require("lodash/isNil")); var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu")); var _Checkbox = _interopRequireDefault(require("../Checkbox")); var _utils = require("./utils"); var _treeUtils = require("../utils/treeUtils"); var _utils2 = require("../Cascader/utils"); var _utils3 = require("../utils"); var _Picker = require("../Picker"); var emptyArray = []; var MultiCascader = /*#__PURE__*/_react.default.forwardRef(function (props, ref) { var _selectedPaths; var _props$as = props.as, Component = _props$as === void 0 ? 'div' : _props$as, _props$data = props.data, data = _props$data === void 0 ? emptyArray : _props$data, _props$classPrefix = props.classPrefix, classPrefix = _props$classPrefix === void 0 ? 'picker' : _props$classPrefix, defaultValue = props.defaultValue, valueProp = props.value, _props$valueKey = props.valueKey, valueKey = _props$valueKey === void 0 ? 'value' : _props$valueKey, _props$labelKey = props.labelKey, labelKey = _props$labelKey === void 0 ? 'label' : _props$labelKey, _props$childrenKey = props.childrenKey, childrenKey = _props$childrenKey === void 0 ? 'children' : _props$childrenKey, disabled = props.disabled, _props$disabledItemVa = props.disabledItemValues, disabledItemValues = _props$disabledItemVa === void 0 ? emptyArray : _props$disabledItemVa, _props$cleanable = props.cleanable, cleanable = _props$cleanable === void 0 ? true : _props$cleanable, overrideLocale = props.locale, toggleAs = props.toggleAs, style = props.style, _props$countable = props.countable, countable = _props$countable === void 0 ? true : _props$countable, _props$cascade = props.cascade, cascade = _props$cascade === void 0 ? true : _props$cascade, inline = props.inline, placeholder = props.placeholder, _props$placement = props.placement, placement = _props$placement === void 0 ? 'bottomStart' : _props$placement, _props$appearance = props.appearance, appearance = _props$appearance === void 0 ? 'default' : _props$appearance, menuWidth = props.menuWidth, menuHeight = props.menuHeight, menuClassName = props.menuClassName, menuStyle = props.menuStyle, _props$searchable = props.searchable, searchable = _props$searchable === void 0 ? true : _props$searchable, _props$uncheckableIte = props.uncheckableItemValues, uncheckableItemValues = _props$uncheckableIte === void 0 ? emptyArray : _props$uncheckableIte, id = props.id, getChildren = props.getChildren, renderValue = props.renderValue, renderMenu = props.renderMenu, renderMenuItem = props.renderMenuItem, renderExtraFooter = props.renderExtraFooter, onEnter = props.onEnter, onExit = props.onExit, onExited = props.onExited, onClean = props.onClean, onSearch = props.onSearch, onSelect = props.onSelect, onChange = props.onChange, onOpen = props.onOpen, onClose = props.onClose, onCheck = props.onCheck, rest = (0, _objectWithoutPropertiesLoose2.default)(props, ["as", "data", "classPrefix", "defaultValue", "value", "valueKey", "labelKey", "childrenKey", "disabled", "disabledItemValues", "cleanable", "locale", "toggleAs", "style", "countable", "cascade", "inline", "placeholder", "placement", "appearance", "menuWidth", "menuHeight", "menuClassName", "menuStyle", "searchable", "uncheckableItemValues", "id", "getChildren", "renderValue", "renderMenu", "renderMenuItem", "renderExtraFooter", "onEnter", "onExit", "onExited", "onClean", "onSearch", "onSelect", "onChange", "onOpen", "onClose", "onCheck"]); var itemKeys = { childrenKey: childrenKey, labelKey: labelKey, valueKey: valueKey }; var _useState = (0, _react.useState)(false), active = _useState[0], setActive = _useState[1]; var _useFlattenData = (0, _utils.useFlattenData)(data, itemKeys), flattenData = _useFlattenData.flattenData, addFlattenData = _useFlattenData.addFlattenData; var _useCascadeValue = (0, _utils.useCascadeValue)((0, _extends3.default)({}, itemKeys, { uncheckableItemValues: uncheckableItemValues, cascade: cascade, value: valueProp || defaultValue }), flattenData), value = _useCascadeValue.value, setValue = _useCascadeValue.setValue, splitValue = _useCascadeValue.splitValue; // The columns displayed in the cascading panel. var _useColumnData = (0, _utils.useColumnData)(flattenData), columnData = _useColumnData.columnData, setColumnData = _useColumnData.setColumnData, addColumn = _useColumnData.addColumn, enforceUpdateColumnData = _useColumnData.enforceUpdateColumnData; (0, _utils3.useUpdateEffect)(function () { enforceUpdateColumnData(data); }, [data]); // The path after cascading data selection. var _useState2 = (0, _react.useState)(), selectedPaths = _useState2[0], setSelectedPaths = _useState2[1]; var triggerRef = (0, _react.useRef)(null); var overlayRef = (0, _react.useRef)(null); var targetRef = (0, _react.useRef)(null); var searchInputRef = (0, _react.useRef)(null); (0, _Picker.usePublicMethods)(ref, { triggerRef: triggerRef, overlayRef: overlayRef, targetRef: targetRef }); var _useCustom = (0, _utils3.useCustom)('Picker', overrideLocale), locale = _useCustom.locale, rtl = _useCustom.rtl; var selectedItems = flattenData.filter(function (item) { return value.some(function (v) { return v === item[valueKey]; }); }) || []; // Used to hover the focuse item when trigger `onKeydown` var _useFocusItemValue = (0, _Picker.useFocusItemValue)(selectedPaths === null || selectedPaths === void 0 ? 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 overlayRef.current; }, callback: (0, _react.useCallback)(function (value) { var _getColumnsAndPaths = (0, _utils2.getColumnsAndPaths)(data, value, { valueKey: valueKey, childrenKey: childrenKey, isAttachChildren: true }), columns = _getColumnsAndPaths.columns, paths = _getColumnsAndPaths.paths; setColumnData(columns); setSelectedPaths(paths); }, [childrenKey, data, setColumnData, valueKey]) }), focusItemValue = _useFocusItemValue.focusItemValue, setLayer = _useFocusItemValue.setLayer, setKeys = _useFocusItemValue.setKeys, onFocusItem = _useFocusItemValue.onKeyDown; /** * 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 && (0, _isFunction.default)(renderValue); var _useClassNames = (0, _utils3.useClassNames)(classPrefix), prefix = _useClassNames.prefix, merge = _useClassNames.merge; var _useState3 = (0, _react.useState)(''), searchKeyword = _useState3[0], setSearchKeyword = _useState3[1]; var handleEntered = (0, _react.useCallback)(function () { onOpen === null || onOpen === void 0 ? void 0 : onOpen(); setActive(true); }, [onOpen]); var handleExited = (0, _react.useCallback)(function () { setActive(false); setSearchKeyword(''); }, []); var handleSelect = (0, _react.useCallback)(function (node, cascadePaths, event) { var _node$childrenKey, _node$childrenKey2, _triggerRef$current, _triggerRef$current$u; setSelectedPaths(cascadePaths); onSelect === null || onSelect === void 0 ? void 0 : onSelect(node, cascadePaths, event); // Lazy load node's children if (typeof getChildren === 'function' && ((_node$childrenKey = node[childrenKey]) === null || _node$childrenKey === void 0 ? void 0 : _node$childrenKey.length) === 0) { node.loading = true; var children = getChildren(node); if (children instanceof Promise) { children.then(function (data) { node.loading = false; node[childrenKey] = data; if (targetRef.current) { addFlattenData(data, node); addColumn(data, cascadePaths.length); } }); } else { node.loading = false; node[childrenKey] = children; addFlattenData(children, node); addColumn(children, cascadePaths.length); } } else if ((_node$childrenKey2 = node[childrenKey]) !== null && _node$childrenKey2 !== void 0 && _node$childrenKey2.length) { addColumn(node[childrenKey], cascadePaths.length); } (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : (_triggerRef$current$u = _triggerRef$current.updatePosition) === null || _triggerRef$current$u === void 0 ? void 0 : _triggerRef$current$u.call(_triggerRef$current); }, [onSelect, getChildren, childrenKey, addColumn, addFlattenData]); var handleCheck = (0, _react.useCallback)(function (node, event, checked) { var nodeValue = node[valueKey]; var nextValue = []; if (cascade) { nextValue = splitValue(node, checked, value).value; } else { nextValue = [].concat(value); if (checked) { nextValue.push(nodeValue); } else { nextValue = nextValue.filter(function (n) { return n !== nodeValue; }); } } setValue(nextValue); onChange === null || onChange === void 0 ? void 0 : onChange(nextValue, event); onCheck === null || onCheck === void 0 ? void 0 : onCheck(nextValue, node, checked, event); }, [cascade, onChange, onCheck, setValue, splitValue, value, valueKey]); var handleClean = (0, _react.useCallback)(function (event) { if (disabled || !targetRef.current) { return; } setSelectedPaths([]); setValue([]); setColumnData([data]); onChange === null || onChange === void 0 ? void 0 : onChange([], event); }, [data, disabled, onChange, setColumnData, setValue]); var handleMenuPressEnter = (0, _react.useCallback)(function (event) { var _overlayRef$current; var focusItem = (0, _treeUtils.findNodeOfTree)(data, function (item) { return item[valueKey] === focusItemValue; }); var checkbox = (_overlayRef$current = overlayRef.current) === null || _overlayRef$current === void 0 ? void 0 : _overlayRef$current.querySelector("[data-key=\"" + focusItemValue + "\"] [type=\"checkbox\"]"); if (checkbox) { handleCheck(focusItem, event, (checkbox === null || checkbox === void 0 ? void 0 : checkbox.getAttribute('aria-checked')) !== 'true'); } }, [data, focusItemValue, handleCheck, valueKey]); var onPickerKeyDown = (0, _Picker.useToggleKeyDownEvent)((0, _extends3.default)({ toggle: !focusItemValue || !active, triggerRef: triggerRef, targetRef: targetRef, overlayRef: overlayRef, searchInputRef: searchInputRef, active: active, onExit: handleClean, onMenuKeyDown: onFocusItem, onMenuPressEnter: handleMenuPressEnter }, rest)); var handleSearch = (0, _react.useCallback)(function (value, event) { setSearchKeyword(value); onSearch === null || onSearch === void 0 ? void 0 : onSearch(value, event); if (value) { setLayer(0); } else if (selectedPaths !== null && selectedPaths !== void 0 && selectedPaths.length) { setLayer(selectedPaths.length - 1); } setKeys([]); }, [onSearch, selectedPaths, setKeys, setLayer]); var getSearchResult = (0, _react.useCallback)(function () { var items = []; var result = flattenData.filter(function (item) { if (uncheckableItemValues.some(function (value) { return item[valueKey] === value; })) { return false; } if (item[labelKey].match(new RegExp((0, _utils3.getSafeRegExpString)(searchKeyword), 'i'))) { return true; } return false; }); for (var i = 0; i < result.length; i++) { items.push(result[i]); // A maximum of 100 search results are returned. if (i === 99) { return items; } } return items; }, [flattenData, labelKey, searchKeyword, uncheckableItemValues, valueKey]); var renderSearchRow = function renderSearchRow(item, key) { var _extends2; var nodes = (0, _treeUtils.getNodeParents)(item); var regx = new RegExp((0, _utils3.getSafeRegExpString)(searchKeyword), 'ig'); var labelElements = []; var a = item[labelKey].split(regx); var b = item[labelKey].match(regx); for (var i = 0; i < a.length; i++) { labelElements.push(a[i]); if (b[i]) { labelElements.push( /*#__PURE__*/_react.default.createElement("span", { key: i, className: prefix('cascader-search-match') }, b[i])); } } nodes.push((0, _extends3.default)({}, item, (_extends2 = {}, _extends2[labelKey] = labelElements, _extends2))); var active = value.some(function (value) { if (cascade) { return nodes.some(function (node) { return node[valueKey] === value; }); } return item[valueKey] === value; }); var disabled = disabledItemValues.some(function (value) { return nodes.some(function (node) { return node[valueKey] === value; }); }); var itemClasses = prefix('cascader-row', { 'cascader-row-disabled': disabled, 'cascader-row-focus': item[valueKey] === focusItemValue }); return /*#__PURE__*/_react.default.createElement("div", { key: key, className: itemClasses, "aria-disabled": disabled, "data-key": item[valueKey] }, /*#__PURE__*/_react.default.createElement(_Checkbox.default, { disabled: disabled, checked: active, value: item[valueKey], indeterminate: cascade && !active && (0, _utils.isSomeChildChecked)(item, value, { valueKey: valueKey, childrenKey: childrenKey }), onChange: function onChange(_value, checked, event) { handleCheck(item, event, checked); } }, /*#__PURE__*/_react.default.createElement("span", { className: prefix('cascader-cols') }, nodes.map(function (node, index) { return /*#__PURE__*/_react.default.createElement("span", { key: "col-" + index, className: prefix('cascader-col') }, node[labelKey]); })))); }; var renderSearchResultPanel = function renderSearchResultPanel() { if (searchKeyword === '') { return null; } var items = getSearchResult(); return /*#__PURE__*/_react.default.createElement("div", { className: prefix('cascader-search-panel'), "data-layer": 0 }, items.length ? items.map(renderSearchRow) : /*#__PURE__*/_react.default.createElement("div", { className: prefix('none') }, locale.noResultsText)); }; var renderDropdownMenu = function renderDropdownMenu(positionProps, speakerRef) { var _ref = positionProps || {}, left = _ref.left, top = _ref.top, className = _ref.className; var styles = (0, _extends3.default)({}, menuStyle, { left: left, top: top }); var classes = merge(className, menuClassName, prefix('cascader-menu', 'multi-cascader-menu', { inline: inline })); return /*#__PURE__*/_react.default.createElement(_Picker.PickerOverlay, { ref: (0, _utils3.mergeRefs)(overlayRef, speakerRef), className: classes, style: styles, target: triggerRef, onKeyDown: onPickerKeyDown }, searchable && /*#__PURE__*/_react.default.createElement(_Picker.SearchBar, { placeholder: locale === null || locale === void 0 ? void 0 : locale.searchPlaceholder, onChange: handleSearch, value: searchKeyword, inputRef: searchInputRef }), renderSearchResultPanel(), searchKeyword === '' && /*#__PURE__*/_react.default.createElement(_DropdownMenu.default, { id: id ? id + "-listbox" : undefined, cascade: cascade, menuWidth: menuWidth, menuHeight: menuHeight, uncheckableItemValues: uncheckableItemValues, disabledItemValues: disabledItemValues, valueKey: valueKey, labelKey: labelKey, childrenKey: childrenKey, classPrefix: 'picker-cascader-menu', cascadeData: columnData, cascadePaths: selectedPaths, value: value, onSelect: handleSelect, onCheck: handleCheck, renderMenu: renderMenu, renderMenuItem: renderMenuItem }), renderExtraFooter === null || renderExtraFooter === void 0 ? void 0 : renderExtraFooter()); }; var selectedElement = placeholder; if (selectedItems.length > 0) { selectedElement = /*#__PURE__*/_react.default.createElement(_Picker.SelectedElement, { selectedItems: selectedItems, countable: countable, valueKey: valueKey, labelKey: labelKey, childrenKey: childrenKey, prefix: prefix, cascade: cascade, locale: locale }); } if (hasValue && (0, _isFunction.default)(renderValue)) { selectedElement = renderValue(value.length ? value : valueProp !== null && valueProp !== void 0 ? valueProp : [], selectedItems, selectedElement); // If renderValue returns null or undefined, hasValue is false. if ((0, _isNil.default)(selectedElement)) { hasValue = false; } } var _usePickerClassName = (0, _Picker.usePickerClassName)((0, _extends3.default)({}, props, { classPrefix: classPrefix, hasValue: hasValue, countable: countable, name: 'cascader', appearance: appearance, cleanable: cleanable })), classes = _usePickerClassName[0], usedClassNamePropKeys = _usePickerClassName[1]; if (inline) { return renderDropdownMenu(); } return /*#__PURE__*/_react.default.createElement(_Picker.PickerToggleTrigger, { pickerProps: (0, _pick.default)(props, _Picker.pickTriggerPropKeys), ref: triggerRef, placement: placement, onEnter: (0, _utils3.createChainedFunction)(handleEntered, onEnter), onExited: (0, _utils3.createChainedFunction)(handleExited, onExited), onExit: (0, _utils3.createChainedFunction)(onClose, onExit), speaker: renderDropdownMenu }, /*#__PURE__*/_react.default.createElement(Component, { className: classes, style: style }, /*#__PURE__*/_react.default.createElement(_Picker.PickerToggle, (0, _extends3.default)({}, (0, _omit.default)(rest, [].concat(_Picker.omitTriggerPropKeys, usedClassNamePropKeys)), { id: id, as: toggleAs, appearance: appearance, disabled: disabled, ref: targetRef, onClean: (0, _utils3.createChainedFunction)(handleClean, onClean), onKeyDown: onPickerKeyDown, cleanable: cleanable && !disabled, hasValue: hasValue, active: active, placement: placement, inputValue: value }), selectedElement || locale.placeholder))); }); MultiCascader.displayName = 'MultiCascader'; MultiCascader.propTypes = (0, _extends3.default)({}, _Picker.listPickerPropTypes, { value: _propTypes.default.array, disabledItemValues: _propTypes.default.array, locale: _propTypes.default.any, appearance: _propTypes.default.oneOf(['default', 'subtle']), cascade: _propTypes.default.bool, inline: _propTypes.default.bool, countable: _propTypes.default.bool, menuWidth: _propTypes.default.number, menuHeight: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]), uncheckableItemValues: _propTypes.default.array, searchable: _propTypes.default.bool, renderMenuItem: _propTypes.default.func, renderMenu: _propTypes.default.func, onSearch: _propTypes.default.func, onSelect: _propTypes.default.func, onCheck: _propTypes.default.func }); var _default = MultiCascader; exports.default = _default;