Refactor Select!

This commit is contained in:
Sergio Crisostomo 2018-03-27 09:58:27 +02:00
parent aaa96346f4
commit c9b86944ec
5 changed files with 647 additions and 672 deletions

View file

@ -0,0 +1,29 @@
<script>
const returnArrayFn = () => [];
export default {
props: {
options: {
type: Array,
default: returnArrayFn
},
slotOptions: {
type: Array,
default: returnArrayFn
},
slotUpdateHook: {
type: Function,
default: () => {}
},
},
functional: true,
render(h, {props, parent}){
// to detect changes in the $slot children/options we do this hack
// so we can trigger the parents computed properties and have everything reactive
// although $slot.default is not
if (props.slotOptions !== parent.$slots.default) props.slotUpdateHook();
return props.options;
}
};
</script>

View file

@ -1,5 +1,5 @@
<template>
<li :class="classes" @click.stop="select" @mouseout.stop="blur" v-show="!hidden"><slot>{{ showLabel }}</slot></li>
<li :class="classes" @click.stop="select"><slot>{{ showLabel }}</slot></li>
</template>
<script>
import Emitter from '../../mixins/emitter';
@ -22,15 +22,19 @@
disabled: {
type: Boolean,
default: false
},
selected: {
type: Boolean,
default: false
},
isFocused: {
type: Boolean,
default: false
}
},
data () {
return {
selected: false,
index: 0, // for up and down to focus
isFocus: false,
hidden: false, // for search
searchLabel: '', // the value is slot,only for search
searchLabel: '', // the slot value (textContent)
autoComplete: false
};
},
@ -41,53 +45,34 @@
{
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-selected`]: this.selected && !this.autoComplete,
[`${prefixCls}-focus`]: this.isFocus
[`${prefixCls}-focus`]: this.isFocused
}
];
},
showLabel () {
return (this.label) ? this.label : this.value;
},
optionLabel(){
return (this.$el && this.$el.textContent) || this.label;
}
},
methods: {
select () {
if (this.disabled) {
return false;
}
if (this.disabled) return false;
this.dispatch('iSelect', 'on-select-selected', this.value);
this.dispatch('iSelect', 'on-select-selected', {
value: this.value,
label: this.optionLabel,
});
this.$emit('on-select-selected', {
value: this.value,
label: this.optionLabel,
});
},
blur () {
this.isFocus = false;
},
queryChange (val) {
const parsedQuery = val.replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
this.hidden = !new RegExp(parsedQuery, 'i').test(this.searchLabel);
},
// 使 key SearchLabel #1865
updateSearchLabel () {
this.searchLabel = this.$el.textContent;
},
onSelectClose(){
this.isFocus = false;
},
onQueryChange(val){
this.queryChange(val);
}
},
mounted () {
this.updateSearchLabel();
this.dispatch('iSelect', 'append');
this.$on('on-select-close', this.onSelectClose);
this.$on('on-query-change',this.onQueryChange);
const Select = findComponentUpward(this, 'iSelect');
if (Select) this.autoComplete = Select.autoComplete;
},
beforeDestroy () {
this.dispatch('iSelect', 'remove');
this.$off('on-select-close', this.onSelectClose);
this.$off('on-query-change',this.onQueryChange);
}
};
</script>

View file

@ -0,0 +1,196 @@
<template>
<div @click="onHeaderClick">
<div class="ivu-tag ivu-tag-checked" v-for="item in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.native.stop="removeTag(item)"></Icon>
</div>
<span
:class="singleDisplayClasses"
v-show="singleDisplayValue"
>{{ singleDisplayValue }}</span>
<input
:id="inputElementId"
type="text"
v-if="filterable"
v-model="query"
:disabled="disabled"
:class="[prefixCls + '-input']"
:placeholder="showPlaceholder ? localePlaceholder : ''"
:style="inputStyle"
autocomplete="off"
spellcheck="false"
@keydown="resetInputState"
@keydown.delete="handleInputDelete"
@focus="onInputFocus"
@blur="onInputFocus"
ref="input">
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-if="resetSelect" @click.native.stop="resetSelect"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!resetSelect && !remote && !disabled"></Icon>
</div>
</template>
<script>
import Icon from '../icon';
import Emitter from '../../mixins/emitter';
import Locale from '../../mixins/locale';
const prefixCls = 'ivu-select';
export default {
name: 'iSelectHead',
mixins: [ Emitter, Locale ],
components: { Icon },
props: {
disabled: {
type: Boolean,
default: false
},
filterable: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: false
},
remote: {
type: Boolean,
default: false
},
initialLabel: {
type: String,
},
values: {
type: Array,
default: () => []
},
clearable: {
type: [Function, Boolean],
default: false,
},
inputElementId: {
type: String
},
placeholder: {
type: String
},
queryProp: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls,
query: '',
inputLength: 20,
remoteInitialLabel: this.initialLabel,
preventRemoteCall: false,
};
},
computed: {
singleDisplayClasses(){
const {filterable, multiple, showPlaceholder} = this;
return [{
[prefixCls + '-placeholder']: showPlaceholder && !filterable,
[prefixCls + '-selected-value']: !showPlaceholder && !multiple && !filterable,
}];
},
singleDisplayValue(){
if ((this.multiple && this.values.length > 0) || this.filterable) return '';
return `${this.selectedSingle}` || this.localePlaceholder;
},
showPlaceholder () {
let status = false;
if (!this.multiple) {
const value = this.values[0];
if (typeof value === 'undefined' || String(value).trim() === ''){
status = !this.remoteInitialLabel;
}
} else {
if (!this.values.length > 0) {
status = true;
}
}
return status;
},
resetSelect(){
return !this.showPlaceholder && this.clearable;
},
inputStyle () {
let style = {};
if (this.multiple) {
if (this.showPlaceholder) {
style.width = '100%';
} else {
style.width = `${this.inputLength}px`;
}
}
return style;
},
localePlaceholder () {
if (this.placeholder === undefined) {
return this.t('i.select.placeholder');
} else {
return this.placeholder;
}
},
selectedSingle(){
const selected = this.values[0];
return selected ? selected.label : (this.remoteInitialLabel || '');
},
selectedMultiple(){
return this.multiple ? this.values : [];
}
},
methods: {
onInputFocus(e){
this.$emit(e.type === 'focus' ? 'on-input-focus' : 'on-input-blur');
},
removeTag (value) {
if (this.disabled) return false;
this.dispatch('iSelect', 'on-select-selected', value);
},
resetInputState () {
this.inputLength = this.$refs.input.value.length * 12 + 20;
},
handleInputDelete () {
if (this.multiple && this.selectedMultiple.length && this.query === '') {
this.removeTag(this.selectedMultiple[this.selectedMultiple.length - 1]);
}
},
onHeaderClick(e){
if (this.filterable && e.target === this.$el){
this.$refs.input.focus();
}
}
},
watch: {
values ([value]) {
if (!this.filterable) return;
this.preventRemoteCall = true;
if (this.multiple){
this.query = '';
this.preventRemoteCall = false; // this should be after the query change setter above
return;
}
// #982
if (typeof value === 'undefined' || value === '' || value === null) this.query = '';
else this.query = value.label;
},
query (val) {
if (this.preventRemoteCall) {
this.preventRemoteCall = false;
return;
}
this.$emit('on-query-change', val);
},
queryProp(query){
if (query !== this.query) this.query = query;
},
}
};
</script>

File diff suppressed because it is too large Load diff

View file

@ -25,23 +25,14 @@
border: 1px solid @border-color-base;
transition: all @transition-time @ease-in-out;
.@{select-prefix-cls}-arrow:nth-of-type(1) {
display: none;
cursor: pointer;
}
&:hover {
&:hover, &-focused {
.hover();
.@{select-prefix-cls}-arrow:nth-of-type(1) {
.@{select-prefix-cls}-arrow {
display: inline-block;
}
}
}
&-show-clear &-selection:hover .@{select-prefix-cls}-arrow:nth-of-type(2){
display: none;
}
&-arrow {
.inner-arrow();
}
@ -51,14 +42,9 @@
.active();
}
.@{select-prefix-cls}-arrow:nth-of-type(2) {
.@{select-prefix-cls}-arrow {
transform: rotate(180deg);
}
}
&:focus{
outline: 0;
.@{select-prefix-cls}-selection{
.active();
display: inline-block;
}
}
@ -66,7 +52,7 @@
.@{select-prefix-cls}-selection {
.disabled();
.@{select-prefix-cls}-arrow:nth-of-type(1) {
.@{select-prefix-cls}-arrow {
display: none;
}
@ -74,7 +60,7 @@
border-color: @border-color-base;
box-shadow: none;
.@{select-prefix-cls}-arrow:nth-of-type(2) {
.@{select-prefix-cls}-arrow {
display: inline-block;
}
}