675 lines
18 KiB
Markdown
675 lines
18 KiB
Markdown
|
|
# INTERFAZ MML
|
||
|
|
## Mindmap Linking - Capa de Interfaz
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Concepto Visual
|
||
|
|
|
||
|
|
### 1.1 Referencia
|
||
|
|
|
||
|
|
Estética tipo **MindNode**:
|
||
|
|
- Diseño limpio y minimalista
|
||
|
|
- Líneas curvas orgánicas (bezier)
|
||
|
|
- Colores vibrantes por rama
|
||
|
|
- Tipografía sans-serif moderna
|
||
|
|
|
||
|
|
### 1.2 Principios
|
||
|
|
|
||
|
|
| Principio | Aplicación |
|
||
|
|
|-----------|------------|
|
||
|
|
| Simplicidad | Sin elementos decorativos innecesarios |
|
||
|
|
| Jerarquía | Nodos nivel 1 destacan, subnodos secundarios |
|
||
|
|
| Color | Cada rama tiene identidad cromática |
|
||
|
|
| Espacio | Generoso, respira |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Estructura Visual
|
||
|
|
|
||
|
|
### 2.1 Elementos
|
||
|
|
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────┐
|
||
|
|
│ │
|
||
|
|
│ [Header - Título del proyecto (opcional)] │
|
||
|
|
│ │
|
||
|
|
├─────────────────────────────────────────────────────────────────┤
|
||
|
|
│ │
|
||
|
|
│ NODOS NIVEL 1 LÍNEAS SUBNODOS │
|
||
|
|
│ (izquierda) (curvas) (derecha) │
|
||
|
|
│ │
|
||
|
|
│ Texto subrayado ────────────────► Imagen 16:9 │
|
||
|
|
│ con color + título │
|
||
|
|
│ + botones │
|
||
|
|
│ │
|
||
|
|
└─────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.2 Layout Desktop (>768px)
|
||
|
|
|
||
|
|
```
|
||
|
|
╭─────────────────────────────────────────────────────────────────╮
|
||
|
|
│ Proyecto Demo HST │
|
||
|
|
├─────────────────────────────────────────────────────────────────┤
|
||
|
|
│ │
|
||
|
|
│ ╭─────────────╮ │
|
||
|
|
│ ╭─────│ IMAGEN │ │
|
||
|
|
│ Arquitectura ───────────────┤ ╰─────────────╯ │
|
||
|
|
│ │ plano.pdf │
|
||
|
|
│ │ │
|
||
|
|
│ │ ╭─────────────╮ │
|
||
|
|
│ ╰─────│ IMAGEN │ │
|
||
|
|
│ ╰─────────────╯ │
|
||
|
|
│ secciones.pdf │
|
||
|
|
│ │
|
||
|
|
│ ╭─────────────╮ │
|
||
|
|
│ ╭─────│ IMAGEN │ │
|
||
|
|
│ Renders ────────────────────┤ ╰─────────────╯ │
|
||
|
|
│ │ exterior.jpg │
|
||
|
|
│ │ │
|
||
|
|
│ ╰─────╭─────────────╮ │
|
||
|
|
│ │ IMAGEN │ │
|
||
|
|
│ ╰─────────────╯ │
|
||
|
|
│ interior.jpg │
|
||
|
|
│ │
|
||
|
|
╰─────────────────────────────────────────────────────────────────╯
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.3 Layout Mobile (<768px)
|
||
|
|
|
||
|
|
```
|
||
|
|
╭──────────────────────╮
|
||
|
|
│ Proyecto Demo │
|
||
|
|
├──────────────────────┤
|
||
|
|
│ │
|
||
|
|
│ Arquitectura │
|
||
|
|
│ │ │
|
||
|
|
│ ┌────┴────┐ │
|
||
|
|
│ ▼ ▼ │
|
||
|
|
│ ╭─────╮ ╭─────╮ │
|
||
|
|
│ │ IMG │ │ IMG │ │
|
||
|
|
│ ╰─────╯ ╰─────╯ │
|
||
|
|
│ plano secciones │
|
||
|
|
│ │
|
||
|
|
│ Renders │
|
||
|
|
│ │ │
|
||
|
|
│ ┌────┴────┐ │
|
||
|
|
│ ▼ ▼ │
|
||
|
|
│ ╭─────╮ ╭─────╮ │
|
||
|
|
│ │ IMG │ │ IMG │ │
|
||
|
|
│ ╰─────╯ ╰─────╯ │
|
||
|
|
│ exterior interior │
|
||
|
|
│ │
|
||
|
|
│ ↕ scroll vertical │
|
||
|
|
╰──────────────────────╯
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Componentes
|
||
|
|
|
||
|
|
### 3.1 Header (opcional)
|
||
|
|
|
||
|
|
```html
|
||
|
|
<header id="header">Proyecto Demo HST</header>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
#header {
|
||
|
|
font-size: 24px;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #333;
|
||
|
|
margin-bottom: 40px;
|
||
|
|
padding-left: 20px;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Visibilidad:** Controlada por `mostrar_titulo` en JSON.
|
||
|
|
|
||
|
|
### 3.2 Nodo Nivel 1
|
||
|
|
|
||
|
|
```html
|
||
|
|
<div class="level1-node" data-color="orange" data-node-id="n1">
|
||
|
|
<span class="node-text">Arquitectura</span>
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
.level1-node {
|
||
|
|
position: relative;
|
||
|
|
cursor: pointer;
|
||
|
|
padding: 8px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
.level1-node .node-text {
|
||
|
|
font-size: 18px;
|
||
|
|
font-weight: 500;
|
||
|
|
color: #333;
|
||
|
|
border-bottom: 3px solid currentColor;
|
||
|
|
padding-bottom: 4px;
|
||
|
|
display: inline-block;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Colores por data-color */
|
||
|
|
.level1-node[data-color="orange"] .node-text {
|
||
|
|
color: #FF6B35;
|
||
|
|
border-color: #FF6B35;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Características:**
|
||
|
|
- Solo texto, sin caja ni fondo
|
||
|
|
- Subrayado del color de la rama
|
||
|
|
- Click para expandir/contraer
|
||
|
|
|
||
|
|
### 3.3 Subnodo
|
||
|
|
|
||
|
|
```html
|
||
|
|
<div class="subnode" data-color="orange" data-parent-id="n1">
|
||
|
|
<div class="subnode-image-container">
|
||
|
|
<img src="http://72.62.2.84:8090/arc_arquitectura.png" alt="plano.pdf">
|
||
|
|
<a href="#" download class="btn-download">↓</a>
|
||
|
|
<a href="#" target="_blank" class="btn-preview">👁</a>
|
||
|
|
</div>
|
||
|
|
<div class="subnode-title">plano.pdf</div>
|
||
|
|
</div>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
.subnode {
|
||
|
|
position: relative;
|
||
|
|
width: 200px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.subnode-image-container {
|
||
|
|
position: relative;
|
||
|
|
width: 100%;
|
||
|
|
aspect-ratio: 16/9;
|
||
|
|
border-radius: 8px;
|
||
|
|
overflow: hidden;
|
||
|
|
border: 3px solid #ddd;
|
||
|
|
}
|
||
|
|
|
||
|
|
.subnode-image-container img {
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
object-fit: cover;
|
||
|
|
}
|
||
|
|
|
||
|
|
.subnode-title {
|
||
|
|
font-size: 14px;
|
||
|
|
color: #555;
|
||
|
|
margin-top: 8px;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Características:**
|
||
|
|
- Imagen 16:9 con bordes redondeados
|
||
|
|
- Borde del color de la rama padre
|
||
|
|
- Título debajo, centrado
|
||
|
|
- Botones siempre visibles
|
||
|
|
|
||
|
|
### 3.4 Botones de Acción
|
||
|
|
|
||
|
|
```css
|
||
|
|
.btn-download, .btn-preview {
|
||
|
|
position: absolute;
|
||
|
|
width: 28px;
|
||
|
|
height: 28px;
|
||
|
|
border-radius: 6px;
|
||
|
|
background: rgba(0,0,0,0.6);
|
||
|
|
color: white;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
font-size: 14px;
|
||
|
|
cursor: pointer;
|
||
|
|
text-decoration: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-download {
|
||
|
|
bottom: 6px;
|
||
|
|
left: 6px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-preview {
|
||
|
|
top: 6px;
|
||
|
|
right: 6px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.btn-download:hover, .btn-preview:hover {
|
||
|
|
background: rgba(0,0,0,0.8);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
| Botón | Icono | Posición | Visible |
|
||
|
|
|-------|-------|----------|---------|
|
||
|
|
| Descarga | ↓ | Esquina inferior izquierda | Siempre |
|
||
|
|
| Preview | 👁 | Esquina superior derecha | Solo PDF/imágenes |
|
||
|
|
|
||
|
|
**Archivos con preview:**
|
||
|
|
- `.pdf`
|
||
|
|
- `.png`
|
||
|
|
- `.jpg` / `.jpeg`
|
||
|
|
- `.gif`
|
||
|
|
- `.webp`
|
||
|
|
|
||
|
|
### 3.5 Líneas SVG
|
||
|
|
|
||
|
|
```html
|
||
|
|
<svg id="lines-svg">
|
||
|
|
<path d="M 150,50 C 200,50 200,120 250,120" stroke="#FF6B35" />
|
||
|
|
</svg>
|
||
|
|
```
|
||
|
|
|
||
|
|
```css
|
||
|
|
#lines-svg {
|
||
|
|
position: absolute;
|
||
|
|
top: 0;
|
||
|
|
left: 0;
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
pointer-events: none;
|
||
|
|
overflow: visible;
|
||
|
|
}
|
||
|
|
|
||
|
|
#lines-svg path {
|
||
|
|
fill: none;
|
||
|
|
stroke-width: 3;
|
||
|
|
stroke-linecap: round;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Curva Bezier:**
|
||
|
|
```
|
||
|
|
M x1,y1 C cx1,cy1 cx2,cy2 x2,y2
|
||
|
|
|
||
|
|
Donde:
|
||
|
|
- (x1,y1) = Punto inicio (borde derecho del nodo nivel 1)
|
||
|
|
- (cx1,cy1) = Primer punto de control
|
||
|
|
- (cx2,cy2) = Segundo punto de control
|
||
|
|
- (x2,y2) = Punto final (borde izquierdo del subnodo)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Paleta de Colores
|
||
|
|
|
||
|
|
### 4.1 Colores de ramas
|
||
|
|
|
||
|
|
| Nombre | Hex | Muestra |
|
||
|
|
|--------|-----|---------|
|
||
|
|
| Orange | #FF6B35 | 🟠 |
|
||
|
|
| Yellow | #E5A000 | 🟡 |
|
||
|
|
| Green | #06D6A0 | 🟢 |
|
||
|
|
| Cyan | #0891B2 | 🔵 |
|
||
|
|
| Blue | #3B82F6 | 🔷 |
|
||
|
|
| Purple | #8B5CF6 | 🟣 |
|
||
|
|
| Pink | #EC4899 | 🩷 |
|
||
|
|
|
||
|
|
### 4.2 Colores base
|
||
|
|
|
||
|
|
| Uso | Hex |
|
||
|
|
|-----|-----|
|
||
|
|
| Fondo | #f5f5f7 |
|
||
|
|
| Texto principal | #333333 |
|
||
|
|
| Texto secundario | #555555 |
|
||
|
|
| Borde default | #dddddd |
|
||
|
|
|
||
|
|
### 4.3 Asignación automática
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const COLORS = ['orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'pink'];
|
||
|
|
|
||
|
|
// Cada nodo nivel 1 recibe un color según su índice
|
||
|
|
const color = COLORS[index % COLORS.length];
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Tipografía
|
||
|
|
|
||
|
|
### 5.1 Familia
|
||
|
|
|
||
|
|
```css
|
||
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5.2 Tamaños
|
||
|
|
|
||
|
|
| Elemento | Tamaño | Peso |
|
||
|
|
|----------|--------|------|
|
||
|
|
| Header | 24px | 600 |
|
||
|
|
| Nodo nivel 1 | 18px | 500 |
|
||
|
|
| Título subnodo | 14px | 400 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Interactividad
|
||
|
|
|
||
|
|
### 6.1 Click en nodo nivel 1
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
node.addEventListener('click', () => {
|
||
|
|
const nodeId = node.dataset.nodeId;
|
||
|
|
const subnodes = document.querySelectorAll(`[data-parent-id="${nodeId}"]`);
|
||
|
|
|
||
|
|
subnodes.forEach(sub => {
|
||
|
|
sub.classList.toggle('hidden');
|
||
|
|
});
|
||
|
|
|
||
|
|
// Redibujar líneas
|
||
|
|
drawLines();
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**Comportamiento:**
|
||
|
|
- Toggle: muestra/oculta subnodos
|
||
|
|
- Recalcula posiciones de líneas SVG
|
||
|
|
- Animación suave (opcional)
|
||
|
|
|
||
|
|
### 6.2 Cálculo de líneas
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
function drawLines() {
|
||
|
|
const svg = document.getElementById('lines-svg');
|
||
|
|
svg.innerHTML = '';
|
||
|
|
|
||
|
|
const containerRect = document.getElementById('mindmap-container').getBoundingClientRect();
|
||
|
|
|
||
|
|
document.querySelectorAll('.level1-node').forEach(node => {
|
||
|
|
const nodeId = node.dataset.nodeId;
|
||
|
|
const color = node.dataset.color;
|
||
|
|
const nodeRect = node.getBoundingClientRect();
|
||
|
|
|
||
|
|
// Punto inicio: borde derecho del subrayado
|
||
|
|
const startX = nodeRect.right - containerRect.left;
|
||
|
|
const startY = nodeRect.bottom - containerRect.top - 2;
|
||
|
|
|
||
|
|
// Encontrar subnodos visibles
|
||
|
|
const subnodes = document.querySelectorAll(
|
||
|
|
`.subnode[data-parent-id="${nodeId}"]:not(.hidden)`
|
||
|
|
);
|
||
|
|
|
||
|
|
subnodes.forEach(subnode => {
|
||
|
|
const subRect = subnode.querySelector('.subnode-image-container')
|
||
|
|
.getBoundingClientRect();
|
||
|
|
|
||
|
|
// Punto final: borde izquierdo del subnodo
|
||
|
|
const endX = subRect.left - containerRect.left;
|
||
|
|
const endY = subRect.top + subRect.height/2 - containerRect.top;
|
||
|
|
|
||
|
|
// Puntos de control para curva suave
|
||
|
|
const midX = startX + (endX - startX) / 2;
|
||
|
|
|
||
|
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||
|
|
path.setAttribute('d',
|
||
|
|
`M ${startX},${startY} C ${midX},${startY} ${midX},${endY} ${endX},${endY}`
|
||
|
|
);
|
||
|
|
path.setAttribute('stroke', getColorValue(color));
|
||
|
|
svg.appendChild(path);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6.3 Responsive
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Redibujar en resize
|
||
|
|
window.addEventListener('resize', drawLines);
|
||
|
|
|
||
|
|
// Detectar mobile
|
||
|
|
const isMobile = window.innerWidth < 768;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. Estructura JSON
|
||
|
|
|
||
|
|
### 7.1 Formato completo
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"slug": "proyecto-demo",
|
||
|
|
"titulo": "Proyecto Demo HST",
|
||
|
|
"mostrar_titulo": true,
|
||
|
|
"tema": "light",
|
||
|
|
"nodos": [
|
||
|
|
{
|
||
|
|
"id": "n1",
|
||
|
|
"titulo": "Arquitectura",
|
||
|
|
"color": "#FF6B35",
|
||
|
|
"subnodos": [
|
||
|
|
{
|
||
|
|
"titulo": "plano_general.pdf",
|
||
|
|
"icono_hst": "arc_arquitectura",
|
||
|
|
"url_archivo": "https://git.tzzr.pro/.../plano.pdf",
|
||
|
|
"url_corta": "https://tzzr.pro/abc123"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"titulo": "secciones.pdf",
|
||
|
|
"icono_hst": "pln_plano",
|
||
|
|
"url_archivo": "https://git.tzzr.pro/.../secciones.pdf",
|
||
|
|
"url_corta": "https://tzzr.pro/def456"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"id": "n2",
|
||
|
|
"titulo": "Renders",
|
||
|
|
"color": "#E5A000",
|
||
|
|
"subnodos": [
|
||
|
|
{
|
||
|
|
"titulo": "exterior.jpg",
|
||
|
|
"icono_hst": "ren_render",
|
||
|
|
"url_archivo": "https://git.tzzr.pro/.../exterior.jpg"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7.2 Campos
|
||
|
|
|
||
|
|
| Campo | Tipo | Requerido | Descripción |
|
||
|
|
|-------|------|-----------|-------------|
|
||
|
|
| slug | string | ✅ | Identificador URL |
|
||
|
|
| titulo | string | ✅ | Nombre del proyecto |
|
||
|
|
| mostrar_titulo | boolean | ❌ | Si mostrar header (default: true) |
|
||
|
|
| tema | string | ❌ | "light" o "dark" |
|
||
|
|
| nodos | array | ✅ | Lista de nodos nivel 1 |
|
||
|
|
|
||
|
|
**Nodo:**
|
||
|
|
| Campo | Tipo | Requerido | Descripción |
|
||
|
|
|-------|------|-----------|-------------|
|
||
|
|
| id | string | ✅ | Identificador único |
|
||
|
|
| titulo | string | ✅ | Nombre de la carpeta |
|
||
|
|
| color | string | ❌ | Color hex (si no, se asigna auto) |
|
||
|
|
| subnodos | array | ✅ | Lista de subnodos |
|
||
|
|
|
||
|
|
**Subnodo:**
|
||
|
|
| Campo | Tipo | Requerido | Descripción |
|
||
|
|
|-------|------|-----------|-------------|
|
||
|
|
| titulo | string | ✅ | Nombre del archivo |
|
||
|
|
| icono_hst | string | ✅ | Referencia imagen HST |
|
||
|
|
| url_archivo | string | ✅ | URL de descarga |
|
||
|
|
| url_corta | string | ❌ | URL acortada |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. Código Base
|
||
|
|
|
||
|
|
### 8.1 index.html
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="es">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>MML - Mindmap Linking</title>
|
||
|
|
<link rel="stylesheet" href="css/main.css">
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div id="app">
|
||
|
|
<header id="header"></header>
|
||
|
|
<div id="mindmap-container">
|
||
|
|
<svg id="lines-svg"></svg>
|
||
|
|
<div id="nodes-container"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<script src="js/app.js" type="module"></script>
|
||
|
|
</body>
|
||
|
|
</html>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8.2 Variables CSS
|
||
|
|
|
||
|
|
```css
|
||
|
|
:root {
|
||
|
|
--bg-primary: #f5f5f7;
|
||
|
|
--text-primary: #333;
|
||
|
|
--text-secondary: #555;
|
||
|
|
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
|
|
--line-width: 3px;
|
||
|
|
--image-radius: 8px;
|
||
|
|
--transition-speed: 0.3s;
|
||
|
|
|
||
|
|
/* Colores de ramas */
|
||
|
|
--color-orange: #FF6B35;
|
||
|
|
--color-yellow: #E5A000;
|
||
|
|
--color-green: #06D6A0;
|
||
|
|
--color-cyan: #0891B2;
|
||
|
|
--color-blue: #3B82F6;
|
||
|
|
--color-purple: #8B5CF6;
|
||
|
|
--color-pink: #EC4899;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. Imágenes HST
|
||
|
|
|
||
|
|
### 9.1 URL Base
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const HST_BASE = 'http://72.62.2.84:8090/';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.2 Construcción de URL
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
function getImageUrl(icono_hst) {
|
||
|
|
if (!icono_hst) return null;
|
||
|
|
return `${HST_BASE}${icono_hst}.png`;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.3 Formato de nombres
|
||
|
|
|
||
|
|
```
|
||
|
|
{codigo}_{nombre}.png
|
||
|
|
|
||
|
|
Ejemplos:
|
||
|
|
- arc_arquitectura.png
|
||
|
|
- pln_plano.png
|
||
|
|
- doc_documento.png
|
||
|
|
- fto_foto.png
|
||
|
|
```
|
||
|
|
|
||
|
|
### 9.4 Fallback si imagen no existe
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
img.onerror = function() {
|
||
|
|
this.src = '/assets/placeholder.png';
|
||
|
|
// O ocultar el contenedor de imagen
|
||
|
|
this.parentElement.style.display = 'none';
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. Tests de Interfaz
|
||
|
|
|
||
|
|
### 10.1 Checklist visual
|
||
|
|
|
||
|
|
| Test | Criterio |
|
||
|
|
|------|----------|
|
||
|
|
| Carga | Página muestra contenido en <2s |
|
||
|
|
| Header | Título visible si `mostrar_titulo: true` |
|
||
|
|
| Nodos | Todos los nodos nivel 1 visibles |
|
||
|
|
| Imágenes | Ninguna imagen rota |
|
||
|
|
| Líneas | Curvas conectan correctamente |
|
||
|
|
| Click | Expandir/contraer funciona |
|
||
|
|
| Responsive | Layout adapta a mobile |
|
||
|
|
|
||
|
|
### 10.2 Tests Puppeteer
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Test de imágenes
|
||
|
|
const images = await page.$$eval('img', imgs =>
|
||
|
|
imgs.map(img => ({
|
||
|
|
src: img.src,
|
||
|
|
loaded: img.complete && img.naturalWidth > 0
|
||
|
|
}))
|
||
|
|
);
|
||
|
|
const broken = images.filter(i => !i.loaded);
|
||
|
|
if (broken.length > 0) {
|
||
|
|
errors.push(`Imágenes rotas: ${broken.map(i => i.src).join(', ')}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test de líneas
|
||
|
|
const paths = await page.$$('svg path');
|
||
|
|
if (paths.length === 0) {
|
||
|
|
errors.push('No hay curvas SVG');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Test de interactividad
|
||
|
|
await page.click('.level1-node');
|
||
|
|
await page.waitForTimeout(300);
|
||
|
|
const visibleSubnodes = await page.$$('.subnode:not(.hidden)');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 11. Accesibilidad
|
||
|
|
|
||
|
|
### 11.1 Básico
|
||
|
|
|
||
|
|
```html
|
||
|
|
<!-- Alt text en imágenes -->
|
||
|
|
<img src="..." alt="plano_general.pdf">
|
||
|
|
|
||
|
|
<!-- Roles semánticos -->
|
||
|
|
<nav role="navigation">
|
||
|
|
<main role="main">
|
||
|
|
|
||
|
|
<!-- Focus visible -->
|
||
|
|
.level1-node:focus {
|
||
|
|
outline: 2px solid var(--color-blue);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 11.2 Navegación por teclado
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
// Enter/Space para expandir
|
||
|
|
node.addEventListener('keydown', (e) => {
|
||
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
||
|
|
e.preventDefault();
|
||
|
|
node.click();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Tab order
|
||
|
|
node.setAttribute('tabindex', '0');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Versión:** 1.0
|
||
|
|
**Fecha:** 2025-12-11
|