state started

This commit is contained in:
Seth Trowbridge 2022-11-27 23:56:37 -05:00
parent 2d023fade1
commit 1336ad6d63
5 changed files with 130 additions and 955 deletions

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"deno.enable": true,
"deno.unstable": true
}

12
app.js
View File

@ -3,6 +3,7 @@ import * as TW from "https://esm.sh/@twind/core@1.0.1";
import TWPreTail from "https://esm.sh/@twind/preset-tailwind@1.0.1";
import TWPreAuto from "https://esm.sh/@twind/preset-autoprefix@1.0.1";
/** @type {TW.TwindConfig} */
const Configure = {
theme:
{
@ -41,8 +42,9 @@ const Configure = {
[
"stroke-draw",
{
"vector-effect":"non-scaling-stroke",
"stroke-linecap":"square"
"vector-effect": "non-scaling-stroke",
"stroke-linecap": "square",
"fill": "none"
},
],
[
@ -77,8 +79,10 @@ render(html`
<${UI.Button} inactive>Right<//>
<${UI.Button} disabled>Right<//>
<${UI.Chart}>
<svg class="overflow-visible stroke(blue-700 bold draw)">
<${UI.Mark} />
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 bold draw)">
<${UI.Mark} right=${true} x=${"20%"} y="20%" />
<${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} />
<${UI.Mark} right=${false}/>
</svg>
<//>
`, ShadowDiv);

File diff suppressed because one or more lines are too long

122
store.js
View File

@ -1,31 +1,109 @@
//@ts-check
/** @typedef {[f1:ListEntry, f2:ListEntry, f3:ListEntry, f4:ListEntry, f5:ListEntry, f6:ListEntry, f7:ListEntry]} FreqList */
/** @typedef {number|TestMark} ListEntry */
/** @typedef {{Name:string, Sample:{Left:FreqList, Right:FreqList}, Answer?:{Left:FreqList, Right:FreqList}}} Test */
/** @typedef {{Stim:number|null, Resp:boolean}|null} TestMark*/
/** @type FreqList */
export const Frequencies = [500, 1000, 2000, 3000, 4000, 6000, 8000];
/** @type Array<Test> */
export const Tests = [
{
Name: "Patient A Asymmetric Notch",
Sample:{
Left:[15, 10, 15, 30, 40, 35, 20],
Right:[10, 10, 20, 40, 55, 40, 15]
}
}
const size = 100/6;
/** @typedef {[frequency:number, position:number, normal:boolean]} ColumnMapping */
/** @type {Array<ColumnMapping>} */
export const ColumnMapping = [
[ 125, size*0.0, true ],
[ 250, size*1.0, true ],
[ 500, size*2.0, true ],
[1000, size*3.0, true ],
[2000, size*4.0, true ],
[3000, size*4.5, false],
[4000, size*5.0, true ],
[6000, size*5.5, false],
[8000, size*6.0, true ]
];
export const Controls =
/** @type {(inFrequency:number)=>ColumnMapping|false} */
export const ColumnLookup =(inFrequency)=>
{
Test:0,
Channel: "left",
Frequency: 1,
Stimulus: 30
for(let i=0; i<ColumnMapping.length; i++)
{
const map = ColumnMapping[i];
if(map[0] == inFrequency){ return map; }
}
return false;
}
/** @typedef {{Stim:number, Resp:boolean}} TestFrequencySample */
/** @typedef {{Hz:number, TestL:TestFrequencySample, TestR:TestFrequencySample, UserL?:TestFrequencySample, UserR?:TestFrequencySample}} TestFrequency */
/** @typedef {{Name:string, Plot:Array<TestFrequency>}} Test */
/** @typedef {{Test?:Test, Freq?:TestFrequency, Mark?:TestFrequencySample}} Context */
/** @typedef {{Chan:"left"|"right", Freq:number, Stim:number, Selection:Context, Tests:Array<Test>}} State */
/** @type {State} */
export const Initial =
{
Chan: "left",
Freq: 3,
Stim: 30,
Selection:
{
Test: undefined,
Freq: undefined,
Mark: undefined
},
Tests: [
{
Name: "Patient A Asymmetric Notch",
Plot:
[
{ Hz: 500, TestL: { Stim: 30, Resp: true }, TestR: { Stim: 50, Resp: true } },
{ Hz: 1000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }
]
}
]
};
function Reducer(inState, inAction)
/** @typedef {{Name:"Mark", Data:boolean|undefined}} ActionMark */
/** @typedef {{Name:"Test", Data:number}} ActionTest*/
/** @typedef {ActionMark|ActionTest} Action */
/** @typedef {(inState:State, inAction:Action)=>State} Reducer */
/** @type {Reducer} */
export function Reducer(inState, inAction)
{
const clone = {...inState};
switch(inAction.Name)
{
case "Test" :
{
let selTest = clone.Tests[inAction.Data];
let selFreq = undefined;
let selMark = undefined;
const column = ColumnLookup(clone.Freq);
if(column)
{
let hz = column[0];
let plot;
for(let i=0; i<selTest.Plot.length; i++)
{
plot = selTest.Plot[i];
if(plot.Hz == hz)
{
selFreq = plot;
if(clone.Chan == "left" && selFreq.UserL)
{
selMark = selFreq.UserL;
}
else if(clone.Chan == "right" && selFreq.UserR)
{
selMark = selFreq.UserR;
}
}
}
}
const freq = test.Plot[]
//clone.Selection = {...clone.Selection, Test:clone.Tests[inAction.Data]}
break;
}
case "Mark" :
{
if(clone.Test)
{
clone.Test.Plot
}
}
}
return clone;
}

48
ui.js
View File

@ -1,10 +1,11 @@
//@ts-check
import React from "https://esm.sh/preact@10.11.3/compat";
/// <reference types="https://esm.sh/v99/htm@3.1.1/preact/index.d.ts"/>
import {html} from "https://esm.sh/htm@3.1.1/preact";
import { html } from "https://esm.sh/htm@3.1.1/preact";
import { ColumnMapping, ColumnLookup } from "./store.js";
/** @typedef {({children}:{children:React.ReactNode})=>JSX.Element} BasicElement */
/** @type {({children, icon, light, disabled, inactive}:{children:React.ReactNode, icon?:JSX.Element, light:boolean, disabled:boolean, inactive:boolean})=>JSX.Element} */
export function Button({children, icon, light, disabled, inactive})
{
const [LightGet, LightSet] = React.useState(light);
@ -38,36 +39,23 @@ export function Button({children, icon, light, disabled, inactive})
/** @type {BasicElement} */
export function Chart({children})
{
const inset = 20
const size = 1/6;
/** @type {Record<string, [position:number, normal:boolean]>} */
const rulesXMapping = {
"125": [size*0.0, true ],
"250": [size*1.0, true ],
"500": [size*2.0, true ],
"1000": [size*3.0, true ],
"2000": [size*4.0, true ],
"3000": [size*4.5, false],
"4000": [size*5.0, true ],
"6000": [size*5.5, false],
"8000": [size*6.0, true ]
};
const rulesX = Object.entries(rulesXMapping).map(([label, [position, normal]])=>
const inset = 20;
/** @type {Array<JSX.Element>} */
const rules = [];
ColumnMapping.forEach(([label, position, normal])=>
{
return html`
<span class="block absolute top-[-${inset}px] left-[${position*100}%] w-0 h-[calc(100%+${inset*2}px)] border-r(1 slate-400) ${!normal && "border-dashed"}">
rules.push(html`
<span class="block absolute top-[-${inset}px] left-[${position}%] w-0 h-[calc(100%+${inset*2}px)] border-r(1 slate-400) ${!normal && "border-dashed"}">
<span class="block absolute top-0 left-0 -translate-x-1/2 -translate-y-full pb-${normal ? 4 : 1}">${label}</span>
</span>`;
</span>`);
});
const rulesY = [];
const rulesYMin = -10;
const rulesYMax = 120;
for(let db = rulesYMin; db <= rulesYMax; db+=10)
const dbMin = -10;
const dbMax = 120;
for(let db = dbMin; db <= dbMax; db+=10)
{
const percent = ((db-rulesYMin) / (rulesYMax-rulesYMin))*100;
rulesY.push(html`
<span class="block absolute left-[-${inset}px] top-[${percent}%] h-0 w-[calc(100%+${inset*2}px)] border-b(${db == 0 ? "2 black" : "1 slate-400"})">
rules.push(html`
<span class="block absolute left-[-${inset}px] top-[${((db-dbMin) / (dbMax-dbMin))*100}%] h-0 w-[calc(100%+${inset*2}px)] border-b(${db == 0 ? "2 black" : "1 slate-400"})">
<span class="block absolute top-0 left-0 -translate-x-full -translate-y-1/2 pr-2">${db}</span>
</span>
`);
@ -83,8 +71,7 @@ export function Chart({children})
</span>
<div class=${`relative top-[${inset}px] left-[${inset}px] w-[calc(100%-${inset*2}px)] h-[calc(100%-${inset*2}px)]`}>
<span class="block absolute top-0 left-[-${inset}px] w-[calc(100%+${inset*2}px)] h-[27%] bg-black opacity-10"></span>
${ rulesX }
${ rulesY }
${ rules }
<div class="absolute top-0 left-0 w-full h-full">
${ children }
</div>
@ -94,6 +81,7 @@ export function Chart({children})
`;
}
/** @type {Record<string, BasicElement>} */
const Glyph = {
Arrow:({children})=> html`