@jjlmoya/utils-health 1.36.0 → 1.37.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.
- package/package.json +1 -1
- package/src/category/index.ts +3 -1
- package/src/entries.ts +5 -1
- package/src/index.ts +2 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/correctedCalciumCalculator/bibliography.astro +14 -0
- package/src/tool/correctedCalciumCalculator/bibliography.ts +20 -0
- package/src/tool/correctedCalciumCalculator/component.astro +317 -0
- package/src/tool/correctedCalciumCalculator/corrected-calcium-calculator.css +492 -0
- package/src/tool/correctedCalciumCalculator/entry.ts +27 -0
- package/src/tool/correctedCalciumCalculator/i18n/de.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/en.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/es.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/fr.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/id.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/it.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/ja.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/ko.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/nl.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/pl.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/pt.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/ru.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/sv.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/tr.ts +292 -0
- package/src/tool/correctedCalciumCalculator/i18n/zh.ts +292 -0
- package/src/tool/correctedCalciumCalculator/index.ts +12 -0
- package/src/tool/correctedCalciumCalculator/logic.ts +55 -0
- package/src/tool/correctedCalciumCalculator/seo.astro +15 -0
- package/src/tool/correctedCalciumCalculator/ui.ts +30 -0
- package/src/tool/egfrCalculator/bibliography.astro +14 -0
- package/src/tool/egfrCalculator/bibliography.ts +20 -0
- package/src/tool/egfrCalculator/component.astro +298 -0
- package/src/tool/egfrCalculator/egfr-calculator.css +399 -0
- package/src/tool/egfrCalculator/entry.ts +27 -0
- package/src/tool/egfrCalculator/i18n/de.ts +242 -0
- package/src/tool/egfrCalculator/i18n/en.ts +242 -0
- package/src/tool/egfrCalculator/i18n/es.ts +242 -0
- package/src/tool/egfrCalculator/i18n/fr.ts +242 -0
- package/src/tool/egfrCalculator/i18n/id.ts +242 -0
- package/src/tool/egfrCalculator/i18n/it.ts +242 -0
- package/src/tool/egfrCalculator/i18n/ja.ts +242 -0
- package/src/tool/egfrCalculator/i18n/ko.ts +242 -0
- package/src/tool/egfrCalculator/i18n/nl.ts +242 -0
- package/src/tool/egfrCalculator/i18n/pl.ts +242 -0
- package/src/tool/egfrCalculator/i18n/pt.ts +242 -0
- package/src/tool/egfrCalculator/i18n/ru.ts +242 -0
- package/src/tool/egfrCalculator/i18n/sv.ts +242 -0
- package/src/tool/egfrCalculator/i18n/tr.ts +242 -0
- package/src/tool/egfrCalculator/i18n/zh.ts +242 -0
- package/src/tool/egfrCalculator/index.ts +12 -0
- package/src/tool/egfrCalculator/logic.ts +69 -0
- package/src/tool/egfrCalculator/seo.astro +15 -0
- package/src/tool/egfrCalculator/ui.ts +33 -0
- package/src/tools.ts +3 -1
package/package.json
CHANGED
package/src/category/index.ts
CHANGED
|
@@ -32,10 +32,12 @@ import { correctedReticulocyteIndex } from '../tool/correctedReticulocyteIndex/e
|
|
|
32
32
|
import { estimatedBloodVolumeCalculator } from '../tool/estimatedBloodVolumeCalculator/entry';
|
|
33
33
|
import { anionGapCalculator } from '../tool/anionGapCalculator/entry';
|
|
34
34
|
import { creatinineClearanceCockcroftGault } from '../tool/creatinineClearanceCockcroftGault/entry';
|
|
35
|
+
import { correctedCalciumCalculator } from '../tool/correctedCalciumCalculator/entry';
|
|
36
|
+
import { egfrCalculator } from '../tool/egfrCalculator/entry';
|
|
35
37
|
|
|
36
38
|
export const healthCategory: HealthCategoryEntry = {
|
|
37
39
|
icon: 'mdi:heart-pulse',
|
|
38
|
-
tools: [bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, leanBodyMassCalculator, metabolicAgeCalculator, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, meanArterialPressureCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, estimatedBloodVolumeCalculator, anionGapCalculator, creatinineClearanceCockcroftGault, targetHeightEstimator, daltonismSimulator, tinnitusReliever, parametricNoiseSynthesizer, breathingVisualizer, caffeineTracker, waterPurifier, digestionStopwatch, binauralTuner, pelliRobsonTest, peripheralVisionTrainer, epworthSleepinessScale],
|
|
40
|
+
tools: [bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, leanBodyMassCalculator, metabolicAgeCalculator, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, meanArterialPressureCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, estimatedBloodVolumeCalculator, anionGapCalculator, creatinineClearanceCockcroftGault, correctedCalciumCalculator, egfrCalculator, targetHeightEstimator, daltonismSimulator, tinnitusReliever, parametricNoiseSynthesizer, breathingVisualizer, caffeineTracker, waterPurifier, digestionStopwatch, binauralTuner, pelliRobsonTest, peripheralVisionTrainer, epworthSleepinessScale],
|
|
39
41
|
i18n: {
|
|
40
42
|
en: () => import('./i18n/en').then((m) => m.content),
|
|
41
43
|
es: () => import('./i18n/es').then((m) => m.content),
|
package/src/entries.ts
CHANGED
|
@@ -28,6 +28,8 @@ export { correctedReticulocyteIndex } from './tool/correctedReticulocyteIndex/en
|
|
|
28
28
|
export { estimatedBloodVolumeCalculator } from './tool/estimatedBloodVolumeCalculator/entry';
|
|
29
29
|
export { anionGapCalculator } from './tool/anionGapCalculator/entry';
|
|
30
30
|
export { creatinineClearanceCockcroftGault } from './tool/creatinineClearanceCockcroftGault/entry';
|
|
31
|
+
export { correctedCalciumCalculator } from './tool/correctedCalciumCalculator/entry';
|
|
32
|
+
export { egfrCalculator } from './tool/egfrCalculator/entry';
|
|
31
33
|
export { pelliRobsonTest } from './tool/pelliRobsonTest/entry';
|
|
32
34
|
export { peripheralVisionTrainer } from './tool/peripheralVisionTrainer/entry';
|
|
33
35
|
export { readingDistanceCalculator } from './tool/readingDistanceCalculator/entry';
|
|
@@ -67,6 +69,8 @@ import { correctedReticulocyteIndex } from './tool/correctedReticulocyteIndex/en
|
|
|
67
69
|
import { estimatedBloodVolumeCalculator } from './tool/estimatedBloodVolumeCalculator/entry';
|
|
68
70
|
import { anionGapCalculator } from './tool/anionGapCalculator/entry';
|
|
69
71
|
import { creatinineClearanceCockcroftGault } from './tool/creatinineClearanceCockcroftGault/entry';
|
|
72
|
+
import { correctedCalciumCalculator } from './tool/correctedCalciumCalculator/entry';
|
|
73
|
+
import { egfrCalculator } from './tool/egfrCalculator/entry';
|
|
70
74
|
import { pelliRobsonTest } from './tool/pelliRobsonTest/entry';
|
|
71
75
|
import { peripheralVisionTrainer } from './tool/peripheralVisionTrainer/entry';
|
|
72
76
|
import { readingDistanceCalculator } from './tool/readingDistanceCalculator/entry';
|
|
@@ -75,4 +79,4 @@ import { tinnitusReliever } from './tool/tinnitusReliever/entry';
|
|
|
75
79
|
import { parametricNoiseSynthesizer } from './tool/parametricNoiseSynthesizer/entry';
|
|
76
80
|
import { ubeCalculator } from './tool/ubeCalculator/entry';
|
|
77
81
|
import { waterPurifier } from './tool/waterPurifier/entry';
|
|
78
|
-
export const ALL_ENTRIES = [binauralTuner, bloodUnitConverter, bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, breathingVisualizer, caffeineTracker, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, meanArterialPressureCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, estimatedBloodVolumeCalculator, anionGapCalculator, creatinineClearanceCockcroftGault, targetHeightEstimator, daltonismSimulator, digestionStopwatch, epworthSleepinessScale, hydrationCalculator, leanBodyMassCalculator, metabolicAgeCalculator, pelliRobsonTest, peripheralVisionTrainer, readingDistanceCalculator, screenDecompressionTime, tinnitusReliever, parametricNoiseSynthesizer, ubeCalculator, waterPurifier];
|
|
82
|
+
export const ALL_ENTRIES = [binauralTuner, bloodUnitConverter, bmiCalculator, amputationAdjustedBmiCalculator, bodyAdiposityIndexCalculator, bodyFatPercentageCalculator, waistHipRatioCalculator, waistHeightRatioCalculator, breathingVisualizer, caffeineTracker, caloricDeficitCalculator, dailyProteinCalculator, macronutrientCalculator, tdeeCalculator, bunCreatinineCalculator, serumOsmolalityCalculator, meanArterialPressureCalculator, correctedSodiumHyperglycemiaCalculator, fractionalExcretionSodiumCalculator, correctedReticulocyteIndex, estimatedBloodVolumeCalculator, anionGapCalculator, creatinineClearanceCockcroftGault, correctedCalciumCalculator, egfrCalculator, targetHeightEstimator, daltonismSimulator, digestionStopwatch, epworthSleepinessScale, hydrationCalculator, leanBodyMassCalculator, metabolicAgeCalculator, pelliRobsonTest, peripheralVisionTrainer, readingDistanceCalculator, screenDecompressionTime, tinnitusReliever, parametricNoiseSynthesizer, ubeCalculator, waterPurifier];
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,8 @@ export * from './tool/correctedReticulocyteIndex';
|
|
|
36
36
|
export * from './tool/estimatedBloodVolumeCalculator';
|
|
37
37
|
export * from './tool/anionGapCalculator';
|
|
38
38
|
export * from './tool/creatinineClearanceCockcroftGault';
|
|
39
|
+
export * from './tool/correctedCalciumCalculator';
|
|
40
|
+
export * from './tool/egfrCalculator';
|
|
39
41
|
|
|
40
42
|
export { healthCategory } from './category';
|
|
41
43
|
export const healthCategorySEO = () => import('./category/seo.astro').then((m) => m.default);
|
|
@@ -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
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
7
|
+
it('should have 40 tools in ALL_TOOLS', () => {
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(40);
|
|
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 { correctedCalciumCalculator } 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 correctedCalciumCalculator.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <Bibliography links={content.bibliography} />}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: 'Payne RB, et al. (1973). Interpretation of serum calcium in patients with abnormal serum proteins. British Medical Journal.',
|
|
6
|
+
url: 'https://www.bmj.com/content/4/5893/643',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: 'StatPearls: Hypocalcemia (clinical review of calcium homeostasis).',
|
|
10
|
+
url: 'https://www.ncbi.nlm.nih.gov/books/NBK560604/',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'StatPearls: Hypercalcemia (clinical guidance on calcium abnormalities).',
|
|
14
|
+
url: 'https://www.ncbi.nlm.nih.gov/books/NBK430714/',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'Wikipedia: Calcium in biology (physiology and binding characteristics).',
|
|
18
|
+
url: 'https://en.wikipedia.org/wiki/Calcium_in_biology',
|
|
19
|
+
},
|
|
20
|
+
];
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
import './corrected-calcium-calculator.css';
|
|
3
|
+
import type { CorrectedCalciumCalculatorUI } from './ui';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: string;
|
|
7
|
+
ui?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const t = (Astro.props.ui || {}) as CorrectedCalciumCalculatorUI;
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<div class="ccc" data-ui={JSON.stringify(t)}>
|
|
14
|
+
<div class="ccc__warning" role="alert">
|
|
15
|
+
<div class="ccc__warning-mark">{t.safetyMark}</div>
|
|
16
|
+
<div class="ccc__warning-text">
|
|
17
|
+
<strong class="ccc__warning-title">{t.safetyTitle}</strong>
|
|
18
|
+
<span>{t.safetyBody}</span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="ccc__layout">
|
|
23
|
+
<section class="ccc__meter-card" aria-label={t.calciumAxisLabel}>
|
|
24
|
+
<h3 class="ccc__meter-title">{t.calciumAxisLabel}</h3>
|
|
25
|
+
|
|
26
|
+
<div class="ccc__gauge-container">
|
|
27
|
+
<div class="ccc__gauge-track">
|
|
28
|
+
<div id="ccc-segment-low" class="ccc__gauge-segment ccc__gauge-segment--low"></div>
|
|
29
|
+
<div id="ccc-segment-normal" class="ccc__gauge-segment ccc__gauge-segment--normal"></div>
|
|
30
|
+
<div id="ccc-segment-high" class="ccc__gauge-segment ccc__gauge-segment--high"></div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div id="ccc-displacement-bridge" class="ccc__displacement-bridge"></div>
|
|
34
|
+
|
|
35
|
+
<div id="ccc-ptr-measured" class="ccc__pointer ccc__pointer--measured">
|
|
36
|
+
<span class="ccc__pointer-label ccc__pointer-label--measured">{t.measuredMarker}</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div id="ccc-ptr-corrected" class="ccc__pointer ccc__pointer--corrected">
|
|
40
|
+
<span class="ccc__pointer-label ccc__pointer-label--corrected">{t.correctedMarker}</span>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="ccc__scale-labels">
|
|
45
|
+
<span id="ccc-tick-min" class="ccc__scale-tick" style="left: 0%;"></span>
|
|
46
|
+
<span id="ccc-tick-low-val" class="ccc__scale-tick"></span>
|
|
47
|
+
<span id="ccc-tick-high-val" class="ccc__scale-tick"></span>
|
|
48
|
+
<span id="ccc-tick-max" class="ccc__scale-tick" style="left: 100%;"></span>
|
|
49
|
+
</div>
|
|
50
|
+
</section>
|
|
51
|
+
|
|
52
|
+
<section class="ccc__panel">
|
|
53
|
+
<div class="ccc__fields">
|
|
54
|
+
<div class="ccc__field">
|
|
55
|
+
<label class="ccc__field-title" for="ccc-calcium">{t.calciumLabel}</label>
|
|
56
|
+
<div class="ccc__input-group">
|
|
57
|
+
<input id="ccc-calcium" class="ccc__input" type="number" min="0" step="0.1" placeholder={t.calciumPlaceholder} />
|
|
58
|
+
<div class="ccc__toggle-group" role="group">
|
|
59
|
+
<button type="button" class="ccc__toggle-btn ccc__toggle-btn--active" data-calcium-unit="mg/dL">{t.calciumMgdl}</button>
|
|
60
|
+
<button type="button" class="ccc__toggle-btn" data-calcium-unit="mmol/L">{t.calciumMmol}</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<div class="ccc__field">
|
|
66
|
+
<label class="ccc__field-title" for="ccc-albumin">{t.albuminLabel}</label>
|
|
67
|
+
<div class="ccc__input-group">
|
|
68
|
+
<input id="ccc-albumin" class="ccc__input" type="number" min="0" step="0.1" placeholder={t.albuminPlaceholder} />
|
|
69
|
+
<div class="ccc__toggle-group" role="group">
|
|
70
|
+
<button type="button" class="ccc__toggle-btn ccc__toggle-btn--active" data-albumin-unit="g/dL">{t.albuminGdl}</button>
|
|
71
|
+
<button type="button" class="ccc__toggle-btn" data-albumin-unit="g/L">{t.albuminGl}</button>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="ccc__results">
|
|
78
|
+
<div class="ccc__result-box ccc__result-box--highlight">
|
|
79
|
+
<span class="ccc__result-label">{t.correctedLabel}</span>
|
|
80
|
+
<div class="ccc__result-values">
|
|
81
|
+
<span id="ccc-val-corrected" class="ccc__result-number">{t.emptyValue}</span>
|
|
82
|
+
<span id="ccc-unit-corrected" class="ccc__result-unit"></span>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="ccc__result-box">
|
|
87
|
+
<span class="ccc__result-label">{t.measuredLabel}</span>
|
|
88
|
+
<div class="ccc__result-values">
|
|
89
|
+
<span id="ccc-val-measured" class="ccc__result-number">{t.emptyValue}</span>
|
|
90
|
+
<span id="ccc-unit-measured" class="ccc__result-unit"></span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div id="ccc-interpretation" class="ccc__interpretation" style="display: none;"></div>
|
|
96
|
+
|
|
97
|
+
<div class="ccc__formula">
|
|
98
|
+
<span>{t.formulaLabel}: </span>
|
|
99
|
+
<code id="ccc-formula-text"></code>
|
|
100
|
+
</div>
|
|
101
|
+
</section>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<script>
|
|
106
|
+
type CalciumUnit = 'mg/dL' | 'mmol/L';
|
|
107
|
+
type AlbuminUnit = 'g/dL' | 'g/L';
|
|
108
|
+
|
|
109
|
+
interface UIStrings {
|
|
110
|
+
emptyValue: string;
|
|
111
|
+
formulaConventional: string;
|
|
112
|
+
formulaSi: string;
|
|
113
|
+
interpretationLow: string;
|
|
114
|
+
interpretationNormal: string;
|
|
115
|
+
interpretationHigh: string;
|
|
116
|
+
interpretationBody: string;
|
|
117
|
+
calciumMgdl: string;
|
|
118
|
+
calciumMmol: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const root = document.querySelector('.ccc');
|
|
122
|
+
if (root) {
|
|
123
|
+
const t: UIStrings = JSON.parse(root.getAttribute('data-ui') || '{}');
|
|
124
|
+
|
|
125
|
+
const inputCalcium = root.querySelector('#ccc-calcium') as HTMLInputElement;
|
|
126
|
+
const inputAlbumin = root.querySelector('#ccc-albumin') as HTMLInputElement;
|
|
127
|
+
|
|
128
|
+
const valMeasured = root.querySelector('#ccc-val-measured') as HTMLSpanElement;
|
|
129
|
+
const valCorrected = root.querySelector('#ccc-val-corrected') as HTMLSpanElement;
|
|
130
|
+
const unitMeasured = root.querySelector('#ccc-unit-measured') as HTMLSpanElement;
|
|
131
|
+
const unitCorrected = root.querySelector('#ccc-unit-corrected') as HTMLSpanElement;
|
|
132
|
+
|
|
133
|
+
const ptrMeasured = root.querySelector('#ccc-ptr-measured') as HTMLDivElement;
|
|
134
|
+
const ptrCorrected = root.querySelector('#ccc-ptr-corrected') as HTMLDivElement;
|
|
135
|
+
const bridge = root.querySelector('#ccc-displacement-bridge') as HTMLDivElement;
|
|
136
|
+
|
|
137
|
+
const segmentLow = root.querySelector('#ccc-segment-low') as HTMLDivElement;
|
|
138
|
+
const segmentNormal = root.querySelector('#ccc-segment-normal') as HTMLDivElement;
|
|
139
|
+
const segmentHigh = root.querySelector('#ccc-segment-high') as HTMLDivElement;
|
|
140
|
+
|
|
141
|
+
const tickMin = root.querySelector('#ccc-tick-min') as HTMLSpanElement;
|
|
142
|
+
const tickLowVal = root.querySelector('#ccc-tick-low-val') as HTMLSpanElement;
|
|
143
|
+
const tickHighVal = root.querySelector('#ccc-tick-high-val') as HTMLSpanElement;
|
|
144
|
+
const tickMax = root.querySelector('#ccc-tick-max') as HTMLSpanElement;
|
|
145
|
+
|
|
146
|
+
const interpretation = root.querySelector('#ccc-interpretation') as HTMLDivElement;
|
|
147
|
+
const formulaText = root.querySelector('#ccc-formula-text') as HTMLElement;
|
|
148
|
+
|
|
149
|
+
let calciumUnit: CalciumUnit = 'mg/dL';
|
|
150
|
+
let albuminUnit: AlbuminUnit = 'g/dL';
|
|
151
|
+
|
|
152
|
+
const getLimits = (unit: CalciumUnit) => {
|
|
153
|
+
if (unit === 'mg/dL') {
|
|
154
|
+
return { min: 5.0, max: 14.0, low: 8.5, high: 10.5 };
|
|
155
|
+
}
|
|
156
|
+
return { min: 1.25, max: 3.50, low: 2.15, high: 2.55 };
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const updateScale = () => {
|
|
160
|
+
const limits = getLimits(calciumUnit);
|
|
161
|
+
const total = limits.max - limits.min;
|
|
162
|
+
const lowPct = ((limits.low - limits.min) / total) * 100;
|
|
163
|
+
const normalPct = ((limits.high - limits.low) / total) * 100;
|
|
164
|
+
const highPct = ((limits.max - limits.high) / total) * 100;
|
|
165
|
+
|
|
166
|
+
segmentLow.style.width = `${lowPct}%`;
|
|
167
|
+
segmentNormal.style.width = `${normalPct}%`;
|
|
168
|
+
segmentHigh.style.width = `${highPct}%`;
|
|
169
|
+
|
|
170
|
+
tickMin.textContent = limits.min.toFixed(unitDecimals());
|
|
171
|
+
|
|
172
|
+
tickLowVal.textContent = limits.low.toFixed(unitDecimals());
|
|
173
|
+
tickLowVal.style.left = `${lowPct}%`;
|
|
174
|
+
|
|
175
|
+
tickHighVal.textContent = limits.high.toFixed(unitDecimals());
|
|
176
|
+
tickHighVal.style.left = `${lowPct + normalPct}%`;
|
|
177
|
+
|
|
178
|
+
tickMax.textContent = limits.max.toFixed(unitDecimals());
|
|
179
|
+
|
|
180
|
+
formulaText.textContent = calciumUnit === 'mg/dL' ? t.formulaConventional : t.formulaSi;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const unitDecimals = () => {
|
|
184
|
+
return calciumUnit === 'mg/dL' ? 1 : 2;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const getPositionPct = (val: number, min: number, max: number) => {
|
|
188
|
+
const pct = ((val - min) / (max - min)) * 100;
|
|
189
|
+
return Math.max(0, Math.min(100, pct));
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const getCorrectedVal = (ca: number, alb: number) => {
|
|
193
|
+
if (calciumUnit === 'mg/dL') {
|
|
194
|
+
const albGdl = albuminUnit === 'g/L' ? alb / 10 : alb;
|
|
195
|
+
return ca + 0.8 * (4.0 - albGdl);
|
|
196
|
+
}
|
|
197
|
+
const albGl = albuminUnit === 'g/dL' ? alb * 10 : alb;
|
|
198
|
+
return ca + 0.02 * (40 - albGl);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const updatePointers = (ca: number, corrected: number) => {
|
|
202
|
+
const limits = getLimits(calciumUnit);
|
|
203
|
+
const measuredPct = getPositionPct(ca, limits.min, limits.max);
|
|
204
|
+
const correctedPct = getPositionPct(corrected, limits.min, limits.max);
|
|
205
|
+
|
|
206
|
+
ptrMeasured.style.left = `${measuredPct}%`;
|
|
207
|
+
ptrCorrected.style.left = `${correctedPct}%`;
|
|
208
|
+
|
|
209
|
+
const startPct = Math.min(measuredPct, correctedPct);
|
|
210
|
+
const widthPct = Math.abs(measuredPct - correctedPct);
|
|
211
|
+
bridge.style.left = `${startPct}%`;
|
|
212
|
+
bridge.style.width = `${widthPct}%`;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const showInterpretation = (corrected: number) => {
|
|
216
|
+
const limits = getLimits(calciumUnit);
|
|
217
|
+
interpretation.style.display = 'block';
|
|
218
|
+
interpretation.className = 'ccc__interpretation';
|
|
219
|
+
|
|
220
|
+
if (corrected < limits.low) {
|
|
221
|
+
interpretation.classList.add('ccc__interpretation--low');
|
|
222
|
+
interpretation.innerHTML = `<strong>${t.interpretationLow}</strong> — ${t.interpretationBody}`;
|
|
223
|
+
} else if (corrected > limits.high) {
|
|
224
|
+
interpretation.classList.add('ccc__interpretation--high');
|
|
225
|
+
interpretation.innerHTML = `<strong>${t.interpretationHigh}</strong> — ${t.interpretationBody}`;
|
|
226
|
+
} else {
|
|
227
|
+
interpretation.classList.add('ccc__interpretation--normal');
|
|
228
|
+
interpretation.innerHTML = `<strong>${t.interpretationNormal}</strong> — ${t.interpretationBody}`;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const calculate = () => {
|
|
233
|
+
let ca = parseFloat(inputCalcium.value);
|
|
234
|
+
let alb = parseFloat(inputAlbumin.value);
|
|
235
|
+
const isBlank = isNaN(ca) || isNaN(alb);
|
|
236
|
+
|
|
237
|
+
if (isBlank) {
|
|
238
|
+
ca = calciumUnit === 'mg/dL' ? 8.0 : 2.0;
|
|
239
|
+
alb = albuminUnit === 'g/dL' ? 3.0 : 30;
|
|
240
|
+
ptrMeasured.classList.add('ccc__pointer--preview');
|
|
241
|
+
ptrCorrected.classList.add('ccc__pointer--preview');
|
|
242
|
+
valMeasured.textContent = t.emptyValue;
|
|
243
|
+
valCorrected.textContent = t.emptyValue;
|
|
244
|
+
unitMeasured.textContent = '';
|
|
245
|
+
unitCorrected.textContent = '';
|
|
246
|
+
interpretation.style.display = 'none';
|
|
247
|
+
} else {
|
|
248
|
+
ptrMeasured.classList.remove('ccc__pointer--preview');
|
|
249
|
+
ptrCorrected.classList.remove('ccc__pointer--preview');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const corrected = getCorrectedVal(ca, alb);
|
|
253
|
+
const decimals = unitDecimals();
|
|
254
|
+
|
|
255
|
+
if (!isBlank) {
|
|
256
|
+
valMeasured.textContent = ca.toFixed(decimals);
|
|
257
|
+
valCorrected.textContent = corrected.toFixed(decimals);
|
|
258
|
+
unitMeasured.textContent = calciumUnit;
|
|
259
|
+
unitCorrected.textContent = calciumUnit;
|
|
260
|
+
showInterpretation(corrected);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
updatePointers(ca, corrected);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
root.querySelectorAll('[data-calcium-unit]').forEach((btn) => {
|
|
267
|
+
btn.addEventListener('click', (e) => {
|
|
268
|
+
const target = e.currentTarget as HTMLButtonElement;
|
|
269
|
+
const newUnit = target.getAttribute('data-calcium-unit') as CalciumUnit;
|
|
270
|
+
if (newUnit !== calciumUnit) {
|
|
271
|
+
root.querySelectorAll('[data-calcium-unit]').forEach((b) => b.classList.remove('ccc__toggle-btn--active'));
|
|
272
|
+
target.classList.add('ccc__toggle-btn--active');
|
|
273
|
+
|
|
274
|
+
const caVal = parseFloat(inputCalcium.value);
|
|
275
|
+
if (!isNaN(caVal)) {
|
|
276
|
+
if (newUnit === 'mmol/L') {
|
|
277
|
+
inputCalcium.value = (caVal / 4).toFixed(2);
|
|
278
|
+
} else {
|
|
279
|
+
inputCalcium.value = (caVal * 4).toFixed(1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
calciumUnit = newUnit;
|
|
283
|
+
updateScale();
|
|
284
|
+
calculate();
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
root.querySelectorAll('[data-albumin-unit]').forEach((btn) => {
|
|
290
|
+
btn.addEventListener('click', (e) => {
|
|
291
|
+
const target = e.currentTarget as HTMLButtonElement;
|
|
292
|
+
const newUnit = target.getAttribute('data-albumin-unit') as AlbuminUnit;
|
|
293
|
+
if (newUnit !== albuminUnit) {
|
|
294
|
+
root.querySelectorAll('[data-albumin-unit]').forEach((b) => b.classList.remove('ccc__toggle-btn--active'));
|
|
295
|
+
target.classList.add('ccc__toggle-btn--active');
|
|
296
|
+
|
|
297
|
+
const albVal = parseFloat(inputAlbumin.value);
|
|
298
|
+
if (!isNaN(albVal)) {
|
|
299
|
+
if (newUnit === 'g/L') {
|
|
300
|
+
inputAlbumin.value = (albVal * 10).toFixed(0);
|
|
301
|
+
} else {
|
|
302
|
+
inputAlbumin.value = (albVal / 10).toFixed(1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
albuminUnit = newUnit;
|
|
306
|
+
calculate();
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
inputCalcium.addEventListener('input', calculate);
|
|
312
|
+
inputAlbumin.addEventListener('input', calculate);
|
|
313
|
+
|
|
314
|
+
updateScale();
|
|
315
|
+
calculate();
|
|
316
|
+
}
|
|
317
|
+
</script>
|