Skip to content

Commit 45415a6

Browse files
committed
udt format bindings
1 parent 4151584 commit 45415a6

File tree

10 files changed

+217
-31
lines changed

10 files changed

+217
-31
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"recoil": "^0.0.13",
2121
"seamless-immutable": "^7.1.4",
2222
"transformation-matrix-js": "^2.7.6",
23+
"use-async-memo": "^1.2.2",
2324
"use-event-callback": "^0.1.0"
2425
},
2526
"scripts": {

src/components/DurationBox/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const Box = styled("div")(({ x, width, color }) => ({
3333
}))
3434
const Label = styled("div")(({ colors }) => ({
3535
position: "absolute",
36+
pointerEvents: "none",
3637
left: 4,
3738
top: 0,
3839
...(colors.dark
@@ -53,7 +54,7 @@ export const DurationBox = ({
5354
onClick,
5455
onRemoveBox,
5556
onClickBox,
56-
label = "testing label",
57+
label = "",
5758
}) => {
5859
const [toolMode] = useToolMode()
5960
const colors = useColors()

src/components/MainLayout/index.stories.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,36 @@ export const Primary = () => {
6161
/>
6262
)
6363
}
64+
65+
export const ExampleMiscLayer = () => {
66+
const [durationGroups, setDurationGroups] = useState([
67+
{
68+
color: solarized.cyan,
69+
durations: [
70+
{
71+
start: 1537416000000 - 1000 * 60 * 60 * 24 * 40,
72+
end: 1539316800000 - 1000 * 60 * 60 * 24 * 40,
73+
label: "one",
74+
},
75+
{
76+
start: 1537416000000,
77+
end: 1539316800000,
78+
label: "two",
79+
},
80+
],
81+
},
82+
])
83+
return (
84+
<MainLayout
85+
timeFormat="dates"
86+
curveGroups={[[{ data: tesla.curve2018, color: solarized.yellow }]]}
87+
timestamps={[]}
88+
durationGroups={durationGroups}
89+
onChangeTimestamps={() => null}
90+
onChangeDurationGroups={(newDurationGroups) => {
91+
console.log(newDurationGroups.asMutable())
92+
setDurationGroups(newDurationGroups)
93+
}}
94+
/>
95+
)
96+
}

src/components/MouseTransformHandler/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ export const MouseTransformHandler = ({
6868
.scale(1 + (shiftKeyDown ? 0 : scroll), 1 + (shiftKeyDown ? scroll : 0))
6969
.translate(-px, -py)
7070
)
71-
e.preventDefault()
7271
})
7372

7473
const onMouseMove = useEventCallback((e) => {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { useState } from "react"
2+
import ReactTimeSeries from "./"
3+
4+
export default {
5+
title: "ReactTimeSeries",
6+
component: ReactTimeSeries,
7+
}
8+
9+
export const Primary = () => {
10+
const [sample, setSample] = useState({
11+
timeData: [
12+
{ time: 0, value: 0 },
13+
{ time: 500, value: 1 },
14+
{ time: 1000, value: 0 },
15+
],
16+
annotation: {
17+
timestamps: [{ time: 250, label: "example timestamp" }],
18+
durations: [
19+
{ start: 0, end: 100, label: "example duration" },
20+
{ start: 200, end: 300, label: "example duration" },
21+
{ start: 800, end: 1000, label: "byeeee" },
22+
],
23+
},
24+
})
25+
return (
26+
<ReactTimeSeries
27+
interface={{}}
28+
sample={sample}
29+
onModifySample={setSample}
30+
/>
31+
)
32+
}

src/components/ReactTimeSeries/index.js

Lines changed: 141 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import React, { useState, useMemo } from "react"
2+
import { setIn } from "seamless-immutable"
23
import useEventCallback from "use-event-callback"
4+
import { useAsyncMemo } from "use-async-memo"
5+
import { RecoilRoot } from "recoil"
6+
import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"
37

48
import MainLayout from "../MainLayout"
59

@@ -17,12 +21,15 @@ const defaultEnabledTools = [
1721
]
1822
const defaultGraphs = [{ keyName: "value" }]
1923

20-
export const ReactTimeSeries = ({
24+
export const ReactTimeSeriesWithoutContext = ({
2125
corsProxy = defaultCorsProxy,
2226
interface: iface,
2327
sample,
2428
onModifySample,
2529
}) => {
30+
if (!iface) throw new Error(`"interface" is a required prop`)
31+
if (!sample) throw new Error(`"sample" is a required prop`)
32+
const getRandomColorUsingHash = useGetRandomColorUsingHash()
2633
const {
2734
timeFormat,
2835
enabledTools = defaultEnabledTools,
@@ -33,27 +40,137 @@ export const ReactTimeSeries = ({
3340
} = iface
3441
let { timeData: sampleTimeData, audioUrl, csvUrl, annotation } = sample
3542

36-
const timeData = useMemo(() => {
37-
if (sampleTimeData) return sampleTimeData
38-
// TODO load audioUrl
39-
// TODO load csvUrl
40-
}, [sampleTimeData, audioUrl, csvUrl])
43+
const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean)
4144

42-
const curveGroups = useMemo(() => {}, [timeData, graphs])
45+
const timeData = useAsyncMemo(
46+
async () => {
47+
if (sampleTimeData) return sampleTimeData
48+
// TODO load audioUrl
49+
// TODO load csvUrl
50+
},
51+
[sampleTimeData, audioUrl, csvUrl],
52+
null
53+
)
54+
55+
const timeDataLoading = !timeData && timeDataAvailable
56+
57+
const curveGroups = useMemo(() => {
58+
if (!timeData) return []
59+
const anonRows = []
60+
const namedRows = {}
61+
for (const graph of graphs) {
62+
const curveData = timeData
63+
.filter(
64+
(a) => a[graph.keyName] !== undefined && a[graph.keyName] !== null
65+
)
66+
.map((a) => [a.time, a[graph.keyName]])
67+
if (graph.row === undefined || graph.row === null) {
68+
const curveGroup = [
69+
{
70+
data: curveData,
71+
color: graph.color || getRandomColorUsingHash(graph.keyName),
72+
},
73+
]
74+
anonRows.push(curveGroup)
75+
} else {
76+
if (!namedRows[graph.row]) {
77+
namedRows[graph.row] = []
78+
}
79+
namedRows[graph.row].push({
80+
data: curveData,
81+
color: graph.color || getRandomColorUsingHash(graph.keyName),
82+
})
83+
}
84+
}
85+
return anonRows.concat(
86+
Object.entries(namedRows).sort((a, b) => a[0].localeCompare(b[0]))
87+
)
88+
}, [timeData, graphs, getRandomColorUsingHash])
4389

44-
const [timestamps, setTimestamps] = useState(() => {
90+
const timestamps = useMemo(() => {
4591
if (!annotation?.timestamps) return []
46-
// TODO derive timestamps
47-
return []
48-
})
49-
const [durationGroups, setDurationGroups] = useState(() => {
92+
return annotation?.timestamps.map((ts) => ({
93+
time: ts.time,
94+
label: ts.label,
95+
color: ts.color || getRandomColorUsingHash(ts.label),
96+
}))
97+
// eslint-disable-next-line
98+
}, [annotation?.timestamps, getRandomColorUsingHash])
99+
100+
const durationGroups = useMemo(() => {
50101
if (!annotation?.durations) return []
51-
// TODO derive timestamps
52-
return []
102+
103+
const availableLabels = Array.from(
104+
new Set(
105+
annotation.durations.flatMap((d) => [d.label, d.layer]).filter(Boolean)
106+
)
107+
)
108+
availableLabels.sort()
109+
110+
// TODO no more than 5 layers, after 5 layers start reusing layers
111+
112+
let durationGroups = availableLabels
113+
.map((label) => {
114+
return {
115+
label,
116+
color: getRandomColorUsingHash(label),
117+
durations: annotation.durations
118+
.filter((d) => d.label === label)
119+
.map((d) => ({
120+
start: d.start,
121+
end: d.end,
122+
label: d.label,
123+
})),
124+
}
125+
})
126+
.filter((dg) => dg.durations.length > 0)
127+
128+
durationGroups.push({
129+
color: "#888888",
130+
misc: true,
131+
durations: durationGroups
132+
.filter((dg) => dg.durations.length === 1)
133+
.flatMap((dg) => dg.durations)
134+
.concat(annotation.durations.filter((d) => !d.label))
135+
.map((d) => ({ ...d, color: getRandomColorUsingHash(d.label) })),
136+
})
137+
durationGroups = durationGroups.filter(
138+
(dg) => dg.misc || dg.durations.length > 1
139+
)
140+
141+
return durationGroups
142+
// eslint-disable-next-line
143+
}, [annotation?.durations])
144+
145+
const onChangeDurationGroups = useEventCallback((newDurationGroups) => {
146+
onModifySample(
147+
setIn(
148+
sample,
149+
["annotation", "durations"],
150+
newDurationGroups.flatMap((dg) =>
151+
dg.durations.map((d) => ({
152+
...(d.label && dg.label !== d.label ? {} : { label: dg.label }),
153+
...d,
154+
}))
155+
)
156+
)
157+
)
158+
})
159+
const onChangeTimestamps = useEventCallback((newTimestamps) => {
160+
onModifySample(setIn(sample, ["annotation", "timestamps"], newTimestamps))
53161
})
54162

55-
const onChangeDurationGroups = useEventCallback(() => {})
56-
const onChangeTimestamps = useEventCallback(() => {})
163+
if (timeDataLoading) return "loading" // TODO real loader
164+
165+
if (!timeData) {
166+
throw new Error(
167+
`No time data provided. Try sample={{timeData: [{time: 0, value: 1}, ...]}} or sample={{audioUrl:"https://..."}}`
168+
)
169+
}
170+
171+
if (curveGroups.length === 0) {
172+
throw new Error(`For some reason, no curves are able to be displayed.`)
173+
}
57174

58175
return (
59176
<MainLayout
@@ -67,4 +184,12 @@ export const ReactTimeSeries = ({
67184
)
68185
}
69186

187+
export const ReactTimeSeries = (props) => {
188+
return (
189+
<RecoilRoot>
190+
<ReactTimeSeriesWithoutContext {...props} />
191+
</RecoilRoot>
192+
)
193+
}
194+
70195
export default ReactTimeSeries

src/components/ReactTimeSeries/index.story.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/hooks/use-get-random-color-using-hash.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export default () => {
2020
for (let i = 0; i < label.length; i++) {
2121
hashNumber += label.charCodeAt(i)
2222
}
23-
24-
return colorsToCycle[hashNumber % colorsToCycle.length]
23+
return themeColors[colorsToCycle[hashNumber % colorsToCycle.length]]
2524
})
2625
}

src/utils/get-minor-major-duration-lines.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const weeks = 7 * days
55
const months = 30 * days
66
const years = 12 * months
77
const timeIntervals = [
8+
["1 ns", 0.000001],
9+
["1 us", 0.001],
810
["1 ms", 1],
911
["5 ms", 5],
1012
["50 ms", 50],

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15240,6 +15240,11 @@ url@^0.11.0:
1524015240
punycode "1.3.2"
1524115241
querystring "0.2.0"
1524215242

15243+
use-async-memo@^1.2.2:
15244+
version "1.2.2"
15245+
resolved "https://registry.yarnpkg.com/use-async-memo/-/use-async-memo-1.2.2.tgz#6549579335c00afc0fff6f18eafc4a9d29596ece"
15246+
integrity sha512-xJ6a+yE7l8rtCKYDEhIS4S1lQF5mNPHF5nD2bLFVh9eTePuWuOCMHZBNv5S8Jy0Mj9mYVBJWUzBiIoKNBE71eA==
15247+
1524315248
use-composed-ref@^1.0.0:
1524415249
version "1.0.0"
1524515250
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.0.0.tgz#bb13e8f4a0b873632cde4940abeb88b92d03023a"

0 commit comments

Comments
 (0)