UNPKG

@arco-design/web-vue

Version:

Arco Design Vue 2.0: A Vue.js 3 UI Library

715 lines (714 loc) 22.9 kB
import { defineComponent, toRefs, computed, reactive, ref, provide, resolveComponent, openBlock, createElementBlock, normalizeClass, createBlock, mergeProps, withCtx, Fragment, renderList } from "vue"; import { getPrefixCls } from "../_utils/global-config.js"; import { TreeInjectionKey } from "./context.js"; import usePickSlots from "../_hooks/use-pick-slots.js"; import { isLeafNode, isNodeSelectable, isNodeExpandable } from "./utils/index.js"; import { isNodeCheckable, getCheckedStateByCheck } from "./utils/check-utils.js"; import TreeNode from "./node.js"; import { isUndefined, isFunction, isArray } from "../_utils/is.js"; import useMergeState from "../_hooks/use-merge-state.js"; import useCheckedState from "./hooks/use-checked-state.js"; import useTreeData from "./hooks/use-tree-data.js"; import VirtualList from "../_components/virtual-list-v2/virtual-list.js"; import _export_sfc from "../_virtual/plugin-vue_export-helper.js"; const _sfc_main = defineComponent({ name: "Tree", components: { VirtualList, TreeNode }, props: { size: { type: String, default: "medium" }, blockNode: { type: Boolean }, defaultExpandAll: { type: Boolean, default: true }, multiple: { type: Boolean }, checkable: { type: [Boolean, String, Function], default: false }, selectable: { type: [Boolean, Function], default: true }, checkStrictly: { type: Boolean }, checkedStrategy: { type: String, default: "all" }, defaultSelectedKeys: { type: Array }, selectedKeys: { type: Array }, defaultCheckedKeys: { type: Array }, checkedKeys: { type: Array }, defaultExpandedKeys: { type: Array }, expandedKeys: { type: Array }, data: { type: Array, default: () => [] }, fieldNames: { type: Object }, showLine: { type: Boolean }, loadMore: { type: Function }, draggable: { type: Boolean }, allowDrop: { type: Function }, filterTreeNode: { type: Function }, searchValue: { type: String, default: "" }, virtualListProps: { type: Object }, defaultExpandSelected: { type: Boolean }, defaultExpandChecked: { type: Boolean }, autoExpandParent: { type: Boolean, default: true }, halfCheckedKeys: { type: Array }, onlyCheckLeaf: { type: Boolean, default: false }, animation: { type: Boolean, default: true }, actionOnNodeClick: { type: String }, disableSelectActionOnly: { type: Boolean, default: false } }, emits: { "select": (selectedKeys, data) => true, "update:selectedKeys": (selectedKeys) => true, "check": (checkedKeys, data) => true, "update:checkedKeys": (checkedKeys) => true, "update:halfCheckedKeys": (halfCheckedKeys) => true, "expand": (expandKeys, data) => true, "update:expandedKeys": (expandKeys) => true, "dragStart": (ev, node) => true, "dragEnd": (ev, node) => true, "dragOver": (ev, node) => true, "dragLeave": (ev, node) => true, "drop": (data) => true }, setup(props, { emit, slots }) { const { data: propTreeData, showLine, multiple, loadMore, checkStrictly, checkedKeys: propCheckedKeys, defaultCheckedKeys, selectedKeys: propSelectedKeys, defaultSelectedKeys, expandedKeys: propExpandedKeys, defaultExpandedKeys, checkedStrategy, selectable, checkable, blockNode, fieldNames, size, defaultExpandAll, filterTreeNode, draggable, allowDrop, defaultExpandSelected, defaultExpandChecked, autoExpandParent, halfCheckedKeys, onlyCheckLeaf, animation } = toRefs(props); const prefixCls = getPrefixCls("tree"); const classNames = computed(() => [ `${prefixCls}`, { [`${prefixCls}-checkable`]: checkable.value, [`${prefixCls}-show-line`]: showLine.value }, `${prefixCls}-size-${size.value}` ]); const switcherIcon = usePickSlots(slots, "switcher-icon"); const loadingIcon = usePickSlots(slots, "loading-icon"); const dragIcon = usePickSlots(slots, "drag-icon"); const nodeIcon = usePickSlots(slots, "icon"); const nodeTitle = usePickSlots(slots, "title"); const nodeExtra = usePickSlots(slots, "extra"); const { treeData, flattenTreeData, key2TreeNode } = useTreeData( reactive({ treeData: propTreeData, selectable, showLine, blockNode, checkable, fieldNames, loadMore, draggable }) ); const { checkedKeys, indeterminateKeys, setCheckedState } = useCheckedState( reactive({ defaultCheckedKeys, checkedKeys: propCheckedKeys, checkStrictly, key2TreeNode, halfCheckedKeys, onlyCheckLeaf }) ); const [selectedKeys, setSelectedState] = useMergeState( (defaultSelectedKeys == null ? void 0 : defaultSelectedKeys.value) || [], reactive({ value: propSelectedKeys }) ); const loadingKeys = ref([]); const dragNode = ref(); function getDefaultExpandedKeys() { if (defaultExpandedKeys == null ? void 0 : defaultExpandedKeys.value) { const expandedKeysSet = /* @__PURE__ */ new Set([]); defaultExpandedKeys.value.forEach((_key) => { if (expandedKeysSet.has(_key)) return; const node = key2TreeNode.value.get(_key); if (!node) return; [ ...autoExpandParent.value ? node.pathParentKeys : [], _key ].forEach((_key2) => expandedKeysSet.add(_key2)); }); return [...expandedKeysSet]; } if (defaultExpandAll.value) { return flattenTreeData.value.filter((node) => node.children && node.children.length).map((node) => node.key); } if (defaultExpandSelected.value || defaultExpandChecked.value) { const expandedKeysSet = /* @__PURE__ */ new Set([]); const addToExpandKeysSet = (keys) => { keys.forEach((key) => { const node = key2TreeNode.value.get(key); if (!node) return; (node.pathParentKeys || []).forEach((k) => expandedKeysSet.add(k)); }); }; if (defaultExpandSelected.value) { addToExpandKeysSet(selectedKeys.value); } if (defaultExpandChecked.value) { addToExpandKeysSet(checkedKeys.value); } return [...expandedKeysSet]; } return []; } const [expandedKeys, setExpandState] = useMergeState( getDefaultExpandedKeys(), reactive({ value: propExpandedKeys }) ); const currentExpandKeys = ref([]); const visibleTreeNodeList = computed(() => { const expandedKeysSet = new Set(expandedKeys.value); const currentExpandKeysSet = new Set(currentExpandKeys.value); return flattenTreeData.value.filter((node) => { var _a; const passFilter = !filterTreeNode || !filterTreeNode.value || (filterTreeNode == null ? void 0 : filterTreeNode.value(node.treeNodeData)); if (!passFilter) return false; const isRoot = isUndefined(node.parentKey); const isVisibleNode = (_a = node.pathParentKeys) == null ? void 0 : _a.every( (_key) => expandedKeysSet.has(_key) && !currentExpandKeysSet.has(_key) ); return isRoot || isVisibleNode; }); }); function getPublicCheckedKeys(rawCheckedKeys, rawCheckedStrategy = checkedStrategy.value) { let publicCheckedKeys = [...rawCheckedKeys]; if (rawCheckedStrategy === "parent") { publicCheckedKeys = rawCheckedKeys.filter((_key) => { const item = key2TreeNode.value.get(_key); return item && !(!isUndefined(item.parentKey) && rawCheckedKeys.includes(item.parentKey)); }); } else if (rawCheckedStrategy === "child") { publicCheckedKeys = rawCheckedKeys.filter((_key) => { var _a, _b; return !((_b = (_a = key2TreeNode.value.get(_key)) == null ? void 0 : _a.children) == null ? void 0 : _b.length); }); } return publicCheckedKeys; } function getNodes(keys) { return keys.map((key) => { var _a; return ((_a = key2TreeNode.value.get(key)) == null ? void 0 : _a.treeNodeData) || void 0; }).filter(Boolean); } function emitCheckEvent(options) { const { targetKey, targetChecked, newCheckedKeys, newIndeterminateKeys, event } = options; const targetNode = targetKey ? key2TreeNode.value.get(targetKey) : void 0; const publicCheckedKeys = getPublicCheckedKeys(newCheckedKeys); emit("update:checkedKeys", publicCheckedKeys); emit("update:halfCheckedKeys", newIndeterminateKeys); emit("check", publicCheckedKeys, { checked: targetChecked, node: targetNode == null ? void 0 : targetNode.treeNodeData, checkedNodes: getNodes(publicCheckedKeys), halfCheckedKeys: newIndeterminateKeys, halfCheckedNodes: getNodes(newIndeterminateKeys), e: event }); } function emitSelectEvent(options) { const { targetKey, targetSelected, newSelectedKeys, event } = options; const targetNode = targetKey ? key2TreeNode.value.get(targetKey) : void 0; emit("update:selectedKeys", newSelectedKeys); emit("select", newSelectedKeys, { selected: targetSelected, node: targetNode == null ? void 0 : targetNode.treeNodeData, selectedNodes: getNodes(newSelectedKeys), e: event }); } function emitExpandEvent(options) { const { targetKey, targetExpanded, newExpandedKeys, event } = options; const targetNode = targetKey ? key2TreeNode.value.get(targetKey) : void 0; emit("expand", newExpandedKeys, { expanded: targetExpanded, node: targetNode == null ? void 0 : targetNode.treeNodeData, expandedNodes: getNodes(newExpandedKeys), e: event }); emit("update:expandedKeys", newExpandedKeys); } function setCheckedKeys(keys) { const [newCheckedKeys, newIndeterminateKeys] = setCheckedState( keys, [], true ); emitCheckEvent({ newCheckedKeys, newIndeterminateKeys }); } function setSelectedKeys(keys) { let newSelectedKeys = keys; if (!multiple.value && keys.length > 1) { newSelectedKeys = [keys[0]]; } setSelectedState(newSelectedKeys); emitSelectEvent({ newSelectedKeys }); } function setExpandedKeys(keys) { currentExpandKeys.value = []; setExpandState(keys); emitExpandEvent({ newExpandedKeys: keys }); } function checkNodes(keys, checked, targetKey) { if (!keys.length) return; let newCheckedKeys = [...checkedKeys.value]; let newIndeterminateKeys = [...indeterminateKeys.value]; keys.forEach((key) => { const node = key2TreeNode.value.get(key); if (node) { [newCheckedKeys, newIndeterminateKeys] = getCheckedStateByCheck({ node, checked, checkedKeys: [...newCheckedKeys], indeterminateKeys: [...newIndeterminateKeys], checkStrictly: checkStrictly.value }); } }); setCheckedState(newCheckedKeys, newIndeterminateKeys); emitCheckEvent({ targetKey, targetChecked: isUndefined(targetKey) ? void 0 : checked, newCheckedKeys, newIndeterminateKeys }); } function selectNodes(keys, selected, targetKey) { if (!keys.length) return; let newSelectedKeys; if (multiple.value) { const selectedKeysSet = new Set(selectedKeys.value); keys.forEach((key) => { selected ? selectedKeysSet.add(key) : selectedKeysSet.delete(key); }); newSelectedKeys = [...selectedKeysSet]; } else { newSelectedKeys = selected ? [keys[0]] : []; } setSelectedState(newSelectedKeys); emitSelectEvent({ targetKey, targetSelected: isUndefined(targetKey) ? void 0 : selected, newSelectedKeys }); } function expandNodes(keys, expanded, targetKey) { const expandedKeysSet = new Set(expandedKeys.value); keys.forEach((key) => { expanded ? expandedKeysSet.add(key) : expandedKeysSet.delete(key); onExpandEnd(key); }); const newExpandedKeys = [...expandedKeysSet]; setExpandState(newExpandedKeys); emitExpandEvent({ targetKey, targetExpanded: isUndefined(targetKey) ? void 0 : expanded, newExpandedKeys }); } function onCheck(checked, key, e) { const node = key2TreeNode.value.get(key); if (!node) return; const [newCheckedKeys, newIndeterminateKeys] = getCheckedStateByCheck({ node, checked, checkedKeys: checkedKeys.value, indeterminateKeys: indeterminateKeys.value, checkStrictly: checkStrictly.value }); setCheckedState(newCheckedKeys, newIndeterminateKeys); emitCheckEvent({ targetKey: key, targetChecked: checked, newCheckedKeys, newIndeterminateKeys, event: e }); } function onSelect(key, e) { const node = key2TreeNode.value.get(key); if (!node) return; let newSelectedKeys; let selected; if (multiple.value) { const selectedKeysSet = new Set(selectedKeys.value); selected = !selectedKeysSet.has(key); selected ? selectedKeysSet.add(key) : selectedKeysSet.delete(key); newSelectedKeys = [...selectedKeysSet]; } else { selected = true; newSelectedKeys = [key]; } setSelectedState(newSelectedKeys); emitSelectEvent({ targetKey: key, targetSelected: selected, newSelectedKeys, event: e }); } function onExpand(expanded, key, e) { if (currentExpandKeys.value.includes(key)) return; const node = key2TreeNode.value.get(key); if (!node) return; const expandedKeysSet = new Set(expandedKeys.value); expanded ? expandedKeysSet.add(key) : expandedKeysSet.delete(key); const newExpandedKeys = [...expandedKeysSet]; setExpandState(newExpandedKeys); if (animation.value) { currentExpandKeys.value.push(key); } emitExpandEvent({ targetKey: key, targetExpanded: expanded, newExpandedKeys, event: e }); } function onExpandEnd(key) { const index = currentExpandKeys.value.indexOf(key); currentExpandKeys.value.splice(index, 1); } const onLoadMore = computed( () => (loadMore == null ? void 0 : loadMore.value) ? async (key) => { if (!isFunction(loadMore.value)) return; const node = key2TreeNode.value.get(key); if (!node) return; const { treeNodeData } = node; loadingKeys.value = [.../* @__PURE__ */ new Set([...loadingKeys.value, key])]; try { await loadMore.value(treeNodeData); loadingKeys.value = loadingKeys.value.filter((v) => v !== key); onExpand(true, key); if (checkedKeys.value.includes(key)) { onCheck(true, key); } } catch (err) { loadingKeys.value = loadingKeys.value.filter((v) => v !== key); console.error("[tree]load data error: ", err); } } : void 0 ); const treeContext = reactive({ treeProps: props, switcherIcon, loadingIcon, dragIcon, nodeIcon, nodeTitle, nodeExtra, treeData, flattenTreeData, key2TreeNode, checkedKeys, indeterminateKeys, selectedKeys, expandedKeys, loadingKeys, currentExpandKeys, onLoadMore, filterTreeNode, onCheck, onSelect, onExpand, onExpandEnd, allowDrop(key, dropPosition) { const node = key2TreeNode.value.get(key); if (node && isFunction(allowDrop.value)) { return !!allowDrop.value({ dropNode: node.treeNodeData, dropPosition }); } return true; }, onDragStart(key, e) { const node = key2TreeNode.value.get(key); dragNode.value = node; if (node) { emit("dragStart", e, node.treeNodeData); } }, onDragEnd(key, e) { const node = key2TreeNode.value.get(key); dragNode.value = void 0; if (node) { emit("dragEnd", e, node.treeNodeData); } }, onDragOver(key, e) { const node = key2TreeNode.value.get(key); if (node) { emit("dragOver", e, node.treeNodeData); } }, onDragLeave(key, e) { const node = key2TreeNode.value.get(key); if (node) { emit("dragLeave", e, node.treeNodeData); } }, onDrop(key, dropPosition, e) { const node = key2TreeNode.value.get(key); if (dragNode.value && node && !(node.key === dragNode.value.key || node.pathParentKeys.includes(dragNode.value.key || ""))) { emit("drop", { e, dragNode: dragNode.value.treeNodeData, dropNode: node.treeNodeData, dropPosition }); } } }); provide(TreeInjectionKey, treeContext); return { classNames, visibleTreeNodeList, treeContext, virtualListRef: ref(), computedSelectedKeys: selectedKeys, computedExpandedKeys: expandedKeys, computedCheckedKeys: checkedKeys, computedIndeterminateKeys: indeterminateKeys, getPublicCheckedKeys, getNodes, internalCheckNodes: checkNodes, internalSetCheckedKeys: setCheckedKeys, internalSelectNodes: selectNodes, internalSetSelectedKeys: setSelectedKeys, internalExpandNodes: expandNodes, internalSetExpandedKeys: setExpandedKeys }; }, methods: { toggleCheck(key, e) { const { key2TreeNode, onCheck, checkedKeys } = this.treeContext; const checked = !checkedKeys.includes(key); const node = key2TreeNode.get(key); if (node && isNodeCheckable(node)) { onCheck(checked, key, e); } }, scrollIntoView(options) { this.virtualListRef && this.virtualListRef.scrollTo(options); }, getSelectedNodes() { return this.getNodes(this.computedSelectedKeys); }, getCheckedNodes(options = {}) { const { checkedStrategy, includeHalfChecked } = options; const checkedKeys = this.getPublicCheckedKeys( this.computedCheckedKeys, checkedStrategy ); const checkedNodes = this.getNodes(checkedKeys); return [ ...checkedNodes, ...includeHalfChecked ? this.getHalfCheckedNodes() : [] ]; }, getHalfCheckedNodes() { return this.getNodes(this.computedIndeterminateKeys); }, getExpandedNodes() { return this.getNodes(this.computedExpandedKeys); }, checkAll(checked = true) { const { key2TreeNode } = this.treeContext; const newKeys = checked ? [...key2TreeNode.keys()].filter((key) => { const node = key2TreeNode.get(key); return node && isNodeCheckable(node); }) : []; this.internalSetCheckedKeys(newKeys); }, checkNode(key, checked = true, onlyCheckLeaf = false) { const { checkStrictly, treeContext } = this; const { key2TreeNode } = treeContext; const isBatch = isArray(key); const keys = (isBatch ? key : [key]).filter((key2) => { const node = key2TreeNode.get(key2); return node && isNodeCheckable(node) && (checkStrictly || !onlyCheckLeaf || isLeafNode(node)); }); this.internalCheckNodes(keys, checked, isBatch ? void 0 : key); }, selectAll(selected = true) { const { key2TreeNode } = this.treeContext; const newKeys = selected ? [...key2TreeNode.keys()].filter((key) => { const node = key2TreeNode.get(key); return node && isNodeSelectable(node); }) : []; this.internalSetSelectedKeys(newKeys); }, selectNode(key, selected = true) { const { key2TreeNode } = this.treeContext; const isBatch = isArray(key); const keys = (isBatch ? key : [key]).filter((key2) => { const node = key2TreeNode.get(key2); return node && isNodeSelectable(node); }); this.internalSelectNodes(keys, selected, isBatch ? void 0 : key); }, expandAll(expanded = true) { const { key2TreeNode } = this.treeContext; const newKeys = expanded ? [...key2TreeNode.keys()].filter((key) => { const node = key2TreeNode.get(key); return node && isNodeExpandable(node); }) : []; this.internalSetExpandedKeys(newKeys); }, expandNode(key, expanded = true) { const { key2TreeNode } = this.treeContext; const isBatch = isArray(key); const keys = (isBatch ? key : [key]).filter((key2) => { const node = key2TreeNode.get(key2); return node && isNodeExpandable(node); }); this.internalExpandNodes(keys, expanded, isBatch ? void 0 : key); } } }); function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_TreeNode = resolveComponent("TreeNode"); const _component_VirtualList = resolveComponent("VirtualList"); return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.classNames) }, [ _ctx.virtualListProps ? (openBlock(), createBlock(_component_VirtualList, mergeProps({ key: 0, ref: "virtualListRef" }, _ctx.virtualListProps, { data: _ctx.visibleTreeNodeList }), { item: withCtx(({ item: node }) => [ (openBlock(), createBlock(_component_TreeNode, mergeProps({ key: `${_ctx.searchValue}-${node.key}` }, node.treeNodeProps), null, 16)) ]), _: 1 }, 16, ["data"])) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(_ctx.visibleTreeNodeList, (node) => { return openBlock(), createBlock(_component_TreeNode, mergeProps({ key: node.key, ref_for: true }, node.treeNodeProps), null, 16); }), 128)) ], 2); } var _Tree = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]); export { _Tree as default };