{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "onboarding-checklist",
  "title": "Onboarding Checklist",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/onboarding-checklist/onboarding-checklist.component.ts",
      "content": "// Edit this file to change the tasks and copy. State is local -- wire `toggle()` and\n// `dismiss()` to your own activation API.\nimport { Component, Input, OnInit, booleanAttribute, computed, signal, ChangeDetectionStrategy } from '@angular/core'\nimport {\n  Bell,\n  BookOpen,\n  Building2,\n  Check,\n  ChevronRight,\n  Database,\n  LucideAngularModule,\n  Users,\n  X,\n  type LucideIconData,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\n\nexport interface OnboardingChecklistTask {\n  id: string\n  title: string\n  description: string\n  icon: LucideIconData\n  done: boolean\n}\n\nconst baseTasks: Omit<OnboardingChecklistTask, 'done'>[] = [\n  {\n    id: 'workspace',\n    title: 'Create workspace',\n    description: 'Name your team space and pick a URL.',\n    icon: Building2,\n  },\n  {\n    id: 'teammates',\n    title: 'Invite teammates',\n    description: 'Bring in the people you work with.',\n    icon: Users,\n  },\n  {\n    id: 'data',\n    title: 'Connect your data',\n    description: 'Sync sources so insights flow in automatically.',\n    icon: Database,\n  },\n  {\n    id: 'alerts',\n    title: 'Set up alerts',\n    description: 'Get notified when metrics move.',\n    icon: Bell,\n  },\n  {\n    id: 'api',\n    title: 'Explore API docs',\n    description: 'Learn the endpoints and ship your first request.',\n    icon: BookOpen,\n  },\n]\n\n/**\n * OnboardingChecklist -- the React block, 1:1: a card with an \"N of M complete\" counter and\n * a progress bar over tappable task rows (a done task flips to a filled check tile with muted\n * copy). \"Skip for now\" and the X fade the card out; at 5 of 5 the rows become a success panel.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-onboarding-checklist, [ui-onboarding-checklist]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCardDescriptionComponent,\n    UiCardContentComponent,\n    UiCardFooterComponent,\n    UiProgressComponent,\n  ],\n  template: `\n    <div data-slot=\"onboarding-checklist\" [class]=\"rootClass\">\n      <ui-card>\n        <ui-card-header>\n          <ui-card-title>Get started</ui-card-title>\n          <ui-card-description>Finish these steps to unlock everything your workspace can do.</ui-card-description>\n        </ui-card-header>\n        <ui-card-content class=\"space-y-4\">\n          <div class=\"space-y-2\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-xs\">\n                <span class=\"text-foreground font-medium\">{{ completed() }} of {{ total() }}</span> complete\n              </p>\n              <p class=\"text-muted-foreground text-xs tabular-nums\">{{ percent() }}%</p>\n            </div>\n            <ui-progress [value]=\"percent()\" />\n          </div>\n\n          @if (allDone()) {\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 mt-1 text-sm\">\n                  Every step is complete — your workspace is ready to go.\n                </p>\n              </div>\n            </div>\n          } @else {\n            <div class=\"-mx-2 divide-y\">\n              @for (task of tasks(); track task.id) {\n                <button\n                  type=\"button\"\n                  role=\"checkbox\"\n                  [attr.aria-checked]=\"task.done\"\n                  class=\"group hover:bg-muted/50 focus-visible:ring-ring flex w-full items-center gap-3 rounded-lg p-3 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                  (click)=\"toggle(task.id)\"\n                >\n                  @if (task.done) {\n                    <span\n                      class=\"bg-primary text-primary-foreground flex size-9 shrink-0 items-center justify-center rounded-md shadow-xs\"\n                    >\n                      <lucide-icon [img]=\"Check\" class=\"size-4\" aria-hidden=\"true\" />\n                    </span>\n                  } @else {\n                    <span\n                      class=\"border-border bg-muted text-muted-foreground group-hover:bg-background group-hover:text-foreground flex size-9 shrink-0 items-center justify-center rounded-md border transition-colors\"\n                    >\n                      <lucide-icon [img]=\"task.icon\" class=\"size-4\" aria-hidden=\"true\" />\n                    </span>\n                  }\n                  <span class=\"min-w-0 flex-1\">\n                    <span [class]=\"titleClass(task.done)\">{{ task.title }}</span>\n                    <span class=\"text-muted-foreground mt-0.5 block truncate text-xs\">{{ task.description }}</span>\n                  </span>\n                  <!-- [class] (not class=) feeds only the svg, so the hover nudge is not applied twice (host + svg). -->\n                  <lucide-icon\n                    [img]=\"ChevronRight\"\n                    [class]=\"'text-muted-foreground/50 size-4 shrink-0 transition-transform duration-200 group-hover:translate-x-0.5'\"\n                    aria-hidden=\"true\"\n                  />\n                </button>\n              }\n            </div>\n          }\n        </ui-card-content>\n        <ui-card-footer class=\"justify-between\">\n          @if (!allDone()) {\n            <button ui-button variant=\"ghost\" size=\"sm\" (click)=\"dismiss()\">Skip for now</button>\n          } @else {\n            <span aria-hidden=\"true\"></span>\n          }\n          <button\n            type=\"button\"\n            aria-label=\"Dismiss checklist\"\n            class=\"hover:bg-foreground/10 focus-visible:ring-ring rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n            (click)=\"dismiss()\"\n          >\n            <lucide-icon [img]=\"X\" class=\"text-muted-foreground size-4\" aria-hidden=\"true\" />\n          </button>\n        </ui-card-footer>\n      </ui-card>\n    </div>\n  `,\n})\nexport class UiOnboardingChecklistComponent implements OnInit {\n  /** Seed every task as done so the success panel can be previewed in isolation. */\n  @Input({ transform: booleanAttribute }) initialComplete = false\n  @Input('class') className?: string\n\n  readonly tasks = signal<OnboardingChecklistTask[]>(this.seed())\n  readonly open = signal(true)\n\n  readonly completed = computed(() => this.tasks().filter((task) => task.done).length)\n  readonly total = computed(() => this.tasks().length)\n  readonly percent = computed(() => Math.round((this.completed() / this.total()) * 100))\n  readonly allDone = computed(() => this.completed() === this.total())\n\n  protected get rootClass(): string {\n    return cn(\n      'mx-auto w-full max-w-md transition-[opacity,translate] duration-200 ease-out motion-reduce:transition-none',\n      !this.open() && 'pointer-events-none translate-y-1 opacity-0',\n      this.className,\n    )\n  }\n\n  protected readonly Check = Check\n  protected readonly ChevronRight = ChevronRight\n  protected readonly X = X\n\n  ngOnInit(): void {\n    // React seeds its useState once from `initialComplete`; inputs land after construction.\n    this.tasks.set(this.seed())\n  }\n\n  toggle(id: string): void {\n    this.tasks.update((prev) => prev.map((task) => (task.id === id ? { ...task, done: !task.done } : task)))\n  }\n\n  dismiss(): void {\n    this.open.set(false)\n  }\n\n  protected titleClass(done: boolean): string {\n    return cn('block truncate text-sm font-medium', done && 'text-muted-foreground')\n  }\n\n  private seed(): OnboardingChecklistTask[] {\n    return baseTasks.map((task, index) => ({ ...task, done: this.initialComplete || index < 3 }))\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/onboarding-checklist/onboarding-checklist.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/onboarding-checklist/index.ts",
      "content": "export { UiOnboardingChecklistComponent, type OnboardingChecklistTask } from './onboarding-checklist.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/onboarding-checklist/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/progress.json"
  ],
  "description": "Progressive activation checklist card: header with title, an N of M complete counter and a linear Progress bar, tappable task rows with icon tiles, one-line descriptions and chevrons (completed tasks flip to a filled check tile with muted copy), a footer with a Skip for now ghost link and an X dismiss that fades the card out, and a success panel with a glowing check circle at 5 of 5. State is local — wire tasks to your own activation API.",
  "categories": [
    "auth",
    "app",
    "onboarding"
  ]
}