import { createContext, Fragment, useCallback, useContext, useEffect, useMemo, useState } from 'react'
import classNames from 'classnames'
import { useRouter } from 'next/router'
const MultiStepFormContext = createContext()
export function MultiStepForm ({ children, initial, steps }) {
const [stepIndex, setStepIndex] = useState(0)
const [formState, setFormState] = useState({})
const router = useRouter()
useEffect(() => {
// initial state might not be available on first render so we sync changes
if (initial) setFormState(initial)
}, [initial])
useEffect(() => {
const idx = Math.max(0, steps.indexOf(router.query.step))
setStepIndex(idx)
router.replace({
pathname: router.pathname,
query: { type: router.query.type, step: steps[idx] }
}, null, { shallow: true })
}, [router.query.step, steps])
const next = useCallback(() => {
const idx = Math.min(stepIndex + 1, steps.length - 1)
router.push(
{ pathname: router.pathname, query: { type: router.query.type, step: steps[idx] } },
null,
{ shallow: true }
)
}, [stepIndex, steps, router])
const prev = useCallback(() => router.back(), [router])
const updateFormState = useCallback((id, state) => {
setFormState(formState => {
return id ? { ...formState, [id]: state } : state
})
}, [])
const value = useMemo(
() => ({ stepIndex, steps, next, prev, formState, updateFormState }),
[stepIndex, steps, next, prev, formState, updateFormState])
return (
{children[stepIndex]}
)
}
function Progress () {
const steps = useSteps()
const stepIndex = useStepIndex()
const style = (index) => {
switch (index) {
case 0: return { marginLeft: '-5px', marginRight: '-13px' }
case 1: return { marginLeft: '-13px', marginRight: '-15px' }
default: return {}
}
}
return (
{
steps.map((label, i) => {
const last = i === steps.length - 1
return (
= i} />
{!last && = i + 1} />}
)
})
}
)
}
function ProgressNumber ({ number, label, active }) {
return (
)
}
const NUMBER_SVG_WIDTH = 24
const NUMBER_SVG_HEIGHT = 24
function NumberSVG ({ number, active }) {
const width = NUMBER_SVG_WIDTH
const height = NUMBER_SVG_HEIGHT
const Wrapper = ({ children }) => (
{children}
)
const Circle = () => {
const circleProps = {
fill: active ? 'var(--bs-info)' : 'var(--bs-body-bg)',
stroke: active ? 'var(--bs-info)' : 'var(--theme-grey)'
}
return (
)
}
const Number = () => {
const svgProps = {
xmlns: 'http://www.w3.org/2000/svg',
viewBox: '0 0 24 24',
// we scale the number down and render it in the center of the circle
width: 0.5 * width,
height: 0.5 * height,
style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }
}
const numberColor = active ? 'var(--bs-white)' : 'var(--theme-grey)'
// svgs are from https://remixicon.com/icon/number-1 etc.
switch (number) {
case 1:
return (
)
case 2:
return (
)
case 3:
return (
)
default:
return null
}
}
return (
)
}
function ProgressLine ({ style, active }) {
const svgStyle = { display: 'block', position: 'relative', top: `${NUMBER_SVG_HEIGHT / 2}px` }
return (
)
}
function useSteps () {
const { steps } = useContext(MultiStepFormContext)
return steps
}
export function useStepIndex () {
const { stepIndex } = useContext(MultiStepFormContext)
return stepIndex
}
export function useMaxSteps () {
const steps = useSteps()
return steps.length
}
export function useStep () {
const stepIndex = useStepIndex()
const steps = useSteps()
return steps[stepIndex]
}
export function useNext () {
const { next } = useContext(MultiStepFormContext)
return next
}
export function usePrev () {
const { prev } = useContext(MultiStepFormContext)
return prev
}
export function useFormState (id) {
const { formState, updateFormState } = useContext(MultiStepFormContext)
const setFormState = useCallback(state => updateFormState(id, state), [id, updateFormState])
return useMemo(
() => [
id ? formState[id] : formState,
setFormState
], [formState, id, setFormState])
}