@optilogic/charts 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/index.cjs +327 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +240 -0
- package/dist/index.d.ts +240 -0
- package/dist/index.js +302 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/bar-chart.tsx +337 -0
- package/src/index.ts +21 -0
- package/src/line-chart.tsx +266 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Optilogic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @optilogic/charts
|
|
2
|
+
|
|
3
|
+
Chart components for opti-ui - LineChart and BarChart built on Recharts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @optilogic/charts @optilogic/core recharts
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Make sure you have configured `@optilogic/core` with the Tailwind preset and CSS variables.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { LineChart, BarChart } from '@optilogic/charts';
|
|
19
|
+
|
|
20
|
+
// Line chart
|
|
21
|
+
const data = [
|
|
22
|
+
{ month: 'Jan', revenue: 4000, profit: 2400 },
|
|
23
|
+
{ month: 'Feb', revenue: 3000, profit: 1398 },
|
|
24
|
+
{ month: 'Mar', revenue: 2000, profit: 9800 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
<LineChart
|
|
28
|
+
data={data}
|
|
29
|
+
series={[
|
|
30
|
+
{ dataKey: 'revenue', name: 'Revenue', color: 'hsl(var(--chart-1))' },
|
|
31
|
+
{ dataKey: 'profit', name: 'Profit', color: 'hsl(var(--chart-2))' },
|
|
32
|
+
]}
|
|
33
|
+
xAxis={{ dataKey: 'month' }}
|
|
34
|
+
yAxis={{ domain: [0, 'auto'] }}
|
|
35
|
+
tooltip
|
|
36
|
+
legend
|
|
37
|
+
/>
|
|
38
|
+
|
|
39
|
+
// Bar chart
|
|
40
|
+
<BarChart
|
|
41
|
+
data={data}
|
|
42
|
+
series={[
|
|
43
|
+
{ dataKey: 'revenue', name: 'Revenue' },
|
|
44
|
+
{ dataKey: 'profit', name: 'Profit' },
|
|
45
|
+
]}
|
|
46
|
+
xAxis={{ dataKey: 'month' }}
|
|
47
|
+
tooltip
|
|
48
|
+
legend={{ position: 'top' }}
|
|
49
|
+
/>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Components
|
|
53
|
+
|
|
54
|
+
### LineChart
|
|
55
|
+
|
|
56
|
+
A theme-aware line chart with support for:
|
|
57
|
+
- Multiple series
|
|
58
|
+
- Configurable axes
|
|
59
|
+
- Grid lines
|
|
60
|
+
- Tooltips
|
|
61
|
+
- Legends
|
|
62
|
+
- Animations
|
|
63
|
+
|
|
64
|
+
### BarChart
|
|
65
|
+
|
|
66
|
+
A theme-aware bar chart with support for:
|
|
67
|
+
- Vertical and horizontal layouts
|
|
68
|
+
- Stacked and grouped bars
|
|
69
|
+
- Configurable axes
|
|
70
|
+
- Grid lines
|
|
71
|
+
- Tooltips
|
|
72
|
+
- Legends
|
|
73
|
+
- Animations
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var recharts = require('recharts');
|
|
5
|
+
var core = require('@optilogic/core');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
27
|
+
|
|
28
|
+
// src/line-chart.tsx
|
|
29
|
+
var CHART_COLORS = [
|
|
30
|
+
"hsl(var(--chart-1))",
|
|
31
|
+
"hsl(var(--chart-2))",
|
|
32
|
+
"hsl(var(--chart-3))",
|
|
33
|
+
"hsl(var(--chart-4))",
|
|
34
|
+
"hsl(var(--chart-5))"
|
|
35
|
+
];
|
|
36
|
+
function getChartColor(index, custom) {
|
|
37
|
+
if (custom) return custom;
|
|
38
|
+
return CHART_COLORS[index % CHART_COLORS.length] ?? "hsl(var(--chart-1))";
|
|
39
|
+
}
|
|
40
|
+
var LineChart = React__namespace.forwardRef(
|
|
41
|
+
({
|
|
42
|
+
data,
|
|
43
|
+
series,
|
|
44
|
+
xAxis,
|
|
45
|
+
yAxis,
|
|
46
|
+
grid = true,
|
|
47
|
+
tooltip = true,
|
|
48
|
+
legend = false,
|
|
49
|
+
animate = false,
|
|
50
|
+
className,
|
|
51
|
+
height = "100%",
|
|
52
|
+
margin = { top: 8, right: 12, left: 0, bottom: 4 }
|
|
53
|
+
}, ref) => {
|
|
54
|
+
const gridConfig = React__namespace.useMemo(() => {
|
|
55
|
+
if (grid === false) return null;
|
|
56
|
+
if (grid === true) return { vertical: false, horizontal: true };
|
|
57
|
+
return grid;
|
|
58
|
+
}, [grid]);
|
|
59
|
+
const legendConfig = React__namespace.useMemo(() => {
|
|
60
|
+
if (legend === false) return null;
|
|
61
|
+
if (legend === true)
|
|
62
|
+
return { position: "top", align: "right" };
|
|
63
|
+
return legend;
|
|
64
|
+
}, [legend]);
|
|
65
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: core.cn("w-full", className), style: { height }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, margin, children: [
|
|
66
|
+
gridConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
67
|
+
recharts.CartesianGrid,
|
|
68
|
+
{
|
|
69
|
+
strokeDasharray: "3 3",
|
|
70
|
+
stroke: "hsl(var(--divider))",
|
|
71
|
+
vertical: gridConfig.vertical ?? false,
|
|
72
|
+
horizontal: gridConfig.horizontal ?? true
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
76
|
+
recharts.XAxis,
|
|
77
|
+
{
|
|
78
|
+
dataKey: xAxis.dataKey,
|
|
79
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
80
|
+
tickLine: false,
|
|
81
|
+
axisLine: false,
|
|
82
|
+
minTickGap: xAxis.minTickGap ?? 24,
|
|
83
|
+
tickFormatter: xAxis.tickFormatter,
|
|
84
|
+
label: xAxis.label ? {
|
|
85
|
+
value: xAxis.label,
|
|
86
|
+
position: "insideBottom",
|
|
87
|
+
offset: -4,
|
|
88
|
+
fontSize: 11,
|
|
89
|
+
fill: "hsl(var(--muted-foreground))"
|
|
90
|
+
} : void 0
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
94
|
+
recharts.YAxis,
|
|
95
|
+
{
|
|
96
|
+
domain: yAxis?.domain ?? ["auto", "auto"],
|
|
97
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
98
|
+
tickLine: false,
|
|
99
|
+
axisLine: false,
|
|
100
|
+
width: yAxis?.width ?? 30,
|
|
101
|
+
tickFormatter: yAxis?.tickFormatter,
|
|
102
|
+
label: yAxis?.label ? {
|
|
103
|
+
value: yAxis.label,
|
|
104
|
+
angle: -90,
|
|
105
|
+
position: "insideLeft",
|
|
106
|
+
fontSize: 11,
|
|
107
|
+
fill: "hsl(var(--muted-foreground))"
|
|
108
|
+
} : void 0
|
|
109
|
+
}
|
|
110
|
+
),
|
|
111
|
+
tooltip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
112
|
+
recharts.Tooltip,
|
|
113
|
+
{
|
|
114
|
+
contentStyle: {
|
|
115
|
+
background: "hsl(var(--card))",
|
|
116
|
+
border: "1px solid hsl(var(--border))",
|
|
117
|
+
borderRadius: 6,
|
|
118
|
+
fontSize: 11
|
|
119
|
+
},
|
|
120
|
+
labelStyle: {
|
|
121
|
+
color: "hsl(var(--foreground))",
|
|
122
|
+
fontWeight: 500,
|
|
123
|
+
marginBottom: 4
|
|
124
|
+
},
|
|
125
|
+
itemStyle: {
|
|
126
|
+
color: "hsl(var(--foreground))"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
legendConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
131
|
+
recharts.Legend,
|
|
132
|
+
{
|
|
133
|
+
verticalAlign: legendConfig.position ?? "top",
|
|
134
|
+
align: legendConfig.align ?? "right",
|
|
135
|
+
wrapperStyle: { fontSize: 11, paddingBottom: 4 }
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
series.map((s, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
139
|
+
recharts.Line,
|
|
140
|
+
{
|
|
141
|
+
type: s.type ?? "monotone",
|
|
142
|
+
dataKey: s.dataKey,
|
|
143
|
+
name: s.name,
|
|
144
|
+
stroke: getChartColor(index, s.color),
|
|
145
|
+
strokeWidth: s.strokeWidth ?? 2,
|
|
146
|
+
dot: s.dot ?? false,
|
|
147
|
+
isAnimationActive: animate
|
|
148
|
+
},
|
|
149
|
+
s.dataKey
|
|
150
|
+
))
|
|
151
|
+
] }) }) });
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
LineChart.displayName = "LineChart";
|
|
155
|
+
var BarChart = React__namespace.forwardRef(
|
|
156
|
+
({
|
|
157
|
+
data,
|
|
158
|
+
series,
|
|
159
|
+
layout = "vertical",
|
|
160
|
+
xAxis,
|
|
161
|
+
yAxis,
|
|
162
|
+
grid = true,
|
|
163
|
+
tooltip = true,
|
|
164
|
+
legend = false,
|
|
165
|
+
animate = true,
|
|
166
|
+
barSize,
|
|
167
|
+
barGap,
|
|
168
|
+
barCategoryGap,
|
|
169
|
+
className,
|
|
170
|
+
height = "100%",
|
|
171
|
+
margin = { top: 8, right: 12, left: 0, bottom: 4 }
|
|
172
|
+
}, ref) => {
|
|
173
|
+
const gridConfig = React__namespace.useMemo(() => {
|
|
174
|
+
if (grid === false) return null;
|
|
175
|
+
if (grid === true) return { vertical: false, horizontal: true };
|
|
176
|
+
return grid;
|
|
177
|
+
}, [grid]);
|
|
178
|
+
const legendConfig = React__namespace.useMemo(() => {
|
|
179
|
+
if (legend === false) return null;
|
|
180
|
+
if (legend === true)
|
|
181
|
+
return { position: "top", align: "right" };
|
|
182
|
+
return legend;
|
|
183
|
+
}, [legend]);
|
|
184
|
+
const isHorizontal = layout === "horizontal";
|
|
185
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: core.cn("w-full", className), style: { height }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
186
|
+
recharts.BarChart,
|
|
187
|
+
{
|
|
188
|
+
data,
|
|
189
|
+
layout: isHorizontal ? "vertical" : "horizontal",
|
|
190
|
+
margin,
|
|
191
|
+
barSize,
|
|
192
|
+
barGap,
|
|
193
|
+
barCategoryGap,
|
|
194
|
+
children: [
|
|
195
|
+
gridConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
196
|
+
recharts.CartesianGrid,
|
|
197
|
+
{
|
|
198
|
+
strokeDasharray: "3 3",
|
|
199
|
+
stroke: "hsl(var(--divider))",
|
|
200
|
+
vertical: isHorizontal ? gridConfig.horizontal ?? true : gridConfig.vertical ?? false,
|
|
201
|
+
horizontal: isHorizontal ? gridConfig.vertical ?? false : gridConfig.horizontal ?? true
|
|
202
|
+
}
|
|
203
|
+
),
|
|
204
|
+
isHorizontal ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
205
|
+
recharts.XAxis,
|
|
206
|
+
{
|
|
207
|
+
type: "number",
|
|
208
|
+
domain: yAxis?.domain ?? ["auto", "auto"],
|
|
209
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
210
|
+
tickLine: false,
|
|
211
|
+
axisLine: false,
|
|
212
|
+
tickFormatter: yAxis?.tickFormatter,
|
|
213
|
+
label: yAxis?.label ? {
|
|
214
|
+
value: yAxis.label,
|
|
215
|
+
position: "insideBottom",
|
|
216
|
+
offset: -4,
|
|
217
|
+
fontSize: 11,
|
|
218
|
+
fill: "hsl(var(--muted-foreground))"
|
|
219
|
+
} : void 0
|
|
220
|
+
}
|
|
221
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
222
|
+
recharts.XAxis,
|
|
223
|
+
{
|
|
224
|
+
dataKey: xAxis.dataKey,
|
|
225
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
226
|
+
tickLine: false,
|
|
227
|
+
axisLine: false,
|
|
228
|
+
minTickGap: xAxis.minTickGap ?? 24,
|
|
229
|
+
tickFormatter: xAxis.tickFormatter,
|
|
230
|
+
label: xAxis.label ? {
|
|
231
|
+
value: xAxis.label,
|
|
232
|
+
position: "insideBottom",
|
|
233
|
+
offset: -4,
|
|
234
|
+
fontSize: 11,
|
|
235
|
+
fill: "hsl(var(--muted-foreground))"
|
|
236
|
+
} : void 0
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
isHorizontal ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
240
|
+
recharts.YAxis,
|
|
241
|
+
{
|
|
242
|
+
type: "category",
|
|
243
|
+
dataKey: xAxis.dataKey,
|
|
244
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
245
|
+
tickLine: false,
|
|
246
|
+
axisLine: false,
|
|
247
|
+
width: yAxis?.width ?? 80,
|
|
248
|
+
tickFormatter: xAxis.tickFormatter,
|
|
249
|
+
label: xAxis.label ? {
|
|
250
|
+
value: xAxis.label,
|
|
251
|
+
angle: -90,
|
|
252
|
+
position: "insideLeft",
|
|
253
|
+
fontSize: 11,
|
|
254
|
+
fill: "hsl(var(--muted-foreground))"
|
|
255
|
+
} : void 0
|
|
256
|
+
}
|
|
257
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
258
|
+
recharts.YAxis,
|
|
259
|
+
{
|
|
260
|
+
domain: yAxis?.domain ?? ["auto", "auto"],
|
|
261
|
+
tick: { fontSize: 10, fill: "hsl(var(--muted-foreground))" },
|
|
262
|
+
tickLine: false,
|
|
263
|
+
axisLine: false,
|
|
264
|
+
width: yAxis?.width ?? 30,
|
|
265
|
+
tickFormatter: yAxis?.tickFormatter,
|
|
266
|
+
label: yAxis?.label ? {
|
|
267
|
+
value: yAxis.label,
|
|
268
|
+
angle: -90,
|
|
269
|
+
position: "insideLeft",
|
|
270
|
+
fontSize: 11,
|
|
271
|
+
fill: "hsl(var(--muted-foreground))"
|
|
272
|
+
} : void 0
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
tooltip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
276
|
+
recharts.Tooltip,
|
|
277
|
+
{
|
|
278
|
+
contentStyle: {
|
|
279
|
+
background: "hsl(var(--card))",
|
|
280
|
+
border: "1px solid hsl(var(--border))",
|
|
281
|
+
borderRadius: 6,
|
|
282
|
+
fontSize: 11
|
|
283
|
+
},
|
|
284
|
+
labelStyle: {
|
|
285
|
+
color: "hsl(var(--foreground))",
|
|
286
|
+
fontWeight: 500,
|
|
287
|
+
marginBottom: 4
|
|
288
|
+
},
|
|
289
|
+
itemStyle: {
|
|
290
|
+
color: "hsl(var(--foreground))"
|
|
291
|
+
},
|
|
292
|
+
cursor: { fill: "hsl(var(--accent))", fillOpacity: 0.3 }
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
legendConfig && /* @__PURE__ */ jsxRuntime.jsx(
|
|
296
|
+
recharts.Legend,
|
|
297
|
+
{
|
|
298
|
+
verticalAlign: legendConfig.position ?? "top",
|
|
299
|
+
align: legendConfig.align ?? "right",
|
|
300
|
+
wrapperStyle: { fontSize: 11, paddingBottom: 4 }
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
series.map((s, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
304
|
+
recharts.Bar,
|
|
305
|
+
{
|
|
306
|
+
dataKey: s.dataKey,
|
|
307
|
+
name: s.name,
|
|
308
|
+
fill: getChartColor(index, s.color),
|
|
309
|
+
stackId: s.stackId,
|
|
310
|
+
radius: s.radius ?? 0,
|
|
311
|
+
isAnimationActive: animate
|
|
312
|
+
},
|
|
313
|
+
s.dataKey
|
|
314
|
+
))
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
) }) });
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
BarChart.displayName = "BarChart";
|
|
321
|
+
|
|
322
|
+
exports.BarChart = BarChart;
|
|
323
|
+
exports.CHART_COLORS = CHART_COLORS;
|
|
324
|
+
exports.LineChart = LineChart;
|
|
325
|
+
exports.getChartColor = getChartColor;
|
|
326
|
+
//# sourceMappingURL=index.cjs.map
|
|
327
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/line-chart.tsx","../src/bar-chart.tsx"],"names":["React","jsx","cn","ResponsiveContainer","jsxs","RechartsLineChart","CartesianGrid","XAxis","YAxis","Tooltip","Legend","Line","React2","RechartsBarChart","Bar"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * LineChart\n *\n * A reusable, theme-aware line chart component built on Recharts.\n * Supports multiple series, configurable axes, animations, and styling\n * that integrates with the project's CSS variable theming system.\n */\n\nimport * as React from \"react\";\nimport {\n LineChart as RechartsLineChart,\n Line,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n ResponsiveContainer,\n} from \"recharts\";\nimport { cn } from \"@optilogic/core\";\n\n/** Theme-aware chart colors from CSS variables */\nexport const CHART_COLORS = [\n \"hsl(var(--chart-1))\",\n \"hsl(var(--chart-2))\",\n \"hsl(var(--chart-3))\",\n \"hsl(var(--chart-4))\",\n \"hsl(var(--chart-5))\",\n];\n\nexport function getChartColor(index: number, custom?: string): string {\n if (custom) return custom;\n return CHART_COLORS[index % CHART_COLORS.length] ?? \"hsl(var(--chart-1))\";\n}\n\n/** Configuration for a single line series */\nexport interface LineChartSeries {\n /** Key in data objects for this series' values */\n dataKey: string;\n /** Display name shown in legend/tooltip */\n name: string;\n /** Custom color (defaults to theme chart colors) */\n color?: string;\n /** Line stroke width (default: 2) */\n strokeWidth?: number;\n /** Show dots on data points (default: false) */\n dot?: boolean;\n /** Line interpolation type */\n type?: \"monotone\" | \"linear\" | \"step\" | \"basis\" | \"natural\";\n}\n\n/** X-axis configuration */\nexport interface LineChartXAxis {\n /** Key in data objects for x-axis values */\n dataKey: string;\n /** Axis label */\n label?: string;\n /** Custom tick formatter */\n tickFormatter?: (value: unknown) => string;\n /** Minimum gap between ticks in pixels */\n minTickGap?: number;\n}\n\n/** Y-axis configuration */\nexport interface LineChartYAxis {\n /** Domain bounds [min, max] - use \"auto\" for automatic */\n domain?: [\n number | \"auto\" | \"dataMin\" | \"dataMax\",\n number | \"auto\" | \"dataMin\" | \"dataMax\"\n ];\n /** Axis label */\n label?: string;\n /** Custom tick formatter */\n tickFormatter?: (value: unknown) => string;\n /** Axis width in pixels */\n width?: number;\n}\n\n/** Grid configuration */\nexport interface LineChartGrid {\n /** Show vertical grid lines */\n vertical?: boolean;\n /** Show horizontal grid lines */\n horizontal?: boolean;\n}\n\n/** Legend configuration */\nexport interface LineChartLegend {\n /** Vertical position */\n position?: \"top\" | \"bottom\";\n /** Horizontal alignment */\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport interface LineChartProps {\n /** Array of data points */\n data: Record<string, unknown>[];\n /** Series configurations */\n series: LineChartSeries[];\n /** X-axis configuration */\n xAxis: LineChartXAxis;\n /** Y-axis configuration */\n yAxis?: LineChartYAxis;\n /** Grid configuration (true for default, false to hide, or object for fine control) */\n grid?: boolean | LineChartGrid;\n /** Show tooltip on hover */\n tooltip?: boolean;\n /** Legend configuration (true for default, false to hide, or object for fine control) */\n legend?: boolean | LineChartLegend;\n /** Enable animations */\n animate?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Chart height (default: 100%) */\n height?: number | string;\n /** Chart margins */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n}\n\n/**\n * A flexible, theme-aware line chart component.\n *\n * @example\n * <LineChart\n * data={metrics}\n * series={[\n * { dataKey: \"cpu\", name: \"CPU %\" },\n * { dataKey: \"memory\", name: \"Memory %\", color: \"hsl(var(--success))\" }\n * ]}\n * xAxis={{ dataKey: \"time\", tickFormatter: formatTime }}\n * yAxis={{ domain: [0, 100] }}\n * animate\n * />\n */\nconst LineChart = React.forwardRef<HTMLDivElement, LineChartProps>(\n (\n {\n data,\n series,\n xAxis,\n yAxis,\n grid = true,\n tooltip = true,\n legend = false,\n animate = false,\n className,\n height = \"100%\",\n margin = { top: 8, right: 12, left: 0, bottom: 4 },\n },\n ref\n ) => {\n const gridConfig = React.useMemo(() => {\n if (grid === false) return null;\n if (grid === true) return { vertical: false, horizontal: true };\n return grid;\n }, [grid]);\n\n const legendConfig = React.useMemo(() => {\n if (legend === false) return null;\n if (legend === true)\n return { position: \"top\" as const, align: \"right\" as const };\n return legend;\n }, [legend]);\n\n return (\n <div ref={ref} className={cn(\"w-full\", className)} style={{ height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsLineChart data={data} margin={margin}>\n {gridConfig && (\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"hsl(var(--divider))\"\n vertical={gridConfig.vertical ?? false}\n horizontal={gridConfig.horizontal ?? true}\n />\n )}\n\n <XAxis\n dataKey={xAxis.dataKey}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n minTickGap={xAxis.minTickGap ?? 24}\n tickFormatter={xAxis.tickFormatter}\n label={\n xAxis.label\n ? {\n value: xAxis.label,\n position: \"insideBottom\",\n offset: -4,\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n\n <YAxis\n domain={yAxis?.domain ?? [\"auto\", \"auto\"]}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n width={yAxis?.width ?? 30}\n tickFormatter={yAxis?.tickFormatter}\n label={\n yAxis?.label\n ? {\n value: yAxis.label,\n angle: -90,\n position: \"insideLeft\",\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n\n {tooltip && (\n <Tooltip\n contentStyle={{\n background: \"hsl(var(--card))\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: 6,\n fontSize: 11,\n }}\n labelStyle={{\n color: \"hsl(var(--foreground))\",\n fontWeight: 500,\n marginBottom: 4,\n }}\n itemStyle={{\n color: \"hsl(var(--foreground))\",\n }}\n />\n )}\n\n {legendConfig && (\n <Legend\n verticalAlign={legendConfig.position ?? \"top\"}\n align={legendConfig.align ?? \"right\"}\n wrapperStyle={{ fontSize: 11, paddingBottom: 4 }}\n />\n )}\n\n {series.map((s, index) => (\n <Line\n key={s.dataKey}\n type={s.type ?? \"monotone\"}\n dataKey={s.dataKey}\n name={s.name}\n stroke={getChartColor(index, s.color)}\n strokeWidth={s.strokeWidth ?? 2}\n dot={s.dot ?? false}\n isAnimationActive={animate}\n />\n ))}\n </RechartsLineChart>\n </ResponsiveContainer>\n </div>\n );\n }\n);\n\nLineChart.displayName = \"LineChart\";\n\nexport { LineChart };\n","/**\n * BarChart\n *\n * A reusable, theme-aware bar chart component built on Recharts.\n * Supports vertical/horizontal layouts, stacked/grouped bars,\n * configurable axes, animations, and styling that integrates\n * with the project's CSS variable theming system.\n */\n\nimport * as React from \"react\";\nimport {\n BarChart as RechartsBarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n Legend,\n ResponsiveContainer,\n} from \"recharts\";\nimport { cn } from \"@optilogic/core\";\nimport { CHART_COLORS, getChartColor } from \"./line-chart\";\n\n/** Configuration for a single bar series */\nexport interface BarChartSeries {\n /** Key in data objects for this series' values */\n dataKey: string;\n /** Display name shown in legend/tooltip */\n name: string;\n /** Custom color (defaults to theme chart colors) */\n color?: string;\n /** Stack ID - bars with the same stackId will be stacked */\n stackId?: string;\n /** Border radius for bars */\n radius?: number | [number, number, number, number];\n}\n\n/** X-axis configuration */\nexport interface BarChartXAxis {\n /** Key in data objects for x-axis values (category axis in vertical layout) */\n dataKey: string;\n /** Axis label */\n label?: string;\n /** Custom tick formatter */\n tickFormatter?: (value: unknown) => string;\n /** Minimum gap between ticks in pixels */\n minTickGap?: number;\n}\n\n/** Y-axis configuration */\nexport interface BarChartYAxis {\n /** Domain bounds [min, max] - use \"auto\" for automatic */\n domain?: [\n number | \"auto\" | \"dataMin\" | \"dataMax\",\n number | \"auto\" | \"dataMin\" | \"dataMax\"\n ];\n /** Axis label */\n label?: string;\n /** Custom tick formatter */\n tickFormatter?: (value: unknown) => string;\n /** Axis width in pixels */\n width?: number;\n}\n\n/** Grid configuration */\nexport interface BarChartGrid {\n /** Show vertical grid lines */\n vertical?: boolean;\n /** Show horizontal grid lines */\n horizontal?: boolean;\n}\n\n/** Legend configuration */\nexport interface BarChartLegend {\n /** Vertical position */\n position?: \"top\" | \"bottom\";\n /** Horizontal alignment */\n align?: \"left\" | \"center\" | \"right\";\n}\n\nexport interface BarChartProps {\n /** Array of data points */\n data: Record<string, unknown>[];\n /** Series configurations */\n series: BarChartSeries[];\n /** Chart layout orientation */\n layout?: \"vertical\" | \"horizontal\";\n /** X-axis configuration */\n xAxis: BarChartXAxis;\n /** Y-axis configuration */\n yAxis?: BarChartYAxis;\n /** Grid configuration (true for default, false to hide, or object for fine control) */\n grid?: boolean | BarChartGrid;\n /** Show tooltip on hover */\n tooltip?: boolean;\n /** Legend configuration (true for default, false to hide, or object for fine control) */\n legend?: boolean | BarChartLegend;\n /** Enable animations */\n animate?: boolean;\n /** Bar width in pixels */\n barSize?: number;\n /** Gap between bars in a group (pixels) */\n barGap?: number;\n /** Gap between bar groups (pixels or percentage) */\n barCategoryGap?: number | string;\n /** Additional CSS classes */\n className?: string;\n /** Chart height (default: 100%) */\n height?: number | string;\n /** Chart margins */\n margin?: { top?: number; right?: number; bottom?: number; left?: number };\n}\n\n/**\n * A flexible, theme-aware bar chart component.\n *\n * @example\n * // Grouped bar chart\n * <BarChart\n * data={salesData}\n * series={[\n * { dataKey: \"revenue\", name: \"Revenue\" },\n * { dataKey: \"profit\", name: \"Profit\" }\n * ]}\n * xAxis={{ dataKey: \"month\" }}\n * legend={{ position: \"top\" }}\n * />\n *\n * @example\n * // Stacked bar chart\n * <BarChart\n * data={salesData}\n * series={[\n * { dataKey: \"q1\", name: \"Q1\", stackId: \"revenue\" },\n * { dataKey: \"q2\", name: \"Q2\", stackId: \"revenue\" }\n * ]}\n * xAxis={{ dataKey: \"category\" }}\n * />\n */\nconst BarChart = React.forwardRef<HTMLDivElement, BarChartProps>(\n (\n {\n data,\n series,\n layout = \"vertical\",\n xAxis,\n yAxis,\n grid = true,\n tooltip = true,\n legend = false,\n animate = true,\n barSize,\n barGap,\n barCategoryGap,\n className,\n height = \"100%\",\n margin = { top: 8, right: 12, left: 0, bottom: 4 },\n },\n ref\n ) => {\n const gridConfig = React.useMemo(() => {\n if (grid === false) return null;\n if (grid === true) return { vertical: false, horizontal: true };\n return grid;\n }, [grid]);\n\n const legendConfig = React.useMemo(() => {\n if (legend === false) return null;\n if (legend === true)\n return { position: \"top\" as const, align: \"right\" as const };\n return legend;\n }, [legend]);\n\n const isHorizontal = layout === \"horizontal\";\n\n return (\n <div ref={ref} className={cn(\"w-full\", className)} style={{ height }}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsBarChart\n data={data}\n layout={isHorizontal ? \"vertical\" : \"horizontal\"}\n margin={margin}\n barSize={barSize}\n barGap={barGap}\n barCategoryGap={barCategoryGap}\n >\n {gridConfig && (\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"hsl(var(--divider))\"\n vertical={\n isHorizontal\n ? (gridConfig.horizontal ?? true)\n : (gridConfig.vertical ?? false)\n }\n horizontal={\n isHorizontal\n ? (gridConfig.vertical ?? false)\n : (gridConfig.horizontal ?? true)\n }\n />\n )}\n\n {isHorizontal ? (\n <XAxis\n type=\"number\"\n domain={yAxis?.domain ?? [\"auto\", \"auto\"]}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n tickFormatter={yAxis?.tickFormatter}\n label={\n yAxis?.label\n ? {\n value: yAxis.label,\n position: \"insideBottom\",\n offset: -4,\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n ) : (\n <XAxis\n dataKey={xAxis.dataKey}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n minTickGap={xAxis.minTickGap ?? 24}\n tickFormatter={xAxis.tickFormatter}\n label={\n xAxis.label\n ? {\n value: xAxis.label,\n position: \"insideBottom\",\n offset: -4,\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n )}\n\n {isHorizontal ? (\n <YAxis\n type=\"category\"\n dataKey={xAxis.dataKey}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n width={yAxis?.width ?? 80}\n tickFormatter={xAxis.tickFormatter}\n label={\n xAxis.label\n ? {\n value: xAxis.label,\n angle: -90,\n position: \"insideLeft\",\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n ) : (\n <YAxis\n domain={yAxis?.domain ?? [\"auto\", \"auto\"]}\n tick={{ fontSize: 10, fill: \"hsl(var(--muted-foreground))\" }}\n tickLine={false}\n axisLine={false}\n width={yAxis?.width ?? 30}\n tickFormatter={yAxis?.tickFormatter}\n label={\n yAxis?.label\n ? {\n value: yAxis.label,\n angle: -90,\n position: \"insideLeft\",\n fontSize: 11,\n fill: \"hsl(var(--muted-foreground))\",\n }\n : undefined\n }\n />\n )}\n\n {tooltip && (\n <Tooltip\n contentStyle={{\n background: \"hsl(var(--card))\",\n border: \"1px solid hsl(var(--border))\",\n borderRadius: 6,\n fontSize: 11,\n }}\n labelStyle={{\n color: \"hsl(var(--foreground))\",\n fontWeight: 500,\n marginBottom: 4,\n }}\n itemStyle={{\n color: \"hsl(var(--foreground))\",\n }}\n cursor={{ fill: \"hsl(var(--accent))\", fillOpacity: 0.3 }}\n />\n )}\n\n {legendConfig && (\n <Legend\n verticalAlign={legendConfig.position ?? \"top\"}\n align={legendConfig.align ?? \"right\"}\n wrapperStyle={{ fontSize: 11, paddingBottom: 4 }}\n />\n )}\n\n {series.map((s, index) => (\n <Bar\n key={s.dataKey}\n dataKey={s.dataKey}\n name={s.name}\n fill={getChartColor(index, s.color)}\n stackId={s.stackId}\n radius={s.radius ?? 0}\n isAnimationActive={animate}\n />\n ))}\n </RechartsBarChart>\n </ResponsiveContainer>\n </div>\n );\n }\n);\n\nBarChart.displayName = \"BarChart\";\n\nexport { BarChart };\n"]}
|