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,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 | }; |