Contenido y datos
Widget Board
Un panel de widgets que el usuario edita, como la pantalla de inicio de iCloud: una grilla estática de cards que, con «Editar», se reordena arrastrando o con el teclado, saca widgets con su «−», los vuelve a agregar desde un catálogo y se restablece. Se guarda en localStorage.
import { WidgetBoard, WidgetBoardEditButton } from "sebs7n-ui/widget-board"Ejemplos
Panel editable
«Editar» pone los widgets en modo edición: tiemblan, se reordenan arrastrando o con Espacio y las flechas, se sacan con su «−» y vuelven desde «Agregar widget». «Restablecer» deja el panel original. El orden se guarda en este navegador.
import { WidgetBoard, WidgetBoardEditButton } from "sebs7n-ui/widget-board"
import { useWidgetLayout } from "sebs7n-ui/lib/widget-layout"
function Editable() {
const layout = useWidgetLayout({ storageKey: "demo:widget-board", widgets: WIDGETS })
return (
<div className="@container flex w-full flex-col gap-4">
<div className="flex justify-end">
<WidgetBoardEditButton layout={layout} size="sm" />
</div>
<WidgetBoard layout={layout} />
</div>
)
}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».
WidgetBoard
Hereda las props de <div>.
| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
layout* | WidgetLayout | — | El estado: lo que devuelve useWidgetLayout. |
gridClassName | string | — | Clases de la grilla, por ejemplo otro gap. |
className | string | — | Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana. |
WidgetBoardEditButton
| Prop | Tipo | Por defecto | Descripción |
|---|---|---|---|
layout* | WidgetLayout | — | El estado: lo que devuelve useWidgetLayout. |
loading | boolean | — | Como en Button: deshabilita y marca el botón como ocupado. |
size | "sm" | "md" | "lg" | "icon-sm" | "icon-md" | "icon-lg" | — | El tamaño del botón, como en Button. En la cabecera de una pantalla, md (por defecto). |
className | string | — | Se fusiona con las clases del componente vía cn() (tailwind-merge): lo que pongas gana. |
Teclado
- Tab
- Recorre los botones de la barra de edición, los «−» y, en cada card, lo interactivo de adentro.
- Espacio
- En un widget en edición lo toma; con otro Espacio lo suelta en su nuevo lugar.
- ← → ↑ ↓
- Con un widget tomado, lo mueven por la grilla.
- Escape
- Cancela el movimiento; sin un widget tomado, sale de la edición.
- Enter · Espacio
- En un «−» saca el widget; en una fila del catálogo lo agrega.
Accesibilidad
- La grilla es una lista con nombre («Widgets»). Entrar en edición, sacar, agregar, mover y restablecer se anuncian en español en regiones
role="status". - Cada «−» y cada fila del catálogo se llaman con el widget («Sacar Clientes», «Agregar Clientes»). Al sacar uno el foco pasa al «−» que queda (o a «Agregar widget» si no queda ninguno); al agregar, al «−» del nuevo.
- Con movimiento reducido el temblor se apaga y un contorno punteado dice que está en edición. La vista previa del catálogo es decorativa (
aria-hidden). - Sin widgets en pantalla hay un
EmptyStatecon «Agregar widget» y «Restablecer»: el panel nunca queda mudo.
Reglas de uso
- Tres piezas:
useWidgetLayout({ storageKey, widgets })(el estado),<WidgetBoard layout />(la grilla) y<WidgetBoardEditButton layout />(el «Editar»/«Listo» de la cabecera, secundario: la acción primaria sigue siendo la de la pantalla). - Cada widget es
{ id, title, size?, description?, preview?, icon?, render }.size:sm(1 columna de 4),md(2) olg(4); por debajo de 36 rem de ancho del contenedor son 2 columnas y por debajo de 56 rem, 1. Las cards de una fila quedan alineadas: la card llena el alto de su celda. - Carga diferida: sin editar es una grilla estática (sin
@dnd-kit); el arrastre, la barra y el catálogo se piden la primera vez que se aprieta «Editar» (React.lazy), con la misma grilla de fondo mientras llegan. Los peers opcionales@dnd-kit/*hacen falta solo si se usa. - El HTML del servidor muestra el orden original; lo guardado se adopta después de montar. Si el panel depende de un proyecto o una cuenta, una clave por cada uno y
keyen la pantalla para que el estado no se arrastre. - Solo por subpath (
sebs7n-ui/widget-board); el modelo puro (moveWidget,removeWidget,addWidget,reorderWidgets,serializeLayout) sale desebs7n-ui/lib/widget-layout.