{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "onboarding-wizard",
  "title": "Onboarding Wizard",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/onboarding-wizard/onboarding-wizard.component.ts",
      "content": "import { Component, Input, OnInit, computed, signal, ChangeDetectionStrategy } from '@angular/core'\nimport { Check, ChevronLeft, ChevronRight, Globe, LucideAngularModule, Plus, X } from 'lucide-angular'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiSectionCardComponent } from '@/ui/section-card/section-card.component'\nimport { UiStepperComponent, type StepperStep } from '@/ui/stepper/stepper.component'\nimport { UiSwitchComponent } from '@/ui/switch/switch.component'\n\ninterface Preference {\n  id: string\n  label: string\n  description: string\n  enabled: boolean\n}\n\nconst steps: StepperStep[] = [\n  { id: 1, title: 'Workspace' },\n  { id: 2, title: 'Team' },\n  { id: 3, title: 'Preferences' },\n  { id: 4, title: 'Done' },\n]\n\nconst defaultPreferences: Preference[] = [\n  {\n    id: 'digests',\n    label: 'Email digests',\n    description: 'A daily summary of workspace activity.',\n    enabled: true,\n  },\n  {\n    id: 'updates',\n    label: 'Product updates',\n    description: 'Occasional notes about new features and improvements.',\n    enabled: false,\n  },\n  {\n    id: 'reports',\n    label: 'Weekly reports',\n    description: 'Usage analytics delivered every Monday morning.',\n    enabled: true,\n  },\n]\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/**\n * Angular port of the React OnboardingWizard block: a SectionCard with a four-step Stepper\n * (Workspace, Team, Preferences, Done), a workspace-name input with a live URL slug, teammate\n * invites as removable chips, preference Switch rows and a success panel. Backward navigation\n * through the stepper is free; forward goes through the footer button only.\n *\n * Icons inside `ui-button` sit in `<lucide-icon>`, so the React `size=\"sm\"` `has-[>svg]:px-2.5`\n * padding is set explicitly on those buttons.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-onboarding-wizard, [ui-onboarding-wizard]',\n  standalone: true,\n  // React renders the SectionCard itself: the host stays out of layout and `class` goes to the card.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiInputComponent,\n    UiSectionCardComponent,\n    UiStepperComponent,\n    UiSwitchComponent,\n  ],\n  template: `\n    <ui-section-card\n      data-slot=\"onboarding-wizard\"\n      [title]=\"'Set up your workspace'\"\n      [description]=\"'Four quick steps and your team is ready to collaborate.'\"\n      [class]=\"className\"\n    >\n      <ui-stepper [steps]=\"steps\" [value]=\"step()\" (valueChange)=\"onStepperInput($event)\" class=\"mb-6\" />\n\n      @if (step() === 1) {\n        <div class=\"space-y-4\">\n          <div class=\"space-y-2\">\n            <label for=\"onboarding-workspace-name\" class=\"text-sm font-medium\">Workspace name</label>\n            <ui-input\n              id=\"onboarding-workspace-name\"\n              [value]=\"workspaceName()\"\n              (valueChange)=\"workspaceName.set($event)\"\n              placeholder=\"Acme Inc\"\n              autoComplete=\"off\"\n            />\n          </div>\n          <div class=\"border-border bg-muted/40 flex items-center gap-2 rounded-md border px-3 py-2\">\n            <lucide-icon [img]=\"Globe\" class=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n            <p class=\"min-w-0 truncate text-xs\">\n              <span class=\"text-muted-foreground\">uipkge.dev/</span>\n              <span class=\"text-foreground font-medium\">{{ slug() }}</span>\n            </p>\n          </div>\n        </div>\n      }\n\n      @if (step() === 2) {\n        <div class=\"space-y-4\">\n          <form class=\"flex gap-2\" (submit)=\"onInviteSubmit($event)\">\n            <ui-input\n              [value]=\"inviteDraft()\"\n              (valueChange)=\"onInviteDraft($event)\"\n              type=\"email\"\n              placeholder=\"teammate@company.com\"\n              [status]=\"inviteInvalid() ? 'error' : undefined\"\n              class=\"flex-1\"\n            />\n            <button ui-button type=\"submit\" variant=\"outline\" size=\"sm\" class=\"shrink-0 px-2.5\">\n              <lucide-icon [img]=\"Plus\" aria-hidden=\"true\" />\n              Add\n            </button>\n          </form>\n          @if (inviteInvalid()) {\n            <p role=\"alert\" class=\"text-destructive text-xs\">Enter a valid email address.</p>\n          }\n          @if (invites().length > 0) {\n            <div class=\"flex flex-wrap gap-2\">\n              @for (email of invites(); track email) {\n                <span ui-badge variant=\"secondary\" class=\"gap-1 py-1 pr-1 pl-2.5\"\n                  >{{ email\n                  }}<button\n                    type=\"button\"\n                    [attr.aria-label]=\"'Remove ' + email\"\n                    class=\"hover:bg-foreground/10 focus-visible:ring-ring min-h-6 rounded-full p-0.5 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                    (click)=\"removeInvite(email)\"\n                  >\n                    <lucide-icon [img]=\"X\" class=\"size-3\" aria-hidden=\"true\" />\n                  </button>\n                </span>\n              }\n            </div>\n          } @else {\n            <p class=\"text-muted-foreground text-xs\">No teammates invited yet.</p>\n          }\n          <button\n            type=\"button\"\n            class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring min-h-6 text-xs underline-offset-4 transition-colors hover:underline focus-visible:ring-2 focus-visible:outline-none\"\n            (click)=\"goNext()\"\n          >\n            Skip for now\n          </button>\n        </div>\n      }\n\n      @if (step() === 3) {\n        <div class=\"divide-y rounded-lg border\">\n          @for (pref of preferences(); track pref.id) {\n            <div class=\"flex items-center justify-between gap-4 p-4\">\n              <div class=\"min-w-0\">\n                <p class=\"text-sm font-medium\">{{ pref.label }}</p>\n                <p class=\"text-muted-foreground mt-0.5 text-xs\">{{ pref.description }}</p>\n              </div>\n              <button\n                ui-switch\n                [attr.aria-label]=\"pref.label\"\n                [checked]=\"pref.enabled\"\n                (checkedChange)=\"setPreference(pref.id, $event)\"\n              ></button>\n            </div>\n          }\n        </div>\n      }\n\n      @if (step() === 4) {\n        <div class=\"space-y-4 py-6 text-center\">\n          <div\n            class=\"border-success/30 bg-success/10 text-success mx-auto flex size-16 items-center justify-center rounded-full border shadow-xs\"\n          >\n            <lucide-icon [img]=\"Check\" class=\"size-8\" aria-hidden=\"true\" />\n          </div>\n          <div>\n            <p class=\"text-lg font-semibold\">You're all set</p>\n            <p class=\"text-muted-foreground mx-auto mt-1 max-w-sm text-sm\">{{ doneMessage() }}</p>\n          </div>\n          <button ui-button variant=\"outline\" size=\"sm\" (click)=\"reset()\">Set up another workspace</button>\n        </div>\n      }\n\n      <div slot=\"footer\" class=\"flex w-full items-center justify-between\">\n        @if (step() > 1 && step() < 4) {\n          <button ui-button variant=\"ghost\" size=\"sm\" class=\"px-2.5\" (click)=\"step.set(step() - 1)\">\n            <lucide-icon [img]=\"ChevronLeft\" aria-hidden=\"true\" />\n            Back\n          </button>\n        } @else {\n          <span aria-hidden=\"true\"></span>\n        }\n        <span class=\"text-muted-foreground text-xs\">Step {{ step() }} of 4</span>\n        @if (step() < 4) {\n          <button\n            ui-button\n            size=\"sm\"\n            class=\"px-2.5\"\n            [disabled]=\"step() === 1 && workspaceName().trim() === ''\"\n            (click)=\"goNext()\"\n          >\n            {{ step() === 3 ? 'Finish' : 'Continue' }}\n            <lucide-icon [img]=\"ChevronRight\" aria-hidden=\"true\" />\n          </button>\n        } @else {\n          <span aria-hidden=\"true\"></span>\n        }\n      </div>\n    </ui-section-card>\n  `,\n})\nexport class UiOnboardingWizardComponent implements OnInit {\n  /** Jump straight to a step (1 Workspace · 2 Team · 3 Preferences · 4 Done). */\n  @Input() initialStep = 1\n  /** Pre-fill the workspace name so later steps read naturally in isolation. */\n  @Input() initialWorkspaceName = 'Acme Inc'\n  /** Pre-seed invited teammates. */\n  @Input() initialInvites: string[] = []\n  @Input('class') className?: string\n\n  protected readonly steps = steps\n  protected readonly Check = Check\n  protected readonly ChevronLeft = ChevronLeft\n  protected readonly ChevronRight = ChevronRight\n  protected readonly Globe = Globe\n  protected readonly Plus = Plus\n  protected readonly X = X\n\n  readonly step = signal(1)\n  readonly workspaceName = signal('Acme Inc')\n  readonly invites = signal<string[]>([])\n  readonly inviteDraft = signal('')\n  readonly inviteInvalid = signal(false)\n  readonly preferences = signal<Preference[]>(defaultPreferences.map((p) => ({ ...p })))\n\n  readonly slug = computed(\n    () =>\n      this.workspaceName()\n        .trim()\n        .toLowerCase()\n        .replace(/[^a-z0-9]+/g, '-')\n        .replace(/^-+|-+$/g, '') || 'your-workspace',\n  )\n\n  readonly doneMessage = computed(() => {\n    const name = this.workspaceName() || 'Your workspace'\n    const count = this.invites().length\n    return count > 0\n      ? `${name} is ready and ${count} invitation${count === 1 ? '' : 's'} sent.`\n      : `${name} is ready — invite teammates anytime from settings.`\n  })\n\n  ngOnInit(): void {\n    this.step.set(this.initialStep)\n    this.workspaceName.set(this.initialWorkspaceName)\n    this.invites.set([...this.initialInvites])\n  }\n\n  addInvite(): void {\n    const email = this.inviteDraft().trim()\n    if (!EMAIL_RE.test(email) || this.invites().includes(email)) {\n      this.inviteInvalid.set(true)\n      return\n    }\n    this.inviteInvalid.set(false)\n    this.invites.update((prev) => [...prev, email])\n    this.inviteDraft.set('')\n  }\n\n  onInviteSubmit(event: Event): void {\n    event.preventDefault()\n    this.addInvite()\n  }\n\n  onInviteDraft(value: string): void {\n    this.inviteDraft.set(value)\n    this.inviteInvalid.set(false)\n  }\n\n  removeInvite(email: string): void {\n    this.invites.update((prev) => prev.filter((e) => e !== email))\n  }\n\n  setPreference(id: string, checked: boolean): void {\n    this.preferences.update((prev) => prev.map((p) => (p.id === id ? { ...p, enabled: checked } : p)))\n  }\n\n  onStepperInput(value: number): void {\n    // Free backward navigation; forward goes through the footer button only.\n    const current = this.step()\n    if (value < current && current !== 4) this.step.set(value)\n  }\n\n  goNext(): void {\n    if (this.step() < 4) this.step.update((s) => s + 1)\n  }\n\n  reset(): void {\n    this.step.set(1)\n    this.workspaceName.set('')\n    this.invites.set([])\n    this.inviteDraft.set('')\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/onboarding-wizard/onboarding-wizard.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/onboarding-wizard/index.ts",
      "content": "export { UiOnboardingWizardComponent } from './onboarding-wizard.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/onboarding-wizard/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/section-card.json",
    "https://uipkge.dev/r/angular/stepper.json",
    "https://uipkge.dev/r/angular/switch.json"
  ],
  "description": "Multi-step workspace setup flow in a SectionCard: a four-step Stepper (Workspace → Team → Preferences → Done) with check icons on completed steps, a workspace-name input with live URL slug preview, teammate invites as removable email chips with a skip link, preference Switch rows, and a success panel with a glowing check circle. Footer carries Back/Continue actions plus a Step X of 4 counter. Data is stubbed inline — wire your API of choice.",
  "categories": [
    "auth",
    "app",
    "onboarding"
  ]
}