# FileGrid

> La vista de íconos de iCloud Drive: miniaturas con el filo de 1 px, nombre y tipo abajo, una caja gris con el puntero y la misma caja con un borde del acento en la elegida, el «…» con el menú del ítem y flechas en dos dimensiones.

```tsx
import { FileGrid } from "sebs7n-ui/file-grid"
```

## Ejemplos

### La vista de íconos

Miniaturas con el filo de 1 px de Drive, nombre y tipo abajo, una caja gris con el puntero y la misma caja con un borde del acento en la elegida. Flechas en dos dimensiones y Enter abre. `menu` da las acciones: las abre el «…» (en el ítem con el puntero o con el foco), el click derecho y, con teclado, Shift+F10 o la tecla de menú.

```tsx
import { FileGrid } from "sebs7n-ui/file-grid"

function Basico() {
  return <FileGrid aria-label="Archivos" defaultSelected="f-0012" items={ARCHIVOS} menu={acciones} />
}
```

### Varios elegidos

`selectionMode="multiple"`: ⌘ (o Ctrl) + click suma o saca, ⇧ + click elige el rango y ⌘A todos. Con el teclado, las flechas mueven sin elegir, Espacio suma o saca y ⇧ + flechas extiende en el orden de la grilla. El menú sobre un elegido actúa sobre toda la selección («Descargar 3 elementos»); sobre otro, lo elige solo.

```tsx
import { FileGrid } from "sebs7n-ui/file-grid"
import { useState } from "react"

function Varios() {
  const [selected, setSelected] = useState<string[]>(["f-0012", "f-0013", "nc-0003"])
  return (
    <div className="flex w-full flex-col gap-3">
      <FileGrid aria-label="Archivos" items={ARCHIVOS} menu={acciones} onSelectedChange={(ids) => setSelected(ids)} selected={selected} selectionMode="multiple" />
      <p className="text-callout text-label-secondary" role="status">
        {selected.length === 1 ? "1 elegido" : `${selected.length} elegidos`}
      </p>
    </div>
  )
}
```

## Props

### FileGrid

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `items` * | `FileGridItem[]` | — | Los archivos: `{ id, name, kind?, thumbnail?, folder?, disabled? }`. |
| `defaultSelected` | `string \| string[]` | — | El id elegido al arrancar (en `multiple`, los ids). |
| `menu` | `(item: FileGridItem, selected: FileGridItem[]) => React.ReactNode` | — | Los ítems del menú de un ítem (`ContextMenuItem`): los abre el «…», el click derecho, mantener apretado con el dedo (lo elige, como el click derecho), Shift+F10 y la tecla de menú; nunca el espacio vacío ni un deshabilitado. Recibe el ítem y los elegidos sobre los que actúa. |
| `onKeyDown` | `KeyboardEventHandler<T>` | — | Se llama antes de la navegación por teclado. Con `event.preventDefault()`, la tecla no navega. |
| `onOpen` | `(item: FileGridItem) => void` | — | Enter o doble click sobre un ítem. |
| `onSelectedChange` | `((id: string \| null, item: FileGridItem \| null) => void) \| ((ids: string[], items: FileGridItem[]) => void)` | — | Se llama con el id y el ítem elegidos; en `multiple`, con los ids y los ítems, en el orden de la grilla. |
| `selected` | `string \| string[]` | — | El id elegido (en `multiple`, los ids). Pasarlo lo vuelve controlado. |
| `selectionMode` | `"multiple" \| "single"` | — | `single` (default): un elegido que sigue al foco. `multiple`: ⌘/Ctrl+click, ⇧+click, ⌘A, Espacio y ⇧+flechas; `selected` pasa a ser `string[]`. |
| `className` | `string` | — | Se fusiona con las clases del componente vía `cn()` (tailwind-merge): lo que pongas gana. |

`*` obligatoria.

## Teclado

| Tecla | Qué hace |
|---|---|
| a–z | Salta al siguiente cuyo nombre empieza con lo tipeado (medio segundo entre teclas); la misma letra repetida recorre los que empiezan con ella. |
| Tab | Entra a la grilla en el elegido (o en el primero) y sale de una: la grilla es una sola parada. |
| ← → | El ítem anterior o siguiente. En simple, la selección sigue al foco; en múltiple, solo mueve el foco. |
| ↑ ↓ | El ítem de la fila de arriba o de abajo, el más cercano en horizontal (según el layout real). |
| Home End | El primero y el último. |
| Enter | Abre el ítem (`onOpen`). |
| Espacio | Elige el ítem. En múltiple, lo suma o lo saca. |
| ⇧ + flechas · ⇧ Home End | En múltiple: mueve y elige el rango desde el ancla, en el orden de la grilla. |
| ⌘A · Ctrl+A | En múltiple: elige todos; si ya estaban todos, ninguno. |
| ⌘/Ctrl+click · ⇧+click | En múltiple: suma o saca un ítem · elige el rango desde el ancla. |
| Shift+F10 · tecla de menú | Con `menu`, abre las acciones del ítem enfocado (el mismo menú que el «…» y el click derecho). |

## Accesibilidad

- `role="listbox"` con `option`s y `aria-selected`: **nombre obligatorio** (`aria-label` o `aria-labelledby`).
- Foco itinerante: una sola opción tiene `tabIndex=0`. En una no elegida, el anillo interior de iCloud alrededor del ítem entero; en una elegida el borde ya es del acento, y el foco lo duplica (de 2 a 4 px), así en `multiple` la enfocada se distingue de las otras elegidas.
- El nombre accesible de cada opción es su nombre y su tipo («Factura 0012.pdf, 128 KB»). La miniatura es decorativa: `<img alt="">`.
- El «…» aparece en el ítem con el puntero o con el foco, no en cada elegido. Es para el puntero: sale del orden de Tab y del árbol de accesibilidad, porque un botón adentro de una opción no se puede anunciar. Abre el mismo menú que Shift+F10 o la tecla de menú sobre el ítem enfocado (cada opción lo dice en `aria-keyshortcuts`), así que el teclado llega a las mismas acciones. Al cerrar el menú, el foco vuelve al ítem; si la acción lo sacó, a la que quedó en su lugar.
- Con el puntero, una caja gris sin borde; la elegida (una o varias), la misma caja (`selection-inactive`) más un borde de 2 px del acento por dentro (`selection-border`), como Drive. El borde llega a 3:1 contra la caja, la página y el wallpaper en los dos temas (WCAG 1.4.11; en oscuro es el paso claro de la marca). El nombre sigue en `label`: no depende del color para leerse.
- Con `selectionMode="multiple"`, el listbox lleva `aria-multiselectable="true"`; las flechas mueven sin elegir (el modelo recomendado de WAI-ARIA) y Espacio suma o saca.

## Reglas de uso

- **Las acciones del ítem van en `menu`**: `ContextMenuItem` (o `DropdownMenuItem`, es el mismo componente), sin el `ContextMenuContent`. Un solo juego para el «…», el click derecho y Shift+F10; el segundo argumento son los ítems sobre los que actúa (en `multiple`, la selección). Las más usadas, también en una `Toolbar` arriba de la grilla, como la barra de Drive.
- **Para archivos que se reconocen por cómo se ven**: comprobantes escaneados, logos, fotos de recibos. Si se comparan por tamaño o fecha, es una `Table` (la vista de lista de Drive).
- Miniaturas con `<img alt="">`: se ajustan sin recortar (como Photos) con el filo de 1 px y radio 4. Sin miniatura, el ícono de archivo o de carpeta (`folder`).
- `kind` es lo que va abajo en gris: el tipo, el tamaño o cuántos ítems tiene una carpeta. Uno solo, corto.
- La grilla llena el ancho con columnas de 136 como mínimo: no hay que calcular cuántas entran.
- Selección simple por defecto, como `Tree`. `selectionMode="multiple"` para elegir varios: `selected` pasa a ser `string[]`, y la `Toolbar` de arriba actúa sobre todos.
- Solo por subpath (`sebs7n-ui/file-grid`): no está en el barrel, por peso.

## Relacionados

[tree](/docs/components/tree.md) · [table](/docs/components/table.md) · [context-menu](/docs/components/context-menu.md)
