77 lines
1.5 KiB
TypeScript
77 lines
1.5 KiB
TypeScript
import React, {
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useMemo,
|
|
useState,
|
|
type ReactElement,
|
|
type ReactNode
|
|
} from 'react'
|
|
|
|
type WizardContextProps = (name: string) => void
|
|
|
|
const WizardContext = createContext<WizardContextProps | null>(null)
|
|
|
|
export function useWizard(): WizardContextProps {
|
|
const ctx = useContext(WizardContext)
|
|
|
|
if (!ctx) {
|
|
throw new Error('useWizard must be used within <Wizard />')
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
type WizardProps = {
|
|
children: ReactNode
|
|
index?: number
|
|
onChange?: (index: number) => void
|
|
}
|
|
|
|
export function Wizard({
|
|
children,
|
|
index: initIndex = 0,
|
|
onChange
|
|
}: WizardProps) {
|
|
const [index, setIndex] = useState<number>(initIndex)
|
|
|
|
const components = useMemo(
|
|
() => React.Children.toArray(children) as ReactElement<WizardStepProps>[],
|
|
[children]
|
|
)
|
|
|
|
const steps = useMemo(
|
|
() => components.map((child) => child.props.name),
|
|
[components]
|
|
)
|
|
|
|
const child = components[index]
|
|
|
|
const onChange_ = useCallback<WizardContextProps>(
|
|
(name) => {
|
|
const nextIndex = steps.findIndex((n) => n === name)
|
|
|
|
if (nextIndex >= 0) {
|
|
setIndex(nextIndex)
|
|
onChange?.(nextIndex)
|
|
}
|
|
},
|
|
[steps]
|
|
)
|
|
|
|
return (
|
|
<WizardContext.Provider value={onChange_}>
|
|
{React.isValidElement(child) ? React.cloneElement(child) : child}
|
|
</WizardContext.Provider>
|
|
)
|
|
}
|
|
|
|
export type WizardStepProps = {
|
|
name: string
|
|
children: React.ReactNode
|
|
}
|
|
|
|
export function WizardStep({ children }: WizardStepProps) {
|
|
return <>{children}</>
|
|
}
|