iview/components/select/select.vue
梁灏 6932b4d73c update Page component
change Page select size in mini type
2016-10-25 09:10:02 +08:00

511 lines
No EOL
18 KiB
Vue

<template>
<div :class="classes" v-clickoutside="handleClose">
<div
:class="[`${prefixCls}-selection`]"
v-el:reference
@click="toggleMenu">
<div class="ivu-tag" v-for="item in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.stop="removeTag($index)"></Icon>
</div>
<span :class="[`${prefixCls}-placeholder`]" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
<span :class="[`${prefixCls}-selected-value`]" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
<input
type="text"
v-if="filterable"
v-model="query"
:class="[`${prefixCls}-input`]"
:placeholder="showPlaceholder ? placeholder : ''"
:style="inputStyle"
@blur="handleBlur"
@keydown="resetInputState"
@keydown.delete="handleInputDelete"
v-el:input>
<Icon type="ios-close" :class="[`${prefixCls}-arrow`]" v-show="showCloseIcon" @click.stop="clearSingleSelect"></Icon>
<Icon type="arrow-down-b" :class="[`${prefixCls}-arrow`]"></Icon>
</div>
<Dropdown v-show="visible" transition="slide-up" v-ref:dropdown>
<ul v-show="notFound" :class="[`${prefixCls}-not-found`]"><li>{{ notFoundText }}</li></ul>
<ul v-else :class="[`${prefixCls}-dropdown-list`]"><slot></slot></ul>
</Dropdown>
</div>
</template>
<script>
import Icon from '../icon';
import Dropdown from './dropdown.vue';
import clickoutside from '../../directives/clickoutside';
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-select';
export default {
components: { Icon, Dropdown },
directives: { clickoutside },
props: {
model: {
type: [String, Number, Array],
default: ''
},
multiple: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择'
},
filterable: {
type: Boolean,
default: false
},
filterMethod: {
type: Function
},
size: {
validator (value) {
return oneOf(value, ['small', 'large', 'default']);
}
},
labelInValue: {
type: Boolean,
default: false
},
notFoundText: {
type: String,
default: '无匹配数据'
}
},
data () {
return {
prefixCls: prefixCls,
visible: false,
options: [],
optionInstances: [],
selectedSingle: '', // label
selectedMultiple: [],
focusIndex: 0,
query: '',
inputLength: 20,
notFound: false
}
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-visible`]: this.visible,
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-multiple`]: this.multiple,
[`${prefixCls}-single`]: !this.multiple,
[`${prefixCls}-show-clear`]: this.showCloseIcon,
[`${prefixCls}-${this.size}`]: !!this.size
}
]
},
showPlaceholder () {
let status = false;
if ((typeof this.model) === 'string') {
if (this.model === '') {
status = true;
}
} else if (Array.isArray(this.model)) {
if (!this.model.length) {
status = true;
}
}
return status;
},
showCloseIcon () {
return !this.multiple && this.clearable && !this.showPlaceholder;
},
inputStyle () {
let style = {};
if (this.multiple) {
if (this.showPlaceholder) {
style.width = '100%';
} else {
style.width = `${this.inputLength}px`;
}
}
return style;
}
},
methods: {
toggleMenu () {
if (this.disabled) {
return false;
}
this.visible = !this.visible;
},
hideMenu () {
this.visible = false;
this.focusIndex = 0;
this.$broadcast('on-select-close');
},
// find option component
findChild (cb) {
const find = function (child) {
const name = child.$options.componentName;
if (name) {
cb(child);
} else if (child.$children.length) {
child.$children.forEach((innerChild) => {
find(innerChild, cb);
});
}
};
if (this.optionInstances.length) {
this.optionInstances.forEach((child) => {
find(child);
})
} else {
this.$children.forEach((child) => {
find(child);
});
}
},
updateOptions (init) {
let options = [];
let index = 1;
this.findChild((child) => {
options.push({
value: child.value,
label: (child.label === undefined) ? child.$el.innerHTML : child.label
});
child.index = index++;
if (init) {
this.optionInstances.push(child);
}
});
this.options = options;
if (init) {
this.updateSingleSelected(true);
this.updateMultipleSelected(true);
}
},
updateSingleSelected (init = false) {
const type = typeof this.model;
if (type === 'string' || type === 'number') {
for (let i = 0; i < this.options.length; i++) {
if (this.model === this.options[i].value) {
this.selectedSingle = this.options[i].label;
break;
}
}
}
this.toggleSingleSelected(this.model, init);
},
clearSingleSelect () {
if (this.showCloseIcon) {
this.findChild((child) => {
child.selected = false;
});
this.model = '';
if (this.filterable) {
this.query = '';
}
}
},
updateMultipleSelected (init = false) {
if (this.multiple && Array.isArray(this.model)) {
let selected = [];
for (let i = 0; i < this.model.length; i++) {
const model = this.model[i];
for (let j = 0; j < this.options.length; j++) {
const option = this.options[j];
if (model === option.value) {
selected.push({
value: option.value,
label: option.label
})
}
}
}
this.selectedMultiple = selected;
}
this.toggleMultipleSelected(this.model, init);
},
removeTag (index) {
if (this.disabled) {
return false;
}
this.model.splice(index, 1);
if (this.filterable && this.visible) {
this.$els.input.focus();
}
this.$broadcast('on-update-popper');
},
// to select option for single
toggleSingleSelected (value, init = false) {
if (!this.multiple) {
let label = '';
this.findChild((child) => {
if (child.value === value) {
child.selected = true;
label = (child.label === undefined) ? child.$el.innerHTML : child.label;
} else {
child.selected = false;
}
});
this.hideMenu();
if (!init) {
if (this.labelInValue) {
this.$emit('on-change', {
value: value,
label: label
});
} else {
this.$emit('on-change', value);
}
}
}
},
// to select option for multiple
toggleMultipleSelected (value, init = false) {
if (this.multiple) {
let hybridValue = [];
for (let i = 0; i < value.length; i++) {
hybridValue.push({
value: value[i]
});
}
this.findChild((child) => {
const index = value.indexOf(child.value);
if (index >= 0) {
child.selected = true;
hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
} else {
child.selected = false;
}
});
if (!init) {
if (this.labelInValue) {
this.$emit('on-change', hybridValue);
} else {
this.$emit('on-change', value);
}
}
}
},
handleClose () {
this.hideMenu();
},
handleKeydown (e) {
if (this.visible) {
const keyCode = e.keyCode;
// Esc slide-up
if (keyCode === 27) {
e.preventDefault();
this.hideMenu();
}
// next
if (keyCode === 40) {
e.preventDefault();
this.navigateOptions('next');
}
// prev
if (keyCode === 38) {
e.preventDefault();
this.navigateOptions('prev');
}
// enter
if (keyCode === 13) {
e.preventDefault();
this.findChild((child) => {
if (child.isFocus) {
child.select();
}
});
}
}
},
navigateOptions (direction) {
if (direction === 'next') {
const next = this.focusIndex + 1;
this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
} else if (direction === 'prev') {
const prev = this.focusIndex - 1;
this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
}
let child_status = {
disabled: false,
hidden: false
};
let find_deep = false; // can next find allowed
this.findChild((child) => {
if (child.index === this.focusIndex) {
child_status.disabled = child.disabled;
child_status.hidden = child.hidden;
if (!child.disabled && !child.hidden) {
child.isFocus = true;
}
} else {
child.isFocus = false;
}
if (!child.hidden && !child.disabled) {
find_deep = true;
}
});
this.resetScrollTop();
if ((child_status.disabled || child_status.hidden) && find_deep) {
this.navigateOptions(direction);
}
},
resetScrollTop () {
const index = this.focusIndex - 1;
let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;
if (bottomOverflowDistance > 0) {
this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
}
if (topOverflowDistance < 0) {
this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
}
},
handleBlur () {
setTimeout(() => {
const model = this.model;
if (this.multiple) {
} else {
if (model !== '') {
this.findChild((child) => {
if (child.value === model) {
this.query = child.searchLabel;
}
});
}
}
}, 300);
},
resetInputState () {
this.inputLength = this.$els.input.value.length * 12 + 20;
},
handleInputDelete () {
if (this.multiple && this.model.length && this.query === '') {
this.removeTag(this.model.length - 1);
}
}
},
ready () {
this.updateOptions(true);
document.addEventListener('keydown', this.handleKeydown);
},
beforeDestroy () {
document.removeEventListener('keydown', this.handleKeydown);
},
watch: {
model () {
if (this.multiple) {
this.updateMultipleSelected();
} else {
this.updateSingleSelected();
}
},
visible (val) {
if (val) {
if (this.multiple && this.filterable) {
this.$els.input.focus();
}
this.$broadcast('on-update-popper');
} else {
if (this.filterable) {
this.$els.input.blur();
}
this.$broadcast('on-destroy-popper');
}
},
query (val) {
this.$broadcast('on-query-change', val);
let is_hidden = true;
this.$nextTick(() => {
this.findChild((child) => {
if (!child.hidden) {
is_hidden = false;
}
});
this.notFound = is_hidden;
});
}
},
events: {
'on-select-selected' (value) {
if (this.model === value) {
this.hideMenu();
} else {
if (this.multiple) {
const index = this.model.indexOf(value);
if (index >= 0) {
this.removeTag(index);
} else {
this.model.push(value);
this.$broadcast('on-update-popper');
}
if (this.filterable) {
this.query = '';
this.$els.input.focus();
}
} else {
this.model = value;
if (this.filterable) {
this.findChild((child) => {
if (child.value === value) {
this.query = child.searchLabel;
}
});
}
}
}
}
}
}
</script>