From 61cb268f2d17dd6b5348e282f25f68aea6508032 Mon Sep 17 00:00:00 2001 From: "Ghislain \"Ao\" Linais" Date: Fri, 17 Jul 2026 21:33:40 +0200 Subject: [PATCH 1/4] feat(Progress): add circular progress variant Also adds thickness prop to control circle stroke width --- docs/content/docs/2.components/progress.md | 15 ++ .../nuxt/app/pages/components/progress.vue | 27 ++- src/runtime/components/Progress.vue | 72 +++++-- src/runtime/keyframes.css | 60 ++++++ src/theme/progress.ts | 177 ++++++++++++++++-- test/components/Progress.spec.ts | 2 + .../__snapshots__/Progress-vue.spec.ts.snap | 123 +++++++----- .../__snapshots__/Progress.spec.ts.snap | 123 +++++++----- .../__snapshots__/Toast-vue.spec.ts.snap | 72 +++---- .../__snapshots__/Toast.spec.ts.snap | 72 +++---- 10 files changed, 541 insertions(+), 202 deletions(-) diff --git a/docs/content/docs/2.components/progress.md b/docs/content/docs/2.components/progress.md index f0d88221b7..1246f5425c 100644 --- a/docs/content/docs/2.components/progress.md +++ b/docs/content/docs/2.components/progress.md @@ -71,6 +71,21 @@ props: --- :: +### Variant + +Use the `variant` prop to change the style of the progress. May be `linear` (default) or `circular` + +::component-code +--- +external: + - modelValue + - variant +props: + modelValue: 50 + variant: 'linear' +--- +:: + ### Indeterminate When no `v-model` is set or the value is `null`, the Progress becomes _indeterminate_. The progress bar is animated as a `carousel`, but you can change it using the [`animation`](#animation) prop. diff --git a/playgrounds/nuxt/app/pages/components/progress.vue b/playgrounds/nuxt/app/pages/components/progress.vue index 50a4e52c1c..4aeaf6bae7 100644 --- a/playgrounds/nuxt/app/pages/components/progress.vue +++ b/playgrounds/nuxt/app/pages/components/progress.vue @@ -6,6 +6,7 @@ const colors = Object.keys(theme.variants.color) const sizes = Object.keys(theme.variants.size) const animations = Object.keys(theme.variants.animation) const orientations = Object.keys(theme.variants.orientation) +const variants = Object.keys(theme.variants.variant) const attrs = reactive({ color: [theme.defaultVariants.color], @@ -14,6 +15,7 @@ const attrs = reactive({ }) const orientation = ref('horizontal' as keyof typeof theme.variants.orientation) +const variant = ref('linear' as keyof typeof theme.variants.variant) const value1 = ref(0) const value2 = ref(0) @@ -46,22 +48,39 @@ onMounted(() => { + - - + + + diff --git a/src/runtime/components/Progress.vue b/src/runtime/components/Progress.vue index 5180665048..3e3808dc6d 100644 --- a/src/runtime/components/Progress.vue +++ b/src/runtime/components/Progress.vue @@ -38,6 +38,12 @@ export interface ProgressProps extends Pick VNode[] @@ -65,7 +72,9 @@ import { tv } from '../utils/tv' const _props = withDefaults(defineProps(), { inverted: false, modelValue: null, - orientation: 'horizontal' + orientation: 'horizontal', + variant: 'linear', + thickness: 'auto' }) const emits = defineEmits() const slots = defineSlots() @@ -109,24 +118,44 @@ const indicatorStyle = computed(() => { return } - if (props.orientation === 'vertical') { - return { - transform: `translateY(${props.inverted ? '' : '-'}${100 - percent.value}%)` - } - } else { - if (dir.value === 'rtl') { + if (props.variant === 'linear') { + if (props.orientation === 'vertical') { return { - transform: `translateX(${props.inverted ? '-' : ''}${100 - percent.value}%)` + transform: `translateY(${props.inverted ? '' : '-'}${100 - percent.value}%)` } } else { - return { - transform: `translateX(${props.inverted ? '' : '-'}${100 - percent.value}%)` + if (dir.value === 'rtl') { + return { + transform: `translateX(${props.inverted ? '-' : ''}${100 - percent.value}%)` + } + } else { + return { + transform: `translateX(${props.inverted ? '' : '-'}${100 - percent.value}%)` + } } } + } else { + const strokeDasharray = `${percent.value ?? 25}, 100` + + return { + 'stroke-dasharray': strokeDasharray + } + } +}) + +const thicknessStyle = computed(() => { + if (props.variant === 'linear') return + + if (props.thickness === 'auto') return + + return { + '--ui-progress-thickness': `${props.thickness}px` } }) const statusStyle = computed(() => { + if (props.variant === 'circular') return undefined + const value = `${Math.max(percent.value ?? 0, 0)}%` return props.orientation === 'vertical' ? { height: value } : { width: value } }) @@ -167,20 +196,35 @@ const ui = computed(() => tv({ extend: theme, ...(appConfig.ui?.progress || {}) size: props.size, color: props.color, orientation: props.orientation, - inverted: props.inverted + inverted: props.inverted, + variant: props.variant }))