react-vite-demo
react and vite demo
git clone https://9o.is/git/react-vite-demo.git
Progress.tsx
(1661B)
1 import { createContext, ReactNode, useContext, useId } from "react"
2
3 type ProgressContext = {
4 labelId: string
5 progressBarId: string
6 loading: boolean
7 }
8
9 type ProgressProps = {
10 loading: boolean
11 children: ReactNode
12 }
13
14 const ProgressContext = createContext<ProgressContext | undefined>(undefined)
15
16 export function useProgressContext() {
17 const context = useContext(ProgressContext)
18 if (!context) throw new Error('useProgressContext must be used within a Progress component')
19 return context
20 }
21
22 export function Progress ({ loading, children }: ProgressProps) {
23 const labelId = useId()
24 const progressBarId = useId()
25
26 return (
27 <ProgressContext.Provider value={{ labelId, progressBarId, loading }}>
28 {children}
29 </ProgressContext.Provider>
30 )
31 }
32
33 function ProgressBar({ children }: { children: ReactNode }) {
34 const { progressBarId, labelId, loading } = useProgressContext()
35
36 if (!loading) return <> </>
37
38 return (
39 <div id={progressBarId} role="progressbar" aria-labelledby={labelId}>
40 {children}
41 </div>
42 )
43 }
44
45 function ProgressLabel({ children }: { children: ReactNode }) {
46 const { labelId } = useProgressContext()
47 return (
48 <span id={labelId}>{children}</span>
49 )
50 }
51
52 function ProgressContent({ children }: { children: ReactNode }) {
53 const { loading, progressBarId } = useProgressContext()
54 return (
55 <div aria-live="polite" aria-describedby={loading ? progressBarId : undefined}>
56 {children}
57 </div>
58 )
59 }
60
61 Progress.Bar = ProgressBar
62 Progress.Label = ProgressLabel
63 Progress.Content = ProgressContent