Progress
An element that shows either determinate or indeterminate progress.
'use client'
import { useEffect, useState } from 'react'
import { Progress, type ProgressProps } from '~/components/ui/progress'
export const Demo = (props: ProgressProps) => {
const [value, setValue] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setValue((value) => (value === 100 ? 0 : value + 1))
}, Math.random() * 500)
return () => clearInterval(interval)
})
return <Progress {...props} value={value} min={0} max={100} />
}
import { createEffect, createSignal } from 'solid-js'
import { Progress, type ProgressProps } from '~/components/ui/progress'
export const Demo = (props: ProgressProps) => {
const [value, setValue] = createSignal(0)
createEffect(() => {
const interval = setInterval(() => {
setValue((prevValue) => (prevValue === 100 ? 0 : prevValue + 1))
}, Math.random() * 500)
return () => clearInterval(interval)
})
return <Progress {...props} value={value()} min={0} max={100} />
}
Usage
import { Progress } from '~/components/ui/progress'
Installation
npx @park-ui/cli components add progress
1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/progress.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { Progress } from '@ark-ui/react/progress'
import { type ProgressVariantProps, progress } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(progress)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, Progress.RootProviderBaseProps>, ProgressVariantProps>
>(Progress.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
HTMLDivElement,
Assign<Assign<HTMLStyledProps<'div'>, Progress.RootBaseProps>, ProgressVariantProps>
>(Progress.Root, 'root')
export const Circle = withContext<
SVGSVGElement,
Assign<HTMLStyledProps<'svg'>, Progress.CircleBaseProps>
>(Progress.Circle, 'circle')
export const CircleRange = withContext<
SVGCircleElement,
Assign<HTMLStyledProps<'circle'>, Progress.CircleRangeBaseProps>
>(Progress.CircleRange, 'circleRange')
export const CircleTrack = withContext<
SVGCircleElement,
Assign<HTMLStyledProps<'circle'>, Progress.CircleTrackBaseProps>
>(Progress.CircleTrack, 'circleTrack')
export const Label = withContext<
HTMLLabelElement,
Assign<HTMLStyledProps<'label'>, Progress.LabelBaseProps>
>(Progress.Label, 'label')
export const Range = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, Progress.RangeBaseProps>
>(Progress.Range, 'range')
export const Track = withContext<
HTMLDivElement,
Assign<HTMLStyledProps<'div'>, Progress.TrackBaseProps>
>(Progress.Track, 'track')
export const ValueText = withContext<
HTMLSpanElement,
Assign<HTMLStyledProps<'span'>, Progress.ValueTextBaseProps>
>(Progress.ValueText, 'valueText')
export const View = withContext<
HTMLSpanElement,
Assign<HTMLStyledProps<'span'>, Progress.ViewBaseProps>
>(Progress.View, 'view')
export { ProgressContext as Context } from '@ark-ui/react/progress'
import { type Assign, Progress } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type ProgressVariantProps, progress } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(progress)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, Progress.RootProviderBaseProps>, ProgressVariantProps>
>(Progress.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
Assign<Assign<HTMLStyledProps<'div'>, Progress.RootBaseProps>, ProgressVariantProps>
>(Progress.Root, 'root')
export const Circle = withContext<Assign<HTMLStyledProps<'svg'>, Progress.CircleBaseProps>>(
Progress.Circle,
'circle',
)
export const CircleRange = withContext<
Assign<HTMLStyledProps<'circle'>, Progress.CircleRangeBaseProps>
>(Progress.CircleRange, 'circleRange')
export const CircleTrack = withContext<
Assign<HTMLStyledProps<'circle'>, Progress.CircleTrackBaseProps>
>(Progress.CircleTrack, 'circleTrack')
export const Label = withContext<Assign<HTMLStyledProps<'label'>, Progress.LabelBaseProps>>(
Progress.Label,
'label',
)
export const Range = withContext<Assign<HTMLStyledProps<'div'>, Progress.RangeBaseProps>>(
Progress.Range,
'range',
)
export const Track = withContext<Assign<HTMLStyledProps<'div'>, Progress.TrackBaseProps>>(
Progress.Track,
'track',
)
export const ValueText = withContext<Assign<HTMLStyledProps<'span'>, Progress.ValueTextBaseProps>>(
Progress.ValueText,
'valueText',
)
export const View = withContext<Assign<HTMLStyledProps<'span'>, Progress.ViewBaseProps>>(
Progress.View,
'view',
)
export { ProgressContext as Context } from '@ark-ui/solid'
No snippet found
2
Add Composition
Copy the composition snippet below into ~/components/ui/progress.tsx
import { forwardRef } from 'react'
import * as StyledProgress from './styled/progress'
export interface ProgressProps extends StyledProgress.RootProps {
/**
* The type of progress to render.
* @default linear
*/
type?: 'linear' | 'circular'
}
export const Progress = forwardRef<HTMLDivElement, ProgressProps>((props, ref) => {
const { children, type = 'linear', ...rootProps } = props
return (
<StyledProgress.Root ref={ref} {...rootProps}>
{children && <StyledProgress.Label>{children}</StyledProgress.Label>}
{type === 'linear' && (
<StyledProgress.Track>
<StyledProgress.Range />
</StyledProgress.Track>
)}
{type === 'circular' && (
<StyledProgress.Circle>
<StyledProgress.CircleTrack />
<StyledProgress.CircleRange />
<StyledProgress.ValueText />
</StyledProgress.Circle>
)}
<StyledProgress.ValueText />
</StyledProgress.Root>
)
})
Progress.displayName = 'Progress'
import { Show, children, splitProps } from 'solid-js'
import * as StyledProgress from './styled/progress'
export interface ProgressProps extends StyledProgress.RootProps {
/**
* The type of progress to render.
* @default linear
*/
type?: 'linear' | 'circular'
}
export const Progress = (props: ProgressProps) => {
const [localProps, rootProps] = splitProps(props, ['children', 'type'])
const getChildren = children(() => localProps.children)
return (
<StyledProgress.Root {...rootProps}>
<Show when={getChildren()}>
<StyledProgress.Label>{getChildren()}</StyledProgress.Label>
</Show>
<Show
when={localProps.type === 'circular'}
fallback={
<StyledProgress.Track>
<StyledProgress.Range />
</StyledProgress.Track>
}
>
<StyledProgress.Circle>
<StyledProgress.CircleTrack />
<StyledProgress.CircleRange />
<StyledProgress.ValueText />
</StyledProgress.Circle>
</Show>
<StyledProgress.ValueText />
</StyledProgress.Root>
)
}
3
Integrate Recipe
If you're not using @park-ui/preset
, add the following recipe to yourpanda.config.ts
:
import { progressAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const progress = defineSlotRecipe({
className: 'progress',
slots: progressAnatomy.keys(),
base: {
root: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
gap: '1.5',
width: 'full',
},
label: {
color: 'fg.default',
fontWeight: 'medium',
textStyle: 'sm',
},
track: {
backgroundColor: 'bg.emphasized',
borderRadius: 'l2',
overflow: 'hidden',
width: '100%',
},
range: {
backgroundColor: 'colorPalette.default',
height: '100%',
transition: 'width 0.2s ease-in-out',
'--translate-x': '-100%',
},
circleTrack: {
stroke: 'bg.emphasized',
},
circleRange: {
stroke: 'colorPalette.default',
transitionProperty: 'stroke-dasharray, stroke',
transitionDuration: '0.6s',
},
valueText: {
textStyle: 'sm',
},
},
defaultVariants: {
size: 'md',
},
variants: {
size: {
sm: {
circle: {
'--size': '36px',
'--thickness': '4px',
},
track: {
height: '1.5',
},
},
md: {
track: {
height: '2',
},
circle: {
'--size': '40px',
'--thickness': '4px',
},
},
lg: {
track: {
height: '2.5',
},
circle: {
'--size': '44px',
'--thickness': '4px',
},
},
},
},
})