682 lines
21 KiB
Vue
682 lines
21 KiB
Vue
import PropTypes from '../../_util/vue-types';
|
|
import classNames from 'classnames';
|
|
import warning from 'warning';
|
|
import { hasProp, initDefaultProps, getOptionProps, getSlots } from '../../_util/props-util';
|
|
import { cloneElement } from '../../_util/vnode';
|
|
import BaseMixin from '../../_util/BaseMixin';
|
|
import proxyComponent from '../../_util/proxyComponent';
|
|
import {
|
|
convertTreeToEntities,
|
|
convertDataToTree,
|
|
getPosition,
|
|
getDragNodesKeys,
|
|
parseCheckedKeys,
|
|
conductExpandParent,
|
|
calcSelectedKeys,
|
|
calcDropPosition,
|
|
arrAdd,
|
|
arrDel,
|
|
posToArr,
|
|
mapChildren,
|
|
conductCheck,
|
|
warnOnlyTreeNode,
|
|
} from './util';
|
|
|
|
/**
|
|
* Thought we still use `cloneElement` to pass `key`,
|
|
* other props can pass with context for future refactor.
|
|
*/
|
|
|
|
function getWatch(keys = []) {
|
|
const watch = {};
|
|
keys.forEach(k => {
|
|
watch[k] = function() {
|
|
this.needSyncKeys[k] = true;
|
|
};
|
|
});
|
|
return watch;
|
|
}
|
|
|
|
const Tree = {
|
|
name: 'Tree',
|
|
mixins: [BaseMixin],
|
|
props: initDefaultProps(
|
|
{
|
|
prefixCls: PropTypes.string,
|
|
tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
children: PropTypes.any,
|
|
treeData: PropTypes.array, // Generate treeNode by children
|
|
showLine: PropTypes.bool,
|
|
showIcon: PropTypes.bool,
|
|
icon: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
|
focusable: PropTypes.bool,
|
|
selectable: PropTypes.bool,
|
|
disabled: PropTypes.bool,
|
|
multiple: PropTypes.bool,
|
|
checkable: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
|
|
checkStrictly: PropTypes.bool,
|
|
draggable: PropTypes.bool,
|
|
defaultExpandParent: PropTypes.bool,
|
|
autoExpandParent: PropTypes.bool,
|
|
defaultExpandAll: PropTypes.bool,
|
|
defaultExpandedKeys: PropTypes.array,
|
|
expandedKeys: PropTypes.array,
|
|
defaultCheckedKeys: PropTypes.array,
|
|
checkedKeys: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
|
defaultSelectedKeys: PropTypes.array,
|
|
selectedKeys: PropTypes.array,
|
|
// onClick: PropTypes.func,
|
|
// onDoubleClick: PropTypes.func,
|
|
// onExpand: PropTypes.func,
|
|
// onCheck: PropTypes.func,
|
|
// onSelect: PropTypes.func,
|
|
loadData: PropTypes.func,
|
|
loadedKeys: PropTypes.array,
|
|
// onMouseEnter: PropTypes.func,
|
|
// onMouseLeave: PropTypes.func,
|
|
// onRightClick: PropTypes.func,
|
|
// onDragStart: PropTypes.func,
|
|
// onDragEnter: PropTypes.func,
|
|
// onDragOver: PropTypes.func,
|
|
// onDragLeave: PropTypes.func,
|
|
// onDragEnd: PropTypes.func,
|
|
// onDrop: PropTypes.func,
|
|
filterTreeNode: PropTypes.func,
|
|
openTransitionName: PropTypes.string,
|
|
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
switcherIcon: PropTypes.any,
|
|
_propsSymbol: PropTypes.any,
|
|
},
|
|
{
|
|
prefixCls: 'rc-tree',
|
|
showLine: false,
|
|
showIcon: true,
|
|
selectable: true,
|
|
multiple: false,
|
|
checkable: false,
|
|
disabled: false,
|
|
checkStrictly: false,
|
|
draggable: false,
|
|
defaultExpandParent: true,
|
|
autoExpandParent: false,
|
|
defaultExpandAll: false,
|
|
defaultExpandedKeys: [],
|
|
defaultCheckedKeys: [],
|
|
defaultSelectedKeys: [],
|
|
},
|
|
),
|
|
|
|
data() {
|
|
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__');
|
|
warning(this.$props.children, 'please use children prop replace slots.default');
|
|
this.needSyncKeys = {};
|
|
this.domTreeNodes = {};
|
|
const state = {
|
|
_posEntities: new Map(),
|
|
_keyEntities: new Map(),
|
|
_expandedKeys: [],
|
|
_selectedKeys: [],
|
|
_checkedKeys: [],
|
|
_halfCheckedKeys: [],
|
|
_loadedKeys: [],
|
|
_loadingKeys: [],
|
|
_treeNode: [],
|
|
_prevProps: null,
|
|
_dragOverNodeKey: '',
|
|
_dropPosition: null,
|
|
_dragNodesKeys: [],
|
|
};
|
|
return {
|
|
...state,
|
|
...this.getDerivedStateFromProps(getOptionProps(this), state),
|
|
};
|
|
},
|
|
provide() {
|
|
return {
|
|
vcTree: this,
|
|
};
|
|
},
|
|
|
|
watch: {
|
|
...getWatch([
|
|
'treeData',
|
|
'children',
|
|
'expandedKeys',
|
|
'autoExpandParent',
|
|
'selectedKeys',
|
|
'checkedKeys',
|
|
'loadedKeys',
|
|
]),
|
|
__propsSymbol__() {
|
|
this.setState(this.getDerivedStateFromProps(getOptionProps(this), this.$data));
|
|
this.needSyncKeys = {};
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
getDerivedStateFromProps(props, prevState) {
|
|
const { _prevProps } = prevState;
|
|
const newState = {
|
|
_prevProps: { ...props },
|
|
};
|
|
const self = this;
|
|
function needSync(name) {
|
|
return (!_prevProps && name in props) || (_prevProps && self.needSyncKeys[name]);
|
|
}
|
|
|
|
// ================== Tree Node ==================
|
|
let treeNode = null;
|
|
|
|
// Check if `treeData` or `children` changed and save into the state.
|
|
if (needSync('treeData')) {
|
|
treeNode = convertDataToTree(this.$createElement, props.treeData);
|
|
} else if (needSync('children')) {
|
|
treeNode = props.children;
|
|
}
|
|
|
|
// Tree support filter function which will break the tree structure in the vdm.
|
|
// We cache the treeNodes in state so that we can return the treeNode in event trigger.
|
|
if (treeNode) {
|
|
newState._treeNode = treeNode;
|
|
|
|
// Calculate the entities data for quick match
|
|
const entitiesMap = convertTreeToEntities(treeNode);
|
|
newState._keyEntities = entitiesMap.keyEntities;
|
|
}
|
|
|
|
const keyEntities = newState._keyEntities || prevState._keyEntities;
|
|
|
|
// ================ expandedKeys =================
|
|
if (needSync('expandedKeys') || (_prevProps && needSync('autoExpandParent'))) {
|
|
newState._expandedKeys =
|
|
props.autoExpandParent || (!_prevProps && props.defaultExpandParent)
|
|
? conductExpandParent(props.expandedKeys, keyEntities)
|
|
: props.expandedKeys;
|
|
} else if (!_prevProps && props.defaultExpandAll) {
|
|
newState._expandedKeys = [...keyEntities.keys()];
|
|
} else if (!_prevProps && props.defaultExpandedKeys) {
|
|
newState._expandedKeys =
|
|
props.autoExpandParent || props.defaultExpandParent
|
|
? conductExpandParent(props.defaultExpandedKeys, keyEntities)
|
|
: props.defaultExpandedKeys;
|
|
}
|
|
|
|
// ================ selectedKeys =================
|
|
if (props.selectable) {
|
|
if (needSync('selectedKeys')) {
|
|
newState._selectedKeys = calcSelectedKeys(props.selectedKeys, props);
|
|
} else if (!_prevProps && props.defaultSelectedKeys) {
|
|
newState._selectedKeys = calcSelectedKeys(props.defaultSelectedKeys, props);
|
|
}
|
|
}
|
|
|
|
// ================= checkedKeys =================
|
|
if (props.checkable) {
|
|
let checkedKeyEntity;
|
|
|
|
if (needSync('checkedKeys')) {
|
|
checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {};
|
|
} else if (!_prevProps && props.defaultCheckedKeys) {
|
|
checkedKeyEntity = parseCheckedKeys(props.defaultCheckedKeys) || {};
|
|
} else if (treeNode) {
|
|
// If treeNode changed, we also need check it
|
|
checkedKeyEntity = parseCheckedKeys(props.checkedKeys) || {
|
|
checkedKeys: prevState._checkedKeys,
|
|
halfCheckedKeys: prevState._halfCheckedKeys,
|
|
};
|
|
}
|
|
|
|
if (checkedKeyEntity) {
|
|
let { checkedKeys = [], halfCheckedKeys = [] } = checkedKeyEntity;
|
|
|
|
if (!props.checkStrictly) {
|
|
const conductKeys = conductCheck(checkedKeys, true, keyEntities);
|
|
({ checkedKeys, halfCheckedKeys } = conductKeys);
|
|
}
|
|
|
|
newState._checkedKeys = checkedKeys;
|
|
newState._halfCheckedKeys = halfCheckedKeys;
|
|
}
|
|
}
|
|
// ================= loadedKeys ==================
|
|
if (needSync('loadedKeys')) {
|
|
newState._loadedKeys = props.loadedKeys;
|
|
}
|
|
|
|
return newState;
|
|
},
|
|
onNodeDragStart(event, node) {
|
|
const { _expandedKeys } = this.$data;
|
|
const { eventKey } = node;
|
|
const children = getSlots(node).default;
|
|
this.dragNode = node;
|
|
|
|
this.setState({
|
|
_dragNodesKeys: getDragNodesKeys(
|
|
typeof children === 'function' ? children() : children,
|
|
node,
|
|
),
|
|
_expandedKeys: arrDel(_expandedKeys, eventKey),
|
|
});
|
|
this.__emit('dragstart', { event, node });
|
|
},
|
|
|
|
/**
|
|
* [Legacy] Select handler is less small than node,
|
|
* so that this will trigger when drag enter node or select handler.
|
|
* This is a little tricky if customize css without padding.
|
|
* Better for use mouse move event to refresh drag state.
|
|
* But let's just keep it to avoid event trigger logic change.
|
|
*/
|
|
onNodeDragEnter(event, node) {
|
|
const { _expandedKeys: expandedKeys } = this.$data;
|
|
const { pos, eventKey } = node;
|
|
|
|
if (!this.dragNode || !node.$refs.selectHandle) return;
|
|
|
|
const dropPosition = calcDropPosition(event, node);
|
|
|
|
// Skip if drag node is self
|
|
if (this.dragNode.eventKey === eventKey && dropPosition === 0) {
|
|
this.setState({
|
|
_dragOverNodeKey: '',
|
|
_dropPosition: null,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Ref: https://github.com/react-component/tree/issues/132
|
|
// Add timeout to let onDragLevel fire before onDragEnter,
|
|
// so that we can clean drag props for onDragLeave node.
|
|
// Macro task for this:
|
|
// https://html.spec.whatwg.org/multipage/webappapis.html#clean-up-after-running-script
|
|
setTimeout(() => {
|
|
// Update drag over node
|
|
this.setState({
|
|
_dragOverNodeKey: eventKey,
|
|
_dropPosition: dropPosition,
|
|
});
|
|
|
|
// Side effect for delay drag
|
|
if (!this.delayedDragEnterLogic) {
|
|
this.delayedDragEnterLogic = {};
|
|
}
|
|
Object.keys(this.delayedDragEnterLogic).forEach(key => {
|
|
clearTimeout(this.delayedDragEnterLogic[key]);
|
|
});
|
|
this.delayedDragEnterLogic[pos] = setTimeout(() => {
|
|
const newExpandedKeys = arrAdd(expandedKeys, eventKey);
|
|
if (!hasProp(this, 'expandedKeys')) {
|
|
this.setState({
|
|
_expandedKeys: newExpandedKeys,
|
|
});
|
|
}
|
|
this.__emit('dragenter', { event, node, expandedKeys: newExpandedKeys });
|
|
}, 400);
|
|
}, 0);
|
|
},
|
|
onNodeDragOver(event, node) {
|
|
const { eventKey } = node;
|
|
const { _dragOverNodeKey, _dropPosition } = this.$data;
|
|
// Update drag position
|
|
if (this.dragNode && eventKey === _dragOverNodeKey && node.$refs.selectHandle) {
|
|
const dropPosition = calcDropPosition(event, node);
|
|
|
|
if (dropPosition === _dropPosition) return;
|
|
|
|
this.setState({
|
|
_dropPosition: dropPosition,
|
|
});
|
|
}
|
|
this.__emit('dragover', { event, node });
|
|
},
|
|
onNodeDragLeave(event, node) {
|
|
this.setState({
|
|
_dragOverNodeKey: '',
|
|
});
|
|
this.__emit('dragleave', { event, node });
|
|
},
|
|
onNodeDragEnd(event, node) {
|
|
this.setState({
|
|
_dragOverNodeKey: '',
|
|
});
|
|
this.__emit('dragend', { event, node });
|
|
this.dragNode = null;
|
|
},
|
|
onNodeDrop(event, node) {
|
|
const { _dragNodesKeys = [], _dropPosition } = this.$data;
|
|
|
|
const { eventKey, pos } = node;
|
|
|
|
this.setState({
|
|
_dragOverNodeKey: '',
|
|
});
|
|
|
|
if (_dragNodesKeys.indexOf(eventKey) !== -1) {
|
|
warning(false, "Can not drop to dragNode(include it's children node)");
|
|
return;
|
|
}
|
|
|
|
const posArr = posToArr(pos);
|
|
|
|
const dropResult = {
|
|
event,
|
|
node,
|
|
dragNode: this.dragNode,
|
|
dragNodesKeys: _dragNodesKeys.slice(),
|
|
dropPosition: _dropPosition + Number(posArr[posArr.length - 1]),
|
|
dropToGap: false,
|
|
};
|
|
|
|
if (_dropPosition !== 0) {
|
|
dropResult.dropToGap = true;
|
|
}
|
|
this.__emit('drop', dropResult);
|
|
this.dragNode = null;
|
|
},
|
|
|
|
onNodeClick(e, treeNode) {
|
|
this.__emit('click', e, treeNode);
|
|
},
|
|
|
|
onNodeDoubleClick(e, treeNode) {
|
|
this.__emit('dblclick', e, treeNode);
|
|
},
|
|
|
|
onNodeSelect(e, treeNode) {
|
|
let { _selectedKeys: selectedKeys } = this.$data;
|
|
const { _keyEntities: keyEntities } = this.$data;
|
|
const { multiple } = this.$props;
|
|
const { selected, eventKey } = getOptionProps(treeNode);
|
|
const targetSelected = !selected;
|
|
// Update selected keys
|
|
if (!targetSelected) {
|
|
selectedKeys = arrDel(selectedKeys, eventKey);
|
|
} else if (!multiple) {
|
|
selectedKeys = [eventKey];
|
|
} else {
|
|
selectedKeys = arrAdd(selectedKeys, eventKey);
|
|
}
|
|
|
|
// [Legacy] Not found related usage in doc or upper libs
|
|
const selectedNodes = selectedKeys
|
|
.map(key => {
|
|
const entity = keyEntities.get(key);
|
|
if (!entity) return null;
|
|
|
|
return entity.node;
|
|
})
|
|
.filter(node => node);
|
|
|
|
this.setUncontrolledState({ _selectedKeys: selectedKeys });
|
|
|
|
const eventObj = {
|
|
event: 'select',
|
|
selected: targetSelected,
|
|
node: treeNode,
|
|
selectedNodes,
|
|
nativeEvent: e.nativeEvent,
|
|
};
|
|
this.__emit('update:selectedKeys', selectedKeys);
|
|
this.__emit('select', selectedKeys, eventObj);
|
|
},
|
|
onNodeCheck(e, treeNode, checked) {
|
|
const {
|
|
_keyEntities: keyEntities,
|
|
_checkedKeys: oriCheckedKeys,
|
|
_halfCheckedKeys: oriHalfCheckedKeys,
|
|
} = this.$data;
|
|
const { checkStrictly } = this.$props;
|
|
const { eventKey } = getOptionProps(treeNode);
|
|
|
|
// Prepare trigger arguments
|
|
let checkedObj;
|
|
const eventObj = {
|
|
event: 'check',
|
|
node: treeNode,
|
|
checked,
|
|
nativeEvent: e,
|
|
};
|
|
|
|
if (checkStrictly) {
|
|
const checkedKeys = checked
|
|
? arrAdd(oriCheckedKeys, eventKey)
|
|
: arrDel(oriCheckedKeys, eventKey);
|
|
const halfCheckedKeys = arrDel(oriHalfCheckedKeys, eventKey);
|
|
checkedObj = { checked: checkedKeys, halfChecked: halfCheckedKeys };
|
|
|
|
eventObj.checkedNodes = checkedKeys
|
|
.map(key => keyEntities.get(key))
|
|
.filter(entity => entity)
|
|
.map(entity => entity.node);
|
|
|
|
this.setUncontrolledState({ _checkedKeys: checkedKeys });
|
|
} else {
|
|
const { checkedKeys, halfCheckedKeys } = conductCheck([eventKey], checked, keyEntities, {
|
|
checkedKeys: oriCheckedKeys,
|
|
halfCheckedKeys: oriHalfCheckedKeys,
|
|
});
|
|
|
|
checkedObj = checkedKeys;
|
|
|
|
// [Legacy] This is used for `rc-tree-select`
|
|
eventObj.checkedNodes = [];
|
|
eventObj.checkedNodesPositions = [];
|
|
eventObj.halfCheckedKeys = halfCheckedKeys;
|
|
|
|
checkedKeys.forEach(key => {
|
|
const entity = keyEntities.get(key);
|
|
if (!entity) return;
|
|
|
|
const { node, pos } = entity;
|
|
|
|
eventObj.checkedNodes.push(node);
|
|
eventObj.checkedNodesPositions.push({ node, pos });
|
|
});
|
|
|
|
this.setUncontrolledState({
|
|
_checkedKeys: checkedKeys,
|
|
_halfCheckedKeys: halfCheckedKeys,
|
|
});
|
|
}
|
|
this.__emit('check', checkedObj, eventObj);
|
|
},
|
|
onNodeLoad(treeNode) {
|
|
return new Promise(resolve => {
|
|
// We need to get the latest state of loading/loaded keys
|
|
this.setState(({ _loadedKeys: loadedKeys = [], _loadingKeys: loadingKeys = [] }) => {
|
|
const { loadData } = this.$props;
|
|
const { eventKey } = getOptionProps(treeNode);
|
|
|
|
if (
|
|
!loadData ||
|
|
loadedKeys.indexOf(eventKey) !== -1 ||
|
|
loadingKeys.indexOf(eventKey) !== -1
|
|
) {
|
|
return {};
|
|
}
|
|
|
|
// Process load data
|
|
const promise = loadData(treeNode);
|
|
promise.then(() => {
|
|
const { _loadedKeys: currentLoadedKeys, _loadingKeys: currentLoadingKeys } = this.$data;
|
|
const newLoadedKeys = arrAdd(currentLoadedKeys, eventKey);
|
|
const newLoadingKeys = arrDel(currentLoadingKeys, eventKey);
|
|
|
|
// onLoad should trigger before internal setState to avoid `loadData` trigger twice.
|
|
// https://github.com/ant-design/ant-design/issues/12464
|
|
this.__emit('load', newLoadedKeys, {
|
|
event: 'load',
|
|
node: treeNode,
|
|
});
|
|
this.setUncontrolledState({
|
|
_loadedKeys: newLoadedKeys,
|
|
});
|
|
this.setState({
|
|
_loadingKeys: newLoadingKeys,
|
|
});
|
|
resolve();
|
|
});
|
|
|
|
return {
|
|
_loadingKeys: arrAdd(loadingKeys, eventKey),
|
|
};
|
|
});
|
|
});
|
|
},
|
|
|
|
onNodeExpand(e, treeNode) {
|
|
let { _expandedKeys: expandedKeys } = this.$data;
|
|
const { loadData } = this.$props;
|
|
const { eventKey, expanded } = getOptionProps(treeNode);
|
|
|
|
// Update selected keys
|
|
const index = expandedKeys.indexOf(eventKey);
|
|
const targetExpanded = !expanded;
|
|
|
|
warning(
|
|
(expanded && index !== -1) || (!expanded && index === -1),
|
|
'Expand state not sync with index check',
|
|
);
|
|
|
|
if (targetExpanded) {
|
|
expandedKeys = arrAdd(expandedKeys, eventKey);
|
|
} else {
|
|
expandedKeys = arrDel(expandedKeys, eventKey);
|
|
}
|
|
|
|
this.setUncontrolledState({ _expandedKeys: expandedKeys });
|
|
this.__emit('expand', expandedKeys, {
|
|
node: treeNode,
|
|
expanded: targetExpanded,
|
|
nativeEvent: e,
|
|
});
|
|
this.__emit('update:expandedKeys', expandedKeys);
|
|
|
|
// Async Load data
|
|
if (targetExpanded && loadData) {
|
|
const loadPromise = this.onNodeLoad(treeNode);
|
|
return loadPromise
|
|
? loadPromise.then(() => {
|
|
// [Legacy] Refresh logic
|
|
this.setUncontrolledState({ _expandedKeys: expandedKeys });
|
|
})
|
|
: null;
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
onNodeMouseEnter(event, node) {
|
|
this.__emit('mouseenter', { event, node });
|
|
},
|
|
|
|
onNodeMouseLeave(event, node) {
|
|
this.__emit('mouseleave', { event, node });
|
|
},
|
|
|
|
onNodeContextMenu(event, node) {
|
|
event.preventDefault();
|
|
this.__emit('rightClick', { event, node });
|
|
},
|
|
|
|
/**
|
|
* Only update the value which is not in props
|
|
*/
|
|
setUncontrolledState(state) {
|
|
let needSync = false;
|
|
const newState = {};
|
|
const props = getOptionProps(this);
|
|
Object.keys(state).forEach(name => {
|
|
if (name.replace('_', '') in props) return;
|
|
needSync = true;
|
|
newState[name] = state[name];
|
|
});
|
|
|
|
if (needSync) {
|
|
this.setState(newState);
|
|
}
|
|
},
|
|
|
|
registerTreeNode(key, node) {
|
|
if (node) {
|
|
this.domTreeNodes[key] = node;
|
|
} else {
|
|
delete this.domTreeNodes[key];
|
|
}
|
|
},
|
|
|
|
isKeyChecked(key) {
|
|
const { _checkedKeys: checkedKeys = [] } = this.$data;
|
|
return checkedKeys.indexOf(key) !== -1;
|
|
},
|
|
|
|
/**
|
|
* [Legacy] Original logic use `key` as tracking clue.
|
|
* We have to use `cloneElement` to pass `key`.
|
|
*/
|
|
renderTreeNode(child, index, level = 0) {
|
|
const {
|
|
_keyEntities: keyEntities,
|
|
_expandedKeys: expandedKeys = [],
|
|
_selectedKeys: selectedKeys = [],
|
|
_halfCheckedKeys: halfCheckedKeys = [],
|
|
_loadedKeys: loadedKeys = [],
|
|
_loadingKeys: loadingKeys = [],
|
|
_dragOverNodeKey: dragOverNodeKey,
|
|
_dropPosition: dropPosition,
|
|
} = this.$data;
|
|
const pos = getPosition(level, index);
|
|
let key = child.key;
|
|
if (!key && (key === undefined || key === null)) {
|
|
key = pos;
|
|
}
|
|
if (!keyEntities.get(key)) {
|
|
warnOnlyTreeNode();
|
|
return null;
|
|
}
|
|
|
|
return cloneElement(child, {
|
|
props: {
|
|
eventKey: key,
|
|
expanded: expandedKeys.indexOf(key) !== -1,
|
|
selected: selectedKeys.indexOf(key) !== -1,
|
|
loaded: loadedKeys.indexOf(key) !== -1,
|
|
loading: loadingKeys.indexOf(key) !== -1,
|
|
checked: this.isKeyChecked(key),
|
|
halfChecked: halfCheckedKeys.indexOf(key) !== -1,
|
|
pos,
|
|
|
|
// [Legacy] Drag props
|
|
dragOver: dragOverNodeKey === key && dropPosition === 0,
|
|
dragOverGapTop: dragOverNodeKey === key && dropPosition === -1,
|
|
dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1,
|
|
},
|
|
key,
|
|
});
|
|
},
|
|
},
|
|
|
|
render() {
|
|
const { _treeNode: treeNode } = this.$data;
|
|
const { prefixCls, focusable, showLine, tabIndex = 0 } = this.$props;
|
|
|
|
return (
|
|
<ul
|
|
class={classNames(prefixCls, {
|
|
[`${prefixCls}-show-line`]: showLine,
|
|
})}
|
|
role="tree"
|
|
unselectable="on"
|
|
tabIndex={focusable ? tabIndex : null}
|
|
>
|
|
{mapChildren(treeNode, (node, index) => this.renderTreeNode(node, index))}
|
|
</ul>
|
|
);
|
|
},
|
|
};
|
|
|
|
export { Tree };
|
|
|
|
export default proxyComponent(Tree);
|