add AutoComplete component

This commit is contained in:
梁灏 2017-08-23 14:42:54 +08:00
parent 1183836a92
commit fed3e09d15
11 changed files with 255 additions and 31 deletions

View file

@ -56,6 +56,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
<li><router-link to="/notice">Notice</router-link></li>
<li><router-link to="/avatar">Avatar</router-link></li>
<li><router-link to="/color-picker">ColorPicker</router-link></li>
<li><router-link to="/auto-complete">AutoComplete</router-link></li>
</ul>
</nav>
<router-view></router-view>

View file

@ -188,6 +188,10 @@ const router = new VueRouter({
{
path: '/color-picker',
component: require('./routers/color-picker.vue')
},
{
path: '/auto-complete',
component: require('./routers/auto-complete.vue')
}
]
});

View file

@ -0,0 +1,40 @@
<template>
<div style="margin: 100px;width: 200px;">
<AutoComplete transfer v-model="value" :data="data" @on-change="hc" :filter-method="fm">
<!--<Option v-for="item in data" :value="item" :label="item" :key="item">-->
<!--<span style="color: red">{{ item }}</span>-->
<!--</Option>-->
</AutoComplete>
</div>
</template>
<script>
export default {
props: {
},
data () {
return {
value: '',
// data: [],
data: ['Burns Bay Road', 'Downing Street', 'Wall Street']
};
},
computed: {},
methods: {
handleSearch (value) {
this.data = !value ? [] : [
value + '@qq.com',
value + '@sina.com',
value + '@163.com'
]
},
hc (v) {
console.log(v)
},
fm (value, item) {
return item.toUpperCase().indexOf(value.toUpperCase()) !== -1;
}
}
};
</script>

View file

@ -0,0 +1,130 @@
<template>
<i-select
ref="select"
class="ivu-auto-complete"
:label="label"
:disabled="disabled"
:clearable="clearable"
:placeholder="placeholder"
:size="size"
filterable
remote
auto-complete
:remote-method="remoteMethod"
@on-change="handleChange"
:transfer="transfer">
<slot name="input">
<i-input
ref="input"
slot="input"
v-model="currentValue"
:placeholder="placeholder"
:disabled="disabled"
:size="size"
:icon="closeIcon"
@on-click="handleClear"
@on-focus="handleFocus"
@on-blur="handleBlur"></i-input>
</slot>
<slot>
<i-option v-for="item in filteredData" :value="item" :key="item">{{ item }}</i-option>
</slot>
</i-select>
</template>
<script>
import iSelect from '../select/select.vue';
import iOption from '../select/option.vue';
import iInput from '../input/input.vue';
import { oneOf } from '../../utils/assist';
export default {
name: 'AutoComplete',
components: { iSelect, iOption, iInput },
props: {
value: {
type: [String, Number],
default: ''
},
label: {
type: [String, Number],
default: ''
},
data: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: false
},
placeholder: {
type: String
},
size: {
validator (value) {
return oneOf(value, ['small', 'large', 'default']);
}
},
filterMethod: {
type: [Function, Boolean],
default: false
},
transfer: {
type: Boolean,
default: false
}
},
data () {
return {
currentValue: this.value
};
},
computed: {
closeIcon () {
return this.clearable && this.currentValue ? 'ios-close' : '';
},
filteredData () {
if (this.filterMethod) {
return this.data.filter(item => this.filterMethod(this.currentValue, item));
} else {
return this.data;
}
}
},
watch: {
value (val) {
this.currentValue = val;
},
currentValue (val) {
this.$refs.select.query = val;
this.$emit('input', val);
this.$emit('on-change', val);
}
},
methods: {
remoteMethod (query) {
this.$emit('on-search', query);
},
handleChange (val) {
this.currentValue = val;
this.$refs.select.model = val;
this.$refs.input.blur();
this.$emit('on-select', val);
},
handleFocus () {
this.$refs.select.visible = true;
},
handleBlur () {
this.$refs.select.visible = false;
},
handleClear () {
this.currentValue = '';
this.$refs.select.model = '';
}
}
};
</script>

View file

@ -0,0 +1,2 @@
import AutoComplete from './auto-complete.vue';
export default AutoComplete;

View file

@ -212,12 +212,19 @@
this.textareaStyles = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
},
focus() {
focus () {
if (this.type === 'textarea') {
this.$refs.textarea.focus();
} else {
this.$refs.input.focus();
}
},
blur () {
if (this.type === 'textarea') {
this.$refs.textarea.blur();
} else {
this.$refs.input.blur();
}
}
},
watch: {

View file

@ -3,6 +3,7 @@
</template>
<script>
import Emitter from '../../mixins/emitter';
import { findComponentUpward } from '../../utils/assist';
const prefixCls = 'ivu-select-item';
@ -29,7 +30,8 @@
index: 0, // for up and down to focus
isFocus: false,
hidden: false, // for search
searchLabel: '' // the value is slot,only for search
searchLabel: '', // the value is slot,only for search
autoComplete: false
};
},
computed: {
@ -38,7 +40,7 @@
`${prefixCls}`,
{
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-selected`]: this.selected,
[`${prefixCls}-selected`]: this.selected && !this.autoComplete,
[`${prefixCls}-focus`]: this.isFocus
}
];
@ -72,6 +74,9 @@
this.$on('on-query-change', (val) => {
this.queryChange(val);
});
const Select = findComponentUpward(this, 'iSelect');
if (Select) this.autoComplete = Select.autoComplete;
},
beforeDestroy () {
this.dispatch('iSelect', 'remove');

View file

@ -1,29 +1,31 @@
<template>
<div :class="classes" v-clickoutside="handleClose">
<div
:class="[prefixCls + '-selection']"
:class="selectionCls"
ref="reference"
@click="toggleMenu">
<div class="ivu-tag" v-for="(item, index) in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
</div>
<span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ localePlaceholder }}</span>
<span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
<input
type="text"
v-if="filterable"
v-model="query"
:disabled="disabled"
:class="[prefixCls + '-input']"
:placeholder="showPlaceholder ? localePlaceholder : ''"
:style="inputStyle"
@blur="handleBlur"
@keydown="resetInputState"
@keydown.delete="handleInputDelete"
ref="input">
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
<slot name="input">
<div class="ivu-tag" v-for="(item, index) in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
</div>
<span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ localePlaceholder }}</span>
<span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
<input
type="text"
v-if="filterable"
v-model="query"
:disabled="disabled"
:class="[prefixCls + '-input']"
:placeholder="showPlaceholder ? localePlaceholder : ''"
:style="inputStyle"
@blur="handleBlur"
@keydown="resetInputState"
@keydown.delete="handleInputDelete"
ref="input">
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
</slot>
</div>
<transition :name="transitionName">
<Drop
@ -33,7 +35,7 @@
ref="dropdown"
:data-transfer="transfer"
v-transfer-dom>
<ul v-show="notFountShow" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
<ul v-show="notFoundShow" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
<ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)" :class="[prefixCls + '-dropdown-list']"><slot></slot></ul>
<ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
</Drop>
@ -123,6 +125,11 @@
transfer: {
type: Boolean,
default: false
},
// Use for AutoComplete
autoComplete: {
type: Boolean,
default: false
}
},
data () {
@ -161,7 +168,13 @@
dropdownCls () {
return {
[prefixCls + '-dropdown-transfer']: this.transfer,
[prefixCls + '-multiple']: this.multiple && this.transfer
[prefixCls + '-multiple']: this.multiple && this.transfer,
['ivu-auto-complete']: this.autoComplete,
};
},
selectionCls () {
return {
[`${prefixCls}-selection`]: !this.autoComplete
};
},
showPlaceholder () {
@ -225,16 +238,19 @@
let status = true;
const options = this.$slots.default || [];
if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
if (this.autoComplete && !options.length) status = false;
return this.visible && status;
},
notFountShow () {
notFoundShow () {
const options = this.$slots.default || [];
return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
}
},
methods: {
toggleMenu () {
if (this.disabled) {
if (this.disabled || this.autoComplete) {
return false;
}
this.visible = !this.visible;
@ -543,6 +559,7 @@
},
handleBlur () {
setTimeout(() => {
if (this.autoComplete) return;
const model = this.model;
if (this.multiple) {
@ -737,7 +754,7 @@
if (this.multiple) {
this.$refs.input.focus();
} else {
this.$refs.input.select();
if (!this.autoComplete) this.$refs.input.select();
}
if (this.remote) {
this.findChild(child => {
@ -753,7 +770,7 @@
this.broadcast('Drop', 'on-update-popper');
} else {
if (this.filterable) {
this.$refs.input.blur();
if (!this.autoComplete) this.$refs.input.blur();
// #566 reset options visible
setTimeout(() => {
this.broadcastQuery('');

View file

@ -3,6 +3,7 @@ import 'core-js/fn/array/find-index';
import Affix from './components/affix';
import Alert from './components/alert';
import AutoComplete from './components/auto-complete';
import Avatar from './components/avatar';
import BackTop from './components/back-top';
import Badge from './components/badge';
@ -51,6 +52,7 @@ import locale from './locale';
const iview = {
Affix,
Alert,
AutoComplete,
Avatar,
BackTop,
Badge,

View file

@ -0,0 +1,15 @@
@auto-complete-prefix-cls: ~"@{css-prefix}auto-complete";
.@{auto-complete-prefix-cls} {
.@{select-prefix-cls} {
&-not-found{
display: none;
}
}
.@{icon-prefix-cls}-ios-close{
display: none;
}
&:hover .@{icon-prefix-cls}-ios-close{
display: inline-block;
}
}

View file

@ -40,4 +40,5 @@
@import "upload";
@import "tree";
@import "avatar";
@import "color-picker";
@import "color-picker";
@import "auto-complete";