button-group.tsx 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { mergeProps } from '@base-ui/react/merge-props';
  2. import { useRender } from '@base-ui/react/use-render';
  3. import { cva, type VariantProps } from 'class-variance-authority';
  4. import { cn } from '@/lib/utils';
  5. import { Separator } from '@/components/ui/separator';
  6. const buttonGroupVariants = cva(
  7. "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
  8. {
  9. variants: {
  10. orientation: {
  11. horizontal:
  12. '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! *:data-slot:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0',
  13. vertical:
  14. '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! flex-col *:data-slot:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0',
  15. },
  16. },
  17. defaultVariants: {
  18. orientation: 'horizontal',
  19. },
  20. },
  21. );
  22. function ButtonGroup({
  23. className,
  24. orientation,
  25. ...props
  26. }: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) {
  27. return (
  28. <div
  29. role="group"
  30. data-slot="button-group"
  31. data-orientation={orientation}
  32. className={cn(buttonGroupVariants({ orientation }), className)}
  33. {...props}
  34. />
  35. );
  36. }
  37. function ButtonGroupText({
  38. className,
  39. render,
  40. ...props
  41. }: useRender.ComponentProps<'div'>) {
  42. return useRender({
  43. defaultTagName: 'div',
  44. props: mergeProps<'div'>(
  45. {
  46. className: cn(
  47. "bg-muted gap-2 rounded-lg border px-2.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
  48. className,
  49. ),
  50. },
  51. props,
  52. ),
  53. render,
  54. state: {
  55. slot: 'button-group-text',
  56. },
  57. });
  58. }
  59. function ButtonGroupSeparator({
  60. className,
  61. orientation = 'vertical',
  62. ...props
  63. }: React.ComponentProps<typeof Separator>) {
  64. return (
  65. <Separator
  66. data-slot="button-group-separator"
  67. orientation={orientation}
  68. className={cn(
  69. 'bg-input relative self-stretch data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto',
  70. className,
  71. )}
  72. {...props}
  73. />
  74. );
  75. }
  76. export {
  77. ButtonGroup,
  78. ButtonGroupSeparator,
  79. ButtonGroupText,
  80. buttonGroupVariants,
  81. };