@jjlmoya/utils-babies 1.2.0 → 1.4.0

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.
Files changed (27) hide show
  1. package/package.json +2 -2
  2. package/src/tool/baby-feeding-calculator/bibliography.astro +8 -4
  3. package/src/tool/baby-feeding-calculator/component.astro +208 -177
  4. package/src/tool/baby-feeding-calculator/style.css +317 -229
  5. package/src/tool/baby-percentile-calculator/bibliography.astro +8 -4
  6. package/src/tool/baby-percentile-calculator/component.astro +98 -240
  7. package/src/tool/baby-percentile-calculator/i18n/en.ts +1 -0
  8. package/src/tool/baby-percentile-calculator/i18n/es.ts +1 -0
  9. package/src/tool/baby-percentile-calculator/i18n/fr.ts +1 -0
  10. package/src/tool/baby-percentile-calculator/index.ts +1 -0
  11. package/src/tool/baby-percentile-calculator/style.css +342 -268
  12. package/src/tool/baby-size-converter/bibliography.astro +8 -4
  13. package/src/tool/baby-size-converter/component.astro +221 -212
  14. package/src/tool/baby-size-converter/style.css +433 -263
  15. package/src/tool/fertile-days-estimator/bibliography.astro +8 -4
  16. package/src/tool/fertile-days-estimator/component.astro +202 -200
  17. package/src/tool/fertile-days-estimator/style.css +408 -270
  18. package/src/tool/pregnancy-calculator/bibliography.astro +8 -4
  19. package/src/tool/pregnancy-calculator/component.astro +50 -8
  20. package/src/tool/pregnancy-calculator/i18n/en.ts +8 -0
  21. package/src/tool/pregnancy-calculator/i18n/es.ts +8 -0
  22. package/src/tool/pregnancy-calculator/i18n/fr.ts +8 -0
  23. package/src/tool/pregnancy-calculator/index.ts +8 -0
  24. package/src/tool/pregnancy-calculator/style.css +351 -134
  25. package/src/tool/vaccination-calendar/bibliography.astro +8 -4
  26. package/src/tool/vaccination-calendar/component.astro +120 -124
  27. package/src/tool/vaccination-calendar/style.css +296 -209
@@ -1,7 +1,11 @@
1
1
  ---
2
2
  import { Bibliography as BibliographyUI } from '@jjlmoya/utils-shared';
3
- import type { BibliographyEntry } from '../../types';
4
- interface Props { links?: BibliographyEntry[]; title: string; }
5
- const { links = [], title } = Astro.props;
3
+ import { babySizeConverter } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props { locale?: KnownLocale; }
7
+ const { locale = 'es' } = Astro.props;
8
+ const content = await babySizeConverter.i18n[locale]?.();
9
+ if (!content) return null;
6
10
  ---
7
- <BibliographyUI links={links} title={title} />
11
+ <BibliographyUI links={content.bibliography} title={content.bibliographyTitle ?? ''} />
@@ -10,106 +10,120 @@ const brands = brandsData.brands;
10
10
  ---
11
11
  <div
12
12
  id="baby-size-converter-root"
13
- class="baby-size-converter"
13
+ class="bsc-card"
14
14
  data-ui={JSON.stringify(ui)}
15
15
  data-brands={JSON.stringify(brands)}
16
16
  >
17
- <div class="baby-size-converter-main">
18
- <div class="baby-size-converter-left">
19
- <div class="baby-size-converter-section-marker">{ui.labelInput}</div>
20
-
21
- <div class="baby-size-converter-unit-nav">
22
- <button class="baby-size-converter-unit-tab active" data-unit="metric">{ui.unitMetric}</button>
23
- <button class="baby-size-converter-unit-tab" data-unit="imperial">{ui.unitImperial}</button>
17
+ <div class="bsc-main">
18
+ <div class="bsc-left">
19
+ <span class="bsc-section-marker">{ui.labelInput}</span>
20
+
21
+ <div class="bsc-brand-picker">
22
+ <div class="bsc-select-box">
23
+ <select id="bsc-origin-brand" aria-label={ui.labelBrandFit}>
24
+ {brands.map((brand) => <option value={brand.id}>{brand.name}</option>)}
25
+ </select>
26
+ </div>
27
+ <div class="bsc-unit-nav">
28
+ <button class="bsc-unit-tab bsc-unit-active" data-unit="metric">{ui.unitMetric}</button>
29
+ <button class="bsc-unit-tab" data-unit="imperial">{ui.unitImperial}</button>
30
+ </div>
24
31
  </div>
25
32
 
26
- <div class="baby-size-converter-num-ctrl">
27
- <div class="baby-size-converter-num-label" id="bsc-height-label">{ui.labelHeight} ({ui.labelCm})</div>
28
- <div class="baby-size-converter-stepper-box">
29
- <button class="baby-size-converter-btn-step" id="bsc-height-dec" aria-label="Reducir altura">−</button>
30
- <div class="baby-size-converter-val-view">
31
- <span class="baby-size-converter-val-big" id="bsc-height-val">68</span>
32
- <span class="baby-size-converter-val-sub" id="bsc-height-unit">{ui.labelCm}</span>
33
+ <div class="bsc-num-ctrl">
34
+ <label class="bsc-num-label">{ui.labelHeight}</label>
35
+ <div class="bsc-stepper-box">
36
+ <button class="bsc-btn-step" data-action="dec" data-target="bsc-height-slider" aria-label="Menos altura">-</button>
37
+ <div class="bsc-val-view">
38
+ <span class="bsc-val-big" id="bsc-height-val">68</span>
39
+ <span class="bsc-val-sub" id="bsc-height-unit">cm</span>
33
40
  </div>
34
- <button class="baby-size-converter-btn-step" id="bsc-height-inc" aria-label="Aumentar altura">+</button>
41
+ <button class="bsc-btn-step" data-action="inc" data-target="bsc-height-slider" aria-label="Más altura">+</button>
35
42
  </div>
36
- <input class="baby-size-converter-slider-line" type="range" id="bsc-height-slider" min="45" max="100" value="68" />
43
+ <input type="range" id="bsc-height-slider" min="45" max="100" step="1" value="68" class="bsc-slider" />
37
44
  </div>
38
45
 
39
- <div class="baby-size-converter-num-ctrl">
40
- <div class="baby-size-converter-num-label" id="bsc-weight-label">{ui.labelWeight} ({ui.labelKg})</div>
41
- <div class="baby-size-converter-stepper-box">
42
- <button class="baby-size-converter-btn-step" id="bsc-weight-dec" aria-label="Reducir peso">−</button>
43
- <div class="baby-size-converter-val-view">
44
- <span class="baby-size-converter-val-big" id="bsc-weight-val">8.0</span>
45
- <span class="baby-size-converter-val-sub" id="bsc-weight-unit">{ui.labelKg}</span>
46
+ <div class="bsc-num-ctrl">
47
+ <label class="bsc-num-label">{ui.labelWeight}</label>
48
+ <div class="bsc-stepper-box">
49
+ <button class="bsc-btn-step" data-action="dec" data-target="bsc-weight-slider" aria-label="Menos peso">-</button>
50
+ <div class="bsc-val-view">
51
+ <span class="bsc-val-big" id="bsc-weight-val">8.0</span>
52
+ <span class="bsc-val-sub" id="bsc-weight-unit">kg</span>
46
53
  </div>
47
- <button class="baby-size-converter-btn-step" id="bsc-weight-inc" aria-label="Aumentar peso">+</button>
54
+ <button class="bsc-btn-step" data-action="inc" data-target="bsc-weight-slider" aria-label="Más peso">+</button>
48
55
  </div>
49
- <input class="baby-size-converter-slider-line" type="range" id="bsc-weight-slider" min="2" max="20" step="0.1" value="8" />
56
+ <input type="range" id="bsc-weight-slider" min="3" max="18" step="0.5" value="8" class="bsc-slider" />
50
57
  </div>
51
58
 
52
- <div class="baby-size-converter-num-ctrl">
53
- <div class="baby-size-converter-num-label">{ui.labelPresets}</div>
54
- <div class="baby-size-converter-age-rack">
55
- <button class="baby-size-converter-age-tile" data-height="56" data-weight="4.5">0-1m</button>
56
- <button class="baby-size-converter-age-tile" data-height="62" data-weight="6">1-3m</button>
57
- <button class="baby-size-converter-age-tile" data-height="68" data-weight="8">3-6m</button>
58
- <button class="baby-size-converter-age-tile" data-height="74" data-weight="9">6-9m</button>
59
- <button class="baby-size-converter-age-tile" data-height="80" data-weight="10">9-12m</button>
60
- <button class="baby-size-converter-age-tile" data-height="86" data-weight="11.5">12-18m</button>
61
- <button class="baby-size-converter-age-tile" data-height="92" data-weight="13">18-24m</button>
59
+ <div class="bsc-num-ctrl">
60
+ <label class="bsc-num-label">{ui.labelPresets}</label>
61
+ <div class="bsc-age-rack">
62
+ <button class="bsc-age-tile" data-age="0.5">NB</button>
63
+ <button class="bsc-age-tile" data-age="3">3m</button>
64
+ <button class="bsc-age-tile bsc-age-active" data-age="6">6m</button>
65
+ <button class="bsc-age-tile" data-age="9">9m</button>
66
+ <button class="bsc-age-tile" data-age="12">12m</button>
67
+ <button class="bsc-age-tile" data-age="18">18m</button>
68
+ <button class="bsc-age-tile" data-age="24">24m</button>
62
69
  </div>
63
70
  </div>
64
-
65
- <div class="baby-size-converter-num-ctrl">
66
- <div class="baby-size-converter-num-label">{ui.labelBrandFit}</div>
67
- <select class="baby-size-converter-select-box" id="bsc-brand-select">
68
- {brands.map((b) => (
69
- <option value={b.id}>{b.name}</option>
70
- ))}
71
- </select>
72
- </div>
73
71
  </div>
74
72
 
75
- <div class="baby-size-converter-right">
76
- <div class="baby-size-converter-section-marker">{ui.labelResults}</div>
77
-
78
- <div class="baby-size-converter-res-header">
79
- <div class="baby-size-converter-res-size-main" id="bsc-size-label">68</div>
80
- <div class="baby-size-converter-res-brand-hint" id="bsc-brand-hint">Zara</div>
81
- <span class="baby-size-converter-fit-label" id="bsc-fit-label">{ui.fitRegular}</span>
82
- <button class="baby-size-converter-share-btn" id="bsc-share-btn" aria-label={ui.shareAriaLabel}>
83
- &#8679;
84
- </button>
73
+ <div class="bsc-right">
74
+ <button id="bsc-share-btn" class="bsc-share-btn" aria-label={ui.shareAriaLabel}>
75
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
76
+ <path d="M18 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"></path>
77
+ <path d="M6 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"></path>
78
+ <path d="M18 22a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"></path>
79
+ <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
80
+ <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
81
+ </svg>
82
+ </button>
83
+
84
+ <span class="bsc-section-marker">{ui.labelResults}</span>
85
+
86
+ <div class="bsc-res-header">
87
+ <span class="bsc-res-brand-hint" id="bsc-display-brand">Zara</span>
88
+ <span class="bsc-res-size-main" id="bsc-display-size">3-6 m</span>
89
+ <span class="bsc-fit-label bsc-fit-regular" id="bsc-status-badge">{ui.fitRegular}</span>
85
90
  </div>
86
91
 
87
- <div class="baby-size-converter-clothes-meta">
88
- <div class="baby-size-converter-meta-title">{ui.labelGarmentMeasures}</div>
89
- <div class="baby-size-converter-meta-vals">
90
- <div class="baby-size-converter-meta-cell">
91
- <span class="baby-size-converter-num-label">{ui.labelChest}</span>
92
- <span class="baby-size-converter-val-big" id="bsc-chest-val">44 cm</span>
93
- </div>
94
- <div class="baby-size-converter-meta-cell">
95
- <span class="baby-size-converter-num-label">{ui.labelWaist}</span>
96
- <span class="baby-size-converter-val-big" id="bsc-waist-val">43 cm</span>
97
- </div>
92
+ <div class="bsc-equivalents-box">
93
+ <div class="bsc-eq-head-row">
94
+ <span class="bsc-eq-col-name">{ui.labelBrandFit}</span>
95
+ <span class="bsc-eq-col-name">{ui.labelSuggested}</span>
98
96
  </div>
97
+ <div class="bsc-eq-data-list" id="bsc-equivalents-grid"></div>
99
98
  </div>
100
99
 
101
- <div class="baby-size-converter-equivalents-grid-box">
102
- <div class="baby-size-converter-eq-head-row">
103
- <span class="baby-size-converter-eq-col-name">{ui.labelBrandFit}</span>
104
- <span>{ui.labelSuggested}</span>
100
+ <div class="bsc-clothes-meta">
101
+ <span class="bsc-meta-title">{ui.labelGarmentMeasures}</span>
102
+ <div class="bsc-meta-vals">
103
+ <div class="bsc-meta-cell">
104
+ <label>{ui.labelChest}</label>
105
+ <span id="bsc-chest-val">44 cm</span>
106
+ </div>
107
+ <div class="bsc-meta-cell">
108
+ <label>{ui.labelWaist}</label>
109
+ <span id="bsc-waist-val">43 cm</span>
110
+ </div>
105
111
  </div>
106
- <div id="bsc-eq-list"></div>
107
112
  </div>
113
+ </div>
114
+ </div>
108
115
 
109
- <div class="baby-size-converter-pro-tip-footer">
110
- <span class="baby-size-converter-footer-icon">&#9432;</span>
111
- <span class="baby-size-converter-footer-text">{ui.tipText}</span>
112
- </div>
116
+ <div class="bsc-pro-tip-footer">
117
+ <div class="bsc-footer-icon">
118
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
119
+ <circle cx="12" cy="12" r="10"></circle>
120
+ <path d="M12 16v-4"></path>
121
+ <path d="M12 8h.01"></path>
122
+ </svg>
123
+ </div>
124
+ <div class="bsc-footer-text">
125
+ <h5>{ui.tipTitle}</h5>
126
+ <p set:html={ui.tipText}></p>
113
127
  </div>
114
128
  </div>
115
129
  </div>
@@ -121,169 +135,164 @@ const brands = brandsData.brands;
121
135
  const ui = JSON.parse(root.dataset.ui);
122
136
  const brands = JSON.parse(root.dataset.brands);
123
137
 
124
- let heightCm = 68;
125
- let weightKg = 8.0;
126
- let unitMode = 'metric';
127
- let activeBrandId = brands[0].id;
128
-
129
- const heightSlider = root.querySelector('#bsc-height-slider');
130
- const weightSlider = root.querySelector('#bsc-weight-slider');
131
- const heightValEl = root.querySelector('#bsc-height-val');
132
- const weightValEl = root.querySelector('#bsc-weight-val');
133
- const heightLabelEl = root.querySelector('#bsc-height-label');
134
- const weightLabelEl = root.querySelector('#bsc-weight-label');
135
- const heightUnitEl = root.querySelector('#bsc-height-unit');
136
- const weightUnitEl = root.querySelector('#bsc-weight-unit');
137
- const brandSelect = root.querySelector('#bsc-brand-select');
138
- const sizeLabelEl = root.querySelector('#bsc-size-label');
139
- const brandHintEl = root.querySelector('#bsc-brand-hint');
140
- const fitLabelEl = root.querySelector('#bsc-fit-label');
141
- const chestValEl = root.querySelector('#bsc-chest-val');
142
- const waistValEl = root.querySelector('#bsc-waist-val');
143
- const eqList = root.querySelector('#bsc-eq-list');
144
- const shareBtn = root.querySelector('#bsc-share-btn');
145
-
146
- function findBestSize(brand, height, weight) {
138
+ let uMode = 'metric';
139
+ let bCid = brands[0] ? brands[0].id : '';
140
+ let hVal = 68;
141
+ let wVal = 8;
142
+
143
+ const hIn = document.getElementById('bsc-height-slider');
144
+ const wIn = document.getElementById('bsc-weight-slider');
145
+ const hOut = document.getElementById('bsc-height-val');
146
+ const wOut = document.getElementById('bsc-weight-val');
147
+ const hUnit = document.getElementById('bsc-height-unit');
148
+ const wUnit = document.getElementById('bsc-weight-unit');
149
+ const bSel = document.getElementById('bsc-origin-brand');
150
+ const eqList = document.getElementById('bsc-equivalents-grid');
151
+ const uTabs = root.querySelectorAll('.bsc-unit-tab');
152
+ const aPick = root.querySelectorAll('.bsc-age-tile');
153
+ const bSteps = root.querySelectorAll('.bsc-btn-step');
154
+
155
+ function findS(brand, h, w) {
147
156
  let best = brand.sizes[0];
148
157
  let diff = Infinity;
149
158
  brand.sizes.forEach(function (s) {
150
- const d = Math.abs(s.height - height) + Math.abs(s.weight - weight) * 8;
151
- if (d < diff) {
152
- diff = d;
153
- best = s;
154
- }
159
+ const d = Math.abs(s.height - h) + Math.abs(s.weight - w) * 8;
160
+ if (d < diff) { diff = d; best = s; }
155
161
  });
156
162
  return best;
157
163
  }
158
164
 
159
- function getFitLabel(fit) {
160
- if (fit === 'large') return ui.fitLarge;
161
- if (fit === 'small') return ui.fitSmall;
162
- return ui.fitRegular;
165
+ function switchU() {
166
+ uTabs.forEach(function (b) { b.classList.toggle('bsc-unit-active', b.dataset.unit === uMode); });
167
+ if (hUnit) hUnit.textContent = uMode === 'metric' ? 'cm' : 'in';
168
+ if (wUnit) wUnit.textContent = uMode === 'metric' ? 'kg' : 'lb';
169
+ drawNums();
163
170
  }
164
171
 
165
- function getFitClass(fit) {
166
- if (fit === 'large') return 'baby-size-converter-fit-large';
167
- if (fit === 'small') return 'baby-size-converter-fit-small';
168
- return '';
172
+ function drawNums() {
173
+ if (hOut) hOut.textContent = uMode === 'metric' ? hVal.toString() : (hVal / 2.54).toFixed(1);
174
+ if (wOut) wOut.textContent = uMode === 'metric' ? wVal.toFixed(1) : (wVal * 2.20462).toFixed(1);
169
175
  }
170
176
 
171
- function displayHeight() {
172
- if (unitMode === 'imperial') return (heightCm / 2.54).toFixed(1);
173
- return heightCm.toString();
174
- }
175
-
176
- function displayWeight() {
177
- if (unitMode === 'imperial') return (weightKg * 2.20462).toFixed(1);
178
- return weightKg.toFixed(1);
179
- }
180
-
181
- function updateDisplay() {
182
- const activeBrand = brands.find(function (b) { return b.id === activeBrandId; }) || brands[0];
183
- const best = findBestSize(activeBrand, heightCm, weightKg);
184
- const measureUnit = unitMode === 'imperial' ? ui.labelIn : ui.labelCm;
185
- const weightUnit = unitMode === 'imperial' ? ui.labelLb : ui.labelKg;
186
-
187
- heightValEl.textContent = displayHeight();
188
- weightValEl.textContent = displayWeight();
189
- heightUnitEl.textContent = measureUnit;
190
- weightUnitEl.textContent = weightUnit;
191
- heightLabelEl.textContent = ui.labelHeight + ' (' + measureUnit + ')';
192
- weightLabelEl.textContent = ui.labelWeight + ' (' + weightUnit + ')';
193
-
194
- heightSlider.value = heightCm;
195
- weightSlider.value = weightKg;
177
+ function run() {
178
+ const activeBrand = brands.find(function (x) { return x.id === bCid; });
179
+ if (!activeBrand) return;
180
+ const size = findS(activeBrand, hVal, wVal);
181
+
182
+ const dBrand = document.getElementById('bsc-display-brand');
183
+ const dSize = document.getElementById('bsc-display-size');
184
+ const badge = document.getElementById('bsc-status-badge');
185
+
186
+ if (dBrand) dBrand.textContent = activeBrand.name;
187
+ if (dSize) dSize.textContent = size.label;
188
+ if (badge) {
189
+ if (activeBrand.fit === 'large') { badge.className = 'bsc-fit-label bsc-fit-large'; badge.textContent = ui.fitLarge; }
190
+ else if (activeBrand.fit === 'small') { badge.className = 'bsc-fit-label bsc-fit-small'; badge.textContent = ui.fitSmall; }
191
+ else { badge.className = 'bsc-fit-label bsc-fit-regular'; badge.textContent = ui.fitRegular; }
192
+ }
196
193
 
197
- sizeLabelEl.textContent = best.label;
198
- brandHintEl.textContent = activeBrand.name;
199
- fitLabelEl.textContent = getFitLabel(activeBrand.fit);
200
- fitLabelEl.className = 'baby-size-converter-fit-label ' + getFitClass(activeBrand.fit);
194
+ const refBrand = brands[0];
195
+ const refSize = findS(refBrand, hVal, wVal);
196
+ aPick.forEach(function (btn) {
197
+ btn.classList.toggle('bsc-age-active', parseFloat(btn.dataset.age) === refSize.age);
198
+ });
201
199
 
202
- const chestDisplay = unitMode === 'imperial'
203
- ? (best.chest / 2.54).toFixed(1) + ' ' + ui.labelIn
204
- : best.chest + ' ' + ui.labelCm;
205
- const waistDisplay = unitMode === 'imperial'
206
- ? (best.waist / 2.54).toFixed(1) + ' ' + ui.labelIn
207
- : best.waist + ' ' + ui.labelCm;
208
- chestValEl.textContent = chestDisplay;
209
- waistValEl.textContent = waistDisplay;
200
+ const ch = document.getElementById('bsc-chest-val');
201
+ const wa = document.getElementById('bsc-waist-val');
202
+ if (ch && wa) {
203
+ if (uMode === 'metric') {
204
+ ch.textContent = size.chest + ' cm';
205
+ wa.textContent = size.waist + ' cm';
206
+ } else {
207
+ ch.textContent = (size.chest / 2.54).toFixed(1) + ' in';
208
+ wa.textContent = (size.waist / 2.54).toFixed(1) + ' in';
209
+ }
210
+ }
210
211
 
211
- const rows = brands.map(function (b) {
212
- const s = findBestSize(b, heightCm, weightKg);
213
- const isActive = b.id === activeBrandId;
214
- return '<div class="baby-size-converter-eq-data-row' + (isActive ? ' baby-size-converter-eq-active' : '') + '">'
215
- + '<span class="baby-size-converter-eq-brand-v">' + b.name + '</span>'
216
- + '<span class="baby-size-converter-eq-size-v">' + s.label + '</span>'
217
- + '</div>';
218
- }).join('');
219
- eqList.innerHTML = rows;
212
+ if (eqList) {
213
+ eqList.innerHTML = '';
214
+ brands.filter(function (x) { return x.id !== bCid; }).forEach(function (x) {
215
+ const s = findS(x, hVal, wVal);
216
+ const row = document.createElement('div');
217
+ row.className = 'bsc-eq-data-row';
218
+ let fitL = ui.fitRegular;
219
+ if (x.fit === 'large') fitL = ui.fitLarge;
220
+ if (x.fit === 'small') fitL = ui.fitSmall;
221
+ row.innerHTML = '<div class="bsc-eq-brand-v"><span class="bsc-eq-brand-name">' + x.name + '</span><span class="bsc-eq-fit-v bsc-eq-fit-' + x.fit + '">' + fitL + '</span></div><span class="bsc-eq-size-v">' + s.label + '</span>';
222
+ eqList.appendChild(row);
223
+ });
224
+ }
220
225
  }
221
226
 
222
- root.querySelectorAll('.baby-size-converter-unit-tab').forEach(function (btn) {
223
- btn.addEventListener('click', function () {
224
- unitMode = btn.dataset.unit;
225
- root.querySelectorAll('.baby-size-converter-unit-tab').forEach(function (b) { b.classList.remove('active'); });
226
- btn.classList.add('active');
227
- updateDisplay();
227
+ uTabs.forEach(function (b) {
228
+ b.addEventListener('click', function () {
229
+ uMode = b.dataset.unit;
230
+ switchU();
231
+ run();
228
232
  });
229
233
  });
230
234
 
231
- root.querySelectorAll('.baby-size-converter-age-tile').forEach(function (btn) {
232
- btn.addEventListener('click', function () {
233
- heightCm = Number(btn.dataset.height);
234
- weightKg = Number(btn.dataset.weight);
235
- updateDisplay();
235
+ bSteps.forEach(function (b) {
236
+ b.addEventListener('click', function () {
237
+ const i = document.getElementById(b.dataset.target);
238
+ if (!i) return;
239
+ const v = parseFloat(i.value);
240
+ const s = parseFloat(i.step || '1');
241
+ i.value = (b.dataset.action === 'inc' ? v + s : v - s).toString();
242
+ if (b.dataset.target === 'bsc-height-slider') hVal = parseFloat(i.value);
243
+ else wVal = parseFloat(i.value);
244
+ drawNums();
245
+ run();
236
246
  });
237
247
  });
238
248
 
239
- root.querySelector('#bsc-height-dec').addEventListener('click', function () {
240
- if (heightCm > 45) { heightCm -= 1; updateDisplay(); }
241
- });
242
- root.querySelector('#bsc-height-inc').addEventListener('click', function () {
243
- if (heightCm < 100) { heightCm += 1; updateDisplay(); }
244
- });
245
- heightSlider.addEventListener('input', function () {
246
- heightCm = Number(heightSlider.value);
247
- updateDisplay();
248
- });
249
-
250
- root.querySelector('#bsc-weight-dec').addEventListener('click', function () {
251
- if (weightKg > 2) { weightKg = Math.round((weightKg - 0.1) * 10) / 10; updateDisplay(); }
252
- });
253
- root.querySelector('#bsc-weight-inc').addEventListener('click', function () {
254
- if (weightKg < 20) { weightKg = Math.round((weightKg + 0.1) * 10) / 10; updateDisplay(); }
255
- });
256
- weightSlider.addEventListener('input', function () {
257
- weightKg = Number(weightSlider.value);
258
- updateDisplay();
259
- });
260
-
261
- brandSelect.addEventListener('change', function () {
262
- activeBrandId = brandSelect.value;
263
- updateDisplay();
249
+ aPick.forEach(function (b) {
250
+ b.addEventListener('click', function () {
251
+ const age = parseFloat(b.dataset.age || '6');
252
+ const reference = brands[0];
253
+ const match = reference.sizes.find(function (s) { return s.age >= age; }) || reference.sizes[reference.sizes.length - 1];
254
+ if (match) {
255
+ hVal = match.height;
256
+ wVal = match.weight;
257
+ if (hIn) hIn.value = hVal.toString();
258
+ if (wIn) wIn.value = wVal.toString();
259
+ drawNums();
260
+ run();
261
+ }
262
+ });
264
263
  });
265
264
 
266
- shareBtn.addEventListener('click', function () {
267
- const params = new URLSearchParams();
268
- params.set('h', heightCm.toString());
269
- params.set('w', weightKg.toString());
270
- params.set('b', activeBrandId);
271
- const url = window.location.pathname + '?' + params.toString();
272
- if (navigator.share) {
273
- navigator.share({ url: url });
274
- } else {
275
- navigator.clipboard.writeText(window.location.origin + url);
276
- }
277
- });
265
+ if (hIn) hIn.addEventListener('input', function () { hVal = parseFloat(hIn.value); drawNums(); run(); });
266
+ if (wIn) wIn.addEventListener('input', function () { wVal = parseFloat(wIn.value); drawNums(); run(); });
267
+ if (bSel) bSel.addEventListener('change', function () { bCid = bSel.value; run(); });
268
+
269
+ const shareBtn = document.getElementById('bsc-share-btn');
270
+ if (shareBtn) {
271
+ shareBtn.addEventListener('click', function () {
272
+ const activeBrand = brands.find(function (x) { return x.id === bCid; });
273
+ const size = findS(activeBrand, hVal, wVal);
274
+ const shareData = {
275
+ title: 'Conversor de tallas de bebé',
276
+ text: 'Mi bebé mide ' + hVal + 'cm (' + wVal + 'kg). En ' + activeBrand.name + ' usa la talla ' + size.label + '.',
277
+ url: window.location.href,
278
+ };
279
+ if (navigator.share) {
280
+ navigator.share(shareData);
281
+ } else {
282
+ navigator.clipboard.writeText(shareData.text + ' ' + shareData.url).then(function () {
283
+ shareBtn.style.color = '#10b981';
284
+ setTimeout(function () { shareBtn.style.color = ''; }, 2000);
285
+ });
286
+ }
287
+ });
288
+ }
278
289
 
279
290
  const urlParams = new URLSearchParams(window.location.search);
280
- if (urlParams.get('h')) heightCm = Number(urlParams.get('h'));
281
- if (urlParams.get('w')) weightKg = Number(urlParams.get('w'));
282
- if (urlParams.get('b')) {
283
- activeBrandId = urlParams.get('b');
284
- brandSelect.value = activeBrandId;
285
- }
291
+ if (urlParams.get('h')) { hVal = Number(urlParams.get('h')); if (hIn) hIn.value = hVal.toString(); }
292
+ if (urlParams.get('w')) { wVal = Number(urlParams.get('w')); if (wIn) wIn.value = wVal.toString(); }
293
+ if (urlParams.get('b') && bSel) { bCid = urlParams.get('b'); bSel.value = bCid; }
286
294
 
287
- updateDisplay();
295
+ switchU();
296
+ run();
288
297
  })();
289
298
  </script>