瀏覽代碼

Popper: Fixing getPosition comments;

Liril 8 年之前
父節點
當前提交
796185b56a
共有 100 個文件被更改,包括 1148 次插入581 次删除
  1. 14 0
      CHANGELOG.en-US.md
  2. 13 0
      CHANGELOG.zh-CN.md
  3. 8 0
      README.md
  4. 11 3
      build/bin/build-entry.js
  5. 5 0
      build/config.js
  6. 0 1
      examples/app.vue
  7. 2 0
      examples/docs/en-US/form.md
  8. 16 0
      examples/docs/en-US/i18n.md
  9. 46 4
      examples/docs/en-US/input.md
  10. 2 2
      examples/docs/en-US/menu.md
  11. 33 4
      examples/docs/en-US/message-box.md
  12. 10 5
      examples/docs/en-US/notification.md
  13. 1 0
      examples/docs/en-US/popover.md
  14. 1 0
      examples/docs/en-US/slider.md
  15. 1 0
      examples/docs/en-US/steps.md
  16. 6 2
      examples/docs/en-US/table.md
  17. 4 4
      examples/docs/en-US/tooltip.md
  18. 2 0
      examples/docs/zh-CN/form.md
  19. 17 0
      examples/docs/zh-CN/i18n.md
  20. 45 3
      examples/docs/zh-CN/input.md
  21. 3 2
      examples/docs/zh-CN/menu.md
  22. 33 4
      examples/docs/zh-CN/message-box.md
  23. 8 4
      examples/docs/zh-CN/notification.md
  24. 1 0
      examples/docs/zh-CN/popover.md
  25. 1 0
      examples/docs/zh-CN/slider.md
  26. 2 1
      examples/docs/zh-CN/steps.md
  27. 8 3
      examples/docs/zh-CN/table.md
  28. 2 2
      examples/docs/zh-CN/tabs.md
  29. 2 2
      examples/docs/zh-CN/tooltip.md
  30. 1 0
      examples/pages/template/changelog.tpl
  31. 1 1
      examples/versions.json
  32. 2 2
      package.json
  33. 10 3
      packages/autocomplete/src/autocomplete-suggestions.vue
  34. 47 26
      packages/autocomplete/src/autocomplete.vue
  35. 2 2
      packages/carousel/src/main.vue
  36. 11 62
      packages/collapse/src/collapse-item.vue
  37. 3 3
      packages/date-picker/src/basic/date-table.vue
  38. 5 1
      packages/date-picker/src/panel/date-range.vue
  39. 4 1
      packages/date-picker/src/panel/date.vue
  40. 1 1
      packages/date-picker/src/panel/time-select.vue
  41. 32 38
      packages/date-picker/src/picker.vue
  42. 6 0
      packages/date-picker/src/picker/time-picker.js
  43. 1 1
      packages/date-picker/src/picker/time-select.js
  44. 7 3
      packages/date-picker/src/util/index.js
  45. 6 2
      packages/form/src/form-item.vue
  46. 5 1
      packages/form/src/form.vue
  47. 28 24
      packages/input-number/src/input-number.vue
  48. 5 2
      packages/input/src/calcTextareaHeight.js
  49. 5 1
      packages/input/src/input.vue
  50. 24 20
      packages/loading/src/directive.js
  51. 10 4
      packages/loading/src/index.js
  52. 19 7
      packages/loading/src/loading.vue
  53. 11 9
      packages/menu/src/menu-item.vue
  54. 14 1
      packages/menu/src/menu-mixin.js
  55. 59 39
      packages/menu/src/menu.vue
  56. 58 10
      packages/menu/src/submenu.vue
  57. 8 1
      packages/message-box/src/main.js
  58. 39 6
      packages/message-box/src/main.vue
  59. 7 1
      packages/notification/src/main.js
  60. 2 2
      packages/notification/src/main.vue
  61. 2 1
      packages/popover/src/main.vue
  62. 3 3
      packages/select/src/option.vue
  63. 25 2
      packages/select/src/select.vue
  64. 5 0
      packages/slider/src/main.vue
  65. 1 1
      packages/steps/README.md
  66. 15 4
      packages/steps/src/step.vue
  67. 8 2
      packages/steps/src/steps.vue
  68. 10 8
      packages/table/src/table-body.js
  69. 7 2
      packages/table/src/table-column.js
  70. 56 17
      packages/table/src/table-header.js
  71. 2 1
      packages/table/src/table-layout.js
  72. 14 3
      packages/table/src/table.vue
  73. 42 0
      packages/tabs/src/tab-bar.vue
  74. 7 6
      packages/tabs/src/tab-pane.vue
  75. 45 57
      packages/tabs/src/tabs.vue
  76. 1 1
      packages/theme-default/package.json
  77. 3 3
      packages/theme-default/src/alert.css
  78. 6 6
      packages/theme-default/src/autocomplete.css
  79. 2 2
      packages/theme-default/src/badge.css
  80. 3 3
      packages/theme-default/src/breadcrumb.css
  81. 9 9
      packages/theme-default/src/button.css
  82. 1 1
      packages/theme-default/src/card.css
  83. 80 74
      packages/theme-default/src/common/var.css
  84. 1 1
      packages/theme-default/src/date-picker/month-table.css
  85. 3 3
      packages/theme-default/src/date-picker/picker-panel.css
  86. 1 1
      packages/theme-default/src/date-picker/time-picker.css
  87. 1 1
      packages/theme-default/src/date-picker/year-table.css
  88. 3 3
      packages/theme-default/src/dialog.css
  89. 6 6
      packages/theme-default/src/dropdown.css
  90. 2 2
      packages/theme-default/src/form.css
  91. 1 1
      packages/theme-default/src/input-number.css
  92. 4 3
      packages/theme-default/src/input.css
  93. 6 0
      packages/theme-default/src/loading.css
  94. 20 15
      packages/theme-default/src/menu.css
  95. 1 1
      packages/theme-default/src/message-box.css
  96. 1 1
      packages/theme-default/src/message.css
  97. 2 2
      packages/theme-default/src/mixins/_button.css
  98. 14 12
      packages/theme-default/src/notification.css
  99. 2 0
      packages/theme-default/src/pagination.css
  100. 3 3
      packages/theme-default/src/progress.css

+ 14 - 0
CHANGELOG.en-US.md

@@ -1,5 +1,19 @@
 ## Changelog
 
+### 1.1.3
+
+*2017-01-09*
+
+- Fixed DatePicker not firing change event when cleared for the first time upon page load, #2167
+- Fixed DatePicker year calculating error when choosing the next year, #2152
+- Added `default-sort-prop` and `default-sort-order` attributes for Table, #2182 (by @njleonzhang)
+- Fixed filterable Select filtering other options with initial value, #2196
+- Added custom i18n processing, making Element compatible with i18n plugins other than `vue-i18n`, #2129
+- Added `resize` attribute for Input, #2263 (by @Kingwl)
+- Fixed Autocomplete not hiding dropdown when blurred, #2247
+- Fixed style issues with nested Tabs, #2212 (by @Kingwl)
+- Fixed Tabs' tab bar locating error when non-first item is initially activated, #2192
+
 ### 1.1.2
 
 *2016-12-30*

+ 13 - 0
CHANGELOG.zh-CN.md

@@ -1,5 +1,18 @@
 ## 更新日志
 
+### 1.1.3
+*2017-01-09*
+
+- 修复 DatePicker 页面加载后首次清空不会触发 `change` 事件,#2167
+- 修复 DatePicker 选择下一年时,年份计算错误,#2152
+- 新增 Table 的 `default-sort-prop` 和 `default-sort-order` 属性,#2182(by @njleonzhang)
+- 修复有默认值的可搜索 Select 其他数据被过滤的问题,#2196
+- 新增自定义 i18n 处理方法,方便和除了 `vue-i18n` 之外的插件使用,#2129
+- 新增 Input `resize` 属性,#2263(by @Kingwl)
+- 修复 Autocomplete 在 blur 事件触发时没有隐藏下拉列表的问题,#2247
+- 修复 Tabs 嵌套使用时的样式问题,#2212(by @Kingwl)
+- 修复 Tabs 默认激活非第一项时 tabBar 的显示位置不正确的问题,#2192
+
 ### 1.1.2
 *2016-12-30*
 

+ 8 - 0
README.md

@@ -15,6 +15,14 @@
 
 > A Vue.js 2.0 UI Toolkit for Web.
 
+<p align="center">
+  <b>Special thanks to the generous sponsorship by:</b>
+  <br><br>
+  <a href="https://laravist.com">
+    <img width="300px" src="https://fuss10.elemecdn.com/4/87/c072c1651b0efd1c5cde39bc8b422png.png">
+  </a>
+</p>
+
 ## Links
 - [Home Page](http://element.eleme.io/)
 - [Docs](http://element.eleme.io/#/component)

+ 11 - 3
build/bin/build-entry.js

@@ -6,18 +6,25 @@ var path = require('path');
 
 var OUTPUT_PATH = path.join(__dirname, '../../src/index.js');
 var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}\';';
-var INSTALL_COMPONENT_TEMPLATE = '  Vue.component({{name}}.name, {{name}});';
+var INSTALL_COMPONENT_TEMPLATE = '  {{name}}';
 var MAIN_TEMPLATE = `/* Automatic generated by './build/bin/build-entry.js' */
 
 {{include}}
 import locale from 'element-ui/src/locale';
 
+const components = [
+{{install}}
+];
+
 const install = function(Vue, opts = {}) {
   /* istanbul ignore if */
   if (install.installed) return;
   locale.use(opts.locale);
+  locale.i18n(opts.i18n);
 
-{{install}}
+  components.map(component => {
+    Vue.component(component.name, component);
+  });
 
   Vue.use(Loading.directive);
 
@@ -38,6 +45,7 @@ if (typeof window !== 'undefined' && window.Vue) {
 module.exports = {
   version: '{{version}}',
   locale: locale.use,
+  i18n: locale.i18n,
   install,
   Loading,
 {{list}}
@@ -72,7 +80,7 @@ ComponentNames.forEach(name => {
 
 var template = render(MAIN_TEMPLATE, {
   include: includeComponentTemplate.join('\n'),
-  install: installTemplate.join('\n'),
+  install: installTemplate.join(',\n'),
   version: process.env.VERSION || require('../../package.json').version,
   list: listTemplate.join(',\n')
 });

+ 5 - 0
build/config.js

@@ -6,6 +6,7 @@ var saladConfig = require('../packages/theme-default/salad.config.json');
 
 var utilsList = fs.readdirSync(path.resolve(__dirname, '../src/utils'));
 var mixinsList = fs.readdirSync(path.resolve(__dirname, '../src/mixins'));
+var transitionList = fs.readdirSync(path.resolve(__dirname, '../src/transitions'));
 var externals = {};
 
 Object.keys(Components).forEach(function(key) {
@@ -21,6 +22,10 @@ mixinsList.forEach(function(file) {
   file = path.basename(file, '.js');
   externals[`element-ui/src/mixins/${file}`] = `element-ui/lib/mixins/${file}`;
 });
+transitionList.forEach(function(file) {
+  file = path.basename(file, '.js');
+  externals[`element-ui/src/transitions/${file}`] = `element-ui/lib/transitions/${file}`;
+});
 
 externals = [Object.assign({
   vue: 'vue'

+ 0 - 1
examples/app.vue

@@ -1,7 +1,6 @@
 <style lang="css">
   @import 'highlight.js/styles/color-brewer.css';
   @import 'assets/styles/common.css';
-  @import 'assets/styles/fonts/style.css';
 
   html, body {
     margin: 0;

+ 2 - 0
examples/docs/en-US/form.md

@@ -818,6 +818,7 @@ Form component allows you to verify your data, helping you find and correct erro
 | label-position | position of label | string | left/right/top | right |
 | label-width | width of label, and all form items will inherit from `Form` | string | — | — |
 | label-suffix | suffix of the label | string | — | — |
+| show-message  | whether to show the error message | boolean | — | true |
 
 ### Form Methods
 
@@ -837,3 +838,4 @@ Form component allows you to verify your data, helping you find and correct erro
 | required | whether the field is required or not, will be determined by validation rules if omitted | string |  — | false |
 | rules | validation rules of form | object | — | — |
 | error | field error message, set its value and the field will validate error and show this message immediately | string | — | — |
+| show-message  | whether to show the error message | boolean | — | true |

+ 16 - 0
examples/docs/en-US/i18n.md

@@ -56,6 +56,21 @@ Vue.locale('zh-cn', zhLocale)
 Vue.locale('en', enLocale)
 ```
 
+## Compatibility with other i18n plugins
+Element may not be compatible with i18n plugins other than vue-i18n, but you can customize how Element processes i18n.
+
+```javascript
+import Vue from 'vue'
+import Element from 'element-ui'
+import enLocale from 'element-ui/lib/locale/lang/en'
+import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
+
+Vue.use(Element, {
+  i18n: function (path, options) {
+    // ...
+  }
+})
+```
 
 Currently Element ships with the following languages:
 <ul class="language-list">
@@ -79,6 +94,7 @@ Currently Element ships with the following languages:
   <li>Farsi (fa)</li>
   <li>Thai (th)</li>
   <li>Indonesian (id)</li>
+  <li>Bulgarian (bg)</li>
 </ul>
 
 If your target language is not included, you are more than welcome to contribute: just add another language config [here](https://github.com/ElemeFE/element/tree/master/src/locale/lang) and create a pull request.

+ 46 - 4
examples/docs/en-US/input.md

@@ -28,6 +28,8 @@
         input8: '',
         input9: '',
         textarea: '',
+        textarea2: '',
+        textarea3: '',
         select: '',
         state1: '',
         state2: '',
@@ -218,14 +220,14 @@ export default {
 
 ### Textarea
 
-Resizable for entering multiple lines of text information.
+Resizable for entering multiple lines of text information. Add attribute `type="textarea"` to change `input` into native `textarea`.
 
-::: demo Add attribute `type="textarea"` to change `input` into native `textarea`.
+::: demo Control the height by setting the `rows` prop.
 
 ```html
 <el-input
   type="textarea"
-  :autosize="{ minRows: 2, maxRows: 4}"
+  :rows="2"
   placeholder="Please input"
   v-model="textarea">
 </el-input>
@@ -242,6 +244,40 @@ export default {
 ```
 :::
 
+### Autosize Textarea
+
+Setting the `autosize` prop for a textarea type of Input makes the height to automatically adjust based on the content. An options object can be provided to `autosize` to specify the minimum and maximum number of lines the textarea can automatically adjust.
+
+::: demo
+
+```html
+<el-input
+  type="textarea"
+  autosize
+  placeholder="Please input"
+  v-model="textarea2">
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  :autosize="{ minRows: 2, maxRows: 4}"
+  placeholder="Please input"
+  v-model="textarea3">
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      textarea2: '',
+      textarea3: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### Mixed input
 
 Prepend or append an element, generally a label or a button.
@@ -269,7 +305,11 @@ Prepend or append an element, generally a label or a button.
     <el-button slot="append" icon="search"></el-button>
   </el-input>
 </div>
-
+<style>
+  .el-select .el-input {
+    width: 110px;
+  }
+</style>
 <script>
 export default {
   data() {
@@ -565,6 +605,7 @@ Search data from server-side.
 |name | same as `name` in native input | string | — | — |
 |max | same as `max` in native input | * | — | — |
 |min | same as `min` in native input | * | — | — |
+|resize| control the resizability | string | none, both, horizontal, vertical | — |
 |autofocus | same as `autofocus` in native input | boolean | — | false |
 |form | same as `form` in native input | string | — | — |
 
@@ -588,6 +629,7 @@ Attribute | Description | Type | Options | Default
 |custom-item | component name of your customized suggestion list item | string | — | — |
 |fetch-suggestions | a method to fetch input suggestions. When suggestions are ready, invoke `callback(data:[])` to return them to Autocomplete | Function(queryString, callback) | — | — |
 | popper-class | custom class name for autocomplete's dropdown | string | — | — |
+| trigger-on-focus | whether show suggestions when input focus | boolean | — | true |
 
 ### Autocomplete Events
 

+ 2 - 2
examples/docs/en-US/menu.md

@@ -32,7 +32,7 @@ Top bar NavMenu can be used in a variety of scenarios.
     <el-menu-item index="2-2">item two</el-menu-item>
     <el-menu-item index="2-3">item three</el-menu-item>
   </el-submenu>
-  <el-menu-item index="3">Orders</el-menu-item>
+  <el-menu-item index="3"><a href="https://www.ele.me" target="_blank">Orders</a></el-menu-item>
 </el-menu>
 <div class="line"></div>
 <el-menu default-active="1" class="el-menu-demo" mode="horizontal" @select="handleSelect">
@@ -43,7 +43,7 @@ Top bar NavMenu can be used in a variety of scenarios.
     <el-menu-item index="2-2">item two</el-menu-item>
     <el-menu-item index="2-3">item three</el-menu-item>
   </el-submenu>
-  <el-menu-item index="3">Orders </el-menu-item>
+  <el-menu-item index="3"><a href="https://www.ele.me" target="_blank">Orders</a></el-menu-item>
 </el-menu>
 
 <script>

+ 33 - 4
examples/docs/en-US/message-box.md

@@ -64,7 +64,21 @@
           message: 'This is a message',
           showCancelButton: true,
           confirmButtonText: 'OK',
-          cancelButtonText: 'Cancel'
+          cancelButtonText: 'Cancel',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true;
+              instance.confirmButtonText = 'Loading...';
+              setTimeout(() => {
+                done();
+                setTimeout(() => {
+                  instance.confirmButtonLoading = false;
+                }, 300);
+              }, 3000);
+            } else {
+              done();
+            }
+          }
         }).then(action => {
           setTimeout(() => {
             this.$message({
@@ -194,7 +208,7 @@ Prompt is used when user input is required.
 
 Can be customized to show various content.
 
-:::demo The three methods mentioned above are repackagings of the `$msgbox` method. This example calls `$msgbox` method directly using the `showCancelButton` attribute, which is used to indicate if a cancel button is displayed. Besides we can use `cancelButtonClass` to add a custom style and `cancelButtonText` to customize the button text. The confirm button also has these fields. A complete list of fields can be found at the end of this documentation.
+:::demo The three methods mentioned above are repackagings of the `$msgbox` method. This example calls `$msgbox` method directly using the `showCancelButton` attribute, which is used to indicate if a cancel button is displayed. Besides we can use `cancelButtonClass` to add a custom style and `cancelButtonText` to customize the button text (the confirm button also has these fields, and a complete list of fields can be found at the end of this documentation). This example also uses the `beforeClose` attribute. It is a method and will be triggered when the MessageBox instance will be closed, and its execution will stop the instance from closing. It has three parameters: `action`, `instance` and `done`. Using it enables you to manipulate the instance before it closes, e.g. activating `loading` for confirm button; you can invoke the `done` method to close the MessageBox instance (if `done` is not called inside `beforeClose`, the instance will not be closed).
 
 ```html
 <template>
@@ -210,7 +224,21 @@ Can be customized to show various content.
           message: 'This is a message',
           showCancelButton: true,
           confirmButtonText: 'OK',
-          cancelButtonText: 'Cancel'
+          cancelButtonText: 'Cancel',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true;
+              instance.confirmButtonText = 'Loading...';
+              setTimeout(() => {
+                done();
+                setTimeout(() => {
+                  instance.confirmButtonLoading = false;
+                }, 300);
+              }, 3000);
+            } else {
+              done();
+            }
+          }
         }).then(action => {
           this.$message({
             type: 'info',
@@ -246,7 +274,8 @@ The corresponding methods are: `MessageBox`, `MessageBox.alert`, `MessageBox.con
 | message | content of the MessageBox | string | — | — |
 | type | message type, used for icon display | string | success/info/warning/error | — |
 | customClass | custom class name for MessageBox | string | — | — |
-| callback | MessageBox closing callback if you don't prefer Promise | function(action), where action can be 'confirm' or 'cancel' | — | — |
+| callback | MessageBox closing callback if you don't prefer Promise | function(action), where action can be 'confirm' or 'cancel', and `instance` is the MessageBox instance. You can access to that instance's attributes and methods | — | — |
+| beforeClose | callback before MessageBox closes, and it will prevent MessageBox from closing | function(action, instance, done), where `action` can be 'confirm' or 'cancel'; `instance` is the MessageBox instance, and you can access to that instance's attributes and methods; `done` is for closing the instance | — | — |
 | lockScroll | whether to lock body scroll when MessageBox prompts | boolean | — | true |
 | showCancelButton | whether to show a cancel button | boolean | — | false (true when called with confirm and prompt) |
 | showConfirmButton | whether to show a confirm button | boolean | — | true |

+ 10 - 5
examples/docs/en-US/notification.md

@@ -2,9 +2,11 @@
   module.exports = {
     methods: {
       open() {
+        const h = this.$createElement;
+
         this.$notify({
           title: 'Title',
-          message: 'This is a reminder'
+          message: h('p', { style: 'color: red' }, 'This is a reminder')
         });
       },
 
@@ -61,14 +63,14 @@
   };
 </script>
 
-## Notification 
+## Notification
 
 Displays a global notification message at the upper right corner of the page.
 
 ### Basic usage
 
 ::: demo Element has registered the `$notify` method and it receives an object as its parameter. In the simplest case, you can set the `title` field and the` message` field for the title and body of the notification. By default, the notification automatically closes after 4500ms, but by setting `duration` you can control its duration. Specifically, if set to `0`, it will not close automatically. Note that `duration` receives a `Number` in milliseconds.
-   
+
 ```html
 <template>
   <el-button
@@ -87,9 +89,11 @@ Displays a global notification message at the upper right corner of the page.
   export default {
     methods: {
       open() {
+        const h = this.$createElement;
+
         this.$notify({
           title: 'Title',
-          message: 'This is a reminder'
+          message: h('p', { style: 'color: red' }, 'This is a reminder')
         });
       },
 
@@ -201,6 +205,7 @@ Customize Notification's offset from the top edge of the screen
   }
 </script>
 ```
+:::
 
 ### Global method
 
@@ -220,7 +225,7 @@ In this case you should call `Notification(options)`. We have also registered me
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
 | title | title | string | — | — |
-| message | description text | string | — | — |
+| message | description text | string/Vue.VNode | — | — |
 | type | notification type | string | success/warning/info/error | — |
 | iconClass | custom icon's class. It will be overridden by `type` | string | — | — |
 | customClass | custom class name for Notification | string | — | — |

+ 1 - 0
examples/docs/en-US/popover.md

@@ -212,6 +212,7 @@ Of course, you can nest other operations. It's more light-weight than using a di
 |  content        |  popover content, can be replaced with a default `slot`    | string            | — | — |
 |  width        |  popover width  | string, number            | — | Min width 150px |
 |  placement        |  popover placement  | string | top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end |  bottom |
+|  disabled       |  whether Popover is disabled  | boolean    | — |  false |
 |  value(v-model)        |  whether popover is visible  | Boolean           | — |  false |
 |  offset        |  popover offset  | number           | — |  0 |
 |  transition     |  popover transition animation      | string             | — | fade-in-linear |

+ 1 - 0
examples/docs/en-US/slider.md

@@ -127,6 +127,7 @@ Set value via a input box.
 | disabled | whether Slider is disabled | boolean | — | false |
 | step | step size | number | — | 1 |
 | show-input | whether to display an input box | boolean | — | false |
+| show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true |
 | show-stops | whether to display breakpoints | boolean | — | false |
 
 ## Events

+ 1 - 0
examples/docs/en-US/steps.md

@@ -120,6 +120,7 @@ Vertical step bars.
 | process-status | status of current step | string | wait/process/finish/error/success | process |
 | finish-status | status of end step | string | wait/process/finish/error/success | finish |
 | align-center | whether step description is centered | boolean | — | false |
+| center | center whole `Steps` component | boolean | - | false |
 
 ### Step Attributes
 | Attribute      | Description          | Type      | Accepted Values       | Default  |

+ 6 - 2
examples/docs/en-US/table.md

@@ -1122,13 +1122,15 @@ You can also select multiple rows.
 
 Sort the data to find or compare data quickly.
 
-:::demo Set attribute `sortable` in a certain column to sort the data based on this column. It accepts `Boolean` with a default value `false`. In this example we use another attribute named `formatter` to format the value of certain columns. It accepts a function which has two parameters: `row` and `column`. You can handle it according to your own needs.
+:::demo Set table attribute `default-sort-prop` and `default-sort-order` to determine default sort column and order. Set attribute `sortable` in a certain column to sort the data based on this column. It accepts `Boolean` with a default value `false`. In this example we use another attribute named `formatter` to format the value of certain columns. It accepts a function which has two parameters: `row` and `column`. You can handle it according to your own needs.
 ```html
 <template>
   <el-table
     :data="tableData"
     border
-    style="width: 100%">
+    style="width: 100%"
+    default-sort-prop="date"
+    default-sort-order="descending">
     <el-table-column
       prop="date"
       label="Date"
@@ -1453,6 +1455,8 @@ When the row content is too long and you do not want to display the horizontal s
 | empty-text | Displayed text when data is empty. You can customize this area with `slot="empty"` | String | — | No Data |
 | default-expand-all | whether expand all rows by default, only works when the table has a column type="expand" | Boolean | — | false |
 | expand-row-keys | set expanded rows by this prop, prop's value is the keys of expand rows, you should set row-key before using this prop | Array | — | |
+| default-sort-prop | set the `prop` of default sort column. | String | - | - |
+| default-sort-order | set the default sort order. You should set `default-sort-prop` before using this prop. | String | ascending, descending | ascending |
 
 ### Table Events
 | Event Name | Description | Parameters |

+ 4 - 4
examples/docs/en-US/tooltip.md

@@ -113,12 +113,12 @@ Tooltip has 9 placements.
     .item {
       margin: 4px;
     }
-    
+
     .left .el-tooltip__popper,
     .right .el-tooltip__popper {
       padding: 8px 10px;
     }
-    
+
     .el-button {
       width: 110px;
     }
@@ -145,7 +145,7 @@ Tooltip has two themes: `dark` and `light`。
 
 ### More Content
 
-Display multiple lines of text and set their format. 
+Display multiple lines of text and set their format.
 
 :::demo Override attribute `content` of `el-tooltip` by adding a slot named `content`.
 ```html
@@ -170,7 +170,7 @@ In fact, Tooltip is an extension based on [Vue-popper](https://github.com/elemen
 ```html
 <template>
   <el-tooltip :disabled="disabled" content="click to close tooltip function" placement="bottom" effect="light">
-    <el-button @click="disabled=true">click to close tooltip function</el-button>
+    <el-button @click="disabled = !disabled">click to {{disabled ? 'active' : 'close'}} tooltip function</el-button>
   </el-tooltip>
 </template>
 

+ 2 - 0
examples/docs/zh-CN/form.md

@@ -803,6 +803,7 @@
 | label-position | 表单域标签的位置 | string |  right/left/top            | right |
 | label-width | 表单域标签的宽度,所有的 form-item 都会继承 form 组件的 labelWidth 的值 | string | — | — |
 | label-suffix | 表单域标签的后缀 | string | — | — |
+| show-message  | 是否显示校验错误信息 | boolean | — | true |
 
 ### Form Methods
 
@@ -822,3 +823,4 @@
 | required | 是否必填,如不设置,则会根据校验规则自动生成 | bolean | — | false |
 | rules    | 表单验证规则 | object | — | — |
 | error    | 表单域验证错误信息, 设置该值会使表单验证状态变为`error`,并显示该错误信息 | string | — | — |
+| show-message  | 是否显示校验错误信息 | boolean | — | true |

+ 17 - 0
examples/docs/zh-CN/i18n.md

@@ -68,6 +68,22 @@ Vue.locale('zh-cn', zhLocale)
 Vue.locale('en', enLocale)
 ```
 
+## 兼容其他 i18n 插件
+如果不使用 `vue-i18n`,而是用其他的 i18n 插件,Element 将无法兼容,但是可以自定义 Element 的 i18n 的处理方法。
+
+```javascript
+import Vue from 'vue'
+import Element from 'element-ui'
+import enLocale from 'element-ui/lib/locale/lang/en'
+import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
+
+Vue.use(Element, {
+  i18n: function (path, options) {
+    // ...
+  }
+})
+```
+
 目前 Element 内置了以下语言:
 <ul class="language-list">
   <li>简体中文(zh-CN)</li>
@@ -90,6 +106,7 @@ Vue.locale('en', enLocale)
   <li>波斯语(fa)</li>
   <li>泰语(th)</li>
   <li>印尼语(id)</li>
+  <li>保加利亚语(bg)</li>
 </ul>
 
 如果你需要使用其他的语言,欢迎贡献 PR:只需在 [这里](https://github.com/ElemeFE/element/tree/master/src/locale/lang) 添加一个语言配置文件即可。

+ 45 - 3
examples/docs/zh-CN/input.md

@@ -28,6 +28,8 @@
         input8: '',
         input9: '',
         textarea: '',
+        textarea2: '',
+        textarea3: '',
         select: '',
         state1: '',
         state2: '',
@@ -257,13 +259,13 @@ export default {
 
 ### 文本域
 
-可调整大小,用于输入多行文本信息
+用于输入多行文本信息,通过将 `type` 属性的值指定为 textarea。
 
-::: demo 通过将 `type` 属性的值指定为 textarea。
+::: demo 文本域高度可通过 `rows` 属性控制
 ```html
 <el-input
   type="textarea"
-  :autosize="{ minRows: 2, maxRows: 4}"
+  :rows="2"
   placeholder="请输入内容"
   v-model="textarea">
 </el-input>
@@ -280,6 +282,39 @@ export default {
 ```
 :::
 
+### 可自适应文本高度的文本域
+
+通过设置 `autosize` 属性可以使得文本域的高度能够根据文本内容自动进行调整,并且 `autosize` 还可以设定为一个对象,指定最小行数和最大行数。
+
+::: demo
+```html
+<el-input
+  type="textarea"
+  autosize
+  placeholder="请输入内容"
+  v-model="textarea2">
+</el-input>
+<div style="margin: 20px 0;"></div>
+<el-input
+  type="textarea"
+  :autosize="{ minRows: 2, maxRows: 4}"
+  placeholder="请输入内容"
+  v-model="textarea3">
+</el-input>
+
+<script>
+export default {
+  data() {
+    return {
+      textarea2: '',
+      textarea3: ''
+    }
+  }
+}
+</script>
+```
+:::
+
 ### 复合型输入框
 
 可前置或后置元素,一般为标签或按钮
@@ -306,6 +341,11 @@ export default {
     <el-button slot="append" icon="search"></el-button>
   </el-input>
 </div>
+<style>
+  .el-select .el-input {
+    width: 110px;
+  }
+</style>
 <script>
 export default {
   data() {
@@ -728,6 +768,7 @@ export default {
 | name | 原生属性 | string | — | — |
 | max | 原生属性,设置最大值 | * | — | — |
 | min | 原生属性,设置最小值 | * | — | — |
+| resize | 控制是否能被用户缩放 | string | none, both, horizontal, vertical | — |
 | autofocus | 原生属性,自动获取焦点 | boolean | true, false | false |
 | form | 原生属性 | string | — | — |
 
@@ -749,6 +790,7 @@ export default {
 | custom-item  | 通过该参数指定自定义的输入建议列表项的组件名 | string  | — | — |
 | fetch-suggestions | 返回输入建议的方法,仅当你的输入建议数据 resolve 时,通过调用 callback(data:[]) 来返回它  | Function(queryString, callback)  | — | — |
 | popper-class | Autocomplete 下拉列表的类名 | string | — | — |
+| trigger-on-focus | 是否在输入框 focus 时显示建议列表 | boolean | — | true |
 
 ### Autocomplete Events
 | 事件名称 | 说明 | 回调参数 |

+ 3 - 2
examples/docs/zh-CN/menu.md

@@ -53,6 +53,7 @@
 适用广泛的基础用法。
 
 ::: demo 导航菜单默认为垂直模式,通过 `mode` 属性可以使导航菜单变更为水平模式。另外,在菜单中通过 `submenu` 组件可以生成二级菜单。
+
 ```html
 <el-menu theme="dark" default-active="1" class="el-menu-demo" mode="horizontal" @select="handleSelect">
   <el-menu-item index="1">处理中心</el-menu-item>
@@ -62,7 +63,7 @@
     <el-menu-item index="2-2">选项2</el-menu-item>
     <el-menu-item index="2-3">选项3</el-menu-item>
   </el-submenu>
-  <el-menu-item index="3">订单管理</el-menu-item>
+  <el-menu-item index="3"><a href="https://www.ele.me" target="_blank">订单管理</a></el-menu-item>
 </el-menu>
 <div class="line"></div>
 <el-menu default-active="1" class="el-menu-demo" mode="horizontal" @select="handleSelect">
@@ -73,7 +74,7 @@
     <el-menu-item index="2-2">选项2</el-menu-item>
     <el-menu-item index="2-3">选项3</el-menu-item>
   </el-submenu>
-  <el-menu-item index="3">订单管理</el-menu-item>
+  <el-menu-item index="3"><a href="https://www.ele.me" target="_blank">订单管理</a></el-menu-item>
 </el-menu>
 
 <script>

+ 33 - 4
examples/docs/zh-CN/message-box.md

@@ -65,7 +65,21 @@
           message: '这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容',
           showCancelButton: true,
           confirmButtonText: '确定',
-          cancelButtonText: '取消'
+          cancelButtonText: '取消',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true;
+              instance.confirmButtonText = '执行中...';
+              setTimeout(() => {
+                done();
+                setTimeout(() => {
+                  instance.confirmButtonLoading = false;
+                }, 300);
+              }, 3000);
+            } else {
+              done();
+            }
+          }
         }).then(action => {
           setTimeout(() => {
             this.$message({
@@ -191,7 +205,7 @@
 
 可自定义配置不同内容。
 
-:::demo 以上三个方法都是对`$msgbox`方法的再包装。本例直接调用`$msgbox`方法,使用了`showCancelButton`字段,用于显示取消按钮。另外可使用`cancelButtonClass`为其添加自定义样式,使用`cancelButtonText`来自定义按钮文本Confirm 按钮也具有相同的字段,在文末的字段说明中有完整的字段列表。
+:::demo 以上三个方法都是对`$msgbox`方法的再包装。本例直接调用`$msgbox`方法,使用了`showCancelButton`字段,用于显示取消按钮。另外可使用`cancelButtonClass`为其添加自定义样式,使用`cancelButtonText`来自定义按钮文本Confirm 按钮也具有相同的字段,在文末的字段说明中有完整的字段列表)。此例还使用了`beforeClose`属性,它的值是一个方法,会在 MessageBox 的实例关闭前被调用,同时暂停实例的关闭。它有三个参数:`action`、实例本身和`done`方法。使用它能够在关闭前对实例进行一些操作,比如为确定按钮添加`loading`状态等;此时若需要关闭实例,可以调用`done`方法(若在`beforeClose`中没有调用`done`,则实例不会关闭)
 
 ```html
 <template>
@@ -207,7 +221,21 @@
           message: '这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容, 这是一段内容',
           showCancelButton: true,
           confirmButtonText: '确定',
-          cancelButtonText: '取消'
+          cancelButtonText: '取消',
+          beforeClose: (action, instance, done) => {
+            if (action === 'confirm') {
+              instance.confirmButtonLoading = true;
+              instance.confirmButtonText = '执行中...';
+              setTimeout(() => {
+                done();
+                setTimeout(() => {
+                  instance.confirmButtonLoading = false;
+                }, 300);
+              }, 3000);
+            } else {
+              done();
+            }
+          }
         }).then(action => {
           this.$message({
             type: 'info',
@@ -243,7 +271,8 @@ import { MessageBox } from 'element-ui';
 | message | MessageBox 消息正文内容 | string | — | — |
 | type | 消息类型,用于显示图标 | string | success/info/warning/error | — |
 | customClass | MessageBox 的自定义类名 | string | — | — |
-| callback | 若不使用 Promise,可以使用此参数指定 MessageBox 关闭后的回调 | function(action),action 的值为'confirm'或'cancel' | — | — |
+| callback | 若不使用 Promise,可以使用此参数指定 MessageBox 关闭后的回调 | function(action, instance),action 的值为'confirm'或'cancel', instance 为 MessageBox 实例,可以通过它访问实例上的属性和方法 | — | — |
+| beforeClose | MessageBox 关闭前的回调,会暂停实例的关闭 | function(action, instance, done),action 的值为'confirm'或'cancel';instance 为 MessageBox 实例,可以通过它访问实例上的属性和方法;done 用于关闭 MessageBox 实例 | — | — |
 | lockScroll | 是否在 MessageBox 出现时将 body 滚动锁定 | boolean | — | true |
 | showCancelButton | 是否显示取消按钮 | boolean | — | false(以 confirm 和 prompt 方式调用时为 true) |
 | showConfirmButton | 是否显示确定按钮 | boolean | — | true |

+ 8 - 4
examples/docs/zh-CN/notification.md

@@ -2,9 +2,11 @@
   module.exports = {
     methods: {
       open() {
+        const h = this.$createElement;
+
         this.$notify({
           title: '标题名称',
-          message: '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案'
+          message: h('p', { style: 'color: red'}, '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案')
         });
       },
 
@@ -45,7 +47,7 @@
           message: '这是一条错误的提示消息'
         });
       },
-      
+
       open7() {
         this.$notify.success({
           title: '成功',
@@ -88,9 +90,11 @@
   export default {
     methods: {
       open() {
+        const h = this.$createElement;
+
         this.$notify({
           title: '标题名称',
-          message: '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案'
+          message: h('p', { style: 'color: red'}, '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案')
         });
       },
 
@@ -222,7 +226,7 @@ import { Notification } from 'element-ui';
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
 | title | 标题 | string | — | — |
-| message | 说明文字 | string | — | — |
+| message | 说明文字 | string/Vue.VNode | — | — |
 | type | 主题样式,如果不在可选值内将被忽略 | string | success/warning/info/error | — |
 | iconClass | 自定义图标的类名。若设置了 `type`,则 `iconClass` 会被覆盖 | string | — | — |
 | customClass | 自定义类名 | string | — | — |

+ 1 - 0
examples/docs/zh-CN/popover.md

@@ -239,6 +239,7 @@ Popover 的属性与 Tooltip 很类似,它们都是基于`Vue-popper`开发的
 |  content        |  显示的内容,也可以通过 `slot` 传入 DOM   | String            | — | — |
 |  width        |  宽度  | String, Number            | — | 最小宽度 150px |
 |  placement        |  出现位置  | String | top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end |  bottom |
+|  disabled       |  Popover 是否可用  | Boolean           | — |  false |
 |  value(v-model)        |  状态是否可见  | Boolean           | — |  false |
 |  offset        |  出现位置的偏移量  | Number           | — |  0 |
 |  transition     |  定义渐变动画      | String             | — | fade-in-linear |

+ 1 - 0
examples/docs/zh-CN/slider.md

@@ -151,6 +151,7 @@
 | disabled | 是否禁用 | boolean | — | false |
 | step | 步长 | number | — | 1 |
 | show-input | 是否显示输入框 | boolean | — | false |
+| show-input-controls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true|
 | show-stops | 是否显示间断点 | boolean | — | false |
 
 ### Events

+ 2 - 1
examples/docs/zh-CN/steps.md

@@ -113,7 +113,8 @@
 | active | 设置当前激活步骤  | number | — | 0 |
 | process-status | 设置当前步骤的状态 | string | wait/process/finish/error/success | process |
 | finish-status | 设置结束步骤的状态 | string | wait/process/finish/error/success | finish |
-| align-center | 标题描述居中对齐 | boolean | false, true | false |
+| align-center | 标题描述居中对齐 | boolean | - | false |
+| center | 组件居中显示 | boolean | - | false |
 
 ### Step Attributes
 | 参数      | 说明    | 类型      | 可选值       | 默认值   |

+ 8 - 3
examples/docs/zh-CN/table.md

@@ -1139,13 +1139,16 @@
 
 对表格进行排序,可快速查找或对比数据。
 
-:::demo 在列中设置`sortable`属性即可实现以该列为基准的排序,接受一个`Boolean`,默认为`false`。在本例中,我们还使用了`formatter`属性,它用于格式化指定列的值,接受一个`Function`,会传入两个参数:`row`和`column`,可以根据自己的需求进行处理。
+:::demo 可以通过表的`default-sort-prop`和`default-sort-order`属性设置默认的排序列和排序顺序。在列中设置`sortable`属性即可实现以该列为基准的排序,接受一个`Boolean`,默认为`false`。在本例中,我们还使用了`formatter`属性,它用于格式化指定列的值,接受一个`Function`,会传入两个参数:`row`和`column`,可以根据自己的需求进行处理。
 ```html
 <template>
   <el-table
     :data="tableData"
     border
-    style="width: 100%">
+    style="width: 100%"
+    default-sort-prop="date"
+    default-sort-order="descending"
+    >
     <el-table-column
       prop="date"
       label="日期"
@@ -1475,6 +1478,8 @@
 | empty-text | 空数据时显示的文本内容,也可以通过 `slot="empty"` 设置 | String | — | 暂无数据 |
 | default-expand-all | 是否默认展开所有行,当 Table 中存在 type="expand" 的 Column 的时候有效 | Boolean | — | false |
 | expand-row-keys | 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。| Array | — | |
+| default-sort-prop | 默认的排序列的prop。| String | - | - |
+| default-sort-order | 设置默认的排序顺序。需要设置`default-sort-prop`才能使用。 | String | ascending, descending | ascending |
 
 
 ### Table Events
@@ -1525,4 +1530,4 @@
 | filters | 数据过滤的选项,数组格式,数组中的元素需要有 text 和 value 属性。 | Array[{ text, value }] | — | — |
 | filter-multiple | 数据过滤的选项是否多选 | Boolean | — | true |
 | filter-method | 数据过滤使用的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。 | Function(value, row) | — | — |
-| filtered-value | 选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性。 | Array | — | — |
+| filtered-value | 选中的数据过滤项,如果需要自定义表头过滤的渲染方式,可能会需要此属性。 | Array | — | — |

+ 2 - 2
examples/docs/zh-CN/tabs.md

@@ -2,7 +2,7 @@
   export default {
     data() {
       return {
-        activeName: 'first',
+        activeName: 'second',
         activeName2: 'first',
         tabs: [{
           title: 'Tab 1',
@@ -50,7 +50,7 @@
   export default {
     data() {
       return {
-        activeName: 'first'
+        activeName: 'second'
       };
     },
     methods: {

+ 2 - 2
examples/docs/zh-CN/tooltip.md

@@ -134,7 +134,7 @@
     .item {
       margin: 4px;
     }
-    
+
     .left .el-tooltip__popper,
     .right .el-tooltip__popper {
       padding: 8px 10px;
@@ -189,7 +189,7 @@ Tooltip 组件提供了两个不同的主题:`dark`和`light`。
 ```html
 <template>
   <el-tooltip :disabled="disabled" content="点击关闭 tooltip 功能" placement="bottom" effect="light">
-    <el-button @click="disabled = true">点击关闭 tooltip 功能</el-button>
+    <el-button @click="disabled = !disabled">点击{{disabled ? '开启' : '关闭'}} tooltip 功能</el-button>
   </el-tooltip>
 </template>
 ```

+ 1 - 0
examples/pages/template/changelog.tpl

@@ -159,6 +159,7 @@
         }
       }
       fragments = fragments.replace(/#(\d+)/g, '<a href="https://github.com/ElemeFE/element/issues/$1" target="_blank">#$1</a>');
+      fragments = fragments.replace(/@(\w+)/g, '<a href="https://github.com/$1" target="_blank">@$1</a>');
       this.$refs.timeline.innerHTML = `${fragments}</li>`;
 
       changeLog.$el.remove();

+ 1 - 1
examples/versions.json

@@ -1,4 +1,4 @@
 {
   "1.0.9": "1.0",
-  "1.1.2": "1.1"
+  "1.1.3": "1.1"
 }

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "element-ui",
-  "version": "1.1.2",
+  "version": "1.1.3",
   "description": "A Component Library for Vue.js.",
   "main": "lib/element-ui.common.js",
   "files": [
@@ -103,7 +103,7 @@
     "phantomjs-prebuilt": "^2.1.13",
     "postcss": "^5.1.2",
     "postcss-loader": "^0.11.1",
-    "postcss-salad": "^1.0.5",
+    "postcss-salad": "^1.0.8",
     "rimraf": "^2.5.4",
     "sinon": "^1.17.6",
     "sinon-chai": "^2.8.0",

+ 10 - 3
packages/autocomplete/src/autocomplete-suggestions.vue

@@ -11,14 +11,14 @@
         <li
           v-if="!parent.customItem"
           :class="{'highlighted': parent.highlightedIndex === index}"
-          @click="parent.select(index)"
+          @click="select(item)"
         >
           {{item.value}}
         </li>
         <component
           v-else
           :class="{'highlighted': parent.highlightedIndex === index}"
-          @click="parent.select(index)"
+          @click="select(item)"
           :is="parent.customItem"
           :item="item"
           :index="index">
@@ -29,8 +29,9 @@
 </template>
 <script>
   import Popper from 'element-ui/src/utils/vue-popper';
+  import Emitter from 'element-ui/src/mixins/emitter';
   export default {
-    mixins: [Popper],
+    mixins: [Popper, Emitter],
 
     componentName: 'ElAutocompleteSuggestions',
 
@@ -53,6 +54,12 @@
       }
     },
 
+    methods: {
+      select(item) {
+        this.dispatch('ElAutocomplete', 'item-click', item);
+      }
+    },
+
     mounted() {
       this.popperElm = this.$el;
       this.referenceElm = this.$parent.$refs.input.$refs.input;

+ 47 - 26
packages/autocomplete/src/autocomplete.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="el-autocomplete" v-clickoutside="handleBlur">
+  <div class="el-autocomplete" v-clickoutside="handleClickoutside">
     <el-input
       ref="input"
       :value="value"
@@ -9,9 +9,10 @@
       :size="size"
       @change="handleChange"
       @focus="handleFocus"
+      @blur="handleBlur"
       @keydown.up.native="highlight(highlightedIndex - 1)"
       @keydown.down.native="highlight(highlightedIndex + 1)"
-      @keydown.enter.stop.native="select(highlightedIndex)"
+      @keydown.enter.stop.native="handleKeyEnter"
     >
       <template slot="prepend" v-if="$slots.prepend">
         <slot name="prepend"></slot>
@@ -39,6 +40,8 @@
 
     mixins: [Emitter],
 
+    componentName: 'ElAutocomplete',
+
     components: {
       ElInput,
       ElAutocompleteSuggestions
@@ -53,6 +56,7 @@
       name: String,
       size: String,
       value: String,
+      autofocus: Boolean,
       fetchSuggestions: Function,
       triggerOnFocus: {
         type: Boolean,
@@ -62,53 +66,65 @@
     },
     data() {
       return {
+        isFocus: false,
         suggestions: [],
-        suggestionVisible: false,
         loading: false,
         highlightedIndex: -1
       };
     },
+    computed: {
+      suggestionVisible() {
+        const suggestions = this.suggestions;
+        let isValidData = Array.isArray(suggestions) && suggestions.length > 0;
+        return (isValidData || this.loading) && this.isFocus;
+      }
+    },
     watch: {
       suggestionVisible(val) {
         this.broadcast('ElAutocompleteSuggestions', 'visible', [val, this.$refs.input.$refs.input.offsetWidth]);
       }
     },
     methods: {
+      getData(queryString) {
+        this.loading = true;
+        this.fetchSuggestions(queryString, (suggestions) => {
+          this.loading = false;
+          if (Array.isArray(suggestions)) {
+            this.suggestions = suggestions;
+          } else {
+            console.error('autocomplete suggestions must be an array');
+          }
+        });
+      },
       handleChange(value) {
         this.$emit('input', value);
-        this.showSuggestions(value);
+        this.getData(value);
       },
       handleFocus() {
+        this.isFocus = true;
         if (this.triggerOnFocus) {
-          this.showSuggestions(this.value);
+          this.getData(this.value);
         }
       },
       handleBlur() {
-        this.hideSuggestions();
+        // 因为 blur 事件处理优先于 select 事件执行
+        setTimeout(_ => {
+          this.isFocus = false;
+        }, 100);
       },
-      select(index) {
-        if (this.suggestions && this.suggestions[index]) {
-          this.$emit('input', this.suggestions[index].value);
-          this.$emit('select', this.suggestions[index]);
-          this.$nextTick(() => {
-            this.hideSuggestions();
-          });
+      handleKeyEnter() {
+        if (this.suggestionVisible) {
+          this.select(this.suggestions[this.highlightedIndex]);
         }
       },
-      hideSuggestions() {
-        this.suggestionVisible = false;
-        this.loading = false;
+      handleClickoutside() {
+        this.isFocus = false;
       },
-      showSuggestions(value) {
-        this.suggestionVisible = true;
-        this.loading = true;
-        this.fetchSuggestions(value, (suggestions) => {
-          this.loading = false;
-          if (Array.isArray(suggestions) && suggestions.length > 0) {
-            this.suggestions = suggestions;
-          } else {
-            this.hideSuggestions();
-          }
+      select(item) {
+        this.$emit('input', item.value);
+        this.$emit('select', item);
+        this.$nextTick(_ => {
+          this.suggestions = [];
         });
       },
       highlight(index) {
@@ -134,6 +150,11 @@
         this.highlightedIndex = index;
       }
     },
+    mounted() {
+      this.$on('item-click', item => {
+        this.select(item);
+      });
+    },
     beforeDestroy() {
       this.$refs.suggestions.$destroy();
     }

+ 2 - 2
packages/carousel/src/main.vue

@@ -65,7 +65,7 @@ export default {
       type: String,
       default: 'hover'
     },
-    autoPlay: {
+    autoplay: {
       type: Boolean,
       default: true
     },
@@ -168,7 +168,7 @@ export default {
     },
 
     startTimer() {
-      if (this.interval <= 0 || !this.autoPlay) return;
+      if (this.interval <= 0 || !this.autoplay) return;
       this.timer = setInterval(this.playSlides, this.interval);
     },
 

+ 11 - 62
packages/collapse/src/collapse-item.vue

@@ -4,32 +4,18 @@
       <i class="el-collapse-item__header__arrow el-icon-arrow-right"></i>
       <slot name="title">{{title}}</slot>
     </div>
-    <div class="el-collapse-item__wrap" ref="contentWrap">
-      <div class="el-collapse-item__content" ref="content">
-        <slot></slot>
+    <collapse-transition>
+      <div class="el-collapse-item__wrap" v-show="isActive">
+        <div class="el-collapse-item__content">
+          <slot></slot>
+        </div>
       </div>
-    </div>
+    </collapse-transition>
   </div>
 </template>
 <script>
-  import { once } from 'element-ui/src/utils/dom';
   import Emitter from 'element-ui/src/mixins/emitter';
-
-  function getTransitionendEvent(el) {
-    let t;
-    let transitions = {
-      'transition': 'transitionend',
-      'OTransition': 'oTransitionEnd',
-      'MozTransition': 'transitionend',
-      'WebkitTransition': 'webkitTransitionEnd'
-    };
-
-    for (t in transitions) {
-      if (el.style[t] !== undefined) {
-        return transitions[t];
-      }
-    }
-  };
+  import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
 
   export default {
     name: 'ElCollapseItem',
@@ -38,6 +24,10 @@
 
     mixins: [Emitter],
 
+    components: {
+      CollapseTransition
+    },
+
     data() {
       return {
         contentWrapStyle: {
@@ -66,57 +56,16 @@
 
     watch: {
       'isActive'(value) {
-        value ? this.open() : this.close();
       }
     },
 
     methods: {
-      open() {
-        const contentWrapElm = this.$refs.contentWrap;
-        const contentHeight = this.contentHeight;
-
-        contentWrapElm.style.display = 'block';
-        contentWrapElm.style.height = '0';
-
-        setTimeout(_ => {
-          contentWrapElm.style.height = contentHeight + 'px';
-          once(contentWrapElm, getTransitionendEvent(contentWrapElm), () => {
-            if (!this.isActive) return;
-            contentWrapElm.style.height = 'auto';
-          });
-        }, 10);
-      },
-      close() {
-        const contentWrapElm = this.$refs.contentWrap;
-        const contentElm = this.$refs.content;
-        const contentHeight = contentElm.offsetHeight;
-
-        this.contentHeight = contentHeight;
-        contentWrapElm.style.height = contentHeight + 'px';
-
-        setTimeout(_ => {
-          contentWrapElm.style.height = '0';
-          once(contentWrapElm, getTransitionendEvent(contentWrapElm), () => {
-            if (this.isActive) return;
-            contentWrapElm.style.display = 'none';
-          });
-        }, 10);
-      },
-      init() {
-        if (!this.isActive) {
-          let contentWrapElm = this.$refs.contentWrap;
-          this.contentHeight = this.$refs.content.offsetHeight;
-          contentWrapElm.style.height = '0';
-          contentWrapElm.style.display = 'none';
-        }
-      },
       handleHeaderClick() {
         this.dispatch('ElCollapse', 'item-click', this);
       }
     },
 
     mounted() {
-      this.init();
     }
   };
 </script>

+ 3 - 3
packages/date-picker/src/basic/date-table.vue

@@ -367,8 +367,8 @@
           target = target.parentNode.cells[1];
         }
 
-        let year = this.year;
-        let month = this.month;
+        let year = Number(this.year);
+        let month = Number(this.month);
 
         const cellIndex = target.cellIndex;
         const rowIndex = target.parentNode.rowIndex;
@@ -377,7 +377,7 @@
         const text = cell.text;
         const className = target.className;
 
-        const newDate = new Date(this.year, this.month, 1);
+        const newDate = new Date(year, month, 1);
 
         if (className.indexOf('prev') !== -1) {
           if (month === 0) {

+ 5 - 1
packages/date-picker/src/panel/date-range.vue

@@ -100,6 +100,7 @@
               :range-state="rangeState"
               :disabled-date="disabledDate"
               @changerange="handleChangeRange"
+              :first-day-of-week="firstDayOfWeek"
               @pick="handleRangePick">
             </date-table>
           </div>
@@ -125,7 +126,9 @@
               :range-state="rangeState"
               :disabled-date="disabledDate"
               @changerange="handleChangeRange"
-              @pick="handleRangePick"></date-table>
+              :first-day-of-week="firstDayOfWeek"
+              @pick="handleRangePick">
+            </date-table>
           </div>
         </div>
       </div>
@@ -232,6 +235,7 @@
         value: '',
         visible: '',
         disabledDate: '',
+        firstDayOfWeek: 7,
         minTimePickerVisible: false,
         maxTimePickerVisible: false,
         width: 0

+ 4 - 1
packages/date-picker/src/panel/date.vue

@@ -436,7 +436,10 @@
         const yearTranslation = this.t('el.datepicker.year');
         if (this.currentView === 'year') {
           const startYear = Math.floor(year / 10) * 10;
-          return startYear + ' ' + yearTranslation + '-' + (startYear + 9) + ' ' + yearTranslation;
+          if (yearTranslation) {
+            return startYear + ' ' + yearTranslation + ' - ' + (startYear + 9) + ' ' + yearTranslation;
+          }
+          return startYear + ' - ' + (startYear + 9);
         }
         return this.year + ' ' + yearTranslation;
       }

+ 1 - 1
packages/date-picker/src/panel/time-select.vue

@@ -124,7 +124,7 @@
             result.push({
               value: current,
               disabled: compareTime(current, this.minTime || '-1:-1') <= 0 ||
-                compareTime(current, this.maxTime || '100:100') > 0
+                compareTime(current, this.maxTime || '100:100') >= 0
             });
             current = nextTime(current, step);
           }

+ 32 - 38
packages/date-picker/src/picker.vue

@@ -10,8 +10,8 @@
     @focus="handleFocus"
     @blur="handleBlur"
     @keydown.native="handleKeydown"
-    :value="visualValue"
-    @change.native="visualValue = $event.target.value"
+    :value="displayValue"
+    @change.native="displayValue = $event.target.value"
     ref="reference">
     <i slot="icon"
       class="el-input__icon"
@@ -27,7 +27,7 @@
 <script>
 import Vue from 'vue';
 import Clickoutside from 'element-ui/src/utils/clickoutside';
-import { formatDate, parseDate, getWeekNumber, equalDate } from './util';
+import { formatDate, parseDate, getWeekNumber, equalDate, isDate } from './util';
 import Popper from 'element-ui/src/utils/vue-popper';
 import Emitter from 'element-ui/src/mixins/emitter';
 import ElInput from 'element-ui/packages/input';
@@ -213,7 +213,7 @@ export default {
     return {
       pickerVisible: false,
       showClose: false,
-      internalValue: ''
+      currentValue: ''
     };
   },
 
@@ -222,16 +222,22 @@ export default {
       if (this.readonly || this.disabled) return;
       val ? this.showPicker() : this.hidePicker();
     },
-    internalValue(val) {
-      if (!val && this.picker && typeof this.picker.handleClear === 'function') {
+    currentValue(val) {
+      if (val) return;
+      if (this.picker && typeof this.picker.handleClear === 'function') {
         this.picker.handleClear();
+      } else {
+        this.$emit('input');
       }
     },
     value: {
       immediate: true,
       handler(val) {
-        this.internalValue = val;
+        this.currentValue = isDate(val) ? new Date(val) : val;
       }
+    },
+    displayValue(val) {
+      this.$emit('change', val);
     }
   },
 
@@ -246,7 +252,7 @@ export default {
     },
 
     valueIsEmpty() {
-      const val = this.internalValue;
+      const val = this.currentValue;
       if (Array.isArray(val)) {
         for (let i = 0, len = val.length; i < len; i++) {
           if (val[i]) {
@@ -284,9 +290,9 @@ export default {
       return HAVE_TRIGGER_TYPES.indexOf(this.type) !== -1;
     },
 
-    visualValue: {
+    displayValue: {
       get() {
-        const value = this.internalValue;
+        const value = this.currentValue;
         if (!value) return;
         const formatter = (
           TYPE_VALUE_RESOLVER_MAP[this.type] ||
@@ -318,9 +324,6 @@ export default {
   },
 
   created() {
-    this.cachePicker = {};
-    this.cacheChange = {};
-
     // vue-popper
     this.options = {
       boundariesPadding: 0,
@@ -340,26 +343,25 @@ export default {
     handleClickIcon() {
       if (this.readonly || this.disabled) return;
       if (this.showClose) {
-        this.internalValue = '';
+        this.currentValue = '';
+        this.showClose = false;
       } else {
         this.pickerVisible = !this.pickerVisible;
       }
     },
 
-    dateIsUpdated(date, cache) {
-      let updated = true;
-
-      if (Array.isArray(date)) {
-        if (equalDate(cache.cacheDateMin, date[0]) &&
-          equalDate(cache.cacheDateMax, date[1])) updated = false;
-        cache.cacheDateMin = new Date(date[0]);
-        cache.cacheDateMax = new Date(date[1]);
+    dateChanged(dateA, dateB) {
+      if (Array.isArray(dateA)) {
+        let len = dateA.length;
+        if (!dateB) return true;
+        while (len--) {
+          if (!equalDate(dateA[len], dateB[len])) return true;
+        }
       } else {
-        if (equalDate(cache.cacheDate, date)) updated = false;
-        cache.cacheDate = new Date(date);
+        if (!equalDate(dateA, dateB)) return true;
       }
 
-      return updated;
+      return false;
     },
 
     handleClose() {
@@ -400,7 +402,7 @@ export default {
     showPicker() {
       if (this.$isServer) return;
       if (!this.picker) {
-        this.panel.defaultValue = this.internalValue;
+        this.panel.defaultValue = this.currentValue;
         this.picker = new Vue(this.panel).$mount(document.createElement('div'));
         this.picker.popperClass = this.popperClass;
         this.popperElm = this.picker.$el;
@@ -423,12 +425,6 @@ export default {
             this.picker.selectableRange = ranges.map(range => parser(range, format));
           }
 
-          if (this.type === 'time-select' && options) {
-            this.$watch('pickerOptions.minTime', val => {
-              this.picker.minTime = val;
-            });
-          }
-
           for (const option in options) {
             if (options.hasOwnProperty(option) &&
                 // 忽略 time-picker 的该配置项
@@ -446,9 +442,7 @@ export default {
 
         this.picker.$on('dodestroy', this.doDestroy);
         this.picker.$on('pick', (date, visible = false) => {
-          if (this.dateIsUpdated(date, this.cachePicker)) this.$emit('input', date);
-
-          this.$nextTick(() => this.dateIsUpdated(date, this.cacheChange) && this.$emit('change', this.visualValue));
+          if (this.dateChanged(date, this.value)) this.$emit('input', date);
           this.pickerVisible = this.picker.visible = visible;
           this.picker.resetView && this.picker.resetView();
         });
@@ -463,10 +457,10 @@ export default {
 
       this.updatePopper();
 
-      if (this.internalValue instanceof Date) {
-        this.picker.date = new Date(this.internalValue.getTime());
+      if (this.currentValue instanceof Date) {
+        this.picker.date = new Date(this.currentValue.getTime());
       } else {
-        this.picker.value = this.internalValue;
+        this.picker.value = this.currentValue;
       }
       this.picker.resetView && this.picker.resetView();
 

+ 6 - 0
packages/date-picker/src/picker/time-picker.js

@@ -11,6 +11,12 @@ export default {
     isRange: Boolean
   },
 
+  data() {
+    return {
+      type: ''
+    };
+  },
+
   created() {
     this.type = this.isRange ? 'timerange' : 'time';
     this.panel = this.isRange ? TimeRangePanel : TimePanel;

+ 1 - 1
packages/date-picker/src/picker/time-select.js

@@ -6,7 +6,7 @@ export default {
 
   name: 'ElTimeSelect',
 
-  created() {
+  beforeCreate() {
     this.type = 'time-select';
     this.panel = Panel;
   }

+ 7 - 3
packages/date-picker/src/util/index.js

@@ -13,9 +13,13 @@ export const equalDate = function(dateA, dateB) {
 };
 
 export const toDate = function(date) {
-  date = new Date(date);
-  if (isNaN(date.getTime())) return null;
-  return date;
+  return isDate(date) ? date : null;
+};
+
+export const isDate = function(date) {
+  if (date === null || date === undefined) return false;
+  if (isNaN(new Date(date).getTime())) return false;
+  return true;
 };
 
 export const formatDate = function(date, format) {

+ 6 - 2
packages/form/src/form-item.vue

@@ -10,7 +10,7 @@
     <div class="el-form-item__content" v-bind:style="contentStyle">
       <slot></slot>
       <transition name="el-zoom-in-top">
-        <div class="el-form-item__error" v-if="validateState === 'error'">{{validateMessage}}</div>
+        <div class="el-form-item__error" v-if="validateState === 'error' && showMessage && form.showMessage">{{validateMessage}}</div>
       </transition>
     </div>
   </div>
@@ -58,7 +58,11 @@
       required: Boolean,
       rules: [Object, Array],
       error: String,
-      validateStatus: String
+      validateStatus: String,
+      showMessage: {
+        type: Boolean,
+        default: true
+      }
     },
     watch: {
       error(value) {

+ 5 - 1
packages/form/src/form.vue

@@ -21,7 +21,11 @@
         type: String,
         default: ''
       },
-      inline: Boolean
+      inline: Boolean,
+      showMessage: {
+        type: Boolean,
+        default: true
+      }
     },
     watch: {
       rules() {

+ 28 - 24
packages/input-number/src/input-number.vue

@@ -21,10 +21,11 @@
     >
     </span>
     <el-input
-      v-model.number="currentValue"
+      :value="currentValue"
       @keydown.up.native="increase"
       @keydown.down.native="decrease"
       @blur="handleBlur"
+      @input="handleInput"
       :disabled="disabled"
       :size="size"
       :max="max"
@@ -95,32 +96,20 @@
       }
     },
     data() {
-      // correct the init value
-      let value = this.value;
-      if (value < this.min) {
-        this.$emit('input', this.min);
-        value = this.min;
-      }
-      if (value > this.max) {
-        this.$emit('input', this.max);
-        value = this.max;
-      }
-
       return {
-        currentValue: value
+        currentValue: 0
       };
     },
     watch: {
-      value(val) {
-        this.currentValue = val;
-      },
-
-      currentValue(newVal, oldVal) {
-        if (newVal <= this.max && newVal >= this.min) {
-          this.$emit('change', newVal, oldVal);
+      value: {
+        immediate: true,
+        handler(value) {
+          let newVal = Number(value);
+          if (isNaN(newVal)) return;
+          if (newVal >= this.max) newVal = this.max;
+          if (newVal <= this.min) newVal = this.min;
+          this.currentValue = newVal;
           this.$emit('input', newVal);
-        } else {
-          this.currentValue = oldVal;
         }
       }
     },
@@ -169,17 +158,32 @@
         const value = this.value || 0;
         const newVal = this._increase(value, this.step);
         if (newVal > this.max) return;
-        this.currentValue = newVal;
+        this.setCurrentValue(newVal);
       },
       decrease() {
         if (this.disabled || this.minDisabled) return;
         const value = this.value || 0;
         const newVal = this._decrease(value, this.step);
         if (newVal < this.min) return;
-        this.currentValue = newVal;
+        this.setCurrentValue(newVal);
       },
       handleBlur() {
         this.$refs.input.setCurrentValue(this.currentValue);
+      },
+      setCurrentValue(newVal) {
+        const oldVal = this.currentValue;
+        if (newVal >= this.max) newVal = this.max;
+        if (newVal <= this.min) newVal = this.min;
+        if (oldVal === newVal) return;
+        this.$emit('change', newVal, oldVal);
+        this.$emit('input', newVal);
+        this.currentValue = newVal;
+      },
+      handleInput(value) {
+        const newVal = Number(value);
+        if (!isNaN(newVal)) {
+          this.setCurrentValue(newVal);
+        }
       }
     }
   };

+ 5 - 2
packages/input/src/calcTextareaHeight.js

@@ -1,3 +1,5 @@
+import merge from 'element-ui/src/utils/merge';
+
 let hiddenTextarea;
 
 const HIDDEN_STYLE = `
@@ -53,7 +55,8 @@ function calculateNodeStyling(node) {
 export default function calcTextareaHeight(
   targetNode,
   minRows = null,
-  maxRows = null
+  maxRows = null,
+  options = null
 ) {
   if (!hiddenTextarea) {
     hiddenTextarea = document.createElement('textarea');
@@ -96,5 +99,5 @@ export default function calcTextareaHeight(
     height = Math.min(maxHeight, height);
   }
 
-  return { height: height + 'px'};
+  return merge({ height: height + 'px'}, options);
 };

+ 5 - 1
packages/input/src/input.vue

@@ -88,6 +88,7 @@
       value: [String, Number],
       placeholder: String,
       size: String,
+      resize: String,
       readonly: Boolean,
       autofocus: Boolean,
       icon: String,
@@ -149,7 +150,10 @@
         const minRows = autosize.minRows;
         const maxRows = autosize.maxRows;
 
-        this.textareaStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
+        const options = {
+          resize: this.resize
+        };
+        this.textareaStyle = calcTextareaHeight(this.$refs.textarea, minRows, maxRows, options);
       },
       handleFocus(event) {
         this.$emit('focus', event);

+ 24 - 20
packages/loading/src/directive.js

@@ -36,37 +36,40 @@ exports.install = Vue => {
       });
     } else {
       if (el.domVisible) {
-        el.mask.style.display = 'none';
-        el.domVisible = false;
-
-        if (binding.modifiers.fullscreen && el.originalOverflow !== 'hidden') {
-          document.body.style.overflow = el.originalOverflow;
-        }
-        if (binding.modifiers.fullscreen || binding.modifiers.body) {
-          document.body.style.position = el.originalPosition;
-        } else {
-          el.style.position = el.originalPosition;
-        }
+        el.instance.$on('after-leave', _ => {
+          el.domVisible = false;
+          if (binding.modifiers.fullscreen && el.originalOverflow !== 'hidden') {
+            document.body.style.overflow = el.originalOverflow;
+          }
+          if (binding.modifiers.fullscreen || binding.modifiers.body) {
+            document.body.style.position = el.originalPosition;
+          } else {
+            el.style.position = el.originalPosition;
+          }
+        });
+        el.instance.visible = false;
       }
     }
   };
-  let insertDom = (parent, directive, binding) => {
-    if (!directive.domVisible) {
-      Object.keys(directive.maskStyle).forEach(property => {
-        directive.mask.style[property] = directive.maskStyle[property];
+  let insertDom = (parent, el, binding) => {
+    if (!el.domVisible) {
+      Object.keys(el.maskStyle).forEach(property => {
+        el.mask.style[property] = el.maskStyle[property];
       });
 
-      if (directive.originalPosition !== 'absolute') {
+      if (el.originalPosition !== 'absolute') {
         parent.style.position = 'relative';
       }
       if (binding.modifiers.fullscreen && binding.modifiers.lock) {
         parent.style.overflow = 'hidden';
       }
-      directive.mask.style.display = 'block';
-      directive.domVisible = true;
+      el.domVisible = true;
 
-      parent.appendChild(directive.mask);
-      directive.domInserted = true;
+      parent.appendChild(el.mask);
+      Vue.nextTick(() => {
+        el.instance.visible = true;
+      });
+      el.domInserted = true;
     }
   };
 
@@ -79,6 +82,7 @@ exports.install = Vue => {
           fullscreen: !!binding.modifiers.fullscreen
         }
       });
+      el.instance = mask;
       el.mask = mask.$el;
       el.maskStyle = {};
 

+ 10 - 4
packages/loading/src/index.js

@@ -29,10 +29,13 @@ LoadingConstructor.prototype.close = function() {
   if (this.fullscreen) {
     fullscreenLoading = undefined;
   }
-  this.$el &&
-  this.$el.parentNode &&
-  this.$el.parentNode.removeChild(this.$el);
-  this.$destroy();
+  this.$on('after-leave', _ => {
+    this.$el &&
+    this.$el.parentNode &&
+    this.$el.parentNode.removeChild(this.$el);
+    this.$destroy();
+  });
+  this.visible = false;
 };
 
 const addStyle = (options, parent, instance) => {
@@ -90,6 +93,9 @@ const Loading = (options = {}) => {
     parent.style.overflow = 'hidden';
   }
   parent.appendChild(instance.$el);
+  Vue.nextTick(() => {
+    instance.visible = true;
+  });
   if (options.fullscreen) {
     fullscreenLoading = instance;
   }

+ 19 - 7
packages/loading/src/loading.vue

@@ -1,12 +1,17 @@
 <template>
-  <div class="el-loading-mask" :class="[customClass, { 'is-fullscreen': fullscreen }]">
-    <div class="el-loading-spinner">
-      <svg class="circular" viewBox="25 25 50 50">
-        <circle class="path" cx="50" cy="50" r="20" fill="none"/>
-      </svg>
-      <p v-if="text" class="el-loading-text">{{ text }}</p>
+  <transition name="el-loading-fade" @after-leave="handleAfterLeave">
+    <div
+      v-show="visible"
+      class="el-loading-mask"
+      :class="[customClass, { 'is-fullscreen': fullscreen }]">
+      <div class="el-loading-spinner">
+        <svg class="circular" viewBox="25 25 50 50">
+          <circle class="path" cx="50" cy="50" r="20" fill="none"/>
+        </svg>
+        <p v-if="text" class="el-loading-text">{{ text }}</p>
+      </div>
     </div>
-  </div>
+  </transition>
 </template>
 
 <script>
@@ -15,8 +20,15 @@
       return {
         text: null,
         fullscreen: true,
+        visible: false,
         customClass: ''
       };
+    },
+
+    methods: {
+      handleAfterLeave() {
+        this.$emit('after-leave');
+      }
     }
   };
 </script>

+ 11 - 9
packages/menu/src/menu-item.vue

@@ -11,12 +11,14 @@
 </template>
 <script>
   import Menu from './menu-mixin';
+  import Emitter from 'element-ui/src/mixins/emitter';
+
   module.exports = {
     name: 'ElMenuItem',
 
     componentName: 'ElMenuItem',
 
-    mixins: [Menu],
+    mixins: [Menu, Emitter],
 
     props: {
       index: {
@@ -34,21 +36,21 @@
     },
     computed: {
       active() {
-        return this.index === this.rootMenu.activeIndex;
+        return this.index === this.rootMenu.activedIndex;
       }
     },
     methods: {
       handleClick() {
-        this.rootMenu.handleSelect(
-          this.index,
-          this.indexPath,
-          this.route || this.index,
-          this
-        );
+        this.dispatch('ElMenu', 'item-click', this);
       }
     },
     created() {
-      this.rootMenu.menuItems[this.index] = this;
+      this.parentMenu.addItem(this);
+      this.rootMenu.addItem(this);
+    },
+    beforeDestroy() {
+      this.parentMenu.removeItem(this);
+      this.rootMenu.removeItem(this);
     }
   };
 </script>

+ 14 - 1
packages/menu/src/menu-mixin.js

@@ -13,7 +13,20 @@ module.exports = {
     },
     rootMenu() {
       var parent = this.$parent;
-      while (parent.$options.componentName !== 'ElMenu') {
+      while (
+        parent &&
+        parent.$options.componentName !== 'ElMenu'
+      ) {
+        parent = parent.$parent;
+      }
+      return parent;
+    },
+    parentMenu() {
+      let parent = this.$parent;
+      while (
+        parent &&
+        ['ElMenu', 'ElSubmenu'].indexOf(parent.$options.componentName) === -1
+      ) {
         parent = parent.$parent;
       }
       return parent;

+ 59 - 39
packages/menu/src/menu.vue

@@ -41,26 +41,46 @@
     },
     data() {
       return {
-        activeIndex: this.defaultActive,
+        activedIndex: this.defaultActive,
         openedMenus: this.defaultOpeneds ? this.defaultOpeneds.slice(0) : [],
-        menuItems: {},
+        items: {},
         submenus: {}
       };
     },
     watch: {
       defaultActive(value) {
-        this.activeIndex = value;
-        if (!this.menuItems[value]) return;
-        let menuItem = this.menuItems[value];
-        let indexPath = menuItem.indexPath;
+        const item = this.items[value];
+        if (!item) return;
 
-        this.handleSelect(value, indexPath, null, menuItem);
+        this.activedIndex = item.index;
+        this.initOpenedMenu();
       },
       defaultOpeneds(value) {
         this.openedMenus = value;
+      },
+      '$route': {
+        immediate: true,
+        handler(value) {
+          if (this.router) {
+            this.activedIndex = value.path;
+            this.initOpenedMenu();
+          }
+        }
       }
     },
     methods: {
+      addItem(item) {
+        this.$set(this.items, item.index, item);
+      },
+      removeItem(item) {
+        delete this.items[item.index];
+      },
+      addSubmenu(item) {
+        this.$set(this.submenus, item.index, item);
+      },
+      removeSubmenu(item) {
+        delete this.submenus[item.index];
+      },
       openMenu(index, indexPath) {
         let openedMenus = this.openedMenus;
         if (openedMenus.indexOf(index) !== -1) return;
@@ -75,7 +95,8 @@
       closeMenu(index, indexPath) {
         this.openedMenus.splice(this.openedMenus.indexOf(index), 1);
       },
-      handleSubmenuClick(index, indexPath) {
+      handleSubmenuClick(submenu) {
+        const { index, indexPath } = submenu;
         let isOpened = this.openedMenus.indexOf(index) !== -1;
 
         if (isOpened) {
@@ -86,49 +107,48 @@
           this.$emit('open', index, indexPath);
         }
       },
-      handleSelect(index, indexPath, route, instance) {
-        this.activeIndex = index;
-        this.$emit('select', index, indexPath, instance);
+      handleItemClick(item) {
+        let { index, indexPath } = item;
+
+        this.$emit('select', index, indexPath, item);
 
         if (this.mode === 'horizontal') {
-          this.broadcast('ElSubmenu', 'item-select', [index, indexPath]);
           this.openedMenus = [];
-        } else {
-          this.openActiveItemMenus();
         }
 
-        if (this.router && route) {
-          try {
-            this.$router.push(route);
-          } catch (e) {
-            console.error(e);
-          }
-        }
-      },
-      openActiveItemMenus() {
-        let index = this.activeIndex;
-        // 选中用户指定的路由对应的menu
         if (this.router) {
-          const userSpecifiedIndexs = Object
-                                       .keys(this.menuItems)
-                                       .filter(k => this.menuItems[k].route)
-                                       .filter(k => this.menuItems[k].route.path === this.$route.path);
-          userSpecifiedIndexs.length && (index = this.activeIndex = userSpecifiedIndexs[0]);
+          this.routeToItem(item);
+        } else {
+          this.activedIndex = item.index;
         }
-        if (!this.menuItems[index]) return;
-        if (index && this.mode === 'vertical') {
-          let indexPath = this.menuItems[index].indexPath;
+      },
+      // 初始化展开菜单
+      initOpenedMenu() {
+        const index = this.activedIndex;
+        const activeItem = this.items[index];
+        if (!activeItem || this.mode === 'horizontal') return;
 
-          // 展开该菜单项的路径上所有子菜单
-          indexPath.forEach(index => {
-            let submenu = this.submenus[index];
-            submenu && this.openMenu(index, submenu.indexPath);
-          });
+        let indexPath = activeItem.indexPath;
+
+        // 展开该菜单项的路径上所有子菜单
+        indexPath.forEach(index => {
+          let submenu = this.submenus[index];
+          submenu && this.openMenu(index, submenu.indexPath);
+        });
+      },
+      routeToItem(item) {
+        let route = item.route || item.index;
+        try {
+          this.$router.push(route);
+        } catch (e) {
+          console.error(e);
         }
       }
     },
     mounted() {
-      this.openActiveItemMenus();
+      this.initOpenedMenu();
+      this.$on('item-click', this.handleItemClick);
+      this.$on('submenu-click', this.handleSubmenuClick);
     }
   };
 </script>

+ 58 - 10
packages/menu/src/submenu.vue

@@ -15,20 +15,31 @@
       }">
       </i>
     </div>
-    <transition :name="rootMenu.mode === 'horizontal' ? 'el-zoom-in-top' : ''">
+    <template v-if="rootMenu.mode === 'horizontal'">
+      <transition name="el-zoom-in-top">
+        <ul class="el-menu" v-show="opened"><slot></slot></ul>
+      </transition>
+    </template>
+    <collapse-transition v-else>
       <ul class="el-menu" v-show="opened"><slot></slot></ul>
-    </transition>
+    </collapse-transition>
   </li>
 </template>
 <script>
   import menuMixin from './menu-mixin';
+  import Emitter from 'element-ui/src/mixins/emitter';
+  import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
 
   module.exports = {
     name: 'ElSubmenu',
 
     componentName: 'ElSubmenu',
 
-    mixins: [menuMixin],
+    mixins: [menuMixin, Emitter],
+
+    components: {
+      CollapseTransition
+    },
 
     props: {
       index: {
@@ -39,17 +50,52 @@
     data() {
       return {
         timeout: null,
-        active: false
+        items: {},
+        submenus: {}
       };
     },
     computed: {
       opened() {
-        return this.rootMenu.openedMenus.indexOf(this.index) !== -1;
+        return this.rootMenu.openedMenus.indexOf(this.index) > -1;
+      },
+      active: {
+        cache: false,
+        get() {
+          let isActive = false;
+          const submenus = this.submenus;
+          const items = this.items;
+
+          Object.keys(items).forEach(index => {
+            if (items[index].active) {
+              isActive = true;
+            }
+          });
+
+          Object.keys(submenus).forEach(index => {
+            if (submenus[index].active) {
+              isActive = true;
+            }
+          });
+
+          return isActive;
+        }
       }
     },
     methods: {
+      addItem(item) {
+        this.$set(this.items, item.index, item);
+      },
+      removeItem(item) {
+        delete this.items[item.index];
+      },
+      addSubmenu(item) {
+        this.$set(this.submenus, item.index, item);
+      },
+      removeSubmenu(item) {
+        delete this.submenus[item.index];
+      },
       handleClick() {
-        this.rootMenu.handleSubmenuClick(this.index, this.indexPath);
+        this.dispatch('ElMenu', 'submenu-click', this);
       },
       handleMouseenter() {
         clearTimeout(this.timeout);
@@ -83,12 +129,14 @@
       }
     },
     created() {
-      this.rootMenu.submenus[this.index] = this;
+      this.parentMenu.addSubmenu(this);
+      this.rootMenu.addSubmenu(this);
+    },
+    beforeDestroy() {
+      this.parentMenu.removeSubmenu(this);
+      this.rootMenu.removeSubmenu(this);
     },
     mounted() {
-      this.$on('item-select', (index, indexPath) => {
-        this.active = indexPath.indexOf(this.index) !== -1;
-      });
       this.initEvents();
     }
   };

+ 8 - 1
packages/message-box/src/main.js

@@ -20,7 +20,8 @@ const defaults = {
   confirmButtonText: '',
   cancelButtonText: '',
   confirmButtonClass: '',
-  cancelButtonClass: ''
+  cancelButtonClass: '',
+  beforeClose: null
 };
 
 import Vue from 'vue';
@@ -87,6 +88,12 @@ const showNextMsg = () => {
       if (options.callback === undefined) {
         instance.callback = defaultCallback;
       }
+
+      let oldCb = instance.callback;
+      instance.callback = action => {
+        oldCb(action);
+        showNextMsg();
+      };
       ['modal', 'showClose', 'closeOnClickModal', 'closeOnPressEscape'].forEach(prop => {
         if (instance[prop] === undefined) {
           instance[prop] = true;

+ 39 - 6
packages/message-box/src/main.vue

@@ -15,8 +15,21 @@
           </div>
         </div>
         <div class="el-message-box__btns">
-          <el-button :class="[ cancelButtonClasses ]" v-show="showCancelButton" @click.native="handleAction('cancel')">{{ cancelButtonText || t('el.messagebox.cancel') }}</el-button>
-          <el-button ref="confirm" :class="[ confirmButtonClasses ]" v-show="showConfirmButton" @click.native="handleAction('confirm')">{{ confirmButtonText || t('el.messagebox.confirm') }}</el-button>
+          <el-button
+            :loading="cancelButtonLoading"
+            :class="[ cancelButtonClasses ]"
+            v-show="showCancelButton"
+            @click.native="handleAction('cancel')">
+            {{ cancelButtonText || t('el.messagebox.cancel') }}
+          </el-button>
+          <el-button
+            :loading="confirmButtonLoading"
+            ref="confirm"
+            :class="[ confirmButtonClasses ]"
+            v-show="showConfirmButton"
+            @click.native="handleAction('confirm')">
+            {{ confirmButtonText || t('el.messagebox.confirm') }}
+          </el-button>
         </div>
       </div>
     </div>
@@ -79,7 +92,16 @@
     },
 
     methods: {
+      getSafeClose() {
+        const currentId = this.uid;
+        return () => {
+          this.$nextTick(() => {
+            if (currentId === this.uid) this.doClose();
+          });
+        };
+      },
       doClose() {
+        if (!this.value) return;
         this.value = false;
         this._closing = true;
 
@@ -100,11 +122,13 @@
         if (!this.transition) {
           this.doAfterClose();
         }
+        if (this.action) this.callback(this.action, this);
       },
 
       handleWrapperClick() {
         if (this.closeOnClickModal) {
-          this.close();
+          this.action = '';
+          this.doClose();
         }
       },
 
@@ -112,9 +136,13 @@
         if (this.$type === 'prompt' && action === 'confirm' && !this.validate()) {
           return;
         }
-        var callback = this.callback;
-        this.value = false;
-        callback(action);
+        this.action = action;
+        if (typeof this.beforeClose === 'function') {
+          this.close = this.getSafeClose();
+          this.beforeClose(action, this, this.close);
+        } else {
+          this.doClose();
+        }
       },
 
       validate() {
@@ -153,6 +181,7 @@
       },
 
       value(val) {
+        if (val) this.uid++;
         if (this.$type === 'alert' || this.$type === 'confirm') {
           this.$nextTick(() => {
             this.$refs.confirm.$el.focus();
@@ -174,6 +203,7 @@
 
     data() {
       return {
+        uid: 1,
         title: undefined,
         message: '',
         type: '',
@@ -186,8 +216,11 @@
         inputErrorMessage: '',
         showConfirmButton: true,
         showCancelButton: false,
+        action: '',
         confirmButtonText: '',
         cancelButtonText: '',
+        confirmButtonLoading: false,
+        cancelButtonLoading: false,
         confirmButtonClass: '',
         confirmButtonDisabled: false,
         cancelButtonClass: '',

+ 7 - 1
packages/notification/src/main.js

@@ -1,5 +1,6 @@
 import Vue from 'vue';
 import { PopupManager } from 'element-ui/src/utils/popup';
+import { isVNode } from 'element-ui/src/utils/vdom';
 let NotificationConstructor = Vue.extend(require('./main.vue'));
 
 let instance;
@@ -19,6 +20,11 @@ var Notification = function(options) {
   instance = new NotificationConstructor({
     data: options
   });
+
+  if (isVNode(options.message)) {
+    instance.$slots.default = [options.message];
+    options.message = '';
+  }
   instance.id = id;
   instance.vm = instance.$mount();
   document.body.appendChild(instance.vm.$el);
@@ -39,7 +45,7 @@ var Notification = function(options) {
 
 ['success', 'warning', 'info', 'error'].forEach(type => {
   Notification[type] = options => {
-    if (typeof options === 'string') {
+    if (typeof options === 'string' || isVNode(options)) {
       options = {
         message: options
       };

+ 2 - 2
packages/notification/src/main.vue

@@ -13,8 +13,8 @@
         v-if="type || iconClass">
       </i>
       <div class="el-notification__group" :class="{ 'is-with-icon': typeClass || iconClass }">
-        <span>{{ title }}</span>
-        <p>{{ message }}</p>
+        <h2 class="el-notification__title" v-text="title"></h2>
+        <div class="el-notification__content"><slot>{{ message }}</slot></div>
         <div class="el-notification__closeBtn el-icon-close" @click="close"></div>
       </div>
     </div>

+ 2 - 1
packages/popover/src/main.vue

@@ -5,7 +5,7 @@
         class="el-popover"
         :class="[popperClass]"
         ref="popper"
-        v-show="showPopper"
+        v-show="!disabled && showPopper"
         :style="{ width: width + 'px' }">
         <div class="el-popover__title" v-if="title" v-text="title"></div>
         <slot>{{ content }}</slot>
@@ -31,6 +31,7 @@ export default {
       validator: value => ['click', 'focus', 'hover', 'manual'].indexOf(value) > -1
     },
     title: String,
+    disabled: Boolean,
     content: String,
     reference: {},
     popperClass: String,

+ 3 - 3
packages/select/src/option.vue

@@ -88,10 +88,10 @@
 
     watch: {
       currentLabel() {
-        this.dispatch('ElSelect', 'setSelected');
+        if (!this.created) this.dispatch('ElSelect', 'setSelected');
       },
       value() {
-        this.dispatch('ElSelect', 'setSelected');
+        if (!this.created) this.dispatch('ElSelect', 'setSelected');
       }
     },
 
@@ -114,7 +114,7 @@
 
       queryChange(query) {
         // query 里如果有正则中的特殊字符,需要先将这些字符转义
-        let parsedQuery = query.replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
+        let parsedQuery = String(query).replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
         this.visible = new RegExp(parsedQuery, 'i').test(this.currentLabel) || this.created;
         if (!this.visible) {
           this.parent.filteredOptionsCount--;

+ 25 - 2
packages/select/src/select.vue

@@ -49,7 +49,7 @@
       :disabled="disabled"
       :readonly="!filterable || multiple"
       :validate-event="false"
-      @focus="toggleMenu"
+      @focus="handleFocus"
       @click="handleIconClick"
       @mousedown.native="handleMouseDown"
       @keyup.native="debouncedOnInputChange"
@@ -98,6 +98,7 @@
   import { addClass, removeClass, hasClass } from 'element-ui/src/utils/dom';
   import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
   import { t } from 'element-ui/src/locale';
+  import merge from 'element-ui/src/utils/merge';
   const sizeMap = {
     'large': 42,
     'small': 30,
@@ -191,6 +192,8 @@
       return {
         options: [],
         cachedOptions: [],
+        createdOption: null,
+        createdSelected: false,
         selected: this.multiple ? [] : {},
         isSelect: true,
         inputLength: 20,
@@ -265,6 +268,7 @@
           }
           this.query = '';
           this.selectedLabel = '';
+          this.inputLength = 20;
           this.resetHoverIndex();
           this.$nextTick(() => {
             if (this.$refs.input &&
@@ -276,7 +280,12 @@
           if (!this.multiple) {
             this.getOverflows();
             if (this.selected) {
-              this.selectedLabel = this.selected.currentLabel;
+              if (this.filterable && this.allowCreate &&
+                this.createdSelected && this.createdOption) {
+                this.selectedLabel = this.createdOption.currentLabel;
+              } else {
+                this.selectedLabel = this.selected.currentLabel;
+              }
               if (this.filterable) this.query = this.selectedLabel;
             }
           }
@@ -371,8 +380,15 @@
       setSelected() {
         if (!this.multiple) {
           let option = this.getOption(this.value);
+          if (option.created) {
+            this.createdOption = merge({}, option);
+            this.createdSelected = true;
+          } else {
+            this.createdSelected = false;
+          }
           this.selectedLabel = option.currentLabel;
           this.selected = option;
+          if (this.filterable) this.query = this.selectedLabel;
           return;
         }
         let result = [];
@@ -382,6 +398,13 @@
           });
         }
         this.selected = result;
+        this.$nextTick(() => {
+          this.resetInputHeight();
+        });
+      },
+
+      handleFocus() {
+        this.visible = true;
       },
 
       handleIconClick(event) {

+ 5 - 0
packages/slider/src/main.vue

@@ -8,6 +8,7 @@
       ref="input"
       :step="step"
       :disabled="disabled"
+      :controls="showInputControls"
       :min="min"
       :max="max"
       size="small">
@@ -67,6 +68,10 @@
         type: Boolean,
         default: false
       },
+      showInputControls: {
+        type: Boolean,
+        default: true
+      },
       showStops: {
         type: Boolean,
         default: false

+ 1 - 1
packages/steps/README.md

@@ -37,7 +37,7 @@ Vue.component('el-step', ElStep)
 | active | 设置当前激活步骤  | number | — | 0 |
 | process-status | 设置当前步骤的状态 | string | wait/process/finish/error/success | process |
 | finish-status | 设置结束步骤的状态 | string | wait/process/finish/error/success | finish |
-| align-center | 标题描述居中对齐 | boolean | false, true | false |
+| align-center | 标题描述居中对齐 | boolean | - | false |
 
 ### Step Attributes
 | 参数      | 说明    | 类型      | 可选值       | 默认值   |

+ 15 - 4
packages/steps/src/step.vue

@@ -1,14 +1,15 @@
 <template>
   <div
     class="el-step"
-    :style="style"
+    :style="[style,  isLast ? '' : { marginRight: - $parent.stepOffset + 'px' }]"
     :class="['is-' + $parent.direction]">
     <div
       class="el-step__head"
       :class="['is-' + currentStatus, { 'is-text': !icon }]">
       <div
         class="el-step__line"
-        :class="['is-' + $parent.direction,{ 'is-icon': icon }]">
+        :style="isLast ? '' : { marginRight: $parent.stepOffset + 'px' }"
+        :class="['is-' + $parent.direction, { 'is-icon': icon }]">
         <i class="el-step__line-inner" :style="lineStyle"></i>
       </div>
 
@@ -63,6 +64,7 @@ export default {
       style: {},
       lineStyle: {},
       mainOffset: 0,
+      isLast: false,
       currentStatus: this.status
     };
   },
@@ -103,22 +105,31 @@ export default {
         : style.width = step + '%';
 
       this.lineStyle = style;
+    },
+
+    adjustPosition() {
+      this.style = {};
+      this.$parent.stepOffset = this.$el.getBoundingClientRect().width / (this.$parent.steps.length - 1);
     }
   },
 
   mounted() {
     const parent = this.$parent;
+    const isCenter = parent.center;
+    const len = parent.steps.length;
+    const isLast = this.isLast = parent.steps[parent.steps.length - 1] === this;
     const space = parent.space
       ? parent.space + 'px'
-      : 100 / parent.steps.length + '%';
+      : 100 / (isCenter ? len - 1 : len) + '%';
 
     if (parent.direction === 'horizontal') {
       this.style = { width: space };
       if (parent.alignCenter) {
         this.mainOffset = -this.$refs.title.getBoundingClientRect().width / 2 + 16 + 'px';
       }
+      isCenter && isLast && this.adjustPosition();
     } else {
-      if (parent.steps[parent.steps.length - 1] !== this) {
+      if (!isLast) {
         this.style = { height: space };
       }
     }

+ 8 - 2
packages/steps/src/steps.vue

@@ -1,5 +1,9 @@
 <template>
-  <div class="el-steps" :class="['is-' + direction]"><slot></slot></div>
+  <div
+    class="el-steps"
+    :class="['is-' + direction, center ? 'is-center' : '']">
+    <slot></slot>
+  </div>
 </template>
 
 <script>
@@ -14,6 +18,7 @@ export default {
       default: 'horizontal'
     },
     alignCenter: Boolean,
+    center: Boolean,
     finishStatus: {
       type: String,
       default: 'finish'
@@ -26,7 +31,8 @@ export default {
 
   data() {
     return {
-      steps: []
+      steps: [],
+      stepOffset: 0
     };
   },
 

+ 10 - 8
packages/table/src/table-body.js

@@ -28,13 +28,15 @@ export default {
         cellspacing="0"
         cellpadding="0"
         border="0">
-        {
-          this._l(this.columns, column =>
-            <col
-              name={ column.id }
-              width={ column.realWidth || column.width }
-            />)
-        }
+        <colgroup>
+          {
+            this._l(this.columns, column =>
+              <col
+                name={ column.id }
+                width={ column.realWidth || column.width }
+              />)
+          }
+        </colgroup>
         <tbody>
           {
             this._l(this.data, (row, $index) =>
@@ -114,7 +116,7 @@ export default {
 
   computed: {
     table() {
-      return this.$parent.$parent.columns ? this.$parent.$parent : this.$parent;
+      return this.$parent;
     },
 
     data() {

+ 7 - 2
packages/table/src/table-column.js

@@ -143,6 +143,7 @@ export default {
     selectable: Function,
     reserveSelection: Boolean,
     filterMethod: Function,
+    filteredValue: Array,
     filters: Array,
     filterMultiple: {
       type: Boolean,
@@ -235,7 +236,7 @@ export default {
       filterable: this.filters || this.filterMethod,
       filterMultiple: this.filterMultiple,
       filterOpened: false,
-      filteredValue: []
+      filteredValue: this.filteredValue || []
     });
 
     objectAssign(column, forced[type] || {});
@@ -335,12 +336,16 @@ export default {
     align(newVal) {
       if (this.columnConfig) {
         this.columnConfig.align = newVal ? 'is-' + newVal : null;
+
+        if (!this.headerAlign) {
+          this.columnConfig.headerAlign = newVal ? 'is-' + newVal : null;
+        }
       }
     },
 
     headerAlign(newVal) {
       if (this.columnConfig) {
-        this.columnConfig.headerAlign = newVal ? 'is-' + newVal : this.align;
+        this.columnConfig.headerAlign = 'is-' + (newVal ? newVal : this.align);
       }
     },
 

+ 56 - 17
packages/table/src/table-header.js

@@ -78,18 +78,20 @@ export default {
         cellspacing="0"
         cellpadding="0"
         border="0">
-        {
-          this._l(this.columns, column =>
-            <col
-              name={ column.id }
-              width={ column.realWidth || column.width }
-            />)
-        }
-        {
-          !this.fixed && this.layout.gutterWidth
-            ? <col name="gutter" width={ this.layout.scrollY ? this.layout.gutterWidth : '' }></col>
-            : ''
-        }
+        <colgroup>
+          {
+            this._l(this.columns, column =>
+              <col
+                name={ column.id }
+                width={ column.realWidth || column.width }
+              />)
+          }
+          {
+            !this.fixed && this.layout.gutterWidth
+              ? <col name="gutter" width={ this.layout.scrollY ? this.layout.gutterWidth : '' }></col>
+              : ''
+          }
+        </colgroup>
         <thead>
           {
             this._l(columnRows, (columns, rowIndex) =>
@@ -112,9 +114,9 @@ export default {
                     }
                     {
                       column.sortable
-                        ? <span class="caret-wrapper">
-                            <i class="sort-caret ascending" on-click={ ($event) => this.handleHeaderClick($event, column, 'ascending')}></i>
-                            <i class="sort-caret descending" on-click={ ($event) => this.handleHeaderClick($event, column, 'descending')}></i>
+                        ? <span class="caret-wrapper" on-click={ ($event) => this.handleHeaderClick($event, column) }>
+                            <i class="sort-caret ascending"></i>
+                            <i class="sort-caret descending"></i>
                           </span>
                         : ''
                     }
@@ -148,7 +150,12 @@ export default {
     layout: {
       required: true
     },
-    border: Boolean
+    border: Boolean,
+    defaultSortProp: String,
+    defaultSortOrder: {
+      type: String,
+      default: 'ascending'
+    }
   },
 
   components: {
@@ -182,6 +189,29 @@ export default {
     this.filterPanels = {};
   },
 
+  mounted() {
+    if (this.defaultSortProp) {
+      const states = this.store.states;
+      states.sortProp = this.defaultSortProp;
+      states.sortOrder = this.defaultSortOrder;
+
+      this.$nextTick(_ => {
+        for (let i = 0, length = this.columns.length; i < length; i++) {
+          let column = this.columns[i];
+          if (column.property === this.defaultSortProp) {
+            column.order = this.defaultSortOrder;
+            states.sortingColumn = column;
+            break;
+          }
+        }
+
+        if (states.sortingColumn) {
+          this.store.commit('changeSortCondition');
+        }
+      });
+    }
+  },
+
   beforeDestroy() {
     const panels = this.filterPanels;
     for (let prop in panels) {
@@ -334,7 +364,16 @@ export default {
       document.body.style.cursor = '';
     },
 
-    handleHeaderClick(event, column, order) {
+    toggleOrder(column) {
+      if (column.order === 'ascending') {
+        return 'descending';
+      }
+      return 'ascending';
+    },
+
+    handleHeaderClick(event, column) {
+      let order = this.toggleOrder(column);
+
       let target = event.target;
       while (target && target.tagName !== 'TH') {
         target = target.parentNode;

+ 2 - 1
packages/table/src/table-layout.js

@@ -69,6 +69,7 @@ class TableLayout {
 
   updateHeight() {
     const height = this.tableHeight = this.table.$el.clientHeight;
+    const noData = !this.table.data || this.table.data.length === 0;
     const { headerWrapper } = this.table.$refs;
     if (this.showHeader && !headerWrapper) return;
     if (!this.showHeader) {
@@ -85,7 +86,7 @@ class TableLayout {
       }
       this.fixedBodyHeight = this.scrollX ? bodyHeight - this.gutterWidth : bodyHeight;
     }
-    this.viewportHeight = this.scrollX ? height - this.gutterWidth : height;
+    this.viewportHeight = this.scrollX ? height - (noData ? 0 : this.gutterWidth) : height;
   }
 
   update() {

+ 14 - 3
packages/table/src/table.vue

@@ -15,6 +15,8 @@
         :store="store"
         :layout="layout"
         :border="border"
+        :default-sort-prop="defaultSortProp"
+        :default-sort-order="defaultSortOrder"
         :style="{ width: layout.bodyWidth ? layout.bodyWidth + 'px' : '' }">
       </table-header>
     </div>
@@ -29,9 +31,9 @@
         :row-class-name="rowClassName"
         :row-style="rowStyle"
         :highlight="highlightCurrentRow"
-        :style="{ width: layout.bodyWidth ? layout.bodyWidth - (layout.scrollY ? layout.gutterWidth : 0 ) + 'px' : '' }">
+        :style="{ width: bodyWidth }">
       </table-body>
-      <div class="el-table__empty-block" v-if="!data || data.length === 0">
+      <div :style="{ width: bodyWidth }" class="el-table__empty-block" v-if="!data || data.length === 0">
         <span class="el-table__empty-text"><slot name="empty">{{ emptyText || t('el.table.emptyText') }}</slot></span>
       </div>
     </div>
@@ -166,7 +168,11 @@
 
       expandRowKeys: Array,
 
-      defaultExpandAll: Boolean
+      defaultExpandAll: Boolean,
+
+      defaultSortProp: String,
+
+      defaultSortOrder: String
     },
 
     components: {
@@ -291,6 +297,11 @@
         return style;
       },
 
+      bodyWidth() {
+        const { bodyWidth, scrollY, gutterWidth } = this.layout;
+        return bodyWidth ? bodyWidth - (scrollY ? gutterWidth : 0) + 'px' : '';
+      },
+
       fixedBodyHeight() {
         let style = {};
 

+ 42 - 0
packages/tabs/src/tab-bar.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="el-tabs__active-bar" :style="barStyle"></div>
+</template>
+<script>
+  export default {
+    props: {
+      tabs: Array
+    },
+    computed: {
+      barStyle: {
+        cache: false,
+        get() {
+          if (!this.$parent.$refs.tabs) return {};
+          let style = {};
+          let offset = 0;
+          let tabWidth = 0;
+
+          this.tabs.every((tab, index) => {
+            let $el = this.$parent.$refs.tabs[index];
+            if (!$el) { return false; }
+
+            if (!tab.active) {
+              offset += $el.clientWidth;
+              return true;
+            } else {
+              tabWidth = $el.clientWidth;
+              return false;
+            }
+          });
+
+          const transform = `translateX(${offset}px)`;
+          style.width = tabWidth + 'px';
+          style.transform = transform;
+          style.msTransform = transform;
+          style.webkitTransform = transform;
+
+          return style;
+        }
+      }
+    }
+  };
+</script>

+ 7 - 6
packages/tabs/src/tab-pane.vue

@@ -1,14 +1,14 @@
 <template>
-  <div class="el-tab-pane">
-    <div class="el-tab-pane__content" v-show="active">
-      <slot></slot>
-    </div>
+  <div class="el-tab-pane" v-show="active">
+    <slot></slot>
   </div>
 </template>
 <script>
   module.exports = {
     name: 'ElTabPane',
 
+    componentName: 'ElTabPane',
+
     props: {
       label: String,
       labelContent: Function,
@@ -32,14 +32,15 @@
       }
     },
 
-    created() {
-      this.$parent.$forceUpdate();
+    mounted() {
+      this.$parent.addPanes(this);
     },
 
     destroyed() {
       if (this.$el && this.$el.parentNode) {
         this.$el.parentNode.removeChild(this.$el);
       }
+      this.$parent.removePanes(this);
     },
 
     watch: {

+ 45 - 57
packages/tabs/src/tabs.vue

@@ -1,7 +1,12 @@
 <script>
+  import TabBar from './tab-bar';
   module.exports = {
     name: 'ElTabs',
 
+    components: {
+      TabBar
+    },
+
     props: {
       type: String,
       activeName: String,
@@ -15,7 +20,8 @@
     data() {
       return {
         children: null,
-        currentName: this.value || this.activeName
+        currentName: this.value || this.activeName,
+        panes: []
       };
     },
 
@@ -30,9 +36,8 @@
 
     computed: {
       currentTab() {
-        if (!this.$children) return;
         let result;
-        this.$children.forEach(tab => {
+        this.panes.forEach(tab => {
           if (this.currentName === (tab.name || tab.index)) {
             result = tab;
           }
@@ -42,21 +47,25 @@
     },
 
     methods: {
-      handleTabRemove(tab, event) {
+      handleTabRemove(pane, event) {
         event.stopPropagation();
-        const tabs = this.$children;
+        const panes = this.panes;
         const currentTab = this.currentTab;
 
-        let index = tabs.indexOf(tab);
-        tab.$destroy();
+        let index = panes.indexOf(pane);
+
+        if (index === -1) return;
 
-        this.$emit('tab-remove', tab);
-        this.$forceUpdate();
+        panes.splice(index, 1);
+        pane.$destroy();
+
+        this.$emit('tab-remove', pane);
 
         this.$nextTick(_ => {
-          if (tab.active) {
-            let nextChild = tabs[index];
-            let prevChild = tabs[index - 1];
+          if (pane.active) {
+            const panes = this.panes;
+            let nextChild = panes[index];
+            let prevChild = panes[index - 1];
             let nextActiveTab = nextChild || prevChild || null;
 
             if (nextActiveTab) {
@@ -76,80 +85,58 @@
       setCurrentName(value) {
         this.currentName = value;
         this.$emit('input', value);
+      },
+      addPanes(item) {
+        this.panes.push(item);
+      },
+      removePanes(item) {
+        const panes = this.panes;
+        const index = panes.indexOf(item);
+        if (index > -1) {
+          panes.splice(index, 1);
+        }
       }
     },
-    mounted() {
-      this.$forceUpdate();
-    },
     render(h) {
       let {
         type,
         handleTabRemove,
         handleTabClick,
-        currentName
+        currentName,
+        panes
       } = this;
 
-      const getBarStyle = () => {
-        if (this.type || !this.$refs.tabs) return {};
-        let style = {};
-        let offset = 0;
-        let tabWidth = 0;
-
-        this.$children.every((tab, index) => {
-          let $el = this.$refs.tabs[index];
-          if (!$el) { return false; }
-
-          if (!tab.active) {
-            offset += $el.clientWidth;
-            return true;
-          } else {
-            tabWidth = $el.clientWidth;
-            return false;
-          }
-        });
-
-        style.width = tabWidth + 'px';
-        style.transform = `translateX(${offset}px)`;
-
-        return style;
-      };
-
-      const tabs = this.$children.map((tab, index) => {
-        let tabName = tab.name || tab.index || index;
+      const tabs = this._l(panes, (pane, index) => {
+        let tabName = pane.name || pane.index || index;
         if (currentName === undefined && index === 0) {
           this.setCurrentName(tabName);
         }
 
-        tab.index = index;
-
-        const activeBar = !type && index === 0
-          ? <div class="el-tabs__active-bar" style={getBarStyle()}></div>
-          : null;
+        pane.index = index;
 
-        const btnClose = tab.isClosable
-          ? <span class="el-icon-close" on-click={(ev) => { handleTabRemove(tab, ev); }}></span>
+        const btnClose = pane.isClosable
+          ? <span class="el-icon-close" on-click={(ev) => { handleTabRemove(pane, ev); }}></span>
           : null;
 
-        const tabLabelContent = tab.$slots.label || tab.label;
-
+        const tabLabelContent = pane.$slots.label || pane.label;
         return (
           <div
             class={{
               'el-tabs__item': true,
-              'is-active': tab.active,
-              'is-disabled': tab.disabled,
-              'is-closable': tab.isClosable
+              'is-active': pane.active,
+              'is-disabled': pane.disabled,
+              'is-closable': pane.isClosable
             }}
             ref="tabs"
             refInFor
-            on-click={(ev) => { handleTabClick(tab, tabName, ev); }}
+            on-click={(ev) => { handleTabClick(pane, tabName, ev); }}
           >
             {tabLabelContent}
             {btnClose}
-            {activeBar}
           </div>
         );
       });
+
       return (
         <div class={{
           'el-tabs': true,
@@ -157,6 +144,7 @@
           'el-tabs--border-card': type === 'border-card'
         }}>
           <div class="el-tabs__header">
+            {!type ? <tab-bar tabs={panes}></tab-bar> : null}
             {tabs}
           </div>
           <div class="el-tabs__content">

+ 1 - 1
packages/theme-default/package.json

@@ -1,6 +1,6 @@
 {
   "name": "element-theme-default",
-  "version": "1.1.2",
+  "version": "1.1.3",
   "description": "Element component default theme.",
   "main": "lib/index.css",
   "style": "lib/index.css",

+ 3 - 3
packages/theme-default/src/alert.css

@@ -12,7 +12,7 @@
     position: relative;
     background-color: var(--color-white);
     overflow: hidden;
-    color: #fff;
+    color: var(--color-white);
     opacity: 1;
     display: table;
     transition: opacity .2s;
@@ -59,14 +59,14 @@
     }
 
     & .el-alert__description {
-      color: #fff;
+      color: var(--color-white);
       font-size: var(--alert-description-font-size);
       margin: 5px 0 0 0;
     }
 
     @e closebtn {
       font-size: var(--alert-close-font-size);
-      color: #fff;
+      color: var(--color-white);
       opacity: 1;
       position: absolute 12px 15px * *;
       cursor: pointer;

+ 6 - 6
packages/theme-default/src/autocomplete.css

@@ -12,8 +12,8 @@
       left: 0;
       top: 110%;
       margin: 5px 0 0;
-      background-color: #fff;
-      border: 1px solid #D3DCE6;
+      background-color: var(--color-white);
+      border: 1px solid var(--color-base-gray);
       width: 100%;
       padding: 6px 0;
       z-index: 10;
@@ -29,7 +29,7 @@
         padding: 0 10px;
         margin: 0;
         cursor: pointer;
-        color: #475669;
+        color: var(--color-extra-light-black);
         font-size: 14px;
         white-space: nowrap;
         overflow: hidden;
@@ -40,14 +40,14 @@
         }
         &.highlighted {
           background-color: var(--color-primary);
-          color: #fff;
+          color: var(--color-white);
         }
         &:active {
           background-color: darken(var(--color-primary), 0.2);
         }
         &.divider {
           margin-top: 6px;
-          border-top: 1px solid #D3DCE6;
+          border-top: 1px solid var(--color-base-gray);
         }
         &.divider:last-child {
           margin-bottom: -6px;
@@ -64,7 +64,7 @@
           @utils-vertical-center;
 
           &:hover {
-            background-color: #fff;
+            background-color: var(--color-white);
           }
         }
 

+ 2 - 2
packages/theme-default/src/badge.css

@@ -10,7 +10,7 @@
     @e content {
       background-color: var(--badge-fill);
       border-radius: var(--badge-radius);
-      color: #fff;
+      color: var(--color-white);
       display: inline-block;
       font-size: var(--badge-font-size);
       height: var(--badge-size);
@@ -18,7 +18,7 @@
       padding: 0 var(--badge-padding);
       text-align: center;
       white-space: nowrap;
-      border: 1px solid #fff;
+      border: 1px solid var(--color-white);
 
       @when fixed {
         position: absolute 0 calc(var(--badge-size) / 2 + 1) * *;

+ 3 - 3
packages/theme-default/src/breadcrumb.css

@@ -10,7 +10,7 @@
 
     @e separator {
       margin: 0 8px;
-      color: #c0ccda;
+      color: var(--color-extra-light-silver);
     }
     @e item {
       float: left;
@@ -18,7 +18,7 @@
       @e inner {
         &, & a {
           transition: color .15s linear;
-          color:#475669;
+          color:var(--color-extra-light-black);
 
           &:hover {
             color: var(--color-primary);
@@ -31,7 +31,7 @@
         .el-breadcrumb__item__inner,
         .el-breadcrumb__item__inner a {
           &, &:hover {
-            color: #99a9bf;
+            color: var(--color-light-silver);
             cursor: text;
           }
         }

+ 9 - 9
packages/theme-default/src/button.css

@@ -48,13 +48,13 @@
     @when plain {
       &:hover,
       &:focus {
-        background: #fff;
+        background: var(--color-white);
         border-color: var(--color-primary);
         color: var(--color-primary);
       }
       
       &:active {
-        background: #fff;
+        background: var(--color-white);
         border-color: shade(var(--color-primary), var(--button-active-shade-percent));
         color: shade(var(--color-primary), var(--button-active-shade-percent));
         outline: none;
@@ -85,9 +85,9 @@
         &,
         &:hover,
         &:focus {
-          background-color: #fff;
-          border-color: #d3dce6;
-          color: #C0CCDA;
+          background-color: var(--color-white);
+          border-color: var(--color-base-gray);
+          color: var(--color-extra-light-silver);
         }
       }
     }
@@ -190,14 +190,14 @@
     @each $type in (primary, success, warning, danger, info) {
       .el-button--$type {
         &:first-child {
-          border-right-color: rgba(#fff, 0.5);
+          border-right-color: rgba(var(--color-white), 0.5);
         }
         &:last-child {
-          border-left-color: rgba(#fff, 0.5);
+          border-left-color: rgba(var(--color-white), 0.5);
         }
         &:not(:first-child):not(:last-child) {
-          border-left-color: rgba(#fff, 0.5);
-          border-right-color: rgba(#fff, 0.5);
+          border-left-color: rgba(var(--color-white), 0.5);
+          border-right-color: rgba(var(--color-white), 0.5);
         }
       }
     }

+ 1 - 1
packages/theme-default/src/card.css

@@ -5,7 +5,7 @@
   @b card {
     border: 1px solid var(--card-border-color);
     border-radius: var(--card-border-radius);
-    background-color: #fff;
+    background-color: var(--color-white);
     overflow: hidden;
     box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, .12),
                 0px 0px 6px 0px rgba(0, 0, 0, .04);

+ 80 - 74
packages/theme-default/src/common/var.css

@@ -12,33 +12,44 @@
   /* Colors
   -------------------------- */
   --color-primary: #20a0ff;
+
   --color-success: #13ce66;
   --color-warning: #f7ba2a;
   --color-danger: #ff4949;
   --color-info: #50bfff;
-  --color-blue: #2e90fe;
-  --color-blue-light: #5da9ff;
-  --color-blue-lighter: rgba(var(--color-blue), 0.12);
+
+  --color-secondary: color(var(--color-primary) s(99%) l(*0.9));
   --color-white: #fff;
+  --color-dark-white: color(var(--color-white) blend(var(--color-primary) 2%));
   --color-black: #000;
-  --color-grey: #c0ccda;
+
+  --color-base-black: color(var(--color-primary) h(+6) s(33%) l(18%));
+  --color-light-black: color(var(--color-base-black) h(+5) s(27%) l(27%));
+  --color-extra-light-black: color(var(--color-base-black) h(+2) s(19%) l(35%));
+
+  --color-base-silver: color(var(--color-base-black) h(+3) s(16%) l(58%));
+  --color-light-silver: color(var(--color-base-black) h(+3) s(23%) l(67%));
+  --color-extra-light-silver: color(var(--color-base-black) s(26%) l(80%));
+
+  --color-base-gray: color(var(--color-base-black) s(28%) l(86%));
+  --color-light-gray: color(var(--color-base-black) h(+10) s(33%) l(92%));
+  --color-extra-light-gray: color(var(--color-base-black) h(+6) s(33%) l(95%));
 
   /* Link
   -------------------------- */
-  --link-color: #475669;
+  --link-color: var(--color-extra-light-black);
   --link-hover-color: var(--color-primary);
 
   /* Border
   -------------------------- */
   --border-width-base: 1px;
   --border-style-base: solid;
-  --border-color-base: var(--color-grey);
-  --border-color-hover: #8492a6;
+  --border-color-base: var(--color-extra-light-silver);
+  --border-color-hover: var(--color-base-silver);
   --border-base: var(--border-width-base) var(--border-style-base) var(--border-color-base);
   --border-radius-base: 4px;
   --border-radius-small: 2px;
   --border-radius-circle: 100%;
-  --shadow-base: 0 0 2px rgba(var(--color-black), 0.18), 0 0 1px var(--color-blue-light);
 
   /* Box-shadow
   -------------------------- */
@@ -52,7 +63,7 @@
   /* Font
   -------------------------- */
   --font-size-base: 14px;
-  --font-color-base: #1f2d3d;
+  --font-color-base: var(--color-base-black);
   --font-color-disabled-base: #bbb;
 
   /* Size
@@ -67,9 +78,9 @@
 
   /* Disable base
   -------------------------- */
-  --disabled-fill-base: #EFF2F7;
+  --disabled-fill-base: var(--color-extra-light-gray);
   --disabled-color-base: #bbb;
-  --disabled-border-base: #D3DCE6;
+  --disabled-border-base: var(--color-base-gray);
 
   /* Icon
   -------------------------- */
@@ -78,7 +89,7 @@
   /* Checkbox
   -------------------------- */
   --checkbox-font-size: 14px;
-  --checkbox-color: #1f2d3d;
+  --checkbox-color: var(--color-base-black);
   --checkbox-input-height: 18px;
   --checkbox-input-width: 18px;
   --checkbox-input-border-radius: var(--border-radius-base);
@@ -95,17 +106,16 @@
   --checkbox-disabled-checked-input-border-color: var(--disabled-border-base);
   --checkbox-disabled-checked-icon-color: var(--color-white);
 
-  --checkbox-checked-input-border-color: var(--color-blue);
+  --checkbox-checked-input-border-color: var(--color-secondary);
   --checkbox-checked-input-fill: var(--color-primary);
   --checkbox-checked-icon-color: var(--fill-base);
 
-  --checkbox-input-shadow-hover: var(--shadow-base);
   --checkbox-input-border-color-hover: var(--color-primary);
 
   /* Radio
   -------------------------- */
   --radio-font-size: 14px;
-  --radio-color: #1f2d3d;
+  --radio-color: var(--color-base-black);
   --radio-input-height: 18px;
   --radio-input-width: 18px;
   --radio-input-border-radius: var(--border-radius-circle);
@@ -126,7 +136,6 @@
   --radio-checked-input-fill: var(--color-white);
   --radio-checked-icon-color: var(--color-primary);
 
-  --radio-input-shadow-hover: var(--shadow-base);
   --radio-input-border-color-hover: var(--color-primary);
 
   --radio-button-font-size: var(--font-size-base);
@@ -136,9 +145,9 @@
   --select-border-color-hover: var(--border-color-hover);
   --select-disabled-border: var(--disabled-border-base);
   --select-font-size: var(--font-size-base);
-  --select-close-hover-color: #99a9bf;
+  --select-close-hover-color: var(--color-light-silver);
 
-  --select-input-color: var(--color-grey);
+  --select-input-color: var(--color-extra-light-silver);
   --select-multiple-input-color: #666;
   --select-input-focus-background: var(--color-primary);
   --select-input-font-size: 12px;
@@ -148,11 +157,11 @@
   --select-tag-background: var(--color-primary);
 
   --select-option-color: var(--link-color);
-  --select-option-disabled-color: var(--color-grey);
+  --select-option-disabled-color: var(--color-extra-light-silver);
   --select-option-height: 36px;
-  --select-option-hover-background: #e5e9f2;
+  --select-option-hover-background: var(--color-light-gray);
   --select-option-selected: var(--color-primary);
-  --select-option-selected-hover: #1D8CE0;
+  --select-option-selected-hover: shade(var(--color-primary), 0.12);
 
   --select-group-color: #999;
   --select-group-height: 30px;
@@ -203,8 +212,8 @@
   --message-min-width: 300px;
   --message-padding: 10px 12px;
   --message-content-color: var(--border-color-hover);
-  --message-close-color: var(--color-grey);
-  --message-close-hover-color: #99A9BF;
+  --message-close-color: var(--color-extra-light-silver);
+  --message-close-hover-color: var(--color-light-silver);
 
   --message-success-color: var(--color-success);
   --message-info-color: var(--color-info);
@@ -220,10 +229,10 @@
   --notification-font-size: var(--font-size-base);
   --notification-color: var(--border-color-hover);
   --notification-title-font-size: 16px;
-  --notification-title-color: #1f2d3d;
+  --notification-title-color: var(--color-base-black);
 
-  --notification-close-color: var(--color-grey);
-  --notification-close-hover-color: #99A9BF;
+  --notification-close-color: var(--color-extra-light-silver);
+  --notification-close-hover-color: var(--color-light-silver);
 
   --notification-success-color: var(--color-success);
   --notification-info-color: var(--color-info);
@@ -236,7 +245,6 @@
   --input-color: var(--font-color-base);
   --input-width: 140px;
   --input-height: 36px;
-  --input-shadow-hover: var(--shadow-base);
   --input-border: var(--border-base);
   --input-border-color: var(--border-color-base);
   --input-border-radius: var(--border-radius-base);
@@ -244,8 +252,8 @@
   --input-fill: var(--color-white);
   --input-fill-disabled: var(--disabled-fill-base);
   --input-color-disabled: var(--font-color-disabled-base);
-  --input-icon-color: var(--color-grey);
-  --input-placeholder-color: #99a9bf;
+  --input-icon-color: var(--color-extra-light-silver);
+  --input-placeholder-color: var(--color-light-silver);
   --input-max-width: 314px;
 
   --input-hover-border: var(--border-color-hover);
@@ -256,7 +264,7 @@
   --input-disabled-fill: var(--disabled-fill-base);
   --input-disabled-border: var(--disabled-border-base);
   --input-disabled-color: var(--disabled-color-base);
-  --input-disabled-placeholder-color: var(--color-grey);
+  --input-disabled-placeholder-color: var(--color-extra-light-silver);
 
   --input-large-font-size: 16px;
   --input-large-height: 42px;
@@ -276,8 +284,8 @@
   --cascader-menu-border-color: var(--border-color-base);
   --cascader-menu-border-width: var(--border-width-base);
   --cascader-menu-color: var(--font-color-base);
-  --cascader-menu-option-color-active: var(--color-blue);
-  --cascader-menu-option-fill-active: rgba(var(--color-blue), 0.12);
+  --cascader-menu-option-color-active: var(--color-secondary);
+  --cascader-menu-option-fill-active: rgba(var(--color-secondary), 0.12);
   --cascader-menu-option-color-hover: var(--font-color-base);
   --cascader-menu-option-fill-hover: rgba(var(--color-black), 0.06);
   --cascader-menu-option-color-disabled: #999;
@@ -301,15 +309,14 @@
   -------------------------- */
   --tab-font-size: var(--font-size-base);
   --tab-border-line: 1px solid #e4e4e4;
-  --tab-header-color-active: var(--color-blue);
+  --tab-header-color-active: var(--color-secondary);
   --tab-header-color-hover: var(--font-color-base);
   --tab-header-color: var(--font-color-base);
   --tab-header-fill-active: rgba(var(--color-black), 0.06);
   --tab-header-fill-hover: rgba(var(--color-black), 0.06);
   --tab-vertical-header-width: 90px;
   --tab-vertical-header-count-color: var(--color-white);
-  --tab-vertical-header-count-fill: var(--color-blue);
-  --tab-horizontal-border: 2px solid #438de0;
+  --tab-vertical-header-count-fill: var(--color-secondary);
 
   /* Button
   -------------------------- */
@@ -331,7 +338,7 @@
   --button-mini-padding-vertical: 4px;
   --button-mini-padding-horizontal: 4px;
 
-  --button-default-color: #1F2D3D;
+  --button-default-color: var(--color-base-black);
   --button-default-fill: var(--color-white);
   --button-default-border: #c4c4c4;
 
@@ -339,8 +346,8 @@
   --button-ghost-fill: transparent;
   --button-ghost-border: none;
 
-  --button-disabled-color: var(--color-grey);
-  --button-disabled-fill: #EFF2F7;
+  --button-disabled-color: var(--color-extra-light-silver);
+  --button-disabled-fill: var(--color-extra-light-gray);
   --button-disabled-border: var(--disabled-border-base);
 
   --button-primary-border: var(--color-primary);
@@ -374,9 +381,9 @@
   /* Switch
  -------------------------- */
   --switch-on-color: var(--color-primary);
-  --switch-off-color: var(--color-grey);
-  --switch-disabled-color: #E5E9F3;
-  --switch-disabled-text-color: #F9FAFC;
+  --switch-off-color: var(--color-extra-light-silver);
+  --switch-disabled-color: var(--color-light-gray);
+  --switch-disabled-text-color: var(--color-dark-white);
 
   --switch-font-size: var(--font-size-base);
   --switch-core-border-radius: 12px;
@@ -386,22 +393,21 @@
 
   /* Dialog
  -------------------------- */
-  --dialog-background-color: var(--color-blue);
-  --dialog-footer-background: var(--color-blue-lighter);
+  --dialog-background-color: var(--color-secondary);
   --dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
   --dialog-tiny-width: 30%;
   --dialog-small-width: 50%;
   --dialog-large-width: 90%;
-  --dialog-close-color: var(--color-grey);
+  --dialog-close-color: var(--color-extra-light-silver);
   --dialog-close-hover-color: var(--color-primary);
   --dialog-title-font-size: 16px;
   --dialog-font-size: 14px;
 
   /* Table
  -------------------------- */
-  --table-border-color: #e0e6ed;
-  --table-text-color: #1f2d3d;
-  --table-header-background: #EFF2F7;
+  --table-border-color: color(var(--border-color-base) h(-3) s(27%) l(90%));
+  --table-text-color: var(--color-base-black);
+  --table-header-background: var(--color-extra-light-gray);
 
   /* Pagination
  -------------------------- */
@@ -409,7 +415,7 @@
   --pagination-fill: var(--color-white);
   --pagination-color: var(--link-color);
   --pagination-border-radius: 2px;
-  --pagination-button-color: #99a9bf;
+  --pagination-button-color: var(--color-light-silver);
   --pagination-button-size: 28px;
   --pagination-button-disabled-color: #e4e4e4;
   --pagination-button-disabled-fill: var(--color-white);
@@ -425,14 +431,14 @@
   --popover-arrow-size: 6px;
   --popover-padding: 10px;
   --popover-title-font-size: 13px;
-  --popover-title-color: #1f2d3d;
+  --popover-title-color: var(--color-base-black);
 
   /* Tooltip
   -------------------------- */
-  --tooltip-fill: #1f2d3d;
+  --tooltip-fill: var(--color-base-black);
   --tooltip-color: var(--color-white);
   --tooltip-font-size: 12px;
-  --tooltip-border-color: #1f2d3d;
+  --tooltip-border-color: var(--color-base-black);
   --tooltip-arrow-size: 6px;
   --tooltip-padding: 10px;
 
@@ -445,8 +451,8 @@
   --tag-font-size: 12px;
   --tag-border-radius: 4px;
 
-  --tag-gray-fill: #e5e9f2;
-  --tag-gray-border: #e5e9f2;
+  --tag-gray-fill: var(--color-light-gray);
+  --tag-gray-border: var(--color-light-gray);
   --tag-gray-color: var(--link-color);
 
   --tag-primary-fill: rgba(32,159,255,0.10);
@@ -468,7 +474,7 @@
   /* Dropdown
   -------------------------- */
   --dropdown-menu-box-shadow: var(--box-shadow-dark);
-  --dropdown-menuItem-hover-fill: #e5e9f2;
+  --dropdown-menuItem-hover-fill: var(--color-light-gray);
   --dropdown-menuItem-hover-color: var(--link-color);
 
   /* Badge
@@ -488,10 +494,10 @@
   /* Slider
   --------------------------*/
   --slider-main-background-color: var(--color-primary);
-  --slider-runway-background-color: #e5e9f2;
-  --slider-button-hover-color: #1d8ce0;
-  --slider-stop-background-color: var(--color-grey);
-  --slider-disable-color: var(--color-grey);
+  --slider-runway-background-color: var(--color-light-gray);
+  --slider-button-hover-color: shade(var(--color-primary), 0.12);
+  --slider-stop-background-color: var(--color-extra-light-silver);
+  --slider-disable-color: var(--color-extra-light-silver);
 
   --slider-margin: 16px 0;
   --slider-border-radius: 3px;
@@ -509,14 +515,14 @@
   /* Steps
   --------------------------*/
   --menu-item-color: var(--link-color);
-  --menu-item-fill: #eff2f7;
+  --menu-item-fill: var(--color-extra-light-gray);
   --menu-item-hover-fill: var(--disabled-border-base);
-  --submenu-item-fill: #e5e9f2;
+  --submenu-item-fill: var(--color-light-gray);
 
   --dark-menu-item-color: var(--link-color);
-  --dark-menu-item-fill: #324057;
+  --dark-menu-item-fill: var(--color-light-black);
   --dark-menu-item-hover-fill: var(--link-color);
-  --dark-submenu-item-fill: #1f2d3d;
+  --dark-submenu-item-fill: var(--color-base-black);
 
   /* Rate
   --------------------------*/
@@ -524,19 +530,19 @@
   --rate-font-size: var(--font-size-base);
   --rate-icon-size: 18px;
   --rate-icon-margin: 6px;
-  --rate-icon-color: #C6D1DE;
+  --rate-icon-color: var(--color-extra-light-silver);
 
   /* DatePicker
   --------------------------*/
   --datepicker-color: var(--link-color);
   --datepicker-off-color: #ddd;
   --datepicker-header-color: var(--border-color-hover);
-  --datepicker-icon-color: #99a9bf;
+  --datepicker-icon-color: var(--color-light-silver);
   --datepicker-border-color: var(--disabled-border-base);
   --datepicker-inner-border-color: #e4e4e4;
-  --datepicker-cell-hover-color: #e5e9f2;
-  --datepicker-inrange-color: #D3ECFF;
-  --datepicker-inrange-hover-color: #AFDCFF;
+  --datepicker-cell-hover-color: var(--color-light-gray);
+  --datepicker-inrange-color: tint(var(--color-primary), 0.8);
+  --datepicker-inrange-hover-color: tint(var(--color-primary), 0.64);
   --datepicker-active-color: var(--color-primary);
   --datepicker-text-hover-color: var(--color-primary);
 
@@ -547,8 +553,8 @@
 
   /* Scrollbar
   --------------------------*/
-  --scrollbar-background-color: rgba(#99a9bf, .3);
-  --scrollbar-hover-background-color: rgba(#99a9bf, .5);
+  --scrollbar-background-color: rgba(var(--color-light-silver), .3);
+  --scrollbar-hover-background-color: rgba(var(--color-light-silver), .5);
 
   /* Carousel
   --------------------------*/
@@ -564,14 +570,14 @@
 
   /* Collapse
   --------------------------*/
-  --collapse-border-color: #e0e6ed;
+  --collapse-border-color: color(var(--border-color-base) h(-3) s(27%) l(90%));
   --collapse-header-height: 43px;
   --collapse-border-radius: 0;
   --collapse-header-padding: 20px;
-  --collapse-header-fill: #fff;
-  --collapse-header-color: #475669;
+  --collapse-header-fill: var(--color-white);
+  --collapse-header-color: var(--color-extra-light-black);
   --collapse-header-size: 13px;
-  --collapse-content-fill: #f9fafc;
+  --collapse-content-fill: var(--color-dark-white);
   --collapse-content-size: 13px;
-  --collapse-content-color: #1f2d3d;
+  --collapse-content-color: var(--color-base-black);
 }

+ 1 - 1
packages/theme-default/src/date-picker/month-table.css

@@ -31,7 +31,7 @@
 
       &.current .cell {
         background-color: var(--datepicker-active-color) !important;
-        color: #fff;
+        color: var(--color-white);
       }
     }
   }

+ 3 - 3
packages/theme-default/src/date-picker/picker-panel.css

@@ -5,7 +5,7 @@
     color: var(--datepicker-color);
     border: 1px solid var(--datepicker-border-color);
     box-shadow: 0 2px 6px #ccc;
-    background: #fff;
+    background: var(--color-white);
     border-radius: 2px;
     line-height: 20px;
     margin: 5px 0;
@@ -27,7 +27,7 @@
       border-top: 1px solid var(--datepicker-inner-border-color);
       padding: 4px;
       text-align: right;
-      background-color: #fff;
+      background-color: var(--color-white);
       position: relative;
     }
 
@@ -103,7 +103,7 @@
     border-right: 1px solid var(--datepicker-inner-border-color);
     box-sizing: border-box;
     padding-top: 6px;
-    background-color: #f9fafc;
+    background-color: var(--color-dark-white);
   }
 
   .el-picker-panel *[slot=sidebar] + .el-picker-panel__body,

+ 1 - 1
packages/theme-default/src/date-picker/time-picker.css

@@ -75,7 +75,7 @@
       background-color: transparent;
       outline: none;
       font-size: 12px;
-      color: #8492a6;
+      color: var(--color-base-silver);
 
       &.confirm {
         font-weight: 800;

+ 1 - 1
packages/theme-default/src/date-picker/year-table.css

@@ -35,7 +35,7 @@
 
       &.current .cell {
         background-color: var(--datepicker-active-color) !important;
-        color: #fff;
+        color: var(--color-white);
       }
     }
   }

+ 3 - 3
packages/theme-default/src/dialog.css

@@ -8,7 +8,7 @@
     position: absolute;
     left: 50%;
     transform: translateX(-50%);
-    background: #fff;
+    background: var(--color-white);
     border-radius: var(--border-radius-small);
     box-shadow: var(--dialog-box-shadow);
     box-sizing: border-box;
@@ -55,12 +55,12 @@
       line-height: 1;
       font-size: var(--dialog-title-font-size);
       font-weight: bold;
-      color: #1f2d3d;
+      color: var(--color-base-black);
     }
 
     @e body {
       padding: 30px 20px;
-      color: #475669;
+      color: var(--color-extra-light-black);
       font-size: var(--dialog-font-size);
     }
 

+ 6 - 6
packages/theme-default/src/dropdown.css

@@ -6,7 +6,7 @@
   @b dropdown {
     display: inline-block;
     position: relative;
-    color: #475669;
+    color: var(--color-extra-light-black);
     font-size: var(--font-size-base);
 
     .el-button-group {
@@ -27,8 +27,8 @@
   }
   @b dropdown-menu {
     margin: 5px 0;
-    background-color: #fff;
-    border: 1px solid #D3DCE6;
+    background-color: var(--color-white);
+    border: 1px solid var(--color-base-gray);
     box-shadow: var(--dropdown-menu-box-shadow);
     padding: 6px 0;
     z-index: 10;
@@ -51,19 +51,19 @@
       @m divided {
         position: relative;
         margin-top: 6px;
-        border-top: 1px solid #D3DCE6;
+        border-top: 1px solid var(--color-base-gray);
 
         &:before {
           content: '';
           height: 6px;
           display: block;
           margin: 0 -10px;
-          background-color: #fff;
+          background-color: var(--color-white);
         }
       }
       @when disabled {
         cursor: default;
-        color: #c0ccda;
+        color: var(--color-extra-light-silver);
         pointer-events: none;
       }
     }

+ 2 - 2
packages/theme-default/src/form.css

@@ -40,7 +40,7 @@
       vertical-align: middle;
       float: left;
       font-size: 14px;
-      color: #5e6d82;
+      color: var(--color-extra-light-black);
       line-height: 1;
       padding: 11px 12px 11px 0;
       box-sizing: border-box;
@@ -52,7 +52,7 @@
       @utils-clearfix;
     }
     @e error {
-      color: #ff4949;
+      color: var(--color-danger);
       font-size: 12px;
       line-height: 1;
       padding-top: 4px;

+ 1 - 1
packages/theme-default/src/input-number.css

@@ -20,7 +20,7 @@
       line-height: calc(var(--input-height) - 2);
       top: 1px;
       text-align: center;
-      color: #99A9BF;
+      color: var(--color-light-silver);
       cursor: pointer;
       position: absolute;
       z-index: 1;

+ 4 - 3
packages/theme-default/src/input.css

@@ -114,8 +114,8 @@
       display: table-cell;
     }
     @e append, prepend {
-      background-color: #f9fafc;
-      color: #99a9bf;
+      background-color: var(--color-dark-white);
+      color: var(--color-light-silver);
       vertical-align: middle;
       display: table-cell;
       position: relative;
@@ -173,6 +173,7 @@
   @b textarea {
     display: inline-block;
     width: 100%;
+    vertical-align: bottom;
 
     @e inner {
       display: block;
@@ -183,7 +184,7 @@
       width: 100%;
       font-size: var(--font-size-base);
       color: var(--input-color);
-      background-color: #fff;
+      background-color: var(--color-white);
       background-image: none;
       border: var(--input-border);
       border-radius: 4px;

+ 6 - 0
packages/theme-default/src/loading.css

@@ -11,6 +11,7 @@
     right: 0;
     bottom: 0;
     left: 0;
+    transition: opacity 0.3s;
 
     @when fullscreen {
       position: fixed;
@@ -54,6 +55,11 @@
   }
 }
 
+.el-loading-fade-enter,
+.el-loading-fade-leave-active {
+  opacity: 0;
+}
+
 @keyframes loading-rotate {
   100% {
     transform: rotate(360deg);

+ 20 - 15
packages/theme-default/src/menu.css

@@ -33,18 +33,18 @@
 
       & .el-menu-item,
       & .el-submenu__title {
-        color: #c0ccda;
+        color: var(--color-extra-light-silver);
 
         &:hover {
-          background-color: #475669;
+          background-color: var(--color-extra-light-black);
         }
       }
       
       & .el-submenu .el-menu {
-        background-color: #1f2f3d;
+        background-color: var(--color-base-black);
 
         & .el-menu-item:hover {
-          background-color: #475669;
+          background-color: var(--color-extra-light-black);
         }
       }
     }
@@ -59,6 +59,11 @@
         box-sizing: border-box;
         border-bottom: 5px solid transparent;
 
+        a,
+        a:hover {
+          color: inherit;
+        }
+
         &:hover {
           background-color: var(--menu-item-hover-fill);
         }
@@ -71,9 +76,9 @@
           position: absolute;
           top: 65px;
           left: 0;
-          border:1px solid #d3dce6;
+          border:1px solid var(--color-base-gray);
           padding: 5px 0;
-          background-color: #fff;
+          background-color: var(--color-white);
           z-index: 100;
           min-width: 100%;
           box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.12), 0px 0px 6px 0px rgba(0,0,0,0.04);
@@ -86,7 +91,7 @@
         }
 
         & .el-menu-item {
-          background-color: #fff;
+          background-color: var(--color-white);
           float: none;
           height: 36px;
           line-height: 36px;
@@ -97,7 +102,7 @@
           position: static;
           vertical-align: middle;
           margin-left: 5px;
-          color: #99a9bf;
+          color: var(--color-light-silver);
           margin-top: -3px;
         }
       }
@@ -120,10 +125,10 @@
         & .el-submenu {
           .el-menu-item,
           .el-submenu-title {
-            color: #475669;
+            color: var(--color-extra-light-black);
 
             &:hover {
-              background-color: #d3dce6;
+              background-color: var(--color-base-gray);
             }
           }
           .el-menu-item.is-active {
@@ -147,7 +152,7 @@
       margin-right: 0;
     }
     &:hover {
-      background-color: #d3dce6;
+      background-color: var(--color-base-gray);
     }
     @when active {
       color: var(--color-primary);
@@ -160,11 +165,11 @@
       @extend menu-item;
 
       &:hover {
-        background-color: #d3dce6;
+        background-color: var(--color-base-gray);
       }
     }
     & .el-menu {
-      background-color: #e5e9f2;
+      background-color: var(--color-light-gray);
     }
     & .el-menu-item {
       height: 50px;
@@ -172,7 +177,7 @@
       padding: 0 45px;
 
       &:hover {
-        background-color: #d3dce6;
+        background-color: var(--color-base-gray);
       }
     }
     @e icon-arrow {
@@ -208,7 +213,7 @@
       line-height: normal;
       font-size: 14px;
       padding-left: 20px;
-      color: #99a9bf;
+      color: var(--color-light-silver);
     }
   }
 }

+ 1 - 1
packages/theme-default/src/message-box.css

@@ -10,7 +10,7 @@
     text-align: left;
     display: inline-block;
     vertical-align: middle;
-    background-color: #fff;
+    background-color: var(--color-white);
     width: var(--msgbox-width);
     border-radius: var(--msgbox-border-radius);
     font-size: var(--msgbox-font-size);

+ 1 - 1
packages/theme-default/src/message.css

@@ -13,7 +13,7 @@
     left: 50%;
     top: 20px;
     transform: translateX(-50%);
-    background-color: #fff;
+    background-color: var(--color-white);
     transition: opacity 0.3s, transform .4s;
     overflow: hidden;
 

+ 2 - 2
packages/theme-default/src/mixins/_button.css

@@ -30,13 +30,13 @@
 
     &:hover,
     &:focus {
-      background: #fff;
+      background: var(--color-white);
       border-color: $border-color;
       color: $background-color;
     }
     
     &:active {
-      background: #fff;
+      background: var(--color-white);
       border-color: shade($border-color, var(--button-active-shade-percent));
       color: shade($background-color, var(--button-active-shade-percent));
       outline: none;

+ 14 - 12
packages/theme-default/src/notification.css

@@ -10,7 +10,7 @@
     border-radius: var(--border-radius-small);
     position: fixed;
     right: 16px;
-    background-color: #fff;
+    background-color: var(--color-white);
     box-shadow: var(--notification-shadow);
     transition: opacity 0.3s, transform .3s, right .3s, top 0.4s;
     overflow: hidden;
@@ -20,18 +20,20 @@
       @when with-icon {
         margin-left: 55px;
       }
-      & span {
-        font-size: var(--notification-title-font-size);
-        color: var(--notification-title-color);
-      }
+    }
 
-      & p {
-        font-size: var(--notification-font-size);
-        line-height: 21px;
-        margin: 10px 0 0 0;
-        color: var(--notification-color);
-        text-align: justify;
-      }
+    @e title {
+      font-weight: normal;
+      font-size: var(--notification-title-font-size);
+      color: var(--notification-title-color);
+    }
+
+    @e content {
+      font-size: var(--notification-font-size);
+      line-height: 21px;
+      margin: 10px 0 0 0;
+      color: var(--notification-color);
+      text-align: justify;
     }
 
     @e icon {

+ 2 - 0
packages/theme-default/src/pagination.css

@@ -152,6 +152,7 @@
     vertical-align: top;
     font-size: 0;
     padding: 0;
+    margin: 0;
 
     li {
       padding: 0 4px;
@@ -167,6 +168,7 @@
       cursor: pointer;
       box-sizing: border-box;
       text-align: center;
+      margin: 0;
 
       &:last-child {
         border-right: 1px solid var(--pagination-border-color);

+ 3 - 3
packages/theme-default/src/progress.css

@@ -8,7 +8,7 @@
 
     @e text {
       font-size:14px;
-      color:#475669;
+      color:var(--color-extra-light-black);
       display: inline-block;
       vertical-align: middle;
       margin-left: 10px;
@@ -81,7 +81,7 @@
     @e outer {
       height: 6px;
       border-radius: 100px;
-      background-color: #e5e9f2;
+      background-color: var(--color-light-gray);
       overflow: hidden;
       position: relative;
       vertical-align: middle;
@@ -102,7 +102,7 @@
     @e innerText {
       display: inline-block;
       vertical-align: middle;
-      color: #fff;
+      color: var(--color-white);
       font-size: 12px;
       margin: 0 5px;
     }

部分文件因文件數量過多而無法顯示