CreaRack-SL

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

VistaCondiciónDescripción
Desktop splitwindow.innerWidth > 768Layout actual: columnas 1fr 420px. Sin cambios funcionales.
Mobile listaisMobile && selected === null<ul> compacto con nombre + avatar pequeño + chip Persona/Empresa + chevron ›. Tabla densa de 5 columnas sustituida.
Mobile detalleisMobile && selected !== nullposition: 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:

URLEstado
/tools/directionsVista lista (ninguna ficha abierta)
/tools/directions?id=NVista detalle full-screen (ficha N)

Mecanismo:

  • pushIdToUrl(id) — escribe en window.history.pushState al abrir una ficha.
  • readIdFromUrl() — lee ?id= al montar el componente y al recibir popstate.
  • 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 por var(--border-soft).
  • .directions-mobile-name-row — button flex, min-height: 56px para tap area ergonómica, :active con var(--bg-raised-2).
  • .directions-mobile-detail — position: fixed; inset: 0; z-index: 40; background: var(--bg).
  • .directions-mobile-bar — barra superior con backdrop-filter: blur(20px) y env(safe-area-inset-top) para notch iOS.
  • .directions-mobile-body — flex: 1; overflow-y: auto; padding: 1rem.

Archivos cambiados

ArchivoLOC+LOC-Tipo
src/components/directions/DirectionsPage.tsx~280~130refactor + feat
src/styles/tools.css~130~5feat (media queries)

Véase también

  • [[concept—ux—mobile-lista-detalle-pattern]]