Dynamically generated plot

This commit is contained in:
Rene Saarsoo 2020-10-01 23:29:13 +03:00
parent 739bb2d7bf
commit 9f9c316f30
2 changed files with 48 additions and 19 deletions

View File

@ -6,9 +6,12 @@ import { ErrorMessage } from './components/ErrorMessage';
const defaultWorkout = `Name: Hello const defaultWorkout = `Name: Hello
Warmup: 10:00 30%..60%
Interval: 20:00 100%
Rest: 10:00 75% Rest: 10:00 75%
Interval: 20:00 100% Interval: 20:00 100%
Rest: 10:00 75% Rest: 10:00 75%
Cooldown: 10:00 60%..30%
`; `;
export function App() { export function App() {
@ -31,7 +34,7 @@ export function App() {
<div> <div>
<h1>Workout editor</h1> <h1>Workout editor</h1>
<textarea rows={10} cols={100} onChange={onChange} value={text} /> <textarea rows={10} cols={100} onChange={onChange} value={text} />
<WorkoutPlot /> <WorkoutPlot intervals={workout.intervals} />
{error && <ErrorMessage>{error}</ErrorMessage>} {error && <ErrorMessage>{error}</ErrorMessage>}
<WorkoutStats workout={workout} /> <WorkoutStats workout={workout} />
</div> </div>

View File

@ -1,7 +1,14 @@
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { Interval, Intensity, IntensityRange, FreeIntensity, Duration } from "make-workout";
const Bar = styled.div<{ width: string; height: string; background: string }>` type BarProps = {
width: string;
height: string;
background: string;
};
const Bar = styled.div<BarProps>`
display: inline-block; display: inline-block;
border-radius: 10px; border-radius: 10px;
margin-right: 0.1%; margin-right: 0.1%;
@ -11,31 +18,50 @@ const Bar = styled.div<{ width: string; height: string; background: string }>`
width: ${(props) => props.width}; width: ${(props) => props.width};
`; `;
const zoneColor = (intensity: Intensity | IntensityRange | FreeIntensity): string => {
if (intensity.value >= 1.18) {
return "#ff330c";
}
if (intensity.value >= 1.05) {
return "#ff6639";
}
if (intensity.value >= 0.90) {
return "#ffcc3f";
}
if (intensity.value >= 0.75) {
return "#59bf59";
}
if (intensity.value >= 0.60) {
return "#338cff";
}
return "#7f7f7f";
}
export const totalDuration = (intervals: Interval[]): Duration =>
intervals.reduce((total, interval) => total.add(interval.duration), new Duration(0));
const toBarProps = (interval: Interval, workoutDuration: Duration): BarProps => ({
background: zoneColor(interval.intensity),
width: `${interval.duration.seconds / workoutDuration.seconds * 100 - 0.1}%`,
height: `${interval.intensity.value * 25}%`,
});
const Plot = styled.div` const Plot = styled.div`
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
width: 800px; width: 800px;
outline: 1px dashed #0c0; background: rgba(0,0,0, 0.03);
border-radius: 5px;
padding: 5px;
margin: 10px 0;
`; `;
export const WorkoutPlot: React.FC<{}> = () => { export const WorkoutPlot: React.FC<{ intervals: Interval[] }> = ({ intervals }) => {
const workoutDuration = totalDuration(intervals);
return ( return (
<Plot> <Plot>
<Bar background="#7f7f7f" width="1%" height="7.5%" /> { intervals.map((interval) => toBarProps(interval, workoutDuration)).map((props) => (<Bar {...props} />)) }
<Bar background="#7f7f7f" width="1%" height="10%" />
<Bar background="#7f7f7f" width="1%" height="12.5%" />
<Bar background="#7f7f7f" width="1%" height="15%" />
<Bar background="#338cff" width="5%" height="20%" />
<Bar background="#ffcc3f" width="15%" height="25%" />
<Bar background="#338cff" width="5%" height="20%" />
<Bar background="#ffcc3f" width="15%" height="25%" />
<Bar background="#338cff" width="5%" height="20%" />
<Bar background="#ffcc3f" width="15%" height="25%" />
<Bar background="#338cff" width="5%" height="20%" />
<Bar background="#7f7f7f" width="1%" height="15%" />
<Bar background="#7f7f7f" width="1%" height="12.5%" />
<Bar background="#7f7f7f" width="1%" height="10%" />
<Bar background="#7f7f7f" width="1%" height="7.5%" />
</Plot> </Plot>
); );
} }