# Toolbar

> La toolbar de iCloud: a todo el ancho, 44 de alto, botones de ícono de 28, y una sola parada de tabulación para toda la barra.

```tsx
import { Toolbar, ToolbarButton, ToolbarGroup, ToolbarInput, … } from "sebs7n-ui/toolbar"
```

## Ejemplos

### La barra de una app

La toolbar de iCloud: a todo el ancho, 44 de alto, botones de ícono de 28 con el glifo en el acento y un cuadrado gris al pasar el puntero. A la izquierda la vista, en el centro lo que actúa sobre la selección —apagado a .4 mientras no hay nada elegido— y a la derecha buscar y crear.

```tsx
import { PanelLeftIcon, SearchIcon, ShareIcon, SquarePenIcon, Trash2Icon } from "lucide-react"
import { Toolbar, ToolbarButton, ToolbarGroup } from "sebs7n-ui/toolbar"

function Barra() {
  return (
    <div className="w-full overflow-hidden rounded-surface border border-separator">
      <Toolbar aria-label="Acciones de la lista">
        <ToolbarButton aria-label="Mostrar la barra lateral">
          <PanelLeftIcon />
        </ToolbarButton>
        <ToolbarGroup aria-label="Selección" className="mx-auto">
          <ToolbarButton aria-label="Compartir" disabled>
            <ShareIcon />
          </ToolbarButton>
          <ToolbarButton aria-label="Eliminar" disabled>
            <Trash2Icon />
          </ToolbarButton>
        </ToolbarGroup>
        <ToolbarButton aria-label="Buscar">
          <SearchIcon />
        </ToolbarButton>
        <ToolbarButton aria-label="Nuevo">
          <SquarePenIcon />
        </ToolbarButton>
      </Toolbar>
      <div className="h-24 bg-background" />
    </div>
  )
}
```

### La barra del editor de texto

Ocho controles y una sola parada de tabulación: se entra con Tab, se recorre con ← →, se sale con Tab. Sin la barra, llegar del título al cuerpo del artículo cuesta ocho teclas.

```tsx
import { AlignCenterIcon, AlignLeftIcon, AlignRightIcon, BoldIcon, ItalicIcon, LinkIcon, UnderlineIcon } from "lucide-react"
import { ToggleGroup, ToggleGroupItem } from "sebs7n-ui/toggle-group"
import { Toolbar, ToolbarButton, ToolbarGroup, ToolbarLink, ToolbarSeparator } from "sebs7n-ui/toolbar"

function Formato() {
  return (
    <Toolbar aria-label="Formato del artículo">
      <ToggleGroup aria-label="Estilo" className="gap-0.5">
        <ToolbarButton aria-label="Negrita" render={<ToggleGroupItem value="bold" />}>
          <BoldIcon />
        </ToolbarButton>
        <ToolbarButton aria-label="Cursiva" render={<ToggleGroupItem value="italic" />}>
          <ItalicIcon />
        </ToolbarButton>
        <ToolbarButton aria-label="Subrayado" render={<ToggleGroupItem value="underline" />}>
          <UnderlineIcon />
        </ToolbarButton>
      </ToggleGroup>

      <ToolbarSeparator />

      <ToolbarGroup aria-label="Alineación">
        <ToolbarButton aria-label="Alinear a la izquierda">
          <AlignLeftIcon />
        </ToolbarButton>
        <ToolbarButton aria-label="Centrar">
          <AlignCenterIcon />
        </ToolbarButton>
        <ToolbarButton aria-label="Alinear a la derecha">
          <AlignRightIcon />
        </ToolbarButton>
      </ToolbarGroup>

      <ToolbarSeparator />

      <ToolbarButton aria-label="Insertar enlace">
        <LinkIcon />
      </ToolbarButton>

      <ToolbarLink className="ml-auto" href="#">
        Editado hace 5 min
      </ToolbarLink>
    </Toolbar>
  )
}
```

### Un menú y un campo adentro de la barra

`render` mete el trigger de un `DropdownMenu` en el recorrido con flechas — el `DropdownMenu` envuelve la barra porque no renderiza ningún elemento propio. `ToolbarInput` deja que ← → muevan el cursor dentro del texto en vez de saltar al control de al lado.

```tsx
import { Button } from "sebs7n-ui/button"
import { ChevronDownIcon } from "lucide-react"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "sebs7n-ui/dropdown-menu"
import { Toolbar, ToolbarButton, ToolbarInput, ToolbarSeparator } from "sebs7n-ui/toolbar"

function Lienzo() {
  return (
    <DropdownMenu>
      <Toolbar aria-label="Herramientas del lienzo">
        <ToolbarButton render={<DropdownMenuTrigger render={<Button size="sm" variant="ghost" />} />}>
          Insertar
          <ChevronDownIcon />
        </ToolbarButton>

        <ToolbarSeparator />

        <label className="flex items-center gap-2 pl-1 text-callout text-label-secondary">
          Zoom
          <ToolbarInput className="w-16" defaultValue="100" inputMode="numeric" />
        </label>

        <ToolbarSeparator />

        <ToolbarButton render={<Button size="sm" variant="default" />}>Publicar</ToolbarButton>
      </Toolbar>

      <DropdownMenuContent align="start">
        <DropdownMenuItem>Imagen</DropdownMenuItem>
        <DropdownMenuItem>Tabla</DropdownMenuItem>
        <DropdownMenuItem>Gráfico</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Props

### Toolbar

Hereda las props de `Toolbar.Root`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `onKeyDown` | `KeyboardEventHandler<T>` | — | Corre **antes** que el manejador propio de la barra. Si hacés `preventDefault()`, Home y End no mueven el foco. |
| `variant` | `"plain" \| "bar" \| "glass"` | `"bar"` | `bar` (default): la toolbar de iCloud, a todo el ancho con `surface-bar` y el separador abajo · `plain` sin fondo ni borde, para una barra adentro de otra superficie · `glass`: **obsoleta**, alias de `bar` (se borra en 3.0). |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |
| `loopFocus` | `boolean` | — | **Heredada de Base UI.** Si al pasar del último control se vuelve al primero. |
| `orientation` | `"horizontal" \| "vertical"` | — | **Heredada de Base UI.** `vertical` cambia las flechas a ↑ ↓ y da vuelta los separadores. |

### ToolbarButton

Hereda las props de `Toolbar.Button`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `aria-label` | `string` | — | Nombre accesible del elemento. |
| `render` | `React.ReactElement \| ComponentRenderFn<RenderFunctionProps, State>` | — | El componente que pone los estilos. Por defecto, `<Button size="icon-sm" variant="plain" />`. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |
| `focusableWhenDisabled` | `boolean` | — | **Heredada de Base UI.** Deshabilitado pero todavía en el recorrido con flechas. Dejalo en `true`. |

### ToolbarGroup

Hereda las props de `Toolbar.Group`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

### ToolbarInput

Hereda las props de `Toolbar.Input`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

### ToolbarLink

Hereda las props de `Toolbar.Link`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

### ToolbarSeparator

Hereda las props de `Toolbar.Separator`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

## Teclado

| Tecla | Qué hace |
|---|---|
| Tab | Entra a la barra y sale. Veinte botones adentro siguen siendo **una** parada. |
| ← → | Mueve entre controles en una barra horizontal. |
| ↑ ↓ | Lo mismo con `orientation="vertical"`. |
| Home · End | Primer y último control. Lo agrega sebs7n-ui: Base UI las deja apagadas en `Toolbar`. |
| Enter · Espacio | Activa el control enfocado. |
| ← → · Home · End dentro de un `ToolbarInput` | Mueven el cursor en el texto, no saltan de control. |

## Accesibilidad

- Base UI emite `role="toolbar"` con `aria-orientation` y maneja el roving tabindex: un solo hijo tiene `tabIndex=0` a la vez.
- Home y End las pone el componente. El patrón toolbar de la WAI las pide y el composite de Base UI las trae detrás de un flag que `Menubar` prende y `Toolbar` no; en una barra larga son la diferencia entre una tecla y quince flechas.
- `ToolbarGroup` **exige `aria-label` o `aria-labelledby` en el tipo**: sin él el lector anuncia «grupo» y nada más.
- `focusableWhenDisabled` viene en `true`: un control apagado sigue en el recorrido, así se puede leer por qué está apagado y la barra no se mueve abajo de los dedos.
- Los `ToolbarButton` de ícono necesitan `aria-label`: no hay texto que leer.

## Reglas de uso

- **El roving tabindex es toda la razón del componente.** Veinte botones sueltos son veinte paradas de Tab entre el contenido de arriba y el de abajo; quien navega con teclado o con un switch los atraviesa todos cada vez. La barra ya se veía bien con un `<div className="flex gap-1">`.
- **Todo hijo interactivo tiene que ser `ToolbarButton`, `ToolbarLink` o `ToolbarInput`.** Un `<button>` puesto a mano queda fuera del recorrido y se vuelve inalcanzable, porque la barra le sacó el Tab al resto.
- **`render` en vez de estilos nuevos**: `render={<Toggle />}`, `render={<ToggleGroupItem value="bold" />}`, `render={<DropdownMenuTrigger render={<Button />} />}`. El default ya es el botón de ícono de iCloud (`Button plain size="icon-sm"`): 28, glifo en el acento y un cuadrado gris al pasar.
- **Agrupá por intención, como iCloud**: a la izquierda la vista, en el centro lo que actúa sobre la selección (apagado a .4 sin selección, `disabled`), a la derecha buscar y crear.
- `ToolbarInput` es el campo chico de una barra —zoom, ancho de línea—, no un campo de formulario: para eso está `Field` + `Input`, con label, error y descripción.
- Menos de tres o cuatro controles no justifica la barra: son botones sueltos y se acabó.
- `bar` **sobre el wallpaper** pasa sola a `material-translucent`; `plain` sigue sin material.

## Relacionados

[button](/docs/components/button.md) · [toggle-group](/docs/components/toggle-group.md) · [menubar](/docs/components/menubar.md) · [dropdown-menu](/docs/components/dropdown-menu.md)
