UNPKG

@alifd/next

Version:

A configurable component library for web built on React.

468 lines (467 loc) 21.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TreeNode = void 0; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importStar(require("react")); var react_dom_1 = require("react-dom"); var prop_types_1 = tslib_1.__importDefault(require("prop-types")); var react_lifecycles_compat_1 = require("react-lifecycles-compat"); var classnames_1 = tslib_1.__importDefault(require("classnames")); var icon_1 = tslib_1.__importDefault(require("../../icon")); var checkbox_1 = tslib_1.__importDefault(require("../../checkbox")); var animate_1 = tslib_1.__importDefault(require("../../animate")); var util_1 = require("../../util"); var tree_node_input_1 = tslib_1.__importDefault(require("./tree-node-input")); var tree_node_indent_1 = tslib_1.__importDefault(require("./tree-node-indent")); var Expand = animate_1.default.Expand; var bindCtx = util_1.func.bindCtx; var isPromise = util_1.obj.isPromise, pickOthers = util_1.obj.pickOthers, pickAttrsWith = util_1.obj.pickAttrsWith; var isRoot = function (pos) { return /^0-(\d)+$/.test(pos); }; /** * Tree.Node */ var TreeNode = /** @class */ (function (_super) { tslib_1.__extends(TreeNode, _super); function TreeNode(props) { var _this = _super.call(this, props) || this; _this.saveRef = function (ref) { _this.nodeEl = ref; }; _this.saveLabelWrapperRef = function (ref) { _this.labelWrapperEl = ref; }; _this.state = { editing: false, loading: false, label: props.label, }; bindCtx(_this, [ 'handleExpand', 'handleSelect', 'handleCheck', 'handleEditStart', 'handleEditFinish', 'handleRightClick', 'handleDragStart', 'handleDragEnter', 'handleDragOver', 'handleDragLeave', 'handleDragEnd', 'handleDrop', 'handleInputKeyDown', 'handleKeyDown', ]); return _this; } TreeNode.getDerivedStateFromProps = function (props) { if ('label' in props) { return { label: props.label, }; } return null; }; TreeNode.prototype.componentDidMount = function () { this.itemLabelWrapperNode = (0, react_dom_1.findDOMNode)(this.labelWrapperEl); this.setFocus(); }; TreeNode.prototype.componentDidUpdate = function () { this.setFocus(); }; TreeNode.prototype.focusable = function () { var _a = this.props, root = _a.root, disabled = _a.disabled; var focusable = root.props.focusable; return focusable && !disabled; }; TreeNode.prototype.getFocused = function () { var _a = this.props, _key = _a._key, root = _a.root; var focusedKey = root.state.focusedKey; return focusedKey === _key; }; TreeNode.prototype.setFocus = function () { var focused = this.getFocused(); if (focused && this.focusable()) { this.itemLabelWrapperNode.focus({ preventScroll: true }); } }; TreeNode.prototype.handleExpand = function (e) { var _this = this; var _a = this.props, root = _a.root, expanded = _a.expanded, eventKey = _a.eventKey; if (root.props.isNodeBlock) { e.stopPropagation(); } var loading = this.state.loading; if (loading) { return; } var returnValue = root.handleExpand(!expanded, eventKey, this); if (isPromise(returnValue)) { this.setLoading(true); return returnValue.then(function () { _this.setLoading(false); }, function () { _this.setLoading(false); }); } }; TreeNode.prototype.setLoading = function (loading) { this.setState({ loading: loading }); }; TreeNode.prototype.handleSelect = function (e) { e.preventDefault(); var _a = this.props, root = _a.root, selected = _a.selected, eventKey = _a.eventKey; root.handleSelect(!selected, eventKey, this, e); var checkable = typeof this.props.checkable !== 'undefined' ? this.props.checkable : root.props.checkable; var clickToCheck = typeof this.props.clickToCheck !== 'undefined' ? this.props.clickToCheck : root.props.clickToCheck; clickToCheck && checkable && this.handleCheck(); }; TreeNode.prototype.handleCheck = function () { var _a = this.props, root = _a.root, checked = _a.checked, eventKey = _a.eventKey; root.handleCheck(!checked, eventKey, this); }; TreeNode.prototype.handleEditStart = function (e) { e.preventDefault(); this.setState({ editing: true, }); }; TreeNode.prototype.handleEditFinish = function (e) { var target = e.target; var label = target.value; this.setState({ editing: false, }); var _a = this.props, root = _a.root, eventKey = _a.eventKey; root.props.onEditFinish(eventKey, label, this); }; TreeNode.prototype.handleRightClick = function (e) { this.props.root.props.onRightClick({ event: e, node: this, }); }; TreeNode.prototype.handleDragStart = function (e) { e.stopPropagation(); this.props.root.handleDragStart(e, this); }; TreeNode.prototype.handleDragEnter = function (e) { e.preventDefault(); e.stopPropagation(); this.props.root.handleDragEnter(e, this); }; TreeNode.prototype.handleDragOver = function (e) { if (this.props.root.canDrop(this)) { e.preventDefault(); this.props.root.handleDragOver(e, this); } e.stopPropagation(); }; TreeNode.prototype.handleDragLeave = function (e) { e.stopPropagation(); this.props.root.handleDragLeave(e, this); }; TreeNode.prototype.handleDragEnd = function (e) { e.stopPropagation(); this.props.root.handleDragEnd(e, this); }; TreeNode.prototype.handleDrop = function (e) { e.preventDefault(); e.stopPropagation(); this.props.root.handleDrop(e, this); }; TreeNode.prototype.handleInputKeyDown = function (e) { if (e.keyCode === util_1.KEYCODE.ENTER) { this.handleEditFinish(e); } e.stopPropagation(); }; TreeNode.prototype.handleKeyDown = function (e) { var _a = this.props, _key = _a._key, root = _a.root, disabled = _a.disabled; if (disabled) { return; } if (this.focusable()) { root.handleItemKeyDown(_key, this, e); } this.props.onKeyDown && this.props.onKeyDown(e); }; TreeNode.prototype.addCallbacks = function (props) { var _a = this.props, disabled = _a.disabled, root = _a.root; var checkable = typeof this.props.checkable !== 'undefined' ? this.props.checkable : root.props.checkable; var clickToCheck = typeof this.props.clickToCheck !== 'undefined' ? this.props.clickToCheck : root.props.clickToCheck; if (!disabled) { var selectable = typeof this.props.selectable !== 'undefined' ? this.props.selectable : root.props.selectable; if (selectable) { props.onClick = this.handleSelect; } else if (clickToCheck && checkable) { props.onClick = this.handleCheck; } var editable = typeof this.props.editable !== 'undefined' ? this.props.editable : root.props.editable; if (editable) { props.onDoubleClick = this.handleEditStart; } var draggable = typeof this.props.draggable !== 'undefined' ? this.props.draggable : root.props.draggable; if (draggable) { props.draggable = true; props.onDragStart = this.handleDragStart; props.onDragEnd = this.handleDragEnd; } props.onContextMenu = this.handleRightClick; } }; TreeNode.prototype.renderSwitcher = function (showLine) { var _a, _b; var _c = this.props, prefix = _c.prefix, disabled = _c.disabled, expanded = _c.expanded, root = _c.root; var loadData = root.props.loadData; var loading = this.state.loading; var lineState = showLine ? 'line' : 'noline'; var className = (0, classnames_1.default)((_a = {}, _a["".concat(prefix, "tree-switcher")] = true, _a["".concat(prefix).concat(lineState)] = !loading, _a["".concat(prefix, "close")] = !loading && !showLine && !expanded, _a["".concat(prefix, "disabled")] = disabled, _a["".concat(prefix, "loading")] = loading, _a["".concat(prefix, "loading-").concat(lineState)] = loading, _a)); var iconType = loadData && loading ? 'loading' : showLine ? expanded ? 'minus' : 'add' : 'arrow-down'; var iconCls = (0, classnames_1.default)((_b = {}, _b["".concat(prefix, "tree-switcher-icon")] = true, _b["".concat(prefix, "tree-fold-icon")] = iconType === 'arrow-down', _b["".concat(prefix, "tree-switcher-fold-icon")] = showLine && !expanded, _b["".concat(prefix, "tree-switcher-unfold-icon")] = showLine && expanded, _b)); return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events react_1.default.createElement("span", { className: className, onClick: disabled ? undefined : this.handleExpand }, this.renderRightAngle(showLine), react_1.default.createElement(icon_1.default, { className: iconCls, type: iconType }))); }; TreeNode.prototype.renderNoopSwitcher = function (showLine) { var _a; var _b = this.props, prefix = _b.prefix, pos = _b.pos; var lineState = showLine ? 'line' : 'noline'; var className = (0, classnames_1.default)((_a = {}, _a["".concat(prefix, "tree-switcher")] = true, _a["".concat(prefix, "noop-").concat(lineState)] = true, _a["".concat(prefix, "noop-line-noroot")] = showLine && !isRoot(pos), _a)); return react_1.default.createElement("span", { className: className }, this.renderRightAngle(showLine)); }; TreeNode.prototype.renderRightAngle = function (showLine) { var _a = this.props, prefix = _a.prefix, pos = _a.pos; return showLine && !isRoot(pos) ? react_1.default.createElement("span", { className: "".concat(prefix, "tree-right-angle") }) : null; }; TreeNode.prototype.renderCheckbox = function () { var _a = this.props, checked = _a.checked, indeterminate = _a.indeterminate, disabled = _a.disabled, checkboxDisabled = _a.checkboxDisabled; var label = this.state.label; return ( // @ts-expect-error aria-label should be undefined instead of null react_1.default.createElement(checkbox_1.default, { "aria-label": typeof label === 'string' ? label : null, checked: checked, tabIndex: -1, indeterminate: indeterminate, disabled: disabled || checkboxDisabled, // don't use onChange, fix https://github.com/alibaba-fusion/next/issues/3850 onClick: this.handleCheck })); }; TreeNode.prototype.renderLabel = function () { var _a; var _b = this.props, prefix = _b.prefix, root = _b.root, disabled = _b.disabled, icon = _b.icon, _key = _b._key; var isNodeBlock = root.props.isNodeBlock; var label = this.state.label; var selectable = typeof this.props.selectable !== 'undefined' ? this.props.selectable : root.props.selectable; var labelProps = { className: (0, classnames_1.default)((_a = {}, _a["".concat(prefix, "tree-node-label")] = true, _a["".concat(prefix, "tree-node-label-selectable")] = selectable && !disabled, _a)), }; var labelWrapperProps = { onKeyDown: this.handleKeyDown, // @ts-expect-error should covert to number tabIndex: (root.tabbableKey === _key ? '0' : '-1'), }; if (!isNodeBlock) { this.addCallbacks(labelProps); } var iconEl = typeof icon === 'string' ? react_1.default.createElement(icon_1.default, { type: icon }) : icon; return (react_1.default.createElement("div", tslib_1.__assign({ className: "".concat(prefix, "tree-node-label-wrapper"), ref: this.saveLabelWrapperRef }, labelWrapperProps), react_1.default.createElement("div", tslib_1.__assign({}, labelProps), iconEl, label))); }; TreeNode.prototype.renderInput = function () { var prefix = this.props.prefix; var label = this.state.label; return (react_1.default.createElement("div", { className: "".concat(prefix, "tree-node-label-wrapper"), ref: this.saveLabelWrapperRef }, react_1.default.createElement(tree_node_input_1.default, { prefix: prefix, defaultValue: label, onBlur: this.handleEditFinish, onKeyDown: this.handleInputKeyDown }))); }; TreeNode.prototype.renderChildTree = function () { var _a = this.props, prefix = _a.prefix, children = _a.children; return (children && this.addAnimationIfNeeded(react_1.default.createElement("ul", { role: "group", className: "".concat(prefix, "tree-child-tree") }, children))); }; TreeNode.prototype.addAnimationIfNeeded = function (node) { var root = this.props.root; return root && root.props.animation ? (react_1.default.createElement(Expand, { animationAppear: false }, node)) : (node); }; TreeNode.prototype.render = function () { var _a, _b, _c; var _d = this.props, prefix = _d.prefix, rtl = _d.rtl, className = _d.className, isLeaf = _d.isLeaf, level = _d.level, root = _d.root, selected = _d.selected, checked = _d.checked, disabled = _d.disabled, dragOver = _d.dragOver, dragOverGapTop = _d.dragOverGapTop, dragOverGapBottom = _d.dragOverGapBottom, _key = _d._key, size = _d.size, posinset = _d.posinset, children = _d.children, expanded = _d.expanded, isLastChild = _d.isLastChild; var _e = root.props, isNodeBlock = _e.isNodeBlock, showLine = _e.showLine, rootDraggable = _e.draggable, filterTreeNode = _e.filterTreeNode; var label = this.state.label; var ARIA_PREFIX = 'aria-'; var ariaProps = pickAttrsWith(this.props, ARIA_PREFIX); var others = pickOthers(Object.keys(TreeNode.propTypes), this.props); var hasRenderChildNodes = root && root.props.renderChildNodes; var shouldShouldLine = !isNodeBlock && showLine && !hasRenderChildNodes; var useVirtual = root && root.props.useVirtual; // remove aria keys Object.keys(others).forEach(function (key) { if (key.match(ARIA_PREFIX)) { delete others[key]; } }); if (rootDraggable) { others.onDragEnter = this.handleDragEnter; others.onDragOver = this.handleDragOver; others.onDragLeave = this.handleDragLeave; others.onDrop = this.handleDrop; } var newClassName = (0, classnames_1.default)((_a = {}, _a["".concat(prefix, "tree-node")] = true, _a["".concat(prefix, "filtered")] = !!filterTreeNode && !!root.filterTreeNode(this), _a[className] = !!className, _a)); var checkable = typeof this.props.checkable !== 'undefined' ? this.props.checkable : root.props.checkable; var innerClassName = (0, classnames_1.default)((_b = {}, _b["".concat(prefix, "tree-node-inner")] = true, _b["".concat(prefix, "selected")] = selected, _b["".concat(prefix, "disabled")] = disabled, _b["".concat(prefix, "drag-over")] = dragOver, _b["".concat(prefix, "drag-over-gap-top")] = dragOverGapTop, _b["".concat(prefix, "drag-over-gap-bottom")] = dragOverGapBottom, _b)); var defaultPaddingLeft = typeof isNodeBlock === 'object' ? // @ts-expect-error should convert 0 to '0' parseInt(isNodeBlock.defaultPaddingLeft || 0) : 0; var paddingLeftProp = rtl ? 'paddingRight' : 'paddingLeft'; var indent = // @ts-expect-error should convert 24 to '24' typeof isNodeBlock === 'object' ? parseInt(isNodeBlock.indent || 24) : 24; var innerStyle = isNodeBlock ? (_c = {}, _c[paddingLeftProp] = "".concat((useVirtual ? 0 : indent * (level - 1)) + defaultPaddingLeft, "px"), _c) : null; var innerProps = tslib_1.__assign({ className: innerClassName, style: innerStyle, onKeyDown: this.handleKeyDown }, ariaProps); if (isNodeBlock) { this.addCallbacks(innerProps); } var editing = this.state.editing; // @ts-expect-error should convert to number type innerProps.tabIndex = root.tabbableKey === _key ? '0' : '-1'; if (rtl) { others.dir = 'rtl'; } return this.addAnimationIfNeeded(react_1.default.createElement("li", tslib_1.__assign({ role: "presentation", className: newClassName }, others), react_1.default.createElement("div", tslib_1.__assign({ ref: this.saveRef, role: "treeitem", "aria-selected": selected, "aria-disabled": disabled, "aria-checked": checked, "aria-expanded": !isLeaf, "aria-label": typeof label === 'string' ? label : null, "aria-level": level, "aria-posinset": posinset, "aria-setsize": size }, innerProps), useVirtual && !hasRenderChildNodes && (react_1.default.createElement(tree_node_indent_1.default, { prefix: prefix, level: level, isLastChild: isLastChild, showLine: shouldShouldLine })), isLeaf ? this.renderNoopSwitcher(shouldShouldLine) : this.renderSwitcher(shouldShouldLine), checkable ? this.renderCheckbox() : null, editing ? this.renderInput() : this.renderLabel()), expanded && (hasRenderChildNodes ? children : this.renderChildTree()))); }; TreeNode.propTypes = { prefix: prop_types_1.default.string, rtl: prop_types_1.default.bool, _key: prop_types_1.default.string, className: prop_types_1.default.string, children: prop_types_1.default.node, label: prop_types_1.default.node, /** * 单独设置是否支持选中,覆盖 Tree 的 selectable */ selectable: prop_types_1.default.bool, /** * 单独设置是否出现复选框,覆盖 Tree 的 checkable */ checkable: prop_types_1.default.bool, /** * 单独设置是否支持编辑,覆盖 Tree 的 editable */ editable: prop_types_1.default.bool, /** * 单独设置是否支持拖拽,覆盖 Tree 的 draggable */ draggable: prop_types_1.default.bool, /** * 是否禁止节点响应 */ disabled: prop_types_1.default.bool, /** * 是否禁止勾选节点复选框 */ checkboxDisabled: prop_types_1.default.bool, /** * 是否是叶子节点,设置loadData时生效 */ isLeaf: prop_types_1.default.bool, root: prop_types_1.default.object, eventKey: prop_types_1.default.string, pos: prop_types_1.default.string, expanded: prop_types_1.default.bool, selected: prop_types_1.default.bool, checked: prop_types_1.default.bool, indeterminate: prop_types_1.default.bool, dragOver: prop_types_1.default.bool, dragOverGapTop: prop_types_1.default.bool, dragOverGapBottom: prop_types_1.default.bool, parentNode: prop_types_1.default.object, onKeyDown: prop_types_1.default.func, size: prop_types_1.default.number, posinset: prop_types_1.default.number, // 是否是最后一个子节点,数组类型,包含对祖先节点的判断 isLastChild: prop_types_1.default.arrayOf(prop_types_1.default.bool), /** * 自定义图标,可以使用 Icon 的 type,也可以使用组件 `<Icon type="icon type" />` * @version 1.21 */ icon: prop_types_1.default.oneOfType([prop_types_1.default.string, prop_types_1.default.node]), }; TreeNode.defaultProps = { prefix: 'next-', label: '---', rtl: false, disabled: false, checkboxDisabled: false, size: 1, posinset: 1, }; return TreeNode; }(react_1.Component)); exports.TreeNode = TreeNode; exports.default = (0, react_lifecycles_compat_1.polyfill)(TreeNode);