# ListIndex

> El índice A–Z de Contactos: una tira de letras junto a una lista agrupada, cada una un link a su sección, las vacías apagadas y, con el dedo, se desliza.

```tsx
import { ListIndex } from "sebs7n-ui/list-index"
```

## Ejemplos

### Clientes de la A a la Z

Una lista agrupada por inicial con la tira de letras al costado: cada letra es un link a su sección y las que no tienen clientes se ven apagadas. En un teléfono, apoyá el dedo en la tira y deslizalo.

```tsx
import { List, ListRow, ListSection } from "sebs7n-ui/list-row"
import { ListIndex } from "sebs7n-ui/list-index"

function Clientes() {
  return (
    <div className="relative h-[660px] w-full max-w-md overflow-hidden rounded-surface border border-separator">
      <div className="h-full overflow-y-auto scroll-smooth pe-8 motion-reduce:scroll-auto">
        <List aria-label="Clientes">
          {SECTIONS.map(([letter, names]) => (
            <ListSection className="scroll-mt-2" id={`customer-${letter}`} key={letter} title={letter}>
              {names.map((name) => (
                <ListRow key={name} title={name} />
              ))}
            </ListSection>
          ))}
        </List>
      </div>
      <ListIndex
        available={SECTIONS.map(([letter]) => letter)}
        className="absolute inset-y-2 end-1"
        getHref={(letter) => `#customer-${letter}`}
      />
    </div>
  )
}
```

## Props

### ListIndex

Hereda las props de `<nav>`.

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `available` * | `readonly string[]` | — | Las letras que tienen sección. Las demás se ven apagadas y no son links. |
| `getHref` | `(letter: string) => string` | `defaultHref` | El destino de cada letra. Por defecto `#A`, `#B`…: la sección lleva ese `id`. Deslizar, mover solo la caja y pasar el foco con Enter necesitan un `#…` a un id de la página; con otro destino, la letra es un link común. |
| `labels` | `Partial<NonNullable<Labels["listIndex"]>>` | — | Textos: `label` (el nombre del `nav`, «Índice alfabético»). El que viene por defecto es `listIndexLabels`: un provider sin el grupo `listIndex` (es opcional) cae a ese texto en español. |
| `letters` | `readonly string[]` | `ALPHABET` | Todas las letras de la tira, en orden. Por defecto, A–Z. |
| `onPointerCancel` | `PointerEventHandler<T>` | — | Se suma al de la tira, que termina el deslizamiento igual. |
| `onPointerDown` | `PointerEventHandler<T>` | — | Corre antes de empezar a deslizar: con `event.preventDefault()` la tira no se desliza. |
| `onPointerMove` | `PointerEventHandler<T>` | — | Corre antes de mover el deslizamiento: con `event.preventDefault()` ese movimiento no cambia de letra. |
| `onPointerUp` | `PointerEventHandler<T>` | — | Se suma al de la tira, que termina el deslizamiento igual. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

`*` obligatoria.

## Teclado

| Tecla | Qué hace |
|---|---|
| Tab | Recorre las letras que tienen sección; las apagadas no son paradas. |
| Enter | Sigue el link: la lista va a la sección de esa letra y el foco pasa a la sección (Tab sigue desde ahí). |

## Accesibilidad

- Es un `<nav>` llamado «Índice alfabético» (`labels.listIndex.label`) con una `<ol>` de links: el lector lo ofrece en su lista de regiones y dice cuántas letras hay.
- Las letras sin sección son `role="link"` + `aria-disabled="true"` sin `href`: se leen «atenuado» en vez de desaparecer, y no son paradas de Tab.
- Cada letra ocupa 24 de ancho y el alto se reparte: en una tira de 624 o más, cada una llega a 24×24 (WCAG 2.5.8). Más corta, dale alto al contenedor o acortá `letters`.
- Deslizar es un atajo del dedo: lo mismo se logra tocando cada letra, o scrolleando la lista.

## Reglas de uso

- **Para listas largas ordenadas por nombre** (clientes, proveedores) que ya están agrupadas por inicial: cada `ListSection` lleva `id` con su letra (`id="A"`), o el que devuelva `getHref`.
- `available` son las letras que tienen sección; `letters` cambia la tira (sumá `#` o `Ñ` si tu lista las usa).
- Ponela al costado de la lista que scrollea, pegada con `sticky` o `absolute` y con el alto de la lista. La tira no scrollea: se reparte el alto que le den.
- Si la lista scrollea en su propia caja (`overflow-y` `auto`, `scroll` u `overlay`), una letra mueve **solo esa caja** hasta la sección (respeta `scroll-margin-top` y `scroll-smooth`): la página no salta. Si la sección está en la página, es el ancla de siempre.
- Con el dedo (`pointer: coarse`) se desliza: la lista sigue a la letra que está bajo el dedo sin sumar entradas al historial.
- Solo por subpath (`sebs7n-ui/list-index`).

## Relacionados

[list-row](/docs/components/list-row.md) · [sidebar](/docs/components/sidebar.md)
