humanmap-vas 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/humanmap-vas-standalone.js +400 -13
- package/package.json +1 -1
@@ -6,12 +6,18 @@
|
|
6
6
|
.hm-toolbar { display:grid; grid-template-columns: auto 1fr auto; align-items:center; gap:8px; padding:8px 10px; border-bottom:1px solid #eef2f7; background:#fafafa; }
|
7
7
|
.hm-center { text-align:center; font-weight:600; color:#1f2937; }
|
8
8
|
.hm-toolbar select, .hm-toolbar button { appearance:none; border:1px solid #d1d5db; border-radius:10px; padding:6px 10px; background:#fff; cursor:pointer; font-weight:500; }
|
9
|
-
.hm-canvas-wrap { position:relative; width:100%; height:
|
9
|
+
.hm-canvas-wrap { position:relative; width:100%; height:600px; margin:auto; aspect-ratio: 2/3; background:#fff; }
|
10
10
|
svg.hm-svg { position:absolute; inset:0; width:100%; height:100%; }
|
11
11
|
.zone { fill: rgba(31,41,55,0); transition: fill 120ms ease; cursor: pointer; }
|
12
12
|
.zone:hover { fill: rgba(31,41,55,0.22); }
|
13
|
+
.zone.readonly { cursor: default; }
|
14
|
+
.zone.readonly:not(.selected):hover { fill: rgba(31,41,55,0); }
|
13
15
|
.zone.selected { fill: rgba(31,41,55,0.36); }
|
16
|
+
.hm-all-label { fill: #111827; font-weight: 700; font-size: 36px; text-anchor: middle; dominant-baseline: middle; }
|
14
17
|
.label { fill:#0a0a0a; font-size:36px; pointer-events: none; user-select: none; text-anchor: middle; dominant-baseline: middle; font-weight:800; }
|
18
|
+
.hm-print-btn { position: absolute; top: 10px; right: 10px; background: rgba(17,24,39,0.85); color: #f9fafb; border: none; border-radius: 8px; cursor: pointer; font-size: 18px; padding: 6px 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.3); transition: opacity 0.25s ease, background 0.2s ease; opacity: 0; pointer-events: none; }
|
19
|
+
.hm-canvas-wrap:hover .hm-print-btn { opacity: 1; pointer-events: auto; }
|
20
|
+
.hm-print-btn:hover { background: rgba(37,99,235,0.9); }
|
15
21
|
`;
|
16
22
|
|
17
23
|
// ───────────────────────────────────────────────────────────────────────────
|
@@ -117,6 +123,11 @@
|
|
117
123
|
this._view=this.getAttribute('view') || 'head_right';
|
118
124
|
this._zones=ZONES;
|
119
125
|
this._selected=new Set();
|
126
|
+
this._readOnly = this.hasAttribute('read-only') && this.getAttribute('read-only') !== 'false';
|
127
|
+
|
128
|
+
this._upgradeProperty('selectedIds');
|
129
|
+
this._upgradeProperty('selectedZones');
|
130
|
+
|
120
131
|
|
121
132
|
// Detectar automáticamente la ruta base del script (compatible con todos los entornos)
|
122
133
|
let scriptBase = '';
|
@@ -148,8 +159,26 @@
|
|
148
159
|
};
|
149
160
|
}
|
150
161
|
|
151
|
-
|
152
|
-
|
162
|
+
// Captura valores asignados *antes* de que el custom element se registre
|
163
|
+
_upgradeProperty(prop) {
|
164
|
+
if (this.hasOwnProperty(prop)) {
|
165
|
+
const value = this[prop];
|
166
|
+
delete this[prop]; // elimina la propiedad “propia” del elemento “no mejorado”
|
167
|
+
this[prop] = value; // re-ejecuta el setter ya del elemento mejorado
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
connectedCallback(){
|
172
|
+
this._renderShell();
|
173
|
+
this._renderCanvas();
|
174
|
+
this.dispatchEvent(new CustomEvent('human-map-vas:ready'));
|
175
|
+
this.dispatchEvent(new CustomEvent('human-map-vas:readonly-change', {
|
176
|
+
detail: { readOnly: this._readOnly }
|
177
|
+
}));
|
178
|
+
}
|
179
|
+
|
180
|
+
static get observedAttributes() { return ['view', 'img-root', 'read-only']; }
|
181
|
+
|
153
182
|
attributeChangedCallback (name, oldValue, newValue) {
|
154
183
|
if (oldValue === newValue) return;
|
155
184
|
|
@@ -172,6 +201,76 @@
|
|
172
201
|
if (this._root) this._renderCanvas();
|
173
202
|
return;
|
174
203
|
}
|
204
|
+
|
205
|
+
if (name === 'read-only') {
|
206
|
+
this._readOnly = newValue === 'true' || newValue === true;
|
207
|
+
|
208
|
+
if (this._root) {
|
209
|
+
// Refrescar la vista completamente según el modo
|
210
|
+
if (this._view === 'all') {
|
211
|
+
this._renderAllViews();
|
212
|
+
} else {
|
213
|
+
this._renderCanvas();
|
214
|
+
}
|
215
|
+
|
216
|
+
// Actualizar la visibilidad de toolbar y botón de impresión
|
217
|
+
const toolbar = this._root.querySelector('.hm-toolbar');
|
218
|
+
const printBtn = this.shadowRoot.getElementById('printBtn');
|
219
|
+
|
220
|
+
if (this._view === 'all' && this._readOnly) {
|
221
|
+
if (toolbar) toolbar.style.display = 'none';
|
222
|
+
if (printBtn) printBtn.style.display = 'block';
|
223
|
+
} else {
|
224
|
+
if (toolbar) toolbar.style.display = '';
|
225
|
+
if (printBtn) printBtn.style.display = 'none';
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
return;
|
230
|
+
}
|
231
|
+
|
232
|
+
}
|
233
|
+
|
234
|
+
// Devuelve solo IDs seleccionados
|
235
|
+
get selectedIds() {
|
236
|
+
return Array.from(this._selected);
|
237
|
+
}
|
238
|
+
|
239
|
+
// Asigna selección por IDs (array de strings)
|
240
|
+
set selectedIds(ids) {
|
241
|
+
if (!Array.isArray(ids)) return;
|
242
|
+
this._selected = new Set(ids);
|
243
|
+
|
244
|
+
if (this._root) {
|
245
|
+
// Si está en modo global, renderizamos todas las vistas
|
246
|
+
if (this._view === 'all') this._renderAllViews();
|
247
|
+
else this._renderZones();
|
248
|
+
this._emit();
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
// Devuelve objetos completos (id, code, label, view)
|
253
|
+
get selectedZones() {
|
254
|
+
const map = new Map(this._zones.map(z => [z.id, z]));
|
255
|
+
return this.selectedIds.map(id => {
|
256
|
+
const z = map.get(id);
|
257
|
+
return z ? { id: z.id, code: z.code, label: z.label, view: z.view } : { id };
|
258
|
+
});
|
259
|
+
}
|
260
|
+
|
261
|
+
// Asigna selección pasando objetos (tomamos los IDs)
|
262
|
+
set selectedZones(zones) {
|
263
|
+
if (!Array.isArray(zones)) return;
|
264
|
+
const ids = zones.map(z => z && z.id).filter(Boolean);
|
265
|
+
this.selectedIds = ids; // reutiliza el setter de IDs para redibujar y emitir
|
266
|
+
}
|
267
|
+
|
268
|
+
get selectedCodes() {
|
269
|
+
const map = new Map(this._zones.map(z => [z.id, z]));
|
270
|
+
return this.selectedIds.map(id => {
|
271
|
+
const z = map.get(id);
|
272
|
+
return z ? z.code : { id };
|
273
|
+
});
|
175
274
|
}
|
176
275
|
|
177
276
|
getSelected(){
|
@@ -191,7 +290,11 @@
|
|
191
290
|
this._root=document.createElement('div');
|
192
291
|
this._root.className='hm';
|
193
292
|
|
194
|
-
const opts=
|
293
|
+
const opts = [
|
294
|
+
`<option value="all">Todas las vistas</option>`,
|
295
|
+
...VIEWS.map(v => `<option value="${v.id}">${v.label}</option>`)
|
296
|
+
].join('');
|
297
|
+
|
195
298
|
this._root.innerHTML=`
|
196
299
|
<div class="hm-toolbar">
|
197
300
|
<button id="prev">◀</button>
|
@@ -208,6 +311,7 @@
|
|
208
311
|
<g id="bg"></g>
|
209
312
|
<g id="zones"></g>
|
210
313
|
</svg>
|
314
|
+
<button id="printBtn" title="Imprimir vista" class="hm-print-btn">🖨️</button>
|
211
315
|
</div>`;
|
212
316
|
this.shadowRoot.append(style,this._root);
|
213
317
|
|
@@ -226,6 +330,15 @@
|
|
226
330
|
this._els.prev.addEventListener('click',()=>this._cycle(-1));
|
227
331
|
this._els.next.addEventListener('click',()=>this._cycle(1));
|
228
332
|
this._els.reset.addEventListener('click',()=>this.clear());
|
333
|
+
this._els.printBtn = this.shadowRoot.getElementById('printBtn');
|
334
|
+
this._els.printBtn.addEventListener('click', () => {
|
335
|
+
this._els.printBtn.style.transform = 'scale(0.94)';
|
336
|
+
setTimeout(() => {
|
337
|
+
this._els.printBtn.style.transform = '';
|
338
|
+
this._printCanvasOnly(); // ⬅️ imprime solo el área actual
|
339
|
+
}, 120);
|
340
|
+
});
|
341
|
+
|
229
342
|
}
|
230
343
|
|
231
344
|
_cycle(dir){
|
@@ -235,8 +348,55 @@
|
|
235
348
|
}
|
236
349
|
|
237
350
|
_renderCanvas(){
|
238
|
-
|
239
|
-
|
351
|
+
// Ocultar toolbar si está en modo global y solo lectura
|
352
|
+
if (this._els && this._root) {
|
353
|
+
const toolbar = this._root.querySelector('.hm-toolbar');
|
354
|
+
if (this._view === 'all' && this._readOnly) {
|
355
|
+
toolbar.style.display = 'none';
|
356
|
+
} else {
|
357
|
+
toolbar.style.display = '';
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
// Mostrar botón de impresión solo en modo global + solo lectura
|
362
|
+
const printBtn = this.shadowRoot.getElementById('printBtn');
|
363
|
+
if (printBtn) {
|
364
|
+
if (this._view === 'all' && this._readOnly) {
|
365
|
+
printBtn.style.display = 'block';
|
366
|
+
} else {
|
367
|
+
printBtn.style.display = 'none';
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
// Limpia cualquier render anterior (modo all o vista única)
|
372
|
+
if (this._els && this._els.svg) {
|
373
|
+
this._els.svg.innerHTML = `
|
374
|
+
<defs id="defs"></defs>
|
375
|
+
<g id="bg"></g>
|
376
|
+
<g id="zones"></g>
|
377
|
+
`;
|
378
|
+
this._els.bg = this._els.svg.querySelector('#bg');
|
379
|
+
this._els.zones = this._els.svg.querySelector('#zones');
|
380
|
+
}
|
381
|
+
|
382
|
+
// 🆕 Detección de modo global
|
383
|
+
if (this._view === 'all') {
|
384
|
+
this._renderAllViews();
|
385
|
+
this._els.cur.textContent = 'Todas las vistas';
|
386
|
+
this._els.picker.value = 'all';
|
387
|
+
this._els.prev.disabled = true;
|
388
|
+
this._els.next.disabled = true;
|
389
|
+
this._els.reset.disabled = this._readOnly;
|
390
|
+
return;
|
391
|
+
} else {
|
392
|
+
this._els.prev.disabled = false;
|
393
|
+
this._els.next.disabled = false;
|
394
|
+
this._els.reset.disabled = false;
|
395
|
+
}
|
396
|
+
|
397
|
+
// --- Render normal ---
|
398
|
+
const layout = VIEW_LAYOUTS[this._view];
|
399
|
+
const v = VIEWS.find(v => v.id === this._view);
|
240
400
|
if(!layout||!v)return;
|
241
401
|
this._els.cur.textContent=v.label;
|
242
402
|
this._els.picker.value=v.id;
|
@@ -247,6 +407,112 @@
|
|
247
407
|
this._renderZones();
|
248
408
|
}
|
249
409
|
|
410
|
+
_renderAllViews() {
|
411
|
+
const svg = this._els.svg;
|
412
|
+
svg.innerHTML = ''; // limpiar
|
413
|
+
|
414
|
+
// Creamos un grupo por cada vista
|
415
|
+
const cols = 2, gap = 40;
|
416
|
+
const cellW = 480, cellH = 720;
|
417
|
+
const gridW = cols * (cellW + gap);
|
418
|
+
const gridH = Math.ceil(VIEWS.length / cols) * (cellH + gap);
|
419
|
+
|
420
|
+
svg.setAttribute('viewBox', `0 0 ${gridW} ${gridH}`);
|
421
|
+
const gRoot = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
422
|
+
svg.appendChild(gRoot);
|
423
|
+
|
424
|
+
VIEWS.forEach((v, i) => {
|
425
|
+
const layout = VIEW_LAYOUTS[v.id] || { vb: [0, 0, 1024, 1536], y: 0, h: 1536, rotate: 0 };
|
426
|
+
const row = Math.floor(i / cols);
|
427
|
+
const col = i % cols;
|
428
|
+
const gx = col * (cellW + gap);
|
429
|
+
const gy = row * (cellH + gap);
|
430
|
+
|
431
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
432
|
+
g.setAttribute('transform', `translate(${gx}, ${gy}) scale(0.45)`);
|
433
|
+
|
434
|
+
// Fondo (imagen)
|
435
|
+
const img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
|
436
|
+
img.setAttribute('href', this._bg[v.id]);
|
437
|
+
img.setAttribute('x', '0');
|
438
|
+
img.setAttribute('y', layout?.y || 0);
|
439
|
+
img.setAttribute('width', layout?.vb[2] || 1024);
|
440
|
+
img.setAttribute('height', layout?.h || 1536);
|
441
|
+
img.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
442
|
+
g.appendChild(img);
|
443
|
+
|
444
|
+
// Grupo para las zonas (permite rotar sin afectar la imagen)
|
445
|
+
const gZones = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
446
|
+
|
447
|
+
// Rotar solo las zonas del cuello
|
448
|
+
let rotation = 0;
|
449
|
+
if (v.id === 'neck_right') rotation = 12;
|
450
|
+
if (v.id === 'neck_left') rotation = -12;
|
451
|
+
if (rotation !== 0) {
|
452
|
+
gZones.setAttribute(
|
453
|
+
'transform',
|
454
|
+
`rotate(${rotation}, ${layout.vb[2]*0.55}, ${layout.vb[3]*0.45})`
|
455
|
+
);
|
456
|
+
}
|
457
|
+
|
458
|
+
// --- Dibujar zonas ---
|
459
|
+
const zones = this._zones.filter(z => z.view === v.id);
|
460
|
+
zones.forEach(z => {
|
461
|
+
const { x, y, w, h } = z.shape;
|
462
|
+
|
463
|
+
// Zona rectangular
|
464
|
+
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
465
|
+
rect.setAttribute('x', x * layout.vb[2]);
|
466
|
+
rect.setAttribute('y', y * layout.vb[3]);
|
467
|
+
rect.setAttribute('width', w * layout.vb[2]);
|
468
|
+
rect.setAttribute('height', h * layout.vb[3]);
|
469
|
+
rect.setAttribute('rx', Math.min(w, h) * layout.vb[2] * 0.1);
|
470
|
+
rect.classList.add('zone');
|
471
|
+
if (this._selected.has(z.id)) rect.classList.add('selected');
|
472
|
+
if (this._readOnly) {
|
473
|
+
rect.classList.add('readonly');
|
474
|
+
} else {
|
475
|
+
rect.addEventListener('click', e => {
|
476
|
+
e.stopPropagation();
|
477
|
+
if (this._selected.has(z.id)) this._selected.delete(z.id);
|
478
|
+
else this._selected.add(z.id);
|
479
|
+
this._renderAllViews(); // refrescar vista global
|
480
|
+
this._emit();
|
481
|
+
});
|
482
|
+
}
|
483
|
+
gZones.appendChild(rect);
|
484
|
+
|
485
|
+
// Label centrado
|
486
|
+
const t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
487
|
+
t.textContent = z.code;
|
488
|
+
t.setAttribute('x', (x + w / 2) * layout.vb[2]);
|
489
|
+
t.setAttribute('y', (y + h / 2) * layout.vb[3]);
|
490
|
+
t.setAttribute('fill', '#0a0a0a');
|
491
|
+
t.setAttribute('font-size', '32');
|
492
|
+
t.setAttribute('font-weight', '700');
|
493
|
+
t.setAttribute('text-anchor', 'middle');
|
494
|
+
t.setAttribute('dominant-baseline', 'middle');
|
495
|
+
t.setAttribute('pointer-events', 'none');
|
496
|
+
gZones.appendChild(t);
|
497
|
+
});
|
498
|
+
|
499
|
+
// Añadir grupo de zonas dentro del grupo principal
|
500
|
+
g.appendChild(gZones);
|
501
|
+
|
502
|
+
|
503
|
+
// Label de la vista
|
504
|
+
const t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
505
|
+
t.textContent = v.label;
|
506
|
+
t.setAttribute('x', layout.vb[2] / 2);
|
507
|
+
t.setAttribute('y', 80);
|
508
|
+
t.setAttribute('class', 'hm-all-label');
|
509
|
+
g.appendChild(t);
|
510
|
+
|
511
|
+
|
512
|
+
gRoot.appendChild(g);
|
513
|
+
});
|
514
|
+
}
|
515
|
+
|
250
516
|
_renderZones(){
|
251
517
|
const g=this._els.zones;g.innerHTML='';
|
252
518
|
const layout=VIEW_LAYOUTS[this._view];
|
@@ -258,19 +524,34 @@
|
|
258
524
|
Z.forEach(z=>{
|
259
525
|
const{x,y,w,h}=z.shape;
|
260
526
|
const rect=document.createElementNS('http://www.w3.org/2000/svg','rect');
|
527
|
+
|
261
528
|
rect.setAttribute('x',x*vw);rect.setAttribute('y',y*vh);
|
262
529
|
rect.setAttribute('width',w*vw);rect.setAttribute('height',h*vh);
|
263
530
|
rect.setAttribute('rx',Math.min(w*vw,h*vh)*0.1);
|
531
|
+
|
264
532
|
rect.classList.add('zone');
|
265
|
-
|
266
|
-
rect.
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
}
|
533
|
+
|
534
|
+
if (this._selected.has(z.id)) rect.classList.add('selected');
|
535
|
+
|
536
|
+
// Si está en modo lectura, añadir clase visual
|
537
|
+
if (this._readOnly) {
|
538
|
+
rect.classList.add('readonly');
|
539
|
+
} else {
|
540
|
+
rect.classList.remove('readonly');
|
541
|
+
rect.addEventListener('click', e => {
|
542
|
+
e.stopPropagation();
|
543
|
+
if (this._selected.has(z.id)) this._selected.delete(z.id);
|
544
|
+
else this._selected.add(z.id);
|
545
|
+
this._renderZones();
|
546
|
+
this._emit();
|
547
|
+
});
|
548
|
+
}
|
549
|
+
|
550
|
+
|
272
551
|
g.appendChild(rect);
|
552
|
+
|
273
553
|
const t=document.createElementNS('http://www.w3.org/2000/svg','text');
|
554
|
+
|
274
555
|
t.setAttribute('x',(x+w/2)*vw);
|
275
556
|
t.setAttribute('y',(y+h/2)*vh);
|
276
557
|
t.textContent=z.code;
|
@@ -279,6 +560,112 @@
|
|
279
560
|
});
|
280
561
|
}
|
281
562
|
|
563
|
+
// Imprime solo el área visible (hm-canvas-wrap) en una nueva ventana
|
564
|
+
_printCanvasOnly() {
|
565
|
+
if (!this._els || !this._els.svg) return;
|
566
|
+
|
567
|
+
// Clonar el SVG actual completo (vistas individuales o "all")
|
568
|
+
const clone = this._els.svg.cloneNode(true);
|
569
|
+
|
570
|
+
// Asegurar rutas absolutas en <image> (importante para que se muestren)
|
571
|
+
clone.querySelectorAll('image').forEach(img => {
|
572
|
+
const href = img.getAttribute('href') || img.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
|
573
|
+
if (href) {
|
574
|
+
const a = document.createElement('a');
|
575
|
+
a.href = href;
|
576
|
+
const abs = a.href;
|
577
|
+
img.setAttribute('href', abs);
|
578
|
+
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', abs);
|
579
|
+
}
|
580
|
+
});
|
581
|
+
|
582
|
+
// Inyectar los estilos básicos directamente en el SVG (para conservar colores y transparencias)
|
583
|
+
const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
|
584
|
+
style.textContent = `
|
585
|
+
.zone { fill: rgba(31,41,55,0); cursor: pointer; transition: fill 120ms ease; }
|
586
|
+
.zone:hover { fill: rgba(31,41,55,0.22); }
|
587
|
+
.zone.selected { fill: rgba(31,41,55,0.36); }
|
588
|
+
.label { fill: #0a0a0a; font-size: 36px; font-weight: 800;
|
589
|
+
text-anchor: middle; dominant-baseline: middle; pointer-events: none; user-select: none; }
|
590
|
+
`;
|
591
|
+
clone.insertBefore(style, clone.firstChild);
|
592
|
+
|
593
|
+
const serializer = new XMLSerializer();
|
594
|
+
const svgMarkup = serializer.serializeToString(clone);
|
595
|
+
|
596
|
+
const isGlobal = this._view === 'all';
|
597
|
+
|
598
|
+
// HTML limpio con estilos mínimos
|
599
|
+
const html = `
|
600
|
+
<!DOCTYPE html>
|
601
|
+
<html lang="es">
|
602
|
+
<head>
|
603
|
+
<meta charset="utf-8">
|
604
|
+
<title>Impresión del mapa anatómico</title>
|
605
|
+
<style>
|
606
|
+
html, body {
|
607
|
+
margin: 0;
|
608
|
+
padding: 0mm;
|
609
|
+
background: #fff;
|
610
|
+
text-align: center;
|
611
|
+
}
|
612
|
+
svg {
|
613
|
+
width: 100%;
|
614
|
+
height: auto;
|
615
|
+
display: block;
|
616
|
+
}
|
617
|
+
.hm-all-label {
|
618
|
+
font-family: system-ui, sans-serif;
|
619
|
+
font-size: 48px;
|
620
|
+
font-weight: 800;
|
621
|
+
fill: #111827;
|
622
|
+
text-anchor: middle;
|
623
|
+
dominant-baseline: middle;
|
624
|
+
}
|
625
|
+
@page {
|
626
|
+
size: ${isGlobal ? 'A4 landscape' : 'A4 portrait'};
|
627
|
+
margin: 5mm;
|
628
|
+
}
|
629
|
+
</style>
|
630
|
+
</head>
|
631
|
+
<body>
|
632
|
+
${svgMarkup}
|
633
|
+
</body>
|
634
|
+
</html>
|
635
|
+
`;
|
636
|
+
|
637
|
+
// Abrir ventana y escribir el HTML
|
638
|
+
const printWin = window.open('', '_blank', 'width=1024,height=768');
|
639
|
+
if (!printWin) {
|
640
|
+
alert('El navegador bloqueó la ventana de impresión. Permite popups para continuar.');
|
641
|
+
return;
|
642
|
+
}
|
643
|
+
|
644
|
+
printWin.document.open();
|
645
|
+
printWin.document.write(html);
|
646
|
+
printWin.document.close();
|
647
|
+
|
648
|
+
// Esperar hasta que las imágenes del SVG estén listas
|
649
|
+
const waitForImages = () => {
|
650
|
+
const imgs = printWin.document.querySelectorAll('image');
|
651
|
+
const promises = Array.from(imgs).map(img => {
|
652
|
+
return new Promise(resolve => {
|
653
|
+
const test = new Image();
|
654
|
+
test.onload = test.onerror = resolve;
|
655
|
+
test.src = img.getAttribute('href') || img.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
|
656
|
+
});
|
657
|
+
});
|
658
|
+
return Promise.all(promises);
|
659
|
+
};
|
660
|
+
|
661
|
+
waitForImages().then(() => {
|
662
|
+
printWin.focus();
|
663
|
+
printWin.print();
|
664
|
+
setTimeout(() => printWin.close(), 1000);
|
665
|
+
});
|
666
|
+
}
|
667
|
+
|
668
|
+
|
282
669
|
_emit(){this.dispatchEvent(new CustomEvent('human-map-vas:select',{detail:{selected:this.getSelected()}}));}
|
283
670
|
}
|
284
671
|
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "humanmap-vas",
|
3
|
-
"version": "1.0.
|
3
|
+
"version": "1.0.23",
|
4
4
|
"description": "**HumanMap VAS** es una librería web que permite graficar el cuerpo humano con vistas anatómicas interactivas para identificar zonas según el sistema VAS. Desarrollada como *Web Component standalone*, puede integrarse fácilmente en proyectos **HTML**, **Django**, o **Vue.js**.",
|
5
5
|
"main": "humanmap-vas-standalone.js",
|
6
6
|
"files": [
|