Progress Linear
Vue feedbackTop-of-screen page-loading bar (à la NProgress / nuxt loading-indicator). Auto-advances while a navigation or fetch is in flight, then completes. Drop into the app shell once.
Also available for React ->Installation
$ pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/progress-linear.json$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/progress-linear.json$ yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/progress-linear.json$ bunx shadcn-vue@latest add https://uipkge.dev/r/vue/progress-linear.json
Or with the named registry:
npx shadcn-vue@latest add @uipkge/progress-linear
Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
modelValue | number | 0 | optional |
bgColor | string | — | optional |
buffer | number | — | optional |
color | string | — | optional |
height | number | string | — | optional |
indeterminate | boolean | false | optional |
reverse | boolean | false | optional |
rounded | 'none''sm''default''md''lg''xl''full' | — | optional |
stream | boolean | false | optional |
striped | boolean | false | optional |
active | boolean | true | optional |
Dependencies
Files (3)
-
app/components/ui/progress-linear/ProgressLinear.vue 5.1 kB
<script setup lang="ts"> import { computed } from 'vue' import type { HTMLAttributes } from 'vue' import { cn } from '@/lib/utils' import { progressLinearVariants } from './progress-linear.variants' const props = withDefaults( defineProps<{ class?: HTMLAttributes['class'] modelValue?: number bgColor?: string buffer?: number color?: string height?: number | string indeterminate?: boolean reverse?: boolean rounded?: 'none' | 'sm' | 'default' | 'md' | 'lg' | 'xl' | 'full' stream?: boolean striped?: boolean active?: boolean }>(), { modelValue: 0, indeterminate: false, reverse: false, stream: false, striped: false, active: true, }, ) const normalizedValue = computed(() => { return Math.min(100, Math.max(0, props.modelValue)) }) const normalizedBuffer = computed(() => { return Math.min(100, Math.max(0, props.buffer || 0)) }) const heightValue = computed(() => { if (typeof props.height === 'number') return `${props.height}px` if (typeof props.height === 'string') return props.height return '4px' }) const bgColorValue = computed(() => props.bgColor || 'currentColor') const progressColorValue = computed(() => props.color || 'currentColor') const containerClasses = computed(() => cn(progressLinearVariants({ rounded: props.rounded }), props.class)) </script> <template> <div data-uipkge data-slot="progress-linear" role="progressbar" :aria-valuemin="0" :aria-valuemax="100" :aria-valuenow="indeterminate ? undefined : normalizedValue" :class="containerClasses" :style="{ height: heightValue }" > <!-- Background --> <div class="absolute inset-0 transition-colors duration-300" :class="[ striped ? 'bg-[repeating-linear-gradient(45deg,transparent,transparent_8px,rgba(255,255,255,0.1)_8px,rgba(255,255,255,0.1)_16px)]' : '', !indeterminate && normalizedBuffer > 0 ? 'opacity-30' : 'opacity-100', ]" :style="{ backgroundColor: bgColorValue, width: normalizedBuffer > 0 ? `${normalizedBuffer}%` : '100%', }" /> <!-- Buffer (if buffer > 0) --> <div v-if="!indeterminate && normalizedBuffer > 0 && normalizedBuffer < 100" class="absolute inset-0 transition-colors duration-300" :class="[ reverse ? 'right-0 left-auto' : 'right-auto left-0', striped ? 'bg-[repeating-linear-gradient(45deg,transparent,transparent_8px,rgba(255,255,255,0.15)_8px,rgba(255,255,255,0.15)_16px)]' : '', ]" :style="{ backgroundColor: bgColorValue, width: `${normalizedBuffer}%`, opacity: 0.3, }" /> <!-- Stream lines (when stream is true) --> <div v-if="stream && !indeterminate && active" class="absolute inset-0 overflow-hidden" :class="reverse ? 'right-0 left-auto' : 'right-auto left-0'" > <div class="animate-stream absolute inset-0 bg-[repeating-linear-gradient(90deg,transparent,transparent_10px,rgba(255,255,255,0.2)_10px,rgba(255,255,255,0.2)_20px)] bg-[length:40px_40px]" :style="{ width: `${normalizedBuffer || 100}%` }" /> </div> <!-- Progress bar --> <div class="absolute inset-y-0 transition-colors duration-300" :class="[ reverse ? 'right-0 left-auto' : 'right-auto left-0', indeterminate ? 'motion-safe:animate-indeterminate' : '', striped && !indeterminate ? 'bg-[repeating-linear-gradient(45deg,transparent,transparent_8px,rgba(255,255,255,0.25)_8px,rgba(255,255,255,0.25)_16px)]' : '', ]" :style="{ width: indeterminate ? '100%' : `${normalizedValue}%`, backgroundColor: indeterminate ? undefined : progressColorValue, }" > <!-- Indeterminate animations --> <template v-if="indeterminate"> <div class="motion-safe:animate-indeterminate1 absolute inset-y-0 w-full bg-inherit" :style="{ backgroundColor: progressColorValue }" /> <div class="motion-safe:animate-indeterminate2 absolute inset-y-0 w-full bg-inherit" :style="{ backgroundColor: progressColorValue }" /> </template> </div> </div> </template> <style scoped> @media (prefers-reduced-motion: no-preference) { .animate-indeterminate { animation: indeterminate 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } .animate-indeterminate1 { animation: indeterminate1 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } .animate-indeterminate2 { animation: indeterminate2 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } } .animate-stream { animation: stream 1s linear infinite; } @keyframes indeterminate { 0% { transform: translateX(-100%); } 100% { transform: translateX(400%); } } @keyframes indeterminate1 { 0% { transform: translateX(-100%); } 100% { transform: translateX(400%); } } @keyframes indeterminate2 { 0% { transform: translateX(-100%); opacity: 1; } 100% { transform: translateX(400%); opacity: 0; } } @keyframes stream { 0% { transform: translateX(0); } 100% { transform: translateX(40px); } } </style> -
app/components/ui/progress-linear/progress-linear.variants.ts 1.1 kB
import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' /** * Variant definitions live in their own file (rather than inline in the * SFC) so `ProgressLinear.vue` can import them without the cva runtime * coupling that breaks SSR when several `<ProgressLinear>` instances * render before the module graph fully resolves. Sibling pattern to * `card/card.variants.ts`. */ export const progressLinearVariants = cva('relative overflow-hidden w-full', { variants: { rounded: { none: 'rounded-none', sm: 'rounded-sm', default: 'rounded-full', md: 'rounded-md', lg: 'rounded-lg', xl: 'rounded-xl', full: 'rounded-full', }, color: { default: 'bg-primary', primary: 'bg-primary', secondary: 'bg-secondary', destructive: 'bg-destructive', success: 'bg-[var(--success)]', warning: 'bg-[var(--warning)]', info: 'bg-[var(--info)]', error: 'bg-destructive', }, }, defaultVariants: { rounded: 'default', color: 'default', }, }) export type ProgressLinearVariants = VariantProps<typeof progressLinearVariants> -
app/components/ui/progress-linear/index.ts 0.3 kB
export { default as ProgressLinear } from './ProgressLinear.vue' // Re-export variant API from the sibling file (kept separate to avoid the // ProgressLinear.vue <-> index.ts circular import that broke dev SSR). export { progressLinearVariants, type ProgressLinearVariants } from './progress-linear.variants'
Raw manifest: https://uipkge.dev/r/vue/progress-linear.json