80 lines
1.9 KiB
TypeScript
80 lines
1.9 KiB
TypeScript
import { getCurrentInstance, onBeforeUnmount, onMounted, ref, unref, watch } from "vue";
|
|
|
|
function readBool(value: any, fallback = true) {
|
|
const resolved = unref(value);
|
|
if (resolved == null) return fallback;
|
|
return !!resolved;
|
|
}
|
|
|
|
export function useInfiniteTrigger({
|
|
rootRef,
|
|
enabled = true,
|
|
canLoad = true,
|
|
loadMore,
|
|
rootMargin = "900px",
|
|
threshold = 0.01,
|
|
}: Record<string, any> = {}) {
|
|
const sentinelRef = ref(null);
|
|
let observer: any = null;
|
|
let loading = false;
|
|
|
|
function disconnect() {
|
|
try {
|
|
observer?.disconnect?.();
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
observer = null;
|
|
}
|
|
|
|
async function maybeLoad() {
|
|
if (loading) return;
|
|
if (!readBool(enabled, true) || !readBool(canLoad, true)) return;
|
|
if (typeof loadMore !== "function") return;
|
|
loading = true;
|
|
try {
|
|
await loadMore();
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
function connect() {
|
|
disconnect();
|
|
const sentinel = sentinelRef.value;
|
|
if (!sentinel || typeof IntersectionObserver !== "function") return;
|
|
|
|
observer = new IntersectionObserver(
|
|
(entries = []) => {
|
|
const hit = entries.some((entry) => entry?.isIntersecting);
|
|
if (!hit) return;
|
|
void maybeLoad();
|
|
},
|
|
{
|
|
root: unref(rootRef) || null,
|
|
rootMargin,
|
|
threshold,
|
|
},
|
|
);
|
|
observer.observe(sentinel);
|
|
}
|
|
|
|
if (getCurrentInstance()) {
|
|
onMounted(connect);
|
|
onBeforeUnmount(disconnect);
|
|
|
|
watch(
|
|
() => [unref(rootRef), sentinelRef.value],
|
|
() => connect(),
|
|
{ flush: "post" },
|
|
);
|
|
}
|
|
|
|
return {
|
|
sentinelRef,
|
|
connect,
|
|
disconnect,
|
|
maybeLoad,
|
|
};
|
|
}
|