{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "conditional-onboarding",
  "type": "registry:component",
  "title": "Conditional Onboarding",
  "description": "Onboarding whose path is computed from an earlier answer: team accounts visit the invite step, personal accounts skip it.",
  "author": "Stepperize",
  "dependencies": [
    "@stepperize/react",
    "lucide-react"
  ],
  "registryDependencies": [],
  "categories": [
    "onboarding"
  ],
  "meta": {
    "capabilities": [
      "branching"
    ],
    "level": "intermediate",
    "tags": [
      "branching",
      "conditional",
      "dynamic path",
      "skip step"
    ]
  },
  "files": [
    {
      "path": "components/stepperize/conditional-onboarding.tsx",
      "type": "registry:component",
      "target": "components/stepperize/conditional-onboarding.tsx",
      "content": "\"use client\";\n\nimport { defineStepper } from \"@stepperize/react\";\nimport { ArrowRight, Check, User, Users } from \"lucide-react\";\n\nconst onboarding = defineStepper([\n\t{ id: \"account\", title: \"Account type\" },\n\t{ id: \"profile\", title: \"Your profile\" },\n\t{ id: \"team\", title: \"Invite team\" },\n\t{ id: \"done\", title: \"All set\" },\n] as const);\n\nconst { Stepper, useStepper } = onboarding;\n\ntype AccountType = \"personal\" | \"team\";\n\n// The path through the flow is computed from an earlier answer: a \"team\"\n// account visits the invite step, a \"personal\" account skips it entirely.\nfunction pathFor(\n\ttype: AccountType,\n): (\"account\" | \"profile\" | \"team\" | \"done\")[] {\n\treturn type === \"team\"\n\t\t? [\"account\", \"profile\", \"team\", \"done\"]\n\t\t: [\"account\", \"profile\", \"done\"];\n}\n\n/**\n * Branching + dynamic flow paths: the account type chosen on the first step\n * decides which steps come next. `goTo` jumps across the skipped step, and the\n * progress dots are rendered from the same computed path.\n */\nexport function ConditionalOnboardingBlock() {\n\treturn (\n\t\t<Stepper.Root className=\"w-full max-w-md rounded-xl border bg-background p-6 shadow-sm\">\n\t\t\t{() => <Inner />}\n\t\t</Stepper.Root>\n\t);\n}\n\nfunction Inner() {\n\tconst stepper = useStepper();\n\tconst type =\n\t\t(stepper.data.get(\"account\") as AccountType | undefined) ?? \"team\";\n\tconst path = pathFor(type);\n\tconst pos = path.indexOf(stepper.current.id as (typeof path)[number]);\n\n\tconst goNext = () => {\n\t\tconst next = path[pos + 1];\n\t\tif (next) stepper.goTo(next);\n\t};\n\tconst goBack = () => {\n\t\tconst prev = path[pos - 1];\n\t\tif (prev) stepper.goTo(prev);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t{/* progress dots come from the *computed* path, so they shrink/grow with the branch */}\n\t\t\t<div className=\"flex items-center gap-1.5\">\n\t\t\t\t{path.map((id, i) => (\n\t\t\t\t\t<span\n\t\t\t\t\t\tkey={id}\n\t\t\t\t\t\tclassName={`h-1.5 flex-1 rounded-full transition-colors ${i <= pos ? \"bg-primary\" : \"bg-muted\"}`}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t\t<p className=\"mt-3 text-xs text-muted-foreground\">\n\t\t\t\tStep {pos + 1} of {path.length}\n\t\t\t\t{type === \"personal\" && \" · team step skipped\"}\n\t\t\t</p>\n\n\t\t\t<div className=\"mt-4 min-h-40\">\n\t\t\t\t<Stepper.Content step=\"account\" className=\"space-y-2\">\n\t\t\t\t\t<p className=\"text-sm font-semibold\">How will you use the app?</p>\n\t\t\t\t\t<Choice\n\t\t\t\t\t\tactive={type === \"personal\"}\n\t\t\t\t\t\ticon={User}\n\t\t\t\t\t\ttitle=\"Just me\"\n\t\t\t\t\t\thint=\"A personal workspace\"\n\t\t\t\t\t\tonClick={() => stepper.data.set(\"account\", \"personal\")}\n\t\t\t\t\t/>\n\t\t\t\t\t<Choice\n\t\t\t\t\t\tactive={type === \"team\"}\n\t\t\t\t\t\ticon={Users}\n\t\t\t\t\t\ttitle=\"With my team\"\n\t\t\t\t\t\thint=\"Invite people to collaborate\"\n\t\t\t\t\t\tonClick={() => stepper.data.set(\"account\", \"team\")}\n\t\t\t\t\t/>\n\t\t\t\t</Stepper.Content>\n\n\t\t\t\t<Stepper.Content step=\"profile\" className=\"space-y-3\">\n\t\t\t\t\t<p className=\"text-sm font-semibold\">Tell us about you</p>\n\t\t\t\t\t<input\n\t\t\t\t\t\tplaceholder=\"Display name\"\n\t\t\t\t\t\tclassName=\"w-full rounded-lg border bg-muted/30 px-3 py-2 text-sm outline-none focus:border-primary\"\n\t\t\t\t\t/>\n\t\t\t\t\t<input\n\t\t\t\t\t\tplaceholder=\"Role\"\n\t\t\t\t\t\tclassName=\"w-full rounded-lg border bg-muted/30 px-3 py-2 text-sm outline-none focus:border-primary\"\n\t\t\t\t\t/>\n\t\t\t\t</Stepper.Content>\n\n\t\t\t\t<Stepper.Content step=\"team\" className=\"space-y-3\">\n\t\t\t\t\t<p className=\"text-sm font-semibold\">Invite your team</p>\n\t\t\t\t\t<input\n\t\t\t\t\t\tplaceholder=\"teammate@company.com\"\n\t\t\t\t\t\tclassName=\"w-full rounded-lg border bg-muted/30 px-3 py-2 text-sm outline-none focus:border-primary\"\n\t\t\t\t\t/>\n\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\tOnly shown because you picked a team account.\n\t\t\t\t\t</p>\n\t\t\t\t</Stepper.Content>\n\n\t\t\t\t<Stepper.Content\n\t\t\t\t\tstep=\"done\"\n\t\t\t\t\tclassName=\"grid place-items-center gap-2 py-6 text-center\"\n\t\t\t\t>\n\t\t\t\t\t<span className=\"grid size-12 place-items-center rounded-full bg-chart-2/15 text-chart-2\">\n\t\t\t\t\t\t<Check className=\"size-6\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<p className=\"text-sm font-medium\">You're all set</p>\n\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t{type === \"team\"\n\t\t\t\t\t\t\t? \"Your team workspace is ready.\"\n\t\t\t\t\t\t\t: \"Your personal workspace is ready.\"}\n\t\t\t\t\t</p>\n\t\t\t\t</Stepper.Content>\n\t\t\t</div>\n\n\t\t\t{!stepper.is(\"done\") && (\n\t\t\t\t<div className=\"mt-4 flex justify-between\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={goBack}\n\t\t\t\t\t\tdisabled={pos === 0}\n\t\t\t\t\t\tclassName=\"inline-flex h-9 items-center rounded-lg border bg-background px-4 text-sm font-medium transition-colors hover:bg-muted disabled:pointer-events-none disabled:opacity-50\"\n\t\t\t\t\t>\n\t\t\t\t\t\tBack\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={goNext}\n\t\t\t\t\t\tclassName=\"inline-flex h-9 items-center gap-1.5 rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{path[pos + 1] === \"done\" ? \"Finish\" : \"Continue\"}\n\t\t\t\t\t\t<ArrowRight className=\"size-4\" />\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\nfunction Choice({\n\tactive,\n\ticon: Icon,\n\ttitle,\n\thint,\n\tonClick,\n}: {\n\tactive: boolean;\n\ticon: typeof User;\n\ttitle: string;\n\thint: string;\n\tonClick: () => void;\n}) {\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={onClick}\n\t\t\tclassName={`flex w-full items-center gap-3 rounded-lg border p-3 text-left transition-colors ${\n\t\t\t\tactive ? \"border-primary bg-primary/5\" : \"hover:bg-muted\"\n\t\t\t}`}\n\t\t>\n\t\t\t<span\n\t\t\t\tclassName={`grid size-9 place-items-center rounded-lg ${active ? \"bg-primary text-primary-foreground\" : \"bg-muted text-muted-foreground\"}`}\n\t\t\t>\n\t\t\t\t<Icon className=\"size-4\" />\n\t\t\t</span>\n\t\t\t<span className=\"flex-1\">\n\t\t\t\t<span className=\"block text-sm font-medium\">{title}</span>\n\t\t\t\t<span className=\"block text-xs text-muted-foreground\">{hint}</span>\n\t\t\t</span>\n\t\t\t{active && <Check className=\"size-4 text-primary\" />}\n\t\t</button>\n\t);\n}\n\nexport default ConditionalOnboardingBlock;\n"
    }
  ]
}
