Commit 96f542e0 authored by Leopoldthecoder's avatar Leopoldthecoder

Merge branch 'dev' into carbon

parents 0a2dd8bd 527f3bdb
...@@ -12,3 +12,4 @@ examples/pages/zh-CN ...@@ -12,3 +12,4 @@ examples/pages/zh-CN
fe.element/element-ui fe.element/element-ui
.npmrc .npmrc
coverage coverage
waiter.config.js
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var version = process.env.VERSION || require('../../package.json').version; var version = process.env.VERSION || require('../../package.json').version;
var content = { '1.0.9': '1.0', '1.1.6': '1.1', '1.2.9': '1.2' }; var content = { '1.0.9': '1.0', '1.1.6': '1.1', '1.2.9': '1.2', '1.3.7': '1.3' };
if (!content[version]) content[version] = '1.3'; if (!content[version]) content[version] = '1.4';
fs.writeFileSync(path.resolve(__dirname, '../../examples/versions.json'), JSON.stringify(content)); fs.writeFileSync(path.resolve(__dirname, '../../examples/versions.json'), JSON.stringify(content));
...@@ -42,7 +42,7 @@ if [ "$TRAVIS_TAG" ]; then ...@@ -42,7 +42,7 @@ if [ "$TRAVIS_TAG" ]; then
export SUB_FOLDER=$(echo "$TRAVIS_TAG" | grep -o -E "\d+\.\d+") export SUB_FOLDER=$(echo "$TRAVIS_TAG" | grep -o -E "\d+\.\d+")
echo $SUB_FOLDER echo $SUB_FOLDER
SUB_FOLDER='1.3' SUB_FOLDER='1.4'
mkdir $SUB_FOLDER mkdir $SUB_FOLDER
rm -rf *.js *.css *.map static rm -rf *.js *.css *.map static
rm -rf $SUB_FOLDER/** rm -rf $SUB_FOLDER/**
......
...@@ -107,6 +107,7 @@ ...@@ -107,6 +107,7 @@
background-color: #ECF8FF; background-color: #ECF8FF;
border-radius: 4px; border-radius: 4px;
border-left: #50bfff 5px solid; border-left: #50bfff 5px solid;
margin-top: 20px;
code { code {
background-color: rgba(#fff, .7); background-color: rgba(#fff, .7);
......
...@@ -243,7 +243,7 @@ Checkbox with button styles. ...@@ -243,7 +243,7 @@ Checkbox with button styles.
| min | minimum number of checkbox checked | number | — | — | | min | minimum number of checkbox checked | number | — | — |
| max | maximum number of checkbox checked | number | — | — | | max | maximum number of checkbox checked | number | — | — |
### Checkbox-group Events ### Checkbox Events
| Event Name | Description | Parameters | | Event Name | Description | Parameters |
|---------- |-------- |---------- | |---------- |-------- |---------- |
| change | triggers when the binding value changes | Event object | | change | triggers when the binding value changes | Event object |
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.el-menu-demo { .el-menu-demo {
padding-left: 55px; padding-left: 55px;
} }
.el-menu-vertical-demo { .el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px; width: 200px;
min-height: 400px; min-height: 400px;
} }
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
data() { data() {
return { return {
activeIndex: '1', activeIndex: '1',
activeIndex2: '1' activeIndex2: '1',
isCollapse: false
}; };
}, },
methods: { methods: {
...@@ -179,10 +180,70 @@ Vertical NavMenu with sub-menus. ...@@ -179,10 +180,70 @@ Vertical NavMenu with sub-menus.
``` ```
::: :::
### Collapse
Vertical NavMenu could be collapsed.
::: demo
```html
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
<el-radio-button :label="false">expand</el-radio-button>
<el-radio-button :label="true">collapse</el-radio-button>
</el-radio-group>
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title">Navigator One</span>
</template>
<el-menu-item-group>
<span slot="title">Group One</span>
<el-menu-item index="1-1">item one</el-menu-item>
<el-menu-item index="1-2">item two</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group Two">
<el-menu-item index="1-3">item three</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<span slot="title">item four</span>
<el-menu-item index="1-4-1">item one</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">Navigator Two</span>
</el-menu-item>
<el-menu-item index="3">
<i class="el-icon-setting"></i>
<span slot="title">Navigator Three</span>
</el-menu-item>
</el-menu>
<script>
export default {
data() {
return {
isCollapse: false
};
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
}
}
}
</script>
```
:::
### Menu Attribute ### Menu Attribute
| Attribute | Description | Type | Accepted Values | Default | | Attribute | Description | Type | Accepted Values | Default |
|---------- |-------- |---------- |------------- |-------- | |---------- |-------- |---------- |------------- |-------- |
| mode | menu display mode | string | horizontal/vertical | vertical | | mode | menu display mode | string | horizontal/vertical | vertical |
| collapse | whether the menu is collapsed (available only in vertical mode) | boolean | — | false |
| theme | theme color | string | light/dark | light | | theme | theme color | string | light/dark | light |
| default-active | index of currently active menu | string | — | — | | default-active | index of currently active menu | string | — | — |
| default-openeds | array that contains keys of currently active sub-menus | Array | — | — | | default-openeds | array that contains keys of currently active sub-menus | Array | — | — |
......
...@@ -637,11 +637,16 @@ Create and select new items that are not included in select options ...@@ -637,11 +637,16 @@ Create and select new items that are not included in select options
``` ```
::: :::
:::tip
If the binding value of Select is an object, make sure to assign `value-key` as its unique identity key name.
:::
### Select Attributes ### Select Attributes
| Attribute | Description | Type | Accepted Values | Default | | Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- | |---------- |-------------- |---------- |-------------------------------- |-------- |
| multiple | whether multiple-select is activated | boolean | — | false | | multiple | whether multiple-select is activated | boolean | — | false |
| disabled | whether Select is disabled | boolean | — | false | | disabled | whether Select is disabled | boolean | — | false |
| value-key | unique identity key name for value, required when value is an object | string | — | value |
| size | size of Input | string | large/small/mini | — | | size | size of Input | string | large/small/mini | — |
| clearable | whether single select can be cleared | boolean | — | false | | clearable | whether single select can be cleared | boolean | — | false |
| multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 | | multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 |
......
...@@ -93,6 +93,35 @@ ...@@ -93,6 +93,35 @@
}] }]
}]; }];
const data3 = [{
id: 1,
label: 'Level one 1',
children: [{
id: 3,
label: 'Level two 2-1',
children: [{
id: 4,
label: 'Level three 3-1-1'
}, {
id: 5,
label: 'Level three 3-1-2',
disabled: true
}]
}, {
id: 2,
label: 'Level two 2-2',
disabled: true,
children: [{
id: 6,
label: 'Level three 3-2-1'
}, {
id: 7,
label: 'Level three 3-2-2',
disabled: true
}]
}]
}];
let id = 1000; let id = 1000;
const regions = [{ const regions = [{
...@@ -211,6 +240,7 @@ ...@@ -211,6 +240,7 @@
return { return {
data, data,
data2, data2,
data3,
regions, regions,
defaultProps, defaultProps,
props, props,
...@@ -363,6 +393,63 @@ Used for node selection. In the following example, data for each layer is acquir ...@@ -363,6 +393,63 @@ Used for node selection. In the following example, data for each layer is acquir
``` ```
::: :::
### Disabled checkbox
The checkbox of a node can be set as disabled.
::: demo In the example, 'disabled' property is declared in defaultProps, and some nodes are set as 'disabled:true'. The corresponding checkboxes are disabled and can't be clicked.
```html
<el-tree
:data="data3"
:props="defaultProps"
show-checkbox
@check-change="handleCheckChange">
</el-tree>
<script>
export default {
data() {
return {
data3: [{
id: 1,
label: 'Level one 1',
children: [{
id: 3,
label: 'Level two 2-1',
children: [{
id: 4,
label: 'Level three 3-1-1'
}, {
id: 5,
label: 'Level three 3-1-2',
disabled: true
}]
}, {
id: 2,
label: 'Level two 2-2',
disabled: true,
children: [{
id: 6,
label: 'Level three 3-2-1'
}, {
id: 7,
label: 'Level three 3-2-2',
disabled: true
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label',
disabled: 'disabled',
},
};
}
};
</script>
```
:::
### Default expanded and default checked ### Default expanded and default checked
Tree nodes can be initially expanded or checked Tree nodes can be initially expanded or checked
...@@ -789,8 +876,9 @@ Only one node among the same level can be expanded at one time. ...@@ -789,8 +876,9 @@ Only one node among the same level can be expanded at one time.
### props ### props
| Attribute | Description | Type | Accepted Values | Default | | Attribute | Description | Type | Accepted Values | Default |
| --------- | ---------------------------------------- | ------ | --------------- | ------- | | --------- | ---------------------------------------- | ------ | --------------- | ------- |
| label | specify which key of node object is used as the node's label | string | — | — | | label | specify which key of node object is used as the node's label | string, function(data, node) | — | — |
| children | specify which key of node object is used as the node's subtree | string | — | — | | children | specify which node object is used as the node's subtree | string, function(data, node) | — | — |
| disabled | specify which node's checkbox disabled | boolean, function(data, node) | — | — |
### Method ### Method
`Tree` has the following method, which returns the currently selected array of nodes. `Tree` has the following method, which returns the currently selected array of nodes.
......
...@@ -254,7 +254,7 @@ ...@@ -254,7 +254,7 @@
| min | 可被勾选的 checkbox 的最小数量 | number | — | — | | min | 可被勾选的 checkbox 的最小数量 | number | — | — |
| max | 可被勾选的 checkbox 的最大数量 | number | — | — | | max | 可被勾选的 checkbox 的最大数量 | number | — | — |
### Checkbox-group Events ### Checkbox Events
| 事件名称 | 说明 | 回调参数 | | 事件名称 | 说明 | 回调参数 |
|---------- |-------- |---------- | |---------- |-------- |---------- |
| change | 当绑定值变化时触发的事件 | event 事件对象 | | change | 当绑定值变化时触发的事件 | event 事件对象 |
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.el-menu-demo { .el-menu-demo {
padding-left: 55px; padding-left: 55px;
} }
.el-menu-vertical-demo { .el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px; width: 200px;
min-height: 400px; min-height: 400px;
} }
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
data() { data() {
return { return {
activeIndex: '1', activeIndex: '1',
activeIndex2: '1' activeIndex2: '1',
isCollapse: true
}; };
}, },
methods: { methods: {
...@@ -181,10 +182,68 @@ ...@@ -181,10 +182,68 @@
``` ```
::: :::
### 折叠
::: demo
```html
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
<el-radio-button :label="false">展开</el-radio-button>
<el-radio-button :label="true">收起</el-radio-button>
</el-radio-group>
<el-menu default-active="2" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" :collapse="isCollapse">
<el-submenu index="1">
<template slot="title">
<i class="el-icon-message"></i>
<span slot="title">导航一</span>
</template>
<el-menu-item-group>
<span slot="title">分组一</span>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<span slot="title">选项4</span>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3">
<i class="el-icon-setting"></i>
<span slot="title">导航三</span>
</el-menu-item>
</el-menu>
<script>
export default {
data() {
return {
isCollapse: false
};
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath);
},
handleClose(key, keyPath) {
console.log(key, keyPath);
}
}
}
</script>
```
:::
### Menu Attribute ### Menu Attribute
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- | |---------- |-------- |---------- |------------- |-------- |
| mode | 模式 | string | horizontal,vertical | vertical | | mode | 模式 | string | horizontal,vertical | vertical |
| collapse | 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用)| boolean | — | false |
| theme | 主题色 | string | light,dark | light | | theme | 主题色 | string | light,dark | light |
| default-active | 当前激活菜单的 index | string | — | — | | default-active | 当前激活菜单的 index | string | — | — |
| default-openeds | 当前打开的submenu的 key 数组 | Array | — | — | | default-openeds | 当前打开的submenu的 key 数组 | Array | — | — |
......
...@@ -632,11 +632,16 @@ ...@@ -632,11 +632,16 @@
``` ```
::: :::
:::tip
如果 Select 的绑定值为对象类型,请务必指定 `value-key` 作为它的唯一性标识。
:::
### Select Attributes ### Select Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- | |---------- |-------------- |---------- |-------------------------------- |-------- |
| multiple | 是否多选 | boolean | — | false | | multiple | 是否多选 | boolean | — | false |
| disabled | 是否禁用 | boolean | — | false | | disabled | 是否禁用 | boolean | — | false |
| value-key | 作为 value 唯一标识的键名,绑定值为对象类型时必填 | string | — | value |
| size | 输入框尺寸 | string | large/small/mini | — | | size | 输入框尺寸 | string | large/small/mini | — |
| clearable | 单选时是否可以清空选项 | boolean | — | false | | clearable | 单选时是否可以清空选项 | boolean | — | false |
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 | | multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
......
...@@ -93,6 +93,35 @@ ...@@ -93,6 +93,35 @@
}] }]
}]; }];
const data3 = [{
id: 1,
label: '一级 2',
children: [{
id: 3,
label: '二级 2-1',
children: [{
id: 4,
label: '三级 3-1-1'
}, {
id: 5,
label: '三级 3-1-2',
disabled: true
}]
}, {
id: 2,
label: '二级 2-2',
disabled: true,
children: [{
id: 6,
label: '三级 3-2-1'
}, {
id: 7,
label: '三级 3-2-2',
disabled: true
}]
}]
}];
let id = 1000; let id = 1000;
const regions = [{ const regions = [{
...@@ -211,6 +240,7 @@ ...@@ -211,6 +240,7 @@
return { return {
data, data,
data2, data2,
data3,
regions, regions,
defaultProps, defaultProps,
props, props,
...@@ -427,6 +457,62 @@ ...@@ -427,6 +457,62 @@
``` ```
::: :::
### 禁用状态
可将 Tree 的某些节点设置为禁用状态
::: demo 通过`disabled`设置禁用状态。
```html
<el-tree
:data="data3"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]">
</el-tree>
<script>
export default {
data() {
return {
data3: [{
id: 1,
label: '一级 2',
children: [{
id: 3,
label: '二级 2-1',
children: [{
id: 4,
label: '三级 3-1-1'
}, {
id: 5,
label: '三级 3-1-2',
disabled: true
}]
}, {
id: 2,
label: '二级 2-2',
disabled: true,
children: [{
id: 6,
label: '三级 3-2-1'
}, {
id: 7,
label: '三级 3-2-2',
disabled: true
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
};
}
};
</script>
```
:::
### 树节点的选择 ### 树节点的选择
::: demo 本例展示如何获取和设置选中节点。获取和设置各有两种方式:通过 node 或通过 key。如果需要通过 key 来获取或设置,则必须设置`node-key` ::: demo 本例展示如何获取和设置选中节点。获取和设置各有两种方式:通过 node 或通过 key。如果需要通过 key 来获取或设置,则必须设置`node-key`
...@@ -789,8 +875,9 @@ ...@@ -789,8 +875,9 @@
### props ### props
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
| -------- | ----------------- | ------ | ---- | ---- | | -------- | ----------------- | ------ | ---- | ---- |
| label | 指定节点标签为节点对象的某个属性值 | string | — | — | | label | 指定节点标签为节点对象的某个属性值 | string, function(data, node) | — | — |
| children | 指定子树为节点对象的某个属性值 | string | — | — | | children | 指定子树为节点对象的某个属性值 | string, function(data, node) | — | — |
| disabled | 指定节点选择框是否禁用 | boolean, function(data, node) | — | — |
### 方法 ### 方法
`Tree` 拥有如下方法,返回目前被选中的节点数组: `Tree` 拥有如下方法,返回目前被选中的节点数组:
......
{"1.0.9":"1.0","1.1.6":"1.1","1.2.9":"1.2","1.3.7":"1.3"} {"1.0.9":"1.0","1.1.6":"1.1","1.2.9":"1.2","1.3.7":"1.3","1.4.0-beta.1":"1.4"}
\ No newline at end of file \ No newline at end of file
{ {
"name": "element-ui", "name": "element-ui",
"version": "1.3.7", "version": "1.4.0-beta.1",
"description": "A Component Library for Vue.js.", "description": "A Component Library for Vue.js.",
"main": "lib/element-ui.common.js", "main": "lib/element-ui.common.js",
"files": [ "files": [
......
<script> <script>
import { isDef } from 'element-ui/src/utils/shared'; import { isDef } from 'element-ui/src/utils/shared';
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
export default { export default {
name: 'ElCascaderMenu', name: 'ElCascaderMenu',
...@@ -94,6 +95,12 @@ ...@@ -94,6 +95,12 @@
} else { } else {
this.$emit('activeItemChange', this.activeValue); this.$emit('activeItemChange', this.activeValue);
} }
},
scrollMenu(menu) {
scrollIntoView(menu, menu.getElementsByClassName('is-active')[0]);
},
handleMenuEnter() {
this.$nextTick(() => this.$refs.menus.forEach(menu => this.scrollMenu(menu)));
} }
}, },
...@@ -121,9 +128,19 @@ ...@@ -121,9 +128,19 @@
click: 'click', click: 'click',
hover: 'mouseenter' hover: 'mouseenter'
}[expandTrigger]; }[expandTrigger];
events.on[triggerEvent] = () => { this.activeItem(item, menuIndex); }; events.on[triggerEvent] = () => {
this.activeItem(item, menuIndex);
this.$nextTick(() => {
// adjust self and next level
this.scrollMenu(this.$refs.menus[menuIndex]);
this.scrollMenu(this.$refs.menus[menuIndex + 1]);
});
};
} else { } else {
events.on.click = () => { this.select(item, menuIndex); }; events.on.click = () => {
this.select(item, menuIndex);
this.$nextTick(() => this.scrollMenu(this.$refs.menus[menuIndex]));
};
} }
} }
...@@ -152,19 +169,22 @@ ...@@ -152,19 +169,22 @@
'el-cascader-menu': true, 'el-cascader-menu': true,
'el-cascader-menu--flexible': isFlat 'el-cascader-menu--flexible': isFlat
}} }}
style={menuStyle}> style={menuStyle}
refInFor
ref="menus">
{items} {items}
</ul> </ul>
); );
}); });
return ( return (
<transition name="el-zoom-in-top" on-after-leave={this.handleMenuLeave}> <transition name="el-zoom-in-top" on-before-enter={this.handleMenuEnter} on-after-leave={this.handleMenuLeave}>
<div <div
v-show={visible} v-show={visible}
class={[ class={[
'el-cascader-menus', 'el-cascader-menus',
popperClass popperClass
]} ]}
ref="wrapper"
> >
{menus} {menus}
</div> </div>
......
...@@ -63,11 +63,29 @@ ...@@ -63,11 +63,29 @@
methods: { methods: {
getCellStyle(month) { getCellStyle(month) {
const style = {}; const style = {};
const date = new Date(this.date);
var year = this.date.getFullYear();
var date = new Date(0);
date.setFullYear(year);
date.setMonth(month); date.setMonth(month);
style.disabled = typeof this.disabledDate === 'function' && date.setHours(0);
this.disabledDate(date); var nextMonth = new Date(date);
nextMonth.setMonth(month + 1);
var flag = false;
if (typeof this.disabledDate === 'function') {
while (date < nextMonth) {
if (this.disabledDate(date)) {
date = new Date(date.getTime() + 8.64e7);
} else {
break;
}
}
if ((date - nextMonth) === 0) flag = true;
}
style.disabled = flag;
style.current = this.month === month; style.current = this.month === month;
return style; return style;
......
...@@ -62,11 +62,28 @@ ...@@ -62,11 +62,28 @@
methods: { methods: {
getCellStyle(year) { getCellStyle(year) {
const style = {}; const style = {};
const date = new Date(this.date);
var date = new Date(0);
date.setFullYear(year); date.setFullYear(year);
style.disabled = typeof this.disabledDate === 'function' && date.setHours(0);
this.disabledDate(date); var nextYear = new Date(date);
nextYear.setFullYear(year + 1);
var flag = false;
if (typeof this.disabledDate === 'function') {
while (date < nextYear) {
if (this.disabledDate(date)) {
date = new Date(date.getTime() + 8.64e7);
} else {
break;
}
}
if ((date - nextYear) === 0) flag = true;
}
style.disabled = flag;
style.current = Number(this.year) === year; style.current = Number(this.year) === year;
return style; return style;
......
...@@ -406,11 +406,11 @@ ...@@ -406,11 +406,11 @@
visibleDate: { visibleDate: {
get() { get() {
return formatDate(this.date); return formatDate(this.date, this.dateFormat);
}, },
set(val) { set(val) {
const date = parseDate(val, 'yyyy-MM-dd'); const date = parseDate(val, this.dateFormat);
if (!date) { if (!date) {
return; return;
} }
...@@ -445,6 +445,14 @@ ...@@ -445,6 +445,14 @@
} else { } else {
return 'HH:mm:ss'; return 'HH:mm:ss';
} }
},
dateFormat() {
if (this.format) {
return this.format.replace('HH:mm', '').replace(':ss', '').trim();
} else {
return 'yyyy-MM-dd';
}
} }
} }
}; };
......
<template> <template>
<transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')"> <transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="$emit('dodestroy')">
<div <div
ref="popper"
v-show="visible" v-show="visible"
:style="{ width: width + 'px' }" :style="{ width: width + 'px' }"
:class="popperClass" :class="popperClass"
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
<script type="text/babel"> <script type="text/babel">
import ElScrollbar from 'element-ui/packages/scrollbar'; import ElScrollbar from 'element-ui/packages/scrollbar';
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
const parseTime = function(time) { const parseTime = function(time) {
const values = ('' || time).split(':'); const values = ('' || time).split(':');
...@@ -81,6 +83,7 @@ ...@@ -81,6 +83,7 @@
} else if (this.maxTime && compareTime(val, this.maxTime) > 0) { } else if (this.maxTime && compareTime(val, this.maxTime) > 0) {
this.$emit('pick'); this.$emit('pick');
} }
this.$nextTick(() => this.scrollToOption());
} }
}, },
...@@ -93,6 +96,15 @@ ...@@ -93,6 +96,15 @@
handleClear() { handleClear() {
this.$emit('pick'); this.$emit('pick');
},
scrollToOption(className = 'selected') {
const menu = this.$refs.popper.querySelector('.el-picker-panel__content');
scrollIntoView(menu, menu.getElementsByClassName(className)[0]);
},
handleMenuEnter() {
this.$nextTick(() => this.scrollToOption());
} }
}, },
......
...@@ -179,7 +179,7 @@ const TYPE_VALUE_RESOLVER_MAP = { ...@@ -179,7 +179,7 @@ const TYPE_VALUE_RESOLVER_MAP = {
}; };
const PLACEMENT_MAP = { const PLACEMENT_MAP = {
left: 'bottom-start', left: 'bottom-start',
center: 'bottom-center', center: 'bottom',
right: 'bottom-end' right: 'bottom-end'
}; };
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
'is-validating': validateState === 'validating', 'is-validating': validateState === 'validating',
'is-required': isRequired || required 'is-required': isRequired || required
}"> }">
<label :for="prop" class="el-form-item__label" v-bind:style="labelStyle" v-if="label"> <label :for="prop" class="el-form-item__label" v-bind:style="labelStyle" v-if="label || $slots.label">
<slot name="label">{{label + form.labelSuffix}}</slot> <slot name="label">{{label + form.labelSuffix}}</slot>
</label> </label>
<div class="el-form-item__content" v-bind:style="contentStyle"> <div class="el-form-item__content" v-bind:style="contentStyle">
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
componentName: 'ElMenuItemGroup', componentName: 'ElMenuItemGroup',
inject: ['rootMenu'],
props: { props: {
title: { title: {
type: String type: String
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
levelPadding() { levelPadding() {
let padding = 10; let padding = 10;
let parent = this.$parent; let parent = this.$parent;
if (this.rootMenu.collapse) return 20;
while (parent && parent.$options.componentName !== 'ElMenu') { while (parent && parent.$options.componentName !== 'ElMenu') {
if (parent.$options.componentName === 'ElSubmenu') { if (parent.$options.componentName === 'ElSubmenu') {
padding += 20; padding += 20;
......
...@@ -6,7 +6,19 @@ ...@@ -6,7 +6,19 @@
'is-active': active, 'is-active': active,
'is-disabled': disabled 'is-disabled': disabled
}"> }">
<el-tooltip
v-if="$parent === rootMenu && rootMenu.collapse"
effect="dark"
placement="right">
<div slot="content"><slot name="title"></slot></div>
<div style="position: absolute;left: 0;top: 0;height: 100%;width: 100%;display: inline-block;box-sizing: border-box;padding: 0 20px;">
<slot></slot> <slot></slot>
</div>
</el-tooltip>
<template v-else>
<slot></slot>
<slot name="title"></slot>
</template>
</li> </li>
</template> </template>
<script> <script>
......
...@@ -36,12 +36,17 @@ export default { ...@@ -36,12 +36,17 @@ export default {
let padding = 20; let padding = 20;
let parent = this.$parent; let parent = this.$parent;
if (this.rootMenu.collapse) {
padding = 20;
} else {
while (parent && parent.$options.componentName !== 'ElMenu') { while (parent && parent.$options.componentName !== 'ElMenu') {
if (parent.$options.componentName === 'ElSubmenu') { if (parent.$options.componentName === 'ElSubmenu') {
padding += 20; padding += 20;
} }
parent = parent.$parent; parent = parent.$parent;
} }
}
return {paddingLeft: padding + 'px'}; return {paddingLeft: padding + 'px'};
} }
} }
......
<template> <template>
<el-menu-collapse-transition>
<ul class="el-menu" <ul class="el-menu"
:key="+collapse"
:class="{ :class="{
'el-menu--horizontal': mode === 'horizontal', 'el-menu--horizontal': mode === 'horizontal',
'el-menu--dark': theme === 'dark' 'el-menu--dark': theme === 'dark',
'el-menu--collapse': collapse
}" }"
> >
<slot></slot> <slot></slot>
</ul> </ul>
</el-menu-collapse-transition>
</template> </template>
<script> <script>
import Vue from 'vue';
import emitter from 'element-ui/src/mixins/emitter'; import emitter from 'element-ui/src/mixins/emitter';
import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
Vue.component('el-menu-collapse-transition', {
functional: true,
render(createElement, context) {
const data = {
props: {
mode: 'out-in'
},
on: {
beforeEnter(el) {
el.style.opacity = 0.2;
},
enter(el) {
addClass(el, 'el-opacity-transition');
el.style.opacity = 1;
},
afterEnter(el) {
removeClass(el, 'el-opacity-transition');
el.style.opacity = '';
},
beforeLeave(el) {
if (!el.dataset) el.dataset = {};
if (hasClass(el, 'el-menu--collapse')) {
removeClass(el, 'el-menu--collapse');
el.dataset.oldOverflow = el.style.overflow;
el.dataset.scrollWidth = el.scrollWidth;
addClass(el, 'el-menu--collapse');
}
el.style.width = el.scrollWidth + 'px';
el.style.overflow = 'hidden';
},
leave(el) {
if (!hasClass(el, 'el-menu--collapse')) {
addClass(el, 'horizontal-collapse-transition');
el.style.width = '64px';
} else {
addClass(el, 'horizontal-collapse-transition');
el.style.width = el.dataset.scrollWidth + 'px';
}
},
afterLeave(el) {
removeClass(el, 'horizontal-collapse-transition');
if (hasClass(el, 'el-menu--collapse')) {
el.style.width = el.dataset.scrollWidth + 'px';
} else {
el.style.width = '64px';
}
el.style.overflow = el.dataset.oldOverflow;
}
}
};
return createElement('transition', data, context.children);
}
});
export default { export default {
name: 'ElMenu', name: 'ElMenu',
...@@ -18,6 +85,12 @@ ...@@ -18,6 +85,12 @@
mixins: [emitter], mixins: [emitter],
provide() {
return {
rootMenu: this
};
},
props: { props: {
mode: { mode: {
type: String, type: String,
...@@ -37,7 +110,8 @@ ...@@ -37,7 +110,8 @@
menuTrigger: { menuTrigger: {
type: String, type: String,
default: 'hover' default: 'hover'
} },
collapse: Boolean
}, },
data() { data() {
return { return {
...@@ -106,7 +180,7 @@ ...@@ -106,7 +180,7 @@
this.activedIndex = item.index; this.activedIndex = item.index;
this.$emit('select', index, indexPath, item); this.$emit('select', index, indexPath, item);
if (this.mode === 'horizontal') { if (this.mode === 'horizontal' || this.collapse) {
this.openedMenus = []; this.openedMenus = [];
} }
......
...@@ -5,18 +5,21 @@ ...@@ -5,18 +5,21 @@
'is-active': active, 'is-active': active,
'is-opened': opened 'is-opened': opened
}" }"
@mouseenter="handleMouseenter"
@mouseleave="handleMouseleave"
> >
<div class="el-submenu__title" ref="submenu-title" :style="paddingStyle"> <div class="el-submenu__title" ref="submenu-title" @click="handleClick" :style="paddingStyle">
<slot name="title"></slot> <slot name="title"></slot>
<i :class="{ <i :class="{
'el-submenu__icon-arrow': true, 'el-submenu__icon-arrow': true,
'el-icon-arrow-down': rootMenu.mode === 'vertical', 'el-icon-caret-bottom': rootMenu.mode === 'horizontal',
'el-icon-caret-bottom': rootMenu.mode === 'horizontal' 'el-icon-arrow-down': rootMenu.mode === 'vertical' && !rootMenu.collapse,
'el-icon-caret-right': rootMenu.mode === 'vertical' && rootMenu.collapse
}"> }">
</i> </i>
</div> </div>
<template v-if="rootMenu.mode === 'horizontal'"> <template v-if="rootMenu.mode === 'horizontal' || (rootMenu.mode === 'vertical' && rootMenu.collapse)">
<transition name="el-zoom-in-top"> <transition :name="menuTransitionName">
<ul class="el-menu" v-show="opened"><slot></slot></ul> <ul class="el-menu" v-show="opened"><slot></slot></ul>
</transition> </transition>
</template> </template>
...@@ -45,6 +48,7 @@ ...@@ -45,6 +48,7 @@
required: true required: true
} }
}, },
data() { data() {
return { return {
timeout: null, timeout: null,
...@@ -53,6 +57,9 @@ ...@@ -53,6 +57,9 @@
}; };
}, },
computed: { computed: {
menuTransitionName() {
return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top';
},
opened() { opened() {
return this.rootMenu.openedMenus.indexOf(this.index) > -1; return this.rootMenu.openedMenus.indexOf(this.index) > -1;
}, },
...@@ -93,37 +100,40 @@ ...@@ -93,37 +100,40 @@
delete this.submenus[item.index]; delete this.submenus[item.index];
}, },
handleClick() { handleClick() {
const {rootMenu} = this;
if (
(rootMenu.menuTrigger === 'hover' && rootMenu.mode === 'horizontal') ||
(rootMenu.collapse && rootMenu.mode === 'vertical')
) {
return;
}
this.dispatch('ElMenu', 'submenu-click', this); this.dispatch('ElMenu', 'submenu-click', this);
}, },
handleMouseenter() { handleMouseenter() {
const {rootMenu} = this;
if (
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
(!rootMenu.collapse && rootMenu.mode === 'vertical')
) {
return;
}
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.rootMenu.openMenu(this.index, this.indexPath); this.rootMenu.openMenu(this.index, this.indexPath);
}, 300); }, 300);
}, },
handleMouseleave() { handleMouseleave() {
const {rootMenu} = this;
if (
(rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') ||
(!rootMenu.collapse && rootMenu.mode === 'vertical')
) {
return;
}
clearTimeout(this.timeout); clearTimeout(this.timeout);
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.rootMenu.closeMenu(this.index, this.indexPath); this.rootMenu.closeMenu(this.index, this.indexPath);
}, 300); }, 300);
},
initEvents() {
let {
rootMenu,
handleMouseenter,
handleMouseleave,
handleClick
} = this;
let triggerElm;
if (rootMenu.mode === 'horizontal' && rootMenu.menuTrigger === 'hover') {
triggerElm = this.$el;
triggerElm.addEventListener('mouseenter', handleMouseenter);
triggerElm.addEventListener('mouseleave', handleMouseleave);
} else {
triggerElm = this.$refs['submenu-title'];
triggerElm.addEventListener('click', handleClick);
}
} }
}, },
created() { created() {
...@@ -133,9 +143,6 @@ ...@@ -133,9 +143,6 @@
beforeDestroy() { beforeDestroy() {
this.parentMenu.removeSubmenu(this); this.parentMenu.removeSubmenu(this);
this.rootMenu.removeSubmenu(this); this.rootMenu.removeSubmenu(this);
},
mounted() {
this.initEvents();
} }
}; };
</script> </script>
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<script type="text/babel"> <script type="text/babel">
import Emitter from 'element-ui/src/mixins/emitter'; import Emitter from 'element-ui/src/mixins/emitter';
import { getValueByPath } from 'element-ui/src/utils/util';
export default { export default {
mixins: [Emitter], mixins: [Emitter],
...@@ -47,8 +48,13 @@ ...@@ -47,8 +48,13 @@
}, },
computed: { computed: {
isObject() {
const type = typeof this.value;
return type !== 'string' && type !== 'number' && type !== 'boolean';
},
currentLabel() { currentLabel() {
return this.label || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : ''); return this.label || (this.isObject ? '' : this.value);
}, },
currentValue() { currentValue() {
...@@ -65,9 +71,9 @@ ...@@ -65,9 +71,9 @@
itemSelected() { itemSelected() {
if (!this.parent.multiple) { if (!this.parent.multiple) {
return this.value === this.parent.value; return this.isEqual(this.value, this.parent.value);
} else { } else {
return this.parent.value.indexOf(this.value) > -1; return this.contains(this.parent.value, this.value);
} }
}, },
...@@ -92,6 +98,26 @@ ...@@ -92,6 +98,26 @@
}, },
methods: { methods: {
isEqual(a, b) {
if (!this.isObject) {
return a === b;
} else {
const valueKey = this.parent.valueKey;
return getValueByPath(a, valueKey) === getValueByPath(b, valueKey);
}
},
contains(arr = [], target) {
if (!this.isObject) {
return arr.indexOf(target) > -1;
} else {
const valueKey = this.parent.valueKey;
return arr.some(item => {
return getValueByPath(item, valueKey) === getValueByPath(target, valueKey);
});
}
},
handleGroupDisabled(val) { handleGroupDisabled(val) {
this.groupDisabled = val; this.groupDisabled = val;
}, },
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<transition-group @after-leave="resetInputHeight"> <transition-group @after-leave="resetInputHeight">
<el-tag <el-tag
v-for="item in selected" v-for="item in selected"
:key="item.value" :key="getValueKey(item)"
closable closable
:hit="item.hitState" :hit="item.hitState"
type="primary" type="primary"
...@@ -67,8 +67,8 @@ ...@@ -67,8 +67,8 @@
</el-input> </el-input>
<transition <transition
name="el-zoom-in-top" name="el-zoom-in-top"
@after-leave="doDestroy" @before-enter="handleMenuEnter"
@after-enter="handleMenuEnter"> @after-leave="doDestroy">
<el-select-menu <el-select-menu
ref="popper" ref="popper"
v-show="visible && emptyText !== false"> v-show="visible && emptyText !== false">
...@@ -105,6 +105,9 @@ ...@@ -105,6 +105,9 @@
import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom'; import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'; import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
import { t } from 'element-ui/src/locale'; import { t } from 'element-ui/src/locale';
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
import { getValueByPath } from 'element-ui/src/utils/util';
const sizeMap = { const sizeMap = {
'large': 42, 'large': 42,
'small': 30, 'small': 30,
...@@ -194,7 +197,11 @@ ...@@ -194,7 +197,11 @@
return t('el.select.placeholder'); return t('el.select.placeholder');
} }
}, },
defaultFirstOption: Boolean defaultFirstOption: Boolean,
valueKey: {
type: String,
default: 'value'
}
}, },
data() { data() {
...@@ -210,13 +217,10 @@ ...@@ -210,13 +217,10 @@
cachedPlaceHolder: '', cachedPlaceHolder: '',
optionsCount: 0, optionsCount: 0,
filteredOptionsCount: 0, filteredOptionsCount: 0,
dropdownUl: null,
visible: false, visible: false,
selectedLabel: '', selectedLabel: '',
hoverIndex: -1, hoverIndex: -1,
query: '', query: '',
bottomOverflow: 0,
topOverflow: 0,
optionsAllDisabled: false, optionsAllDisabled: false,
inputHovering: false, inputHovering: false,
currentPlaceholder: '' currentPlaceholder: ''
...@@ -292,7 +296,6 @@ ...@@ -292,7 +296,6 @@
} }
}); });
if (!this.multiple) { if (!this.multiple) {
this.getOverflows();
if (this.selected) { if (this.selected) {
if (this.filterable && this.allowCreate && if (this.filterable && this.allowCreate &&
this.createdSelected && this.createdOption) { this.createdSelected && this.createdOption) {
...@@ -353,44 +356,31 @@ ...@@ -353,44 +356,31 @@
} }
}, },
handleMenuEnter() { scrollToOption(className = 'selected') {
if (!this.dropdownUl) { const menu = this.$refs.popper.$el.querySelector('.el-select-dropdown__wrap');
this.dropdownUl = this.$refs.popper.$el.querySelector('.el-select-dropdown__wrap'); scrollIntoView(menu, menu.getElementsByClassName(className)[0]);
this.getOverflows();
}
if (!this.multiple && this.dropdownUl) {
this.resetMenuScroll();
}
},
getOverflows() {
if (this.dropdownUl && this.selected && this.selected.$el) {
let selectedRect = this.selected.$el.getBoundingClientRect();
let popperRect = this.$refs.popper.$el.getBoundingClientRect();
this.bottomOverflow = selectedRect.bottom - popperRect.bottom;
this.topOverflow = selectedRect.top - popperRect.top;
}
}, },
resetMenuScroll() { handleMenuEnter() {
if (this.bottomOverflow > 0) { this.$nextTick(() => this.scrollToOption());
this.dropdownUl.scrollTop += this.bottomOverflow;
} else if (this.topOverflow < 0) {
this.dropdownUl.scrollTop += this.topOverflow;
}
}, },
getOption(value) { getOption(value) {
let option; let option;
const type = typeof value;
const isObject = type !== 'string' && type !== 'number' && type !== 'boolean';
for (let i = this.cachedOptions.length - 1; i >= 0; i--) { for (let i = this.cachedOptions.length - 1; i >= 0; i--) {
const cachedOption = this.cachedOptions[i]; const cachedOption = this.cachedOptions[i];
if (cachedOption.value === value) { const isEqual = isObject
? getValueByPath(cachedOption.value, this.valueKey) === getValueByPath(value, this.valueKey)
: cachedOption.value === value;
if (isEqual) {
option = cachedOption; option = cachedOption;
break; break;
} }
} }
if (option) return option; if (option) return option;
const label = typeof value === 'string' || typeof value === 'number' const label = !isObject
? value : ''; ? value : '';
let newOption = { let newOption = {
value: value, value: value,
...@@ -525,7 +515,7 @@ ...@@ -525,7 +515,7 @@
handleOptionSelect(option) { handleOptionSelect(option) {
if (this.multiple) { if (this.multiple) {
const value = this.value.slice(); const value = this.value.slice();
const optionIndex = value.indexOf(option.value); const optionIndex = this.getValueIndex(value, option.value);
if (optionIndex > -1) { if (optionIndex > -1) {
value.splice(optionIndex, 1); value.splice(optionIndex, 1);
} else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) { } else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) {
...@@ -541,6 +531,26 @@ ...@@ -541,6 +531,26 @@
this.$emit('input', option.value); this.$emit('input', option.value);
this.visible = false; this.visible = false;
} }
this.$nextTick(() => this.scrollToOption());
},
getValueIndex(arr = [], value) {
const type = typeof value;
const isObject = type !== 'string' && type !== 'number' && type !== 'boolean';
if (!isObject) {
return arr.indexOf(value);
} else {
const valueKey = this.valueKey;
let index = -1;
arr.some((item, i) => {
if (getValueByPath(item, valueKey) === getValueByPath(value, valueKey)) {
index = i;
return true;
}
return false;
});
return index;
}
}, },
toggleMenu() { toggleMenu() {
...@@ -565,7 +575,6 @@ ...@@ -565,7 +575,6 @@
if (this.hoverIndex === this.options.length) { if (this.hoverIndex === this.options.length) {
this.hoverIndex = 0; this.hoverIndex = 0;
} }
this.resetScrollTop();
if (this.options[this.hoverIndex].disabled === true || if (this.options[this.hoverIndex].disabled === true ||
this.options[this.hoverIndex].groupDisabled === true || this.options[this.hoverIndex].groupDisabled === true ||
!this.options[this.hoverIndex].visible) { !this.options[this.hoverIndex].visible) {
...@@ -577,7 +586,6 @@ ...@@ -577,7 +586,6 @@
if (this.hoverIndex < 0) { if (this.hoverIndex < 0) {
this.hoverIndex = this.options.length - 1; this.hoverIndex = this.options.length - 1;
} }
this.resetScrollTop();
if (this.options[this.hoverIndex].disabled === true || if (this.options[this.hoverIndex].disabled === true ||
this.options[this.hoverIndex].groupDisabled === true || this.options[this.hoverIndex].groupDisabled === true ||
!this.options[this.hoverIndex].visible) { !this.options[this.hoverIndex].visible) {
...@@ -585,19 +593,7 @@ ...@@ -585,19 +593,7 @@
} }
} }
} }
}, this.$nextTick(() => this.scrollToOption('hover'));
resetScrollTop() {
let bottomOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().bottom -
this.$refs.popper.$el.getBoundingClientRect().bottom;
let topOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().top -
this.$refs.popper.$el.getBoundingClientRect().top;
if (bottomOverflowDistance > 0) {
this.dropdownUl.scrollTop += bottomOverflowDistance;
}
if (topOverflowDistance < 0) {
this.dropdownUl.scrollTop += topOverflowDistance;
}
}, },
selectOption() { selectOption() {
...@@ -667,6 +663,15 @@ ...@@ -667,6 +663,15 @@
} }
} }
} }
},
getValueKey(item) {
const type = typeof item.value;
if (type === 'number' || type === 'string') {
return item.value;
} else {
return getValueByPath(item.value, this.valueKey);
}
} }
}, },
......
import ElCheckbox from 'element-ui/packages/checkbox'; import ElCheckbox from 'element-ui/packages/checkbox';
import ElTag from 'element-ui/packages/tag'; import ElTag from 'element-ui/packages/tag';
import objectAssign from 'element-ui/src/utils/merge'; import objectAssign from 'element-ui/src/utils/merge';
import { getValueByPath } from './util'; import { getValueByPath } from 'element-ui/src/utils/util';
let columnIdSeed = 1; let columnIdSeed = 1;
......
...@@ -312,7 +312,7 @@ ...@@ -312,7 +312,7 @@
}, },
selection() { selection() {
return this.store.selection; return this.store.states.selection;
}, },
columns() { columns() {
......
import { getValueByPath } from 'element-ui/src/utils/util';
export const getCell = function(event) { export const getCell = function(event) {
let cell = event.target; let cell = event.target;
...@@ -11,24 +13,6 @@ export const getCell = function(event) { ...@@ -11,24 +13,6 @@ export const getCell = function(event) {
return null; return null;
}; };
export const getValueByPath = function(object, prop) {
prop = prop || '';
const paths = prop.split('.');
let current = object;
let result = null;
for (let i = 0, j = paths.length; i < j; i++) {
const path = paths[i];
if (!current) break;
if (i === j - 1) {
result = current[path];
break;
}
current = current[path];
}
return result;
};
const isObject = function(obj) { const isObject = function(obj) {
return obj !== null && typeof obj === 'object'; return obj !== null && typeof obj === 'object';
}; };
......
{ {
"name": "element-theme-default", "name": "element-theme-default",
"version": "1.3.7", "version": "1.4.0-beta.1",
"description": "Element component default theme.", "description": "Element component default theme.",
"main": "lib/index.css", "main": "lib/index.css",
"style": "lib/index.css", "style": "lib/index.css",
......
...@@ -68,9 +68,25 @@ ...@@ -68,9 +68,25 @@
transform: scaleY(0); transform: scaleY(0);
} }
.el-zoom-in-left-enter-active,
.el-zoom-in-left-leave-active {
opacity: 1;
transform: scale(1, 1);
transition: var(--md-fade-transition);
transform-origin: top left;
}
.el-zoom-in-left-enter,
.el-zoom-in-left-leave-active {
opacity: 0;
transform: scale(.45, .45);
}
.collapse-transition { .collapse-transition {
transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out; transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out;
} }
.horizontal-collapse-transition {
transition: 0.3s width ease-in-out, 0.3s padding-left ease-in-out, 0.3s padding-right ease-in-out;
}
.el-list-enter-active, .el-list-enter-active,
.el-list-leave-active { .el-list-leave-active {
...@@ -80,3 +96,7 @@ ...@@ -80,3 +96,7 @@
opacity: 0; opacity: 0;
transform: translateY(-30px); transform: translateY(-30px);
} }
.el-opacity-transition {
transition: opacity .3s cubic-bezier(.55,0,.1,1);
}
\ No newline at end of file
...@@ -137,6 +137,45 @@ ...@@ -137,6 +137,45 @@
} }
} }
} }
@m collapse {
width: 64px;
> .el-menu-item,
> .el-submenu > .el-submenu__title {
text-align: center;
[class^="el-icon-"] {
margin: 0;
vertical-align: middle;
}
.el-submenu__icon-arrow {
display: none;
}
span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
.el-submenu {
position: relative;
& .el-menu {
position: absolute;
margin-left: 5px;
top: 0;
left: 100%;
z-index: 10;
}
&.is-opened {
> .el-submenu__title .el-submenu__icon-arrow {
transform: none;
}
}
}
}
} }
@b menu-item { @b menu-item {
@extend menu-item; @extend menu-item;
...@@ -175,6 +214,7 @@ ...@@ -175,6 +214,7 @@
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
padding: 0 45px; padding: 0 45px;
min-width: 200px;
&:hover { &:hover {
background-color: var(--color-base-gray); background-color: var(--color-base-gray);
......
...@@ -80,9 +80,12 @@ export default { ...@@ -80,9 +80,12 @@ export default {
if (!vnode) return vnode; if (!vnode) return vnode;
const data = vnode.data = vnode.data || {}; const data = vnode.data = vnode.data || {};
const on = vnode.data.on = vnode.data.on || {}; const on = vnode.data.on = vnode.data.on || {};
const nativeOn = vnode.data.nativeOn = vnode.data.nativeOn || {};
on.mouseenter = this.addEventHandle(on.mouseenter, () => { this.setExpectedState(true); this.handleShowPopper(); }); on.mouseenter = this.addEventHandle(on.mouseenter, () => { this.setExpectedState(true); this.handleShowPopper(); });
on.mouseleave = this.addEventHandle(on.mouseleave, () => { this.setExpectedState(false); this.debounceClose(); }); on.mouseleave = this.addEventHandle(on.mouseleave, () => { this.setExpectedState(false); this.debounceClose(); });
nativeOn.mouseenter = this.addEventHandle(nativeOn.mouseenter, () => { this.setExpectedState(true); this.handleShowPopper(); });
nativeOn.mouseleave = this.addEventHandle(nativeOn.mouseleave, () => { this.setExpectedState(false); this.debounceClose(); });
data.staticClass = this.concatClass(data.staticClass, 'el-tooltip'); data.staticClass = this.concatClass(data.staticClass, 'el-tooltip');
return vnode; return vnode;
......
import objectAssign from 'element-ui/src/utils/merge'; import objectAssign from 'element-ui/src/utils/merge';
import { markNodeData, NODE_KEY } from './util'; import { markNodeData, NODE_KEY } from './util';
const reInitChecked = function(node) { export const getChildState = node => {
const siblings = node.childNodes;
let all = true; let all = true;
let none = true; let none = true;
let allWithoutDisable = true;
for (let i = 0, j = siblings.length; i < j; i++) { for (let i = 0, j = node.length; i < j; i++) {
const sibling = siblings[i]; const n = node[i];
if (sibling.checked !== true || sibling.indeterminate) { if (n.checked !== true || n.indeterminate) {
all = false; all = false;
if (!n.disabled) {
allWithoutDisable = false;
} }
if (sibling.checked !== false || sibling.indeterminate) { }
if (n.checked !== false || n.indeterminate) {
none = false; none = false;
} }
} }
return { all, none, allWithoutDisable, half: !all && !none };
};
const reInitChecked = function(node) {
const {all, none, half} = getChildState(node.childNodes);
if (all) { if (all) {
node.setChecked(true); node.checked = true;
} else if (!all && !none) { node.indeterminate = false;
node.setChecked('half'); } else if (half) {
node.checked = false;
node.indeterminate = true;
} else if (none) { } else if (none) {
node.setChecked(false); node.checked = false;
node.indeterminate = false;
}
const parent = node.parent;
if (!parent || parent.level === 0) return;
if (!node.store.checkStrictly) {
reInitChecked(parent);
} }
}; };
...@@ -145,6 +161,10 @@ export default class Node { ...@@ -145,6 +161,10 @@ export default class Node {
return null; return null;
} }
get disabled() {
return getPropertyFromData(this, 'disabled');
}
insertChild(child, index) { insertChild(child, index) {
if (!child) throw new Error('insertChild error: child is required.'); if (!child) throw new Error('insertChild error: child is required.');
...@@ -260,16 +280,29 @@ export default class Node { ...@@ -260,16 +280,29 @@ export default class Node {
this.isLeaf = false; this.isLeaf = false;
} }
setChecked(value, deep) { setChecked(value, deep, recursion, passValue) {
this.indeterminate = value === 'half'; this.indeterminate = value === 'half';
this.checked = value === true; this.checked = value === true;
let { allWithoutDisable } = getChildState(this.childNodes);
if (this.childNodes.length && allWithoutDisable) {
this.checked = false;
value = false;
}
const handleDescendants = () => { const handleDescendants = () => {
if (deep) { if (deep) {
const childNodes = this.childNodes; const childNodes = this.childNodes;
for (let i = 0, j = childNodes.length; i < j; i++) { for (let i = 0, j = childNodes.length; i < j; i++) {
const child = childNodes[i]; const child = childNodes[i];
child.setChecked(value !== false, deep); passValue = passValue || value !== false;
const isCheck = child.disabled ? child.checked : passValue;
child.setChecked(isCheck, deep, true, passValue);
}
const { half, all } = getChildState(childNodes);
if (!all) {
this.checked = all;
this.indeterminate = half;
} }
} }
}; };
...@@ -288,7 +321,7 @@ export default class Node { ...@@ -288,7 +321,7 @@ export default class Node {
const parent = this.parent; const parent = this.parent;
if (!parent || parent.level === 0) return; if (!parent || parent.level === 0) return;
if (!this.store.checkStrictly) { if (!this.store.checkStrictly && !recursion) {
reInitChecked(parent); reInitChecked(parent);
} }
} }
...@@ -337,7 +370,7 @@ export default class Node { ...@@ -337,7 +370,7 @@ export default class Node {
} }
loadData(callback, defaultProps = {}) { loadData(callback, defaultProps = {}) {
if (this.store.lazy === true && this.store.load && !this.loaded && !this.loading) { if (this.store.lazy === true && this.store.load && !this.loaded && (!this.loading || Object.keys(defaultProps).length)) {
this.loading = true; this.loading = true;
const resolve = (children) => { const resolve = (children) => {
......
...@@ -188,47 +188,37 @@ export default class TreeStore { ...@@ -188,47 +188,37 @@ export default class TreeStore {
} }
_setCheckedKeys(key, leafOnly = false, checkedKeys) { _setCheckedKeys(key, leafOnly = false, checkedKeys) {
const allNodes = this._getAllNodes(); const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
allNodes.sort((a, b) => b.level - a.level); const cache = Object.create(null);
const keys = Object.keys(checkedKeys); const keys = Object.keys(checkedKeys);
allNodes.forEach((node) => { allNodes.forEach(node => node.setChecked(false, false));
let checked = keys.indexOf(node.data[key] + '') > -1; for (let i = 0, j = allNodes.length; i < j; i++) {
const node = allNodes[i];
if (!node.isLeaf) { const nodeKey = node.data[key].toString();
if (!this.checkStrictly) { let checked = keys.indexOf(nodeKey) > -1;
const childNodes = node.childNodes; if (!checked) {
if (node.checked && !cache[nodeKey]) {
let all = true; node.setChecked(false, false);
let none = true;
for (let i = 0, j = childNodes.length; i < j; i++) {
const child = childNodes[i];
if (child.checked !== true || child.indeterminate) {
all = false;
}
if (child.checked !== false || child.indeterminate) {
none = false;
} }
continue;
} }
if (all) { let parent = node.parent;
node.setChecked(true, !this.checkStrictly); while (parent && parent.level > 0) {
} else if (!all && !none) { cache[parent.data[key]] = true;
checked = checked ? true : 'half'; parent = parent.parent;
node.setChecked(checked, !this.checkStrictly && checked === true);
} else if (none) {
node.setChecked(checked, !this.checkStrictly);
} }
} else {
node.setChecked(checked, false); if (node.isLeaf || this.checkStrictly) {
node.setChecked(true, false);
continue;
} }
node.setChecked(true, true);
if (leafOnly) { if (leafOnly) {
node.setChecked(false, false); node.setChecked(false, false);
const traverse = function(node) { const traverse = function(node) {
const childNodes = node.childNodes; const childNodes = node.childNodes;
childNodes.forEach((child) => { childNodes.forEach((child) => {
if (!child.isLeaf) { if (!child.isLeaf) {
child.setChecked(false, false); child.setChecked(false, false);
...@@ -236,13 +226,9 @@ export default class TreeStore { ...@@ -236,13 +226,9 @@ export default class TreeStore {
traverse(child); traverse(child);
}); });
}; };
traverse(node); traverse(node);
} }
} else {
node.setChecked(checked, false);
} }
});
} }
setCheckedNodes(array, leafOnly = false) { setCheckedNodes(array, leafOnly = false) {
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
v-if="showCheckbox" v-if="showCheckbox"
v-model="node.checked" v-model="node.checked"
:indeterminate="node.indeterminate" :indeterminate="node.indeterminate"
@change="handleCheckChange" :disabled="!!node.disabled"
@click.native.stop="handleUserClick"> @change="handleCheckChange">
</el-checkbox> </el-checkbox>
<span <span
v-if="node.loading" v-if="node.loading"
...@@ -155,16 +155,8 @@ ...@@ -155,16 +155,8 @@
} }
}, },
handleUserClick() {
if (this.node.indeterminate) {
this.node.setChecked(this.node.checked, !this.tree.checkStrictly);
}
},
handleCheckChange(ev) { handleCheckChange(ev) {
if (!this.node.indeterminate) {
this.node.setChecked(ev.target.checked, !this.tree.checkStrictly); this.node.setChecked(ev.target.checked, !this.tree.checkStrictly);
}
}, },
handleChildNodeExpand(nodeData, node, instance) { handleChildNodeExpand(nodeData, node, instance) {
......
...@@ -69,7 +69,8 @@ ...@@ -69,7 +69,8 @@
return { return {
children: 'children', children: 'children',
label: 'label', label: 'label',
icon: 'icon' icon: 'icon',
disabled: 'disabled'
}; };
} }
}, },
......
...@@ -158,7 +158,7 @@ if (typeof window !== 'undefined' && window.Vue) { ...@@ -158,7 +158,7 @@ if (typeof window !== 'undefined' && window.Vue) {
}; };
module.exports = { module.exports = {
version: '1.3.7', version: '1.4.0-beta.1',
locale: locale.use, locale: locale.use,
i18n: locale.i18n, i18n: locale.i18n,
install, install,
......
import Vue from 'vue';
export default function scrollIntoView(container, selected) {
if (Vue.prototype.$isServer) return;
if (!selected) {
container.scrollTop = 0;
return;
}
const top = selected.offsetTop;
const bottom = selected.offsetTop + selected.offsetHeight;
const viewRectTop = container.scrollTop;
const viewRectBottom = viewRectTop + container.clientHeight;
if (top < viewRectTop) {
container.scrollTop = top;
} else if (bottom > viewRectBottom) {
container.scrollTop = bottom - container.clientHeight;
}
}
...@@ -19,3 +19,21 @@ export function toObject(arr) { ...@@ -19,3 +19,21 @@ export function toObject(arr) {
} }
return res; return res;
}; };
export const getValueByPath = function(object, prop) {
prop = prop || '';
const paths = prop.split('.');
let current = object;
let result = null;
for (let i = 0, j = paths.length; i < j; i++) {
const path = paths[i];
if (!current) break;
if (i === j - 1) {
result = current[path];
break;
}
current = current[path];
}
return result;
};
...@@ -113,6 +113,7 @@ describe('Select', () => { ...@@ -113,6 +113,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-select> </el-select>
...@@ -147,6 +148,7 @@ describe('Select', () => { ...@@ -147,6 +148,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
<p>{{item.label}} {{item.value}}</p> <p>{{item.label}} {{item.value}}</p>
</el-option> </el-option>
...@@ -226,6 +228,7 @@ describe('Select', () => { ...@@ -226,6 +228,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-select> </el-select>
...@@ -285,6 +288,44 @@ describe('Select', () => { ...@@ -285,6 +288,44 @@ describe('Select', () => {
}, 100); }, 100);
}); });
it('object typed value', done => {
vm = createVue({
template: `
<div>
<el-select v-model="value" value-key="id">
<el-option
v-for="item in options"
:label="item.label"
:key="item.id"
:value="item">
</el-option>
</el-select>
</div>
`,
data() {
return {
options: [{
id: 1,
label: 'label1'
}, {
id: 2,
label: 'label2'
}],
value: {
id: 1,
label: 'label1'
}
};
}
}, true);
setTimeout(() => {
expect(vm.$el.querySelector('.el-input__inner').value).to.equal('label1');
expect(vm.$el.querySelector('.el-select-dropdown__item').classList.contains('selected'));
done();
}, 100);
});
it('custom el-option template', () => { it('custom el-option template', () => {
vm = createVue({ vm = createVue({
template: ` template: `
...@@ -293,6 +334,7 @@ describe('Select', () => { ...@@ -293,6 +334,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
<p>{{item.label}} {{item.value}}</p> <p>{{item.label}} {{item.value}}</p>
</el-option> </el-option>
...@@ -320,11 +362,13 @@ describe('Select', () => { ...@@ -320,11 +362,13 @@ describe('Select', () => {
<el-select v-model="value"> <el-select v-model="value">
<el-option-group <el-option-group
v-for="group in options" v-for="group in options"
:key="group.label"
:disabled="group.disabled" :disabled="group.disabled"
:label="group.label"> :label="group.label">
<el-option <el-option
v-for="item in group.options" v-for="item in group.options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-option-group> </el-option-group>
...@@ -416,6 +460,7 @@ describe('Select', () => { ...@@ -416,6 +460,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item" :label="item"
:key="item.value"
:value="item" :value="item"
/> />
</el-select> </el-select>
...@@ -504,6 +549,7 @@ describe('Select', () => { ...@@ -504,6 +549,7 @@ describe('Select', () => {
<el-option <el-option
v-for="item in options" v-for="item in options"
:label="item.label" :label="item.label"
:key="item.value"
:value="item.value"> :value="item.value">
<p>{{item.label}} {{item.value}}</p> <p>{{item.label}} {{item.value}}</p>
</el-option> </el-option>
......
...@@ -61,6 +61,61 @@ describe('Tree', () => { ...@@ -61,6 +61,61 @@ describe('Tree', () => {
}, options), true); }, options), true);
}; };
const getDisableTreeVm = (props, options) => {
return createVue(Object.assign({
template: `
<el-tree ref="tree" :data="data" ${ props }></el-tree>
`,
data() {
return {
defaultExpandedKeys: [],
defaultCheckedKeys: [],
clickedNode: null,
count: 1,
data: [{
id: 1,
label: '一级 1',
children: [{
id: 11,
label: '二级 1-1',
children: [{
id: 111,
label: '三级 1-1',
disabled: true
}]
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 21,
label: '二级 2-1'
}, {
id: 22,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 31,
label: '二级 3-1'
}, {
id: 32,
label: '二级 3-2'
}]
}],
defaultProps: {
children: 'children',
label: 'label',
disabled: 'disabled'
}
};
}
}, options), true);
};
const ALL_NODE_COUNT = 9; const ALL_NODE_COUNT = 9;
it('create', () => { it('create', () => {
...@@ -344,6 +399,16 @@ describe('Tree', () => { ...@@ -344,6 +399,16 @@ describe('Tree', () => {
}, 0); }, 0);
}); });
it('set disabled checkbox', done => {
vm = getDisableTreeVm(':props="defaultProps" show-checkbox node-key="id"');
const node = document.querySelectorAll('.el-tree-node__content')[2];
const nodeCheckbox = node.querySelector('.el-checkbox input');
vm.$nextTick(() => {
expect(nodeCheckbox.disabled).to.equal(true);
done();
});
});
it('check strictly', (done) => { it('check strictly', (done) => {
vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly'); vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
const tree = vm.$children[0]; const tree = vm.$children[0];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment