updatge
This commit is contained in:
@@ -1,194 +1,7 @@
|
||||
import { defineComponent, computed, ref } from 'vue';
|
||||
|
||||
/*
|
||||
class GrainInput {
|
||||
static inputs = [];
|
||||
name;
|
||||
unit;
|
||||
mpg;
|
||||
inputEl;
|
||||
reference;
|
||||
|
||||
* @param {{
|
||||
* name: string,
|
||||
* mpg: number,
|
||||
* reference: GrainInput | 'self',
|
||||
* unit: string,
|
||||
* step?: number,
|
||||
* }} opts
|
||||
constructor(opts) {
|
||||
this.name = opts.name;
|
||||
this.mpg = opts.mpg;
|
||||
this.unit = opts.unit;
|
||||
this.reference = opts.reference === "self" ? this : opts.reference;
|
||||
this.inputEl = h("input", { type: "number", min: 0, step: opts.step ?? 1 });
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
attachInput(insertionPoint) {
|
||||
insertionPoint.appendChild(this.inputEl);
|
||||
this.inputEl.valueAsNumber = this.mpg;
|
||||
this.inputEl.addEventListener("input", (e) => {
|
||||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
|
||||
if (this !== this.reference) {
|
||||
this.reference.inputEl.valueAsNumber = newVal / this.mpg;
|
||||
}
|
||||
eventBus.sendChange(this);
|
||||
});
|
||||
}
|
||||
|
||||
onChange() {
|
||||
if (!this.reference || this === this.reference) return;
|
||||
this.inputEl.valueAsNumber = this.mpg * this.reference.inputEl.valueAsNumber;
|
||||
}
|
||||
|
||||
getCurrentValue() {
|
||||
return this.inputEl.valueAsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
class RatiosController {
|
||||
t3Ratio = h("input", { type: "number", min: 0, step: 1 });
|
||||
t4Ratio = h("input", { type: "number", min: 0, step: 1 });
|
||||
t3Syn = h("input", { type: "number", min: 0 });
|
||||
t4Syn = h("input", { type: "number", min: 0 });
|
||||
t3 = 1;
|
||||
t4 = 4;
|
||||
reference;
|
||||
|
||||
constructor(referenceInput) {
|
||||
this.reference = referenceInput;
|
||||
}
|
||||
|
||||
attachInputs(inputs) {
|
||||
inputs.t3Ratio.replaceWith(this.t3Ratio);
|
||||
inputs.t4Ratio.replaceWith(this.t4Ratio);
|
||||
inputs.t3Syn.replaceWith(this.t3Syn);
|
||||
inputs.t4Syn.replaceWith(this.t4Syn);
|
||||
|
||||
this.t3Ratio.addEventListener("input", (e) => {
|
||||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
|
||||
this.t3 = newVal;
|
||||
this.onChange();
|
||||
});
|
||||
|
||||
this.t4Ratio.addEventListener("input", (e) => {
|
||||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
|
||||
this.t4 = newVal;
|
||||
this.onChange();
|
||||
});
|
||||
|
||||
this.t3Syn.addEventListener("input", (e) => {
|
||||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
|
||||
this.reference.inputEl.valueAsNumber = newVal / MPG_T3_SYN + this.t4Syn.valueAsNumber / MPG_T4_SYN;
|
||||
this.t3 = newVal;
|
||||
this.t4 = this.t4Syn.valueAsNumber;
|
||||
this.updateRatio();
|
||||
this.updateSyn();
|
||||
eventBus.sendChange(this);
|
||||
});
|
||||
|
||||
this.t4Syn.addEventListener("input", (e) => {
|
||||
const newVal = (e.currentTarget)?.valueAsNumber ?? 0;
|
||||
this.reference.inputEl.valueAsNumber = newVal / MPG_T4_SYN + this.t3Syn.valueAsNumber / MPG_T3_SYN;
|
||||
this.t3 = this.t3Syn.valueAsNumber;
|
||||
this.t4 = newVal;
|
||||
this.updateRatio();
|
||||
this.updateSyn();
|
||||
eventBus.sendChange(this);
|
||||
});
|
||||
|
||||
eventBus.addListener(this);
|
||||
this.onChange();
|
||||
}
|
||||
|
||||
onChange() {
|
||||
this.updateRatio();
|
||||
this.updateSyn();
|
||||
}
|
||||
|
||||
updateRatio() {
|
||||
this.t3Ratio.valueAsNumber = this.t3;
|
||||
this.t4Ratio.valueAsNumber = this.t4;
|
||||
}
|
||||
|
||||
updateSyn() {
|
||||
const total = this.t3 + this.t4;
|
||||
const t3Proportion = this.t3 / total;
|
||||
const t4Proportion = this.t4 / total;
|
||||
const grainsSyn = t3Proportion / MPG_T3_SYN + t4Proportion / MPG_T4_SYN;
|
||||
const multiplierSyn = this.reference.getCurrentValue() / grainsSyn;
|
||||
this.t3Syn.valueAsNumber = t3Proportion * multiplierSyn;
|
||||
this.t4Syn.valueAsNumber = t4Proportion * multiplierSyn;
|
||||
}
|
||||
}
|
||||
|
||||
class ThyroidConverter extends DJElement {
|
||||
static styles = css``;
|
||||
|
||||
static template = html;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const mainGrainInput = new GrainInput({
|
||||
name: "Grains",
|
||||
mpg: 1,
|
||||
reference: "self",
|
||||
unit: "",
|
||||
step: 0.1,
|
||||
});
|
||||
|
||||
const ratiosController = new RatiosController(mainGrainInput);
|
||||
|
||||
const inputs = [
|
||||
mainGrainInput,
|
||||
new GrainInput({
|
||||
name: "Armour, Natural Dessicated Thyroid",
|
||||
mpg: 60,
|
||||
unit: "mg",
|
||||
reference: mainGrainInput,
|
||||
}),
|
||||
new GrainInput({
|
||||
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)',
|
||||
mpg: MPG_T3_SYN,
|
||||
unit: "mcg",
|
||||
reference: mainGrainInput,
|
||||
}),
|
||||
new GrainInput({
|
||||
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)',
|
||||
mpg: MPG_T4_SYN,
|
||||
unit: "mcg",
|
||||
reference: mainGrainInput,
|
||||
}),
|
||||
];
|
||||
|
||||
const tableBody = qs("#table-body", this.root);
|
||||
const compoundedStart = qs("#compounded-start", this.root);
|
||||
|
||||
for (const field of inputs) {
|
||||
const newRow = h("tr", {}, [
|
||||
h("td", { textContent: field.name }),
|
||||
h("td", {}, [
|
||||
h("div", { className: "input", style: "display: inline-block;" }),
|
||||
h("span", { className: "breathe", textContent: field.unit }),
|
||||
]),
|
||||
]);
|
||||
field.attachInput(qs("div.input", newRow));
|
||||
tableBody.insertBefore(newRow, compoundedStart);
|
||||
}
|
||||
|
||||
ratiosController.attachInputs({
|
||||
t3Ratio: qs(".ratios .t3", tableBody),
|
||||
t4Ratio: qs(".ratios .t4", tableBody),
|
||||
t3Syn: qs(".synthetic .t3", tableBody),
|
||||
t4Syn: qs(".synthetic .t4", tableBody),
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
import { computed, defineComponent, ref } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ge-calculator',
|
||||
name: "ge-calculator",
|
||||
setup() {
|
||||
const t3Ratio = ref(1);
|
||||
const t4Ratio = ref(4);
|
||||
@@ -209,12 +22,12 @@ export default defineComponent({
|
||||
unit: "mg",
|
||||
},
|
||||
{
|
||||
name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)',
|
||||
name: 'Liothyronine (Triiodothyronine, "Cytomel/Cynomel", T3)',
|
||||
mpg: MPG_T3_SYN,
|
||||
unit: "mcg",
|
||||
},
|
||||
{
|
||||
name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)',
|
||||
name: "Levothyroxine (Thyroxine, T4)",
|
||||
mpg: MPG_T4_SYN,
|
||||
unit: "mcg",
|
||||
},
|
||||
@@ -222,7 +35,6 @@ export default defineComponent({
|
||||
|
||||
const numGrains = ref(2);
|
||||
|
||||
const ratio = computed(() => t3Ratio.value + t4Ratio.value);
|
||||
const compounded = computed(() => {
|
||||
const total = t3Ratio.value + t4Ratio.value;
|
||||
const t3Proportion = t3Ratio.value / total;
|
||||
@@ -235,121 +47,138 @@ export default defineComponent({
|
||||
};
|
||||
});
|
||||
|
||||
return () => <>
|
||||
<header>
|
||||
<h1>Thyroid Calculator</h1>
|
||||
</header>
|
||||
<div class="text-slab">
|
||||
<p>
|
||||
Use this calculator to convert between different forms and units of thyroid hormone. Common
|
||||
synthetic, pure and natural dessicated forms are listed. The last section of the table provides
|
||||
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the
|
||||
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of the
|
||||
table, but editing any field will automatically update the rest to keep them in sync.
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-slab ge-calculator">
|
||||
<table>
|
||||
<tbody>
|
||||
{ inputDefs.map((_, i) => (
|
||||
<tr key={_.name}>
|
||||
<td>
|
||||
{ _.name }
|
||||
</td>
|
||||
<td class="right">
|
||||
<div style="display: inline-block;">
|
||||
<input
|
||||
value={_.name === 'Grains' ? numGrains.value : _.mpg * numGrains.value}
|
||||
onInput={(e) => {
|
||||
const val = (e.target as HTMLInputElement).valueAsNumber;
|
||||
if (_.name === 'Grains') {
|
||||
numGrains.value = val;
|
||||
} else {
|
||||
numGrains.value = _.mpg / val;
|
||||
}
|
||||
}}
|
||||
min="0"
|
||||
step={ _.step ?? 1 }
|
||||
type="number" />
|
||||
</div>
|
||||
<span class="breathe">{ _.unit }</span>
|
||||
return () => (
|
||||
<>
|
||||
<header>
|
||||
<h1>Thyroid Calculator</h1>
|
||||
</header>
|
||||
<div class="text-slab">
|
||||
<p>
|
||||
Use this calculator to convert between different forms and units of thyroid hormone. Common
|
||||
synthetic, pure and natural dessicated forms are listed. The last section of the table provides
|
||||
input fields for a specified ratio of T3 to T4, and will give the dosages required for both the
|
||||
synthetic and pure forms. All dosages given are based on the "Grains" field at the beginning of
|
||||
the table, but editing any field will automatically update the rest to keep them in sync.
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-slab ge-calculator">
|
||||
<table>
|
||||
<tbody>
|
||||
{inputDefs.map((_) => (
|
||||
<tr key={_.name}>
|
||||
<td>
|
||||
{_.name}
|
||||
</td>
|
||||
<td class="right">
|
||||
<div style="display: inline-block;">
|
||||
<input
|
||||
value={_.name === "Grains" ? numGrains.value : _.mpg * numGrains.value}
|
||||
onInput={(e) => {
|
||||
const val = (e.target as HTMLInputElement).valueAsNumber;
|
||||
if (_.name === "Grains") {
|
||||
numGrains.value = val;
|
||||
} else {
|
||||
numGrains.value = _.mpg / val;
|
||||
}
|
||||
}}
|
||||
min="0"
|
||||
step={_.step ?? 1}
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
<span class="breathe">{_.unit}</span>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<strong>Compounded (T3 and T4, "Cynoplus")</strong>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr><td colspan="2"><strong>Compounded (T3 and T4, "Cynpolus")</strong></td></tr>
|
||||
<tr class="ratios">
|
||||
<td>
|
||||
Desired Ratio (T3:T4)
|
||||
</td>
|
||||
<td class="right">
|
||||
<div>
|
||||
<input
|
||||
value={t3Ratio.value}
|
||||
onInput={(e) => {
|
||||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number" />
|
||||
</div> : <div>
|
||||
<input
|
||||
value={t4Ratio.value}
|
||||
onInput={(e) => {
|
||||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="synthetic">
|
||||
<td>
|
||||
Synthetic T3/T4 Combo
|
||||
</td>
|
||||
<td class="right">
|
||||
<div>
|
||||
<input
|
||||
value={compounded.value.t3Syn}
|
||||
onInput={(e) => {
|
||||
t4Ratio.value = compounded.value.t4Syn;
|
||||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number" />
|
||||
</div> : <div>
|
||||
<input
|
||||
value={compounded.value.t4Syn}
|
||||
onInput={(e) => {
|
||||
t3Ratio.value = compounded.value.t3Syn;
|
||||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
numGrains.value = t3Ratio.value / MPG_T3_SYN + t4Ratio.value / MPG_T4_SYN;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="text-slab">
|
||||
<strong>Changelog:</strong>
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
November 2024: Migrated to new web framework and fixed some buggy input.
|
||||
</li>
|
||||
<li>
|
||||
13th March 2024: Removed the synthetic/pure distinction as it was confusing and unnecessary
|
||||
bloat.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
<tr class="ratios">
|
||||
<td>
|
||||
Desired Ratio (T3:T4)
|
||||
</td>
|
||||
<td class="right">
|
||||
<div>
|
||||
<input
|
||||
value={t3Ratio.value}
|
||||
onInput={(e) => {
|
||||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number"
|
||||
/>
|
||||
</div>{" "}
|
||||
:{" "}
|
||||
<div>
|
||||
<input
|
||||
value={t4Ratio.value}
|
||||
onInput={(e) => {
|
||||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="synthetic">
|
||||
<td>
|
||||
Synthetic T3/T4 Combo
|
||||
</td>
|
||||
<td class="right">
|
||||
<div>
|
||||
<input
|
||||
value={compounded.value.t3Syn}
|
||||
onInput={(e) => {
|
||||
t4Ratio.value = compounded.value.t4Syn;
|
||||
t3Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
numGrains.value = t3Ratio.value / MPG_T3_SYN +
|
||||
t4Ratio.value / MPG_T4_SYN;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number"
|
||||
/>
|
||||
</div>{" "}
|
||||
:{" "}
|
||||
<div>
|
||||
<input
|
||||
value={compounded.value.t4Syn}
|
||||
onInput={(e) => {
|
||||
t3Ratio.value = compounded.value.t3Syn;
|
||||
t4Ratio.value = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
||||
numGrains.value = t3Ratio.value / MPG_T3_SYN +
|
||||
t4Ratio.value / MPG_T4_SYN;
|
||||
}}
|
||||
min="0"
|
||||
step="1"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="text-slab">
|
||||
<strong>Changelog:</strong>
|
||||
<p>
|
||||
<ul>
|
||||
<li>
|
||||
November 2024: Migrated to new web framework and fixed some buggy input.
|
||||
</li>
|
||||
<li>
|
||||
13th March 2024: Removed the synthetic/pure distinction as it was confusing and
|
||||
unnecessary bloat.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user