Merge pull request #54 from baiyaaaaa/next

fix autocomplete
pull/2/head
FuryBean 2016-08-12 10:06:28 +08:00 committed by GitHub
commit af71d7b708
5 changed files with 203 additions and 267 deletions

View File

@ -3,9 +3,7 @@
.el-autocomplete {
width: 180px;
}
.el-autocomplete__suggestions.my-autocomplete-suggestions {
width: 300px;
.my-suggestions-item {
& .remark {
float: right;
font-size: 13px;
@ -14,8 +12,28 @@
}
</style>
<script>
var $q = require('q');
var Vue = require('vue');
Vue.component('my-item', {
functional: true,
render: function (h, ctx) {
var item = ctx.props.item;
return h('li', {
attrs: { class: 'my-suggestions-item' }
}, [
h('span', { attrs: { class: 'label' } }, ['选项' + ctx.props.index]),
h('span', { attrs: { class: 'remark' } }, [item.display])
]);
},
props: {
item: {
type: Object,
required: true
},
index: {
type: Number
}
}
});
export default {
data() {
return {
@ -24,10 +42,7 @@
state2: '',
state3: '',
state4: '',
myPartial: {
name: 'my-autocomplete-suggestions',
template: '<span class="label">选项{{$index}}</span><span class="remark">{{item.display}}</span>'
}
timeout: null
}
},
methods: {
@ -52,32 +67,28 @@
return result;
},
querySearch(query, simulateQuery) {
querySearch(queryString, cb) {
var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states,
deferred;
var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
cb(results);
},
createStateFilter(query) {
querySearchAsync(queryString, cb) {
var states = this.states;
var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
cb(results);
}, 3000 * Math.random());
},
createStateFilter(queryString) {
return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0);
return (state.value.indexOf(queryString.toLowerCase()) === 0);
};
}
},
ready() {
mounted() {
this.states = this.loadAll();
}
};
@ -87,24 +98,25 @@
<div class="demo-box">
<el-autocomplete
:value.sync = "state1"
:suggestions = "querySearch(state1)"
placeholder = "请输入内容"
v-model="state1"
:fetch-suggestions="querySearch"
placeholder="请输入内容"
></el-autocomplete>
</div>
```html
<template>
<el-autocomplete
:value.sync = "state1"
:suggestions = "querySearch(state1)"
placeholder = "请输入内容"
v-model="state1"
:fetch-suggestions="querySearch"
placeholder="请输入内容"
></el-autocomplete>
</template>
<script>
export default {
data() {
return {
states: [],
state1: ''
}
},
@ -130,62 +142,71 @@
return result;
},
querySearch(query, simulateQuery) {
querySearch(queryString, callback) {
var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states,
deferred;
var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
callback(results);
},
createStateFilter(query) {
createStateFilter(queryString) {
return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0);
return (state.value.indexOf(queryString.toLowerCase()) === 0);
};
}
},
ready() {
mounted() {
this.states = this.loadAll();
}
};
</script>
```
## 通过键盘控制下拉的显示
## 自定义模板
<div class="demo-box">
<el-autocomplete
:value.sync = "state2"
:suggestions = "querySearch(state2)"
:show-on-up-down = "true"
placeholder = "请输入内容"
v-model="state2"
:fetch-suggestions="querySearch"
custom-item="my-item"
placeholder="请输入内容"
></el-autocomplete>
</div>
```html
<template>
<el-autocomplete
:value.sync = "state2"
:suggestions = "querySearch(state2)"
:show-on-up-down = "true"
placeholder = "请输入内容"
></el-autocomplete>
</template>
<el-autocomplete
v-model="state2"
:fetch-suggestions="querySearch"
custom-item="my-item"
placeholder="请输入内容"
></el-autocomplete>
<script>
var Vue = require('vue');
Vue.component('my-item', {
functional: true,
render: function (h, ctx) {
var item = ctx.props.item;
return h('li', {
attrs: { class: 'my-suggestions-item' }
}, [
h('span', { attrs: { class: 'label' } }, ['选项' + ctx.props.index]),
h('span', { attrs: { class: 'remark' } }, [item.display])
]);
},
props: {
item: {
type: Object,
required: true
},
index: {
type: Number
}
}
});
export default {
data() {
return {
states: [],
state2: ''
}
},
@ -211,116 +232,19 @@
return result;
},
querySearch(query, simulateQuery) {
querySearch(queryString, cb) {
var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states,
deferred;
var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
cb(results);
},
createStateFilter(query) {
createStateFilter(queryString) {
return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0);
return (state.value.indexOf(queryString.toLowerCase()) === 0);
};
}
},
ready() {
this.states = this.loadAll();
}
};
</script>
```
## 自定义模板
<div class="demo-box">
<el-autocomplete
:value.sync = "state3"
:suggestions = "querySearch(state3)"
:partial = "myPartial"
placeholder = "请输入内容"
></el-autocomplete>
</div>
```html
<el-autocomplete
:value.sync = "state3"
:suggestions = "querySearch(state3)"
:partial = "myPartial"
placeholder = "请输入内容"
></el-autocomplete>
<script>
export default {
data() {
return {
state3: '',
myPartial: {
name: 'my-autocomplete-suggestions',
template: '<span class="label">选项{{$index}}</span><span class="remark">{{item.display}}</span>'
}
}
},
methods: {
loadAll() {
var allStates = 'Alabama, Alaska, Arizona, Arkansas, California, Colorado, Connecticut, Delaware,\
Florida, Georgia, Hawaii, Idaho, Illinois, Indiana, Iowa, Kansas, Kentucky, Louisiana,\
Maine, Maryland, Massachusetts, Michigan, Minnesota, Mississippi, Missouri, Montana,\
Nebraska, Nevada, New Hampshire, New Jersey, New Mexico, New York, North Carolina,\
North Dakota, Ohio, Oklahoma, Oregon, Pennsylvania, Rhode Island, South Carolina,\
South Dakota, Tennessee, Texas, Utah, Vermont, Virginia, Washington, West Virginia,\
Wisconsin, Wyoming';
var result = [];
allStates.split(/, +/g).forEach((state) => {
if (state) {
result.push({
value: state.toLowerCase(),
display: state
});
}
});
return result;
},
querySearch(query, simulateQuery) {
var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states,
deferred;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
},
createStateFilter(query) {
return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0);
};
}
},
ready() {
mounted() {
this.states = this.loadAll();
}
};
@ -331,27 +255,26 @@
<div class="demo-box">
<el-autocomplete
:value.sync = "state4"
:suggestions = "querySearch(state4, true)"
:search-from-server = "true"
v-model="state3"
placeholder = "请输入内容"
:fetch-Suggestions="querySearchAsync"
></el-autocomplete>
</div>
```html
<el-autocomplete
:value.sync = "state4"
:suggestions = "querySearch(state4, true)"
:search-from-server = "true"
placeholder = "请输入内容"
></el-autocomplete>
<template>
<el-autocomplete
v-model="state3"
placeholder = "请输入内容"
:fetch-Suggestions="querySearchAsync"
></el-autocomplete>
</template>
<script>
var $q = require('q');
export default {
data() {
return {
state4: ''
state3: '',
states: []
}
},
methods: {
@ -376,24 +299,15 @@
return result;
},
querySearch(query, simulateQuery) {
querySearchAsync(query, callback) {
var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states,
deferred;
if (simulateQuery) {
if (!query) { return []; }
var results = query ? states.filter(this.createStateFilter(query)) : states;
deferred = $q.defer();
if (!query) { return []; }
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
setTimeout(() => {
callback(results);
}, 3000 * Math.random());
},
createStateFilter(query) {
return (state) => {
@ -413,7 +327,6 @@
|------------- |---------------- |---------------- |---------------------- |-------- |
| placeholder | 输入框占位文本 | string | | |
| disabled | 禁用 | boolean | true, false | false |
| suggestions | 建议列表 | array,object | | |
| value | 输入绑定值 | string | | |
| value | 必填值输入绑定值 | string | | |
| showOnUpDown | 是否通过键盘上下键控制建议列表 | boolean | | |
| partial | 建议列表的自定义模板 | object | | |
| fetch-suggestions | 返回输入建议的方法,组件内部通过调用该方法来获得输入建议的数据,在该方法中,仅当你的输入建议数据 resolve 时再通过调用 callback(data:[]) 来返回它 | Function(queryString, callback) | | |

View File

@ -32,7 +32,7 @@
"devDependencies": {
"babel-helper-vue-jsx-merge-props": "^1.0.1",
"babel-plugin-syntax-jsx": "^6.8.0",
"babel-plugin-transform-vue-jsx": "^1.1.1",
"babel-plugin-transform-vue-jsx": "^3.1.0",
"file-save": "^0.2.0",
"gh-pages": "^0.11.0",
"highlight.js": "^9.3.0",
@ -44,7 +44,7 @@
"purecss": "^0.6.0",
"q": "^1.4.1",
"uppercamelcase": "^1.1.0",
"vue": "^2.0.0-beta.5",
"vue": "^2.0.0-beta.8",
"vue-loader": "^9.2.3",
"vue-markdown-loader": "^0.4.0",
"vue-popup": "^0.2.1",

View File

@ -12,5 +12,6 @@
"author": "haiping.zeng<haiping.zeng@ele.me>",
"license": "MIT",
"dependencies": {
"vue-clickoutside": "^0.1.0"
}
}

View File

@ -1,36 +1,54 @@
<template>
<div class="el-autocomplete">
<div class="el-autocomplete" v-clickoutside="handleBlur">
<el-input
:value="value"
:disabled="disabled"
:placeholder="placeholder"
:name = 'name'
:name='name'
@onchange="handleChange"
@onfocus="handleFocus()"
@onblur="handleBlur()"
@keydown.up="highlight(highlightedIndex - 1)"
@keydown.down="highlight(highlightedIndex + 1)"
@keydown.enter="select(highlightedIndex)"
@onfocus="handleFocus"
@keydown.up.native="highlight(highlightedIndex - 1)"
@keydown.down.native="highlight(highlightedIndex + 1)"
@keydown.enter.native="select(highlightedIndex)"
></el-input>
<ul
v-show="showSuggestions && !loading && suggestions.length > 0"
class="el-autocomplete__suggestions"
:class="[partial ? partial.name : '']"
transition="md-fade-bottom"
v-el:suggestions
>
<li :class="{'highlighted': highlightedIndex === $index}" @click="select($index)" v-for="item in suggestions">{{item.display}}</li>
</ul>
<div
v-show="showSuggestions && loading"
class="el-autocomplete__suggestions is-loading"
>
<i class="el-icon-loading"></i>
</div>
<transition name="md-fade-bottom">
<ul
v-show="suggestionVisible && !loading && suggestions.length > 0"
class="el-autocomplete__suggestions"
ref="suggestions"
>
<li
v-if="!customItem"
:class="{'highlighted': highlightedIndex === index}"
@click="select(index)"
v-for="(item, index) in suggestions">
{{item.display}}
</li>
<component
v-else
:is="customItem"
@click.native="select(index)"
v-for="(item, index) in suggestions"
:item="item"
:index="index">
</component>
</ul>
</transition>
<transition name="md-fade-bottom">
<div
v-show="suggestionVisible && loading"
class="el-autocomplete__suggestions is-loading"
>
<i class="el-icon-loading"></i>
</div>
</transition>
</div>
</template>
<script>
import ElInput from 'packages/input/index.js';
import Vue from 'vue';
import VueClickoutside from 'main/utils/clickoutside';
Vue.use(VueClickoutside);
export default {
name: 'ElAutocomplete',
@ -42,61 +60,58 @@
placeholder: String,
disabled: Boolean,
name: String,
suggestions: [Array, Object],
value: String,
showOnUpDown: Boolean,
partial: Object
fetchSuggestions: Function,
triggerOnfocus: {
type: Boolean,
default: true
},
customItem: String
},
data() {
return {
showSuggestions: false,
suggestions: [],
suggestionVisible: false,
inputFocusing: false,
loading: false,
highlightedIndex: -1
};
},
created() {
if (this.partial) {
this.$options.template = this.$options.template.replace(/(item\sin\ssuggestions">)(?:.|\s)*?(<)/, '$1' + this.partial.template + '$2');
}
},
watch: {
'suggestions'(val) {
if (val && val.then) {
this.loading = true;
this.suggestions.then((res) => {
this.loading = false;
this.suggestions = res;
});
}
}
},
methods: {
handleChange(value) {
this.value = value;
this.showSuggestions = true;
this.$emit('input', value);
this.showSuggestions(value);
},
handleFocus() {
if (!this.showOnUpDown) {
this.showSuggestions = true;
if (this.triggerOnfocus) {
this.showSuggestions(this.value);
}
},
handleBlur() {
this.showSuggestions = false;
this.suggestionVisible = false;
},
select(index) {
debugger;
if (this.suggestions && this.suggestions[index]) {
this.value = this.suggestions[index].value;
this.$emit('input', this.suggestions[index].value);
this.$nextTick(() => {
this.showSuggestions = false;
this.suggestionVisible = false;
});
}
},
showSuggestions(value) {
this.suggestionVisible = true;
this.loading = true;
this.fetchSuggestions(value, (suggestions) => {
this.loading = false;
this.suggestions = suggestions;
});
},
getSuggestionElement(index) {
if (!this.suggestions || !this.suggestions[index]) {
return null;
} else {
return this.$els.suggestions.children[index];
return this.$refs.suggestions.children[index];
}
},
highlight(index) {
@ -107,7 +122,7 @@
}
var elSelect = this.getSuggestionElement(index);
var elSuggestions = this.$els.suggestions;
var elSuggestions = this.$refs.suggestions;
var scrollTop = elSuggestions.scrollTop;
var offsetTop = elSelect.offsetTop;
@ -121,7 +136,7 @@
this.highlightedIndex = index;
if (this.showOnUpDown) {
this.showSuggestions = true;
this.suggestionVisible = true;
}
}
}

View File

@ -28,28 +28,28 @@
transition: all .3s cubic-bezier(.55,0,.1,1);
}
.md-fade-center-enter,
.md-fade-center-leave {
opacity: 0;
transform: scaleY(0);
}
.md-fade-center-leave,
.md-fade-center-leave-active {
opacity: 0;
transform: scaleY(0);
}
.md-fade-bottom-transition {
.md-fade-bottom-enter-active,
.md-fade-bottom-leave-active {
opacity: 1;
transform: scaleY(1);
transition: var(--md-fade-transition);
transform-origin: center top;
}
.md-fade-bottom-enter,
.md-fade-bottom-leave {
.md-fade-bottom-leave,
.md-fade-bottom-leave-active {
opacity: 0;
transform: scaleY(0);
}
.md-fade-top-transition {
.md-fade-top-enter-active,
.md-fade-top-leave-active {
opacity: 1;
transform: scaleY(1);
transition: var(--md-fade-transition);
@ -57,40 +57,47 @@
}
.md-fade-top-enter,
.md-fade-top-leave {
.md-fade-top-leave,
.md-fade-top-leave-active {
opacity: 0;
transform: scaleY(0);
}
.md-fade-left-transition {
.md-fade-left-enter-active,
.md-fade-left-leave-active {
opacity: 1;
transform: scaleX(1);
transition: var(--md-fade-transition);
transform-origin: right center;
}
.md-fade-left-enter,
.md-fade-left-leave {
.md-fade-left-leave,
.md-fade-left-leave-active {
opacity: 0;
transform: scaleX(0);
}
.md-fade-right-transition {
.md-fade-right-enter-active,
.md-fade-right-leave-active {
opacity: 1;
transform: scaleX(1);
transition: var(--md-fade-transition);
transform-origin: left center;
}
.md-fade-right-enter,
.md-fade-right-leave {
.md-fade-right-leave,
.md-fade-right-leave-active {
opacity: 0;
transform: scaleX(0);
}
.fade-enter-active, .fade-leave-active {
.fade-enter-active,
.fade-leave-active {
transition: opacity .3s cubic-bezier(.645,.045,.355,1);
}
.fade-enter,
.fade-leave {
.fade-leave,
.fade-leave-active {
opacity: 0;
}