{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button-group",
  "type": "registry:component",
  "title": "Button Group",
  "description": "Related buttons joined into one control, with separators, text cells and fields between them.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/panelui/button-group.tsx",
      "type": "registry:component",
      "target": "@components/panelui/button-group.tsx",
      "content": "'use client';\n\n/**\n * ButtonGroup — related actions held together in one control.\n *\n * ```tsx\n * <ButtonGroup aria-label=\"History\">\n *   <Button variant=\"outline\">Undo</Button>\n *   <Button variant=\"outline\">Redo</Button>\n * </ButtonGroup>\n * ```\n *\n * ## A track, like the segmented Tabs\n *\n * The group is drawn the way the segmented `Tabs` are: a muted track with\n * rounded corners, and the buttons inside it at the radius one step smaller,\n * so every corner follows the track's. Outline and ghost buttons lie flat in\n * the track with muted labels; the one under the pointer rises out of it the\n * way the chosen tab does, and the short dividers on either side of it fade.\n * A field or a filled button keeps its own surface, so it reads as raised\n * from the start.\n *\n * It does this with child selectors rather than props, so any `Button`,\n * `Input`, `Menu.Trigger` or `ButtonGroup.Text` placed directly inside takes\n * part without being told to.\n *\n * ## Split buttons\n *\n * A group with a `ButtonGroup.Separator` in it is a split button instead: no\n * track, and its filled halves joined edge to edge, with the separator\n * carrying their fill across the seam.\n *\n * ## Groups of groups\n *\n * A group whose children are groups spaces them apart, which is how a\n * toolbar is built: one track per kind of action.\n */\nimport type { ComponentProps } from 'react';\nimport { tv, type VariantProps } from 'tailwind-variants';\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport type { ElementType as RenderElementType } from 'react';\n\n/** Draws a part as another element, or composes it with a component: `render={<a href=\"/\" />}`. */\ntype RenderProp<Tag extends RenderElementType> = { render?: useRender.ComponentProps<Tag>['render'] };\n\n/*\n * Which of the three a group is depends on what it holds, so the rules below\n * are scoped by selector rather than by prop:\n *\n *   track  &:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))\n *   split  &:has(>[data-slot=button-group-separator])\n *\n * Tailwind reads class names whole, so each selector is written out in full\n * in every class that uses it.\n */\nexport const buttonGroupVariants = tv({\n  slots: {\n    root: [\n      'flex w-fit items-stretch',\n      // A focused child draws its ring over its neighbours, not under them.\n      '*:focus-visible:relative *:focus-visible:z-10',\n      // A field takes the room the buttons leave.\n      '[&>[data-slot=input]]:min-w-0 [&>[data-slot=input]]:flex-1',\n\n      // The track, in the segmented Tabs' colour and radius. Its children\n      // take the radius one step down, which is the track's less its padding.\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))]:gap-1 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))]:rounded-xl [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))]:bg-muted [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))]:p-1',\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*]:relative [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*]:rounded-lg',\n      // Outline and ghost buttons lie flat, with muted labels, and rise out\n      // of the track under the pointer like the chosen tab.\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost])]:border-transparent [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost])]:bg-transparent [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost])]:text-muted-foreground [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost])]:shadow-none',\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:bg-background [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:text-foreground [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:shadow-sm',\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:inset-ring [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:inset-ring-foreground/[0.06]',\n      'dark:[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:bg-input dark:[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>:is([data-variant=outline],[data-variant=ghost]):hover:not(:disabled)]:inset-ring-foreground/10',\n      // Short dividers in the gaps, drawn by the second child of each pair so\n      // the first never gets one. None beside a field, which has its own\n      // edge, and none beside the child under the pointer.\n      \"[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:pointer-events-none [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:absolute [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:bg-foreground/10 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:transition-opacity [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:duration-150 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:content-['']\",\n      '[&>[data-slot=input]+*]:before:hidden [&>*+[data-slot=input]]:before:hidden',\n      '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*:hover:not(:disabled)]:before:opacity-0 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*:hover:not(:disabled)+*]:before:opacity-0',\n\n      // Groups inside a group are spaced, one track each.\n      'has-[>[data-slot=button-group]]:gap-2',\n    ],\n    separator: [\n      'relative shrink-0 self-stretch bg-border',\n      // Between filled buttons the separator carries their fill across the\n      // seam, and a short line in their text colour divides it.\n      \"before:absolute before:content-['']\",\n      'has-[+[data-variant=default]]:bg-primary has-[+[data-variant=default]]:before:bg-primary-foreground/25',\n      'has-[+[data-variant=secondary]]:bg-secondary has-[+[data-variant=secondary]]:before:bg-secondary-foreground/15',\n      'has-[+[data-variant=destructive]]:bg-destructive has-[+[data-variant=destructive]]:before:bg-white/30',\n    ],\n    text: [\n      'relative flex shrink-0 items-center gap-2 whitespace-nowrap font-medium text-muted-foreground',\n      '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n    ],\n  },\n  variants: {\n    orientation: {\n      horizontal: {\n        root: [\n          '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:inset-y-2 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:-left-0.5 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:w-px',\n          // A split button's halves meet edge to edge.\n          '[&:has(>[data-slot=button-group-separator])>*:not(:first-child)]:rounded-l-none [&:has(>[data-slot=button-group-separator])>*:not(:last-child)]:rounded-r-none',\n        ],\n        separator: 'w-px before:inset-y-2 before:inset-x-0',\n      },\n      vertical: {\n        root: [\n          'flex-col',\n          '[&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:inset-x-2 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:-top-0.5 [&:not(:has(>[data-slot=button-group-separator],>[data-slot=button-group]))>*+*]:before:h-px',\n          '[&:has(>[data-slot=button-group-separator])>*:not(:first-child)]:rounded-t-none [&:has(>[data-slot=button-group-separator])>*:not(:last-child)]:rounded-b-none',\n        ],\n        separator: 'h-px before:inset-x-2 before:inset-y-0',\n      },\n    },\n    /** Matches the heights of `Button` and `Input` at the same size. */\n    size: {\n      sm: { text: 'h-8 px-2.5 text-[0.8125rem]' },\n      md: { text: 'h-9 px-3 text-sm' },\n      lg: { text: 'h-11 px-3.5 text-[0.9375rem]' },\n    },\n  },\n  defaultVariants: { orientation: 'horizontal', size: 'md' },\n});\n\nexport type ButtonGroupOrientation = NonNullable<VariantProps<typeof buttonGroupVariants>['orientation']>;\nexport type ButtonGroupTextSize = NonNullable<VariantProps<typeof buttonGroupVariants>['size']>;\n\nexport interface ButtonGroupProps extends ComponentProps<'div'> {\n  /** Lay the children out in a row or a column. Default `horizontal`. */\n  orientation?: ButtonGroupOrientation;\n}\n\n/**\n * The group. It is a `role=\"group\"`, so give it an `aria-label` that names\n * what its actions have in common.\n */\nfunction ButtonGroupRoot({\n  orientation = 'horizontal',\n  className,\n  render,\n  ...props\n}: ButtonGroupProps & RenderProp<'div'>) {\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      {\n        role: 'group',\n        'data-slot': 'button-group',\n        'data-orientation': orientation,\n        className: buttonGroupVariants({ orientation }).root({ className }),\n      },\n      props\n    ),\n  });\n}\nButtonGroupRoot.displayName = 'ButtonGroup';\n\nexport interface ButtonGroupSeparatorProps extends ComponentProps<'span'> {\n  /**\n   * Which way the line runs. It follows the group: a row of buttons is divided\n   * by upright lines, a column by flat ones. Default `vertical`, for a row.\n   */\n  orientation?: 'vertical' | 'horizontal';\n}\n\n/**\n * The divider of a split button. A group with one in it drops its track and\n * joins its children edge to edge. Before a primary, secondary or destructive\n * button the separator takes that button's fill, so the two halves read as\n * one, and draws a short line in the button's text colour. It is decorative\n * and hidden from assistive technology.\n */\nfunction ButtonGroupSeparator({\n  orientation = 'vertical',\n  className,\n  render,\n  ...props\n}: ButtonGroupSeparatorProps & RenderProp<'span'>) {\n  const slots = buttonGroupVariants({\n    orientation: orientation === 'vertical' ? 'horizontal' : 'vertical',\n  });\n  return useRender({\n    defaultTagName: 'span',\n    render,\n    props: mergeProps(\n      {\n        'aria-hidden': 'true',\n        'data-slot': 'button-group-separator',\n        'data-orientation': orientation,\n        className: slots.separator({ className }),\n      },\n      props\n    ),\n  });\n}\nButtonGroupSeparator.displayName = 'ButtonGroup.Separator';\n\nexport interface ButtonGroupTextProps extends ComponentProps<'div'> {\n  /** The height, matching the buttons and fields beside it. Default `md`. */\n  size?: ButtonGroupTextSize;\n}\n\n/**\n * A cell that is read rather than pressed: a prefix such as `https://`, a unit,\n * or the value a pair of buttons changes.\n */\nfunction ButtonGroupText({ size, className, render, ...props }: ButtonGroupTextProps & RenderProp<'div'>) {\n  return useRender({\n    defaultTagName: 'div',\n    render,\n    props: mergeProps(\n      { 'data-slot': 'button-group-text', className: buttonGroupVariants({ size }).text({ className }) },\n      props\n    ),\n  });\n}\nButtonGroupText.displayName = 'ButtonGroup.Text';\n\nexport const ButtonGroup = Object.assign(ButtonGroupRoot, {\n  Separator: ButtonGroupSeparator,\n  Text: ButtonGroupText,\n});\n\nexport { ButtonGroupSeparator, ButtonGroupText };\n"
    }
  ]
}
