feature/layout-updates #1
14
src/app.js
14
src/app.js
@ -64,7 +64,19 @@ const Audiogram =()=>
|
|||||||
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 bold draw) opacity-50">${testMarksL}${testLinesL}</svg>
|
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 bold draw) opacity-50">${testMarksL}${testLinesL}</svg>
|
||||||
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 bold draw) opacity-50">${testMarksR}${testLinesR}</svg>
|
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 bold draw) opacity-50">${testMarksR}${testLinesR}</svg>
|
||||||
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 2 draw)">${userMarksL}${userLinesL}</svg>
|
<svg class="absolute top-0 w-full h-full overflow-visible stroke(blue-700 2 draw)">${userMarksL}${userLinesL}</svg>
|
||||||
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 2 draw)">${userMarksR}${userLinesR}</svg>`;
|
<svg class="absolute top-0 w-full h-full overflow-visible stroke(red-700 2 draw)">${userMarksR}${userLinesR}</svg>
|
||||||
|
<svg class="absolute top-0 w-full h-full overflow-visible" >
|
||||||
|
<ellipse cx="0" cy="0" rx="5" ry="30" fill="url(#glow)"></ellipse>
|
||||||
|
<ellipse cx="0" cy="0" rx="30" ry="5" fill="url(#glow)"></ellipse>
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="glow">
|
||||||
|
<stop stop-color="blue" stop-opacity="0.6" offset="0.0"></stop>
|
||||||
|
<stop stop-color="blue" stop-opacity="0.3" offset="0.2"></stop>
|
||||||
|
<stop stop-color="blue" stop-opacity="0.0" offset="1.0"></stop>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
React.render(html`
|
React.render(html`
|
||||||
|
118
src/store.js
118
src/store.js
@ -28,8 +28,6 @@ export const ColumnLookup =(inFrequency)=>
|
|||||||
/** @type {(freq:Store.TestFrequency, chan:number, user:boolean)=>Store.TestFrequencySample|undefined} */
|
/** @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"}`)];
|
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} */
|
/** @type {Store.State} */
|
||||||
export const Initial =
|
export const Initial =
|
||||||
@ -67,74 +65,65 @@ export const Initial =
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @type {Record<string, Store.ContextUpdater>} */
|
|
||||||
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];
|
const hz = column[0];
|
||||||
if(column && inState.Live.Test)
|
for(let i=0; i<inState.Live.Test.Plot.length; i++)
|
||||||
{
|
{
|
||||||
const hz = column[0];
|
const plot = inState.Live.Test.Plot[i];
|
||||||
inState.Live.Freq = undefined;
|
if(plot.Hz == hz)
|
||||||
for(let i=0; i<inState.Live.Test.Plot.length; i++)
|
|
||||||
{
|
{
|
||||||
const plot = inState.Live.Test.Plot[i];
|
output.Freq = plot;
|
||||||
if(plot.Hz == hz)
|
output.Mark = plot[`User${inState.Chan.Value ? "R" : "L"}`];
|
||||||
{
|
|
||||||
inState.Live.Freq = plot;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
},
|
|
||||||
Mark(inState)
|
|
||||||
{
|
|
||||||
const freq = inState.Live.Freq;
|
|
||||||
if(freq)
|
|
||||||
{
|
|
||||||
inState.Live.Mark = MarkGet(freq, inState.Chan.Value, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @type {(inTest:Store.Test|undefined, inChan:number, inStim:Store.Range, inIsUser:boolean)=>Store.DrawGroup} */
|
||||||
/** @type {(inTest:Store.Test, inChan:number, inStim:Store.Range, inIsUser:boolean)=>Store.DrawGroup} */
|
const Redraw =(inTest, inChan, inStim, inIsUser)=>
|
||||||
export function Congtiguous(inTest, inChan, inStim, inIsUser)
|
|
||||||
{
|
{
|
||||||
/** @type {Store.DrawGroup} */
|
/** @type {Store.DrawGroup} */
|
||||||
const output = {Points:[], Paths:[]};
|
const output = {Points:[], Paths:[]};
|
||||||
|
|
||||||
let plot;
|
if(inTest)
|
||||||
for(let i=0; i<inTest.Plot.length; i++)
|
|
||||||
{
|
{
|
||||||
plot = inTest.Plot[i];
|
let plot;
|
||||||
const mark = MarkGet(plot, inChan, inIsUser);
|
for(let i=0; i<inTest.Plot.length; i++)
|
||||||
if(mark)
|
|
||||||
{
|
{
|
||||||
const lookup = ColumnLookup(plot.Hz);
|
plot = inTest.Plot[i];
|
||||||
if(lookup)
|
const mark = MarkGet(plot, inChan, inIsUser);
|
||||||
|
if(mark)
|
||||||
{
|
{
|
||||||
/** @type {Store.DrawPoint} */
|
const lookup = ColumnLookup(plot.Hz);
|
||||||
const point = {
|
if(lookup)
|
||||||
X: lookup[1],
|
{
|
||||||
Y: (mark.Stim - inStim.Min)/(inStim.Max - inStim.Min),
|
/** @type {Store.DrawPoint} */
|
||||||
Mark: mark
|
const point = {
|
||||||
};
|
X: lookup[1],
|
||||||
output.Points.push(point);
|
Y: (mark.Stim - inStim.Min)/(inStim.Max - inStim.Min),
|
||||||
|
Mark: mark
|
||||||
|
};
|
||||||
|
output.Points.push(point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for(let i=1; i<output.Points.length; i++)
|
||||||
for(let i=1; i<output.Points.length; i++)
|
|
||||||
{
|
|
||||||
/** @type {Store.DrawLine} */
|
|
||||||
const line = {Head:output.Points[i-1], Tail:output.Points[i]};
|
|
||||||
if(line.Head.Mark.Resp && line.Tail.Mark.Resp)
|
|
||||||
{
|
{
|
||||||
output.Paths.push(line);
|
/** @type {Store.DrawLine} */
|
||||||
|
const line = {Head:output.Points[i-1], Tail:output.Points[i]};
|
||||||
|
if(line.Head.Mark.Resp && line.Tail.Mark.Resp)
|
||||||
|
{
|
||||||
|
output.Paths.push(line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@ -149,24 +138,27 @@ export function Reducer(inState, inAction)
|
|||||||
|
|
||||||
if(Name == "Test")
|
if(Name == "Test")
|
||||||
{
|
{
|
||||||
clone.Live.Test = clone.Tests[Data];
|
const test = clone.Tests[Data];
|
||||||
Update.Freq(clone);
|
clone.Live = Reselect(clone, test);
|
||||||
Update.Mark(clone);
|
|
||||||
clone.Draw = {
|
clone.Draw = {
|
||||||
UserL: Congtiguous(clone.Live.Test, 0, clone.Stim, true ),
|
UserL: Redraw(test, 0, clone.Stim, true ),
|
||||||
UserR: Congtiguous(clone.Live.Test, 1, clone.Stim, true ),
|
UserR: Redraw(test, 1, clone.Stim, true ),
|
||||||
TestL: Congtiguous(clone.Live.Test, 0, clone.Stim, false),
|
TestL: Redraw(test, 0, clone.Stim, false),
|
||||||
TestR: Congtiguous(clone.Live.Test, 1, clone.Stim, false)
|
TestR: Redraw(test, 1, clone.Stim, false)
|
||||||
};
|
};
|
||||||
clone.Live = {...clone.Live};
|
|
||||||
}
|
}
|
||||||
else if (Name == "Mark")
|
else if (Name == "Mark")
|
||||||
{
|
{
|
||||||
if(clone.Live.Test && clone.Live.Freq)
|
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);
|
const key = clone.Chan.Value == 0 ? "UserL" : "UserR";
|
||||||
clone.Draw[clone.Chan.Value == 0 ? "UserL" : "UserR"] = Congtiguous(clone.Live.Test, clone.Chan.Value, clone.Stim, true);
|
|
||||||
|
clone.Live.Mark = Data !== null ? {Stim:clone.Stim.Value, Resp:Data} : undefined;
|
||||||
|
clone.Live.Freq[key] = clone.Live.Mark;
|
||||||
|
clone.Live.Freq = {...clone.Live.Freq};
|
||||||
clone.Live = {...clone.Live};
|
clone.Live = {...clone.Live};
|
||||||
|
|
||||||
|
clone.Draw[key] = Redraw(clone.Live.Test, clone.Chan.Value, clone.Stim, true);
|
||||||
clone.Draw = {...clone.Draw};
|
clone.Draw = {...clone.Draw};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,9 +171,7 @@ export function Reducer(inState, inAction)
|
|||||||
clone[Name] = tone;
|
clone[Name] = tone;
|
||||||
if(Name != "Stim")
|
if(Name != "Stim")
|
||||||
{
|
{
|
||||||
Update.Freq(clone);
|
clone.Live = Reselect(clone);
|
||||||
Update.Mark(clone);
|
|
||||||
clone.Live = {...clone.Live};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
src/ui.js
12
src/ui.js
@ -1,6 +1,6 @@
|
|||||||
import React from "https://esm.sh/preact@10.11.3/compat";
|
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";
|
||||||
import { ColumnMapping } from "./store.js";
|
import * as Store from "./store.js";
|
||||||
|
|
||||||
/** @typedef {({children}:{children?:preact.ComponentChildren})=>preact.VNode} BasicElement */
|
/** @typedef {({children}:{children?:preact.ComponentChildren})=>preact.VNode} BasicElement */
|
||||||
|
|
||||||
@ -37,10 +37,11 @@ export function Button({children, icon, light, disabled, inactive, onClick})
|
|||||||
/** @type {BasicElement} */
|
/** @type {BasicElement} */
|
||||||
export function Chart({children})
|
export function Chart({children})
|
||||||
{
|
{
|
||||||
|
const [State] = Store.Consumer();
|
||||||
const inset = 20;
|
const inset = 20;
|
||||||
/** @type {Array<preact.VNode>} */
|
/** @type {Array<preact.VNode>} */
|
||||||
const rules = [];
|
const rules = [];
|
||||||
ColumnMapping.forEach(([label, position, normal])=>
|
Store.ColumnMapping.forEach(([label, position, normal])=>
|
||||||
{
|
{
|
||||||
rules.push(html`
|
rules.push(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"}">
|
<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"}">
|
||||||
@ -49,12 +50,10 @@ export function Chart({children})
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const dbMin = -10;
|
for(let db = State.Stim.Min; db <= State.Stim.Max; db+=10)
|
||||||
const dbMax = 120;
|
|
||||||
for(let db = dbMin; db <= dbMax; db+=10)
|
|
||||||
{
|
{
|
||||||
rules.push(html`
|
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 left-[-${inset}px] top-[${((db-State.Stim.Min) / (State.Stim.Max-State.Stim.Min))*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 class="block absolute top-0 left-0 -translate-x-full -translate-y-1/2 pr-2">${db}</span>
|
||||||
</span>`
|
</span>`
|
||||||
);
|
);
|
||||||
@ -87,7 +86,6 @@ const Glyph = {
|
|||||||
<line vector-effect="non-scaling-stroke" x1="100%" y1="100%" x2="25%" y2="100%"></line>
|
<line vector-effect="non-scaling-stroke" x1="100%" y1="100%" x2="25%" y2="100%"></line>
|
||||||
<line vector-effect="non-scaling-stroke" x1="100%" y1="100%" x2="100%" y2="25%" ></line>`,
|
<line vector-effect="non-scaling-stroke" x1="100%" y1="100%" x2="100%" y2="25%" ></line>`,
|
||||||
|
|
||||||
//style="transform: translate(50%, 50%) rotate(-15deg) scale(0.5);"
|
|
||||||
X: ({children})=> html`
|
X: ({children})=> html`
|
||||||
<line x1="-50%" y1="-50%" x2="50%" y2="50%" ></line>
|
<line x1="-50%" y1="-50%" x2="50%" y2="50%" ></line>
|
||||||
<line x1="-50%" y1="50%" x2="50%" y2="-50%"></line>
|
<line x1="-50%" y1="50%" x2="50%" y2="-50%"></line>
|
||||||
|
Loading…
Reference in New Issue
Block a user