{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button-group-styled",
  "title": "Group-styled button",
  "description": "Styled button: the Group-styled set. 5 decorative button variations (AttachedGroup, SplitGroup, ToolbarGroup, PillGroup, StepperGroup) on the ai2 token system, driven by CSS token transitions and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/button-group-styled.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Minus, Plus } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Styled button-group family: binds several buttons into a single visual unit (shared edges, rounded first and last). These are ACTION buttons (not a selection toggle). Colour comes ONLY from semantic tokens; alpha through color-mix, never var(--x)/0.4. Deterministic. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst sizes: Record<StyledSize, string> = {\n  sm: \"h-8 px-3 text-sm\",\n  md: \"h-9 px-4 text-sm\",\n  lg: \"h-10 px-5 text-sm\",\n  xl: \"h-12 px-6 text-base\",\n}\n\nconst iconSizes: Record<StyledSize, string> = {\n  sm: \"size-8\",\n  md: \"size-9\",\n  lg: \"size-10\",\n  xl: \"size-12\",\n}\n\n/* A single button inside the group: outline/secondary token appearance, focus-visible ring. On focus z-10 keeps the ring above the neighbouring buttons. */\nconst item =\n  \"relative inline-flex shrink-0 select-none items-center justify-center gap-2 font-medium whitespace-nowrap outline-none transition-colors focus-visible:z-10 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst outline =\n  \"border border-field-border bg-background text-foreground hover:bg-secondary hover:text-secondary-foreground\"\n\ntype Action = {\n  label?: React.ReactNode\n  icon?: React.ReactNode\n  onClick?: () => void\n}\n\ntype GroupProps = {\n  className?: string\n  size?: StyledSize\n  actions?: Action[]\n}\n\nconst defaultActions: Action[] = [\n  { label: \"Left\" },\n  { label: \"Center\" },\n  { label: \"Right\" },\n]\n\nconst defaultIconActions: Action[] = [\n  { label: \"Bold\", icon: <span className=\"font-bold\">B</span> },\n  { label: \"Italic\", icon: <span className=\"italic\">I</span> },\n  { label: \"Underline\", icon: <span className=\"underline\">U</span> },\n]\n\n/* AttachedGroup: kenarlarini paylasan temiz bir buton sirasi. */\nexport function AttachedGroup({ className, size = \"md\", actions = defaultActions }: GroupProps) {\n  return (\n    <div\n      data-slot=\"styled-button-group\"\n      role=\"group\"\n      className={cn(\"inline-flex isolate\", className)}\n    >\n      {actions.map((action, i) => (\n        <button\n          key={i}\n          type=\"button\"\n          onClick={action.onClick}\n          data-slot=\"styled-button-group-item\"\n          className={cn(\n            item,\n            outline,\n            sizes[size],\n            i > 0 && \"-ml-px\",\n            i === 0 && \"rounded-l-lg\",\n            i === actions.length - 1 && \"rounded-r-lg\"\n          )}\n        >\n          {action.icon}\n          {action.label}\n        </button>\n      ))}\n    </div>\n  )\n}\n\n/* SplitGroup: birincil aksiyon butonu + sagina bagli caret butonu.\n   Caret kucuk bir token menuyu acar/kapatir. */\nexport function SplitGroup({ className, size = \"md\", actions = defaultActions }: GroupProps) {\n  const [open, setOpen] = React.useState(false)\n  const primary = actions[0]\n  const rest = actions.slice(1)\n\n  return (\n    <div\n      data-slot=\"styled-button-group\"\n      role=\"group\"\n      className={cn(\"relative inline-flex isolate\", className)}\n    >\n      <button\n        type=\"button\"\n        onClick={primary?.onClick}\n        data-slot=\"styled-button-group-item\"\n        className={cn(\n          item,\n          \"rounded-l-lg bg-primary text-primary-foreground hover:bg-primary/90\",\n          sizes[size]\n        )}\n      >\n        {primary?.icon}\n        {primary?.label}\n      </button>\n      <button\n        type=\"button\"\n        aria-haspopup=\"menu\"\n        aria-expanded={open}\n        aria-label=\"More actions\"\n        onClick={() => setOpen((v) => !v)}\n        data-slot=\"styled-button-group-caret\"\n        className={cn(\n          item,\n          \"-ml-px rounded-r-lg border-l border-primary-foreground/20 bg-primary px-2 text-primary-foreground hover:bg-primary/90\",\n          iconSizes[size]\n        )}\n      >\n        <ChevronDown className={cn(\"transition-transform\", open && \"rotate-180\")} />\n      </button>\n\n      {open ? (\n        <div\n          role=\"menu\"\n          data-slot=\"styled-button-group-menu\"\n          className=\"absolute top-full right-0 z-20 mt-1 min-w-40 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\"\n        >\n          {rest.map((action, i) => (\n            <button\n              key={i}\n              type=\"button\"\n              role=\"menuitem\"\n              onClick={() => {\n                action.onClick?.()\n                setOpen(false)\n              }}\n              className={cn(\n                \"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-secondary hover:text-secondary-foreground focus-visible:bg-secondary focus-visible:text-secondary-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n              )}\n            >\n              {action.icon}\n              {action.label}\n            </button>\n          ))}\n        </div>\n      ) : null}\n    </div>\n  )\n}\n\n/* ToolbarGroup: kume halinde ikon butonlari, kumeler arasinda ince token ayirici. */\nexport function ToolbarGroup({ className, size = \"md\", actions = defaultIconActions }: GroupProps) {\n  const mid = Math.ceil(actions.length / 2)\n  const clusters = [actions.slice(0, mid), actions.slice(mid)].filter((c) => c.length > 0)\n\n  return (\n    <div\n      data-slot=\"styled-button-group\"\n      role=\"group\"\n      className={cn(\n        \"inline-flex items-center gap-1 rounded-lg border border-field-border bg-background p-1\",\n        className\n      )}\n    >\n      {clusters.map((cluster, ci) => (\n        <React.Fragment key={ci}>\n          {ci > 0 ? (\n            <span\n              aria-hidden=\"true\"\n              data-slot=\"styled-button-group-divider\"\n              className=\"mx-0.5 h-5 w-px bg-border\"\n            />\n          ) : null}\n          <div className=\"flex items-center gap-0.5\">\n            {cluster.map((action, i) => (\n              <button\n                key={i}\n                type=\"button\"\n                onClick={action.onClick}\n                aria-label={typeof action.label === \"string\" ? action.label : undefined}\n                data-slot=\"styled-button-group-item\"\n                className={cn(\n                  item,\n                  \"rounded-md text-foreground hover:bg-secondary hover:text-secondary-foreground\",\n                  iconSizes[size]\n                )}\n              >\n                {action.icon ?? action.label}\n              </button>\n            ))}\n          </div>\n        </React.Fragment>\n      ))}\n    </div>\n  )\n}\n\n/* PillGroup: butonlari tasiyan tamamen yuvarlatilmis pill kabi. */\nexport function PillGroup({ className, size = \"md\", actions = defaultActions }: GroupProps) {\n  return (\n    <div\n      data-slot=\"styled-button-group\"\n      role=\"group\"\n      className={cn(\n        \"inline-flex items-center gap-1 rounded-full border border-field-border bg-secondary/40 p-1\",\n        className\n      )}\n    >\n      {actions.map((action, i) => (\n        <button\n          key={i}\n          type=\"button\"\n          onClick={action.onClick}\n          data-slot=\"styled-button-group-item\"\n          className={cn(\n            item,\n            \"rounded-full text-foreground hover:bg-background hover:text-foreground\",\n            sizes[size]\n          )}\n        >\n          {action.icon}\n          {action.label}\n        </button>\n      ))}\n    </div>\n  )\n}\n\n/* StepperGroup: a minus button, a value display and a plus button (a numeric stepper). The number is held in state. */\nexport function StepperGroup({ className, size = \"md\", actions }: GroupProps) {\n  const [value, setValue] = React.useState(0)\n  void actions\n\n  return (\n    <div\n      data-slot=\"styled-button-group\"\n      role=\"group\"\n      className={cn(\"inline-flex isolate\", className)}\n    >\n      <button\n        type=\"button\"\n        aria-label=\"Decrease\"\n        onClick={() => setValue((v) => v - 1)}\n        data-slot=\"styled-button-group-item\"\n        className={cn(item, outline, \"rounded-l-lg\", iconSizes[size])}\n      >\n        <Minus />\n      </button>\n      <output\n        aria-live=\"polite\"\n        data-slot=\"styled-button-group-value\"\n        className={cn(\n          item,\n          \"-ml-px -mr-px min-w-12 border-y border-field-border bg-background tabular-nums text-foreground\",\n          sizes[size]\n        )}\n      >\n        {value}\n      </output>\n      <button\n        type=\"button\"\n        aria-label=\"Increase\"\n        onClick={() => setValue((v) => v + 1)}\n        data-slot=\"styled-button-group-item\"\n        className={cn(item, outline, \"rounded-r-lg\", iconSizes[size])}\n      >\n        <Plus />\n      </button>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/button-group-styled.tsx"
    }
  ],
  "categories": [
    "styled",
    "button"
  ],
  "type": "registry:component"
}