{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checkbox-indeterminate",
  "title": "Indeterminate checkbox",
  "description": "Styled checkbox: the Indeterminate set. 5 decorative checkbox variations (MixedCheckbox, ParentCheckbox, TreeCheckbox, PartialCheckbox, AllCheckbox) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/checkbox-indeterminate.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, Minus } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Indeterminate checkbox family: 5 parent/child selection groups. The logic is\n   real: the parent derives from the children (all on -> true, none -> false,\n   mixed -> \"mixed\") and pressing the parent turns every child on or off. A parent\n   in the mixed state carries aria-checked=\"mixed\" and shows a dash. Because the\n   boxes are under 24px the invisible touch-area extension (after:-inset-1.5) is\n   mandatory in every variant. The labels are visible and bound with\n   aria-labelledby; the ids are produced by React.useId(). All useState lives in\n   the root component, never inside a subtree that opens and closes. Color comes\n   ONLY from tokens, via alpha color-mix. framer-motion switches instantly under\n   reduced-motion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst boxSize: Record<StyledSize, string> = {\n  sm: \"size-4\",\n  md: \"size-5\",\n  lg: \"size-6\",\n  xl: \"size-7\",\n}\n\nconst iconSize: Record<StyledSize, string> = {\n  sm: \"size-3\",\n  md: \"size-3.5\",\n  lg: \"size-4\",\n  xl: \"size-5\",\n}\n\nconst textSize: Record<StyledSize, string> = {\n  sm: \"text-xs\",\n  md: \"text-sm\",\n  lg: \"text-sm\",\n  xl: \"text-base\",\n}\n\nconst boxBase =\n  \"relative inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md border outline-none transition-colors after:absolute after:-inset-1.5 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-danger/20 dark:aria-invalid:ring-danger/40 [&_svg]:shrink-0 [&_i]:leading-none\"\n\nconst rowBase = \"inline-flex items-center gap-2.5\"\n\n/* Transition type: instant under reduced-motion, spring otherwise. */\ntype SpringLike = { duration: number } | { type: \"spring\"; stiffness: number; damping: number }\n\ntype CheckboxProps = Omit<React.ComponentProps<\"button\">, \"onChange\"> & {\n  size?: StyledSize\n  label?: string\n  checked?: boolean\n  defaultChecked?: boolean\n  onCheckedChange?: (checked: boolean) => void\n}\n\nconst CHILD_LABELS = [\"Comments\", \"Mentions\", \"Replies\"] as const\n\ntype BoxState = boolean | \"mixed\"\n\n/* Real parent/child state. Without props it starts in the mixed state so the \"mixed\" appearance is readable on the first frame (deterministic, not random). */\nfunction useGroup(props: {\n  checked?: boolean\n  defaultChecked?: boolean\n  onCheckedChange?: (checked: boolean) => void\n}) {\n  const { checked, defaultChecked, onCheckedChange } = props\n  const reduce = useReducedMotion()\n  const [kids, setKids] = React.useState<boolean[]>(() =>\n    defaultChecked === undefined\n      ? [true, false, false]\n      : [defaultChecked, defaultChecked, defaultChecked]\n  )\n  const effective = checked === undefined ? kids : kids.map(() => checked)\n  const all = effective.every(Boolean)\n  const none = effective.every((v) => !v)\n  const parent: BoxState = all ? true : none ? false : \"mixed\"\n\n  const toggleParent = () => {\n    const next = !all\n    if (checked === undefined) setKids((list) => list.map(() => next))\n    onCheckedChange?.(next)\n  }\n  const toggleChild = (index: number) => {\n    const next = effective.map((v, i) => (i === index ? !v : v))\n    setKids(next)\n    onCheckedChange?.(next.every(Boolean))\n  }\n\n  const spring: SpringLike = reduce\n    ? { duration: 0 }\n    : { type: \"spring\" as const, stiffness: 500, damping: 30 }\n  return { kids: effective, parent, all, toggleParent, toggleChild, spring }\n}\n\n/* Durum kutusu: mixed -> tire, true -> onay, false -> bos. */\nfunction StateBox({\n  state,\n  size,\n  spring,\n  labelId,\n  onPress,\n  className,\n  ...props\n}: {\n  state: BoxState\n  size: StyledSize\n  spring: SpringLike\n  labelId: string\n  onPress: () => void\n  className?: string\n} & Omit<React.ComponentProps<\"button\">, \"onChange\">) {\n  const filled = state !== false\n  return (\n    <button\n      type=\"button\"\n      role=\"checkbox\"\n      aria-checked={state === \"mixed\" ? \"mixed\" : state}\n      aria-labelledby={labelId}\n      data-slot=\"styled-checkbox\"\n      onClick={onPress}\n      className={cn(\n        boxBase,\n        boxSize[size],\n        filled\n          ? \"border-primary bg-primary text-primary-foreground\"\n          : \"border-field-border text-transparent\",\n        className\n      )}\n      {...props}\n    >\n      <motion.span\n        initial={false}\n        animate={{ scale: filled ? 1 : 0, opacity: filled ? 1 : 0 }}\n        transition={spring}\n        className=\"flex items-center justify-center\"\n      >\n        {state === \"mixed\" ? (\n          <Minus className={cn(iconSize[size], \"stroke-[3]\")} />\n        ) : (\n          <Check className={cn(iconSize[size], \"stroke-[3]\")} />\n        )}\n      </motion.span>\n    </button>\n  )\n}\n\n/* A child row: its own box + a visible label. */\nfunction ChildRow({\n  label,\n  state,\n  size,\n  spring,\n  onPress,\n}: {\n  label: string\n  state: boolean\n  size: StyledSize\n  spring: SpringLike\n  onPress: () => void\n}) {\n  const labelId = React.useId()\n  return (\n    <span className={rowBase}>\n      <StateBox state={state} size={size} spring={spring} labelId={labelId} onPress={onPress} />\n      <span\n        id={labelId}\n        onClick={onPress}\n        className={cn(textSize[size], \"cursor-pointer select-none text-muted-foreground\")}\n      >\n        {label}\n      </span>\n    </span>\n  )\n}\n\n/* Mixed: the parent shows the mixed state with a dash, and the children are listed\n   beneath it. */\nexport function MixedCheckbox({\n  className,\n  size = \"md\",\n  label = \"Notifications\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { kids, parent, spring, toggleParent, toggleChild } = useGroup({\n    checked,\n    defaultChecked,\n    onCheckedChange,\n  })\n  const labelId = React.useId()\n  return (\n    <div className={cn(\"flex flex-col gap-2\", className)}>\n      <span className={rowBase}>\n        <StateBox\n          state={parent}\n          size={size}\n          spring={spring}\n          labelId={labelId}\n          onPress={toggleParent}\n          {...props}\n        />\n        <span\n          id={labelId}\n          onClick={toggleParent}\n          className={cn(textSize[size], \"cursor-pointer select-none font-medium text-foreground\")}\n        >\n          {label}\n        </span>\n      </span>\n      <div className=\"flex flex-col gap-2 pl-6\">\n        {CHILD_LABELS.map((child, i) => (\n          <ChildRow\n            key={child}\n            label={child}\n            state={kids[i]}\n            size={size}\n            spring={spring}\n            onPress={() => toggleChild(i)}\n          />\n        ))}\n      </div>\n    </div>\n  )\n}\n\n/* Parent: the parent sits on a card and the children are gathered in an indented\n   block. */\nexport function ParentCheckbox({\n  className,\n  size = \"md\",\n  label = \"All permissions\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { kids, parent, spring, toggleParent, toggleChild } = useGroup({\n    checked,\n    defaultChecked,\n    onCheckedChange,\n  })\n  const labelId = React.useId()\n  return (\n    <div className={cn(\"flex w-full max-w-xs flex-col rounded-lg border border-border\", className)}>\n      <span\n        className={cn(\n          rowBase,\n          \"border-b border-border bg-[color-mix(in_oklab,var(--color-foreground)_4%,transparent)] px-3 py-2.5\"\n        )}\n      >\n        <StateBox\n          state={parent}\n          size={size}\n          spring={spring}\n          labelId={labelId}\n          onPress={toggleParent}\n          {...props}\n        />\n        <span\n          id={labelId}\n          onClick={toggleParent}\n          className={cn(textSize[size], \"cursor-pointer select-none font-medium text-foreground\")}\n        >\n          {label}\n        </span>\n      </span>\n      <div className=\"flex flex-col gap-2 px-3 py-2.5\">\n        {CHILD_LABELS.map((child, i) => (\n          <ChildRow\n            key={child}\n            label={child}\n            state={kids[i]}\n            size={size}\n            spring={spring}\n            onPress={() => toggleChild(i)}\n          />\n        ))}\n      </div>\n    </div>\n  )\n}\n\n/* Tree: the children are connected like a tree with vertical and horizontal\n   connector lines. */\nexport function TreeCheckbox({\n  className,\n  size = \"md\",\n  label = \"src\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { kids, parent, spring, toggleParent, toggleChild } = useGroup({\n    checked,\n    defaultChecked,\n    onCheckedChange,\n  })\n  const labelId = React.useId()\n  return (\n    <div className={cn(\"flex flex-col gap-2\", className)}>\n      <span className={rowBase}>\n        <StateBox\n          state={parent}\n          size={size}\n          spring={spring}\n          labelId={labelId}\n          onPress={toggleParent}\n          {...props}\n        />\n        <span\n          id={labelId}\n          onClick={toggleParent}\n          className={cn(textSize[size], \"cursor-pointer select-none font-medium text-foreground\")}\n        >\n          {label}\n        </span>\n      </span>\n      <div className=\"relative flex flex-col gap-2 pl-6\">\n        <span\n          aria-hidden=\"true\"\n          className=\"absolute bottom-3 left-2 top-0 w-px bg-border\"\n        />\n        {CHILD_LABELS.map((child, i) => (\n          <span key={child} className=\"relative\">\n            <span aria-hidden=\"true\" className=\"absolute left-[-1rem] top-1/2 h-px w-3 bg-border\" />\n            <ChildRow\n              label={child}\n              state={kids[i]}\n              size={size}\n              spring={spring}\n              onPress={() => toggleChild(i)}\n            />\n          </span>\n        ))}\n      </div>\n    </div>\n  )\n}\n\n/* Partial: ebeveynin yaninda kac cocugun secili oldugunu gosteren token cubuk. */\nexport function PartialCheckbox({\n  className,\n  size = \"md\",\n  label = \"Selected filters\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { kids, parent, spring, toggleParent, toggleChild } = useGroup({\n    checked,\n    defaultChecked,\n    onCheckedChange,\n  })\n  const labelId = React.useId()\n  const done = kids.filter(Boolean).length\n  return (\n    <div className={cn(\"flex w-full max-w-xs flex-col gap-2\", className)}>\n      <span className={cn(rowBase, \"w-full\")}>\n        <StateBox\n          state={parent}\n          size={size}\n          spring={spring}\n          labelId={labelId}\n          onPress={toggleParent}\n          {...props}\n        />\n        <span\n          id={labelId}\n          onClick={toggleParent}\n          className={cn(textSize[size], \"cursor-pointer select-none font-medium text-foreground\")}\n        >\n          {label}\n        </span>\n        <span className={cn(textSize[size], \"ml-auto tabular-nums text-muted-foreground\")}>\n          {done}/{kids.length}\n        </span>\n      </span>\n      <span aria-hidden=\"true\" className=\"h-1 w-full overflow-hidden rounded-full bg-muted\">\n        <motion.span\n          initial={false}\n          animate={{ scaleX: done / kids.length }}\n          transition={spring}\n          className=\"block h-full w-full origin-left rounded-full bg-primary\"\n        />\n      </span>\n      <div className=\"flex flex-col gap-2 pl-6\">\n        {CHILD_LABELS.map((child, i) => (\n          <ChildRow\n            key={child}\n            label={child}\n            state={kids[i]}\n            size={size}\n            spring={spring}\n            onPress={() => toggleChild(i)}\n          />\n        ))}\n      </div>\n    </div>\n  )\n}\n\n/* All: a \"select all\" header, with the children in a separated list. */\nexport function AllCheckbox({\n  className,\n  size = \"md\",\n  label = \"Select all\",\n  checked,\n  defaultChecked,\n  onCheckedChange,\n  ...props\n}: CheckboxProps) {\n  const { kids, parent, spring, toggleParent, toggleChild } = useGroup({\n    checked,\n    defaultChecked,\n    onCheckedChange,\n  })\n  const labelId = React.useId()\n  const done = kids.filter(Boolean).length\n  return (\n    <div className={cn(\"flex w-full max-w-xs flex-col gap-2\", className)}>\n      <span className={cn(rowBase, \"w-full\")}>\n        <StateBox\n          state={parent}\n          size={size}\n          spring={spring}\n          labelId={labelId}\n          onPress={toggleParent}\n          {...props}\n        />\n        <span\n          id={labelId}\n          onClick={toggleParent}\n          className={cn(\n            textSize[size],\n            \"cursor-pointer select-none font-semibold uppercase tracking-wide text-foreground\"\n          )}\n        >\n          {label}\n        </span>\n        <span className={cn(textSize[size], \"ml-auto text-muted-foreground\")}>\n          {done} selected\n        </span>\n      </span>\n      <span aria-hidden=\"true\" className=\"h-px w-full bg-border\" />\n      <div className=\"flex flex-col gap-2\">\n        {CHILD_LABELS.map((child, i) => (\n          <ChildRow\n            key={child}\n            label={child}\n            state={kids[i]}\n            size={size}\n            spring={spring}\n            onPress={() => toggleChild(i)}\n          />\n        ))}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/checkbox-indeterminate.tsx"
    }
  ],
  "categories": [
    "styled",
    "checkbox"
  ],
  "type": "registry:component"
}