diff --git a/components.json b/components.json
index 76515c414..fcee75b73 100644
--- a/components.json
+++ b/components.json
@@ -87,5 +87,6 @@
"empty": "./packages/empty/index.js",
"descriptions": "./packages/descriptions/index.js",
"descriptions-item": "./packages/descriptions-item/index.js",
- "result": "./packages/result/index.js"
+ "result": "./packages/result/index.js",
+ "tree-select": "./packages/tree-select/index.js"
}
diff --git a/examples/docs/en-US/tree-select.md b/examples/docs/en-US/tree-select.md
new file mode 100644
index 000000000..8eefb8160
--- /dev/null
+++ b/examples/docs/en-US/tree-select.md
@@ -0,0 +1,817 @@
+## TreeSelect
+
+The tree selector of the dropdown menu, it combines the functions of components el-tree and el-select.
+
+### Basic usage
+
+:::demo
+```html
+
+
+
+ click text to select
+
+
+
+ show-checkbox
+
+
+
+
+
+
+```
+:::
+
+### Select any level
+
+When using the check-strictly=true attribute, any node can be checked, otherwise only leaf nodes are supported.
+
+:::demo
+```html
+
+
+ click text to select
+
+
+
+ show-checkbox, only click checkbox to select
+
+
+
+
+
+```
+:::
+
+### Multiple Selection
+
+Multiple selection using clicks or checkbox.
+:::demo
+```html
+
+
+ click text to select
+
+
+
+ show checkbox
+
+
+
+ show checkbox with `check-strictly`
+
+
+
+
+
+```
+:::
+
+### Clearable
+
+You can clear tree-select using a clear icon.
+
+Set clearable attribute for el-tree-select and a clear icon will appear.
+
+:::demo
+```html
+
+
+
+
+
+
+
+
+
+
+```
+:::
+
+### Disabled Selection
+
+Disable options using the disabled field.
+
+:::demo
+```html
+
+
+
+```
+:::
+
+### Filterable
+
+Use keyword filtering or custom filtering methods. filterMethod can custom filter method for data.
+
+:::demo
+```html
+
+
+ filterable
+
+
+
+ filterMethod
+
+
+
+
+
+```
+:::
+
+### LazyLoad
+
+Lazy loading of tree nodes, suitable for large data lists.
+
+:::demo
+```html
+
+
+
+```
+:::
+
+
+### Attributes
+
+| Name | Description | Type | Accepted Values | Default|
+| --------------------- | ------------------------------------------------------------------------------------------------- | --------------------------- | ----------------- | ------ |
+| value / v-model | binding value | boolean / string / number | — | — |
+| data | tree data | array | — | — |
+| multiple | whether multiple-select is activated | boolean | — | false |
+| disabled | whether Select is disabled | boolean | — | false |
+| size | size of Input | string | medium/small/mini | — |
+| clearable | whether select can be cleared | boolean | — | false |
+| collapse-tags | whether to collapse tags to a text when multiple selecting | boolean | — | false |
+| name | the name attribute of Tree-select input | string | — | — |
+| placeholder | placeholder | string | — | 请选择 |
+| filterable | whether Tree-select is filterable | boolean | — | false |
+| empty-text | displayed text when there is no data | String | — | — |
+| node-key | unique identity key name for nodes, its value should be unique across the whole tree | String | — | — |
+| props | configuration options, see the following table | object | — | — |
+| render-after-expand | whether to render child nodes only after a parent node is expanded for the first time | boolean | — | true |
+| load | method for loading subtree data, only works when lazy is true | function(node, resolve) | — | — |
+| default-expand-all | whether to expand all nodes by default | boolean | — | false |
+| auto-expand-parent | whether to expand father node when a child node is expanded | boolean | — | true |
+| show-checkbox | whether node is selectable | boolean | — | false |
+| check-strictly | whether checked state of a node not affects its father and child nodes when show-checkbox is true | boolean | — | false |
+| filter-method | custom filter method | Function(value, data, node) | — | — |
+| accordion | whether only one node among the same level can be expanded at one time | boolean | — | false |
+| indent | horizontal indentation of nodes in adjacent levels in pixels | number | — | 16 |
+| lazy | whether to lazy load leaf node, used with load attribute | boolean | — | false |
+| popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | true |
+
+### props
+
+| Attribute| Description | Type | Accepted Values | Default |
+| -------- | ----------------------------------------------------------------------------- | ----------------------------- | -------------------| ----------------|
+| label | specify which key of node object is used as the node's label | string, function(data, node) | — | — |
+| children | specify which node object is used as the node's subtree | string | — | — |
+| disabled | specify which key of node object represents if node's checkbox is disabled | boolean, function(data, node) | — | — |
+| isLeaf | specify whether the node is a leaf node, only works when lazy load is enabled | boolean, function(data, node) | — | — |
+
+### Events
+
+| Method | Description | Parameters |
+| -------------- | --------------------------------------------------------------- | ------------------------------------------ |
+| change | triggers when the selected value changes | current selected value |
+| visible-change | triggers when the dropdown appears/disappears | true when it appears, and false otherwise |
+| remove-tag | triggers when a tag is removed in multiple mode | removed tag value |
+| clear | triggers when the clear icon is clicked in a clearable Select | — |
+| blur | triggers when Input blurs | (event: Event) |
+| focus | triggers when Input focuses | (event: Event) |
+
diff --git a/examples/docs/es/tree-select.md b/examples/docs/es/tree-select.md
new file mode 100644
index 000000000..c364c8d02
--- /dev/null
+++ b/examples/docs/es/tree-select.md
@@ -0,0 +1 @@
+## TreeSelect
diff --git a/examples/docs/fr-FR/tree-select.md b/examples/docs/fr-FR/tree-select.md
new file mode 100644
index 000000000..c364c8d02
--- /dev/null
+++ b/examples/docs/fr-FR/tree-select.md
@@ -0,0 +1 @@
+## TreeSelect
diff --git a/examples/docs/zh-CN/tree-select.md b/examples/docs/zh-CN/tree-select.md
new file mode 100644
index 000000000..a8bb45fb6
--- /dev/null
+++ b/examples/docs/zh-CN/tree-select.md
@@ -0,0 +1,821 @@
+## TreeSelect 树形选择器
+
+含有下拉菜单的树形选择器,结合了 el-tree 和 el-select 两个组件的功能。
+
+### 基础单选
+
+:::demo
+```html
+
+
+
+ click text to select
+
+
+
+ show-checkbox
+
+
+
+
+
+
+```
+:::
+
+### 选择任意级别
+
+当属性 check-strictly=true 时,任何节点都可以被选择,否则只有子节点可被选择。
+
+:::demo
+```html
+
+
+ click text to select
+
+
+
+ show-checkbox, only click checkbox to select
+
+
+
+
+
+```
+:::
+
+### 多选
+
+通过点击或复选框选择多个选项。
+:::demo
+```html
+
+
+ click text to select
+
+
+
+ show checkbox
+
+
+
+ show checkbox with `check-strictly`
+
+
+
+
+
+```
+:::
+
+### 可清空选项
+
+包含清空按钮,可将选择器清空为初始状态,为`el-tree-select`设置`clearable`属性,则可将选择器清空
+
+:::demo
+```html
+
+
+
+
+
+
+
+
+
+
+```
+:::
+
+### 禁用选项
+
+使用 disabled 字段禁用选项。
+
+:::demo
+```html
+
+
+
+```
+:::
+
+### 可筛选
+
+使用关键字筛选或自定义筛选方法。 filterMethod可以自定义数据筛选的方法。
+
+:::demo
+```html
+
+
+ filterable
+
+
+
+ filterMethod
+
+
+
+
+
+```
+:::
+
+### 懒加载
+
+树节点懒加载,更加适合于数据量大的列表。
+
+:::demo
+```html
+
+
+
+```
+:::
+
+
+### Attributes
+
+| 参数 | 说明 | 类型 | 可选值 | 默认值 |
+| --------------------- | ----------------------------------------------------------------------------------------------- | --------------------------- | ----------------- | ------ |
+| value / v-model | 绑定值 | boolean / string / number | — | — |
+| data | 展示数据 | array | — | — |
+| multiple | 是否多选 | boolean | — | false |
+| disabled | 是否禁用 | boolean | — | false |
+| size | 输入框尺寸 | string | medium/small/mini | — |
+| clearable | 是否可以清空选项 | boolean | — | false |
+| collapse-tags | 多选时是否将选中值按文字的形式展示 | boolean | — | false |
+| name | tree-select input 的 name 属性 | string | — | — |
+| placeholder | 占位符 | string | — | 请选择 |
+| filterable | 是否可搜索 | boolean | — | false |
+| empty-text | 内容为空的时候展示的文本 | String | — | — |
+| node-key | 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 | String | — | — |
+| props | 配置选项,具体看下表 | object | — | — |
+| render-after-expand | 是否在第一次展开某个树节点后才渲染其子节点 | boolean | — | true |
+| load | 加载子树数据的方法,仅当 lazy 属性为 true 时生效 | function(node, resolve) | — | — |
+| default-expand-all | 是否默认展开所有节点 | boolean | — | false |
+| auto-expand-parent | 展开子节点的时候是否自动展开父节点 | boolean | — | true |
+| show-checkbox | 节点是否可被选择 | boolean | — | false |
+| check-strictly | 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false | boolean | — | false |
+| filter-method | 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏 | Function(value, data, node) | — | — |
+| accordion | 是否每次只打开一个同级树节点展开 | boolean | — | false |
+| indent | 相邻级节点间的水平缩进,单位为像素 | number | — | 16 |
+| lazy | 是否懒加载子节点,需与 load 方法结合使用 | boolean | — | false |
+| popper-append-to-body | 是否将弹出框插入至 body 元素。在弹出框的定位出现问题时,可将该属性设置为 false | boolean | - | true |
+
+### props
+
+| 参数 | 说明 | 类型 | 可选值 | 默认值 |
+| -------- | -------------------------------------------------------- | ----------------------------- | ------ | ------ |
+| label | 指定节点标签为节点对象的某个属性值 | string, function(data, node) | — | — |
+| children | 指定子树为节点对象的某个属性值 | string | — | — |
+| disabled | 指定节点选择框是否禁用为节点对象的某个属性值 | boolean, function(data, node) | — | — |
+| isLeaf | 指定节点是否为叶子节点,仅在指定了 lazy 属性的情况下生效 | boolean, function(data, node) | — | — |
+
+### Events
+
+| 事件名称 | 说明 | 回调参数 |
+| -------------- | ---------------------------------------- | ----------------------------- |
+| change | 选中值发生变化时触发 | 目前的选中值 |
+| visible-change | 下拉框出现/隐藏时触发 | 出现则为 true,隐藏则为 false |
+| remove-tag | 多选模式下移除 tag 时触发 | 移除的 tag 值 |
+| clear | 可清空的单选模式下用户点击清空按钮时触发 | — |
+| blur | 当 input 失去焦点时触发 | (event: Event) |
+| focus | 当 input 获得焦点时触发 | (event: Event) |
+
+### Methods
+
+| 方法名 | 说明 | 参数 |
+| ------ | ------------------------------- | ---- |
+| focus | 使 input 获取焦点 | - |
+| blur | 使 input 失去焦点,并隐藏下拉框 | - |
diff --git a/examples/nav.config.json b/examples/nav.config.json
index f648024cd..a2451f1ef 100644
--- a/examples/nav.config.json
+++ b/examples/nav.config.json
@@ -104,6 +104,10 @@
"path": "/cascader",
"title": "Cascader 级联选择器"
},
+ {
+ "path": "/tree-select",
+ "title": "TreeSelect 树形选择器"
+ },
{
"path": "/switch",
"title": "Switch 开关"
@@ -422,6 +426,10 @@
"path": "/cascader",
"title": "Cascader"
},
+ {
+ "path": "/tree-select",
+ "title": "TreeSelect"
+ },
{
"path": "/switch",
"title": "Switch"
@@ -1271,4 +1279,4 @@
]
}
]
-}
+}
\ No newline at end of file
diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss
index ad9a1aaa2..0c11ccbc6 100644
--- a/packages/theme-chalk/src/common/var.scss
+++ b/packages/theme-chalk/src/common/var.scss
@@ -6,19 +6,20 @@
/* Transition
-------------------------- */
-$--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default;
+$--all-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !default;
$--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--fade-linear-transition: opacity 200ms linear !default;
-$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
-$--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default;
-$--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
+$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1),
+ opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
+$--border-transition-base: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !default;
+$--color-transition-base: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !default;
/* Color
-------------------------- */
/// color|1|Brand Color|0
-$--color-primary: #409EFF !default;
+$--color-primary: #409eff !default;
/// color|1|Background Color|4
-$--color-white: #FFFFFF !default;
+$--color-white: #ffffff !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
@@ -31,11 +32,11 @@ $--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /
$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
/// color|1|Functional Color|1
-$--color-success: #67C23A !default;
+$--color-success: #67c23a !default;
/// color|1|Functional Color|1
-$--color-warning: #E6A23C !default;
+$--color-warning: #e6a23c !default;
/// color|1|Functional Color|1
-$--color-danger: #F56C6C !default;
+$--color-danger: #f56c6c !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;
@@ -55,19 +56,19 @@ $--color-text-regular: #606266 !default;
/// color|1|Font Color|2
$--color-text-secondary: #909399 !default;
/// color|1|Font Color|2
-$--color-text-placeholder: #C0C4CC !default;
+$--color-text-placeholder: #c0c4cc !default;
/// color|1|Border Color|3
-$--border-color-base: #DCDFE6 !default;
+$--border-color-base: #dcdfe6 !default;
/// color|1|Border Color|3
-$--border-color-light: #E4E7ED !default;
+$--border-color-light: #e4e7ed !default;
/// color|1|Border Color|3
-$--border-color-lighter: #EBEEF5 !default;
+$--border-color-lighter: #ebeef5 !default;
/// color|1|Border Color|3
-$--border-color-extra-light: #F2F6FC !default;
+$--border-color-extra-light: #f2f6fc !default;
// Background
/// color|1|Background Color|4
-$--background-color-base: #F5F7FA !default;
+$--background-color-base: #f5f7fa !default;
/* Link
-------------------------- */
@@ -91,9 +92,9 @@ $--border-radius-zero: 0 !default;
// Box-shadow
/// boxShadow|1|Shadow|1
-$--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default;
+$--box-shadow-base: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04) !default;
// boxShadow|1|Shadow|1
-$--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default;
+$--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.12) !default;
/// boxShadow|1|Shadow|1
$--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default;
@@ -103,8 +104,8 @@ $--fill-base: $--color-white !default;
/* Typography
-------------------------- */
-$--font-path: 'fonts' !default;
-$--font-display: 'auto' !default;
+$--font-path: "fonts" !default;
+$--font-display: "auto" !default;
/// fontSize|1|Font Size|0
$--font-size-extra-large: 20px !default;
/// fontSize|1|Font Size|0
@@ -210,8 +211,6 @@ $--checkbox-button-checked-font-color: $--color-white !default;
/// color||Color|0
$--checkbox-button-checked-border-color: $--color-primary !default;
-
-
/* Radio
-------------------------- */
/// fontSize||Font|1
@@ -478,8 +477,8 @@ $--cascader-menu-radius: $--border-radius-base !default;
$--cascader-menu-border: solid 1px $--border-color-light !default;
$--cascader-menu-shadow: $--box-shadow-light !default;
$--cascader-node-background-hover: $--background-color-base !default;
-$--cascader-node-color-disabled:$--color-text-placeholder !default;
-$--cascader-color-empty:$--color-text-placeholder !default;
+$--cascader-node-color-disabled: $--color-text-placeholder !default;
+$--cascader-color-empty: $--color-text-placeholder !default;
$--cascader-tag-background: #f0f2f5;
/* Group
@@ -589,7 +588,6 @@ $--button-info-background-color: $--color-info !default;
$--button-hover-tint-percent: 20% !default;
$--button-active-shade-percent: 10% !default;
-
/* cascader
-------------------------- */
$--cascader-height: 200px !default;
@@ -637,7 +635,7 @@ $--table-row-hover-background-color: $--background-color-base !default;
$--table-current-row-background-color: $--color-primary-light-9 !default;
/// color||Color|0
$--table-header-background-color: $--color-white !default;
-$--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default;
+$--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, 0.12) !default;
/* Pagination
-------------------------- */
@@ -825,8 +823,8 @@ $--loading-fullscreen-spinner-size: 50px !default;
/* Scrollbar
--------------------------*/
-$--scrollbar-background-color: rgba($--color-text-secondary, .3) !default;
-$--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default;
+$--scrollbar-background-color: rgba($--color-text-secondary, 0.3) !default;
+$--scrollbar-hover-background-color: rgba($--color-text-secondary, 0.5) !default;
/* Carousel
--------------------------*/
@@ -935,7 +933,7 @@ $--link-info-font-color: $--color-info !default;
/// border||Other|4
$--calendar-border: $--table-border !default;
/// color||Other|4
-$--calendar-selected-background-color: #F2F8FE !default;
+$--calendar-selected-background-color: #f2f8fe !default;
$--calendar-cell-width: 85px !default;
/* Form
@@ -948,7 +946,7 @@ $--form-label-font-size: $--font-size-base !default;
/// color||Color|0
$--avatar-font-color: #fff !default;
/// color||Color|0
-$--avatar-background-color: #C0C4CC !default;
+$--avatar-background-color: #c0c4cc !default;
/// fontSize||Font Size|1
$--avatar-text-font-size: 14px !default;
/// fontSize||Font Size|1
@@ -983,7 +981,7 @@ $--skeleton-to-color: #e6e6e6 !default;
/* Svg
--------------- */
-$--svg-monochrome-grey: #DCDDE0 !default;
+$--svg-monochrome-grey: #dcdde0 !default;
/* Result
-------------------------- */
@@ -998,6 +996,23 @@ $--result-success-color: $--color-success !default;
$--result-warning-color: $--color-warning !default;
$--result-danger-color: $--color-danger !default;
+/* Tree-select
+-------------------------- */
+$--tree-select-font-size: $--font-size-base !default;
+$--tree-select-dropdown-border: solid 1px $--border-color-light !default;
+$--tree-select-multiple-input-color: $--color-text-placeholder !default;
+$--tree-select-dropdown-background: $--color-white !default;
+$--tree-select-option-selected-hover: $--background-color-base !default;
+$--tree-select-dropdown-max-height: 272px !default;
+$--tree-select-dropdown-padding: 6px 0 !default;
+$--tree-select-dropdown-empty-color: #999 !default;
+$--tree-select-dropdown-empty-padding: 10px 0 !default;
+
+$--tree-select-mini-dropdown-max-height: 176px !default;
+$--tree-select-small-dropdown-max-height: 216px !default;
+$--tree-select-large-dropdown-max-height: 386px !default;
+
+
/* Break-point
--------------------------*/
$--sm: 768px !default;
@@ -1006,23 +1021,49 @@ $--lg: 1200px !default;
$--xl: 1920px !default;
$--breakpoints: (
- 'xs' : (max-width: $--sm - 1),
- 'sm' : (min-width: $--sm),
- 'md' : (min-width: $--md),
- 'lg' : (min-width: $--lg),
- 'xl' : (min-width: $--xl)
+ "xs": (
+ max-width: $--sm - 1,
+ ),
+ "sm": (
+ min-width: $--sm,
+ ),
+ "md": (
+ min-width: $--md,
+ ),
+ "lg": (
+ min-width: $--lg,
+ ),
+ "xl": (
+ min-width: $--xl,
+ ),
);
$--breakpoints-spec: (
- 'xs-only' : (max-width: $--sm - 1),
- 'sm-and-up' : (min-width: $--sm),
- 'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
- 'sm-and-down': (max-width: $--md - 1),
- 'md-and-up' : (min-width: $--md),
- 'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
- 'md-and-down': (max-width: $--lg - 1),
- 'lg-and-up' : (min-width: $--lg),
- 'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
- 'lg-and-down': (max-width: $--xl - 1),
- 'xl-only' : (min-width: $--xl),
+ "xs-only": (
+ max-width: $--sm - 1,
+ ),
+ "sm-and-up": (
+ min-width: $--sm,
+ ),
+ "sm-only": "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
+ "sm-and-down": (
+ max-width: $--md - 1,
+ ),
+ "md-and-up": (
+ min-width: $--md,
+ ),
+ "md-only": "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
+ "md-and-down": (
+ max-width: $--lg - 1,
+ ),
+ "lg-and-up": (
+ min-width: $--lg,
+ ),
+ "lg-only": "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
+ "lg-and-down": (
+ max-width: $--xl - 1,
+ ),
+ "xl-only": (
+ min-width: $--xl,
+ ),
);
diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss
index 00183c6fd..6d292b4c8 100644
--- a/packages/theme-chalk/src/index.scss
+++ b/packages/theme-chalk/src/index.scss
@@ -85,3 +85,4 @@
@import "./descriptions.scss";
@import "./descriptions-item.scss";
@import "./result.scss";
+@import "./tree-select.scss";
diff --git a/packages/theme-chalk/src/tree-select.scss b/packages/theme-chalk/src/tree-select.scss
new file mode 100644
index 000000000..e59234aee
--- /dev/null
+++ b/packages/theme-chalk/src/tree-select.scss
@@ -0,0 +1,165 @@
+@import "mixins/mixins";
+@import "common/var";
+
+@include b(tree-select) {
+ display: inline-block;
+ position: relative;
+ width: 240px;
+
+ .el-tree-select__tags > span {
+ display: contents;
+ }
+
+ .el-input__inner {
+ cursor: pointer;
+ padding-right: 35px;
+ transition: none;
+ }
+
+ .el-input {
+ display: block;
+ & .el-tree-select__caret {
+ transition: transform 0.3s;
+ transform: rotateZ(180deg);
+ cursor: pointer;
+
+ @include when(reverse) {
+ transform: rotateZ(0deg);
+ }
+ }
+
+ & .el-icon-reload {
+ animation: spin 1s linear infinite;
+ }
+
+ &.is-disabled {
+ & .el-input__inner {
+ cursor: not-allowed;
+ }
+ }
+ }
+
+ @include e(input) {
+ border: none;
+ outline: none;
+ padding: 0;
+ margin-left: 15px;
+ color: $--tree-select-multiple-input-color;
+ font-size: $--tree-select-font-size;
+ appearance: none;
+ height: 28px;
+ background-color: transparent;
+ flex-grow: 1;
+ }
+
+ @include e(tags) {
+ position: absolute;
+ line-height: normal;
+ white-space: normal;
+ z-index: $--index-normal;
+ top: 50%;
+ transform: translateY(-50%);
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+
+ .el-tag {
+ box-sizing: border-box;
+ border-color: transparent;
+ margin: 2px 0 2px 6px;
+ background-color: #f0f2f5;
+ display: flex;
+ max-width: 100%;
+ align-items: center;
+
+ &__close.el-icon-close {
+ background-color: $--color-text-placeholder;
+ top: 0;
+ color: $--color-white;
+ flex-shrink: 0;
+
+ &:hover {
+ background-color: $--color-text-secondary;
+ }
+
+ &::before {
+ display: block;
+ transform: translate(0, .5px);
+ }
+ }
+ }
+}
+
+@include b(tree-select-dropdown) {
+ position: absolute;
+ z-index: #{$--index-top + 1};
+ border-radius: $--border-radius-base;
+ border: $--tree-select-dropdown-border;
+ box-sizing: border-box;
+ background-color: $--tree-select-dropdown-background;
+ margin: 0 0 5px;
+
+ @include b(tree-select-dropdown__wrap) {
+ max-height: $--tree-select-dropdown-max-height;
+ }
+
+ @include b(tree-select-dropdown__list) {
+ list-style: none;
+ padding: $--tree-select-dropdown-padding;
+ margin: 0;
+ box-sizing: border-box;
+ .el-tree-node {
+ &:focus {
+ > .el-tree-node__content:not(.is-disabled) {
+ background: $--tree-select-option-selected-hover;
+ }
+ }
+ .el-tree-node__content {
+ transition: background-color 0.3s;
+ color: $--color-text-primary;
+ .el-tree-node__content-text {
+ font-size: $--tree-select-font-size;
+ }
+ &:hover:not(.is-disabled),
+ &:focus:not(.is-disabled) {
+ background: $--tree-select-option-selected-hover;
+ color: $--color-text-primary;
+ }
+ > .is-disabled {
+ cursor: not-allowed;
+ color: $--color-text-secondary;
+ }
+ > .is-selected {
+ color: $--color-primary;
+ }
+ }
+ }
+ }
+
+ @include b(tree-select-dropdown__empty) {
+ padding: $--tree-select-dropdown-empty-padding;
+ margin: 0;
+ text-align: center;
+ color: $--tree-select-dropdown-empty-color;
+ font-size: $--tree-select-font-size;
+ }
+
+ @include m(mini) {
+ @include b(tree-select-dropdown__wrap) {
+ max-height: $--tree-select-mini-dropdown-max-height;
+ }
+ }
+
+ @include m(small) {
+ @include b(tree-select-dropdown__wrap) {
+ max-height: $--tree-select-small-dropdown-max-height;
+ }
+ }
+
+ @include m(large) {
+ @include b(tree-select-dropdown__wrap) {
+ max-height: $--tree-select-large-dropdown-max-height;
+ }
+ }
+}
diff --git a/packages/tree-select/index.js b/packages/tree-select/index.js
new file mode 100644
index 000000000..74eb00e69
--- /dev/null
+++ b/packages/tree-select/index.js
@@ -0,0 +1,8 @@
+import TreeSelect from './src/main';
+
+/* istanbul ignore next */
+TreeSelect.install = function(Vue) {
+ Vue.component(TreeSelect.name, TreeSelect);
+};
+
+export default TreeSelect;
diff --git a/packages/tree-select/src/main.vue b/packages/tree-select/src/main.vue
new file mode 100644
index 000000000..0d96c00f7
--- /dev/null
+++ b/packages/tree-select/src/main.vue
@@ -0,0 +1,771 @@
+
+
+
+
+ {{ selected[0].label }}
+
+ + {{ selected.length - 1 }}
+
+
+
+
+
+ {{ item.data.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data[props['label']] }}
+
+
+
+
+
+
+
+
+
diff --git a/packages/tree-select/src/tree-select-dropdown.vue b/packages/tree-select/src/tree-select-dropdown.vue
new file mode 100644
index 000000000..363936d3f
--- /dev/null
+++ b/packages/tree-select/src/tree-select-dropdown.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
diff --git a/packages/tree/src/tree-node.vue b/packages/tree/src/tree-node.vue
index 3c922dbe6..d7c071964 100644
--- a/packages/tree/src/tree-node.vue
+++ b/packages/tree/src/tree-node.vue
@@ -211,7 +211,7 @@
checkedKeys: store.getCheckedKeys(),
halfCheckedNodes: store.getHalfCheckedNodes(),
halfCheckedKeys: store.getHalfCheckedKeys(),
- });
+ }, this.node);
});
},
diff --git a/packages/tree/src/tree.vue b/packages/tree/src/tree.vue
index 5b1c1b4c4..433cde25c 100644
--- a/packages/tree/src/tree.vue
+++ b/packages/tree/src/tree.vue
@@ -291,6 +291,16 @@
this.treeItems[0] && this.treeItems[0].setAttribute('tabindex', 0);
},
+ initFocusedItem(direction) {
+ if (!this.treeItemArray.length) return;
+ if (direction === 'next') {
+ this.treeItemArray[0].focus();
+ }
+ if (direction === 'prev') {
+ this.treeItemArray[this.treeItemArray.length - 1].focus();
+ }
+ },
+
handleKeydown(ev) {
const currentItem = ev.target;
if (currentItem.className.indexOf('el-tree-node') === -1) return;
diff --git a/src/index.js b/src/index.js
index b5d91bccd..0a7cd1f06 100644
--- a/src/index.js
+++ b/src/index.js
@@ -89,6 +89,7 @@ import Empty from '../packages/empty/index.js';
import Descriptions from '../packages/descriptions/index.js';
import DescriptionsItem from '../packages/descriptions-item/index.js';
import Result from '../packages/result/index.js';
+import TreeSelect from '../packages/tree-select/index.js';
import locale from 'element-ui/src/locale';
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
@@ -177,6 +178,7 @@ const components = [
Descriptions,
DescriptionsItem,
Result,
+ TreeSelect,
CollapseTransition
];
@@ -305,5 +307,6 @@ export default {
Empty,
Descriptions,
DescriptionsItem,
- Result
+ Result,
+ TreeSelect
};
diff --git a/test/unit/specs/tree-select.spec.js b/test/unit/specs/tree-select.spec.js
new file mode 100644
index 000000000..ecb2fa061
--- /dev/null
+++ b/test/unit/specs/tree-select.spec.js
@@ -0,0 +1,95 @@
+import { createTest, createVue, destroyVM } from '../util';
+import TreeSelect from 'packages/tree-select';
+
+const data = [
+ {
+ value: 1,
+ label: '一级 1',
+ children: [
+ {
+ value: 11,
+ label: '二级 1-1',
+ children: [
+ {
+ value: 111,
+ label: '三级 1-1'
+ }
+ ]
+ }
+ ]
+ }
+];
+
+describe('TreeSelect', () => {
+ const getTreeSelectVm = (configs = {}, options) => {
+ ['multiple', 'checkStrictly', 'showCheckbox'].forEach(config => {
+ configs[config] = configs[config] || false;
+ });
+ const vm = createVue(Object.assign({
+ template: `
+
+ `,
+ data() {
+ return {
+ data,
+ value: '',
+ multiple: configs.multiple,
+ checkStrictly: configs.checkStrictly,
+ showCheckbox: configs.showCheckbox
+ };
+ }
+ }, options), true);
+ return vm;
+ };
+
+ let vm;
+ afterEach(() => {
+ destroyVM(vm);
+ });
+
+ it('create', () => {
+ vm = createTest(TreeSelect, true);
+ expect(vm.$el).to.exist;
+ });
+
+ it('render dropdown', async() => {
+ vm = getTreeSelectVm();
+ expect(vm.$el.querySelector('.el-tree')).to.exist;
+ expect(vm.$el.querySelectorAll('.el-tree > .el-tree-node').length).to.equal(1);
+ expect(vm.$el.querySelectorAll('.el-tree .el-tree-node').length).to.equal(3);
+ });
+
+ it('default value', done => {
+ vm = createVue({
+ template: `
+
+
+
+ `,
+ data() {
+ return {
+ data: data,
+ value: 111
+ };
+ }
+ }, true);
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.el-input__inner').value).to.equal('三级 1-1');
+ done();
+ }, 100);
+ });
+
+ it('disabled select', () => {
+ vm = createTest(TreeSelect, { disabled: true, value: '' }, true);
+ expect(vm.$el.querySelector('.el-input').classList.contains('is-disabled')).to.true;
+ });
+});
diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts
index 62c4f2ad4..34d832ccd 100644
--- a/types/element-ui.d.ts
+++ b/types/element-ui.d.ts
@@ -89,7 +89,8 @@ import { ElDescriptions } from './descriptions'
import { ElDescriptionsItem } from './descriptions-item'
import { ElResult } from './result'
import { ElStatistic } from './statistic'
-
+
import { ElTreeSelect } from './tree-select'
+
export interface InstallationOptions {
locale: any,
i18n: any,
@@ -379,4 +380,6 @@ export class DescriptionsItem extends ElDescriptionsItem {}
export class Result extends ElResult {}
/** Statistic Component */
-export class Statistic extends ElStatistic {}
\ No newline at end of file
+export class Statistic extends ElStatistic {}
+/** TreeSelect Component */
+export class TreeSelect extends ElTreeSelect {}
diff --git a/types/tree-select.d.ts b/types/tree-select.d.ts
new file mode 100644
index 000000000..d2e20f877
--- /dev/null
+++ b/types/tree-select.d.ts
@@ -0,0 +1,111 @@
+import { ElementUIComponent, ElementUIComponentSize } from "./component";
+
+export interface TreeData {
+ value?: any;
+ label?: string;
+ disabled?: boolean;
+ isLeaf?: boolean;
+ children?: TreeData[];
+}
+
+export interface TreeProps {
+ label: string;
+ disabled: string;
+ isLeaf: string;
+ children: string;
+}
+
+export interface QueryChangeHandler {
+ /**
+ * @param queryString Current value of the text input
+ */
+ (queryString: string): void;
+}
+
+/** TreeSelect Component */
+export declare class ElTreeSelect extends ElementUIComponent {
+ /** The form input value */
+ value: V | V[];
+
+ /** Tree data */
+ data: D[];
+
+ /** Whether multiple-select is activated */
+ multiple: boolean;
+
+ /** Whether Select is disabled */
+ disabled: boolean;
+
+ /** Unique identity key name for value, required when value is an object */
+ nodeKey: string;
+
+ /** Configuration options, see the following table */
+ props: TreeProps;
+
+ /** Size of Input */
+ size: ElementUIComponentSize;
+
+ /** Whether single select can be cleared */
+ clearable: boolean;
+
+ /** The name attribute of select input */
+ name: string;
+
+ /** Placeholder */
+ placeholder: string;
+
+ /** Whether Select is filterable */
+ filterable: boolean;
+
+ /** Custom filter method */
+ filterMethod: QueryChangeHandler;
+
+ /** Whether to append the popper menu to body */
+ popperAppendToBody: boolean;
+
+ /** Method for loading subtree data */
+ load: (data: D, resolve: Function) => void;
+
+ /** Displayed text when data is empty */
+ emptyText: string;
+
+ /** Horizontal indentation of nodes in adjacent levels in pixels */
+ indent: number;
+
+ /** Whether checked state of a node not affects its father and child nodes when show-checkbox is true */
+ checkStrictly: boolean;
+
+ /** Whether only one node among the same level can be expanded at one time */
+ accordion: boolean;
+
+ /** Whether node is selectable */
+ showCheckbox: boolean;
+
+ /**whether to collapse tags to a text when multiple selecting */
+ collapseTags: boolean;
+
+ /** Whether Select is loading data from server */
+ loading: boolean;
+
+ /** Method for loading subtree data */
+ load: (data: D, resolve: Function) => void;
+
+ /** whether to lazy load leaf node, used with load attribute */
+ lazy: boolean;
+
+ /** Whether to expand all nodes by default */
+ defaultExpandAll: boolean;
+
+ /** Whether to expand father node when a child node is expanded */
+ autoExpandParent: boolean;
+
+ /**
+ * Focus the Input component
+ */
+ focus(): void;
+
+ /**
+ * Blur the Input component, and hide the dropdown
+ */
+ blur(): void;
+}