Direcciones · Vista mobile lista de nombres + detalle full-screen
Direcciones · Vista mobile lista de nombres + detalle full-screen
Resumen
Commit 031c209 (2026-05-13, @Esquembri) añade soporte mobile completo a la tool Direcciones (/tools/directions), siguiendo el mismo patrón ya establecido en el Cuaderno mobile (commit 9a0c41b).
Petición de Edu: “en mobile mejor resumir un listado solo con los Nombres y al clickar un nombre que se abran los datos en una nueva página”.
Las 3 vistas de render
| Vista | Condición | Descripción |
|---|---|---|
| Desktop split | window.innerWidth > 768 | Layout actual: columnas 1fr 420px. Sin cambios funcionales. |
| Mobile lista | isMobile && selected === null | <ul> compacto con nombre + avatar pequeño + chip Persona/Empresa + chevron ›. Tabla densa de 5 columnas sustituida. |
| Mobile detalle | isMobile && selected !== null | position: fixed; inset: 0; z-index: 40. Barra superior con back / título / botón editar. |
Detección de breakpoint
const MOBILE_BREAKPOINT = 768;
function detectMobile(): boolean {
if (typeof window === 'undefined') return false;
return window.innerWidth <= MOBILE_BREAKPOINT;
}
El estado isMobile se inicializa con useState(() => detectMobile()) y se mantiene actualizado mediante un listener resize (soporte a rotación y redimensionado de ventana).
Sincronización URL ↔ ficha activa
La URL actúa como fuente de verdad en mobile:
| URL | Estado |
|---|---|
/tools/directions | Vista lista (ninguna ficha abierta) |
/tools/directions?id=N | Vista detalle full-screen (ficha N) |
Mecanismo:
pushIdToUrl(id)— escribe enwindow.history.pushStateal abrir una ficha.readIdFromUrl()— lee?id=al montar el componente y al recibirpopstate.- Listener
popstate— el botón back del navegador cierra el detalle y vuelve a la lista.
Comportamiento fetchAll en mobile
En desktop, fetchAll auto-selecciona la primera ficha para llenar el panel lateral. En mobile, arranca con selectedId = null (vista lista vacía):
setSelectedId((current) => {
if (current && data.find((d) => d.id === current)) return current;
if (detectMobile()) return null; // ← mobile arranca en lista
return data[0]?.id ?? null;
});
Componente DirectionDetail (refactor)
El contenido del aside de detalle se extrajo a un componente funcional reutilizable:
function DirectionDetail({ direction, onEdit }: { direction: Direction; onEdit: () => void })
Contiene: cabecera (avatar + nombre + NIF + tags) + secciones Identificación / Contacto / Dirección / Notas / Actividad + botones vCard / Editar.
Se reusa en:
- Desktop: dentro del
<aside>lateral. - Mobile: dentro de
<div className="directions-mobile-body">en la vista full-screen.
CSS relevante (tools.css)
Corrección breakpoint tablet
/* Antes: */
@media (max-width: 1100px) { .directions-split { … } }
/* Después: solo tablet, NO mobile */
@media (max-width: 1100px) and (min-width: 769px) { .directions-split { … } }
Nuevo bloque @media (max-width: 768px) (~130 LOC)
.directions-mobile-list .directions-detail-pane { display: none }— oculta el aside desktop en vista lista mobile..directions-mobile-name-list—<ul>reset con filas separadas porvar(--border-soft)..directions-mobile-name-row—buttonflex,min-height: 56pxpara tap area ergonómica,:activeconvar(--bg-raised-2)..directions-mobile-detail—position: fixed; inset: 0; z-index: 40; background: var(--bg)..directions-mobile-bar— barra superior conbackdrop-filter: blur(20px)yenv(safe-area-inset-top)para notch iOS..directions-mobile-body—flex: 1; overflow-y: auto; padding: 1rem.
Archivos cambiados
| Archivo | LOC+ | LOC- | Tipo |
|---|---|---|---|
src/components/directions/DirectionsPage.tsx | ~280 | ~130 | refactor + feat |
src/styles/tools.css | ~130 | ~5 | feat (media queries) |
Véase también
- [[concept—ux—mobile-lista-detalle-pattern]]