From d93daf81ea95012f92eda84231a8041cd5872ed7 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sat, 3 Dec 2022 22:51:22 -0500 Subject: [PATCH 01/33] jsdoc woes chipping away --- src/app.js | 27 ++++++++++++++++++++++++--- src/store.js | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/app.js b/src/app.js index be31cc0..40b2c2a 100644 --- a/src/app.js +++ b/src/app.js @@ -6,8 +6,12 @@ import TWPreAuto from "https://esm.sh/@twind/preset-autoprefix@1.0.1"; import * as UI from "./ui.js"; import { Reducer, Initial } from "./store.js"; import React from "https://esm.sh/preact@10.11.3/compat"; -import {html} from "https://esm.sh/htm@3.1.1/preact"; +import {html} from "https://esm.sh/htm@3.1.1/preact"; +/** @typedef {import("./store.js").State} State */ +/** @typedef {import("https://esm.sh/preact@10.11.3/compat").JSX.Element} JSX.Element */ +/** @typedef {import("./store.js").Action} Action */ +/** @type {JSX.Element} */ /** @type {TW.TwindConfig} */ const Configure = { @@ -76,25 +80,42 @@ ShadowDOM.append(ShadowCSS); ShadowDOM.append(ShadowDiv); TW.observe(TW.twind(Configure, TW.cssom(ShadowCSS)), ShadowDiv); +/** @type {import("https://esm.sh/v99/preact@10.11.3/src/index.js").Context} */ +const StoreContext = React.createContext([Initial, (a)=>{}]); -const StoreContext = React.createContext(null); const StoreProvider =(props)=> { const reducer = React.useReducer(Reducer, Initial); return html`<${StoreContext.Provider} value=${reducer}>${props.children}`; } + +/** @typedef {[state:State, dispatch:(inAction:Action)=>void]} Binding */ +/** @type {()=>Binding} */ const StoreConsumer =()=> React.useContext(StoreContext); const Deep =()=> { - const [State, Dispatch] = React.useContext(StoreContext); + const [State, Dispatch] = StoreConsumer(); return html` <${UI.Button} onClick=${()=>Dispatch({Name:"Stim", Data:1})} disabled=${State.Stim.Value == State.Stim.Max}> ${State.Stim.Value} `; } +const Audiogram =()=> +{ + const [State, Dispatch] = StoreConsumer(); + return html` + + ${State.Draw} + <${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} /> + <${UI.Mark} right=${false}/> + + + `; +} + React.render(html` <${StoreProvider}> <${UI.Button} icon="+">hey! diff --git a/src/store.js b/src/store.js index 96113fd..c523e4a 100644 --- a/src/store.js +++ b/src/store.js @@ -37,7 +37,7 @@ export const MarkSet =(freq, chan, mark)=> freq[ chan ? "UserR" : "UserL" ] = ma /** @typedef {{Hz:number, TestL:TestFrequencySample, TestR:TestFrequencySample, UserL?:TestFrequencySample, UserR?:TestFrequencySample}} TestFrequency */ /** @typedef {{Name:string, Plot:Array}} Test */ /** @typedef {{Test?:Test, Freq?:TestFrequency, Mark?:TestFrequencySample}} Context */ -/** @typedef {{Chan:Range, Freq:Range, Stim:Range, Live:Context, Draw:{UserL:DrawGroup, UserR:DrawGroup, TestL:DrawGroup, TestR:DrawGroup}, Tests:Array}} State */ +/** @typedef {{Chan:Range, Freq:Range, Stim:Range, Live:Context, Draw:{UserL:DrawGroup, UserR:DrawGroup, TestL:DrawGroup, TestR:DrawGroup}, Tests:Array}} State @memberof Store*/ /** @type {State} */ export const Initial = { From b905f92c069e13a1e0672293f4ad408f5e3ff185 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sun, 4 Dec 2022 16:19:22 -0500 Subject: [PATCH 02/33] move types to .d.ts file --- .vscode/settings.json | 7 +-- deno.json | 5 +- src/app.js | 93 ++++------------------------- src/store.js | 76 +++++------------------ src/twind.js | 70 ++++++++++++++++++++++ store.d.ts | 54 +++++++++++++++++ test/store_test.js => store.test.js | 4 +- 7 files changed, 157 insertions(+), 152 deletions(-) create mode 100644 src/twind.js create mode 100644 store.d.ts rename test/store_test.js => store.test.js (96%) diff --git a/.vscode/settings.json b/.vscode/settings.json index f393838..8675ad5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,4 @@ { "deno.enable": true, - "deno.unstable": true, - "deno.codeLens.testArgs": [ - "--allow-all", - "--no-check", - "--no-lock" - ] + "deno.unstable": true } \ No newline at end of file diff --git a/deno.json b/deno.json index 24d03de..346cbdd 100644 --- a/deno.json +++ b/deno.json @@ -1,8 +1,9 @@ { + "compilerOptions": {"types":["store.d.ts"]}, "tasks": { "fs": "deno run -A --no-lock https://deno.land/std@0.166.0/http/file_server.ts", - "test": "deno test --no-lock --watch test/store_test.js", - "test-debug": "deno test --no-lock --inspect-brk test/store_test.js" + "test": "deno test --no-lock --watch store.test.js", + "test-debug": "deno test --no-lock --inspect-brk store.test.js" } } \ No newline at end of file diff --git a/src/app.js b/src/app.js index 40b2c2a..c4919d9 100644 --- a/src/app.js +++ b/src/app.js @@ -1,99 +1,24 @@ -//@ts-check -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"; - +import * as TW from "./twind.js"; import * as UI from "./ui.js"; import { Reducer, Initial } from "./store.js"; import React from "https://esm.sh/preact@10.11.3/compat"; import {html} from "https://esm.sh/htm@3.1.1/preact"; -/** @typedef {import("./store.js").State} State */ -/** @typedef {import("https://esm.sh/preact@10.11.3/compat").JSX.Element} JSX.Element */ -/** @typedef {import("./store.js").Action} Action */ -/** @type {JSX.Element} */ +/** @type {preact.Context} */ +const StoreContext = React.createContext([Initial, (_a)=>{}]); -/** @type {TW.TwindConfig} */ -const Configure = { - theme: - { - extend: - { - keyframes: - { - flash: - { - '0%': { opacity: 1.0 }, - '50%': { opacity: 0.3 }, - '100%': { opacity: 0.0 } - }, - pulse: - { - "0%": { opacity: 0.0 }, - "10%": { opacity: 0.0 }, - "12%": { opacity: 1.0 }, - "22%": { opacity: 1.0 }, - "42%": { opacity: 0.2 }, - "100%": { opacity: 0.0 } - } - }, - animation: - { - flash: "flash 1s both" - }, - strokeWidth: - { - "bold": "3px" - } - } - }, - rules: - [ - [ - "stroke-draw", - { - "vector-effect": "non-scaling-stroke", - "stroke-linecap": "square", - "fill": "none" - }, - ], - [ - 'shadow-glow-(.*)', - (match, context)=> - { - return { "box-shadow": `0px 0px 5px 2px ${context.theme().colors[match[1]]}` }; - } - ], - [ - 'shadow-sss', - { - "box-shadow": "rgb(0 0 0 / 50%) 0px -3px 2px inset, rgb(255 255 255 / 50%) 0px 10px 10px inset" - } - ] - ], - presets: [TWPreTail(), TWPreAuto()] -}; -const ShadowDOM = document.querySelector("#app").attachShadow({mode: "open"}); -const ShadowDiv = document.createElement("div"); -const ShadowCSS = document.createElement("style"); -ShadowDOM.append(ShadowCSS); -ShadowDOM.append(ShadowDiv); -TW.observe(TW.twind(Configure, TW.cssom(ShadowCSS)), ShadowDiv); - -/** @type {import("https://esm.sh/v99/preact@10.11.3/src/index.js").Context} */ -const StoreContext = React.createContext([Initial, (a)=>{}]); +/** @type {(props:{children:preact.ComponentChildren})=>preact.VNode} */ const StoreProvider =(props)=> { const reducer = React.useReducer(Reducer, Initial); return html`<${StoreContext.Provider} value=${reducer}>${props.children}`; } -/** @typedef {[state:State, dispatch:(inAction:Action)=>void]} Binding */ +/** @typedef {[state:Store.State, dispatch:(inAction:Store.Action)=>void]} Binding */ /** @type {()=>Binding} */ const StoreConsumer =()=> React.useContext(StoreContext); - const Deep =()=> { const [State, Dispatch] = StoreConsumer(); @@ -116,6 +41,14 @@ const Audiogram =()=> `; } +const ShadowDOM = document.querySelector("#app").attachShadow({mode: "open"}); +const ShadowDiv = document.createElement("div"); +const ShadowCSS = document.createElement("style"); +ShadowDOM.append(ShadowCSS); +ShadowDOM.append(ShadowDiv); + +TW.Init(ShadowCSS, ShadowDiv); + React.render(html` <${StoreProvider}> <${UI.Button} icon="+">hey! diff --git a/src/store.js b/src/store.js index c523e4a..0dca492 100644 --- a/src/store.js +++ b/src/store.js @@ -1,8 +1,5 @@ -//@ts-check - const size = 1/6; -/** @typedef {[frequency:number, position:number, normal:boolean]} ColumnMapping */ -/** @type {Array} */ +/** @type {Array} */ export const ColumnMapping = [ [ 125, size*0.0, true ], [ 250, size*1.0, true ], @@ -14,7 +11,7 @@ export const ColumnMapping = [ [6000, size*5.5, false], [8000, size*6.0, true ] ]; -/** @type {(inFrequency:number)=>ColumnMapping|false} */ +/** @type {(inFrequency:number)=>Store.ColumnMapping|false} */ export const ColumnLookup =(inFrequency)=> { for(let i=0; i }; -/** @type {(freq:TestFrequency, chan:number, user:boolean)=>TestFrequencySample|undefined} */ -export const MarkGet =(freq, chan, user)=> freq[/** @type {"UserL"|"UserR"|"TestL"|"TestR"} */ (`${user ? "User" : "Test"}${chan ? "R" : "L"}`)]; +/** @type {(freq:Store.TestFrequency, chan:number, user:boolean)=>Store.TestFrequencySample|undefined} */ +export const MarkGet =(freq, chan, user)=> freq[/** @type {Store.PlotKey} */ (`${user ? "User" : "Test"}${chan ? "R" : "L"}`)]; -/** @type {(freq:TestFrequency, chan:number, mark:TestFrequencySample|undefined)=>TestFrequencySample|undefined} */ +/** @type {(freq:Store.TestFrequency, chan:number, mark:TestFrequencySample|undefined)=>Store.TestFrequencySample|undefined} */ export const MarkSet =(freq, chan, mark)=> freq[ chan ? "UserR" : "UserL" ] = mark; -/** @typedef {{Min:number, Max:number, Value:number, Step:number}} Range */ -/** @typedef {{Stim:number, Resp:boolean}} TestFrequencySample */ -/** @typedef {{Hz:number, TestL:TestFrequencySample, TestR:TestFrequencySample, UserL?:TestFrequencySample, UserR?:TestFrequencySample}} TestFrequency */ -/** @typedef {{Name:string, Plot:Array}} Test */ -/** @typedef {{Test?:Test, Freq?:TestFrequency, Mark?:TestFrequencySample}} Context */ -/** @typedef {{Chan:Range, Freq:Range, Stim:Range, Live:Context, Draw:{UserL:DrawGroup, UserR:DrawGroup, TestL:DrawGroup, TestR:DrawGroup}, Tests:Array}} State @memberof Store*/ -/** @type {State} */ +/** @type {Store.State} */ export const Initial = { Chan: { Min:0, Max:1, Value:0, Step:1 }, @@ -69,15 +60,7 @@ export const Initial = ] }; -/** @typedef {{Name:"Mark", Data:boolean|null}} ActionMark */ -/** @typedef {{Name:"Test", Data:number}} ActionTest */ -/** @typedef {{Name:"Chan", Data:number}} ActionChan */ -/** @typedef {{Name:"Freq", Data:number}} ActionFreq */ -/** @typedef {{Name:"Stim", Data:number}} ActionStim */ -/** @typedef {ActionMark|ActionTest|ActionChan|ActionFreq|ActionStim} Action */ -/** @typedef {(inState:State, inAction:Action)=>State} Reducer */ -/** @typedef {(inState:State)=>boolean} SelectionUpdater */ -/** @type {Record} */ +/** @type {Record} */ const Update = { Freq(inState) @@ -111,19 +94,16 @@ const Update = } }; -/** @typedef {{X:number, Y:number, Mark:TestFrequencySample}} DrawPoint */ -/** @typedef {{Points:Array, Paths:Array>}} DrawGroup */ -/** @typedef {{Left:DrawGroup, Right:DrawGroup}} DrawChart */ -/** @typedef {{User?:DrawChart, Test?:DrawChart}} DrawTest */ -/** @type {(inTest:Test, inChan:number, inStim:Range, inIsUser:boolean)=>DrawGroup} */ + +/** @type {(inTest:Store.Test, inChan:number, inStim:Range, inIsUser:boolean)=>Store.DrawGroup} */ export function Congtiguous(inTest, inChan, inStim, inIsUser) { - /** @type {DrawGroup} */ + /** @type {Store.DrawGroup} */ const output = {Points:[], Paths:[]}; let plot; let valid = false; - /** @type {Array} */ + /** @type {Array} */ let segment = []; for(let i=0; i -{ - const outTests = []; - const inFreq = inMin[0]; - for(let i=1; i + { + return { "box-shadow": `0px 0px 5px 2px ${context.theme().colors[match[1]]}` }; + } + ], + [ + 'shadow-sss', + { + "box-shadow": "rgb(0 0 0 / 50%) 0px -3px 2px inset, rgb(255 255 255 / 50%) 0px 10px 10px inset" + } + ] + ], + presets: [TWPreTail(), TWPreAuto()] +}; + +/** @type {(elStyle:HTMLStyleElement, elDiv:HTMLDivElement)=>void} */ +export const Init =(elStyle, elDiv)=> +{ + TW.observe(TW.twind(Configure, TW.cssom(elStyle)), elDiv); +}; \ No newline at end of file diff --git a/store.d.ts b/store.d.ts new file mode 100644 index 0000000..b3193f2 --- /dev/null +++ b/store.d.ts @@ -0,0 +1,54 @@ +declare namespace Store { + type ColumnMapping = [frequency: number, position: number, normal: boolean]; + + type Range = { Min: number; Max: number; Value: number; Step: number }; + type TestFrequencySample = { Stim: number; Resp: boolean }; + + type TestFrequency = { + Hz: number; + TestL: TestFrequencySample; + TestR: TestFrequencySample; + UserL?: TestFrequencySample; + UserR?: TestFrequencySample; + }; + + type Test = { Name: string; Plot: Array }; + + type Context = { + Test?: Test; + Freq?: TestFrequency; + Mark?: TestFrequencySample; + }; + + type State = { + Chan: Range; + Freq: Range; + Stim: Range; + Live: Context; + Draw: { + UserL: DrawGroup; + UserR: DrawGroup; + TestL: DrawGroup; + TestR: DrawGroup; + }; + Tests: Array; + }; + + type ActionMark = { Name: "Mark"; Data: boolean | null }; + type ActionTest = { Name: "Test"; Data: number }; + type ActionChan = { Name: "Chan"; Data: number }; + type ActionFreq = { Name: "Freq"; Data: number }; + type ActionStim = { Name: "Stim"; Data: number }; + type Action = ActionMark | ActionTest | ActionChan | ActionFreq | ActionStim; + type Reducer = (inState: State, inAction: Action) => State; + type ContextUpdater = (inState: State) => boolean; + + type PlotKeyUser = "UserL" | "UserR"; + type PlotKeyTest = "TestL" | "TestR"; + type PlotKey = PlotKeyUser | PlotKeyTest; + + type DrawPoint = { X: number; Y: number; Mark: TestFrequencySample }; + type DrawGroup = { Points: Array; Paths: Array> }; + type DrawChart = { Left: DrawGroup; Right: DrawGroup }; + type DrawTest = { User?: DrawChart; Test?: DrawChart }; +} diff --git a/test/store_test.js b/store.test.js similarity index 96% rename from test/store_test.js rename to store.test.js index 1fd7d1e..b3681c9 100644 --- a/test/store_test.js +++ b/store.test.js @@ -1,5 +1,5 @@ import { assertEquals } from "https://deno.land/std@0.166.0/testing/asserts.ts"; -import { Reducer, ColumnMapping, Congtiguous, Initial } from "../src/store.js"; +import { Reducer, ColumnMapping, Congtiguous, Initial } from "./src/store.js"; let state = {...Initial}; @@ -103,7 +103,7 @@ Deno.test("Make Marks", async(t)=> Deno.test("Contiguous Lines", ()=> { - /** @type {import("../src/store.js").Test} */ + /** @type {Store.Test} */ const model = { Name:"", Plot:[ From 20f4a2ac47792d282626faa6fb28c8179a01685d Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sun, 4 Dec 2022 16:21:13 -0500 Subject: [PATCH 03/33] path update... --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 346cbdd..26d8a62 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,5 @@ { - "compilerOptions": {"types":["store.d.ts"]}, + "compilerOptions": {"types":["/store.d.ts"]}, "tasks": { "fs": "deno run -A --no-lock https://deno.land/std@0.166.0/http/file_server.ts", From 4897f7021f276bd3e856b0939146101fad055ebe Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Mon, 5 Dec 2022 09:55:34 -0500 Subject: [PATCH 04/33] cleanup --- deno.json | 2 ++ src/app.js | 35 +++++++++++------------------------ src/store.js | 4 ++-- src/ui.js | 30 ++++++++++++++---------------- 4 files changed, 29 insertions(+), 42 deletions(-) diff --git a/deno.json b/deno.json index 26d8a62..f11ff68 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,7 @@ { "compilerOptions": {"types":["/store.d.ts"]}, + "lint": {"files": {"include": ["src/"]}}, + "fmt": {"files": {"include": ["src/"]}}, "tasks": { "fs": "deno run -A --no-lock https://deno.land/std@0.166.0/http/file_server.ts", diff --git a/src/app.js b/src/app.js index c4919d9..17c80d4 100644 --- a/src/app.js +++ b/src/app.js @@ -6,37 +6,30 @@ import {html} from "https://esm.sh/htm@3.1.1/preact"; /** @type {preact.Context} */ const StoreContext = React.createContext([Initial, (_a)=>{}]); - - /** @type {(props:{children:preact.ComponentChildren})=>preact.VNode} */ const StoreProvider =(props)=> { - const reducer = React.useReducer(Reducer, Initial); + const initialized = Reducer(Initial, {Name:"Test", Data:0}) + const reducer = React.useReducer(Reducer, initialized); return html`<${StoreContext.Provider} value=${reducer}>${props.children}`; } - /** @typedef {[state:Store.State, dispatch:(inAction:Store.Action)=>void]} Binding */ /** @type {()=>Binding} */ const StoreConsumer =()=> React.useContext(StoreContext); -const Deep =()=> -{ - const [State, Dispatch] = StoreConsumer(); - return html` - <${UI.Button} onClick=${()=>Dispatch({Name:"Stim", Data:1})} disabled=${State.Stim.Value == State.Stim.Max}> - ${State.Stim.Value} - `; -} - const Audiogram =()=> { const [State, Dispatch] = StoreConsumer(); + + const testL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); + const testR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); + const userL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); + const userR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); + return html` - ${State.Draw} - <${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} /> - <${UI.Mark} right=${false}/> - + ${testL} + ${testR} `; } @@ -55,14 +48,8 @@ React.render(html` <${UI.Button} light>Left <${UI.Button} inactive>Right <${UI.Button} disabled>Right - <${Deep}/> <${UI.Chart}> - - <${UI.Mark} right=${true} x=${"20%"} y="20%" /> - <${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} /> - <${UI.Mark} right=${false}/> - - + <${Audiogram}/> `, ShadowDiv); \ No newline at end of file diff --git a/src/store.js b/src/store.js index 0dca492..00f81d3 100644 --- a/src/store.js +++ b/src/store.js @@ -116,8 +116,8 @@ export function Congtiguous(inTest, inChan, inStim, inIsUser) { /** @type {Store.DrawPoint} */ const point = { - X: lookup[1]*100, - Y: (mark.Stim - inStim.Min)/(inStim.Max - inStim.Min) * 100, + X: lookup[1], + Y: (mark.Stim - inStim.Min)/(inStim.Max - inStim.Min), Mark: mark }; output.Points.push(point); diff --git a/src/ui.js b/src/ui.js index 56f709a..10468b5 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,11 +1,10 @@ -//@ts-check import React from "https://esm.sh/preact@10.11.3/compat"; import { html } from "https://esm.sh/htm@3.1.1/preact"; import { ColumnMapping } from "./store.js"; -/** @typedef {({children}:{children:React.ReactNode})=>JSX.Element} BasicElement */ +/** @typedef {({children}:{children?:preact.ComponentChildren})=>preact.VNode} BasicElement */ -/** @type {({children, icon, light, disabled, inactive, onClick}:{children:React.ReactNode, icon?:JSX.Element, light:boolean, disabled:boolean, inactive:boolean, onClick:()=>void})=>JSX.Element} */ +/** @type {({children, icon, light, disabled, inactive, onClick}:{children:preact.VNode, icon?:preact.VNode, light:boolean, disabled:boolean, inactive:boolean, onClick:()=>void})=>preact.VNode} */ export function Button({children, icon, light, disabled, inactive, onClick}) { const [FlashGet, FlashSet] = React.useState(0); @@ -39,14 +38,15 @@ export function Button({children, icon, light, disabled, inactive, onClick}) export function Chart({children}) { const inset = 20; - /** @type {Array} */ + /** @type {Array} */ const rules = []; ColumnMapping.forEach(([label, position, normal])=> { rules.push(html` ${label} - `); + ` + ); }); const dbMin = -10; @@ -56,8 +56,8 @@ export function Chart({children}) rules.push(html` - `); + ` + ); } return html`
@@ -76,14 +76,13 @@ export function Chart({children})
- - `; + `; } /** @type {Record} */ const Glyph = { - Arrow:({children})=> html` + Arrow:()=> html` `, @@ -99,14 +98,13 @@ const Glyph = { ${children}` }; -/** @type {({right, response, x, y}:{right:boolean, response?:boolean, x:string|number, y:string|number})=>JSX.Element} */ -export function Mark({right, response, x, y}) +/** @type {({right, response, x, y}:{right:boolean, response?:boolean, x:number, y:number})=>preact.VNode} */ +export const Mark =({right, response, x, y})=> { return html` - + <${ right ? Glyph.O : Glyph.X }> - ${ !response && html`<${Glyph.Arrow}/>` } + ${ !response && html`<${Glyph.Arrow}/>` } - - `; + `; } \ No newline at end of file From 142c72eb146493df7773ab38a31a559121f607d9 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Mon, 5 Dec 2022 23:22:37 -0500 Subject: [PATCH 05/33] trying to fix language server issues --- deno.json | 6 ++-- src/app.js | 65 ++++++++++------------------------ src/store.js | 53 +++++++++++++++------------ store.d.ts | 7 ++-- store.test.js => store.test.ts | 46 +++++++----------------- 5 files changed, 69 insertions(+), 108 deletions(-) rename store.test.js => store.test.ts (55%) diff --git a/deno.json b/deno.json index 26d8a62..331aa3a 100644 --- a/deno.json +++ b/deno.json @@ -1,9 +1,9 @@ { - "compilerOptions": {"types":["/store.d.ts"]}, + "compilerOptions": {"types":["./store"], "checkJs": true}, "tasks": { "fs": "deno run -A --no-lock https://deno.land/std@0.166.0/http/file_server.ts", - "test": "deno test --no-lock --watch store.test.js", - "test-debug": "deno test --no-lock --inspect-brk store.test.js" + "test": "deno test --no-lock --no-check --watch store.test.ts", + "test-debug": "deno test --no-lock --no-check --inspect-brk store.test.ts" } } \ No newline at end of file diff --git a/src/app.js b/src/app.js index c4919d9..402a0a7 100644 --- a/src/app.js +++ b/src/app.js @@ -1,46 +1,9 @@ import * as TW from "./twind.js"; import * as UI from "./ui.js"; -import { Reducer, Initial } from "./store.js"; +import * as Store from "./store.js"; import React from "https://esm.sh/preact@10.11.3/compat"; import {html} from "https://esm.sh/htm@3.1.1/preact"; -/** @type {preact.Context} */ -const StoreContext = React.createContext([Initial, (_a)=>{}]); - - -/** @type {(props:{children:preact.ComponentChildren})=>preact.VNode} */ -const StoreProvider =(props)=> -{ - const reducer = React.useReducer(Reducer, Initial); - return html`<${StoreContext.Provider} value=${reducer}>${props.children}`; -} - -/** @typedef {[state:Store.State, dispatch:(inAction:Store.Action)=>void]} Binding */ -/** @type {()=>Binding} */ -const StoreConsumer =()=> React.useContext(StoreContext); - -const Deep =()=> -{ - const [State, Dispatch] = StoreConsumer(); - return html` - <${UI.Button} onClick=${()=>Dispatch({Name:"Stim", Data:1})} disabled=${State.Stim.Value == State.Stim.Max}> - ${State.Stim.Value} - `; -} - -const Audiogram =()=> -{ - const [State, Dispatch] = StoreConsumer(); - return html` - - ${State.Draw} - <${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} /> - <${UI.Mark} right=${false}/> - - - `; -} - const ShadowDOM = document.querySelector("#app").attachShadow({mode: "open"}); const ShadowDiv = document.createElement("div"); const ShadowCSS = document.createElement("style"); @@ -49,20 +12,30 @@ ShadowDOM.append(ShadowDiv); TW.Init(ShadowCSS, ShadowDiv); +const Audiogram =()=> +{ + const [State, Dispatch] = Store.Consumer(); + + const testL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); + const testR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); + const userL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); + const userR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); + + return html` + + ${testL} + ${testR} + `; +}; + React.render(html` - <${StoreProvider}> + <${Store.Provider}> <${UI.Button} icon="+">hey! <${UI.Button} light>Left <${UI.Button} inactive>Right <${UI.Button} disabled>Right - <${Deep}/> <${UI.Chart}> - - <${UI.Mark} right=${true} x=${"20%"} y="20%" /> - <${UI.Mark} right=${false} x=${"10%"} y="20%" response=${true} /> - <${UI.Mark} right=${false}/> - - + <${Audiogram}/> `, ShadowDiv); \ No newline at end of file diff --git a/src/store.js b/src/store.js index 0dca492..955f66a 100644 --- a/src/store.js +++ b/src/store.js @@ -1,3 +1,5 @@ +import React from "https://esm.sh/preact@10.11.3/compat"; + const size = 1/6; /** @type {Array} */ export const ColumnMapping = [ @@ -26,7 +28,7 @@ export const ColumnLookup =(inFrequency)=> /** @type {(freq:Store.TestFrequency, chan:number, user:boolean)=>Store.TestFrequencySample|undefined} */ export const MarkGet =(freq, chan, user)=> freq[/** @type {Store.PlotKey} */ (`${user ? "User" : "Test"}${chan ? "R" : "L"}`)]; -/** @type {(freq:Store.TestFrequency, chan:number, mark:TestFrequencySample|undefined)=>Store.TestFrequencySample|undefined} */ +/** @type {(freq:Store.TestFrequency, chan:number, mark:Store.TestFrequencySample|undefined)=>Store.TestFrequencySample|undefined} */ export const MarkSet =(freq, chan, mark)=> freq[ chan ? "UserR" : "UserL" ] = mark; /** @type {Store.State} */ @@ -95,16 +97,13 @@ const Update = }; -/** @type {(inTest:Store.Test, inChan:number, inStim:Range, inIsUser:boolean)=>Store.DrawGroup} */ +/** @type {(inTest:Store.Test, inChan:number, inStim:Store.Range, inIsUser:boolean)=>Store.DrawGroup} */ export function Congtiguous(inTest, inChan, inStim, inIsUser) { /** @type {Store.DrawGroup} */ const output = {Points:[], Paths:[]}; let plot; - let valid = false; - /** @type {Array} */ - let segment = []; for(let i=0; i} */ +export const Context = React.createContext([Initial, (_a)=>{}]); +/** @type {(props:{children:preact.ComponentChildren})=>preact.VNode} */ +export const Provider =(props)=> +{ + const initialized = Reducer(Initial, {Name:"Test", Data:0}); + /** @type {Store.Binding} */ + const reducer = React.useReducer(Reducer, initialized); + return React.createElement(Context.Provider, {value:reducer, children:props.children}); +} +/** @type {()=>Store.Binding} */ +export const Consumer =()=> React.useContext(Context); \ No newline at end of file diff --git a/store.d.ts b/store.d.ts index b3193f2..9aecda1 100644 --- a/store.d.ts +++ b/store.d.ts @@ -47,8 +47,11 @@ declare namespace Store { type PlotKeyTest = "TestL" | "TestR"; type PlotKey = PlotKeyUser | PlotKeyTest; - type DrawPoint = { X: number; Y: number; Mark: TestFrequencySample }; - type DrawGroup = { Points: Array; Paths: Array> }; + type DrawPoint = { X: number|string; Y: number|string; Mark: TestFrequencySample }; + type DrawLine = { Head:DrawPoint, Tail:DrawPoint}; + type DrawGroup = { Points: Array; Paths: Array }; type DrawChart = { Left: DrawGroup; Right: DrawGroup }; type DrawTest = { User?: DrawChart; Test?: DrawChart }; + + type Binding = [state:State, dispatch:(inAction:Action)=>void] } diff --git a/store.test.js b/store.test.ts similarity index 55% rename from store.test.js rename to store.test.ts index b3681c9..0b46df9 100644 --- a/store.test.js +++ b/store.test.ts @@ -1,5 +1,5 @@ import { assertEquals } from "https://deno.land/std@0.166.0/testing/asserts.ts"; -import { Reducer, ColumnMapping, Congtiguous, Initial } from "./src/store.js"; +import { Reducer, ColumnMapping, Initial } from "./src/store.js"; let state = {...Initial}; @@ -41,7 +41,7 @@ Deno.test("Initialize", async(t)=> await t.step("Live context values are correct", ()=> { assertEquals(state.Live.Test, state.Tests[0]); - assertEquals(state.Live.Freq.Hz, ColumnMapping[state.Freq.Value][0]); + assertEquals(state.Live.Freq?.Hz, ColumnMapping[state.Freq.Value][0]); assertEquals(state.Live.Mark, undefined, "(User) Mark is undefined"); }); }); @@ -64,10 +64,10 @@ Deno.test("Make Marks", async(t)=> await t.step("Check marked value", ()=> { - assertEquals(state.Live.Freq.UserL !== undefined, true, `there will be a user mark for the left channel`); - assertEquals(state.Live.Freq.UserR === undefined, true, `but not the right`); - assertEquals(state.Live.Mark.Stim, state.Stim.Value); - assertEquals(state.Live.Mark.Resp, true); + assertEquals(state.Live.Freq?.UserL !== undefined, true, `there will be a user mark for the left channel`); + assertEquals(state.Live.Freq?.UserR === undefined, true, `but not the right`); + assertEquals(state.Live.Mark?.Stim, state.Stim.Value); + assertEquals(state.Live.Mark?.Resp, true); }); await t.step("Dispatch Freq, Stim, and Chan updates", ()=> @@ -85,40 +85,18 @@ Deno.test("Make Marks", async(t)=> await t.step("Check marked value", ()=> { - assertEquals(state.Live.Freq.UserR !== undefined, true, `there will be a user mark for the right channel`); - assertEquals(state.Live.Freq.UserL !== undefined, true, `and the left`); - assertEquals(state.Live.Mark.Stim, state.Stim.Value); - assertEquals(state.Live.Mark.Resp, false); + assertEquals(state.Live.Freq?.UserR !== undefined, true, `there will be a user mark for the right channel`); + assertEquals(state.Live.Freq?.UserL !== undefined, true, `and the left`); + assertEquals(state.Live.Mark?.Stim, state.Stim.Value); + assertEquals(state.Live.Mark?.Resp, false); }); await t.step("Live context values are correct", ()=> { assertEquals(state.Live.Test, state.Tests[0]); - assertEquals(state.Live.Freq.Hz, ColumnMapping[state.Freq.Value][0]); - assertEquals(state.Live.Mark.Stim, state.Stim.Value); + assertEquals(state.Live.Freq?.Hz, ColumnMapping[state.Freq.Value][0]); + assertEquals(state.Live.Mark?.Stim, state.Stim.Value); }); console.log(state.Draw); -}); - -Deno.test("Contiguous Lines", ()=> -{ - /** @type {Store.Test} */ - const model = { - Name:"", - Plot:[ - {Hz: 500, TestL: {Stim:30, Resp:true}, TestR: {Stim:35, Resp:true}, UserL:{Stim:20, Resp:true}}, - {Hz: 1000, TestL: {Stim:40, Resp:true}, TestR: {Stim:45, Resp:true}, UserL:{Stim:30, Resp:true}}, - {Hz: 2000, TestL: {Stim:40, Resp:true}, TestR: {Stim:45, Resp:true}, UserL:{Stim:30, Resp:false}}, - {Hz: 3000, TestL: {Stim:30, Resp:true}, TestR: {Stim:35, Resp:true}, UserL:{Stim:20, Resp:true}}, - {Hz: 4000, TestL: {Stim:40, Resp:true}, TestR: {Stim:45, Resp:true}, UserL:{Stim:30, Resp:true}}, - {Hz: 4000, TestL: {Stim:50, Resp:true}, TestR: {Stim:55, Resp:true}, UserL:{Stim:40, Resp:true}} - ] - } - - const {Points, Paths} = Congtiguous(model, 0, Initial.Stim, true); - assertEquals(Points.length, 6); - assertEquals(Paths.length, 2); - assertEquals(Paths[0].length, 2); - assertEquals(Paths[1].length, 3); }); \ No newline at end of file From bf4762ce65ebb654a6b0567b37a59cd3b858f158 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Tue, 6 Dec 2022 23:37:39 -0500 Subject: [PATCH 06/33] chart drawing --- index.html | 2 ++ src/app.js | 27 ++++++++++++++++++--------- src/store.js | 4 ++-- src/twind.js | 2 +- src/ui.js | 6 +++--- store.d.ts | 2 +- store.test.ts | 6 ++++++ 7 files changed, 33 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index 601fcf6..904fa1f 100644 --- a/index.html +++ b/index.html @@ -1,2 +1,4 @@ + +
\ No newline at end of file diff --git a/src/app.js b/src/app.js index 402a0a7..16cc514 100644 --- a/src/app.js +++ b/src/app.js @@ -12,20 +12,29 @@ ShadowDOM.append(ShadowDiv); TW.Init(ShadowCSS, ShadowDiv); + + const Audiogram =()=> { - const [State, Dispatch] = Store.Consumer(); + const [State] = Store.Consumer(); - const testL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); - const testR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); - const userL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${false}/>`); - const userR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${p.X} y=${p.Y} response=${p.Mark.Resp} right=${true} />`); + /** @type {(inAmount:number)=>string} */ const Perc =(inAmount)=> (inAmount*100)+"%"; + + const testMarksL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false}/>`); + const testMarksR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} />`); + const testLinesL = State.Draw.TestL.Paths.map( p=>html``); + const testLinesR = State.Draw.TestR.Paths.map( p=>html``); + + const userMarksL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false}/>`); + const userMarksR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} />`); + const userLinesL = State.Draw.UserL.Paths.map( p=>html``); + const userLinesR = State.Draw.UserR.Paths.map( p=>html``); return html` - - ${testL} - ${testR} - `; + ${testMarksL}${testLinesL} + ${testMarksR}${testLinesR} + ${userMarksL}${userLinesL} + ${userMarksR}${userLinesR}`; }; React.render(html` diff --git a/src/store.js b/src/store.js index b5fe329..fb80c3d 100644 --- a/src/store.js +++ b/src/store.js @@ -55,8 +55,8 @@ export const Initial = 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 } } + { Hz: 500, TestL: { Stim: 30, Resp: true }, TestR: { Stim: 50, Resp: true }, UserL: { Stim: 55, Resp: true }, UserR: { Stim: 50, Resp: true } }, + { Hz: 1000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true }, UserL: { Stim: 50, Resp: true }, UserR: { Stim: 30, Resp: true } } ] } ] diff --git a/src/twind.js b/src/twind.js index 0d28464..226342d 100644 --- a/src/twind.js +++ b/src/twind.js @@ -32,7 +32,7 @@ export const Configure = { }, strokeWidth: { - "bold": "3px" + "bold": "4px" } } }, diff --git a/src/ui.js b/src/ui.js index 10468b5..232a91b 100644 --- a/src/ui.js +++ b/src/ui.js @@ -98,11 +98,11 @@ const Glyph = { ${children}` }; -/** @type {({right, response, x, y}:{right:boolean, response?:boolean, x:number, y:number})=>preact.VNode} */ -export const Mark =({right, response, x, y})=> +/** @type {({right, response, x, y, classes}:{right:boolean, response?:boolean, x:number|string, y:number|string, classes:string})=>preact.VNode} */ +export const Mark =({right, response, x, y, classes})=> { return html` - + <${ right ? Glyph.O : Glyph.X }> ${ !response && html`<${Glyph.Arrow}/>` } diff --git a/store.d.ts b/store.d.ts index 9aecda1..c9cec84 100644 --- a/store.d.ts +++ b/store.d.ts @@ -47,7 +47,7 @@ declare namespace Store { type PlotKeyTest = "TestL" | "TestR"; type PlotKey = PlotKeyUser | PlotKeyTest; - type DrawPoint = { X: number|string; Y: number|string; Mark: TestFrequencySample }; + type DrawPoint = { X: number; Y: number; Mark: TestFrequencySample }; type DrawLine = { Head:DrawPoint, Tail:DrawPoint}; type DrawGroup = { Points: Array; Paths: Array }; type DrawChart = { Left: DrawGroup; Right: DrawGroup }; diff --git a/store.test.ts b/store.test.ts index 0b46df9..829a9d9 100644 --- a/store.test.ts +++ b/store.test.ts @@ -98,5 +98,11 @@ Deno.test("Make Marks", async(t)=> assertEquals(state.Live.Mark?.Stim, state.Stim.Value); }); + await t.step("Check Draw output", ()=> + { + assertEquals(state.Draw.TestL.Points.length, 2); + assertEquals(state.Draw.TestL.Paths.length, 1); + }); + console.log(state.Draw); }); \ No newline at end of file From 2c4d506f40e48cfaf7701e640fd857d8791fc2f7 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Wed, 7 Dec 2022 21:58:55 -0500 Subject: [PATCH 07/33] controls --- src/app.js | 45 ++++++++++++++++++++++++++++++++++++--------- src/store.js | 15 +++++++++++---- store.test.ts | 29 ++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src/app.js b/src/app.js index 16cc514..0fc1cdf 100644 --- a/src/app.js +++ b/src/app.js @@ -12,22 +12,52 @@ ShadowDOM.append(ShadowDiv); TW.Init(ShadowCSS, ShadowDiv); +const Controls =()=> +{ + const [State, Dispatch] = Store.Consumer(); + return html` +
+
Channel
+
${State.Chan.Value}
+ <${UI.Button} light=${State.Chan.Value == 0} inactive=${State.Chan.Value == 0} onClick=${()=>Dispatch({Name:"Chan", Data:-1})}>Left + <${UI.Button} light=${State.Chan.Value == 1} inactive=${State.Chan.Value == 1} onClick=${()=>Dispatch({Name:"Chan", Data:1})}>Right +
+
+
Frequency
+
${Store.ColumnMapping[State.Freq.Value][0]}
+ <${UI.Button} disabled=${State.Freq.Value == State.Freq.Min} onClick=${()=>Dispatch({Name:"Freq", Data:-1})}>- + <${UI.Button} disabled=${State.Freq.Value == State.Freq.Max} onClick=${()=>Dispatch({Name:"Freq", Data:1})}>+ +
+
+
Stimulus
+
${State.Stim.Value}
+ <${UI.Button} disabled=${State.Stim.Value == State.Stim.Min} onClick=${()=>Dispatch({Name:"Stim", Data:-1})}>- + <${UI.Button} disabled=${State.Stim.Value == State.Stim.Max} onClick=${()=>Dispatch({Name:"Stim", Data:1})}>+ +
+
+
Mark
+ <${UI.Button} onClick=${()=>Dispatch({Name:"Mark", Data:true })}>Response + <${UI.Button} onClick=${()=>Dispatch({Name:"Mark", Data:false})}>No Response + <${UI.Button} onClick=${()=>Dispatch({Name:"Mark", Data:null })} disabled=${State.Live.Mark == undefined}>Clear +
+ `; +}; const Audiogram =()=> { const [State] = Store.Consumer(); - + /** @type {(inAmount:number)=>string} */ const Perc =(inAmount)=> (inAmount*100)+"%"; const testMarksL = State.Draw.TestL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false}/>`); + const userMarksL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`); const testMarksR = State.Draw.TestR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} />`); - const testLinesL = State.Draw.TestL.Paths.map( p=>html``); - const testLinesR = State.Draw.TestR.Paths.map( p=>html``); + const userMarksR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} classes=${State.Live.Mark == p.Mark ? "stroke-bold":""}/>`); - const userMarksL = State.Draw.UserL.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${false}/>`); - const userMarksR = State.Draw.UserR.Points.map(p=>html`<${UI.Mark} x=${Perc(p.X)} y=${Perc(p.Y)} response=${p.Mark.Resp} right=${true} />`); + const testLinesL = State.Draw.TestL.Paths.map( p=>html``); const userLinesL = State.Draw.UserL.Paths.map( p=>html``); + const testLinesR = State.Draw.TestR.Paths.map( p=>html``); const userLinesR = State.Draw.UserR.Paths.map( p=>html``); return html` @@ -39,10 +69,7 @@ const Audiogram =()=> React.render(html` <${Store.Provider}> - <${UI.Button} icon="+">hey! - <${UI.Button} light>Left - <${UI.Button} inactive>Right - <${UI.Button} disabled>Right + <${Controls}/> <${UI.Chart}> <${Audiogram}/> diff --git a/src/store.js b/src/store.js index fb80c3d..e15317a 100644 --- a/src/store.js +++ b/src/store.js @@ -55,8 +55,13 @@ export const Initial = Name: "Patient A Asymmetric Notch", Plot: [ - { Hz: 500, TestL: { Stim: 30, Resp: true }, TestR: { Stim: 50, Resp: true }, UserL: { Stim: 55, Resp: true }, UserR: { Stim: 50, Resp: true } }, - { Hz: 1000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true }, UserL: { Stim: 50, Resp: true }, UserR: { Stim: 30, Resp: true } } + { Hz: 500, TestL: { Stim: 30, Resp: true }, TestR: { Stim: 50, Resp: true } }, + { Hz: 1000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }, + { Hz: 2000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }, + { Hz: 3000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }, + { Hz: 4000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }, + { Hz: 6000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } }, + { Hz: 8000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true } } ] } ] @@ -153,15 +158,16 @@ export function Reducer(inState, inAction) TestL: Congtiguous(clone.Live.Test, 0, clone.Stim, false), TestR: Congtiguous(clone.Live.Test, 1, clone.Stim, false) }; + clone.Live = {...clone.Live}; } else if (Name == "Mark") { if(clone.Live.Test && clone.Live.Freq) { clone.Live.Mark = MarkSet(clone.Live.Freq, clone.Chan.Value, Data !== null ? {Stim:clone.Stim.Value, Resp:Data} : undefined); - - clone.Draw = {...clone.Draw}; clone.Draw[clone.Chan.Value == 0 ? "UserL" : "UserR"] = Congtiguous(clone.Live.Test, clone.Chan.Value, clone.Stim, true); + clone.Live = {...clone.Live}; + clone.Draw = {...clone.Draw}; } } else if( Name=="Stim" || Name=="Chan" || Name=="Freq") @@ -175,6 +181,7 @@ export function Reducer(inState, inAction) { Update.Freq(clone); Update.Mark(clone); + clone.Live = {...clone.Live}; } } diff --git a/store.test.ts b/store.test.ts index 829a9d9..aa2b479 100644 --- a/store.test.ts +++ b/store.test.ts @@ -1,7 +1,34 @@ import { assertEquals } from "https://deno.land/std@0.166.0/testing/asserts.ts"; import { Reducer, ColumnMapping, Initial } from "./src/store.js"; -let state = {...Initial}; +let state:Store.State = { + Chan: { Min:0, Max:1, Value:0, Step:1 }, + Freq: { Min:2, Max:8, Value:2, Step:1 }, + Stim: { Min:-10, Max:120, Value:30, Step:5 }, + Live: + { + Test: undefined, + Freq: undefined, + Mark: undefined + }, + Draw: + { + UserL:{Points:[], Paths:[]}, + UserR:{Points:[], Paths:[]}, + TestL:{Points:[], Paths:[]}, + TestR:{Points:[], Paths:[]} + }, + Tests: [ + { + Name: "Patient A Asymmetric Notch", + Plot: + [ + { Hz: 500, TestL: { Stim: 30, Resp: true }, TestR: { Stim: 50, Resp: true }, UserL: { Stim: 55, Resp: true }, UserR: { Stim: 50, Resp: true } }, + { Hz: 1000, TestL: { Stim: 50, Resp: true }, TestR: { Stim: 55, Resp: true }, UserL: { Stim: 50, Resp: true }, UserR: { Stim: 30, Resp: true } } + ] + } + ] +}; Deno.test("Initialize", async(t)=> { From 7b969e0bfa0681389733f4ab9fa4169336fd8e0c Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sat, 10 Dec 2022 00:33:38 -0500 Subject: [PATCH 08/33] tweaks --- src/app.js | 14 +++++- src/store.js | 118 +++++++++++++++++++++++---------------------------- src/ui.js | 12 +++--- 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/app.js b/src/app.js index 0fc1cdf..4dce494 100644 --- a/src/app.js +++ b/src/app.js @@ -64,7 +64,19 @@ const Audiogram =()=> ${testMarksL}${testLinesL} ${testMarksR}${testLinesR} ${userMarksL}${userLinesL} - ${userMarksR}${userLinesR}`; + ${userMarksR}${userLinesR} + + + + + + + + + + + + `; }; React.render(html` diff --git a/src/store.js b/src/store.js index e15317a..9f61a19 100644 --- a/src/store.js +++ b/src/store.js @@ -28,8 +28,6 @@ export const ColumnLookup =(inFrequency)=> /** @type {(freq:Store.TestFrequency, chan:number, user:boolean)=>Store.TestFrequencySample|undefined} */ export const MarkGet =(freq, chan, user)=> freq[/** @type {Store.PlotKey} */ (`${user ? "User" : "Test"}${chan ? "R" : "L"}`)]; -/** @type {(freq:Store.TestFrequency, chan:number, mark:Store.TestFrequencySample|undefined)=>Store.TestFrequencySample|undefined} */ -export const MarkSet =(freq, chan, mark)=> freq[ chan ? "UserR" : "UserL" ] = mark; /** @type {Store.State} */ export const Initial = @@ -67,74 +65,65 @@ export const Initial = ] }; -/** @type {Record} */ -const Update = + +/** @type {(inState:Store.State, inTest?:Store.Test)=>Store.Context} */ +const Reselect =(inState, inTest)=> { - Freq(inState) + /** @type {Store.Context} */ + const output = { Test:inTest??inState.Live.Test }; + const column = ColumnMapping[inState.Freq.Value]; + if(column && inState.Live.Test) { - const column = ColumnMapping[inState.Freq.Value]; - if(column && inState.Live.Test) + const hz = column[0]; + for(let i=0; iStore.DrawGroup} */ -export function Congtiguous(inTest, inChan, inStim, inIsUser) +/** @type {(inTest:Store.Test|undefined, inChan:number, inStim:Store.Range, inIsUser:boolean)=>Store.DrawGroup} */ +const Redraw =(inTest, inChan, inStim, inIsUser)=> { /** @type {Store.DrawGroup} */ const output = {Points:[], Paths:[]}; - let plot; - for(let i=0; ipreact.VNode} BasicElement */ @@ -37,10 +37,11 @@ export function Button({children, icon, light, disabled, inactive, onClick}) /** @type {BasicElement} */ export function Chart({children}) { + const [State] = Store.Consumer(); const inset = 20; /** @type {Array} */ const rules = []; - ColumnMapping.forEach(([label, position, normal])=> + Store.ColumnMapping.forEach(([label, position, normal])=> { rules.push(html` @@ -49,12 +50,10 @@ export function Chart({children}) ); }); - const dbMin = -10; - const dbMax = 120; - for(let db = dbMin; db <= dbMax; db+=10) + for(let db = State.Stim.Min; db <= State.Stim.Max; db+=10) { rules.push(html` -