127 lines
4.0 KiB
Vue
Executable File
127 lines
4.0 KiB
Vue
Executable File
<template>
|
|
<slot v-bind="$attrs" />
|
|
|
|
<slot v-if="loading" name="loading"
|
|
:text="loadText"
|
|
>
|
|
<div v-if="loading" class="col-span-full flex justify-center">
|
|
<svg class="flex-shrink-0 animate-spin h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg"
|
|
fill="none" viewBox="0 0 24 24"
|
|
> <circle class="opacity-25" cx="12"
|
|
cy="12" r="10"
|
|
stroke="currentColor" stroke-width="4"
|
|
></circle><path class="opacity-75" fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
></path></svg>
|
|
</div>
|
|
</slot>
|
|
</template>
|
|
<script>
|
|
import { ref, onUpdated, onUnmounted, watch } from 'vue'
|
|
import axios from 'axios'
|
|
import { usePage } from '@inertiajs/inertia-vue3'
|
|
|
|
export default {
|
|
props: {
|
|
nodeElement: Object,
|
|
loadText: {
|
|
type: String,
|
|
default: 'Загрузка ...',
|
|
},
|
|
nextCursor: { type: String, default: '' },
|
|
},
|
|
|
|
emits: ['fromPagination'],
|
|
|
|
setup(props, { emit }) {
|
|
const cursor = ref(props.nextCursor)
|
|
const loading = ref(false)
|
|
|
|
let installObserver = false
|
|
let observer = null
|
|
|
|
// watch(props, (props) => {
|
|
// observer?.disconnect();
|
|
// createObserver();
|
|
// });
|
|
|
|
const loadMore = () => {
|
|
loading.value = true
|
|
if (!cursor.value) {
|
|
return Promise.resolve(null)
|
|
}
|
|
return new Promise((resolve) => {
|
|
axios
|
|
.get(usePage().url.value, { params: { cursor: cursor.value } })
|
|
.then(({ data }) => {
|
|
if (data.collections.length) {
|
|
cursor.value = data.next
|
|
emit('fromPagination', data.collections)
|
|
resolve(true)
|
|
}
|
|
resolve(false)
|
|
})
|
|
})
|
|
}
|
|
|
|
const createObserver = () => {
|
|
if (props.nodeElement) {
|
|
const intersectionCallback = (entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
console.log('isIntersecting')
|
|
console.log(props.nodeElement)
|
|
removeObserver(entry.target)
|
|
loadMore().then((exist) => {
|
|
loading.value = false
|
|
if (exist) {
|
|
createObserver()
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
observer = new IntersectionObserver(intersectionCallback, {})
|
|
observer.observe(props.nodeElement)
|
|
}
|
|
}
|
|
|
|
const removeObserver = (target) => {
|
|
observer.unobserve(target)
|
|
observer?.disconnect()
|
|
}
|
|
|
|
onUpdated(() => {
|
|
if (!installObserver) {
|
|
createObserver()
|
|
installObserver = true
|
|
}
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
observer?.disconnect()
|
|
})
|
|
|
|
// onMounted(() => {
|
|
// window.addEventListener(
|
|
// "scroll",
|
|
// debounce((e) => {
|
|
// console.log(props.nodeElement);
|
|
// let pixelsFromBottom =
|
|
// document.documentElement.offsetHeight -
|
|
// document.documentElement.scrollTop -
|
|
// window.innerHeight;
|
|
// if (pixelsFromBottom < props.bottomLine && !loading.value) {
|
|
// loading.value = true;
|
|
// props.loadMore().finally(() => (loading.value = false));
|
|
// }
|
|
// }, 200)
|
|
// );
|
|
// });
|
|
return {
|
|
loading,
|
|
}
|
|
},
|
|
}
|
|
</script>
|