Commit 930b85838edabe95256b5323d05297015d9ce6a7

Authored by Aresn
Committed by GitHub
2 parents acbd8b17 5266c905

Merge pull request #3579 from SergioCrisostomo/refactor-select

more select improvements
examples/routers/select.vue
@@ -671,16 +671,16 @@ @@ -671,16 +671,16 @@
671 671
672 <template> 672 <template>
673 <div> 673 <div>
674 - <Select v-model="model1" size="small" style="width:200px;"> 674 + <Select v-model="model1" size="small" style="width:200px;" >
675 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option> 675 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
676 </Select> 676 </Select>
677 - <Select v-model="model10" size="small" multiple style="width:260px"> 677 + <Select v-model="model10" size="small" multiple style="width:260px" >
678 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option> 678 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
679 </Select> 679 </Select>
680 680
681 <br><br> 681 <br><br>
682 682
683 - <Select v-model="model1" size="large" style="width:200px"> 683 + <Select v-model="model1" size="large" style="width:200px" clearable @on-clear="onClear">
684 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option> 684 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
685 </Select> 685 </Select>
686 <Select v-model="model10" size="large" multiple style="width:260px"> 686 <Select v-model="model10" size="large" multiple style="width:260px">
@@ -698,11 +698,11 @@ @@ -698,11 +698,11 @@
698 <Select v-model="model10" multiple style="width:260px"> 698 <Select v-model="model10" multiple style="width:260px">
699 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option> 699 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
700 </Select> 700 </Select>
701 - 701 +
702 <br><br> 702 <br><br>
703 - 703 +
704 <br><br> 704 <br><br>
705 - 705 +
706 <br><br> 706 <br><br>
707 <br><br> 707 <br><br>
708 <br><br> 708 <br><br>
@@ -713,9 +713,9 @@ @@ -713,9 +713,9 @@
713 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option> 713 <Option v-for="item in cityList" :value="item.value" :key="item.value">{{ item.label }}</Option>
714 </Select> 714 </Select>
715 <br><br> 715 <br><br>
716 - 716 +
717 <br><br> 717 <br><br>
718 - 718 +
719 <br><br> 719 <br><br>
720 <br><br> 720 <br><br>
721 <br><br> 721 <br><br>
@@ -761,6 +761,11 @@ @@ -761,6 +761,11 @@
761 model10: [], 761 model10: [],
762 model11: [] 762 model11: []
763 } 763 }
  764 + },
  765 + methods: {
  766 + onClear(){
  767 + console.log('onClear');
  768 + }
764 } 769 }
765 } 770 }
766 </script> 771 </script>
src/components/select/select-head.vue
@@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
25 @blur="onInputFocus" 25 @blur="onInputFocus"
26 26
27 ref="input"> 27 ref="input">
28 - <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-if="resetSelect" @click.native.stop="resetSelect"></Icon> 28 + <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-if="resetSelect" @click.native.stop="onClear"></Icon>
29 <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!resetSelect && !remote && !disabled"></Icon> 29 <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!resetSelect && !remote && !disabled"></Icon>
30 </div> 30 </div>
31 </template> 31 </template>
@@ -165,6 +165,9 @@ @@ -165,6 +165,9 @@
165 if (this.filterable && e.target === this.$el){ 165 if (this.filterable && e.target === this.$el){
166 this.$refs.input.focus(); 166 this.$refs.input.focus();
167 } 167 }
  168 + },
  169 + onClear(){
  170 + this.$emit('on-clear');
168 } 171 }
169 }, 172 },
170 watch: { 173 watch: {
@@ -179,6 +182,7 @@ @@ -179,6 +182,7 @@
179 // #982 182 // #982
180 if (typeof value === 'undefined' || value === '' || value === null) this.query = ''; 183 if (typeof value === 'undefined' || value === '' || value === null) this.query = '';
181 else this.query = value.label; 184 else this.query = value.label;
  185 + this.$nextTick(() => this.preventRemoteCall = false); // this should be after the query change setter above
182 }, 186 },
183 query (val) { 187 query (val) {
184 if (this.preventRemoteCall) { 188 if (this.preventRemoteCall) {
src/components/select/select.vue
@@ -42,6 +42,7 @@ @@ -42,6 +42,7 @@
42 @on-query-change="onQueryChange" 42 @on-query-change="onQueryChange"
43 @on-input-focus="isFocused = true" 43 @on-input-focus="isFocused = true"
44 @on-input-blur="isFocused = false" 44 @on-input-blur="isFocused = false"
  45 + @on-clear="clearSingleSelect"
45 /> 46 />
46 </slot> 47 </slot>
47 </div> 48 </div>
@@ -121,6 +122,8 @@ @@ -121,6 +122,8 @@
121 }; 122 };
122 }; 123 };
123 124
  125 + const ANIMATION_TIMEOUT = 300;
  126 +
124 export default { 127 export default {
125 name: 'iSelect', 128 name: 'iSelect',
126 mixins: [ Emitter, Locale ], 129 mixins: [ Emitter, Locale ],
@@ -229,6 +232,7 @@ @@ -229,6 +232,7 @@
229 slotOptions: this.$slots.default, 232 slotOptions: this.$slots.default,
230 caretPosition: -1, 233 caretPosition: -1,
231 lastRemoteQuery: '', 234 lastRemoteQuery: '',
  235 + unchangedQuery: true,
232 hasExpectedValue: false, 236 hasExpectedValue: false,
233 preventRemoteCall: false, 237 preventRemoteCall: false,
234 }; 238 };
@@ -260,6 +264,12 @@ @@ -260,6 +264,12 @@
260 [`${prefixCls}-selection-focused`]: this.isFocused 264 [`${prefixCls}-selection-focused`]: this.isFocused
261 }; 265 };
262 }, 266 },
  267 + queryStringMatchesSelectedOption(){
  268 + const selectedOptions = this.values[0];
  269 + if (!selectedOptions) return false;
  270 + const [query, label] = [this.query, selectedOptions.label].map(str => (str || '').trim());
  271 + return !this.multiple && this.unchangedQuery && query === label;
  272 + },
263 localeNotFoundText () { 273 localeNotFoundText () {
264 if (typeof this.notFoundText === 'undefined') { 274 if (typeof this.notFoundText === 'undefined') {
265 return this.t('i.select.noMatch'); 275 return this.t('i.select.noMatch');
@@ -382,6 +392,8 @@ @@ -382,6 +392,8 @@
382 } 392 }
383 }, 393 },
384 clearSingleSelect(){ // PUBLIC API 394 clearSingleSelect(){ // PUBLIC API
  395 + this.$emit('on-clear');
  396 + this.hideMenu();
385 if (this.clearable) this.values = []; 397 if (this.clearable) this.values = [];
386 }, 398 },
387 getOptionData(value){ 399 getOptionData(value){
@@ -423,18 +435,19 @@ @@ -423,18 +435,19 @@
423 }, 435 },
424 436
425 validateOption({elm, propsData}){ 437 validateOption({elm, propsData}){
  438 + if (this.queryStringMatchesSelectedOption) return true;
426 const value = propsData.value; 439 const value = propsData.value;
427 const label = propsData.label || ''; 440 const label = propsData.label || '';
428 const textContent = elm && elm.textContent || ''; 441 const textContent = elm && elm.textContent || '';
429 const stringValues = JSON.stringify([value, label, textContent]); 442 const stringValues = JSON.stringify([value, label, textContent]);
430 - return stringValues.toLowerCase().includes(this.query.toLowerCase()); 443 + const query = this.query.toLowerCase().trim();
  444 + return stringValues.toLowerCase().includes(query);
431 }, 445 },
432 446
433 toggleMenu (e, force) { 447 toggleMenu (e, force) {
434 if (this.disabled || this.autoComplete) { 448 if (this.disabled || this.autoComplete) {
435 return false; 449 return false;
436 } 450 }
437 - this.focusIndex = -1;  
438 451
439 this.visible = typeof force !== 'undefined' ? force : !this.visible; 452 this.visible = typeof force !== 'undefined' ? force : !this.visible;
440 if (this.visible){ 453 if (this.visible){
@@ -444,6 +457,7 @@ @@ -444,6 +457,7 @@
444 }, 457 },
445 hideMenu () { 458 hideMenu () {
446 this.toggleMenu(null, false); 459 this.toggleMenu(null, false);
  460 + setTimeout(() => this.unchangedQuery = true, ANIMATION_TIMEOUT);
447 }, 461 },
448 onClickOutside(event){ 462 onClickOutside(event){
449 if (this.visible) { 463 if (this.visible) {
@@ -467,6 +481,7 @@ @@ -467,6 +481,7 @@
467 } 481 }
468 }, 482 },
469 reset(){ 483 reset(){
  484 + this.unchangedQuery = true;
470 this.values = []; 485 this.values = [];
471 }, 486 },
472 handleKeydown (e) { 487 handleKeydown (e) {
@@ -551,11 +566,17 @@ @@ -551,11 +566,17 @@
551 566
552 this.isFocused = true; // so we put back focus after clicking with mouse on option elements 567 this.isFocused = true; // so we put back focus after clicking with mouse on option elements
553 } else { 568 } else {
  569 + this.query = String(option.label).trim();
554 this.values = [option]; 570 this.values = [option];
555 this.lastRemoteQuery = ''; 571 this.lastRemoteQuery = '';
556 this.hideMenu(); 572 this.hideMenu();
557 } 573 }
558 574
  575 + this.focusIndex = this.flatOptions.findIndex((opt) => {
  576 + if (!opt || !opt.componentOptions) return false;
  577 + return opt.componentOptions.propsData.value === option.value;
  578 + });
  579 +
559 if (this.filterable){ 580 if (this.filterable){
560 const inputField = this.$el.querySelector('input[type="text"]'); 581 const inputField = this.$el.querySelector('input[type="text"]');
561 if (!this.autoComplete) this.$nextTick(() => inputField.focus()); 582 if (!this.autoComplete) this.$nextTick(() => inputField.focus());
@@ -563,8 +584,9 @@ @@ -563,8 +584,9 @@
563 this.broadcast('Drop', 'on-update-popper'); 584 this.broadcast('Drop', 'on-update-popper');
564 }, 585 },
565 onQueryChange(query) { 586 onQueryChange(query) {
  587 + if (query.length > 0 && query !== this.query) this.visible = true;
566 this.query = query; 588 this.query = query;
567 - if (this.query.length > 0) this.visible = true; 589 + this.unchangedQuery = this.visible;
568 }, 590 },
569 toggleHeaderFocus({type}){ 591 toggleHeaderFocus({type}){
570 if (this.disabled) { 592 if (this.disabled) {
@@ -632,7 +654,7 @@ @@ -632,7 +654,7 @@
632 // restore query value in filterable single selects 654 // restore query value in filterable single selects
633 const [selectedOption] = this.values; 655 const [selectedOption] = this.values;
634 if (selectedOption && this.filterable && !this.multiple && !focused){ 656 if (selectedOption && this.filterable && !this.multiple && !focused){
635 - const selectedLabel = selectedOption.label || selectedOption.value; 657 + const selectedLabel = String(selectedOption.label || selectedOption.value).trim();
636 if (selectedLabel && this.query !== selectedLabel) { 658 if (selectedLabel && this.query !== selectedLabel) {
637 this.preventRemoteCall = true; 659 this.preventRemoteCall = true;
638 this.query = selectedLabel; 660 this.query = selectedLabel;
@@ -668,6 +690,9 @@ @@ -668,6 +690,9 @@
668 if (this.slotOptions && this.slotOptions.length === 0){ 690 if (this.slotOptions && this.slotOptions.length === 0){
669 this.query = ''; 691 this.query = '';
670 } 692 }
  693 + },
  694 + visible(state){
  695 + this.$emit('on-open-change', state);
671 } 696 }
672 } 697 }
673 }; 698 };