QuickLinks — Vista dual Tarjetas / Listado
Resumen
El módulo QuickLinks del Workspace permite a cada usuario gestionar y navegar sus enlaces rápidos. Desde el commit 6a1358e (2026-05-07, @Esquembri), la sección dispone de dos vistas intercambiables:
| Vista | Clase CSS contenedor | Descripción |
|---|---|---|
Tarjetas (grid) | tools-bento-grid | Bento cards agrupadas por categoría. Vista original. |
Listado (list) | tools-list | Filas densas tipo Raycast con 5 columnas: icono · label · URL · descripción · acciones. |
La elección se persiste en localStorage bajo la clave workspace.quicklinks.view y sobrevive a recargas de página.
Motivación
Edu solicitó tener ambas vistas disponibles simultáneamente: la vista de tarjetas bento ya existente (agrupada por categoría) y la del mockup del agente (lista densa, navegación rápida con más información visible a la vez).
Implementación técnica
Tipo View (reemplaza Density)
// QuickLinksPage.tsx
type View = 'grid' | 'list';
const VIEW_STORAGE_KEY = 'workspace.quicklinks.view';
Anteriormente el tipo era Density = 'cozy' | 'compact'. El estado density ha sido renombrado a view y su valor inicial se lee de localStorage:
const [view, setView] = useState<View>(() => {
if (typeof window === 'undefined') return 'grid';
return (localStorage.getItem(VIEW_STORAGE_KEY) as View) ?? 'grid';
});
Un useEffect escribe en localStorage cada vez que el usuario cambia la vista.
Toggle de vista (accesible)
El segmento de control ahora usa role="tablist" / role="tab" + aria-selected para cumplir con accesibilidad ARIA:
<div className="tools-seg" role="tablist" aria-label="Vista">
<button role="tab" aria-selected={view === 'grid'} onClick={() => setView('grid')}>Tarjetas</button>
<button role="tab" aria-selected={view === 'list'} onClick={() => setView('list')}>Listado</button>
</div>
QuickLinkCard — renderizado condicional
El componente recibe view: 'grid' | 'list' (antes density: 'cozy' | 'compact'). Ramifica su JSX según el valor:
view === 'list'→<a className="tools-list-row">con grid de 5 columnas.view === 'grid'→<a className="tools-bento">con el bento original (simplificado: el modo compacto que ocultaba URL y descripción ha sido eliminado).
Los handlers handleEdit / handleDelete han sido extraídos como funciones nombradas compartidas entre ambas ramas, eliminando duplicación.
CSS — Vista listado
.tools-list {
display: flex;
flex-direction: column;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
background: var(--bg-raised);
}
.tools-list-row {
display: grid;
grid-template-columns: 28px minmax(180px, 1fr) minmax(180px, 1.2fr) minmax(0, 2fr) auto;
gap: 0.85rem;
padding: 0.55rem 0.9rem;
}
Responsive (<900 px): las columnas row-url y row-desc se ocultan, el grid colapsa a 3 columnas (28px · 1fr · auto).
Las acciones (Editar / Borrar) aparecen con opacity: 0 y se muestran al hacer hover sobre la fila.
CSS eliminado
La clase .tools-bento.compact (y sus modificadores .bento-desc, .bento-url, .bento-label) ha sido retirada al quedar sin uso.
Columnas de la vista Listado
| Columna | Clase CSS | Visible <900px |
|---|---|---|
| Icono (favicon / iniciales) | .row-icon | ✅ |
Etiqueta (link.label) | .row-label | ✅ |
Host (hostnameOf(url)) | .row-url | ❌ |
| Descripción | .row-desc | ❌ |
| Acciones (Editar / Borrar) | .row-actions | ✅ |
Persistencia
| Clave localStorage | Valores | Default |
|---|---|---|
workspace.quicklinks.view | 'grid' | 'list' | 'grid' |
La lectura es lazy (función init de useState) para ser compatible con SSR / ventana no disponible.
Archivos afectados
| Archivo | Cambio |
|---|---|
src/components/quicklinks/QuickLinksPage.tsx | Density → View, toggle renombrado, persistencia localStorage, container CSS condicional |
src/components/quicklinks/QuickLinkCard.tsx | Prop density → view, rama JSX para vista listado, handlers refactorizados |
src/styles/tools.css | Elimina .tools-bento.compact; añade .tools-list + .tools-list-row + responsive |
Véase también
- [[workspace—que-es-workspace]]
- [[workspace—informes]]