Toggle
Un botón de dos estados cuyo ícono cambia con un fundido cruzado, con la presión estándar.
Instalación
$ pnpm dlx shadcn@latest add @ssntl/toggleUso
import { Toggle } from "@/components/ui/toggle"
<Toggle aria-label="Guardar producto" off={<Bookmark />} on={<BookmarkCheck />} />
<Toggle off={<Bell />} on={<BellRing />}>Seguir novedades</Toggle>Reglas
Lo que este componente espera de ti, tal como se instala en components/ui/toggle.md junto al código. El archivo completo agrega cuándo usarlo, teclado, los fundamentos de UI que aplica y cada regla compartida (en inglés).
## Reglas de Toggle
- **Respuesta al alternar y seleccionar.** La presión estándar de 0.96 en 150ms, nunca una compresión lenta o profunda.
- El ícono cambia con un fundido cruzado entre sus dos estados; `aria-pressed` lleva el estado a los lectores de pantalla.
- Un toggle solo de ícono necesita un `aria-label` que nombre la acción, no el estado.Código
"use client"
import * as React from "react"
import { Toggle as TogglePrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/registry/ssntl/ui/button"
// A two-state button (Radix Toggle, aria-pressed): save, follow, pin.
// Toggle and selection feedback: the same 0.96 press in 150ms as every
// button, and the icon cross-fades between its two states (icon-swap), so
// the change reads instantly and the state stays in the icon and aria-pressed.
function Toggle({
className,
children,
on,
off,
pressed,
defaultPressed = false,
onPressedChange,
...props
}: Omit<React.ComponentProps<typeof TogglePrimitive.Root>, "children"> & {
/** The icon while pressed. */
on: React.ReactNode
/** The icon while not pressed. */
off: React.ReactNode
/** A visible label; without one, pass aria-label. */
children?: React.ReactNode
}) {
const [inner, setInner] = React.useState(defaultPressed)
const current = pressed ?? inner
return (
<TogglePrimitive.Root
data-slot="toggle"
pressed={current}
onPressedChange={(next) => {
setInner(next)
onPressedChange?.(next)
}}
className={cn(
buttonVariants({ variant: "ghost", size: children ? "md" : "icon" }),
!children && "px-0",
"data-[state=on]:text-foreground",
className
)}
{...props}
>
<span
className="icon-swap"
data-swapped={current}
data-icon={children ? "inline-start" : undefined}
>
{off}
{on}
</span>
{children}
</TogglePrimitive.Root>
)
}
export { Toggle }