react-muscle-stats 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +73 -0
  2. package/dist/components/MuscleRadar/index.cjs.js +84 -0
  3. package/dist/components/MuscleRadar/index.cjs.js.map +1 -0
  4. package/dist/components/MuscleRadar/index.d.ts +43 -0
  5. package/dist/components/MuscleRadar/index.d.ts.map +1 -0
  6. package/dist/components/MuscleRadar/index.es.js +83 -0
  7. package/dist/components/MuscleRadar/index.es.js.map +1 -0
  8. package/dist/components/MuscleRadar/normalization.cjs.js +100 -0
  9. package/dist/components/MuscleRadar/normalization.cjs.js.map +1 -0
  10. package/dist/components/MuscleRadar/normalization.d.ts +21 -0
  11. package/dist/components/MuscleRadar/normalization.d.ts.map +1 -0
  12. package/dist/components/MuscleRadar/normalization.es.js +99 -0
  13. package/dist/components/MuscleRadar/normalization.es.js.map +1 -0
  14. package/dist/components/MuscleVisualizer/index.cjs.js +49 -0
  15. package/dist/components/MuscleVisualizer/index.cjs.js.map +1 -0
  16. package/dist/components/MuscleVisualizer/index.d.ts +32 -0
  17. package/dist/components/MuscleVisualizer/index.d.ts.map +1 -0
  18. package/dist/components/MuscleVisualizer/index.es.js +48 -0
  19. package/dist/components/MuscleVisualizer/index.es.js.map +1 -0
  20. package/dist/components/MuscleVisualizer/svgs.cjs.js +2274 -0
  21. package/dist/components/MuscleVisualizer/svgs.cjs.js.map +1 -0
  22. package/dist/components/MuscleVisualizer/svgs.d.ts +2 -0
  23. package/dist/components/MuscleVisualizer/svgs.d.ts.map +1 -0
  24. package/dist/components/MuscleVisualizer/svgs.es.js +2274 -0
  25. package/dist/components/MuscleVisualizer/svgs.es.js.map +1 -0
  26. package/dist/components/OneRepMaxChart/index.cjs.js +132 -0
  27. package/dist/components/OneRepMaxChart/index.cjs.js.map +1 -0
  28. package/dist/components/OneRepMaxChart/index.d.ts +30 -0
  29. package/dist/components/OneRepMaxChart/index.d.ts.map +1 -0
  30. package/dist/components/OneRepMaxChart/index.es.js +127 -0
  31. package/dist/components/OneRepMaxChart/index.es.js.map +1 -0
  32. package/dist/components/VolumeChart/index.cjs.js +155 -0
  33. package/dist/components/VolumeChart/index.cjs.js.map +1 -0
  34. package/dist/components/VolumeChart/index.d.ts +30 -0
  35. package/dist/components/VolumeChart/index.d.ts.map +1 -0
  36. package/dist/components/VolumeChart/index.es.js +150 -0
  37. package/dist/components/VolumeChart/index.es.js.map +1 -0
  38. package/dist/index.cjs.js +16 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.es.js +8 -0
  42. package/dist/utils/math.cjs.js +16 -0
  43. package/dist/utils/math.cjs.js.map +1 -0
  44. package/dist/utils/math.d.ts +11 -0
  45. package/dist/utils/math.d.ts.map +1 -0
  46. package/dist/utils/math.es.js +16 -0
  47. package/dist/utils/math.es.js.map +1 -0
  48. package/package.json +87 -0
@@ -0,0 +1,132 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let recharts = require("recharts");
7
+ var tooltipWrapperStyle = {
8
+ backgroundColor: "#1e1e2f",
9
+ border: "1px solid #3a3a5c",
10
+ borderRadius: 8,
11
+ padding: "8px 12px",
12
+ color: "#fff",
13
+ fontSize: 13,
14
+ lineHeight: 1.4
15
+ };
16
+ function OneRepMaxTooltip({ active, payload, label, unit }) {
17
+ if (!active || !payload?.length) return null;
18
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19
+ style: tooltipWrapperStyle,
20
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
21
+ style: {
22
+ margin: 0,
23
+ fontWeight: 600
24
+ },
25
+ children: label
26
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
27
+ style: {
28
+ margin: 0,
29
+ opacity: .85
30
+ },
31
+ children: [
32
+ payload[0].value,
33
+ " ",
34
+ unit
35
+ ]
36
+ })]
37
+ });
38
+ }
39
+ var emptyStyle = {
40
+ display: "flex",
41
+ alignItems: "center",
42
+ justifyContent: "center",
43
+ color: "#888",
44
+ fontSize: 14,
45
+ height: 200
46
+ };
47
+ /**
48
+ * A sleek `<LineChart>` for tracking estimated one-rep-max progress
49
+ * over time. Inspired by the Hevy app's performance charts.
50
+ */
51
+ function OneRepMaxChart({ data, lineColor = "#6C63FF", unit = "lbs", width = "100%", height = 300, style, className }) {
52
+ if (data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
53
+ className,
54
+ style: {
55
+ ...emptyStyle,
56
+ ...style
57
+ },
58
+ children: "No 1RM data available"
59
+ });
60
+ const glowFilter = `drop-shadow(0 0 6px ${lineColor})`;
61
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
62
+ className,
63
+ style: {
64
+ width,
65
+ ...style
66
+ },
67
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.ResponsiveContainer, {
68
+ width: "100%",
69
+ height,
70
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(recharts.LineChart, {
71
+ data,
72
+ margin: {
73
+ top: 10,
74
+ right: 20,
75
+ bottom: 0,
76
+ left: 0
77
+ },
78
+ children: [
79
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.XAxis, {
80
+ dataKey: "date",
81
+ axisLine: false,
82
+ tickLine: false,
83
+ tick: {
84
+ fontSize: 11,
85
+ fill: "#aaa"
86
+ }
87
+ }),
88
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.YAxis, {
89
+ axisLine: false,
90
+ tickLine: false,
91
+ tick: {
92
+ fontSize: 11,
93
+ fill: "#aaa"
94
+ },
95
+ domain: ["dataMin - 10", "dataMax + 10"]
96
+ }),
97
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Tooltip, {
98
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OneRepMaxTooltip, { unit }),
99
+ cursor: {
100
+ stroke: "#555",
101
+ strokeDasharray: "4 4"
102
+ }
103
+ }),
104
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Line, {
105
+ type: "monotone",
106
+ dataKey: "weight",
107
+ stroke: lineColor,
108
+ strokeWidth: 2.5,
109
+ dot: {
110
+ r: 4,
111
+ fill: lineColor,
112
+ stroke: "#fff",
113
+ strokeWidth: 2,
114
+ filter: glowFilter
115
+ },
116
+ activeDot: {
117
+ r: 7,
118
+ fill: lineColor,
119
+ stroke: "#fff",
120
+ strokeWidth: 2,
121
+ filter: glowFilter
122
+ }
123
+ })
124
+ ]
125
+ })
126
+ })
127
+ });
128
+ }
129
+ exports.OneRepMaxChart = OneRepMaxChart;
130
+ exports.default = OneRepMaxChart;
131
+
132
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","names":[],"sources":["../../../src/components/OneRepMaxChart/index.tsx"],"sourcesContent":["import type { CSSProperties } from 'react';\r\nimport {\r\n LineChart,\r\n Line,\r\n XAxis,\r\n YAxis,\r\n Tooltip,\r\n ResponsiveContainer,\r\n type TooltipProps,\r\n} from 'recharts';\r\n\r\nexport interface OneRepMaxDataPoint {\r\n /** Date string displayed on the X axis (e.g. \"Jan 1\" or \"2026-01-15\"). */\r\n date: string;\r\n /** Estimated or actual 1RM weight value. */\r\n weight: number;\r\n}\r\n\r\nexport interface OneRepMaxChartProps {\r\n /** Array of data points to plot. */\r\n data: OneRepMaxDataPoint[];\r\n /** Stroke / dot colour for the line (default: `'#6C63FF'`). */\r\n lineColor?: string;\r\n /** Weight unit shown in the tooltip (default: `'lbs'`). */\r\n unit?: 'lbs' | 'kg';\r\n /** Width of the responsive container (default: `'100%'`). */\r\n width?: string | number;\r\n /** Height of the responsive container (default: `300`). */\r\n height?: number;\r\n /** Optional inline styles for the outer wrapper. */\r\n style?: CSSProperties;\r\n /** Optional CSS class name for the outer wrapper. */\r\n className?: string;\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Custom dark-mode tooltip */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst tooltipWrapperStyle: CSSProperties = {\r\n backgroundColor: '#1e1e2f',\r\n border: '1px solid #3a3a5c',\r\n borderRadius: 8,\r\n padding: '8px 12px',\r\n color: '#fff',\r\n fontSize: 13,\r\n lineHeight: 1.4,\r\n};\r\n\r\nfunction OneRepMaxTooltip({\r\n active,\r\n payload,\r\n label,\r\n unit,\r\n}: TooltipProps<number, string> & { unit: string }) {\r\n if (!active || !payload?.length) return null;\r\n return (\r\n <div style={tooltipWrapperStyle}>\r\n <p style={{ margin: 0, fontWeight: 600 }}>{label}</p>\r\n <p style={{ margin: 0, opacity: 0.85 }}>\r\n {payload[0].value} {unit}\r\n </p>\r\n </div>\r\n );\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Empty-state fallback */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst emptyStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#888',\r\n fontSize: 14,\r\n height: 200,\r\n};\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Component */\r\n/* ------------------------------------------------------------------ */\r\n\r\n/**\r\n * A sleek `<LineChart>` for tracking estimated one-rep-max progress\r\n * over time. Inspired by the Hevy app's performance charts.\r\n */\r\nexport function OneRepMaxChart({\r\n data,\r\n lineColor = '#6C63FF',\r\n unit = 'lbs',\r\n width = '100%',\r\n height = 300,\r\n style,\r\n className,\r\n}: OneRepMaxChartProps) {\r\n if (data.length === 0) {\r\n return (\r\n <div className={className} style={{ ...emptyStyle, ...style }}>\r\n No 1RM data available\r\n </div>\r\n );\r\n }\r\n\r\n const glowFilter = `drop-shadow(0 0 6px ${lineColor})`;\r\n\r\n return (\r\n <div className={className} style={{ width, ...style }}>\r\n <ResponsiveContainer width=\"100%\" height={height}>\r\n <LineChart data={data} margin={{ top: 10, right: 20, bottom: 0, left: 0 }}>\r\n <XAxis\r\n dataKey=\"date\"\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n />\r\n <YAxis\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n domain={['dataMin - 10', 'dataMax + 10']}\r\n />\r\n <Tooltip\r\n content={<OneRepMaxTooltip unit={unit} />}\r\n cursor={{ stroke: '#555', strokeDasharray: '4 4' }}\r\n />\r\n <Line\r\n type=\"monotone\"\r\n dataKey=\"weight\"\r\n stroke={lineColor}\r\n strokeWidth={2.5}\r\n dot={{\r\n r: 4,\r\n fill: lineColor,\r\n stroke: '#fff',\r\n strokeWidth: 2,\r\n filter: glowFilter,\r\n }}\r\n activeDot={{\r\n r: 7,\r\n fill: lineColor,\r\n stroke: '#fff',\r\n strokeWidth: 2,\r\n filter: glowFilter,\r\n }}\r\n />\r\n </LineChart>\r\n </ResponsiveContainer>\r\n </div>\r\n );\r\n}\r\n\r\nexport default OneRepMaxChart;\r\n"],"mappings":";;;;;;AAuCA,IAAM,sBAAqC;CACzC,iBAAiB;CACjB,QAAQ;CACR,cAAc;CACd,SAAS;CACT,OAAO;CACP,UAAU;CACV,YAAY;CACb;AAED,SAAS,iBAAiB,EACxB,QACA,SACA,OACA,QACkD;AAClD,KAAI,CAAC,UAAU,CAAC,SAAS,OAAQ,QAAO;AACxC,QACE,iBAAA,GAAA,kBAAA,MAAC,OAAD;EAAK,OAAO;YAAZ,CACE,iBAAA,GAAA,kBAAA,KAAC,KAAD;GAAG,OAAO;IAAE,QAAQ;IAAG,YAAY;IAAK;aAAG;GAAU,CAAA,EACrD,iBAAA,GAAA,kBAAA,MAAC,KAAD;GAAG,OAAO;IAAE,QAAQ;IAAG,SAAS;IAAM;aAAtC;IACG,QAAQ,GAAG;IAAM;IAAE;IAClB;KACA;;;AAQV,IAAM,aAA4B;CAChC,SAAS;CACT,YAAY;CACZ,gBAAgB;CAChB,OAAO;CACP,UAAU;CACV,QAAQ;CACT;;;;;AAUD,SAAgB,eAAe,EAC7B,MACA,YAAY,WACZ,OAAO,OACP,QAAQ,QACR,SAAS,KACT,OACA,aACsB;AACtB,KAAI,KAAK,WAAW,EAClB,QACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAgB;EAAW,OAAO;GAAE,GAAG;GAAY,GAAG;GAAO;YAAE;EAEzD,CAAA;CAIV,MAAM,aAAa,uBAAuB,UAAU;AAEpD,QACE,iBAAA,GAAA,kBAAA,KAAC,OAAD;EAAgB;EAAW,OAAO;GAAE;GAAO,GAAG;GAAO;YACnD,iBAAA,GAAA,kBAAA,KAAC,SAAA,qBAAD;GAAqB,OAAM;GAAe;aACxC,iBAAA,GAAA,kBAAA,MAAC,SAAA,WAAD;IAAiB;IAAM,QAAQ;KAAE,KAAK;KAAI,OAAO;KAAI,QAAQ;KAAG,MAAM;KAAG;cAAzE;KACE,iBAAA,GAAA,kBAAA,KAAC,SAAA,OAAD;MACE,SAAQ;MACR,UAAU;MACV,UAAU;MACV,MAAM;OAAE,UAAU;OAAI,MAAM;OAAQ;MACpC,CAAA;KACF,iBAAA,GAAA,kBAAA,KAAC,SAAA,OAAD;MACE,UAAU;MACV,UAAU;MACV,MAAM;OAAE,UAAU;OAAI,MAAM;OAAQ;MACpC,QAAQ,CAAC,gBAAgB,eAAe;MACxC,CAAA;KACF,iBAAA,GAAA,kBAAA,KAAC,SAAA,SAAD;MACE,SAAS,iBAAA,GAAA,kBAAA,KAAC,kBAAD,EAAwB,MAAQ,CAAA;MACzC,QAAQ;OAAE,QAAQ;OAAQ,iBAAiB;OAAO;MAClD,CAAA;KACF,iBAAA,GAAA,kBAAA,KAAC,SAAA,MAAD;MACE,MAAK;MACL,SAAQ;MACR,QAAQ;MACR,aAAa;MACb,KAAK;OACH,GAAG;OACH,MAAM;OACN,QAAQ;OACR,aAAa;OACb,QAAQ;OACT;MACD,WAAW;OACT,GAAG;OACH,MAAM;OACN,QAAQ;OACR,aAAa;OACb,QAAQ;OACT;MACD,CAAA;KACQ;;GACQ,CAAA;EAClB,CAAA"}
@@ -0,0 +1,30 @@
1
+ import { CSSProperties } from 'react';
2
+ export interface OneRepMaxDataPoint {
3
+ /** Date string displayed on the X axis (e.g. "Jan 1" or "2026-01-15"). */
4
+ date: string;
5
+ /** Estimated or actual 1RM weight value. */
6
+ weight: number;
7
+ }
8
+ export interface OneRepMaxChartProps {
9
+ /** Array of data points to plot. */
10
+ data: OneRepMaxDataPoint[];
11
+ /** Stroke / dot colour for the line (default: `'#6C63FF'`). */
12
+ lineColor?: string;
13
+ /** Weight unit shown in the tooltip (default: `'lbs'`). */
14
+ unit?: 'lbs' | 'kg';
15
+ /** Width of the responsive container (default: `'100%'`). */
16
+ width?: string | number;
17
+ /** Height of the responsive container (default: `300`). */
18
+ height?: number;
19
+ /** Optional inline styles for the outer wrapper. */
20
+ style?: CSSProperties;
21
+ /** Optional CSS class name for the outer wrapper. */
22
+ className?: string;
23
+ }
24
+ /**
25
+ * A sleek `<LineChart>` for tracking estimated one-rep-max progress
26
+ * over time. Inspired by the Hevy app's performance charts.
27
+ */
28
+ export declare function OneRepMaxChart({ data, lineColor, unit, width, height, style, className, }: OneRepMaxChartProps): import("react/jsx-runtime").JSX.Element;
29
+ export default OneRepMaxChart;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/OneRepMaxChart/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAW3C,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkDD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,SAAqB,EACrB,IAAY,EACZ,KAAc,EACd,MAAY,EACZ,KAAK,EACL,SAAS,GACV,EAAE,mBAAmB,2CAuDrB;AAED,eAAe,cAAc,CAAC"}
@@ -0,0 +1,127 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
3
+ var tooltipWrapperStyle = {
4
+ backgroundColor: "#1e1e2f",
5
+ border: "1px solid #3a3a5c",
6
+ borderRadius: 8,
7
+ padding: "8px 12px",
8
+ color: "#fff",
9
+ fontSize: 13,
10
+ lineHeight: 1.4
11
+ };
12
+ function OneRepMaxTooltip({ active, payload, label, unit }) {
13
+ if (!active || !payload?.length) return null;
14
+ return /* @__PURE__ */ jsxs("div", {
15
+ style: tooltipWrapperStyle,
16
+ children: [/* @__PURE__ */ jsx("p", {
17
+ style: {
18
+ margin: 0,
19
+ fontWeight: 600
20
+ },
21
+ children: label
22
+ }), /* @__PURE__ */ jsxs("p", {
23
+ style: {
24
+ margin: 0,
25
+ opacity: .85
26
+ },
27
+ children: [
28
+ payload[0].value,
29
+ " ",
30
+ unit
31
+ ]
32
+ })]
33
+ });
34
+ }
35
+ var emptyStyle = {
36
+ display: "flex",
37
+ alignItems: "center",
38
+ justifyContent: "center",
39
+ color: "#888",
40
+ fontSize: 14,
41
+ height: 200
42
+ };
43
+ /**
44
+ * A sleek `<LineChart>` for tracking estimated one-rep-max progress
45
+ * over time. Inspired by the Hevy app's performance charts.
46
+ */
47
+ function OneRepMaxChart({ data, lineColor = "#6C63FF", unit = "lbs", width = "100%", height = 300, style, className }) {
48
+ if (data.length === 0) return /* @__PURE__ */ jsx("div", {
49
+ className,
50
+ style: {
51
+ ...emptyStyle,
52
+ ...style
53
+ },
54
+ children: "No 1RM data available"
55
+ });
56
+ const glowFilter = `drop-shadow(0 0 6px ${lineColor})`;
57
+ return /* @__PURE__ */ jsx("div", {
58
+ className,
59
+ style: {
60
+ width,
61
+ ...style
62
+ },
63
+ children: /* @__PURE__ */ jsx(ResponsiveContainer, {
64
+ width: "100%",
65
+ height,
66
+ children: /* @__PURE__ */ jsxs(LineChart, {
67
+ data,
68
+ margin: {
69
+ top: 10,
70
+ right: 20,
71
+ bottom: 0,
72
+ left: 0
73
+ },
74
+ children: [
75
+ /* @__PURE__ */ jsx(XAxis, {
76
+ dataKey: "date",
77
+ axisLine: false,
78
+ tickLine: false,
79
+ tick: {
80
+ fontSize: 11,
81
+ fill: "#aaa"
82
+ }
83
+ }),
84
+ /* @__PURE__ */ jsx(YAxis, {
85
+ axisLine: false,
86
+ tickLine: false,
87
+ tick: {
88
+ fontSize: 11,
89
+ fill: "#aaa"
90
+ },
91
+ domain: ["dataMin - 10", "dataMax + 10"]
92
+ }),
93
+ /* @__PURE__ */ jsx(Tooltip, {
94
+ content: /* @__PURE__ */ jsx(OneRepMaxTooltip, { unit }),
95
+ cursor: {
96
+ stroke: "#555",
97
+ strokeDasharray: "4 4"
98
+ }
99
+ }),
100
+ /* @__PURE__ */ jsx(Line, {
101
+ type: "monotone",
102
+ dataKey: "weight",
103
+ stroke: lineColor,
104
+ strokeWidth: 2.5,
105
+ dot: {
106
+ r: 4,
107
+ fill: lineColor,
108
+ stroke: "#fff",
109
+ strokeWidth: 2,
110
+ filter: glowFilter
111
+ },
112
+ activeDot: {
113
+ r: 7,
114
+ fill: lineColor,
115
+ stroke: "#fff",
116
+ strokeWidth: 2,
117
+ filter: glowFilter
118
+ }
119
+ })
120
+ ]
121
+ })
122
+ })
123
+ });
124
+ }
125
+ export { OneRepMaxChart, OneRepMaxChart as default };
126
+
127
+ //# sourceMappingURL=index.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../../../src/components/OneRepMaxChart/index.tsx"],"sourcesContent":["import type { CSSProperties } from 'react';\r\nimport {\r\n LineChart,\r\n Line,\r\n XAxis,\r\n YAxis,\r\n Tooltip,\r\n ResponsiveContainer,\r\n type TooltipProps,\r\n} from 'recharts';\r\n\r\nexport interface OneRepMaxDataPoint {\r\n /** Date string displayed on the X axis (e.g. \"Jan 1\" or \"2026-01-15\"). */\r\n date: string;\r\n /** Estimated or actual 1RM weight value. */\r\n weight: number;\r\n}\r\n\r\nexport interface OneRepMaxChartProps {\r\n /** Array of data points to plot. */\r\n data: OneRepMaxDataPoint[];\r\n /** Stroke / dot colour for the line (default: `'#6C63FF'`). */\r\n lineColor?: string;\r\n /** Weight unit shown in the tooltip (default: `'lbs'`). */\r\n unit?: 'lbs' | 'kg';\r\n /** Width of the responsive container (default: `'100%'`). */\r\n width?: string | number;\r\n /** Height of the responsive container (default: `300`). */\r\n height?: number;\r\n /** Optional inline styles for the outer wrapper. */\r\n style?: CSSProperties;\r\n /** Optional CSS class name for the outer wrapper. */\r\n className?: string;\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Custom dark-mode tooltip */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst tooltipWrapperStyle: CSSProperties = {\r\n backgroundColor: '#1e1e2f',\r\n border: '1px solid #3a3a5c',\r\n borderRadius: 8,\r\n padding: '8px 12px',\r\n color: '#fff',\r\n fontSize: 13,\r\n lineHeight: 1.4,\r\n};\r\n\r\nfunction OneRepMaxTooltip({\r\n active,\r\n payload,\r\n label,\r\n unit,\r\n}: TooltipProps<number, string> & { unit: string }) {\r\n if (!active || !payload?.length) return null;\r\n return (\r\n <div style={tooltipWrapperStyle}>\r\n <p style={{ margin: 0, fontWeight: 600 }}>{label}</p>\r\n <p style={{ margin: 0, opacity: 0.85 }}>\r\n {payload[0].value} {unit}\r\n </p>\r\n </div>\r\n );\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Empty-state fallback */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst emptyStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#888',\r\n fontSize: 14,\r\n height: 200,\r\n};\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Component */\r\n/* ------------------------------------------------------------------ */\r\n\r\n/**\r\n * A sleek `<LineChart>` for tracking estimated one-rep-max progress\r\n * over time. Inspired by the Hevy app's performance charts.\r\n */\r\nexport function OneRepMaxChart({\r\n data,\r\n lineColor = '#6C63FF',\r\n unit = 'lbs',\r\n width = '100%',\r\n height = 300,\r\n style,\r\n className,\r\n}: OneRepMaxChartProps) {\r\n if (data.length === 0) {\r\n return (\r\n <div className={className} style={{ ...emptyStyle, ...style }}>\r\n No 1RM data available\r\n </div>\r\n );\r\n }\r\n\r\n const glowFilter = `drop-shadow(0 0 6px ${lineColor})`;\r\n\r\n return (\r\n <div className={className} style={{ width, ...style }}>\r\n <ResponsiveContainer width=\"100%\" height={height}>\r\n <LineChart data={data} margin={{ top: 10, right: 20, bottom: 0, left: 0 }}>\r\n <XAxis\r\n dataKey=\"date\"\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n />\r\n <YAxis\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n domain={['dataMin - 10', 'dataMax + 10']}\r\n />\r\n <Tooltip\r\n content={<OneRepMaxTooltip unit={unit} />}\r\n cursor={{ stroke: '#555', strokeDasharray: '4 4' }}\r\n />\r\n <Line\r\n type=\"monotone\"\r\n dataKey=\"weight\"\r\n stroke={lineColor}\r\n strokeWidth={2.5}\r\n dot={{\r\n r: 4,\r\n fill: lineColor,\r\n stroke: '#fff',\r\n strokeWidth: 2,\r\n filter: glowFilter,\r\n }}\r\n activeDot={{\r\n r: 7,\r\n fill: lineColor,\r\n stroke: '#fff',\r\n strokeWidth: 2,\r\n filter: glowFilter,\r\n }}\r\n />\r\n </LineChart>\r\n </ResponsiveContainer>\r\n </div>\r\n );\r\n}\r\n\r\nexport default OneRepMaxChart;\r\n"],"mappings":";;AAuCA,IAAM,sBAAqC;CACzC,iBAAiB;CACjB,QAAQ;CACR,cAAc;CACd,SAAS;CACT,OAAO;CACP,UAAU;CACV,YAAY;CACb;AAED,SAAS,iBAAiB,EACxB,QACA,SACA,OACA,QACkD;AAClD,KAAI,CAAC,UAAU,CAAC,SAAS,OAAQ,QAAO;AACxC,QACE,qBAAC,OAAD;EAAK,OAAO;YAAZ,CACE,oBAAC,KAAD;GAAG,OAAO;IAAE,QAAQ;IAAG,YAAY;IAAK;aAAG;GAAU,CAAA,EACrD,qBAAC,KAAD;GAAG,OAAO;IAAE,QAAQ;IAAG,SAAS;IAAM;aAAtC;IACG,QAAQ,GAAG;IAAM;IAAE;IAClB;KACA;;;AAQV,IAAM,aAA4B;CAChC,SAAS;CACT,YAAY;CACZ,gBAAgB;CAChB,OAAO;CACP,UAAU;CACV,QAAQ;CACT;;;;;AAUD,SAAgB,eAAe,EAC7B,MACA,YAAY,WACZ,OAAO,OACP,QAAQ,QACR,SAAS,KACT,OACA,aACsB;AACtB,KAAI,KAAK,WAAW,EAClB,QACE,oBAAC,OAAD;EAAgB;EAAW,OAAO;GAAE,GAAG;GAAY,GAAG;GAAO;YAAE;EAEzD,CAAA;CAIV,MAAM,aAAa,uBAAuB,UAAU;AAEpD,QACE,oBAAC,OAAD;EAAgB;EAAW,OAAO;GAAE;GAAO,GAAG;GAAO;YACnD,oBAAC,qBAAD;GAAqB,OAAM;GAAe;aACxC,qBAAC,WAAD;IAAiB;IAAM,QAAQ;KAAE,KAAK;KAAI,OAAO;KAAI,QAAQ;KAAG,MAAM;KAAG;cAAzE;KACE,oBAAC,OAAD;MACE,SAAQ;MACR,UAAU;MACV,UAAU;MACV,MAAM;OAAE,UAAU;OAAI,MAAM;OAAQ;MACpC,CAAA;KACF,oBAAC,OAAD;MACE,UAAU;MACV,UAAU;MACV,MAAM;OAAE,UAAU;OAAI,MAAM;OAAQ;MACpC,QAAQ,CAAC,gBAAgB,eAAe;MACxC,CAAA;KACF,oBAAC,SAAD;MACE,SAAS,oBAAC,kBAAD,EAAwB,MAAQ,CAAA;MACzC,QAAQ;OAAE,QAAQ;OAAQ,iBAAiB;OAAO;MAClD,CAAA;KACF,oBAAC,MAAD;MACE,MAAK;MACL,SAAQ;MACR,QAAQ;MACR,aAAa;MACb,KAAK;OACH,GAAG;OACH,MAAM;OACN,QAAQ;OACR,aAAa;OACb,QAAQ;OACT;MACD,WAAW;OACT,GAAG;OACH,MAAM;OACN,QAAQ;OACR,aAAa;OACb,QAAQ;OACT;MACD,CAAA;KACQ;;GACQ,CAAA;EAClB,CAAA"}
@@ -0,0 +1,155 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let recharts = require("recharts");
7
+ /** Formats large numbers with a "k" suffix (e.g. 12 500 → "12.5k"). */
8
+ function formatVolumeTick(value) {
9
+ if (value >= 1e3) {
10
+ const k = value / 1e3;
11
+ return `${Number.isInteger(k) ? k : k.toFixed(1)}k`;
12
+ }
13
+ return String(value);
14
+ }
15
+ var tooltipWrapperStyle = {
16
+ backgroundColor: "#1e1e2f",
17
+ border: "1px solid #3a3a5c",
18
+ borderRadius: 8,
19
+ padding: "8px 12px",
20
+ color: "#fff",
21
+ fontSize: 13,
22
+ lineHeight: 1.4
23
+ };
24
+ function VolumeTooltip({ active, payload, label, unit }) {
25
+ if (!active || !payload?.length) return null;
26
+ const vol = payload[0].value;
27
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
28
+ style: tooltipWrapperStyle,
29
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
30
+ style: {
31
+ margin: 0,
32
+ fontWeight: 600
33
+ },
34
+ children: label
35
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
36
+ style: {
37
+ margin: 0,
38
+ opacity: .85
39
+ },
40
+ children: [
41
+ vol.toLocaleString(),
42
+ " ",
43
+ unit
44
+ ]
45
+ })]
46
+ });
47
+ }
48
+ var emptyStyle = {
49
+ display: "flex",
50
+ alignItems: "center",
51
+ justifyContent: "center",
52
+ color: "#888",
53
+ fontSize: 14,
54
+ height: 200
55
+ };
56
+ /**
57
+ * A Recharts `<AreaChart>` with a fading gradient fill for tracking
58
+ * training volume over time. Inspired by the Hevy app.
59
+ */
60
+ function VolumeChart({ data, fillColor = "#00C49F", unit = "lbs", width = "100%", height = 300, style, className }) {
61
+ if (data.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
62
+ className,
63
+ style: {
64
+ ...emptyStyle,
65
+ ...style
66
+ },
67
+ children: "No volume data available"
68
+ });
69
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
70
+ className,
71
+ style: {
72
+ width,
73
+ ...style
74
+ },
75
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.ResponsiveContainer, {
76
+ width: "100%",
77
+ height,
78
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(recharts.AreaChart, {
79
+ data,
80
+ margin: {
81
+ top: 10,
82
+ right: 20,
83
+ bottom: 0,
84
+ left: 0
85
+ },
86
+ children: [
87
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
88
+ id: "colorVolume",
89
+ x1: "0",
90
+ y1: "0",
91
+ x2: "0",
92
+ y2: "1",
93
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
94
+ offset: "5%",
95
+ stopColor: fillColor,
96
+ stopOpacity: .4
97
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
98
+ offset: "95%",
99
+ stopColor: fillColor,
100
+ stopOpacity: .02
101
+ })]
102
+ }) }),
103
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.XAxis, {
104
+ dataKey: "date",
105
+ axisLine: false,
106
+ tickLine: false,
107
+ tick: {
108
+ fontSize: 11,
109
+ fill: "#aaa"
110
+ }
111
+ }),
112
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.YAxis, {
113
+ axisLine: false,
114
+ tickLine: false,
115
+ tick: {
116
+ fontSize: 11,
117
+ fill: "#aaa"
118
+ },
119
+ tickFormatter: formatVolumeTick
120
+ }),
121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Tooltip, {
122
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VolumeTooltip, { unit }),
123
+ cursor: {
124
+ stroke: "#555",
125
+ strokeDasharray: "4 4"
126
+ }
127
+ }),
128
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(recharts.Area, {
129
+ type: "monotone",
130
+ dataKey: "volume",
131
+ stroke: fillColor,
132
+ strokeWidth: 2,
133
+ fill: "url(#colorVolume)",
134
+ dot: {
135
+ r: 3,
136
+ fill: fillColor,
137
+ stroke: "#fff",
138
+ strokeWidth: 1.5
139
+ },
140
+ activeDot: {
141
+ r: 6,
142
+ fill: fillColor,
143
+ stroke: "#fff",
144
+ strokeWidth: 2
145
+ }
146
+ })
147
+ ]
148
+ })
149
+ })
150
+ });
151
+ }
152
+ exports.VolumeChart = VolumeChart;
153
+ exports.default = VolumeChart;
154
+
155
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","names":[],"sources":["../../../src/components/VolumeChart/index.tsx"],"sourcesContent":["import type { CSSProperties } from 'react';\r\nimport {\r\n AreaChart,\r\n Area,\r\n XAxis,\r\n YAxis,\r\n Tooltip,\r\n ResponsiveContainer,\r\n type TooltipProps,\r\n} from 'recharts';\r\n\r\nexport interface VolumeDataPoint {\r\n /** Date string displayed on the X axis. */\r\n date: string;\r\n /** Total volume (sets × reps × weight). */\r\n volume: number;\r\n}\r\n\r\nexport interface VolumeChartProps {\r\n /** Array of data points to plot. */\r\n data: VolumeDataPoint[];\r\n /** Primary fill / stroke colour for the area (default: `'#00C49F'`). */\r\n fillColor?: string;\r\n /** Weight unit shown in the tooltip (default: `'lbs'`). */\r\n unit?: 'lbs' | 'kg';\r\n /** Width of the responsive container (default: `'100%'`). */\r\n width?: string | number;\r\n /** Height of the responsive container (default: `300`). */\r\n height?: number;\r\n /** Optional inline styles for the outer wrapper. */\r\n style?: CSSProperties;\r\n /** Optional CSS class name for the outer wrapper. */\r\n className?: string;\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Helpers */\r\n/* ------------------------------------------------------------------ */\r\n\r\n/** Formats large numbers with a \"k\" suffix (e.g. 12 500 → \"12.5k\"). */\r\nfunction formatVolumeTick(value: number): string {\r\n if (value >= 1000) {\r\n const k = value / 1000;\r\n // Drop the decimal when it's a whole number (e.g. 10k not 10.0k)\r\n return `${Number.isInteger(k) ? k : k.toFixed(1)}k`;\r\n }\r\n return String(value);\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Custom dark-mode tooltip */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst tooltipWrapperStyle: CSSProperties = {\r\n backgroundColor: '#1e1e2f',\r\n border: '1px solid #3a3a5c',\r\n borderRadius: 8,\r\n padding: '8px 12px',\r\n color: '#fff',\r\n fontSize: 13,\r\n lineHeight: 1.4,\r\n};\r\n\r\nfunction VolumeTooltip({\r\n active,\r\n payload,\r\n label,\r\n unit,\r\n}: TooltipProps<number, string> & { unit: string }) {\r\n if (!active || !payload?.length) return null;\r\n const vol = payload[0].value as number;\r\n return (\r\n <div style={tooltipWrapperStyle}>\r\n <p style={{ margin: 0, fontWeight: 600 }}>{label}</p>\r\n <p style={{ margin: 0, opacity: 0.85 }}>\r\n {vol.toLocaleString()} {unit}\r\n </p>\r\n </div>\r\n );\r\n}\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Empty-state fallback */\r\n/* ------------------------------------------------------------------ */\r\n\r\nconst emptyStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: '#888',\r\n fontSize: 14,\r\n height: 200,\r\n};\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Component */\r\n/* ------------------------------------------------------------------ */\r\n\r\n/**\r\n * A Recharts `<AreaChart>` with a fading gradient fill for tracking\r\n * training volume over time. Inspired by the Hevy app.\r\n */\r\nexport function VolumeChart({\r\n data,\r\n fillColor = '#00C49F',\r\n unit = 'lbs',\r\n width = '100%',\r\n height = 300,\r\n style,\r\n className,\r\n}: VolumeChartProps) {\r\n if (data.length === 0) {\r\n return (\r\n <div className={className} style={{ ...emptyStyle, ...style }}>\r\n No volume data available\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className={className} style={{ width, ...style }}>\r\n <ResponsiveContainer width=\"100%\" height={height}>\r\n <AreaChart data={data} margin={{ top: 10, right: 20, bottom: 0, left: 0 }}>\r\n <defs>\r\n <linearGradient id=\"colorVolume\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\r\n <stop offset=\"5%\" stopColor={fillColor} stopOpacity={0.4} />\r\n <stop offset=\"95%\" stopColor={fillColor} stopOpacity={0.02} />\r\n </linearGradient>\r\n </defs>\r\n\r\n <XAxis\r\n dataKey=\"date\"\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n />\r\n <YAxis\r\n axisLine={false}\r\n tickLine={false}\r\n tick={{ fontSize: 11, fill: '#aaa' }}\r\n tickFormatter={formatVolumeTick}\r\n />\r\n <Tooltip\r\n content={<VolumeTooltip unit={unit} />}\r\n cursor={{ stroke: '#555', strokeDasharray: '4 4' }}\r\n />\r\n <Area\r\n type=\"monotone\"\r\n dataKey=\"volume\"\r\n stroke={fillColor}\r\n strokeWidth={2}\r\n fill=\"url(#colorVolume)\"\r\n dot={{ r: 3, fill: fillColor, stroke: '#fff', strokeWidth: 1.5 }}\r\n activeDot={{ r: 6, fill: fillColor, stroke: '#fff', strokeWidth: 2 }}\r\n />\r\n </AreaChart>\r\n </ResponsiveContainer>\r\n </div>\r\n );\r\n}\r\n\r\nexport default VolumeChart;\r\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { CSSProperties } from 'react';
2
+ export interface VolumeDataPoint {
3
+ /** Date string displayed on the X axis. */
4
+ date: string;
5
+ /** Total volume (sets × reps × weight). */
6
+ volume: number;
7
+ }
8
+ export interface VolumeChartProps {
9
+ /** Array of data points to plot. */
10
+ data: VolumeDataPoint[];
11
+ /** Primary fill / stroke colour for the area (default: `'#00C49F'`). */
12
+ fillColor?: string;
13
+ /** Weight unit shown in the tooltip (default: `'lbs'`). */
14
+ unit?: 'lbs' | 'kg';
15
+ /** Width of the responsive container (default: `'100%'`). */
16
+ width?: string | number;
17
+ /** Height of the responsive container (default: `300`). */
18
+ height?: number;
19
+ /** Optional inline styles for the outer wrapper. */
20
+ style?: CSSProperties;
21
+ /** Optional CSS class name for the outer wrapper. */
22
+ className?: string;
23
+ }
24
+ /**
25
+ * A Recharts `<AreaChart>` with a fading gradient fill for tracking
26
+ * training volume over time. Inspired by the Hevy app.
27
+ */
28
+ export declare function VolumeChart({ data, fillColor, unit, width, height, style, className, }: VolumeChartProps): import("react/jsx-runtime").JSX.Element;
29
+ export default VolumeChart;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VolumeChart/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAW3C,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oCAAoC;IACpC,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,SAAqB,EACrB,IAAY,EACZ,KAAc,EACd,MAAY,EACZ,KAAK,EACL,SAAS,GACV,EAAE,gBAAgB,2CAiDlB;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,150 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
3
+ /** Formats large numbers with a "k" suffix (e.g. 12 500 → "12.5k"). */
4
+ function formatVolumeTick(value) {
5
+ if (value >= 1e3) {
6
+ const k = value / 1e3;
7
+ return `${Number.isInteger(k) ? k : k.toFixed(1)}k`;
8
+ }
9
+ return String(value);
10
+ }
11
+ var tooltipWrapperStyle = {
12
+ backgroundColor: "#1e1e2f",
13
+ border: "1px solid #3a3a5c",
14
+ borderRadius: 8,
15
+ padding: "8px 12px",
16
+ color: "#fff",
17
+ fontSize: 13,
18
+ lineHeight: 1.4
19
+ };
20
+ function VolumeTooltip({ active, payload, label, unit }) {
21
+ if (!active || !payload?.length) return null;
22
+ const vol = payload[0].value;
23
+ return /* @__PURE__ */ jsxs("div", {
24
+ style: tooltipWrapperStyle,
25
+ children: [/* @__PURE__ */ jsx("p", {
26
+ style: {
27
+ margin: 0,
28
+ fontWeight: 600
29
+ },
30
+ children: label
31
+ }), /* @__PURE__ */ jsxs("p", {
32
+ style: {
33
+ margin: 0,
34
+ opacity: .85
35
+ },
36
+ children: [
37
+ vol.toLocaleString(),
38
+ " ",
39
+ unit
40
+ ]
41
+ })]
42
+ });
43
+ }
44
+ var emptyStyle = {
45
+ display: "flex",
46
+ alignItems: "center",
47
+ justifyContent: "center",
48
+ color: "#888",
49
+ fontSize: 14,
50
+ height: 200
51
+ };
52
+ /**
53
+ * A Recharts `<AreaChart>` with a fading gradient fill for tracking
54
+ * training volume over time. Inspired by the Hevy app.
55
+ */
56
+ function VolumeChart({ data, fillColor = "#00C49F", unit = "lbs", width = "100%", height = 300, style, className }) {
57
+ if (data.length === 0) return /* @__PURE__ */ jsx("div", {
58
+ className,
59
+ style: {
60
+ ...emptyStyle,
61
+ ...style
62
+ },
63
+ children: "No volume data available"
64
+ });
65
+ return /* @__PURE__ */ jsx("div", {
66
+ className,
67
+ style: {
68
+ width,
69
+ ...style
70
+ },
71
+ children: /* @__PURE__ */ jsx(ResponsiveContainer, {
72
+ width: "100%",
73
+ height,
74
+ children: /* @__PURE__ */ jsxs(AreaChart, {
75
+ data,
76
+ margin: {
77
+ top: 10,
78
+ right: 20,
79
+ bottom: 0,
80
+ left: 0
81
+ },
82
+ children: [
83
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", {
84
+ id: "colorVolume",
85
+ x1: "0",
86
+ y1: "0",
87
+ x2: "0",
88
+ y2: "1",
89
+ children: [/* @__PURE__ */ jsx("stop", {
90
+ offset: "5%",
91
+ stopColor: fillColor,
92
+ stopOpacity: .4
93
+ }), /* @__PURE__ */ jsx("stop", {
94
+ offset: "95%",
95
+ stopColor: fillColor,
96
+ stopOpacity: .02
97
+ })]
98
+ }) }),
99
+ /* @__PURE__ */ jsx(XAxis, {
100
+ dataKey: "date",
101
+ axisLine: false,
102
+ tickLine: false,
103
+ tick: {
104
+ fontSize: 11,
105
+ fill: "#aaa"
106
+ }
107
+ }),
108
+ /* @__PURE__ */ jsx(YAxis, {
109
+ axisLine: false,
110
+ tickLine: false,
111
+ tick: {
112
+ fontSize: 11,
113
+ fill: "#aaa"
114
+ },
115
+ tickFormatter: formatVolumeTick
116
+ }),
117
+ /* @__PURE__ */ jsx(Tooltip, {
118
+ content: /* @__PURE__ */ jsx(VolumeTooltip, { unit }),
119
+ cursor: {
120
+ stroke: "#555",
121
+ strokeDasharray: "4 4"
122
+ }
123
+ }),
124
+ /* @__PURE__ */ jsx(Area, {
125
+ type: "monotone",
126
+ dataKey: "volume",
127
+ stroke: fillColor,
128
+ strokeWidth: 2,
129
+ fill: "url(#colorVolume)",
130
+ dot: {
131
+ r: 3,
132
+ fill: fillColor,
133
+ stroke: "#fff",
134
+ strokeWidth: 1.5
135
+ },
136
+ activeDot: {
137
+ r: 6,
138
+ fill: fillColor,
139
+ stroke: "#fff",
140
+ strokeWidth: 2
141
+ }
142
+ })
143
+ ]
144
+ })
145
+ })
146
+ });
147
+ }
148
+ export { VolumeChart, VolumeChart as default };
149
+
150
+ //# sourceMappingURL=index.es.js.map