{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "checkbox-group",
  "type": "registry:ui",
  "title": "Checkbox group",
  "description": "Several independent choices in a form: row-size checkboxes with labels and an optional parent; ticking the parent ticks its rows in a cascade from the top, clearing clears them at once.",
  "dependencies": [
    "@base-ui/react@^1.8.0"
  ],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/components/checkbox-group/checkbox-group.tsx",
      "type": "registry:ui",
      "target": "components/metalui/checkbox-group/checkbox-group.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { CheckboxGroup as BaseCheckboxGroup } from '@base-ui/react/checkbox-group';\nimport { Checkbox } from '../checkbox/checkbox';\n\n/* ─────────────────────────────────────────────────────────\n * CHECKBOX GROUP, several independent choices in a form, on Base UI CheckboxGroup\n *\n *   rows      the row-size checkbox beside its label; the whole row is the hit area, and pressing it\n *             anywhere gives the checkbox's press (as the radio group's rows do)\n *   parent    optional: ticks or clears every row; some ticked shows the half (mixed) look\n *   cascade   ticking several at once (the parent) ticks them from the top, one row every\n *             30 ms, each drawing the checkbox's own tick (40 ms beat, 220 ms draw)\n *   clear     clearing several at once clears them together: letting go is quicker than taking\n *   focus     the green ring on the checkbox; Tab moves row to row, Space ticks\n *   disabled  the row at 40 %\n * Reduce Motion: no cascade (its steps scale with the part travel); ticks are instant.\n * The checkboxes are the checkbox recipe; the checkbox-group recipe adds rows and the cascade.\n * Slots: CheckboxGroup.Root, CheckboxGroup.Parent, CheckboxGroup.Item.\n * ───────────────────────────────────────────────────────── */\n\nconst ROOT = 'mu-checkbox-group grid gap-checkbox-group-gap';\nconst ROW = 'mu-checkbox-group-row inline-flex items-center gap-checkbox-group-row-gap min-h-checkbox-group-row-height type-ui text-ink cursor-pointer select-none w-max checkbox-group-cascade checkbox-group-press has-data-disabled:opacity-checkbox-group-disabled has-data-disabled:cursor-default';\nconst CHILD = 'pl-checkbox-group-indent';\n\nconst CascadeCtx = React.createContext<{ step: (value: string) => number; hasParent: boolean }>({ step: () => 0, hasParent: false });\n\nexport interface CheckboxGroupProps extends Omit<BaseCheckboxGroup.Props, 'className' | 'value' | 'defaultValue' | 'onValueChange'> {\n  value?: string[];\n  defaultValue?: string[];\n  onValueChange?: (value: string[]) => void;\n  /** Every item's value, in order: needed for a parent, and the order of the cascade. */\n  allValues?: string[];\n  className?: string;\n}\n\nfunction Root({ value, defaultValue, onValueChange, allValues, className, children, ...props }: CheckboxGroupProps) {\n  const [own, setOwn] = React.useState<string[]>(defaultValue ?? []);\n  const current = value ?? own;\n  const [added, setAdded] = React.useState<string[]>([]);\n  const order = allValues ?? [];\n  const hasParent = React.Children.toArray(children).some((c) => React.isValidElement(c) && c.type === Parent);\n  const step = React.useCallback((v: string) => {\n    const i = added.indexOf(v);\n    return i < 0 ? 0 : i;\n  }, [added]);\n  return (\n    <CascadeCtx.Provider value={{ step, hasParent }}>\n      <BaseCheckboxGroup\n        className={className ? `${ROOT} ${className}` : ROOT}\n        value={current}\n        allValues={allValues}\n        onValueChange={(next) => {\n          const fresh = (next as string[]).filter((v) => !current.includes(v));\n          setAdded(fresh.length > 1 ? [...fresh].sort((a, b) => order.indexOf(a) - order.indexOf(b)) : []);\n          if (value === undefined) setOwn(next as string[]);\n          onValueChange?.(next as string[]);\n        }}\n        {...props}\n      >\n        {children}\n      </BaseCheckboxGroup>\n    </CascadeCtx.Provider>\n  );\n}\n\n/** Ticks or clears every item; half-filled when some are ticked. */\nfunction Parent({ children, disabled }: { children: React.ReactNode; disabled?: boolean }) {\n  return (\n    <label className={ROW}>\n      <Checkbox size=\"row\" parent disabled={disabled} />\n      <span className=\"mu-checkbox-group-label\">{children}</span>\n    </label>\n  );\n}\n\nexport interface CheckboxGroupItemProps {\n  value: string;\n  children: React.ReactNode;\n  disabled?: boolean;\n}\n\n/** One choice: the checkbox and its label. */\nfunction Item({ value, children, disabled }: CheckboxGroupItemProps) {\n  const { step, hasParent } = React.useContext(CascadeCtx);\n  return (\n    <label className={hasParent ? `${ROW} ${CHILD}` : ROW} style={{ '--mu-cascade-step': step(value) } as React.CSSProperties}>\n      <Checkbox size=\"row\" value={value} disabled={disabled} />\n      <span className=\"mu-checkbox-group-label\">{children}</span>\n    </label>\n  );\n}\n\nexport const CheckboxGroup = Object.assign(Root, { Parent, Item, Root });\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/checkbox-group.md. SwiftUI: MetalCheckboxGroup in the MetalUI Swift package."
}
