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:[