Browse Source

Select: remove resetIndex

Leopoldthecoder 8 years ago
parent
commit
f23629ac43

+ 54 - 0
packages/select/src/navigation-mixin.js

@@ -0,0 +1,54 @@
+export default {
+  data() {
+    return {
+      hoverOption: -1
+    };
+  },
+
+  computed: {
+    optionsAllDisabled() {
+      return this.options.length === this.options.filter(item => item.disabled === true).length;
+    }
+  },
+
+  watch: {
+    hoverIndex(val) {
+      if (typeof val === 'number' && val > -1) {
+        this.hoverOption = this.options[val] || {};
+      }
+      this.options.forEach(option => {
+        option.hover = this.hoverOption === option;
+      });
+    }
+  },
+
+  methods: {
+    navigateOptions(direction) {
+      if (!this.visible) {
+        this.visible = true;
+        return;
+      }
+      if (this.options.length === 0 || this.filteredOptionsCount === 0) return;
+      if (!this.optionsAllDisabled) {
+        if (direction === 'next') {
+          this.hoverIndex++;
+          if (this.hoverIndex === this.options.length) {
+            this.hoverIndex = 0;
+          }
+        } else if (direction === 'prev') {
+          this.hoverIndex--;
+          if (this.hoverIndex < 0) {
+            this.hoverIndex = this.options.length - 1;
+          }
+        }
+        const option = this.options[this.hoverIndex];
+        if (option.disabled === true ||
+          option.groupDisabled === true ||
+          !option.visible) {
+          this.navigateOptions(direction);
+        }
+      }
+      this.$nextTick(() => this.scrollToOption(this.hoverOption.$el));
+    }
+  }
+};

+ 22 - 34
packages/select/src/option.vue

@@ -7,7 +7,7 @@
     :class="{
       'selected': itemSelected,
       'is-disabled': disabled || groupDisabled || limitReached,
-      'hover': parent.hoverIndex === index
+      'hover': hover
     }">
     <slot>
       <span>{{ currentLabel }}</span>
@@ -26,6 +26,8 @@
 
     componentName: 'ElOption',
 
+    inject: ['select'],
+
     props: {
       value: {
         required: true
@@ -43,7 +45,8 @@
         index: -1,
         groupDisabled: false,
         visible: true,
-        hitState: false
+        hitState: false,
+        hover: false
       };
     },
 
@@ -60,27 +63,19 @@
         return this.value || this.label || '';
       },
 
-      parent() {
-        let result = this.$parent;
-        while (!result.isSelect) {
-          result = result.$parent;
-        }
-        return result;
-      },
-
       itemSelected() {
-        if (!this.parent.multiple) {
-          return this.isEqual(this.value, this.parent.value);
+        if (!this.select.multiple) {
+          return this.isEqual(this.value, this.select.value);
         } else {
-          return this.contains(this.parent.value, this.value);
+          return this.contains(this.select.value, this.value);
         }
       },
 
       limitReached() {
-        if (this.parent.multiple) {
+        if (this.select.multiple) {
           return !this.itemSelected &&
-            this.parent.value.length >= this.parent.multipleLimit &&
-            this.parent.multipleLimit > 0;
+            this.select.value.length >= this.select.multipleLimit &&
+            this.select.multipleLimit > 0;
         } else {
           return false;
         }
@@ -89,10 +84,10 @@
 
     watch: {
       currentLabel() {
-        if (!this.created && !this.parent.remote) this.dispatch('ElSelect', 'setSelected');
+        if (!this.created && !this.select.remote) this.dispatch('ElSelect', 'setSelected');
       },
       value() {
-        if (!this.created && !this.parent.remote) this.dispatch('ElSelect', 'setSelected');
+        if (!this.created && !this.select.remote) this.dispatch('ElSelect', 'setSelected');
       }
     },
 
@@ -101,7 +96,7 @@
         if (!this.isObject) {
           return a === b;
         } else {
-          const valueKey = this.parent.valueKey;
+          const valueKey = this.select.valueKey;
           return getValueByPath(a, valueKey) === getValueByPath(b, valueKey);
         }
       },
@@ -110,7 +105,7 @@
         if (!this.isObject) {
           return arr.indexOf(target) > -1;
         } else {
-          const valueKey = this.parent.valueKey;
+          const valueKey = this.select.valueKey;
           return arr.some(item => {
             return getValueByPath(item, valueKey) === getValueByPath(target, valueKey);
           });
@@ -123,7 +118,7 @@
 
       hoverItem() {
         if (!this.disabled && !this.groupDisabled) {
-          this.parent.hoverIndex = this.parent.options.indexOf(this);
+          this.select.hoverIndex = this.select.options.indexOf(this);
         }
       },
 
@@ -138,27 +133,20 @@
         let parsedQuery = String(query).replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
         this.visible = new RegExp(parsedQuery, 'i').test(this.currentLabel) || this.created;
         if (!this.visible) {
-          this.parent.filteredOptionsCount--;
+          this.select.filteredOptionsCount--;
         }
-      },
-
-      resetIndex() {
-        this.$nextTick(() => {
-          this.index = this.parent.options.indexOf(this);
-        });
       }
     },
 
     created() {
-      this.parent.options.push(this);
-      this.parent.cachedOptions.push(this);
-      this.parent.optionsCount++;
-      this.parent.filteredOptionsCount++;
-      this.index = this.parent.options.indexOf(this);
+      this.select.options.push(this);
+      this.select.cachedOptions.push(this);
+      this.select.optionsCount++;
+      this.select.filteredOptionsCount++;
+      this.index = this.select.options.indexOf(this);
 
       this.$on('queryChange', this.queryChange);
       this.$on('handleGroupDisabled', this.handleGroupDisabled);
-      this.$on('resetIndex', this.resetIndex);
     },
 
     beforeDestroy() {

+ 46 - 70
packages/select/src/select.vue

@@ -35,6 +35,7 @@
         @keydown.esc.stop.prevent="visible = false"
         @keydown.delete="deletePrevTag"
         v-model="query"
+        @input="e => handleQueryChange(e.target.value)"
         :debounce="remote ? 300 : 0"
         v-if="filterable"
         :style="{ width: inputLength + 'px', 'max-width': inputWidth - 42 + 'px' }"
@@ -50,6 +51,7 @@
       :disabled="disabled"
       :readonly="!filterable || multiple"
       :validate-event="false"
+      :class="{ 'is-focus': visible }"
       @focus="handleFocus"
       @blur="handleBlur"
       @click="handleIconClick"
@@ -107,6 +109,7 @@
   import { t } from 'element-ui/src/locale';
   import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
   import { getValueByPath } from 'element-ui/src/utils/util';
+  import NavigationMixin from './navigation-mixin';
 
   const sizeMap = {
     'large': 42,
@@ -127,12 +130,18 @@
   };
 
   export default {
-    mixins: [Emitter, Locale, Focus('reference')],
+    mixins: [Emitter, Locale, Focus('reference'), NavigationMixin],
 
     name: 'ElSelect',
 
     componentName: 'ElSelect',
 
+    provide() {
+      return {
+        'select': this
+      };
+    },
+
     computed: {
       iconClass() {
         let criteria = this.clearable &&
@@ -223,7 +232,6 @@
         createdLabel: null,
         createdSelected: false,
         selected: this.multiple ? [] : {},
-        isSelect: true,
         inputLength: 20,
         inputWidth: 0,
         cachedPlaceHolder: '',
@@ -233,7 +241,7 @@
         selectedLabel: '',
         hoverIndex: -1,
         query: '',
-        optionsAllDisabled: false,
+        previousQuery: '',
         inputHovering: false,
         currentPlaceholder: ''
       };
@@ -260,33 +268,6 @@
         this.dispatch('ElFormItem', 'el.form.change', val);
       },
 
-      query(val) {
-        this.$nextTick(() => {
-          if (this.visible) this.broadcast('ElSelectDropdown', 'updatePopper');
-        });
-        this.hoverIndex = -1;
-        if (this.multiple && this.filterable) {
-          this.inputLength = this.$refs.input.value.length * 15 + 20;
-          this.managePlaceholder();
-          this.resetInputHeight();
-        }
-        if (this.remote && typeof this.remoteMethod === 'function') {
-          this.hoverIndex = -1;
-          this.remoteMethod(val);
-          this.broadcast('ElOption', 'resetIndex');
-        } else if (typeof this.filterMethod === 'function') {
-          this.filterMethod(val);
-          this.broadcast('ElOptionGroup', 'queryChange');
-        } else {
-          this.filteredOptionsCount = this.optionsCount;
-          this.broadcast('ElOption', 'queryChange', val);
-          this.broadcast('ElOptionGroup', 'queryChange');
-        }
-        if (this.defaultFirstOption && (this.filterable || this.remote) && this.filteredOptionsCount) {
-          this.checkDefaultFirstOption();
-        }
-      },
-
       visible(val) {
         if (!val) {
           this.$refs.reference.$el.querySelector('input').blur();
@@ -321,7 +302,8 @@
           this.handleIconShow();
           this.broadcast('ElSelectDropdown', 'updatePopper');
           if (this.filterable) {
-            this.query = this.selectedLabel;
+            this.query = this.remote ? '' : this.selectedLabel;
+            this.handleQueryChange(this.query);
             if (this.multiple) {
               this.$refs.input.focus();
             } else {
@@ -336,9 +318,8 @@
         this.$emit('visible-change', val);
       },
 
-      options(val) {
+      options() {
         if (this.$isServer) return;
-        this.optionsAllDisabled = val.length === val.filter(item => item.disabled === true).length;
         if (this.multiple) {
           this.resetInputHeight();
         }
@@ -353,6 +334,35 @@
     },
 
     methods: {
+      handleQueryChange(val) {
+        if (this.previousQuery === val) return;
+        console.log(val);
+        this.previousQuery = val;
+        this.$nextTick(() => {
+          if (this.visible) this.broadcast('ElSelectDropdown', 'updatePopper');
+        });
+        this.hoverIndex = -1;
+        if (this.multiple && this.filterable) {
+          this.inputLength = this.$refs.input.value.length * 15 + 20;
+          this.managePlaceholder();
+          this.resetInputHeight();
+        }
+        if (this.remote && typeof this.remoteMethod === 'function') {
+          this.hoverIndex = -1;
+          this.remoteMethod(val);
+        } else if (typeof this.filterMethod === 'function') {
+          this.filterMethod(val);
+          this.broadcast('ElOptionGroup', 'queryChange');
+        } else {
+          this.filteredOptionsCount = this.optionsCount;
+          this.broadcast('ElOption', 'queryChange', val);
+          this.broadcast('ElOptionGroup', 'queryChange');
+        }
+        if (this.defaultFirstOption && (this.filterable || this.remote) && this.filteredOptionsCount) {
+          this.checkDefaultFirstOption();
+        }
+      },
+
       handleIconHide() {
         let icon = this.$el.querySelector('.el-input__icon');
         if (icon) {
@@ -464,7 +474,6 @@
 
       doDestroy() {
         this.$refs.popper && this.$refs.popper.doDestroy();
-        this.dropdownUl = null;
       },
 
       handleClose() {
@@ -545,6 +554,7 @@
           this.emitChange(value);
           if (option.created) {
             this.query = '';
+            this.handleQueryChange('');
             this.inputLength = 20;
           }
           if (this.filterable) this.$refs.input.focus();
@@ -583,40 +593,6 @@
         }
       },
 
-      navigateOptions(direction) {
-        if (!this.visible) {
-          this.visible = true;
-          return;
-        }
-        if (this.options.length === 0 || this.filteredOptionsCount === 0) return;
-        this.optionsAllDisabled = this.options.length === this.options.filter(item => item.disabled === true).length;
-        if (!this.optionsAllDisabled) {
-          if (direction === 'next') {
-            this.hoverIndex++;
-            if (this.hoverIndex === this.options.length) {
-              this.hoverIndex = 0;
-            }
-            if (this.options[this.hoverIndex].disabled === true ||
-              this.options[this.hoverIndex].groupDisabled === true ||
-              !this.options[this.hoverIndex].visible) {
-              this.navigateOptions('next');
-            }
-          }
-          if (direction === 'prev') {
-            this.hoverIndex--;
-            if (this.hoverIndex < 0) {
-              this.hoverIndex = this.options.length - 1;
-            }
-            if (this.options[this.hoverIndex].disabled === true ||
-              this.options[this.hoverIndex].groupDisabled === true ||
-              !this.options[this.hoverIndex].visible) {
-              this.navigateOptions('prev');
-            }
-          }
-        }
-        this.$nextTick(() => this.scrollToOption(this.options[this.hoverIndex]));
-      },
-
       selectOption() {
         if (this.options[this.hoverIndex]) {
           this.handleOptionSelect(this.options[this.hoverIndex]);
@@ -644,8 +620,9 @@
       },
 
       onInputChange() {
-        if (this.filterable) {
+        if (this.filterable && this.query !== this.selectedLabel) {
           this.query = this.selectedLabel;
+          this.handleQueryChange(this.query);
         }
       },
 
@@ -656,7 +633,6 @@
         if (index > -1) {
           this.options.splice(index, 1);
         }
-        this.broadcast('ElOption', 'resetIndex');
       },
 
       resetInputWidth() {

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

@@ -91,6 +91,10 @@
           color: var(--input-disabled-placeholder-color);
         }
       }
+
+      .el-input__icon {
+        cursor: not-allowed;
+      }
     }
     @m large {
       font-size: var(--input-large-font-size);

+ 4 - 0
packages/theme-default/src/select.css

@@ -67,6 +67,10 @@
           }
         }
       }
+
+      &.is-focus .el-input__inner {
+        border-color: var(--input-focus-border);
+      }
     }
 
     & > .el-input {

+ 9 - 18
test/unit/specs/select.spec.js

@@ -140,7 +140,6 @@ describe('Select', () => {
   });
 
   it('single select', done => {
-    sinon.stub(window.console, 'log');
     vm = createVue({
       template: `
         <div>
@@ -174,13 +173,14 @@ describe('Select', () => {
             value: '选项5',
             label: '北京烤鸭'
           }],
-          value: ''
+          value: '',
+          count: 0
         };
       },
 
       methods: {
         handleChange() {
-          console.log('changed');
+          this.count++;
         }
       }
     }, true);
@@ -190,12 +190,12 @@ describe('Select', () => {
     options[2].click();
     setTimeout(() => {
       expect(vm.value).to.equal('选项3');
-      expect(window.console.log.callCount).to.equal(1);
+      expect(vm.count).to.equal(1);
+      triggerEvent(options[2], 'mouseenter');
       options[4].click();
       setTimeout(() => {
         expect(vm.value).to.equal('选项5');
-        expect(window.console.log.callCount).to.equal(2);
-        window.console.log.restore();
+        expect(vm.count).to.equal(2);
         done();
       }, 100);
     }, 100);
@@ -460,7 +460,7 @@ describe('Select', () => {
             <el-option
               v-for="item in options"
               :label="item"
-              :key="item.value"
+              :key="item"
               :value="item"
             />
           </el-select>
@@ -471,14 +471,6 @@ describe('Select', () => {
           options: ['1', '2', '3', '4', '5'],
           value: ''
         };
-      },
-      methods: {
-        filterMethod(query) {
-          // simulate async filterMethod / remoteMethod
-          setTimeout(() => {
-            this.options.filter(option => option.label.indexOf(query) !== -1);
-          }, 5);
-        }
       }
     }, true);
 
@@ -486,7 +478,7 @@ describe('Select', () => {
     setTimeout(() => {
       select.$el.querySelector('input').focus();
       select.query = '3';
-      select.selectedLabel = '3';
+      select.handleQueryChange('3');
       setTimeout(() => {
         const enterKey = document.createEvent('Events');
         enterKey.initEvent('keydown', true, true);
@@ -635,10 +627,9 @@ describe('Select', () => {
     });
     const select = vm.$children[0];
     vm.$nextTick(() => {
-      select.query = '面';
+      select.handleQueryChange('面');
       setTimeout(() => {
         expect(select.filteredOptionsCount).to.equal(1);
-        select.query = '';
         select.options[0].$el.click();
         vm.$nextTick(() => {
           expect(vm.value[0]).to.equal('选项4');