{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-toggle-group",
  "title": "Glass Toggle Group",
  "description": "A segmented control with a sliding glass puck indicator supporting icons, text, or any React content.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@glasscn/utils",
    "@glasscn/radio-group",
    "@glasscn/liquid-glass"
  ],
  "files": [
    {
      "path": "components/ui/glasscn/glass-toggle-group.tsx",
      "content": "\"use client\";\n\nimport { motion, useAnimationControls } from \"motion/react\";\nimport { createContext, useCallback, useContext, useEffect, useRef, useState } from \"react\";\n\nimport { LiquidGlass, type LiquidGlassProps } from \"@/components/ui/glasscn/liquid-glass\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { cn } from \"@/lib/utils\";\n\ntype PuckStyle = { left: number; width: number };\n\ntype GlassToggleGroupContextValue = { registerItem: (value: string, element: HTMLButtonElement | null) => void };\n\nconst GlassToggleGroupContext = createContext<GlassToggleGroupContextValue | null>(null);\n\ntype GlassToggleGroupProps = Omit<LiquidGlassProps, \"children\"> & {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  children: React.ReactNode;\n  \"aria-label\"?: string;\n};\n\nfunction GlassToggleGroup({\n  value,\n  defaultValue,\n  onValueChange,\n  children,\n  className,\n  \"aria-label\": ariaLabel,\n  blur,\n  refraction,\n  mapSize,\n  bezel,\n  ...props\n}: GlassToggleGroupProps) {\n  const [currentValue, setCurrentValue] = useState(value ?? defaultValue ?? \"\");\n  const [puckStyle, setPuckStyle] = useState<PuckStyle | null>(null);\n  const itemRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n  const containerRef = useRef<HTMLDivElement>(null);\n  const puckControls = useAnimationControls();\n  const isFirstRender = useRef(true);\n\n  const actualValue = value ?? currentValue;\n\n  const handleValueChange = (newValue: string) => {\n    if (value === undefined) {\n      setCurrentValue(newValue);\n    }\n    onValueChange?.(newValue);\n  };\n\n  const registerItem = useCallback((itemValue: string, element: HTMLButtonElement | null) => {\n    if (element) {\n      itemRefs.current.set(itemValue, element);\n    } else {\n      itemRefs.current.delete(itemValue);\n    }\n  }, []);\n\n  useEffect(() => {\n    const updatePuck = () => {\n      const selectedElement = itemRefs.current.get(actualValue);\n      const container = containerRef.current;\n\n      if (selectedElement && container) {\n        const containerRect = container.getBoundingClientRect();\n        const itemRect = selectedElement.getBoundingClientRect();\n        const newLeft = itemRect.left - containerRect.left;\n        const newWidth = itemRect.width;\n\n        setPuckStyle({ left: newLeft, width: newWidth });\n\n        if (isFirstRender.current) {\n          isFirstRender.current = false;\n          puckControls.set({ left: newLeft, width: newWidth, scaleY: 1 });\n        } else {\n          puckControls.start({ left: newLeft, width: newWidth, scaleY: [1, 1.25, 1] });\n        }\n      }\n    };\n\n    updatePuck();\n    window.addEventListener(\"resize\", updatePuck);\n    return () => window.removeEventListener(\"resize\", updatePuck);\n  }, [actualValue, puckControls]);\n\n  return (\n    <GlassToggleGroupContext.Provider value={{ registerItem }}>\n      <LiquidGlass\n        blur={blur}\n        refraction={refraction}\n        mapSize={mapSize}\n        bezel={bezel}\n        className={cn(\"inline-block overflow-visible rounded-full\", className)}\n        {...props}\n      >\n        <RadioGroup\n          ref={containerRef}\n          value={actualValue}\n          onValueChange={handleValueChange}\n          aria-label={ariaLabel}\n          className=\"relative flex w-full items-center gap-0\"\n        >\n          {puckStyle ? (\n            <motion.span\n              aria-hidden\n              className={cn(\n                \"pointer-events-none absolute top-0 h-full rounded-full\",\n                \"bg-gradient-to-b from-white/50 to-white/30 dark:from-white/[0.12] dark:to-white/[0.04]\",\n                \"border border-white/40 dark:border-white/[0.06]\",\n                \"shadow-[0_-1px_2px_rgba(255,255,255,0.8),0_1px_1px_rgba(0,0,0,0.08),0_2px_4px_rgba(0,0,0,0.1),0_4px_8px_rgba(0,0,0,0.1),0_8px_16px_rgba(0,0,0,0.08),inset_0_1px_1px_rgba(255,255,255,0.9),inset_0_-1px_1px_rgba(0,0,0,0.05)]\",\n                \"dark:shadow-[0_-1px_2px_rgba(255,255,255,0.1),0_1px_1px_rgba(0,0,0,0.2),0_2px_4px_rgba(0,0,0,0.2),0_4px_8px_rgba(0,0,0,0.25),0_8px_16px_rgba(0,0,0,0.2),inset_0_1px_1px_rgba(255,255,255,0.12),inset_0_-1px_1px_rgba(0,0,0,0.3)]\",\n              )}\n              initial={{ left: puckStyle.left, width: puckStyle.width, scaleY: 1 }}\n              animate={puckControls}\n              transition={{\n                left: { type: \"spring\", stiffness: 400, damping: 30 },\n                width: { type: \"spring\", stiffness: 400, damping: 30 },\n                scaleY: { duration: 0.3, ease: \"easeInOut\" },\n              }}\n            />\n          ) : null}\n          {children}\n        </RadioGroup>\n      </LiquidGlass>\n    </GlassToggleGroupContext.Provider>\n  );\n}\n\ntype GlassToggleGroupItemProps = {\n  value: string;\n  children: React.ReactNode;\n  className?: string;\n  \"aria-label\"?: string;\n};\n\nfunction GlassToggleGroupItem({ value, children, className, \"aria-label\": ariaLabel }: GlassToggleGroupItemProps) {\n  const context = useContext(GlassToggleGroupContext);\n  const ref = useRef<HTMLButtonElement>(null);\n\n  useEffect(() => {\n    context?.registerItem(value, ref.current);\n    return () => context?.registerItem(value, null);\n  }, [context, value]);\n\n  return (\n    <RadioGroupItem\n      ref={ref}\n      value={value}\n      aria-label={ariaLabel}\n      className={cn(\n        \"relative z-10 inline-flex h-auto w-auto cursor-pointer items-center justify-center rounded-full px-4 py-2\",\n        \"aspect-auto shrink-0 border-0 bg-transparent shadow-none after:hidden dark:bg-transparent data-checked:bg-transparent dark:data-checked:bg-transparent\",\n        \"text-sm font-medium text-foreground/60 transition-colors duration-200\",\n        \"hover:text-foreground/85\",\n        \"data-checked:text-foreground\",\n        \"focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent\",\n        className,\n      )}\n    >\n      {children}\n    </RadioGroupItem>\n  );\n}\n\nexport { GlassToggleGroup, GlassToggleGroupItem };\nexport type { GlassToggleGroupProps, GlassToggleGroupItemProps };\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}