Commit 930b85838edabe95256b5323d05297015d9ce6a7
Committed by
GitHub
Merge pull request #3579 from SergioCrisostomo/refactor-select
more select improvements
Showing
3 changed files
with
47 additions
and
13 deletions
Show diff stats
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 | }; | ... | ... |