{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checklist",
  "type": "registry:component",
  "title": "Checklist",
  "description": "Interactive checklist with progress tracking and toggleable items.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/checklist/checklist.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\nexport const CHECKLIST_PROGRESS_EVENT = \"vllnt:checklist-progress-change\";\n\nexport type ChecklistItem = {\n  description?: string;\n  id: string;\n  label: string;\n};\n\ntype ChecklistItemRowProps = {\n  isChecked: boolean;\n  item: ChecklistItem;\n  onToggle: () => void;\n};\n\nfunction ChecklistItemRow({\n  isChecked,\n  item,\n  onToggle,\n}: ChecklistItemRowProps): React.ReactNode {\n  return (\n    <li>\n      <button\n        className={cn(\n          \"w-full flex items-start gap-3 p-2 rounded-md text-left transition-colors hover:bg-muted/50\",\n          isChecked && \"opacity-60\",\n        )}\n        onClick={onToggle}\n        type=\"button\"\n      >\n        {isChecked ? (\n          <svg\n            className=\"size-5 text-green-500 flex-shrink-0 mt-0.5\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            viewBox=\"0 0 24 24\"\n          >\n            <path\n              d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth={2}\n            />\n          </svg>\n        ) : (\n          <svg\n            className=\"size-5 text-muted-foreground flex-shrink-0 mt-0.5\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            viewBox=\"0 0 24 24\"\n          >\n            <rect\n              height=\"18\"\n              rx=\"2\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth={2}\n              width=\"18\"\n              x=\"3\"\n              y=\"3\"\n            />\n          </svg>\n        )}\n        <div className=\"flex-1 min-w-0\">\n          <span className={cn(\"text-sm\", isChecked && \"line-through\")}>\n            {item.label}\n          </span>\n          {item.description ? (\n            <p className=\"text-xs text-muted-foreground mt-0.5\">\n              {item.description}\n            </p>\n          ) : null}\n        </div>\n      </button>\n    </li>\n  );\n}\n\ntype ChecklistHeaderProps = {\n  checked: number;\n  progress: number;\n  title?: string;\n  total: number;\n};\n\nfunction ChecklistHeader({\n  checked,\n  progress,\n  title,\n  total,\n}: ChecklistHeaderProps): React.ReactNode {\n  if (!title) return null;\n  return (\n    <div className=\"flex items-center justify-between mb-3\">\n      <h4 className=\"font-semibold flex items-center gap-2\">\n        <svg\n          className=\"size-5 text-primary\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          viewBox=\"0 0 24 24\"\n        >\n          <path\n            d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth={2}\n          />\n        </svg>\n        {title}\n      </h4>\n      <span className=\"text-xs text-muted-foreground\">\n        {checked}/{total} ({progress}%)\n      </span>\n    </div>\n  );\n}\n\nexport type ChecklistProps = {\n  className?: string;\n  items: ChecklistItem[];\n  onComplete?: () => void;\n  persistKey?: string;\n  title?: string;\n};\n\n// eslint-disable-next-line max-lines-per-function -- Complex interactive component with state and localStorage\nexport function Checklist({\n  className,\n  items,\n  onComplete,\n  persistKey,\n  title,\n}: ChecklistProps): React.ReactNode {\n  const [checked, setChecked] = useState<Set<string>>(() => {\n    if (typeof window !== \"undefined\" && persistKey) {\n      const saved = localStorage.getItem(`checklist:${persistKey}`);\n      if (saved) {\n        try {\n          return new Set(JSON.parse(saved) as string[]);\n        } catch {\n          /* skip */\n        }\n      }\n    }\n    return new Set();\n  });\n\n  const toggleItem = (id: string): void => {\n    const newChecked = new Set(checked);\n    if (newChecked.has(id)) newChecked.delete(id);\n    else newChecked.add(id);\n    setChecked(newChecked);\n    if (persistKey) {\n      try {\n        localStorage.setItem(\n          `checklist:${persistKey}`,\n          JSON.stringify([...newChecked]),\n        );\n        window.dispatchEvent(\n          new CustomEvent(CHECKLIST_PROGRESS_EVENT, {\n            detail: { persistKey },\n          }),\n        );\n      } catch {\n        /* skip */\n      }\n    }\n    if (newChecked.size === items.length) {\n      onComplete?.();\n    }\n  };\n\n  const allChecked = checked.size === items.length;\n  const progress =\n    items.length > 0 ? Math.round((checked.size / items.length) * 100) : 0;\n\n  return (\n    <div className={cn(\"my-6 rounded-lg border bg-card p-4\", className)}>\n      <ChecklistHeader\n        checked={checked.size}\n        progress={progress}\n        title={title}\n        total={items.length}\n      />\n      <div className=\"h-1 bg-muted rounded-full mb-4 overflow-hidden\">\n        <div\n          className={cn(\n            \"h-full transition-all duration-300\",\n            allChecked ? \"bg-green-500\" : \"bg-primary\",\n          )}\n          style={{ width: `${progress}%` }}\n        />\n      </div>\n      <ul className=\"space-y-2\">\n        {items.map((item) => (\n          <ChecklistItemRow\n            isChecked={checked.has(item.id)}\n            item={item}\n            key={item.id}\n            onToggle={() => {\n              toggleItem(item.id);\n            }}\n          />\n        ))}\n      </ul>\n      {allChecked ? (\n        <div className=\"mt-4 p-2 rounded bg-green-500/10 text-green-700 dark:text-green-300 text-sm text-center\">\n          All items completed!\n        </div>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
