humanmap-vas 1.0.27 → 1.0.28

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.
@@ -1,47 +1,372 @@
1
1
  // humanmap-vas-standalone.js — Cabeza + Cuello + Tórax (2 vistas: anterior/posterior)
2
2
  (function(){
3
+
3
4
  const STYLE = `
4
- :host { display:block; font:14px/1.5 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, Noto Sans, sans-serif; color:#111827; }
5
- .hm { position: relative; border: var(--hm-border, 1px solid #e5e7eb); border-radius: var(--hm-border-radius, 14px); overflow: hidden; background: var(--hm-background, #fff); }
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
- .hm-center { text-align:center; font-weight:600; color:#1f2937; }
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-toolbar .menu-btn { font-size: 18px; width: 36px; height: 32px; line-height: 1; text-align: center; background: #fff; border: 1px solid #d1d5db; border-radius: 10px; cursor: pointer; transition: background 0.2s; }
10
- .hm-toolbar .menu-btn:hover { background: #f3f4f6; }
11
- .hm-dropdown { position: absolute; right: 10px; top: 46px; background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); display: none; flex-direction: column; z-index: 9999; }
5
+ :host {
6
+ display: block;
7
+ font: 14px/1.5 system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, Noto Sans, sans-serif;
8
+ color: var(--hm-text-color, #111827);
9
+ }
10
+
11
+ /* ==================== TEMAS ==================== */
12
+ :host([theme="light"]) {
13
+ --hm-border: 1px solid #e5e7eb;
14
+ --hm-border-radius: 14px;
15
+ --hm-background: #ffffff;
16
+ --hm-text-color: #111827;
17
+ --hm-toolbar-bg: #fafafa;
18
+ --hm-toolbar-border: #e5e7eb;
19
+ --hm-btn-bg: #ffffff;
20
+ --hm-btn-hover: #f3f4f6;
21
+ --hm-zone-fill: rgba(31,41,55,0);
22
+ --hm-zone-hover: rgba(31,41,55,0.22);
23
+ --hm-zone-selected: rgba(31,41,55,0.36);
24
+ --hm-label-color: #0a0a0a;
25
+ }
26
+
27
+ :host([theme="dark"]) {
28
+ --hm-border: 1px solid #374151;
29
+ --hm-border-radius: 14px;
30
+ --hm-background: #1f2937;
31
+ --hm-text-color: #f3f4f6;
32
+ --hm-toolbar-bg: #111827;
33
+ --hm-toolbar-border: #374151;
34
+ --hm-btn-bg: #374151;
35
+ --hm-btn-hover: #4b5563;
36
+ --hm-zone-fill: rgba(255,255,255,0);
37
+ --hm-zone-hover: rgba(255,255,255,0.1);
38
+ --hm-zone-selected: rgba(94, 101, 111, 0.9);
39
+ --hm-label-color: #d1d5db;
40
+ }
41
+
42
+ @media (prefers-color-scheme: dark) {
43
+ :host(:not([theme])) {
44
+ --hm-border: 1px solid #374151;
45
+ --hm-background: #1f2937;
46
+ --hm-text-color: #f3f4f6;
47
+ --hm-toolbar-bg: #111827;
48
+ --hm-toolbar-border: #374151;
49
+ --hm-btn-bg: #374151;
50
+ --hm-btn-hover: #4b5563;
51
+ --hm-zone-fill: rgba(255,255,255,0);
52
+ --hm-zone-hover: rgba(255,255,255,0.1);
53
+ --hm-zone-selected: rgba(94, 101, 111, 0.9);
54
+ --hm-label-color: #d1d5db;
55
+ }
56
+ }
57
+
58
+ /* ==================== ESTRUCTURA BASE ==================== */
59
+ .hm {
60
+ position: relative;
61
+ border: var(--hm-border);
62
+ border-radius: var(--hm-border-radius);
63
+ overflow: hidden;
64
+ background: var(--hm-background);
65
+ color: var(--hm-text-color);
66
+ transition: background 0.4s ease, color 0.4s ease;
67
+ }
68
+
69
+ .hm-toolbar {
70
+ display: grid;
71
+ grid-template-columns: auto 1fr auto;
72
+ align-items: center;
73
+ gap: 8px;
74
+ padding: 8px 10px;
75
+ border-bottom: 1px solid var(--hm-toolbar-border);
76
+ background: var(--hm-toolbar-bg);
77
+ transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease;
78
+ }
79
+
80
+ .hm-center {
81
+ text-align: center;
82
+ font-weight: 600;
83
+ color: var(--hm-text-color);
84
+ }
85
+
86
+ .hm-toolbar select,
87
+ .hm-toolbar button {
88
+ appearance: none;
89
+ border: 1px solid var(--hm-toolbar-border);
90
+ border-radius: 10px;
91
+ padding: 6px 10px;
92
+ background: var(--hm-btn-bg);
93
+ cursor: pointer;
94
+ font-weight: 500;
95
+ color: var(--hm-text-color);
96
+ transition: background 0.4s ease, color 0.4s ease, border-color 0.4s ease;
97
+ }
98
+
99
+ .hm-toolbar button:hover,
100
+ .hm-toolbar select:hover {
101
+ background: var(--hm-btn-hover);
102
+ }
103
+
104
+ .hm-toolbar .menu-btn {
105
+ font-size: 18px;
106
+ width: 36px;
107
+ height: 32px;
108
+ line-height: 1;
109
+ text-align: center;
110
+ }
111
+
112
+ .hm-dropdown {
113
+ position: absolute;
114
+ right: 10px;
115
+ top: 46px;
116
+ background: var(--hm-background);
117
+ border: 1px solid var(--hm-toolbar-border);
118
+ border-radius: 8px;
119
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
120
+ display: none;
121
+ flex-direction: column;
122
+ z-index: 9999;
123
+ }
124
+
12
125
  .hm-dropdown.active { display: flex; }
13
- .hm-dropdown button { background: none; border: none; padding: 8px 16px; text-align: left; font-size: 14px; cursor: pointer; transition: background 0.15s; }
14
- .hm-dropdown button:hover { background: #f3f4f6; }
15
- .hm-canvas-wrap { position: relative; width: var(--hm-width, 100%); height: var(--hm-height, 500px); aspect-ratio: 2 / 3; background: var(--hm-background, #fff); display: flex; align-items: center; justify-content: center; overflow: hidden; }
16
- svg.hm-svg { position:absolute; inset:0; width:100%; height:100%; margin: auto; }
17
- .zone { fill: rgba(31,41,55,0); transition: fill 120ms ease; cursor: pointer; }
18
- .zone:hover { fill: rgba(31,41,55,0.22); }
126
+
127
+ .hm-dropdown button {
128
+ background: none;
129
+ border: none;
130
+ padding: 8px 16px;
131
+ text-align: left;
132
+ font-size: 14px;
133
+ cursor: pointer;
134
+ transition: background 0.15s;
135
+ color: var(--hm-text-color);
136
+ }
137
+
138
+ .hm-dropdown button:hover {
139
+ background: var(--hm-btn-hover);
140
+ }
141
+
142
+ /* ==================== LIENZO ==================== */
143
+ .hm-canvas-wrap {
144
+ position: relative;
145
+ width: var(--hm-width, 100%);
146
+ height: var(--hm-height, 500px);
147
+ aspect-ratio: 2 / 3;
148
+ background: var(--hm-background);
149
+ display: flex;
150
+ align-items: center;
151
+ justify-content: center;
152
+ overflow: hidden;
153
+ transition: background 0.4s ease, color 0.4s ease;
154
+ }
155
+
156
+ svg.hm-svg {
157
+ position: absolute;
158
+ inset: 0;
159
+ width: 100%;
160
+ height: 100%;
161
+ margin: auto;
162
+ }
163
+
164
+ /* ==================== ZONAS ==================== */
165
+ .zone {
166
+ fill: var(--hm-zone-fill);
167
+ transition: fill 0.4s ease;
168
+ cursor: pointer;
169
+ }
170
+
171
+ .zone:hover { fill: var(--hm-zone-hover); }
172
+
19
173
  .zone.readonly { cursor: default; }
20
- .zone.readonly:not(.selected):hover { fill: rgba(31,41,55,0); }
21
- .zone.selected { fill: rgba(31,41,55,0.36); }
22
- .hm-all-label { fill: #111827; font-weight: 700; font-size: 36px; text-anchor: middle; dominant-baseline: middle; }
23
- .label { fill:#0a0a0a; font-size:36px; pointer-events: none; user-select: none; text-anchor: middle; dominant-baseline: middle; font-weight:800; }
24
- .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; }
25
- .hm-canvas-wrap:hover .hm-print-btn { opacity: 1; pointer-events: auto; }
174
+
175
+ .zone.readonly:not(.selected):hover { fill: var(--hm-zone-fill); }
176
+
177
+ .zone.selected { fill: var(--hm-zone-selected); }
178
+
179
+ /* ==================== LABELS ==================== */
180
+ .hm-all-label {
181
+ fill: var(--hm-text-color);
182
+ font-weight: 700;
183
+ font-size: 36px;
184
+ text-anchor: middle;
185
+ dominant-baseline: middle;
186
+ transition: fill 0.4s ease;
187
+ }
188
+
189
+ .label {
190
+ fill: var(--hm-label-color);
191
+ font-size: 36px;
192
+ pointer-events: none;
193
+ user-select: none;
194
+ text-anchor: middle;
195
+ dominant-baseline: middle;
196
+ font-weight: 800;
197
+ transition: fill 0.4s ease;
198
+ }
199
+
200
+ /* ==================== BOTONES FLOTANTES / MODAL ==================== */
201
+ .hm-print-btn {
202
+ position: absolute;
203
+ top: 10px;
204
+ right: 10px;
205
+ background: rgba(17,24,39,0.85);
206
+ color: #f9fafb;
207
+ border: none;
208
+ border-radius: 8px;
209
+ cursor: pointer;
210
+ font-size: 18px;
211
+ padding: 6px 10px;
212
+ box-shadow: 0 2px 4px rgba(0,0,0,0.3);
213
+ transition: opacity 0.25s ease, background 0.2s ease;
214
+ opacity: 0;
215
+ pointer-events: none;
216
+ }
217
+
218
+ .hm-canvas-wrap:hover .hm-print-btn {
219
+ opacity: 1;
220
+ pointer-events: auto;
221
+ }
222
+
26
223
  .hm-print-btn:hover { background: rgba(37,99,235,0.9); }
27
- .hm-zoom-float { position: absolute; bottom: 10px; right: 10px; background: rgba(31,41,55,0.85); color: #fff; border: none; border-radius: 50%; width: 42px; height: 42px; font-size: 20px; line-height: 1; cursor: pointer; box-shadow: 0 3px 8px rgba(0,0,0,0.3); transition: transform 0.2s ease, background 0.2s ease; z-index: 20; }
224
+
225
+ .hm-zoom-float {
226
+ position: absolute;
227
+ bottom: 10px;
228
+ right: 10px;
229
+ background: rgba(31,41,55,0.85);
230
+ color: #fff;
231
+ border: none;
232
+ border-radius: 50%;
233
+ width: 42px;
234
+ height: 42px;
235
+ font-size: 20px;
236
+ line-height: 1;
237
+ cursor: pointer;
238
+ box-shadow: 0 3px 8px rgba(0,0,0,0.3);
239
+ transition: transform 0.2s ease, background 0.2s ease;
240
+ z-index: 20;
241
+ }
242
+
28
243
  .hm-zoom-float:hover { transform: scale(1.08); background: rgba(31,41,55,1); }
29
- .hm-zoom-modal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.85); display: flex; align-items: center; justify-content: center; z-index: 9999; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; }
244
+
245
+ /* ==================== MODAL DE ZOOM ==================== */
246
+ .hm-zoom-modal {
247
+ position: fixed;
248
+ inset: 0;
249
+ background: rgba(0, 0, 0, 0.85);
250
+ display: flex;
251
+ align-items: center;
252
+ justify-content: center;
253
+ z-index: 9999;
254
+ opacity: 0;
255
+ pointer-events: none;
256
+ transition: opacity 0.3s ease;
257
+ }
258
+
30
259
  .hm-zoom-modal.active { opacity: 1; pointer-events: auto; }
31
- .hm-zoom-inner { position: relative; width: 95vw; height: 90vh; background: #fff; border-radius: 10px; overflow: hidden; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; }
32
- .hm-zoom-content { position: relative; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: #fff; overflow: auto; }
33
- .hm-zoom-close { position: absolute; top: 12px; right: 12px; border: none; background: rgba(0, 0, 0, 0.75); color: #fff; border-radius: 50%; width: 42px; height: 42px; font-size: 24px; cursor: pointer; z-index: 10000; line-height: 1; }
34
- .hm-zoom-close:hover { background: rgba(0, 0, 0, 0.9); }
35
- .hm-zoom-modal svg { width: auto; height: auto; max-width: 100%; max-height: 100%; display: block; transition: transform 0.25s ease; }
36
- .hm-zoom-inner { transform: scale(0.95); opacity: 0; transition: transform 0.3s ease, opacity 0.3s ease; }
260
+
261
+ .hm-zoom-inner {
262
+ position: relative;
263
+ width: 95vw;
264
+ height: 90vh;
265
+ background: var(--hm-background);
266
+ border-radius: 10px;
267
+ overflow: hidden;
268
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
269
+ display: flex;
270
+ align-items: center;
271
+ justify-content: center;
272
+ transform: scale(0.95);
273
+ opacity: 0;
274
+ transition: transform 0.3s ease, opacity 0.3s ease;
275
+ }
276
+
37
277
  .hm-zoom-modal.active .hm-zoom-inner { transform: scale(1); opacity: 1; }
38
- .hm-zoom-hint { position: absolute; bottom: 10px; right: 20px; color: rgba(0,0,0,0.4); font-size: 14px; font-family: system-ui, sans-serif; background: rgba(255,255,255,0.7); padding: 4px 10px; border-radius: 6px; pointer-events: none; user-select: none; transition: opacity 1s ease 2s; opacity: 1; }
39
- .hm-loader { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(2px); z-index: 1000; opacity: 0; transition: opacity 0.25s ease; pointer-events: none; }
278
+
279
+ .hm-zoom-content {
280
+ position: relative;
281
+ width: 100%;
282
+ height: 100%;
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ background: var(--hm-background);
287
+ overflow: auto;
288
+ }
289
+
290
+ .hm-zoom-close {
291
+ position: absolute;
292
+ top: 12px;
293
+ right: 12px;
294
+ border: none;
295
+ background: rgba(0, 0, 0, 0.75);
296
+ color: #fff;
297
+ border-radius: 50%;
298
+ width: 42px;
299
+ height: 42px;
300
+ font-size: 24px;
301
+ cursor: pointer;
302
+ z-index: 10000;
303
+ line-height: 1;
304
+ }
305
+
306
+ .hm-zoom-close:hover { background: rgba(0, 0, 0, 0.9); }
307
+
308
+ .hm-zoom-modal svg {
309
+ width: auto;
310
+ height: auto;
311
+ max-width: 100%;
312
+ max-height: 100%;
313
+ display: block;
314
+ transition: transform 0.25s ease;
315
+ }
316
+
317
+ .hm-zoom-hint {
318
+ position: absolute;
319
+ bottom: 10px;
320
+ right: 20px;
321
+ color: rgba(0,0,0,0.4);
322
+ font-size: 14px;
323
+ background: rgba(255,255,255,0.7);
324
+ padding: 4px 10px;
325
+ border-radius: 6px;
326
+ pointer-events: none;
327
+ user-select: none;
328
+ transition: opacity 1s ease 2s;
329
+ opacity: 1;
330
+ }
331
+
332
+ /* ==================== LOADER ==================== */
333
+ .hm-loader {
334
+ position: absolute;
335
+ inset: 0;
336
+ display: flex;
337
+ flex-direction: column;
338
+ align-items: center;
339
+ justify-content: center;
340
+ background: rgba(255, 255, 255, 0.7);
341
+ backdrop-filter: blur(2px);
342
+ z-index: 1000;
343
+ opacity: 0;
344
+ transition: opacity 0.25s ease;
345
+ pointer-events: none;
346
+ }
347
+
40
348
  .hm-loader.active { opacity: 1; pointer-events: all; }
41
- .hm-loader::before { content: ''; width: 42px; height: 42px; border: 3px solid rgba(31,41,55,0.2); border-top-color: #3b82f6; border-radius: 50%; animation: hm-spin 1s linear infinite; }
42
- .hm-loader span { margin-top: 10px; font-size: 14px; color: #1f2937; font-weight: 500; letter-spacing: 0.3px; opacity: 0.85; }
43
- @keyframes hm-spin { to { transform: rotate(360deg); } }
44
349
 
350
+ .hm-loader::before {
351
+ content: '';
352
+ width: 42px;
353
+ height: 42px;
354
+ border: 3px solid rgba(31,41,55,0.2);
355
+ border-top-color: #3b82f6;
356
+ border-radius: 50%;
357
+ animation: hm-spin 1s linear infinite;
358
+ }
359
+
360
+ .hm-loader span {
361
+ margin-top: 10px;
362
+ font-size: 14px;
363
+ color: var(--hm-text-color);
364
+ font-weight: 500;
365
+ letter-spacing: 0.3px;
366
+ opacity: 0.85;
367
+ }
368
+
369
+ @keyframes hm-spin { to { transform: rotate(360deg); } }
45
370
  `;
46
371
 
47
372
  // ───────────────────────────────────────────────────────────────────────────
@@ -144,6 +469,8 @@
144
469
  constructor(){
145
470
  super();
146
471
  this.attachShadow({mode:'open'});
472
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
473
+ this._theme = this.getAttribute('theme') || (prefersDark ? 'dark' : 'light');
147
474
  this._view=this.getAttribute('view') || 'head_right';
148
475
  this._zones=ZONES;
149
476
  this._selected=new Set();
@@ -181,6 +508,8 @@
181
508
  thorax_front: this._imgRoot + 'torax_front.svg',
182
509
  thorax_back: this._imgRoot + 'torax_back.svg'
183
510
  };
511
+
512
+ this._applyTheme();
184
513
  }
185
514
 
186
515
  _upgradeProperty(prop) {
@@ -198,6 +527,22 @@
198
527
  this._imgRoot = this.getAttribute('img-root') || this._imgRoot;
199
528
  this._syncUrl = this.getAttribute('sync-url') || null;
200
529
 
530
+ if (!this.hasAttribute('theme')) {
531
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
532
+ this.setAttribute('theme', prefersDark ? 'dark' : 'light');
533
+ }
534
+
535
+ this._applyTheme();
536
+
537
+ // --- 3️⃣ Escuchar cambios del sistema (modo oscuro/claro del SO) ---
538
+ this._mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
539
+ this._mediaListener = e => {
540
+ if (!this.hasAttribute('theme')) {
541
+ this.setAttribute('theme', e.matches ? 'dark' : 'light');
542
+ }
543
+ };
544
+ this._mediaQuery.addEventListener('change', this._mediaListener);
545
+
201
546
  this._renderShell();
202
547
  this._renderCanvas();
203
548
 
@@ -224,11 +569,23 @@
224
569
  }
225
570
  }
226
571
 
227
- static get observedAttributes() { return ['view', 'img-root', 'read-only', 'sync-url']; }
572
+ disconnectedCallback() {
573
+ if (this._mediaQuery && this._mediaListener) {
574
+ this._mediaQuery.removeEventListener('change', this._mediaListener);
575
+ }
576
+ }
577
+
578
+ static get observedAttributes() { return ['view', 'img-root', 'read-only', 'sync-url', 'theme']; }
228
579
 
229
580
  attributeChangedCallback (name, oldValue, newValue) {
230
581
  if (oldValue === newValue) return;
231
582
 
583
+ if (name === 'theme' && oldValue !== newValue) {
584
+ this._theme = newValue === 'dark' ? 'dark' : 'light';
585
+ this._applyTheme();
586
+ return;
587
+ }
588
+
232
589
  if (name === 'view') {
233
590
  this._view = newValue;
234
591
  if (this._root) this._renderCanvas();
@@ -340,6 +697,32 @@
340
697
  });
341
698
  }
342
699
 
700
+ _applyTheme() {
701
+ const base = this._imgRoot.endsWith('/') ? this._imgRoot : this._imgRoot + '/';
702
+ const basePath = base + (this._theme === 'dark' ? 'dark/' : 'light/');
703
+
704
+ this._bg = {
705
+ head_right: basePath + 'head_right.svg',
706
+ head_left: basePath + 'head_left.svg',
707
+ neck_right: basePath + 'neck_right.svg',
708
+ neck_left: basePath + 'neck_left.svg',
709
+ thorax_front: basePath + 'torax_front.svg',
710
+ thorax_back: basePath + 'torax_back.svg'
711
+ };
712
+
713
+ // Si tienes tus imágenes organizadas por vistas
714
+ /*Object.keys(this._bg).forEach(k => {
715
+ this._bg[k] = `${basePath}${k}.svg`;
716
+ });*/
717
+
718
+ this._root?.classList.toggle('theme-dark', this._theme === 'dark');
719
+ this._root?.classList.toggle('theme-light', this._theme === 'light');
720
+
721
+ if (this._root) {
722
+ this._renderCanvas();
723
+ }
724
+ }
725
+
343
726
  get syncUrl() { return this._syncUrl; }
344
727
 
345
728
  set syncUrl(url) {
@@ -386,8 +769,12 @@
386
769
  <select id="picker">${opts}</select>
387
770
  <button id="menu" class="menu-btn" title="Más opciones">⋮</button>
388
771
  <div id="dropdown" class="hm-dropdown">
389
- <button id="export">📤 Exportar zonas (.json)</button>
390
- <button id="import">📥 Importar zonas (.json)</button>
772
+ <button data-action="export">📤 Exportar zonas (.json)</button>
773
+ <button data-action="import">📥 Importar zonas (.json)</button>
774
+ <hr style="margin:4px 0;">
775
+ <div style="padding: 4px 16px; font-weight:600;">Tema</div>
776
+ <button data-action="theme-light">🌞 Claro</button>
777
+ <button data-action="theme-dark">🌙 Oscuro</button>
391
778
  <input id="fileInput" type="file" accept=".json" style="display:none;">
392
779
  </div>
393
780
  </div>
@@ -444,8 +831,6 @@
444
831
  // Dropdown menú
445
832
  this._els.menu = this.shadowRoot.getElementById('menu');
446
833
  this._els.dropdown = this.shadowRoot.getElementById('dropdown');
447
- this._els.exportBtn = this.shadowRoot.getElementById('export');
448
- this._els.importBtn = this.shadowRoot.getElementById('import');
449
834
  this._els.fileInput = this.shadowRoot.getElementById('fileInput');
450
835
 
451
836
  // Mostrar/ocultar menú
@@ -461,25 +846,27 @@
461
846
  }
462
847
  });
463
848
 
464
- // Exportar zonas seleccionadas
465
- this._els.exportBtn.addEventListener('click', () => {
466
- this._els.dropdown.classList.remove('active');
467
- const data = JSON.stringify(this.selectedZones, null, 2);
468
- const blob = new Blob([data], { type: 'application/json' });
469
- const url = URL.createObjectURL(blob);
470
- const a = document.createElement('a');
471
- a.href = url;
472
- a.download = 'zonas_seleccionadas.json';
473
- a.click();
474
- URL.revokeObjectURL(url);
475
- this._showToast('✅ Zonas exportadas correctamente');
476
- });
477
-
478
- // Importar zonas desde archivo .json
479
- this._els.importBtn.addEventListener('click', () => {
849
+ this._els.dropdown.addEventListener('click', e => {
850
+ const action = e.target.dataset.action;
851
+ if (!action) return;
852
+
853
+ switch (action) {
854
+ case 'export':
855
+ this.exportSelectedZones();
856
+ break;
857
+ case 'import':
858
+ this.importZonesFromFile();
859
+ break;
860
+ case 'theme-light':
861
+ this.setAttribute('theme', 'light');
862
+ break;
863
+ case 'theme-dark':
864
+ this.setAttribute('theme', 'dark');
865
+ break;
866
+ }
867
+
480
868
  this._els.dropdown.classList.remove('active');
481
- this._els.fileInput.click();
482
- });
869
+ });
483
870
 
484
871
  this._els.fileInput.addEventListener('change', e => {
485
872
  const file = e.target.files[0];
@@ -661,12 +1048,13 @@
661
1048
  t.textContent = z.code;
662
1049
  t.setAttribute('x', (x + w / 2) * layout.vb[2]);
663
1050
  t.setAttribute('y', (y + h / 2) * layout.vb[3]);
664
- t.setAttribute('fill', '#0a0a0a');
1051
+ t.classList.add('label');
665
1052
  t.setAttribute('font-size', '32');
666
1053
  t.setAttribute('font-weight', '700');
667
1054
  t.setAttribute('text-anchor', 'middle');
668
1055
  t.setAttribute('dominant-baseline', 'middle');
669
1056
  t.setAttribute('pointer-events', 'none');
1057
+ t.setAttribute('class','label');
670
1058
  gZones.appendChild(t);
671
1059
  });
672
1060
 
@@ -679,7 +1067,7 @@
679
1067
  t.textContent = v.label;
680
1068
  t.setAttribute('x', layout.vb[2] / 2);
681
1069
  t.setAttribute('y', 80);
682
- t.setAttribute('class', 'hm-all-label');
1070
+ t.classList.add('hm-all-label');
683
1071
  g.appendChild(t);
684
1072
 
685
1073
 
@@ -726,7 +1114,6 @@
726
1114
  });
727
1115
  }
728
1116
 
729
-
730
1117
  g.appendChild(rect);
731
1118
 
732
1119
  const t=document.createElementNS('http://www.w3.org/2000/svg','text');
@@ -864,19 +1251,9 @@
864
1251
  // Ajustar estilos dentro del SVG
865
1252
  const style = document.createElementNS('http://www.w3.org/2000/svg', 'style');
866
1253
  style.textContent = `
867
- .zone { fill: rgba(31,41,55,0); cursor: default; transition: fill 120ms ease; }
868
- .zone.selected { fill: rgba(31,41,55,0.36); }
869
- .label { fill: #0a0a0a; font-size: 42px; font-weight: 800;
870
- text-anchor: middle; dominant-baseline: middle;
871
- pointer-events: none; user-select: none; }
872
- .hm-all-label {
873
- font-family: system-ui, sans-serif;
874
- font-size: 48px;
875
- font-weight: 800;
876
- fill: #111827;
877
- text-anchor: middle;
878
- dominant-baseline: middle;
879
- }
1254
+ .zone { cursor: default; transition: fill 120ms ease; }
1255
+ .label { font-size: 42px; font-weight: 800; text-anchor: middle; dominant-baseline: middle; pointer-events: none; user-select: none; }
1256
+ .hm-all-label { font-family: system-ui, sans-serif; font-size: 48px; font-weight: 800; text-anchor: middle; dominant-baseline: middle; }
880
1257
  `;
881
1258
  clone.insertBefore(style, clone.firstChild);
882
1259
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanmap-vas",
3
- "version": "1.0.27",
3
+ "version": "1.0.28",
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": [
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 595.28 841.89">
3
+ <defs>
4
+ <style>
5
+ .cls-1 {
6
+ fill: #7c7c7c;
7
+ }
8
+
9
+ .cls-1, .cls-2 {
10
+ stroke-width: 0px;
11
+ }
12
+
13
+ .cls-2 {
14
+ fill: #828282;
15
+ }
16
+ </style>
17
+ </defs>
18
+ <path class="cls-1" d="m498.88,734.49c-25.62-42.22-37.2-92.98-31.73-142.12,0,0,.7-5.68.7-5.68l1.09-5.62c.27-1.75.78-3.92,1.25-5.59,0,0,1.5-5.54,1.5-5.54.75-2.63,1.92-5.52,2.77-8.15.67-1.64,2.56-6.2,3.31-7.94,0,0,2.52-5.15,2.52-5.15,5.09-10.2,11.29-19.88,17.25-29.56,10.03-16.06,19.25-32.57,26.57-50.01,29.7-69.45,48.07-150.02,14.87-221.57-37.95-81.21-126.25-134.06-214.82-137.49-71.03-2.81-144.36,24.49-192.23,77.81-1.92,2.09-5.52,6.36-7.28,8.57-7.14,8.68-12.45,18.54-17.36,28.66-15.91,34.28-30.72,81.44-30.69,119.33.06,3.7.44,7.37,1.19,10.88,1.31,6.95,6.1,12.95,9.28,20.22,3.34,7.26,4.08,15.63,2.75,23.49-1.34,7.8-4.84,14.87-8.42,21.76-3.61,6.76-7.47,13.31-11.35,19.84-6.79,11.26-16.67,27.96-23.89,38.82-3.85,6.05-8.17,12.46-7.69,19.78.51,4.92,2,10.59,6.93,12.7,8.6,3.71,19.82,1.37,27.34,8.15,3.13,2.81,5.06,6.76,5.71,10.86,1.32,7.95-4.94,14.9-6.38,22.07-2.03,11.67,10.83,18.04,20.29,20.91,0,0,3.28,1.03,3.28,1.03l-3.05,1.57c-2.48,1.28-4.89,2.63-6.8,4.4-2.84,2.56-4.32,6.51-3.61,10.25,1,3.97,4.23,6.53,7.07,9.53,9.06,8.61,13.78,19.65,9.37,31.92-1.75,5.38-3.7,10.58-3.98,16.22-.2,5.48.86,11.04,3.02,16.17,6.39,15.16,20.16,28.43,36.76,31.16,22.06,2.98,36.4-17.72,65.88-11.9,16.97,3.16,31.41,13.99,42.82,26.46-15.11-16.39-36.76-29.52-59.78-26.11-11.37,1.44-21.32,7.34-32.05,11.03-11.17,4.07-23.23,2.34-33.21-4.06-14.48-9.07-25.92-25.25-25.6-42.84.2-5.8,2.19-11.39,3.9-16.8,2.82-8.18,1.51-16.2-3.53-23.17-3.6-5.36-9.43-8.85-12.53-14.74-2.37-5.09-.49-11.46,3.56-15.15,2.26-2.11,4.86-3.55,7.4-4.89l.23,2.61c-3.69-1.14-7.34-2.53-10.79-4.43-7.1-3.72-13.46-11.13-11.67-19.73,1.61-8.66,9.27-15.87,5.15-24.77-4.92-12.21-20.27-8.1-30.29-12.64-6.06-2.6-8.5-9.46-9.01-15.58-.64-8.23,3.99-15.47,8.12-22.01,2.05-3.21,4.1-6.38,6.09-9.59,9.1-15.15,20.29-33.49,28.6-49.01,7.09-13,11.88-27.5,5.38-41.57-1.48-3.32-3.37-6.51-5.22-9.91-6.17-10.3-6.19-22.79-5.49-34.39,3.38-38.1,14.65-75.25,30.55-109.95,5.71-12.15,12.67-23.9,21.55-34.08,48.7-57.26,125.39-86.91,199.84-83.8,90.46,3.58,180.01,57.56,218.52,140.61,33.37,72.59,14.44,154.37-16.11,224.49-7.47,17.51-16.92,34.18-27.08,50.2-6.02,9.64-12.27,19.11-17.44,29.22,0,0-2.54,5.05-2.54,5.05-.76,1.88-2.66,5.93-3.34,7.78-.91,2.53-2,5.43-2.81,7.98,0,0-1.53,5.43-1.53,5.43-.94,3.32-1.72,7.67-2.42,11.03,0,0-.74,5.61-.74,5.61-5.93,48.88,5.15,99.48,30.02,141.91h0Z"/>
19
+ <path class="cls-1" d="m286.36,445.9c-7.25-14.31-9.02-31.29-4.37-46.71,5.55-20.62,20.25-38.87,43.73-35.1,16.28,2.98,31.07,14.96,36.71,30.62,5.32,15.65,2.82,32.28.85,48.28-1.88,12-5.15,23.95-10.9,34.71-3.5,6.17-9.24,14.08-13.15,20.03-3.37,4.93-6.72,9.91-10.42,14.64-1.91,2.4-4.02,4.94-6.63,6.75-6.61,4.08-14.53,7.07-22.38,5.99-17.13-3.38-17.68-27.02-16.34-40.72.7-7.93,2.31-15.72,3.94-23.49-2.14,15.39-4.62,31.68-.42,46.82,2.33,8.17,8.11,15.5,17.37,14.25,2.79-.33,5.51-1.04,8.14-2.09,3.4-1.41,6.88-2.96,9.48-5.62,5.12-5.58,9.3-12.6,13.62-18.95,4.86-7.79,11.61-16.64,15.11-25.05,5.45-12.56,7.85-26.08,9.05-39.67.8-7.72,1.38-15.52.53-23.2-1.32-15.67-10.72-28.47-24.64-35.51-30.44-14.85-49.63,11.71-54.03,39.4-1.68,11.69.2,23.71,4.76,34.64h0Z"/>
20
+ <path class="cls-1" d="m302.02,440.62c-3.29.99-5.58,3.41-3.35,6.66,1.74,2.76,3.57,5.82,4.06,9.2,1.06,6.85-1.79,13.87-5.8,19.2-.4.6-.57,1.11-.53,1.21.4,1.11,2.11,1.96,3.45,2.08,2.78.31,3.43-1.89,4.67-4.34,1.8-4.16,5.28-8.35,9.97-9.52,2.33-.67,5.25-.11,7.18-.74,2.47-.68,4.88-2.4,6.7-4.58,3.7-4.42,5.56-10.44,4.3-16.19-2.01-8.87-8.48-16.86-16.86-20.57-2.96-1.46-5.82-3.21-8.44-5.18-2.55-2.01-5.48-3.61-7.69-6.02,2.57,2.02,5.67,3.16,8.45,4.85,2.82,1.59,5.66,2.99,8.67,4.09,3.09,1.09,6.33,2.72,8.89,4.87,11.48,9.17,16.58,25.05,6.43,37.27-3.44,4.34-8.92,7.27-14.58,6.75-4.36-.04-7,3.41-8.81,7.07-1.05,2.18-3.09,5.81-6.17,6.39-4.67,1.38-12.24-2.78-9.64-8.36,1.04-1.92,2.64-3.44,3.64-5.12,2.39-3.83,4.03-8.37,3.75-12.9-.09-1.5-.4-3-.94-4.45-.74-2.28-2.36-4.43-2.61-6.96-.21-2.91,2.91-4.44,5.27-4.73h0Z"/>
21
+ <path class="cls-1" d="m313.51,437.94c-6.12-5.61-10.75-12.92-13.51-20.74-1.47-3.88-2.74-8-3-12.38-.99-14.4,10.88-24.58,23.98-27.63,6.61-1.63,14.52.41,19.22,5.57,4.42,4.83,7.55,10.3,10.66,15.98,8.55,16.48,6,32.8-.04,49.56-4.27,11.72-10.92,22.64-19.31,31.77,4.73-6.76,8.85-13.88,12.05-21.37,4.63-11.09,7.94-23.1,8.43-35.11.1-7.88-1.86-15.65-5.48-22.64-1.82-3.49-4.14-6.97-6.32-10.24-2.11-3.1-4.44-6.08-7.77-7.72-9.37-4.72-24.11,2.41-29.21,10.89-4.24,6.78-3.26,15.09-.89,22.52,2.12,7.93,5.92,15.21,11.19,21.52h0Z"/>
22
+ <path class="cls-2" d="m298.64,402.89c2.76,1.31,5.24,2.98,7.96,4.26,4.55,2.01,9.35,3.16,14.25,4.18-8.27,1.51-16.9-2-22.21-8.43h0Z"/>
23
+ </svg>