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 | 36x 36x 112x 36x 36x 36x 36x 78x 36x 78x 36x 36x 38x 38x 78x 2x | <script setup lang="ts">
import { computed } from "vue";
import { resolveTrack } from "../Spinner/track";
import { clampProgress, progressA11y } from "../../utils/progress";
import { usePrefersReducedMotion } from "../../utils/usePrefersReducedMotion";
import { useLoadingConfig } from "../../config";
const props = withDefaults(
defineProps<{
/** 0–100. Without it the bar is indeterminate (a sliding segment). */
value?: number;
color?: string;
/** The background rail. `true` derives it from `color`; a string sets it. */
track?: boolean | string;
/** Bar height in px. */
thickness?: number;
speed?: number;
/** Announced to screen readers (progressbar or live status). */
label?: string;
}>(),
{
// explicit undefined opts out of Vue's Boolean casting, so an unset
// `track` can fall through to the global config
track: undefined,
}
);
// explicit prop > global config > built-in default
const config = useLoadingConfig();
const conf = () => config.progressBar ?? {};
const color = computed(() => props.color ?? conf().color ?? "currentColor");
const thickness = computed(() => props.thickness ?? conf().thickness ?? 4);
const speed = computed(() => props.speed ?? conf().speed ?? 1);
// unlike the arc, a bar with no visible rail is hard to read as progress
const track = computed(() => props.track ?? conf().track ?? true);
const progress = computed(() => clampProgress(props.value));
const trackInfo = computed(() => resolveTrack(track.value));
const a11y = computed(() => progressA11y(props.label, progress.value));
// Same reduced-motion policy as Spinner: slow down rather than freeze.
const prefersReducedMotion = usePrefersReducedMotion();
const duration = computed(() => {
const s = speed.value > 0 ? speed.value : 1;
return `${(1.5 / s) * (prefersReducedMotion.value ? 2 : 1)}s`;
});
</script>
<template>
<div
class="vslk-progress"
:class="{ 'vslk-progress--indeterminate': progress === null }"
:data-vslk-track="trackInfo?.attr"
:style="{
height: `${thickness}px`,
color: color,
'--vslk-progress-track': trackInfo?.color ?? 'transparent',
'--vslk-progress-duration': duration,
}"
v-bind="a11y"
>
<div
class="vslk-progress__bar"
:style="progress === null ? undefined : { width: `${progress}%` }"
/>
<span v-if="Iprops.label && progress === null" class="vslk-sr-only">{{ props.label }}</span>
</div>
</template>
<style scoped>
.vslk-progress {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 999px;
background: var(--vslk-progress-track);
}
.vslk-progress__bar {
height: 100%;
border-radius: inherit;
background: currentColor;
transition: width 0.3s ease;
}
/* Indeterminate: a 40%-wide segment slides from fully off the left edge
(-100% of its own width) to fully off the right (250% of 40% = 100%). */
.vslk-progress--indeterminate .vslk-progress__bar {
width: 40%;
transition: none;
animation: vslk-progress-slide var(--vslk-progress-duration) ease-in-out infinite;
}
@keyframes vslk-progress-slide {
from {
transform: translateX(-100%);
}
to {
transform: translateX(250%);
}
}
.vslk-sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
</style>
|