tangjinzhou
6 years ago
14 changed files with 200 additions and 704 deletions
@ -0,0 +1,38 @@ |
|||||||
|
import { mount } from '@vue/test-utils' |
||||||
|
import Tree from '../index' |
||||||
|
import { calcRangeKeys } from '../util' |
||||||
|
|
||||||
|
const TreeNode = Tree.TreeNode |
||||||
|
|
||||||
|
describe('Tree util', () => { |
||||||
|
it('calc range keys', () => { |
||||||
|
const wrapper = mount({ |
||||||
|
render () { |
||||||
|
return ( |
||||||
|
<Tree> |
||||||
|
<TreeNode key='0-0'> |
||||||
|
<TreeNode key='0-0-0' /> |
||||||
|
<TreeNode key='0-0-1' /> |
||||||
|
</TreeNode> |
||||||
|
<TreeNode key='0-1'> |
||||||
|
<TreeNode key='0-1-0' /> |
||||||
|
<TreeNode key='0-1-1' /> |
||||||
|
</TreeNode> |
||||||
|
<TreeNode key='0-2'> |
||||||
|
<TreeNode key='0-2-0'> |
||||||
|
<TreeNode key='0-2-0-0' /> |
||||||
|
<TreeNode key='0-2-0-1' /> |
||||||
|
<TreeNode key='0-2-0-2' /> |
||||||
|
</TreeNode> |
||||||
|
</TreeNode> |
||||||
|
</Tree> |
||||||
|
) |
||||||
|
}, |
||||||
|
}) |
||||||
|
|
||||||
|
const treeWrapper = wrapper.find({ name: 'ATree' }) |
||||||
|
const keys = calcRangeKeys(treeWrapper.vm.$slots.default, ['0-0', '0-2', '0-2-0'], '0-2-0-1', '0-0-0') |
||||||
|
const target = ['0-0-0', '0-0-1', '0-1', '0-2', '0-2-0', '0-2-0-0', '0-2-0-1'] |
||||||
|
expect(keys.sort()).toEqual(target.sort()) |
||||||
|
}) |
||||||
|
}) |
@ -0,0 +1,43 @@ |
|||||||
|
<cn> |
||||||
|
#### 目录 |
||||||
|
内置的目录树,`multiple` 模式支持 `ctrl(Windows)` / `command(Mac)` 复选。 |
||||||
|
</cn> |
||||||
|
|
||||||
|
<us> |
||||||
|
#### Directory |
||||||
|
Built-in directory tree. `multiple` support `ctrl(Windows)` / `command(Mac)` selection. |
||||||
|
</us> |
||||||
|
|
||||||
|
```html |
||||||
|
<template> |
||||||
|
<a-directory-tree |
||||||
|
multiple |
||||||
|
defaultExpandAll |
||||||
|
@select="onSelect" |
||||||
|
@expand="onExpand" |
||||||
|
> |
||||||
|
<a-tree-node title="parent 0" key="0-0"> |
||||||
|
<a-tree-node title="leaf 0-0" key="0-0-0" isLeaf /> |
||||||
|
<a-tree-node title="leaf 0-1" key="0-0-1" isLeaf /> |
||||||
|
</a-tree-node> |
||||||
|
<a-tree-node title="parent 1" key="0-1"> |
||||||
|
<a-tree-node title="leaf 1-0" key="0-1-0" isLeaf /> |
||||||
|
<a-tree-node title="leaf 1-1" key="0-1-1" isLeaf /> |
||||||
|
</a-tree-node> |
||||||
|
</a-directory-tree> |
||||||
|
</template> |
||||||
|
<script> |
||||||
|
export default { |
||||||
|
methods: { |
||||||
|
onSelect (keys) { |
||||||
|
console.log('Trigger Select', keys); |
||||||
|
}, |
||||||
|
onExpand () { |
||||||
|
console.log('Trigger Expand'); |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
``` |
@ -1,183 +0,0 @@ |
|||||||
|
|
||||||
import VcTree, { TreeNode } from '../vc-tree' |
|
||||||
import animation from '../_util/openAnimation' |
|
||||||
import PropTypes from '../_util/vue-types' |
|
||||||
import { initDefaultProps, getOptionProps } from '../_util/props-util' |
|
||||||
|
|
||||||
// export interface AntTreeNodeProps { |
|
||||||
// disabled: PropTypes.bool, |
|
||||||
// disableCheckbox: PropTypes.bool, |
|
||||||
// title?: string | React.ReactNode; |
|
||||||
// key?: string; |
|
||||||
// isLeaf: PropTypes.bool, |
|
||||||
// children?: React.ReactNode; |
|
||||||
// } |
|
||||||
|
|
||||||
// export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> {} |
|
||||||
|
|
||||||
// export interface AntTreeNodeEvent { |
|
||||||
// event: 'check' | 'select'; |
|
||||||
// node: AntTreeNode; |
|
||||||
// checked: PropTypes.bool, |
|
||||||
// checkedNodes?: Array<AntTreeNode>; |
|
||||||
// selected: PropTypes.bool, |
|
||||||
// selectedNodes?: Array<AntTreeNode>; |
|
||||||
// } |
|
||||||
|
|
||||||
// export interface AntTreeNodeMouseEvent { |
|
||||||
// node: AntTreeNode; |
|
||||||
// event: React.MouseEventHandler<any>; |
|
||||||
// } |
|
||||||
|
|
||||||
export const TreeProps = () => ({ |
|
||||||
treeNodes: PropTypes.array, |
|
||||||
showLine: PropTypes.bool, |
|
||||||
/** 是否支持多选 */ |
|
||||||
multiple: PropTypes.boolean, |
|
||||||
/** 是否自动展开父节点 */ |
|
||||||
autoExpandParent: PropTypes.boolean, |
|
||||||
/** checkable状态下节点选择完全受控(父子节点选中状态不再关联)*/ |
|
||||||
checkStrictly: PropTypes.bool, |
|
||||||
/** 是否支持选中 */ |
|
||||||
checkable: PropTypes.bool, |
|
||||||
/** 默认展开所有树节点 */ |
|
||||||
defaultExpandAll: PropTypes.bool, |
|
||||||
/** 默认展开指定的树节点 */ |
|
||||||
defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string), |
|
||||||
/** (受控)展开指定的树节点 */ |
|
||||||
expandedKeys: PropTypes.arrayOf(PropTypes.string), |
|
||||||
/** (受控)选中复选框的树节点 */ |
|
||||||
checkedKeys: PropTypes.oneOfType( |
|
||||||
[ |
|
||||||
PropTypes.arrayOf(PropTypes.string), |
|
||||||
PropTypes.shape({ |
|
||||||
checked: PropTypes.arrayOf(String), |
|
||||||
halfChecked: PropTypes.arrayOf(String), |
|
||||||
}).loose, |
|
||||||
] |
|
||||||
), |
|
||||||
/** 默认选中复选框的树节点 */ |
|
||||||
defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string), |
|
||||||
/** (受控)设置选中的树节点 */ |
|
||||||
selectedKeys: PropTypes.arrayOf(PropTypes.string), |
|
||||||
/** 默认选中的树节点 */ |
|
||||||
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string), |
|
||||||
/** 展开/收起节点时触发 */ |
|
||||||
// onExpand?: (expandedKeys: Array<string>, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike<any>; |
|
||||||
/** 点击复选框触发 */ |
|
||||||
// onCheck?: (checkedKeys: Array<string>, e: AntTreeNodeEvent) => void; |
|
||||||
/** 点击树节点触发 */ |
|
||||||
// onSelect?: (selectedKeys: Array<string>, e: AntTreeNodeEvent) => void; |
|
||||||
/** filter some AntTreeNodes as you need. it should return true */ |
|
||||||
filterAntTreeNode: PropTypes.func, |
|
||||||
/** 异步加载数据 */ |
|
||||||
loadData: PropTypes.func, |
|
||||||
/** 响应右键点击 */ |
|
||||||
// onRightClick?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
/** 设置节点可拖拽(IE>8)*/ |
|
||||||
draggable: PropTypes.bool, |
|
||||||
// /** 开始拖拽时调用 */ |
|
||||||
// onDragStart?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
// /** dragenter 触发时调用 */ |
|
||||||
// onDragEnter?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
// /** dragover 触发时调用 */ |
|
||||||
// onDragOver?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
// /** dragleave 触发时调用 */ |
|
||||||
// onDragLeave?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
// /** drop 触发时调用 */ |
|
||||||
// onDrop?: (options: AntTreeNodeMouseEvent) => void; |
|
||||||
prefixCls: PropTypes.string, |
|
||||||
filterTreeNode: PropTypes.func, |
|
||||||
showIcon: PropTypes.bool, |
|
||||||
openAnimation: PropTypes.any, |
|
||||||
}) |
|
||||||
|
|
||||||
const Tree = { |
|
||||||
name: 'ATree', |
|
||||||
TreeNode: { ...TreeNode, name: 'ATreeNode' }, |
|
||||||
props: initDefaultProps(TreeProps(), { |
|
||||||
prefixCls: 'ant-tree', |
|
||||||
checkable: false, |
|
||||||
showIcon: false, |
|
||||||
openAnimation: animation, |
|
||||||
}), |
|
||||||
model: { |
|
||||||
prop: 'checkedKeys', |
|
||||||
event: 'check', |
|
||||||
}, |
|
||||||
methods: { |
|
||||||
handleCheck (checkedKeys, e) { |
|
||||||
this.$emit('check', checkedKeys, e) |
|
||||||
}, |
|
||||||
handelSelect (selectedKeys, e) { |
|
||||||
this.$emit('select', selectedKeys, e) |
|
||||||
this.$emit('update:select', selectedKeys) |
|
||||||
}, |
|
||||||
handleExpand (expandedKeys, info) { |
|
||||||
this.$emit('expand', expandedKeys, info) |
|
||||||
this.$emit('update:expand', expandedKeys) |
|
||||||
}, |
|
||||||
renderTreeNodes (data = []) { |
|
||||||
const { $slots, $scopedSlots } = this |
|
||||||
return data.map((item) => { |
|
||||||
const { children, on = {}, slots = {}, scopedSlots = {}, key, class: cls, style, ...restProps } = item |
|
||||||
const treeNodeProps = { |
|
||||||
props: { |
|
||||||
...restProps, |
|
||||||
icon: restProps.icon || |
|
||||||
$slots[slots.icon] || |
|
||||||
($scopedSlots[scopedSlots.icon] && $scopedSlots[scopedSlots.icon]), |
|
||||||
title: restProps.title || |
|
||||||
$slots[slots.title] || |
|
||||||
($scopedSlots[scopedSlots.title] && $scopedSlots[scopedSlots.title])(item), |
|
||||||
dataRef: item, |
|
||||||
}, |
|
||||||
on, |
|
||||||
key, |
|
||||||
class: cls, |
|
||||||
style, |
|
||||||
} |
|
||||||
if (children) { |
|
||||||
return ( |
|
||||||
<TreeNode {...treeNodeProps}> |
|
||||||
{this.renderTreeNodes(children)} |
|
||||||
</TreeNode> |
|
||||||
) |
|
||||||
} |
|
||||||
return <TreeNode {...treeNodeProps} /> |
|
||||||
}) |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
render () { |
|
||||||
const props = getOptionProps(this) |
|
||||||
const { prefixCls, checkable, treeNodes, ...restProps } = props |
|
||||||
const { handelSelect, handleCheck, handleExpand, renderTreeNodes } = this |
|
||||||
const vcTreeProps = { |
|
||||||
props: { |
|
||||||
...restProps, |
|
||||||
prefixCls, |
|
||||||
checkable: checkable ? <span class={`${prefixCls}-checkbox-inner`} /> : checkable, |
|
||||||
}, |
|
||||||
on: { |
|
||||||
...this.$listeners, |
|
||||||
check: handleCheck, |
|
||||||
select: handelSelect, |
|
||||||
expand: handleExpand, |
|
||||||
}, |
|
||||||
} |
|
||||||
return ( |
|
||||||
<VcTree {...vcTreeProps}> |
|
||||||
{treeNodes ? renderTreeNodes(treeNodes) : this.$slots.default} |
|
||||||
</VcTree> |
|
||||||
) |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
/* istanbul ignore next */ |
|
||||||
Tree.install = function (Vue) { |
|
||||||
Vue.component(Tree.name, Tree) |
|
||||||
Vue.component(Tree.TreeNode.name, Tree.TreeNode) |
|
||||||
} |
|
||||||
|
|
||||||
export default Tree |
|
@ -1,378 +0,0 @@ |
|||||||
/* eslint no-loop-func: 0*/ |
|
||||||
import warning from 'warning' |
|
||||||
import { getSlotOptions, getOptionProps } from '../../_util/props-util' |
|
||||||
const DRAG_SIDE_RANGE = 0.25 |
|
||||||
const DRAG_MIN_GAP = 2 |
|
||||||
|
|
||||||
let onlyTreeNodeWarned = false |
|
||||||
|
|
||||||
export function warnOnlyTreeNode () { |
|
||||||
if (onlyTreeNodeWarned) return |
|
||||||
|
|
||||||
onlyTreeNodeWarned = true |
|
||||||
warning(false, 'Tree only accept TreeNode as children.') |
|
||||||
} |
|
||||||
|
|
||||||
export function arrDel (list, value) { |
|
||||||
const clone = list.slice() |
|
||||||
const index = clone.indexOf(value) |
|
||||||
if (index >= 0) { |
|
||||||
clone.splice(index, 1) |
|
||||||
} |
|
||||||
return clone |
|
||||||
} |
|
||||||
|
|
||||||
export function arrAdd (list, value) { |
|
||||||
const clone = list.slice() |
|
||||||
if (clone.indexOf(value) === -1) { |
|
||||||
clone.push(value) |
|
||||||
} |
|
||||||
return clone |
|
||||||
} |
|
||||||
|
|
||||||
export function posToArr (pos) { |
|
||||||
return pos.split('-') |
|
||||||
} |
|
||||||
|
|
||||||
export function getPosition (level, index) { |
|
||||||
return `${level}-${index}` |
|
||||||
} |
|
||||||
|
|
||||||
export function isTreeNode (node) { |
|
||||||
return getSlotOptions(node).isTreeNode |
|
||||||
} |
|
||||||
|
|
||||||
export function getNodeChildren (children = []) { |
|
||||||
return children.filter(isTreeNode) |
|
||||||
} |
|
||||||
|
|
||||||
export function isCheckDisabled (node) { |
|
||||||
const { disabled, disableCheckbox } = getOptionProps(node) || {} |
|
||||||
return !!(disabled || disableCheckbox) |
|
||||||
} |
|
||||||
|
|
||||||
export function traverseTreeNodes (treeNodes, subTreeData, callback) { |
|
||||||
function processNode (node, index, parent) { |
|
||||||
const children = node ? node.componentOptions.children : treeNodes |
|
||||||
const pos = node ? getPosition(parent.pos, index) : 0 |
|
||||||
|
|
||||||
// Filter children
|
|
||||||
const childList = getNodeChildren(children) |
|
||||||
|
|
||||||
// Process node if is not root
|
|
||||||
if (node) { |
|
||||||
const data = { |
|
||||||
node, |
|
||||||
index, |
|
||||||
pos, |
|
||||||
key: node.key || pos, |
|
||||||
parentPos: parent.node ? parent.pos : null, |
|
||||||
} |
|
||||||
callback(data) |
|
||||||
} |
|
||||||
|
|
||||||
// Process children node
|
|
||||||
childList.forEach((subNode, subIndex) => { |
|
||||||
processNode(subNode, subIndex, { node, pos }) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
processNode(null) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Use `rc-util` `toArray` to get the children list which keeps the key. |
|
||||||
* And return single node if children is only one(This can avoid `key` missing check). |
|
||||||
*/ |
|
||||||
export function mapChildren (children = [], func) { |
|
||||||
const list = children.map(func) |
|
||||||
if (list.length === 1) { |
|
||||||
return list[0] |
|
||||||
} |
|
||||||
return list |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* [Legacy] Return halfChecked when it has value. |
|
||||||
* @param checkedKeys |
|
||||||
* @param halfChecked |
|
||||||
* @returns {*} |
|
||||||
*/ |
|
||||||
export function getStrictlyValue (checkedKeys, halfChecked) { |
|
||||||
if (halfChecked) { |
|
||||||
return { checked: checkedKeys, halfChecked } |
|
||||||
} |
|
||||||
return checkedKeys |
|
||||||
} |
|
||||||
|
|
||||||
export function getFullKeyList (treeNodes) { |
|
||||||
const keyList = [] |
|
||||||
traverseTreeNodes(treeNodes, ({ key }) => { |
|
||||||
keyList.push(key) |
|
||||||
}) |
|
||||||
return keyList |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check position relation. |
|
||||||
* @param parentPos |
|
||||||
* @param childPos |
|
||||||
* @param directly only directly parent can be true |
|
||||||
* @returns {boolean} |
|
||||||
*/ |
|
||||||
export function isParent (parentPos, childPos, directly = false) { |
|
||||||
if (!parentPos || !childPos || parentPos.length > childPos.length) return false |
|
||||||
|
|
||||||
const parentPath = posToArr(parentPos) |
|
||||||
const childPath = posToArr(childPos) |
|
||||||
|
|
||||||
// Directly check
|
|
||||||
if (directly && parentPath.length !== childPath.length - 1) return false |
|
||||||
|
|
||||||
const len = parentPath.length |
|
||||||
for (let i = 0; i < len; i += 1) { |
|
||||||
if (parentPath[i] !== childPath[i]) return false |
|
||||||
} |
|
||||||
|
|
||||||
return true |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Statistic TreeNodes info |
|
||||||
* @param treeNodes |
|
||||||
* @returns {{}} |
|
||||||
*/ |
|
||||||
export function getNodesStatistic (treeNodes) { |
|
||||||
const statistic = { |
|
||||||
keyNodes: {}, |
|
||||||
posNodes: {}, |
|
||||||
nodeList: [], |
|
||||||
} |
|
||||||
|
|
||||||
traverseTreeNodes(treeNodes, true, ({ node, index, pos, key, subNodes, parentPos }) => { |
|
||||||
const data = { node, index, pos, key, subNodes, parentPos } |
|
||||||
statistic.keyNodes[key] = data |
|
||||||
statistic.posNodes[pos] = data |
|
||||||
statistic.nodeList.push(data) |
|
||||||
}) |
|
||||||
|
|
||||||
return statistic |
|
||||||
} |
|
||||||
|
|
||||||
export function getDragNodesKeys (treeNodes, node) { |
|
||||||
const { eventKey, pos } = getOptionProps(node) |
|
||||||
const dragNodesKeys = [] |
|
||||||
|
|
||||||
traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => { |
|
||||||
if (isParent(pos, nodePos)) { |
|
||||||
dragNodesKeys.push(key) |
|
||||||
} |
|
||||||
}) |
|
||||||
dragNodesKeys.push(eventKey || pos) |
|
||||||
return dragNodesKeys |
|
||||||
} |
|
||||||
|
|
||||||
export function calcDropPosition (event, treeNode) { |
|
||||||
const { clientY } = event |
|
||||||
const { top, bottom, height } = treeNode.$refs.selectHandle.getBoundingClientRect() |
|
||||||
const des = Math.max(height * DRAG_SIDE_RANGE, DRAG_MIN_GAP) |
|
||||||
|
|
||||||
if (clientY <= top + des) { |
|
||||||
return -1 |
|
||||||
} else if (clientY >= bottom - des) { |
|
||||||
return 1 |
|
||||||
} |
|
||||||
return 0 |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Auto expand all related node when sub node is expanded |
|
||||||
* @param keyList |
|
||||||
* @param props |
|
||||||
* @returns [string] |
|
||||||
*/ |
|
||||||
export function calcExpandedKeys (keyList, props, children = []) { |
|
||||||
if (!keyList) { |
|
||||||
return [] |
|
||||||
} |
|
||||||
|
|
||||||
// Fill parent expanded keys
|
|
||||||
const { keyNodes, nodeList } = getNodesStatistic(children) |
|
||||||
const needExpandKeys = {} |
|
||||||
const needExpandPathList = [] |
|
||||||
|
|
||||||
// Fill expanded nodes
|
|
||||||
keyList.forEach((key) => { |
|
||||||
const node = keyNodes[key] |
|
||||||
if (node) { |
|
||||||
needExpandKeys[key] = true |
|
||||||
needExpandPathList.push(node.pos) |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
// Match parent by path
|
|
||||||
nodeList.forEach(({ pos, key }) => { |
|
||||||
if (needExpandPathList.some(childPos => isParent(pos, childPos))) { |
|
||||||
needExpandKeys[key] = true |
|
||||||
} |
|
||||||
}) |
|
||||||
|
|
||||||
const calcExpandedKeyList = Object.keys(needExpandKeys) |
|
||||||
|
|
||||||
// [Legacy] Return origin keyList if calc list is empty
|
|
||||||
return calcExpandedKeyList.length ? calcExpandedKeyList : keyList |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Return selectedKeys according with multiple prop |
|
||||||
* @param selectedKeys |
|
||||||
* @param props |
|
||||||
* @returns [string] |
|
||||||
*/ |
|
||||||
export function calcSelectedKeys (selectedKeys, props) { |
|
||||||
if (!selectedKeys) { |
|
||||||
return undefined |
|
||||||
} |
|
||||||
|
|
||||||
const { multiple } = props |
|
||||||
if (multiple) { |
|
||||||
return selectedKeys.slice() |
|
||||||
} |
|
||||||
|
|
||||||
if (selectedKeys.length) { |
|
||||||
return [selectedKeys[0]] |
|
||||||
} |
|
||||||
return selectedKeys |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Check conduct is by key level. It pass though up & down. |
|
||||||
* When conduct target node is check means already conducted will be skip. |
|
||||||
* @param treeNodes |
|
||||||
* @param checkedKeys |
|
||||||
* @returns {{checkedKeys: Array, halfCheckedKeys: Array}} |
|
||||||
*/ |
|
||||||
export function calcCheckStateConduct (treeNodes, checkedKeys) { |
|
||||||
const { keyNodes, posNodes } = getNodesStatistic(treeNodes) |
|
||||||
|
|
||||||
const tgtCheckedKeys = {} |
|
||||||
const tgtHalfCheckedKeys = {} |
|
||||||
|
|
||||||
// Conduct up
|
|
||||||
function conductUp (key, halfChecked) { |
|
||||||
if (tgtCheckedKeys[key]) return |
|
||||||
|
|
||||||
const { subNodes = [], parentPos, node } = keyNodes[key] |
|
||||||
if (isCheckDisabled(node)) return |
|
||||||
|
|
||||||
const allSubChecked = !halfChecked && subNodes |
|
||||||
.filter(sub => !isCheckDisabled(sub.node)) |
|
||||||
.every(sub => tgtCheckedKeys[sub.key]) |
|
||||||
|
|
||||||
if (allSubChecked) { |
|
||||||
tgtCheckedKeys[key] = true |
|
||||||
} else { |
|
||||||
tgtHalfCheckedKeys[key] = true |
|
||||||
} |
|
||||||
|
|
||||||
if (parentPos !== null) { |
|
||||||
conductUp(posNodes[parentPos].key, !allSubChecked) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Conduct down
|
|
||||||
function conductDown (key) { |
|
||||||
if (tgtCheckedKeys[key]) return |
|
||||||
const { subNodes = [], node } = keyNodes[key] |
|
||||||
|
|
||||||
if (isCheckDisabled(node)) return |
|
||||||
|
|
||||||
tgtCheckedKeys[key] = true |
|
||||||
|
|
||||||
subNodes.forEach((sub) => { |
|
||||||
conductDown(sub.key) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
function conduct (key) { |
|
||||||
if (!keyNodes[key]) { |
|
||||||
warning(false, `'${key}' does not exist in the tree.`) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
const { subNodes = [], parentPos, node } = keyNodes[key] |
|
||||||
|
|
||||||
tgtCheckedKeys[key] = true |
|
||||||
|
|
||||||
if (isCheckDisabled(node)) return |
|
||||||
|
|
||||||
// Conduct down
|
|
||||||
subNodes |
|
||||||
.filter(sub => !isCheckDisabled(sub.node)) |
|
||||||
.forEach((sub) => { |
|
||||||
conductDown(sub.key) |
|
||||||
}) |
|
||||||
|
|
||||||
// Conduct up
|
|
||||||
if (parentPos !== null) { |
|
||||||
conductUp(posNodes[parentPos].key) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
checkedKeys.forEach((key) => { |
|
||||||
conduct(key) |
|
||||||
}) |
|
||||||
|
|
||||||
return { |
|
||||||
checkedKeys: Object.keys(tgtCheckedKeys), |
|
||||||
halfCheckedKeys: Object.keys(tgtHalfCheckedKeys) |
|
||||||
.filter(key => !tgtCheckedKeys[key]), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function keyListToString (keyList) { |
|
||||||
if (!keyList) return keyList |
|
||||||
return keyList.map(key => String(key)) |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Calculate the value of checked and halfChecked keys. |
|
||||||
* This should be only run in init or props changed. |
|
||||||
*/ |
|
||||||
export function calcCheckedKeys (keys, props, children = []) { |
|
||||||
const { checkable, checkStrictly } = props |
|
||||||
|
|
||||||
if (!checkable || !keys) { |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
// Convert keys to object format
|
|
||||||
let keyProps |
|
||||||
if (Array.isArray(keys)) { |
|
||||||
// [Legacy] Follow the api doc
|
|
||||||
keyProps = { |
|
||||||
checkedKeys: keys, |
|
||||||
halfCheckedKeys: undefined, |
|
||||||
} |
|
||||||
} else if (typeof keys === 'object') { |
|
||||||
keyProps = { |
|
||||||
checkedKeys: keys.checked || undefined, |
|
||||||
halfCheckedKeys: keys.halfChecked || undefined, |
|
||||||
} |
|
||||||
} else { |
|
||||||
warning(false, '`CheckedKeys` is not an array or an object') |
|
||||||
return null |
|
||||||
} |
|
||||||
|
|
||||||
keyProps.checkedKeys = keyListToString(keyProps.checkedKeys) |
|
||||||
keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys) |
|
||||||
|
|
||||||
// Do nothing if is checkStrictly mode
|
|
||||||
if (checkStrictly) { |
|
||||||
return keyProps |
|
||||||
} |
|
||||||
|
|
||||||
// Conduct calculate the check status
|
|
||||||
const { checkedKeys = [] } = keyProps |
|
||||||
return calcCheckStateConduct(children, checkedKeys) |
|
||||||
} |
|
Loading…
Reference in new issue