mirror of
https://github.com/tenrok/vue-select.git
synced 2026-06-22 10:30:34 +03:00
feat: add open & close events (#1101)
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<v-select
|
||||||
|
:options="paginated"
|
||||||
|
:filterable="false"
|
||||||
|
@open="onOpen"
|
||||||
|
@close="onClose"
|
||||||
|
@search="query => search = query"
|
||||||
|
ref="select"
|
||||||
|
>
|
||||||
|
<template #list-footer v-if="hasNextPage">
|
||||||
|
<li ref="load" class="loader">
|
||||||
|
Loading more options...
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import countries from '../data/countries';
|
||||||
|
import vSelect from '../../../src/components/Select'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "InfiniteScroll",
|
||||||
|
components: {vSelect},
|
||||||
|
data: () => ({
|
||||||
|
observer: null,
|
||||||
|
limit: 10,
|
||||||
|
search: ''
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
filtered () {
|
||||||
|
return countries.filter(country => country.includes(this.search));
|
||||||
|
},
|
||||||
|
paginated () {
|
||||||
|
return this.filtered.slice(0, this.limit);
|
||||||
|
},
|
||||||
|
hasNextPage () {
|
||||||
|
return this.paginated.length + 10 < this.filtered.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.observer = new IntersectionObserver(this.infiniteScroll);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onOpen () {
|
||||||
|
if (this.hasNextPage) {
|
||||||
|
await this.$nextTick();
|
||||||
|
this.observer.observe(this.$refs.load)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose () {
|
||||||
|
this.observer.disconnect();
|
||||||
|
},
|
||||||
|
async infiniteScroll ([{isIntersecting, target}]) {
|
||||||
|
if (isIntersecting) {
|
||||||
|
const ul = target.offsetParent;
|
||||||
|
const scrollTop = target.offsetParent.scrollTop;
|
||||||
|
this.limit += 10;
|
||||||
|
await this.$nextTick();
|
||||||
|
ul.scrollTop = scrollTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.loader {
|
||||||
|
text-align: center;
|
||||||
|
color: #bbbbbb;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -120,6 +120,7 @@ module.exports = {
|
|||||||
['guide/validation', 'Validation'],
|
['guide/validation', 'Validation'],
|
||||||
['guide/selectable', 'Limiting Selections'],
|
['guide/selectable', 'Limiting Selections'],
|
||||||
['guide/pagination', 'Pagination'],
|
['guide/pagination', 'Pagination'],
|
||||||
|
['guide/infinite-scroll', 'Infinite Scroll'],
|
||||||
['guide/vuex', 'Vuex'],
|
['guide/vuex', 'Vuex'],
|
||||||
['guide/ajax', 'AJAX'],
|
['guide/ajax', 'AJAX'],
|
||||||
['guide/loops', 'Using in Loops'],
|
['guide/loops', 'Using in Loops'],
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
Vue Select doesn't ship with first party support for infinite scroll, but it's possible to implement
|
||||||
|
by hooking into the `open`, `close`, and `search` events, along with the `filterable` prop, and the
|
||||||
|
`list-footer` slot.
|
||||||
|
|
||||||
|
Let's break down the example below, starting with the `data`.
|
||||||
|
|
||||||
|
- `observer` - when the component is mounted, a new `IntersectionObserver` will be set here
|
||||||
|
- `limit` - the number of options to display 'per page'
|
||||||
|
- `search` - since we've disabled Vue Selects filtering, we'll need to filter options ourselves
|
||||||
|
|
||||||
|
When Vue Select opens, the `open` event is emitted and `onOpen` will be called. We wait for
|
||||||
|
`$nextTick()` so that the `$ref` we need will exist, then begin observing it for intersection.
|
||||||
|
|
||||||
|
The observer is set to call `infiniteScroll` when the `<li>` is completely visible within the list.
|
||||||
|
Some fancy destructuring is done here to get the first `ObservedEntry`, and specifically the
|
||||||
|
`isIntersecting` & `target` properties. If the `<li>` is intersecting, we increase the `limit`, and
|
||||||
|
ensure that the scroll position remains where it was before the list size changed. Again, it's
|
||||||
|
important to wait for `$nextTick` here so that the DOM elements have been inserted before setting
|
||||||
|
the scroll position.
|
||||||
|
|
||||||
|
<InfiniteScroll />
|
||||||
|
|
||||||
|
<<< @/.vuepress/components/InfiniteScroll.vue
|
||||||
@@ -619,6 +619,10 @@
|
|||||||
*/
|
*/
|
||||||
multiple() {
|
multiple() {
|
||||||
this.clearSelection()
|
this.clearSelection()
|
||||||
|
},
|
||||||
|
|
||||||
|
open(isOpen) {
|
||||||
|
this.$emit(isOpen ? 'open' : 'close');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user