import { computed, defineComponent, h, onBeforeUnmount, ref, watch } from "vue"; import { startLoading, stopLoading, useNuxtApp } from "#imports"; export default defineComponent({ name: "ModrinthLoadingIndicator", props: { throttle: { type: Number, default: 50, }, duration: { type: Number, default: 500, }, height: { type: Number, default: 3, }, color: { type: [String, Boolean], default: "repeating-linear-gradient(to right, var(--color-green) 0%, var(--landing-green-label) 100%)", }, }, setup(props, { slots }) { const indicator = useLoadingIndicator({ duration: props.duration, throttle: props.throttle, }); const nuxtApp = useNuxtApp(); nuxtApp.hook("page:start", () => { startLoading(); indicator.start(); }); nuxtApp.hook("page:finish", () => { stopLoading(); indicator.finish(); }); onBeforeUnmount(() => indicator.clear); const loading = useLoading(); watch(loading, (newValue) => { if (newValue) { indicator.start(); } else { indicator.finish(); } }); return () => h( "div", { class: "nuxt-loading-indicator", style: { position: "fixed", top: 0, right: 0, left: 0, pointerEvents: "none", width: `${indicator.progress.value}%`, height: `${props.height}px`, opacity: indicator.isLoading.value ? 1 : 0, background: props.color || undefined, backgroundSize: `${(100 / indicator.progress.value) * 100}% auto`, transition: "width 0.1s, height 0.4s, opacity 0.4s", zIndex: 999999, }, }, slots, ); }, }); function useLoadingIndicator(opts: { duration: number; throttle: number }) { const progress = ref(0); const isLoading = ref(false); const step = computed(() => 10000 / opts.duration); let _timer: any = null; let _throttle: any = null; function start() { clear(); progress.value = 0; if (opts.throttle && import.meta.client) { _throttle = setTimeout(() => { isLoading.value = true; _startTimer(); }, opts.throttle); } else { isLoading.value = true; _startTimer(); } } function finish() { progress.value = 100; _hide(); } function clear() { clearInterval(_timer); clearTimeout(_throttle); _timer = null; _throttle = null; } function _increase(num: number) { progress.value = Math.min(100, progress.value + num); } function _hide() { clear(); if (import.meta.client) { setTimeout(() => { isLoading.value = false; setTimeout(() => { progress.value = 0; }, 400); }, 500); } } function _startTimer() { if (import.meta.client) { _timer = setInterval(() => { _increase(step.value); }, 100); } } return { progress, isLoading, start, finish, clear, }; }