@optilogic/charts 1.0.0-beta.8 → 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 (37) hide show
  1. package/dist/index.cjs +3034 -174
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +489 -192
  4. package/dist/index.d.ts +489 -192
  5. package/dist/index.js +3006 -175
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/src/cartesian/area-chart.tsx +177 -0
  9. package/src/cartesian/bar-chart.tsx +217 -0
  10. package/src/cartesian/composed-chart.tsx +222 -0
  11. package/src/cartesian/line-chart.tsx +159 -0
  12. package/src/cartesian/scatter-chart.tsx +158 -0
  13. package/src/cartesian/waterfall-chart.tsx +171 -0
  14. package/src/dashboard/chart-builder.tsx +310 -0
  15. package/src/dashboard/chart-renderer.tsx +250 -0
  16. package/src/dashboard/kpi-card.tsx +121 -0
  17. package/src/dashboard/scenario-comparison.tsx +235 -0
  18. package/src/dashboard/sparkline.tsx +86 -0
  19. package/src/index.ts +50 -19
  20. package/src/radial/donut-chart.tsx +135 -0
  21. package/src/radial/pie-chart.tsx +153 -0
  22. package/src/radial/radar-chart.tsx +111 -0
  23. package/src/radial/radial-bar-chart.tsx +115 -0
  24. package/src/shared/chart-container.tsx +104 -0
  25. package/src/shared/chart-legend.tsx +57 -0
  26. package/src/shared/chart-tooltip.tsx +159 -0
  27. package/src/shared/colors.ts +37 -0
  28. package/src/shared/formatters.ts +51 -0
  29. package/src/shared/types.ts +66 -0
  30. package/src/shared/use-live-data.ts +83 -0
  31. package/src/specialized/funnel-chart.tsx +93 -0
  32. package/src/specialized/gantt-chart.tsx +416 -0
  33. package/src/specialized/heatmap-chart.tsx +250 -0
  34. package/src/specialized/sankey-chart.tsx +155 -0
  35. package/src/specialized/treemap-chart.tsx +121 -0
  36. package/src/bar-chart.tsx +0 -337
  37. package/src/line-chart.tsx +0 -266
package/src/bar-chart.tsx DELETED
@@ -1,337 +0,0 @@
1
- /**
2
- * BarChart
3
- *
4
- * A reusable, theme-aware bar chart component built on Recharts.
5
- * Supports vertical/horizontal layouts, stacked/grouped bars,
6
- * configurable axes, animations, and styling that integrates
7
- * with the project's CSS variable theming system.
8
- */
9
-
10
- import * as React from "react";
11
- import {
12
- BarChart as RechartsBarChart,
13
- Bar,
14
- XAxis,
15
- YAxis,
16
- CartesianGrid,
17
- Tooltip,
18
- Legend,
19
- ResponsiveContainer,
20
- } from "recharts";
21
- import { cn } from "@optilogic/core";
22
- import { CHART_COLORS, getChartColor } from "./line-chart";
23
-
24
- /** Configuration for a single bar series */
25
- export interface BarChartSeries {
26
- /** Key in data objects for this series' values */
27
- dataKey: string;
28
- /** Display name shown in legend/tooltip */
29
- name: string;
30
- /** Custom color (defaults to theme chart colors) */
31
- color?: string;
32
- /** Stack ID - bars with the same stackId will be stacked */
33
- stackId?: string;
34
- /** Border radius for bars */
35
- radius?: number | [number, number, number, number];
36
- }
37
-
38
- /** X-axis configuration */
39
- export interface BarChartXAxis {
40
- /** Key in data objects for x-axis values (category axis in vertical layout) */
41
- dataKey: string;
42
- /** Axis label */
43
- label?: string;
44
- /** Custom tick formatter */
45
- tickFormatter?: (value: unknown) => string;
46
- /** Minimum gap between ticks in pixels */
47
- minTickGap?: number;
48
- }
49
-
50
- /** Y-axis configuration */
51
- export interface BarChartYAxis {
52
- /** Domain bounds [min, max] - use "auto" for automatic */
53
- domain?: [
54
- number | "auto" | "dataMin" | "dataMax",
55
- number | "auto" | "dataMin" | "dataMax"
56
- ];
57
- /** Axis label */
58
- label?: string;
59
- /** Custom tick formatter */
60
- tickFormatter?: (value: unknown) => string;
61
- /** Axis width in pixels */
62
- width?: number;
63
- }
64
-
65
- /** Grid configuration */
66
- export interface BarChartGrid {
67
- /** Show vertical grid lines */
68
- vertical?: boolean;
69
- /** Show horizontal grid lines */
70
- horizontal?: boolean;
71
- }
72
-
73
- /** Legend configuration */
74
- export interface BarChartLegend {
75
- /** Vertical position */
76
- position?: "top" | "bottom";
77
- /** Horizontal alignment */
78
- align?: "left" | "center" | "right";
79
- }
80
-
81
- export interface BarChartProps {
82
- /** Array of data points */
83
- data: Record<string, unknown>[];
84
- /** Series configurations */
85
- series: BarChartSeries[];
86
- /** Chart layout orientation */
87
- layout?: "vertical" | "horizontal";
88
- /** X-axis configuration */
89
- xAxis: BarChartXAxis;
90
- /** Y-axis configuration */
91
- yAxis?: BarChartYAxis;
92
- /** Grid configuration (true for default, false to hide, or object for fine control) */
93
- grid?: boolean | BarChartGrid;
94
- /** Show tooltip on hover */
95
- tooltip?: boolean;
96
- /** Legend configuration (true for default, false to hide, or object for fine control) */
97
- legend?: boolean | BarChartLegend;
98
- /** Enable animations */
99
- animate?: boolean;
100
- /** Bar width in pixels */
101
- barSize?: number;
102
- /** Gap between bars in a group (pixels) */
103
- barGap?: number;
104
- /** Gap between bar groups (pixels or percentage) */
105
- barCategoryGap?: number | string;
106
- /** Additional CSS classes */
107
- className?: string;
108
- /** Chart height (default: 100%) */
109
- height?: number | string;
110
- /** Chart margins */
111
- margin?: { top?: number; right?: number; bottom?: number; left?: number };
112
- }
113
-
114
- /**
115
- * A flexible, theme-aware bar chart component.
116
- *
117
- * @example
118
- * // Grouped bar chart
119
- * <BarChart
120
- * data={salesData}
121
- * series={[
122
- * { dataKey: "revenue", name: "Revenue" },
123
- * { dataKey: "profit", name: "Profit" }
124
- * ]}
125
- * xAxis={{ dataKey: "month" }}
126
- * legend={{ position: "top" }}
127
- * />
128
- *
129
- * @example
130
- * // Stacked bar chart
131
- * <BarChart
132
- * data={salesData}
133
- * series={[
134
- * { dataKey: "q1", name: "Q1", stackId: "revenue" },
135
- * { dataKey: "q2", name: "Q2", stackId: "revenue" }
136
- * ]}
137
- * xAxis={{ dataKey: "category" }}
138
- * />
139
- */
140
- const BarChart = React.forwardRef<HTMLDivElement, BarChartProps>(
141
- (
142
- {
143
- data,
144
- series,
145
- layout = "vertical",
146
- xAxis,
147
- yAxis,
148
- grid = true,
149
- tooltip = true,
150
- legend = false,
151
- animate = true,
152
- barSize,
153
- barGap,
154
- barCategoryGap,
155
- className,
156
- height = "100%",
157
- margin = { top: 8, right: 12, left: 0, bottom: 4 },
158
- },
159
- ref
160
- ) => {
161
- const gridConfig = React.useMemo(() => {
162
- if (grid === false) return null;
163
- if (grid === true) return { vertical: false, horizontal: true };
164
- return grid;
165
- }, [grid]);
166
-
167
- const legendConfig = React.useMemo(() => {
168
- if (legend === false) return null;
169
- if (legend === true)
170
- return { position: "top" as const, align: "right" as const };
171
- return legend;
172
- }, [legend]);
173
-
174
- const isHorizontal = layout === "horizontal";
175
-
176
- return (
177
- <div ref={ref} className={cn("w-full", className)} style={{ height }}>
178
- <ResponsiveContainer width="100%" height="100%">
179
- <RechartsBarChart
180
- data={data}
181
- layout={isHorizontal ? "vertical" : "horizontal"}
182
- margin={margin}
183
- barSize={barSize}
184
- barGap={barGap}
185
- barCategoryGap={barCategoryGap}
186
- >
187
- {gridConfig && (
188
- <CartesianGrid
189
- strokeDasharray="3 3"
190
- stroke="hsl(var(--divider))"
191
- vertical={
192
- isHorizontal
193
- ? (gridConfig.horizontal ?? true)
194
- : (gridConfig.vertical ?? false)
195
- }
196
- horizontal={
197
- isHorizontal
198
- ? (gridConfig.vertical ?? false)
199
- : (gridConfig.horizontal ?? true)
200
- }
201
- />
202
- )}
203
-
204
- {isHorizontal ? (
205
- <XAxis
206
- type="number"
207
- domain={yAxis?.domain ?? ["auto", "auto"]}
208
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
209
- tickLine={false}
210
- axisLine={false}
211
- tickFormatter={yAxis?.tickFormatter}
212
- label={
213
- yAxis?.label
214
- ? {
215
- value: yAxis.label,
216
- position: "insideBottom",
217
- offset: -4,
218
- fontSize: 11,
219
- fill: "hsl(var(--muted-foreground))",
220
- }
221
- : undefined
222
- }
223
- />
224
- ) : (
225
- <XAxis
226
- dataKey={xAxis.dataKey}
227
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
228
- tickLine={false}
229
- axisLine={false}
230
- minTickGap={xAxis.minTickGap ?? 24}
231
- tickFormatter={xAxis.tickFormatter}
232
- label={
233
- xAxis.label
234
- ? {
235
- value: xAxis.label,
236
- position: "insideBottom",
237
- offset: -4,
238
- fontSize: 11,
239
- fill: "hsl(var(--muted-foreground))",
240
- }
241
- : undefined
242
- }
243
- />
244
- )}
245
-
246
- {isHorizontal ? (
247
- <YAxis
248
- type="category"
249
- dataKey={xAxis.dataKey}
250
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
251
- tickLine={false}
252
- axisLine={false}
253
- width={yAxis?.width ?? 80}
254
- tickFormatter={xAxis.tickFormatter}
255
- label={
256
- xAxis.label
257
- ? {
258
- value: xAxis.label,
259
- angle: -90,
260
- position: "insideLeft",
261
- fontSize: 11,
262
- fill: "hsl(var(--muted-foreground))",
263
- }
264
- : undefined
265
- }
266
- />
267
- ) : (
268
- <YAxis
269
- domain={yAxis?.domain ?? ["auto", "auto"]}
270
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
271
- tickLine={false}
272
- axisLine={false}
273
- width={yAxis?.width ?? 30}
274
- tickFormatter={yAxis?.tickFormatter}
275
- label={
276
- yAxis?.label
277
- ? {
278
- value: yAxis.label,
279
- angle: -90,
280
- position: "insideLeft",
281
- fontSize: 11,
282
- fill: "hsl(var(--muted-foreground))",
283
- }
284
- : undefined
285
- }
286
- />
287
- )}
288
-
289
- {tooltip && (
290
- <Tooltip
291
- contentStyle={{
292
- background: "hsl(var(--card))",
293
- border: "1px solid hsl(var(--border))",
294
- borderRadius: 6,
295
- fontSize: 11,
296
- }}
297
- labelStyle={{
298
- color: "hsl(var(--foreground))",
299
- fontWeight: 500,
300
- marginBottom: 4,
301
- }}
302
- itemStyle={{
303
- color: "hsl(var(--foreground))",
304
- }}
305
- cursor={{ fill: "hsl(var(--accent))", fillOpacity: 0.3 }}
306
- />
307
- )}
308
-
309
- {legendConfig && (
310
- <Legend
311
- verticalAlign={legendConfig.position ?? "top"}
312
- align={legendConfig.align ?? "right"}
313
- wrapperStyle={{ fontSize: 11, paddingBottom: 4 }}
314
- />
315
- )}
316
-
317
- {series.map((s, index) => (
318
- <Bar
319
- key={s.dataKey}
320
- dataKey={s.dataKey}
321
- name={s.name}
322
- fill={getChartColor(index, s.color)}
323
- stackId={s.stackId}
324
- radius={s.radius ?? 0}
325
- isAnimationActive={animate}
326
- />
327
- ))}
328
- </RechartsBarChart>
329
- </ResponsiveContainer>
330
- </div>
331
- );
332
- }
333
- );
334
-
335
- BarChart.displayName = "BarChart";
336
-
337
- export { BarChart };
@@ -1,266 +0,0 @@
1
- /**
2
- * LineChart
3
- *
4
- * A reusable, theme-aware line chart component built on Recharts.
5
- * Supports multiple series, configurable axes, animations, and styling
6
- * that integrates with the project's CSS variable theming system.
7
- */
8
-
9
- import * as React from "react";
10
- import {
11
- LineChart as RechartsLineChart,
12
- Line,
13
- XAxis,
14
- YAxis,
15
- CartesianGrid,
16
- Tooltip,
17
- Legend,
18
- ResponsiveContainer,
19
- } from "recharts";
20
- import { cn } from "@optilogic/core";
21
-
22
- /** Theme-aware chart colors from CSS variables */
23
- export const CHART_COLORS = [
24
- "hsl(var(--chart-1))",
25
- "hsl(var(--chart-2))",
26
- "hsl(var(--chart-3))",
27
- "hsl(var(--chart-4))",
28
- "hsl(var(--chart-5))",
29
- ];
30
-
31
- export function getChartColor(index: number, custom?: string): string {
32
- if (custom) return custom;
33
- return CHART_COLORS[index % CHART_COLORS.length] ?? "hsl(var(--chart-1))";
34
- }
35
-
36
- /** Configuration for a single line series */
37
- export interface LineChartSeries {
38
- /** Key in data objects for this series' values */
39
- dataKey: string;
40
- /** Display name shown in legend/tooltip */
41
- name: string;
42
- /** Custom color (defaults to theme chart colors) */
43
- color?: string;
44
- /** Line stroke width (default: 2) */
45
- strokeWidth?: number;
46
- /** Show dots on data points (default: false) */
47
- dot?: boolean;
48
- /** Line interpolation type */
49
- type?: "monotone" | "linear" | "step" | "basis" | "natural";
50
- }
51
-
52
- /** X-axis configuration */
53
- export interface LineChartXAxis {
54
- /** Key in data objects for x-axis values */
55
- dataKey: string;
56
- /** Axis label */
57
- label?: string;
58
- /** Custom tick formatter */
59
- tickFormatter?: (value: unknown) => string;
60
- /** Minimum gap between ticks in pixels */
61
- minTickGap?: number;
62
- }
63
-
64
- /** Y-axis configuration */
65
- export interface LineChartYAxis {
66
- /** Domain bounds [min, max] - use "auto" for automatic */
67
- domain?: [
68
- number | "auto" | "dataMin" | "dataMax",
69
- number | "auto" | "dataMin" | "dataMax"
70
- ];
71
- /** Axis label */
72
- label?: string;
73
- /** Custom tick formatter */
74
- tickFormatter?: (value: unknown) => string;
75
- /** Axis width in pixels */
76
- width?: number;
77
- }
78
-
79
- /** Grid configuration */
80
- export interface LineChartGrid {
81
- /** Show vertical grid lines */
82
- vertical?: boolean;
83
- /** Show horizontal grid lines */
84
- horizontal?: boolean;
85
- }
86
-
87
- /** Legend configuration */
88
- export interface LineChartLegend {
89
- /** Vertical position */
90
- position?: "top" | "bottom";
91
- /** Horizontal alignment */
92
- align?: "left" | "center" | "right";
93
- }
94
-
95
- export interface LineChartProps {
96
- /** Array of data points */
97
- data: Record<string, unknown>[];
98
- /** Series configurations */
99
- series: LineChartSeries[];
100
- /** X-axis configuration */
101
- xAxis: LineChartXAxis;
102
- /** Y-axis configuration */
103
- yAxis?: LineChartYAxis;
104
- /** Grid configuration (true for default, false to hide, or object for fine control) */
105
- grid?: boolean | LineChartGrid;
106
- /** Show tooltip on hover */
107
- tooltip?: boolean;
108
- /** Legend configuration (true for default, false to hide, or object for fine control) */
109
- legend?: boolean | LineChartLegend;
110
- /** Enable animations */
111
- animate?: boolean;
112
- /** Additional CSS classes */
113
- className?: string;
114
- /** Chart height (default: 100%) */
115
- height?: number | string;
116
- /** Chart margins */
117
- margin?: { top?: number; right?: number; bottom?: number; left?: number };
118
- }
119
-
120
- /**
121
- * A flexible, theme-aware line chart component.
122
- *
123
- * @example
124
- * <LineChart
125
- * data={metrics}
126
- * series={[
127
- * { dataKey: "cpu", name: "CPU %" },
128
- * { dataKey: "memory", name: "Memory %", color: "hsl(var(--success))" }
129
- * ]}
130
- * xAxis={{ dataKey: "time", tickFormatter: formatTime }}
131
- * yAxis={{ domain: [0, 100] }}
132
- * animate
133
- * />
134
- */
135
- const LineChart = React.forwardRef<HTMLDivElement, LineChartProps>(
136
- (
137
- {
138
- data,
139
- series,
140
- xAxis,
141
- yAxis,
142
- grid = true,
143
- tooltip = true,
144
- legend = false,
145
- animate = false,
146
- className,
147
- height = "100%",
148
- margin = { top: 8, right: 12, left: 0, bottom: 4 },
149
- },
150
- ref
151
- ) => {
152
- const gridConfig = React.useMemo(() => {
153
- if (grid === false) return null;
154
- if (grid === true) return { vertical: false, horizontal: true };
155
- return grid;
156
- }, [grid]);
157
-
158
- const legendConfig = React.useMemo(() => {
159
- if (legend === false) return null;
160
- if (legend === true)
161
- return { position: "top" as const, align: "right" as const };
162
- return legend;
163
- }, [legend]);
164
-
165
- return (
166
- <div ref={ref} className={cn("w-full", className)} style={{ height }}>
167
- <ResponsiveContainer width="100%" height="100%">
168
- <RechartsLineChart data={data} margin={margin}>
169
- {gridConfig && (
170
- <CartesianGrid
171
- strokeDasharray="3 3"
172
- stroke="hsl(var(--divider))"
173
- vertical={gridConfig.vertical ?? false}
174
- horizontal={gridConfig.horizontal ?? true}
175
- />
176
- )}
177
-
178
- <XAxis
179
- dataKey={xAxis.dataKey}
180
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
181
- tickLine={false}
182
- axisLine={false}
183
- minTickGap={xAxis.minTickGap ?? 24}
184
- tickFormatter={xAxis.tickFormatter}
185
- label={
186
- xAxis.label
187
- ? {
188
- value: xAxis.label,
189
- position: "insideBottom",
190
- offset: -4,
191
- fontSize: 11,
192
- fill: "hsl(var(--muted-foreground))",
193
- }
194
- : undefined
195
- }
196
- />
197
-
198
- <YAxis
199
- domain={yAxis?.domain ?? ["auto", "auto"]}
200
- tick={{ fontSize: 10, fill: "hsl(var(--muted-foreground))" }}
201
- tickLine={false}
202
- axisLine={false}
203
- width={yAxis?.width ?? 30}
204
- tickFormatter={yAxis?.tickFormatter}
205
- label={
206
- yAxis?.label
207
- ? {
208
- value: yAxis.label,
209
- angle: -90,
210
- position: "insideLeft",
211
- fontSize: 11,
212
- fill: "hsl(var(--muted-foreground))",
213
- }
214
- : undefined
215
- }
216
- />
217
-
218
- {tooltip && (
219
- <Tooltip
220
- contentStyle={{
221
- background: "hsl(var(--card))",
222
- border: "1px solid hsl(var(--border))",
223
- borderRadius: 6,
224
- fontSize: 11,
225
- }}
226
- labelStyle={{
227
- color: "hsl(var(--foreground))",
228
- fontWeight: 500,
229
- marginBottom: 4,
230
- }}
231
- itemStyle={{
232
- color: "hsl(var(--foreground))",
233
- }}
234
- />
235
- )}
236
-
237
- {legendConfig && (
238
- <Legend
239
- verticalAlign={legendConfig.position ?? "top"}
240
- align={legendConfig.align ?? "right"}
241
- wrapperStyle={{ fontSize: 11, paddingBottom: 4 }}
242
- />
243
- )}
244
-
245
- {series.map((s, index) => (
246
- <Line
247
- key={s.dataKey}
248
- type={s.type ?? "monotone"}
249
- dataKey={s.dataKey}
250
- name={s.name}
251
- stroke={getChartColor(index, s.color)}
252
- strokeWidth={s.strokeWidth ?? 2}
253
- dot={s.dot ?? false}
254
- isAnimationActive={animate}
255
- />
256
- ))}
257
- </RechartsLineChart>
258
- </ResponsiveContainer>
259
- </div>
260
- );
261
- }
262
- );
263
-
264
- LineChart.displayName = "LineChart";
265
-
266
- export { LineChart };