Ir al contenido
Navegación

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.

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.

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.

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.

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

Generadas del TypeScript del paquete. Las propias del componente, más las heredadas del primitivo que tienen algo que explicar —marcadas «heredada de Base UI»—. El resto está en la línea «hereda de».

Toolbar

Hereda las props de Toolbar.Root.

PropTipoPor defectoDescripción
onKeyDownKeyboardEventHandler<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).
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.
loopFocusboolean—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.

PropTipoPor defectoDescripción
aria-labelstring—Nombre accesible del elemento.
renderReact.ReactElement | ComponentRenderFn<RenderFunctionProps, State>—El componente que pone los estilos. Por defecto, <Button size="icon-sm" variant="plain" />.
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.
focusableWhenDisabledboolean—Heredada de Base UI. Deshabilitado pero todavía en el recorrido con flechas. Dejalo en true.

ToolbarGroup

Hereda las props de Toolbar.Group.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

ToolbarInput

Hereda las props de Toolbar.Input.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

Hereda las props de Toolbar.Link.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

ToolbarSeparator

Hereda las props de Toolbar.Separator.

PropTipoPor defectoDescripción
classNamestring—Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana.

Teclado

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