Search

Jump to any page.

Actions

Toggle Group

Segmented control for switching between views or filters.

pnpm dlx shadcn@latest add @paragon/toggle-group
Brings in@paragon/toggle@base-ui/reactcn

Multiple

Outline toggles for filters where several can be on.

registry/paragon/ui/toggle-group.tsx
"use client"

import * as React from "react"
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
import { cn } from "cn"

import { toggleVariants } from "@/registry/paragon/ui/toggle"

type Variant = "segmented" | "default" | "outline"
type Size = "sm" | "default" | "lg"

const ToggleGroupContext = React.createContext<{ variant: Variant; size: Size }>({
  variant: "segmented",
  size: "default",
})

/**
 * A set of toggles. `segmented` (the default) is the Paragon switcher: a sunken
 * track with the active option raised as a card, like TabsList.
 */
function ToggleGroup({
  className,
  variant = "segmented",
  size = "default",
  orientation = "horizontal",
  children,
  ...props
}: ToggleGroupPrimitive.Props & {
  variant?: Variant
  size?: Size
  orientation?: "horizontal" | "vertical"
}) {
  return (
    <ToggleGroupPrimitive
      data-slot="toggle-group"
      data-variant={variant}
      data-size={size}
      data-orientation={orientation}
      orientation={orientation}
      className={cn(
        "group/toggle-group flex w-fit items-center gap-1 data-vertical:flex-col data-vertical:items-stretch",
        variant === "segmented" && "rounded-button bg-secondary p-1",
        className
      )}
      {...props}
    >
      <ToggleGroupContext.Provider value={{ variant, size }}>{children}</ToggleGroupContext.Provider>
    </ToggleGroupPrimitive>
  )
}

function ToggleGroupItem({ className, children, ...props }: TogglePrimitive.Props) {
  const { variant, size } = React.useContext(ToggleGroupContext)

  return (
    <TogglePrimitive
      data-slot="toggle-group-item"
      className={cn(
        toggleVariants({ variant: variant === "outline" ? "outline" : "default", size }),
        variant === "segmented" &&
          "rounded-[calc(var(--radius-button)-4px)] hover:bg-transparent data-pressed:bg-card data-pressed:shadow-[0_1px_2px_rgb(19_20_17/0.08),0_0_0_1px_rgb(19_20_17/0.05)] dark:data-pressed:bg-input/30 dark:data-pressed:shadow-[0_0_0_1px_rgb(255_255_255/0.06)]",
        className
      )}
      {...props}
    >
      {children}
    </TogglePrimitive>
  )
}

export { ToggleGroup, ToggleGroupItem }