From 5ea54b104a5e41dcf63d51aa9b136fdad44b2869 Mon Sep 17 00:00:00 2001
From: tjz <415800467@qq.com>
Date: Fri, 23 Mar 2018 21:30:20 +0800
Subject: [PATCH] add tree
---
components/checkbox/Checkbox.jsx | 1 -
components/dropdown/index.en-US.md | 4 +-
components/dropdown/index.zh-CN.md | 4 +-
components/trigger/index.md | 2 +-
components/vc-tree/assets/icons.png | Bin 0 -> 11173 bytes
components/vc-tree/assets/index.less | 192 ++++++++
components/vc-tree/assets/line.gif | Bin 0 -> 45 bytes
components/vc-tree/assets/loading.gif | Bin 0 -> 381 bytes
components/vc-tree/index.js | 3 +
components/vc-tree/src/Tree.jsx | 610 ++++++++++++++++++++++++++
components/vc-tree/src/TreeNode.jsx | 585 ++++++++++++++++++++++++
components/vc-tree/src/index.js | 6 +
components/vc-tree/src/util.js | 399 +++++++++++++++++
13 files changed, 1800 insertions(+), 6 deletions(-)
create mode 100644 components/vc-tree/assets/icons.png
create mode 100644 components/vc-tree/assets/index.less
create mode 100644 components/vc-tree/assets/line.gif
create mode 100644 components/vc-tree/assets/loading.gif
create mode 100644 components/vc-tree/index.js
create mode 100644 components/vc-tree/src/Tree.jsx
create mode 100644 components/vc-tree/src/TreeNode.jsx
create mode 100644 components/vc-tree/src/index.js
create mode 100644 components/vc-tree/src/util.js
diff --git a/components/checkbox/Checkbox.jsx b/components/checkbox/Checkbox.jsx
index 722ed86e9..3056fc5c1 100644
--- a/components/checkbox/Checkbox.jsx
+++ b/components/checkbox/Checkbox.jsx
@@ -20,7 +20,6 @@ export default {
},
inject: {
checkboxGroupContext: { default: null },
- test: { default: null },
},
data () {
const { checkboxGroupContext, checked, defaultChecked, value } = this
diff --git a/components/dropdown/index.en-US.md b/components/dropdown/index.en-US.md
index 4c872bd8c..e0f7c5b56 100644
--- a/components/dropdown/index.en-US.md
+++ b/components/dropdown/index.en-US.md
@@ -8,7 +8,7 @@
| getPopupContainer | to set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | Function(triggerNode) | `() => document.body` |
| overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - |
| placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
-| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` |
+| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` |
| visible(v-model) | whether the dropdown menu is visible | boolean | - |
### events
@@ -30,7 +30,7 @@ You should use [Menu](#/us/components/menu/) as `overlay`. The menu items and di
| overlay(slot) | the dropdown menu | [Menu](#/us/components/menu) | - |
| placement | placement of pop menu: `bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
| size | size of the button, the same as [Button](#/us/components/button) | string | `default` |
-| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` |
+| trigger | the trigger mode which executes the drop-down action | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` |
| type | type of the button, the same as [Button](#/us/components/button) | string | `default` |
| visible | whether the dropdown menu is visible | boolean | - |
diff --git a/components/dropdown/index.zh-CN.md b/components/dropdown/index.zh-CN.md
index 8c4f839ff..b65b5cf1d 100644
--- a/components/dropdown/index.zh-CN.md
+++ b/components/dropdown/index.zh-CN.md
@@ -8,7 +8,7 @@
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | `() => document.body` |
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu) | - |
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
-| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` |
+| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` |
| visible(v-model) | 菜单是否显示 | boolean | - |
`overlay` 菜单使用 [Menu](#/cn/components/menu/),还包括菜单项 `Menu.Item`,分割线 `Menu.Divider`。
@@ -31,7 +31,7 @@
| overlay(slot) | 菜单 | [Menu](#/cn/components/menu/) | - |
| placement | 菜单弹出位置:`bottomLeft` `bottomCenter` `bottomRight` `topLeft` `topCenter` `topRight` | String | `bottomLeft` |
| size | 按钮大小,和 [Button](#/cn/components/button/) 一致 | string | 'default' |
-| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | `['hover']` |
+| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextmenu`> | `['hover']` |
| type | 按钮类型,和 [Button](#/cn/components/button/) 一致 | string | 'default' |
| visible(v-model) | 菜单是否显示 | boolean | - |
diff --git a/components/trigger/index.md b/components/trigger/index.md
index 69f9cb643..bae1a0f2a 100644
--- a/components/trigger/index.md
+++ b/components/trigger/index.md
@@ -40,7 +40,7 @@
action |
string[] |
['hover'] |
- which actions cause popup shown. enum of 'hover','click','focus','contextMenu' |
+ which actions cause popup shown. enum of 'hover','click','focus','contextmenu' |
mouseEnterDelay |
diff --git a/components/vc-tree/assets/icons.png b/components/vc-tree/assets/icons.png
new file mode 100644
index 0000000000000000000000000000000000000000..ffda01ef1cccc398ee4e2327f4093ba1130a4961
GIT binary patch
literal 11173
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
X3wf1`XyoU;TL_tNiW1dgpn(4aGkUu%
literal 0
HcmV?d00001
diff --git a/components/vc-tree/assets/index.less b/components/vc-tree/assets/index.less
new file mode 100644
index 000000000..cde71e949
--- /dev/null
+++ b/components/vc-tree/assets/index.less
@@ -0,0 +1,192 @@
+@treePrefixCls: rc-tree;
+.@{treePrefixCls} {
+ margin: 0;
+ padding: 5px;
+ li {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ .draggable {
+ color: #333;
+ -moz-user-select: none;
+ -khtml-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ /* Required to make elements draggable in old WebKit */
+ -khtml-user-drag: element;
+ -webkit-user-drag: element;
+ }
+ &.drag-over {
+ > .draggable {
+ background-color: #316ac5;
+ color: white;
+ border: 1px #316ac5 solid;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > .draggable {
+ border-top: 2px blue solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > .draggable {
+ border-bottom: 2px blue solid;
+ }
+ }
+ &.filter-node {
+ > .@{treePrefixCls}-node-content-wrapper {
+ color: #a60000!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ }
+ .@{treePrefixCls}-node-content-wrapper {
+ display: inline-block;
+ padding: 1px 3px 0 0;
+ margin: 0;
+ cursor: pointer;
+ height: 17px;
+ text-decoration: none;
+ vertical-align: top;
+ }
+ span {
+ &.@{treePrefixCls}-switcher,
+ &.@{treePrefixCls}-checkbox,
+ &.@{treePrefixCls}-iconEle {
+ line-height: 16px;
+ margin-right: 2px;
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-attachment: scroll;
+ background-image: url('data:image/png;base64,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');
+
+ &.@{treePrefixCls}-icon__customize {
+ background-image: none;
+ }
+ }
+ &.@{treePrefixCls}-icon_loading {
+ margin-right: 2px;
+ vertical-align: top;
+ background: url('data:image/gif;base64,R0lGODlhEAAQAKIGAMLY8YSx5HOm4Mjc88/g9Ofw+v///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgAGACwAAAAAEAAQAAADMGi6RbUwGjKIXCAA016PgRBElAVlG/RdLOO0X9nK61W39qvqiwz5Ls/rRqrggsdkAgAh+QQFCgAGACwCAAAABwAFAAADD2hqELAmiFBIYY4MAutdCQAh+QQFCgAGACwGAAAABwAFAAADD1hU1kaDOKMYCGAGEeYFCQAh+QQFCgAGACwKAAIABQAHAAADEFhUZjSkKdZqBQG0IELDQAIAIfkEBQoABgAsCgAGAAUABwAAAxBoVlRKgyjmlAIBqCDCzUoCACH5BAUKAAYALAYACgAHAAUAAAMPaGpFtYYMAgJgLogA610JACH5BAUKAAYALAIACgAHAAUAAAMPCAHWFiI4o1ghZZJB5i0JACH5BAUKAAYALAAABgAFAAcAAAMQCAFmIaEp1motpDQySMNFAgA7') no-repeat scroll 0 0 transparent;
+ }
+ &.@{treePrefixCls}-switcher {
+ &.@{treePrefixCls}-switcher-noop {
+ cursor: auto;
+ }
+ &.@{treePrefixCls}-switcher_open {
+ background-position: -93px -56px;
+ }
+ &.@{treePrefixCls}-switcher_close {
+ background-position: -75px -56px;
+ }
+ }
+ &.@{treePrefixCls}-checkbox {
+ width: 13px;
+ height: 13px;
+ margin: 0 3px;
+ background-position: 0 0;
+ &-checked {
+ background-position: -14px 0;
+ }
+ &-indeterminate {
+ background-position: -14px -28px;
+ }
+ &-disabled {
+ background-position: 0 -56px;
+ }
+ &.@{treePrefixCls}-checkbox-checked.@{treePrefixCls}-checkbox-disabled {
+ background-position: -14px -56px;
+ }
+ &.@{treePrefixCls}-checkbox-indeterminate.@{treePrefixCls}-checkbox-disabled {
+ position: relative;
+ background: #ccc;
+ border-radius: 3px;
+ &::after {
+ content: ' ';
+ -webkit-transform: scale(1);
+ transform: scale(1);
+ position: absolute;
+ left: 3px;
+ top: 5px;
+ width: 5px;
+ height: 0;
+ border: 2px solid #fff;
+ border-top: 0;
+ border-left: 0;
+ }
+ }
+ }
+ }
+ }
+ &:not(.@{treePrefixCls}-show-line) {
+ .@{treePrefixCls}-switcher-noop {
+ background: none;
+ }
+ }
+ &.@{treePrefixCls}-show-line {
+ li:not(:last-child) {
+ > ul {
+ background: url('data:image/gif;base64,R0lGODlhCQACAIAAAMzMzP///yH5BAEAAAEALAAAAAAJAAIAAAIEjI9pUAA7') 0 0 repeat-y;
+ }
+ > .@{treePrefixCls}-switcher-noop {
+ background-position: -56px -18px;
+ }
+ }
+ li:last-child {
+ > .@{treePrefixCls}-switcher-noop {
+ background-position: -56px -36px;
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span:not(.@{treePrefixCls}-switcher),
+ >a,
+ >a span {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+ }
+ &-node-selected {
+ background-color: #ffe6b0;
+ border: 1px #ffb951 solid;
+ opacity: 0.8;
+ }
+ &-icon__open {
+ margin-right: 2px;
+ background-position: -110px -16px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ background-position: -110px 0;
+ vertical-align: top;
+ }
+ &-icon__docu {
+ margin-right: 2px;
+ background-position: -110px -32px;
+ vertical-align: top;
+ }
+ &-icon__customize {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+}
diff --git a/components/vc-tree/assets/line.gif b/components/vc-tree/assets/line.gif
new file mode 100644
index 0000000000000000000000000000000000000000..d561d36a915776730eb3069cee4c949f027667ed
GIT binary patch
literal 45
xcmZ?wbhEHbT_)p@)n
z{^qIIq1(T$e$~zMQ}6t1w^a|Cj;Amo3}FHq!p^`7G=x7ROJIXqM}S9S9}m;(SWbi*
zHiVjpkZW$u7K%QH
zEX1o> {
+ const { expandedKeys } = this.state
+ const { onDragEnter } = this.props
+ const { pos, eventKey } = node.props
+
+ const dropPosition = calcDropPosition(event, node)
+
+ // Skip if drag node is self
+ if (
+ this.dragNode.props.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,
+ })
+
+ // 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)
+ this.setState({
+ expandedKeys: newExpandedKeys,
+ })
+
+ if (onDragEnter) {
+ onDragEnter({ event, node, expandedKeys: newExpandedKeys })
+ }
+ }, 400)
+ }, 0)
+ };
+ onNodeDragOver = (event, node) => {
+ const { onDragOver } = this.props
+ if (onDragOver) {
+ onDragOver({ event, node })
+ }
+ };
+ onNodeDragLeave = (event, node) => {
+ const { onDragLeave } = this.props
+
+ this.setState({
+ dragOverNodeKey: '',
+ })
+
+ if (onDragLeave) {
+ onDragLeave({ event, node })
+ }
+ };
+ onNodeDragEnd = (event, node) => {
+ const { onDragEnd } = this.props
+ this.setState({
+ dragOverNodeKey: '',
+ })
+ if (onDragEnd) {
+ onDragEnd({ event, node })
+ }
+ };
+ onNodeDrop = (event, node) => {
+ const { dragNodesKeys, dropPosition } = this.state
+ const { onDrop } = this.props
+ const { eventKey, pos } = node.props
+
+ this.setState({
+ dragOverNodeKey: '',
+ dropNodeKey: eventKey,
+ })
+
+ 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]),
+ }
+
+ if (dropPosition !== 0) {
+ dropResult.dropToGap = true
+ }
+
+ if (onDrop) {
+ onDrop(dropResult)
+ }
+ };
+
+ onNodeSelect = (e, treeNode) => {
+ let { selectedKeys } = this.state
+ const { onSelect, multiple, children } = this.props
+ const { selected, eventKey } = treeNode.props
+ 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
+ // [Legacy] TODO: add optimize prop to skip node process
+ const selectedNodes = []
+ if (selectedKeys.length) {
+ traverseTreeNodes(children, ({ node, key }) => {
+ if (selectedKeys.indexOf(key) !== -1) {
+ selectedNodes.push(node)
+ }
+ })
+ }
+
+ this.setUncontrolledState({ selectedKeys })
+
+ if (onSelect) {
+ const eventObj = {
+ event: 'select',
+ selected: targetSelected,
+ node: treeNode,
+ selectedNodes,
+ }
+ onSelect(selectedKeys, eventObj)
+ }
+ };
+
+ /**
+ * This will cache node check status to optimize update process.
+ * When Tree get trigger `onCheckConductFinished` will flush all the update.
+ */
+ onBatchNodeCheck = (key, checked, halfChecked, startNode) => {
+ if (startNode) {
+ this.checkedBatch = {
+ treeNode: startNode,
+ checked,
+ list: [],
+ }
+ }
+
+ // This code should never called
+ if (!this.checkedBatch) {
+ this.checkedBatch = {
+ list: [],
+ }
+ warning(
+ false,
+ 'Checked batch not init. This should be a bug. Please fire a issue.'
+ )
+ }
+
+ this.checkedBatch.list.push({ key, checked, halfChecked })
+ };
+
+ /**
+ * When top `onCheckConductFinished` called, will execute all batch update.
+ * And trigger `onCheck` event.
+ */
+ onCheckConductFinished = () => {
+ const { checkedKeys, halfCheckedKeys } = this.state
+ const { onCheck, checkStrictly, children } = this.props
+
+ // Use map to optimize update speed
+ const checkedKeySet = {}
+ const halfCheckedKeySet = {}
+
+ checkedKeys.forEach(key => {
+ checkedKeySet[key] = true
+ })
+ halfCheckedKeys.forEach(key => {
+ halfCheckedKeySet[key] = true
+ })
+
+ // Batch process
+ this.checkedBatch.list.forEach(({ key, checked, halfChecked }) => {
+ checkedKeySet[key] = checked
+ halfCheckedKeySet[key] = halfChecked
+ })
+ const newCheckedKeys = Object.keys(checkedKeySet).filter(key => checkedKeySet[key])
+ const newHalfCheckedKeys = Object.keys(halfCheckedKeySet).filter(key => halfCheckedKeySet[key])
+
+ // Trigger onChecked
+ let selectedObj
+
+ const eventObj = {
+ event: 'check',
+ node: this.checkedBatch.treeNode,
+ checked: this.checkedBatch.checked,
+ }
+
+ if (checkStrictly) {
+ selectedObj = getStrictlyValue(newCheckedKeys, newHalfCheckedKeys)
+
+ // [Legacy] TODO: add optimize prop to skip node process
+ eventObj.checkedNodes = []
+ traverseTreeNodes(children, ({ node, key }) => {
+ if (checkedKeySet[key]) {
+ eventObj.checkedNodes.push(node)
+ }
+ })
+
+ this.setUncontrolledState({ checkedKeys: newCheckedKeys })
+ } else {
+ selectedObj = newCheckedKeys
+
+ // [Legacy] TODO: add optimize prop to skip node process
+ eventObj.checkedNodes = []
+ eventObj.checkedNodesPositions = [] // [Legacy] TODO: not in API
+ eventObj.halfCheckedKeys = newHalfCheckedKeys // [Legacy] TODO: not in API
+ traverseTreeNodes(children, ({ node, pos, key }) => {
+ if (checkedKeySet[key]) {
+ eventObj.checkedNodes.push(node)
+ eventObj.checkedNodesPositions.push({ node, pos })
+ }
+ })
+
+ this.setUncontrolledState({
+ checkedKeys: newCheckedKeys,
+ halfCheckedKeys: newHalfCheckedKeys,
+ })
+ }
+
+ if (onCheck) {
+ onCheck(selectedObj, eventObj)
+ }
+
+ // Clean up
+ this.checkedBatch = null
+ };
+
+ onNodeExpand = (e, treeNode) => {
+ let { expandedKeys } = this.state
+ const { onExpand, loadData } = this.props
+ const { eventKey, expanded } = treeNode.props
+
+ // 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 })
+
+ if (onExpand) {
+ onExpand(expandedKeys, { node: treeNode, expanded: targetExpanded })
+ }
+
+ // Async Load data
+ if (targetExpanded && loadData) {
+ return loadData(treeNode).then(() => {
+ // [Legacy] Refresh logic
+ this.setUncontrolledState({ expandedKeys })
+ })
+ }
+
+ return null
+ };
+
+ onNodeMouseEnter = (event, node) => {
+ const { onMouseEnter } = this.props
+ if (onMouseEnter) {
+ onMouseEnter({ event, node })
+ }
+ };
+
+ onNodeMouseLeave = (event, node) => {
+ const { onMouseLeave } = this.props
+ if (onMouseLeave) {
+ onMouseLeave({ event, node })
+ }
+ };
+
+ onNodeContextMenu = (event, node) => {
+ const { onRightClick } = this.props
+ if (onRightClick) {
+ event.preventDefault()
+ onRightClick({ event, node })
+ }
+ };
+
+ /**
+ * Sync state with props if needed
+ */
+ getSyncProps = (props = {}, prevProps) => {
+ let needSync = false
+ const newState = {}
+ const myPrevProps = prevProps || {}
+
+ function checkSync (name) {
+ if (props[name] !== myPrevProps[name]) {
+ needSync = true
+ return true
+ }
+ return false
+ }
+
+ // Children change will affect check box status.
+ // And no need to check when prev props not provided
+ if (prevProps && checkSync('children')) {
+ const { checkedKeys = [], halfCheckedKeys = [] } =
+ calcCheckedKeys(props.checkedKeys || this.state.checkedKeys, props) || {}
+ newState.checkedKeys = checkedKeys
+ newState.halfCheckedKeys = halfCheckedKeys
+ }
+
+ if (checkSync('expandedKeys')) {
+ newState.expandedKeys = calcExpandedKeys(props.expandedKeys, props)
+ }
+
+ if (checkSync('selectedKeys')) {
+ newState.selectedKeys = calcSelectedKeys(props.selectedKeys, props)
+ }
+
+ if (checkSync('checkedKeys')) {
+ const { checkedKeys = [], halfCheckedKeys = [] } =
+ calcCheckedKeys(props.checkedKeys, props) || {}
+ newState.checkedKeys = checkedKeys
+ newState.halfCheckedKeys = halfCheckedKeys
+ }
+
+ return needSync ? newState : null
+ };
+
+ /**
+ * Only update the value which is not in props
+ */
+ setUncontrolledState = (state) => {
+ let needSync = false
+ const newState = {}
+
+ Object.keys(state).forEach(name => {
+ if (name in this.props) return
+
+ needSync = true
+ newState[name] = state[name]
+ })
+
+ this.setState(needSync ? newState : null)
+ };
+
+ isKeyChecked = (key) => {
+ const { checkedKeys = [] } = this.state
+ 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 {
+ expandedKeys = [], selectedKeys = [], halfCheckedKeys = [],
+ dragOverNodeKey, dropPosition,
+ } = this.state
+ const {} = this.props
+ const pos = getPosition(level, index)
+ const key = child.key || pos
+
+ return React.cloneElement(child, {
+ eventKey: key,
+ expanded: expandedKeys.indexOf(key) !== -1,
+ selected: selectedKeys.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,
+ })
+ };
+
+ render () {
+ const {
+ prefixCls, className, focusable,
+ showLine,
+ children,
+ } = this.props
+ const domProps = {}
+
+ // [Legacy] Commit: 0117f0c9db0e2956e92cb208f51a42387dfcb3d1
+ if (focusable) {
+ domProps.tabIndex = '0'
+ domProps.onKeyDown = this.onKeyDown
+ }
+
+ return (
+
+ {React.Children.map(children, this.renderTreeNode, this)}
+
+ )
+ }
+}
+
+export default Tree
diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx
new file mode 100644
index 000000000..155585404
--- /dev/null
+++ b/components/vc-tree/src/TreeNode.jsx
@@ -0,0 +1,585 @@
+import PropTypes from '../../_util/vue-types'
+import classNames from 'classnames'
+import warning from 'warning'
+import { contextTypes } from './Tree'
+import { getPosition, getNodeChildren, isCheckDisabled, traverseTreeNodes } from './util'
+import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util'
+
+const ICON_OPEN = 'open'
+const ICON_CLOSE = 'close'
+
+const LOAD_STATUS_NONE = 0
+const LOAD_STATUS_LOADING = 1
+const LOAD_STATUS_LOADED = 2
+const LOAD_STATUS_FAILED = 0 // Action align, let's make failed same as init.
+
+const defaultTitle = '---'
+
+let onlyTreeNodeWarned = false // Only accept TreeNode
+
+export const nodeContextTypes = {
+ ...contextTypes,
+ rcTreeNode: PropTypes.shape({
+ onUpCheckConduct: PropTypes.func,
+ }),
+}
+
+const TreeNode = {
+ props: initDefaultProps({
+ eventKey: PropTypes.string, // Pass by parent `cloneElement`
+ prefixCls: PropTypes.string,
+ // className: PropTypes.string,
+ root: PropTypes.object,
+ // onSelect: PropTypes.func,
+
+ // By parent
+ expanded: PropTypes.bool,
+ selected: PropTypes.bool,
+ checked: PropTypes.bool,
+ halfChecked: PropTypes.bool,
+ title: PropTypes.node,
+ pos: PropTypes.string,
+ dragOver: PropTypes.bool,
+ dragOverGapTop: PropTypes.bool,
+ dragOverGapBottom: PropTypes.bool,
+
+ // By user
+ isLeaf: PropTypes.bool,
+ selectable: PropTypes.bool,
+ disabled: PropTypes.bool,
+ disableCheckbox: PropTypes.bool,
+ icon: PropTypes.any,
+ }, {
+ title: defaultTitle,
+ }),
+
+ data () {
+ return {
+ loadStatus: LOAD_STATUS_NONE,
+ dragNodeHighlight: false,
+ }
+ },
+ inject: {
+ context: { default: {}},
+ },
+ provide: {
+ ...this.context,
+ rcTreeNode: this,
+ },
+
+ // Isomorphic needn't load data in server side
+ mounted () {
+ this.$nextTick(() => {
+ this.syncLoadData(this.$props)
+ })
+ },
+
+ componentWillReceiveProps (nextProps) {
+ this.syncLoadData(nextProps)
+ },
+
+ onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked) {
+ const { pos: nodePos } = getOptionProps(treeNode)
+ const { eventKey, pos, checked, halfChecked } = this
+ const {
+ rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished },
+ rcTreeNode: { onUpCheckConduct } = {},
+ } = this.context
+
+ // Stop conduct when current node is disabled
+ if (isCheckDisabled(this)) {
+ onCheckConductFinished()
+ return
+ }
+
+ const children = this.getNodeChildren()
+
+ let checkedCount = nodeChecked ? 1 : 0
+
+ // Statistic checked count
+ children.forEach((node, index) => {
+ const childPos = getPosition(pos, index)
+
+ if (nodePos === childPos || isCheckDisabled(node)) {
+ return
+ }
+
+ if (isKeyChecked(node.key || childPos)) {
+ checkedCount += 1
+ }
+ })
+
+ // Static enabled children count
+ const enabledChildrenCount = children
+ .filter(node => !isCheckDisabled(node))
+ .length
+
+ // checkStrictly will not conduct check status
+ const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount
+ const nextHalfChecked = checkStrictly // propagated or child checked
+ ? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked))
+
+ // Add into batch update
+ if (checked !== nextChecked || halfChecked !== nextHalfChecked) {
+ onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked)
+
+ if (onUpCheckConduct) {
+ onUpCheckConduct(this, nextChecked, nextHalfChecked)
+ } else {
+ // Flush all the update
+ onCheckConductFinished()
+ }
+ } else {
+ // Flush all the update
+ onCheckConductFinished()
+ }
+ },
+
+ onDownCheckConduct (nodeChecked) {
+ const { $slots } = this
+ const children = $slots.default || []
+ const { rcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this.context
+ if (checkStrictly) return
+
+ traverseTreeNodes(children, ({ node, key }) => {
+ if (isCheckDisabled(node)) return false
+
+ if (nodeChecked !== isKeyChecked(key)) {
+ onBatchNodeCheck(key, nodeChecked, false)
+ }
+ })
+ },
+
+ onSelectorClick (e) {
+ if (this.isSelectable()) {
+ this.onSelect(e)
+ } else {
+ this.onCheck(e)
+ }
+ },
+
+ onSelect (e) {
+ if (this.isDisabled()) return
+
+ const { rcTree: { onNodeSelect }} = this.context
+ e.preventDefault()
+ onNodeSelect(e, this)
+ },
+
+ onCheck (e) {
+ if (this.isDisabled()) return
+
+ const { disableCheckbox, checked, eventKey } = this
+ const {
+ rcTree: { checkable, onBatchNodeCheck, onCheckConductFinished },
+ rcTreeNode: { onUpCheckConduct } = {},
+ } = this.context
+
+ if (!checkable || disableCheckbox) return
+
+ e.preventDefault()
+ const targetChecked = !checked
+ onBatchNodeCheck(eventKey, targetChecked, false, this)
+
+ // Children conduct
+ this.onDownCheckConduct(targetChecked)
+
+ // Parent conduct
+ if (onUpCheckConduct) {
+ onUpCheckConduct(this, targetChecked, false)
+ } else {
+ onCheckConductFinished()
+ }
+ },
+
+ onMouseEnter (e) {
+ const { rcTree: { onNodeMouseEnter }} = this.context
+ onNodeMouseEnter(e, this)
+ },
+
+ onMouseLeave (e) {
+ const { rcTree: { onNodeMouseLeave }} = this.context
+ onNodeMouseLeave(e, this)
+ },
+
+ onContextMenu (e) {
+ const { rcTree: { onNodeContextMenu }} = this.context
+ onNodeContextMenu(e, this)
+ },
+
+ onDragStart (e) {
+ const { rcTree: { onNodeDragStart }} = this.context
+
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: true,
+ })
+ onNodeDragStart(e, this)
+
+ try {
+ // ie throw error
+ // firefox-need-it
+ e.dataTransfer.setData('text/plain', '')
+ } catch (error) {
+ // empty
+ }
+ },
+
+ onDragEnter (e) {
+ const { rcTree: { onNodeDragEnter }} = this.context
+
+ e.preventDefault()
+ e.stopPropagation()
+ onNodeDragEnter(e, this)
+ },
+
+ onDragOver (e) {
+ const { rcTree: { onNodeDragOver }} = this.context
+
+ e.preventDefault()
+ e.stopPropagation()
+ onNodeDragOver(e, this)
+ },
+
+ onDragLeave (e) {
+ const { rcTree: { onNodeDragLeave }} = this.context
+
+ e.stopPropagation()
+ onNodeDragLeave(e, this)
+ },
+
+ onDragEnd (e) {
+ const { rcTree: { onNodeDragEnd }} = this.context
+
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: false,
+ })
+ onNodeDragEnd(e, this)
+ },
+
+ onDrop (e) {
+ const { rcTree: { onNodeDrop }} = this.context
+
+ e.preventDefault()
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: false,
+ })
+ onNodeDrop(e, this)
+ },
+
+ // Disabled item still can be switch
+ onExpand (e) {
+ const { rcTree: { onNodeExpand }} = this.context
+ const callbackPromise = onNodeExpand(e, this)
+
+ // Promise like
+ if (callbackPromise && callbackPromise.then) {
+ this.setState({ loadStatus: LOAD_STATUS_LOADING })
+
+ callbackPromise.then(() => {
+ this.setState({ loadStatus: LOAD_STATUS_LOADED })
+ }).catch(() => {
+ this.setState({ loadStatus: LOAD_STATUS_FAILED })
+ })
+ }
+ },
+
+ // Drag usage
+ setSelectHandle (node) {
+ this.selectHandle = node
+ },
+
+ getNodeChildren () {
+ const { $slots: { default: children }} = this
+ const originList = filterEmpty(children)
+ const targetList = getNodeChildren(originList)
+
+ if (originList.length !== targetList.length && !onlyTreeNodeWarned) {
+ onlyTreeNodeWarned = true
+ warning(false, 'Tree only accept TreeNode as children.')
+ }
+
+ return targetList
+ },
+
+ getNodeState () {
+ const { expanded } = this
+
+ if (this.isLeaf()) {
+ return null
+ }
+
+ return expanded ? ICON_OPEN : ICON_CLOSE
+ },
+
+ isLeaf () {
+ const { isLeaf, loadStatus } = this
+ const { rcTree: { loadData }} = this.context
+
+ const hasChildren = this.getNodeChildren().length !== 0
+
+ return (
+ isLeaf ||
+ (!loadData && !hasChildren) ||
+ (loadData && loadStatus === LOAD_STATUS_LOADED && !hasChildren)
+ )
+ },
+
+ isDisabled () {
+ const { disabled } = this
+ const { rcTree: { disabled: treeDisabled }} = this.context
+
+ // Follow the logic of Selectable
+ if (disabled === false) {
+ return false
+ }
+
+ return !!(treeDisabled || disabled)
+ },
+
+ isSelectable () {
+ const { selectable } = this
+ const { rcTree: { selectable: treeSelectable }} = this.context
+
+ // Ignore when selectable is undefined or null
+ if (typeof selectable === 'boolean') {
+ return selectable
+ }
+
+ return treeSelectable
+ },
+
+ // Load data to avoid default expanded tree without data
+ syncLoadData (props) {
+ const { loadStatus } = this
+ const { expanded } = props
+ const { rcTree: { loadData }} = this.context
+
+ if (loadData && loadStatus === LOAD_STATUS_NONE && expanded && !this.isLeaf()) {
+ this.setState({ loadStatus: LOAD_STATUS_LOADING })
+
+ loadData(this).then(() => {
+ this.setState({ loadStatus: LOAD_STATUS_LOADED })
+ }).catch(() => {
+ this.setState({ loadStatus: LOAD_STATUS_FAILED })
+ })
+ }
+ },
+
+ // Switcher
+ renderSwitcher () {
+ const { expanded } = this
+ const { rcTree: { prefixCls }} = this.context
+
+ if (this.isLeaf()) {
+ return
+ }
+
+ return (
+
+ )
+ },
+
+ // Checkbox
+ renderCheckbox () {
+ const { checked, halfChecked, disableCheckbox } = this
+ const { rcTree: { prefixCls, checkable }} = this.context
+ const disabled = this.isDisabled()
+
+ if (!checkable) return null
+
+ // [Legacy] Custom element should be separate with `checkable` in future
+ const $custom = typeof checkable !== 'boolean' ? checkable : null
+
+ return (
+
+ {$custom}
+
+ )
+ },
+
+ renderIcon () {
+ const { loadStatus } = this
+ const { rcTree: { prefixCls }} = this.context
+
+ return (
+
+ )
+ },
+
+ // Icon + Title
+ renderSelector () {
+ const { title, selected, icon, loadStatus, dragNodeHighlight } = this
+ const { rcTree: { prefixCls, showIcon, draggable, loadData }} = this.context
+ const disabled = this.isDisabled()
+
+ const wrapClass = `${prefixCls}-node-content-wrapper`
+
+ // Icon - Still show loading icon when loading without showIcon
+ let $icon
+
+ if (showIcon) {
+ $icon = icon ? (
+
+ {typeof icon === 'function'
+ ? icon(this.$props) : icon}
+
+ ) : this.renderIcon()
+ } else if (loadData && loadStatus === LOAD_STATUS_LOADING) {
+ $icon = this.renderIcon()
+ }
+
+ // Title
+ const $title = {title}
+
+ return (
+
+ {$icon}{$title}
+
+ )
+ },
+
+ // Children list wrapped with `Animation`
+ renderChildren () {
+ const { expanded, pos } = this
+ const { rcTree: {
+ prefixCls,
+ openTransitionName, openAnimation,
+ renderTreeNode,
+ }} = this.context
+
+ // [Legacy] Animation control
+ const renderFirst = this.renderFirst
+ this.renderFirst = 1
+ let transitionAppear = true
+ if (!renderFirst && expanded) {
+ transitionAppear = false
+ }
+
+ const animProps = {}
+ if (openTransitionName) {
+ animProps.transitionName = openTransitionName
+ } else if (typeof openAnimation === 'object') {
+ animProps.animation = { ...openAnimation }
+ if (!transitionAppear) {
+ delete animProps.animation.appear
+ }
+ }
+
+ // Children TreeNode
+ const nodeList = this.getNodeChildren()
+
+ if (nodeList.length === 0) {
+ return null
+ }
+
+ let $children
+ if (expanded) {
+ $children = (
+
+ {nodeList.map((node, index) => (
+ renderTreeNode(node, index, pos)
+ ))}
+
+ )
+ }
+
+ return (
+
+ {$children}
+
+ )
+ },
+
+ render () {
+ const {
+ dragOver, dragOverGapTop, dragOverGapBottom,
+ } = this
+ const { rcTree: {
+ prefixCls,
+ filterTreeNode,
+ }} = this.context
+ const disabled = this.isDisabled()
+
+ return (
+
+ {this.renderSwitcher()}
+ {this.renderCheckbox()}
+ {this.renderSelector()}
+ {this.renderChildren()}
+
+ )
+ },
+}
+
+TreeNode.isTreeNode = 1
+
+export default TreeNode
diff --git a/components/vc-tree/src/index.js b/components/vc-tree/src/index.js
new file mode 100644
index 000000000..d053f4c9f
--- /dev/null
+++ b/components/vc-tree/src/index.js
@@ -0,0 +1,6 @@
+import Tree from './Tree'
+import TreeNode from './TreeNode'
+Tree.TreeNode = TreeNode
+
+export { TreeNode }
+export default Tree
diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js
new file mode 100644
index 000000000..41e39ed08
--- /dev/null
+++ b/components/vc-tree/src/util.js
@@ -0,0 +1,399 @@
+/* eslint no-loop-func: 0*/
+import { Children } from 'react'
+import warning from 'warning'
+
+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('-')
+}
+
+// Only used when drag, not affect SSR.
+export function getOffset (ele) {
+ if (!ele.getClientRects().length) {
+ return { top: 0, left: 0 }
+ }
+
+ const rect = ele.getBoundingClientRect()
+ if (rect.width || rect.height) {
+ const doc = ele.ownerDocument
+ const win = doc.defaultView
+ const docElem = doc.documentElement
+
+ return {
+ top: rect.top + win.pageYOffset - docElem.clientTop,
+ left: rect.left + win.pageXOffset - docElem.clientLeft,
+ }
+ }
+
+ return rect
+}
+
+export function getPosition (level, index) {
+ return `${level}-${index}`
+}
+
+export function getNodeChildren (children) {
+ const childList = Array.isArray(children) ? children : [children]
+ return childList
+ .filter(child => child && child.type && child.type.isTreeNode)
+}
+
+export function isCheckDisabled (node) {
+ const { disabled, disableCheckbox } = node.props || {}
+ return !!(disabled || disableCheckbox)
+}
+
+export function traverseTreeNodes (treeNodes, subTreeData, callback) {
+ if (typeof subTreeData === 'function') {
+ callback = subTreeData
+ subTreeData = false
+ }
+
+ function processNode (node, index, parent) {
+ const children = node ? node.props.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,
+ }
+
+ // Children data is not must have
+ if (subTreeData) {
+ // Statistic children
+ const subNodes = []
+ Children.forEach(childList, (subNode, subIndex) => {
+ // Provide limit snapshot
+ const subPos = getPosition(pos, index)
+ subNodes.push({
+ node: subNode,
+ key: subNode.key || subPos,
+ pos: subPos,
+ index: subIndex,
+ })
+ })
+ data.subNodes = subNodes
+ }
+
+ // Can break traverse by return false
+ if (callback(data) === false) {
+ return
+ }
+ }
+
+ // Process children node
+ Children.forEach(childList, (subNode, subIndex) => {
+ processNode(subNode, subIndex, { node, pos })
+ })
+ }
+
+ processNode(null)
+}
+
+/**
+ * [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 } = node.props
+ 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 offsetTop = getOffset(treeNode.selectHandle).top
+ const offsetHeight = treeNode.selectHandle.offsetHeight
+ const pageY = event.pageY
+ const gapHeight = 2 // [Legacy] TODO: remove hard code
+ if (pageY > offsetTop + offsetHeight - gapHeight) {
+ return 1
+ }
+ if (pageY < offsetTop + gapHeight) {
+ 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) {
+ if (!keyList) {
+ return []
+ }
+
+ const { autoExpandParent, children } = props
+
+ // Do nothing if not auto expand parent
+ if (!autoExpandParent) {
+ return keyList
+ }
+
+ // 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]
+ if (isCheckDisabled(node)) return
+
+ tgtCheckedKeys[key] = true
+
+ // 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]),
+ }
+}
+
+/**
+ * Calculate the value of checked and halfChecked keys.
+ * This should be only run in init or props changed.
+ */
+export function calcCheckedKeys (keys, props) {
+ const { checkable, children, 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
+ }
+
+ // Do nothing if is checkStrictly mode
+ if (checkStrictly) {
+ return keyProps
+ }
+
+ // Conduct calculate the check status
+ const { checkedKeys = [] } = keyProps
+ return calcCheckStateConduct(children, checkedKeys)
+}