# NotificationsPopover

> La campana de la barra con el contador de lo no leído y un popover con la lista de avisos y «Marcar todas como leídas».

```tsx
import { NotificationsPopover } from "sebs7n-ui/notifications-popover"
```

## Ejemplos

### En una barra

La campana lleva el contador y su nombre dice el número («Avisos, 3 sin leer»). Abrir el popover no marca nada: «Marcar todas como leídas» sí.

```tsx
import { NotificationsPopover } from "sebs7n-ui/notifications-popover"
import { Toolbar } from "sebs7n-ui/toolbar"

function InToolbar() {
  return (
    <Toolbar aria-label="Facturación" className="w-full max-w-md">
      <span className="flex-1 px-2 text-headline text-label">Facturas</span>
      <NotificationsPopover items={NOTIFICATIONS} />
    </Toolbar>
  )
}
```

### Leídos de la app

Con `read` y `onReadChange` los ids leídos los lleva la app: acá se muestran debajo, pero es donde se guardarían en el servidor.

```tsx
import { NotificationsPopover } from "sebs7n-ui/notifications-popover"
import { useState } from "react"

function Controlled() {
  const [read, setRead] = useState<string[]>(["n-2"])
  return (
    <div className="flex items-center gap-3">
      <NotificationsPopover items={NOTIFICATIONS} onReadChange={setRead} read={read} />
      <span className="text-callout text-label-secondary">Leídos: {read.join(", ") || "ninguno"}</span>
    </div>
  )
}
```

### Sin avisos

La lista vacía lo dice y «Marcar todas» queda apagado.

```tsx
import { NotificationsPopover } from "sebs7n-ui/notifications-popover"

function Empty() {
  return <NotificationsPopover items={[]} />
}
```

## Props

### NotificationsPopover

| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
| `items` * | `readonly NotificationItem[]` | — | Los avisos, del más nuevo al más viejo: `{ id, title, description?, time?, tone? }`. El `id` es lo que se guarda como leído. |
| `align` | `"center" \| "end" \| "start"` | `"end"` | Alineación del popover respecto de la campana. Por defecto, `end`. |
| `contentClassName` | `string` | — | Clases del contenido del popover. |
| `defaultRead` | `readonly string[]` | `[]` | Los `id` leídos al montar, sin controlar. |
| `label` | `string` | — | El nombre del botón de la campana, sin el contador: «Avisos». Por defecto, `labels.title`. |
| `labels` | `Partial<NotificationsLabels>` | — | Textos: `title`, `unreadDetail`, `upToDate`, `empty`, `markAllRead` y `unreadBadge`. Por defecto, `notificationsLabels`. |
| `onReadChange` | `(read: string[]) => void` | — | Avisa los `id` leídos cuando cambian (al marcar todas): el total, no solo lo nuevo. |
| `read` | `readonly string[]` | — | Los `id` leídos, controlado: va con `onReadChange`. |
| `className` | `string` | — | Clases del botón de la campana. |

`*` obligatoria.

## Teclado

| Tecla | Qué hace |
|---|---|
| Enter · Espacio | En la campana, abre el popover; en «Marcar todas como leídas», marca los avisos. |
| Tab | Recorre lo de adentro: el botón de marcar todas. |
| Escape | Cierra el popover y devuelve el foco a la campana. |

## Accesibilidad

- El nombre de la campana dice el número con todas las letras: «Avisos, 3 sin leer» (`countLabel`). El contador de adentro es decorativo.
- Un aviso sin leer se distingue por el punto de color, el texto en negrita **y** la etiqueta «Nueva»: el color nunca es el único dato.
- El popover se nombra con su título, y la lista (`<ul>`) con el mismo nombre. En pantallas angostas se abre como la hoja de abajo, como cualquier `Popover`.
- **Un aviso nuevo no se anuncia**: el nombre de la campana se lee al enfocarla. Si importa enterarse en el momento, la app lo avisa en una región viva (`role="status"`).

## Reglas de uso

- La app pasa los avisos en `items` (`{ id, title, description?, time?, tone? }`, del más nuevo al más viejo) y el componente no sabe de dónde salen.
- Leer es una acción: abrir el popover **no** marca nada. «Marcar todas como leídas» queda a la vista y se apaga cuando no queda ninguna.
- Los leídos son del componente (`defaultRead`) o de la app (`read` + `onReadChange`, que recibe el total de ids leídos): lo segundo para guardarlos en el servidor o en `useStoredState`.
- `time` va ya formateado, en el idioma de la app. Solo por subpath (`sebs7n-ui/notifications-popover`).

## Relacionados

[count-badge](/docs/components/count-badge.md) · [popover](/docs/components/popover.md) · [list-row](/docs/components/list-row.md)
