UNPKG

preact-arco-design

Version:

Arco Design React UI Library.

627 lines (532 loc) 17.5 kB
var __assign = this && this.__assign || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) { if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } } return t; }; return __assign.apply(this, arguments); }; var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = this && this.__generator || function (thisArg, body) { var _ = { label: 0, sent: function sent() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) { try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __read = this && this.__read || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) { ar.push(r.value); } } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import React, { useState, useEffect, useCallback } from "preact/compat"; import scrollIntoView from 'scroll-into-view-if-needed'; import isEqualWith from 'lodash/isEqualWith'; import { CSSTransition, TransitionGroup } from "preact-transition-group-4"; import cs from "../../_util/classNames"; import Option from "./option"; import { isFunction, isObject } from "../../_util/is"; import useRefs from "../../_util/hooks/useRefs"; import useForceUpdate from "../../_util/hooks/useForceUpdate"; import { ArrowDown, Esc, Enter, ArrowUp, ArrowRight, ArrowLeft } from "../../_util/keycode"; import useUpdate from "../../_util/hooks/useUpdate"; import { getMultipleCheckValue } from "../util"; import VirtualList from "../../_class/VirtualList"; var getLegalActiveNode = function getLegalActiveNode(options) { for (var index = 0; index < options.length; index++) { if (!options[index].disabled) { return options[index]; } } }; var getBaseActiveNode = function getBaseActiveNode(currentNode) { if (currentNode && currentNode.disabled) { var node = currentNode; while (node.parent) { if (node.parent.disabled) { node = node.parent; } else { break; } } return node; } return currentNode; }; export var getLegalIndex = function getLegalIndex(currentIndex, maxIndex) { if (currentIndex < 0) { return maxIndex; } if (currentIndex > maxIndex) { return 0; } return currentIndex; }; var ListPanel = function ListPanel(props) { var _a; var _b = __read(useRefs(), 2), activeOptionList = _b[0], setActiveOptionList = _b[1]; var _c = __read(useRefs(), 2), refWrapper = _c[0], setRefWrapper = _c[1]; var forceUpdate = useForceUpdate(); var store = props.store, prefixCls = props.prefixCls, value = props.value, multiple = props.multiple, renderFooter = props.renderFooter, renderOption = props.renderOption, showEmptyChildren = props.showEmptyChildren, loadMore = props.loadMore, renderEmpty = props.renderEmpty, rtl = props.rtl; var _d = __read(useState(store.findNodeByValue(value && value[value.length - 1]) || null), 2), activeNode = _d[0], setActiveNode = _d[1]; var options = store.getOptions(); var triggerChange = function triggerChange(newValue) { props.onChange && props.onChange(newValue); }; var loadData = function loadData(option) { return __awaiter(void 0, void 0, void 0, function () { var options_1, e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(!option.isLeaf && isFunction(loadMore) && !option.children)) return [3 /*break*/ , 5]; option.setLoading(true); forceUpdate(); _a.label = 1; case 1: _a.trys.push([1, 3,, 4]); return [4 /*yield*/ , loadMore(option.pathValue, option.pathValue.length)]; case 2: options_1 = _a.sent(); store.appendOptionChildren(option, options_1); store.setNodeCheckedByValue(props.value); return [3 /*break*/ , 4]; case 3: e_1 = _a.sent(); console.error(e_1); return [3 /*break*/ , 4]; case 4: option.setLoading(false); forceUpdate(); _a.label = 5; case 5: return [2 /*return*/ ]; } }); }); }; var _onClickOption = function onClickOption(option, isEnterClick) { if (isEnterClick === void 0) { isEnterClick = true; } return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { if (!option || option.disabled) { return [2 /*return*/ ]; } setActiveNode(option); loadData(option); // 在键盘上下左右键操作时,isEnterClick 是false,不触发triggerChange if (!multiple && isEnterClick) { if (props.changeOnSelect || option.isLeaf) { triggerChange([option.pathValue]); } } return [2 /*return*/ ]; }); }); }; var _onMultipleChecked = function onMultipleChecked(option, checked) { var newValue = getMultipleCheckValue(props.value, store, option, checked); if (option === activeNode) { // setActiveNode 不会执行rerender,需要forceupdate forceUpdate(); } setActiveNode(option); if (!props.changeOnSelect) { // 父子节点关联,选中复选框时执行loadMore,否则直接选中父节点 loadData(option); } triggerChange(newValue); }; var handleKeyDown = useCallback(function (e) { e.stopPropagation(); // 使用keycode,避免中文输入法输入时,触发enter,space等事件。 // p.s 中文输入时,keycode 都是229 var keyCode = e.keyCode || e.which; var nextActiveNode; switch (keyCode) { case Esc.code: { e.preventDefault(); props.onEsc(); break; } case ArrowDown.code: case ArrowUp.code: { if (!activeNode) { nextActiveNode = getLegalActiveNode(options); } else { var baseActiveNode = getBaseActiveNode(activeNode); var list = baseActiveNode.parent && baseActiveNode.parent.children || options; var diff = keyCode === ArrowDown.code ? 1 : -1; var nextIndex = getLegalIndex(baseActiveNode._index + diff, list.length - 1); while (nextIndex !== baseActiveNode._index) { nextActiveNode = list[nextIndex]; if (nextActiveNode.disabled) { nextIndex = getLegalIndex(nextIndex + diff, list.length - 1); } else { break; } } } _onClickOption(nextActiveNode, false); e.preventDefault(); return false; } case ArrowRight.code: { if (activeNode && !activeNode.disabled) { var list = activeNode.children || []; nextActiveNode = list[0] || activeNode; _onClickOption(nextActiveNode, false); } e.preventDefault(); return false; } case ArrowLeft.code: { if (activeNode) { var baseActiveNode = getBaseActiveNode(activeNode); nextActiveNode = baseActiveNode.parent || baseActiveNode; } _onClickOption(nextActiveNode, false); e.preventDefault(); return false; } case Enter.code: if (activeNode) { if (multiple) { _onMultipleChecked(activeNode, !activeNode._checked); } else { _onClickOption(activeNode); } } e.preventDefault(); return false; default: break; } }, [activeNode]); useUpdate(function () { setActiveNode(function (activeNode) { // store 改变时候,更新下activeNode.如果当前activeNode不存在于store里了,就设置为null var newActiveNode; if (activeNode && activeNode.pathValue && activeNode.pathValue.length) { var values = activeNode.pathValue; var parent_1 = { children: options }; values.map(function (value) { var list = parent_1.children || []; var item = list.find(function (x) { return x.value === value; }); if (item) { parent_1 = item; newActiveNode = item; } }); } return newActiveNode; }); }, [store]); useEffect(function () { if (props.popupVisible && options.length) { var scrollTo_1 = function scrollTo_1() { activeOptionList.forEach(function (activeOption, i) { activeOption && scrollIntoView(activeOption, { block: 'nearest', boundary: refWrapper[i] }); }); }; setTimeout(function () { scrollTo_1(); }); } }, [props.popupVisible, activeNode]); useEffect(function () { if (props.popupVisible) { document.addEventListener('keydown', handleKeyDown); } else { document.removeEventListener('keydown', handleKeyDown); } return function () { document.removeEventListener('keydown', handleKeyDown); }; }, [props.popupVisible, handleKeyDown]); var menus = function () { var list = [options]; var pathNodes = activeNode ? activeNode.getPathNodes() : []; pathNodes.forEach(function (option) { option && option.children && list.push(option.children); }); return list; }(); var dropdownColumnRender = isFunction(props.dropdownColumnRender) ? props.dropdownColumnRender : function (menu) { return menu; }; return !menus.length || !((_a = menus[0]) === null || _a === void 0 ? void 0 : _a.length) ? React.createElement(React.Fragment, null, renderEmpty()) : React.createElement(TransitionGroup, { component: React.Fragment }, menus.map(function (list, level) { var _a, _b, _c; var footer = renderFooter ? renderFooter(level, activeNode || null) : null; return list.length === 0 && !showEmptyChildren ? null : React.createElement(CSSTransition, { key: level, timeout: { enter: 300, exit: 0 }, classNames: "cascaderSlide", onEnter: function onEnter(e) { e.style.marginLeft = "-".concat(e.scrollWidth, "px"); }, onEntering: function onEntering(e) { e.style.marginLeft = "0px"; }, onEntered: function onEntered(e) { e.style.marginLeft = ''; } }, React.createElement("div", { className: cs("".concat(prefixCls, "-list-column"), (_a = {}, _a["".concat(prefixCls, "-list-column-virtual")] = props.virtualListProps && props.virtualListProps.threshold !== null, _a["".concat(prefixCls, "-list-column-rtl")] = rtl, _a)), style: __assign({ zIndex: menus.length - level }, props.dropdownMenuColumnStyle) }, dropdownColumnRender(React.createElement("div", { className: cs("".concat(prefixCls, "-list-wrapper"), (_b = {}, _b["".concat(prefixCls, "-list-wrapper-with-footer")] = footer !== null, _b)) }, list.length === 0 ? renderEmpty && renderEmpty(props.virtualListProps ? '100%' : 120) : React.createElement(VirtualList, __assign({ needFiller: false, threshold: props.virtualListProps ? 100 : null, data: list, isStaticItemHeight: true, itemKey: "value" }, isObject(props.virtualListProps) ? props.virtualListProps : {}, { wrapper: "ul", role: "menu", ref: function ref(node) { return setRefWrapper(node === null || node === void 0 ? void 0 : node.dom, level); }, className: cs("".concat(prefixCls, "-list"), "".concat(prefixCls, "-list-select"), (_c = {}, _c["".concat(prefixCls, "-list-multiple")] = multiple, _c["".concat(prefixCls, "-list-rtl")] = rtl, _c)) }), function (option) { var _a; var isActive = false; if (activeNode) { isActive = activeNode.pathValue[level] === option.value; } return React.createElement("li", { tabIndex: 0, role: "menuitem", "aria-haspopup": !option.isLeaf, "aria-expanded": isActive && !option.isLeaf, "aria-disabled": option.disabled, key: option.value, title: option.label, className: cs("".concat(prefixCls, "-list-item"), (_a = {}, _a["".concat(prefixCls, "-list-item-active")] = isActive, _a["".concat(prefixCls, "-list-item-disabled")] = option.disabled, _a)), ref: function ref(_ref) { if (isActive) { setActiveOptionList(_ref, level); } } }, React.createElement(Option, { prefixCls: prefixCls, rtl: rtl, multiple: multiple, option: option, // 叶子节点被选中 selected: !multiple && option.isLeaf && isEqualWith(props.value, option.pathValue), onMouseEnter: function onMouseEnter() { if (option.disabled) { return; } if (props.expandTrigger === 'hover') { setActiveNode(option); !option.isLeaf && loadData(option); } }, renderOption: renderOption && function () { return renderOption(option._data, level); }, onClickOption: function onClickOption() { if (option.isLeaf && multiple && !option.disableCheckbox) { _onMultipleChecked(option, !option._checked); } else { _onClickOption(option); } }, onMultipleChecked: function onMultipleChecked(checked) { _onMultipleChecked(option, checked); }, onDoubleClickOption: props.onDoubleClickOption })); }), footer && React.createElement("div", { className: "".concat(prefixCls, "-list-footer"), onMouseDown: function onMouseDown(e) { // 这里是为了阻止冒泡到面板节点的onMousedown事件。因为弹出层会阻止默认行为,避免选择框失去焦点 // 如果这里不阻止冒泡,footer里如果渲染了input标签,将无法被focus e.stopPropagation(); } }, footer)), level))); })); }; export default ListPanel;