@useatlas/react 0.0.1
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/README.md +95 -0
- package/dist/chunk-2WFDP7G5.js +231 -0
- package/dist/chunk-2WFDP7G5.js.map +1 -0
- package/dist/chunk-44HBZYKP.js +224 -0
- package/dist/chunk-44HBZYKP.js.map +1 -0
- package/dist/chunk-5SEVKHS5.cjs +229 -0
- package/dist/chunk-5SEVKHS5.cjs.map +1 -0
- package/dist/chunk-UIRB6L36.cjs +249 -0
- package/dist/chunk-UIRB6L36.cjs.map +1 -0
- package/dist/hooks.cjs +251 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +132 -0
- package/dist/hooks.d.ts +132 -0
- package/dist/hooks.js +237 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.cjs +2976 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +2926 -0
- package/dist/index.js.map +1 -0
- package/dist/result-chart-NFAJ4IQ5.js +398 -0
- package/dist/result-chart-NFAJ4IQ5.js.map +1 -0
- package/dist/result-chart-YLCKBNV4.cjs +400 -0
- package/dist/result-chart-YLCKBNV4.cjs.map +1 -0
- package/dist/styles.css +59 -0
- package/dist/use-dark-mode-rFxawUv1.d.cts +123 -0
- package/dist/use-dark-mode-rFxawUv1.d.ts +123 -0
- package/dist/widget.css +2 -0
- package/dist/widget.js +445 -0
- package/package.json +113 -0
- package/src/components/__tests__/tool-renderers.test.tsx +239 -0
- package/src/components/actions/action-approval-card.tsx +296 -0
- package/src/components/actions/action-status-badge.tsx +50 -0
- package/src/components/admin/change-password-dialog.tsx +128 -0
- package/src/components/atlas-chat.tsx +656 -0
- package/src/components/chart/chart-detection.ts +318 -0
- package/src/components/chart/result-chart.tsx +590 -0
- package/src/components/chat/api-key-bar.tsx +66 -0
- package/src/components/chat/copy-button.tsx +25 -0
- package/src/components/chat/data-table.tsx +104 -0
- package/src/components/chat/error-banner.tsx +32 -0
- package/src/components/chat/explore-card.tsx +41 -0
- package/src/components/chat/follow-up-chips.tsx +29 -0
- package/src/components/chat/loading-card.tsx +10 -0
- package/src/components/chat/managed-auth-card.tsx +116 -0
- package/src/components/chat/markdown.tsx +146 -0
- package/src/components/chat/python-result-card.tsx +245 -0
- package/src/components/chat/sql-block.tsx +54 -0
- package/src/components/chat/sql-result-card.tsx +163 -0
- package/src/components/chat/starter-prompts.ts +6 -0
- package/src/components/chat/tool-part.tsx +106 -0
- package/src/components/chat/typing-indicator.tsx +22 -0
- package/src/components/conversations/conversation-item.tsx +135 -0
- package/src/components/conversations/conversation-list.tsx +69 -0
- package/src/components/conversations/conversation-sidebar.tsx +113 -0
- package/src/components/conversations/delete-confirmation.tsx +27 -0
- package/src/components/schema-explorer/schema-explorer.tsx +517 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/scroll-area.tsx +62 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/context.tsx +85 -0
- package/src/env.d.ts +9 -0
- package/src/hooks/__tests__/provider.test.tsx +83 -0
- package/src/hooks/__tests__/use-atlas-auth.test.tsx +283 -0
- package/src/hooks/__tests__/use-atlas-chat.test.tsx +157 -0
- package/src/hooks/__tests__/use-atlas-conversations.test.tsx +159 -0
- package/src/hooks/__tests__/use-atlas-theme.test.tsx +56 -0
- package/src/hooks/index.ts +47 -0
- package/src/hooks/provider.tsx +77 -0
- package/src/hooks/theme-init-script.ts +17 -0
- package/src/hooks/use-atlas-auth.ts +131 -0
- package/src/hooks/use-atlas-chat.ts +102 -0
- package/src/hooks/use-atlas-conversations.ts +61 -0
- package/src/hooks/use-atlas-theme.ts +34 -0
- package/src/hooks/use-conversations.ts +189 -0
- package/src/hooks/use-dark-mode.ts +150 -0
- package/src/index.ts +36 -0
- package/src/lib/action-types.ts +11 -0
- package/src/lib/helpers.ts +198 -0
- package/src/lib/tool-renderer-types.ts +76 -0
- package/src/lib/types.ts +29 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +59 -0
- package/src/test-setup.ts +55 -0
- package/src/widget-entry.ts +20 -0
- package/src/widget.css +12 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import { detectCharts, transformData, CHART_COLORS_DARK, CHART_COLORS_LIGHT } from './chunk-44HBZYKP.js';
|
|
2
|
+
import { useMemo, useState, Component, useId } from 'react';
|
|
3
|
+
import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LineChart, Line, AreaChart, Area, ScatterChart, ZAxis, Scatter, PieChart, Pie, Cell } from 'recharts';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var ChartErrorBoundary = class extends Component {
|
|
7
|
+
constructor(props) {
|
|
8
|
+
super(props);
|
|
9
|
+
this.state = { hasError: false };
|
|
10
|
+
}
|
|
11
|
+
static getDerivedStateFromError() {
|
|
12
|
+
return { hasError: true };
|
|
13
|
+
}
|
|
14
|
+
componentDidCatch(error, info) {
|
|
15
|
+
console.error("Chart rendering failed:", error, info.componentStack);
|
|
16
|
+
}
|
|
17
|
+
render() {
|
|
18
|
+
if (this.state.hasError) {
|
|
19
|
+
return this.props.fallback ?? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400", children: "Chart could not be rendered. Switch to Table view to see your data." });
|
|
20
|
+
}
|
|
21
|
+
return this.props.children;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
function getColors(dark) {
|
|
25
|
+
return dark ? CHART_COLORS_DARK : CHART_COLORS_LIGHT;
|
|
26
|
+
}
|
|
27
|
+
function themeTokens(dark) {
|
|
28
|
+
return {
|
|
29
|
+
grid: dark ? "#3f3f46" : "#e4e4e7",
|
|
30
|
+
axis: dark ? "#a1a1aa" : "#71717a",
|
|
31
|
+
tooltipBg: dark ? "#18181b" : "#ffffff",
|
|
32
|
+
tooltipBorder: dark ? "#3f3f46" : "#e4e4e7",
|
|
33
|
+
tooltipText: dark ? "#e4e4e7" : "#27272a",
|
|
34
|
+
legendText: dark ? "#a1a1aa" : "#71717a"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function formatNumber(value) {
|
|
38
|
+
const num = Number(value);
|
|
39
|
+
if (!isFinite(num)) return String(value ?? "");
|
|
40
|
+
if (Math.abs(num) >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
|
|
41
|
+
if (Math.abs(num) >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
|
|
42
|
+
return Number.isInteger(num) ? num.toLocaleString() : num.toFixed(2);
|
|
43
|
+
}
|
|
44
|
+
function truncateLabel(label, maxLen = 12) {
|
|
45
|
+
const str = String(label ?? "");
|
|
46
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "\u2026" : str;
|
|
47
|
+
}
|
|
48
|
+
var TOOLTIP_LABEL_STYLE = { fontWeight: 600, marginBottom: 4 };
|
|
49
|
+
var tooltipStyleCache = /* @__PURE__ */ new Map();
|
|
50
|
+
function getTooltipStyle(dark) {
|
|
51
|
+
let style = tooltipStyleCache.get(dark);
|
|
52
|
+
if (!style) {
|
|
53
|
+
const t = themeTokens(dark);
|
|
54
|
+
style = {
|
|
55
|
+
background: t.tooltipBg,
|
|
56
|
+
border: `1px solid ${t.tooltipBorder}`,
|
|
57
|
+
borderRadius: 6,
|
|
58
|
+
padding: "8px 12px",
|
|
59
|
+
fontSize: 12,
|
|
60
|
+
color: t.tooltipText
|
|
61
|
+
};
|
|
62
|
+
tooltipStyleCache.set(dark, style);
|
|
63
|
+
}
|
|
64
|
+
return style;
|
|
65
|
+
}
|
|
66
|
+
function ChartTooltip({ active, payload, label, dark }) {
|
|
67
|
+
if (!active || !payload?.length) return null;
|
|
68
|
+
return /* @__PURE__ */ jsxs("div", { style: getTooltipStyle(dark), children: [
|
|
69
|
+
label && /* @__PURE__ */ jsx("p", { style: TOOLTIP_LABEL_STYLE, children: label }),
|
|
70
|
+
payload.map((entry, i) => /* @__PURE__ */ jsxs("p", { style: { color: entry.color }, children: [
|
|
71
|
+
entry.name,
|
|
72
|
+
": ",
|
|
73
|
+
typeof entry.value === "number" ? formatNumber(entry.value) : entry.value
|
|
74
|
+
] }, i))
|
|
75
|
+
] });
|
|
76
|
+
}
|
|
77
|
+
function BarChartView({
|
|
78
|
+
data,
|
|
79
|
+
rec,
|
|
80
|
+
dark
|
|
81
|
+
}) {
|
|
82
|
+
const colors = getColors(dark);
|
|
83
|
+
const t = themeTokens(dark);
|
|
84
|
+
const catKey = rec.categoryColumn.header;
|
|
85
|
+
const valKeys = rec.valueColumns.map((c) => c.header);
|
|
86
|
+
return /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] sm:aspect-[16/9]", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 8, bottom: 40, left: 8 }, children: [
|
|
87
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: t.grid }),
|
|
88
|
+
/* @__PURE__ */ jsx(
|
|
89
|
+
XAxis,
|
|
90
|
+
{
|
|
91
|
+
dataKey: catKey,
|
|
92
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
93
|
+
tickFormatter: (v) => truncateLabel(v),
|
|
94
|
+
angle: -45,
|
|
95
|
+
textAnchor: "end",
|
|
96
|
+
height: 60
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fill: t.axis, fontSize: 11 }, tickFormatter: formatNumber }),
|
|
100
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltip, { dark }) }),
|
|
101
|
+
valKeys.length > 1 && /* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12, color: t.legendText } }),
|
|
102
|
+
valKeys.map((key, i) => /* @__PURE__ */ jsx(
|
|
103
|
+
Bar,
|
|
104
|
+
{
|
|
105
|
+
dataKey: key,
|
|
106
|
+
fill: colors[i % colors.length],
|
|
107
|
+
radius: [4, 4, 0, 0]
|
|
108
|
+
},
|
|
109
|
+
key
|
|
110
|
+
))
|
|
111
|
+
] }) }) });
|
|
112
|
+
}
|
|
113
|
+
function LineChartView({
|
|
114
|
+
data,
|
|
115
|
+
rec,
|
|
116
|
+
dark
|
|
117
|
+
}) {
|
|
118
|
+
const colors = getColors(dark);
|
|
119
|
+
const t = themeTokens(dark);
|
|
120
|
+
const catKey = rec.categoryColumn.header;
|
|
121
|
+
const valKeys = rec.valueColumns.map((c) => c.header);
|
|
122
|
+
return /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] sm:aspect-[16/9]", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(LineChart, { data, margin: { top: 8, right: 8, bottom: 40, left: 8 }, children: [
|
|
123
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: t.grid }),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
XAxis,
|
|
126
|
+
{
|
|
127
|
+
dataKey: catKey,
|
|
128
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
129
|
+
tickFormatter: (v) => truncateLabel(v),
|
|
130
|
+
angle: -45,
|
|
131
|
+
textAnchor: "end",
|
|
132
|
+
height: 60
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fill: t.axis, fontSize: 11 }, tickFormatter: formatNumber }),
|
|
136
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltip, { dark }) }),
|
|
137
|
+
valKeys.length > 1 && /* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12, color: t.legendText } }),
|
|
138
|
+
valKeys.map((key, i) => /* @__PURE__ */ jsx(
|
|
139
|
+
Line,
|
|
140
|
+
{
|
|
141
|
+
type: "monotone",
|
|
142
|
+
dataKey: key,
|
|
143
|
+
stroke: colors[i % colors.length],
|
|
144
|
+
strokeWidth: 2,
|
|
145
|
+
dot: { r: 3, fill: colors[i % colors.length] },
|
|
146
|
+
activeDot: { r: 5 }
|
|
147
|
+
},
|
|
148
|
+
key
|
|
149
|
+
))
|
|
150
|
+
] }) }) });
|
|
151
|
+
}
|
|
152
|
+
function PieChartView({
|
|
153
|
+
data,
|
|
154
|
+
rec,
|
|
155
|
+
dark
|
|
156
|
+
}) {
|
|
157
|
+
const colors = getColors(dark);
|
|
158
|
+
const t = themeTokens(dark);
|
|
159
|
+
const catKey = rec.categoryColumn.header;
|
|
160
|
+
const valKey = rec.valueColumns[0].header;
|
|
161
|
+
const total = data.reduce((sum, d) => sum + (typeof d[valKey] === "number" ? d[valKey] : 0), 0);
|
|
162
|
+
const hasNegative = data.some((d) => typeof d[valKey] === "number" && d[valKey] < 0);
|
|
163
|
+
if (total <= 0 || hasNegative) {
|
|
164
|
+
return /* @__PURE__ */ jsx("div", { className: "flex aspect-[4/3] items-center justify-center text-xs text-zinc-400 sm:aspect-[16/9]", children: "Pie chart is not suitable for this data." });
|
|
165
|
+
}
|
|
166
|
+
return /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] sm:aspect-[16/9]", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
Pie,
|
|
169
|
+
{
|
|
170
|
+
data,
|
|
171
|
+
dataKey: valKey,
|
|
172
|
+
nameKey: catKey,
|
|
173
|
+
cx: "50%",
|
|
174
|
+
cy: "50%",
|
|
175
|
+
innerRadius: 40,
|
|
176
|
+
outerRadius: 100,
|
|
177
|
+
label: ({ name, value }) => `${truncateLabel(String(name ?? ""), 10)} ${total > 0 && value != null ? (value / total * 100).toFixed(0) : 0}%`,
|
|
178
|
+
labelLine: { stroke: t.axis },
|
|
179
|
+
fontSize: 11,
|
|
180
|
+
children: data.map((_, i) => /* @__PURE__ */ jsx(Cell, { fill: colors[i % colors.length] }, i))
|
|
181
|
+
}
|
|
182
|
+
),
|
|
183
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltip, { dark }) })
|
|
184
|
+
] }) }) });
|
|
185
|
+
}
|
|
186
|
+
function AreaChartView({
|
|
187
|
+
data,
|
|
188
|
+
rec,
|
|
189
|
+
dark
|
|
190
|
+
}) {
|
|
191
|
+
const chartId = useId();
|
|
192
|
+
const colors = getColors(dark);
|
|
193
|
+
const t = themeTokens(dark);
|
|
194
|
+
const catKey = rec.categoryColumn.header;
|
|
195
|
+
const valKeys = rec.valueColumns.map((c) => c.header);
|
|
196
|
+
return /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ jsxs(AreaChart, { data, margin: { top: 8, right: 8, bottom: 40, left: 8 }, children: [
|
|
197
|
+
/* @__PURE__ */ jsx("defs", { children: valKeys.map((key, i) => /* @__PURE__ */ jsxs("linearGradient", { id: `area-grad-${chartId}-${i}`, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
198
|
+
/* @__PURE__ */ jsx("stop", { offset: "5%", stopColor: colors[i % colors.length], stopOpacity: 0.3 }),
|
|
199
|
+
/* @__PURE__ */ jsx("stop", { offset: "95%", stopColor: colors[i % colors.length], stopOpacity: 0.05 })
|
|
200
|
+
] }, key)) }),
|
|
201
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: t.grid }),
|
|
202
|
+
/* @__PURE__ */ jsx(
|
|
203
|
+
XAxis,
|
|
204
|
+
{
|
|
205
|
+
dataKey: catKey,
|
|
206
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
207
|
+
tickFormatter: (v) => truncateLabel(v),
|
|
208
|
+
angle: -45,
|
|
209
|
+
textAnchor: "end",
|
|
210
|
+
height: 60
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fill: t.axis, fontSize: 11 }, tickFormatter: formatNumber }),
|
|
214
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltip, { dark }) }),
|
|
215
|
+
valKeys.length > 1 && /* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12, color: t.legendText } }),
|
|
216
|
+
valKeys.map((key, i) => /* @__PURE__ */ jsx(
|
|
217
|
+
Area,
|
|
218
|
+
{
|
|
219
|
+
type: "monotone",
|
|
220
|
+
dataKey: key,
|
|
221
|
+
stroke: colors[i % colors.length],
|
|
222
|
+
strokeWidth: 2,
|
|
223
|
+
fill: `url(#area-grad-${chartId}-${i})`
|
|
224
|
+
},
|
|
225
|
+
key
|
|
226
|
+
))
|
|
227
|
+
] }) });
|
|
228
|
+
}
|
|
229
|
+
function StackedBarChartView({
|
|
230
|
+
data,
|
|
231
|
+
rec,
|
|
232
|
+
dark
|
|
233
|
+
}) {
|
|
234
|
+
const colors = getColors(dark);
|
|
235
|
+
const t = themeTokens(dark);
|
|
236
|
+
const catKey = rec.categoryColumn.header;
|
|
237
|
+
const valKeys = rec.valueColumns.map((c) => c.header);
|
|
238
|
+
return /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ jsxs(BarChart, { data, margin: { top: 8, right: 8, bottom: 40, left: 8 }, children: [
|
|
239
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: t.grid }),
|
|
240
|
+
/* @__PURE__ */ jsx(
|
|
241
|
+
XAxis,
|
|
242
|
+
{
|
|
243
|
+
dataKey: catKey,
|
|
244
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
245
|
+
tickFormatter: (v) => truncateLabel(v),
|
|
246
|
+
angle: -45,
|
|
247
|
+
textAnchor: "end",
|
|
248
|
+
height: 60
|
|
249
|
+
}
|
|
250
|
+
),
|
|
251
|
+
/* @__PURE__ */ jsx(YAxis, { tick: { fill: t.axis, fontSize: 11 }, tickFormatter: formatNumber }),
|
|
252
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ChartTooltip, { dark }) }),
|
|
253
|
+
/* @__PURE__ */ jsx(Legend, { wrapperStyle: { fontSize: 12, color: t.legendText } }),
|
|
254
|
+
valKeys.map((key, i) => /* @__PURE__ */ jsx(
|
|
255
|
+
Bar,
|
|
256
|
+
{
|
|
257
|
+
dataKey: key,
|
|
258
|
+
stackId: "a",
|
|
259
|
+
fill: colors[i % colors.length],
|
|
260
|
+
radius: i === valKeys.length - 1 ? [4, 4, 0, 0] : void 0
|
|
261
|
+
},
|
|
262
|
+
key
|
|
263
|
+
))
|
|
264
|
+
] }) });
|
|
265
|
+
}
|
|
266
|
+
function ScatterChartView({
|
|
267
|
+
data,
|
|
268
|
+
rec,
|
|
269
|
+
dark
|
|
270
|
+
}) {
|
|
271
|
+
const colors = getColors(dark);
|
|
272
|
+
const t = themeTokens(dark);
|
|
273
|
+
const xKey = rec.categoryColumn.header;
|
|
274
|
+
const yKey = rec.valueColumns[0].header;
|
|
275
|
+
const zKey = rec.valueColumns.length > 1 ? rec.valueColumns[1].header : void 0;
|
|
276
|
+
return /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ jsxs(ScatterChart, { margin: { top: 8, right: 8, bottom: 40, left: 8 }, children: [
|
|
277
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: t.grid }),
|
|
278
|
+
/* @__PURE__ */ jsx(
|
|
279
|
+
XAxis,
|
|
280
|
+
{
|
|
281
|
+
dataKey: xKey,
|
|
282
|
+
type: "number",
|
|
283
|
+
name: xKey,
|
|
284
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
285
|
+
tickFormatter: formatNumber
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
/* @__PURE__ */ jsx(
|
|
289
|
+
YAxis,
|
|
290
|
+
{
|
|
291
|
+
dataKey: yKey,
|
|
292
|
+
type: "number",
|
|
293
|
+
name: yKey,
|
|
294
|
+
tick: { fill: t.axis, fontSize: 11 },
|
|
295
|
+
tickFormatter: formatNumber
|
|
296
|
+
}
|
|
297
|
+
),
|
|
298
|
+
zKey && /* @__PURE__ */ jsx(ZAxis, { dataKey: zKey, type: "number", name: zKey, range: [40, 400] }),
|
|
299
|
+
/* @__PURE__ */ jsx(
|
|
300
|
+
Tooltip,
|
|
301
|
+
{
|
|
302
|
+
content: /* @__PURE__ */ jsx(ChartTooltip, { dark }),
|
|
303
|
+
cursor: { strokeDasharray: "3 3" }
|
|
304
|
+
}
|
|
305
|
+
),
|
|
306
|
+
/* @__PURE__ */ jsx(
|
|
307
|
+
Scatter,
|
|
308
|
+
{
|
|
309
|
+
data,
|
|
310
|
+
fill: colors[0]
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
] }) });
|
|
314
|
+
}
|
|
315
|
+
var CHART_LABELS = {
|
|
316
|
+
bar: "Bar",
|
|
317
|
+
line: "Line",
|
|
318
|
+
pie: "Pie",
|
|
319
|
+
area: "Area",
|
|
320
|
+
"stacked-bar": "Stacked",
|
|
321
|
+
scatter: "Scatter"
|
|
322
|
+
};
|
|
323
|
+
function ChartTypeSelector({
|
|
324
|
+
recommendations,
|
|
325
|
+
active,
|
|
326
|
+
onChange
|
|
327
|
+
}) {
|
|
328
|
+
if (recommendations.length <= 1) return null;
|
|
329
|
+
const seen = /* @__PURE__ */ new Set();
|
|
330
|
+
const unique = recommendations.filter((r) => {
|
|
331
|
+
if (seen.has(r.type)) return false;
|
|
332
|
+
seen.add(r.type);
|
|
333
|
+
return true;
|
|
334
|
+
});
|
|
335
|
+
if (unique.length <= 1) return null;
|
|
336
|
+
return /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: unique.map((rec) => /* @__PURE__ */ jsx(
|
|
337
|
+
"button",
|
|
338
|
+
{
|
|
339
|
+
onClick: () => onChange(rec.type),
|
|
340
|
+
className: `rounded px-2 py-0.5 text-xs font-medium transition-colors ${active === rec.type ? "bg-blue-100 text-blue-700 dark:bg-blue-600/20 dark:text-blue-400" : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"}`,
|
|
341
|
+
children: CHART_LABELS[rec.type]
|
|
342
|
+
},
|
|
343
|
+
rec.type
|
|
344
|
+
)) });
|
|
345
|
+
}
|
|
346
|
+
function ChartRenderer({
|
|
347
|
+
rows,
|
|
348
|
+
rec,
|
|
349
|
+
defaultData,
|
|
350
|
+
defaultRec,
|
|
351
|
+
dark
|
|
352
|
+
}) {
|
|
353
|
+
const chartData = rec === defaultRec ? defaultData : transformData(rows, rec);
|
|
354
|
+
const type = rec.type;
|
|
355
|
+
return /* @__PURE__ */ jsx("div", { className: "p-2", children: type === "bar" ? /* @__PURE__ */ jsx(BarChartView, { data: chartData, rec, dark }) : type === "line" ? /* @__PURE__ */ jsx(LineChartView, { data: chartData, rec, dark }) : type === "area" ? /* @__PURE__ */ jsx(AreaChartView, { data: chartData, rec, dark }) : type === "stacked-bar" ? /* @__PURE__ */ jsx(StackedBarChartView, { data: chartData, rec, dark }) : type === "scatter" ? /* @__PURE__ */ jsx(ScatterChartView, { data: chartData, rec, dark }) : /* @__PURE__ */ jsx(PieChartView, { data: chartData, rec, dark }) });
|
|
356
|
+
}
|
|
357
|
+
function ResultChart({
|
|
358
|
+
headers,
|
|
359
|
+
rows,
|
|
360
|
+
dark,
|
|
361
|
+
detectionResult
|
|
362
|
+
}) {
|
|
363
|
+
const result = useMemo(
|
|
364
|
+
() => detectionResult ?? detectCharts(headers, rows),
|
|
365
|
+
[headers, rows, detectionResult]
|
|
366
|
+
);
|
|
367
|
+
const [activeType, setActiveType] = useState(null);
|
|
368
|
+
if (!result.chartable) return null;
|
|
369
|
+
const currentType = activeType ?? result.recommendations[0].type;
|
|
370
|
+
const currentRec = result.recommendations.find((r) => r.type === currentType) ?? result.recommendations[0];
|
|
371
|
+
return /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-700", children: [
|
|
372
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-zinc-100 bg-zinc-50/50 px-3 py-2 dark:border-zinc-800 dark:bg-zinc-900/50", children: [
|
|
373
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-500 dark:text-zinc-400", children: currentRec.reason }),
|
|
374
|
+
/* @__PURE__ */ jsx(
|
|
375
|
+
ChartTypeSelector,
|
|
376
|
+
{
|
|
377
|
+
recommendations: result.recommendations,
|
|
378
|
+
active: currentType,
|
|
379
|
+
onChange: setActiveType
|
|
380
|
+
}
|
|
381
|
+
)
|
|
382
|
+
] }),
|
|
383
|
+
/* @__PURE__ */ jsx(ChartErrorBoundary, { children: /* @__PURE__ */ jsx(
|
|
384
|
+
ChartRenderer,
|
|
385
|
+
{
|
|
386
|
+
rows,
|
|
387
|
+
rec: currentRec,
|
|
388
|
+
defaultData: result.data,
|
|
389
|
+
defaultRec: result.recommendations[0],
|
|
390
|
+
dark
|
|
391
|
+
}
|
|
392
|
+
) }, currentType)
|
|
393
|
+
] });
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export { ResultChart };
|
|
397
|
+
//# sourceMappingURL=result-chart-NFAJ4IQ5.js.map
|
|
398
|
+
//# sourceMappingURL=result-chart-NFAJ4IQ5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chart/result-chart.tsx"],"names":[],"mappings":"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","file":"result-chart-NFAJ4IQ5.js","sourcesContent":["\"use client\";\n\nimport { Component, type ReactNode, type ErrorInfo, useMemo, useId, useState } from \"react\";\nimport {\n ResponsiveContainer,\n BarChart,\n Bar,\n LineChart,\n Line,\n AreaChart,\n Area,\n ScatterChart,\n Scatter,\n ZAxis,\n PieChart,\n Pie,\n Cell,\n CartesianGrid,\n XAxis,\n YAxis,\n Tooltip,\n Legend,\n} from \"recharts\";\nimport {\n detectCharts,\n transformData,\n CHART_COLORS_LIGHT,\n CHART_COLORS_DARK,\n type ChartRecommendation,\n type ChartType,\n type RechartsRow,\n type ChartDetectionResult,\n} from \"./chart-detection\";\n\n/* ------------------------------------------------------------------ */\n/* Error boundary */\n/* ------------------------------------------------------------------ */\n\nclass ChartErrorBoundary extends Component<\n { children: ReactNode; fallback?: ReactNode },\n { hasError: boolean }\n> {\n constructor(props: { children: ReactNode; fallback?: ReactNode }) {\n super(props);\n this.state = { hasError: false };\n }\n\n static getDerivedStateFromError(): { hasError: boolean } {\n return { hasError: true };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo) {\n console.error(\"Chart rendering failed:\", error, info.componentStack);\n }\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ?? (\n <div className=\"rounded-lg border border-yellow-300 bg-yellow-50 px-3 py-2 text-xs text-yellow-700 dark:border-yellow-900/50 dark:bg-yellow-950/20 dark:text-yellow-400\">\n Chart could not be rendered. Switch to Table view to see your data.\n </div>\n );\n }\n return this.props.children;\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Theme helpers */\n/* ------------------------------------------------------------------ */\n\nfunction getColors(dark: boolean) {\n return dark ? CHART_COLORS_DARK : CHART_COLORS_LIGHT;\n}\n\nfunction themeTokens(dark: boolean) {\n return {\n grid: dark ? \"#3f3f46\" : \"#e4e4e7\",\n axis: dark ? \"#a1a1aa\" : \"#71717a\",\n tooltipBg: dark ? \"#18181b\" : \"#ffffff\",\n tooltipBorder: dark ? \"#3f3f46\" : \"#e4e4e7\",\n tooltipText: dark ? \"#e4e4e7\" : \"#27272a\",\n legendText: dark ? \"#a1a1aa\" : \"#71717a\",\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Number formatter for axis / tooltip */\n/* ------------------------------------------------------------------ */\n\nfunction formatNumber(value: unknown): string {\n const num = Number(value);\n if (!isFinite(num)) return String(value ?? \"\");\n if (Math.abs(num) >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;\n if (Math.abs(num) >= 1_000) return `${(num / 1_000).toFixed(1)}K`;\n return Number.isInteger(num) ? num.toLocaleString() : num.toFixed(2);\n}\n\nfunction truncateLabel(label: unknown, maxLen = 12): string {\n const str = String(label ?? \"\");\n return str.length > maxLen ? str.slice(0, maxLen) + \"\\u2026\" : str;\n}\n\n/* ------------------------------------------------------------------ */\n/* Tooltip */\n/* ------------------------------------------------------------------ */\n\nconst TOOLTIP_LABEL_STYLE = { fontWeight: 600, marginBottom: 4 } as const;\n\nconst tooltipStyleCache = new Map<boolean, React.CSSProperties>();\nfunction getTooltipStyle(dark: boolean): React.CSSProperties {\n let style = tooltipStyleCache.get(dark);\n if (!style) {\n const t = themeTokens(dark);\n style = {\n background: t.tooltipBg,\n border: `1px solid ${t.tooltipBorder}`,\n borderRadius: 6,\n padding: \"8px 12px\",\n fontSize: 12,\n color: t.tooltipText,\n };\n tooltipStyleCache.set(dark, style);\n }\n return style;\n}\n\nfunction ChartTooltip({ active, payload, label, dark }: {\n active?: boolean;\n payload?: Array<{ name: string; value: number; color: string }>;\n label?: string;\n dark: boolean;\n}) {\n if (!active || !payload?.length) return null;\n return (\n <div style={getTooltipStyle(dark)}>\n {label && <p style={TOOLTIP_LABEL_STYLE}>{label}</p>}\n {payload.map((entry, i) => (\n <p key={i} style={{ color: entry.color }}>\n {entry.name}: {typeof entry.value === \"number\" ? formatNumber(entry.value) : entry.value}\n </p>\n ))}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Sub-chart components */\n/* ------------------------------------------------------------------ */\n\nfunction BarChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const catKey = rec.categoryColumn.header;\n const valKeys = rec.valueColumns.map((c) => c.header);\n\n return (\n <div className=\"aspect-[4/3] sm:aspect-[16/9]\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={data} margin={{ top: 8, right: 8, bottom: 40, left: 8 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={t.grid} />\n <XAxis\n dataKey={catKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={(v: string) => truncateLabel(v)}\n angle={-45}\n textAnchor=\"end\"\n height={60}\n />\n <YAxis tick={{ fill: t.axis, fontSize: 11 }} tickFormatter={formatNumber} />\n <Tooltip content={<ChartTooltip dark={dark} />} />\n {valKeys.length > 1 && (\n <Legend wrapperStyle={{ fontSize: 12, color: t.legendText }} />\n )}\n {valKeys.map((key, i) => (\n <Bar\n key={key}\n dataKey={key}\n fill={colors[i % colors.length]}\n radius={[4, 4, 0, 0]}\n />\n ))}\n </BarChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\nfunction LineChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const catKey = rec.categoryColumn.header;\n const valKeys = rec.valueColumns.map((c) => c.header);\n\n return (\n <div className=\"aspect-[4/3] sm:aspect-[16/9]\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <LineChart data={data} margin={{ top: 8, right: 8, bottom: 40, left: 8 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={t.grid} />\n <XAxis\n dataKey={catKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={(v: string) => truncateLabel(v)}\n angle={-45}\n textAnchor=\"end\"\n height={60}\n />\n <YAxis tick={{ fill: t.axis, fontSize: 11 }} tickFormatter={formatNumber} />\n <Tooltip content={<ChartTooltip dark={dark} />} />\n {valKeys.length > 1 && (\n <Legend wrapperStyle={{ fontSize: 12, color: t.legendText }} />\n )}\n {valKeys.map((key, i) => (\n <Line\n key={key}\n type=\"monotone\"\n dataKey={key}\n stroke={colors[i % colors.length]}\n strokeWidth={2}\n dot={{ r: 3, fill: colors[i % colors.length] }}\n activeDot={{ r: 5 }}\n />\n ))}\n </LineChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\nfunction PieChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const catKey = rec.categoryColumn.header;\n const valKey = rec.valueColumns[0].header;\n\n const total = data.reduce((sum, d) => sum + (typeof d[valKey] === \"number\" ? (d[valKey] as number) : 0), 0);\n\n const hasNegative = data.some(d => typeof d[valKey] === \"number\" && (d[valKey] as number) < 0);\n if (total <= 0 || hasNegative) {\n return (\n <div className=\"flex aspect-[4/3] items-center justify-center text-xs text-zinc-400 sm:aspect-[16/9]\">\n Pie chart is not suitable for this data.\n </div>\n );\n }\n\n return (\n <div className=\"aspect-[4/3] sm:aspect-[16/9]\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={data}\n dataKey={valKey}\n nameKey={catKey}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={40}\n outerRadius={100}\n label={({ name, value }: { name?: string; value?: number }) =>\n `${truncateLabel(String(name ?? \"\"), 10)} ${total > 0 && value != null ? ((value / total) * 100).toFixed(0) : 0}%`\n }\n labelLine={{ stroke: t.axis }}\n fontSize={11}\n >\n {data.map((_, i) => (\n <Cell key={i} fill={colors[i % colors.length]} />\n ))}\n </Pie>\n <Tooltip content={<ChartTooltip dark={dark} />} />\n </PieChart>\n </ResponsiveContainer>\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Area chart */\n/* ------------------------------------------------------------------ */\n\nfunction AreaChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const chartId = useId();\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const catKey = rec.categoryColumn.header;\n const valKeys = rec.valueColumns.map((c) => c.header);\n\n return (\n <ResponsiveContainer width=\"100%\" height={300}>\n <AreaChart data={data} margin={{ top: 8, right: 8, bottom: 40, left: 8 }}>\n <defs>\n {valKeys.map((key, i) => (\n <linearGradient key={key} id={`area-grad-${chartId}-${i}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"5%\" stopColor={colors[i % colors.length]} stopOpacity={0.3} />\n <stop offset=\"95%\" stopColor={colors[i % colors.length]} stopOpacity={0.05} />\n </linearGradient>\n ))}\n </defs>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={t.grid} />\n <XAxis\n dataKey={catKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={(v: string) => truncateLabel(v)}\n angle={-45}\n textAnchor=\"end\"\n height={60}\n />\n <YAxis tick={{ fill: t.axis, fontSize: 11 }} tickFormatter={formatNumber} />\n <Tooltip content={<ChartTooltip dark={dark} />} />\n {valKeys.length > 1 && (\n <Legend wrapperStyle={{ fontSize: 12, color: t.legendText }} />\n )}\n {valKeys.map((key, i) => (\n <Area\n key={key}\n type=\"monotone\"\n dataKey={key}\n stroke={colors[i % colors.length]}\n strokeWidth={2}\n fill={`url(#area-grad-${chartId}-${i})`}\n />\n ))}\n </AreaChart>\n </ResponsiveContainer>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Stacked bar chart */\n/* ------------------------------------------------------------------ */\n\nfunction StackedBarChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const catKey = rec.categoryColumn.header;\n const valKeys = rec.valueColumns.map((c) => c.header);\n\n return (\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={data} margin={{ top: 8, right: 8, bottom: 40, left: 8 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={t.grid} />\n <XAxis\n dataKey={catKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={(v: string) => truncateLabel(v)}\n angle={-45}\n textAnchor=\"end\"\n height={60}\n />\n <YAxis tick={{ fill: t.axis, fontSize: 11 }} tickFormatter={formatNumber} />\n <Tooltip content={<ChartTooltip dark={dark} />} />\n <Legend wrapperStyle={{ fontSize: 12, color: t.legendText }} />\n {valKeys.map((key, i) => (\n <Bar\n key={key}\n dataKey={key}\n stackId=\"a\"\n fill={colors[i % colors.length]}\n radius={i === valKeys.length - 1 ? [4, 4, 0, 0] : undefined}\n />\n ))}\n </BarChart>\n </ResponsiveContainer>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Scatter chart */\n/* ------------------------------------------------------------------ */\n\nfunction ScatterChartView({\n data,\n rec,\n dark,\n}: {\n data: RechartsRow[];\n rec: ChartRecommendation;\n dark: boolean;\n}) {\n const colors = getColors(dark);\n const t = themeTokens(dark);\n const xKey = rec.categoryColumn.header;\n const yKey = rec.valueColumns[0].header;\n const zKey = rec.valueColumns.length > 1 ? rec.valueColumns[1].header : undefined;\n\n return (\n <ResponsiveContainer width=\"100%\" height={300}>\n <ScatterChart margin={{ top: 8, right: 8, bottom: 40, left: 8 }}>\n <CartesianGrid strokeDasharray=\"3 3\" stroke={t.grid} />\n <XAxis\n dataKey={xKey}\n type=\"number\"\n name={xKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={formatNumber}\n />\n <YAxis\n dataKey={yKey}\n type=\"number\"\n name={yKey}\n tick={{ fill: t.axis, fontSize: 11 }}\n tickFormatter={formatNumber}\n />\n {zKey && <ZAxis dataKey={zKey} type=\"number\" name={zKey} range={[40, 400]} />}\n <Tooltip\n content={<ChartTooltip dark={dark} />}\n cursor={{ strokeDasharray: \"3 3\" }}\n />\n <Scatter\n data={data}\n fill={colors[0]}\n />\n </ScatterChart>\n </ResponsiveContainer>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Chart type selector */\n/* ------------------------------------------------------------------ */\n\nconst CHART_LABELS: Record<ChartType, string> = {\n bar: \"Bar\",\n line: \"Line\",\n pie: \"Pie\",\n area: \"Area\",\n \"stacked-bar\": \"Stacked\",\n scatter: \"Scatter\",\n};\n\nfunction ChartTypeSelector({\n recommendations,\n active,\n onChange,\n}: {\n recommendations: ChartRecommendation[];\n active: ChartType;\n onChange: (t: ChartType) => void;\n}) {\n if (recommendations.length <= 1) return null;\n\n const seen = new Set<ChartType>();\n const unique = recommendations.filter((r) => {\n if (seen.has(r.type)) return false;\n seen.add(r.type);\n return true;\n });\n\n if (unique.length <= 1) return null;\n\n return (\n <div className=\"flex gap-1\">\n {unique.map((rec) => (\n <button\n key={rec.type}\n onClick={() => onChange(rec.type)}\n className={`rounded px-2 py-0.5 text-xs font-medium transition-colors ${\n active === rec.type\n ? \"bg-blue-100 text-blue-700 dark:bg-blue-600/20 dark:text-blue-400\"\n : \"text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200\"\n }`}\n >\n {CHART_LABELS[rec.type]}\n </button>\n ))}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Chart renderer (inside error boundary) */\n/* ------------------------------------------------------------------ */\n\nfunction ChartRenderer({\n rows,\n rec,\n defaultData,\n defaultRec,\n dark,\n}: {\n rows: string[][];\n rec: ChartRecommendation;\n defaultData: RechartsRow[];\n defaultRec: ChartRecommendation;\n dark: boolean;\n}) {\n // Re-transform data when switching chart type (category axis may differ)\n const chartData = rec === defaultRec ? defaultData : transformData(rows, rec);\n const type = rec.type;\n\n return (\n <div className=\"p-2\">\n {type === \"bar\" ? <BarChartView data={chartData} rec={rec} dark={dark} />\n : type === \"line\" ? <LineChartView data={chartData} rec={rec} dark={dark} />\n : type === \"area\" ? <AreaChartView data={chartData} rec={rec} dark={dark} />\n : type === \"stacked-bar\" ? <StackedBarChartView data={chartData} rec={rec} dark={dark} />\n : type === \"scatter\" ? <ScatterChartView data={chartData} rec={rec} dark={dark} />\n : <PieChartView data={chartData} rec={rec} dark={dark} />}\n </div>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* Main ResultChart component */\n/* ------------------------------------------------------------------ */\n\nexport function ResultChart({\n headers,\n rows,\n dark,\n detectionResult,\n}: {\n headers: string[];\n rows: string[][];\n dark: boolean;\n detectionResult?: ChartDetectionResult;\n}) {\n const result = useMemo(\n () => detectionResult ?? detectCharts(headers, rows),\n [headers, rows, detectionResult],\n );\n\n const [activeType, setActiveType] = useState<ChartType | null>(null);\n\n if (!result.chartable) return null;\n\n const currentType = activeType ?? result.recommendations[0].type;\n const currentRec = result.recommendations.find((r) => r.type === currentType) ?? result.recommendations[0];\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-zinc-200 dark:border-zinc-700\">\n <div className=\"flex items-center justify-between border-b border-zinc-100 bg-zinc-50/50 px-3 py-2 dark:border-zinc-800 dark:bg-zinc-900/50\">\n <span className=\"text-xs text-zinc-500 dark:text-zinc-400\">{currentRec.reason}</span>\n <ChartTypeSelector\n recommendations={result.recommendations}\n active={currentType}\n onChange={setActiveType}\n />\n </div>\n <ChartErrorBoundary key={currentType}>\n <ChartRenderer\n rows={rows}\n rec={currentRec}\n defaultData={result.data}\n defaultRec={result.recommendations[0]}\n dark={dark}\n />\n </ChartErrorBoundary>\n </div>\n );\n}\n"]}
|