{"version":3,"file":"Wizard.cjs","names":[],"sources":["../../../src/components/Wizard/Wizard.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — steps plus the\n * controlled-or-not pair (activeIndex, defaultActiveIndex, onStepChange,\n * onComplete), the four strings it renders (nextLabel, backLabel, finishLabel,\n * optionalLabel) and the two behaviours (clickableSteps, renderActions). The labels\n * are props because a wizard's buttons are the most translated text in an app.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport { Stepper } from \"../Stepper\";\nimport styles from \"./Wizard.module.css\";\n\n/** One step of the flow. */\nexport interface WizardStep {\n    /** Stable identifier. */\n    id: string;\n    /** Step label shown in the indicator. */\n    label: string;\n    /** Optional description under the label. */\n    description?: string;\n    /** Step body. A function receives the flow controls, for a \"skip\" link inside the form. */\n    content: ReactNode | ((controls: WizardControls) => ReactNode);\n    /**\n     * Gate for leaving this step forward. Return `false` (or a rejected/`false`\n     * promise) to keep the user here — typically `() => form.trigger()`.\n     * Async is supported: the Next button shows a pending state while it runs.\n     */\n    validate?: () => boolean | Promise<boolean>;\n    /** Marks the step as optional, so `onComplete` can ignore it. */\n    optional?: boolean;\n}\n\n/** Flow controls handed to a step body and to `renderActions`. */\nexport interface WizardControls {\n    /** Zero-based index of the current step. */\n    activeIndex: number;\n    /** The current step. */\n    step: WizardStep;\n    /** `true` while a `validate` promise is pending. */\n    validating: boolean;\n    isFirst: boolean;\n    isLast: boolean;\n    /** Run the current step's `validate` and advance when it passes. */\n    next: () => Promise<void>;\n    /** Go back one step. No validation — going back never blocks. */\n    back: () => void;\n    /** Jump to an index. Forward jumps validate every step in between. */\n    goTo: (index: number) => Promise<void>;\n}\n\nexport interface WizardProps {\n    steps: WizardStep[];\n    /** Controlled active index. */\n    activeIndex?: number;\n    /** Uncontrolled initial index. Default `0`. */\n    defaultActiveIndex?: number;\n    onStepChange?: (index: number, step: WizardStep) => void;\n    /** Called when the last step passes validation. */\n    onComplete?: () => void | Promise<void>;\n    /** Label of the advance button. Default `\"Next\"`. */\n    nextLabel?: string;\n    /** Label of the back button. Default `\"Back\"`. */\n    backLabel?: string;\n    /** Label of the button on the last step. Default `\"Finish\"`. */\n    finishLabel?: string;\n    /**\n     * Suffix appended to an optional step's description in the indicator.\n     * Default `\"(optional)\"` — override it to localize, since the SDK ships no\n     * translation for component-internal copy.\n     */\n    optionalLabel?: string;\n    /**\n     * Allow clicking the indicator to jump. Default `false` — a wizard exists\n     * because order matters, and a free jump skips the gates.\n     */\n    clickableSteps?: boolean;\n    /** Replace the default button row. */\n    renderActions?: (controls: WizardControls) => ReactNode;\n    className?: string;\n}\n\n/**\n * Multi-step flow: step indicator, one body at a time, and navigation that\n * respects per-step validation.\n *\n * `Stepper` draws the indicator; this owns the part every app was rewriting — the\n * active index, the async gate before advancing, the disabled/pending buttons and\n * the completion call.\n *\n * Only the active step's body is mounted. Uncommitted input in a step you leave is\n * therefore lost unless the state lives outside (react-hook-form's `FormProvider`,\n * a store, a parent `useState`) — which is the right place for it anyway, since\n * the last step usually needs to submit everything at once.\n *\n * @example\n * ```tsx\n * const form = useZodForm(schema);\n *\n * <FormProvider {...form}>\n *   <Wizard\n *     steps={[\n *       {\n *         id: \"dados\",\n *         label: \"Dados\",\n *         validate: () => form.trigger([\"nome\", \"email\"]),\n *         content: (\n *           <>\n *             <FormField name=\"nome\" label=\"Nome\"><Input /></FormField>\n *             <FormField name=\"email\" label=\"E-mail\"><Input type=\"email\" /></FormField>\n *           </>\n *         ),\n *       },\n *       { id: \"revisao\", label: \"Revisão\", content: <Review /> },\n *     ]}\n *     onComplete={form.handleSubmit(onSubmit)}\n *   />\n * </FormProvider>\n * ```\n */\nexport function Wizard({\n    steps,\n    activeIndex,\n    defaultActiveIndex = 0,\n    onStepChange,\n    onComplete,\n    nextLabel = \"Next\",\n    backLabel = \"Back\",\n    finishLabel = \"Finish\",\n    optionalLabel = \"(optional)\",\n    clickableSteps = false,\n    renderActions,\n    className,\n}: WizardProps) {\n    const isControlled = activeIndex !== undefined;\n    const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n    const [validating, setValidating] = useState(false);\n\n    const current = Math.min(isControlled ? activeIndex : internalIndex, steps.length - 1);\n    const step = steps[current];\n\n    const moveTo = useCallback(\n        (index: number): void => {\n            if (!isControlled) setInternalIndex(index);\n            onStepChange?.(index, steps[index]);\n        },\n        [isControlled, onStepChange, steps],\n    );\n\n    /**\n     * Run a step's gate. A gate that throws counts as \"not allowed\": a `validate`\n     * wired to a network check should not strand the user on a half-advanced flow\n     * when the request fails.\n     */\n    const runValidate = useCallback(async (candidate: WizardStep): Promise<boolean> => {\n        if (!candidate.validate) return true;\n        setValidating(true);\n        try {\n            return await candidate.validate();\n        } catch {\n            return false;\n        } finally {\n            setValidating(false);\n        }\n    }, []);\n\n    const next = useCallback(async (): Promise<void> => {\n        if (!(await runValidate(step))) return;\n        if (current === steps.length - 1) {\n            await onComplete?.();\n            return;\n        }\n        moveTo(current + 1);\n    }, [current, moveTo, onComplete, runValidate, step, steps.length]);\n\n    const back = useCallback((): void => {\n        if (current > 0) moveTo(current - 1);\n    }, [current, moveTo]);\n\n    const goTo = useCallback(\n        async (index: number): Promise<void> => {\n            const target = Math.max(0, Math.min(index, steps.length - 1));\n            if (target === current) return;\n            if (target < current) {\n                moveTo(target);\n                return;\n            }\n            for (let i = current; i < target; i += 1) {\n                if (!(await runValidate(steps[i]))) return;\n            }\n            moveTo(target);\n        },\n        [current, moveTo, runValidate, steps],\n    );\n\n    const controls = useMemo<WizardControls>(\n        () => ({\n            activeIndex: current,\n            step,\n            validating,\n            isFirst: current === 0,\n            isLast: current === steps.length - 1,\n            next,\n            back,\n            goTo,\n        }),\n        [back, current, goTo, next, step, steps.length, validating],\n    );\n\n    const indicatorSteps = useMemo(\n        () =>\n            steps.map((item) => ({\n                label: item.label,\n                description: item.optional\n                    ? `${item.description ?? \"\"} ${optionalLabel}`.trim()\n                    : item.description,\n            })),\n        [optionalLabel, steps],\n    );\n\n    return (\n        <div className={cn(styles.wizard, className)}>\n            <Stepper\n                steps={indicatorSteps}\n                current={current}\n                onStepClick={clickableSteps ? (index) => void goTo(index) : undefined}\n            />\n\n            <div className={styles.body} role=\"group\" aria-label={step.label}>\n                {typeof step.content === \"function\" ? step.content(controls) : step.content}\n            </div>\n\n            {renderActions ? (\n                renderActions(controls)\n            ) : (\n                <div className={styles.actions}>\n                    <Button\n                        variant=\"secondary\"\n                        onClick={back}\n                        disabled={controls.isFirst || validating}\n                    >\n                        {backLabel}\n                    </Button>\n                    <Button onClick={() => void next()} loading={validating}>\n                        {controls.isLast ? finishLabel : nextLabel}\n                    </Button>\n                </div>\n            )}\n        </div>\n    );\n}\n"],"mappings":"mMAyHA,SAAgB,EAAO,CACnB,QACA,cACA,qBAAqB,EACrB,eACA,aACA,YAAY,OACZ,YAAY,OACZ,cAAc,SACd,gBAAgB,aAChB,iBAAiB,GACjB,gBACA,aACY,CACZ,IAAM,EAAe,IAAgB,IAAA,GAC/B,CAAC,EAAe,IAAA,EAAoB,EAAA,SAAA,CAAS,CAAkB,EAC/D,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAAS,EAAK,EAE5C,EAAU,KAAK,IAAI,EAAe,EAAc,EAAe,EAAM,OAAS,CAAC,EAC/E,EAAO,EAAM,GAEb,GAAA,EAAS,EAAA,YAAA,CACV,GAAwB,CAChB,GAAc,EAAiB,CAAK,EACzC,IAAe,EAAO,EAAM,EAAM,CACtC,EACA,CAAC,EAAc,EAAc,CAAK,CACtC,EAOM,GAAA,EAAc,EAAA,YAAA,CAAY,KAAO,IAA4C,CAC/E,GAAI,CAAC,EAAU,SAAU,MAAO,GAChC,EAAc,EAAI,EAClB,GAAI,CACA,OAAO,MAAM,EAAU,SAAS,CACpC,MAAQ,CACJ,MAAO,EACX,QAAU,CACN,EAAc,EAAK,CACvB,CACJ,EAAG,CAAC,CAAC,EAEC,GAAA,EAAO,EAAA,YAAA,CAAY,SAA2B,CAC1C,SAAM,EAAY,CAAI,EAC5B,IAAI,IAAY,EAAM,OAAS,EAAG,CAC9B,MAAM,IAAa,EACnB,MACJ,CACA,EAAO,EAAU,CAAC,CADlB,CAEJ,EAAG,CAAC,EAAS,EAAQ,EAAY,EAAa,EAAM,EAAM,MAAM,CAAC,EAE3D,GAAA,EAAO,EAAA,YAAA,KAAwB,CAC7B,EAAU,GAAG,EAAO,EAAU,CAAC,CACvC,EAAG,CAAC,EAAS,CAAM,CAAC,EAEd,GAAA,EAAO,EAAA,YAAA,CACT,KAAO,IAAiC,CACpC,IAAM,EAAS,KAAK,IAAI,EAAG,KAAK,IAAI,EAAO,EAAM,OAAS,CAAC,CAAC,EACxD,OAAW,EACf,IAAI,EAAS,EAAS,CAClB,EAAO,CAAM,EACb,MACJ,CACA,IAAK,IAAI,EAAI,EAAS,EAAI,EAAQ,GAAK,EACnC,GAAI,CAAE,MAAM,EAAY,EAAM,EAAE,EAAI,OAExC,EAAO,CAAM,CAJb,CAKJ,EACA,CAAC,EAAS,EAAQ,EAAa,CAAK,CACxC,EAEM,GAAA,EAAW,EAAA,QAAA,MACN,CACH,YAAa,EACb,OACA,aACA,QAAS,IAAY,EACrB,OAAQ,IAAY,EAAM,OAAS,EACnC,OACA,OACA,MACJ,GACA,CAAC,EAAM,EAAS,EAAM,EAAM,EAAM,EAAM,OAAQ,CAAU,CAC9D,EAEM,GAAA,EAAiB,EAAA,QAAA,KAEf,EAAM,IAAK,IAAU,CACjB,MAAO,EAAK,MACZ,YAAa,EAAK,SACZ,GAAG,EAAK,aAAe,GAAG,GAAG,IAAgB,KAAK,EAClD,EAAK,WACf,EAAE,EACN,CAAC,EAAe,CAAK,CACzB,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EAA3C,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CACI,MAAO,EACE,UACT,YAAa,EAAkB,GAAU,KAAK,EAAK,CAAK,EAAI,IAAA,EAC/D,CAAA,GAED,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAM,KAAK,QAAQ,aAAY,EAAK,MACtD,SAAA,OAAO,EAAK,SAAY,WAAa,EAAK,QAAQ,CAAQ,EAAI,EAAK,OACnE,CAAA,EAEJ,EACG,EAAc,CAAQ,GAEtB,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,QAAQ,YACR,QAAS,EACT,SAAU,EAAS,SAAW,EAE7B,SAAA,CACG,CAAA,GACR,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAQ,YAAe,KAAK,EAAK,EAAG,QAAS,EACxC,SAAA,EAAS,OAAS,EAAc,CAC7B,CAAA,CACP,GAER,GAEb"}