-
-
Save samselikoff/4aff333f7c8538bb44f1806931c39be5 to your computer and use it in GitHub Desktop.
import * as d3 from "d3"; | |
import { | |
eachMonthOfInterval, | |
endOfMonth, | |
format, | |
isSameMonth, | |
parseISO, | |
startOfMonth, | |
} from "date-fns"; | |
import useMeasure from "react-use-measure"; | |
import { motion } from "framer-motion"; | |
import estimatedMax from "~/utils/estimated-max"; | |
export default function Chart({ entries }) { | |
let [ref, bounds] = useMeasure(); | |
if ( | |
!entries | |
.flatMap((entry) => entry.sets) | |
.some((set) => set.reps > 0 && set.tracked) | |
) { | |
return ( | |
<div className="flex h-full w-full items-center justify-center"> | |
<p className="text-sm italic text-gray-400"> | |
Add a tracked set to see a chart! | |
</p> | |
</div> | |
); | |
} | |
let data = [...entries] | |
.sort((a, b) => (a.date > b.date ? 1 : -1)) | |
.map((entry) => { | |
let setWithHighestEstimatedMax = entry.sets | |
.filter((set) => set.reps > 0 && set.tracked) | |
.sort((a, b) => estimatedMax(b) - estimatedMax(a))[0]; | |
return { | |
date: parseISO(entry.date), | |
estimatedMax: setWithHighestEstimatedMax | |
? estimatedMax(setWithHighestEstimatedMax) | |
: null, | |
}; | |
}) | |
.filter((s) => s.estimatedMax); | |
return ( | |
<div className="relative h-full w-full" ref={ref}> | |
{bounds.width > 0 && ( | |
<ChartInner data={data} width={bounds.width} height={bounds.height} /> | |
)} | |
</div> | |
); | |
} | |
function ChartInner({ data, width, height }) { | |
let margin = { | |
top: 10, | |
right: 10, | |
bottom: 20, | |
left: 24, | |
}; | |
let startDay = startOfMonth(data.at(0).date); | |
let endDay = endOfMonth(data.at(-1).date); | |
let months = eachMonthOfInterval({ start: startDay, end: endDay }); | |
let xScale = d3 | |
.scaleTime() | |
.domain([startDay, endDay]) | |
.range([margin.left, width - margin.right]); | |
let yScale = d3 | |
.scaleLinear() | |
.domain(d3.extent(data.map((d) => d.estimatedMax))) | |
.range([height - margin.bottom, margin.top]); | |
let line = d3 | |
.line() | |
.x((d) => xScale(d.date)) | |
.y((d) => yScale(d.estimatedMax)); | |
let d = line(data); | |
return ( | |
<> | |
<svg className="" viewBox={`0 0 ${width} ${height}`}> | |
{/* X axis */} | |
{months.map((month, i) => ( | |
<g | |
key={month} | |
className="text-gray-400" | |
transform={`translate(${xScale(month)},0)`} | |
> | |
{i % 2 === 1 && ( | |
<rect | |
width={xScale(endOfMonth(month)) - xScale(month)} | |
height={height - margin.bottom} | |
fill="currentColor" | |
className="text-gray-100" | |
/> | |
)} | |
<text | |
x={(xScale(endOfMonth(month)) - xScale(month)) / 2} | |
y={height - 5} | |
textAnchor="middle" | |
fill="currentColor" | |
className="text-[10px]" | |
> | |
{format(month, "MMM")} | |
</text> | |
</g> | |
))} | |
{/* Y axis */} | |
{yScale.ticks(5).map((max) => ( | |
<g | |
transform={`translate(0,${yScale(max)})`} | |
className="text-gray-400" | |
key={max} | |
> | |
<line | |
x1={margin.left} | |
x2={width - margin.right} | |
stroke="currentColor" | |
strokeDasharray="1,3" | |
/> | |
<text | |
alignmentBaseline="middle" | |
className="text-[10px]" | |
fill="currentColor" | |
> | |
{max} | |
</text> | |
</g> | |
))} | |
{/* Line */} | |
<motion.path | |
initial={{ pathLength: 0 }} | |
animate={{ pathLength: 1 }} | |
transition={{ duration: 1.5, type: "spring" }} | |
d={d} | |
fill="none" | |
stroke="currentColor" | |
strokeWidth="2" | |
/> | |
{/* Circles */} | |
{data.map((d, i) => ( | |
<motion.circle | |
key={d.date} | |
r="5" | |
cx={xScale(d.date)} | |
cy={yScale(d.estimatedMax)} | |
fill="currentColor" | |
strokeWidth={2} | |
stroke={ | |
months.findIndex((m) => isSameMonth(m, d.date)) % 2 === 1 | |
? "#f5f5f4" | |
: "white" | |
} | |
/> | |
))} | |
</svg> | |
</> | |
); | |
} |
Oh should have in-lined that – here it is, you can just replace the import with this:
export default function estimatedMax(set) {
if (+set.reps <= 0) {
return 0;
}
// Jim Wendler formula
return +set.weight * +set.reps * 0.0333 + +set.weight;
}
It's specific to the project, it's not a React thing btw :) Lemme know if that makes sense!
i tried replacing it and this is the error im getting
TypeError
Cannot assign to read only property 'message' of object 'SyntaxError: /src/index.js: Only one default export allowed per module. (21:0)
19 | }
20 |
21 | export default function Chart({ entries }) {
| ^
22 | let [ref, bounds] = useMeasure();
23 |
24 | if ('
this is the original error before replacing it :
ModuleNotFoundError
Could not find module in path: '~/utils/estimated-max' relative to '/src/index.js'
▶ 3 stack frames were collapsed.
$csb$eval
/src/index.js:12
9 | } from "date-fns";
10 | import useMeasure from "react-use-measure";
11 | import { motion } from "framer-motion";
12 | import estimatedMax from "~/utils/estimated-max";
13 |
14 | export default function Chart({ entries }) {
15 | let [ref, bounds] = useMeasure();
Hello, great video on YouTube. One question, what is this utils file? ~/utils/estimated-max , I'm still studying React and haven't been able to figure out what it is. Thanks.