UNPKG

element-plus

Version:

A Component Library for Vue3.0

1,402 lines (1,388 loc) 58.3 kB
import { reactive, defineComponent, inject, h, provide, ref, getCurrentInstance, watch, nextTick, resolveComponent, withDirectives, openBlock, createBlock, withModifiers, createVNode, createCommentVNode, withCtx, Fragment, renderList, vShow, onMounted, onBeforeUnmount, onUpdated, computed, toDisplayString } from 'vue'; import ElCollapseTransition from '../el-collapse-transition'; import ElCheckbox from '../el-checkbox'; import mitt from 'mitt'; import { removeClass, addClass, on, off } from '../utils/dom'; import { EVENT_CODE } from '../utils/aria'; import { t } from '../locale'; const NODE_KEY = '$treeNodeId'; const markNodeData = function (node, data) { if (!data || data[NODE_KEY]) return; Object.defineProperty(data, NODE_KEY, { value: node.id, enumerable: false, configurable: false, writable: false, }); }; const getNodeKey = function (key, data) { if (!key) return data[NODE_KEY]; return data[key]; }; const getChildState = (node) => { let all = true; let none = true; let allWithoutDisable = true; for (let i = 0, j = node.length; i < j; i++) { const n = node[i]; if (n.checked !== true || n.indeterminate) { all = false; if (!n.disabled) { allWithoutDisable = false; } } if (n.checked !== false || n.indeterminate) { none = false; } } return { all, none, allWithoutDisable, half: !all && !none }; }; const reInitChecked = function (node) { if (node.childNodes.length === 0) return; const { all, none, half } = getChildState(node.childNodes); if (all) { node.checked = true; node.indeterminate = false; } else if (half) { node.checked = false; node.indeterminate = true; } else if (none) { node.checked = false; node.indeterminate = false; } const parent = node.parent; if (!parent || parent.level === 0) return; if (!node.store.checkStrictly) { reInitChecked(parent); } }; const getPropertyFromData = function (node, prop) { const props = node.store.props; const data = node.data || {}; const config = props[prop]; if (typeof config === 'function') { return config(data, node); } else if (typeof config === 'string') { return data[config]; } else if (typeof config === 'undefined') { const dataProp = data[prop]; return dataProp === undefined ? '' : dataProp; } }; let nodeIdSeed = 0; class Node { constructor(options) { this.id = nodeIdSeed++; this.text = null; this.checked = false; this.indeterminate = false; this.data = null; this.expanded = false; this.parent = null; this.visible = true; this.isCurrent = false; this.canFocus = false; for (const name in options) { if (options.hasOwnProperty(name)) { this[name] = options[name]; } } this.level = 0; this.loaded = false; this.childNodes = []; this.loading = false; if (this.parent) { this.level = this.parent.level + 1; } } initialize() { const store = this.store; if (!store) { throw new Error('[Node]store is required!'); } store.registerNode(this); const props = store.props; if (props && typeof props.isLeaf !== 'undefined') { const isLeaf = getPropertyFromData(this, 'isLeaf'); if (typeof isLeaf === 'boolean') { this.isLeafByUser = isLeaf; } } if (store.lazy !== true && this.data) { this.setData(this.data); if (store.defaultExpandAll) { this.expanded = true; this.canFocus = true; } } else if (this.level > 0 && store.lazy && store.defaultExpandAll) { this.expand(); } if (!Array.isArray(this.data)) { markNodeData(this, this.data); } if (!this.data) return; const defaultExpandedKeys = store.defaultExpandedKeys; const key = store.key; if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) { this.expand(null, store.autoExpandParent); } if (key && store.currentNodeKey !== undefined && this.key === store.currentNodeKey) { store.currentNode = this; store.currentNode.isCurrent = true; } if (store.lazy) { store._initDefaultCheckedNode(this); } this.updateLeafState(); if (this.parent && (this.level === 1 || this.parent.expanded === true)) this.canFocus = true; } setData(data) { if (!Array.isArray(data)) { markNodeData(this, data); } this.data = data; this.childNodes = []; let children; if (this.level === 0 && this.data instanceof Array) { children = this.data; } else { children = getPropertyFromData(this, 'children') || []; } for (let i = 0, j = children.length; i < j; i++) { this.insertChild({ data: children[i] }); } } get label() { return getPropertyFromData(this, 'label'); } get key() { const nodeKey = this.store.key; if (this.data) return this.data[nodeKey]; return null; } get disabled() { return getPropertyFromData(this, 'disabled'); } get nextSibling() { const parent = this.parent; if (parent) { const index = parent.childNodes.indexOf(this); if (index > -1) { return parent.childNodes[index + 1]; } } return null; } get previousSibling() { const parent = this.parent; if (parent) { const index = parent.childNodes.indexOf(this); if (index > -1) { return index > 0 ? parent.childNodes[index - 1] : null; } } return null; } contains(target, deep = true) { return (this.childNodes || []).some(child => child === target || (deep && child.contains(target))); } remove() { const parent = this.parent; if (parent) { parent.removeChild(this); } } insertChild(child, index, batch) { if (!child) throw new Error('insertChild error: child is required.'); if (!(child instanceof Node)) { if (!batch) { const children = this.getChildren(true); if (children.indexOf(child.data) === -1) { if (typeof index === 'undefined' || index < 0) { children.push(child.data); } else { children.splice(index, 0, child.data); } } } Object.assign(child, { parent: this, store: this.store, }); child = reactive(new Node(child)); if (child instanceof Node) { child.initialize(); } } child.level = this.level + 1; if (typeof index === 'undefined' || index < 0) { this.childNodes.push(child); } else { this.childNodes.splice(index, 0, child); } this.updateLeafState(); } insertBefore(child, ref) { let index; if (ref) { index = this.childNodes.indexOf(ref); } this.insertChild(child, index); } insertAfter(child, ref) { let index; if (ref) { index = this.childNodes.indexOf(ref); if (index !== -1) index += 1; } this.insertChild(child, index); } removeChild(child) { const children = this.getChildren() || []; const dataIndex = children.indexOf(child.data); if (dataIndex > -1) { children.splice(dataIndex, 1); } const index = this.childNodes.indexOf(child); if (index > -1) { this.store && this.store.deregisterNode(child); child.parent = null; this.childNodes.splice(index, 1); } this.updateLeafState(); } removeChildByData(data) { let targetNode = null; for (let i = 0; i < this.childNodes.length; i++) { if (this.childNodes[i].data === data) { targetNode = this.childNodes[i]; break; } } if (targetNode) { this.removeChild(targetNode); } } expand(callback, expandParent) { const done = () => { if (expandParent) { let parent = this.parent; while (parent.level > 0) { parent.expanded = true; parent = parent.parent; } } this.expanded = true; if (callback) callback(); this.childNodes.forEach(item => { item.canFocus = true; }); }; if (this.shouldLoadData()) { this.loadData(data => { if (Array.isArray(data)) { if (this.checked) { this.setChecked(true, true); } else if (!this.store.checkStrictly) { reInitChecked(this); } done(); } }); } else { done(); } } doCreateChildren(array, defaultProps = {}) { array.forEach(item => { this.insertChild(Object.assign({ data: item }, defaultProps), undefined, true); }); } collapse() { this.expanded = false; this.childNodes.forEach(item => { item.canFocus = false; }); } shouldLoadData() { return this.store.lazy === true && this.store.load && !this.loaded; } updateLeafState() { if (this.store.lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') { this.isLeaf = this.isLeafByUser; return; } const childNodes = this.childNodes; if (!this.store.lazy || (this.store.lazy === true && this.loaded === true)) { this.isLeaf = !childNodes || childNodes.length === 0; return; } this.isLeaf = false; } setChecked(value, deep, recursion, passValue) { this.indeterminate = value === 'half'; this.checked = value === true; if (this.store.checkStrictly) return; if (!(this.shouldLoadData() && !this.store.checkDescendants)) { const { all, allWithoutDisable } = getChildState(this.childNodes); if (!this.isLeaf && (!all && allWithoutDisable)) { this.checked = false; value = false; } const handleDescendants = () => { if (deep) { const childNodes = this.childNodes; for (let i = 0, j = childNodes.length; i < j; i++) { const child = childNodes[i]; passValue = passValue || value !== false; const isCheck = child.disabled ? child.checked : passValue; child.setChecked(isCheck, deep, true, passValue); } const { half, all } = getChildState(childNodes); if (!all) { this.checked = all; this.indeterminate = half; } } }; if (this.shouldLoadData()) { this.loadData(() => { handleDescendants(); reInitChecked(this); }, { checked: value !== false, }); return; } else { handleDescendants(); } } const parent = this.parent; if (!parent || parent.level === 0) return; if (!recursion) { reInitChecked(parent); } } getChildren(forceInit = false) { if (this.level === 0) return this.data; const data = this.data; if (!data) return null; const props = this.store.props; let children = 'children'; if (props) { children = props.children || 'children'; } if (data[children] === undefined) { data[children] = null; } if (forceInit && !data[children]) { data[children] = []; } return data[children]; } updateChildren() { const newData = (this.getChildren() || []); const oldData = this.childNodes.map(node => node.data); const newDataMap = {}; const newNodes = []; newData.forEach((item, index) => { const key = item[NODE_KEY]; const isNodeExists = !!key && oldData.findIndex(data => data[NODE_KEY] === key) >= 0; if (isNodeExists) { newDataMap[key] = { index, data: item }; } else { newNodes.push({ index, data: item }); } }); if (!this.store.lazy) { oldData.forEach(item => { if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item); }); } newNodes.forEach(({ index, data }) => { this.insertChild({ data }, index); }); this.updateLeafState(); } loadData(callback, defaultProps = {}) { if (this.store.lazy === true && this.store.load && !this.loaded && (!this.loading || Object.keys(defaultProps).length)) { this.loading = true; const resolve = children => { this.loaded = true; this.loading = false; this.childNodes = []; this.doCreateChildren(children, defaultProps); this.updateLeafState(); if (callback) { callback.call(this, children); } }; this.store.load(this, resolve); } else { if (callback) { callback.call(this); } } } } class TreeStore { constructor(options) { this.currentNode = null; this.currentNodeKey = null; for (const option in options) { if (options.hasOwnProperty(option)) { this[option] = options[option]; } } this.nodesMap = {}; } initialize() { this.root = new Node({ data: this.data, store: this, }); this.root.initialize(); if (this.lazy && this.load) { const loadFn = this.load; loadFn(this.root, data => { this.root.doCreateChildren(data); this._initDefaultCheckedNodes(); }); } else { this._initDefaultCheckedNodes(); } } filter(value) { const filterNodeMethod = this.filterNodeMethod; const lazy = this.lazy; const traverse = function (node) { const childNodes = node.root ? node.root.childNodes : node.childNodes; childNodes.forEach(child => { child.visible = filterNodeMethod.call(child, value, child.data, child); traverse(child); }); if (!node.visible && childNodes.length) { let allHidden = true; allHidden = !childNodes.some(child => child.visible); if (node.root) { node.root.visible = allHidden === false; } else { node.visible = allHidden === false; } } if (!value) return; if (node.visible && !node.isLeaf && !lazy) node.expand(); }; traverse(this); } setData(newVal) { const instanceChanged = newVal !== this.root.data; if (instanceChanged) { this.root.setData(newVal); this._initDefaultCheckedNodes(); } else { this.root.updateChildren(); } } getNode(data) { if (data instanceof Node) return data; const key = typeof data !== 'object' ? data : getNodeKey(this.key, data); return this.nodesMap[key] || null; } insertBefore(data, refData) { const refNode = this.getNode(refData); refNode.parent.insertBefore({ data }, refNode); } insertAfter(data, refData) { const refNode = this.getNode(refData); refNode.parent.insertAfter({ data }, refNode); } remove(data) { const node = this.getNode(data); if (node && node.parent) { if (node === this.currentNode) { this.currentNode = null; } node.parent.removeChild(node); } } append(data, parentData) { const parentNode = parentData ? this.getNode(parentData) : this.root; if (parentNode) { parentNode.insertChild({ data }); } } _initDefaultCheckedNodes() { const defaultCheckedKeys = this.defaultCheckedKeys || []; const nodesMap = this.nodesMap; defaultCheckedKeys.forEach(checkedKey => { const node = nodesMap[checkedKey]; if (node) { node.setChecked(true, !this.checkStrictly); } }); } _initDefaultCheckedNode(node) { const defaultCheckedKeys = this.defaultCheckedKeys || []; if (defaultCheckedKeys.indexOf(node.key) !== -1) { node.setChecked(true, !this.checkStrictly); } } setDefaultCheckedKey(newVal) { if (newVal !== this.defaultCheckedKeys) { this.defaultCheckedKeys = newVal; this._initDefaultCheckedNodes(); } } registerNode(node) { const key = this.key; if (!node || !node.data) return; if (!key) { this.nodesMap[node.id] = node; } else { const nodeKey = node.key; if (nodeKey !== undefined) this.nodesMap[node.key] = node; } } deregisterNode(node) { const key = this.key; if (!key || !node || !node.data) return; node.childNodes.forEach(child => { this.deregisterNode(child); }); delete this.nodesMap[node.key]; } getCheckedNodes(leafOnly = false, includeHalfChecked = false) { const checkedNodes = []; const traverse = function (node) { const childNodes = node.root ? node.root.childNodes : node.childNodes; childNodes.forEach(child => { if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (leafOnly && child.isLeaf))) { checkedNodes.push(child.data); } traverse(child); }); }; traverse(this); return checkedNodes; } getCheckedKeys(leafOnly = false) { return this.getCheckedNodes(leafOnly).map(data => (data || {})[this.key]); } getHalfCheckedNodes() { const nodes = []; const traverse = function (node) { const childNodes = node.root ? node.root.childNodes : node.childNodes; childNodes.forEach(child => { if (child.indeterminate) { nodes.push(child.data); } traverse(child); }); }; traverse(this); return nodes; } getHalfCheckedKeys() { return this.getHalfCheckedNodes().map(data => (data || {})[this.key]); } _getAllNodes() { const allNodes = []; const nodesMap = this.nodesMap; for (const nodeKey in nodesMap) { if (nodesMap.hasOwnProperty(nodeKey)) { allNodes.push(nodesMap[nodeKey]); } } return allNodes; } updateChildren(key, data) { const node = this.nodesMap[key]; if (!node) return; const childNodes = node.childNodes; for (let i = childNodes.length - 1; i >= 0; i--) { const child = childNodes[i]; this.remove(child.data); } for (let i = 0, j = data.length; i < j; i++) { const child = data[i]; this.append(child, node.data); } } _setCheckedKeys(key, leafOnly = false, checkedKeys) { const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level); const cache = Object.create(null); const keys = Object.keys(checkedKeys); allNodes.forEach(node => node.setChecked(false, false)); for (let i = 0, j = allNodes.length; i < j; i++) { const node = allNodes[i]; const nodeKey = node.data[key].toString(); const checked = keys.indexOf(nodeKey) > -1; if (!checked) { if (node.checked && !cache[nodeKey]) { node.setChecked(false, false); } continue; } let parent = node.parent; while (parent && parent.level > 0) { cache[parent.data[key]] = true; parent = parent.parent; } if (node.isLeaf || this.checkStrictly) { node.setChecked(true, false); continue; } node.setChecked(true, true); if (leafOnly) { node.setChecked(false, false); const traverse = function (node) { const childNodes = node.childNodes; childNodes.forEach(child => { if (!child.isLeaf) { child.setChecked(false, false); } traverse(child); }); }; traverse(node); } } } setCheckedNodes(array, leafOnly = false) { const key = this.key; const checkedKeys = {}; array.forEach(item => { checkedKeys[(item || {})[key]] = true; }); this._setCheckedKeys(key, leafOnly, checkedKeys); } setCheckedKeys(keys, leafOnly = false) { this.defaultCheckedKeys = keys; const key = this.key; const checkedKeys = {}; keys.forEach(key => { checkedKeys[key] = true; }); this._setCheckedKeys(key, leafOnly, checkedKeys); } setDefaultExpandedKeys(keys) { keys = keys || []; this.defaultExpandedKeys = keys; keys.forEach(key => { const node = this.getNode(key); if (node) node.expand(null, this.autoExpandParent); }); } setChecked(data, checked, deep) { const node = this.getNode(data); if (node) { node.setChecked(!!checked, deep); } } getCurrentNode() { return this.currentNode; } setCurrentNode(currentNode) { const prevCurrentNode = this.currentNode; if (prevCurrentNode) { prevCurrentNode.isCurrent = false; } this.currentNode = currentNode; this.currentNode.isCurrent = true; } setUserCurrentNode(node) { const key = node[this.key]; const currNode = this.nodesMap[key]; this.setCurrentNode(currNode); } setCurrentNodeKey(key) { if (key === null || key === undefined) { this.currentNode && (this.currentNode.isCurrent = false); this.currentNode = null; return; } const node = this.getNode(key); if (node) { this.setCurrentNode(node); } } } var script = defineComponent({ name: 'ElTreeNodeContent', props: { node: { type: Object, required: true, }, renderContent: Function, }, setup(props) { const nodeInstance = inject('NodeInstance'); const tree = inject('RootTree'); return () => { const node = props.node; const { data, store } = node; return (props.renderContent ? props.renderContent(h, { _self: nodeInstance, node, data, store }) : tree.ctx.slots.default ? tree.ctx.slots.default({ node, data }) : h('span', { class: 'el-tree-node__label' }, [node.label])); }; }, }); script.__file = "packages/tree/src/tree-node-content.vue"; function useNodeExpandEventBroadcast(props) { const parentNodeMap = inject('TreeNodeMap', null); const currentNodeMap = { treeNodeExpand: node => { if (props.node !== node) { props.node.collapse(); } }, children: [], }; if (parentNodeMap) { parentNodeMap.children.push(currentNodeMap); } provide('TreeNodeMap', currentNodeMap); return { broadcastExpanded: (node) => { if (!props.accordion) return; for (const childNode of currentNodeMap.children) { childNode.treeNodeExpand(node); } }, }; } function useDragNodeHandler({ props, ctx, el$, dropIndicator$, store }) { const emitter = mitt(); provide('DragNodeEmitter', emitter); const dragState = ref({ showDropIndicator: false, draggingNode: null, dropNode: null, allowDrop: true, dropType: null, }); emitter.on('tree-node-drag-start', ({ event, treeNode }) => { console.log(event, treeNode); if (typeof props.allowDrag === 'function' && !props.allowDrag(treeNode.node)) { event.preventDefault(); return false; } event.dataTransfer.effectAllowed = 'move'; try { event.dataTransfer.setData('text/plain', ''); } catch (e) { } dragState.value.draggingNode = treeNode; ctx.emit('node-drag-start', treeNode.node, event); }); emitter.on('tree-node-drag-over', ({ event, treeNode }) => { const dropNode = treeNode; const oldDropNode = dragState.value.dropNode; if (oldDropNode && oldDropNode !== dropNode) { removeClass(oldDropNode.$el, 'is-drop-inner'); } const draggingNode = dragState.value.draggingNode; if (!draggingNode || !dropNode) return; let dropPrev = true; let dropInner = true; let dropNext = true; let userAllowDropInner = true; if (typeof props.allowDrop === 'function') { dropPrev = props.allowDrop(draggingNode.node, dropNode.node, 'prev'); userAllowDropInner = dropInner = props.allowDrop(draggingNode.node, dropNode.node, 'inner'); dropNext = props.allowDrop(draggingNode.node, dropNode.node, 'next'); } event.dataTransfer.dropEffect = dropInner ? 'move' : 'none'; if ((dropPrev || dropInner || dropNext) && oldDropNode !== dropNode) { if (oldDropNode) { ctx.emit('node-drag-leave', draggingNode.node, oldDropNode.node, event); } ctx.emit('node-drag-enter', draggingNode.node, dropNode.node, event); } if (dropPrev || dropInner || dropNext) { dragState.value.dropNode = dropNode; } if (dropNode.node.nextSibling === draggingNode.node) { dropNext = false; } if (dropNode.node.previousSibling === draggingNode.node) { dropPrev = false; } if (dropNode.node.contains(draggingNode.node, false)) { dropInner = false; } if (draggingNode.node === dropNode.node || draggingNode.node.contains(dropNode.node)) { dropPrev = false; dropInner = false; dropNext = false; } const targetPosition = dropNode.$el.getBoundingClientRect(); const treePosition = el$.value.getBoundingClientRect(); let dropType; const prevPercent = dropPrev ? (dropInner ? 0.25 : (dropNext ? 0.45 : 1)) : -1; const nextPercent = dropNext ? (dropInner ? 0.75 : (dropPrev ? 0.55 : 0)) : 1; let indicatorTop = -9999; const distance = event.clientY - targetPosition.top; if (distance < targetPosition.height * prevPercent) { dropType = 'before'; } else if (distance > targetPosition.height * nextPercent) { dropType = 'after'; } else if (dropInner) { dropType = 'inner'; } else { dropType = 'none'; } const iconPosition = dropNode.$el.querySelector('.el-tree-node__expand-icon').getBoundingClientRect(); const dropIndicator = dropIndicator$.value; if (dropType === 'before') { indicatorTop = iconPosition.top - treePosition.top; } else if (dropType === 'after') { indicatorTop = iconPosition.bottom - treePosition.top; } dropIndicator.style.top = indicatorTop + 'px'; dropIndicator.style.left = (iconPosition.right - treePosition.left) + 'px'; if (dropType === 'inner') { addClass(dropNode.$el, 'is-drop-inner'); } else { removeClass(dropNode.$el, 'is-drop-inner'); } dragState.value.showDropIndicator = dropType === 'before' || dropType === 'after'; dragState.value.allowDrop = dragState.value.showDropIndicator || userAllowDropInner; dragState.value.dropType = dropType; ctx.emit('node-drag-over', draggingNode.node, dropNode.node, event); }); emitter.on('tree-node-drag-end', (event) => { const { draggingNode, dropType, dropNode } = dragState.value; event.preventDefault(); event.dataTransfer.dropEffect = 'move'; if (draggingNode && dropNode) { const draggingNodeCopy = { data: draggingNode.node.data }; if (dropType !== 'none') { draggingNode.node.remove(); } if (dropType === 'before') { dropNode.node.parent.insertBefore(draggingNodeCopy, dropNode.node); } else if (dropType === 'after') { dropNode.node.parent.insertAfter(draggingNodeCopy, dropNode.node); } else if (dropType === 'inner') { dropNode.node.insertChild(draggingNodeCopy); } if (dropType !== 'none') { store.value.registerNode(draggingNodeCopy); } removeClass(dropNode.$el, 'is-drop-inner'); ctx.emit('node-drag-end', draggingNode.node, dropNode.node, dropType, event); if (dropType !== 'none') { ctx.emit('node-drop', draggingNode.node, dropNode.node, dropType, event); } } if (draggingNode && !dropNode) { ctx.emit('node-drag-end', draggingNode.node, null, dropType, event); } dragState.value.showDropIndicator = false; dragState.value.draggingNode = null; dragState.value.dropNode = null; dragState.value.allowDrop = true; }); return { dragState, }; } function useDragNodeEmitter() { const emitter = inject('DragNodeEmitter'); return { emitter, }; } var script$1 = defineComponent({ name: 'ElTreeNode', components: { ElCollapseTransition, ElCheckbox, NodeContent: script, }, props: { node: { type: Node, default: () => ({}), }, props: { type: Object, default: () => ({}), }, renderContent: Function, renderAfterExpand: Boolean, showCheckbox: { type: Boolean, default: false, }, }, emits: ['node-expand'], setup(props, ctx) { const { broadcastExpanded } = useNodeExpandEventBroadcast(props); const tree = inject('RootTree'); const expanded = ref(false); const childNodeRendered = ref(false); const oldChecked = ref(null); const oldIndeterminate = ref(null); const node$ = ref(null); const { emitter } = useDragNodeEmitter(); const instance = getCurrentInstance(); provide('NodeInstance', instance); if (!tree) { console.warn('Can not find node\'s tree.'); } if (props.node.expanded) { expanded.value = true; childNodeRendered.value = true; } const childrenKey = tree.props['children'] || 'children'; watch(() => { const children = props.node.data[childrenKey]; return children && [...children]; }, () => { props.node.updateChildren(); }); watch(() => props.node.indeterminate, val => { handleSelectChange(props.node.checked, val); }); watch(() => props.node.checked, val => { handleSelectChange(val, props.node.indeterminate); }); watch(() => props.node.expanded, val => { nextTick(() => expanded.value = val); if (val) { childNodeRendered.value = true; } }); const getNodeKey$1 = (node) => { return getNodeKey(tree.props.nodeKey, node.data); }; const handleSelectChange = (checked, indeterminate) => { if (oldChecked.value !== checked || oldIndeterminate.value !== indeterminate) { tree.ctx.emit('check-change', props.node.data, checked, indeterminate); } oldChecked.value = checked; oldIndeterminate.value = indeterminate; }; const handleClick = () => { const store = tree.store.value; store.setCurrentNode(props.node); tree.ctx.emit('current-change', store.currentNode ? store.currentNode.data : null, store.currentNode); tree.currentNode.value = props.node; if (tree.props.expandOnClickNode) { handleExpandIconClick(); } if (tree.props.checkOnClickNode && !props.node.disabled) { handleCheckChange(null, { target: { checked: !props.node.checked }, }); } tree.ctx.emit('node-click', props.node.data, props.node, instance); }; const handleContextMenu = (event) => { if (tree.instance.vnode.props['onNodeContextmenu']) { event.stopPropagation(); event.preventDefault(); } tree.ctx.emit('node-contextmenu', event, props.node.data, props.node, instance); }; const handleExpandIconClick = () => { if (props.node.isLeaf) return; if (expanded.value) { tree.ctx.emit('node-collapse', props.node.data, props.node, instance); props.node.collapse(); } else { props.node.expand(); ctx.emit('node-expand', props.node.data, props.node, instance); } }; const handleCheckChange = (value, ev) => { props.node.setChecked(ev.target.checked, !tree.props.checkStrictly); nextTick(() => { const store = tree.store.value; tree.ctx.emit('check', props.node.data, { checkedNodes: store.getCheckedNodes(), checkedKeys: store.getCheckedKeys(), halfCheckedNodes: store.getHalfCheckedNodes(), halfCheckedKeys: store.getHalfCheckedKeys(), }); }); }; const handleChildNodeExpand = (nodeData, node, instance) => { broadcastExpanded(node); tree.ctx.emit('node-expand', nodeData, node, instance); }; const handleDragStart = (event) => { if (!tree.props.draggable) return; emitter.emit('tree-node-drag-start', { event, treeNode: props }); }; const handleDragOver = (event) => { if (!tree.props.draggable) return; emitter.emit('tree-node-drag-over', { event, treeNode: { $el: node$.value, node: props.node } }); event.preventDefault(); }; const handleDrop = (event) => { event.preventDefault(); }; const handleDragEnd = (event) => { if (!tree.props.draggable) return; emitter.emit('tree-node-drag-end', event); }; return { node$, tree, expanded, childNodeRendered, oldChecked, oldIndeterminate, emitter, parent, getNodeKey: getNodeKey$1, handleSelectChange, handleClick, handleContextMenu, handleExpandIconClick, handleCheckChange, handleChildNodeExpand, handleDragStart, handleDragOver, handleDrop, handleDragEnd, }; }, }); const _hoisted_1 = { key: 1, class: "el-tree-node__loading-icon el-icon-loading" }; function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_el_checkbox = resolveComponent("el-checkbox"); const _component_node_content = resolveComponent("node-content"); const _component_el_tree_node = resolveComponent("el-tree-node"); const _component_el_collapse_transition = resolveComponent("el-collapse-transition"); return withDirectives((openBlock(), createBlock("div", { ref: "node$", class: ["el-tree-node", { 'is-expanded': _ctx.expanded, 'is-current': _ctx.node.isCurrent, 'is-hidden': !_ctx.node.visible, 'is-focusable': !_ctx.node.disabled, 'is-checked': !_ctx.node.disabled && _ctx.node.checked, }], role: "treeitem", tabindex: "-1", "aria-expanded": _ctx.expanded, "aria-disabled": _ctx.node.disabled, "aria-checked": _ctx.node.checked, draggable: _ctx.tree.props.draggable, "data-key": _ctx.getNodeKey(_ctx.node), onClick: _cache[3] || (_cache[3] = withModifiers((...args) => (_ctx.handleClick && _ctx.handleClick(...args)), ["stop"])), onContextmenu: _cache[4] || (_cache[4] = (...args) => (_ctx.handleContextMenu && _ctx.handleContextMenu(...args))), onDragstart: _cache[5] || (_cache[5] = withModifiers((...args) => (_ctx.handleDragStart && _ctx.handleDragStart(...args)), ["stop"])), onDragover: _cache[6] || (_cache[6] = withModifiers((...args) => (_ctx.handleDragOver && _ctx.handleDragOver(...args)), ["stop"])), onDragend: _cache[7] || (_cache[7] = withModifiers((...args) => (_ctx.handleDragEnd && _ctx.handleDragEnd(...args)), ["stop"])), onDrop: _cache[8] || (_cache[8] = withModifiers((...args) => (_ctx.handleDrop && _ctx.handleDrop(...args)), ["stop"])) }, [ createVNode("div", { class: "el-tree-node__content", style: { 'padding-left': (_ctx.node.level - 1) * _ctx.tree.props.indent + 'px' } }, [ createVNode("span", { class: [ { 'is-leaf': _ctx.node.isLeaf, expanded: !_ctx.node.isLeaf && _ctx.expanded, }, 'el-tree-node__expand-icon', _ctx.tree.props.iconClass ? _ctx.tree.props.iconClass : 'el-icon-caret-right', ], onClick: _cache[1] || (_cache[1] = withModifiers((...args) => (_ctx.handleExpandIconClick && _ctx.handleExpandIconClick(...args)), ["stop"])) }, null, 2 /* CLASS */), (_ctx.showCheckbox) ? (openBlock(), createBlock(_component_el_checkbox, { key: 0, "model-value": _ctx.node.checked, indeterminate: _ctx.node.indeterminate, disabled: !!_ctx.node.disabled, onClick: _cache[2] || (_cache[2] = withModifiers(() => {}, ["stop"])), onChange: _ctx.handleCheckChange }, null, 8 /* PROPS */, ["model-value", "indeterminate", "disabled", "onChange"])) : createCommentVNode("v-if", true), (_ctx.node.loading) ? (openBlock(), createBlock("span", _hoisted_1)) : createCommentVNode("v-if", true), createVNode(_component_node_content, { node: _ctx.node, "render-content": _ctx.renderContent }, null, 8 /* PROPS */, ["node", "render-content"]) ], 4 /* STYLE */), createVNode(_component_el_collapse_transition, null, { default: withCtx(() => [ (!_ctx.renderAfterExpand || _ctx.childNodeRendered) ? withDirectives((openBlock(), createBlock("div", { key: 0, class: "el-tree-node__children", role: "group", "aria-expanded": _ctx.expanded }, [ (openBlock(true), createBlock(Fragment, null, renderList(_ctx.node.childNodes, (child) => { return (openBlock(), createBlock(_component_el_tree_node, { key: _ctx.getNodeKey(child), "render-content": _ctx.renderContent, "render-after-expand": _ctx.renderAfterExpand, "show-checkbox": _ctx.showCheckbox, node: child, onNodeExpand: _ctx.handleChildNodeExpand }, null, 8 /* PROPS */, ["render-content", "render-after-expand", "show-checkbox", "node", "onNodeExpand"])) }), 128 /* KEYED_FRAGMENT */)) ], 8 /* PROPS */, ["aria-expanded"])), [ [vShow, _ctx.expanded] ]) : createCommentVNode("v-if", true) ]), _: 1 /* STABLE */ }) ], 42 /* CLASS, PROPS, HYDRATE_EVENTS */, ["aria-expanded", "aria-disabled", "aria-checked", "draggable", "data-key"])), [ [vShow, _ctx.node.visible] ]) } script$1.render = render; script$1.__file = "packages/tree/src/tree-node.vue"; function useKeydown({ el$ }, store) { const treeItems = ref([]); const checkboxItems = ref([]); onMounted(() => { initTabIndex(); on(el$.value, 'keydown', handleKeydown); }); onBeforeUnmount(() => { off(el$.value, 'keydown', handleKeydown); }); onUpdated(() => { treeItems.value = Array.from(el$.value.querySelectorAll('[role=treeitem]')); checkboxItems.value = Array.from(el$.value.querySelectorAll('input[type=checkbox]')); }); watch(checkboxItems, val => { val.forEach(checkbox => { checkbox.setAttribute('tabindex', '-1'); }); }); const handleKeydown = (ev) => { const currentItem = ev.target; if (currentItem.className.indexOf('el-tree-node') === -1) return; const code = ev.code; treeItems.value = Array.from(el$.value.querySelectorAll('.is-focusable[role=treeitem]')); const currentIndex = treeItems.value.indexOf(currentItem); let nextIndex; if ([EVENT_CODE.up, EVENT_CODE.down].indexOf(code) > -1) { ev.preventDefault(); if (code === EVENT_CODE.up) { nextIndex = currentIndex === -1 ? 0 : currentIndex !== 0 ? currentIndex - 1 : treeItems.value.length - 1; const startIndex = nextIndex; while (true) { if (store.value.getNode(treeItems.value[nextIndex].dataset.key).canFocus) break; nextIndex--; if (nextIndex === startIndex) { nextIndex = -1; break; } if (nextIndex < 0) { nextIndex = treeItems.value.length - 1; } } } else { nextIndex = currentIndex === -1 ? 0 : (currentIndex < treeItems.value.length - 1) ? currentIndex + 1 : 0; const startIndex = nextIndex; while (true) { if (store.value.getNode(treeItems.value[nextIndex].dataset.key).canFocus) break; nextIndex++; if (nextIndex === startIndex) { nextIndex = -1; break; } if (nextIndex >= treeItems.value.length) { nextIndex = 0; } } } nextIndex !== -1 && treeItems.value[nextIndex].focus(); } if ([EVENT_CODE.left, EVENT_CODE.right].indexOf(code) > -1) { ev.preventDefault(); currentItem.click(); } const hasInput = currentItem.querySelector('[type="checkbox"]'); if ([EVENT_CODE.enter, EVENT_CODE.space].indexOf(code) > -1 && hasInput) { ev.preventDefault(); hasInput.click(); } }; const initTabIndex = () => { var _a; treeItems.value = Array.from(el$.value.querySelectorAll('.is-focusable[role=treeitem]')); checkboxItems.value = Array.from(el$.value.querySelectorAll('input[type=checkbox]')); const checkedItem = el$.value.querySelectorAll('.is-checked[role=treeitem]'); if (checkedItem.length) { checkedItem[0].setAttribute('tabindex', '0'); return; } (_a = treeItems.value[0]) === null || _a === void 0 ? void 0 : _a.setAttribute('tabindex', '0'); }; } var script$2 = defineComponent({ name: 'ElTree', components: { ElTreeNode: script$1 }, props: { data: { type: Array, }, emptyText: { type: String, default() { return t('el.tree.emptyText'); }, }, renderAfterExpand: { type: Boolean, default: true, }, nodeKey: String, checkStrictly: Boolean, defaultExpandAll: Boolean, expandOnClickNode: { type: Boolean, default: true, }, checkOnClickNode: Boolean, checkDescendants: { type: Boolean, default: false, }, autoExpandParent: { type: Boolean, default: true, }, defaultCheckedKeys: Array, defaultExpandedKeys: Array, currentNodeKey: [String, Number], renderContent: Function, showCheckbox: { type: Boolean, default: false, }, draggable: { type: Boolean, default: false, }, allowDrag: Function, allowDrop: Function, props: { type: Object, default() { return { children: 'children', label: 'label', disabled: 'disabled', }; }, }, lazy: { type: Boolean, default: false, }, highlightCurrent: Boolean, load: Function, filterNodeMethod: Function, accordion: Boolean, indent: { type: Number, default: 18, }, iconClass: String, }, emits: [ 'check-change', 'current-change', 'node-click', 'node-contextmenu', 'node-collapse', 'node-expand', 'check', 'node-drag-start', 'node-drag-end', 'node-drop', 'node-drag-leave', 'node-drag-enter', 'node-drag-over', ], setup(props, ctx) { const store = ref(new TreeStore({ key: props.nodeKey, data: props.data, lazy: props.lazy, props: props.props, load: props.load, curren