@arco-design/web-react
Version:
Arco Design React UI Library.
384 lines (383 loc) • 18.6 kB
JavaScript
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() { 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 'react';
import isEqualWith from 'lodash/isEqualWith';
import { TransitionGroup } from 'react-transition-group';
import cs from '../../_util/classNames';
import Option from './option';
import { isFunction, isObject, isString } 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';
import { on, off } from '../../_util/dom';
import ArcoCSSTransition from '../../_util/CSSTransition';
var getLegalActiveNode = function (options) {
for (var index = 0; index < options.length; index++) {
if (!options[index].disabled) {
return options[index];
}
}
};
var getBaseActiveNode = function (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 (currentIndex, maxIndex) {
if (currentIndex < 0) {
return maxIndex;
}
if (currentIndex > maxIndex) {
return 0;
}
return currentIndex;
};
var ListPanel = function (props) {
var _a;
var _b = __read(useRefs(), 2), refWrapper = _b[0], setRefWrapper = _b[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, icons = props.icons;
var _c = __read(useState(store.findNodeByValue(value && value[value.length - 1]) || null), 2), activeNode = _c[0], setActiveNode = _c[1];
var options = store.getOptions();
var triggerChange = function (newValue) {
props.onChange && props.onChange(newValue);
};
var loadData = function (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 (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 (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 scrollToActiveNode = useCallback(function (targetNode) {
var _a;
if (targetNode === void 0) { targetNode = activeNode; }
var current = targetNode;
while (current) {
(_a = refWrapper[current._level]) === null || _a === void 0 ? void 0 : _a.scrollTo({
index: current._index,
options: { block: 'nearest' },
});
current = current._level < 1 ? null : current.parent;
}
}, [activeNode]);
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;
}
}
}
scrollToActiveNode(nextActiveNode);
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) {
setTimeout(scrollToActiveNode);
}
}, [props.popupVisible]);
useEffect(function () {
var target = props.getTriggerElement();
if (!target) {
return;
}
if (props.popupVisible) {
on(target, 'keydown', handleKeyDown);
}
else {
off(target, 'keydown', handleKeyDown);
}
return function () {
off(target, '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(ArcoCSSTransition, { key: level, timeout: {
enter: 300,
exit: 0,
}, classNames: "cascaderSlide", onEnter: function (e) {
if (!e)
return;
e.style.marginLeft = "-" + e.scrollWidth + "px";
}, onEntering: function (e) {
if (!e)
return;
e.style.marginLeft = "0px";
}, onEntered: function (e) {
if (!e)
return;
e.style.marginLeft = '';
} },
React.createElement("div", { className: cs(prefixCls + "-list-column", (_a = {},
_a[prefixCls + "-list-column-virtual"] = props.virtualListProps && props.virtualListProps.threshold !== null,
_a[prefixCls + "-list-column-rtl"] = rtl,
_a)), style: __assign({ zIndex: menus.length - level }, props.dropdownMenuColumnStyle) }, dropdownColumnRender(React.createElement("div", { className: cs(prefixCls + "-list-wrapper", (_b = {},
_b[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 (node) { return setRefWrapper(node, level); }, className: cs(prefixCls + "-list", prefixCls + "-list-select", (_c = {},
_c[prefixCls + "-list-multiple"] = multiple,
_c[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: isString(option.label) ? option.label : undefined, className: cs(prefixCls + "-list-item", (_a = {},
_a[prefixCls + "-list-item-active"] = isActive,
_a[prefixCls + "-list-item-disabled"] = option.disabled,
_a)) },
React.createElement(Option, { prefixCls: prefixCls, rtl: rtl, multiple: multiple, option: option,
// 叶子节点被选中
selected: !multiple &&
option.isLeaf &&
isEqualWith(props.value, option.pathValue), icons: icons, onMouseEnter: function () {
if (option.disabled) {
return;
}
if (props.expandTrigger === 'hover') {
setActiveNode(option);
!option.isLeaf && loadData(option);
}
}, renderOption: renderOption &&
(function () {
return renderOption(option._data, level);
}), onClickOption: function () {
if (option.isLeaf && multiple && !option.disableCheckbox) {
onMultipleChecked(option, !option._checked);
}
else {
onClickOption(option);
}
}, onMultipleChecked: function (checked) {
onMultipleChecked(option, checked);
}, onDoubleClickOption: props.onDoubleClickOption })));
})),
footer && (React.createElement("div", { className: prefixCls + "-list-footer", onMouseDown: function (e) {
// 这里是为了阻止冒泡到面板节点的onMousedown事件。因为弹出层会阻止默认行为,避免选择框失去焦点
// 如果这里不阻止冒泡,footer里如果渲染了input标签,将无法被focus
e.stopPropagation();
} }, footer))), level))));
})));
};
export default ListPanel;