|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- 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),
- });
- }
- }
- */
-
- export default defineComponent({
- name: 'ge-calculator',
- setup() {
- const t3Ratio = ref(1);
- const t4Ratio = ref(4);
-
- const MPG_T3_SYN = 25;
- const MPG_T4_SYN = 100;
-
- const inputDefs = [
- {
- name: "Grains",
- mpg: 1,
- unit: "",
- step: 0.1,
- },
- {
- name: "Armour, Natural Dessicated Thyroid",
- mpg: 60,
- unit: "mg",
- },
- {
- name: 'Liothyronine (Triiodothyronine, "Cytomel", T3)',
- mpg: MPG_T3_SYN,
- unit: "mcg",
- },
- {
- name: 'Levothyroxine (Thyroxine, "Cynoplus", T4)',
- mpg: MPG_T4_SYN,
- unit: "mcg",
- },
- ];
-
- 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;
- const t4Proportion = t4Ratio.value / total;
- const grainsSyn = t3Proportion / MPG_T3_SYN + t4Proportion / MPG_T4_SYN;
- const multiplierSyn = numGrains.value / grainsSyn;
- return {
- t3Syn: t3Proportion * multiplierSyn,
- t4Syn: t4Proportion * multiplierSyn,
- };
- });
-
- 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>
- </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>
- </>;
- }
- });
|