@jjlmoya/utils-health 1.30.0 → 1.32.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 (56) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +2 -0
  5. package/src/tests/locale_completeness.test.ts +1 -1
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/correctedSodiumHyperglycemiaCalculator/bibliography.astro +14 -0
  8. package/src/tool/correctedSodiumHyperglycemiaCalculator/bibliography.ts +24 -0
  9. package/src/tool/correctedSodiumHyperglycemiaCalculator/component.astro +229 -0
  10. package/src/tool/correctedSodiumHyperglycemiaCalculator/corrected-sodium-hyperglycemia-calculator.css +474 -0
  11. package/src/tool/correctedSodiumHyperglycemiaCalculator/entry.ts +27 -0
  12. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/de.ts +285 -0
  13. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/en.ts +285 -0
  14. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/es.ts +285 -0
  15. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/fr.ts +285 -0
  16. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/id.ts +285 -0
  17. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/it.ts +285 -0
  18. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/ja.ts +285 -0
  19. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/ko.ts +285 -0
  20. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/nl.ts +285 -0
  21. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/pl.ts +285 -0
  22. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/pt.ts +285 -0
  23. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/ru.ts +285 -0
  24. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/sv.ts +285 -0
  25. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/tr.ts +285 -0
  26. package/src/tool/correctedSodiumHyperglycemiaCalculator/i18n/zh.ts +285 -0
  27. package/src/tool/correctedSodiumHyperglycemiaCalculator/index.ts +12 -0
  28. package/src/tool/correctedSodiumHyperglycemiaCalculator/logic.ts +51 -0
  29. package/src/tool/correctedSodiumHyperglycemiaCalculator/seo.astro +15 -0
  30. package/src/tool/correctedSodiumHyperglycemiaCalculator/ui.ts +38 -0
  31. package/src/tool/parametricNoiseSynthesizer/bibliography.astro +14 -0
  32. package/src/tool/parametricNoiseSynthesizer/bibliography.ts +24 -0
  33. package/src/tool/parametricNoiseSynthesizer/client.ts +342 -0
  34. package/src/tool/parametricNoiseSynthesizer/component.astro +77 -0
  35. package/src/tool/parametricNoiseSynthesizer/entry.ts +27 -0
  36. package/src/tool/parametricNoiseSynthesizer/i18n/de.ts +295 -0
  37. package/src/tool/parametricNoiseSynthesizer/i18n/en.ts +295 -0
  38. package/src/tool/parametricNoiseSynthesizer/i18n/es.ts +295 -0
  39. package/src/tool/parametricNoiseSynthesizer/i18n/fr.ts +295 -0
  40. package/src/tool/parametricNoiseSynthesizer/i18n/id.ts +295 -0
  41. package/src/tool/parametricNoiseSynthesizer/i18n/it.ts +295 -0
  42. package/src/tool/parametricNoiseSynthesizer/i18n/ja.ts +295 -0
  43. package/src/tool/parametricNoiseSynthesizer/i18n/ko.ts +295 -0
  44. package/src/tool/parametricNoiseSynthesizer/i18n/nl.ts +295 -0
  45. package/src/tool/parametricNoiseSynthesizer/i18n/pl.ts +295 -0
  46. package/src/tool/parametricNoiseSynthesizer/i18n/pt.ts +295 -0
  47. package/src/tool/parametricNoiseSynthesizer/i18n/ru.ts +295 -0
  48. package/src/tool/parametricNoiseSynthesizer/i18n/sv.ts +295 -0
  49. package/src/tool/parametricNoiseSynthesizer/i18n/tr.ts +295 -0
  50. package/src/tool/parametricNoiseSynthesizer/i18n/zh.ts +295 -0
  51. package/src/tool/parametricNoiseSynthesizer/index.ts +9 -0
  52. package/src/tool/parametricNoiseSynthesizer/logic.ts +12 -0
  53. package/src/tool/parametricNoiseSynthesizer/parametric-pink-brown-noise-synthesizer.css +300 -0
  54. package/src/tool/parametricNoiseSynthesizer/seo.astro +15 -0
  55. package/src/tool/parametricNoiseSynthesizer/ui.ts +21 -0
  56. package/src/tools.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-health",
3
- "version": "1.30.0",
3
+ "version": "1.32.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -7,6 +7,7 @@ import { waistHipRatioCalculator } from '../tool/waistHipRatioCalculator/entry';
7
7
  import { waistHeightRatioCalculator } from '../tool/waistHeightRatioCalculator/entry';
8
8
  import { daltonismSimulator } from '../tool/daltonismSimulator/entry';
9
9
  import { tinnitusReliever } from '../tool/tinnitusReliever/entry';
10
+ import { parametricNoiseSynthesizer } from '../tool/parametricNoiseSynthesizer/entry';
10
11
  import { breathingVisualizer } from '../tool/breathingVisualizer/entry';
11
12
  import { caffeineTracker } from '../tool/caffeineTracker/entry';
12
13
  import { caloricDeficitCalculator } from '../tool/caloricDeficitCalculator/entry';
@@ -23,13 +24,14 @@ import { leanBodyMassCalculator } from '../tool/leanBodyMassCalculator/entry';
23
24
  import { metabolicAgeCalculator } from '../tool/metabolicAgeCalculator/entry';
24
25
  import { bunCreatinineCalculator } from '../tool/bunCreatinineCalculator/entry';
25
26
  import { serumOsmolalityCalculator } from '../tool/serumOsmolalityCalculator/entry';
27
+ import { correctedSodiumHyperglycemiaCalculator } from '../tool/correctedSodiumHyperglycemiaCalculator/entry';
26
28
  import { fractionalExcretionSodiumCalculator } from '../tool/fractionalExcretionSodiumCalculator/entry';
27
29
  import { targetHeightEstimator } from '../tool/targetHeightEstimator/entry';
28
30
  import { correctedReticulocyteIndex } from '../tool/correctedReticulocyteIndex/entry';
29
31
 
30
32
  export const healthCategory: HealthCategoryEntry = {
31
33
  icon: 'mdi:heart-pulse',
32
- tools: [bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, leanBodyMassCalculator, metabolicAgeCalculator, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, targetHeightEstimator, daltonismSimulator, tinnitusReliever, breathingVisualizer, caffeineTracker, waterPurifier, digestionStopwatch, binauralTuner, pelliRobsonTest, peripheralVisionTrainer, epworthSleepinessScale],
34
+ tools: [bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, leanBodyMassCalculator, metabolicAgeCalculator, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, targetHeightEstimator, daltonismSimulator, tinnitusReliever, parametricNoiseSynthesizer, breathingVisualizer, caffeineTracker, waterPurifier, digestionStopwatch, binauralTuner, pelliRobsonTest, peripheralVisionTrainer, epworthSleepinessScale],
33
35
  i18n: {
34
36
  en: () => import('./i18n/en').then((m) => m.content),
35
37
  es: () => import('./i18n/es').then((m) => m.content),
package/src/entries.ts CHANGED
@@ -20,6 +20,7 @@ export { macronutrientCalculator } from './tool/macronutrientCalculator/entry';
20
20
  export { tdeeCalculator } from './tool/tdeeCalculator/entry';
21
21
  export { bunCreatinineCalculator } from './tool/bunCreatinineCalculator/entry';
22
22
  export { serumOsmolalityCalculator } from './tool/serumOsmolalityCalculator/entry';
23
+ export { correctedSodiumHyperglycemiaCalculator } from './tool/correctedSodiumHyperglycemiaCalculator/entry';
23
24
  export { fractionalExcretionSodiumCalculator } from './tool/fractionalExcretionSodiumCalculator/entry';
24
25
  export { targetHeightEstimator } from './tool/targetHeightEstimator/entry';
25
26
  export { correctedReticulocyteIndex } from './tool/correctedReticulocyteIndex/entry';
@@ -28,6 +29,7 @@ export { peripheralVisionTrainer } from './tool/peripheralVisionTrainer/entry';
28
29
  export { readingDistanceCalculator } from './tool/readingDistanceCalculator/entry';
29
30
  export { screenDecompressionTime } from './tool/screenDecompressionTime/entry';
30
31
  export { tinnitusReliever } from './tool/tinnitusReliever/entry';
32
+ export { parametricNoiseSynthesizer } from './tool/parametricNoiseSynthesizer/entry';
31
33
  export { ubeCalculator } from './tool/ubeCalculator/entry';
32
34
  export { waterPurifier } from './tool/waterPurifier/entry';
33
35
  export { healthCategory } from './category';
@@ -53,6 +55,7 @@ import { macronutrientCalculator } from './tool/macronutrientCalculator/entry';
53
55
  import { tdeeCalculator } from './tool/tdeeCalculator/entry';
54
56
  import { bunCreatinineCalculator } from './tool/bunCreatinineCalculator/entry';
55
57
  import { serumOsmolalityCalculator } from './tool/serumOsmolalityCalculator/entry';
58
+ import { correctedSodiumHyperglycemiaCalculator } from './tool/correctedSodiumHyperglycemiaCalculator/entry';
56
59
  import { fractionalExcretionSodiumCalculator } from './tool/fractionalExcretionSodiumCalculator/entry';
57
60
  import { targetHeightEstimator } from './tool/targetHeightEstimator/entry';
58
61
  import { correctedReticulocyteIndex } from './tool/correctedReticulocyteIndex/entry';
@@ -61,6 +64,7 @@ import { peripheralVisionTrainer } from './tool/peripheralVisionTrainer/entry';
61
64
  import { readingDistanceCalculator } from './tool/readingDistanceCalculator/entry';
62
65
  import { screenDecompressionTime } from './tool/screenDecompressionTime/entry';
63
66
  import { tinnitusReliever } from './tool/tinnitusReliever/entry';
67
+ import { parametricNoiseSynthesizer } from './tool/parametricNoiseSynthesizer/entry';
64
68
  import { ubeCalculator } from './tool/ubeCalculator/entry';
65
69
  import { waterPurifier } from './tool/waterPurifier/entry';
66
- export const ALL_ENTRIES = [binauralTuner, bloodUnitConverter, bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, breathingVisualizer, caffeineTracker, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, targetHeightEstimator, daltonismSimulator, digestionStopwatch, epworthSleepinessScale, hydrationCalculator, leanBodyMassCalculator, metabolicAgeCalculator, pelliRobsonTest, peripheralVisionTrainer, readingDistanceCalculator, screenDecompressionTime, tinnitusReliever, ubeCalculator, waterPurifier];
70
+ export const ALL_ENTRIES = [binauralTuner, bloodUnitConverter, bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, breathingVisualizer, caffeineTracker, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, targetHeightEstimator, daltonismSimulator, digestionStopwatch, epworthSleepinessScale, hydrationCalculator, leanBodyMassCalculator, metabolicAgeCalculator, pelliRobsonTest, peripheralVisionTrainer, readingDistanceCalculator, screenDecompressionTime, tinnitusReliever, parametricNoiseSynthesizer, ubeCalculator, waterPurifier];
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export * from './tool/bmiCalculator';
2
2
  export * from './tool/amputationAdjustedBmiCalculator';
3
3
  export * from './tool/daltonismSimulator';
4
4
  export * from './tool/tinnitusReliever';
5
+ export * from './tool/parametricNoiseSynthesizer';
5
6
  export * from './tool/breathingVisualizer';
6
7
  export * from './tool/caffeineTracker';
7
8
  export * from './tool/caloricDeficitCalculator';
@@ -27,6 +28,7 @@ export * from './tool/macronutrientCalculator';
27
28
  export * from './tool/tdeeCalculator';
28
29
  export * from './tool/bunCreatinineCalculator';
29
30
  export * from './tool/serumOsmolalityCalculator';
31
+ export * from './tool/correctedSodiumHyperglycemiaCalculator';
30
32
  export * from './tool/fractionalExcretionSodiumCalculator';
31
33
  export * from './tool/targetHeightEstimator';
32
34
  export * from './tool/correctedReticulocyteIndex';
@@ -36,6 +36,6 @@ describe('Locale Completeness Validation', () => {
36
36
  });
37
37
 
38
38
  it('all tools registered', () => {
39
- expect(ALL_TOOLS.length).toBe(32);
39
+ expect(ALL_TOOLS.length).toBe(34);
40
40
  });
41
41
  });
@@ -4,8 +4,8 @@ import { healthCategory } from '../data';
4
4
 
5
5
  describe('Tool Validation Suite', () => {
6
6
  describe('Library Registration', () => {
7
- it('should have 30 tools in ALL_TOOLS', () => {
8
- expect(ALL_TOOLS.length).toBe(32);
7
+ it('should have 33 tools in ALL_TOOLS', () => {
8
+ expect(ALL_TOOLS.length).toBe(34);
9
9
  });
10
10
 
11
11
  it('healthCategory should be defined', () => {
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography } from '@jjlmoya/utils-shared';
3
+ import { correctedSodiumHyperglycemiaCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await correctedSodiumHyperglycemiaCalculator.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <Bibliography links={content.bibliography} />}
@@ -0,0 +1,24 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'Katz MA. Hyperglycemia-induced hyponatremia: calculation of expected serum sodium depression. New England Journal of Medicine. 1973.',
6
+ url: 'https://pubmed.ncbi.nlm.nih.gov/4700474/',
7
+ },
8
+ {
9
+ name: 'Hillier TA, Abbott RD, Barrett EJ. Hyponatremia: evaluating the correction factor for hyperglycemia. American Journal of Medicine. 1999.',
10
+ url: 'https://pubmed.ncbi.nlm.nih.gov/10225241/',
11
+ },
12
+ {
13
+ name: 'Joint British Diabetes Societies guideline: management of diabetic ketoacidosis in adults',
14
+ url: 'https://abcd.care/resource/jbds-02-management-diabetic-ketoacidosis-adults',
15
+ },
16
+ {
17
+ name: 'American Diabetes Association. Hyperglycemic crises in adult patients with diabetes.',
18
+ url: 'https://pubmed.ncbi.nlm.nih.gov/19564476/',
19
+ },
20
+ {
21
+ name: 'Merck Manual Professional: Hyponatremia',
22
+ url: 'https://www.merckmanuals.com/professional/endocrine-and-metabolic-disorders/electrolyte-disorders/hyponatremia',
23
+ },
24
+ ];
@@ -0,0 +1,229 @@
1
+ ---
2
+ import './corrected-sodium-hyperglycemia-calculator.css';
3
+ import type { CorrectedSodiumHyperglycemiaCalculatorUI } from './ui';
4
+
5
+ interface Props {
6
+ locale?: string;
7
+ ui?: Record<string, unknown>;
8
+ }
9
+
10
+ const t = (Astro.props.ui || {}) as CorrectedSodiumHyperglycemiaCalculatorUI;
11
+ ---
12
+
13
+ <div class="csh" data-ui={JSON.stringify(t)}>
14
+ <div class="csh__warning" role="alert" aria-label={t.safetyTitle}>
15
+ <div class="csh__warning-mark">{t.safetyMark}</div>
16
+ <div>
17
+ <strong>{t.safetyTitle}</strong>
18
+ <span>{t.safetyBody}</span>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="csh__layout">
23
+ <section class="csh__rule" aria-label={t.glucoseAxisLabel}>
24
+ <div class="csh__glucose-rail">
25
+ <span>{t.glucoseTickLow}</span>
26
+ <span>{t.glucoseTickMid}</span>
27
+ <span>{t.glucoseTickHigh}</span>
28
+ <div id="csh-glucose-fill" class="csh__glucose-fill"></div>
29
+ <div id="csh-glucose-dot" class="csh__glucose-dot"></div>
30
+ </div>
31
+ <div class="csh__sodium-stage" aria-label={t.sodiumAxisLabel}>
32
+ <div class="csh__sodium-band csh__sodium-band--low"></div>
33
+ <div class="csh__sodium-band csh__sodium-band--normal"></div>
34
+ <div class="csh__sodium-band csh__sodium-band--high"></div>
35
+ <div id="csh-measured-pin" class="csh__pin csh__pin--measured">
36
+ <b>{t.measuredMarker}</b>
37
+ <span id="csh-measured-pin-value">{t.emptyValue}</span>
38
+ </div>
39
+ <div id="csh-corrected-pin" class="csh__pin csh__pin--corrected">
40
+ <b>{t.correctedMarker}</b>
41
+ <span id="csh-corrected-pin-value">{t.emptyValue}</span>
42
+ </div>
43
+ </div>
44
+ </section>
45
+
46
+ <section class="csh__panel">
47
+ <div class="csh__inputs">
48
+ <label class="csh__field">
49
+ <span>{t.sodiumLabel}</span>
50
+ <div class="csh__input-shell">
51
+ <input id="csh-sodium" type="number" min="80" max="190" step="0.1" placeholder={t.sodiumPlaceholder} />
52
+ <small>{t.sodiumUnit}</small>
53
+ </div>
54
+ </label>
55
+
56
+ <label class="csh__field">
57
+ <span>{t.glucoseLabel}</span>
58
+ <div class="csh__input-shell csh__input-shell--toggle">
59
+ <input id="csh-glucose" type="number" min="0" step="0.1" placeholder={t.glucosePlaceholder} />
60
+ <div class="csh__toggle" role="group" aria-label={t.glucoseLabel}>
61
+ <button type="button" class="csh__toggle-btn csh__toggle-btn--active" data-glucose-unit="mg/dL">{t.glucoseMgdl}</button>
62
+ <button type="button" class="csh__toggle-btn" data-glucose-unit="mmol/L">{t.glucoseMmol}</button>
63
+ </div>
64
+ </div>
65
+ <small id="csh-converted" class="csh__converted"></small>
66
+ </label>
67
+ </div>
68
+
69
+ <div class="csh__factor" role="group" aria-label={t.factorLabel}>
70
+ <button type="button" class="csh__factor-btn csh__factor-btn--active" data-mode="katz">
71
+ <strong>{t.katzLabel}</strong>
72
+ <span>{t.katzHint}</span>
73
+ </button>
74
+ <button type="button" class="csh__factor-btn" data-mode="hillier">
75
+ <strong>{t.hillierLabel}</strong>
76
+ <span>{t.hillierHint}</span>
77
+ </button>
78
+ </div>
79
+
80
+ <div class="csh__results">
81
+ <div class="csh__result csh__result--main">
82
+ <span>{t.correctedLabel}</span>
83
+ <strong id="csh-corrected">{t.emptyValue}</strong>
84
+ <small>{t.sodiumUnit}</small>
85
+ </div>
86
+ <div class="csh__result">
87
+ <span>{t.correctionLabel}</span>
88
+ <strong id="csh-correction">{t.emptyValue}</strong>
89
+ <small>{t.sodiumUnit}</small>
90
+ </div>
91
+ <div class="csh__result">
92
+ <span>{t.factorUsedLabel}</span>
93
+ <strong id="csh-factor">{t.emptyValue}</strong>
94
+ <small>{t.factorUnitLabel}</small>
95
+ </div>
96
+ </div>
97
+
98
+ <div class="csh__formula">
99
+ <span>{t.formulaLabel}</span>
100
+ <code id="csh-formula">{t.formulaKatz}</code>
101
+ </div>
102
+
103
+ <div id="csh-interpretation" class="csh__interpretation">
104
+ <strong>{t.interpretationNormal}</strong>
105
+ <span>{t.interpretationBody}</span>
106
+ </div>
107
+ </section>
108
+ </div>
109
+ </div>
110
+
111
+ <script>
112
+ type GlucoseUnit = 'mg/dL' | 'mmol/L';
113
+ type Mode = 'katz' | 'hillier';
114
+
115
+ interface Ctx {
116
+ root: Element;
117
+ ui: Record<string, string>;
118
+ sodium: HTMLInputElement;
119
+ glucose: HTMLInputElement;
120
+ glucoseUnit: GlucoseUnit;
121
+ mode: Mode;
122
+ }
123
+
124
+ function qs<T extends HTMLElement>(root: Element, selector: string): T {
125
+ const el = root.querySelector<T>(selector);
126
+ if (!el) throw new Error(`Missing ${selector}`);
127
+ return el;
128
+ }
129
+
130
+ function fmt(value: number): string {
131
+ return String(Math.round(value * 10) / 10);
132
+ }
133
+
134
+ function glucoseToMgdl(value: number, unit: GlucoseUnit): number {
135
+ return unit === 'mmol/L' ? value * 18.0182 : value;
136
+ }
137
+
138
+ function factorFor(glucoseMgdl: number, mode: Mode): 1.6 | 2.4 {
139
+ return mode === 'hillier' && glucoseMgdl > 400 ? 2.4 : 1.6;
140
+ }
141
+
142
+ function sodiumPosition(value: number): number {
143
+ return Math.max(12, Math.min(88, ((value - 115) / 45) * 100));
144
+ }
145
+
146
+ function glucosePosition(value: number): number {
147
+ return Math.max(0, Math.min(100, ((value - 60) / 740) * 100));
148
+ }
149
+
150
+ function getCorrectedLabel(ctx: Ctx, corrected: number): string {
151
+ if (corrected < 135) return ctx.ui.interpretationLow;
152
+ if (corrected > 145) return ctx.ui.interpretationHigh;
153
+ return ctx.ui.interpretationNormal;
154
+ }
155
+
156
+ function reset(ctx: Ctx) {
157
+ ['#csh-corrected', '#csh-correction', '#csh-factor', '#csh-measured-pin-value', '#csh-corrected-pin-value'].forEach((selector) => {
158
+ qs<HTMLElement>(ctx.root, selector).textContent = ctx.ui.emptyValue;
159
+ });
160
+ qs<HTMLElement>(ctx.root, '#csh-glucose-fill').style.height = '0%';
161
+ qs<HTMLElement>(ctx.root, '#csh-glucose-dot').style.bottom = '0%';
162
+ qs<HTMLElement>(ctx.root, '#csh-measured-pin').style.left = '12%';
163
+ qs<HTMLElement>(ctx.root, '#csh-corrected-pin').style.left = '12%';
164
+ }
165
+
166
+ function render(ctx: Ctx) {
167
+ const sodium = parseFloat(ctx.sodium.value);
168
+ const glucoseRaw = parseFloat(ctx.glucose.value);
169
+ if (!(sodium > 0) || !(glucoseRaw >= 0)) {
170
+ reset(ctx);
171
+ return;
172
+ }
173
+
174
+ const glucoseMgdl = glucoseToMgdl(glucoseRaw, ctx.glucoseUnit);
175
+ const factor = factorFor(glucoseMgdl, ctx.mode);
176
+ const correction = (factor / 100) * (glucoseMgdl - 100);
177
+ const corrected = sodium + correction;
178
+ const correctedLabel = getCorrectedLabel(ctx, corrected);
179
+
180
+ qs<HTMLElement>(ctx.root, '#csh-corrected').textContent = fmt(corrected);
181
+ qs<HTMLElement>(ctx.root, '#csh-correction').textContent = fmt(correction);
182
+ qs<HTMLElement>(ctx.root, '#csh-factor').textContent = fmt(factor);
183
+ qs<HTMLElement>(ctx.root, '#csh-formula').textContent = factor === 2.4 ? ctx.ui.formulaHillier : ctx.ui.formulaKatz;
184
+ qs<HTMLElement>(ctx.root, '#csh-measured-pin-value').textContent = fmt(sodium);
185
+ qs<HTMLElement>(ctx.root, '#csh-corrected-pin-value').textContent = fmt(corrected);
186
+ qs<HTMLElement>(ctx.root, '#csh-interpretation').querySelector('strong')!.textContent = correctedLabel;
187
+
188
+ qs<HTMLElement>(ctx.root, '#csh-glucose-fill').style.height = `${glucosePosition(glucoseMgdl)}%`;
189
+ qs<HTMLElement>(ctx.root, '#csh-glucose-dot').style.bottom = `${glucosePosition(glucoseMgdl)}%`;
190
+ qs<HTMLElement>(ctx.root, '#csh-measured-pin').style.left = `${sodiumPosition(sodium)}%`;
191
+ qs<HTMLElement>(ctx.root, '#csh-corrected-pin').style.left = `${sodiumPosition(corrected)}%`;
192
+ qs<HTMLElement>(ctx.root, '#csh-converted').textContent =
193
+ ctx.glucoseUnit === 'mg/dL'
194
+ ? `${ctx.ui.convertedGlucose}: ${fmt(glucoseMgdl / 18.0182)} ${ctx.ui.glucoseMmol}`
195
+ : `${ctx.ui.convertedGlucose}: ${fmt(glucoseMgdl)} ${ctx.ui.glucoseMgdl}`;
196
+ }
197
+
198
+ function init(root: Element) {
199
+ const ctx: Ctx = {
200
+ root,
201
+ ui: JSON.parse((root as HTMLElement).dataset.ui || '{}') as Record<string, string>,
202
+ sodium: qs<HTMLInputElement>(root, '#csh-sodium'),
203
+ glucose: qs<HTMLInputElement>(root, '#csh-glucose'),
204
+ glucoseUnit: 'mg/dL',
205
+ mode: 'katz',
206
+ };
207
+
208
+ root.querySelectorAll<HTMLButtonElement>('[data-glucose-unit]').forEach((btn) => {
209
+ btn.addEventListener('click', () => {
210
+ ctx.glucoseUnit = btn.dataset.glucoseUnit as GlucoseUnit;
211
+ root.querySelectorAll<HTMLButtonElement>('[data-glucose-unit]').forEach((item) => item.classList.toggle('csh__toggle-btn--active', item === btn));
212
+ render(ctx);
213
+ });
214
+ });
215
+
216
+ root.querySelectorAll<HTMLButtonElement>('[data-mode]').forEach((btn) => {
217
+ btn.addEventListener('click', () => {
218
+ ctx.mode = btn.dataset.mode as Mode;
219
+ root.querySelectorAll<HTMLButtonElement>('[data-mode]').forEach((item) => item.classList.toggle('csh__factor-btn--active', item === btn));
220
+ render(ctx);
221
+ });
222
+ });
223
+
224
+ [ctx.sodium, ctx.glucose].forEach((input) => input.addEventListener('input', () => render(ctx)));
225
+ render(ctx);
226
+ }
227
+
228
+ document.querySelectorAll('.csh').forEach(init);
229
+ </script>