{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "toggle-group-styled",
  "title": "Group-styled toggle",
  "description": "Styled toggle: the Group-styled set. 5 decorative toggle variations (SegmentedToggleGroup, PillToggleGroup, IconToggleGroup, OutlineToggleGroup, GhostToggleGroup) 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/toggle-group-styled.tsx",
      "content": "\"use client\"\r\n\r\nimport * as React from \"react\"\r\nimport { AlignCenter, AlignLeft, AlignRight, Bold, Italic, Underline } from \"lucide-react\"\r\nimport { motion, useReducedMotion } from \"motion/react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\n/* Toggle group family: 5 decorative connected-button rows. A row of toggle buttons;\n   single-select (like a segmented control) or multi-select (like a formatting bar)\n   through the `type` prop. Every button is a real <button> + aria-pressed + a\n   focus-visible ring. Color comes ONLY from tokens (via alpha color-mix, never\n   var(--x)/0.4). The framer-motion layoutId sliding indicator is disabled through\n   useReducedMotion. Controlled (value) or uncontrolled (defaultValue). */\r\n\r\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\r\n\r\ntype Option = { value: string; label?: React.ReactNode; icon?: React.ReactNode }\r\n\r\ntype ToggleGroupProps = {\r\n  className?: string\r\n  size?: StyledSize\r\n  type?: \"single\" | \"multiple\"\r\n  options?: Option[]\r\n  value?: string | string[]\r\n  defaultValue?: string | string[]\r\n  onValueChange?: (v: string | string[]) => void\r\n}\r\n\r\nconst sizeBtn: Record<StyledSize, string> = {\r\n  sm: \"h-8 px-3 text-xs\",\r\n  md: \"h-9 px-4 text-sm\",\r\n  lg: \"h-10 px-5 text-sm\",\r\n  xl: \"h-11 px-6 text-base\",\r\n}\r\n\r\nconst sizeIconBtn: Record<StyledSize, string> = {\r\n  sm: \"size-8 text-xs\",\r\n  md: \"size-9 text-sm\",\r\n  lg: \"size-10 text-sm\",\r\n  xl: \"size-11 text-base\",\r\n}\r\n\r\nconst btnBase =\r\n  \"relative z-10 inline-flex select-none items-center justify-center gap-2 whitespace-nowrap font-medium outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\r\n\r\nconst TEXT_OPTIONS: Option[] = [\r\n  { value: \"left\", label: \"Left\", icon: <AlignLeft /> },\r\n  { value: \"center\", label: \"Center\", icon: <AlignCenter /> },\r\n  { value: \"right\", label: \"Right\", icon: <AlignRight /> },\r\n]\r\n\r\nconst ICON_OPTIONS: Option[] = [\r\n  { value: \"bold\", label: \"Bold\", icon: <Bold /> },\r\n  { value: \"italic\", label: \"Italic\", icon: <Italic /> },\r\n  { value: \"underline\", label: \"Underline\", icon: <Underline /> },\r\n]\r\n\r\nfunction toArray(v: string | string[] | undefined): string[] {\r\n  if (v === undefined) return []\r\n  return Array.isArray(v) ? v : [v]\r\n}\r\n\r\nfunction useToggleGroup(props: ToggleGroupProps, fallback: Option[]) {\r\n  const { type = \"single\", options, value, defaultValue, onValueChange } = props\r\n  const list = React.useMemo(() => (options && options.length ? options : fallback), [options, fallback])\r\n  const controlled = value !== undefined\r\n  const [internal, setInternal] = React.useState<string[]>(() => {\r\n    const init = toArray(defaultValue)\r\n    if (init.length === 0 && type === \"single\" && defaultValue === undefined) {\r\n      return list[0] ? [list[0].value] : []\r\n    }\r\n    return init\r\n  })\r\n  const selected = controlled ? toArray(value) : internal\r\n\r\n  const emit = React.useCallback(\r\n    (next: string[]) => {\r\n      if (!controlled) setInternal(next)\r\n      onValueChange?.(type === \"single\" ? (next[0] ?? \"\") : next)\r\n    },\r\n    [controlled, onValueChange, type]\r\n  )\r\n\r\n  const toggle = React.useCallback(\r\n    (v: string) => {\r\n      const on = selected.includes(v)\r\n      if (type === \"single\") {\r\n        emit(on ? [] : [v])\r\n      } else {\r\n        emit(on ? selected.filter((s) => s !== v) : [...selected, v])\r\n      }\r\n    },\r\n    [selected, type, emit]\r\n  )\r\n\r\n  const isOn = React.useCallback((v: string) => selected.includes(v), [selected])\r\n  return { list, isOn, toggle }\r\n}\r\n\r\nfunction useSlide() {\r\n  const reduce = useReducedMotion()\r\n  return reduce ? { duration: 0 } : ({ type: \"spring\", stiffness: 420, damping: 34 } as const)\r\n}\r\n\r\nfunction labelText(o: Option): string {\r\n  return typeof o.label === \"string\" ? o.label : o.value\r\n}\r\n\r\n/* Segmented: tek-secim segment kontrol; aktifin altinda token dolgulu gosterge\r\n   framer layoutId ile kayar. */\r\nexport function SegmentedToggleGroup({ className, size = \"md\", options, value, defaultValue, onValueChange }: ToggleGroupProps) {\r\n  const { list, isOn, toggle } = useToggleGroup({ type: \"single\", options, value, defaultValue, onValueChange }, TEXT_OPTIONS)\r\n  const id = React.useId()\r\n  const transition = useSlide()\r\n  return (\r\n    <div\r\n      data-slot=\"styled-toggle-group-viewport\"\r\n      className=\"-m-1 flex max-w-full overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n    >\r\n      <div\r\n        data-slot=\"styled-toggle-group\"\r\n        role=\"group\"\r\n        className={cn(\"inline-flex items-center gap-0.5 rounded-lg border border-border bg-muted p-1\", className)}\r\n      >\r\n        {list.map((o) => {\r\n          const on = isOn(o.value)\r\n          return (\r\n            <button\r\n              key={o.value}\r\n              type=\"button\"\r\n              aria-pressed={on}\r\n              data-slot=\"styled-toggle-group-item\"\r\n              onClick={() => toggle(o.value)}\r\n              className={cn(btnBase, sizeBtn[size], \"rounded-md\", on ? \"text-foreground\" : \"text-muted-foreground hover:text-foreground\")}\r\n            >\r\n              {on && (\r\n                <motion.span\r\n                  layoutId={`${id}-seg`}\r\n                  transition={transition}\r\n                  className=\"absolute inset-0 -z-10 rounded-md border border-border bg-background shadow-sm\"\r\n                />\r\n              )}\r\n              {o.icon}\r\n              {o.label}\r\n            </button>\r\n          )\r\n        })}\r\n      </div>\r\n    </div>\r\n  )\r\n}\r\n\r\n/* Pill: every option is a pill; the active one fills with primary. */\r\nexport function PillToggleGroup({ className, size = \"md\", type = \"single\", options, value, defaultValue, onValueChange }: ToggleGroupProps) {\r\n  const { list, isOn, toggle } = useToggleGroup({ type, options, value, defaultValue, onValueChange }, TEXT_OPTIONS)\r\n  return (\r\n    <div\r\n      data-slot=\"styled-toggle-group-viewport\"\r\n      className=\"-m-1 flex max-w-full overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n    >\r\n      <div data-slot=\"styled-toggle-group\" role=\"group\" className={cn(\"inline-flex flex-wrap items-center gap-2\", className)}>\r\n        {list.map((o) => {\r\n          const on = isOn(o.value)\r\n          return (\r\n            <button\r\n              key={o.value}\r\n              type=\"button\"\r\n              aria-pressed={on}\r\n              data-slot=\"styled-toggle-group-item\"\r\n              onClick={() => toggle(o.value)}\r\n              className={cn(\r\n                btnBase,\r\n                sizeBtn[size],\r\n                \"rounded-full border\",\r\n                on ? \"border-transparent bg-primary text-primary-foreground\" : \"border-border bg-background text-muted-foreground hover:text-foreground\"\r\n              )}\r\n            >\r\n              {o.icon}\r\n              {o.label}\r\n            </button>\r\n          )\r\n        })}\r\n      </div>\r\n    </div>\r\n  )\r\n}\r\n\r\n/* Icon: ikon dugmeler, coklu-secim bicimlendirme cubugu; basili = token tint. */\r\nexport function IconToggleGroup({ className, size = \"md\", type = \"multiple\", options, value, defaultValue, onValueChange }: ToggleGroupProps) {\r\n  const { list, isOn, toggle } = useToggleGroup({ type, options, value, defaultValue, onValueChange }, ICON_OPTIONS)\r\n  return (\r\n    <div\r\n      data-slot=\"styled-toggle-group-viewport\"\r\n      className=\"-m-1 flex max-w-full overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n    >\r\n      <div\r\n        data-slot=\"styled-toggle-group\"\r\n        role=\"group\"\r\n        className={cn(\"inline-flex items-center gap-1 rounded-lg border border-border bg-background p-1\", className)}\r\n      >\r\n        {list.map((o) => {\r\n          const on = isOn(o.value)\r\n          return (\r\n            <button\r\n              key={o.value}\r\n              type=\"button\"\r\n              aria-pressed={on}\r\n              aria-label={labelText(o)}\r\n              title={labelText(o)}\r\n              data-slot=\"styled-toggle-group-item\"\r\n              onClick={() => toggle(o.value)}\r\n              style={on ? { backgroundColor: \"color-mix(in oklab, var(--primary) 14%, transparent)\" } : undefined}\r\n              className={cn(btnBase, sizeIconBtn[size], \"rounded-md\", on ? \"text-primary\" : \"text-muted-foreground hover:text-foreground\")}\r\n            >\r\n              {o.icon ?? o.label}\r\n            </button>\r\n          )\r\n        })}\r\n      </div>\r\n    </div>\r\n  )\r\n}\r\n\r\n/* Outline: bagli outline dugmeler; aktif token dolgu alir. */\r\nexport function OutlineToggleGroup({ className, size = \"md\", type = \"single\", options, value, defaultValue, onValueChange }: ToggleGroupProps) {\r\n  const { list, isOn, toggle } = useToggleGroup({ type, options, value, defaultValue, onValueChange }, TEXT_OPTIONS)\r\n  return (\r\n    <div\r\n      data-slot=\"styled-toggle-group-viewport\"\r\n      className=\"-m-1 flex max-w-full overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n    >\r\n      <div\r\n        data-slot=\"styled-toggle-group\"\r\n        role=\"group\"\r\n        className={cn(\"inline-flex items-center overflow-hidden rounded-lg border border-border\", className)}\r\n      >\r\n        {list.map((o) => {\r\n          const on = isOn(o.value)\r\n          return (\r\n            <button\r\n              key={o.value}\r\n              type=\"button\"\r\n              aria-pressed={on}\r\n              data-slot=\"styled-toggle-group-item\"\r\n              onClick={() => toggle(o.value)}\r\n              className={cn(\r\n                btnBase,\r\n                sizeBtn[size],\r\n                \"rounded-none [&:not(:first-child)]:border-l [&:not(:first-child)]:border-border\",\r\n                on ? \"bg-primary text-primary-foreground\" : \"text-muted-foreground hover:bg-muted hover:text-foreground\"\r\n              )}\r\n            >\r\n              {o.icon}\r\n              {o.label}\r\n            </button>\r\n          )\r\n        })}\r\n      </div>\r\n    </div>\r\n  )\r\n}\r\n\r\n/* Ghost: kenarliksiz; aktif yumusak token arka plan alir. */\r\nexport function GhostToggleGroup({ className, size = \"md\", type = \"single\", options, value, defaultValue, onValueChange }: ToggleGroupProps) {\r\n  const { list, isOn, toggle } = useToggleGroup({ type, options, value, defaultValue, onValueChange }, TEXT_OPTIONS)\r\n  return (\r\n    <div\r\n      data-slot=\"styled-toggle-group-viewport\"\r\n      className=\"-m-1 flex max-w-full overflow-x-auto p-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\r\n    >\r\n      <div data-slot=\"styled-toggle-group\" role=\"group\" className={cn(\"inline-flex items-center gap-1\", className)}>\r\n        {list.map((o) => {\r\n          const on = isOn(o.value)\r\n          return (\r\n            <button\r\n              key={o.value}\r\n              type=\"button\"\r\n              aria-pressed={on}\r\n              data-slot=\"styled-toggle-group-item\"\r\n              onClick={() => toggle(o.value)}\r\n              style={on ? { backgroundColor: \"color-mix(in oklab, var(--primary) 12%, transparent)\" } : undefined}\r\n              className={cn(\r\n                btnBase,\r\n                sizeBtn[size],\r\n                \"rounded-md\",\r\n                on ? \"text-primary\" : \"text-muted-foreground hover:bg-muted hover:text-foreground\"\r\n              )}\r\n            >\r\n              {o.icon}\r\n              {o.label}\r\n            </button>\r\n          )\r\n        })}\r\n      </div>\r\n    </div>\r\n  )\r\n}\r\n",
      "type": "registry:component",
      "target": "components/ui/toggle-group-styled.tsx"
    }
  ],
  "categories": [
    "styled",
    "toggle"
  ],
  "type": "registry:component"
}