bonree-cascader
Version:
cascade select ui component for react
326 lines (274 loc) • 12.7 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.LOAD_STATUS = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var React = _interopRequireWildcard(require("react"));
var _warning = _interopRequireDefault(require("rc-util/lib/warning"));
var _useMergedState3 = _interopRequireDefault(require("rc-util/lib/hooks/useMergedState"));
var _generate = _interopRequireDefault(require("bonree-tree-select/lib/generate"));
var _OptionList = _interopRequireDefault(require("./OptionList"));
var _context = _interopRequireDefault(require("./context"));
var _util = require("./util");
var _useUpdateEffect = _interopRequireDefault(require("./hooks/useUpdateEffect"));
var _useSearchConfig3 = _interopRequireDefault(require("./hooks/useSearchConfig"));
var _excluded = ["checkable", "changeOnSelect", "children", "options", "onChange", "value", "defaultValue", "popupVisible", "open", "dropdownClassName", "popupClassName", "onDropdownVisibleChange", "onPopupVisibleChange", "popupPlacement", "placement", "searchValue", "onSearch", "showSearch", "expandTrigger", "expandIcon", "loadingIcon", "displayRender", "loadData", "dropdownMenuColumnStyle", "dropdownPrefixCls", "dropdownMatchSelectWidth", "requestFailureIcon", "requestFailureText", "refreshText", "empty"];
var INTERNAL_VALUE_FIELD = '__rc_cascader_value__';
/**
* `rc-cascader` is much like `bonree-tree-select` but API is very different.
* It's caused that component developer is not same person
* and we do not rice the API naming standard at that time.
*
* To avoid breaking change, wrap the `bonree-tree-select` to compatible with `rc-cascader` API.
* This should be better to merge to same API like `bonree-tree-select` or `bonree-select` in next major version.
*
* Update:
* - dropdown class change to `rc-cascader-dropdown`
* - direction rtl keyboard
*
* Deprecated:
* - popupVisible
* - hidePopupOnSelect
*
* Removed:
* - builtinPlacements: Handle by select
*/
var RefCascader = (0, _generate.default)({
prefixCls: 'rc-cascader',
optionList: _OptionList.default
});
function defaultDisplayRender(labels) {
return labels.join(' / ');
}
var LOAD_STATUS;
exports.LOAD_STATUS = LOAD_STATUS;
(function (LOAD_STATUS) {
LOAD_STATUS["LOADING"] = "RC_LOADING_RC";
LOAD_STATUS["EMPTY"] = "RC_EMPTY_RC";
LOAD_STATUS["FAILED"] = "RC_LOADED_FAILED_RC";
})(LOAD_STATUS || (exports.LOAD_STATUS = LOAD_STATUS = {}));
var Cascader = /*#__PURE__*/React.forwardRef(function (props, ref) {
var checkable = props.checkable,
changeOnSelect = props.changeOnSelect,
children = props.children,
options = props.options,
onChange = props.onChange,
value = props.value,
defaultValue = props.defaultValue,
popupVisible = props.popupVisible,
open = props.open,
dropdownClassName = props.dropdownClassName,
popupClassName = props.popupClassName,
onDropdownVisibleChange = props.onDropdownVisibleChange,
onPopupVisibleChange = props.onPopupVisibleChange,
popupPlacement = props.popupPlacement,
placement = props.placement,
searchValue = props.searchValue,
onSearch = props.onSearch,
showSearch = props.showSearch,
expandTrigger = props.expandTrigger,
_props$expandIcon = props.expandIcon,
expandIcon = _props$expandIcon === void 0 ? '>' : _props$expandIcon,
loadingIcon = props.loadingIcon,
_props$displayRender = props.displayRender,
displayRender = _props$displayRender === void 0 ? defaultDisplayRender : _props$displayRender,
loadData = props.loadData,
dropdownMenuColumnStyle = props.dropdownMenuColumnStyle,
dropdownPrefixCls = props.dropdownPrefixCls,
_props$dropdownMatchS = props.dropdownMatchSelectWidth,
dropdownMatchSelectWidth = _props$dropdownMatchS === void 0 ? false : _props$dropdownMatchS,
_props$requestFailure = props.requestFailureIcon,
requestFailureIcon = _props$requestFailure === void 0 ? null : _props$requestFailure,
requestFailureText = props.requestFailureText,
refreshText = props.refreshText,
empty = props.empty,
restProps = (0, _objectWithoutProperties2.default)(props, _excluded);
var fieldNames = restProps.fieldNames; // ============================ Ref =============================
var cascaderRef = React.useRef();
React.useImperativeHandle(ref, function () {
return {
focus: function focus() {
cascaderRef.current.focus();
},
blur: function blur() {
cascaderRef.current.blur();
}
};
});
var getEntityByValue = function getEntityByValue(val) {
return cascaderRef.current.getEntityByValue(val);
}; // =========================== Search ===========================
var _useMergedState = (0, _useMergedState3.default)(undefined, {
value: searchValue,
onChange: onSearch
}),
_useMergedState2 = (0, _slicedToArray2.default)(_useMergedState, 2),
mergedSearch = _useMergedState2[0],
setMergedSearch = _useMergedState2[1];
var _useSearchConfig = (0, _useSearchConfig3.default)(showSearch),
_useSearchConfig2 = (0, _slicedToArray2.default)(_useSearchConfig, 2),
mergedShowSearch = _useSearchConfig2[0],
searchConfig = _useSearchConfig2[1]; // ========================== Options ===========================
var outerFieldNames = React.useMemo(function () {
return (0, _util.fillFieldNames)(fieldNames);
}, [fieldNames]);
var mergedFieldNames = React.useMemo(function () {
return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, outerFieldNames), {}, {
value: INTERNAL_VALUE_FIELD
});
}, [outerFieldNames]);
var mergedOptions = React.useMemo(function () {
return (0, _util.convertOptions)(options, outerFieldNames, INTERNAL_VALUE_FIELD);
}, [options, outerFieldNames]); // =========================== Value ============================
/**
* Always pass props value to last value unit:
* - single: ['light', 'little'] => ['light__little']
* - multiple: [['light', 'little'], ['bamboo']] => ['light__little', 'bamboo']
*/
var parseToInternalValue = function parseToInternalValue(propValue) {
var propValueList = [];
if (propValue) {
propValueList = checkable ? propValue : [propValue];
}
return propValueList.map(_util.connectValue);
};
var _React$useState = React.useState(function () {
return parseToInternalValue(value || defaultValue);
}),
_React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
internalValue = _React$useState2[0],
setInternalValue = _React$useState2[1];
(0, _useUpdateEffect.default)(function () {
setInternalValue(parseToInternalValue(value));
}, [value]); // =========================== Label ============================
var labelRender = function labelRender(entity, val) {
var fieldLabel = mergedFieldNames.label;
if (!entity) {
var valPath = (0, _util.splitValue)(val);
return displayRender(valPath, []);
}
if (checkable) {
return entity.data.node[fieldLabel];
}
var _restoreCompatibleVal = (0, _util.restoreCompatibleValue)(entity, mergedFieldNames),
selectedOptions = _restoreCompatibleVal.options;
var rawOptions = selectedOptions.map(function (opt) {
return opt.node;
});
var labelList = rawOptions.map(function (opt) {
return opt[fieldLabel];
});
return displayRender(labelList, rawOptions);
}; // =========================== Change ===========================
var onInternalChange = function onInternalChange(newValue
/** Not care current type */
) {
// TODO: Need improve motion experience
setMergedSearch('');
var valueList = checkable ? newValue : [newValue];
var pathList = [];
var optionsList = [];
var valueEntities = valueList.map(getEntityByValue).filter(function (entity) {
return entity;
});
valueEntities.forEach(function (entity) {
var _restoreCompatibleVal2 = (0, _util.restoreCompatibleValue)(entity, mergedFieldNames),
valueOptions = _restoreCompatibleVal2.options;
var originOptions = valueOptions.map(function (option) {
return option.node;
});
pathList.push(originOptions.map(function (opt) {
return (// Here we should use original FieldNames value mapping
opt[outerFieldNames.value]
);
}));
optionsList.push(originOptions);
}); // Fill state
if (value === undefined) {
setInternalValue(valueList);
}
if (onChange) {
if (checkable) {
onChange(pathList, optionsList);
} else {
// TODO: This should return null as other component.
// But its a breaking change and we should keep the logic.
onChange(pathList[0] || [], optionsList[0] || []);
}
}
}; // ============================ Open ============================
if (process.env.NODE_ENV !== 'production') {
(0, _warning.default)(!onPopupVisibleChange, '`onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.');
(0, _warning.default)(popupVisible === undefined, '`popupVisible` is deprecated. Please use `open` instead.');
(0, _warning.default)(popupClassName === undefined, '`popupClassName` is deprecated. Please use `dropdownClassName` instead.');
(0, _warning.default)(popupPlacement === undefined, '`popupPlacement` is deprecated. Please use `placement` instead.');
}
var mergedOpen = open !== undefined ? open : popupVisible;
var mergedDropdownClassName = dropdownClassName || popupClassName;
var mergedPlacement = placement || popupPlacement;
var onInternalDropdownVisibleChange = function onInternalDropdownVisibleChange(nextVisible) {
onDropdownVisibleChange === null || onDropdownVisibleChange === void 0 ? void 0 : onDropdownVisibleChange(nextVisible);
onPopupVisibleChange === null || onPopupVisibleChange === void 0 ? void 0 : onPopupVisibleChange(nextVisible);
}; // ========================== Context ===========================
var context = React.useMemo(function () {
return {
changeOnSelect: changeOnSelect,
expandTrigger: expandTrigger,
fieldNames: mergedFieldNames,
expandIcon: expandIcon,
loadingIcon: loadingIcon,
loadData: loadData,
dropdownMenuColumnStyle: dropdownMenuColumnStyle,
search: searchConfig,
dropdownPrefixCls: dropdownPrefixCls,
requestFailureIcon: requestFailureIcon,
requestFailureText: requestFailureText,
refreshText: refreshText,
empty: empty
};
}, [changeOnSelect, expandTrigger, mergedFieldNames, expandIcon, loadingIcon, loadData, dropdownMenuColumnStyle, searchConfig, dropdownPrefixCls]); // =========================== Render ===========================
var dropdownStyle = // Search to match width
mergedSearch && searchConfig.matchInputWidth || // Empty keep the width
!mergedOptions.length ? {} : {
minWidth: 'auto'
};
return /*#__PURE__*/React.createElement(_context.default.Provider, {
value: context
}, /*#__PURE__*/React.createElement(RefCascader, (0, _extends2.default)({
ref: cascaderRef
}, restProps, {
fieldNames: mergedFieldNames,
value: checkable ? internalValue : internalValue[0],
placement: mergedPlacement,
dropdownMatchSelectWidth: dropdownMatchSelectWidth,
dropdownStyle: dropdownStyle,
dropdownClassName: mergedDropdownClassName,
treeData: mergedOptions,
treeCheckable: checkable,
treeNodeFilterProp: "label",
onChange: onInternalChange,
showCheckedStrategy: RefCascader.SHOW_PARENT,
open: mergedOpen,
onDropdownVisibleChange: onInternalDropdownVisibleChange,
searchValue: mergedSearch // Customize filter logic in OptionList
,
filterTreeNode: function filterTreeNode() {
return true;
},
showSearch: mergedShowSearch,
onSearch: setMergedSearch,
labelRender: labelRender,
getRawInputElement: function getRawInputElement() {
return children;
}
})));
});
Cascader.displayName = 'Cascader';
var _default = Cascader;
exports.default = _default;