From d46ac021c9b644e7896e87aaedcfc8306ef0afff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Jean?= Date: Tue, 12 Jan 2021 11:34:49 -0500 Subject: [PATCH 1/4] Fix fatal error on time series --- .eslintrc.js | 2 +- .storybook/main.js | 2 +- .storybook/preview.js | 12 +- README.md | 2 - releaserc.js | 2 +- src/App.js | 6 +- src/components/BackgroundContainer/index.js | 20 +- src/components/ControllableWave/index.js | 36 +- src/components/DurationBoxes/index.js | 50 +- src/components/DurationBoxes/index.stories.js | 10 +- src/components/HighlightValueLabels/index.js | 21 +- src/components/MainLayout/index.js | 136 +++--- src/components/MainLayout/index.stories.js | 36 +- src/components/MainLayout/tesla.json | 457 +++++++++++++++++- src/components/MouseTransformHandler/index.js | 143 +++--- .../MouseTransformHandler/index.stories.js | 8 +- .../ReactTimeSeries.stories.js | 58 +-- src/components/ReactTimeSeries/index.js | 185 +++---- src/components/TimeStamp/index.js | 30 +- src/components/Timeline/Timeline.stories.js | 38 +- src/components/Timeline/index.js | 94 ++-- src/components/Toolbar/Toolbar.stories.js | 22 +- src/components/Toolbar/index.js | 182 +++---- src/components/Wave/index.js | 104 ++-- src/components/Wave/index.stories.js | 30 +- src/hooks/use-colors.js | 24 +- src/hooks/use-get-random-color-using-hash.js | 20 +- src/hooks/use-global-transform-matrix.js | 10 +- src/hooks/use-root-audio-elm.js | 8 +- src/hooks/use-time-cursor-time.js | 8 +- src/hooks/use-time-range.js | 10 +- src/hooks/use-tool-mode.js | 6 +- src/index.js | 10 +- src/utils/fetch-audio-data.js | 25 +- src/utils/fetch-csv-data.js | 20 +- src/utils/fix-time-data.js | 28 +- src/utils/format-time.js | 30 +- src/utils/get-minor-major-duration-lines.js | 56 +-- src/utils/init-top-level-matrix.js | 16 +- yarn.lock | 5 - 40 files changed, 1210 insertions(+), 752 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4d0552b..568724b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,4 +19,4 @@ module.exports = { rules: { "react/prop-types": ["off"], }, -} +}; diff --git a/.storybook/main.js b/.storybook/main.js index ce7b287..e6d12c2 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -5,4 +5,4 @@ module.exports = { "@storybook/addon-essentials", "@storybook/preset-create-react-app", ], -} +}; diff --git a/.storybook/preview.js b/.storybook/preview.js index 5d4a7b9..df9e07f 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,7 +1,7 @@ -import React from "react" -import { RecoilRoot } from "recoil" -import "../src/index.css" -import BackgroundContainer from "../src/components/BackgroundContainer" +import React from "react"; +import { RecoilRoot } from "recoil"; +import "../src/index.css"; +import BackgroundContainer from "../src/components/BackgroundContainer"; export const decorators = [ (Story) => ( @@ -18,8 +18,8 @@ export const decorators = [ ), -] +]; export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, -} +}; diff --git a/README.md b/README.md index 6846e6b..d63946d 100644 --- a/README.md +++ b/README.md @@ -20,5 +20,3 @@ See [demos and example code here](https://universaldatatool.com/react-time-serie ![](https://user-images.githubusercontent.com/1910070/97049707-2821d080-154a-11eb-8a58-fa6446e38ef6.png) ![](https://user-images.githubusercontent.com/1910070/97049705-2821d080-154a-11eb-87eb-97e3506d6e1b.png) - - diff --git a/releaserc.js b/releaserc.js index 2655e70..848d499 100644 --- a/releaserc.js +++ b/releaserc.js @@ -14,4 +14,4 @@ module.exports = { }, ], ], -} +}; diff --git a/src/App.js b/src/App.js index 4482171..c45e0a9 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,7 @@ -import React from "react" +import React from "react"; function App() { - return
+ return
; } -export default App +export default App; diff --git a/src/components/BackgroundContainer/index.js b/src/components/BackgroundContainer/index.js index edff6d9..9d18e94 100644 --- a/src/components/BackgroundContainer/index.js +++ b/src/components/BackgroundContainer/index.js @@ -1,14 +1,14 @@ -import React from "react" -import { styled } from "@material-ui/core/styles" -import useColors from "../../hooks/use-colors" +import React from "react"; +import { styled } from "@material-ui/core/styles"; +import useColors from "../../hooks/use-colors"; -const Container = styled("div")(({ themeColors }) => ({ - backgroundColor: themeColors.bg, -})) +const Container = styled("div")(({ themecolors }) => ({ + backgroundColor: themecolors.bg, +})); export const BackgroundContainer = ({ children }) => { - const themeColors = useColors() - return {children} -} + const themecolors = useColors(); + return {children}; +}; -export default BackgroundContainer +export default BackgroundContainer; diff --git a/src/components/ControllableWave/index.js b/src/components/ControllableWave/index.js index 9c9c1a0..2259348 100644 --- a/src/components/ControllableWave/index.js +++ b/src/components/ControllableWave/index.js @@ -1,9 +1,9 @@ -import React from "react" -import MouseTransformHandler from "../MouseTransformHandler" -import Matrix from "immutable-transform-matrix" -import Wave from "../Wave" -import useEventCallback from "use-event-callback" -import useRafState from "react-use/lib/useRafState" +import React from "react"; +import MouseTransformHandler from "../MouseTransformHandler"; +import Matrix from "immutable-transform-matrix"; +import Wave from "../Wave"; +import useEventCallback from "use-event-callback"; +import useRafState from "react-use/lib/useRafState"; export const ControllableWave = ({ curves, @@ -22,27 +22,27 @@ export const ControllableWave = ({ showValues, }) => { let [matrix, setMatrix] = useRafState(() => { - const mat = new Matrix() - const maxY = Math.max(...curves[0].data.map(([, y]) => y)) - const minY = Math.min(...curves[0].data.map(([, y]) => y)) + const mat = new Matrix(); + const maxY = Math.max(...curves[0].data.map(([, y]) => y)); + const minY = Math.min(...curves[0].data.map(([, y]) => y)); return mat .scale(1, -1) .scale(1, height) .scale(1, 1 / (maxY - minY)) - .translate(0, -maxY) - }) + .translate(0, -maxY); + }); matrix = matrix .set("a", topLevelMatrix.get("a")) - .set("e", topLevelMatrix.get("e")) + .set("e", topLevelMatrix.get("e")); const onChangeMatrix = useEventCallback((newMatrix) => { - setMatrix(newMatrix) + setMatrix(newMatrix); setTopLevelMatrix( topLevelMatrix.set("a", newMatrix.get("a")).set("e", newMatrix.get("e")) - ) - }) + ); + }); return ( - ) -} + ); +}; -export default ControllableWave +export default ControllableWave; diff --git a/src/components/DurationBoxes/index.js b/src/components/DurationBoxes/index.js index 7904833..18a4d33 100644 --- a/src/components/DurationBoxes/index.js +++ b/src/components/DurationBoxes/index.js @@ -1,8 +1,8 @@ -import React from "react" -import { styled } from "@material-ui/core/styles" -import colorAlpha from "color-alpha" -import useColors from "../../hooks/use-colors" -import useToolMode from "../../hooks/use-tool-mode" +import React from "react"; +import { styled } from "@material-ui/core/styles"; +import colorAlpha from "color-alpha"; +import useColors from "../../hooks/use-colors"; +import useToolMode from "../../hooks/use-tool-mode"; const Container = styled("div")(({ width, active, color }) => ({ width, @@ -15,7 +15,7 @@ const Container = styled("div")(({ width, active, color }) => ({ "&:hover": { backgroundColor: "rgba(0,0,0,0.2)", }, -})) +})); const Box = styled("div")(({ x, width, color }) => ({ position: "absolute", left: x, @@ -30,7 +30,7 @@ const Box = styled("div")(({ x, width, color }) => ({ whiteSpace: "pre", color: "#fff", }, -})) +})); const Label = styled("div")(({ colors }) => ({ position: "absolute", pointerEvents: "none", @@ -42,7 +42,7 @@ const Label = styled("div")(({ colors }) => ({ mixBlendMode: "multiply", color: "#888", }), -})) +})); export const DurationBox = ({ width, @@ -57,12 +57,16 @@ export const DurationBox = ({ isMiscLayer = false, label = "", }) => { - const [toolMode] = useToolMode() - const colors = useColors() - const visibleDuration = visibleTimeEnd - visibleTimeStart - + const [toolMode] = useToolMode(); + const colors = useColors(); + const visibleDuration = visibleTimeEnd - visibleTimeStart; return ( - + {durations.map( ( { @@ -74,11 +78,11 @@ export const DurationBox = ({ i ) => { const startX = - ((startTime - visibleTimeStart) / visibleDuration) * width - const endX = ((endTime - visibleTimeStart) / visibleDuration) * width + ((startTime - visibleTimeStart) / visibleDuration) * width; + const endX = ((endTime - visibleTimeStart) / visibleDuration) * width; - if (endX < 0) return null - if (isNaN(startX) || isNaN(endX)) return null + if (endX < 0) return null; + if (isNaN(startX) || isNaN(endX)) return null; return ( onClickBox(i)} onMouseUp={(e) => { if (toolMode === "delete" || e.button === 2 || e.button === 1) { - onRemoveBox(i) + onRemoveBox(i); } }} onContextMenu={(e) => { - e.preventDefault() + e.preventDefault(); }} >
{durationLabel}
- ) + ); } )} {(label || (isMiscLayer && durations.length === 0)) && ( @@ -109,7 +113,7 @@ export const DurationBox = ({ )}
- ) -} + ); +}; -export default DurationBox +export default DurationBox; diff --git a/src/components/DurationBoxes/index.stories.js b/src/components/DurationBoxes/index.stories.js index 39b9db6..7eac793 100644 --- a/src/components/DurationBoxes/index.stories.js +++ b/src/components/DurationBoxes/index.stories.js @@ -1,12 +1,12 @@ -import React from "react" -import DurationBox from "./" -import { solarized } from "../../hooks/use-colors" +import React from "react"; +import DurationBox from "./"; +import { solarized } from "../../hooks/use-colors"; export default { title: "DurationBox", component: DurationBox, argTypes: {}, -} +}; export const Primary = () => (
@@ -52,4 +52,4 @@ export const Primary = () => ( visibleTimeEnd={1000} />
-) +); diff --git a/src/components/HighlightValueLabels/index.js b/src/components/HighlightValueLabels/index.js index 672defc..6ce7931 100644 --- a/src/components/HighlightValueLabels/index.js +++ b/src/components/HighlightValueLabels/index.js @@ -1,11 +1,10 @@ -import React, { Fragment, useState, useCallback } from "react" -import useThemeColors from "../../hooks/use-colors" +import React, { Fragment, useState, useCallback } from "react"; -const HOV_SIZE = 50 +const HOV_SIZE = 50; export const Point = ({ x, y, value, t, color, width }) => { - const [showPoint, setShowPoint] = useState(false) - const onShow = useCallback(() => setShowPoint(true), [setShowPoint]) - const onHide = useCallback(() => setShowPoint(false), [setShowPoint]) + const [showPoint, setShowPoint] = useState(false); + const onShow = useCallback(() => setShowPoint(true), [setShowPoint]); + const onHide = useCallback(() => setShowPoint(false), [setShowPoint]); return ( {showPoint && ( @@ -26,8 +25,8 @@ export const Point = ({ x, y, value, t, color, width }) => { // fill="rgba(0,0,0,0.5)" > - ) -} + ); +}; export const HighlightValueLabels = ({ visibleTransformedPointsOnCurves = [], @@ -50,7 +49,7 @@ export const HighlightValueLabels = ({ )) )} - ) -} + ); +}; -export default HighlightValueLabels +export default HighlightValueLabels; diff --git a/src/components/MainLayout/index.js b/src/components/MainLayout/index.js index abdfc26..fdf894b 100644 --- a/src/components/MainLayout/index.js +++ b/src/components/MainLayout/index.js @@ -1,32 +1,32 @@ -import React, { useState } from "react" -import { styled } from "@material-ui/core/styles" -import useTimeRange from "../../hooks/use-time-range.js" -import ControllableWave from "../ControllableWave" -import DurationBoxes from "../DurationBoxes" -import useEventCallback from "use-event-callback" -import { setIn, getIn } from "seamless-immutable" -import useColors from "../../hooks/use-colors" -import Timeline from "../Timeline" -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" -import initTopLevelMatrix from "../../utils/init-top-level-matrix" -import Toolbar from "../Toolbar" -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" +import React, { useState } from "react"; +import { styled } from "@material-ui/core/styles"; +import useTimeRange from "../../hooks/use-time-range.js"; +import ControllableWave from "../ControllableWave"; +import DurationBoxes from "../DurationBoxes"; +import useEventCallback from "use-event-callback"; +import { setIn, getIn } from "seamless-immutable"; +import useColors from "../../hooks/use-colors"; +import Timeline from "../Timeline"; +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; +import initTopLevelMatrix from "../../utils/init-top-level-matrix"; +import Toolbar from "../Toolbar"; +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; -const Container = styled("div")(({ themeColors, width }) => ({ +const Container = styled("div")(({ themecolors, width }) => ({ width: width, display: "flex", flexDirection: "column", - backgroundColor: themeColors.bg, + backgroundColor: themecolors.bg, padding: 16, boxSizing: "border-box", -})) +})); const defaultEnabledTools = [ "create-durations", "label-durations", "create-timestamps", "label-timestamps", -] +]; export const MainLayout = ({ curveGroups, @@ -46,28 +46,28 @@ export const MainLayout = ({ onStopPlayback, isPlayingMedia, }) => { - const themeColors = useColors() - const [activeDurationGroup, setActiveDurationGroup] = useState(null) - const [draggedDurationIndex, setDraggedDurationIndex] = useState(null) - const [selectedDurationIndex, setSelectedDurationIndex] = useState(null) - const [selectedTimestampIndex, setSelectedTimestampIndex] = useState(null) + const themecolors = useColors(); + const [activeDurationGroup, setActiveDurationGroup] = useState(null); + const [draggedDurationIndex, setDraggedDurationIndex] = useState(null); + const [selectedDurationIndex, setSelectedDurationIndex] = useState(null); + const [selectedTimestampIndex, setSelectedTimestampIndex] = useState(null); const [topLevelMatrix, setTopLevelMatrix] = useState(() => initTopLevelMatrix({ curveGroups, width }) - ) + ); const { visibleTimeStart, visibleTimeEnd } = useTimeRange( topLevelMatrix, width - ) + ); const onDragDuration = useEventCallback((startTime, endTime) => { - if (!enabledTools.includes("create-durations")) return - if (activeDurationGroup === null) return - ;[startTime, endTime] = - startTime < endTime ? [startTime, endTime] : [endTime, startTime] + if (!enabledTools.includes("create-durations")) return; + if (activeDurationGroup === null) return; + [startTime, endTime] = + startTime < endTime ? [startTime, endTime] : [endTime, startTime]; if (selectedDurationIndex !== draggedDurationIndex) - setSelectedDurationIndex(selectedDurationIndex) + setSelectedDurationIndex(selectedDurationIndex); onChangeDurationGroups( setIn( @@ -78,27 +78,27 @@ export const MainLayout = ({ end: endTime, } ) - ) - }) + ); + }); const onDragDurationStart = useEventCallback((startTime) => { - if (!enabledTools.includes("create-durations")) return - if (activeDurationGroup === null) return - const lastIndex = durationGroups[activeDurationGroup].durations.length - setDraggedDurationIndex(lastIndex) + if (!enabledTools.includes("create-durations")) return; + if (activeDurationGroup === null) return; + const lastIndex = durationGroups[activeDurationGroup].durations.length; + setDraggedDurationIndex(lastIndex); onChangeDurationGroups( setIn(durationGroups, [activeDurationGroup, "durations", lastIndex], { start: startTime, end: startTime, }) - ) - }) + ); + }); const onDragDurationEnd = useEventCallback(() => { - if (!enabledTools.includes("create-durations")) return - setSelectedDurationIndex(draggedDurationIndex) - setDraggedDurationIndex(null) - }) + if (!enabledTools.includes("create-durations")) return; + setSelectedDurationIndex(draggedDurationIndex); + setDraggedDurationIndex(null); + }); const onCreateTimestamp = useEventCallback((time) => { - if (!enabledTools.includes("create-timestamps")) return + if (!enabledTools.includes("create-timestamps")) return; onChangeTimestamps( timestamps.concat([ { @@ -106,29 +106,29 @@ export const MainLayout = ({ color: "#888", }, ]) - ) - setSelectedTimestampIndex(timestamps.length) - }) + ); + setSelectedTimestampIndex(timestamps.length); + }); const onRemoveDurationBox = useEventCallback((dgi, boxIndex) => { - const durations = durationGroups[dgi].durations + const durations = durationGroups[dgi].durations; onChangeDurationGroups( setIn( durationGroups, [dgi, "durations"], [...durations.slice(0, boxIndex), ...durations.slice(boxIndex + 1)] ) - ) - if (selectedTimestampIndex !== null) setSelectedTimestampIndex(null) - if (selectedDurationIndex !== null) setSelectedDurationIndex(null) - }) + ); + if (selectedTimestampIndex !== null) setSelectedTimestampIndex(null); + if (selectedDurationIndex !== null) setSelectedDurationIndex(null); + }); const onClickTimestamp = useEventCallback((ts, tsi) => { - setSelectedTimestampIndex(tsi) - if (selectedDurationIndex !== null) setSelectedDurationIndex(null) - }) + setSelectedTimestampIndex(tsi); + if (selectedDurationIndex !== null) setSelectedDurationIndex(null); + }); - const getRandomColorUsingHash = useGetRandomColorUsingHash() + const getRandomColorUsingHash = useGetRandomColorUsingHash(); const onChangeSelectedItemLabel = useEventCallback(({ label, color }) => { if (selectedTimestampIndex !== null) { onChangeTimestamps( @@ -136,13 +136,13 @@ export const MainLayout = ({ [selectedTimestampIndex, "color"], color || getRandomColorUsingHash(label) ) - ) + ); } else if (selectedDurationIndex !== null) { const pathToDuration = [ activeDurationGroup, "durations", selectedDurationIndex, - ] + ]; onChangeDurationGroups( setIn(durationGroups, pathToDuration, { ...getIn(durationGroups, pathToDuration), @@ -151,21 +151,21 @@ export const MainLayout = ({ ? {} : { color: color || getRandomColorUsingHash(label) }), }) - ) + ); } - }) + }); const onRemoveTimestamp = useEventCallback((ts, tsi) => { onChangeTimestamps([ ...timestamps.slice(0, tsi), ...timestamps.slice(tsi + 1), - ]) - }) + ]); + }); - const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, width) + const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, width); return ( - + setActiveDurationGroup(i)} onClickBox={(di) => { - setSelectedDurationIndex(di) - setSelectedTimestampIndex(null) + setSelectedDurationIndex(di); + setSelectedTimestampIndex(null); }} onRemoveBox={(boxIndex) => onRemoveDurationBox(i, boxIndex)} key={i} @@ -209,7 +209,7 @@ export const MainLayout = ({ visibleTimeStart={visibleTimeStart} visibleTimeEnd={visibleTimeEnd} /> - ) + ); })} {curveGroups.map((curves, i) => ( ))} - ) -} + ); +}; -export default MainLayout +export default MainLayout; diff --git a/src/components/MainLayout/index.stories.js b/src/components/MainLayout/index.stories.js index a77a556..4c5aa72 100644 --- a/src/components/MainLayout/index.stories.js +++ b/src/components/MainLayout/index.stories.js @@ -1,14 +1,14 @@ -import React, { useState } from "react" +import React, { useState } from "react"; -import MainLayout from "./" -import tesla from "./tesla.json" -import { solarized } from "../../hooks/use-colors" +import MainLayout from "./"; +import tesla from "./tesla.json"; +import { solarized } from "../../hooks/use-colors"; export default { title: "MainLayout", component: MainLayout, argTypes: {}, -} +}; export const Primary = () => { const [durationGroups, setDurationGroups] = useState([ @@ -27,14 +27,14 @@ export const Primary = () => { color: solarized.magenta, durations: [], }, - ]) + ]); const [timestamps, setTimestamps] = useState([ { color: solarized.red, time: 1520398800000, }, - ]) + ]); return ( { onChangeTimestamps={setTimestamps} onChangeDurationGroups={setDurationGroups} /> - ) -} + ); +}; export const ExampleMiscLayer = () => { const [durationGroups, setDurationGroups] = useState([ @@ -79,7 +79,7 @@ export const ExampleMiscLayer = () => { }, ], }, - ]) + ]); return ( { durationGroups={durationGroups} onChangeTimestamps={() => null} onChangeDurationGroups={(newDurationGroups) => { - setDurationGroups(newDurationGroups) + setDurationGroups(newDurationGroups); }} /> - ) -} + ); +}; export const AudioPlayback = () => { - const [durationGroups, setDurationGroups] = useState([]) + const [durationGroups, setDurationGroups] = useState([]); - const [timestamps, setTimestamps] = useState([]) + const [timestamps, setTimestamps] = useState([]); - const [isPlayingMedia, setIsPlayingMedia] = useState(false) + const [isPlayingMedia, setIsPlayingMedia] = useState(false); return ( { onStopPlayback={() => setIsPlayingMedia(false)} isPlayingMedia={isPlayingMedia} /> - ) -} + ); +}; diff --git a/src/components/MainLayout/tesla.json b/src/components/MainLayout/tesla.json index cfcf276..15f4ed5 100644 --- a/src/components/MainLayout/tesla.json +++ b/src/components/MainLayout/tesla.json @@ -1 +1,456 @@ -{"curve2018":[[1539576000000,259.59],[1539316800000,258.78],[1539230400000,252.23],[1539144000000,256.88],[1539057600000,262.8],[1538971200000,250.56],[1538712000000,261.95],[1538625600000,281.83],[1538539200000,294.8],[1538452800000,301.02],[1538366400000,310.7],[1538107200000,264.77],[1538020800000,307.52],[1537934400000,309.58],[1537848000000,300.99],[1537761600000,299.68],[1537502400000,299.1],[1537416000000,298.33],[1537329600000,299.02],[1537243200000,284.96],[1537156800000,294.84],[1536897600000,295.2],[1536811200000,289.46],[1536724800000,290.54],[1536638400000,279.44],[1536552000000,285.5],[1536292800000,263.24],[1536206400000,280.95],[1536120000000,280.74],[1536033600000,288.95],[1535688000000,301.66],[1535601600000,303.15],[1535515200000,305.01],[1535428800000,311.86],[1535342400000,319.27],[1535083200000,322.82],[1534996800000,320.1],[1534910400000,321.64],[1534824000000,321.9],[1534737600000,308.44],[1534478400000,305.5],[1534392000000,335.45],[1534305600000,338.69],[1534219200000,347.64],[1534132800000,356.41],[1533873600000,355.49],[1533787200000,352.45],[1533700800000,370.34],[1533614400000,379.57],[1533528000000,341.99],[1533268800000,348.17],[1533182400000,349.54],[1533096000000,300.84],[1533009600000,298.14],[1532923200000,290.17],[1532664000000,297.18],[1532577600000,306.65],[1532491200000,308.74],[1532404800000,297.43],[1532318400000,303.2],[1532059200000,313.58],[1531972800000,320.23],[1531886400000,323.85],[1531800000000,322.69],[1531713600000,310.1],[1531454400000,318.87],[1531368000000,316.71],[1531281600000,318.96],[1531195200000,322.47],[1531108800000,318.51],[1530849600000,308.9],[1530763200000,309.16],[1530590400000,310.86],[1530504000000,335.07],[1530244800000,342.95],[1530158400000,349.93],[1530072000000,344.5],[1529985600000,342],[1529899200000,333.01],[1529640000000,333.63],[1529553600000,347.51],[1529467200000,362.22],[1529380800000,352.55],[1529294400000,370.83],[1529035200000,358.17],[1528948800000,357.72],[1528862400000,344.78],[1528776000000,342.77],[1528689600000,332.1],[1528430400000,317.66],[1528344000000,316.09],[1528257600000,319.5],[1528171200000,291.13],[1528084800000,296.74],[1527825600000,291.82],[1527739200000,284.73],[1527652800000,291.72],[1527566400000,283.76],[1527220800000,278.85],[1527134400000,277.85],[1527048000000,279.07],[1526961600000,275.01],[1526875200000,284.49],[1526616000000,276.82],[1526529600000,284.54],[1526443200000,286.48],[1526356800000,284.18],[1526270400000,291.97],[1526011200000,301.06],[1525924800000,305.02],[1525838400000,306.85],[1525752000000,301.97],[1525665600000,302.77],[1525406400000,294.09],[1525320000000,284.45],[1525233600000,301.15],[1525147200000,299.92],[1525060800000,293.9],[1524801600000,294.075],[1524715200000,285.48],[1524628800000,280.69],[1524542400000,283.46],[1524456000000,283.37],[1524196800000,290.24],[1524110400000,300.08],[1524024000000,293.35],[1523937600000,287.69],[1523851200000,291.21],[1523592000000,300.34],[1523505600000,294.08],[1523419200000,300.93],[1523332800000,304.7],[1523246400000,289.66],[1522987200000,299.3],[1522900800000,305.72],[1522814400000,286.94],[1522728000000,267.53],[1522641600000,252.48],[1522296000000,266.13],[1522209600000,257.78],[1522123200000,279.18],[1522036800000,304.18],[1521777600000,301.54],[1521691200000,309.1],[1521604800000,316.53],[1521518400000,310.55],[1521432000000,313.56],[1521172800000,321.35],[1521086400000,325.6],[1521000000000,326.63],[1520913600000,341.84],[1520827200000,345.51],[1520571600000,327.17],[1520485200000,329.1],[1520398800000,332.3],[1520312400000,328.2],[1520226000000,333.35],[1519966800000,335.12],[1519880400000,330.93],[1519794000000,343.06],[1519707600000,350.99],[1519621200000,357.42],[1519362000000,352.05],[1519275600000,346.17],[1519189200000,333.3],[1519102800000,334.77],[1518757200000,335.49],[1518670800000,334.065],[1518584400000,322.31],[1518498000000,323.66],[1518411600000,315.73],[1518152400000,310.42],[1518066000000,315.23],[1517979600000,345],[1517893200000,333.97],[1517806800000,333.13],[1517547600000,343.75],[1517461200000,349.25],[1517374800000,354.31],[1517288400000,345.82],[1517202000000,349.53],[1516942800000,342.85],[1516856400000,337.64],[1516770000000,345.89],[1516683600000,352.79],[1516597200000,351.56],[1516338000000,350.02],[1516251600000,344.57],[1516165200000,347.16],[1516078800000,340.06],[1515733200000,336.22],[1515646800000,337.95],[1515560400000,334.8],[1515474000000,333.69],[1515387600000,336.41],[1515128400000,316.58],[1515042000000,314.62],[1514955600000,317.25],[1514869200000,320.53]],"curve2017":[[1514523600000,311.35],[1514437200000,315.36],[1514350800000,311.64],[1514264400000,317.29],[1513918800000,325.2],[1513832400000,331.66],[1513746000000,328.98],[1513659600000,331.1],[1513573200000,338.87],[1513314000000,343.45],[1513227600000,337.89],[1513141200000,339.03],[1513054800000,341.03],[1512968400000,328.91],[1512709200000,315.13],[1512622800000,311.24],[1512536400000,313.26],[1512450000000,303.7],[1512363600000,305.2],[1512104400000,306.53],[1512018000000,308.85],[1511931600000,307.54],[1511845200000,317.55],[1511758800000,316.81],[1511499600000,315.55],[1511326800000,312.6],[1511240400000,317.81],[1511154000000,308.74],[1510894800000,315.05],[1510808400000,312.5],[1510722000000,311.3],[1510635600000,308.7],[1510549200000,315.4],[1510290000000,302.99],[1510203600000,302.99],[1510117200000,304.39],[1510030800000,306.05],[1509944400000,302.78],[1509681600000,306.09],[1509595200000,299.26],[1509508800000,321.08],[1509422400000,331.53],[1509336000000,320.08],[1509076800000,320.87],[1508990400000,326.17],[1508904000000,325.84],[1508817600000,337.34],[1508731200000,337.02],[1508472000000,345.1],[1508385600000,351.81],[1508299200000,359.65],[1508212800000,355.75],[1508126400000,350.6],[1507867200000,355.57],[1507780800000,355.68],[1507694400000,354.6],[1507608000000,355.59],[1507521600000,342.94],[1507262400000,356.88],[1507176000000,355.33],[1507089600000,355.01],[1507003200000,348.14],[1506916800000,341.53],[1506657600000,341.1],[1506571200000,339.6],[1506484800000,340.97],[1506398400000,345.25],[1506312000000,344.99],[1506052800000,351.09],[1505966400000,366.48],[1505880000000,373.91],[1505793600000,375.1],[1505707200000,385],[1505448000000,379.81],[1505361600000,377.64],[1505275200000,366.23],[1505188800000,362.75],[1505102400000,363.69],[1504843200000,343.4],[1504756800000,350.61],[1504670400000,344.53],[1504584000000,349.59],[1504238400000,355.4],[1504152000000,355.9],[1504065600000,353.18],[1503979200000,347.36],[1503892800000,345.66],[1503633600000,348.05],[1503547200000,352.93],[1503460800000,352.77],[1503374400000,341.35],[1503288000000,337.86],[1503028800000,347.46],[1502942400000,351.92],[1502856000000,362.91],[1502769600000,362.33],[1502683200000,363.8],[1502424000000,357.87],[1502337600000,355.4],[1502251200000,363.53],[1502164800000,365.22],[1502078400000,355.17],[1501819200000,356.91],[1501732800000,347.09],[1501646400000,325.89],[1501560000000,319.57],[1501473600000,323.47],[1501214400000,335.07],[1501128000000,334.46],[1501041600000,343.85],[1500955200000,339.6],[1500868800000,342.52],[1500609600000,328.4],[1500523200000,329.92],[1500436800000,325.26],[1500350400000,328.24],[1500264000000,319.57],[1500004800000,327.78],[1499918400000,323.41],[1499832000000,329.52],[1499745600000,327.22],[1499659200000,316.05],[1499400000000,313.22],[1499313600000,308.83],[1499227200000,327.09],[1499054400000,352.62],[1498795200000,361.61],[1498708800000,360.75],[1498622400000,371.24],[1498536000000,362.37],[1498449600000,377.49],[1498190400000,383.45],[1498104000000,382.61],[1498017600000,376.4],[1497931200000,372.24],[1497844800000,369.8],[1497585600000,371.4],[1497499200000,375.34],[1497412800000,380.66],[1497326400000,375.95],[1497240000000,359.01],[1496980800000,357.32],[1496894400000,370],[1496808000000,359.65],[1496721600000,352.85],[1496635200000,347.32],[1496376000000,339.85],[1496289600000,340.37],[1496203200000,341.01],[1496116800000,335.1],[1495771200000,325.14],[1495684800000,316.83],[1495598400000,310.22],[1495512000000,303.86],[1495425600000,310.35],[1495166400000,310.83],[1495080000000,313.06],[1494993600000,306.11],[1494907200000,317.01],[1494820800000,315.88],[1494561600000,324.81],[1494475200000,323.1],[1494388800000,325.22],[1494302400000,321.26],[1494216000000,307.19],[1493956800000,308.35],[1493870400000,295.46],[1493784000000,311.02],[1493697600000,318.89],[1493611200000,322.83],[1493352000000,314.07],[1493265600000,308.63],[1493179200000,310.17],[1493092800000,313.79],[1493006400000,308.03],[1492747200000,305.6],[1492660800000,302.51],[1492574400000,305.52],[1492488000000,300.25],[1492401600000,301.44],[1492056000000,304],[1491969600000,296.84],[1491883200000,308.71],[1491796800000,312.39],[1491537600000,302.54],[1491451200000,298.7],[1491364800000,295],[1491278400000,303.7],[1491192000000,298.52],[1490932800000,278.3],[1490846400000,277.92],[1490760000000,277.38],[1490673600000,277.45],[1490587200000,270.22],[1490328000000,263.16],[1490241600000,254.78],[1490155200000,255.01],[1490068800000,250.68],[1489982400000,261.92],[1489723200000,261.5],[1489636800000,262.05],[1489550400000,255.73],[1489464000000,258],[1489377600000,246.17],[1489122000000,243.69],[1489035600000,244.9],[1488949200000,246.87],[1488862800000,248.59],[1488776400000,251.21],[1488517200000,251.57],[1488430800000,250.48],[1488344400000,250.02],[1488258000000,249.99],[1488171600000,246.23],[1487912400000,257],[1487826000000,255.99],[1487739600000,273.51],[1487653200000,277.39],[1487307600000,272.23],[1487221200000,268.95],[1487134800000,279.76],[1487048400000,280.98],[1486962000000,280.6],[1486702800000,269.23],[1486616400000,269.2],[1486530000000,262.08],[1486443600000,257.48],[1486357200000,257.77],[1486098000000,251.33],[1486011600000,251.55],[1485925200000,249.24],[1485838800000,251.93],[1485752400000,250.63],[1485493200000,252.95],[1485406800000,252.51],[1485320400000,254.47],[1485234000000,254.61],[1485147600000,248.92],[1484888400000,244.73],[1484802000000,243.76],[1484715600000,238.36],[1484629200000,235.58],[1484283600000,237.75],[1484197200000,229.59],[1484110800000,229.73],[1484024400000,229.87],[1483938000000,231.28],[1483678800000,229.01],[1483592400000,226.75],[1483506000000,226.99],[1483419600000,216.99]]} \ No newline at end of file +{ + "curve2018": [ + [1539576000000, 259.59], + [1539316800000, 258.78], + [1539230400000, 252.23], + [1539144000000, 256.88], + [1539057600000, 262.8], + [1538971200000, 250.56], + [1538712000000, 261.95], + [1538625600000, 281.83], + [1538539200000, 294.8], + [1538452800000, 301.02], + [1538366400000, 310.7], + [1538107200000, 264.77], + [1538020800000, 307.52], + [1537934400000, 309.58], + [1537848000000, 300.99], + [1537761600000, 299.68], + [1537502400000, 299.1], + [1537416000000, 298.33], + [1537329600000, 299.02], + [1537243200000, 284.96], + [1537156800000, 294.84], + [1536897600000, 295.2], + [1536811200000, 289.46], + [1536724800000, 290.54], + [1536638400000, 279.44], + [1536552000000, 285.5], + [1536292800000, 263.24], + [1536206400000, 280.95], + [1536120000000, 280.74], + [1536033600000, 288.95], + [1535688000000, 301.66], + [1535601600000, 303.15], + [1535515200000, 305.01], + [1535428800000, 311.86], + [1535342400000, 319.27], + [1535083200000, 322.82], + [1534996800000, 320.1], + [1534910400000, 321.64], + [1534824000000, 321.9], + [1534737600000, 308.44], + [1534478400000, 305.5], + [1534392000000, 335.45], + [1534305600000, 338.69], + [1534219200000, 347.64], + [1534132800000, 356.41], + [1533873600000, 355.49], + [1533787200000, 352.45], + [1533700800000, 370.34], + [1533614400000, 379.57], + [1533528000000, 341.99], + [1533268800000, 348.17], + [1533182400000, 349.54], + [1533096000000, 300.84], + [1533009600000, 298.14], + [1532923200000, 290.17], + [1532664000000, 297.18], + [1532577600000, 306.65], + [1532491200000, 308.74], + [1532404800000, 297.43], + [1532318400000, 303.2], + [1532059200000, 313.58], + [1531972800000, 320.23], + [1531886400000, 323.85], + [1531800000000, 322.69], + [1531713600000, 310.1], + [1531454400000, 318.87], + [1531368000000, 316.71], + [1531281600000, 318.96], + [1531195200000, 322.47], + [1531108800000, 318.51], + [1530849600000, 308.9], + [1530763200000, 309.16], + [1530590400000, 310.86], + [1530504000000, 335.07], + [1530244800000, 342.95], + [1530158400000, 349.93], + [1530072000000, 344.5], + [1529985600000, 342], + [1529899200000, 333.01], + [1529640000000, 333.63], + [1529553600000, 347.51], + [1529467200000, 362.22], + [1529380800000, 352.55], + [1529294400000, 370.83], + [1529035200000, 358.17], + [1528948800000, 357.72], + [1528862400000, 344.78], + [1528776000000, 342.77], + [1528689600000, 332.1], + [1528430400000, 317.66], + [1528344000000, 316.09], + [1528257600000, 319.5], + [1528171200000, 291.13], + [1528084800000, 296.74], + [1527825600000, 291.82], + [1527739200000, 284.73], + [1527652800000, 291.72], + [1527566400000, 283.76], + [1527220800000, 278.85], + [1527134400000, 277.85], + [1527048000000, 279.07], + [1526961600000, 275.01], + [1526875200000, 284.49], + [1526616000000, 276.82], + [1526529600000, 284.54], + [1526443200000, 286.48], + [1526356800000, 284.18], + [1526270400000, 291.97], + [1526011200000, 301.06], + [1525924800000, 305.02], + [1525838400000, 306.85], + [1525752000000, 301.97], + [1525665600000, 302.77], + [1525406400000, 294.09], + [1525320000000, 284.45], + [1525233600000, 301.15], + [1525147200000, 299.92], + [1525060800000, 293.9], + [1524801600000, 294.075], + [1524715200000, 285.48], + [1524628800000, 280.69], + [1524542400000, 283.46], + [1524456000000, 283.37], + [1524196800000, 290.24], + [1524110400000, 300.08], + [1524024000000, 293.35], + [1523937600000, 287.69], + [1523851200000, 291.21], + [1523592000000, 300.34], + [1523505600000, 294.08], + [1523419200000, 300.93], + [1523332800000, 304.7], + [1523246400000, 289.66], + [1522987200000, 299.3], + [1522900800000, 305.72], + [1522814400000, 286.94], + [1522728000000, 267.53], + [1522641600000, 252.48], + [1522296000000, 266.13], + [1522209600000, 257.78], + [1522123200000, 279.18], + [1522036800000, 304.18], + [1521777600000, 301.54], + [1521691200000, 309.1], + [1521604800000, 316.53], + [1521518400000, 310.55], + [1521432000000, 313.56], + [1521172800000, 321.35], + [1521086400000, 325.6], + [1521000000000, 326.63], + [1520913600000, 341.84], + [1520827200000, 345.51], + [1520571600000, 327.17], + [1520485200000, 329.1], + [1520398800000, 332.3], + [1520312400000, 328.2], + [1520226000000, 333.35], + [1519966800000, 335.12], + [1519880400000, 330.93], + [1519794000000, 343.06], + [1519707600000, 350.99], + [1519621200000, 357.42], + [1519362000000, 352.05], + [1519275600000, 346.17], + [1519189200000, 333.3], + [1519102800000, 334.77], + [1518757200000, 335.49], + [1518670800000, 334.065], + [1518584400000, 322.31], + [1518498000000, 323.66], + [1518411600000, 315.73], + [1518152400000, 310.42], + [1518066000000, 315.23], + [1517979600000, 345], + [1517893200000, 333.97], + [1517806800000, 333.13], + [1517547600000, 343.75], + [1517461200000, 349.25], + [1517374800000, 354.31], + [1517288400000, 345.82], + [1517202000000, 349.53], + [1516942800000, 342.85], + [1516856400000, 337.64], + [1516770000000, 345.89], + [1516683600000, 352.79], + [1516597200000, 351.56], + [1516338000000, 350.02], + [1516251600000, 344.57], + [1516165200000, 347.16], + [1516078800000, 340.06], + [1515733200000, 336.22], + [1515646800000, 337.95], + [1515560400000, 334.8], + [1515474000000, 333.69], + [1515387600000, 336.41], + [1515128400000, 316.58], + [1515042000000, 314.62], + [1514955600000, 317.25], + [1514869200000, 320.53] + ], + "curve2017": [ + [1514523600000, 311.35], + [1514437200000, 315.36], + [1514350800000, 311.64], + [1514264400000, 317.29], + [1513918800000, 325.2], + [1513832400000, 331.66], + [1513746000000, 328.98], + [1513659600000, 331.1], + [1513573200000, 338.87], + [1513314000000, 343.45], + [1513227600000, 337.89], + [1513141200000, 339.03], + [1513054800000, 341.03], + [1512968400000, 328.91], + [1512709200000, 315.13], + [1512622800000, 311.24], + [1512536400000, 313.26], + [1512450000000, 303.7], + [1512363600000, 305.2], + [1512104400000, 306.53], + [1512018000000, 308.85], + [1511931600000, 307.54], + [1511845200000, 317.55], + [1511758800000, 316.81], + [1511499600000, 315.55], + [1511326800000, 312.6], + [1511240400000, 317.81], + [1511154000000, 308.74], + [1510894800000, 315.05], + [1510808400000, 312.5], + [1510722000000, 311.3], + [1510635600000, 308.7], + [1510549200000, 315.4], + [1510290000000, 302.99], + [1510203600000, 302.99], + [1510117200000, 304.39], + [1510030800000, 306.05], + [1509944400000, 302.78], + [1509681600000, 306.09], + [1509595200000, 299.26], + [1509508800000, 321.08], + [1509422400000, 331.53], + [1509336000000, 320.08], + [1509076800000, 320.87], + [1508990400000, 326.17], + [1508904000000, 325.84], + [1508817600000, 337.34], + [1508731200000, 337.02], + [1508472000000, 345.1], + [1508385600000, 351.81], + [1508299200000, 359.65], + [1508212800000, 355.75], + [1508126400000, 350.6], + [1507867200000, 355.57], + [1507780800000, 355.68], + [1507694400000, 354.6], + [1507608000000, 355.59], + [1507521600000, 342.94], + [1507262400000, 356.88], + [1507176000000, 355.33], + [1507089600000, 355.01], + [1507003200000, 348.14], + [1506916800000, 341.53], + [1506657600000, 341.1], + [1506571200000, 339.6], + [1506484800000, 340.97], + [1506398400000, 345.25], + [1506312000000, 344.99], + [1506052800000, 351.09], + [1505966400000, 366.48], + [1505880000000, 373.91], + [1505793600000, 375.1], + [1505707200000, 385], + [1505448000000, 379.81], + [1505361600000, 377.64], + [1505275200000, 366.23], + [1505188800000, 362.75], + [1505102400000, 363.69], + [1504843200000, 343.4], + [1504756800000, 350.61], + [1504670400000, 344.53], + [1504584000000, 349.59], + [1504238400000, 355.4], + [1504152000000, 355.9], + [1504065600000, 353.18], + [1503979200000, 347.36], + [1503892800000, 345.66], + [1503633600000, 348.05], + [1503547200000, 352.93], + [1503460800000, 352.77], + [1503374400000, 341.35], + [1503288000000, 337.86], + [1503028800000, 347.46], + [1502942400000, 351.92], + [1502856000000, 362.91], + [1502769600000, 362.33], + [1502683200000, 363.8], + [1502424000000, 357.87], + [1502337600000, 355.4], + [1502251200000, 363.53], + [1502164800000, 365.22], + [1502078400000, 355.17], + [1501819200000, 356.91], + [1501732800000, 347.09], + [1501646400000, 325.89], + [1501560000000, 319.57], + [1501473600000, 323.47], + [1501214400000, 335.07], + [1501128000000, 334.46], + [1501041600000, 343.85], + [1500955200000, 339.6], + [1500868800000, 342.52], + [1500609600000, 328.4], + [1500523200000, 329.92], + [1500436800000, 325.26], + [1500350400000, 328.24], + [1500264000000, 319.57], + [1500004800000, 327.78], + [1499918400000, 323.41], + [1499832000000, 329.52], + [1499745600000, 327.22], + [1499659200000, 316.05], + [1499400000000, 313.22], + [1499313600000, 308.83], + [1499227200000, 327.09], + [1499054400000, 352.62], + [1498795200000, 361.61], + [1498708800000, 360.75], + [1498622400000, 371.24], + [1498536000000, 362.37], + [1498449600000, 377.49], + [1498190400000, 383.45], + [1498104000000, 382.61], + [1498017600000, 376.4], + [1497931200000, 372.24], + [1497844800000, 369.8], + [1497585600000, 371.4], + [1497499200000, 375.34], + [1497412800000, 380.66], + [1497326400000, 375.95], + [1497240000000, 359.01], + [1496980800000, 357.32], + [1496894400000, 370], + [1496808000000, 359.65], + [1496721600000, 352.85], + [1496635200000, 347.32], + [1496376000000, 339.85], + [1496289600000, 340.37], + [1496203200000, 341.01], + [1496116800000, 335.1], + [1495771200000, 325.14], + [1495684800000, 316.83], + [1495598400000, 310.22], + [1495512000000, 303.86], + [1495425600000, 310.35], + [1495166400000, 310.83], + [1495080000000, 313.06], + [1494993600000, 306.11], + [1494907200000, 317.01], + [1494820800000, 315.88], + [1494561600000, 324.81], + [1494475200000, 323.1], + [1494388800000, 325.22], + [1494302400000, 321.26], + [1494216000000, 307.19], + [1493956800000, 308.35], + [1493870400000, 295.46], + [1493784000000, 311.02], + [1493697600000, 318.89], + [1493611200000, 322.83], + [1493352000000, 314.07], + [1493265600000, 308.63], + [1493179200000, 310.17], + [1493092800000, 313.79], + [1493006400000, 308.03], + [1492747200000, 305.6], + [1492660800000, 302.51], + [1492574400000, 305.52], + [1492488000000, 300.25], + [1492401600000, 301.44], + [1492056000000, 304], + [1491969600000, 296.84], + [1491883200000, 308.71], + [1491796800000, 312.39], + [1491537600000, 302.54], + [1491451200000, 298.7], + [1491364800000, 295], + [1491278400000, 303.7], + [1491192000000, 298.52], + [1490932800000, 278.3], + [1490846400000, 277.92], + [1490760000000, 277.38], + [1490673600000, 277.45], + [1490587200000, 270.22], + [1490328000000, 263.16], + [1490241600000, 254.78], + [1490155200000, 255.01], + [1490068800000, 250.68], + [1489982400000, 261.92], + [1489723200000, 261.5], + [1489636800000, 262.05], + [1489550400000, 255.73], + [1489464000000, 258], + [1489377600000, 246.17], + [1489122000000, 243.69], + [1489035600000, 244.9], + [1488949200000, 246.87], + [1488862800000, 248.59], + [1488776400000, 251.21], + [1488517200000, 251.57], + [1488430800000, 250.48], + [1488344400000, 250.02], + [1488258000000, 249.99], + [1488171600000, 246.23], + [1487912400000, 257], + [1487826000000, 255.99], + [1487739600000, 273.51], + [1487653200000, 277.39], + [1487307600000, 272.23], + [1487221200000, 268.95], + [1487134800000, 279.76], + [1487048400000, 280.98], + [1486962000000, 280.6], + [1486702800000, 269.23], + [1486616400000, 269.2], + [1486530000000, 262.08], + [1486443600000, 257.48], + [1486357200000, 257.77], + [1486098000000, 251.33], + [1486011600000, 251.55], + [1485925200000, 249.24], + [1485838800000, 251.93], + [1485752400000, 250.63], + [1485493200000, 252.95], + [1485406800000, 252.51], + [1485320400000, 254.47], + [1485234000000, 254.61], + [1485147600000, 248.92], + [1484888400000, 244.73], + [1484802000000, 243.76], + [1484715600000, 238.36], + [1484629200000, 235.58], + [1484283600000, 237.75], + [1484197200000, 229.59], + [1484110800000, 229.73], + [1484024400000, 229.87], + [1483938000000, 231.28], + [1483678800000, 229.01], + [1483592400000, 226.75], + [1483506000000, 226.99], + [1483419600000, 216.99] + ] +} diff --git a/src/components/MouseTransformHandler/index.js b/src/components/MouseTransformHandler/index.js index f050189..ea0bd7c 100644 --- a/src/components/MouseTransformHandler/index.js +++ b/src/components/MouseTransformHandler/index.js @@ -1,9 +1,9 @@ -import React, { useState, useRef, useEffect, useCallback } from "react" -import { styled } from "@material-ui/core/styles" -import useEventCallback from "use-event-callback" -import useToolMode from "../../hooks/use-tool-mode" +import React, { useState, useRef, useEffect, useCallback } from "react"; +import { styled } from "@material-ui/core/styles"; +import useEventCallback from "use-event-callback"; +import useToolMode from "../../hooks/use-tool-mode"; -const Container = styled("div")({}) +const Container = styled("div")({}); export const MouseTransformHandler = ({ children, @@ -14,83 +14,82 @@ export const MouseTransformHandler = ({ onDragDurationEnd, onCreateTimestamp, }) => { - const mousePosition = useRef({ x: 0, y: 0 }) - const [dragStartTime, setDragStartTime] = useState(null) - const [primaryDrag, setPrimaryDrag] = useState(false) - const [shiftKeyDown, setShiftKeyDown] = useState(false) - const [middleMouseDown, setMiddleMouseDown] = useState(false) - const [toolMode] = useToolMode() - const containerRef = useRef() - + const mousePosition = useRef({ x: 0, y: 0 }); + const [dragStartTime, setDragStartTime] = useState(null); + const [primaryDrag, setPrimaryDrag] = useState(false); + const [shiftKeyDown, setShiftKeyDown] = useState(false); + const [middleMouseDown, setMiddleMouseDown] = useState(false); + const [toolMode] = useToolMode(); + const containerRef = useRef(); useEffect(() => { const onKeyDown = (e) => { if (e.key === "Shift") { - setShiftKeyDown(true) + setShiftKeyDown(true); } - } + }; const onKeyUp = (e) => { if (e.key === "Shift") { - setShiftKeyDown(false) + setShiftKeyDown(false); } - } + }; - window.addEventListener("keydown", onKeyDown) - window.addEventListener("keyup", onKeyUp) + window.addEventListener("keydown", onKeyDown); + window.addEventListener("keyup", onKeyUp); return () => { - window.removeEventListener("keydown", onKeyDown) - window.removeEventListener("keyup", onKeyUp) - } - }, []) + window.removeEventListener("keydown", onKeyDown); + window.removeEventListener("keyup", onKeyUp); + }; + }, []); const projectMouse = useEventCallback((e) => { - const { clientX, clientY } = e + const { clientX, clientY } = e; - const rect = containerRef.current.getBoundingClientRect() + const rect = containerRef.current.getBoundingClientRect(); - const xRelToElm = clientX - rect.x - const yRelToElm = clientY - rect.y + const xRelToElm = clientX - rect.x; + const yRelToElm = clientY - rect.y; - return matrix.inverse().applyToPoint(xRelToElm, yRelToElm) - }) + return matrix.inverse().applyToPoint(xRelToElm, yRelToElm); + }); const onWheel = useEventCallback((e) => { if (e && e.preventDefault) { - e.preventDefault() + e.preventDefault(); } - const { deltaY } = e - const scroll = -Math.sign(deltaY) / 10 - const { px, py } = mousePosition.current + const { deltaY } = e; + const scroll = -Math.sign(deltaY) / 10; + const { px, py } = mousePosition.current; onChangeMatrix( matrix .translate(px, py) .scale(1 + (shiftKeyDown ? 0 : scroll), 1 + (shiftKeyDown ? scroll : 0)) .translate(-px, -py) - ) - }) + ); + }); const onMouseMove = useEventCallback((e) => { - const { clientX, clientY } = e + const { clientX, clientY } = e; - const projectedMouse = projectMouse(e) + const projectedMouse = projectMouse(e); if (middleMouseDown) { const delta = { x: clientX - mousePosition.current.x, y: clientY - mousePosition.current.y, - } + }; const scaleFac = { x: matrix.get("a"), y: matrix.get("d"), - } + }; onChangeMatrix( matrix.translate(delta.x / scaleFac.x, delta.y / scaleFac.y) - ) + ); } if (primaryDrag) { - onDragDuration(dragStartTime, projectedMouse.x) + onDragDuration(dragStartTime, projectedMouse.x); } mousePosition.current = { @@ -98,61 +97,63 @@ export const MouseTransformHandler = ({ y: clientY, px: projectedMouse.x, py: projectedMouse.y, - } - }) + }; + }); const onMouseDown = useEventCallback((e) => { - if (toolMode === "zoom" && e.button !== 1) return - const { clientX, clientY, button } = e - const projectedMouse = projectMouse(e) + if (toolMode === "zoom" && e.button !== 1) return; + const { clientX, clientY, button } = e; + const projectedMouse = projectMouse(e); mousePosition.current = { x: clientX, y: clientY, px: projectedMouse.x, py: projectedMouse.y, - } - setDragStartTime(projectedMouse.x) + }; + setDragStartTime(projectedMouse.x); if (toolMode === "pan" || button === 1 || e.button === 2) { - setMiddleMouseDown(true) - e.preventDefault() + setMiddleMouseDown(true); + e.preventDefault(); } else if (button === 0) { - onDragDurationStart(projectMouse.x) - setPrimaryDrag(true) + onDragDurationStart(projectMouse.x); + setPrimaryDrag(true); } - }) + }); const onMouseUp = useEventCallback((e) => { if (toolMode === "zoom" && e.button !== 1) { if (e.button === 2) { - onWheel({ ...e, deltaY: 100 }) + onWheel({ ...e, deltaY: 100 }); } else if (e.button === 0) { - onWheel({ ...e, deltaY: -100 }) + onWheel({ ...e, deltaY: -100 }); } - return + return; } - const projectedMouse = projectMouse(e) + const projectedMouse = projectMouse(e); if (toolMode === "pan" || e.button === 1 || e.button === 2) { - setMiddleMouseDown(false) + setMiddleMouseDown(false); } else if (e.button === 0) { if (Math.abs(dragStartTime - projectedMouse.x) === 0) { - onCreateTimestamp(projectedMouse.x) + onCreateTimestamp(projectedMouse.x); } - setPrimaryDrag(false) - onDragDurationEnd() + setPrimaryDrag(false); + onDragDurationEnd(); } - }) + }); const onContextMenu = useEventCallback((e) => { - e.preventDefault() - }) + e.preventDefault(); + }); const containerMountCallback = useCallback((ref) => { if (ref === null) { - containerRef.current.removeEventListener("wheel", onWheel) + containerRef.current.removeEventListener("wheel", onWheel); + containerRef.current = ref; + } else { + containerRef.current = ref; + ref.addEventListener("wheel", onWheel, { passive: false }); } - containerRef.current = ref - ref.addEventListener("wheel", onWheel, { passive: false }) - }, []) + }, []); return ( {children} - ) -} + ); +}; -export default MouseTransformHandler +export default MouseTransformHandler; diff --git a/src/components/MouseTransformHandler/index.stories.js b/src/components/MouseTransformHandler/index.stories.js index e01f62c..51df87c 100644 --- a/src/components/MouseTransformHandler/index.stories.js +++ b/src/components/MouseTransformHandler/index.stories.js @@ -1,11 +1,11 @@ -import React from "react" +import React from "react"; -import MouseTransformHandler from "./" +import MouseTransformHandler from "./"; export default { title: "MouseTransformHandler", component: MouseTransformHandler, argTypes: {}, -} +}; -export const Primary = () => +export const Primary = () => ; diff --git a/src/components/ReactTimeSeries/ReactTimeSeries.stories.js b/src/components/ReactTimeSeries/ReactTimeSeries.stories.js index 5634d66..d50d2c7 100644 --- a/src/components/ReactTimeSeries/ReactTimeSeries.stories.js +++ b/src/components/ReactTimeSeries/ReactTimeSeries.stories.js @@ -1,10 +1,10 @@ -import React, { useState } from "react" -import ReactTimeSeries from "./" +import React, { useState } from "react"; +import ReactTimeSeries from "./"; export default { title: "ReactTimeSeries", component: ReactTimeSeries, -} +}; export const SimpleTimeSeries = () => { const [sample, setSample] = useState({ @@ -21,29 +21,29 @@ export const SimpleTimeSeries = () => { { start: 800, end: 1000, label: "byeeee" }, ], }, - }) + }); return ( - ) -} + ); +}; export const WithAudioURL = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }) + }); return ( - ) -} + ); +}; export const ReallySimple = () => { return ( @@ -58,8 +58,8 @@ export const ReallySimple = () => { }} onModifySample={() => null} /> - ) -} + ); +}; export const SmallValues = () => { return ( @@ -77,14 +77,14 @@ export const SmallValues = () => { }} onModifySample={() => null} /> - ) -} + ); +}; export const PredefinedLabelsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }) + }); return ( { sample={sample} onModifySample={setSample} /> - ) -} + ); +}; export const CreateTimestampsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }) + }); return ( { sample={sample} onModifySample={setSample} /> - ) -} + ); +}; export const CreateDurationsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }) + }); return ( { sample={sample} onModifySample={setSample} /> - ) -} + ); +}; export const TimeDataFromCSV = () => { const [sample, setSample] = useState({ csvUrl: "https://gist.githubusercontent.com/philaturner/644b3e8bd17641766d90f38d475edcc6/raw/a6c35e97a89eda49098def90fe2e750ceb898f79/tesla.csv", - }) + }); return ( { sample={sample} onModifySample={setSample} /> - ) -} + ); +}; export const LargeTimeNoneFormat = () => { return ( @@ -170,8 +170,8 @@ export const LargeTimeNoneFormat = () => { }} onModifySample={() => null} /> - ) -} + ); +}; export const AudioPlayback = () => { return ( @@ -186,5 +186,5 @@ export const AudioPlayback = () => { }} onModifySample={() => null} /> - ) -} + ); +}; diff --git a/src/components/ReactTimeSeries/index.js b/src/components/ReactTimeSeries/index.js index d7fdac6..e69c40a 100644 --- a/src/components/ReactTimeSeries/index.js +++ b/src/components/ReactTimeSeries/index.js @@ -1,33 +1,33 @@ -import React, { useMemo, useState, useEffect, useRef } from "react" -import { setIn } from "seamless-immutable" -import useEventCallback from "use-event-callback" -import { useAsyncMemo } from "use-async-memo" -import { RecoilRoot } from "recoil" -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" -import Measure from "react-measure" -import { useSetTimeCursorTime } from "../../hooks/use-time-cursor-time" -import useRootAudioElm from "../../hooks/use-root-audio-elm" +import React, { useMemo, useState, useEffect, useRef } from "react"; +import { setIn } from "seamless-immutable"; +import useEventCallback from "use-event-callback"; +import { useAsyncMemo } from "use-async-memo"; +import { RecoilRoot } from "recoil"; +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; +import Measure from "react-measure"; +import { useSetTimeCursorTime } from "../../hooks/use-time-cursor-time"; +import useRootAudioElm from "../../hooks/use-root-audio-elm"; -import MainLayout from "../MainLayout" +import MainLayout from "../MainLayout"; -import fetchAudioData from "../../utils/fetch-audio-data" -import fetchCSVData from "../../utils/fetch-csv-data" +import fetchAudioData from "../../utils/fetch-audio-data"; +import fetchCSVData from "../../utils/fetch-csv-data"; -import fixTimeData from "../../utils/fix-time-data" +import fixTimeData from "../../utils/fix-time-data"; -const emptyAr = [] +const emptyAr = []; // This is a cloudflare CORs proxy from the project maintainer @seveibar const defaultCorsProxy = - "https://corsproxy.seve.workers.dev/corsproxy/?apiurl={URL}" + "https://corsproxy.seve.workers.dev/corsproxy/?apiurl={URL}"; const defaultEnabledTools = [ "create-durations", "create-timestamps", "label-durations", "label-timestamps", -] -const defaultGraphs = [{ keyName: "value" }] +]; +const defaultGraphs = [{ keyName: "value" }]; export const ReactTimeSeriesWithoutContext = ({ // eslint-disable-next-line @@ -37,9 +37,9 @@ export const ReactTimeSeriesWithoutContext = ({ width: widthProp, onModifySample, }) => { - if (!iface) throw new Error(`"interface" is a required prop`) - if (!sample) throw new Error(`"sample" is a required prop`) - const getRandomColorUsingHash = useGetRandomColorUsingHash() + if (!iface) throw new Error(`"interface" is a required prop`); + if (!sample) throw new Error(`"sample" is a required prop`); + const getRandomColorUsingHash = useGetRandomColorUsingHash(); let { timeFormat, enabledTools = defaultEnabledTools, @@ -48,76 +48,77 @@ export const ReactTimeSeriesWithoutContext = ({ graphs = defaultGraphs, allowCustomLabels, showValues, - } = iface - let { timeData: sampleTimeData, audioUrl, csvUrl, annotation } = sample - if (showValues === undefined && !audioUrl) showValues = true + } = iface; + let { timeData: sampleTimeData, audioUrl, csvUrl, annotation } = sample; + if (showValues === undefined && !audioUrl) showValues = true; - const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean) + const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean); - const [isPlayingMedia, setIsPlayingMedia] = useState(false) - const [error, setError] = useState(null) + const [isPlayingMedia, setIsPlayingMedia] = useState(false); + const [error, setError] = useState(null); const timeData = useAsyncMemo( async () => { try { - if (sampleTimeData) return fixTimeData(sampleTimeData, graphs) - if (audioUrl) return fixTimeData(await fetchAudioData(audioUrl), graphs) - if (csvUrl) return fixTimeData(await fetchCSVData(csvUrl), graphs) + if (sampleTimeData) return fixTimeData(sampleTimeData, graphs); + if (audioUrl) + return fixTimeData(await fetchAudioData(audioUrl), graphs); + if (csvUrl) return fixTimeData(await fetchCSVData(csvUrl), graphs); throw new Error( `No timeData, no audioUrl, no csvUrl, no time data provided.` - ) + ); } catch (e) { - setError(e) - return [] + setError(e); + return []; } }, [sampleTimeData, audioUrl, csvUrl], null - ) + ); - const timeDataLoading = !timeData && timeDataAvailable + const timeDataLoading = !timeData && timeDataAvailable; const curveGroups = useMemo(() => { - if (!timeData) return [] - const anonRows = [] - const namedRows = {} + if (!timeData) return []; + const anonRows = []; + const namedRows = {}; for (const graph of graphs) { const curveData = timeData .filter( (a) => a[graph.keyName] !== undefined && a[graph.keyName] !== null ) - .map((a) => [a.time, a[graph.keyName]]) + .map((a) => [a.time, a[graph.keyName]]); if (graph.row === undefined || graph.row === null) { const curveGroup = [ { data: curveData, color: graph.color || getRandomColorUsingHash(graph.keyName), }, - ] - anonRows.push(curveGroup) + ]; + anonRows.push(curveGroup); } else { if (!namedRows[graph.row]) { - namedRows[graph.row] = [] + namedRows[graph.row] = []; } namedRows[graph.row].push({ data: curveData, color: graph.color || getRandomColorUsingHash(graph.keyName), - }) + }); } } return anonRows.concat( Object.entries(namedRows).sort((a, b) => a[0].localeCompare(b[0])) - ) - }, [timeData, graphs, getRandomColorUsingHash]) + ); + }, [timeData, graphs, getRandomColorUsingHash]); const timestamps = useMemo(() => { - if (!annotation?.timestamps) return [] + if (!annotation?.timestamps) return []; return annotation?.timestamps.map((ts) => ({ time: ts.time, label: ts.label, color: ts.color || getRandomColorUsingHash(ts.label), - })) + })); // eslint-disable-next-line - }, [annotation?.timestamps, getRandomColorUsingHash]) + }, [annotation?.timestamps, getRandomColorUsingHash]); const durationGroups = useMemo(() => { if (!annotation?.durations) @@ -129,14 +130,14 @@ export const ReactTimeSeriesWithoutContext = ({ misc: true, durations: [], }, - ] + ]; const availableLabels = Array.from( new Set( annotation.durations.flatMap((d) => [d.label, d.layer]).filter(Boolean) ) - ) - availableLabels.sort() + ); + availableLabels.sort(); // TODO no more than 5 layers, after 5 layers start reusing layers @@ -152,9 +153,9 @@ export const ReactTimeSeriesWithoutContext = ({ end: d.end, label: d.label, })), - } + }; }) - .filter((dg) => dg.durations.length > 0) + .filter((dg) => dg.durations.length > 0); if (enabledTools.includes("create-durations")) { durationGroups.push({ @@ -165,16 +166,16 @@ export const ReactTimeSeriesWithoutContext = ({ .flatMap((dg) => dg.durations) .concat(annotation.durations.filter((d) => !d.label)) .map((d) => ({ ...d, color: getRandomColorUsingHash(d.label) })), - }) + }); } durationGroups = durationGroups.filter( (dg) => dg.misc || dg.durations.length > 1 - ) + ); - return durationGroups + return durationGroups; // eslint-disable-next-line - }, [annotation?.durations]) + }, [annotation?.durations]); const onChangeDurationGroups = useEventCallback((newDurationGroups) => { onModifySample( @@ -188,67 +189,67 @@ export const ReactTimeSeriesWithoutContext = ({ })) ) ) - ) - }) + ); + }); const onChangeTimestamps = useEventCallback((newTimestamps) => { - onModifySample(setIn(sample, ["annotation", "timestamps"], newTimestamps)) - }) - const [width, setWidth] = useState(widthProp) + onModifySample(setIn(sample, ["annotation", "timestamps"], newTimestamps)); + }); + const [width, setWidth] = useState(widthProp); const onResize = useEventCallback(({ bounds }) => { - if (!widthProp) setWidth(bounds.width) - }) + if (!widthProp) setWidth(bounds.width); + }); - const audioSource = useRef() + const audioSource = useRef(); - const [, setRootAudioElm] = useRootAudioElm() - const setTimeCursorTime = useSetTimeCursorTime() + const [, setRootAudioElm] = useRootAudioElm(); + const setTimeCursorTime = useSetTimeCursorTime(); useEffect(() => { if (audioUrl) { - audioSource.current = new Audio(audioUrl) - setTimeCursorTime(0) + audioSource.current = new Audio(audioUrl); + setTimeCursorTime(0); } - }, []) + }, []); const onAudioTimeChanged = useEventCallback(() => { - setTimeCursorTime(audioSource.current.currentTime * 1000) - }) + setTimeCursorTime(audioSource.current.currentTime * 1000); + }); const onAudioPaused = useEventCallback(() => { - audioSource.current.removeEventListener("timeupdate", onAudioTimeChanged) - audioSource.current.removeEventListener("pause", onAudioPaused) - }) + audioSource.current.removeEventListener("timeupdate", onAudioTimeChanged); + audioSource.current.removeEventListener("pause", onAudioPaused); + }); const onStartPlayback = useEventCallback(() => { - setIsPlayingMedia(true) + setIsPlayingMedia(true); if (audioSource.current) { - audioSource.current.play() - audioSource.current.addEventListener("timeupdate", onAudioTimeChanged) - audioSource.current.addEventListener("pause", onAudioPaused) - setRootAudioElm(audioSource.current) + audioSource.current.play(); + audioSource.current.addEventListener("timeupdate", onAudioTimeChanged); + audioSource.current.addEventListener("pause", onAudioPaused); + setRootAudioElm(audioSource.current); } - }) + }); const onStopPlayback = useEventCallback(() => { - setIsPlayingMedia(false) + setIsPlayingMedia(false); if (audioSource.current) { - audioSource.current.pause() + audioSource.current.pause(); } - }) + }); - if (timeDataLoading) return "loading" // TODO real loader + if (timeDataLoading) return "loading"; // TODO real loader if (!timeData) { throw new Error( `No time data provided. Try sample={{timeData: [{time: 0, value: 1}, ...]}} or sample={{audioUrl:"https://..."}}` - ) + ); } if (curveGroups.length === 0) { - throw new Error(`For some reason, no curves are able to be displayed.`) + throw new Error(`For some reason, no curves are able to be displayed.`); } if (error) { - throw error + throw error; } return ( @@ -275,15 +276,15 @@ export const ReactTimeSeriesWithoutContext = ({ )} - ) -} + ); +}; export const ReactTimeSeries = (props) => { return ( - ) -} + ); +}; -export default ReactTimeSeries +export default ReactTimeSeries; diff --git a/src/components/TimeStamp/index.js b/src/components/TimeStamp/index.js index 3f43d1e..41993eb 100644 --- a/src/components/TimeStamp/index.js +++ b/src/components/TimeStamp/index.js @@ -1,9 +1,9 @@ -import React from "react" -import { styled } from "@material-ui/core/styles" -import LocationOnIcon from "@material-ui/icons/LocationOn" -import Color from "color" -import useEventCallback from "use-event-callback" -import useToolMode from "../../hooks/use-tool-mode" +import React from "react"; +import { styled } from "@material-ui/core/styles"; +import LocationOnIcon from "@material-ui/icons/LocationOn"; +import Color from "color"; +import useEventCallback from "use-event-callback"; +import useToolMode from "../../hooks/use-tool-mode"; export const Container = styled("div")(({ left, color, hasIcon }) => ({ position: "absolute", @@ -49,18 +49,18 @@ export const Container = styled("div")(({ left, color, hasIcon }) => ({ height: 2, backgroundColor: color, }, -})) +})); export const TimeStamp = ({ left, color, label, onClick, onRemove }) => { - const [toolMode] = useToolMode() + const [toolMode] = useToolMode(); const onMouseUp = useEventCallback((e) => { if (toolMode === "delete" || e.button === 2 || e.button === 1) { - onRemove() + onRemove(); } - }) + }); const onContextMenu = useEventCallback((e) => { - e.preventDefault() - }) + e.preventDefault(); + }); return ( {
{label ? {label} : } - ) -} + ); +}; -export default TimeStamp +export default TimeStamp; diff --git a/src/components/Timeline/Timeline.stories.js b/src/components/Timeline/Timeline.stories.js index cae4eea..dc6b759 100644 --- a/src/components/Timeline/Timeline.stories.js +++ b/src/components/Timeline/Timeline.stories.js @@ -1,9 +1,9 @@ -import React from "react" +import React from "react"; -import useColors from "../../hooks/use-colors" -import Timeline from "./" -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" -import Matrix from "immutable-transform-matrix" +import useColors from "../../hooks/use-colors"; +import Timeline from "./"; +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; +import Matrix from "immutable-transform-matrix"; export default { title: "Timeline", @@ -12,7 +12,7 @@ export default { width: "number", onClickTimestamp: { action: "onClickTimestamp" }, }, -} +}; export const TimeWithColons = (args) => { return ( @@ -24,8 +24,8 @@ export const TimeWithColons = (args) => { visibleTimeEnd={60000 * 80} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ) -} + ); +}; export const Dates = (args) => { return ( @@ -37,11 +37,11 @@ export const Dates = (args) => { visibleTimeEnd={60000 * 60 * 24 * 400} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ) -} + ); +}; export const TimeWithTimestamps = (args) => { - const colors = useColors() + const colors = useColors(); return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ) -} + ); +}; export const TimeWithTextMarkers = (args) => { - const colors = useColors() + const colors = useColors(); return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ) -} + ); +}; export const TimeWithCurrentTimeCursor = (args) => { - const colors = useColors() + const colors = useColors(); return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ) -} + ); +}; diff --git a/src/components/Timeline/index.js b/src/components/Timeline/index.js index a03fa9c..1077b00 100644 --- a/src/components/Timeline/index.js +++ b/src/components/Timeline/index.js @@ -1,26 +1,26 @@ -import React, { useRef } from "react" -import range from "lodash/range" -import { styled } from "@material-ui/core/styles" -import useColors from "../../hooks/use-colors" -import TimeStamp from "../TimeStamp" +import React, { useRef } from "react"; +import range from "lodash/range"; +import { styled } from "@material-ui/core/styles"; +import useColors from "../../hooks/use-colors"; +import TimeStamp from "../TimeStamp"; import { useTimeCursorTime, useSetTimeCursorTime, -} from "../../hooks/use-time-cursor-time" -import useRootAudioElm from "../../hooks/use-root-audio-elm" -import useEventCallback from "use-event-callback" +} from "../../hooks/use-time-cursor-time"; +import useRootAudioElm from "../../hooks/use-root-audio-elm"; +import useEventCallback from "use-event-callback"; -import { formatTime } from "../../utils/format-time" +import { formatTime } from "../../utils/format-time"; -const Container = styled("div")(({ width, themeColors }) => ({ +const Container = styled("div")(({ width, themecolors }) => ({ width, overflow: "hidden", position: "relative", height: 64, cursor: "pointer", - borderBottom: `1px solid ${themeColors.Selection}`, - color: themeColors.fg, -})) + borderBottom: `1px solid ${themecolors.Selection}`, + color: themecolors.fg, +})); const TimeText = styled("div")(({ x, faded }) => ({ display: "inline-block", @@ -34,9 +34,9 @@ const TimeText = styled("div")(({ x, faded }) => ({ paddingLeft: 4, whiteSpace: "pre-wrap", opacity: faded ? 0.25 : 0.75, -})) +})); -const TimeCursor = styled("div")(({ left, themeColors }) => ({ +const TimeCursor = styled("div")(({ left, themecolors }) => ({ position: "absolute", width: 0, height: 0, @@ -44,14 +44,14 @@ const TimeCursor = styled("div")(({ left, themeColors }) => ({ left: left - 6, borderLeft: "8px solid transparent", borderRight: "8px solid transparent", - borderTop: `12px solid ${themeColors.green}`, -})) + borderTop: `12px solid ${themecolors.green}`, +})); const Svg = styled("svg")({ position: "absolute", left: 0, bottom: 0, -}) +}); export const Timeline = ({ timeFormat, @@ -64,43 +64,45 @@ export const Timeline = ({ onRemoveTimestamp, timeCursorTime: timeCursorTimeProp, }) => { - const themeColors = useColors() - const visibleDuration = visibleTimeEnd - visibleTimeStart + const themecolors = useColors(); + const visibleDuration = visibleTimeEnd - visibleTimeStart; // TODO compute tick count using width - const timeTextCount = Math.ceil(width / 100) + const timeTextCount = Math.ceil(width / 100); const timeTextTimes = range(timeTextCount).map( (i) => visibleTimeStart + (visibleDuration / timeTextCount) * i - ) - const recoilTimeCursorTime = useTimeCursorTime() - const setTimeCursorTime = useSetTimeCursorTime() - const [rootAudioElm] = useRootAudioElm() + ); + const recoilTimeCursorTime = useTimeCursorTime(); + const setTimeCursorTime = useSetTimeCursorTime(); + const [rootAudioElm] = useRootAudioElm(); const timeCursorTime = - timeCursorTimeProp === undefined ? recoilTimeCursorTime : timeCursorTimeProp + timeCursorTimeProp === undefined + ? recoilTimeCursorTime + : timeCursorTimeProp; const { numberOfMajorGridLines, majorGridLinePixelOffset, majorGridLinePixelDistance, - } = gridLineMetrics + } = gridLineMetrics; - const containerRef = useRef() + const containerRef = useRef(); const onClickTimeline = useEventCallback((e) => { - if (!rootAudioElm) return - const { clientX } = e + if (!rootAudioElm) return; + const { clientX } = e; const pxDistanceFromStart = - clientX - containerRef.current.getBoundingClientRect().left + clientX - containerRef.current.getBoundingClientRect().left; const time = (pxDistanceFromStart / width) * (visibleTimeEnd - visibleTimeStart) + - visibleTimeStart - rootAudioElm.currentTime = time / 1000 - setTimeCursorTime(time) - }) + visibleTimeStart; + rootAudioElm.currentTime = time / 1000; + setTimeCursorTime(time); + }); return ( @@ -108,7 +110,7 @@ export const Timeline = ({ {formatTime( timeTextTimes[timeTextIndex], @@ -120,7 +122,7 @@ export const Timeline = ({ {range(numberOfMajorGridLines).map((tickIndex) => { const x = - majorGridLinePixelOffset + majorGridLinePixelDistance * tickIndex + majorGridLinePixelOffset + majorGridLinePixelDistance * tickIndex; return ( - ) + ); })} {timestamps.map((timestamp, i) => { const left = - ((timestamp.time - visibleTimeStart) / visibleDuration) * width + ((timestamp.time - visibleTimeStart) / visibleDuration) * width; return ( onClickTimestamp(timestamp, i)} onRemove={() => onRemoveTimestamp(timestamp, i)} /> - ) + ); })} {timeCursorTime !== undefined && ( )} - ) -} + ); +}; -export default Timeline +export default Timeline; diff --git a/src/components/Toolbar/Toolbar.stories.js b/src/components/Toolbar/Toolbar.stories.js index b5ceeb9..b7ddcbe 100644 --- a/src/components/Toolbar/Toolbar.stories.js +++ b/src/components/Toolbar/Toolbar.stories.js @@ -1,16 +1,16 @@ -import React, { useState } from "react" -import Toolbar from "./" -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" -import { solarized } from "../../hooks/use-colors" +import React, { useState } from "react"; +import Toolbar from "./"; +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; +import { solarized } from "../../hooks/use-colors"; export default { title: "Toolbar", component: Toolbar, -} +}; export const Primary = () => { - const selectedTimestampIndex = 0 - const getRandomColorUsingHash = useGetRandomColorUsingHash() + const selectedTimestampIndex = 0; + const getRandomColorUsingHash = useGetRandomColorUsingHash(); const [timestamps, setTimestamps] = useState([ { color: solarized.red, @@ -22,7 +22,7 @@ export const Primary = () => { time: 200, label: "mouse", }, - ]) + ]); return ( { color: color ? color : getRandomColorUsingHash(label), } ) - ) + ); }} onStartPlayback={() => null} onStopPlayback={() => null} isPlayingMedia={false} /> - ) -} + ); +}; diff --git a/src/components/Toolbar/index.js b/src/components/Toolbar/index.js index 9a3f10d..74e667c 100644 --- a/src/components/Toolbar/index.js +++ b/src/components/Toolbar/index.js @@ -1,92 +1,92 @@ -import React, { useMemo } from "react" -import { styled } from "@material-ui/core/styles" -import ButtonGroup from "@material-ui/core/ButtonGroup" -import Button from "@material-ui/core/Button" -import useColors from "../../hooks/use-colors" -import CloseIcon from "@material-ui/icons/Close" -import Brightness4Icon from "@material-ui/icons/Brightness4" -import PanToolIcon from "@material-ui/icons/PanTool" -import Box from "@material-ui/core/Box" -import CreateIcon from "@material-ui/icons/Create" -import classnames from "classnames" -import useToolMode from "../../hooks/use-tool-mode" -import useEventCallback from "use-event-callback" -import { useSetRecoilState } from "recoil" -import { themeAtom } from "../../hooks/use-colors" -import CreatableSelect from "react-select/creatable" -import NormalSelect from "react-select" -import LocationOnIcon from "@material-ui/icons/LocationOn" -import TimelapseIcon from "@material-ui/icons/Timelapse" -import ZoomInIcon from "@material-ui/icons/ZoomIn" -import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline" -import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline" -import Color from "color" +import React, { useMemo } from "react"; +import { styled } from "@material-ui/core/styles"; +import ButtonGroup from "@material-ui/core/ButtonGroup"; +import Button from "@material-ui/core/Button"; +import useColors from "../../hooks/use-colors"; +import CloseIcon from "@material-ui/icons/Close"; +import Brightness4Icon from "@material-ui/icons/Brightness4"; +import PanToolIcon from "@material-ui/icons/PanTool"; +import Box from "@material-ui/core/Box"; +import CreateIcon from "@material-ui/icons/Create"; +import classnames from "classnames"; +import useToolMode from "../../hooks/use-tool-mode"; +import useEventCallback from "use-event-callback"; +import { useSetRecoilState } from "recoil"; +import { themeAtom } from "../../hooks/use-colors"; +import CreatableSelect from "react-select/creatable"; +import NormalSelect from "react-select"; +import LocationOnIcon from "@material-ui/icons/LocationOn"; +import TimelapseIcon from "@material-ui/icons/Timelapse"; +import ZoomInIcon from "@material-ui/icons/ZoomIn"; +import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline"; +import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline"; +import Color from "color"; -const Container = styled("div")(({ themeColors }) => ({ +const Container = styled("div")(({ themecolors }) => ({ display: "flex", paddingBottom: 16, "&&& .MuiButtonBase-root": { - borderColor: themeColors.fg, + borderColor: themecolors.fg, }, "&&& .MuiButton-label": { - color: themeColors.fg, + color: themecolors.fg, textTransform: "none", }, "&&& .active.MuiButtonBase-root": { - backgroundColor: themeColors.fg, + backgroundColor: themecolors.fg, }, "&&& .active .MuiButton-label": { - color: themeColors.bg, + color: themecolors.bg, }, "&&& .MuiSvgIcon-root": { width: 16, height: 16, }, -})) +})); -const getSelectFieldStyles = (themeColors) => ({ +const getSelectFieldStyles = (themecolors) => ({ control: (styles) => ({ ...styles, border: `1px solid ${ - themeColors.dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)" + themecolors.dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)" }`, boxShadow: "none", - backgroundColor: themeColors.bg, - borderColor: themeColors.base1, + backgroundColor: themecolors.bg, + borderColor: themecolors.base1, userSelect: "none", "&:hover": { - backgroundColor: Color(themeColors.bg).darken(0.2).string(), + backgroundColor: Color(themecolors.bg).darken(0.2).string(), }, }), input: (styles) => ({ ...styles, - color: themeColors.fg, + color: themecolors.fg, }), option: (styles) => ({ ...styles, - backgroundColor: themeColors.base02, - color: themeColors.fg, + backgroundColor: themecolors.base02, + color: themecolors.fg, "&:hover": { - backgroundColor: Color(themeColors.bg).darken(0.2).string(), + backgroundColor: Color(themecolors.bg).darken(0.2).string(), }, "&:focus": { - backgroundColor: Color(themeColors.bg).darken(0.2).string(), + backgroundColor: Color(themecolors.bg).darken(0.2).string(), }, }), singleValue: (styles) => ({ ...styles, - color: themeColors.fg, + color: themecolors.fg, }), menu: (styles) => ({ ...styles, - backgroundColor: themeColors.base02, + backgroundColor: themecolors.base02, }), -}) +}); const iconStyle = { width: 20, height: 20, -} +}; export const Toolbar = ({ timestamps = [], @@ -102,90 +102,90 @@ export const Toolbar = ({ onStopPlayback, isPlayingMedia = false, }) => { - const themeColors = useColors() - const [mode, setToolMode] = useToolMode() - const setTheme = useSetRecoilState(themeAtom) + const themecolors = useColors(); + const [mode, setToolMode] = useToolMode(); + const setTheme = useSetRecoilState(themeAtom); const [durationLabelSet, durationLabelColorMap] = useMemo(() => { - const labelSet = new Set(durationLabels) - const labelColorMap = {} + const labelSet = new Set(durationLabels); + const labelColorMap = {}; for (const dg of durationGroups) { for (const duration of dg.durations) { - if (!duration.label) continue - labelSet.add(duration.label) - labelColorMap[duration.label] = duration.color || dg.color + if (!duration.label) continue; + labelSet.add(duration.label); + labelColorMap[duration.label] = duration.color || dg.color; } - if (!dg.label) continue - labelSet.add(dg.label) - labelColorMap[dg.label] = dg.color + if (!dg.label) continue; + labelSet.add(dg.label); + labelColorMap[dg.label] = dg.color; } - return [labelSet, labelColorMap] - }, [timestamps, durationGroups, durationLabels]) + return [labelSet, labelColorMap]; + }, [timestamps, durationGroups, durationLabels]); const [timestampLabelSet, timestampLabelColorMap] = useMemo(() => { - const labelSet = new Set(timestampLabels) - const labelColorMap = {} + const labelSet = new Set(timestampLabels); + const labelColorMap = {}; for (const timestamp of timestamps) { - if (!timestamp.label) continue - labelSet.add(timestamp.label) - labelColorMap[timestamp.label] = timestamp.color + if (!timestamp.label) continue; + labelSet.add(timestamp.label); + labelColorMap[timestamp.label] = timestamp.color; } - return [labelSet, labelColorMap] - }, [timestamps, durationGroups, timestampLabels]) + return [labelSet, labelColorMap]; + }, [timestamps, durationGroups, timestampLabels]); - const onSelectCreateTool = useEventCallback(() => setToolMode("create")) - const onSelectPanTool = useEventCallback(() => setToolMode("pan")) - const onSelectZoomTool = useEventCallback(() => setToolMode("zoom")) - const onSelectCloseTool = useEventCallback(() => setToolMode("delete")) + const onSelectCreateTool = useEventCallback(() => setToolMode("create")); + const onSelectPanTool = useEventCallback(() => setToolMode("pan")); + const onSelectZoomTool = useEventCallback(() => setToolMode("zoom")); + const onSelectCloseTool = useEventCallback(() => setToolMode("delete")); const toggleTheme = useEventCallback(() => - setTheme(themeColors.dark ? "light" : "dark") - ) + setTheme(themecolors.dark ? "light" : "dark") + ); const selectFieldStyles = useMemo( - () => getSelectFieldStyles(themeColors, selectedTimestampIndex), - [themeColors, selectedTimestampIndex] - ) - const formatCreateLabel = useEventCallback((s) => `Add "${s}"`) + () => getSelectFieldStyles(themecolors, selectedTimestampIndex), + [themecolors, selectedTimestampIndex] + ); + const formatCreateLabel = useEventCallback((s) => `Add "${s}"`); const onChangeSelectedLabel = useEventCallback((newValue) => { - const { label } = newValue || {} + const { label } = newValue || {}; onChangeSelectedItemLabel({ label, color: durationLabelColorMap[label] || timestampLabelColorMap[label], - }) - }) + }); + }); const timestampCreatableSelectOptions = useMemo( () => Array.from(timestampLabelSet).map((label) => ({ label, value: label })), [timestampLabelSet] - ) + ); const durationCreatableSelectOptions = useMemo( () => Array.from(durationLabelSet).map((label) => ({ label, value: label })), [durationLabelSet] - ) + ); const selectedTimestamp = typeof selectedTimestampIndex === "number" ? timestamps[selectedTimestampIndex] - : null + : null; const selectedDuration = typeof selectedDurationGroupIndex === "number" && typeof selectedDurationIndex === "number" ? durationGroups?.[selectedDurationGroupIndex]?.durations?.[ selectedDurationIndex ] - : null + : null; const selectedItemValue = useMemo(() => { - const label = selectedTimestamp?.label || selectedDuration?.label - return { label, value: label } - }, [selectedTimestamp, selectedDuration]) + const label = selectedTimestamp?.label || selectedDuration?.label; + return { label, value: label }; + }, [selectedTimestamp, selectedDuration]); - const SelectComponent = allowCustomLabels ? CreatableSelect : NormalSelect + const SelectComponent = allowCustomLabels ? CreatableSelect : NormalSelect; return ( - + ) : selectedDuration ? ( ) : null} @@ -273,7 +273,7 @@ export const Toolbar = ({ - ) -} + ); +}; -export default Toolbar +export default Toolbar; diff --git a/src/components/Wave/index.js b/src/components/Wave/index.js index 962e78c..ceafa04 100644 --- a/src/components/Wave/index.js +++ b/src/components/Wave/index.js @@ -1,59 +1,59 @@ -import React, { Fragment, useMemo } from "react" -import range from "lodash/range" -import { styled } from "@material-ui/core/styles" -import colorAlpha from "color-alpha" -import useColors from "../../hooks/use-colors" -import { formatTime } from "../../utils/format-time" -import { useTimeCursorTime } from "../../hooks/use-time-cursor-time" -import HighlightValueLabels from "../HighlightValueLabels" +import React, { Fragment, useMemo } from "react"; +import range from "lodash/range"; +import { styled } from "@material-ui/core/styles"; +import colorAlpha from "color-alpha"; +import useColors from "../../hooks/use-colors"; +import { formatTime } from "../../utils/format-time"; +import { useTimeCursorTime } from "../../hooks/use-time-cursor-time"; +import HighlightValueLabels from "../HighlightValueLabels"; const userSelectOffStyle = { userSelect: "none", whiteSpace: "pre", fontVariantNumeric: "tabular-nums", -} +}; const reduceForVisibleDuration = (data, startTime, visibleDuration, width) => { - const firstInnerIndex = data.findIndex(([t]) => t >= startTime) + const firstInnerIndex = data.findIndex(([t]) => t >= startTime); let visibleSamples = data .slice(firstInnerIndex) - .findIndex(([t]) => t >= startTime + visibleDuration) + .findIndex(([t]) => t >= startTime + visibleDuration); visibleSamples = - visibleSamples === -1 ? data.length - firstInnerIndex : visibleSamples - const lastInnerIndex = firstInnerIndex + visibleSamples + visibleSamples === -1 ? data.length - firstInnerIndex : visibleSamples; + const lastInnerIndex = firstInnerIndex + visibleSamples; - data = data.slice(Math.max(0, firstInnerIndex - 1), lastInnerIndex + 1) + data = data.slice(Math.max(0, firstInnerIndex - 1), lastInnerIndex + 1); - const minDistance = visibleDuration / (width / 4) - const points = [data[0]] - let lastAddedPointIndex = 0 + const minDistance = visibleDuration / (width / 4); + const points = [data[0]]; + let lastAddedPointIndex = 0; for (let i = 1; i < data.length; i++) { if (data[i][0] - points[points.length - 1][0] > minDistance) { // points.push(data[i]) - const timeSinceLastPoint = data[i][0] - points[points.length - 1][0] + const timeSinceLastPoint = data[i][0] - points[points.length - 1][0]; if (i - lastAddedPointIndex === 1) { - points.push(data[i]) + points.push(data[i]); } else { points.push([ data[i][0] - timeSinceLastPoint / 2, Math.max( ...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v) ), - ]) + ]); points.push([ data[i][0], Math.min( ...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v) ), - ]) + ]); } - lastAddedPointIndex = i + lastAddedPointIndex = i; } } - return points -} + return points; +}; export const Wave = ({ curves, @@ -67,12 +67,12 @@ export const Wave = ({ timeCursorTime, showValues = false, }) => { - const colors = useColors() + const colors = useColors(); - const recoilTimeCursorTime = useTimeCursorTime() + const recoilTimeCursorTime = useTimeCursorTime(); timeCursorTime = - timeCursorTime === undefined ? recoilTimeCursorTime : timeCursorTime + timeCursorTime === undefined ? recoilTimeCursorTime : timeCursorTime; const { visibleDuration, @@ -84,15 +84,15 @@ export const Wave = ({ minorGridLinePixelOffset, majorGridLinePixelDistance, minorGridLinePixelDistance, - } = gridLineMetrics + } = gridLineMetrics; const timeCursorXPosition = timeCursorTime !== undefined ? transformMatrix.applyToPoint(timeCursorTime, 0).x - : undefined + : undefined; const visibleTransformedPointsOnCurves = useMemo(() => { - const visibleTransformedPointsOnCurves = [] + const visibleTransformedPointsOnCurves = []; for (const curve of curves) { visibleTransformedPointsOnCurves.push( reduceForVisibleDuration( @@ -105,29 +105,29 @@ export const Wave = ({ t, value: y, })) - ) + ); } - return visibleTransformedPointsOnCurves - }, [transformMatrix, curves, startTimeOnGraph, visibleDuration, width]) + return visibleTransformedPointsOnCurves; + }, [transformMatrix, curves, startTimeOnGraph, visibleDuration, width]); return ( {range(-5, numberOfMajorGridLines + 1).map((i) => { const timeAtLine = Math.floor(startTimeOnGraph / majorDuration) * majorDuration + - majorDuration * i + majorDuration * i; - const lineX = majorGridLinePixelOffset + majorGridLinePixelDistance * i + const lineX = majorGridLinePixelOffset + majorGridLinePixelDistance * i; - const globalTimelineIndex = Math.floor(timeAtLine / majorDuration) + const globalTimelineIndex = Math.floor(timeAtLine / majorDuration); - let textElm = null + let textElm = null; if (globalTimelineIndex % 1 === 0) { const timeLines = formatTime( timeAtLine, timeFormat, visibleDuration - ).split("\n") + ).split("\n"); textElm = timeLines.map((tl, i) => ( {tl} - )) + )); } return ( @@ -157,12 +157,12 @@ export const Wave = ({ )} {textElm} - ) + ); })} {numberOfMajorGridLines < 12 && range(numberOfMinorGridLines).map((i) => { const lineX = - minorGridLinePixelOffset + minorGridLinePixelDistance * i + minorGridLinePixelOffset + minorGridLinePixelDistance * i; return ( - ) + ); })} {durationGroups.flatMap(({ durations, color: dgColor }, dgi) => { return durations.map((duration, di) => { - const { x: startX } = transformMatrix.applyToPoint(duration.start, 0) - const { x: endX } = transformMatrix.applyToPoint(duration.end, 0) - if (isNaN(startX) || isNaN(endX)) return null + const { x: startX } = transformMatrix.applyToPoint(duration.start, 0); + const { x: endX } = transformMatrix.applyToPoint(duration.end, 0); + if (isNaN(startX) || isNaN(endX)) return null; return ( - ) - }) + ); + }); })} {curves.map((curve, i) => ( ))} {timestamps.map((ts, i) => { - const { x } = transformMatrix.applyToPoint(ts.time, 0) + const { x } = transformMatrix.applyToPoint(ts.time, 0); return ( - ) + ); })} {timeCursorTime !== undefined && ( )} - ) -} + ); +}; -export default Wave +export default Wave; diff --git a/src/components/Wave/index.stories.js b/src/components/Wave/index.stories.js index 639da35..d450941 100644 --- a/src/components/Wave/index.stories.js +++ b/src/components/Wave/index.stories.js @@ -1,28 +1,28 @@ -import React from "react" -import range from "lodash/range" -import Matrix from "immutable-transform-matrix" -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" +import React from "react"; +import range from "lodash/range"; +import Matrix from "immutable-transform-matrix"; +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; -import Wave from "./" +import Wave from "./"; export default { title: "Wave", component: Wave, argTypes: {}, -} +}; const curve1 = { color: "#f00", data: range(500).map((i) => [i, Math.sin(i / 20) * 100]), -} +}; const curve2 = { color: "#00f", data: range(500).map((i) => [i, Math.cos(i / 40) * 100]), -} +}; -const topLevelMatrix = new Matrix().translate(0, 100) -const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, 500) +const topLevelMatrix = new Matrix().translate(0, 100); +const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, 500); export const SingleCurve = () => ( ( gridLineMetrics={gridLineMetrics} transformMatrix={new Matrix().translate(0, 100)} /> -) +); export const DoubleCurve = () => ( ( gridLineMetrics={gridLineMetrics} transformMatrix={new Matrix().translate(0, 100)} /> -) +); export const DoubleCurveWithDuration = () => ( ( }, ]} /> -) +); export const DoubleCurveWithTimestamp = () => ( ( }, ]} /> -) +); export const DoubleCurveWithTimeCursor = () => ( ( gridLineMetrics={gridLineMetrics} timeCursorTime={250} /> -) +); diff --git a/src/hooks/use-colors.js b/src/hooks/use-colors.js index ce9f7dc..c8eef9d 100644 --- a/src/hooks/use-colors.js +++ b/src/hooks/use-colors.js @@ -1,9 +1,9 @@ -import { atom, useRecoilValue } from "recoil" +import { atom, useRecoilValue } from "recoil"; export const themeAtom = atom({ key: "themeAtom", default: "dark", -}) +}); export const solarized = { base03: "#002b36", @@ -23,9 +23,9 @@ export const solarized = { cyan: "#2aa198", green: "#859900", dark: true, -} -solarized.fg = solarized.base3 -solarized.bg = solarized.base03 +}; +solarized.fg = solarized.base3; +solarized.bg = solarized.base03; const invertedSolarized = { ...solarized, @@ -40,7 +40,7 @@ const invertedSolarized = { base2: solarized.base02, base3: solarized.base03, dark: false, -} +}; // Current Line -> base02 const dracula = { @@ -61,9 +61,9 @@ const dracula = { red: "#ff5555", yellow: "#f1fa8c", dark: true, -} -dracula.fg = dracula.base2 -dracula.bg = dracula.base03 +}; +dracula.fg = dracula.base2; +dracula.bg = dracula.base03; // // export const draculaTheme = { // bg: "#282a36", @@ -81,8 +81,8 @@ dracula.bg = dracula.base03 // } export default () => { - const themeName = useRecoilValue(themeAtom) + const themeName = useRecoilValue(themeAtom); // TODO switch between light and dark theme // Dracula - return themeName === "dark" ? dracula : invertedSolarized -} + return themeName === "dark" ? dracula : invertedSolarized; +}; diff --git a/src/hooks/use-get-random-color-using-hash.js b/src/hooks/use-get-random-color-using-hash.js index ed510c3..415e6d0 100644 --- a/src/hooks/use-get-random-color-using-hash.js +++ b/src/hooks/use-get-random-color-using-hash.js @@ -1,5 +1,5 @@ -import useColors from "./use-colors" -import useEventCallback from "use-event-callback" +import useColors from "./use-colors"; +import useEventCallback from "use-event-callback"; const colorsToCycle = [ "red", @@ -10,16 +10,16 @@ const colorsToCycle = [ "blue", "cyan", "green", -] +]; export default () => { - const themeColors = useColors() + const themecolors = useColors(); return useEventCallback((label) => { - if (!label) return themeColors.fg - let hashNumber = 0 + if (!label) return themecolors.fg; + let hashNumber = 0; for (let i = 0; i < label.length; i++) { - hashNumber += label.charCodeAt(i) + hashNumber += label.charCodeAt(i); } - return themeColors[colorsToCycle[hashNumber % colorsToCycle.length]] - }) -} + return themecolors[colorsToCycle[hashNumber % colorsToCycle.length]]; + }); +}; diff --git a/src/hooks/use-global-transform-matrix.js b/src/hooks/use-global-transform-matrix.js index 284b032..a66d045 100644 --- a/src/hooks/use-global-transform-matrix.js +++ b/src/hooks/use-global-transform-matrix.js @@ -1,11 +1,11 @@ -import Matrix from "immutable-transform-matrix" -import { atom, useRecoilState } from "recoil" +import Matrix from "immutable-transform-matrix"; +import { atom, useRecoilState } from "recoil"; const matrix = atom({ key: "globalTransformMatrix", default: new Matrix(), -}) +}); export default () => { - return useRecoilState(matrix) -} + return useRecoilState(matrix); +}; diff --git a/src/hooks/use-root-audio-elm.js b/src/hooks/use-root-audio-elm.js index 60481c9..8d99602 100644 --- a/src/hooks/use-root-audio-elm.js +++ b/src/hooks/use-root-audio-elm.js @@ -1,9 +1,9 @@ -import { atom, useRecoilState } from "recoil" +import { atom, useRecoilState } from "recoil"; export const rootAudioElmAtom = atom({ key: "rootAudioElmAtom", -}) +}); -export const useRootAudioElm = () => useRecoilState(rootAudioElmAtom) +export const useRootAudioElm = () => useRecoilState(rootAudioElmAtom); -export default useRootAudioElm +export default useRootAudioElm; diff --git a/src/hooks/use-time-cursor-time.js b/src/hooks/use-time-cursor-time.js index e5dd31f..d631a5f 100644 --- a/src/hooks/use-time-cursor-time.js +++ b/src/hooks/use-time-cursor-time.js @@ -1,9 +1,9 @@ -import { atom, useSetRecoilState, useRecoilValue } from "recoil" +import { atom, useSetRecoilState, useRecoilValue } from "recoil"; export const timeCursorTimeAtom = atom({ key: "timeCursorTime", -}) +}); -export const useSetTimeCursorTime = () => useSetRecoilState(timeCursorTimeAtom) +export const useSetTimeCursorTime = () => useSetRecoilState(timeCursorTimeAtom); -export const useTimeCursorTime = () => useRecoilValue(timeCursorTimeAtom) +export const useTimeCursorTime = () => useRecoilValue(timeCursorTimeAtom); diff --git a/src/hooks/use-time-range.js b/src/hooks/use-time-range.js index a51146e..d56edaa 100644 --- a/src/hooks/use-time-range.js +++ b/src/hooks/use-time-range.js @@ -1,11 +1,11 @@ export default (matrix, width) => { - const visibleTimeStart = matrix.inverse().applyToPoint(0, 0).x - const visibleTimeEnd = matrix.inverse().applyToPoint(width, 0).x - const visibleDuration = visibleTimeEnd - visibleTimeStart + const visibleTimeStart = matrix.inverse().applyToPoint(0, 0).x; + const visibleTimeEnd = matrix.inverse().applyToPoint(width, 0).x; + const visibleDuration = visibleTimeEnd - visibleTimeStart; return { visibleTimeStart, visibleTimeEnd, visibleDuration, - } -} + }; +}; diff --git a/src/hooks/use-tool-mode.js b/src/hooks/use-tool-mode.js index 5a24685..c7f808c 100644 --- a/src/hooks/use-tool-mode.js +++ b/src/hooks/use-tool-mode.js @@ -1,8 +1,8 @@ -import { atom, useRecoilState } from "recoil" +import { atom, useRecoilState } from "recoil"; export const atomToolMode = atom({ key: "toolMode", default: "create", -}) +}); -export default () => useRecoilState(atomToolMode) +export default () => useRecoilState(atomToolMode); diff --git a/src/index.js b/src/index.js index 6832e78..846ed2c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; ReactDOM.render( , - document.getElementById('root') + document.getElementById("root") ); diff --git a/src/utils/fetch-audio-data.js b/src/utils/fetch-audio-data.js index 9bd2002..c494569 100644 --- a/src/utils/fetch-audio-data.js +++ b/src/utils/fetch-audio-data.js @@ -1,23 +1,26 @@ export default async (audioUrl) => { - const AudioContext = window.AudioContext || window.webkitAudioContext - const audioContext = new AudioContext() + const AudioContext = window.AudioContext || window.webkitAudioContext; + const audioContext = new AudioContext(); const audioBuffer = await fetch(audioUrl) .then((res) => res.arrayBuffer()) - .then((arBuf) => audioContext.decodeAudioData(arBuf)) + .then((arBuf) => audioContext.decodeAudioData(arBuf)); // Convert into timeData - const channel = audioBuffer.getChannelData(0) + const channel = audioBuffer.getChannelData(0); - const blockDuration = 1 - const samplesPerBlock = audioBuffer.sampleRate * (blockDuration / 1000) - const timeData = [] + const blockDuration = 1; + const samplesPerBlock = audioBuffer.sampleRate * (blockDuration / 1000); + const timeData = []; for (let i = 0; i < channel.length; i += samplesPerBlock) { const avg = channel.slice(i, i + samplesPerBlock).reduce((acc, a) => acc + a, 0) / - samplesPerBlock + samplesPerBlock; - timeData.push({ time: i * (1 / audioBuffer.sampleRate) * 1000, value: avg }) + timeData.push({ + time: i * (1 / audioBuffer.sampleRate) * 1000, + value: avg, + }); } - return timeData -} + return timeData; +}; diff --git a/src/utils/fetch-csv-data.js b/src/utils/fetch-csv-data.js index 6311ac5..25c8730 100644 --- a/src/utils/fetch-csv-data.js +++ b/src/utils/fetch-csv-data.js @@ -1,31 +1,31 @@ -import Papa from "papaparse" +import Papa from "papaparse"; -const possibleTimeHeaders = ["time", "date", "t"] +const possibleTimeHeaders = ["time", "date", "t"]; export default async (csvUrl) => { - const csvText = await fetch(csvUrl).then((res) => res.text()) + const csvText = await fetch(csvUrl).then((res) => res.text()); - const parseResult = Papa.parse(csvText, { header: true }) + const parseResult = Papa.parse(csvText, { header: true }); - const header = parseResult.meta.fields + const header = parseResult.meta.fields; if (!possibleTimeHeaders.some((columnName) => header.includes(columnName))) { throw new Error( `No time fields in header. Acceptable fields: ${possibleTimeHeaders.join( "," )}` - ) + ); } if (!header.includes("time")) { for (const row of parseResult.data) { - row.time = row.date || row.t + row.time = row.date || row.t; } } for (const row of parseResult.data) { - row.time = new Date(row.time).valueOf() + row.time = new Date(row.time).valueOf(); } - return parseResult.data -} + return parseResult.data; +}; diff --git a/src/utils/fix-time-data.js b/src/utils/fix-time-data.js index 20ca6b0..68818c9 100644 --- a/src/utils/fix-time-data.js +++ b/src/utils/fix-time-data.js @@ -2,42 +2,42 @@ export default (timeData, graphs) => { // Make sure that all points have time, and time is always a number for (const sample of timeData) { if (sample.time === undefined || sample.time === null) - throw new Error(`Undefined time for sample\n\n${JSON.stringify(sample)}`) + throw new Error(`Undefined time for sample\n\n${JSON.stringify(sample)}`); if (typeof sample.time === "string") { - const ogTime = sample.time + const ogTime = sample.time; if (isNaN(ogTime)) { - sample.time = new Date(sample.time).valueOf() + sample.time = new Date(sample.time).valueOf(); } else { - sample.time = parseFloat(sample.time) + sample.time = parseFloat(sample.time); } if (isNaN(sample.time)) { - throw new Error(`Couldn't parse time "${sample.time}"`) + throw new Error(`Couldn't parse time "${sample.time}"`); } } - if (isNaN(sample.time)) throw new Error("Time must be a number") + if (isNaN(sample.time)) throw new Error("Time must be a number"); } // Make sure that timeData has a value in each datapoint for // each graph (or explicitly doesn't define it w/ undefined) // No NaNs allowed for (const graph of graphs) { - let pointsDefined = 0 + let pointsDefined = 0; for (const sample of timeData) { - const v = sample[graph.keyName] + const v = sample[graph.keyName]; if (v !== undefined && v !== null && isNaN(v)) - throw new Error(`Bad value for "${graph.keyName}": "${v}"`) + throw new Error(`Bad value for "${graph.keyName}": "${v}"`); if (typeof v === "string") { - sample[graph.keyName] = parseFloat(v) + sample[graph.keyName] = parseFloat(v); } - pointsDefined++ + pointsDefined++; } if (pointsDefined < 2) throw new Error( `Less than two points defined for the "${graph.keyName}" graph` - ) + ); } - return timeData -} + return timeData; +}; diff --git a/src/utils/format-time.js b/src/utils/format-time.js index 9094cb4..7f3a4a1 100644 --- a/src/utils/format-time.js +++ b/src/utils/format-time.js @@ -1,29 +1,29 @@ -import moment from "moment" +import moment from "moment"; export const formatTime = (time, format, visibleDuration) => { - const lessThan3DaysShown = visibleDuration < 1000 * 60 * 60 * 24 * 3 + const lessThan3DaysShown = visibleDuration < 1000 * 60 * 60 * 24 * 3; if (format === "none") return visibleDuration > 10 ? Math.round(time).toString() - : time.toFixed(2 - Math.log(visibleDuration) / Math.log(10)) + : time.toFixed(2 - Math.log(visibleDuration) / Math.log(10)); if (format === "dates") { return ( moment(time).format("L") + (!lessThan3DaysShown ? "" : "\n" + moment(time).format("h:mm:ss a")) - ) + ); } - const showNs = visibleDuration < 5 - const showMs = visibleDuration < 5000 - const ns = Math.floor((time * 1000) % 1000) - const ms = Math.floor(time % 1000) - const secs = Math.floor((time / 1000) % 60) - const mins = Math.floor((time / 60000) % 60) - const hours = Math.floor(time / (60000 * 60)) - if (time < 0) return "< 00:00:00" + const showNs = visibleDuration < 5; + const showMs = visibleDuration < 5000; + const ns = Math.floor((time * 1000) % 1000); + const ms = Math.floor(time % 1000); + const secs = Math.floor((time / 1000) % 60); + const mins = Math.floor((time / 60000) % 60); + const hours = Math.floor(time / (60000 * 60)); + if (time < 0) return "< 00:00:00"; return ( [hours, mins, secs].map((t) => t.toString().padStart(2, "0")).join(":") + (showMs ? `.${ms.toString().padStart(3, "0")}` : "") + (showNs ? `\n+${ns.toString()} ns` : "") - ) -} -export default formatTime + ); +}; +export default formatTime; diff --git a/src/utils/get-minor-major-duration-lines.js b/src/utils/get-minor-major-duration-lines.js index 36989a1..ab39812 100644 --- a/src/utils/get-minor-major-duration-lines.js +++ b/src/utils/get-minor-major-duration-lines.js @@ -1,9 +1,9 @@ -const mins = 1000 * 60 -const hours = 60 * mins -const days = 24 * hours -const weeks = 7 * days -const months = 30 * days -const years = 12 * months +const mins = 1000 * 60; +const hours = 60 * mins; +const days = 24 * hours; +const weeks = 7 * days; +const months = 30 * days; +const years = 12 * months; const timeIntervals = [ ["1 ns", 0.000001], ["10 ns", 0.00001], @@ -26,66 +26,66 @@ const timeIntervals = [ ["1 month", months], ["3 months", months * 3], ["1 year", years], -] +]; export const findReasonableGridDuration = (duration) => { - let bestFittingIntervalIndex = 0 - let bestFittingIntervalScore = -Infinity + let bestFittingIntervalIndex = 0; + let bestFittingIntervalScore = -Infinity; for (const [i, [, timeInterval]] of Object.entries(timeIntervals)) { - const timeIntervalScore = -1 * Math.abs(duration / timeInterval - 5) + const timeIntervalScore = -1 * Math.abs(duration / timeInterval - 5); if (timeIntervalScore > bestFittingIntervalScore) { - bestFittingIntervalIndex = i - bestFittingIntervalScore = timeIntervalScore + bestFittingIntervalIndex = i; + bestFittingIntervalScore = timeIntervalScore; } } return [ timeIntervals[bestFittingIntervalIndex], timeIntervals[bestFittingIntervalIndex - 1], - ] -} + ]; +}; export default (transformMatrix, graphWidth) => { - const { x: startTimeOnGraph } = transformMatrix.inverse().applyToPoint(0, 0) + const { x: startTimeOnGraph } = transformMatrix.inverse().applyToPoint(0, 0); const { x: endTimeOnGraph } = transformMatrix .inverse() - .applyToPoint(graphWidth, 0) + .applyToPoint(graphWidth, 0); - const visibleDuration = endTimeOnGraph - startTimeOnGraph + const visibleDuration = endTimeOnGraph - startTimeOnGraph; const [ [majorDurationLabel, majorDuration], [minorDurationLabel, minorDuration], - ] = findReasonableGridDuration(endTimeOnGraph - startTimeOnGraph) + ] = findReasonableGridDuration(endTimeOnGraph - startTimeOnGraph); const numberOfMajorGridLines = Math.ceil( (endTimeOnGraph - startTimeOnGraph) / majorDuration - ) + ); const numberOfMinorGridLines = Math.ceil( (endTimeOnGraph - startTimeOnGraph) / minorDuration - ) + ); const majorGridLineStartTime = - Math.floor(startTimeOnGraph / majorDuration) * majorDuration + Math.floor(startTimeOnGraph / majorDuration) * majorDuration; const minorGridLineStartTime = - Math.floor(startTimeOnGraph / minorDuration) * minorDuration + Math.floor(startTimeOnGraph / minorDuration) * minorDuration; const majorGridLinePixelOffset = transformMatrix.applyToPoint( majorGridLineStartTime, 0 - ).x + ).x; const minorGridLinePixelOffset = transformMatrix.applyToPoint( minorGridLineStartTime, 0 - ).x + ).x; const majorGridLinePixelDistance = transformMatrix.applyToPoint(majorDuration, 0).x - - transformMatrix.applyToPoint(0, 0).x + transformMatrix.applyToPoint(0, 0).x; const minorGridLinePixelDistance = transformMatrix.applyToPoint(minorDuration, 0).x - - transformMatrix.applyToPoint(0, 0).x + transformMatrix.applyToPoint(0, 0).x; return { visibleDuration, @@ -101,5 +101,5 @@ export default (transformMatrix, graphWidth) => { minorGridLinePixelOffset, majorGridLinePixelDistance, minorGridLinePixelDistance, - } -} + }; +}; diff --git a/src/utils/init-top-level-matrix.js b/src/utils/init-top-level-matrix.js index c26d5e0..e167f71 100644 --- a/src/utils/init-top-level-matrix.js +++ b/src/utils/init-top-level-matrix.js @@ -1,19 +1,19 @@ -import Matrix from "immutable-transform-matrix" +import Matrix from "immutable-transform-matrix"; export default ({ curveGroups, width }) => { - const mat = new Matrix() + const mat = new Matrix(); const allTimes = curveGroups .flatMap((curveGroup) => curveGroup) - .flatMap((curve) => curve.data.map(([t]) => t)) + .flatMap((curve) => curve.data.map(([t]) => t)); - if (allTimes.length === 0) return mat + if (allTimes.length === 0) return mat; - const minT = Math.min(...allTimes) - const maxT = Math.max(...allTimes) + const minT = Math.min(...allTimes); + const maxT = Math.max(...allTimes); return mat .scale(width, 1) .scale(1 / (maxT - minT), 1) - .translate(-minT, 0) -} + .translate(-minT, 0); +}; diff --git a/yarn.lock b/yarn.lock index 14fc338..50ad50a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15438,11 +15438,6 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -transformation-matrix-js@^2.7.6: - version "2.7.6" - resolved "https://registry.yarnpkg.com/transformation-matrix-js/-/transformation-matrix-js-2.7.6.tgz#25c7ff055c99b8528ffbd4c4a2684be6c9d5ef60" - integrity sha512-1CxDIZmCQ3vA0GGnkdMQqxUXVm3xXAFmglPYRS1hr37LzSg22TC7QAWOT38OmdUvMEs/rqcnkFoAsqvzdiluDg== - trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" From 3fce26bf148f60c74e375b643ee1795c8ef49273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Jean?= Date: Thu, 14 Jan 2021 11:33:50 -0500 Subject: [PATCH 2/4] add prettierrc --- .eslintrc.js | 2 +- .prettierrc | 1 + .storybook/main.js | 2 +- .storybook/preview.js | 12 +- releaserc.js | 2 +- src/App.js | 6 +- src/components/BackgroundContainer/index.js | 16 +- src/components/ControllableWave/index.js | 36 ++-- src/components/DurationBoxes/index.js | 42 ++-- src/components/DurationBoxes/index.stories.js | 10 +- src/components/HighlightValueLabels/index.js | 20 +- src/components/MainLayout/index.js | 130 ++++++------ src/components/MainLayout/index.stories.js | 36 ++-- src/components/MouseTransformHandler/index.js | 142 +++++++------- .../MouseTransformHandler/index.stories.js | 8 +- .../ReactTimeSeries.stories.js | 58 +++--- src/components/ReactTimeSeries/index.js | 185 +++++++++--------- src/components/TimeStamp/index.js | 30 +-- src/components/Timeline/Timeline.stories.js | 38 ++-- src/components/Timeline/index.js | 76 ++++--- src/components/Toolbar/Toolbar.stories.js | 22 +-- src/components/Toolbar/index.js | 134 ++++++------- src/components/Wave/index.js | 104 +++++----- src/components/Wave/index.stories.js | 30 +-- src/hooks/use-colors.js | 24 +-- src/hooks/use-get-random-color-using-hash.js | 20 +- src/hooks/use-global-transform-matrix.js | 10 +- src/hooks/use-root-audio-elm.js | 8 +- src/hooks/use-time-cursor-time.js | 8 +- src/hooks/use-time-range.js | 10 +- src/hooks/use-tool-mode.js | 6 +- src/index.js | 10 +- src/utils/fetch-audio-data.js | 22 +-- src/utils/fetch-csv-data.js | 20 +- src/utils/fix-time-data.js | 28 +-- src/utils/format-time.js | 30 +-- src/utils/get-minor-major-duration-lines.js | 56 +++--- src/utils/init-top-level-matrix.js | 16 +- 38 files changed, 704 insertions(+), 706 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc.js b/.eslintrc.js index 568724b..4d0552b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,4 +19,4 @@ module.exports = { rules: { "react/prop-types": ["off"], }, -}; +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..109a78d --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{ "semi": false } diff --git a/.storybook/main.js b/.storybook/main.js index e6d12c2..ce7b287 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -5,4 +5,4 @@ module.exports = { "@storybook/addon-essentials", "@storybook/preset-create-react-app", ], -}; +} diff --git a/.storybook/preview.js b/.storybook/preview.js index df9e07f..5d4a7b9 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,7 +1,7 @@ -import React from "react"; -import { RecoilRoot } from "recoil"; -import "../src/index.css"; -import BackgroundContainer from "../src/components/BackgroundContainer"; +import React from "react" +import { RecoilRoot } from "recoil" +import "../src/index.css" +import BackgroundContainer from "../src/components/BackgroundContainer" export const decorators = [ (Story) => ( @@ -18,8 +18,8 @@ export const decorators = [
), -]; +] export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, -}; +} diff --git a/releaserc.js b/releaserc.js index 848d499..2655e70 100644 --- a/releaserc.js +++ b/releaserc.js @@ -14,4 +14,4 @@ module.exports = { }, ], ], -}; +} diff --git a/src/App.js b/src/App.js index c45e0a9..4482171 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,7 @@ -import React from "react"; +import React from "react" function App() { - return
; + return
} -export default App; +export default App diff --git a/src/components/BackgroundContainer/index.js b/src/components/BackgroundContainer/index.js index 9d18e94..d62d617 100644 --- a/src/components/BackgroundContainer/index.js +++ b/src/components/BackgroundContainer/index.js @@ -1,14 +1,14 @@ -import React from "react"; -import { styled } from "@material-ui/core/styles"; -import useColors from "../../hooks/use-colors"; +import React from "react" +import { styled } from "@material-ui/core/styles" +import useColors from "../../hooks/use-colors" const Container = styled("div")(({ themecolors }) => ({ backgroundColor: themecolors.bg, -})); +})) export const BackgroundContainer = ({ children }) => { - const themecolors = useColors(); - return {children}; -}; + const themecolors = useColors() + return {children} +} -export default BackgroundContainer; +export default BackgroundContainer diff --git a/src/components/ControllableWave/index.js b/src/components/ControllableWave/index.js index 2259348..9c9c1a0 100644 --- a/src/components/ControllableWave/index.js +++ b/src/components/ControllableWave/index.js @@ -1,9 +1,9 @@ -import React from "react"; -import MouseTransformHandler from "../MouseTransformHandler"; -import Matrix from "immutable-transform-matrix"; -import Wave from "../Wave"; -import useEventCallback from "use-event-callback"; -import useRafState from "react-use/lib/useRafState"; +import React from "react" +import MouseTransformHandler from "../MouseTransformHandler" +import Matrix from "immutable-transform-matrix" +import Wave from "../Wave" +import useEventCallback from "use-event-callback" +import useRafState from "react-use/lib/useRafState" export const ControllableWave = ({ curves, @@ -22,27 +22,27 @@ export const ControllableWave = ({ showValues, }) => { let [matrix, setMatrix] = useRafState(() => { - const mat = new Matrix(); - const maxY = Math.max(...curves[0].data.map(([, y]) => y)); - const minY = Math.min(...curves[0].data.map(([, y]) => y)); + const mat = new Matrix() + const maxY = Math.max(...curves[0].data.map(([, y]) => y)) + const minY = Math.min(...curves[0].data.map(([, y]) => y)) return mat .scale(1, -1) .scale(1, height) .scale(1, 1 / (maxY - minY)) - .translate(0, -maxY); - }); + .translate(0, -maxY) + }) matrix = matrix .set("a", topLevelMatrix.get("a")) - .set("e", topLevelMatrix.get("e")); + .set("e", topLevelMatrix.get("e")) const onChangeMatrix = useEventCallback((newMatrix) => { - setMatrix(newMatrix); + setMatrix(newMatrix) setTopLevelMatrix( topLevelMatrix.set("a", newMatrix.get("a")).set("e", newMatrix.get("e")) - ); - }); + ) + }) return ( - ); -}; + ) +} -export default ControllableWave; +export default ControllableWave diff --git a/src/components/DurationBoxes/index.js b/src/components/DurationBoxes/index.js index 18a4d33..11b7855 100644 --- a/src/components/DurationBoxes/index.js +++ b/src/components/DurationBoxes/index.js @@ -1,8 +1,8 @@ -import React from "react"; -import { styled } from "@material-ui/core/styles"; -import colorAlpha from "color-alpha"; -import useColors from "../../hooks/use-colors"; -import useToolMode from "../../hooks/use-tool-mode"; +import React from "react" +import { styled } from "@material-ui/core/styles" +import colorAlpha from "color-alpha" +import useColors from "../../hooks/use-colors" +import useToolMode from "../../hooks/use-tool-mode" const Container = styled("div")(({ width, active, color }) => ({ width, @@ -15,7 +15,7 @@ const Container = styled("div")(({ width, active, color }) => ({ "&:hover": { backgroundColor: "rgba(0,0,0,0.2)", }, -})); +})) const Box = styled("div")(({ x, width, color }) => ({ position: "absolute", left: x, @@ -30,7 +30,7 @@ const Box = styled("div")(({ x, width, color }) => ({ whiteSpace: "pre", color: "#fff", }, -})); +})) const Label = styled("div")(({ colors }) => ({ position: "absolute", pointerEvents: "none", @@ -42,7 +42,7 @@ const Label = styled("div")(({ colors }) => ({ mixBlendMode: "multiply", color: "#888", }), -})); +})) export const DurationBox = ({ width, @@ -57,9 +57,9 @@ export const DurationBox = ({ isMiscLayer = false, label = "", }) => { - const [toolMode] = useToolMode(); - const colors = useColors(); - const visibleDuration = visibleTimeEnd - visibleTimeStart; + const [toolMode] = useToolMode() + const colors = useColors() + const visibleDuration = visibleTimeEnd - visibleTimeStart return ( { const startX = - ((startTime - visibleTimeStart) / visibleDuration) * width; - const endX = ((endTime - visibleTimeStart) / visibleDuration) * width; + ((startTime - visibleTimeStart) / visibleDuration) * width + const endX = ((endTime - visibleTimeStart) / visibleDuration) * width - if (endX < 0) return null; - if (isNaN(startX) || isNaN(endX)) return null; + if (endX < 0) return null + if (isNaN(startX) || isNaN(endX)) return null return ( onClickBox(i)} onMouseUp={(e) => { if (toolMode === "delete" || e.button === 2 || e.button === 1) { - onRemoveBox(i); + onRemoveBox(i) } }} onContextMenu={(e) => { - e.preventDefault(); + e.preventDefault() }} >
{durationLabel}
- ); + ) } )} {(label || (isMiscLayer && durations.length === 0)) && ( @@ -113,7 +113,7 @@ export const DurationBox = ({ )}
- ); -}; + ) +} -export default DurationBox; +export default DurationBox diff --git a/src/components/DurationBoxes/index.stories.js b/src/components/DurationBoxes/index.stories.js index 7eac793..39b9db6 100644 --- a/src/components/DurationBoxes/index.stories.js +++ b/src/components/DurationBoxes/index.stories.js @@ -1,12 +1,12 @@ -import React from "react"; -import DurationBox from "./"; -import { solarized } from "../../hooks/use-colors"; +import React from "react" +import DurationBox from "./" +import { solarized } from "../../hooks/use-colors" export default { title: "DurationBox", component: DurationBox, argTypes: {}, -}; +} export const Primary = () => (
@@ -52,4 +52,4 @@ export const Primary = () => ( visibleTimeEnd={1000} />
-); +) diff --git a/src/components/HighlightValueLabels/index.js b/src/components/HighlightValueLabels/index.js index 6ce7931..c1f41a0 100644 --- a/src/components/HighlightValueLabels/index.js +++ b/src/components/HighlightValueLabels/index.js @@ -1,10 +1,10 @@ -import React, { Fragment, useState, useCallback } from "react"; +import React, { Fragment, useState, useCallback } from "react" -const HOV_SIZE = 50; +const HOV_SIZE = 50 export const Point = ({ x, y, value, t, color, width }) => { - const [showPoint, setShowPoint] = useState(false); - const onShow = useCallback(() => setShowPoint(true), [setShowPoint]); - const onHide = useCallback(() => setShowPoint(false), [setShowPoint]); + const [showPoint, setShowPoint] = useState(false) + const onShow = useCallback(() => setShowPoint(true), [setShowPoint]) + const onHide = useCallback(() => setShowPoint(false), [setShowPoint]) return ( {showPoint && ( @@ -25,8 +25,8 @@ export const Point = ({ x, y, value, t, color, width }) => { // fill="rgba(0,0,0,0.5)" > - ); -}; + ) +} export const HighlightValueLabels = ({ visibleTransformedPointsOnCurves = [], @@ -49,7 +49,7 @@ export const HighlightValueLabels = ({ )) )} - ); -}; + ) +} -export default HighlightValueLabels; +export default HighlightValueLabels diff --git a/src/components/MainLayout/index.js b/src/components/MainLayout/index.js index fdf894b..8e40985 100644 --- a/src/components/MainLayout/index.js +++ b/src/components/MainLayout/index.js @@ -1,16 +1,16 @@ -import React, { useState } from "react"; -import { styled } from "@material-ui/core/styles"; -import useTimeRange from "../../hooks/use-time-range.js"; -import ControllableWave from "../ControllableWave"; -import DurationBoxes from "../DurationBoxes"; -import useEventCallback from "use-event-callback"; -import { setIn, getIn } from "seamless-immutable"; -import useColors from "../../hooks/use-colors"; -import Timeline from "../Timeline"; -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; -import initTopLevelMatrix from "../../utils/init-top-level-matrix"; -import Toolbar from "../Toolbar"; -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; +import React, { useState } from "react" +import { styled } from "@material-ui/core/styles" +import useTimeRange from "../../hooks/use-time-range.js" +import ControllableWave from "../ControllableWave" +import DurationBoxes from "../DurationBoxes" +import useEventCallback from "use-event-callback" +import { setIn, getIn } from "seamless-immutable" +import useColors from "../../hooks/use-colors" +import Timeline from "../Timeline" +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" +import initTopLevelMatrix from "../../utils/init-top-level-matrix" +import Toolbar from "../Toolbar" +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" const Container = styled("div")(({ themecolors, width }) => ({ width: width, @@ -19,14 +19,14 @@ const Container = styled("div")(({ themecolors, width }) => ({ backgroundColor: themecolors.bg, padding: 16, boxSizing: "border-box", -})); +})) const defaultEnabledTools = [ "create-durations", "label-durations", "create-timestamps", "label-timestamps", -]; +] export const MainLayout = ({ curveGroups, @@ -46,28 +46,28 @@ export const MainLayout = ({ onStopPlayback, isPlayingMedia, }) => { - const themecolors = useColors(); - const [activeDurationGroup, setActiveDurationGroup] = useState(null); - const [draggedDurationIndex, setDraggedDurationIndex] = useState(null); - const [selectedDurationIndex, setSelectedDurationIndex] = useState(null); - const [selectedTimestampIndex, setSelectedTimestampIndex] = useState(null); + const themecolors = useColors() + const [activeDurationGroup, setActiveDurationGroup] = useState(null) + const [draggedDurationIndex, setDraggedDurationIndex] = useState(null) + const [selectedDurationIndex, setSelectedDurationIndex] = useState(null) + const [selectedTimestampIndex, setSelectedTimestampIndex] = useState(null) const [topLevelMatrix, setTopLevelMatrix] = useState(() => initTopLevelMatrix({ curveGroups, width }) - ); + ) const { visibleTimeStart, visibleTimeEnd } = useTimeRange( topLevelMatrix, width - ); + ) const onDragDuration = useEventCallback((startTime, endTime) => { - if (!enabledTools.includes("create-durations")) return; - if (activeDurationGroup === null) return; - [startTime, endTime] = - startTime < endTime ? [startTime, endTime] : [endTime, startTime]; + if (!enabledTools.includes("create-durations")) return + if (activeDurationGroup === null) return + ;[startTime, endTime] = + startTime < endTime ? [startTime, endTime] : [endTime, startTime] if (selectedDurationIndex !== draggedDurationIndex) - setSelectedDurationIndex(selectedDurationIndex); + setSelectedDurationIndex(selectedDurationIndex) onChangeDurationGroups( setIn( @@ -78,27 +78,27 @@ export const MainLayout = ({ end: endTime, } ) - ); - }); + ) + }) const onDragDurationStart = useEventCallback((startTime) => { - if (!enabledTools.includes("create-durations")) return; - if (activeDurationGroup === null) return; - const lastIndex = durationGroups[activeDurationGroup].durations.length; - setDraggedDurationIndex(lastIndex); + if (!enabledTools.includes("create-durations")) return + if (activeDurationGroup === null) return + const lastIndex = durationGroups[activeDurationGroup].durations.length + setDraggedDurationIndex(lastIndex) onChangeDurationGroups( setIn(durationGroups, [activeDurationGroup, "durations", lastIndex], { start: startTime, end: startTime, }) - ); - }); + ) + }) const onDragDurationEnd = useEventCallback(() => { - if (!enabledTools.includes("create-durations")) return; - setSelectedDurationIndex(draggedDurationIndex); - setDraggedDurationIndex(null); - }); + if (!enabledTools.includes("create-durations")) return + setSelectedDurationIndex(draggedDurationIndex) + setDraggedDurationIndex(null) + }) const onCreateTimestamp = useEventCallback((time) => { - if (!enabledTools.includes("create-timestamps")) return; + if (!enabledTools.includes("create-timestamps")) return onChangeTimestamps( timestamps.concat([ { @@ -106,29 +106,29 @@ export const MainLayout = ({ color: "#888", }, ]) - ); - setSelectedTimestampIndex(timestamps.length); - }); + ) + setSelectedTimestampIndex(timestamps.length) + }) const onRemoveDurationBox = useEventCallback((dgi, boxIndex) => { - const durations = durationGroups[dgi].durations; + const durations = durationGroups[dgi].durations onChangeDurationGroups( setIn( durationGroups, [dgi, "durations"], [...durations.slice(0, boxIndex), ...durations.slice(boxIndex + 1)] ) - ); - if (selectedTimestampIndex !== null) setSelectedTimestampIndex(null); - if (selectedDurationIndex !== null) setSelectedDurationIndex(null); - }); + ) + if (selectedTimestampIndex !== null) setSelectedTimestampIndex(null) + if (selectedDurationIndex !== null) setSelectedDurationIndex(null) + }) const onClickTimestamp = useEventCallback((ts, tsi) => { - setSelectedTimestampIndex(tsi); - if (selectedDurationIndex !== null) setSelectedDurationIndex(null); - }); + setSelectedTimestampIndex(tsi) + if (selectedDurationIndex !== null) setSelectedDurationIndex(null) + }) - const getRandomColorUsingHash = useGetRandomColorUsingHash(); + const getRandomColorUsingHash = useGetRandomColorUsingHash() const onChangeSelectedItemLabel = useEventCallback(({ label, color }) => { if (selectedTimestampIndex !== null) { onChangeTimestamps( @@ -136,13 +136,13 @@ export const MainLayout = ({ [selectedTimestampIndex, "color"], color || getRandomColorUsingHash(label) ) - ); + ) } else if (selectedDurationIndex !== null) { const pathToDuration = [ activeDurationGroup, "durations", selectedDurationIndex, - ]; + ] onChangeDurationGroups( setIn(durationGroups, pathToDuration, { ...getIn(durationGroups, pathToDuration), @@ -151,18 +151,18 @@ export const MainLayout = ({ ? {} : { color: color || getRandomColorUsingHash(label) }), }) - ); + ) } - }); + }) const onRemoveTimestamp = useEventCallback((ts, tsi) => { onChangeTimestamps([ ...timestamps.slice(0, tsi), ...timestamps.slice(tsi + 1), - ]); - }); + ]) + }) - const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, width); + const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, width) return ( @@ -195,8 +195,8 @@ export const MainLayout = ({ setActiveDurationGroup(i)} onClickBox={(di) => { - setSelectedDurationIndex(di); - setSelectedTimestampIndex(null); + setSelectedDurationIndex(di) + setSelectedTimestampIndex(null) }} onRemoveBox={(boxIndex) => onRemoveDurationBox(i, boxIndex)} key={i} @@ -209,7 +209,7 @@ export const MainLayout = ({ visibleTimeStart={visibleTimeStart} visibleTimeEnd={visibleTimeEnd} /> - ); + ) })} {curveGroups.map((curves, i) => ( ))} - ); -}; + ) +} -export default MainLayout; +export default MainLayout diff --git a/src/components/MainLayout/index.stories.js b/src/components/MainLayout/index.stories.js index 4c5aa72..a77a556 100644 --- a/src/components/MainLayout/index.stories.js +++ b/src/components/MainLayout/index.stories.js @@ -1,14 +1,14 @@ -import React, { useState } from "react"; +import React, { useState } from "react" -import MainLayout from "./"; -import tesla from "./tesla.json"; -import { solarized } from "../../hooks/use-colors"; +import MainLayout from "./" +import tesla from "./tesla.json" +import { solarized } from "../../hooks/use-colors" export default { title: "MainLayout", component: MainLayout, argTypes: {}, -}; +} export const Primary = () => { const [durationGroups, setDurationGroups] = useState([ @@ -27,14 +27,14 @@ export const Primary = () => { color: solarized.magenta, durations: [], }, - ]); + ]) const [timestamps, setTimestamps] = useState([ { color: solarized.red, time: 1520398800000, }, - ]); + ]) return ( { onChangeTimestamps={setTimestamps} onChangeDurationGroups={setDurationGroups} /> - ); -}; + ) +} export const ExampleMiscLayer = () => { const [durationGroups, setDurationGroups] = useState([ @@ -79,7 +79,7 @@ export const ExampleMiscLayer = () => { }, ], }, - ]); + ]) return ( { durationGroups={durationGroups} onChangeTimestamps={() => null} onChangeDurationGroups={(newDurationGroups) => { - setDurationGroups(newDurationGroups); + setDurationGroups(newDurationGroups) }} /> - ); -}; + ) +} export const AudioPlayback = () => { - const [durationGroups, setDurationGroups] = useState([]); + const [durationGroups, setDurationGroups] = useState([]) - const [timestamps, setTimestamps] = useState([]); + const [timestamps, setTimestamps] = useState([]) - const [isPlayingMedia, setIsPlayingMedia] = useState(false); + const [isPlayingMedia, setIsPlayingMedia] = useState(false) return ( { onStopPlayback={() => setIsPlayingMedia(false)} isPlayingMedia={isPlayingMedia} /> - ); -}; + ) +} diff --git a/src/components/MouseTransformHandler/index.js b/src/components/MouseTransformHandler/index.js index ea0bd7c..723099b 100644 --- a/src/components/MouseTransformHandler/index.js +++ b/src/components/MouseTransformHandler/index.js @@ -1,9 +1,9 @@ -import React, { useState, useRef, useEffect, useCallback } from "react"; -import { styled } from "@material-ui/core/styles"; -import useEventCallback from "use-event-callback"; -import useToolMode from "../../hooks/use-tool-mode"; +import React, { useState, useRef, useEffect, useCallback } from "react" +import { styled } from "@material-ui/core/styles" +import useEventCallback from "use-event-callback" +import useToolMode from "../../hooks/use-tool-mode" -const Container = styled("div")({}); +const Container = styled("div")({}) export const MouseTransformHandler = ({ children, @@ -14,82 +14,82 @@ export const MouseTransformHandler = ({ onDragDurationEnd, onCreateTimestamp, }) => { - const mousePosition = useRef({ x: 0, y: 0 }); - const [dragStartTime, setDragStartTime] = useState(null); - const [primaryDrag, setPrimaryDrag] = useState(false); - const [shiftKeyDown, setShiftKeyDown] = useState(false); - const [middleMouseDown, setMiddleMouseDown] = useState(false); - const [toolMode] = useToolMode(); - const containerRef = useRef(); + const mousePosition = useRef({ x: 0, y: 0 }) + const [dragStartTime, setDragStartTime] = useState(null) + const [primaryDrag, setPrimaryDrag] = useState(false) + const [shiftKeyDown, setShiftKeyDown] = useState(false) + const [middleMouseDown, setMiddleMouseDown] = useState(false) + const [toolMode] = useToolMode() + const containerRef = useRef() useEffect(() => { const onKeyDown = (e) => { if (e.key === "Shift") { - setShiftKeyDown(true); + setShiftKeyDown(true) } - }; + } const onKeyUp = (e) => { if (e.key === "Shift") { - setShiftKeyDown(false); + setShiftKeyDown(false) } - }; + } - window.addEventListener("keydown", onKeyDown); - window.addEventListener("keyup", onKeyUp); + window.addEventListener("keydown", onKeyDown) + window.addEventListener("keyup", onKeyUp) return () => { - window.removeEventListener("keydown", onKeyDown); - window.removeEventListener("keyup", onKeyUp); - }; - }, []); + window.removeEventListener("keydown", onKeyDown) + window.removeEventListener("keyup", onKeyUp) + } + }, []) const projectMouse = useEventCallback((e) => { - const { clientX, clientY } = e; + const { clientX, clientY } = e - const rect = containerRef.current.getBoundingClientRect(); + const rect = containerRef.current.getBoundingClientRect() - const xRelToElm = clientX - rect.x; - const yRelToElm = clientY - rect.y; + const xRelToElm = clientX - rect.x + const yRelToElm = clientY - rect.y - return matrix.inverse().applyToPoint(xRelToElm, yRelToElm); - }); + return matrix.inverse().applyToPoint(xRelToElm, yRelToElm) + }) const onWheel = useEventCallback((e) => { if (e && e.preventDefault) { - e.preventDefault(); + e.preventDefault() } - const { deltaY } = e; - const scroll = -Math.sign(deltaY) / 10; - const { px, py } = mousePosition.current; + const { deltaY } = e + const scroll = -Math.sign(deltaY) / 10 + const { px, py } = mousePosition.current onChangeMatrix( matrix .translate(px, py) .scale(1 + (shiftKeyDown ? 0 : scroll), 1 + (shiftKeyDown ? scroll : 0)) .translate(-px, -py) - ); - }); + ) + }) const onMouseMove = useEventCallback((e) => { - const { clientX, clientY } = e; + const { clientX, clientY } = e - const projectedMouse = projectMouse(e); + const projectedMouse = projectMouse(e) if (middleMouseDown) { const delta = { x: clientX - mousePosition.current.x, y: clientY - mousePosition.current.y, - }; + } const scaleFac = { x: matrix.get("a"), y: matrix.get("d"), - }; + } onChangeMatrix( matrix.translate(delta.x / scaleFac.x, delta.y / scaleFac.y) - ); + ) } if (primaryDrag) { - onDragDuration(dragStartTime, projectedMouse.x); + onDragDuration(dragStartTime, projectedMouse.x) } mousePosition.current = { @@ -97,63 +97,63 @@ export const MouseTransformHandler = ({ y: clientY, px: projectedMouse.x, py: projectedMouse.y, - }; - }); + } + }) const onMouseDown = useEventCallback((e) => { - if (toolMode === "zoom" && e.button !== 1) return; - const { clientX, clientY, button } = e; - const projectedMouse = projectMouse(e); + if (toolMode === "zoom" && e.button !== 1) return + const { clientX, clientY, button } = e + const projectedMouse = projectMouse(e) mousePosition.current = { x: clientX, y: clientY, px: projectedMouse.x, py: projectedMouse.y, - }; - setDragStartTime(projectedMouse.x); + } + setDragStartTime(projectedMouse.x) if (toolMode === "pan" || button === 1 || e.button === 2) { - setMiddleMouseDown(true); - e.preventDefault(); + setMiddleMouseDown(true) + e.preventDefault() } else if (button === 0) { - onDragDurationStart(projectMouse.x); - setPrimaryDrag(true); + onDragDurationStart(projectMouse.x) + setPrimaryDrag(true) } - }); + }) const onMouseUp = useEventCallback((e) => { if (toolMode === "zoom" && e.button !== 1) { if (e.button === 2) { - onWheel({ ...e, deltaY: 100 }); + onWheel({ ...e, deltaY: 100 }) } else if (e.button === 0) { - onWheel({ ...e, deltaY: -100 }); + onWheel({ ...e, deltaY: -100 }) } - return; + return } - const projectedMouse = projectMouse(e); + const projectedMouse = projectMouse(e) if (toolMode === "pan" || e.button === 1 || e.button === 2) { - setMiddleMouseDown(false); + setMiddleMouseDown(false) } else if (e.button === 0) { if (Math.abs(dragStartTime - projectedMouse.x) === 0) { - onCreateTimestamp(projectedMouse.x); + onCreateTimestamp(projectedMouse.x) } - setPrimaryDrag(false); - onDragDurationEnd(); + setPrimaryDrag(false) + onDragDurationEnd() } - }); + }) const onContextMenu = useEventCallback((e) => { - e.preventDefault(); - }); + e.preventDefault() + }) const containerMountCallback = useCallback((ref) => { if (ref === null) { - containerRef.current.removeEventListener("wheel", onWheel); - containerRef.current = ref; + containerRef.current.removeEventListener("wheel", onWheel) + containerRef.current = ref } else { - containerRef.current = ref; - ref.addEventListener("wheel", onWheel, { passive: false }); + containerRef.current = ref + ref.addEventListener("wheel", onWheel, { passive: false }) } - }, []); + }, []) return ( {children} - ); -}; + ) +} -export default MouseTransformHandler; +export default MouseTransformHandler diff --git a/src/components/MouseTransformHandler/index.stories.js b/src/components/MouseTransformHandler/index.stories.js index 51df87c..e01f62c 100644 --- a/src/components/MouseTransformHandler/index.stories.js +++ b/src/components/MouseTransformHandler/index.stories.js @@ -1,11 +1,11 @@ -import React from "react"; +import React from "react" -import MouseTransformHandler from "./"; +import MouseTransformHandler from "./" export default { title: "MouseTransformHandler", component: MouseTransformHandler, argTypes: {}, -}; +} -export const Primary = () => ; +export const Primary = () => diff --git a/src/components/ReactTimeSeries/ReactTimeSeries.stories.js b/src/components/ReactTimeSeries/ReactTimeSeries.stories.js index d50d2c7..5634d66 100644 --- a/src/components/ReactTimeSeries/ReactTimeSeries.stories.js +++ b/src/components/ReactTimeSeries/ReactTimeSeries.stories.js @@ -1,10 +1,10 @@ -import React, { useState } from "react"; -import ReactTimeSeries from "./"; +import React, { useState } from "react" +import ReactTimeSeries from "./" export default { title: "ReactTimeSeries", component: ReactTimeSeries, -}; +} export const SimpleTimeSeries = () => { const [sample, setSample] = useState({ @@ -21,29 +21,29 @@ export const SimpleTimeSeries = () => { { start: 800, end: 1000, label: "byeeee" }, ], }, - }); + }) return ( - ); -}; + ) +} export const WithAudioURL = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }); + }) return ( - ); -}; + ) +} export const ReallySimple = () => { return ( @@ -58,8 +58,8 @@ export const ReallySimple = () => { }} onModifySample={() => null} /> - ); -}; + ) +} export const SmallValues = () => { return ( @@ -77,14 +77,14 @@ export const SmallValues = () => { }} onModifySample={() => null} /> - ); -}; + ) +} export const PredefinedLabelsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }); + }) return ( { sample={sample} onModifySample={setSample} /> - ); -}; + ) +} export const CreateTimestampsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }); + }) return ( { sample={sample} onModifySample={setSample} /> - ); -}; + ) +} export const CreateDurationsOnly = () => { const [sample, setSample] = useState({ audioUrl: "https://s3.amazonaws.com/datasets.workaround.online/voice-samples/001/voice.mp3", - }); + }) return ( { sample={sample} onModifySample={setSample} /> - ); -}; + ) +} export const TimeDataFromCSV = () => { const [sample, setSample] = useState({ csvUrl: "https://gist.githubusercontent.com/philaturner/644b3e8bd17641766d90f38d475edcc6/raw/a6c35e97a89eda49098def90fe2e750ceb898f79/tesla.csv", - }); + }) return ( { sample={sample} onModifySample={setSample} /> - ); -}; + ) +} export const LargeTimeNoneFormat = () => { return ( @@ -170,8 +170,8 @@ export const LargeTimeNoneFormat = () => { }} onModifySample={() => null} /> - ); -}; + ) +} export const AudioPlayback = () => { return ( @@ -186,5 +186,5 @@ export const AudioPlayback = () => { }} onModifySample={() => null} /> - ); -}; + ) +} diff --git a/src/components/ReactTimeSeries/index.js b/src/components/ReactTimeSeries/index.js index e69c40a..d7fdac6 100644 --- a/src/components/ReactTimeSeries/index.js +++ b/src/components/ReactTimeSeries/index.js @@ -1,33 +1,33 @@ -import React, { useMemo, useState, useEffect, useRef } from "react"; -import { setIn } from "seamless-immutable"; -import useEventCallback from "use-event-callback"; -import { useAsyncMemo } from "use-async-memo"; -import { RecoilRoot } from "recoil"; -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; -import Measure from "react-measure"; -import { useSetTimeCursorTime } from "../../hooks/use-time-cursor-time"; -import useRootAudioElm from "../../hooks/use-root-audio-elm"; +import React, { useMemo, useState, useEffect, useRef } from "react" +import { setIn } from "seamless-immutable" +import useEventCallback from "use-event-callback" +import { useAsyncMemo } from "use-async-memo" +import { RecoilRoot } from "recoil" +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" +import Measure from "react-measure" +import { useSetTimeCursorTime } from "../../hooks/use-time-cursor-time" +import useRootAudioElm from "../../hooks/use-root-audio-elm" -import MainLayout from "../MainLayout"; +import MainLayout from "../MainLayout" -import fetchAudioData from "../../utils/fetch-audio-data"; -import fetchCSVData from "../../utils/fetch-csv-data"; +import fetchAudioData from "../../utils/fetch-audio-data" +import fetchCSVData from "../../utils/fetch-csv-data" -import fixTimeData from "../../utils/fix-time-data"; +import fixTimeData from "../../utils/fix-time-data" -const emptyAr = []; +const emptyAr = [] // This is a cloudflare CORs proxy from the project maintainer @seveibar const defaultCorsProxy = - "https://corsproxy.seve.workers.dev/corsproxy/?apiurl={URL}"; + "https://corsproxy.seve.workers.dev/corsproxy/?apiurl={URL}" const defaultEnabledTools = [ "create-durations", "create-timestamps", "label-durations", "label-timestamps", -]; -const defaultGraphs = [{ keyName: "value" }]; +] +const defaultGraphs = [{ keyName: "value" }] export const ReactTimeSeriesWithoutContext = ({ // eslint-disable-next-line @@ -37,9 +37,9 @@ export const ReactTimeSeriesWithoutContext = ({ width: widthProp, onModifySample, }) => { - if (!iface) throw new Error(`"interface" is a required prop`); - if (!sample) throw new Error(`"sample" is a required prop`); - const getRandomColorUsingHash = useGetRandomColorUsingHash(); + if (!iface) throw new Error(`"interface" is a required prop`) + if (!sample) throw new Error(`"sample" is a required prop`) + const getRandomColorUsingHash = useGetRandomColorUsingHash() let { timeFormat, enabledTools = defaultEnabledTools, @@ -48,77 +48,76 @@ export const ReactTimeSeriesWithoutContext = ({ graphs = defaultGraphs, allowCustomLabels, showValues, - } = iface; - let { timeData: sampleTimeData, audioUrl, csvUrl, annotation } = sample; - if (showValues === undefined && !audioUrl) showValues = true; + } = iface + let { timeData: sampleTimeData, audioUrl, csvUrl, annotation } = sample + if (showValues === undefined && !audioUrl) showValues = true - const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean); + const timeDataAvailable = [sampleTimeData, audioUrl, csvUrl].some(Boolean) - const [isPlayingMedia, setIsPlayingMedia] = useState(false); - const [error, setError] = useState(null); + const [isPlayingMedia, setIsPlayingMedia] = useState(false) + const [error, setError] = useState(null) const timeData = useAsyncMemo( async () => { try { - if (sampleTimeData) return fixTimeData(sampleTimeData, graphs); - if (audioUrl) - return fixTimeData(await fetchAudioData(audioUrl), graphs); - if (csvUrl) return fixTimeData(await fetchCSVData(csvUrl), graphs); + if (sampleTimeData) return fixTimeData(sampleTimeData, graphs) + if (audioUrl) return fixTimeData(await fetchAudioData(audioUrl), graphs) + if (csvUrl) return fixTimeData(await fetchCSVData(csvUrl), graphs) throw new Error( `No timeData, no audioUrl, no csvUrl, no time data provided.` - ); + ) } catch (e) { - setError(e); - return []; + setError(e) + return [] } }, [sampleTimeData, audioUrl, csvUrl], null - ); + ) - const timeDataLoading = !timeData && timeDataAvailable; + const timeDataLoading = !timeData && timeDataAvailable const curveGroups = useMemo(() => { - if (!timeData) return []; - const anonRows = []; - const namedRows = {}; + if (!timeData) return [] + const anonRows = [] + const namedRows = {} for (const graph of graphs) { const curveData = timeData .filter( (a) => a[graph.keyName] !== undefined && a[graph.keyName] !== null ) - .map((a) => [a.time, a[graph.keyName]]); + .map((a) => [a.time, a[graph.keyName]]) if (graph.row === undefined || graph.row === null) { const curveGroup = [ { data: curveData, color: graph.color || getRandomColorUsingHash(graph.keyName), }, - ]; - anonRows.push(curveGroup); + ] + anonRows.push(curveGroup) } else { if (!namedRows[graph.row]) { - namedRows[graph.row] = []; + namedRows[graph.row] = [] } namedRows[graph.row].push({ data: curveData, color: graph.color || getRandomColorUsingHash(graph.keyName), - }); + }) } } return anonRows.concat( Object.entries(namedRows).sort((a, b) => a[0].localeCompare(b[0])) - ); - }, [timeData, graphs, getRandomColorUsingHash]); + ) + }, [timeData, graphs, getRandomColorUsingHash]) const timestamps = useMemo(() => { - if (!annotation?.timestamps) return []; + if (!annotation?.timestamps) return [] return annotation?.timestamps.map((ts) => ({ time: ts.time, label: ts.label, color: ts.color || getRandomColorUsingHash(ts.label), - })); + })) // eslint-disable-next-line - }, [annotation?.timestamps, getRandomColorUsingHash]); + }, [annotation?.timestamps, getRandomColorUsingHash]) const durationGroups = useMemo(() => { if (!annotation?.durations) @@ -130,14 +129,14 @@ export const ReactTimeSeriesWithoutContext = ({ misc: true, durations: [], }, - ]; + ] const availableLabels = Array.from( new Set( annotation.durations.flatMap((d) => [d.label, d.layer]).filter(Boolean) ) - ); - availableLabels.sort(); + ) + availableLabels.sort() // TODO no more than 5 layers, after 5 layers start reusing layers @@ -153,9 +152,9 @@ export const ReactTimeSeriesWithoutContext = ({ end: d.end, label: d.label, })), - }; + } }) - .filter((dg) => dg.durations.length > 0); + .filter((dg) => dg.durations.length > 0) if (enabledTools.includes("create-durations")) { durationGroups.push({ @@ -166,16 +165,16 @@ export const ReactTimeSeriesWithoutContext = ({ .flatMap((dg) => dg.durations) .concat(annotation.durations.filter((d) => !d.label)) .map((d) => ({ ...d, color: getRandomColorUsingHash(d.label) })), - }); + }) } durationGroups = durationGroups.filter( (dg) => dg.misc || dg.durations.length > 1 - ); + ) - return durationGroups; + return durationGroups // eslint-disable-next-line - }, [annotation?.durations]); + }, [annotation?.durations]) const onChangeDurationGroups = useEventCallback((newDurationGroups) => { onModifySample( @@ -189,67 +188,67 @@ export const ReactTimeSeriesWithoutContext = ({ })) ) ) - ); - }); + ) + }) const onChangeTimestamps = useEventCallback((newTimestamps) => { - onModifySample(setIn(sample, ["annotation", "timestamps"], newTimestamps)); - }); - const [width, setWidth] = useState(widthProp); + onModifySample(setIn(sample, ["annotation", "timestamps"], newTimestamps)) + }) + const [width, setWidth] = useState(widthProp) const onResize = useEventCallback(({ bounds }) => { - if (!widthProp) setWidth(bounds.width); - }); + if (!widthProp) setWidth(bounds.width) + }) - const audioSource = useRef(); + const audioSource = useRef() - const [, setRootAudioElm] = useRootAudioElm(); - const setTimeCursorTime = useSetTimeCursorTime(); + const [, setRootAudioElm] = useRootAudioElm() + const setTimeCursorTime = useSetTimeCursorTime() useEffect(() => { if (audioUrl) { - audioSource.current = new Audio(audioUrl); - setTimeCursorTime(0); + audioSource.current = new Audio(audioUrl) + setTimeCursorTime(0) } - }, []); + }, []) const onAudioTimeChanged = useEventCallback(() => { - setTimeCursorTime(audioSource.current.currentTime * 1000); - }); + setTimeCursorTime(audioSource.current.currentTime * 1000) + }) const onAudioPaused = useEventCallback(() => { - audioSource.current.removeEventListener("timeupdate", onAudioTimeChanged); - audioSource.current.removeEventListener("pause", onAudioPaused); - }); + audioSource.current.removeEventListener("timeupdate", onAudioTimeChanged) + audioSource.current.removeEventListener("pause", onAudioPaused) + }) const onStartPlayback = useEventCallback(() => { - setIsPlayingMedia(true); + setIsPlayingMedia(true) if (audioSource.current) { - audioSource.current.play(); - audioSource.current.addEventListener("timeupdate", onAudioTimeChanged); - audioSource.current.addEventListener("pause", onAudioPaused); - setRootAudioElm(audioSource.current); + audioSource.current.play() + audioSource.current.addEventListener("timeupdate", onAudioTimeChanged) + audioSource.current.addEventListener("pause", onAudioPaused) + setRootAudioElm(audioSource.current) } - }); + }) const onStopPlayback = useEventCallback(() => { - setIsPlayingMedia(false); + setIsPlayingMedia(false) if (audioSource.current) { - audioSource.current.pause(); + audioSource.current.pause() } - }); + }) - if (timeDataLoading) return "loading"; // TODO real loader + if (timeDataLoading) return "loading" // TODO real loader if (!timeData) { throw new Error( `No time data provided. Try sample={{timeData: [{time: 0, value: 1}, ...]}} or sample={{audioUrl:"https://..."}}` - ); + ) } if (curveGroups.length === 0) { - throw new Error(`For some reason, no curves are able to be displayed.`); + throw new Error(`For some reason, no curves are able to be displayed.`) } if (error) { - throw error; + throw error } return ( @@ -276,15 +275,15 @@ export const ReactTimeSeriesWithoutContext = ({ )} - ); -}; + ) +} export const ReactTimeSeries = (props) => { return ( - ); -}; + ) +} -export default ReactTimeSeries; +export default ReactTimeSeries diff --git a/src/components/TimeStamp/index.js b/src/components/TimeStamp/index.js index 41993eb..3f43d1e 100644 --- a/src/components/TimeStamp/index.js +++ b/src/components/TimeStamp/index.js @@ -1,9 +1,9 @@ -import React from "react"; -import { styled } from "@material-ui/core/styles"; -import LocationOnIcon from "@material-ui/icons/LocationOn"; -import Color from "color"; -import useEventCallback from "use-event-callback"; -import useToolMode from "../../hooks/use-tool-mode"; +import React from "react" +import { styled } from "@material-ui/core/styles" +import LocationOnIcon from "@material-ui/icons/LocationOn" +import Color from "color" +import useEventCallback from "use-event-callback" +import useToolMode from "../../hooks/use-tool-mode" export const Container = styled("div")(({ left, color, hasIcon }) => ({ position: "absolute", @@ -49,18 +49,18 @@ export const Container = styled("div")(({ left, color, hasIcon }) => ({ height: 2, backgroundColor: color, }, -})); +})) export const TimeStamp = ({ left, color, label, onClick, onRemove }) => { - const [toolMode] = useToolMode(); + const [toolMode] = useToolMode() const onMouseUp = useEventCallback((e) => { if (toolMode === "delete" || e.button === 2 || e.button === 1) { - onRemove(); + onRemove() } - }); + }) const onContextMenu = useEventCallback((e) => { - e.preventDefault(); - }); + e.preventDefault() + }) return ( {
{label ? {label} : } - ); -}; + ) +} -export default TimeStamp; +export default TimeStamp diff --git a/src/components/Timeline/Timeline.stories.js b/src/components/Timeline/Timeline.stories.js index dc6b759..cae4eea 100644 --- a/src/components/Timeline/Timeline.stories.js +++ b/src/components/Timeline/Timeline.stories.js @@ -1,9 +1,9 @@ -import React from "react"; +import React from "react" -import useColors from "../../hooks/use-colors"; -import Timeline from "./"; -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; -import Matrix from "immutable-transform-matrix"; +import useColors from "../../hooks/use-colors" +import Timeline from "./" +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" +import Matrix from "immutable-transform-matrix" export default { title: "Timeline", @@ -12,7 +12,7 @@ export default { width: "number", onClickTimestamp: { action: "onClickTimestamp" }, }, -}; +} export const TimeWithColons = (args) => { return ( @@ -24,8 +24,8 @@ export const TimeWithColons = (args) => { visibleTimeEnd={60000 * 80} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ); -}; + ) +} export const Dates = (args) => { return ( @@ -37,11 +37,11 @@ export const Dates = (args) => { visibleTimeEnd={60000 * 60 * 24 * 400} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ); -}; + ) +} export const TimeWithTimestamps = (args) => { - const colors = useColors(); + const colors = useColors() return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ); -}; + ) +} export const TimeWithTextMarkers = (args) => { - const colors = useColors(); + const colors = useColors() return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ); -}; + ) +} export const TimeWithCurrentTimeCursor = (args) => { - const colors = useColors(); + const colors = useColors() return ( { ]} gridLineMetrics={getMinorMajorDurationLines(new Matrix(), 500)} /> - ); -}; + ) +} diff --git a/src/components/Timeline/index.js b/src/components/Timeline/index.js index 1077b00..3f944f9 100644 --- a/src/components/Timeline/index.js +++ b/src/components/Timeline/index.js @@ -1,16 +1,16 @@ -import React, { useRef } from "react"; -import range from "lodash/range"; -import { styled } from "@material-ui/core/styles"; -import useColors from "../../hooks/use-colors"; -import TimeStamp from "../TimeStamp"; +import React, { useRef } from "react" +import range from "lodash/range" +import { styled } from "@material-ui/core/styles" +import useColors from "../../hooks/use-colors" +import TimeStamp from "../TimeStamp" import { useTimeCursorTime, useSetTimeCursorTime, -} from "../../hooks/use-time-cursor-time"; -import useRootAudioElm from "../../hooks/use-root-audio-elm"; -import useEventCallback from "use-event-callback"; +} from "../../hooks/use-time-cursor-time" +import useRootAudioElm from "../../hooks/use-root-audio-elm" +import useEventCallback from "use-event-callback" -import { formatTime } from "../../utils/format-time"; +import { formatTime } from "../../utils/format-time" const Container = styled("div")(({ width, themecolors }) => ({ width, @@ -20,7 +20,7 @@ const Container = styled("div")(({ width, themecolors }) => ({ cursor: "pointer", borderBottom: `1px solid ${themecolors.Selection}`, color: themecolors.fg, -})); +})) const TimeText = styled("div")(({ x, faded }) => ({ display: "inline-block", @@ -34,7 +34,7 @@ const TimeText = styled("div")(({ x, faded }) => ({ paddingLeft: 4, whiteSpace: "pre-wrap", opacity: faded ? 0.25 : 0.75, -})); +})) const TimeCursor = styled("div")(({ left, themecolors }) => ({ position: "absolute", @@ -45,13 +45,13 @@ const TimeCursor = styled("div")(({ left, themecolors }) => ({ borderLeft: "8px solid transparent", borderRight: "8px solid transparent", borderTop: `12px solid ${themecolors.green}`, -})); +})) const Svg = styled("svg")({ position: "absolute", left: 0, bottom: 0, -}); +}) export const Timeline = ({ timeFormat, @@ -64,40 +64,38 @@ export const Timeline = ({ onRemoveTimestamp, timeCursorTime: timeCursorTimeProp, }) => { - const themecolors = useColors(); - const visibleDuration = visibleTimeEnd - visibleTimeStart; + const themecolors = useColors() + const visibleDuration = visibleTimeEnd - visibleTimeStart // TODO compute tick count using width - const timeTextCount = Math.ceil(width / 100); + const timeTextCount = Math.ceil(width / 100) const timeTextTimes = range(timeTextCount).map( (i) => visibleTimeStart + (visibleDuration / timeTextCount) * i - ); - const recoilTimeCursorTime = useTimeCursorTime(); - const setTimeCursorTime = useSetTimeCursorTime(); - const [rootAudioElm] = useRootAudioElm(); + ) + const recoilTimeCursorTime = useTimeCursorTime() + const setTimeCursorTime = useSetTimeCursorTime() + const [rootAudioElm] = useRootAudioElm() const timeCursorTime = - timeCursorTimeProp === undefined - ? recoilTimeCursorTime - : timeCursorTimeProp; + timeCursorTimeProp === undefined ? recoilTimeCursorTime : timeCursorTimeProp const { numberOfMajorGridLines, majorGridLinePixelOffset, majorGridLinePixelDistance, - } = gridLineMetrics; + } = gridLineMetrics - const containerRef = useRef(); + const containerRef = useRef() const onClickTimeline = useEventCallback((e) => { - if (!rootAudioElm) return; - const { clientX } = e; + if (!rootAudioElm) return + const { clientX } = e const pxDistanceFromStart = - clientX - containerRef.current.getBoundingClientRect().left; + clientX - containerRef.current.getBoundingClientRect().left const time = (pxDistanceFromStart / width) * (visibleTimeEnd - visibleTimeStart) + - visibleTimeStart; - rootAudioElm.currentTime = time / 1000; - setTimeCursorTime(time); - }); + visibleTimeStart + rootAudioElm.currentTime = time / 1000 + setTimeCursorTime(time) + }) return ( {range(numberOfMajorGridLines).map((tickIndex) => { const x = - majorGridLinePixelOffset + majorGridLinePixelDistance * tickIndex; + majorGridLinePixelOffset + majorGridLinePixelDistance * tickIndex return ( - ); + ) })} {timestamps.map((timestamp, i) => { const left = - ((timestamp.time - visibleTimeStart) / visibleDuration) * width; + ((timestamp.time - visibleTimeStart) / visibleDuration) * width return ( onClickTimestamp(timestamp, i)} onRemove={() => onRemoveTimestamp(timestamp, i)} /> - ); + ) })} {timeCursorTime !== undefined && ( )} - ); -}; + ) +} -export default Timeline; +export default Timeline diff --git a/src/components/Toolbar/Toolbar.stories.js b/src/components/Toolbar/Toolbar.stories.js index b7ddcbe..b5ceeb9 100644 --- a/src/components/Toolbar/Toolbar.stories.js +++ b/src/components/Toolbar/Toolbar.stories.js @@ -1,16 +1,16 @@ -import React, { useState } from "react"; -import Toolbar from "./"; -import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash"; -import { solarized } from "../../hooks/use-colors"; +import React, { useState } from "react" +import Toolbar from "./" +import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" +import { solarized } from "../../hooks/use-colors" export default { title: "Toolbar", component: Toolbar, -}; +} export const Primary = () => { - const selectedTimestampIndex = 0; - const getRandomColorUsingHash = useGetRandomColorUsingHash(); + const selectedTimestampIndex = 0 + const getRandomColorUsingHash = useGetRandomColorUsingHash() const [timestamps, setTimestamps] = useState([ { color: solarized.red, @@ -22,7 +22,7 @@ export const Primary = () => { time: 200, label: "mouse", }, - ]); + ]) return ( { color: color ? color : getRandomColorUsingHash(label), } ) - ); + ) }} onStartPlayback={() => null} onStopPlayback={() => null} isPlayingMedia={false} /> - ); -}; + ) +} diff --git a/src/components/Toolbar/index.js b/src/components/Toolbar/index.js index 74e667c..854e4dc 100644 --- a/src/components/Toolbar/index.js +++ b/src/components/Toolbar/index.js @@ -1,26 +1,26 @@ -import React, { useMemo } from "react"; -import { styled } from "@material-ui/core/styles"; -import ButtonGroup from "@material-ui/core/ButtonGroup"; -import Button from "@material-ui/core/Button"; -import useColors from "../../hooks/use-colors"; -import CloseIcon from "@material-ui/icons/Close"; -import Brightness4Icon from "@material-ui/icons/Brightness4"; -import PanToolIcon from "@material-ui/icons/PanTool"; -import Box from "@material-ui/core/Box"; -import CreateIcon from "@material-ui/icons/Create"; -import classnames from "classnames"; -import useToolMode from "../../hooks/use-tool-mode"; -import useEventCallback from "use-event-callback"; -import { useSetRecoilState } from "recoil"; -import { themeAtom } from "../../hooks/use-colors"; -import CreatableSelect from "react-select/creatable"; -import NormalSelect from "react-select"; -import LocationOnIcon from "@material-ui/icons/LocationOn"; -import TimelapseIcon from "@material-ui/icons/Timelapse"; -import ZoomInIcon from "@material-ui/icons/ZoomIn"; -import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline"; -import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline"; -import Color from "color"; +import React, { useMemo } from "react" +import { styled } from "@material-ui/core/styles" +import ButtonGroup from "@material-ui/core/ButtonGroup" +import Button from "@material-ui/core/Button" +import useColors from "../../hooks/use-colors" +import CloseIcon from "@material-ui/icons/Close" +import Brightness4Icon from "@material-ui/icons/Brightness4" +import PanToolIcon from "@material-ui/icons/PanTool" +import Box from "@material-ui/core/Box" +import CreateIcon from "@material-ui/icons/Create" +import classnames from "classnames" +import useToolMode from "../../hooks/use-tool-mode" +import useEventCallback from "use-event-callback" +import { useSetRecoilState } from "recoil" +import { themeAtom } from "../../hooks/use-colors" +import CreatableSelect from "react-select/creatable" +import NormalSelect from "react-select" +import LocationOnIcon from "@material-ui/icons/LocationOn" +import TimelapseIcon from "@material-ui/icons/Timelapse" +import ZoomInIcon from "@material-ui/icons/ZoomIn" +import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline" +import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline" +import Color from "color" const Container = styled("div")(({ themecolors }) => ({ display: "flex", @@ -42,7 +42,7 @@ const Container = styled("div")(({ themecolors }) => ({ width: 16, height: 16, }, -})); +})) const getSelectFieldStyles = (themecolors) => ({ control: (styles) => ({ @@ -81,12 +81,12 @@ const getSelectFieldStyles = (themecolors) => ({ ...styles, backgroundColor: themecolors.base02, }), -}); +}) const iconStyle = { width: 20, height: 20, -}; +} export const Toolbar = ({ timestamps = [], @@ -102,85 +102,85 @@ export const Toolbar = ({ onStopPlayback, isPlayingMedia = false, }) => { - const themecolors = useColors(); - const [mode, setToolMode] = useToolMode(); - const setTheme = useSetRecoilState(themeAtom); + const themecolors = useColors() + const [mode, setToolMode] = useToolMode() + const setTheme = useSetRecoilState(themeAtom) const [durationLabelSet, durationLabelColorMap] = useMemo(() => { - const labelSet = new Set(durationLabels); - const labelColorMap = {}; + const labelSet = new Set(durationLabels) + const labelColorMap = {} for (const dg of durationGroups) { for (const duration of dg.durations) { - if (!duration.label) continue; - labelSet.add(duration.label); - labelColorMap[duration.label] = duration.color || dg.color; + if (!duration.label) continue + labelSet.add(duration.label) + labelColorMap[duration.label] = duration.color || dg.color } - if (!dg.label) continue; - labelSet.add(dg.label); - labelColorMap[dg.label] = dg.color; + if (!dg.label) continue + labelSet.add(dg.label) + labelColorMap[dg.label] = dg.color } - return [labelSet, labelColorMap]; - }, [timestamps, durationGroups, durationLabels]); + return [labelSet, labelColorMap] + }, [timestamps, durationGroups, durationLabels]) const [timestampLabelSet, timestampLabelColorMap] = useMemo(() => { - const labelSet = new Set(timestampLabels); - const labelColorMap = {}; + const labelSet = new Set(timestampLabels) + const labelColorMap = {} for (const timestamp of timestamps) { - if (!timestamp.label) continue; - labelSet.add(timestamp.label); - labelColorMap[timestamp.label] = timestamp.color; + if (!timestamp.label) continue + labelSet.add(timestamp.label) + labelColorMap[timestamp.label] = timestamp.color } - return [labelSet, labelColorMap]; - }, [timestamps, durationGroups, timestampLabels]); + return [labelSet, labelColorMap] + }, [timestamps, durationGroups, timestampLabels]) - const onSelectCreateTool = useEventCallback(() => setToolMode("create")); - const onSelectPanTool = useEventCallback(() => setToolMode("pan")); - const onSelectZoomTool = useEventCallback(() => setToolMode("zoom")); - const onSelectCloseTool = useEventCallback(() => setToolMode("delete")); + const onSelectCreateTool = useEventCallback(() => setToolMode("create")) + const onSelectPanTool = useEventCallback(() => setToolMode("pan")) + const onSelectZoomTool = useEventCallback(() => setToolMode("zoom")) + const onSelectCloseTool = useEventCallback(() => setToolMode("delete")) const toggleTheme = useEventCallback(() => setTheme(themecolors.dark ? "light" : "dark") - ); + ) const selectFieldStyles = useMemo( () => getSelectFieldStyles(themecolors, selectedTimestampIndex), [themecolors, selectedTimestampIndex] - ); - const formatCreateLabel = useEventCallback((s) => `Add "${s}"`); + ) + const formatCreateLabel = useEventCallback((s) => `Add "${s}"`) const onChangeSelectedLabel = useEventCallback((newValue) => { - const { label } = newValue || {}; + const { label } = newValue || {} onChangeSelectedItemLabel({ label, color: durationLabelColorMap[label] || timestampLabelColorMap[label], - }); - }); + }) + }) const timestampCreatableSelectOptions = useMemo( () => Array.from(timestampLabelSet).map((label) => ({ label, value: label })), [timestampLabelSet] - ); + ) const durationCreatableSelectOptions = useMemo( () => Array.from(durationLabelSet).map((label) => ({ label, value: label })), [durationLabelSet] - ); + ) const selectedTimestamp = typeof selectedTimestampIndex === "number" ? timestamps[selectedTimestampIndex] - : null; + : null const selectedDuration = typeof selectedDurationGroupIndex === "number" && typeof selectedDurationIndex === "number" ? durationGroups?.[selectedDurationGroupIndex]?.durations?.[ selectedDurationIndex ] - : null; + : null const selectedItemValue = useMemo(() => { - const label = selectedTimestamp?.label || selectedDuration?.label; - return { label, value: label }; - }, [selectedTimestamp, selectedDuration]); + const label = selectedTimestamp?.label || selectedDuration?.label + return { label, value: label } + }, [selectedTimestamp, selectedDuration]) - const SelectComponent = allowCustomLabels ? CreatableSelect : NormalSelect; + const SelectComponent = allowCustomLabels ? CreatableSelect : NormalSelect return ( @@ -273,7 +273,7 @@ export const Toolbar = ({ - ); -}; + ) +} -export default Toolbar; +export default Toolbar diff --git a/src/components/Wave/index.js b/src/components/Wave/index.js index ceafa04..962e78c 100644 --- a/src/components/Wave/index.js +++ b/src/components/Wave/index.js @@ -1,59 +1,59 @@ -import React, { Fragment, useMemo } from "react"; -import range from "lodash/range"; -import { styled } from "@material-ui/core/styles"; -import colorAlpha from "color-alpha"; -import useColors from "../../hooks/use-colors"; -import { formatTime } from "../../utils/format-time"; -import { useTimeCursorTime } from "../../hooks/use-time-cursor-time"; -import HighlightValueLabels from "../HighlightValueLabels"; +import React, { Fragment, useMemo } from "react" +import range from "lodash/range" +import { styled } from "@material-ui/core/styles" +import colorAlpha from "color-alpha" +import useColors from "../../hooks/use-colors" +import { formatTime } from "../../utils/format-time" +import { useTimeCursorTime } from "../../hooks/use-time-cursor-time" +import HighlightValueLabels from "../HighlightValueLabels" const userSelectOffStyle = { userSelect: "none", whiteSpace: "pre", fontVariantNumeric: "tabular-nums", -}; +} const reduceForVisibleDuration = (data, startTime, visibleDuration, width) => { - const firstInnerIndex = data.findIndex(([t]) => t >= startTime); + const firstInnerIndex = data.findIndex(([t]) => t >= startTime) let visibleSamples = data .slice(firstInnerIndex) - .findIndex(([t]) => t >= startTime + visibleDuration); + .findIndex(([t]) => t >= startTime + visibleDuration) visibleSamples = - visibleSamples === -1 ? data.length - firstInnerIndex : visibleSamples; - const lastInnerIndex = firstInnerIndex + visibleSamples; + visibleSamples === -1 ? data.length - firstInnerIndex : visibleSamples + const lastInnerIndex = firstInnerIndex + visibleSamples - data = data.slice(Math.max(0, firstInnerIndex - 1), lastInnerIndex + 1); + data = data.slice(Math.max(0, firstInnerIndex - 1), lastInnerIndex + 1) - const minDistance = visibleDuration / (width / 4); - const points = [data[0]]; - let lastAddedPointIndex = 0; + const minDistance = visibleDuration / (width / 4) + const points = [data[0]] + let lastAddedPointIndex = 0 for (let i = 1; i < data.length; i++) { if (data[i][0] - points[points.length - 1][0] > minDistance) { // points.push(data[i]) - const timeSinceLastPoint = data[i][0] - points[points.length - 1][0]; + const timeSinceLastPoint = data[i][0] - points[points.length - 1][0] if (i - lastAddedPointIndex === 1) { - points.push(data[i]); + points.push(data[i]) } else { points.push([ data[i][0] - timeSinceLastPoint / 2, Math.max( ...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v) ), - ]); + ]) points.push([ data[i][0], Math.min( ...data.slice(lastAddedPointIndex + 1, i + 1).map(([, v]) => v) ), - ]); + ]) } - lastAddedPointIndex = i; + lastAddedPointIndex = i } } - return points; -}; + return points +} export const Wave = ({ curves, @@ -67,12 +67,12 @@ export const Wave = ({ timeCursorTime, showValues = false, }) => { - const colors = useColors(); + const colors = useColors() - const recoilTimeCursorTime = useTimeCursorTime(); + const recoilTimeCursorTime = useTimeCursorTime() timeCursorTime = - timeCursorTime === undefined ? recoilTimeCursorTime : timeCursorTime; + timeCursorTime === undefined ? recoilTimeCursorTime : timeCursorTime const { visibleDuration, @@ -84,15 +84,15 @@ export const Wave = ({ minorGridLinePixelOffset, majorGridLinePixelDistance, minorGridLinePixelDistance, - } = gridLineMetrics; + } = gridLineMetrics const timeCursorXPosition = timeCursorTime !== undefined ? transformMatrix.applyToPoint(timeCursorTime, 0).x - : undefined; + : undefined const visibleTransformedPointsOnCurves = useMemo(() => { - const visibleTransformedPointsOnCurves = []; + const visibleTransformedPointsOnCurves = [] for (const curve of curves) { visibleTransformedPointsOnCurves.push( reduceForVisibleDuration( @@ -105,29 +105,29 @@ export const Wave = ({ t, value: y, })) - ); + ) } - return visibleTransformedPointsOnCurves; - }, [transformMatrix, curves, startTimeOnGraph, visibleDuration, width]); + return visibleTransformedPointsOnCurves + }, [transformMatrix, curves, startTimeOnGraph, visibleDuration, width]) return ( {range(-5, numberOfMajorGridLines + 1).map((i) => { const timeAtLine = Math.floor(startTimeOnGraph / majorDuration) * majorDuration + - majorDuration * i; + majorDuration * i - const lineX = majorGridLinePixelOffset + majorGridLinePixelDistance * i; + const lineX = majorGridLinePixelOffset + majorGridLinePixelDistance * i - const globalTimelineIndex = Math.floor(timeAtLine / majorDuration); + const globalTimelineIndex = Math.floor(timeAtLine / majorDuration) - let textElm = null; + let textElm = null if (globalTimelineIndex % 1 === 0) { const timeLines = formatTime( timeAtLine, timeFormat, visibleDuration - ).split("\n"); + ).split("\n") textElm = timeLines.map((tl, i) => ( {tl} - )); + )) } return ( @@ -157,12 +157,12 @@ export const Wave = ({ )} {textElm} - ); + ) })} {numberOfMajorGridLines < 12 && range(numberOfMinorGridLines).map((i) => { const lineX = - minorGridLinePixelOffset + minorGridLinePixelDistance * i; + minorGridLinePixelOffset + minorGridLinePixelDistance * i return ( - ); + ) })} {durationGroups.flatMap(({ durations, color: dgColor }, dgi) => { return durations.map((duration, di) => { - const { x: startX } = transformMatrix.applyToPoint(duration.start, 0); - const { x: endX } = transformMatrix.applyToPoint(duration.end, 0); - if (isNaN(startX) || isNaN(endX)) return null; + const { x: startX } = transformMatrix.applyToPoint(duration.start, 0) + const { x: endX } = transformMatrix.applyToPoint(duration.end, 0) + if (isNaN(startX) || isNaN(endX)) return null return ( - ); - }); + ) + }) })} {curves.map((curve, i) => ( ))} {timestamps.map((ts, i) => { - const { x } = transformMatrix.applyToPoint(ts.time, 0); + const { x } = transformMatrix.applyToPoint(ts.time, 0) return ( - ); + ) })} {timeCursorTime !== undefined && ( )} - ); -}; + ) +} -export default Wave; +export default Wave diff --git a/src/components/Wave/index.stories.js b/src/components/Wave/index.stories.js index d450941..639da35 100644 --- a/src/components/Wave/index.stories.js +++ b/src/components/Wave/index.stories.js @@ -1,28 +1,28 @@ -import React from "react"; -import range from "lodash/range"; -import Matrix from "immutable-transform-matrix"; -import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines"; +import React from "react" +import range from "lodash/range" +import Matrix from "immutable-transform-matrix" +import getMinorMajorDurationLines from "../../utils/get-minor-major-duration-lines" -import Wave from "./"; +import Wave from "./" export default { title: "Wave", component: Wave, argTypes: {}, -}; +} const curve1 = { color: "#f00", data: range(500).map((i) => [i, Math.sin(i / 20) * 100]), -}; +} const curve2 = { color: "#00f", data: range(500).map((i) => [i, Math.cos(i / 40) * 100]), -}; +} -const topLevelMatrix = new Matrix().translate(0, 100); -const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, 500); +const topLevelMatrix = new Matrix().translate(0, 100) +const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, 500) export const SingleCurve = () => ( ( gridLineMetrics={gridLineMetrics} transformMatrix={new Matrix().translate(0, 100)} /> -); +) export const DoubleCurve = () => ( ( gridLineMetrics={gridLineMetrics} transformMatrix={new Matrix().translate(0, 100)} /> -); +) export const DoubleCurveWithDuration = () => ( ( }, ]} /> -); +) export const DoubleCurveWithTimestamp = () => ( ( }, ]} /> -); +) export const DoubleCurveWithTimeCursor = () => ( ( gridLineMetrics={gridLineMetrics} timeCursorTime={250} /> -); +) diff --git a/src/hooks/use-colors.js b/src/hooks/use-colors.js index c8eef9d..ce9f7dc 100644 --- a/src/hooks/use-colors.js +++ b/src/hooks/use-colors.js @@ -1,9 +1,9 @@ -import { atom, useRecoilValue } from "recoil"; +import { atom, useRecoilValue } from "recoil" export const themeAtom = atom({ key: "themeAtom", default: "dark", -}); +}) export const solarized = { base03: "#002b36", @@ -23,9 +23,9 @@ export const solarized = { cyan: "#2aa198", green: "#859900", dark: true, -}; -solarized.fg = solarized.base3; -solarized.bg = solarized.base03; +} +solarized.fg = solarized.base3 +solarized.bg = solarized.base03 const invertedSolarized = { ...solarized, @@ -40,7 +40,7 @@ const invertedSolarized = { base2: solarized.base02, base3: solarized.base03, dark: false, -}; +} // Current Line -> base02 const dracula = { @@ -61,9 +61,9 @@ const dracula = { red: "#ff5555", yellow: "#f1fa8c", dark: true, -}; -dracula.fg = dracula.base2; -dracula.bg = dracula.base03; +} +dracula.fg = dracula.base2 +dracula.bg = dracula.base03 // // export const draculaTheme = { // bg: "#282a36", @@ -81,8 +81,8 @@ dracula.bg = dracula.base03; // } export default () => { - const themeName = useRecoilValue(themeAtom); + const themeName = useRecoilValue(themeAtom) // TODO switch between light and dark theme // Dracula - return themeName === "dark" ? dracula : invertedSolarized; -}; + return themeName === "dark" ? dracula : invertedSolarized +} diff --git a/src/hooks/use-get-random-color-using-hash.js b/src/hooks/use-get-random-color-using-hash.js index 415e6d0..18171fe 100644 --- a/src/hooks/use-get-random-color-using-hash.js +++ b/src/hooks/use-get-random-color-using-hash.js @@ -1,5 +1,5 @@ -import useColors from "./use-colors"; -import useEventCallback from "use-event-callback"; +import useColors from "./use-colors" +import useEventCallback from "use-event-callback" const colorsToCycle = [ "red", @@ -10,16 +10,16 @@ const colorsToCycle = [ "blue", "cyan", "green", -]; +] export default () => { - const themecolors = useColors(); + const themecolors = useColors() return useEventCallback((label) => { - if (!label) return themecolors.fg; - let hashNumber = 0; + if (!label) return themecolors.fg + let hashNumber = 0 for (let i = 0; i < label.length; i++) { - hashNumber += label.charCodeAt(i); + hashNumber += label.charCodeAt(i) } - return themecolors[colorsToCycle[hashNumber % colorsToCycle.length]]; - }); -}; + return themecolors[colorsToCycle[hashNumber % colorsToCycle.length]] + }) +} diff --git a/src/hooks/use-global-transform-matrix.js b/src/hooks/use-global-transform-matrix.js index a66d045..284b032 100644 --- a/src/hooks/use-global-transform-matrix.js +++ b/src/hooks/use-global-transform-matrix.js @@ -1,11 +1,11 @@ -import Matrix from "immutable-transform-matrix"; -import { atom, useRecoilState } from "recoil"; +import Matrix from "immutable-transform-matrix" +import { atom, useRecoilState } from "recoil" const matrix = atom({ key: "globalTransformMatrix", default: new Matrix(), -}); +}) export default () => { - return useRecoilState(matrix); -}; + return useRecoilState(matrix) +} diff --git a/src/hooks/use-root-audio-elm.js b/src/hooks/use-root-audio-elm.js index 8d99602..60481c9 100644 --- a/src/hooks/use-root-audio-elm.js +++ b/src/hooks/use-root-audio-elm.js @@ -1,9 +1,9 @@ -import { atom, useRecoilState } from "recoil"; +import { atom, useRecoilState } from "recoil" export const rootAudioElmAtom = atom({ key: "rootAudioElmAtom", -}); +}) -export const useRootAudioElm = () => useRecoilState(rootAudioElmAtom); +export const useRootAudioElm = () => useRecoilState(rootAudioElmAtom) -export default useRootAudioElm; +export default useRootAudioElm diff --git a/src/hooks/use-time-cursor-time.js b/src/hooks/use-time-cursor-time.js index d631a5f..e5dd31f 100644 --- a/src/hooks/use-time-cursor-time.js +++ b/src/hooks/use-time-cursor-time.js @@ -1,9 +1,9 @@ -import { atom, useSetRecoilState, useRecoilValue } from "recoil"; +import { atom, useSetRecoilState, useRecoilValue } from "recoil" export const timeCursorTimeAtom = atom({ key: "timeCursorTime", -}); +}) -export const useSetTimeCursorTime = () => useSetRecoilState(timeCursorTimeAtom); +export const useSetTimeCursorTime = () => useSetRecoilState(timeCursorTimeAtom) -export const useTimeCursorTime = () => useRecoilValue(timeCursorTimeAtom); +export const useTimeCursorTime = () => useRecoilValue(timeCursorTimeAtom) diff --git a/src/hooks/use-time-range.js b/src/hooks/use-time-range.js index d56edaa..a51146e 100644 --- a/src/hooks/use-time-range.js +++ b/src/hooks/use-time-range.js @@ -1,11 +1,11 @@ export default (matrix, width) => { - const visibleTimeStart = matrix.inverse().applyToPoint(0, 0).x; - const visibleTimeEnd = matrix.inverse().applyToPoint(width, 0).x; - const visibleDuration = visibleTimeEnd - visibleTimeStart; + const visibleTimeStart = matrix.inverse().applyToPoint(0, 0).x + const visibleTimeEnd = matrix.inverse().applyToPoint(width, 0).x + const visibleDuration = visibleTimeEnd - visibleTimeStart return { visibleTimeStart, visibleTimeEnd, visibleDuration, - }; -}; + } +} diff --git a/src/hooks/use-tool-mode.js b/src/hooks/use-tool-mode.js index c7f808c..5a24685 100644 --- a/src/hooks/use-tool-mode.js +++ b/src/hooks/use-tool-mode.js @@ -1,8 +1,8 @@ -import { atom, useRecoilState } from "recoil"; +import { atom, useRecoilState } from "recoil" export const atomToolMode = atom({ key: "toolMode", default: "create", -}); +}) -export default () => useRecoilState(atomToolMode); +export default () => useRecoilState(atomToolMode) diff --git a/src/index.js b/src/index.js index 846ed2c..cbdf9e2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,11 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import "./index.css"; -import App from "./App"; +import React from "react" +import ReactDOM from "react-dom" +import "./index.css" +import App from "./App" ReactDOM.render( , document.getElementById("root") -); +) diff --git a/src/utils/fetch-audio-data.js b/src/utils/fetch-audio-data.js index c494569..59c7bb8 100644 --- a/src/utils/fetch-audio-data.js +++ b/src/utils/fetch-audio-data.js @@ -1,26 +1,26 @@ export default async (audioUrl) => { - const AudioContext = window.AudioContext || window.webkitAudioContext; - const audioContext = new AudioContext(); + const AudioContext = window.AudioContext || window.webkitAudioContext + const audioContext = new AudioContext() const audioBuffer = await fetch(audioUrl) .then((res) => res.arrayBuffer()) - .then((arBuf) => audioContext.decodeAudioData(arBuf)); + .then((arBuf) => audioContext.decodeAudioData(arBuf)) // Convert into timeData - const channel = audioBuffer.getChannelData(0); + const channel = audioBuffer.getChannelData(0) - const blockDuration = 1; - const samplesPerBlock = audioBuffer.sampleRate * (blockDuration / 1000); - const timeData = []; + const blockDuration = 1 + const samplesPerBlock = audioBuffer.sampleRate * (blockDuration / 1000) + const timeData = [] for (let i = 0; i < channel.length; i += samplesPerBlock) { const avg = channel.slice(i, i + samplesPerBlock).reduce((acc, a) => acc + a, 0) / - samplesPerBlock; + samplesPerBlock timeData.push({ time: i * (1 / audioBuffer.sampleRate) * 1000, value: avg, - }); + }) } - return timeData; -}; + return timeData +} diff --git a/src/utils/fetch-csv-data.js b/src/utils/fetch-csv-data.js index 25c8730..6311ac5 100644 --- a/src/utils/fetch-csv-data.js +++ b/src/utils/fetch-csv-data.js @@ -1,31 +1,31 @@ -import Papa from "papaparse"; +import Papa from "papaparse" -const possibleTimeHeaders = ["time", "date", "t"]; +const possibleTimeHeaders = ["time", "date", "t"] export default async (csvUrl) => { - const csvText = await fetch(csvUrl).then((res) => res.text()); + const csvText = await fetch(csvUrl).then((res) => res.text()) - const parseResult = Papa.parse(csvText, { header: true }); + const parseResult = Papa.parse(csvText, { header: true }) - const header = parseResult.meta.fields; + const header = parseResult.meta.fields if (!possibleTimeHeaders.some((columnName) => header.includes(columnName))) { throw new Error( `No time fields in header. Acceptable fields: ${possibleTimeHeaders.join( "," )}` - ); + ) } if (!header.includes("time")) { for (const row of parseResult.data) { - row.time = row.date || row.t; + row.time = row.date || row.t } } for (const row of parseResult.data) { - row.time = new Date(row.time).valueOf(); + row.time = new Date(row.time).valueOf() } - return parseResult.data; -}; + return parseResult.data +} diff --git a/src/utils/fix-time-data.js b/src/utils/fix-time-data.js index 68818c9..20ca6b0 100644 --- a/src/utils/fix-time-data.js +++ b/src/utils/fix-time-data.js @@ -2,42 +2,42 @@ export default (timeData, graphs) => { // Make sure that all points have time, and time is always a number for (const sample of timeData) { if (sample.time === undefined || sample.time === null) - throw new Error(`Undefined time for sample\n\n${JSON.stringify(sample)}`); + throw new Error(`Undefined time for sample\n\n${JSON.stringify(sample)}`) if (typeof sample.time === "string") { - const ogTime = sample.time; + const ogTime = sample.time if (isNaN(ogTime)) { - sample.time = new Date(sample.time).valueOf(); + sample.time = new Date(sample.time).valueOf() } else { - sample.time = parseFloat(sample.time); + sample.time = parseFloat(sample.time) } if (isNaN(sample.time)) { - throw new Error(`Couldn't parse time "${sample.time}"`); + throw new Error(`Couldn't parse time "${sample.time}"`) } } - if (isNaN(sample.time)) throw new Error("Time must be a number"); + if (isNaN(sample.time)) throw new Error("Time must be a number") } // Make sure that timeData has a value in each datapoint for // each graph (or explicitly doesn't define it w/ undefined) // No NaNs allowed for (const graph of graphs) { - let pointsDefined = 0; + let pointsDefined = 0 for (const sample of timeData) { - const v = sample[graph.keyName]; + const v = sample[graph.keyName] if (v !== undefined && v !== null && isNaN(v)) - throw new Error(`Bad value for "${graph.keyName}": "${v}"`); + throw new Error(`Bad value for "${graph.keyName}": "${v}"`) if (typeof v === "string") { - sample[graph.keyName] = parseFloat(v); + sample[graph.keyName] = parseFloat(v) } - pointsDefined++; + pointsDefined++ } if (pointsDefined < 2) throw new Error( `Less than two points defined for the "${graph.keyName}" graph` - ); + ) } - return timeData; -}; + return timeData +} diff --git a/src/utils/format-time.js b/src/utils/format-time.js index 7f3a4a1..9094cb4 100644 --- a/src/utils/format-time.js +++ b/src/utils/format-time.js @@ -1,29 +1,29 @@ -import moment from "moment"; +import moment from "moment" export const formatTime = (time, format, visibleDuration) => { - const lessThan3DaysShown = visibleDuration < 1000 * 60 * 60 * 24 * 3; + const lessThan3DaysShown = visibleDuration < 1000 * 60 * 60 * 24 * 3 if (format === "none") return visibleDuration > 10 ? Math.round(time).toString() - : time.toFixed(2 - Math.log(visibleDuration) / Math.log(10)); + : time.toFixed(2 - Math.log(visibleDuration) / Math.log(10)) if (format === "dates") { return ( moment(time).format("L") + (!lessThan3DaysShown ? "" : "\n" + moment(time).format("h:mm:ss a")) - ); + ) } - const showNs = visibleDuration < 5; - const showMs = visibleDuration < 5000; - const ns = Math.floor((time * 1000) % 1000); - const ms = Math.floor(time % 1000); - const secs = Math.floor((time / 1000) % 60); - const mins = Math.floor((time / 60000) % 60); - const hours = Math.floor(time / (60000 * 60)); - if (time < 0) return "< 00:00:00"; + const showNs = visibleDuration < 5 + const showMs = visibleDuration < 5000 + const ns = Math.floor((time * 1000) % 1000) + const ms = Math.floor(time % 1000) + const secs = Math.floor((time / 1000) % 60) + const mins = Math.floor((time / 60000) % 60) + const hours = Math.floor(time / (60000 * 60)) + if (time < 0) return "< 00:00:00" return ( [hours, mins, secs].map((t) => t.toString().padStart(2, "0")).join(":") + (showMs ? `.${ms.toString().padStart(3, "0")}` : "") + (showNs ? `\n+${ns.toString()} ns` : "") - ); -}; -export default formatTime; + ) +} +export default formatTime diff --git a/src/utils/get-minor-major-duration-lines.js b/src/utils/get-minor-major-duration-lines.js index ab39812..36989a1 100644 --- a/src/utils/get-minor-major-duration-lines.js +++ b/src/utils/get-minor-major-duration-lines.js @@ -1,9 +1,9 @@ -const mins = 1000 * 60; -const hours = 60 * mins; -const days = 24 * hours; -const weeks = 7 * days; -const months = 30 * days; -const years = 12 * months; +const mins = 1000 * 60 +const hours = 60 * mins +const days = 24 * hours +const weeks = 7 * days +const months = 30 * days +const years = 12 * months const timeIntervals = [ ["1 ns", 0.000001], ["10 ns", 0.00001], @@ -26,66 +26,66 @@ const timeIntervals = [ ["1 month", months], ["3 months", months * 3], ["1 year", years], -]; +] export const findReasonableGridDuration = (duration) => { - let bestFittingIntervalIndex = 0; - let bestFittingIntervalScore = -Infinity; + let bestFittingIntervalIndex = 0 + let bestFittingIntervalScore = -Infinity for (const [i, [, timeInterval]] of Object.entries(timeIntervals)) { - const timeIntervalScore = -1 * Math.abs(duration / timeInterval - 5); + const timeIntervalScore = -1 * Math.abs(duration / timeInterval - 5) if (timeIntervalScore > bestFittingIntervalScore) { - bestFittingIntervalIndex = i; - bestFittingIntervalScore = timeIntervalScore; + bestFittingIntervalIndex = i + bestFittingIntervalScore = timeIntervalScore } } return [ timeIntervals[bestFittingIntervalIndex], timeIntervals[bestFittingIntervalIndex - 1], - ]; -}; + ] +} export default (transformMatrix, graphWidth) => { - const { x: startTimeOnGraph } = transformMatrix.inverse().applyToPoint(0, 0); + const { x: startTimeOnGraph } = transformMatrix.inverse().applyToPoint(0, 0) const { x: endTimeOnGraph } = transformMatrix .inverse() - .applyToPoint(graphWidth, 0); + .applyToPoint(graphWidth, 0) - const visibleDuration = endTimeOnGraph - startTimeOnGraph; + const visibleDuration = endTimeOnGraph - startTimeOnGraph const [ [majorDurationLabel, majorDuration], [minorDurationLabel, minorDuration], - ] = findReasonableGridDuration(endTimeOnGraph - startTimeOnGraph); + ] = findReasonableGridDuration(endTimeOnGraph - startTimeOnGraph) const numberOfMajorGridLines = Math.ceil( (endTimeOnGraph - startTimeOnGraph) / majorDuration - ); + ) const numberOfMinorGridLines = Math.ceil( (endTimeOnGraph - startTimeOnGraph) / minorDuration - ); + ) const majorGridLineStartTime = - Math.floor(startTimeOnGraph / majorDuration) * majorDuration; + Math.floor(startTimeOnGraph / majorDuration) * majorDuration const minorGridLineStartTime = - Math.floor(startTimeOnGraph / minorDuration) * minorDuration; + Math.floor(startTimeOnGraph / minorDuration) * minorDuration const majorGridLinePixelOffset = transformMatrix.applyToPoint( majorGridLineStartTime, 0 - ).x; + ).x const minorGridLinePixelOffset = transformMatrix.applyToPoint( minorGridLineStartTime, 0 - ).x; + ).x const majorGridLinePixelDistance = transformMatrix.applyToPoint(majorDuration, 0).x - - transformMatrix.applyToPoint(0, 0).x; + transformMatrix.applyToPoint(0, 0).x const minorGridLinePixelDistance = transformMatrix.applyToPoint(minorDuration, 0).x - - transformMatrix.applyToPoint(0, 0).x; + transformMatrix.applyToPoint(0, 0).x return { visibleDuration, @@ -101,5 +101,5 @@ export default (transformMatrix, graphWidth) => { minorGridLinePixelOffset, majorGridLinePixelDistance, minorGridLinePixelDistance, - }; -}; + } +} diff --git a/src/utils/init-top-level-matrix.js b/src/utils/init-top-level-matrix.js index e167f71..c26d5e0 100644 --- a/src/utils/init-top-level-matrix.js +++ b/src/utils/init-top-level-matrix.js @@ -1,19 +1,19 @@ -import Matrix from "immutable-transform-matrix"; +import Matrix from "immutable-transform-matrix" export default ({ curveGroups, width }) => { - const mat = new Matrix(); + const mat = new Matrix() const allTimes = curveGroups .flatMap((curveGroup) => curveGroup) - .flatMap((curve) => curve.data.map(([t]) => t)); + .flatMap((curve) => curve.data.map(([t]) => t)) - if (allTimes.length === 0) return mat; + if (allTimes.length === 0) return mat - const minT = Math.min(...allTimes); - const maxT = Math.max(...allTimes); + const minT = Math.min(...allTimes) + const maxT = Math.max(...allTimes) return mat .scale(width, 1) .scale(1 / (maxT - minT), 1) - .translate(-minT, 0); -}; + .translate(-minT, 0) +} From 3163df1380176d2d5e6cbcf926818e7fb4a5f150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Jean?= Date: Thu, 14 Jan 2021 16:02:37 -0500 Subject: [PATCH 3/4] keep old name and prevent warning --- src/components/BackgroundContainer/index.js | 11 ++- src/components/MainLayout/index.js | 22 ++--- src/components/Timeline/index.js | 54 +++++-------- src/components/Toolbar/index.js | 84 ++++++++++---------- src/hooks/use-get-random-color-using-hash.js | 6 +- 5 files changed, 82 insertions(+), 95 deletions(-) diff --git a/src/components/BackgroundContainer/index.js b/src/components/BackgroundContainer/index.js index d62d617..de69f51 100644 --- a/src/components/BackgroundContainer/index.js +++ b/src/components/BackgroundContainer/index.js @@ -2,13 +2,12 @@ import React from "react" import { styled } from "@material-ui/core/styles" import useColors from "../../hooks/use-colors" -const Container = styled("div")(({ themecolors }) => ({ - backgroundColor: themecolors.bg, -})) - export const BackgroundContainer = ({ children }) => { - const themecolors = useColors() - return {children} + const themeColors = useColors() + const Container = styled("div")(() => ({ + backgroundColor: themeColors.bg, + })) + return {children} } export default BackgroundContainer diff --git a/src/components/MainLayout/index.js b/src/components/MainLayout/index.js index 8e40985..2ee7faf 100644 --- a/src/components/MainLayout/index.js +++ b/src/components/MainLayout/index.js @@ -12,15 +12,6 @@ import initTopLevelMatrix from "../../utils/init-top-level-matrix" import Toolbar from "../Toolbar" import useGetRandomColorUsingHash from "../../hooks/use-get-random-color-using-hash" -const Container = styled("div")(({ themecolors, width }) => ({ - width: width, - display: "flex", - flexDirection: "column", - backgroundColor: themecolors.bg, - padding: 16, - boxSizing: "border-box", -})) - const defaultEnabledTools = [ "create-durations", "label-durations", @@ -46,7 +37,7 @@ export const MainLayout = ({ onStopPlayback, isPlayingMedia, }) => { - const themecolors = useColors() + const themeColors = useColors() const [activeDurationGroup, setActiveDurationGroup] = useState(null) const [draggedDurationIndex, setDraggedDurationIndex] = useState(null) const [selectedDurationIndex, setSelectedDurationIndex] = useState(null) @@ -164,8 +155,17 @@ export const MainLayout = ({ const gridLineMetrics = getMinorMajorDurationLines(topLevelMatrix, width) + const Container = styled("div")(() => ({ + width: width, + display: "flex", + flexDirection: "column", + backgroundColor: themeColors.bg, + padding: 16, + boxSizing: "border-box", + })) + return ( - + ({ - width, - overflow: "hidden", - position: "relative", - height: 64, - cursor: "pointer", - borderBottom: `1px solid ${themecolors.Selection}`, - color: themecolors.fg, -})) - const TimeText = styled("div")(({ x, faded }) => ({ display: "inline-block", width: 80, @@ -36,17 +26,6 @@ const TimeText = styled("div")(({ x, faded }) => ({ opacity: faded ? 0.25 : 0.75, })) -const TimeCursor = styled("div")(({ left, themecolors }) => ({ - position: "absolute", - width: 0, - height: 0, - top: 0, - left: left - 6, - borderLeft: "8px solid transparent", - borderRight: "8px solid transparent", - borderTop: `12px solid ${themecolors.green}`, -})) - const Svg = styled("svg")({ position: "absolute", left: 0, @@ -64,7 +43,7 @@ export const Timeline = ({ onRemoveTimestamp, timeCursorTime: timeCursorTimeProp, }) => { - const themecolors = useColors() + const themeColors = useColors() const visibleDuration = visibleTimeEnd - visibleTimeStart // TODO compute tick count using width const timeTextCount = Math.ceil(width / 100) @@ -96,12 +75,28 @@ export const Timeline = ({ rootAudioElm.currentTime = time / 1000 setTimeCursorTime(time) }) - + const Container = styled("div")(() => ({ + width, + overflow: "hidden", + position: "relative", + height: 64, + cursor: "pointer", + borderBottom: `1px solid ${themeColors.Selection}`, + color: themeColors.fg, + })) + const TimeCursor = styled("div")(() => ({ + position: "absolute", + width: 0, + height: 0, + top: 0, + left: ((timeCursorTime - visibleTimeStart) / visibleDuration) * width - 6, + borderLeft: "8px solid transparent", + borderRight: "8px solid transparent", + borderTop: `12px solid ${themeColors.green}`, + })) return ( {range(timeTextCount).map((timeTextIndex) => ( @@ -128,7 +123,7 @@ export const Timeline = ({ x2={x} y1={0} y2={12} - stroke={themecolors.base01} + stroke={themeColors.base01} /> ) })} @@ -146,12 +141,7 @@ export const Timeline = ({ /> ) })} - {timeCursorTime !== undefined && ( - - )} + {timeCursorTime !== undefined && } ) } diff --git a/src/components/Toolbar/index.js b/src/components/Toolbar/index.js index 854e4dc..e39e16f 100644 --- a/src/components/Toolbar/index.js +++ b/src/components/Toolbar/index.js @@ -22,64 +22,42 @@ import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline" import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline" import Color from "color" -const Container = styled("div")(({ themecolors }) => ({ - display: "flex", - paddingBottom: 16, - "&&& .MuiButtonBase-root": { - borderColor: themecolors.fg, - }, - "&&& .MuiButton-label": { - color: themecolors.fg, - textTransform: "none", - }, - "&&& .active.MuiButtonBase-root": { - backgroundColor: themecolors.fg, - }, - "&&& .active .MuiButton-label": { - color: themecolors.bg, - }, - "&&& .MuiSvgIcon-root": { - width: 16, - height: 16, - }, -})) - -const getSelectFieldStyles = (themecolors) => ({ +const getSelectFieldStyles = (themeColors) => ({ control: (styles) => ({ ...styles, border: `1px solid ${ - themecolors.dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)" + themeColors.dark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.5)" }`, boxShadow: "none", - backgroundColor: themecolors.bg, - borderColor: themecolors.base1, + backgroundColor: themeColors.bg, + borderColor: themeColors.base1, userSelect: "none", "&:hover": { - backgroundColor: Color(themecolors.bg).darken(0.2).string(), + backgroundColor: Color(themeColors.bg).darken(0.2).string(), }, }), input: (styles) => ({ ...styles, - color: themecolors.fg, + color: themeColors.fg, }), option: (styles) => ({ ...styles, - backgroundColor: themecolors.base02, - color: themecolors.fg, + backgroundColor: themeColors.base02, + color: themeColors.fg, "&:hover": { - backgroundColor: Color(themecolors.bg).darken(0.2).string(), + backgroundColor: Color(themeColors.bg).darken(0.2).string(), }, "&:focus": { - backgroundColor: Color(themecolors.bg).darken(0.2).string(), + backgroundColor: Color(themeColors.bg).darken(0.2).string(), }, }), singleValue: (styles) => ({ ...styles, - color: themecolors.fg, + color: themeColors.fg, }), menu: (styles) => ({ ...styles, - backgroundColor: themecolors.base02, + backgroundColor: themeColors.base02, }), }) @@ -102,7 +80,7 @@ export const Toolbar = ({ onStopPlayback, isPlayingMedia = false, }) => { - const themecolors = useColors() + const themeColors = useColors() const [mode, setToolMode] = useToolMode() const setTheme = useSetRecoilState(themeAtom) @@ -138,11 +116,11 @@ export const Toolbar = ({ const onSelectZoomTool = useEventCallback(() => setToolMode("zoom")) const onSelectCloseTool = useEventCallback(() => setToolMode("delete")) const toggleTheme = useEventCallback(() => - setTheme(themecolors.dark ? "light" : "dark") + setTheme(themeColors.dark ? "light" : "dark") ) const selectFieldStyles = useMemo( - () => getSelectFieldStyles(themecolors, selectedTimestampIndex), - [themecolors, selectedTimestampIndex] + () => getSelectFieldStyles(themeColors, selectedTimestampIndex), + [themeColors, selectedTimestampIndex] ) const formatCreateLabel = useEventCallback((s) => `Add "${s}"`) const onChangeSelectedLabel = useEventCallback((newValue) => { @@ -181,11 +159,31 @@ export const Toolbar = ({ }, [selectedTimestamp, selectedDuration]) const SelectComponent = allowCustomLabels ? CreatableSelect : NormalSelect - + const Container = styled("div")(() => ({ + display: "flex", + paddingBottom: 16, + "&&& .MuiButtonBase-root": { + borderColor: themeColors.fg, + }, + "&&& .MuiButton-label": { + color: themeColors.fg, + textTransform: "none", + }, + "&&& .active.MuiButtonBase-root": { + backgroundColor: themeColors.fg, + }, + "&&& .active .MuiButton-label": { + color: themeColors.bg, + }, + "&&& .MuiSvgIcon-root": { + width: 16, + height: 16, + }, + })) return ( - + ) : selectedDuration ? ( ) : null} diff --git a/src/hooks/use-get-random-color-using-hash.js b/src/hooks/use-get-random-color-using-hash.js index 18171fe..ed510c3 100644 --- a/src/hooks/use-get-random-color-using-hash.js +++ b/src/hooks/use-get-random-color-using-hash.js @@ -13,13 +13,13 @@ const colorsToCycle = [ ] export default () => { - const themecolors = useColors() + const themeColors = useColors() return useEventCallback((label) => { - if (!label) return themecolors.fg + if (!label) return themeColors.fg let hashNumber = 0 for (let i = 0; i < label.length; i++) { hashNumber += label.charCodeAt(i) } - return themecolors[colorsToCycle[hashNumber % colorsToCycle.length]] + return themeColors[colorsToCycle[hashNumber % colorsToCycle.length]] }) } From f5b752d3394cef72f635d172b803e9c96f9708cf Mon Sep 17 00:00:00 2001 From: Severin Ibarluzea Date: Fri, 15 Jan 2021 10:21:44 -0500 Subject: [PATCH 4/4] fix toString causing value to always be truthy --- src/components/Timeline/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Timeline/index.js b/src/components/Timeline/index.js index 1465a7b..1afde99 100644 --- a/src/components/Timeline/index.js +++ b/src/components/Timeline/index.js @@ -103,7 +103,7 @@ export const Timeline = ({ {formatTime( timeTextTimes[timeTextIndex],