Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | 14x 14x 33x 14x 14x 14x 14x 14x 13x 14x 7x 7x 7x 7x 14x 13x 14x 14x 14x 12x 12x 12x 12x 12x 12x 37x 14x 16x 14x 18x 13x 13x 5x 4x 4x 4x 4x 3x 3x 3x 14x 1x 1x 70x 69x | <script setup lang="ts">
import { computed, inject, onBeforeUnmount, onMounted, onScopeDispose, ref, watch } from "vue";
import ProgressBar from "../ProgressBar/ProgressBar.vue";
import { useLoadingConfig } from "../../config";
import { useDelayedLoading } from "../../utils/useDelayedLoading";
import {
PAGE_PROGRESS,
bindRouter,
createPageProgress,
type PageProgress,
type RouterLike,
} from "../../utils/pageProgress";
const props = defineProps<{
color?: string;
/** Bar height in px. */
thickness?: number;
/** ms a task must last before the bar appears. */
delay?: number;
/** Announced to screen readers while the bar is visible. */
label?: string;
/** Follow this router's navigations (instead of passing it to the plugin). */
router?: RouterLike;
/** Drive the bar from a specific controller (defaults to the app's). */
progress?: PageProgress;
}>();
// explicit prop > global config > built-in default
const config = useLoadingConfig();
const conf = () => config.pageProgress ?? {};
const color = computed(() => props.color ?? conf().color);
const thickness = computed(() => props.thickness ?? conf().thickness ?? 3);
const label = computed(() => props.label ?? conf().label);
// The app's controller (from the plugin), or a private one.
const progress = props.progress ?? inject(PAGE_PROGRESS, null) ?? createPageProgress();
let unbindRouter: (() => void) | undefined;
onMounted(() => {
if (props.router) unbindRouter = bindRouter(progress, props.router);
});
onBeforeUnmount(() => unbindRouter?.());
/* Visuals. The real duration is unknown, so the bar starts at 10% and
eases toward 90% (each step covers a tenth of the remaining distance);
done() fills it to 100% and it fades out. A task shorter than `delay`
never shows it at all. */
const START = 10;
const CEILING = 90;
const TRICKLE_MS = 200;
const FINISH_MS = 400;
const shown = useDelayedLoading(progress.loading, {
delay: () => props.delay ?? conf().delay ?? 200,
minDuration: 0,
});
const visible = ref(false);
const finishing = ref(false);
const value = ref(0);
let trickle: ReturnType<typeof setInterval> | undefined;
let finishTimer: ReturnType<typeof setTimeout> | undefined;
function begin() {
clearTimeout(finishTimer);
clearInterval(trickle);
visible.value = true;
finishing.value = false;
value.value = START;
trickle = setInterval(() => {
value.value += (CEILING - value.value) * 0.1;
}, TRICKLE_MS);
}
watch(shown, (isShown) => {
if (isShown && !visible.value) begin();
});
watch(progress.loading, (loading) => {
if (loading) {
// a new task while the bar was fading out: pick up again, no flicker
if (finishing.value) begin();
return;
}
if (!visible.value) return; // finished within the delay: never shown
clearInterval(trickle);
value.value = 100;
finishing.value = true;
finishTimer = setTimeout(() => {
visible.value = false;
finishing.value = false;
value.value = 0;
}, FINISH_MS);
});
onScopeDispose(() => {
clearInterval(trickle);
clearTimeout(finishTimer);
});
</script>
<template>
<div
v-if="Ivisible"
class="vslk-page-progress"
:class="{ 'vslk-page-progress--done': finishing }"
>
<ProgressBar :value="value" :color="color" :thickness="thickness" :track="false" :label="label" />
</div>
</template>
<style scoped>
.vslk-page-progress {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9999;
pointer-events: none;
}
/* after the bar has run to 100% (ProgressBar's own 0.3s width transition) */
.vslk-page-progress--done {
opacity: 0;
transition: opacity 0.2s ease 0.2s;
}
.vslk-page-progress :deep(.vslk-progress) {
border-radius: 0;
}
@media (prefers-reduced-motion: reduce) {
.vslk-page-progress--done {
transition: none;
}
}
</style>
|