{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "quiz",
  "type": "registry:component",
  "title": "Quiz",
  "description": "Interactive multiple-choice quiz with hints, explanations, and scoring.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/quiz/quiz.tsx",
      "content": "\"use client\";\n\nimport { type ReactNode, useState } from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\nexport type QuizOption = {\n  correct?: boolean;\n  explanation?: string;\n  label: string;\n};\n\ntype QuizOptionButtonProps = {\n  index: number;\n  onSelect: (index: number) => void;\n  option: QuizOption;\n  selectedIndex: null | number;\n  submitted: boolean;\n};\n\n// eslint-disable-next-line max-lines-per-function -- Option button showing selected, correct, incorrect states\nfunction QuizOptionButton({\n  index,\n  onSelect,\n  option,\n  selectedIndex,\n  submitted,\n}: QuizOptionButtonProps): React.ReactNode {\n  const isSelected = selectedIndex === index;\n  const showResult = submitted && isSelected;\n  return (\n    <button\n      className={cn(\n        \"w-full text-left p-3 rounded-md border transition-colors hover:bg-muted/50 focus:outline-none focus:ring-2 focus:ring-primary\",\n        isSelected && !submitted && \"border-primary bg-primary/10\",\n        showResult && option.correct && \"border-green-500 bg-green-500/10\",\n        showResult && !option.correct && \"border-red-500 bg-red-500/10\",\n        submitted &&\n          !isSelected &&\n          option.correct &&\n          \"border-green-500/50 bg-green-500/5\",\n      )}\n      disabled={submitted}\n      onClick={() => {\n        onSelect(index);\n      }}\n      type=\"button\"\n    >\n      <div className=\"flex items-center justify-between\">\n        <span className=\"text-sm\">{option.label}</span>\n        {showResult ? (\n          option.correct ? (\n            <svg\n              className=\"size-4 text-green-500\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              viewBox=\"0 0 24 24\"\n            >\n              <path\n                d=\"M5 13l4 4L19 7\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                strokeWidth={2}\n              />\n            </svg>\n          ) : (\n            <svg\n              className=\"size-4 text-red-500\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              viewBox=\"0 0 24 24\"\n            >\n              <path\n                d=\"M6 18L18 6M6 6l12 12\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                strokeWidth={2}\n              />\n            </svg>\n          )\n        ) : null}\n        {submitted && !isSelected && option.correct ? (\n          <svg\n            className=\"size-4 text-green-500/50\"\n            fill=\"none\"\n            stroke=\"currentColor\"\n            viewBox=\"0 0 24 24\"\n          >\n            <path\n              d=\"M5 13l4 4L19 7\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth={2}\n            />\n          </svg>\n        ) : null}\n      </div>\n      {submitted && option.explanation ? (\n        <p className=\"text-xs text-muted-foreground mt-2\">\n          {option.explanation}\n        </p>\n      ) : null}\n    </button>\n  );\n}\n\ntype QuizHintProps = {\n  hint: string;\n  onShow: () => void;\n  showHint: boolean;\n};\n\nfunction QuizHint({ hint, onShow, showHint }: QuizHintProps): React.ReactNode {\n  return (\n    <div className=\"mb-4\">\n      {showHint ? (\n        <p className=\"text-sm text-muted-foreground italic bg-muted/50 p-2 rounded\">\n          {hint}\n        </p>\n      ) : (\n        <button\n          className=\"text-sm text-primary hover:underline\"\n          onClick={onShow}\n          type=\"button\"\n        >\n          Show hint\n        </button>\n      )}\n    </div>\n  );\n}\n\ntype QuizResultProps = {\n  explanation: ReactNode;\n  isCorrect: boolean;\n};\n\nfunction QuizResult({\n  explanation,\n  isCorrect,\n}: QuizResultProps): React.ReactNode {\n  return (\n    <div\n      className={cn(\n        \"p-3 rounded-md text-sm mb-4\",\n        isCorrect\n          ? \"bg-green-500/10 text-green-700 dark:text-green-300\"\n          : \"bg-muted\",\n      )}\n    >\n      <p className=\"font-medium mb-1\">\n        {isCorrect ? \"Correct!\" : \"Not quite right.\"}\n      </p>\n      <div className=\"[&>p]:mb-0\">{explanation}</div>\n    </div>\n  );\n}\n\nexport type QuizProps = {\n  className?: string;\n  explanation?: ReactNode;\n  hint?: string;\n  onAnswer?: (correct: boolean) => void;\n  options: QuizOption[];\n  question: string;\n};\n\n// eslint-disable-next-line max-lines-per-function -- Interactive quiz with state management\nexport function Quiz({\n  className,\n  explanation,\n  hint,\n  onAnswer,\n  options,\n  question,\n}: QuizProps): React.ReactNode {\n  const [selectedIndex, setSelectedIndex] = useState<null | number>(null);\n  const [showHint, setShowHint] = useState(false);\n  const [submitted, setSubmitted] = useState(false);\n\n  const handleReset = (): void => {\n    setSelectedIndex(null);\n    setSubmitted(false);\n    setShowHint(false);\n  };\n\n  const handleSubmit = (): void => {\n    setSubmitted(true);\n    const isCorrect = selectedIndex !== null && options[selectedIndex]?.correct;\n    onAnswer?.(Boolean(isCorrect));\n  };\n\n  const isCorrect = selectedIndex !== null && options[selectedIndex]?.correct;\n\n  return (\n    <div className={cn(\"my-6 rounded-lg border bg-card p-6\", className)}>\n      <div className=\"flex items-start gap-3 mb-4\">\n        <svg\n          className=\"size-5 text-primary flex-shrink-0 mt-0.5\"\n          fill=\"none\"\n          stroke=\"currentColor\"\n          viewBox=\"0 0 24 24\"\n        >\n          <path\n            d=\"M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n            strokeWidth={2}\n          />\n        </svg>\n        <h4 className=\"font-semibold text-foreground\">{question}</h4>\n      </div>\n      <div className=\"space-y-2 mb-4\">\n        {options.map((opt, index) => (\n          <QuizOptionButton\n            index={index}\n            key={typeof opt === \"string\" ? opt : `quiz-option-${String(opt)}`}\n            onSelect={(index_) => {\n              if (!submitted) setSelectedIndex(index_);\n            }}\n            option={opt}\n            selectedIndex={selectedIndex}\n            submitted={submitted}\n          />\n        ))}\n      </div>\n      {hint && !submitted ? (\n        <QuizHint\n          hint={hint}\n          onShow={() => {\n            setShowHint(true);\n          }}\n          showHint={showHint}\n        />\n      ) : null}\n      {submitted && explanation ? (\n        <QuizResult explanation={explanation} isCorrect={Boolean(isCorrect)} />\n      ) : null}\n      <div className=\"flex gap-2\">\n        {submitted ? (\n          <button\n            className=\"px-3 py-1.5 text-sm rounded-md border border-border hover:bg-muted transition-colors\"\n            onClick={handleReset}\n            type=\"button\"\n          >\n            Try Again\n          </button>\n        ) : (\n          <button\n            className=\"px-3 py-1.5 text-sm rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:pointer-events-none transition-colors\"\n            disabled={selectedIndex === null}\n            onClick={handleSubmit}\n            type=\"button\"\n          >\n            Check Answer\n          </button>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
