bonree-cascader
Version:
cascade select ui component for react
311 lines (263 loc) • 12 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import * as React from 'react';
import classNames from 'classnames';
import VirtualList from 'rc-virtual-list';
import { LOAD_STATUS } from '../Cascader';
import { ALL_KEY, connectValue, DATA_EMPTY, isLeaf } from '../util';
import CascaderContext from '../context';
import Checkbox from './Checkbox';
import { judgeOverflowing, getUUID } from "bonree-select/es/utils/commonUtil";
export default 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(getUUID());
var _React$useContext = React.useContext(CascaderContext),
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 = _slicedToArray(_React$useState, 2),
overflowMap = _React$useState2[0],
setOverflowMap = _React$useState2[1]; // 虚拟滚动模式下,跟踪当前可见的选项
var _React$useState3 = React.useState([]),
_React$useState4 = _slicedToArray(_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 === ALL_KEY) return;
var contentId = "cascader-content-".concat(option.value, "-").concat(uuidRef.current);
var el = document.getElementById(contentId);
if (el) {
newEntries[option.value] = judgeOverflowing(el);
}
}); // 非虚拟模式或 options 变化时直接替换;虚拟模式下增量合并,保留已检测过的结果
if (!virtual || optionsChanged) {
setOverflowMap(newEntries);
} else {
setOverflowMap(function (prev) {
return _objectSpread(_objectSpread({}, 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 !== 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 !== 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 === ALL_KEY) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: classNames(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-all"), _defineProperty({}, "".concat(menuItemPrefixCls, "-disabled"), disabled)),
onClick: function onClick() {
return !treeCheckStrictly && !disabled && handleSelectAll(value);
}
}, multiple && /*#__PURE__*/React.createElement(Checkbox, {
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 = isLeaf(option); // const isLoading = loadingKeys.includes(value);
// 加载中
var isLoading = value === connectValue([openFinalValue, LOAD_STATUS.LOADING]) || value === LOAD_STATUS.LOADING;
if (isLoading) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: classNames(menuItemPrefixCls, "".concat(menuItemPrefixCls, "-loading")),
style: dropdownMenuColumnStyle
}, loadingIcon);
} // 加载数据失败
var isLoadFailed = value === connectValue([openFinalValue, LOAD_STATUS.FAILED]) || value === LOAD_STATUS.FAILED;
if (isLoadFailed) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: classNames(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 === connectValue([openFinalValue, LOAD_STATUS.EMPTY]) || value === LOAD_STATUS.EMPTY;
if (isNoData) {
return /*#__PURE__*/React.createElement("li", {
key: value,
className: classNames(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: classNames(menuItemPrefixCls, (_classNames2 = {}, _defineProperty(_classNames2, "".concat(menuItemPrefixCls, "-expand"), !isMergedLeaf), _defineProperty(_classNames2, "".concat(menuItemPrefixCls, "-active"), openKey === value), _defineProperty(_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 !== DATA_EMPTY && !isLoading && /*#__PURE__*/React.createElement(Checkbox, {
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 ? _objectSpread({
title: title,
overlayClassName: "".concat(prefixCls, "-menu-item-content-tooltip"),
destroyTooltipOnHide: true
}, ellipsisConfig) : null, /*#__PURE__*/React.createElement("div", {
id: contentId,
className: classNames("".concat(menuItemPrefixCls, "-content"), _defineProperty({}, "".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: classNames(menuPrefixCls, (_classNames4 = {}, _defineProperty(_classNames4, "".concat(menuPrefixCls, "-virtual"), virtual), _defineProperty(_classNames4, "".concat(menuPrefixCls, "-searchValue"), !!searchValue), _classNames4)),
role: "menu",
onScroll: virtual ? undefined : handleScroll
}, virtual ? /*#__PURE__*/React.createElement(VirtualList, {
data: options,
height: height,
itemHeight: itemHeight,
itemKey: "value",
fullHeight: false,
onVisibleChange: function onVisibleChange(visibleList) {
setVisibleOptions(visibleList);
},
onScroll: handleScroll
}, renderOption) : options.map(renderOption));
}