@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.
Files changed (98) hide show
  1. package/README.md +95 -0
  2. package/dist/chunk-2WFDP7G5.js +231 -0
  3. package/dist/chunk-2WFDP7G5.js.map +1 -0
  4. package/dist/chunk-44HBZYKP.js +224 -0
  5. package/dist/chunk-44HBZYKP.js.map +1 -0
  6. package/dist/chunk-5SEVKHS5.cjs +229 -0
  7. package/dist/chunk-5SEVKHS5.cjs.map +1 -0
  8. package/dist/chunk-UIRB6L36.cjs +249 -0
  9. package/dist/chunk-UIRB6L36.cjs.map +1 -0
  10. package/dist/hooks.cjs +251 -0
  11. package/dist/hooks.cjs.map +1 -0
  12. package/dist/hooks.d.cts +132 -0
  13. package/dist/hooks.d.ts +132 -0
  14. package/dist/hooks.js +237 -0
  15. package/dist/hooks.js.map +1 -0
  16. package/dist/index.cjs +2976 -0
  17. package/dist/index.cjs.map +1 -0
  18. package/dist/index.d.cts +69 -0
  19. package/dist/index.d.ts +69 -0
  20. package/dist/index.js +2926 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/result-chart-NFAJ4IQ5.js +398 -0
  23. package/dist/result-chart-NFAJ4IQ5.js.map +1 -0
  24. package/dist/result-chart-YLCKBNV4.cjs +400 -0
  25. package/dist/result-chart-YLCKBNV4.cjs.map +1 -0
  26. package/dist/styles.css +59 -0
  27. package/dist/use-dark-mode-rFxawUv1.d.cts +123 -0
  28. package/dist/use-dark-mode-rFxawUv1.d.ts +123 -0
  29. package/dist/widget.css +2 -0
  30. package/dist/widget.js +445 -0
  31. package/package.json +113 -0
  32. package/src/components/__tests__/tool-renderers.test.tsx +239 -0
  33. package/src/components/actions/action-approval-card.tsx +296 -0
  34. package/src/components/actions/action-status-badge.tsx +50 -0
  35. package/src/components/admin/change-password-dialog.tsx +128 -0
  36. package/src/components/atlas-chat.tsx +656 -0
  37. package/src/components/chart/chart-detection.ts +318 -0
  38. package/src/components/chart/result-chart.tsx +590 -0
  39. package/src/components/chat/api-key-bar.tsx +66 -0
  40. package/src/components/chat/copy-button.tsx +25 -0
  41. package/src/components/chat/data-table.tsx +104 -0
  42. package/src/components/chat/error-banner.tsx +32 -0
  43. package/src/components/chat/explore-card.tsx +41 -0
  44. package/src/components/chat/follow-up-chips.tsx +29 -0
  45. package/src/components/chat/loading-card.tsx +10 -0
  46. package/src/components/chat/managed-auth-card.tsx +116 -0
  47. package/src/components/chat/markdown.tsx +146 -0
  48. package/src/components/chat/python-result-card.tsx +245 -0
  49. package/src/components/chat/sql-block.tsx +54 -0
  50. package/src/components/chat/sql-result-card.tsx +163 -0
  51. package/src/components/chat/starter-prompts.ts +6 -0
  52. package/src/components/chat/tool-part.tsx +106 -0
  53. package/src/components/chat/typing-indicator.tsx +22 -0
  54. package/src/components/conversations/conversation-item.tsx +135 -0
  55. package/src/components/conversations/conversation-list.tsx +69 -0
  56. package/src/components/conversations/conversation-sidebar.tsx +113 -0
  57. package/src/components/conversations/delete-confirmation.tsx +27 -0
  58. package/src/components/schema-explorer/schema-explorer.tsx +517 -0
  59. package/src/components/ui/alert-dialog.tsx +196 -0
  60. package/src/components/ui/badge.tsx +48 -0
  61. package/src/components/ui/button.tsx +64 -0
  62. package/src/components/ui/card.tsx +92 -0
  63. package/src/components/ui/dialog.tsx +158 -0
  64. package/src/components/ui/dropdown-menu.tsx +257 -0
  65. package/src/components/ui/input.tsx +21 -0
  66. package/src/components/ui/label.tsx +24 -0
  67. package/src/components/ui/scroll-area.tsx +62 -0
  68. package/src/components/ui/separator.tsx +28 -0
  69. package/src/components/ui/sheet.tsx +143 -0
  70. package/src/components/ui/table.tsx +116 -0
  71. package/src/components/ui/toggle-group.tsx +83 -0
  72. package/src/components/ui/toggle.tsx +47 -0
  73. package/src/context.tsx +85 -0
  74. package/src/env.d.ts +9 -0
  75. package/src/hooks/__tests__/provider.test.tsx +83 -0
  76. package/src/hooks/__tests__/use-atlas-auth.test.tsx +283 -0
  77. package/src/hooks/__tests__/use-atlas-chat.test.tsx +157 -0
  78. package/src/hooks/__tests__/use-atlas-conversations.test.tsx +159 -0
  79. package/src/hooks/__tests__/use-atlas-theme.test.tsx +56 -0
  80. package/src/hooks/index.ts +47 -0
  81. package/src/hooks/provider.tsx +77 -0
  82. package/src/hooks/theme-init-script.ts +17 -0
  83. package/src/hooks/use-atlas-auth.ts +131 -0
  84. package/src/hooks/use-atlas-chat.ts +102 -0
  85. package/src/hooks/use-atlas-conversations.ts +61 -0
  86. package/src/hooks/use-atlas-theme.ts +34 -0
  87. package/src/hooks/use-conversations.ts +189 -0
  88. package/src/hooks/use-dark-mode.ts +150 -0
  89. package/src/index.ts +36 -0
  90. package/src/lib/action-types.ts +11 -0
  91. package/src/lib/helpers.ts +198 -0
  92. package/src/lib/tool-renderer-types.ts +76 -0
  93. package/src/lib/types.ts +29 -0
  94. package/src/lib/utils.ts +6 -0
  95. package/src/styles.css +59 -0
  96. package/src/test-setup.ts +55 -0
  97. package/src/widget-entry.ts +20 -0
  98. 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"]}