@coral-ai/charts 3.8.1 → 3.10.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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
2
3
  import { ReactNode } from 'react';
3
4
 
4
5
  /**
@@ -32,24 +33,40 @@ type CoralChartSpec = {
32
33
  declare const CORAL_CHART_PALETTE: string[];
33
34
  declare function pickColor(index: number, palette?: readonly string[]): string;
34
35
 
35
- declare function ChartFrame({ title, children, }: {
36
+ declare function ChartFrame({ title, actions, children,
37
+ /**
38
+ * `"inline"` (default): fixed-height body for in-conversation rendering.
39
+ * `"fill"`: body flexes to fill the parent — used by the overlay so the
40
+ * chart scales to whatever size the overlay container is sized to.
41
+ */
42
+ variant, }: {
36
43
  title: string;
44
+ actions?: ReactNode;
37
45
  children: ReactNode;
46
+ variant?: "inline" | "fill";
38
47
  }): react_jsx_runtime.JSX.Element;
39
48
 
40
49
  type CoralBarChartProps = {
41
50
  spec: CoralChartSpec;
42
51
  /** Override the default palette. */
43
52
  colors?: readonly string[];
53
+ /** Right-aligned buttons rendered in the frame header (e.g. export / expand). */
54
+ actions?: react.ReactNode;
55
+ /** Forwarded to ChartFrame — `"fill"` makes the body flex to the parent. */
56
+ variant?: "inline" | "fill";
44
57
  };
45
- declare function CoralBarChart({ spec, colors, }: CoralBarChartProps): react_jsx_runtime.JSX.Element;
58
+ declare function CoralBarChart({ spec, colors, actions, variant, }: CoralBarChartProps): react_jsx_runtime.JSX.Element;
46
59
 
47
60
  type CoralLineChartProps = {
48
61
  spec: CoralChartSpec;
49
62
  /** Override the default palette. */
50
63
  colors?: readonly string[];
64
+ /** Right-aligned buttons rendered in the frame header (e.g. export / expand). */
65
+ actions?: react.ReactNode;
66
+ /** Forwarded to ChartFrame — `"fill"` makes the body flex to the parent. */
67
+ variant?: "inline" | "fill";
51
68
  };
52
- declare function CoralLineChart({ spec, colors, }: CoralLineChartProps): react_jsx_runtime.JSX.Element;
69
+ declare function CoralLineChart({ spec, colors, actions, variant, }: CoralLineChartProps): react_jsx_runtime.JSX.Element;
53
70
 
54
71
  type CoralChartProps = {
55
72
  spec: CoralChartSpec;
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/palette.ts
2
2
  var CORAL_CHART_PALETTE = [
3
- "#7287ff",
4
- "#34c6a3",
5
- "#f5a524",
3
+ "#6f91ff",
4
+ "#29e4c8",
5
+ "#ffdead",
6
6
  "#e85d75",
7
7
  "#9b6ad8",
8
8
  "#4ab8d9"
@@ -12,27 +12,63 @@ function pickColor(index, palette = CORAL_CHART_PALETTE) {
12
12
  }
13
13
 
14
14
  // src/ChartFrame.tsx
15
+ import { tokens } from "@fluentui/react-components";
15
16
  import { jsx, jsxs } from "react/jsx-runtime";
16
- var frameStyle = {
17
- margin: "12px 0",
18
- padding: 12,
19
- border: "1px solid var(--colorNeutralStroke2, #e0e0e0)",
20
- borderRadius: 8
17
+ var baseFrameStyle = {
18
+ display: "flex",
19
+ flexDirection: "column",
20
+ gap: 4,
21
+ padding: 4,
22
+ borderRadius: tokens.borderRadiusXLarge,
23
+ backgroundColor: tokens.colorNeutralBackground2,
24
+ border: `1px solid ${tokens.colorNeutralStroke2}`
25
+ };
26
+ var titleRowStyle = {
27
+ display: "flex",
28
+ alignItems: "center",
29
+ justifyContent: "space-between",
30
+ padding: "4px 4px 4px 8px",
31
+ gap: 8
21
32
  };
22
33
  var titleStyle = {
23
- fontWeight: 600,
24
- marginBottom: 8
34
+ fontFamily: tokens.fontFamilyBase,
35
+ fontSize: tokens.fontSizeBase300,
36
+ fontWeight: tokens.fontWeightSemibold,
37
+ lineHeight: tokens.lineHeightBase300,
38
+ color: tokens.colorNeutralForeground1
39
+ };
40
+ var actionsStyle = {
41
+ display: "flex",
42
+ alignItems: "center",
43
+ gap: 4,
44
+ flexShrink: 0
25
45
  };
26
- var bodyStyle = {
46
+ var baseBodyStyle = {
27
47
  width: "100%",
28
- height: 240
48
+ padding: 12,
49
+ boxSizing: "border-box",
50
+ borderRadius: tokens.borderRadiusLarge,
51
+ backgroundColor: tokens.colorNeutralBackground3,
52
+ border: `1px solid ${tokens.colorNeutralStroke2}`
29
53
  };
30
54
  function ChartFrame({
31
55
  title,
32
- children
56
+ actions,
57
+ children,
58
+ /**
59
+ * `"inline"` (default): fixed-height body for in-conversation rendering.
60
+ * `"fill"`: body flexes to fill the parent — used by the overlay so the
61
+ * chart scales to whatever size the overlay container is sized to.
62
+ */
63
+ variant = "inline"
33
64
  }) {
65
+ const frameStyle = variant === "fill" ? { ...baseFrameStyle, width: "100%", height: "100%" } : { ...baseFrameStyle, margin: "12px 0" };
66
+ const bodyStyle = variant === "fill" ? { ...baseBodyStyle, flex: 1, minHeight: 0 } : { ...baseBodyStyle, height: 320 };
34
67
  return /* @__PURE__ */ jsxs("div", { style: frameStyle, children: [
35
- /* @__PURE__ */ jsx("div", { style: titleStyle, children: title }),
68
+ /* @__PURE__ */ jsxs("div", { style: titleRowStyle, children: [
69
+ /* @__PURE__ */ jsx("span", { style: titleStyle, children: title }),
70
+ actions && /* @__PURE__ */ jsx("div", { style: actionsStyle, children: actions })
71
+ ] }),
36
72
  /* @__PURE__ */ jsx("div", { style: bodyStyle, children })
37
73
  ] });
38
74
  }
@@ -41,30 +77,188 @@ function ChartFrame({
41
77
  import {
42
78
  Bar,
43
79
  BarChart,
44
- CartesianGrid,
45
80
  Legend,
46
81
  ResponsiveContainer,
47
82
  Tooltip,
48
83
  XAxis,
49
84
  YAxis
50
85
  } from "recharts";
86
+
87
+ // src/chartTheme.ts
88
+ import { tokens as tokens4 } from "@fluentui/react-components";
89
+
90
+ // src/CoralLegend.tsx
91
+ import { tokens as tokens2 } from "@fluentui/react-components";
51
92
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
93
+ function CoralLegendContent(props) {
94
+ const { payload } = props;
95
+ if (!payload?.length) return null;
96
+ return /* @__PURE__ */ jsx2(
97
+ "div",
98
+ {
99
+ style: {
100
+ display: "flex",
101
+ flexDirection: "row",
102
+ flexWrap: "wrap",
103
+ alignItems: "center",
104
+ justifyContent: "flex-end",
105
+ gap: 12
106
+ },
107
+ children: payload.map((entry, i) => /* @__PURE__ */ jsxs2(
108
+ "div",
109
+ {
110
+ style: { display: "flex", alignItems: "center", gap: 6 },
111
+ children: [
112
+ /* @__PURE__ */ jsx2(
113
+ "span",
114
+ {
115
+ style: {
116
+ width: 12,
117
+ height: 12,
118
+ borderRadius: 4,
119
+ backgroundColor: entry.color,
120
+ flexShrink: 0
121
+ }
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsx2(
125
+ "span",
126
+ {
127
+ style: {
128
+ color: tokens2.colorNeutralForeground2,
129
+ fontFamily: tokens2.fontFamilyBase,
130
+ fontSize: tokens2.fontSizeBase200,
131
+ lineHeight: tokens2.lineHeightBase200
132
+ },
133
+ children: entry.value
134
+ }
135
+ )
136
+ ]
137
+ },
138
+ `${entry.value}-${i}`
139
+ ))
140
+ }
141
+ );
142
+ }
143
+
144
+ // src/CoralTooltip.tsx
145
+ import { tokens as tokens3 } from "@fluentui/react-components";
146
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
147
+ function CoralTooltipContent(props) {
148
+ const { active, payload, label } = props;
149
+ if (!active || !payload?.length) return null;
150
+ return /* @__PURE__ */ jsxs3(
151
+ "div",
152
+ {
153
+ style: {
154
+ display: "flex",
155
+ flexDirection: "column",
156
+ gap: 12,
157
+ padding: 16,
158
+ borderRadius: 12,
159
+ backgroundColor: tokens3.colorNeutralBackground2,
160
+ minWidth: 160
161
+ },
162
+ children: [
163
+ label != null && /* @__PURE__ */ jsx3(
164
+ "div",
165
+ {
166
+ style: {
167
+ color: tokens3.colorNeutralForeground2,
168
+ fontFamily: tokens3.fontFamilyBase,
169
+ fontSize: tokens3.fontSizeBase200,
170
+ fontWeight: tokens3.fontWeightSemibold,
171
+ lineHeight: tokens3.lineHeightBase200
172
+ },
173
+ children: label
174
+ }
175
+ ),
176
+ /* @__PURE__ */ jsx3("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: payload.map((entry, i) => /* @__PURE__ */ jsxs3(
177
+ "div",
178
+ {
179
+ style: { display: "flex", alignItems: "center", gap: 6 },
180
+ children: [
181
+ /* @__PURE__ */ jsx3(
182
+ "span",
183
+ {
184
+ style: {
185
+ width: 12,
186
+ height: 12,
187
+ borderRadius: 4,
188
+ backgroundColor: entry.color,
189
+ flexShrink: 0
190
+ }
191
+ }
192
+ ),
193
+ /* @__PURE__ */ jsxs3(
194
+ "span",
195
+ {
196
+ style: {
197
+ color: tokens3.colorNeutralForeground3,
198
+ fontFamily: tokens3.fontFamilyBase,
199
+ fontSize: tokens3.fontSizeBase200,
200
+ lineHeight: tokens3.lineHeightBase200
201
+ },
202
+ children: [
203
+ entry.name,
204
+ ": ",
205
+ entry.value
206
+ ]
207
+ }
208
+ )
209
+ ]
210
+ },
211
+ `${entry.dataKey}-${i}`
212
+ )) })
213
+ ]
214
+ }
215
+ );
216
+ }
217
+
218
+ // src/chartTheme.ts
219
+ var gridProps = {
220
+ strokeDasharray: "3 3",
221
+ stroke: tokens4.colorNeutralStroke2
222
+ };
223
+ var axisProps = {
224
+ axisLine: false,
225
+ tickLine: false,
226
+ tick: { fill: tokens4.colorNeutralForeground4, fontSize: 12 }
227
+ };
228
+ var legendProps = {
229
+ verticalAlign: "top",
230
+ align: "right",
231
+ layout: "horizontal",
232
+ content: CoralLegendContent,
233
+ wrapperStyle: {
234
+ paddingBottom: 28
235
+ }
236
+ };
237
+ var tooltipProps = {
238
+ content: CoralTooltipContent,
239
+ cursor: { fill: tokens4.colorNeutralBackground2Hover, opacity: 0.5 }
240
+ };
241
+
242
+ // src/CoralBarChart.tsx
243
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
52
244
  function CoralBarChart({
53
245
  spec,
54
- colors = CORAL_CHART_PALETTE
246
+ colors = CORAL_CHART_PALETTE,
247
+ actions,
248
+ variant
55
249
  }) {
56
- return /* @__PURE__ */ jsx2(ChartFrame, { title: spec.title, children: /* @__PURE__ */ jsx2(ResponsiveContainer, { children: /* @__PURE__ */ jsxs2(BarChart, { data: spec.data, children: [
57
- /* @__PURE__ */ jsx2(CartesianGrid, { strokeDasharray: "3 3" }),
58
- /* @__PURE__ */ jsx2(XAxis, { dataKey: spec.xKey }),
59
- /* @__PURE__ */ jsx2(YAxis, {}),
60
- /* @__PURE__ */ jsx2(Tooltip, {}),
61
- /* @__PURE__ */ jsx2(Legend, {}),
62
- spec.series.map((s, i) => /* @__PURE__ */ jsx2(
250
+ return /* @__PURE__ */ jsx4(ChartFrame, { title: spec.title, actions, variant, children: /* @__PURE__ */ jsx4(ResponsiveContainer, { children: /* @__PURE__ */ jsxs4(BarChart, { data: spec.data, children: [
251
+ /* @__PURE__ */ jsx4(XAxis, { dataKey: spec.xKey, ...axisProps }),
252
+ /* @__PURE__ */ jsx4(YAxis, { ...axisProps }),
253
+ /* @__PURE__ */ jsx4(Tooltip, { ...tooltipProps }),
254
+ /* @__PURE__ */ jsx4(Legend, { ...legendProps }),
255
+ spec.series.map((s, i) => /* @__PURE__ */ jsx4(
63
256
  Bar,
64
257
  {
65
258
  dataKey: s.dataKey,
66
259
  name: s.label,
67
- fill: pickColor(i, colors)
260
+ fill: pickColor(i, colors),
261
+ radius: [4, 4, 0, 0]
68
262
  },
69
263
  s.dataKey
70
264
  ))
@@ -73,7 +267,6 @@ function CoralBarChart({
73
267
 
74
268
  // src/CoralLineChart.tsx
75
269
  import {
76
- CartesianGrid as CartesianGrid2,
77
270
  Legend as Legend2,
78
271
  Line,
79
272
  LineChart,
@@ -82,18 +275,19 @@ import {
82
275
  XAxis as XAxis2,
83
276
  YAxis as YAxis2
84
277
  } from "recharts";
85
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
278
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
86
279
  function CoralLineChart({
87
280
  spec,
88
- colors = CORAL_CHART_PALETTE
281
+ colors = CORAL_CHART_PALETTE,
282
+ actions,
283
+ variant
89
284
  }) {
90
- return /* @__PURE__ */ jsx3(ChartFrame, { title: spec.title, children: /* @__PURE__ */ jsx3(ResponsiveContainer2, { children: /* @__PURE__ */ jsxs3(LineChart, { data: spec.data, children: [
91
- /* @__PURE__ */ jsx3(CartesianGrid2, { strokeDasharray: "3 3" }),
92
- /* @__PURE__ */ jsx3(XAxis2, { dataKey: spec.xKey }),
93
- /* @__PURE__ */ jsx3(YAxis2, {}),
94
- /* @__PURE__ */ jsx3(Tooltip2, {}),
95
- /* @__PURE__ */ jsx3(Legend2, {}),
96
- spec.series.map((s, i) => /* @__PURE__ */ jsx3(
285
+ return /* @__PURE__ */ jsx5(ChartFrame, { title: spec.title, actions, variant, children: /* @__PURE__ */ jsx5(ResponsiveContainer2, { children: /* @__PURE__ */ jsxs5(LineChart, { data: spec.data, children: [
286
+ /* @__PURE__ */ jsx5(XAxis2, { dataKey: spec.xKey, ...axisProps }),
287
+ /* @__PURE__ */ jsx5(YAxis2, { ...axisProps }),
288
+ /* @__PURE__ */ jsx5(Tooltip2, { ...tooltipProps }),
289
+ /* @__PURE__ */ jsx5(Legend2, { ...legendProps }),
290
+ spec.series.map((s, i) => /* @__PURE__ */ jsx5(
97
291
  Line,
98
292
  {
99
293
  type: "monotone",
@@ -107,16 +301,159 @@ function CoralLineChart({
107
301
  }
108
302
 
109
303
  // src/CoralChart.tsx
110
- import { jsx as jsx4 } from "react/jsx-runtime";
111
- function CoralChart({ spec, colors }) {
304
+ import { useState } from "react";
305
+ import { Button } from "@fluentui/react-components";
306
+ import {
307
+ Dismiss,
308
+ FullScreenMaximize,
309
+ MicrosoftExcel
310
+ } from "@coral-ai/bundleicons";
311
+ import { Overlay } from "@coral-ai/react";
312
+
313
+ // src/csvExport.ts
314
+ function escapeCsvCell(value) {
315
+ if (value === null || value === void 0) return "";
316
+ const str = String(value);
317
+ if (/[",\r\n]/.test(str)) {
318
+ return `"${str.replace(/"/g, '""')}"`;
319
+ }
320
+ return str;
321
+ }
322
+ function chartSpecToCsv(spec) {
323
+ const headers = [spec.xKey, ...spec.series.map((s) => s.label)];
324
+ const rows = spec.data.map(
325
+ (row) => [row[spec.xKey], ...spec.series.map((s) => row[s.dataKey])].map(escapeCsvCell).join(",")
326
+ );
327
+ return [headers.map(escapeCsvCell).join(","), ...rows].join("\r\n");
328
+ }
329
+ function downloadChartCsv(spec) {
330
+ const csv = chartSpecToCsv(spec);
331
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
332
+ const url = URL.createObjectURL(blob);
333
+ const a = document.createElement("a");
334
+ a.href = url;
335
+ a.download = `${(spec.title || "chart").replace(/[^a-z0-9_\- ]+/gi, "_")}.csv`;
336
+ document.body.appendChild(a);
337
+ a.click();
338
+ a.remove();
339
+ URL.revokeObjectURL(url);
340
+ }
341
+
342
+ // src/CoralChart.tsx
343
+ import { Fragment, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
344
+ function ChartBody({ spec, colors, actions, variant }) {
112
345
  switch (spec.kind) {
113
346
  case "line":
114
- return /* @__PURE__ */ jsx4(CoralLineChart, { spec, colors });
347
+ return /* @__PURE__ */ jsx6(
348
+ CoralLineChart,
349
+ {
350
+ spec,
351
+ colors,
352
+ actions,
353
+ variant
354
+ }
355
+ );
115
356
  case "bar":
116
357
  default:
117
- return /* @__PURE__ */ jsx4(CoralBarChart, { spec, colors });
358
+ return /* @__PURE__ */ jsx6(
359
+ CoralBarChart,
360
+ {
361
+ spec,
362
+ colors,
363
+ actions,
364
+ variant
365
+ }
366
+ );
118
367
  }
119
368
  }
369
+ function CoralChart({ spec, colors }) {
370
+ const [open, setOpen] = useState(false);
371
+ const exportCsv = () => downloadChartCsv(spec);
372
+ const inlineActions = /* @__PURE__ */ jsxs6(Fragment, { children: [
373
+ /* @__PURE__ */ jsx6(
374
+ Button,
375
+ {
376
+ appearance: "subtle",
377
+ size: "small",
378
+ icon: /* @__PURE__ */ jsx6(MicrosoftExcel, {}),
379
+ onClick: exportCsv,
380
+ title: "Download as CSV",
381
+ "aria-label": "Download as CSV"
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsx6(
385
+ Button,
386
+ {
387
+ appearance: "subtle",
388
+ size: "small",
389
+ icon: /* @__PURE__ */ jsx6(FullScreenMaximize, {}),
390
+ onClick: () => setOpen(true),
391
+ title: "Expand",
392
+ "aria-label": "Expand"
393
+ }
394
+ )
395
+ ] });
396
+ const overlayActions = /* @__PURE__ */ jsxs6(Fragment, { children: [
397
+ /* @__PURE__ */ jsx6(
398
+ Button,
399
+ {
400
+ appearance: "subtle",
401
+ size: "small",
402
+ icon: /* @__PURE__ */ jsx6(MicrosoftExcel, {}),
403
+ onClick: exportCsv,
404
+ title: "Download as CSV",
405
+ "aria-label": "Download as CSV"
406
+ }
407
+ ),
408
+ /* @__PURE__ */ jsx6(
409
+ Button,
410
+ {
411
+ appearance: "subtle",
412
+ size: "small",
413
+ icon: /* @__PURE__ */ jsx6(Dismiss, {}),
414
+ onClick: () => setOpen(false),
415
+ title: "Close",
416
+ "aria-label": "Close"
417
+ }
418
+ )
419
+ ] });
420
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
421
+ /* @__PURE__ */ jsx6(ChartBody, { spec, colors, actions: inlineActions }),
422
+ /* @__PURE__ */ jsx6(Overlay, { open, onClose: () => setOpen(false), children: /* @__PURE__ */ jsx6(
423
+ "div",
424
+ {
425
+ style: {
426
+ width: "100%",
427
+ height: "100%",
428
+ display: "flex",
429
+ alignItems: "center",
430
+ justifyContent: "center"
431
+ },
432
+ children: /* @__PURE__ */ jsx6(
433
+ "div",
434
+ {
435
+ style: {
436
+ width: "85%",
437
+ height: "85%",
438
+ maxWidth: 1600,
439
+ maxHeight: 900,
440
+ display: "flex"
441
+ },
442
+ children: /* @__PURE__ */ jsx6(
443
+ ChartBody,
444
+ {
445
+ spec,
446
+ colors,
447
+ actions: overlayActions,
448
+ variant: "fill"
449
+ }
450
+ )
451
+ }
452
+ )
453
+ }
454
+ ) })
455
+ ] });
456
+ }
120
457
  export {
121
458
  CORAL_CHART_PALETTE,
122
459
  ChartFrame,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/palette.ts","../src/ChartFrame.tsx","../src/CoralBarChart.tsx","../src/CoralLineChart.tsx","../src/CoralChart.tsx"],"sourcesContent":["/**\n * Default color palette used by built-in chart components. Designed to read\n * cleanly against the Coral neutral surfaces in both light and dark themes.\n * Override by passing `colors` to any chart, or by editing this list to align\n * a project to a different brand palette.\n */\nexport const CORAL_CHART_PALETTE = [\n \"#7287ff\",\n \"#34c6a3\",\n \"#f5a524\",\n \"#e85d75\",\n \"#9b6ad8\",\n \"#4ab8d9\",\n];\n\nexport function pickColor(index: number, palette: readonly string[] = CORAL_CHART_PALETTE): string {\n return palette[index % palette.length];\n}\n","// Shared visual frame around any chart kind: titled bordered card with a\n// fixed-height body that hosts the recharts ResponsiveContainer.\n\nimport { ReactNode } from \"react\";\n\nconst frameStyle: React.CSSProperties = {\n margin: \"12px 0\",\n padding: 12,\n border: \"1px solid var(--colorNeutralStroke2, #e0e0e0)\",\n borderRadius: 8,\n};\n\nconst titleStyle: React.CSSProperties = {\n fontWeight: 600,\n marginBottom: 8,\n};\n\nconst bodyStyle: React.CSSProperties = {\n width: \"100%\",\n height: 240,\n};\n\nexport function ChartFrame({\n title,\n children,\n}: {\n title: string;\n children: ReactNode;\n}) {\n return (\n <div style={frameStyle}>\n <div style={titleStyle}>{title}</div>\n <div style={bodyStyle}>{children}</div>\n </div>\n );\n}\n","// Coral-styled bar chart. Wraps recharts so consumers don't import recharts\n// directly — keeps the underlying library swappable and the public API tiny.\n\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Legend,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { ChartFrame } from \"./ChartFrame\";\nimport { CORAL_CHART_PALETTE, pickColor } from \"./palette\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralBarChartProps = {\n spec: CoralChartSpec;\n /** Override the default palette. */\n colors?: readonly string[];\n};\n\nexport function CoralBarChart({\n spec,\n colors = CORAL_CHART_PALETTE,\n}: CoralBarChartProps) {\n return (\n <ChartFrame title={spec.title}>\n <ResponsiveContainer>\n <BarChart data={spec.data}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey={spec.xKey} />\n <YAxis />\n <Tooltip />\n <Legend />\n {spec.series.map((s, i) => (\n <Bar\n key={s.dataKey}\n dataKey={s.dataKey}\n name={s.label}\n fill={pickColor(i, colors)}\n />\n ))}\n </BarChart>\n </ResponsiveContainer>\n </ChartFrame>\n );\n}\n","// Coral-styled line chart. Wraps recharts so consumers don't import recharts\n// directly — keeps the underlying library swappable and the public API tiny.\n\nimport {\n CartesianGrid,\n Legend,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { ChartFrame } from \"./ChartFrame\";\nimport { CORAL_CHART_PALETTE, pickColor } from \"./palette\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralLineChartProps = {\n spec: CoralChartSpec;\n /** Override the default palette. */\n colors?: readonly string[];\n};\n\nexport function CoralLineChart({\n spec,\n colors = CORAL_CHART_PALETTE,\n}: CoralLineChartProps) {\n return (\n <ChartFrame title={spec.title}>\n <ResponsiveContainer>\n <LineChart data={spec.data}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey={spec.xKey} />\n <YAxis />\n <Tooltip />\n <Legend />\n {spec.series.map((s, i) => (\n <Line\n key={s.dataKey}\n type=\"monotone\"\n dataKey={s.dataKey}\n name={s.label}\n stroke={pickColor(i, colors)}\n />\n ))}\n </LineChart>\n </ResponsiveContainer>\n </ChartFrame>\n );\n}\n","// Dispatcher that renders the right Coral chart for a given spec.kind.\n// Use this when you want a single component that picks bar / line based on\n// what an agent decided to emit; reach for CoralBarChart / CoralLineChart\n// directly when you know the kind statically.\n\nimport { CoralBarChart } from \"./CoralBarChart\";\nimport { CoralLineChart } from \"./CoralLineChart\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralChartProps = {\n spec: CoralChartSpec;\n colors?: readonly string[];\n};\n\nexport function CoralChart({ spec, colors }: CoralChartProps) {\n switch (spec.kind) {\n case \"line\":\n return <CoralLineChart spec={spec} colors={colors} />;\n case \"bar\":\n default:\n return <CoralBarChart spec={spec} colors={colors} />;\n }\n}\n"],"mappings":"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","names":["jsx","jsxs","CartesianGrid","Legend","ResponsiveContainer","Tooltip","XAxis","YAxis","jsx","jsxs","ResponsiveContainer","CartesianGrid","XAxis","YAxis","Tooltip","Legend","jsx"]}
1
+ {"version":3,"sources":["../src/palette.ts","../src/ChartFrame.tsx","../src/CoralBarChart.tsx","../src/chartTheme.ts","../src/CoralLegend.tsx","../src/CoralTooltip.tsx","../src/CoralLineChart.tsx","../src/CoralChart.tsx","../src/csvExport.ts"],"sourcesContent":["/**\n * Default color palette used by built-in chart components. Designed to read\n * cleanly against the Coral neutral surfaces in both light and dark themes.\n * Override by passing `colors` to any chart, or by editing this list to align\n * a project to a different brand palette.\n */\nexport const CORAL_CHART_PALETTE = [\n \"#6f91ff\",\n \"#29e4c8\",\n \"#ffdead\",\n \"#e85d75\",\n \"#9b6ad8\",\n \"#4ab8d9\",\n];\n\nexport function pickColor(index: number, palette: readonly string[] = CORAL_CHART_PALETTE): string {\n return palette[index % palette.length];\n}\n","// Shared visual frame around any chart kind. Two-tier card matching the\n// Coral Figma design: a tinted outer shell wraps a header row (title +\n// optional right-aligned action buttons) and an inset inner panel where\n// the chart itself renders.\n\nimport { tokens } from \"@fluentui/react-components\";\nimport type { CSSProperties, ReactNode } from \"react\";\n\nconst baseFrameStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 4,\n padding: 4,\n borderRadius: tokens.borderRadiusXLarge,\n backgroundColor: tokens.colorNeutralBackground2,\n border: `1px solid ${tokens.colorNeutralStroke2}`,\n};\n\nconst titleRowStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n padding: \"4px 4px 4px 8px\",\n gap: 8,\n};\n\nconst titleStyle: CSSProperties = {\n fontFamily: tokens.fontFamilyBase,\n fontSize: tokens.fontSizeBase300,\n fontWeight: tokens.fontWeightSemibold,\n lineHeight: tokens.lineHeightBase300,\n color: tokens.colorNeutralForeground1,\n};\n\nconst actionsStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 4,\n flexShrink: 0,\n};\n\nconst baseBodyStyle: CSSProperties = {\n width: \"100%\",\n padding: 12,\n boxSizing: \"border-box\",\n borderRadius: tokens.borderRadiusLarge,\n backgroundColor: tokens.colorNeutralBackground3,\n border: `1px solid ${tokens.colorNeutralStroke2}`,\n};\n\nexport function ChartFrame({\n title,\n actions,\n children,\n /**\n * `\"inline\"` (default): fixed-height body for in-conversation rendering.\n * `\"fill\"`: body flexes to fill the parent — used by the overlay so the\n * chart scales to whatever size the overlay container is sized to.\n */\n variant = \"inline\",\n}: {\n title: string;\n actions?: ReactNode;\n children: ReactNode;\n variant?: \"inline\" | \"fill\";\n}) {\n const frameStyle: CSSProperties =\n variant === \"fill\"\n ? { ...baseFrameStyle, width: \"100%\", height: \"100%\" }\n : { ...baseFrameStyle, margin: \"12px 0\" };\n const bodyStyle: CSSProperties =\n variant === \"fill\"\n ? { ...baseBodyStyle, flex: 1, minHeight: 0 }\n : { ...baseBodyStyle, height: 320 };\n\n return (\n <div style={frameStyle}>\n <div style={titleRowStyle}>\n <span style={titleStyle}>{title}</span>\n {actions && <div style={actionsStyle}>{actions}</div>}\n </div>\n <div style={bodyStyle}>{children}</div>\n </div>\n );\n}\n","// Coral-styled bar chart. Wraps recharts so consumers don't import recharts\n// directly — keeps the underlying library swappable and the public API tiny.\n\nimport {\n Bar,\n BarChart,\n Legend,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { ChartFrame } from \"./ChartFrame\";\nimport { axisProps, legendProps, tooltipProps } from \"./chartTheme\";\nimport { CORAL_CHART_PALETTE, pickColor } from \"./palette\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralBarChartProps = {\n spec: CoralChartSpec;\n /** Override the default palette. */\n colors?: readonly string[];\n /** Right-aligned buttons rendered in the frame header (e.g. export / expand). */\n actions?: import(\"react\").ReactNode;\n /** Forwarded to ChartFrame — `\"fill\"` makes the body flex to the parent. */\n variant?: \"inline\" | \"fill\";\n};\n\nexport function CoralBarChart({\n spec,\n colors = CORAL_CHART_PALETTE,\n actions,\n variant,\n}: CoralBarChartProps) {\n return (\n <ChartFrame title={spec.title} actions={actions} variant={variant}>\n <ResponsiveContainer>\n <BarChart data={spec.data}>\n <XAxis dataKey={spec.xKey} {...axisProps} />\n <YAxis {...axisProps} />\n <Tooltip {...tooltipProps} />\n <Legend {...legendProps} />\n {spec.series.map((s, i) => (\n <Bar\n key={s.dataKey}\n dataKey={s.dataKey}\n name={s.label}\n fill={pickColor(i, colors)}\n radius={[4, 4, 0, 0]}\n />\n ))}\n </BarChart>\n </ResponsiveContainer>\n </ChartFrame>\n );\n}\n","// Shared recharts styling — both CoralBarChart and CoralLineChart spread\n// these onto their grid/axis/legend/tooltip so any visual tweak lands in\n// one place.\n//\n// Why props objects instead of wrapper components: recharts traverses its\n// direct children to discover axes, grids, tooltips, etc. Wrapping them in\n// our own component would hide them from that traversal and the chart would\n// silently render without axes.\n\nimport { tokens } from \"@fluentui/react-components\";\nimport type { CSSProperties } from \"react\";\nimport { CoralLegendContent } from \"./CoralLegend\";\nimport { CoralTooltipContent } from \"./CoralTooltip\";\n\nexport const gridProps = {\n strokeDasharray: \"3 3\",\n stroke: tokens.colorNeutralStroke2,\n} as const;\n\nexport const axisProps = {\n axisLine: false,\n tickLine: false,\n tick: { fill: tokens.colorNeutralForeground4, fontSize: 12 },\n} as const;\n\nexport const legendProps = {\n verticalAlign: \"top\" as const,\n align: \"right\" as const,\n layout: \"horizontal\" as const,\n content: CoralLegendContent,\n wrapperStyle: {\n paddingBottom: 28,\n } as CSSProperties,\n} as const;\n\nexport const tooltipProps = {\n content: CoralTooltipContent,\n cursor: { fill: tokens.colorNeutralBackground2Hover, opacity: 0.5 },\n} as const;\n","// Custom recharts legend content matching the Coral Figma design:\n// 12x12 rounded color box + Caption1 label, laid out horizontally above\n// the chart body.\n\nimport { tokens } from \"@fluentui/react-components\";\n\ntype LegendPayloadItem = {\n value?: string | number;\n color?: string;\n};\n\n// Recharts' generic legend-content prop doesn't unify cleanly with a typed\n// custom renderer, so accept props as `any` and narrow inside.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function CoralLegendContent(props: any) {\n const { payload } = props as { payload?: readonly LegendPayloadItem[] };\n if (!payload?.length) return null;\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n flexWrap: \"wrap\",\n alignItems: \"center\",\n justifyContent: \"flex-end\",\n gap: 12,\n }}\n >\n {payload.map((entry, i) => (\n <div\n key={`${entry.value}-${i}`}\n style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}\n >\n <span\n style={{\n width: 12,\n height: 12,\n borderRadius: 4,\n backgroundColor: entry.color,\n flexShrink: 0,\n }}\n />\n <span\n style={{\n color: tokens.colorNeutralForeground2,\n fontFamily: tokens.fontFamilyBase,\n fontSize: tokens.fontSizeBase200,\n lineHeight: tokens.lineHeightBase200,\n }}\n >\n {entry.value}\n </span>\n </div>\n ))}\n </div>\n );\n}\n","// Custom recharts tooltip content matching the Coral Figma design:\n// dark rounded panel, title on top, then 20x20 rounded color swatch +\n// \"name: value\" rows stacked vertically.\n\nimport { tokens } from \"@fluentui/react-components\";\n\ntype TooltipPayloadItem = {\n dataKey?: unknown;\n name?: string | number;\n value?: string | number;\n color?: string;\n};\n\n// Recharts' generic tooltip-content prop is strictly typed and the generics\n// don't unify cleanly with a custom renderer, so accept the props as `any`\n// here and re-narrow inside.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function CoralTooltipContent(props: any) {\n const { active, payload, label } = props as {\n active?: boolean;\n payload?: readonly TooltipPayloadItem[];\n label?: string | number;\n };\n if (!active || !payload?.length) return null;\n return (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n padding: 16,\n borderRadius: 12,\n backgroundColor: tokens.colorNeutralBackground2,\n minWidth: 160,\n\n }}\n >\n {label != null && (\n <div\n style={{\n color: tokens.colorNeutralForeground2,\n fontFamily: tokens.fontFamilyBase,\n fontSize: tokens.fontSizeBase200,\n fontWeight: tokens.fontWeightSemibold,\n lineHeight: tokens.lineHeightBase200,\n }}\n >\n {label}\n </div>\n )}\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n {payload.map((entry, i) => (\n <div\n key={`${entry.dataKey}-${i}`}\n style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}\n >\n <span\n style={{\n width: 12,\n height: 12,\n borderRadius: 4,\n backgroundColor: entry.color,\n flexShrink: 0,\n }}\n />\n <span\n style={{\n color: tokens.colorNeutralForeground3,\n fontFamily: tokens.fontFamilyBase,\n fontSize: tokens.fontSizeBase200,\n lineHeight: tokens.lineHeightBase200,\n }}\n >\n {entry.name}: {entry.value}\n </span>\n </div>\n ))}\n </div>\n </div>\n );\n}\n","// Coral-styled line chart. Wraps recharts so consumers don't import recharts\n// directly — keeps the underlying library swappable and the public API tiny.\n\nimport {\n Legend,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { ChartFrame } from \"./ChartFrame\";\nimport { axisProps, legendProps, tooltipProps } from \"./chartTheme\";\nimport { CORAL_CHART_PALETTE, pickColor } from \"./palette\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralLineChartProps = {\n spec: CoralChartSpec;\n /** Override the default palette. */\n colors?: readonly string[];\n /** Right-aligned buttons rendered in the frame header (e.g. export / expand). */\n actions?: import(\"react\").ReactNode;\n /** Forwarded to ChartFrame — `\"fill\"` makes the body flex to the parent. */\n variant?: \"inline\" | \"fill\";\n};\n\nexport function CoralLineChart({\n spec,\n colors = CORAL_CHART_PALETTE,\n actions,\n variant,\n}: CoralLineChartProps) {\n return (\n <ChartFrame title={spec.title} actions={actions} variant={variant}>\n <ResponsiveContainer>\n <LineChart data={spec.data}>\n <XAxis dataKey={spec.xKey} {...axisProps} />\n <YAxis {...axisProps} />\n <Tooltip {...tooltipProps} />\n <Legend {...legendProps} />\n {spec.series.map((s, i) => (\n <Line\n key={s.dataKey}\n type=\"monotone\"\n dataKey={s.dataKey}\n name={s.label}\n stroke={pickColor(i, colors)}\n />\n ))}\n </LineChart>\n </ResponsiveContainer>\n </ChartFrame>\n );\n}\n","// Dispatcher that renders the right Coral chart for a given spec.kind, and\n// owns the two header-row actions every chart gets:\n// - MicrosoftExcel → downloads the spec data as CSV\n// - FullScreenMaximize → opens the chart in a Coral Overlay at 95vw/95vh\n//\n// In the overlay, the expand button is swapped for a Dismiss button so the\n// chart frame looks identical but offers a clear close affordance.\n\nimport { useState } from \"react\";\nimport { Button } from \"@fluentui/react-components\";\nimport {\n Dismiss,\n FullScreenMaximize,\n MicrosoftExcel,\n} from \"@coral-ai/bundleicons\";\nimport { Overlay } from \"@coral-ai/react\";\nimport { CoralBarChart } from \"./CoralBarChart\";\nimport { CoralLineChart } from \"./CoralLineChart\";\nimport { downloadChartCsv } from \"./csvExport\";\nimport type { CoralChartSpec } from \"./types\";\n\nexport type CoralChartProps = {\n spec: CoralChartSpec;\n colors?: readonly string[];\n};\n\ntype ChartBodyProps = {\n spec: CoralChartSpec;\n colors?: readonly string[];\n actions?: import(\"react\").ReactNode;\n variant?: \"inline\" | \"fill\";\n};\n\nfunction ChartBody({ spec, colors, actions, variant }: ChartBodyProps) {\n switch (spec.kind) {\n case \"line\":\n return (\n <CoralLineChart\n spec={spec}\n colors={colors}\n actions={actions}\n variant={variant}\n />\n );\n case \"bar\":\n default:\n return (\n <CoralBarChart\n spec={spec}\n colors={colors}\n actions={actions}\n variant={variant}\n />\n );\n }\n}\n\nexport function CoralChart({ spec, colors }: CoralChartProps) {\n const [open, setOpen] = useState(false);\n\n const exportCsv = () => downloadChartCsv(spec);\n\n const inlineActions = (\n <>\n <Button\n appearance=\"subtle\"\n size=\"small\"\n icon={<MicrosoftExcel />}\n onClick={exportCsv}\n title=\"Download as CSV\"\n aria-label=\"Download as CSV\"\n />\n <Button\n appearance=\"subtle\"\n size=\"small\"\n icon={<FullScreenMaximize />}\n onClick={() => setOpen(true)}\n title=\"Expand\"\n aria-label=\"Expand\"\n />\n </>\n );\n\n const overlayActions = (\n <>\n <Button\n appearance=\"subtle\"\n size=\"small\"\n icon={<MicrosoftExcel />}\n onClick={exportCsv}\n title=\"Download as CSV\"\n aria-label=\"Download as CSV\"\n />\n <Button\n appearance=\"subtle\"\n size=\"small\"\n icon={<Dismiss />}\n onClick={() => setOpen(false)}\n title=\"Close\"\n aria-label=\"Close\"\n />\n </>\n );\n\n return (\n <>\n <ChartBody spec={spec} colors={colors} actions={inlineActions} />\n <Overlay open={open} onClose={() => setOpen(false)}>\n {/* Fills the Overlay backdrop (whatever container that spans, not\n necessarily the viewport — a transform ancestor would scope it\n to the chat panel). Inner box is then 95% of that. */}\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <div\n style={{\n width: \"85%\",\n height: \"85%\",\n maxWidth: 1600,\n maxHeight: 900,\n display: \"flex\",\n }}\n >\n <ChartBody\n spec={spec}\n colors={colors}\n actions={overlayActions}\n variant=\"fill\"\n />\n </div>\n </div>\n </Overlay>\n </>\n );\n}\n","// Serializes a CoralChartSpec's underlying data into a CSV string and\n// triggers a browser download. One column for the x-axis key, then one\n// column per series — header row uses the series `label` (not the raw\n// dataKey) so the spreadsheet reads cleanly.\n\nimport type { CoralChartSpec } from \"./types\";\n\nfunction escapeCsvCell(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n const str = String(value);\n if (/[\",\\r\\n]/.test(str)) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\nexport function chartSpecToCsv(spec: CoralChartSpec): string {\n const headers = [spec.xKey, ...spec.series.map((s) => s.label)];\n const rows = spec.data.map((row) =>\n [row[spec.xKey], ...spec.series.map((s) => row[s.dataKey])]\n .map(escapeCsvCell)\n .join(\",\"),\n );\n return [headers.map(escapeCsvCell).join(\",\"), ...rows].join(\"\\r\\n\");\n}\n\nexport function downloadChartCsv(spec: CoralChartSpec) {\n const csv = chartSpecToCsv(spec);\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `${(spec.title || \"chart\").replace(/[^a-z0-9_\\- ]+/gi, \"_\")}.csv`;\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(url);\n}\n"],"mappings":"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","names":["tokens","tokens","jsx","jsxs","tokens","jsx","jsxs","tokens","jsx","jsxs","Legend","ResponsiveContainer","Tooltip","XAxis","YAxis","jsx","jsxs","ResponsiveContainer","XAxis","YAxis","Tooltip","Legend","jsx","jsxs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coral-ai/charts",
3
- "version": "3.8.1",
3
+ "version": "3.10.1",
4
4
  "private": false,
5
5
  "description": "Chart primitives for Coral. Recharts-backed under the hood (CoralBarChart, CoralLineChart, CoralChart); version tracks the bundled recharts release.",
6
6
  "license": "MIT",
@@ -28,18 +28,24 @@
28
28
  "LICENSE"
29
29
  ],
30
30
  "dependencies": {
31
- "recharts": "3.8.1"
31
+ "recharts": "3.10.1"
32
32
  },
33
33
  "peerDependencies": {
34
+ "@coral-ai/react": ">=0.5.0 <1.0.0",
35
+ "@fluentui/react-components": "^9.0.0",
34
36
  "react": "^19.0.0",
35
- "react-dom": "^19.0.0"
37
+ "react-dom": "^19.0.0",
38
+ "@coral-ai/bundleicons": "^2.0.325"
36
39
  },
37
40
  "devDependencies": {
41
+ "@fluentui/react-components": "^9.0.0",
38
42
  "@types/react": "^19.2.5",
39
43
  "@types/react-dom": "^19.2.3",
40
44
  "react": "^19.0.0",
41
45
  "react-dom": "^19.0.0",
42
- "tsup": "^8.5.1"
46
+ "tsup": "^8.5.1",
47
+ "@coral-ai/bundleicons": "2.0.325",
48
+ "@coral-ai/react": "0.6.0"
43
49
  },
44
50
  "publishConfig": {
45
51
  "access": "public"