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