diff --git a/components/_util/props-util.js b/components/_util/props-util.js
index a16c3059b..9fddb4427 100644
--- a/components/_util/props-util.js
+++ b/components/_util/props-util.js
@@ -117,6 +117,16 @@ const getComponentFromProp = (instance, prop) => {
}
}
+const getAllProps = (ele) => {
+ let data = ele.data || {}
+ let componentOptions = ele.componentOptions || {}
+ if (ele.$vnode) {
+ data = ele.$vnode.data || {}
+ componentOptions = ele.$vnode.componentOptions || {}
+ }
+ return { ...data.props, ...data.attrs, ...componentOptions.propsData }
+}
+
const getPropsData = (ele) => {
let componentOptions = ele.componentOptions
if (ele.$vnode) {
@@ -247,5 +257,6 @@ export {
isValidElement,
camelize,
getSlots,
+ getAllProps,
}
export default hasProp
diff --git a/components/auto-complete/index.jsx b/components/auto-complete/index.jsx
index e7dcc99ef..f6de0de77 100644
--- a/components/auto-complete/index.jsx
+++ b/components/auto-complete/index.jsx
@@ -21,7 +21,7 @@ import { getComponentFromProp, getOptionProps, filterEmpty, isValidElement } fro
// }
const AutoCompleteProps = {
- ...AbstractSelectProps,
+ ...AbstractSelectProps(),
value: SelectValue,
defaultValue: SelectValue,
dataSource: PropTypes.array,
diff --git a/components/index.js b/components/index.js
index a9779713a..5e6fce8f8 100644
--- a/components/index.js
+++ b/components/index.js
@@ -102,7 +102,7 @@ import { default as Transfer } from './transfer'
import { default as Tree } from './tree'
-// import { default as TreeSelect } from './tree-select'
+import { default as TreeSelect } from './tree-select'
import { default as Tabs } from './tabs'
@@ -196,6 +196,8 @@ const components = [
Transfer,
Tree,
Tree.TreeNode,
+ TreeSelect,
+ TreeSelect.TreeNode,
Tabs,
Tabs.TabPane,
Tag,
@@ -269,6 +271,7 @@ export {
Table,
Transfer,
Tree,
+ TreeSelect,
Tabs,
Tag,
TimePicker,
diff --git a/components/select/index.jsx b/components/select/index.jsx
index 26b779062..7b8563293 100644
--- a/components/select/index.jsx
+++ b/components/select/index.jsx
@@ -5,7 +5,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver'
import defaultLocale from '../locale-provider/default'
import { getComponentFromProp, getOptionProps, filterEmpty } from '../_util/props-util'
-const AbstractSelectProps = {
+const AbstractSelectProps = () => ({
prefixCls: PropTypes.string,
size: PropTypes.oneOf(['small', 'large', 'default']),
notFoundContent: PropTypes.any,
@@ -28,7 +28,7 @@ const AbstractSelectProps = {
autoFocus: PropTypes.bool,
backfill: PropTypes.bool,
showArrow: PropTypes.bool,
-}
+})
const Value = PropTypes.shape({
key: PropTypes.string,
}).loose
@@ -45,7 +45,7 @@ const SelectValue = PropTypes.oneOfType([
])
const SelectProps = {
- ...AbstractSelectProps,
+ ...AbstractSelectProps(),
value: SelectValue,
defaultValue: SelectValue,
mode: PropTypes.oneOf(['default', 'multiple', 'tags', 'combobox']),
diff --git a/components/style.js b/components/style.js
index 640ee4c18..416939770 100644
--- a/components/style.js
+++ b/components/style.js
@@ -46,3 +46,4 @@ import './layout/style'
import './form/style'
import './anchor/style'
import './list/style'
+import './tree-select/style'
diff --git a/components/tree-select/__tests__/__snapshots__/demo.test.js.snap b/components/tree-select/__tests__/__snapshots__/demo.test.js.snap
new file mode 100644
index 000000000..42dac6459
--- /dev/null
+++ b/components/tree-select/__tests__/__snapshots__/demo.test.js.snap
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders ./components/tree-select/demo/basic.md correctly 1`] = `
+Please select
+
+`;
+
+exports[`renders ./components/tree-select/demo/checkable.md correctly 1`] = `
+Node1
+
+
+
Please select
+
+`;
+
+exports[`renders ./components/tree-select/demo/multiple.md correctly 1`] = `
+
+
+Please select
+
+`;
+
+exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `
+Please select
+
+`;
diff --git a/components/tree-select/__tests__/demo.test.js b/components/tree-select/__tests__/demo.test.js
new file mode 100644
index 000000000..1fc1e0b7d
--- /dev/null
+++ b/components/tree-select/__tests__/demo.test.js
@@ -0,0 +1,3 @@
+import demoTest from '../../../tests/shared/demoTest'
+
+demoTest('tree-select')
diff --git a/components/tree-select/__tests__/index.test.js b/components/tree-select/__tests__/index.test.js
new file mode 100644
index 000000000..b1c6d668b
--- /dev/null
+++ b/components/tree-select/__tests__/index.test.js
@@ -0,0 +1,6 @@
+import TreeSelect from '..'
+import focusTest from '../../../tests/shared/focusTest'
+
+describe('TreeSelect', () => {
+ focusTest(TreeSelect)
+})
diff --git a/components/tree-select/demo/basic.md b/components/tree-select/demo/basic.md
new file mode 100644
index 000000000..735280343
--- /dev/null
+++ b/components/tree-select/demo/basic.md
@@ -0,0 +1,53 @@
+
+#### 基本用法
+最简单的用法。
+
+
+
+#### Basic
+The most basic usage.
+
+
+```html
+
+
+
+
+
+
+
+
+
+ sss
+
+
+
+
+
+
+
+```
diff --git a/components/tree-select/demo/checkable.md b/components/tree-select/demo/checkable.md
new file mode 100644
index 000000000..f2ff511ce
--- /dev/null
+++ b/components/tree-select/demo/checkable.md
@@ -0,0 +1,73 @@
+
+#### 可勾选
+使用勾选框实现多选功能。
+
+
+
+#### Checkable
+Multiple and checkable.
+
+
+```html
+
+
+
+
+
+```
diff --git a/components/tree-select/demo/index.vue b/components/tree-select/demo/index.vue
new file mode 100644
index 000000000..a0e0f736d
--- /dev/null
+++ b/components/tree-select/demo/index.vue
@@ -0,0 +1,48 @@
+
diff --git a/components/tree-select/demo/multiple.md b/components/tree-select/demo/multiple.md
new file mode 100644
index 000000000..3fcf57a50
--- /dev/null
+++ b/components/tree-select/demo/multiple.md
@@ -0,0 +1,62 @@
+
+#### 多选
+多选的树选择。
+
+
+
+#### Multiple Selection
+Multiple selection usage.
+
+
+```html
+
+
+
+
+
+
+
+
+
+ sss
+
+
+
+
+
+
+
+```
diff --git a/components/tree-select/demo/treeData.md b/components/tree-select/demo/treeData.md
new file mode 100644
index 000000000..e207ce136
--- /dev/null
+++ b/components/tree-select/demo/treeData.md
@@ -0,0 +1,63 @@
+
+#### 从数据直接生成
+使用 `treeData` 把 JSON 数据直接生成树结构。
+
+
+
+#### Generate form tree data
+The tree structure can be populated using `treeData` property. This is a quick and easy way to provide the tree content.
+
+
+```html
+
+
+
+ Child Node1 {{value}}
+
+
+
+
+
+```
diff --git a/components/tree-select/index.en-US.md b/components/tree-select/index.en-US.md
new file mode 100644
index 000000000..0383beeac
--- /dev/null
+++ b/components/tree-select/index.en-US.md
@@ -0,0 +1,61 @@
+
+## API
+
+### Tree props
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| allowClear | Whether allow clear | boolean | false |
+| defaultValue | To set the initial selected treeNode(s). | string\|string\[] | - |
+| disabled | Disabled or not | boolean | false |
+| dropdownClassName | className of dropdown menu | string | - |
+| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width | boolean | true |
+| dropdownStyle | To set the style of the dropdown menu | object | - |
+| filterTreeNode | Whether to filter treeNodes by input value. The value of `treeNodeFilterProp` is used for filtering by default. | boolean\|Function(inputValue: string, treeNode: TreeNode) (should return boolean) | Function |
+| 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 |
+| labelInValue | whether to embed label in value, turn the format of value from `string` to `{value: string, label: VNode, halfChecked: string[]}` | boolean | false |
+| loadData | Load data asynchronously. | function(node) | - |
+| multiple | Support multiple or not, will be `true` when enable `treeCheckable`. | boolean | false |
+| placeholder | Placeholder of the select input | string\|slot | - |
+| searchPlaceholder | Placeholder of the search input | string\|slot | - |
+| showCheckedStrategy | The way show selected item in box. **Default:** just show child nodes. **`TreeSelect.SHOW_ALL`:** show all checked treeNodes (include parent treeNode). **`TreeSelect.SHOW_PARENT`:** show checked treeNodes (just show parent treeNode). | enum { TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
+| showSearch | Whether to display a search input in the dropdown menu(valid only in the single mode) | boolean | false |
+| size | To set the size of the select input, options: `large` `small` | string | 'default' |
+| treeCheckable | Whether to show checkbox on the treeNodes | boolean | false |
+| treeCheckStrictly | Whether to check nodes precisely (in the `checkable` mode), means parent and child nodes are not associated, and it will make `labelInValue` be true | boolean | false |
+| treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array<{ value, label, children, [disabled, disableCheckbox, selectable] }> | \[] |
+| treeDataSimpleMode | Enable simple mode of treeData.(treeData should like this: [{id:1, pId:0, value:'1', label:"test1",...},...], pId is parent node's id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false |
+| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false |
+| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - |
+| treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | 'value' |
+| treeNodeLabelProp | Will render as content of select | string | 'title' |
+| value(v-model) | To set the current selected treeNode(s). | string\|string\[] | - |
+
+
+### Events
+| Events Name | Description | Arguments |
+| --- | --- | --- |
+| change | A callback function, can be executed when selected treeNodes or input value change | function(value, label, extra) |
+| search | A callback function, can be executed when the search input changes. | function(value: string) |
+| select | A callback function, can be executed when you select a treeNode. | function(value, node, extra) |
+
+### Tree Methods
+
+| Name | Description |
+| ---- | ----------- |
+| blur() | remove focus |
+| focus() | get focus |
+
+### TreeNode props
+
+> We recommend you to use `treeData` rather than `TreeNode`, to avoid the trouble of manual construction.
+
+| Property | Description | Type | Default |
+| -------- | ----------- | ---- | ------- |
+| 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 | - |
+| 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 | - |
diff --git a/components/tree-select/index.jsx b/components/tree-select/index.jsx
new file mode 100644
index 000000000..1a00fb6a0
--- /dev/null
+++ b/components/tree-select/index.jsx
@@ -0,0 +1,115 @@
+
+import VcTreeSelect, { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from '../vc-tree-select'
+import classNames from 'classnames'
+import { TreeSelectProps } from './interface'
+import LocaleReceiver from '../locale-provider/LocaleReceiver'
+import warning from '../_util/warning'
+import { initDefaultProps, getOptionProps, getComponentFromProp, filterEmpty } from '../_util/props-util'
+
+export { TreeData, TreeSelectProps } from './interface'
+
+export default {
+ TreeNode: { ...TreeNode, name: 'ATreeSelectNode' },
+ SHOW_ALL,
+ SHOW_PARENT,
+ SHOW_CHILD,
+ name: 'ATreeSelect',
+ props: initDefaultProps(TreeSelectProps(), {
+ prefixCls: 'ant-select',
+ transitionName: 'slide-up',
+ choiceTransitionName: 'zoom',
+ showSearch: false,
+ }),
+ model: {
+ prop: 'value',
+ event: 'change',
+ },
+
+ created () {
+ warning(
+ this.multiple !== false || !this.treeCheckable,
+ '`multiple` will alway be `true` when `treeCheckable` is true',
+ )
+ },
+ methods: {
+ focus () {
+ this.$refs.vcTreeSelect.focus()
+ },
+
+ blur () {
+ this.$refs.vcTreeSelect.blur()
+ },
+ onChange () {
+ this.$emit('change', ...arguments)
+ },
+ updateTreeData (list = []) {
+ for (let i = 0, len = list.length; i < len; i++) {
+ const { label, title, scopedSlots = {}, children } = list[i]
+ const { $scopedSlots } = this
+ let newLabel = typeof label === 'function' ? label(this.$createElement) : label
+ let newTitle = typeof title === 'function' ? title(this.$createElement) : title
+ if (!newLabel && scopedSlots.label && $scopedSlots[scopedSlots.label]) {
+ newLabel = $scopedSlots.label(list[i])
+ }
+ if (!newTitle && scopedSlots.title && $scopedSlots[scopedSlots.title]) {
+ newTitle = $scopedSlots.title(list[i])
+ }
+ const item = {
+ // label: newLabel,
+ title: newTitle || newLabel,
+ }
+ this.updateTreeData(children)
+ Object.assign(list[i], item)
+ }
+ },
+ renderTreeSelect (locale) {
+ const props = getOptionProps(this)
+ const {
+ prefixCls,
+ size,
+ notFoundContent,
+ dropdownStyle,
+ dropdownClassName,
+ ...restProps
+ } = props
+ this.updateTreeData(props.treeData)
+ const cls = {
+ [`${prefixCls}-lg`]: size === 'large',
+ [`${prefixCls}-sm`]: size === 'small',
+ }
+
+ let checkable = getComponentFromProp(this, 'treeCheckable')
+ if (checkable) {
+ checkable =
+ }
+ const VcTreeSelectProps = {
+ props: {
+ ...restProps,
+ dropdownClassName: classNames(dropdownClassName, `${prefixCls}-tree-dropdown`),
+ prefixCls,
+ dropdownStyle: { maxHeight: '100vh', overflow: 'auto', ...dropdownStyle },
+ treeCheckable: checkable,
+ notFoundContent: notFoundContent || locale.notFoundContent,
+ },
+ class: cls,
+ on: { ...this.$listeners, change: this.onChange },
+ ref: 'vcTreeSelect',
+ }
+ return (
+ {filterEmpty(this.$slots.default)}
+ )
+ },
+ },
+
+ render () {
+ return (
+
+ )
+ },
+}
diff --git a/components/tree-select/index.zh-CN.md b/components/tree-select/index.zh-CN.md
new file mode 100644
index 000000000..a2a911675
--- /dev/null
+++ b/components/tree-select/index.zh-CN.md
@@ -0,0 +1,61 @@
+
+## API
+
+### Tree props
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| allowClear | 显示清除按钮 | boolean | false |
+| defaultValue | 指定默认选中的条目 | string/string\[] | - |
+| disabled | 是否禁用 | boolean | false |
+| dropdownClassName | 下拉菜单的 className 属性 | string | - |
+| dropdownMatchSelectWidth | 下拉菜单和选择器同宽 | boolean | true |
+| dropdownStyle | 下拉菜单的样式 | object | - |
+| filterTreeNode | 是否根据输入项进行筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值 | boolean\|Function(inputValue: string, treeNode: TreeNode) (函数需要返回bool值) | Function |
+| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。 | Function(triggerNode) | () => document.body |
+| labelInValue | 是否把每个选项的 label 包装到 value 中,会把 value 类型从 `string` 变为 `{value: string, label: VNode, halfChecked(treeCheckStrictly 时有效): string[] }` 的格式 | boolean | false |
+| loadData | 异步加载数据 | function(node) | - |
+| multiple | 支持多选(当设置 treeCheckable 时自动变为true) | boolean | false |
+| placeholder | 选择框默认文字 | string\|slot | - |
+| searchPlaceholder | 搜索框默认文字 | string\|slot | - |
+| showCheckedStrategy | 定义选中项回填的方式。`TreeSelect.SHOW_ALL`: 显示所有选中节点(包括父节点). `TreeSelect.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时). 默认只显示子节点. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
+| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | boolean | false |
+| size | 选择框大小,可选 `large` `small` | string | 'default' |
+| treeCheckable | 显示 checkbox | boolean | false |
+| treeCheckStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联),会使得 `labelInValue` 强制为 true | boolean | false |
+| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一) | array<{value, label, children, [disabled, disableCheckbox, selectable]}> | \[] |
+| treeDataSimpleMode | 使用简单格式的 treeData,具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', label:"test1",...},...], `pId` 是父节点的 id) | false\|Array<{ id: string, pId: string, rootPId: null }> | false |
+| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false |
+| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - |
+| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' |
+| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' |
+| value(v-model) | 指定当前选中的条目 | string/string\[] | - |
+
+### 事件
+
+| 事件名称 | 说明 | 回调参数 |
+| --- | --- | --- |
+| change | 选中树节点时调用此函数 | function(value, label, extra) | - |
+| search | 文本框值变化时回调 | function(value: string) | - |
+| select | 被选中时调用 | function(value, node, extra) | - |
+
+### Tree 方法
+
+| 名称 | 描述 |
+| --- | --- |
+| blur() | 移除焦点 |
+| focus() | 获取焦点 |
+
+### TreeNode props
+
+> 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| disableCheckbox | 禁掉 checkbox | boolean | false |
+| disabled | 是否禁用 | boolean | false |
+| isLeaf | 是否是叶子节点 | boolean | false |
+| key | 此项必须设置(其值在整个树范围内唯一) | string | - |
+| title | 树节点显示的内容 | string\|slot | '---' |
+| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - |
+| scopedSlots | 使用treeData时,可以通过该属性配置支持slot的属性,如 `scopedSlots: { title: 'XXX'}` | object | - |
diff --git a/components/tree-select/interface.jsx b/components/tree-select/interface.jsx
new file mode 100644
index 000000000..23564b3cc
--- /dev/null
+++ b/components/tree-select/interface.jsx
@@ -0,0 +1,37 @@
+import PropTypes from '../_util/vue-types'
+import { AbstractSelectProps } from '../select'
+
+export const TreeData = PropTypes.shape({
+ key: PropTypes.string,
+ value: PropTypes.string,
+ label: PropTypes.any,
+ scopedSlots: PropTypes.object,
+ children: PropTypes.array,
+}).loose
+
+export const TreeSelectProps = () => ({
+ ...AbstractSelectProps(),
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.array]),
+ defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
+ multiple: PropTypes.bool,
+ // onSelect: (value: any) => void,
+ // onChange: (value: any, label: any) => void,
+ // onSearch: (value: any) => void,
+ searchPlaceholder: PropTypes.string,
+ dropdownClassName: PropTypes.string,
+ dropdownStyle: PropTypes.object,
+ dropdownMatchSelectWidth: PropTypes.bool,
+ treeDefaultExpandAll: PropTypes.bool,
+ treeCheckable: PropTypes.bool,
+ treeDefaultExpandedKeys: PropTypes.arrayOf(String),
+ filterTreeNode: PropTypes.func,
+ treeNodeFilterProp: PropTypes.string,
+ treeNodeLabelProp: PropTypes.string,
+ treeData: PropTypes.arrayOf(Object),
+ treeDataSimpleMode: PropTypes.oneOfType([Boolean, Object]),
+ loadData: PropTypes.func,
+ showCheckedStrategy: PropTypes.oneOf(['SHOW_ALL', 'SHOW_PARENT', 'SHOW_CHILD']),
+ labelInValue: PropTypes.bool,
+ treeCheckStrictly: PropTypes.bool,
+ getPopupContainer: PropTypes.func,
+})
diff --git a/components/tree-select/style/index.js b/components/tree-select/style/index.js
new file mode 100644
index 000000000..daa33e949
--- /dev/null
+++ b/components/tree-select/style/index.js
@@ -0,0 +1,6 @@
+import '../../style/index.less'
+import './index.less'
+
+// style dependencies
+import '../../select/style'
+import '../../checkbox/style'
diff --git a/components/tree-select/style/index.less b/components/tree-select/style/index.less
new file mode 100644
index 000000000..8048e8086
--- /dev/null
+++ b/components/tree-select/style/index.less
@@ -0,0 +1,145 @@
+@import "../../style/themes/default";
+@import "../../style/mixins/index";
+@import "../../tree/style/mixin";
+@import "../../checkbox/style/mixin";
+
+@select-prefix-cls: ~"@{ant-prefix}-select";
+@select-tree-prefix-cls: ~"@{ant-prefix}-select-tree";
+
+.antCheckboxFn(@checkbox-prefix-cls: ~"@{ant-prefix}-select-tree-checkbox");
+
+.@{select-tree-prefix-cls} {
+ .reset-component;
+ margin: 0;
+ padding: 0 4px;
+ margin-top: -4px;
+ li {
+ padding: 0;
+ margin: 8px 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ &.filter-node {
+ > span {
+ font-weight: 500;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ }
+ .@{select-tree-prefix-cls}-node-content-wrapper {
+ display: inline-block;
+ padding: 3px 5px;
+ border-radius: 2px;
+ margin: 0;
+ cursor: pointer;
+ text-decoration: none;
+ color: @text-color;
+ transition: all .3s;
+ width: ~"calc(100% - 24px)";
+ &:hover {
+ background-color: @item-hover-bg;
+ }
+ &.@{select-tree-prefix-cls}-node-selected {
+ background-color: @primary-2;
+ }
+ }
+ span {
+ &.@{select-tree-prefix-cls}-checkbox {
+ margin: 0 4px 0 0;
+ + .@{select-tree-prefix-cls}-node-content-wrapper {
+ width: ~"calc(100% - 46px)";
+ }
+ }
+ &.@{select-tree-prefix-cls}-switcher,
+ &.@{select-tree-prefix-cls}-iconEle {
+ margin: 0;
+ width: 24px;
+ height: 24px;
+ line-height: 22px;
+ display: inline-block;
+ vertical-align: middle;
+ border: 0 none;
+ cursor: pointer;
+ outline: none;
+ text-align: center;
+ }
+ &.@{select-tree-prefix-cls}-icon_loading {
+ &:after {
+ display: inline-block;
+ .iconfont-font("\e6ae");
+ animation: loadingCircle 1s infinite linear;
+ color: @primary-color;
+ }
+ }
+ &.@{select-tree-prefix-cls}-switcher {
+ &.@{select-tree-prefix-cls}-switcher-noop {
+ cursor: auto;
+ }
+ &.@{select-tree-prefix-cls}-switcher_open {
+ .antTreeSwitcherIcon();
+ }
+ &.@{select-tree-prefix-cls}-switcher_close {
+ .antTreeSwitcherIcon();
+ &:after {
+ transform: rotate(270deg) scale(0.59);
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ li&-treenode-disabled {
+ > span:not(.@{select-tree-prefix-cls}-switcher),
+ > .@{select-tree-prefix-cls}-node-content-wrapper,
+ > .@{select-tree-prefix-cls}-node-content-wrapper span {
+ color: @disabled-color;
+ cursor: not-allowed;
+ }
+ > .@{select-tree-prefix-cls}-node-content-wrapper:hover {
+ background: transparent;
+ }
+ }
+ &-icon__open {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+ &-icon__close {
+ margin-right: 2px;
+ vertical-align: top;
+ }
+}
+
+.@{select-prefix-cls}-tree-dropdown {
+ .reset-component;
+ .@{select-prefix-cls}-dropdown-search {
+ display: block;
+ padding: 4px;
+ .@{select-prefix-cls}-search__field__wrap {
+ width: 100%;
+ }
+ .@{select-prefix-cls}-search__field {
+ padding: 4px 7px;
+ width: 100%;
+ box-sizing: border-box;
+ border: @border-width-base @border-style-base @border-color-base;
+ border-radius: 4px;
+ outline: none;
+ }
+ &.@{select-prefix-cls}-search--hide {
+ display: none;
+ }
+ }
+ .@{select-prefix-cls}-not-found {
+ cursor: not-allowed;
+ color: @disabled-color;
+ padding: 7px 16px;
+ display: block;
+ }
+}
diff --git a/components/vc-tree-select/assets/icons.png b/components/vc-tree-select/assets/icons.png
new file mode 100644
index 000000000..ffda01ef1
Binary files /dev/null and b/components/vc-tree-select/assets/icons.png differ
diff --git a/components/vc-tree-select/assets/index.less b/components/vc-tree-select/assets/index.less
new file mode 100644
index 000000000..e02a9c033
--- /dev/null
+++ b/components/vc-tree-select/assets/index.less
@@ -0,0 +1,2 @@
+@import "./select.less";
+@import "./tree.less";
diff --git a/components/vc-tree-select/assets/loading.gif b/components/vc-tree-select/assets/loading.gif
new file mode 100644
index 000000000..e8c289293
Binary files /dev/null and b/components/vc-tree-select/assets/loading.gif differ
diff --git a/components/vc-tree-select/assets/minus.gif b/components/vc-tree-select/assets/minus.gif
new file mode 100644
index 000000000..d561d36a9
Binary files /dev/null and b/components/vc-tree-select/assets/minus.gif differ
diff --git a/components/vc-tree-select/assets/select.less b/components/vc-tree-select/assets/select.less
new file mode 100644
index 000000000..7efc6c2e4
--- /dev/null
+++ b/components/vc-tree-select/assets/select.less
@@ -0,0 +1,500 @@
+@selectPrefixCls: rc-tree-select;
+
+.effect() {
+ animation-duration: .3s;
+ animation-fill-mode: both;
+ transform-origin: 0 0;
+}
+
+.@{selectPrefixCls} {
+ box-sizing: border-box;
+ display: inline-block;
+ position: relative;
+ vertical-align: middle;
+ color: #666;
+
+ &-allow-clear {
+ .@{selectPrefixCls}-selection--single .@{selectPrefixCls}-selection__rendered {
+ padding-right: 40px;
+ }
+ }
+
+ ul, li {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ }
+
+ > ul > li > a {
+ padding: 0;
+ background-color: #fff;
+ }
+
+ // arrow
+ &-arrow {
+ height: 26px;
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ width: 20px;
+ b {
+ border-color: #999999 transparent transparent transparent;
+ border-style: solid;
+ border-width: 5px 4px 0 4px;
+ height: 0;
+ width: 0;
+ margin-left: -4px;
+ margin-top: -2px;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ }
+ }
+
+ &-selection {
+ outline: none;
+ user-select: none;
+ -webkit-user-select: none;
+
+ box-sizing: border-box;
+ display: block;
+
+ background-color: #fff;
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+
+ &__clear {
+ font-weight: bold;
+ position: absolute;
+
+ &:after {
+ content: '×'
+ }
+ }
+ }
+
+ &-enabled &-selection {
+ &:hover {
+ border-color: #23c0fa;
+ box-shadow: 0 0 2px fadeout(#2db7f5, 20%);
+ }
+ &:active {
+ border-color: #2db7f5;
+ }
+ }
+
+ &-selection--single {
+ height: 28px;
+ cursor: pointer;
+ position: relative;
+
+ .@{selectPrefixCls}-selection__rendered {
+ display: block;
+ padding-left: 10px;
+ padding-right: 20px;
+ line-height: 28px;
+ }
+
+ .@{selectPrefixCls}-selection-selected-value {
+ display: block;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ }
+
+ .@{selectPrefixCls}-selection__clear {
+ top: 5px;
+ right: 20px;
+ }
+ }
+
+ &-disabled {
+ color: #ccc;
+ cursor: not-allowed;
+
+ .@{selectPrefixCls}-selection--single,
+ .@{selectPrefixCls}-selection__choice__remove {
+ cursor: not-allowed;
+ color: #ccc;
+
+ &:hover {
+ cursor: not-allowed;
+ color: #ccc;
+ }
+ }
+ }
+
+ &-search__field__wrap {
+ display: inline-block;
+ position: relative;
+ }
+
+ &-search__field__placeholder {
+ position: absolute;
+ top: 0;
+ left: 3px;
+ color: #aaa;
+ }
+
+ &-search__field__mirror {
+ position: absolute;
+ top: 0;
+ left: -9999px;
+ white-space: pre;
+ pointer-events: none;
+ }
+
+ &-search--inline {
+ float: left;
+ width: 100%;
+ .@{selectPrefixCls}-search__field__wrap {
+ width: 100%;
+ }
+ .@{selectPrefixCls}-search__field {
+ border: none;
+ font-size: 100%;
+ //margin-top: 5px;
+ background: transparent;
+ outline: 0;
+ width: 100%;
+ }
+ > i {
+ float: right;
+ }
+ }
+
+ &-enabled&-selection--multiple {
+ cursor: text;
+ }
+
+ &-selection--multiple {
+ min-height: 28px;
+
+ .@{selectPrefixCls}-search--inline {
+ width: auto;
+ .@{selectPrefixCls}-search__field {
+ width: 0.75em;
+ }
+ }
+
+ .@{selectPrefixCls}-search__field__placeholder {
+ top: 5px;
+ left: 8px;
+ }
+
+ .@{selectPrefixCls}-selection__rendered {
+ //display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ padding-left: 8px;
+ padding-bottom: 2px;
+ padding-right: 10px;
+ }
+
+ > ul > li {
+ margin-top: 4px;
+ height: 20px;
+ line-height: 20px;
+ }
+
+ .@{selectPrefixCls}-selection__clear {
+ top: 5px;
+ right: 8px;
+ }
+ }
+
+ &-enabled {
+ .@{selectPrefixCls}-selection__choice {
+ cursor: default;
+ &:hover {
+ .@{selectPrefixCls}-selection__choice__remove {
+ opacity: 1;
+ transform: scale(1);
+ }
+ .@{selectPrefixCls}-selection__choice__remove +
+ .@{selectPrefixCls}-selection__choice__content {
+ margin-left: -8px;
+ margin-right: 8px;
+ }
+ }
+ }
+ }
+
+ & &-selection__choice {
+ background-color: #f3f3f3;
+ border-radius: 4px;
+ float: left;
+ padding: 0 15px;
+ margin-right: 4px;
+ position: relative;
+ overflow: hidden;
+ transition: padding .3s cubic-bezier(0.6, -0.28, 0.735, 0.045), width .3s cubic-bezier(0.6, -0.28, 0.735, 0.045);
+
+ &__content {
+ margin-left: 0;
+ margin-right: 0;
+ transition: margin .3s cubic-bezier(0.165, 0.84, 0.44, 1);
+ }
+
+ &-zoom-enter, &-zoom-appear, &-zoom-leave {
+ .effect();
+ opacity: 0;
+ animation-play-state: paused;
+ animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ }
+
+ &-zoom-leave {
+ opacity: 1;
+ animation-timing-function: cubic-bezier(0.6, -0.28, 0.735, 0.045);
+ }
+
+ &-zoom-enter.@{selectPrefixCls}-selection__choice-zoom-enter-active,
+ &-zoom-appear.@{selectPrefixCls}-selection__choice-zoom-appear-active {
+ animation-play-state: running;
+ animation-name: rcSelectChoiceZoomIn;
+ }
+
+ &-zoom-leave.@{selectPrefixCls}-selection__choice-zoom-leave-active {
+ animation-play-state: running;
+ animation-name: rcSelectChoiceZoomOut;
+ }
+
+ @keyframes rcSelectChoiceZoomIn {
+ 0% {
+ transform: scale(0.6);
+ opacity: 0;
+ }
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+ }
+
+ @keyframes rcSelectChoiceZoomOut {
+ to {
+ transform: scale(0);
+ opacity: 0;
+ }
+ }
+
+ &__remove {
+ color: #919191;
+ cursor: pointer;
+ font-weight: bold;
+ padding: 0 0 0 8px;
+ position: absolute;
+ opacity: 0;
+ transform: scale(0);
+ top: 0;
+ right: 2px;
+ transition: opacity .3s, transform .3s;
+ &:before {
+ content: '×'
+ }
+
+ &:hover {
+ color: #333;
+ }
+ }
+ }
+
+ &-dropdown {
+ background-color: white;
+ border: 1px solid #d9d9d9;
+ box-shadow: 0 0px 4px #d9d9d9;
+ border-radius: 4px;
+ box-sizing: border-box;
+ z-index: 100;
+ left: -9999px;
+ top: -9999px;
+ //border-top: none;
+ //border-top-left-radius: 0;
+ //border-top-right-radius: 0;
+ position: absolute;
+ outline: none;
+
+ &-hidden {
+ display: none;
+ }
+
+ &-menu {
+ outline: none;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ z-index: 9999;
+
+ > li {
+ margin: 0;
+ padding: 0;
+ }
+
+ &-item-group-list {
+ margin: 0;
+ padding: 0;
+
+ > li.@{selectPrefixCls}-menu-item {
+ padding-left: 20px;
+ }
+ }
+
+ &-item-group-title {
+ color: #999;
+ line-height: 1.5;
+ padding: 8px 10px;
+ border-bottom: 1px solid #dedede;
+ }
+
+ li&-item {
+ margin: 0;
+ position: relative;
+ display: block;
+ padding: 7px 10px;
+ font-weight: normal;
+ color: #666666;
+ white-space: nowrap;
+
+ &-selected {
+ background-color: #ddd;
+ }
+
+ &-active {
+ background-color: #5897fb;
+ color: white;
+ cursor: pointer;
+ }
+
+ &-disabled {
+ color: #ccc;
+ cursor: not-allowed;
+ }
+
+ &-divider {
+ height: 1px;
+ margin: 1px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ line-height: 0;
+ }
+ }
+ }
+
+ &-slide-up-enter, &-slide-up-appear {
+ .effect();
+ opacity: 0;
+ animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
+ animation-play-state: paused;
+ }
+
+ &-slide-up-leave {
+ .effect();
+ opacity: 1;
+ animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
+ animation-play-state: paused;
+ }
+
+ &-slide-up-enter&-slide-up-enter-active&-placement-bottomLeft, &-slide-up-appear&-slide-up-appear-active&-placement-bottomLeft {
+ animation-name: rcSelectDropdownSlideUpIn;
+ animation-play-state: running;
+ }
+
+ &-slide-up-leave&-slide-up-leave-active&-placement-bottomLeft {
+ animation-name: rcSelectDropdownSlideUpOut;
+ animation-play-state: running;
+ }
+
+ &-slide-up-enter&-slide-up-enter-active&-placement-topLeft, &-slide-up-appear&-slide-up-appear-active&-placement-topLeft {
+ animation-name: rcSelectDropdownSlideDownIn;
+ animation-play-state: running;
+ }
+
+ &-slide-up-leave&-slide-up-leave-active&-placement-topLeft {
+ animation-name: rcSelectDropdownSlideDownOut;
+ animation-play-state: running;
+ }
+
+ @keyframes rcSelectDropdownSlideUpIn {
+ 0% {
+ opacity: 0;
+ transform-origin: 0% 0%;
+ transform: scaleY(0);
+ }
+ 100% {
+ opacity: 1;
+ transform-origin: 0% 0%;
+ transform: scaleY(1);
+ }
+ }
+ @keyframes rcSelectDropdownSlideUpOut {
+ 0% {
+ opacity: 1;
+ transform-origin: 0% 0%;
+ transform: scaleY(1);
+ }
+ 100% {
+ opacity: 0;
+ transform-origin: 0% 0%;
+ transform: scaleY(0);
+ }
+ }
+
+ @keyframes rcSelectDropdownSlideDownIn {
+ 0% {
+ opacity: 0;
+ transform-origin: 0% 100%;
+ transform: scaleY(0);
+ }
+ 100% {
+ opacity: 1;
+ transform-origin: 0% 100%;
+ transform: scaleY(1);
+ }
+ }
+ @keyframes rcSelectDropdownSlideDownOut {
+ 0% {
+ opacity: 1;
+ transform-origin: 0% 100%;
+ transform: scaleY(1);
+ }
+ 100% {
+ opacity: 0;
+ transform-origin: 0% 100%;
+ transform: scaleY(0);
+ }
+ }
+ }
+
+ &-dropdown-search {
+ display: block;
+ padding: 4px;
+ .@{selectPrefixCls}-search__field__wrap {
+ width: 100%;
+ }
+ .@{selectPrefixCls}-search__field__placeholder {
+ top: 4px;
+ }
+ .@{selectPrefixCls}-search__field {
+ padding: 4px;
+ width: 100%;
+ box-sizing: border-box;
+ border: 1px solid #d9d9d9;
+ border-radius: 4px;
+ outline: none;
+ }
+ &.@{selectPrefixCls}-search--hide {
+ display: none;
+ }
+ }
+
+ &-open {
+ .@{selectPrefixCls}-arrow b {
+ border-color: transparent transparent #888 transparent;
+ border-width: 0 4px 5px 4px;
+ }
+ }
+
+ &-not-found {
+ display: inline-block;
+ padding: 8px;
+ }
+}
diff --git a/components/vc-tree-select/assets/tree.less b/components/vc-tree-select/assets/tree.less
new file mode 100644
index 000000000..50744851a
--- /dev/null
+++ b/components/vc-tree-select/assets/tree.less
@@ -0,0 +1,164 @@
+@treePrefixCls: rc-tree-select-tree;
+.@{treePrefixCls} {
+ margin: 0;
+ padding: 5px;
+ li {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ white-space: nowrap;
+ outline: 0;
+ a[draggable],
+ a[draggable="true"] {
+ 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 {
+ > a[draggable] {
+ background-color: #316ac5;
+ color: white;
+ border: 1px #316ac5 solid;
+ opacity: 0.8;
+ }
+ }
+ &.drag-over-gap-top {
+ > a[draggable] {
+ border-top: 2px blue solid;
+ }
+ }
+ &.drag-over-gap-bottom {
+ > a[draggable] {
+ border-bottom: 2px blue solid;
+ }
+ }
+ &.filter-node {
+ > a {
+ color: #a60000!important;
+ font-weight: bold!important;
+ }
+ }
+ ul {
+ margin: 0;
+ padding: 0 0 0 18px;
+ &.@{treePrefixCls}-line {
+ background: url("data:image/gif;base64,R0lGODlhCQACAIAAAMzMzP///yH5BAEAAAEALAAAAAAJAAIAAAIEjI9pUAA7") 0 0 repeat-y;
+ }
+ }
+ a {
+ 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_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 {
+ &-noop {
+ cursor: auto;
+ background: none;
+ }
+ &_open {
+ background-position: -93px -56px;
+ }
+ &_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;
+ }
+ }
+ }
+ }
+ }
+ &-child-tree {
+ display: none;
+ &-open {
+ display: block;
+ }
+ }
+ &-treenode-disabled {
+ >span,
+ >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;
+ }
+}
diff --git a/components/vc-tree-select/demo/basic.jsx b/components/vc-tree-select/demo/basic.jsx
new file mode 100644
index 000000000..2eaf66b6d
--- /dev/null
+++ b/components/vc-tree-select/demo/basic.jsx
@@ -0,0 +1,347 @@
+/* eslint react/no-multi-comp:0, no-console:0, no-alert: 0 */
+
+import '../assets/index.less'
+import './demo.less'
+
+import '../../vc-dialog/assets/index.less'
+import Dialog from '../../vc-dialog'
+import TreeSelect, { TreeNode, SHOW_PARENT } from '../index'
+import { gData } from './util'
+
+function isLeaf (value) {
+ if (!value) {
+ return false
+ }
+ let queues = [...gData]
+ while (queues.length) { // BFS
+ const item = queues.shift()
+ if (item.value === value) {
+ if (!item.children) {
+ return true
+ }
+ return false
+ }
+ if (item.children) {
+ queues = queues.concat(item.children)
+ }
+ }
+ return false
+}
+
+function findPath (value, data) {
+ const sel = []
+ function loop (selected, children) {
+ for (let i = 0; i < children.length; i++) {
+ const item = children[i]
+ if (selected === item.value) {
+ sel.push(item)
+ return
+ }
+ if (item.children) {
+ loop(selected, item.children, item)
+ if (sel.length) {
+ sel.push(item)
+ return
+ }
+ }
+ }
+ }
+ loop(value, data)
+ return sel
+}
+
+export default {
+ data () {
+ return {
+ tsOpen: false,
+ visible: false,
+ inputValue: '0-0-0-label',
+ value: '0-0-0-value1',
+ // value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
+ lv: { value: '0-0-0-value', label: 'spe label' },
+ multipleValue: [],
+ simpleTreeData: [
+ { key: 1, pId: 0, label: 'test1', value: 'test1' },
+ { key: 121, pId: 0, label: 'test1', value: 'test121' },
+ { key: 11, pId: 1, label: 'test11', value: 'test11' },
+ { key: 12, pId: 1, label: 'test12', value: 'test12' },
+ { key: 111, pId: 11, label: 'test111', value: 'test111' },
+ ],
+ treeDataSimpleMode: {
+ id: 'key',
+ rootPId: 0,
+ },
+ }
+ },
+
+ mounted () {
+ // console.log(this.refs.mul.getInputDOMNode());
+ // this.refs.mul.getInputDOMNode().setAttribute('disabled', true);
+ },
+ methods: {
+ onClick () {
+ this.visible = true
+ },
+
+ onClose () {
+ this.visible = false
+ },
+
+ onSearch (value) {
+ console.log(value, arguments)
+ },
+
+ onChange (value) {
+ console.log('onChange', arguments)
+ this.value = value
+ },
+
+ onChangeChildren (value) {
+ console.log('onChangeChildren', arguments)
+ const pre = value ? this.value : undefined
+ this.value = isLeaf(value) ? value : pre
+ },
+
+ onChangeLV (value) {
+ console.log('labelInValue', arguments)
+ if (!value) {
+ this.lv = undefined
+ return
+ }
+ const path = findPath(value.value, gData).map(i => i.label).reverse().join(' > ')
+ this.lv = { value: value.value, label: path }
+ },
+
+ onMultipleChange (value) {
+ console.log('onMultipleChange', arguments)
+ this.multipleValue = value
+ },
+
+ onSelect () {
+ // use onChange instead
+ console.log(...arguments)
+ },
+
+ onDropdownVisibleChange (visible, info) {
+ console.log(visible, this.value, info)
+ if (Array.isArray(this.value) && this.value.length > 1 &&
+ this.value.length < 3) {
+ alert('please select more than two item or less than one item.')
+ return false
+ }
+ return true
+ },
+
+ filterTreeNode (input, child) {
+ return String(child.title).indexOf(input) === 0
+ },
+ },
+
+ render () {
+ return (
+
+
tree-select in dialog
+
+ {this.visible ?
: null}
+
+
single select
+
请下拉选择}
+ searchPlaceholder='please search'
+ showSearch allowClear treeLine
+ inputValue={this.inputValue}
+ value={this.value}
+ treeData={gData}
+ treeNodeFilterProp='label'
+ filterTreeNode={false}
+ onSearch={this.onSearch}
+ open={this.tsOpen}
+ onChange={(value) => {
+ console.log('onChange', value, arguments)
+ if (value === '0-0-0-0-value') {
+ this.tsOpen = true
+ } else {
+ this.tsOpen = false
+ }
+ this.value = value
+ } }
+ dropdownVisibleChange={(v, info) => {
+ console.log('single dropdownVisibleChange', v, info)
+ // document clicked
+ if (info.documentClickClose && this.value === '0-0-0-0-value') {
+ return false
+ }
+ return true
+ } }
+ onSelect={this.onSelect}
+ />
+
+ single select (just select children)
+ 请下拉选择}
+ searchPlaceholder='please search'
+ showSearch allowClear treeLine
+ value={this.value}
+ treeData={gData}
+ treeNodeFilterProp='label'
+ filterTreeNode={false}
+ onChange={this.onChangeChildren}
+ />
+
+ multiple select
+ 请下拉选择}
+ searchPlaceholder='please search'
+ multiple
+ value={this.multipleValue}
+ treeData={gData}
+ treeNodeFilterProp='title'
+ onChange={this.onMultipleChange}
+ onSelect={this.onSelect}
+ allowClear
+ />
+
+ check select
+ 请下拉选择}
+ searchPlaceholder='please search'
+ treeLine maxTagTextLength={10}
+ value={this.value}
+ inputValue={null}
+ treeData={gData}
+ treeNodeFilterProp='title'
+ treeCheckable showCheckedStrategy={SHOW_PARENT}
+ onChange={this.onChange}
+ onSelect={this.onSelect}
+ />
+
+ labelInValue & show path
+ 请下拉选择}
+ searchPlaceholder='please search'
+ showSearch allowClear treeLine
+ value={this.lv} labelInValue
+ treeData={gData}
+ treeNodeFilterProp='label'
+ filterTreeNode={false}
+ onChange={this.onChangeLV}
+ />
+
+ use treeDataSimpleMode
+ 请下拉选择}
+ searchPlaceholder='please search'
+ treeLine maxTagTextLength={10}
+ inputValue={'test111'}
+ value={this.value}
+ treeData={this.simpleTreeData}
+ treeNodeFilterProp='title'
+ treeDataSimpleMode={this.treeDataSimpleMode}
+ treeCheckable showCheckedStrategy={SHOW_PARENT}
+ onChange={this.onChange}
+ onSelect={this.onSelect}
+ />
+
+ Testing in extreme conditions (Boundary conditions test)
+ console.log(val, arguments)}
+ />
+
+ use TreeNode Component (not recommend)
+ console.log(val, arguments)}
+ >
+
+
+
+
+
+
+ sss} key='random3'
+ />
+
+
+
+
+
+
+
+
+
+
+
+ )
+ },
+}
+
diff --git a/components/vc-tree-select/demo/big-data-generator.jsx b/components/vc-tree-select/demo/big-data-generator.jsx
new file mode 100644
index 000000000..7166c79aa
--- /dev/null
+++ b/components/vc-tree-select/demo/big-data-generator.jsx
@@ -0,0 +1,63 @@
+import PropTypes from '../../_util/vue-types'
+import { generateData, calcTotal } from './util'
+
+const Gen = {
+ props: {
+ x: PropTypes.number.def(20),
+ y: PropTypes.number.def(18),
+ z: PropTypes.number.def(1),
+ },
+ data () {
+ return {
+ nums: '',
+ }
+ },
+
+ mounted () {
+ this.$refs.x.value = this.x
+ this.$refs.y.value = this.y
+ this.$refs.z.value = this.z
+ const vals = this.getVals()
+ this.$emit('gen', generateData(vals.x, vals.y, vals.z))
+ },
+ methods: {
+ onGen (e) {
+ e.preventDefault()
+ const vals = this.getVals()
+ this.$emit('gen', generateData(vals.x, vals.y, vals.z))
+ this.nums = calcTotal(vals.x, vals.y, vals.z)
+ },
+ getVals () {
+ return {
+ x: parseInt(this.$refs.x.value, 10),
+ y: parseInt(this.$refs.y.value, 10),
+ z: parseInt(this.$refs.z.value, 10),
+ }
+ },
+ },
+
+ render () {
+ const { x, y, z } = this
+ return (
+
big data generator
+
+
+ x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
+
+
)
+ },
+}
+
+export default Gen
diff --git a/components/vc-tree-select/demo/big-data.jsx b/components/vc-tree-select/demo/big-data.jsx
new file mode 100644
index 000000000..9ca93ec24
--- /dev/null
+++ b/components/vc-tree-select/demo/big-data.jsx
@@ -0,0 +1,78 @@
+/* eslint react/no-multi-comp:0, no-console:0 */
+
+import '../assets/index.less'
+import './demo.less'
+import TreeSelect, { SHOW_PARENT } from '../index'
+import Gen from './big-data-generator'
+
+export default {
+ data () {
+ return {
+ gData: [],
+ gData1: [],
+ value: '',
+ value1: '',
+ }
+ },
+ methods: {
+ onChange (value) {
+ console.log('onChange', arguments)
+ this.value = value
+ },
+
+ onChangeStrictly (value1) {
+ console.log('onChangeStrictly', arguments)
+ const ind = parseInt(Math.random() * 3, 10)
+ value1.push({ value: `0-0-0-${ind}-value`, label: `0-0-0-${ind}-label`, halfChecked: true })
+ this.value1 = value1
+ },
+
+ onGen (data) {
+ Object.assign(this.$data, {
+ gData: data,
+ gData1: [...data],
+ value: '0-0-0-value',
+ value1: [
+ { value: '0-0-value', label: '0-0-label', halfChecked: true },
+ { value: '0-0-0-value', label: '0-0-0-label' },
+ ],
+ // value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
+ })
+ },
+ },
+
+ render () {
+ return (
+
+
+
+
normal check
+ 请下拉选择}
+ treeCheckable
+ showCheckedStrategy={SHOW_PARENT}
+ onChange={this.onChange}
+ />
+
+
+
checkStrictly
+ 请下拉选择}
+ treeCheckable
+ treeCheckStrictly
+ showCheckedStrategy={SHOW_PARENT}
+ onChange={this.onChangeStrictly}
+ />
+
+
+
)
+ },
+}
diff --git a/components/vc-tree-select/demo/demo.less b/components/vc-tree-select/demo/demo.less
new file mode 100644
index 000000000..e762c52e6
--- /dev/null
+++ b/components/vc-tree-select/demo/demo.less
@@ -0,0 +1,15 @@
+
+.rc-tree-select-selection--multiple {
+ max-height: 50px;
+ overflow-y: scroll;
+}
+.rc-tree-select-dropdown {
+ max-height: 350px;
+ overflow-y: scroll;
+}
+.check-select {
+ width: 300px;
+ .rc-tree-select-selection--multiple {
+ min-height: 50px;
+ }
+}
diff --git a/components/vc-tree-select/demo/disable.jsx b/components/vc-tree-select/demo/disable.jsx
new file mode 100644
index 000000000..2600215d8
--- /dev/null
+++ b/components/vc-tree-select/demo/disable.jsx
@@ -0,0 +1,80 @@
+/* eslint react/no-multi-comp:0, no-console:0 */
+import '../assets/index.less'
+import TreeSelect from '../index'
+
+const SHOW_PARENT = TreeSelect.SHOW_PARENT
+
+const treeData = [{
+ label: 'Node1',
+ value: '0-0',
+ key: '0-0',
+ children: [{
+ label: 'Child Node1',
+ value: '0-0-0',
+ key: '0-0-0',
+ }],
+}, {
+ label: 'Node2',
+ value: '0-1',
+ key: '0-1',
+ children: [{
+ label: 'Child Node3',
+ value: '0-1-0',
+ key: '0-1-0',
+ }, {
+ label: 'Child Node4',
+ value: '0-1-1',
+ key: '0-1-1',
+ }, {
+ label: 'Child Node5',
+ value: '0-1-2',
+ key: '0-1-2',
+ }],
+}]
+
+export default {
+ data () {
+ return {
+ value: ['0-0-0'],
+ disabled: false,
+ }
+ },
+
+ methods: {
+ onChange (value) {
+ console.log('onChange ', value, arguments)
+ this.value = value
+ },
+ switch (checked) {
+ this.disabled = checked
+ },
+ },
+
+ render () {
+ const tProps = {
+ props: {
+ treeData,
+ disabled: this.disabled,
+ value: this.value,
+ multiple: true,
+ allowClear: true,
+ treeCheckable: true,
+ showCheckedStrategy: SHOW_PARENT,
+ searchPlaceholder: 'Please select',
+ },
+ on: {
+ change: this.onChange,
+ },
+ style: {
+ width: '300px',
+ },
+ }
+ return (
+
+
+ this.switch(e.target.checked)}/> 禁用
+
+ )
+ },
+}
+
diff --git a/components/vc-tree-select/demo/dynamic.jsx b/components/vc-tree-select/demo/dynamic.jsx
new file mode 100644
index 000000000..1e92fc062
--- /dev/null
+++ b/components/vc-tree-select/demo/dynamic.jsx
@@ -0,0 +1,55 @@
+/* eslint react/no-multi-comp:0, no-console:0 */
+
+import '../assets/index.less'
+import TreeSelect from '../index'
+import { getNewTreeData, generateTreeNodes } from './util'
+
+export default {
+ data () {
+ return {
+ treeData: [
+ { label: 'pNode 01', value: '0-0', key: '0-0' },
+ { label: 'pNode 02', value: '0-1', key: '0-1' },
+ { label: 'pNode 03', value: '0-2', key: '0-2', isLeaf: true },
+ ],
+ // value: '0-0',
+ value: { value: '0-0-0-value', label: '0-0-0-label' },
+ }
+ },
+
+ methods: {
+ onChange (value) {
+ console.log(value)
+ this.value = value
+ },
+
+ onLoadData (treeNode) {
+ console.log(treeNode)
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ const treeData = [...this.treeData]
+ getNewTreeData(treeData, treeNode.eventKey, generateTreeNodes(treeNode), 2)
+ this.treeData = treeData
+ resolve()
+ }, 500)
+ })
+ },
+ },
+
+ render () {
+ return (
+
+
dynamic render
+
+
+ )
+ },
+}
+
diff --git a/components/vc-tree-select/demo/filter.jsx b/components/vc-tree-select/demo/filter.jsx
new file mode 100644
index 000000000..36f805386
--- /dev/null
+++ b/components/vc-tree-select/demo/filter.jsx
@@ -0,0 +1,98 @@
+/* eslint react/no-multi-comp:0, no-console:0 */
+
+import '../assets/index.less'
+import TreeSelect, { SHOW_PARENT } from '../index'
+import { gData } from './util'
+
+export default {
+ data () {
+ return {
+ value: '11',
+ // value: ['0-0-0-0-value', '0-0-0-1-value', '0-0-0-2-value'],
+ simpleTreeData: [
+ { key: 1, pId: 0, label: 'a', value: 'a' },
+ { key: 11, pId: 1, label: 'a12', value: 'a12', disabled: true },
+ { key: 111, pId: 11, label: 'a00', value: 'a00', selectable: false },
+ { key: 2, pId: 0, label: 'b', value: 'b' },
+ { key: 20, pId: 2, label: 'b10', value: 'b10' },
+ { key: 21, pId: 2, label: 'b1', value: 'b1' },
+ { key: 22, pId: 2, label: 'b12', value: 'b12' },
+ ],
+ treeDataSimpleMode: {
+ id: 'key',
+ rootPId: 0,
+ },
+ }
+ },
+ methods: {
+ onChange (value) {
+ if (value.length === 1) {
+ // return;
+ }
+ console.log('onChange', arguments, this.simpleTreeData)
+ this.value = value
+ },
+
+ onSelect () {
+ // use onChange instead
+ // console.log(arguments);
+ },
+
+ onDataChange () {
+ const data = [...this.simpleTreeData]
+ data.forEach(i => {
+ if (i.key === 11) {
+ delete i.disabled
+ }
+ if (i.key === 20) {
+ i.disabled = true
+ }
+ })
+ this.simpleTreeData = data
+ },
+ },
+
+ render () {
+ return (
+
+
check select
+ 请下拉选择}
+ searchPlaceholder='please search'
+ treeLine maxTagTextLength={10}
+ value={this.value}
+ treeData={gData}
+ treeNodeFilterProp='title'
+ treeCheckable
+ onChange={this.onChange}
+ onSelect={this.onSelect}
+ />
+
+ use treeDataSimpleMode
+ 请下拉选择}
+ searchPlaceholder='please search'
+ treeLine maxTagTextLength={10}
+ inputValue={null}
+ value={this.value}
+ treeData={this.simpleTreeData}
+ treeDefaultExpandAll
+ treeNodeFilterProp='title'
+ treeDataSimpleMode={this.treeDataSimpleMode}
+ treeCheckable showCheckedStrategy={SHOW_PARENT}
+ onChange={this.onChange}
+ onSelect={this.onSelect}
+ />
+
+
+ )
+ },
+}
+
diff --git a/components/vc-tree-select/demo/form.js b/components/vc-tree-select/demo/form.js
new file mode 100644
index 000000000..f66317371
--- /dev/null
+++ b/components/vc-tree-select/demo/form.js
@@ -0,0 +1,138 @@
+import '../assets/index.less'
+import TreeSelect from '../index'
+import Select from '../../vc-select'
+import { createForm } from '../../vc-form'
+import { regionStyle, errorStyle } from './styles'
+import { gData } from './util'
+import '../../vc-select/assets/index.less'
+import './demo.less'
+
+const { Option } = Select
+
+const TreeSelectInput = {
+ props: [
+ 'multiple',
+ 'treeData',
+ 'treeCheckable',
+ 'value',
+ ],
+ methods: {
+ onChange (value) {
+ console.log(value, arguments)
+ this.$emit('change', value)
+ },
+ },
+
+ render () {
+ return (
+
+ )
+ },
+}
+
+const Form = {
+ methods: {
+ onSubmit (e) {
+ console.log('submit')
+ e.preventDefault()
+ this.form.validateFields((error, values) => {
+ if (!error) {
+ console.log('ok', values)
+ } else {
+ console.log('error', error, values)
+ }
+ })
+ },
+ reset (e) {
+ e.preventDefault()
+ this.form.resetFields()
+ },
+ },
+
+ render () {
+ const { form } = this
+ const { getFieldDecorator, getFieldError } = form
+ const tProps = {
+ props: {
+ multiple: true,
+ treeData: gData,
+ treeCheckable: true,
+ // treeDefaultExpandAll: true,
+ },
+ }
+ return ()
+ },
+}
+
+export default createForm()(Form)
diff --git a/components/vc-tree-select/demo/styles.js b/components/vc-tree-select/demo/styles.js
new file mode 100644
index 000000000..e0a5fe5ac
--- /dev/null
+++ b/components/vc-tree-select/demo/styles.js
@@ -0,0 +1,11 @@
+export const regionStyle = {
+ border: '1px solid red',
+ marginTop: '10px',
+ padding: '10px',
+}
+
+export const errorStyle = {
+ color: 'red',
+ marginTop: '10px',
+ padding: '10px',
+}
diff --git a/components/vc-tree-select/demo/util.js b/components/vc-tree-select/demo/util.js
new file mode 100644
index 000000000..27fe44d06
--- /dev/null
+++ b/components/vc-tree-select/demo/util.js
@@ -0,0 +1,142 @@
+/* eslint no-loop-func: 0, no-console: 0 */
+
+export function generateData (x = 3, y = 2, z = 1, gData = []) {
+ // x:每一级下的节点总数。y:每级节点里有y个节点、存在子节点。z:树的level层级数(0表示一级)
+ function _loop (_level, _preKey, _tns) {
+ const preKey = _preKey || '0'
+ const tns = _tns || gData
+
+ const children = []
+ for (let i = 0; i < x; i++) {
+ const key = `${preKey}-${i}`
+ tns.push({
+ label: `${key}-label`,
+ value: `${key}-value`,
+ key,
+ disabled: key === '0-0-0-1' || false,
+ })
+ if (i < y) {
+ children.push(key)
+ }
+ }
+ if (_level < 0) {
+ return tns
+ }
+ const __level = _level - 1
+ children.forEach((key, index) => {
+ tns[index].children = []
+ return _loop(__level, key, tns[index].children)
+ })
+ }
+ _loop(z)
+ return gData
+}
+export function calcTotal (x = 3, y = 2, z = 1) {
+ /* eslint no-param-reassign:0*/
+ const rec = (n) => n >= 0 ? x * Math.pow(y, n--) + rec(n) : 0
+ return rec(z + 1)
+}
+console.log('总节点数(单个tree):', calcTotal())
+export const gData = generateData()
+
+export function generateTreeNodes (treeNode) {
+ const arr = []
+ const key = treeNode.eventKey
+ for (let i = 0; i < 3; i++) {
+ arr.push({ label: `${key}-${i}-label`, value: `${key}-${i}-value`, key: `${key}-${i}` })
+ }
+ return arr
+}
+
+function setLeaf (treeData, curKey, level) {
+ const loopLeaf = (data, lev) => {
+ const l = lev - 1
+ data.forEach((item) => {
+ if ((item.key.length > curKey.length) ? item.key.indexOf(curKey) !== 0
+ : curKey.indexOf(item.key) !== 0) {
+ return
+ }
+ if (item.children) {
+ loopLeaf(item.children, l)
+ } else if (l < 1) {
+ item.isLeaf = true
+ }
+ })
+ }
+ loopLeaf(treeData, level + 1)
+}
+
+export function getNewTreeData (treeData, curKey, child, level) {
+ const loop = (data) => {
+ if (level < 1 || curKey.length - 3 > level * 2) return
+ data.forEach((item) => {
+ if (curKey.indexOf(item.key) === 0) {
+ if (item.children) {
+ loop(item.children)
+ } else {
+ item.children = child
+ }
+ }
+ })
+ }
+ loop(treeData)
+ setLeaf(treeData, curKey, level)
+}
+
+function loopData (data, callback) {
+ const loop = (d, level = 0) => {
+ d.forEach((item, index) => {
+ const pos = `${level}-${index}`
+ if (item.children) {
+ loop(item.children, pos)
+ }
+ callback(item, index, pos)
+ })
+ }
+ loop(data)
+}
+
+function isPositionPrefix (smallPos, bigPos) {
+ if (bigPos.length < smallPos.length) {
+ return false
+ }
+ // attention: "0-0-1" "0-0-10"
+ if ((bigPos.length > smallPos.length) && (bigPos.charAt(smallPos.length) !== '-')) {
+ return false
+ }
+ return bigPos.substr(0, smallPos.length) === smallPos
+}
+// console.log(isPositionPrefix("0-1", "0-10-1"));
+
+export function getFilterValue (val, sVal, delVal) {
+ const allPos = []
+ const delPos = []
+ loopData(gData, (item, index, pos) => {
+ if (sVal.indexOf(item.value) > -1) {
+ allPos.push(pos)
+ }
+ if (delVal.indexOf(item.value) > -1) {
+ delPos.push(pos)
+ }
+ })
+ const newPos = []
+ delPos.forEach((item) => {
+ allPos.forEach((i) => {
+ if (isPositionPrefix(item, i) || isPositionPrefix(i, item)) {
+ // 过滤掉 父级节点 和 所有子节点。
+ // 因为 node节点 不选时,其 父级节点 和 所有子节点 都不选。
+ return
+ }
+ newPos.push(i)
+ })
+ })
+ const newVal = []
+ if (newPos.length) {
+ loopData(gData, (item, index, pos) => {
+ if (newPos.indexOf(pos) > -1) {
+ newVal.push(item.value)
+ }
+ })
+ }
+ return newVal
+}
diff --git a/components/vc-tree-select/index.js b/components/vc-tree-select/index.js
new file mode 100644
index 000000000..f35201e54
--- /dev/null
+++ b/components/vc-tree-select/index.js
@@ -0,0 +1,7 @@
+// rc-tree-select 1.12.13 tag
+// export this package's api
+import TreeSelect from './src'
+
+export default TreeSelect
+
+export { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './src'
diff --git a/components/vc-tree-select/src/PropTypes.js b/components/vc-tree-select/src/PropTypes.js
new file mode 100644
index 000000000..ac869461b
--- /dev/null
+++ b/components/vc-tree-select/src/PropTypes.js
@@ -0,0 +1,108 @@
+import PropTypes from '../../_util/vue-types'
+import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
+
+function nonEmptyStringType (props, propsName) {
+ const value = props[propsName]
+ if (typeof value !== 'string' || !value) {
+ return new Error() // Just a flag, so don't need message.
+ }
+}
+
+function valueType (props, propName, componentName) {
+ const labelInValueShape = PropTypes.shape({
+ value: nonEmptyStringType,
+ label: PropTypes.node,
+ })
+ if (props.labelInValue) {
+ const validate = PropTypes.oneOfType([
+ PropTypes.arrayOf(labelInValueShape),
+ labelInValueShape,
+ ])
+ const error = validate(...arguments)
+ if (error) {
+ return new Error(
+ `Invalid prop \`${propName}\` supplied to \`${componentName}\`, ` +
+ `when \`labelInValue\` is \`true\`, \`${propName}\` should in ` +
+ `shape of \`{ value: string, label?: string }\`.`
+ )
+ }
+ } else if (props.treeCheckable && props.treeCheckStrictly) {
+ const validate = PropTypes.oneOfType([
+ PropTypes.arrayOf(labelInValueShape),
+ labelInValueShape,
+ ])
+ const error = validate(...arguments)
+ if (error) {
+ return new Error(
+ `Invalid prop \`${propName}\` supplied to \`${componentName}\`, ` +
+ `when \`treeCheckable\` and \`treeCheckStrictly\` are \`true\`, ` +
+ `\`${propName}\` should in shape of \`{ value: string, label?: string }\`.`
+ )
+ }
+ } else if (props.multiple && props[propName] === '') {
+ return new Error(
+ `Invalid prop \`${propName}\` of type \`string\` supplied to \`${componentName}\`, ` +
+ `expected \`array\` when \`multiple\` is \`true\`.`
+ )
+ } else {
+ const validate = PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.string),
+ PropTypes.string,
+ ])
+ return validate(...arguments)
+ }
+}
+
+export const SelectPropTypes = {
+ // className: PropTypes.string,
+ prefixCls: PropTypes.string,
+ multiple: PropTypes.bool,
+ filterTreeNode: PropTypes.any,
+ showSearch: PropTypes.bool,
+ disabled: PropTypes.bool,
+ showArrow: PropTypes.bool,
+ allowClear: PropTypes.bool,
+ defaultOpen: PropTypes.bool,
+ open: PropTypes.bool,
+ transitionName: PropTypes.string,
+ animation: PropTypes.string,
+ choiceTransitionName: PropTypes.string,
+ // onClick: PropTypes.func,
+ // onChange: PropTypes.func,
+ // onSelect: PropTypes.func,
+ // onDeselect: PropTypes.func,
+ // onSearch: PropTypes.func,
+ searchPlaceholder: PropTypes.string,
+ placeholder: PropTypes.any,
+ inputValue: PropTypes.any,
+ value: PropTypes.any,
+ defaultValue: PropTypes.any,
+ label: PropTypes.any, // vnode
+ defaultLabel: PropTypes.any,
+ labelInValue: PropTypes.bool,
+ dropdownClassName: PropTypes.string,
+ dropdownStyle: PropTypes.object,
+ dropdownPopupAlign: PropTypes.object,
+ dropdownVisibleChange: PropTypes.func,
+ maxTagTextLength: PropTypes.number,
+ showCheckedStrategy: PropTypes.oneOf([
+ SHOW_ALL, SHOW_PARENT, SHOW_CHILD,
+ ]),
+ treeCheckStrictly: PropTypes.bool,
+ treeIcon: PropTypes.bool,
+ treeLine: PropTypes.bool,
+ treeDefaultExpandAll: PropTypes.bool,
+ treeCheckable: PropTypes.any, // bool vnode
+ treeNodeLabelProp: PropTypes.string,
+ treeNodeFilterProp: PropTypes.string,
+ treeData: PropTypes.array,
+ treeDataSimpleMode: PropTypes.oneOfType([
+ PropTypes.bool,
+ PropTypes.object,
+ ]),
+ loadData: PropTypes.func,
+ dropdownMatchSelectWidth: PropTypes.bool,
+ notFoundContent: PropTypes.any,
+ children: PropTypes.any,
+ autoFocus: PropTypes.bool,
+}
diff --git a/components/vc-tree-select/src/Select.jsx b/components/vc-tree-select/src/Select.jsx
new file mode 100644
index 000000000..8e1580c56
--- /dev/null
+++ b/components/vc-tree-select/src/Select.jsx
@@ -0,0 +1,1045 @@
+import PropTypes from '../../_util/vue-types'
+import KeyCode from '../../_util/KeyCode'
+import classnames from 'classnames'
+import pick from 'lodash/pick'
+import omit from 'omit.js'
+import {
+ getPropValue, getValuePropValue,
+ isMultiple, toArray,
+ UNSELECTABLE_ATTRIBUTE, UNSELECTABLE_STYLE,
+ preventDefaultEvent,
+ getTreeNodesStates, flatToHierarchy, filterParentPosition,
+ isPositionPrefix, labelCompatible, loopAllChildren, filterAllCheckedData,
+ processSimpleTreeData, toTitle,
+} from './util'
+import SelectTrigger from './SelectTrigger'
+import _TreeNode from './TreeNode'
+import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
+import { SelectPropTypes } from './PropTypes'
+import { initDefaultProps, getOptionProps, hasProp, getAllProps, getComponentFromProp } from '../../_util/props-util'
+import BaseMixin from '../../_util/BaseMixin'
+import getTransitionProps from '../../_util/getTransitionProps'
+
+function noop () {
+}
+
+function filterFn (input, child) {
+ return String(getPropValue(child, labelCompatible(this.$props.treeNodeFilterProp)))
+ .indexOf(input) > -1
+}
+
+const defaultProps = {
+ prefixCls: 'rc-tree-select',
+ // filterTreeNode: filterFn, // [Legacy] TODO: Set false and filter not hide?
+ showSearch: true,
+ allowClear: false,
+ // placeholder: '',
+ // searchPlaceholder: '',
+ labelInValue: false,
+ // onClick: noop,
+ // onChange: noop,
+ // onSelect: noop,
+ // onDeselect: noop,
+ // onSearch: noop,
+ showArrow: true,
+ dropdownMatchSelectWidth: true,
+ dropdownStyle: {},
+ dropdownVisibleChange: () => { return true },
+ notFoundContent: 'Not Found',
+ showCheckedStrategy: SHOW_CHILD,
+ // skipHandleInitValue: false, // Deprecated (use treeCheckStrictly)
+ treeCheckStrictly: false,
+ treeIcon: false,
+ treeLine: false,
+ treeDataSimpleMode: false,
+ treeDefaultExpandAll: false,
+ treeCheckable: false,
+ treeNodeFilterProp: 'value',
+ treeNodeLabelProp: 'title',
+}
+
+const Select = {
+ mixins: [BaseMixin],
+ name: 'VCTreeSelect',
+ props: initDefaultProps({ ...SelectPropTypes, __propsSymbol__: PropTypes.any }, defaultProps),
+ data () {
+ let value = []
+ const props = getOptionProps(this)
+ this.preProps = { ...props }
+ if ('value' in props) {
+ value = toArray(props.value)
+ } else {
+ value = toArray(props.defaultValue)
+ }
+ // save parsed treeData, for performance (treeData may be very big)
+ this.renderedTreeData = this.renderTreeData()
+ value = this.addLabelToValue(props, value)
+ value = this.getValue(props, value, props.inputValue ? '__strict' : true)
+ const inputValue = props.inputValue || ''
+ // if (props.combobox) {
+ // inputValue = value.length ? String(value[0].value) : '';
+ // }
+ return {
+ sValue: value,
+ sInputValue: inputValue,
+ sOpen: props.open || props.defaultOpen,
+ sFocused: false,
+ }
+ },
+
+ mounted () {
+ this.$nextTick(() => {
+ const { autoFocus, disabled } = this
+ if (isMultiple(this.$props)) {
+ const inputNode = this.getInputDOMNode()
+ if (inputNode.value) {
+ inputNode.style.width = ''
+ inputNode.style.width = `${this.$refs.inputMirrorInstance.clientWidth || this.$refs.inputMirrorInstance.offsetWidth}px`
+ } else {
+ inputNode.style.width = ''
+ }
+ }
+ if (autoFocus && !disabled) {
+ this.focus()
+ }
+ })
+ },
+ watch: {
+ // for performance (use __propsSymbol__ avoid deep watch)
+ __propsSymbol__ () {
+ const nextProps = getOptionProps(this)
+ // save parsed treeData, for performance (treeData may be very big)
+ this.renderedTreeData = this.renderTreeData(nextProps)
+ // Detecting whether the object of `onChange`'s argument is old ref.
+ // Better to do a deep equal later.
+ this._cacheTreeNodesStates = this._cacheTreeNodesStates !== 'no' &&
+ this._savedValue &&
+ nextProps.value === this._savedValue
+ if (this.preProps.treeData !== nextProps.treeData ||
+ this.preProps.children !== nextProps.children) {
+ // refresh this._treeNodesStates cache
+ this._treeNodesStates = getTreeNodesStates(
+ this.renderedTreeData || nextProps.children,
+ this.sValue.map(item => item.value)
+ )
+ }
+ if ('value' in nextProps) {
+ let value = toArray(nextProps.value)
+ value = this.addLabelToValue(nextProps, value)
+ value = this.getValue(nextProps, value)
+ this.setState({
+ sValue: value,
+ }, this.forcePopupAlign)
+ // if (nextProps.combobox) {
+ // this.setState({
+ // inputValue: value.length ? String(value[0].key) : '',
+ // });
+ // }
+ }
+ if (nextProps.inputValue !== this.preProps.inputValue) {
+ this.setState({
+ sInputValue: nextProps.inputValue,
+ })
+ }
+ if ('open' in nextProps) {
+ this.setState({
+ sOpen: nextProps.open,
+ })
+ }
+ this.preProps = { ...nextProps }
+ },
+ },
+
+ beforeUpdate () {
+ if (this._savedValue && this.$props.value &&
+ this.$props.value !== this._savedValue &&
+ this.$props.value === this.preProps.value) {
+ this._cacheTreeNodesStates = false
+ this.getValue(this.$props, this.addLabelToValue(this.$props, toArray(this.$props.value)))
+ }
+ },
+
+ updated () {
+ const state = this.$data
+ const props = this.$props
+ if (state.sOpen && isMultiple(props)) {
+ this.$nextTick(() => {
+ const inputNode = this.getInputDOMNode()
+ if (inputNode.value) {
+ inputNode.style.width = ''
+ inputNode.style.width = `${this.$refs.inputMirrorInstance.clientWidth}px`
+ } else {
+ inputNode.style.width = ''
+ }
+ })
+ }
+ },
+
+ beforeDestroy () {
+ this.clearDelayTimer()
+ if (this.dropdownContainer) {
+ document.body.removeChild(this.dropdownContainer)
+ this.dropdownContainer = null
+ }
+ },
+ methods: {
+ loopTreeData (data, level = 0, treeCheckable) {
+ return data.map((item, index) => {
+ const pos = `${level}-${index}`
+ const {
+ label,
+ value,
+ disabled,
+ key,
+ selectable,
+ children,
+ isLeaf,
+ ...otherProps
+ } = item
+ const tnProps = {
+ ...pick(item, ['on', 'class', 'style']),
+ props: {
+ value,
+ title: label,
+ disabled: disabled || false,
+ selectable: selectable === false ? selectable : !treeCheckable,
+ ...omit(otherProps, ['on', 'class', 'style']),
+ },
+ key: key || value || pos,
+ }
+ let ret
+ if (children && children.length) {
+ ret = (<_TreeNode {...tnProps}>{this.loopTreeData(children, pos, treeCheckable)})
+ } else {
+ ret = (<_TreeNode {...tnProps} isLeaf={isLeaf}/>)
+ }
+ return ret
+ })
+ },
+ onInputChange (event) {
+ const val = event.target.value
+ const { $props: props } = this
+ this.setState({
+ sInputValue: val,
+ sOpen: true,
+ }, this.forcePopupAlign)
+ if (props.treeCheckable && !val) {
+ this.setState({
+ sValue: this.getValue(props, [...this.sValue], false),
+ })
+ }
+ this.__emit('search', val)
+ },
+
+ onDropdownVisibleChange (open) {
+ // selection inside combobox cause click
+ if (!open && document.activeElement === this.getInputDOMNode()) {
+ // return;
+ }
+ this.setOpenState(open, undefined, !open)
+ },
+
+ // combobox ignore
+ onKeyDown (event) {
+ const props = this.$props
+ if (props.disabled) {
+ return
+ }
+ const keyCode = event.keyCode
+ if (this.sOpen && !this.getInputDOMNode()) {
+ this.onInputKeyDown(event)
+ } else if (keyCode === KeyCode.ENTER || keyCode === KeyCode.DOWN) {
+ this.setOpenState(true)
+ event.preventDefault()
+ }
+ },
+
+ onInputKeyDown (event) {
+ const props = this.$props
+ if (props.disabled) {
+ return
+ }
+ const state = this.$data
+ const keyCode = event.keyCode
+ if (isMultiple(props) && !event.target.value && keyCode === KeyCode.BACKSPACE) {
+ const value = state.sValue.concat()
+ if (value.length) {
+ const popValue = value.pop()
+ this.removeSelected(this.isLabelInValue() ? popValue : popValue.value)
+ }
+ return
+ }
+ if (keyCode === KeyCode.DOWN) {
+ if (!state.sOpen) {
+ this.openIfHasChildren()
+ event.preventDefault()
+ event.stopPropagation()
+ return
+ }
+ } else if (keyCode === KeyCode.ESC) {
+ if (state.sOpen) {
+ this.setOpenState(false)
+ event.preventDefault()
+ event.stopPropagation()
+ }
+ return
+ }
+ },
+
+ onSelect (selectedKeys, info) {
+ const item = info.node
+ let value = this.sValue
+ const props = this.$props
+ const selectedValue = getValuePropValue(item)
+ const selectedLabel = this.getLabelFromNode(item)
+ const checkableSelect = props.treeCheckable && info.event === 'select'
+ let event = selectedValue
+ if (this.isLabelInValue()) {
+ event = {
+ value: event,
+ label: selectedLabel,
+ }
+ }
+ if (info.selected === false) {
+ this.onDeselect(info)
+ if (!checkableSelect) return
+ }
+ this.__emit('select', event, item, info)
+
+ const checkEvt = info.event === 'check'
+ if (isMultiple(props)) {
+ this.$nextTick(() => { // clearSearchInput will change sInputValue
+ this.clearSearchInput()
+ })
+ if (checkEvt) {
+ value = this.getCheckedNodes(info, props).map(n => {
+ return {
+ value: getValuePropValue(n),
+ label: this.getLabelFromNode(n),
+ }
+ })
+ } else {
+ if (value.some(i => i.value === selectedValue)) {
+ return
+ }
+ value = value.concat([{
+ value: selectedValue,
+ label: selectedLabel,
+ }])
+ }
+ } else {
+ if (value.length && value[0].value === selectedValue) {
+ this.setOpenState(false)
+ return
+ }
+ value = [{
+ value: selectedValue,
+ label: selectedLabel,
+ }]
+ this.setOpenState(false)
+ }
+
+ const extraInfo = {
+ triggerValue: selectedValue,
+ triggerNode: item,
+ }
+ if (checkEvt) {
+ extraInfo.checked = info.checked
+ // if inputValue existing, tree is checkStrictly
+ extraInfo.allCheckedNodes = props.treeCheckStrictly || this.sInputValue
+ ? info.checkedNodes : flatToHierarchy(info.checkedNodesPositions)
+ this._checkedNodes = info.checkedNodesPositions
+ const _tree = this.getPopupComponentRefs()
+ this._treeNodesStates = _tree.checkKeys
+ } else {
+ extraInfo.selected = info.selected
+ }
+
+ this.fireChange(value, extraInfo)
+ if (props.inputValue === null) {
+ this.setState({
+ sInputValue: '',
+ })
+ }
+ },
+
+ onDeselect (info) {
+ this.removeSelected(getValuePropValue(info.node))
+ if (!isMultiple(this.$props)) {
+ this.setOpenState(false)
+ } else {
+ this.clearSearchInput()
+ }
+ },
+
+ onPlaceholderClick () {
+ this.getInputDOMNode().focus()
+ },
+
+ onClearSelection (event) {
+ const props = this.$props
+ const state = this.$data
+ if (props.disabled) {
+ return
+ }
+ event.stopPropagation()
+ this._cacheTreeNodesStates = 'no'
+ this._checkedNodes = []
+ if (state.sInputValue || state.sValue.length) {
+ this.setOpenState(false)
+ if (typeof props.inputValue === 'undefined') {
+ this.setState({
+ sInputValue: '',
+ }, () => {
+ this.fireChange([])
+ })
+ } else {
+ this.fireChange([])
+ }
+ }
+ },
+
+ onChoiceAnimationLeave () {
+ this.forcePopupAlign()
+ },
+
+ getLabelFromNode (child) {
+ return getPropValue(child, this.$props.treeNodeLabelProp)
+ },
+
+ getLabelFromProps (props, value) {
+ if (value === undefined) {
+ return null
+ }
+ let label = null
+ loopAllChildren(this.renderedTreeData || props.children, item => {
+ if (getValuePropValue(item) === value) {
+ label = this.getLabelFromNode(item)
+ }
+ })
+ if (label === null) {
+ return value
+ }
+ return label
+ },
+
+ getDropdownContainer () {
+ if (!this.dropdownContainer) {
+ this.dropdownContainer = document.createElement('div')
+ document.body.appendChild(this.dropdownContainer)
+ }
+ return this.dropdownContainer
+ },
+
+ getSearchPlaceholderElement (hidden) {
+ const props = this.$props
+ let placeholder
+ if (isMultiple(props)) {
+ placeholder = getComponentFromProp(this, 'placeholder') || getComponentFromProp(this, 'searchPlaceholder')
+ } else {
+ placeholder = getComponentFromProp(this, 'placeholder')
+ }
+ if (placeholder) {
+ return (
+
+ {placeholder}
+
+ )
+ }
+ return null
+ },
+
+ getInputElement () {
+ const { sInputValue } = this.$data
+ const { prefixCls, disabled } = this.$props
+ const multiple = isMultiple(this.$props)
+ const inputListeners = {
+ input: this.onInputChange,
+ keydown: this.onInputKeyDown,
+ }
+ if (multiple) {
+ inputListeners.blur = this.onBlur
+ inputListeners.focus = this.onFocus
+ }
+ return (
+
+
+
+ {sInputValue}
+
+ {isMultiple(this.$props) ? null : this.getSearchPlaceholderElement(!!sInputValue)}
+
+ )
+ },
+
+ getInputDOMNode () {
+ return this.$refs.inputInstance
+ },
+
+ getPopupDOMNode () {
+ return this.$refs.trigger.getPopupDOMNode()
+ },
+
+ getPopupComponentRefs () {
+ return this.$refs.trigger.getPopupEleRefs()
+ },
+
+ getValue (_props, val, init = true) {
+ let value = val
+ // if inputValue existing, tree is checkStrictly
+ const _strict = init === '__strict' ||
+ init && (this.sInputValue ||
+ this.inputValue !== _props.inputValue)
+ if (_props.treeCheckable &&
+ (_props.treeCheckStrictly || _strict)) {
+ this.halfCheckedValues = []
+ value = []
+ val.forEach(i => {
+ if (!i.halfChecked) {
+ value.push(i)
+ } else {
+ this.halfCheckedValues.push(i)
+ }
+ })
+ }
+ // if (!(_props.treeCheckable && !_props.treeCheckStrictly)) {
+ if (!_props.treeCheckable || _props.treeCheckable &&
+ (_props.treeCheckStrictly || _strict)) {
+ return value
+ }
+ let checkedTreeNodes
+ if (this._cachetreeData && this._cacheTreeNodesStates && this._checkedNodes &&
+ !this.sInputValue) {
+ this.checkedTreeNodes = checkedTreeNodes = this._checkedNodes
+ } else {
+ /**
+ * Note: `this._treeNodesStates`'s treeNodesStates must correspond to nodes of the
+ * final tree (`processTreeNode` function from SelectTrigger.jsx produce the final tree).
+ *
+ * And, `this._treeNodesStates` from `onSelect` is previous value,
+ * so it perhaps only have a few nodes, but the newly filtered tree can have many nodes,
+ * thus, you cannot use previous _treeNodesStates.
+ */
+ // getTreeNodesStates is not effective.
+ this._treeNodesStates = getTreeNodesStates(
+ this.renderedTreeData || _props.children,
+ value.map(item => item.value)
+ )
+ this.checkedTreeNodes = checkedTreeNodes = this._treeNodesStates.checkedNodes
+ }
+ const mapLabVal = arr => arr.map(itemObj => {
+ return {
+ value: getValuePropValue(itemObj.node),
+ label: getPropValue(itemObj.node, _props.treeNodeLabelProp),
+ }
+ })
+ const props = this.$props
+ let checkedValues = []
+ if (props.showCheckedStrategy === SHOW_ALL) {
+ checkedValues = mapLabVal(checkedTreeNodes)
+ } else if (props.showCheckedStrategy === SHOW_PARENT) {
+ const posArr = filterParentPosition(checkedTreeNodes.map(itemObj => itemObj.pos))
+ checkedValues = mapLabVal(checkedTreeNodes.filter(
+ itemObj => posArr.indexOf(itemObj.pos) !== -1
+ ))
+ } else {
+ checkedValues = mapLabVal(checkedTreeNodes.filter(itemObj => {
+ return !itemObj.node.componentOptions.children
+ }))
+ }
+ return checkedValues
+ },
+
+ getCheckedNodes (info, props) {
+ // TODO treeCheckable does not support tags/dynamic
+ let { checkedNodes } = info
+ // if inputValue existing, tree is checkStrictly
+ if (props.treeCheckStrictly || this.sInputValue) {
+ return checkedNodes
+ }
+ const checkedNodesPositions = info.checkedNodesPositions
+ if (props.showCheckedStrategy === SHOW_ALL) {
+ // checkedNodes = checkedNodes
+ } else if (props.showCheckedStrategy === SHOW_PARENT) {
+ const posArr = filterParentPosition(checkedNodesPositions.map(itemObj => itemObj.pos))
+ checkedNodes = checkedNodesPositions.filter(itemObj => posArr.indexOf(itemObj.pos) !== -1)
+ .map(itemObj => itemObj.node)
+ } else {
+ checkedNodes = checkedNodes.filter(n => {
+ return !n.componentOptions.children
+ })
+ }
+ return checkedNodes
+ },
+
+ getDeselectedValue (selectedValue) {
+ const checkedTreeNodes = this.checkedTreeNodes
+ let unCheckPos
+ checkedTreeNodes.forEach(itemObj => {
+ const nodeProps = getAllProps(itemObj.node)
+ if (nodeProps.value === selectedValue) {
+ unCheckPos = itemObj.pos
+ }
+ })
+ const newVals = []
+ const newCkTns = []
+ checkedTreeNodes.forEach(itemObj => {
+ if (isPositionPrefix(itemObj.pos, unCheckPos) || isPositionPrefix(unCheckPos, itemObj.pos)) {
+ // Filter ancestral and children nodes when uncheck a node.
+ return
+ }
+ const nodeProps = getAllProps(itemObj.node)
+ newCkTns.push(itemObj)
+ newVals.push(nodeProps.value)
+ })
+ this.checkedTreeNodes = this._checkedNodes = newCkTns
+ const nv = this.sValue.filter(val => newVals.indexOf(val.value) !== -1)
+ this.fireChange(nv, { triggerValue: selectedValue, clear: true })
+ },
+
+ setOpenState (open, needFocus, documentClickClose = false) {
+ this.clearDelayTimer()
+ const { $props: props } = this
+ // can not optimize, if children is empty
+ // if (this.sOpen === open) {
+ // return;
+ // }
+ if (!this.$props.dropdownVisibleChange(open, { documentClickClose })) {
+ return
+ }
+ this.setState({
+ sOpen: open,
+ }, () => {
+ if (needFocus || open) {
+ // Input dom init after first time component render
+ // Add delay for this to get focus
+ setTimeout(() => {
+ if (open || isMultiple(props)) {
+ const input = this.getInputDOMNode()
+ if (input && document.activeElement !== input) {
+ input.focus()
+ }
+ } else if (this.$refs.selection) {
+ this.$refs.selection.focus()
+ }
+ }, 0)
+ }
+ })
+ },
+
+ clearSearchInput () {
+ this.getInputDOMNode().focus()
+ if (!hasProp(this, 'inputValue')) {
+ this.setState({ sInputValue: '' })
+ }
+ },
+
+ addLabelToValue (props, value_) {
+ let value = value_
+ if (this.isLabelInValue()) {
+ value.forEach((v, i) => {
+ if (Object.prototype.toString.call(value[i]) !== '[object Object]') {
+ value[i] = {
+ value: '',
+ label: '',
+ }
+ return
+ }
+ v.label = v.label || this.getLabelFromProps(props, v.value)
+ })
+ } else {
+ value = value.map(v => {
+ return {
+ value: v,
+ label: this.getLabelFromProps(props, v),
+ }
+ })
+ }
+ return value
+ },
+
+ clearDelayTimer () {
+ if (this.delayTimer) {
+ clearTimeout(this.delayTimer)
+ this.delayTimer = null
+ }
+ },
+
+ removeSelected (selectedVal, e) {
+ const props = this.$props
+ if (props.disabled) {
+ return
+ }
+
+ // Do not trigger Trigger popup
+ if (e && e.stopPropagation) {
+ e.stopPropagation()
+ }
+
+ this._cacheTreeNodesStates = 'no'
+ if (props.treeCheckable &&
+ (props.showCheckedStrategy === SHOW_ALL || props.showCheckedStrategy === SHOW_PARENT) &&
+ !(props.treeCheckStrictly || this.sInputValue)) {
+ this.getDeselectedValue(selectedVal)
+ return
+ }
+ // click the node's `x`(in select box), likely trigger the TreeNode's `unCheck` event,
+ // cautiously, they are completely different, think about it, the tree may not render at first,
+ // but the nodes in select box are ready.
+ let label
+ const value = this.sValue.filter((singleValue) => {
+ if (singleValue.value === selectedVal) {
+ label = singleValue.label
+ }
+ return (singleValue.value !== selectedVal)
+ })
+ const canMultiple = isMultiple(props)
+
+ if (canMultiple) {
+ let event = selectedVal
+ if (this.isLabelInValue()) {
+ event = {
+ value: selectedVal,
+ label,
+ }
+ }
+ this.__emit('deselect', event)
+ }
+ if (props.treeCheckable) {
+ if (this.checkedTreeNodes && this.checkedTreeNodes.length) {
+ this.checkedTreeNodes = this._checkedNodes = this.checkedTreeNodes.filter(item => {
+ const nodeProps = getAllProps(item.node)
+ return value.some(i => i.value === nodeProps.value)
+ })
+ }
+ }
+
+ this.fireChange(value, { triggerValue: selectedVal, clear: true })
+ },
+
+ openIfHasChildren () {
+ const props = this.$props
+ if (props.children.length || (props.treeData && props.treeData.length) || !isMultiple(props)) {
+ this.setOpenState(true)
+ }
+ },
+
+ fireChange (value, extraInfo = {}) {
+ const props = getOptionProps(this)
+ const vals = value.map(i => i.value)
+ const sv = this.sValue.map(i => i.value)
+ if (vals.length !== sv.length || !vals.every((val, index) => sv[index] === val)) {
+ const ex = {
+ preValue: [...this.sValue],
+ ...extraInfo,
+ }
+ let labs = null
+ let vls = value
+ if (!this.isLabelInValue()) {
+ labs = value.map(i => i.label)
+ vls = vls.map(v => v.value)
+ } else if (this.halfCheckedValues && this.halfCheckedValues.length) {
+ this.halfCheckedValues.forEach(i => {
+ if (!vls.some(v => v.value === i.value)) {
+ vls.push(i)
+ }
+ })
+ }
+ if (props.treeCheckable && ex.clear) {
+ const treeData = this.renderedTreeData || props.children
+ ex.allCheckedNodes = flatToHierarchy(filterAllCheckedData(vals, treeData))
+ }
+ if (props.treeCheckable && this.sInputValue) {
+ const _vls = [...this.sValue]
+ if (ex.checked) {
+ value.forEach(i => {
+ if (_vls.every(ii => ii.value !== i.value)) {
+ _vls.push({ ...i })
+ }
+ })
+ } else {
+ let index
+ const includeVal = _vls.some((i, ind) => {
+ if (i.value === ex.triggerValue) {
+ index = ind
+ return true
+ }
+ })
+ if (includeVal) {
+ _vls.splice(index, 1)
+ }
+ }
+ vls = _vls
+ if (!this.isLabelInValue()) {
+ labs = _vls.map(v => v.label)
+ vls = _vls.map(v => v.value)
+ }
+ }
+ this._savedValue = isMultiple(props) ? vls : vls[0]
+ this.__emit('change', this._savedValue, labs, ex)
+ if (!('value' in props)) {
+ this._cacheTreeNodesStates = false
+ this.setState({
+ sValue: this.getValue(props, toArray(this._savedValue).map((v, i) => {
+ return this.isLabelInValue() ? v : {
+ value: v,
+ label: labs && labs[i],
+ }
+ })),
+ }, this.forcePopupAlign)
+ }
+ }
+ },
+
+ isLabelInValue () {
+ const { treeCheckable, treeCheckStrictly, labelInValue } = this.$props
+ if (treeCheckable && treeCheckStrictly) {
+ return true
+ }
+ return labelInValue || false
+ },
+ onFocus (e) {
+ this.__emit('focus', e)
+ },
+ onBlur (e) {
+ this.__emit('blur', e)
+ },
+
+ focus () {
+ if (!isMultiple(this.$props)) {
+ this.$refs.selection.focus()
+ } else {
+ this.getInputDOMNode().focus()
+ }
+ },
+
+ blur () {
+ if (!isMultiple(this.$props)) {
+ this.$refs.selection.blur()
+ } else {
+ this.getInputDOMNode().blur()
+ }
+ },
+
+ forcePopupAlign () {
+ this.$refs.trigger.$refs.trigger.forcePopupAlign()
+ },
+
+ renderTopControlNode () {
+ const { sValue: value } = this.$data
+ const props = this.$props
+ const { choiceTransitionName, prefixCls, maxTagTextLength } = props
+ const multiple = isMultiple(props)
+
+ // single and not combobox, input is inside dropdown
+ if (!multiple) {
+ let innerNode = (
+ {getComponentFromProp(this, 'placeholder') || ''}
+ )
+ if (value.length) {
+ innerNode = (
+ {value[0].label}
+ )
+ }
+ return (
+ {innerNode}
+ )
+ }
+
+ const selectedValueNodes = value.map((singleValue) => {
+ let content = singleValue.label
+ const title = content
+ if (maxTagTextLength && typeof content === 'string' && content.length > maxTagTextLength) {
+ content = `${content.slice(0, maxTagTextLength)}...`
+ }
+ return (
+
+ {
+ this.removeSelected(singleValue.value, event)
+ }}
+ />
+ {content}
+
+ )
+ })
+
+ selectedValueNodes.push(
+ {this.getInputElement()}
+ )
+ const className = `${prefixCls}-selection__rendered`
+ if (choiceTransitionName) {
+ const transitionProps = getTransitionProps(choiceTransitionName, {
+ tag: 'ul',
+ afterLeave: this.onChoiceAnimationLeave,
+ })
+ return (
+ {selectedValueNodes}
+ )
+ }
+ return ()
+ },
+
+ renderTreeData (props) {
+ const validProps = props || this.$props
+ if (validProps.treeData) {
+ if (props && props.treeData === this.preProps.treeData && this.renderedTreeData) {
+ // cache and use pre data.
+ this._cachetreeData = true
+ return this.renderedTreeData
+ }
+ this._cachetreeData = false
+ let treeData = [...validProps.treeData]
+ // process treeDataSimpleMode
+ if (validProps.treeDataSimpleMode) {
+ let simpleFormat = {
+ id: 'id',
+ pId: 'pId',
+ rootPId: null,
+ }
+ if (Object.prototype.toString.call(validProps.treeDataSimpleMode) === '[object Object]') {
+ simpleFormat = { ...simpleFormat, ...validProps.treeDataSimpleMode }
+ }
+ treeData = processSimpleTreeData(treeData, simpleFormat)
+ }
+ return this.loopTreeData(treeData, undefined, this.preProps.treeCheckable)
+ }
+ },
+ },
+
+ render () {
+ const props = this.$props
+ const multiple = isMultiple(props)
+ const state = this.$data
+ const { disabled, allowClear, prefixCls } = props
+ const ctrlNode = this.renderTopControlNode()
+ let extraSelectionProps = {}
+ if (!multiple) {
+ extraSelectionProps = {
+ on: {
+ keydown: this.onKeyDown,
+ blur: this.onBlur,
+ focus: this.onFocus,
+ },
+ attrs: {
+ tabIndex: 0,
+ },
+ }
+ }
+ const rootCls = {
+ [prefixCls]: 1,
+ [`${prefixCls}-open`]: state.sOpen,
+ [`${prefixCls}-focused`]: state.sOpen || state.sFocused,
+ // [`${prefixCls}-combobox`]: isCombobox(props),
+ [`${prefixCls}-disabled`]: disabled,
+ [`${prefixCls}-enabled`]: !disabled,
+ [`${prefixCls}-allow-clear`]: !!props.allowClear,
+ }
+
+ const clear = ()
+ const selectTriggerProps = {
+ props: {
+ ...props,
+ treeNodes: props.children,
+ treeData: this.renderedTreeData,
+ _cachetreeData: this._cachetreeData,
+ _treeNodesStates: this._treeNodesStates,
+ halfCheckedValues: this.halfCheckedValues,
+ multiple: multiple,
+ disabled: disabled,
+ visible: state.sOpen,
+ inputValue: state.sInputValue,
+ inputElement: this.getInputElement(),
+ value: state.sValue,
+ dropdownVisibleChange: this.onDropdownVisibleChange,
+ getPopupContainer: props.getPopupContainer,
+ filterTreeNode: this.filterTreeNode === undefined ? filterFn : this.filterTreeNode,
+ },
+ on: {
+ ...this.$listeners,
+ select: this.onSelect,
+ },
+ ref: 'trigger',
+ }
+ return (
+
+
+
+ {ctrlNode}
+ {allowClear && state.sValue.length &&
+ state.sValue[0].value ? clear : null}
+ {multiple || !props.showArrow ? null
+ : (
+
+ )}
+ {multiple
+ ? this.getSearchPlaceholderElement(!!state.sInputValue || state.sValue.length)
+ : null}
+
+
+
+ )
+ },
+}
+
+Select.SHOW_ALL = SHOW_ALL
+Select.SHOW_PARENT = SHOW_PARENT
+Select.SHOW_CHILD = SHOW_CHILD
+
+export default Select
diff --git a/components/vc-tree-select/src/SelectTrigger.jsx b/components/vc-tree-select/src/SelectTrigger.jsx
new file mode 100644
index 000000000..8ad2cd7be
--- /dev/null
+++ b/components/vc-tree-select/src/SelectTrigger.jsx
@@ -0,0 +1,371 @@
+import PropTypes from '../../_util/vue-types'
+import classnames from 'classnames'
+import omit from 'omit.js'
+import Trigger from '../../trigger'
+import Tree, { TreeNode } from '../../vc-tree'
+import { SelectPropTypes } from './PropTypes'
+import BaseMixin from '../../_util/BaseMixin'
+import {
+ loopAllChildren,
+ flatToHierarchy,
+ getValuePropValue,
+ labelCompatible,
+ saveRef,
+} from './util'
+
+import { cloneElement } from '../../_util/vnode'
+import { isEmptyElement, getSlotOptions, getKey, getAllProps, getComponentFromProp } from '../../_util/props-util'
+import { noop } from '../../_util/vue-types/utils'
+
+const BUILT_IN_PLACEMENTS = {
+ bottomLeft: {
+ points: ['tl', 'bl'],
+ offset: [0, 4],
+ overflow: {
+ adjustX: 0,
+ adjustY: 1,
+ },
+ },
+ topLeft: {
+ points: ['bl', 'tl'],
+ offset: [0, -4],
+ overflow: {
+ adjustX: 0,
+ adjustY: 1,
+ },
+ },
+}
+
+const SelectTrigger = {
+ mixins: [BaseMixin],
+ name: 'SelectTrigger',
+ props: {
+ ...SelectPropTypes,
+ dropdownMatchSelectWidth: PropTypes.bool,
+ dropdownPopupAlign: PropTypes.object,
+ visible: PropTypes.bool,
+ filterTreeNode: PropTypes.any,
+ treeNodes: PropTypes.any,
+ inputValue: PropTypes.string,
+ prefixCls: PropTypes.string,
+ popupClassName: PropTypes.string,
+ _cachetreeData: PropTypes.any,
+ _treeNodesStates: PropTypes.any,
+ halfCheckedValues: PropTypes.any,
+ inputElement: PropTypes.any,
+ },
+ data () {
+ return {
+ sExpandedKeys: [],
+ fireOnExpand: false,
+ dropdownWidth: null,
+ }
+ },
+
+ mounted () {
+ this.$nextTick(() => {
+ this.setDropdownWidth()
+ })
+ },
+ watch: {
+ inputValue (val) {
+ // set autoExpandParent to true
+ this.setState({
+ sExpandedKeys: [],
+ fireOnExpand: false,
+ })
+ },
+ },
+
+ updated () {
+ this.$nextTick(() => {
+ this.setDropdownWidth()
+ })
+ },
+ methods: {
+ onExpand (expandedKeys) {
+ // rerender
+ this.setState({
+ sExpandedKeys: expandedKeys,
+ fireOnExpand: true,
+ }, () => {
+ // Fix https://github.com/ant-design/ant-design/issues/5689
+ if (this.$refs.trigger && this.$refs.trigger.forcePopupAlign) {
+ this.$refs.trigger.forcePopupAlign()
+ }
+ })
+ },
+
+ setDropdownWidth () {
+ const width = this.$el.offsetWidth
+ if (width !== this.dropdownWidth) {
+ this.setState({ dropdownWidth: width })
+ }
+ },
+
+ getPopupEleRefs () {
+ return this.$refs.popupEle
+ },
+
+ getPopupDOMNode () {
+ return this.$refs.trigger.getPopupDomNode()
+ },
+
+ getDropdownTransitionName () {
+ const props = this.$props
+ let transitionName = props.transitionName
+ if (!transitionName && props.animation) {
+ transitionName = `${this.getDropdownPrefixCls()}-${props.animation}`
+ }
+ return transitionName
+ },
+
+ getDropdownPrefixCls () {
+ return `${this.prefixCls}-dropdown`
+ },
+
+ highlightTreeNode (treeNode) {
+ const props = this.$props
+ const filterVal = treeNode.$props[labelCompatible(props.treeNodeFilterProp)]
+ if (typeof filterVal === 'string') {
+ return props.inputValue && filterVal.indexOf(props.inputValue) > -1
+ }
+ return false
+ },
+
+ filterTreeNode_ (input, child) {
+ if (!input) {
+ return true
+ }
+ const filterTreeNode = this.filterTreeNode
+ if (!filterTreeNode) {
+ return true
+ }
+ const props = getAllProps(child)
+ if (props && props.disabled) {
+ return false
+ }
+ return filterTreeNode.call(this, input, child)
+ },
+
+ processTreeNode (treeNodes) {
+ const filterPoss = []
+ this._expandedKeys = []
+ loopAllChildren(treeNodes, (child, index, pos) => {
+ if (this.filterTreeNode_(this.inputValue, child)) {
+ filterPoss.push(pos)
+ this._expandedKeys.push(String(getKey(child)))
+ }
+ })
+
+ // Include the filtered nodes's ancestral nodes.
+ const processedPoss = []
+ filterPoss.forEach(pos => {
+ const arr = pos.split('-')
+ arr.reduce((pre, cur) => {
+ const res = `${pre}-${cur}`
+ if (processedPoss.indexOf(res) < 0) {
+ processedPoss.push(res)
+ }
+ return res
+ })
+ })
+ const filterNodesPositions = []
+ loopAllChildren(treeNodes, (child, index, pos) => {
+ if (processedPoss.indexOf(pos) > -1) {
+ filterNodesPositions.push({ node: child, pos })
+ }
+ })
+
+ const hierarchyNodes = flatToHierarchy(filterNodesPositions)
+
+ const recursive = children => {
+ return children.map(child => {
+ if (child.children) {
+ return cloneElement(child.node, {
+ children: recursive(child.children),
+ })
+ }
+ return child.node
+ })
+ }
+ return recursive(hierarchyNodes)
+ },
+ onSelect () {
+ this.__emit('select', ...arguments)
+ },
+
+ renderTree (keys, halfCheckedKeys, newTreeNodes, multiple) {
+ const props = this.$props
+
+ const trProps = {
+ multiple,
+ prefixCls: `${props.prefixCls}-tree`,
+ showIcon: props.treeIcon,
+ showLine: props.treeLine,
+ defaultExpandAll: props.treeDefaultExpandAll,
+ defaultExpandedKeys: props.treeDefaultExpandedKeys,
+ filterTreeNode: this.highlightTreeNode,
+ }
+ const trListeners = {}
+
+ if (props.treeCheckable) {
+ trProps.selectable = false
+ trProps.checkable = props.treeCheckable
+ trListeners.check = this.onSelect
+ trProps.checkStrictly = props.treeCheckStrictly
+ if (props.inputValue) {
+ // enable checkStrictly when search tree.
+ trProps.checkStrictly = true
+ } else {
+ trProps._treeNodesStates = props._treeNodesStates
+ }
+ if (trProps.treeCheckStrictly && halfCheckedKeys.length) {
+ trProps.checkedKeys = { checked: keys, halfChecked: halfCheckedKeys }
+ } else {
+ trProps.checkedKeys = keys
+ }
+ } else {
+ trProps.selectedKeys = keys
+ trListeners.select = this.onSelect
+ }
+
+ // expand keys
+ if (!trProps.defaultExpandAll && !trProps.defaultExpandedKeys && !props.loadData) {
+ trProps.expandedKeys = keys
+ }
+ trProps.autoExpandParent = true
+ trListeners.expand = this.onExpand
+ if (this._expandedKeys && this._expandedKeys.length) {
+ trProps.expandedKeys = this._expandedKeys
+ }
+ if (this.fireOnExpand) {
+ trProps.expandedKeys = this.sExpandedKeys
+ trProps.autoExpandParent = false
+ }
+
+ // async loadData
+ if (props.loadData) {
+ trProps.loadData = props.loadData
+ }
+ return (
+
+ {newTreeNodes}
+
+ )
+ },
+ },
+
+ render () {
+ const props = this.$props
+ const multiple = props.multiple
+ const dropdownPrefixCls = this.getDropdownPrefixCls()
+ const popupClassName = {
+ [props.dropdownClassName]: !!props.dropdownClassName,
+ [`${dropdownPrefixCls}--${multiple ? 'multiple' : 'single'}`]: 1,
+ }
+ let visible = props.visible
+ const search = multiple || !props.showSearch ? null : (
+ {props.inputElement}
+ )
+ const recursive = children => {
+ return children.map(function handler(child) { // eslint-disable-line
+ // if (isEmptyElement(child) || (child.data && child.data.slot)) {
+ // return null
+ // }
+ if (!getSlotOptions(child).__ANT_TREE_SELECT_NODE) {
+ return null
+ }
+ const treeNodeProps = {
+ ...child.data,
+ props: {
+ ...getAllProps(child),
+ title: getComponentFromProp(child, 'title') || getComponentFromProp(child, 'label'),
+ },
+ key: String(child.key),
+ }
+ if (child && child.componentOptions.children) {
+ // null or String has no Prop
+ return (
+
+ {recursive(child.componentOptions.children) }
+
+ )
+ }
+ return
+ })
+ }
+ // const s = Date.now();
+ let treeNodes
+ if (props._cachetreeData && this.cacheTreeNodes) {
+ treeNodes = this.cacheTreeNodes
+ } else {
+ treeNodes = recursive(props.treeData || props.treeNodes)
+ this.cacheTreeNodes = treeNodes
+ }
+ // console.log(Date.now()-s);
+
+ if (props.inputValue) {
+ treeNodes = this.processTreeNode(treeNodes)
+ }
+
+ const keys = []
+ const halfCheckedKeys = []
+ loopAllChildren(treeNodes, (child) => {
+ if (props.value.some(item => item.value === getValuePropValue(child))) {
+ keys.push(String(getKey(child)))
+ }
+ if (props.halfCheckedValues &&
+ props.halfCheckedValues.some(item => item.value === getValuePropValue(child))) {
+ halfCheckedKeys.push(String(getKey(child)))
+ }
+ })
+
+ let notFoundContent
+ if (!treeNodes.length) {
+ if (props.notFoundContent) {
+ notFoundContent = (
+
+ {props.notFoundContent}
+
+ )
+ } else if (!search) {
+ visible = false
+ }
+ }
+ const popupElement = (
+
+ {search}
+ {notFoundContent || this.renderTree(keys, halfCheckedKeys, treeNodes, multiple)}
+
+ )
+
+ const popupStyle = { ...props.dropdownStyle }
+ const widthProp = props.dropdownMatchSelectWidth ? 'width' : 'minWidth'
+ if (this.dropdownWidth) {
+ popupStyle[widthProp] = `${this.dropdownWidth}px`
+ }
+
+ return (
+
+ {this.$slots.default}
+
+ )
+ },
+}
+
+export default SelectTrigger
diff --git a/components/vc-tree-select/src/TreeNode.jsx b/components/vc-tree-select/src/TreeNode.jsx
new file mode 100644
index 000000000..9a9d2d195
--- /dev/null
+++ b/components/vc-tree-select/src/TreeNode.jsx
@@ -0,0 +1,12 @@
+import { TreeNode } from '../../vc-tree'
+export default {
+ name: 'TreeNode',
+ __ANT_TREE_SELECT_NODE: true,
+ props: {
+ ...TreeNode.props,
+ value: String,
+ },
+ render () {
+ return this
+ },
+}
diff --git a/components/vc-tree-select/src/index.js b/components/vc-tree-select/src/index.js
new file mode 100644
index 000000000..27d435e50
--- /dev/null
+++ b/components/vc-tree-select/src/index.js
@@ -0,0 +1,26 @@
+// export this package's api
+import TreeSelect from './Select'
+import TreeNode from './TreeNode'
+import omit from 'omit.js'
+import { SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from './strategies'
+TreeSelect.TreeNode = TreeNode
+
+export default {
+ functional: true,
+ render (h, context) {
+ const { props, listeners, children = [], data } = context
+ const treeSelectProps = {
+ ...omit(data, ['attrs']),
+ props: {
+ ...props,
+ children,
+ __propsSymbol__: Symbol(),
+ },
+ on: listeners,
+ }
+ return
+ },
+ TreeNode,
+ SHOW_ALL, SHOW_PARENT, SHOW_CHILD,
+}
+export { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD }
diff --git a/components/vc-tree-select/src/strategies.js b/components/vc-tree-select/src/strategies.js
new file mode 100644
index 000000000..1257b13e4
--- /dev/null
+++ b/components/vc-tree-select/src/strategies.js
@@ -0,0 +1,3 @@
+export const SHOW_ALL = 'SHOW_ALL'
+export const SHOW_PARENT = 'SHOW_PARENT'
+export const SHOW_CHILD = 'SHOW_CHILD'
diff --git a/components/vc-tree-select/src/util.js b/components/vc-tree-select/src/util.js
new file mode 100644
index 000000000..0aede0b40
--- /dev/null
+++ b/components/vc-tree-select/src/util.js
@@ -0,0 +1,569 @@
+import { getPropsData, getAllProps, getKey, getAttrs, getSlotOptions, filterEmpty, getSlots } from '../../_util/props-util'
+import { cloneVNodes, cloneElement } from '../../_util/vnode'
+export function toTitle (title) {
+ if (typeof title === 'string') {
+ return title
+ }
+ return null
+}
+
+export function getValuePropValue (child) {
+ const props = getAllProps(child)
+ if ('value' in props) {
+ return props.value
+ }
+ if (getKey(child) !== undefined) {
+ return getKey(child)
+ }
+ throw new Error(`no key or value for ${child}`)
+}
+
+export function getPropValue (child, prop) {
+ if (prop === 'value') {
+ return getValuePropValue(child)
+ }
+ const slots = getSlots(child)
+ if (prop === 'children') {
+ const newChild = child.$slots ? cloneVNodes(child.$slots.default, true) : cloneVNodes(child.componentOptions.children, true)
+ if (newChild.length === 1 && !newChild[0].tag) {
+ return newChild[0].text
+ }
+ return newChild
+ }
+ if (slots[prop]) {
+ return cloneVNodes(slots[prop], true)
+ }
+ const data = getPropsData(child)
+ if (prop in data) {
+ return data[prop]
+ } else {
+ return getAttrs(child)[prop]
+ }
+}
+
+export function isMultiple (props) {
+ return !!(props.multiple || props.treeCheckable)
+}
+
+export function toArray (value) {
+ let ret = value
+ if (value === undefined) {
+ ret = []
+ } else if (!Array.isArray(value)) {
+ ret = [value]
+ }
+ return ret
+}
+
+export function preventDefaultEvent (e) {
+ e.preventDefault()
+}
+
+export const UNSELECTABLE_STYLE = {
+ userSelect: 'none',
+ WebkitUserSelect: 'none',
+}
+
+export const UNSELECTABLE_ATTRIBUTE = {
+ unselectable: 'unselectable',
+}
+
+export function labelCompatible (prop) {
+ let newProp = prop
+ if (newProp === 'label') {
+ newProp = 'title'
+ }
+ return newProp
+}
+
+export function isInclude (smallArray, bigArray) {
+ // attention: [0,0,1] [0,0,10]
+ return smallArray.every((ii, i) => {
+ return ii === bigArray[i]
+ })
+}
+
+export function isPositionPrefix (smallPos, bigPos) {
+ if (!bigPos || !smallPos) {
+ // console.log(smallPos, bigPos);
+ return false
+ }
+ if (bigPos.length < smallPos.length) {
+ return false
+ }
+ // attention: "0-0-1" "0-0-10"
+ if ((bigPos.length > smallPos.length) && (bigPos.charAt(smallPos.length) !== '-')) {
+ return false
+ }
+ return bigPos.substr(0, smallPos.length) === smallPos
+}
+
+/*
+export function getCheckedKeys(node, checkedKeys, allCheckedNodesKeys) {
+ const nodeKey = node.props.eventKey;
+ let newCks = [...checkedKeys];
+ let nodePos;
+ const unCheck = allCheckedNodesKeys.some(item => {
+ if (item.key === nodeKey) {
+ nodePos = item.pos;
+ return true;
+ }
+ });
+ if (unCheck) {
+ newCks = [];
+ allCheckedNodesKeys.forEach(item => {
+ if (isPositionPrefix(item.pos, nodePos) || isPositionPrefix(nodePos, item.pos)) {
+ return;
+ }
+ newCks.push(item.key);
+ });
+ } else {
+ newCks.push(nodeKey);
+ }
+ return newCks;
+}
+*/
+
+function getChildrenlength (children) {
+ let len = 1
+ if (Array.isArray(children)) {
+ len = children.length
+ }
+ return len
+}
+
+function getSiblingPosition (index, len, siblingPosition) {
+ if (len === 1) {
+ siblingPosition.first = true
+ siblingPosition.last = true
+ } else {
+ siblingPosition.first = index === 0
+ siblingPosition.last = index === len - 1
+ }
+ return siblingPosition
+}
+
+function filterChild (childs) {
+ const newChilds = []
+ childs.forEach(child => {
+ const options = getSlotOptions(child)
+ if (options.__ANT_TREE_NODE || options.__ANT_TREE_SELECT_NODE) {
+ newChilds.push(child)
+ }
+ })
+ return newChilds
+}
+
+export function loopAllChildren (childs, callback, parent) {
+ const loop = (children, level, _parent) => {
+ const len = getChildrenlength(children)
+ children.forEach(function handler(item, index) { // eslint-disable-line
+ const pos = `${level}-${index}`
+ if (item && item.componentOptions && item.componentOptions.children) {
+ loop(filterChild(item.componentOptions.children), pos, { node: item, pos })
+ }
+ if (item) {
+ callback(item, index, pos, item.key || pos, getSiblingPosition(index, len, {}), _parent)
+ }
+ })
+ }
+ loop(filterChild(childs), 0, parent)
+}
+
+// export function loopAllChildren(childs, callback) {
+// const loop = (children, level) => {
+// React.Children.forEach(children, (item, index) => {
+// const pos = `${level}-${index}`;
+// if (item && item.props.children) {
+// loop(item.props.children, pos);
+// }
+// if (item) {
+// callback(item, index, pos, getValuePropValue(item));
+// }
+// });
+// };
+// loop(childs, 0);
+// }
+
+// TODO: Here has the side effect. Update node children data affect.
+export function flatToHierarchy (arr) {
+ if (!arr.length) {
+ return arr
+ }
+ const hierarchyNodes = []
+ const levelObj = {}
+ arr.forEach((item) => {
+ if (!item.pos) {
+ return
+ }
+ const posLen = item.pos.split('-').length
+ if (!levelObj[posLen]) {
+ levelObj[posLen] = []
+ }
+ levelObj[posLen].push(item)
+ })
+ const levelArr = Object.keys(levelObj).sort((a, b) => b - a)
+ // const s = Date.now();
+ // todo: there are performance issues!
+ levelArr.reduce((pre, cur) => {
+ if (cur && cur !== pre) {
+ levelObj[pre].forEach((item) => {
+ let haveParent = false
+ levelObj[cur].forEach((ii) => {
+ if (isPositionPrefix(ii.pos, item.pos)) {
+ haveParent = true
+ if (!ii.children) {
+ ii.children = []
+ }
+ ii.children.push(item)
+ }
+ })
+ if (!haveParent) {
+ hierarchyNodes.push(item)
+ }
+ })
+ }
+ return cur
+ })
+ // console.log(Date.now() - s);
+ return levelObj[levelArr[levelArr.length - 1]].concat(hierarchyNodes)
+}
+
+// arr.length === 628, use time: ~20ms
+export function filterParentPosition (arr) {
+ const levelObj = {}
+ arr.forEach((item) => {
+ const posLen = item.split('-').length
+ if (!levelObj[posLen]) {
+ levelObj[posLen] = []
+ }
+ levelObj[posLen].push(item)
+ })
+ const levelArr = Object.keys(levelObj).sort()
+ for (let i = 0; i < levelArr.length; i++) {
+ if (levelArr[i + 1]) {
+ levelObj[levelArr[i]].forEach(ii => {
+ for (let j = i + 1; j < levelArr.length; j++) {
+ levelObj[levelArr[j]].forEach((_i, index) => {
+ if (isPositionPrefix(ii, _i)) {
+ levelObj[levelArr[j]][index] = null
+ }
+ })
+ levelObj[levelArr[j]] = levelObj[levelArr[j]].filter(p => p)
+ }
+ })
+ }
+ }
+ let nArr = []
+ levelArr.forEach(i => {
+ nArr = nArr.concat(levelObj[i])
+ })
+ return nArr
+}
+// console.log(filterParentPosition(
+// ['0-2', '0-3-3', '0-10', '0-10-0', '0-0-1', '0-0', '0-1-1', '0-1']
+// ));
+
+function stripTail (str) {
+ const arr = str.match(/(.+)(-[^-]+)$/)
+ let st = ''
+ if (arr && arr.length === 3) {
+ st = arr[1]
+ }
+ return st
+}
+function splitPosition (pos) {
+ return pos.split('-')
+}
+
+// todo: do optimization.
+export function handleCheckState (obj, checkedPositionArr, checkIt) {
+ // console.log(stripTail('0-101-000'));
+ // let s = Date.now();
+ let objKeys = Object.keys(obj)
+
+ objKeys.forEach((i, index) => {
+ const iArr = splitPosition(i)
+ let saved = false
+ checkedPositionArr.forEach((_pos) => {
+ const _posArr = splitPosition(_pos)
+ if (iArr.length > _posArr.length && isInclude(_posArr, iArr)) {
+ obj[i].halfChecked = false
+ obj[i].checked = checkIt
+ objKeys[index] = null
+ }
+ if (iArr[0] === _posArr[0] && iArr[1] === _posArr[1]) {
+ saved = true
+ }
+ })
+ if (!saved) {
+ objKeys[index] = null
+ }
+ })
+ objKeys = objKeys.filter(i => i) // filter non null;
+
+ for (let pIndex = 0; pIndex < checkedPositionArr.length; pIndex++) {
+ // loop to set ancestral nodes's `checked` or `halfChecked`
+ const loop = (__pos) => {
+ const _posLen = splitPosition(__pos).length
+ if (_posLen <= 2) { // e.g. '0-0', '0-1'
+ return
+ }
+ let sibling = 0
+ let siblingChecked = 0
+ const parentPosition = stripTail(__pos)
+ objKeys.forEach((i /* , index*/) => {
+ const iArr = splitPosition(i)
+ if (iArr.length === _posLen && isInclude(splitPosition(parentPosition), iArr)) {
+ sibling++
+ if (obj[i].checked) {
+ siblingChecked++
+ const _i = checkedPositionArr.indexOf(i)
+ if (_i > -1) {
+ checkedPositionArr.splice(_i, 1)
+ if (_i <= pIndex) {
+ pIndex--
+ }
+ }
+ } else if (obj[i].halfChecked) {
+ siblingChecked += 0.5
+ }
+ // objKeys[index] = null;
+ }
+ })
+ // objKeys = objKeys.filter(i => i); // filter non null;
+ const parent = obj[parentPosition]
+ // not check, checked, halfChecked
+ if (siblingChecked === 0) {
+ parent.checked = false
+ parent.halfChecked = false
+ } else if (siblingChecked === sibling) {
+ parent.checked = true
+ parent.halfChecked = false
+ } else {
+ parent.halfChecked = true
+ parent.checked = false
+ }
+ loop(parentPosition)
+ }
+ loop(checkedPositionArr[pIndex], pIndex)
+ }
+ // console.log(Date.now()-s, objKeys.length, checkIt);
+}
+
+function getCheck (treeNodesStates, checkedPositions) {
+ const halfCheckedKeys = []
+ const checkedKeys = []
+ const checkedNodes = []
+ Object.keys(treeNodesStates).forEach((item) => {
+ const itemObj = treeNodesStates[item]
+ if (itemObj.checked) {
+ checkedKeys.push(itemObj.key)
+ // checkedNodes.push(getValuePropValue(itemObj.node));
+ checkedNodes.push({ ...itemObj, pos: item })
+ } else if (itemObj.halfChecked) {
+ halfCheckedKeys.push(itemObj.key)
+ }
+ })
+ return {
+ halfCheckedKeys, checkedKeys, checkedNodes, treeNodesStates, checkedPositions,
+ }
+}
+
+export function getTreeNodesStates (children, values) {
+ const checkedPositions = []
+ const treeNodesStates = {}
+ loopAllChildren(children, (item, index, pos, keyOrPos, siblingPosition) => {
+ treeNodesStates[pos] = {
+ node: item,
+ key: keyOrPos,
+ checked: false,
+ halfChecked: false,
+ siblingPosition,
+ }
+ if (values.indexOf(getValuePropValue(item)) !== -1) {
+ treeNodesStates[pos].checked = true
+ checkedPositions.push(pos)
+ }
+ })
+
+ handleCheckState(treeNodesStates, filterParentPosition(checkedPositions.sort()), true)
+
+ return getCheck(treeNodesStates, checkedPositions)
+}
+
+// can add extra prop to every node.
+export function recursiveCloneChildren (children, cb = ch => ch) {
+ // return React.Children.map(children, child => {
+ return Array.from(children).map(child => {
+ const newChild = cb(child)
+ if (newChild && newChild.props && newChild.props.children) {
+ return cloneElement(newChild, {
+ children: recursiveCloneChildren(newChild.props.children, cb),
+ })
+ }
+ return newChild
+ })
+}
+// const newChildren = recursiveCloneChildren(children, child => {
+// const extraProps = {};
+// if (child && child.type && child.type.xxx) {
+// extraProps._prop = true;
+// return React.cloneElement(child, extraProps);
+// }
+// return child;
+// });
+
+function recursiveGen (children, level = 0) {
+ return children.map((child, index) => {
+ const pos = `${level}-${index}`
+ const props = getAllProps(child)
+ const { title, label, value, ...rest } = props
+ const { children: subChildren } = child.componentOptions
+ const o = {
+ ...rest,
+ title,
+ label: label || title,
+ value,
+ key: child.key,
+ _pos: pos,
+ }
+ if (subChildren) {
+ o.children = recursiveGen(subChildren, pos)
+ }
+ return o
+ })
+}
+
+function recursive (children, cb) {
+ children.forEach(item => {
+ cb(item)
+ if (item.children) {
+ recursive(item.children, cb)
+ }
+ })
+}
+
+// Get the tree's checkedNodes (todo: can merge to the `handleCheckState` function)
+// If one node checked, it's all children nodes checked.
+// If sibling nodes all checked, the parent checked.
+export function filterAllCheckedData (vs, treeNodes) {
+ const vals = [...vs]
+ if (!vals.length) {
+ return vals
+ }
+
+ const data = recursiveGen(treeNodes)
+ const checkedNodesPositions = []
+
+ function checkChildren (children) {
+ children.forEach(item => {
+ if (item.__checked) {
+ return
+ }
+ const ci = vals.indexOf(item.value)
+ const childs = item.children
+ if (ci > -1) {
+ item.__checked = true
+ checkedNodesPositions.push({ node: item, pos: item._pos })
+ vals.splice(ci, 1)
+ if (childs) {
+ recursive(childs, child => {
+ child.__checked = true
+ checkedNodesPositions.push({ node: child, pos: child._pos })
+ })
+ }
+ } else {
+ if (childs) {
+ checkChildren(childs)
+ }
+ }
+ })
+ }
+
+ function checkParent (children, parent = { root: true }) {
+ let siblingChecked = 0
+ children.forEach(item => {
+ const childs = item.children
+ if (childs && !item.__checked && !item.__halfChecked) {
+ const p = checkParent(childs, item)
+ if (p.__checked) {
+ siblingChecked++
+ } else if (p.__halfChecked) {
+ siblingChecked += 0.5
+ }
+ } else if (item.__checked) {
+ siblingChecked++
+ } else if (item.__halfChecked) {
+ siblingChecked += 0.5
+ }
+ })
+ const len = children.length
+ if (siblingChecked === len) {
+ parent.__checked = true
+ checkedNodesPositions.push({ node: parent, pos: parent._pos })
+ } else if (siblingChecked < len && siblingChecked > 0) {
+ parent.__halfChecked = true
+ }
+ if (parent.root) {
+ return children
+ }
+ return parent
+ }
+ checkChildren(data)
+ checkParent(data)
+
+ checkedNodesPositions.forEach((i, index) => {
+ // clear private metadata
+ delete checkedNodesPositions[index].node.__checked
+ delete checkedNodesPositions[index].node._pos
+ // create the same structure of `onCheck`'s return.
+ checkedNodesPositions[index].node.props = {
+ title: checkedNodesPositions[index].node.title,
+ label: checkedNodesPositions[index].node.label || checkedNodesPositions[index].node.title,
+ value: checkedNodesPositions[index].node.value,
+ }
+ if (checkedNodesPositions[index].node.children) {
+ checkedNodesPositions[index].node.props.children = checkedNodesPositions[index].node.children
+ }
+ delete checkedNodesPositions[index].node.title
+ delete checkedNodesPositions[index].node.label
+ delete checkedNodesPositions[index].node.value
+ delete checkedNodesPositions[index].node.children
+ })
+ return checkedNodesPositions
+}
+
+export function processSimpleTreeData (treeData, format) {
+ function unflatten2 (array, parent = { [format.id]: format.rootPId }) {
+ const children = []
+ for (let i = 0; i < array.length; i++) {
+ array[i] = { ...array[i] } // copy, can not corrupts original data
+ if (array[i][format.pId] === parent[format.id]) {
+ array[i].key = array[i][format.id]
+ children.push(array[i])
+ array.splice(i--, 1)
+ }
+ }
+ if (children.length) {
+ parent.children = children
+ children.forEach(child => unflatten2(array, child))
+ }
+ if (parent[format.id] === format.rootPId) {
+ return children
+ }
+ }
+ return unflatten2(treeData)
+}
+
+export function saveRef (instance, name) {
+ if (!instance.saveRefs) {
+ instance.saveRefs = {}
+ }
+ if (!instance.saveRefs[name]) {
+ instance.saveRefs[name] = (node) => {
+ instance[name] = node
+ }
+ }
+ return instance.saveRefs[name]
+}
diff --git a/components/vc-tree/src/Tree.jsx b/components/vc-tree/src/Tree.jsx
index 7c75378ea..25d0246e0 100644
--- a/components/vc-tree/src/Tree.jsx
+++ b/components/vc-tree/src/Tree.jsx
@@ -558,7 +558,7 @@ const Tree = {
tabIndex={focusable ? '0' : null}
onKeydown={focusable ? this.onKeydown : () => {}}
>
- {children.map(this.renderTreeNode)}
+ {children.map((child, index) => this.renderTreeNode(child, index))}
)
},
diff --git a/components/vc-tree/src/TreeNode.jsx b/components/vc-tree/src/TreeNode.jsx
index 58c4563b5..34820110f 100644
--- a/components/vc-tree/src/TreeNode.jsx
+++ b/components/vc-tree/src/TreeNode.jsx
@@ -21,6 +21,7 @@ let onlyTreeNodeWarned = false // Only accept TreeNode
const TreeNode = {
name: 'TreeNode',
mixins: [BaseMixin],
+ __ANT_TREE_NODE: true,
props: initDefaultProps({
eventKey: PropTypes.string, // Pass by parent `cloneElement`
prefixCls: PropTypes.string,
diff --git a/components/vc-tree/src/util.js b/components/vc-tree/src/util.js
index 11fd3bd13..e00246cc7 100644
--- a/components/vc-tree/src/util.js
+++ b/components/vc-tree/src/util.js
@@ -332,6 +332,11 @@ export function calcCheckStateConduct (treeNodes, checkedKeys) {
}
}
+// function keyListToString (keyList) {
+// if (!keyList) return keyList
+// return keyList.map(key => String(key))
+// }
+
/**
* Calculate the value of checked and halfChecked keys.
* This should be only run in init or props changed.
@@ -361,6 +366,9 @@ export function calcCheckedKeys (keys, props, children = []) {
return null
}
+ // keyProps.checkedKeys = keyListToString(keyProps.checkedKeys)
+ // keyProps.halfCheckedKeys = keyListToString(keyProps.halfCheckedKeys)
+
// Do nothing if is checkStrictly mode
if (checkStrictly) {
return keyProps
diff --git a/site/components.js b/site/components.js
index 8b262e351..2bfd38350 100644
--- a/site/components.js
+++ b/site/components.js
@@ -45,7 +45,7 @@ import {
Table,
Transfer,
Tree,
- // TreeSelect,
+ TreeSelect,
Tabs,
Tag,
TimePicker,
@@ -133,7 +133,8 @@ Vue.component(Table.ColumnGroup.name, Table.ColumnGroup)
Vue.component(Transfer.name, Transfer)
Vue.component(Tree.name, Tree)
Vue.component(Tree.TreeNode.name, Tree.TreeNode)
-// Vue.component(TreeSelect.name, TreeSelect)
+Vue.component(TreeSelect.name, TreeSelect)
+Vue.component(TreeSelect.TreeNode.name, TreeSelect.TreeNode)
Vue.component(Tabs.name, Tabs)
Vue.component(Tabs.TabPane.name, Tabs.TabPane)
Vue.component(Tag.name, Tag)
diff --git a/site/demo.js b/site/demo.js
index 3e028ce0f..8a713f481 100644
--- a/site/demo.js
+++ b/site/demo.js
@@ -43,6 +43,7 @@ export { default as inputNumber } from 'antd/input-number/demo/index.vue'
export { default as transfer } from 'antd/transfer/demo/index.vue'
export { default as upload } from 'antd/upload/demo/index.vue'
export { default as tree } from 'antd/tree/demo/index.vue'
+export { default as treeSelect } from 'antd/tree-select/demo/index.vue'
export { default as layout } from 'antd/layout/demo/index.vue'
export { default as form } from 'antd/form/demo/index.vue'
export { default as anchor } from 'antd/anchor/demo/index.vue'
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index be8c0de07..4c6bdbab2 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -48,6 +48,7 @@ Array [
"Table",
"Transfer",
"Tree",
+ "TreeSelect",
"Tabs",
"Tag",
"TimePicker",