UNPKG

bonree-cascader

Version:

cascade select ui component for react

311 lines (263 loc) 12 kB
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)); }