bonree-cascader
Version:
cascade select ui component for react
333 lines (270 loc) • 12.9 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = Column;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var React = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _rcVirtualList = _interopRequireDefault(require("rc-virtual-list"));
var _Cascader = require("../Cascader");
var _util = require("../util");
var _context = _interopRequireDefault(require("../context"));
var _Checkbox = _interopRequireDefault(require("./Checkbox"));
var _commonUtil = require("bonree-select/lib/utils/commonUtil");
function Column(_ref) {
var _classNames4;
var prefixCls = _ref.prefixCls,
index = _ref.index,
multiple = _ref.multiple,
options = _ref.options,
openKey = _ref.openKey,
onSelect = _ref.onSelect,
onOpen = _ref.onOpen,
onToggleOpen = _ref.onToggleOpen,
checkedSet = _ref.checkedSet,
halfCheckedSet = _ref.halfCheckedSet,
openFinalValue = _ref.openFinalValue,
searchValue = _ref.searchValue,
height = _ref.height,
itemHeight = _ref.itemHeight,
values = _ref.values,
onSelectAll = _ref.onSelectAll,
autoEllipsis = _ref.autoEllipsis,
Tooltip = _ref.tooltip;
var menuPrefixCls = "".concat(prefixCls, "-menu");
var menuItemPrefixCls = "".concat(prefixCls, "-menu-item");
var uuidRef = React.useRef((0, _commonUtil.getUUID)());
var _React$useContext = React.useContext(_context.default),
changeOnSelect = _React$useContext.changeOnSelect,
expandTrigger = _React$useContext.expandTrigger,
expandIcon = _React$useContext.expandIcon,
loadingIcon = _React$useContext.loadingIcon,
dropdownMenuColumnStyle = _React$useContext.dropdownMenuColumnStyle,
requestFailureIcon = _React$useContext.requestFailureIcon,
requestFailureText = _React$useContext.requestFailureText,
refreshText = _React$useContext.refreshText,
empty = _React$useContext.empty,
virtual = _React$useContext.virtual,
treeCheckStrictly = _React$useContext.treeCheckStrictly;
var hoverOpen = expandTrigger === 'hover'; // ========================= Overflow =========================
// 记录每个选项内容是否溢出,key 为 option.value
var _React$useState = React.useState({}),
_React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
overflowMap = _React$useState2[0],
setOverflowMap = _React$useState2[1]; // 虚拟滚动模式下,跟踪当前可见的选项
var _React$useState3 = React.useState([]),
_React$useState4 = (0, _slicedToArray2.default)(_React$useState3, 2),
visibleOptions = _React$useState4[0],
setVisibleOptions = _React$useState4[1]; // 虚拟滚动模式下仅检测当前可见的选项,非虚拟模式检测所有选项
var itemsToCheck = virtual ? visibleOptions : options; // 通过 ref 追踪 options 变化,用于在虚拟模式下检测数据源切换并重置 overflowMap
var prevOptionsRef = React.useRef(options);
React.useLayoutEffect(function () {
if (!autoEllipsis || !Tooltip) return; // 检测 options 是否发生变化(如切换级联列)
var optionsChanged = prevOptionsRef.current !== options;
prevOptionsRef.current = options;
var newEntries = {};
itemsToCheck.forEach(function (option) {
if (option.value === _util.ALL_KEY) return;
var contentId = "cascader-content-".concat(option.value, "-").concat(uuidRef.current);
var el = document.getElementById(contentId);
if (el) {
newEntries[option.value] = (0, _commonUtil.judgeOverflowing)(el);
}
}); // 非虚拟模式或 options 变化时直接替换;虚拟模式下增量合并,保留已检测过的结果
if (!virtual || optionsChanged) {
setOverflowMap(newEntries);
} else {
setOverflowMap(function (prev) {
return (0, _objectSpread2.default)((0, _objectSpread2.default)({}, prev), newEntries);
});
}
}, [itemsToCheck, options, autoEllipsis, Tooltip, virtual]); // 全选按钮状态
var getCheckStatus = function getCheckStatus() {
var status = 'none';
if (values.size === 0) {
return status;
}
var optValues = options.filter(function (item) {
return item.value !== _util.ALL_KEY && !item.disabled;
}).map(function (item) {
return item.value;
});
if (optValues.some(function (k) {
return values.has(k);
})) {
status = 'part';
}
if (optValues.every(function (k) {
return values.has(k);
})) {
status = 'all';
}
return status;
};
var handleSelectAll = function handleSelectAll(value) {
var newValues = options.filter(function (v) {
return v.value !== undefined && !v.disabled;
}).filter(function (v) {
return v.value !== _util.ALL_KEY;
}).map(function (item) {
return item.value;
});
if (newValues && newValues.length) {
onSelectAll(newValues, {
selected: getCheckStatus() !== 'all',
triggerValue: value
});
}
};
var handleScroll = function handleScroll() {
document.querySelectorAll(".".concat(prefixCls, "-menu-item-content-tooltip")).forEach(function (node) {
if (node) {
node.style.display = 'none';
}
});
}; // ============================ Render ============================
var renderOption = function renderOption(option) {
var _classNames2;
var disabled = option.disabled,
value = option.value,
node = option.node; // 处理"全部"选项
if (value === _util.ALL_KEY) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: (0, _classnames.default)(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-all"), (0, _defineProperty2.default)({}, "".concat(menuItemPrefixCls, "-disabled"), disabled)),
onClick: function onClick() {
return !treeCheckStrictly && !disabled && handleSelectAll(value);
}
}, multiple && /*#__PURE__*/React.createElement(_Checkbox.default, {
prefixCls: "".concat(prefixCls, "-checkbox"),
checked: getCheckStatus() === 'all',
halfChecked: getCheckStatus() === 'part',
disabled: disabled,
onClick: function onClick(e) {
e.stopPropagation();
if (!disabled) {
handleSelectAll(value);
}
}
}), /*#__PURE__*/React.createElement("div", {
className: "".concat(menuItemPrefixCls, "-content")
}, option.title));
}
var isMergedLeaf = (0, _util.isLeaf)(option); // const isLoading = loadingKeys.includes(value);
// 加载中
var isLoading = value === (0, _util.connectValue)([openFinalValue, _Cascader.LOAD_STATUS.LOADING]) || value === _Cascader.LOAD_STATUS.LOADING;
if (isLoading) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: (0, _classnames.default)(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-loading")),
style: dropdownMenuColumnStyle
}, loadingIcon);
} // 加载数据失败
var isLoadFailed = value === (0, _util.connectValue)([openFinalValue, _Cascader.LOAD_STATUS.FAILED]) || value === _Cascader.LOAD_STATUS.FAILED;
if (isLoadFailed) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: (0, _classnames.default)(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-failed")),
style: dropdownMenuColumnStyle
}, /*#__PURE__*/React.createElement("div", {
className: "".concat(menuItemPrefixCls, "-failed-content")
}, requestFailureIcon, requestFailureText, /*#__PURE__*/React.createElement("span", {
className: "".concat(menuItemPrefixCls, "-failed-content-refresh"),
onClick: function onClick() {
onOpen(index, openFinalValue);
}
}, refreshText)));
} // 无数据
var isNoData = value === (0, _util.connectValue)([openFinalValue, _Cascader.LOAD_STATUS.EMPTY]) || value === _Cascader.LOAD_STATUS.EMPTY;
if (isNoData) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: (0, _classnames.default)(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-empty")),
style: dropdownMenuColumnStyle
}, empty);
} // >>>>> checked
var checked = checkedSet.has(value); // >>>>> Open
var triggerOpenPath = function triggerOpenPath() {
// if (!disabled && hoverOpen) {
if (!disabled && (!hoverOpen || !isMergedLeaf)) {
onOpen(index, value);
}
}; // >>>>> Selection
var triggerSelect = function triggerSelect() {
if (!disabled && (isMergedLeaf || changeOnSelect || multiple)) {
onSelect(value, isMergedLeaf);
}
}; // >>>>> Title
var title;
if (typeof (node === null || node === void 0 ? void 0 : node.title) === 'string' || typeof (node === null || node === void 0 ? void 0 : node.title) === 'number') {
title = String(node.title);
} else if (typeof option.title === 'string' || typeof (option === null || option === void 0 ? void 0 : option.title) === 'number') {
title = String(option.title);
} // >>>>> Ellipsis & Tooltip
var ellipsisConfig = autoEllipsis === true ? {} : autoEllipsis;
var contentId = "cascader-content-".concat(value, "-").concat(uuidRef.current);
var isOverflowing = overflowMap[value] || false; // >>>>> Render
return /*#__PURE__*/React.createElement("li", {
key: value,
className: (0, _classnames.default)(menuItemPrefixCls, (_classNames2 = {}, (0, _defineProperty2.default)(_classNames2, "".concat(menuItemPrefixCls, "-expand"), !isMergedLeaf), (0, _defineProperty2.default)(_classNames2, "".concat(menuItemPrefixCls, "-active"), openKey === value), (0, _defineProperty2.default)(_classNames2, "".concat(menuItemPrefixCls, "-disabled"), disabled), _classNames2)),
style: dropdownMenuColumnStyle,
role: "menuitemcheckbox",
"aria-checked": checked,
"data-value": value,
onClick: function onClick() {
triggerOpenPath();
if (!treeCheckStrictly && (!multiple || isMergedLeaf)) {
triggerSelect();
}
},
onDoubleClick: function onDoubleClick() {
if (changeOnSelect) {
onToggleOpen(false);
}
},
onMouseEnter: function onMouseEnter() {
if (!disabled && hoverOpen) {
onOpen(index, value);
}
}
}, multiple && value !== _util.DATA_EMPTY && !isLoading && /*#__PURE__*/React.createElement(_Checkbox.default, {
prefixCls: "".concat(prefixCls, "-checkbox"),
checked: checked,
halfChecked: halfCheckedSet.has(value),
disabled: disabled,
onClick: function onClick(e) {
e.stopPropagation();
triggerSelect();
}
}), /*#__PURE__*/React.createElement(!disabled && ellipsisConfig && isOverflowing && title && Tooltip ? Tooltip : React.Fragment, !disabled && ellipsisConfig && isOverflowing && title && Tooltip ? (0, _objectSpread2.default)({
title: title,
overlayClassName: "".concat(prefixCls, "-menu-item-content-tooltip"),
destroyTooltipOnHide: true
}, ellipsisConfig) : null, /*#__PURE__*/React.createElement("div", {
id: contentId,
className: (0, _classnames.default)("".concat(menuItemPrefixCls, "-content"), (0, _defineProperty2.default)({}, "".concat(menuItemPrefixCls, "-content-overflowing"), isOverflowing))
}, option.title)), expandIcon && !isMergedLeaf && /*#__PURE__*/React.createElement("div", {
className: "".concat(menuItemPrefixCls, "-expand-icon")
}, expandIcon));
};
return /*#__PURE__*/React.createElement("ul", {
className: (0, _classnames.default)(menuPrefixCls, (_classNames4 = {}, (0, _defineProperty2.default)(_classNames4, "".concat(menuPrefixCls, "-virtual"), virtual), (0, _defineProperty2.default)(_classNames4, "".concat(menuPrefixCls, "-searchValue"), !!searchValue), _classNames4)),
role: "menu",
onScroll: virtual ? undefined : handleScroll
}, virtual ? /*#__PURE__*/React.createElement(_rcVirtualList.default, {
data: options,
height: height,
itemHeight: itemHeight,
itemKey: "value",
fullHeight: false,
onVisibleChange: function onVisibleChange(visibleList) {
setVisibleOptions(visibleList);
},
onScroll: handleScroll
}, renderOption) : options.map(renderOption));
}