+
Please select
`;
-exports[`renders ./components/tree-select/demo/suffix.md correctly 1`] = `
Please select`;
+exports[`renders ./components/tree-select/demo/suffix.md correctly 1`] = `
Please select`;
-exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `
Please select`;
+exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `
Please select`;
diff --git a/components/tree-select/__tests__/index.test.js b/components/tree-select/__tests__/index.test.js
index ca6174a1e..b185d68f3 100644
--- a/components/tree-select/__tests__/index.test.js
+++ b/components/tree-select/__tests__/index.test.js
@@ -1,6 +1,8 @@
import TreeSelect from '..';
import focusTest from '../../../tests/shared/focusTest';
+import mountTest from '../../../tests/shared/mountTest';
describe('TreeSelect', () => {
focusTest(TreeSelect);
+ mountTest(TreeSelect);
});
diff --git a/components/tree-select/demo/async.md b/components/tree-select/demo/async.md
new file mode 100644
index 000000000..34742dc3c
--- /dev/null
+++ b/components/tree-select/demo/async.md
@@ -0,0 +1,69 @@
+
+#### 异步加载
+异步加载树节点。
+
+
+
+#### Asynchronous loading
+Asynchronous loading tree node.
+
+
+```tpl
+
+
+
+
+
+```
diff --git a/components/tree-select/demo/basic.md b/components/tree-select/demo/basic.md
index a80eeabc7..9e8041ad0 100644
--- a/components/tree-select/demo/basic.md
+++ b/components/tree-select/demo/basic.md
@@ -12,13 +12,12 @@ The most basic usage.
@@ -42,12 +41,6 @@ The most basic usage.
value: undefined,
};
},
- methods: {
- onChange(value) {
- console.log(value);
- this.value = value;
- },
- },
};
```
diff --git a/components/tree-select/demo/checkable.md b/components/tree-select/demo/checkable.md
index 579b3f21d..83ecafee3 100644
--- a/components/tree-select/demo/checkable.md
+++ b/components/tree-select/demo/checkable.md
@@ -11,10 +11,9 @@ Multiple and checkable.
```tpl
```
diff --git a/components/tree-select/demo/index.vue b/components/tree-select/demo/index.vue
index dca769b62..1b06773cb 100644
--- a/components/tree-select/demo/index.vue
+++ b/components/tree-select/demo/index.vue
@@ -4,6 +4,7 @@ import Checkable from './checkable';
import Multiple from './multiple';
import TreeData from './treeData';
import Suffix from './suffix';
+import Async from './async';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
@@ -28,15 +29,32 @@ export default {
subtitle: '树选择',
type: 'Data Entry',
title: 'TreeSelect',
+ data() {
+ return {
+ show: true,
+ };
+ },
render() {
return (
+
-
-
-
-
-
+ {this.show ? (
+
+ ) : null}
diff --git a/components/tree-select/demo/multiple.md b/components/tree-select/demo/multiple.md
index f4afcb9b7..cfaebb37a 100644
--- a/components/tree-select/demo/multiple.md
+++ b/components/tree-select/demo/multiple.md
@@ -12,7 +12,7 @@ Multiple selection usage.
@@ -42,12 +41,6 @@ The most basic usage.
value: undefined,
};
},
- methods: {
- onChange(value) {
- console.log(value);
- this.value = value;
- },
- },
};
```
diff --git a/components/tree-select/demo/treeData.md b/components/tree-select/demo/treeData.md
index 03d628b3d..370f6831b 100644
--- a/components/tree-select/demo/treeData.md
+++ b/components/tree-select/demo/treeData.md
@@ -11,7 +11,7 @@ The tree structure can be populated using `treeData` property. This is a quick a
```tpl
We recommend you to use `treeData` rather than `TreeNode`, to avoid the trouble of manual construction.
-| Property | Description | Type | Default |
-| --- | --- | --- | --- |
-| selectable | can be selected | boolean | true |
-| disableCheckbox | Disables the checkbox of the treeNode | boolean | false |
-| disabled | Disabled or not | boolean | false |
-| isLeaf | Leaf node or not | boolean | false |
-| key | Required property, should be unique in the tree | string \| number | - |
-| title | Content showed on the treeNodes | string\|slot | '---' |
-| value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | string | - |
-| scopedSlots | When using treeNodes, you can use this property to configure the properties that support the slot, such as `scopedSlots: { title: 'XXX'}` | object | - |
+| Property | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| selectable | can be selected | boolean | true | |
+| checkable | When Tree is checkable, set TreeNode display Checkbox or not | boolean | - | 1.5.0 |
+| disableCheckbox | Disables the checkbox of the treeNode | boolean | false | |
+| disabled | Disabled or not | boolean | false | |
+| isLeaf | Leaf node or not | boolean | false | |
+| key | Required property, should be unique in the tree | string \| number | - | |
+| title | Content showed on the treeNodes | string\|slot | '---' | |
+| value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | string | - | |
+| scopedSlots | When using treeNodes, you can use this property to configure the properties that support the slot, such as `scopedSlots: { title: 'XXX'}` | object | - | |
+
+## FAQ
+
+### How to get parent node in onChange?
+
+We don't provide this since performance consideration.
diff --git a/components/tree-select/index.jsx b/components/tree-select/index.jsx
index f3235d7e4..d65d6afa2 100644
--- a/components/tree-select/index.jsx
+++ b/components/tree-select/index.jsx
@@ -7,7 +7,6 @@ import {
getOptionProps,
getComponentFromProp,
filterEmpty,
- isValidElement,
getListeners,
} from '../_util/props-util';
import { ConfigConsumerProps } from '../config-provider';
@@ -16,7 +15,6 @@ import Base from '../base';
export { TreeData, TreeSelectProps } from './interface';
import Icon from '../icon';
import omit from 'omit.js';
-import { cloneElement } from '../_util/vnode';
const TreeSelect = {
TreeNode: { ...TreeNode, name: 'ATreeSelectNode' },
@@ -39,6 +37,7 @@ const TreeSelect = {
created() {
warning(
this.multiple !== false || !this.treeCheckable,
+ 'TreeSelect',
'`multiple` will alway be `true` when `treeCheckable` is true',
);
},
@@ -102,6 +101,8 @@ const TreeSelect = {
const renderEmpty = this.configProvider.renderEmpty;
const notFoundContent = getComponentFromProp(this, 'notFoundContent');
+ const removeIcon = getComponentFromProp(this, 'removeIcon');
+ const clearIcon = getComponentFromProp(this, 'clearIcon');
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
const rest = omit(restProps, [
'inputIcon',
@@ -121,27 +122,33 @@ const TreeSelect = {
[`${prefixCls}-sm`]: size === 'small',
};
+ // showSearch: single - false, multiple - true
+ let { showSearch } = restProps;
+ if (!('showSearch' in restProps)) {
+ showSearch = !!(restProps.multiple || restProps.treeCheckable);
+ }
+
let checkable = getComponentFromProp(this, 'treeCheckable');
if (checkable) {
checkable = ;
}
- const inputIcon = (suffixIcon &&
- (isValidElement(suffixIcon) ? cloneElement(suffixIcon) : suffixIcon)) || (
-
+ const inputIcon = suffixIcon || ;
+
+ const finalRemoveIcon = removeIcon || ;
+
+ const finalClearIcon = clearIcon || (
+
);
-
- const removeIcon = ;
-
- const clearIcon = ;
const VcTreeSelectProps = {
props: Object.assign(
{
switcherIcon: nodeProps => this.renderSwitcherIcon(prefixCls, nodeProps),
inputIcon,
- removeIcon,
- clearIcon,
+ removeIcon: finalRemoveIcon,
+ clearIcon: finalClearIcon,
...rest,
+ showSearch,
getPopupContainer: getPopupContainer || getContextPopupContainer,
dropdownClassName: classNames(dropdownClassName, `${prefixCls}-tree-dropdown`),
prefixCls,
diff --git a/components/tree-select/index.zh-CN.md b/components/tree-select/index.zh-CN.md
index 3c21d2806..e6c496bd4 100644
--- a/components/tree-select/index.zh-CN.md
+++ b/components/tree-select/index.zh-CN.md
@@ -56,13 +56,14 @@
> 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦
-| 参数 | 说明 | 类型 | 默认值 |
-| --- | --- | --- | --- |
-| selectable | 是否可选 | boolean | true |
-| disableCheckbox | 禁掉 checkbox | boolean | false |
-| disabled | 是否禁用 | boolean | false |
-| isLeaf | 是否是叶子节点 | boolean | false |
-| key | 此项必须设置(其值在整个树范围内唯一) | string \| number | - |
-| title | 树节点显示的内容 | string\|slot | '---' |
-| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - |
-| scopedSlots | 使用 treeData 时,可以通过该属性配置支持 slot 的属性,如 `scopedSlots: { title: 'XXX'}` | object | - |
+| 参数 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| selectable | 是否可选 | boolean | true | |
+| checkable | 当树为 checkable 时,设置独立节点是否展示 Checkbox | boolean | - | 1.5.0 |
+| disableCheckbox | 禁掉 checkbox | boolean | false | |
+| disabled | 是否禁用 | boolean | false | |
+| isLeaf | 是否是叶子节点 | boolean | false | |
+| key | 此项必须设置(其值在整个树范围内唯一) | string \| number | - | |
+| title | 树节点显示的内容 | string\|slot | '---' | |
+| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - | |
+| scopedSlots | 使用 treeData 时,可以通过该属性配置支持 slot 的属性,如 `scopedSlots: { title: 'XXX'}` | object | - | |
diff --git a/components/vc-tree-select/index.js b/components/vc-tree-select/index.js
index a760bfc0c..b6bc82af5 100644
--- a/components/vc-tree-select/index.js
+++ b/components/vc-tree-select/index.js
@@ -1,5 +1,5 @@
// export this package's api
-// base 2.5.4
+// base 2.9.3
import Vue from 'vue';
import TreeSelect from './src';
import ref from 'vue-ref';
diff --git a/components/vc-tree-select/src/Base/BasePopup.jsx b/components/vc-tree-select/src/Base/BasePopup.jsx
index ea8422b9f..8494e6d5b 100644
--- a/components/vc-tree-select/src/Base/BasePopup.jsx
+++ b/components/vc-tree-select/src/Base/BasePopup.jsx
@@ -2,6 +2,7 @@ import warning from 'warning';
import PropTypes from '../../../_util/vue-types';
import { Tree } from '../../../vc-tree';
import BaseMixin from '../../../_util/BaseMixin';
+import { createRef } from '../util';
// export const popupContextTypes = {
// onPopupKeyDown: PropTypes.func.isRequired,
@@ -110,6 +111,7 @@ const BasePopup = {
},
},
data() {
+ this.treeRef = createRef();
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__');
const { treeDefaultExpandAll, treeDefaultExpandedKeys, keyEntities } = this.$props;
@@ -150,6 +152,10 @@ const BasePopup = {
this.setState({ _loadedKeys: loadedKeys });
},
+ getTree() {
+ return this.treeRef.current;
+ },
+
/**
* Not pass `loadData` when searching. To avoid loop ajax call makes browser crash.
*/
@@ -231,7 +237,7 @@ const BasePopup = {
} else {
$notFound = this.renderNotFound();
}
- } else if (!treeNodes.length) {
+ } else if (!treeNodes || !treeNodes.length) {
$notFound = this.renderNotFound();
} else {
$treeNodes = treeNodes;
@@ -265,6 +271,12 @@ const BasePopup = {
expand: this.onTreeExpand,
load: this.onLoad,
},
+ directives: [
+ {
+ name: 'ant-ref',
+ value: this.treeRef,
+ },
+ ],
};
$tree = ;
}
diff --git a/components/vc-tree-select/src/Popup/SinglePopup.jsx b/components/vc-tree-select/src/Popup/SinglePopup.jsx
index 3faf5fc5b..b11cb43ef 100644
--- a/components/vc-tree-select/src/Popup/SinglePopup.jsx
+++ b/components/vc-tree-select/src/Popup/SinglePopup.jsx
@@ -17,12 +17,16 @@ const SinglePopup = {
},
created() {
this.inputRef = createRef();
+ this.searchRef = createRef();
+ this.popupRef = createRef();
},
methods: {
onPlaceholderClick() {
this.inputRef.current.focus();
},
-
+ getTree() {
+ return this.popupRef.current && this.popupRef.current.getTree();
+ },
_renderPlaceholder() {
const { searchPlaceholder, searchValue, prefixCls } = this.$props;
@@ -51,7 +55,17 @@ const SinglePopup = {
}
return (
-
+
);
diff --git a/components/vc-tree-select/src/Select.jsx b/components/vc-tree-select/src/Select.jsx
index 7338b78a3..86ca875ff 100644
--- a/components/vc-tree-select/src/Select.jsx
+++ b/components/vc-tree-select/src/Select.jsx
@@ -21,6 +21,7 @@
import shallowEqual from 'shallowequal';
import raf from 'raf';
+import scrollIntoView from 'dom-scroll-into-view';
import warning from 'warning';
import PropTypes from '../../_util/vue-types';
import KeyCode from '../../_util/KeyCode';
@@ -48,6 +49,7 @@ import {
isLabelInValue,
getFilterTree,
cleanEntity,
+ findPopupContainer,
} from './util';
import SelectNode from './SelectNode';
import {
@@ -222,6 +224,37 @@ const Select = {
this.forcePopupAlign();
});
},
+ '$data._open': function() {
+ this.$nextTick(() => {
+ const { prefixCls } = this.$props;
+ const { _selectorValueList: selectorValueList, _valueEntities: valueEntities } = this.$data;
+ const isMultiple = this.isMultiple();
+
+ // Scroll to value position, only need sync on single mode
+ if (!isMultiple && selectorValueList.length && this.popup) {
+ const { value } = selectorValueList[0];
+ const { domTreeNodes } = this.popup.getTree();
+ const { key } = valueEntities[value] || {};
+ const treeNode = domTreeNodes[key];
+
+ if (treeNode) {
+ const domNode = treeNode.$el;
+ raf(() => {
+ const popupNode = this.popup.$el;
+ const triggerContainer = findPopupContainer(popupNode, `${prefixCls}-dropdown`);
+ const searchNode = this.popup.searchRef.current;
+
+ if (domNode && triggerContainer && searchNode) {
+ scrollIntoView(domNode, triggerContainer, {
+ onlyScrollIfNeeded: true,
+ offsetTop: searchNode.offsetHeight,
+ });
+ }
+ });
+ }
+ }
+ });
+ },
},
mounted() {
this.$nextTick(() => {
@@ -342,9 +375,11 @@ const Select = {
}
// Get key by value
+ const valueLabels = {};
latestValueList.forEach(wrapperValue => {
- const { value } = wrapperValue;
+ const { value, label } = wrapperValue;
const entity = (newState._valueEntities || prevState._valueEntities)[value];
+ valueLabels[value] = label;
if (entity) {
keyList.push(entity.key);
@@ -366,9 +401,19 @@ const Select = {
);
// Format value list again for internal usage
- newState._valueList = checkedKeys.map(key => ({
- value: (newState._keyEntities || prevState._keyEntities).get(key).value,
- }));
+ newState._valueList = checkedKeys.map(key => {
+ const val = (newState._keyEntities || prevState._keyEntities).get(key).value;
+
+ const wrappedValue = {
+ value: val,
+ };
+
+ if (valueLabels[val] !== undefined) {
+ wrappedValue.label = valueLabels[val];
+ }
+
+ return wrappedValue;
+ });
} else {
newState._valueList = filteredValueList;
}
@@ -422,6 +467,7 @@ const Select = {
searchValue,
filterTreeNodeFn,
newState._valueEntities || prevState._valueEntities,
+ SelectNode,
);
}
@@ -827,6 +873,7 @@ const Select = {
value,
filterTreeNodeFn,
valueEntities,
+ SelectNode,
),
});
}
@@ -844,7 +891,13 @@ const Select = {
},
onChoiceAnimationLeave() {
- this.forcePopupAlign();
+ raf(() => {
+ this.forcePopupAlign();
+ });
+ },
+
+ setPopupRef(popup) {
+ this.popup = popup;
},
/**
@@ -1051,6 +1104,12 @@ const Select = {
on: {
treeExpanded: this.delayForcePopupAlign,
},
+ directives: [
+ {
+ name: 'ant-ref',
+ value: this.setPopupRef,
+ },
+ ],
});
const Popup = isMultiple ? MultiplePopup : SinglePopup;
diff --git a/components/vc-tree-select/src/Selector/MultipleSelector/SelectorList.jsx b/components/vc-tree-select/src/Selector/MultipleSelector/SelectorList.jsx
new file mode 100644
index 000000000..e69de29bb
diff --git a/components/vc-tree-select/src/util.js b/components/vc-tree-select/src/util.js
index d737b3dbf..c8b6ea133 100644
--- a/components/vc-tree-select/src/util.js
+++ b/components/vc-tree-select/src/util.js
@@ -5,12 +5,25 @@ import {
convertTreeToEntities as vcConvertTreeToEntities,
conductCheck as rcConductCheck,
} from '../../vc-tree/src/util';
-import SelectNode from './SelectNode';
+import { hasClass } from '../../vc-util/Dom/class';
import { SHOW_CHILD, SHOW_PARENT } from './strategies';
import { getSlots, getPropsData, isEmptyElement } from '../../_util/props-util';
let warnDeprecatedLabel = false;
+// =================== DOM =====================
+export function findPopupContainer(node, prefixClass) {
+ let current = node;
+ while (current) {
+ if (hasClass(current, prefixClass)) {
+ return current;
+ }
+ current = current.parentNode;
+ }
+
+ return null;
+}
+
// =================== MISC ====================
export function toTitle(title) {
if (typeof title === 'string') {
@@ -190,7 +203,7 @@ export function cleanEntity({ node, pos, children }) {
* we have to convert `treeNodes > data > treeNodes` to keep the key.
* Such performance hungry!
*/
-export function getFilterTree(h, treeNodes, searchValue, filterFunc, valueEntities) {
+export function getFilterTree(h, treeNodes, searchValue, filterFunc, valueEntities, Component) {
if (!searchValue) {
return null;
}
@@ -208,9 +221,9 @@ export function getFilterTree(h, treeNodes, searchValue, filterFunc, valueEntiti
.filter(n => n);
if (children.length || match) {
return (
-
+
{children}
-
+
);
}
diff --git a/components/vc-tree/index.js b/components/vc-tree/index.js
index f713ed270..bf7b1b6b1 100644
--- a/components/vc-tree/index.js
+++ b/components/vc-tree/index.js
@@ -1,4 +1,4 @@
-// based on rc-tree 1.14.10
+// based on rc-tree 2.1.3
'use strict';
module.exports = require('./src/');
diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx
index c4b86b9e9..48e29e6cc 100644
--- a/components/vc-tree/src/Tree.jsx
+++ b/components/vc-tree/src/Tree.jsx
@@ -108,8 +108,9 @@ const Tree = {
data() {
warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__');
- warning(this.$props.children, 'please children prop replace slots.default');
+ warning(this.$props.children, 'please use children prop replace slots.default');
this.needSyncKeys = {};
+ this.domTreeNodes = {};
const state = {
_posEntities: new Map(),
_keyEntities: new Map(),
@@ -180,7 +181,6 @@ const Tree = {
// Calculate the entities data for quick match
const entitiesMap = convertTreeToEntities(treeNode);
- newState._posEntities = entitiesMap.posEntities;
newState._keyEntities = entitiesMap.keyEntities;
}
@@ -231,8 +231,7 @@ const Tree = {
if (!props.checkStrictly) {
const conductKeys = conductCheck(checkedKeys, true, keyEntities);
- checkedKeys = conductKeys.checkedKeys;
- halfCheckedKeys = conductKeys.halfCheckedKeys;
+ ({ checkedKeys, halfCheckedKeys } = conductKeys);
}
newState._checkedKeys = checkedKeys;
@@ -366,6 +365,7 @@ const Tree = {
dragNode: this.dragNode,
dragNodesKeys: _dragNodesKeys.slice(),
dropPosition: _dropPosition + Number(posArr[posArr.length - 1]),
+ dropToGap: false,
};
if (_dropPosition !== 0) {
@@ -415,7 +415,7 @@ const Tree = {
selected: targetSelected,
node: treeNode,
selectedNodes,
- nativeEvent: e,
+ nativeEvent: e.nativeEvent,
};
this.__emit('update:selectedKeys', selectedKeys);
this.__emit('select', selectedKeys, eventObj);
@@ -499,16 +499,16 @@ const Tree = {
// Process load data
const promise = loadData(treeNode);
promise.then(() => {
- const newLoadedKeys = arrAdd(this.$data._loadedKeys, eventKey);
- const newLoadingKeys = arrDel(this.$data._loadingKeys, eventKey);
+ const { _loadedKeys: currentLoadedKeys, _loadingKeys: currentLoadingKeys } = this.$data;
+ const newLoadedKeys = arrAdd(currentLoadedKeys, eventKey);
+ const newLoadingKeys = arrDel(currentLoadingKeys, eventKey);
// onLoad should trigger before internal setState to avoid `loadData` trigger twice.
// https://github.com/ant-design/ant-design/issues/12464
- const eventObj = {
+ this.__emit('load', newLoadedKeys, {
event: 'load',
node: treeNode,
- };
- this.__emit('load', newLoadedKeys, eventObj);
+ });
this.setUncontrolledState({
_loadedKeys: newLoadedKeys,
});
@@ -598,6 +598,14 @@ const Tree = {
}
},
+ registerTreeNode(key, node) {
+ if (node) {
+ this.domTreeNodes[key] = node;
+ } else {
+ delete this.domTreeNodes[key];
+ }
+ },
+
isKeyChecked(key) {
const { _checkedKeys: checkedKeys = [] } = this.$data;
return checkedKeys.indexOf(key) !== -1;
@@ -652,18 +660,15 @@ const Tree = {
render() {
const { _treeNode: treeNode } = this.$data;
const { prefixCls, focusable, showLine, tabIndex = 0 } = this.$props;
- const domProps = {};
return (
{}}
>
{mapChildren(treeNode, (node, index) => this.renderTreeNode(node, index))}
diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx
index ade13e1e2..d3855db67 100644
--- a/components/vc-tree/src/TreeNode.jsx
+++ b/components/vc-tree/src/TreeNode.jsx
@@ -39,6 +39,7 @@ const TreeNode = {
// By user
isLeaf: PropTypes.bool,
+ checkable: PropTypes.bool,
selectable: PropTypes.bool,
disabled: PropTypes.bool,
disableCheckbox: PropTypes.bool,
@@ -69,11 +70,23 @@ const TreeNode = {
// Isomorphic needn't load data in server side
mounted() {
+ const {
+ eventKey,
+ vcTree: { registerTreeNode },
+ } = this;
this.syncLoadData(this.$props);
+ registerTreeNode && registerTreeNode(eventKey, this);
},
updated() {
this.syncLoadData(this.$props);
},
+ beforeDestroy() {
+ const {
+ eventKey,
+ vcTree: { registerTreeNode },
+ } = this;
+ registerTreeNode && registerTreeNode(eventKey, null);
+ },
methods: {
onSelectorClick(e) {
@@ -111,10 +124,10 @@ const TreeNode = {
const { disableCheckbox, checked } = this;
const {
- vcTree: { checkable, onNodeCheck },
+ vcTree: { onNodeCheck },
} = this;
- if (!checkable || disableCheckbox) return;
+ if (!this.isCheckable() || disableCheckbox) return;
e.preventDefault();
const targetChecked = !checked;
@@ -275,18 +288,15 @@ const TreeNode = {
return !!(treeDisabled || disabled);
},
- isSelectable() {
- const { selectable } = this;
+ isCheckable() {
+ const { checkable } = this.$props;
const {
- vcTree: { selectable: treeSelectable },
+ vcTree: { checkable: treeCheckable },
} = this;
- // Ignore when selectable is undefined or null
- if (typeof selectable === 'boolean') {
- return selectable;
- }
-
- return treeSelectable;
+ // Return false if tree or treeNode is not checkable
+ if (!treeCheckable || checkable === false) return false;
+ return treeCheckable;
},
// Load data to avoid default expanded tree without data
@@ -307,6 +317,20 @@ const TreeNode = {
}
},
+ isSelectable() {
+ const { selectable } = this;
+ const {
+ vcTree: { selectable: treeSelectable },
+ } = this;
+
+ // Ignore when selectable is undefined or null
+ if (typeof selectable === 'boolean') {
+ return selectable;
+ }
+
+ return treeSelectable;
+ },
+
// Switcher
renderSwitcher() {
const { expanded } = this;
@@ -323,7 +347,7 @@ const TreeNode = {
class={classNames(`${prefixCls}-switcher`, `${prefixCls}-switcher-noop`)}
>
{typeof switcherIcon === 'function'
- ? cloneElement(switcherIcon({ ...this.$props, isLeaf: true }))
+ ? switcherIcon({ ...this.$props, isLeaf: true })
: switcherIcon}
);
@@ -336,7 +360,7 @@ const TreeNode = {
return (
{typeof switcherIcon === 'function'
- ? cloneElement(switcherIcon({ ...this.$props, isLeaf: false }))
+ ? switcherIcon({ ...this.$props, isLeaf: false })
: switcherIcon}
);
@@ -346,9 +370,10 @@ const TreeNode = {
renderCheckbox() {
const { checked, halfChecked, disableCheckbox } = this;
const {
- vcTree: { prefixCls, checkable },
+ vcTree: { prefixCls },
} = this;
const disabled = this.isDisabled();
+ const checkable = this.isCheckable();
if (!checkable) return null;
diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js
index 0df06bde1..cf5cfc1d4 100644
--- a/components/vc-tree/src/util.js
+++ b/components/vc-tree/src/util.js
@@ -49,8 +49,8 @@ export function getNodeChildren(children = []) {
}
export function isCheckDisabled(node) {
- const { disabled, disableCheckbox } = getOptionProps(node) || {};
- return !!(disabled || disableCheckbox);
+ const { disabled, disableCheckbox, checkable } = getOptionProps(node) || {};
+ return !!(disabled || disableCheckbox) || checkable === false;
}
export function traverseTreeNodes(treeNodes, callback) {
@@ -116,7 +116,8 @@ export function calcDropPosition(event, treeNode) {
if (clientY <= top + des) {
return -1;
- } else if (clientY >= bottom - des) {
+ }
+ if (clientY >= bottom - des) {
return 1;
}
return 0;
@@ -162,13 +163,13 @@ const internalProcessProps = (props = {}) => {
key: props.key,
};
};
-export function convertDataToTree(h, treeData, processer) {
+export function convertDataToTree(h, treeData, processor) {
if (!treeData) return [];
- const { processProps = internalProcessProps } = processer || {};
+ const { processProps = internalProcessProps } = processor || {};
const list = Array.isArray(treeData) ? treeData : [treeData];
return list.map(({ children, ...props }) => {
- const childrenNodes = convertDataToTree(h, children, processer);
+ const childrenNodes = convertDataToTree(h, children, processor);
return {childrenNodes};
});
}
@@ -398,8 +399,8 @@ export function conductExpandParent(keyList, keyEntities) {
expandedKeys.set(key, true);
const { parent, node } = entity;
-
- if (isCheckDisabled(node)) return;
+ const props = getOptionProps(node);
+ if (props && props.disabled) return;
if (parent) {
conductUp(parent.key);
diff --git a/components/vc-util/Dom/class-util.js b/components/vc-util/Dom/class-util.js
deleted file mode 100644
index 5012f5722..000000000
--- a/components/vc-util/Dom/class-util.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* @flow */
-
-/**
- * Add class with compatibility for SVG since classList is not supported on
- * SVG elements in IE
- */
-export function addClass(el, cls) {
- /* istanbul ignore if */
- if (!cls || !(cls = cls.trim())) {
- return;
- }
-
- /* istanbul ignore else */
- if (el.classList) {
- if (cls.indexOf(' ') > -1) {
- cls.split(/\s+/).forEach(c => el.classList.add(c));
- } else {
- el.classList.add(cls);
- }
- } else {
- const cur = ` ${el.getAttribute('class') || ''} `;
- if (cur.indexOf(' ' + cls + ' ') < 0) {
- el.setAttribute('class', (cur + cls).trim());
- }
- }
-}
-
-/**
- * Remove class with compatibility for SVG since classList is not supported on
- * SVG elements in IE
- */
-export function removeClass(el, cls) {
- /* istanbul ignore if */
- if (!cls || !(cls = cls.trim())) {
- return;
- }
-
- /* istanbul ignore else */
- if (el.classList) {
- if (cls.indexOf(' ') > -1) {
- cls.split(/\s+/).forEach(c => el.classList.remove(c));
- } else {
- el.classList.remove(cls);
- }
- if (!el.classList.length) {
- el.removeAttribute('class');
- }
- } else {
- let cur = ` ${el.getAttribute('class') || ''} `;
- const tar = ' ' + cls + ' ';
- while (cur.indexOf(tar) >= 0) {
- cur = cur.replace(tar, ' ');
- }
- cur = cur.trim();
- if (cur) {
- el.setAttribute('class', cur);
- } else {
- el.removeAttribute('class');
- }
- }
-}
diff --git a/components/vc-util/Dom/class.js b/components/vc-util/Dom/class.js
new file mode 100644
index 000000000..b72c70b39
--- /dev/null
+++ b/components/vc-util/Dom/class.js
@@ -0,0 +1,28 @@
+export function hasClass(node, className) {
+ if (node.classList) {
+ return node.classList.contains(className);
+ }
+ const originClass = node.className;
+ return ` ${originClass} `.indexOf(` ${className} `) > -1;
+}
+
+export function addClass(node, className) {
+ if (node.classList) {
+ node.classList.add(className);
+ } else {
+ if (!hasClass(node, className)) {
+ node.className = `${node.className} ${className}`;
+ }
+ }
+}
+
+export function removeClass(node, className) {
+ if (node.classList) {
+ node.classList.remove(className);
+ } else {
+ if (hasClass(node, className)) {
+ const originClass = node.className;
+ node.className = ` ${originClass} `.replace(` ${className} `, ' ');
+ }
+ }
+}
diff --git a/tests/shared/mountTest.js b/tests/shared/mountTest.js
index 1b071a464..0da105416 100644
--- a/tests/shared/mountTest.js
+++ b/tests/shared/mountTest.js
@@ -3,7 +3,7 @@ export default function mountTest(Component) {
describe(`mount and unmount`, () => {
// https://github.com/ant-design/ant-design/pull/18441
it(`component could be updated and unmounted without errors`, () => {
- const wrapper = mount(Component);
+ const wrapper = mount(Component, { sync: false });
expect(() => {
wrapper.vm.$forceUpdate();
wrapper.destroy();
diff --git a/types/tree-select.d.ts b/types/tree-select.d.ts
index 855d3cb91..6050c67b5 100644
--- a/types/tree-select.d.ts
+++ b/types/tree-select.d.ts
@@ -1,6 +1,6 @@
-// Project: https://github.com/vueComponent/ant-design-vue
-// Definitions by: akki-jat
-// Definitions: https://github.com/vueComponent/ant-design-vue/types
+// Project: https://github.com/vueComponent/ant-design-vue Definitions by:
+// akki-jat Definitions:
+// https://github.com/vueComponent/ant-design-vue/types
import { AntdComponent } from './component';
import { TreeNode } from './tree-node';
@@ -15,6 +15,8 @@ export interface TreeData {
selectable?: boolean;
}
+export type TreeNodeValue = string | number | string[] | number[];
+
export declare class TreeSelect extends AntdComponent {
static TreeNode: typeof TreeNode;
@@ -29,11 +31,7 @@ export declare class TreeSelect extends AntdComponent {
*/
allowClear: boolean;
- /**
- * To set the initial selected treeNode(s).
- * @type string | string[]
- */
- defaultValue: string | string[];
+ defaultValue: TreeNodeValue;
/**
* Disabled or not
@@ -171,7 +169,13 @@ export declare class TreeSelect extends AntdComponent {
* @default false
* @type boolean | object[]
*/
- treeDataSimpleMode: boolean | Array<{ id: string; pId: string; rootPId: any }>;
+ treeDataSimpleMode:
+ | boolean
+ | Array<{
+ id: string;
+ pId: string;
+ rootPId: any;
+ }>;
/**
* Whether to expand all treeNodes by default
@@ -206,11 +210,7 @@ export declare class TreeSelect extends AntdComponent {
*/
treeNodeLabelProp: string;
- /**
- * To set the current selected treeNode(s).
- * @type string | string[]
- */
- value: string | string[];
+ value: TreeNodeValue;
/**
* The custom suffix icon
@@ -218,6 +218,10 @@ export declare class TreeSelect extends AntdComponent {
*/
suffixIcon: any;
+ removeIcon?: any;
+
+ clearIcon?: any;
+
/**
* remove focus
*/