Commit af71d7b7 authored by FuryBean's avatar FuryBean Committed by GitHub

Merge pull request #54 from baiyaaaaa/next

fix autocomplete
parents d1319b2f 1e780910
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
.el-autocomplete { .el-autocomplete {
width: 180px; width: 180px;
} }
.el-autocomplete__suggestions.my-autocomplete-suggestions { .my-suggestions-item {
width: 300px;
& .remark { & .remark {
float: right; float: right;
font-size: 13px; font-size: 13px;
...@@ -14,8 +12,28 @@ ...@@ -14,8 +12,28 @@
} }
</style> </style>
<script> <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 { export default {
data() { data() {
return { return {
...@@ -24,10 +42,7 @@ ...@@ -24,10 +42,7 @@
state2: '', state2: '',
state3: '', state3: '',
state4: '', state4: '',
myPartial: { timeout: null
name: 'my-autocomplete-suggestions',
template: '<span class="label">选项{{$index}}</span><span class="remark">{{item.display}}</span>'
}
} }
}, },
methods: { methods: {
...@@ -52,141 +67,57 @@ ...@@ -52,141 +67,57 @@
return result; return result;
}, },
querySearch(query, simulateQuery) { querySearch(queryString, cb) {
var states = this.states; var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states, var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
deferred;
if (simulateQuery) { cb(results);
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() {
this.states = this.loadAll();
}
};
</script>
## 基础使用
<div class="demo-box">
<el-autocomplete
:value.sync = "state1"
:suggestions = "querySearch(state1)"
placeholder = "请输入内容"
></el-autocomplete>
</div>
```html
<template>
<el-autocomplete
:value.sync = "state1"
:suggestions = "querySearch(state1)"
placeholder = "请输入内容"
></el-autocomplete>
</template>
<script>
export default {
data() {
return {
state1: ''
}
},
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) { querySearchAsync(queryString, cb) {
var states = this.states; var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states, var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
deferred;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer(); clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
setTimeout(() => { cb(results);
deferred.resolve(results); }, 3000 * Math.random());
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
}, },
createStateFilter(query) { createStateFilter(queryString) {
return (state) => { return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0); return (state.value.indexOf(queryString.toLowerCase()) === 0);
}; };
} }
}, },
ready() { mounted() {
this.states = this.loadAll(); this.states = this.loadAll();
} }
}; };
</script> </script>
```
## 通过键盘控制下拉的显示 ## 基础使用
<div class="demo-box"> <div class="demo-box">
<el-autocomplete <el-autocomplete
:value.sync = "state2" v-model="state1"
:suggestions = "querySearch(state2)" :fetch-suggestions="querySearch"
:show-on-up-down = "true" placeholder="请输入内容"
placeholder = "请输入内容"
></el-autocomplete> ></el-autocomplete>
</div> </div>
```html ```html
<template> <template>
<el-autocomplete <el-autocomplete
:value.sync = "state2" v-model="state1"
:suggestions = "querySearch(state2)" :fetch-suggestions="querySearch"
:show-on-up-down = "true" placeholder="请输入内容"
placeholder = "请输入内容"
></el-autocomplete> ></el-autocomplete>
</template> </template>
<script> <script>
export default { export default {
data() { data() {
return { return {
state2: '' states: [],
state1: ''
} }
}, },
methods: { methods: {
...@@ -211,32 +142,19 @@ ...@@ -211,32 +142,19 @@
return result; return result;
}, },
querySearch(query, simulateQuery) { querySearch(queryString, callback) {
var states = this.states; var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states, var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
deferred;
if (simulateQuery) { callback(results);
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
}, },
createStateFilter(query) { createStateFilter(queryString) {
return (state) => { return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0); return (state.value.indexOf(queryString.toLowerCase()) === 0);
}; };
} }
}, },
ready() { mounted() {
this.states = this.loadAll(); this.states = this.loadAll();
} }
}; };
...@@ -247,30 +165,49 @@ ...@@ -247,30 +165,49 @@
<div class="demo-box"> <div class="demo-box">
<el-autocomplete <el-autocomplete
:value.sync = "state3" v-model="state2"
:suggestions = "querySearch(state3)" :fetch-suggestions="querySearch"
:partial = "myPartial" custom-item="my-item"
placeholder = "请输入内容" placeholder="请输入内容"
></el-autocomplete> ></el-autocomplete>
</div> </div>
```html ```html
<el-autocomplete <el-autocomplete
:value.sync = "state3" v-model="state2"
:suggestions = "querySearch(state3)" :fetch-suggestions="querySearch"
:partial = "myPartial" custom-item="my-item"
placeholder = "请输入内容" placeholder="请输入内容"
></el-autocomplete> ></el-autocomplete>
<script> <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 { export default {
data() { data() {
return { return {
state3: '', states: [],
myPartial: { state2: ''
name: 'my-autocomplete-suggestions',
template: '<span class="label">选项{{$index}}</span><span class="remark">{{item.display}}</span>'
}
} }
}, },
methods: { methods: {
...@@ -295,32 +232,19 @@ ...@@ -295,32 +232,19 @@
return result; return result;
}, },
querySearch(query, simulateQuery) { querySearch(queryString, cb) {
var states = this.states; var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states, var results = queryString ? states.filter(this.createStateFilter(queryString)) : states;
deferred;
if (simulateQuery) { cb(results);
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => {
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise;
} else {
return results;
}
}, },
createStateFilter(query) { createStateFilter(queryString) {
return (state) => { return (state) => {
return (state.value.indexOf(query.toLowerCase()) === 0); return (state.value.indexOf(queryString.toLowerCase()) === 0);
}; };
} }
}, },
ready() { mounted() {
this.states = this.loadAll(); this.states = this.loadAll();
} }
}; };
...@@ -331,27 +255,26 @@ ...@@ -331,27 +255,26 @@
<div class="demo-box"> <div class="demo-box">
<el-autocomplete <el-autocomplete
:value.sync = "state4" v-model="state3"
:suggestions = "querySearch(state4, true)"
:search-from-server = "true"
placeholder = "请输入内容" placeholder = "请输入内容"
:fetch-Suggestions="querySearchAsync"
></el-autocomplete> ></el-autocomplete>
</div> </div>
```html ```html
<el-autocomplete <template>
:value.sync = "state4" <el-autocomplete
:suggestions = "querySearch(state4, true)" v-model="state3"
:search-from-server = "true" placeholder = "请输入内容"
placeholder = "请输入内容" :fetch-Suggestions="querySearchAsync"
></el-autocomplete> ></el-autocomplete>
</template>
<script> <script>
var $q = require('q');
export default { export default {
data() { data() {
return { return {
state4: '' state3: '',
states: []
} }
}, },
methods: { methods: {
...@@ -376,24 +299,15 @@ ...@@ -376,24 +299,15 @@
return result; return result;
}, },
querySearch(query, simulateQuery) { querySearchAsync(query, callback) {
var states = this.states; var states = this.states;
var results = query ? states.filter(this.createStateFilter(query)) : states, var results = query ? states.filter(this.createStateFilter(query)) : states;
deferred;
if (simulateQuery) {
if (!query) { return []; }
deferred = $q.defer();
setTimeout(() => { if (!query) { return []; }
deferred.resolve(results);
}, Math.random() * 3000, false);
return deferred.promise; setTimeout(() => {
} else { callback(results);
return results; }, 3000 * Math.random());
}
}, },
createStateFilter(query) { createStateFilter(query) {
return (state) => { return (state) => {
...@@ -413,7 +327,6 @@ ...@@ -413,7 +327,6 @@
|------------- |---------------- |---------------- |---------------------- |-------- | |------------- |---------------- |---------------- |---------------------- |-------- |
| placeholder | 输入框占位文本 | string | | | | placeholder | 输入框占位文本 | string | | |
| disabled | 禁用 | boolean | true, false | false | | disabled | 禁用 | boolean | true, false | false |
| suggestions | 建议列表 | array,object | | | | value | 必填值输入绑定值 | string | | |
| value | 输入绑定值 | string | | |
| showOnUpDown | 是否通过键盘上下键控制建议列表 | boolean | | | | showOnUpDown | 是否通过键盘上下键控制建议列表 | boolean | | |
| partial | 建议列表的自定义模板 | object | | | | fetch-suggestions | 返回输入建议的方法,组件内部通过调用该方法来获得输入建议的数据,在该方法中,仅当你的输入建议数据 resolve 时再通过调用 callback(data:[]) 来返回它 | Function(queryString, callback) | | |
...@@ -12,5 +12,6 @@ ...@@ -12,5 +12,6 @@
"author": "haiping.zeng<haiping.zeng@ele.me>", "author": "haiping.zeng<haiping.zeng@ele.me>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"vue-clickoutside": "^0.1.0"
} }
} }
<template> <template>
<div class="el-autocomplete"> <div class="el-autocomplete" v-clickoutside="handleBlur">
<el-input <el-input
:value="value" :value="value"
:disabled="disabled" :disabled="disabled"
:placeholder="placeholder" :placeholder="placeholder"
:name = 'name' :name='name'
@onchange="handleChange" @onchange="handleChange"
@onfocus="handleFocus()" @onfocus="handleFocus"
@onblur="handleBlur()" @keydown.up.native="highlight(highlightedIndex - 1)"
@keydown.up="highlight(highlightedIndex - 1)" @keydown.down.native="highlight(highlightedIndex + 1)"
@keydown.down="highlight(highlightedIndex + 1)" @keydown.enter.native="select(highlightedIndex)"
@keydown.enter="select(highlightedIndex)"
></el-input> ></el-input>
<ul <transition name="md-fade-bottom">
v-show="showSuggestions && !loading && suggestions.length > 0" <ul
class="el-autocomplete__suggestions" v-show="suggestionVisible && !loading && suggestions.length > 0"
:class="[partial ? partial.name : '']" class="el-autocomplete__suggestions"
transition="md-fade-bottom" ref="suggestions"
v-el:suggestions >
> <li
<li :class="{'highlighted': highlightedIndex === $index}" @click="select($index)" v-for="item in suggestions">{{item.display}}</li> v-if="!customItem"
</ul> :class="{'highlighted': highlightedIndex === index}"
<div @click="select(index)"
v-show="showSuggestions && loading" v-for="(item, index) in suggestions">
class="el-autocomplete__suggestions is-loading" {{item.display}}
> </li>
<i class="el-icon-loading"></i> <component
</div> 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> </div>
</template> </template>
<script> <script>
import ElInput from 'packages/input/index.js'; import ElInput from 'packages/input/index.js';
import Vue from 'vue';
import VueClickoutside from 'main/utils/clickoutside';
Vue.use(VueClickoutside);
export default { export default {
name: 'ElAutocomplete', name: 'ElAutocomplete',
...@@ -42,61 +60,58 @@ ...@@ -42,61 +60,58 @@
placeholder: String, placeholder: String,
disabled: Boolean, disabled: Boolean,
name: String, name: String,
suggestions: [Array, Object],
value: String, value: String,
showOnUpDown: Boolean, fetchSuggestions: Function,
partial: Object triggerOnfocus: {
type: Boolean,
default: true
},
customItem: String
}, },
data() { data() {
return { return {
showSuggestions: false, suggestions: [],
suggestionVisible: false,
inputFocusing: false, inputFocusing: false,
loading: false, loading: false,
highlightedIndex: -1 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: { methods: {
handleChange(value) { handleChange(value) {
this.value = value; this.$emit('input', value);
this.showSuggestions = true; this.showSuggestions(value);
}, },
handleFocus() { handleFocus() {
if (!this.showOnUpDown) { if (this.triggerOnfocus) {
this.showSuggestions = true; this.showSuggestions(this.value);
} }
}, },
handleBlur() { handleBlur() {
this.showSuggestions = false; this.suggestionVisible = false;
}, },
select(index) { select(index) {
debugger;
if (this.suggestions && this.suggestions[index]) { if (this.suggestions && this.suggestions[index]) {
this.value = this.suggestions[index].value; this.$emit('input', this.suggestions[index].value);
this.$nextTick(() => { 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) { getSuggestionElement(index) {
if (!this.suggestions || !this.suggestions[index]) { if (!this.suggestions || !this.suggestions[index]) {
return null; return null;
} else { } else {
return this.$els.suggestions.children[index]; return this.$refs.suggestions.children[index];
} }
}, },
highlight(index) { highlight(index) {
...@@ -107,7 +122,7 @@ ...@@ -107,7 +122,7 @@
} }
var elSelect = this.getSuggestionElement(index); var elSelect = this.getSuggestionElement(index);
var elSuggestions = this.$els.suggestions; var elSuggestions = this.$refs.suggestions;
var scrollTop = elSuggestions.scrollTop; var scrollTop = elSuggestions.scrollTop;
var offsetTop = elSelect.offsetTop; var offsetTop = elSelect.offsetTop;
...@@ -121,7 +136,7 @@ ...@@ -121,7 +136,7 @@
this.highlightedIndex = index; this.highlightedIndex = index;
if (this.showOnUpDown) { if (this.showOnUpDown) {
this.showSuggestions = true; this.suggestionVisible = true;
} }
} }
} }
......
...@@ -28,28 +28,28 @@ ...@@ -28,28 +28,28 @@
transition: all .3s cubic-bezier(.55,0,.1,1); transition: all .3s cubic-bezier(.55,0,.1,1);
} }
.md-fade-center-enter, .md-fade-center-enter,
.md-fade-center-leave { .md-fade-center-leave,
opacity: 0;
transform: scaleY(0);
}
.md-fade-center-leave-active { .md-fade-center-leave-active {
opacity: 0; opacity: 0;
transform: scaleY(0); transform: scaleY(0);
} }
.md-fade-bottom-transition { .md-fade-bottom-enter-active,
.md-fade-bottom-leave-active {
opacity: 1; opacity: 1;
transform: scaleY(1); transform: scaleY(1);
transition: var(--md-fade-transition); transition: var(--md-fade-transition);
transform-origin: center top; transform-origin: center top;
} }
.md-fade-bottom-enter, .md-fade-bottom-enter,
.md-fade-bottom-leave { .md-fade-bottom-leave,
.md-fade-bottom-leave-active {
opacity: 0; opacity: 0;
transform: scaleY(0); transform: scaleY(0);
} }
.md-fade-top-transition { .md-fade-top-enter-active,
.md-fade-top-leave-active {
opacity: 1; opacity: 1;
transform: scaleY(1); transform: scaleY(1);
transition: var(--md-fade-transition); transition: var(--md-fade-transition);
...@@ -57,40 +57,47 @@ ...@@ -57,40 +57,47 @@
} }
.md-fade-top-enter, .md-fade-top-enter,
.md-fade-top-leave { .md-fade-top-leave,
.md-fade-top-leave-active {
opacity: 0; opacity: 0;
transform: scaleY(0); transform: scaleY(0);
} }
.md-fade-left-transition { .md-fade-left-enter-active,
.md-fade-left-leave-active {
opacity: 1; opacity: 1;
transform: scaleX(1); transform: scaleX(1);
transition: var(--md-fade-transition); transition: var(--md-fade-transition);
transform-origin: right center; transform-origin: right center;
} }
.md-fade-left-enter, .md-fade-left-enter,
.md-fade-left-leave { .md-fade-left-leave,
.md-fade-left-leave-active {
opacity: 0; opacity: 0;
transform: scaleX(0); transform: scaleX(0);
} }
.md-fade-right-transition { .md-fade-right-enter-active,
.md-fade-right-leave-active {
opacity: 1; opacity: 1;
transform: scaleX(1); transform: scaleX(1);
transition: var(--md-fade-transition); transition: var(--md-fade-transition);
transform-origin: left center; transform-origin: left center;
} }
.md-fade-right-enter, .md-fade-right-enter,
.md-fade-right-leave { .md-fade-right-leave,
.md-fade-right-leave-active {
opacity: 0; opacity: 0;
transform: scaleX(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); transition: opacity .3s cubic-bezier(.645,.045,.355,1);
} }
.fade-enter, .fade-enter,
.fade-leave { .fade-leave,
.fade-leave-active {
opacity: 0; opacity: 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment