diff --git a/components/vc-tree/demo/basic.jsx b/components/vc-tree/demo/basic.jsx
new file mode 100644
index 000000000..322d26d96
--- /dev/null
+++ b/components/vc-tree/demo/basic.jsx
@@ -0,0 +1,110 @@
+/* eslint no-console:0 */
+/* eslint no-alert:0 */
+import PropTypes from '../../_util/vue-types'
+import Tree, { TreeNode } from '../index'
+import '../assets/index.less'
+import './basic.less'
+
+export default {
+ props: {
+ keys: PropTypes.array.def(['0-0-0-0']),
+ },
+ data () {
+ const keys = this.keys
+ return {
+ defaultExpandedKeys: keys,
+ defaultSelectedKeys: keys,
+ defaultCheckedKeys: keys,
+ switchIt: true,
+ showMore: false,
+ }
+ },
+ methods: {
+ onExpand (expandedKeys) {
+ console.log('onExpand', expandedKeys, arguments)
+ },
+ onSelect (selectedKeys, info) {
+ console.log('selected', selectedKeys, info)
+ this.selKey = info.node.$options.propsData.eventKey
+ },
+ onCheck (checkedKeys, info) {
+ console.log('onCheck', checkedKeys, info)
+ },
+ onEdit () {
+ setTimeout(() => {
+ console.log('current key: ', this.selKey)
+ }, 0)
+ },
+ onDel (e) {
+ if (!window.confirm('sure to delete?')) {
+ return
+ }
+ e.stopPropagation()
+ },
+ toggleChildren () {
+ this.showMore = !this.showMore
+ },
+ },
+
+ render () {
+ const customLabel = (
+ operations:
+ Edit
+
+ Delete
+ )
+ return (
+
simple
+ {/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ */}
+
+ Check on Click TreeNode
+
+
+
+
+
+
+
+ {this.showMore ?
+
+
+ : null}
+
+
+ )
+ },
+}
+
diff --git a/components/vc-tree/demo/basic.less b/components/vc-tree/demo/basic.less
new file mode 100644
index 000000000..cca0a1860
--- /dev/null
+++ b/components/vc-tree/demo/basic.less
@@ -0,0 +1,6 @@
+.rc-tree li a.rc-tree-node-selected{
+ .cus-label {
+ background-color: white;
+ border: none;
+ }
+}
diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx
index 53a1b9646..6c042a530 100644
--- a/components/vc-tree/src/Tree.jsx
+++ b/components/vc-tree/src/Tree.jsx
@@ -1,7 +1,9 @@
import PropTypes from '../../_util/vue-types'
import classNames from 'classnames'
import warning from 'warning'
-import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util'
+import { initDefaultProps, getOptionProps } from '../../_util/props-util'
+import { cloneElement } from '../../_util/vnode'
+import BaseMixin from '../../_util/BaseMixin'
import {
traverseTreeNodes, getStrictlyValue,
getFullKeyList, getPosition, getDragNodesKeys,
@@ -54,6 +56,8 @@ export const contextTypes = {
}
const Tree = {
+ name: 'Tree',
+ mixins: [BaseMixin],
props: initDefaultProps({
prefixCls: PropTypes.string,
showLine: PropTypes.bool,
@@ -79,9 +83,9 @@ const Tree = {
]),
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
selectedKeys: PropTypes.arrayOf(PropTypes.string),
- //onExpand: PropTypes.func,
- //onCheck: PropTypes.func,
- //onSelect: PropTypes.func,
+ // onExpand: PropTypes.func,
+ // onCheck: PropTypes.func,
+ // onSelect: PropTypes.func,
loadData: PropTypes.func,
// onMouseEnter: PropTypes.func,
// onMouseLeave: PropTypes.func,
@@ -95,6 +99,7 @@ const Tree = {
filterTreeNode: PropTypes.func,
openTransitionName: PropTypes.string,
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
+ children: PropTypes.any,
}, {
prefixCls: 'rc-tree',
showLine: false,
@@ -122,489 +127,476 @@ const Tree = {
defaultCheckedKeys,
defaultSelectedKeys,
} = props
-
+ const children = this.$slots.default
// Sync state with props
const { checkedKeys = [], halfCheckedKeys = [] } =
- calcCheckedKeys(defaultCheckedKeys, props) || {}
+ calcCheckedKeys(defaultCheckedKeys, props, children) || {}
// Cache for check status to optimize
this.checkedBatch = null
-
+ this.propsToStateMap = {
+ expandedKeys: 'sExpandedKeys',
+ selectedKeys: 'sSelectedKeys',
+ checkedKeys: 'sCheckedKeys',
+ halfCheckedKeys: 'sHalfCheckedKeys',
+ }
return {
sExpandedKeys: defaultExpandAll
- ? getFullKeyList(this.$slots.default)
- : calcExpandedKeys(defaultExpandedKeys, props),
- sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props),
- sCheckedKeys,
- sHalfCheckedKeys,
+ ? getFullKeyList(children)
+ : calcExpandedKeys(defaultExpandedKeys, props, children),
+ sSelectedKeys: calcSelectedKeys(defaultSelectedKeys, props, children),
+ sCheckedKeys: checkedKeys,
+ sHalfCheckedKeys: halfCheckedKeys,
...(this.getSyncProps(props) || {}),
- }
+ dragOverNodeKey: '',
+ dropPosition: null,
+ }
},
- provide: {
- rcTree: this,
- },
-
-
- componentWillReceiveProps (nextProps) {
- // React 16 will not trigger update if new state is null
- this.setState(this.getSyncProps(nextProps, this.props))
- },
-
- onNodeDragStart (event, node) {
- const { expandedKeys } = this.state
- const { onDragStart } = this.props
- const { eventKey, children } = node.props
-
- this.dragNode = node
-
- this.setState({
- dragNodesKeys: getDragNodesKeys(children, node),
- expandedKeys: arrDel(expandedKeys, eventKey),
- })
-
- if (onDragStart) {
- onDragStart({ event, node })
+ provide () {
+ return {
+ vcTree: this,
}
},
- /**
- * [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 } = this.state
- const { onDragEnter } = this.props
- const { pos, eventKey } = node.props
+ watch: {
+ children (val) {
+ const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(this.checkedKeys || this.sCheckedKeys, this.$props, this.$slots.default) || {}
+ this.sCheckedKeys = checkedKeys
+ this.sHalfCheckedKeys = halfCheckedKeys
+ },
+ expandedKeys (val) {
+ this.sExpandedKeys = calcExpandedKeys(this.expandedKeys, this.$props, this.$slots.default)
+ },
+ selectedKeys (val) {
+ this.sSelectedKeys = calcSelectedKeys(this.selectedKeys, this.$props, this.$slots.default)
+ },
+ checkedKeys (val) {
+ const { checkedKeys = [], halfCheckedKeys = [] } = calcCheckedKeys(this.checkedKeys, this.$props, this.$slots.default) || {}
+ this.sCheckedKeys = checkedKeys
+ this.sHalfCheckedKeys = halfCheckedKeys
+ },
+ },
- const dropPosition = calcDropPosition(event, node)
+ // componentWillReceiveProps (nextProps) {
+ // // React 16 will not trigger update if new state is null
+ // this.setState(this.getSyncProps(nextProps, this.props))
+ // },
+
+ methods: {
+ onNodeDragStart (event, node) {
+ const { sExpandedKeys } = this
+ const { eventKey, children } = node.props
+
+ this.dragNode = node
- // Skip if drag node is self
- if (
- this.dragNode.props.eventKey === eventKey &&
- dropPosition === 0
- ) {
this.setState({
- dragOverNodeKey: '',
- dropPosition: null,
+ dragNodesKeys: getDragNodesKeys(children, node),
+ sExpandedKeys: arrDel(sExpandedKeys, eventKey),
})
- return
- }
+ this.__emit('dragstart', { event, node })
+ },
- // 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,
- })
+ /**
+ * [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 { sExpandedKeys } = this
+ const { pos, eventKey } = node.props
- // 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)
+ const dropPosition = calcDropPosition(event, node)
+
+ // Skip if drag node is self
+ if (
+ this.dragNode.props.eventKey === eventKey &&
+ dropPosition === 0
+ ) {
this.setState({
- expandedKeys: newExpandedKeys,
+ 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,
})
- 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)
+ // 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(sExpandedKeys, eventKey)
+ this.setState({
+ sExpandedKeys: newExpandedKeys,
+ })
+ this.__emit('dragenter', { event, node, expandedKeys: newExpandedKeys })
+ }, 400)
+ }, 0)
+ },
+ onNodeDragOver (event, node) {
+ 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 })
+ },
+ onNodeDrop (event, node) {
+ const { dragNodesKeys, dropPosition } = this
- this.setUncontrolledState({ selectedKeys })
+ 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
+ }
+ this.__emit('drop', dropResult)
+ },
+
+ onNodeSelect (e, treeNode) {
+ const { sSelectedKeys, multiple, $slots: { default: children }} = this
+ const { selected, eventKey } = getOptionProps(treeNode)
+ const targetSelected = !selected
+ let selectedKeys = sSelectedKeys
+ // 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.__emit('select', 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 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: [],
+ // 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 { sCheckedKeys, sHalfCheckedKeys, checkStrictly, $slots: { default: children }} = this
+
+ // Use map to optimize update speed
+ const checkedKeySet = {}
+ const halfCheckedKeySet = {}
+
+ sCheckedKeys.forEach(key => {
+ checkedKeySet[key] = true
+ })
+ sHalfCheckedKeys.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,
+ })
+ }
+ this.__emit('check', selectedObj, eventObj)
+
+ // Clean up
+ this.checkedBatch = null
+ },
+
+ onNodeExpand (e, treeNode) {
+ const { sExpandedKeys, loadData } = this
+ let expandedKeys = [...sExpandedKeys]
+ const { eventKey, expanded } = getOptionProps(treeNode)
+
+ // Update selected keys
+ const index = expandedKeys.indexOf(eventKey)
+ const targetExpanded = !expanded
+
warning(
- false,
- 'Checked batch not init. This should be a bug. Please fire a issue.'
- )
- }
+ (expanded && index !== -1) || (!expanded && index === -1)
+ , 'Expand state not sync with index check')
- 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
+ if (targetExpanded) {
+ expandedKeys = arrAdd(expandedKeys, eventKey)
+ } else {
+ expandedKeys = arrDel(expandedKeys, eventKey)
}
- 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
- }
+ this.setUncontrolledState({ expandedKeys })
+ this.__emit('expand', expandedKeys, { node: treeNode, expanded: targetExpanded })
- if (checkSync('expandedKeys')) {
- newState.expandedKeys = calcExpandedKeys(props.expandedKeys, props)
- }
+ // Async Load data
+ if (targetExpanded && loadData) {
+ return loadData(treeNode).then(() => {
+ // [Legacy] Refresh logic
+ this.setUncontrolledState({ expandedKeys })
+ })
+ }
- if (checkSync('selectedKeys')) {
- newState.selectedKeys = calcSelectedKeys(props.selectedKeys, props)
- }
+ return null
+ },
- if (checkSync('checkedKeys')) {
- const { checkedKeys = [], halfCheckedKeys = [] } =
- calcCheckedKeys(props.checkedKeys, props) || {}
- newState.checkedKeys = checkedKeys
- newState.halfCheckedKeys = halfCheckedKeys
- }
+ onNodeMouseEnter (event, node) {
+ this.__emit('mouseenter', { event, node })
+ },
- return needSync ? newState : null
- };
+ onNodeMouseLeave (event, node) {
+ this.__emit('mouseleave', { event, node })
+ },
- /**
- * Only update the value which is not in props
- */
- setUncontrolledState = (state) => {
- let needSync = false
- const newState = {}
+ onNodeContextMenu (event, node) {
+ event.preventDefault()
+ this.__emit('rightClick', { event, node })
+ },
- Object.keys(state).forEach(name => {
- if (name in this.props) return
+ /**
+ * Sync state with props if needed
+ */
+ getSyncProps (props = {}, prevProps) {
+ let needSync = false
+ const newState = {}
+ const myPrevProps = prevProps || {}
+ const children = this.$slots.default
+ function checkSync (name) {
+ if (props[name] !== myPrevProps[name]) {
+ needSync = true
+ return true
+ }
+ return false
+ }
- needSync = true
- newState[name] = state[name]
- })
+ // 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.sCheckedKeys, props, children) || {}
+ newState.sCheckedKeys = checkedKeys
+ newState.sHalfCheckedKeys = halfCheckedKeys
+ }
- this.setState(needSync ? newState : null)
- };
+ if (checkSync('expandedKeys')) {
+ newState.sExpandedKeys = calcExpandedKeys(props.expandedKeys, props, children)
+ }
- isKeyChecked = (key) => {
- const { checkedKeys = [] } = this.state
- return checkedKeys.indexOf(key) !== -1
- };
+ if (checkSync('selectedKeys')) {
+ newState.sSelectedKeys = calcSelectedKeys(props.selectedKeys, props, children)
+ }
- /**
- * [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
+ if (checkSync('checkedKeys')) {
+ const { checkedKeys = [], halfCheckedKeys = [] } =
+ calcCheckedKeys(props.checkedKeys, props, children) || {}
+ newState.sCheckedKeys = checkedKeys
+ newState.sHalfCheckedKeys = halfCheckedKeys
+ }
- 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,
+ return needSync ? newState : null
+ },
- // [Legacy] Drag props
- dragOver: dragOverNodeKey === key && dropPosition === 0,
- dragOverGapTop: dragOverNodeKey === key && dropPosition === -1,
- dragOverGapBottom: dragOverNodeKey === key && dropPosition === 1,
- })
- };
+ /**
+ * 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 in props) return
+
+ needSync = true
+ const key = this.propsToStateMap[name]
+ newState[key] = state[name]
+ })
+
+ this.setState(needSync ? newState : null)
+ },
+
+ isKeyChecked (key) {
+ const { sCheckedKeys = [] } = this
+ return sCheckedKeys.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 {
+ sExpandedKeys = [], sSelectedKeys = [], sHalfCheckedKeys = [],
+ dragOverNodeKey, dropPosition,
+ } = this
+ const pos = getPosition(level, index)
+ const key = child.key || pos
+
+ return cloneElement(child, {
+ props: {
+ eventKey: key,
+ expanded: sExpandedKeys.indexOf(key) !== -1,
+ selected: sSelectedKeys.indexOf(key) !== -1,
+ checked: this.isKeyChecked(key),
+ halfChecked: sHalfCheckedKeys.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,
+ prefixCls, focusable,
showLine,
- children,
- } = this.props
+ $slots: { default: children = [] },
+ } = this
const domProps = {}
- // [Legacy] Commit: 0117f0c9db0e2956e92cb208f51a42387dfcb3d1
- if (focusable) {
- domProps.tabIndex = '0'
- domProps.onKeyDown = this.onKeyDown
- }
-
return (
{}}
>
- {React.Children.map(children, this.renderTreeNode, this)}
+ {children.map(this.renderTreeNode)}
)
- }
+ },
}
export default Tree
diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx
index 155585404..fb5ce8a86 100644
--- a/components/vc-tree/src/TreeNode.jsx
+++ b/components/vc-tree/src/TreeNode.jsx
@@ -4,6 +4,8 @@ import warning from 'warning'
import { contextTypes } from './Tree'
import { getPosition, getNodeChildren, isCheckDisabled, traverseTreeNodes } from './util'
import { initDefaultProps, getOptionProps, filterEmpty } from '../../_util/props-util'
+import BaseMixin from '../../_util/BaseMixin'
+import getTransitionProps from '../../_util/getTransitionProps'
const ICON_OPEN = 'open'
const ICON_CLOSE = 'close'
@@ -19,12 +21,14 @@ let onlyTreeNodeWarned = false // Only accept TreeNode
export const nodeContextTypes = {
...contextTypes,
- rcTreeNode: PropTypes.shape({
+ vcTreeNode: PropTypes.shape({
onUpCheckConduct: PropTypes.func,
}),
}
const TreeNode = {
+ name: 'TreeNode',
+ mixins: [BaseMixin],
props: initDefaultProps({
eventKey: PropTypes.string, // Pass by parent `cloneElement`
prefixCls: PropTypes.string,
@@ -37,7 +41,7 @@ const TreeNode = {
selected: PropTypes.bool,
checked: PropTypes.bool,
halfChecked: PropTypes.bool,
- title: PropTypes.node,
+ title: PropTypes.any,
pos: PropTypes.string,
dragOver: PropTypes.bool,
dragOverGapTop: PropTypes.bool,
@@ -60,500 +64,500 @@ const TreeNode = {
}
},
inject: {
- context: { default: {}},
+ vcTree: { default: {}},
},
- provide: {
- ...this.context,
- rcTreeNode: this,
+ provide () {
+ return {
+ vcTree: this.vcTree,
+ vcTreeNode: this,
+ }
},
// Isomorphic needn't load data in server side
mounted () {
- this.$nextTick(() => {
- this.syncLoadData(this.$props)
- })
+ this.syncLoadData(this.$props)
+ },
+ watch: {
+ expanded (val) {
+ this.syncLoadData({ expanded: val })
+ },
},
- componentWillReceiveProps (nextProps) {
- this.syncLoadData(nextProps)
- },
+ methods: {
+ onUpCheckConduct (treeNode, nodeChecked, nodeHalfChecked) {
+ const { pos: nodePos } = getOptionProps(treeNode)
+ const { eventKey, pos, checked, halfChecked } = this
+ const {
+ vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck, onCheckConductFinished },
+ vcTreeNode: { onUpCheckConduct } = {},
+ } = this
- 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)) {
+ // Stop conduct when current node is disabled
+ if (isCheckDisabled(this)) {
+ onCheckConductFinished()
return
}
- if (isKeyChecked(node.key || childPos)) {
- checkedCount += 1
- }
- })
+ const children = this.getNodeChildren()
- // Static enabled children count
- const enabledChildrenCount = children
- .filter(node => !isCheckDisabled(node))
- .length
+ let checkedCount = nodeChecked ? 1 : 0
- // checkStrictly will not conduct check status
- const nextChecked = checkStrictly ? checked : enabledChildrenCount === checkedCount
- const nextHalfChecked = checkStrictly // propagated or child checked
- ? halfChecked : (nodeHalfChecked || (checkedCount > 0 && !nextChecked))
+ // Statistic checked count
+ children.forEach((node, index) => {
+ const childPos = getPosition(pos, index)
- // Add into batch update
- if (checked !== nextChecked || halfChecked !== nextHalfChecked) {
- onBatchNodeCheck(eventKey, nextChecked, nextHalfChecked)
+ if (nodePos === childPos || isCheckDisabled(node)) {
+ return
+ }
+ if (isKeyChecked(node.key || childPos)) {
+ checkedCount += 1
+ }
+ })
- if (onUpCheckConduct) {
- onUpCheckConduct(this, nextChecked, nextHalfChecked)
+ // 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()
}
- } 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
+ onDownCheckConduct (nodeChecked) {
+ const { $slots } = this
+ const children = $slots.default || []
+ const { vcTree: { checkStrictly, isKeyChecked, onBatchNodeCheck }} = this
+ if (checkStrictly) return
- traverseTreeNodes(children, ({ node, key }) => {
- if (isCheckDisabled(node)) return false
+ traverseTreeNodes(children, ({ node, key }) => {
+ if (isCheckDisabled(node)) return false
- if (nodeChecked !== isKeyChecked(key)) {
- onBatchNodeCheck(key, nodeChecked, false)
+ if (nodeChecked !== isKeyChecked(key)) {
+ onBatchNodeCheck(key, nodeChecked, false)
+ }
+ })
+ },
+
+ onSelectorClick (e) {
+ if (this.isSelectable()) {
+ this.onSelect(e)
+ } else {
+ this.onCheck(e)
}
- })
- },
+ },
- onSelectorClick (e) {
- if (this.isSelectable()) {
- this.onSelect(e)
- } else {
- this.onCheck(e)
- }
- },
+ onSelect (e) {
+ if (this.isDisabled()) return
- onSelect (e) {
- if (this.isDisabled()) return
+ const { vcTree: { onNodeSelect }} = this
+ e.preventDefault()
+ onNodeSelect(e, this)
+ },
- const { rcTree: { onNodeSelect }} = this.context
- e.preventDefault()
- onNodeSelect(e, this)
- },
+ onCheck (e) {
+ if (this.isDisabled()) return
- onCheck (e) {
- if (this.isDisabled()) return
+ const { disableCheckbox, checked, eventKey } = this
+ const {
+ vcTree: { checkable, onBatchNodeCheck, onCheckConductFinished },
+ vcTreeNode: { onUpCheckConduct } = {},
+ } = this
- const { disableCheckbox, checked, eventKey } = this
- const {
- rcTree: { checkable, onBatchNodeCheck, onCheckConductFinished },
- rcTreeNode: { onUpCheckConduct } = {},
- } = this.context
+ if (!checkable || disableCheckbox) return
- if (!checkable || disableCheckbox) return
+ e.preventDefault()
+ const targetChecked = !checked
+ onBatchNodeCheck(eventKey, targetChecked, false, this)
- e.preventDefault()
- const targetChecked = !checked
- onBatchNodeCheck(eventKey, targetChecked, false, this)
+ // Children conduct
+ this.onDownCheckConduct(targetChecked)
- // Children conduct
- this.onDownCheckConduct(targetChecked)
+ // Parent conduct
+ if (onUpCheckConduct) {
+ onUpCheckConduct(this, targetChecked, false)
+ } else {
+ onCheckConductFinished()
+ }
+ },
- // Parent conduct
- if (onUpCheckConduct) {
- onUpCheckConduct(this, targetChecked, false)
- } else {
- onCheckConductFinished()
- }
- },
+ onMouseEnter (e) {
+ const { vcTree: { onNodeMouseEnter }} = this
+ onNodeMouseEnter(e, this)
+ },
- onMouseEnter (e) {
- const { rcTree: { onNodeMouseEnter }} = this.context
- onNodeMouseEnter(e, this)
- },
+ onMouseLeave (e) {
+ const { vcTree: { onNodeMouseLeave }} = this
+ onNodeMouseLeave(e, this)
+ },
- onMouseLeave (e) {
- const { rcTree: { onNodeMouseLeave }} = this.context
- onNodeMouseLeave(e, this)
- },
+ onContextMenu (e) {
+ const { vcTree: { onNodeContextMenu }} = this
+ onNodeContextMenu(e, this)
+ },
- onContextMenu (e) {
- const { rcTree: { onNodeContextMenu }} = this.context
- onNodeContextMenu(e, this)
- },
+ onDragStart (e) {
+ const { vcTree: { onNodeDragStart }} = 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 })
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: true,
})
- }
- },
+ onNodeDragStart(e, this)
- // Drag usage
- setSelectHandle (node) {
- this.selectHandle = node
- },
+ try {
+ // ie throw error
+ // firefox-need-it
+ e.dataTransfer.setData('text/plain', '')
+ } catch (error) {
+ // empty
+ }
+ },
- getNodeChildren () {
- const { $slots: { default: children }} = this
- const originList = filterEmpty(children)
- const targetList = getNodeChildren(originList)
+ onDragEnter (e) {
+ const { vcTree: { onNodeDragEnter }} = this
- if (originList.length !== targetList.length && !onlyTreeNodeWarned) {
- onlyTreeNodeWarned = true
- warning(false, 'Tree only accept TreeNode as children.')
- }
+ e.preventDefault()
+ e.stopPropagation()
+ onNodeDragEnter(e, this)
+ },
- return targetList
- },
+ onDragOver (e) {
+ const { vcTree: { onNodeDragOver }} = this
- getNodeState () {
- const { expanded } = this
+ e.preventDefault()
+ e.stopPropagation()
+ onNodeDragOver(e, this)
+ },
- if (this.isLeaf()) {
- return null
- }
+ onDragLeave (e) {
+ const { vcTree: { onNodeDragLeave }} = this
- return expanded ? ICON_OPEN : ICON_CLOSE
- },
+ e.stopPropagation()
+ onNodeDragLeave(e, this)
+ },
- isLeaf () {
- const { isLeaf, loadStatus } = this
- const { rcTree: { loadData }} = this.context
+ onDragEnd (e) {
+ const { vcTree: { onNodeDragEnd }} = this
- 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 })
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: false,
})
- }
- },
+ onNodeDragEnd(e, this)
+ },
- // Switcher
- renderSwitcher () {
- const { expanded } = this
- const { rcTree: { prefixCls }} = this.context
+ onDrop (e) {
+ const { vcTree: { onNodeDrop }} = this
- if (this.isLeaf()) {
- return
- }
+ e.preventDefault()
+ e.stopPropagation()
+ this.setState({
+ dragNodeHighlight: false,
+ })
+ onNodeDrop(e, this)
+ },
- return (
-
- )
- },
+ // Disabled item still can be switch
+ onExpand (e) {
+ const { vcTree: { onNodeExpand }} = this
+ const callbackPromise = onNodeExpand(e, this)
- // Checkbox
- renderCheckbox () {
- const { checked, halfChecked, disableCheckbox } = this
- const { rcTree: { prefixCls, checkable }} = this.context
- const disabled = this.isDisabled()
+ // Promise like
+ if (callbackPromise && callbackPromise.then) {
+ this.setState({ loadStatus: LOAD_STATUS_LOADING })
- if (!checkable) return null
+ callbackPromise.then(() => {
+ this.setState({ loadStatus: LOAD_STATUS_LOADED })
+ }).catch(() => {
+ this.setState({ loadStatus: LOAD_STATUS_FAILED })
+ })
+ }
+ },
- // [Legacy] Custom element should be separate with `checkable` in future
- const $custom = typeof checkable !== 'boolean' ? checkable : null
+ // Drag usage
+ setSelectHandle (node) {
+ this.selectHandle = node
+ },
- return (
-
- {$custom}
-
- )
- },
+ getNodeChildren () {
+ const { $slots: { default: children }} = this
+ const originList = filterEmpty(children)
+ const targetList = getNodeChildren(originList)
- renderIcon () {
- const { loadStatus } = this
- const { rcTree: { prefixCls }} = this.context
+ if (originList.length !== targetList.length && !onlyTreeNodeWarned) {
+ onlyTreeNodeWarned = true
+ warning(false, 'Tree only accept TreeNode as children.')
+ }
- return (
-
- )
- },
+ return targetList
+ },
- // Icon + Title
- renderSelector () {
- const { title, selected, icon, loadStatus, dragNodeHighlight } = this
- const { rcTree: { prefixCls, showIcon, draggable, loadData }} = this.context
- const disabled = this.isDisabled()
+ getNodeState () {
+ const { expanded } = this
- const wrapClass = `${prefixCls}-node-content-wrapper`
+ if (this.isLeaf2()) {
+ return null
+ }
- // Icon - Still show loading icon when loading without showIcon
- let $icon
+ return expanded ? ICON_OPEN : ICON_CLOSE
+ },
- if (showIcon) {
- $icon = icon ? (
+ isLeaf2 () {
+ const { isLeaf, loadStatus } = this
+ const { vcTree: { loadData }} = this
+
+ const hasChildren = this.getNodeChildren().length !== 0
+
+ return (
+ isLeaf ||
+ (!loadData && !hasChildren) ||
+ (loadData && loadStatus === LOAD_STATUS_LOADED && !hasChildren)
+ )
+ },
+
+ isDisabled () {
+ const { disabled } = this
+ const { vcTree: { disabled: treeDisabled }} = this
+
+ // Follow the logic of Selectable
+ if (disabled === false) {
+ return false
+ }
+
+ return !!(treeDisabled || disabled)
+ },
+
+ isSelectable () {
+ const { selectable } = this
+ const { vcTree: { selectable: treeSelectable }} = this
+
+ // 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 { vcTree: { loadData }} = this
+
+ if (loadData && loadStatus === LOAD_STATUS_NONE && expanded && !this.isLeaf2()) {
+ 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 { vcTree: { prefixCls }} = this
+
+ if (this.isLeaf2()) {
+ return
+ }
+
+ return (
+
+ )
+ },
+
+ // Checkbox
+ renderCheckbox () {
+ const { checked, halfChecked, disableCheckbox } = this
+ const { vcTree: { prefixCls, checkable }} = this
+ 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 { vcTree: { prefixCls }} = this
+
+ return (
- {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}
-
- )
+ // Icon + Title
+ renderSelector () {
+ const { title, selected, icon, loadStatus, dragNodeHighlight } = this
+ const { vcTree: { prefixCls, showIcon, draggable, loadData }} = this
+ 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 { vcTree: {
+ prefixCls,
+ openTransitionName, openAnimation,
+ renderTreeNode,
+ }} = this
+
+ // [Legacy] Animation control
+ const renderFirst = this.renderFirst
+ this.renderFirst = 1
+ let transitionAppear = true
+ if (!renderFirst && expanded) {
+ transitionAppear = false
+ }
+
+ let animProps = {}
+ if (openTransitionName) {
+ animProps = getTransitionProps(openTransitionName, { appear: transitionAppear })
+ } else if (typeof openAnimation === 'object') {
+ animProps = { ...openAnimation }
+ if (!transitionAppear) {
+ delete animProps.props.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: {
+ const { vcTree: {
prefixCls,
filterTreeNode,
- }} = this.context
+ }} = this
const disabled = this.isDisabled()
return (
diff --git a/components/vc-tree/src/index.js b/components/vc-tree/src/index.js
index d053f4c9f..e8c0e81b5 100644
--- a/components/vc-tree/src/index.js
+++ b/components/vc-tree/src/index.js
@@ -1,6 +1,25 @@
+import { getOptionProps } from '../../_util/props-util'
import Tree from './Tree'
import TreeNode from './TreeNode'
Tree.TreeNode = TreeNode
+//
+const NewTree = {
+ TreeNode: TreeNode,
+ props: Tree.props,
+ render () {
+ const { $listeners, $slots } = this
+ const treeProps = {
+ props: {
+ ...getOptionProps(this),
+ children: $slots.default,
+ },
+ on: $listeners,
+ }
+ return (
+ {$slots.default}
+ )
+ },
+}
export { TreeNode }
-export default Tree
+export default NewTree
diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js
index 41e39ed08..712853249 100644
--- a/components/vc-tree/src/util.js
+++ b/components/vc-tree/src/util.js
@@ -1,6 +1,6 @@
/* eslint no-loop-func: 0*/
-import { Children } from 'react'
import warning from 'warning'
+import { getSlotOptions, getOptionProps } from '../../_util/props-util'
export function arrDel (list, value) {
const clone = list.slice()
@@ -48,14 +48,13 @@ 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 getNodeChildren (children = []) {
+ return children
+ .filter(child => getSlotOptions(child).isTreeNode)
}
export function isCheckDisabled (node) {
- const { disabled, disableCheckbox } = node.props || {}
+ const { disabled, disableCheckbox } = getOptionProps(node) || {}
return !!(disabled || disableCheckbox)
}
@@ -66,7 +65,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) {
}
function processNode (node, index, parent) {
- const children = node ? node.props.children : treeNodes
+ const children = node ? node.componentOptions.children : treeNodes
const pos = node ? getPosition(parent.pos, index) : 0
// Filter children
@@ -86,7 +85,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) {
if (subTreeData) {
// Statistic children
const subNodes = []
- Children.forEach(childList, (subNode, subIndex) => {
+ childList.forEach((subNode, subIndex) => {
// Provide limit snapshot
const subPos = getPosition(pos, index)
subNodes.push({
@@ -106,7 +105,7 @@ export function traverseTreeNodes (treeNodes, subTreeData, callback) {
}
// Process children node
- Children.forEach(childList, (subNode, subIndex) => {
+ childList.forEach((subNode, subIndex) => {
processNode(subNode, subIndex, { node, pos })
})
}
@@ -182,7 +181,7 @@ export function getNodesStatistic (treeNodes) {
}
export function getDragNodesKeys (treeNodes, node) {
- const { eventKey, pos } = node.props
+ const { eventKey, pos } = getOptionProps(node)
const dragNodesKeys = []
traverseTreeNodes(treeNodes, ({ pos: nodePos, key }) => {
@@ -214,12 +213,12 @@ export function calcDropPosition (event, treeNode) {
* @param props
* @returns [string]
*/
-export function calcExpandedKeys (keyList, props) {
+export function calcExpandedKeys (keyList, props, children = []) {
if (!keyList) {
return []
}
- const { autoExpandParent, children } = props
+ const { autoExpandParent } = props
// Do nothing if not auto expand parent
if (!autoExpandParent) {
@@ -363,8 +362,8 @@ export function calcCheckStateConduct (treeNodes, checkedKeys) {
* 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
+export function calcCheckedKeys (keys, props, children = []) {
+ const { checkable, checkStrictly } = props
if (!checkable || !keys) {
return null
diff --git a/examples/routes.js b/examples/routes.js
index 73e054488..41b6a33fc 100644
--- a/examples/routes.js
+++ b/examples/routes.js
@@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
- component: import(`../components/popconfirm/demo/${d}`),
+ component: import(`../components/vc-tree/demo/${d}`),
}
}
export default [