diff --git a/js/packages/react-ui/package.json b/js/packages/react-ui/package.json index 4945f468..2d45b7be 100644 --- a/js/packages/react-ui/package.json +++ b/js/packages/react-ui/package.json @@ -2,7 +2,7 @@ "type": "module", "name": "@crayonai/react-ui", "license": "MIT", - "version": "0.9.14", + "version": "0.9.15", "description": "Component library for Generative UI SDK", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index d1c78b8b..e690de3d 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -5,7 +5,12 @@ import { usePrintContext } from "../../../context/PrintContext"; import { useId } from "../../../polyfills"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; -import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks"; +import { + useExportChartData, + useMaxLabelHeight, + useTransformedKeys, + useYAxisLabelWidth, +} from "../hooks"; import { ActiveDot, cartesianGrid, @@ -229,6 +234,17 @@ const AreaChartComponent = ({ return getLegendItems(dataKeys, colors, icons); }, [dataKeys, colors, icons]); + const exportData = useExportChartData({ + type: "area", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + }); + const id = useId(); const gradientID = useMemo(() => `area-chart-gradient-${id}`, [id]); @@ -304,6 +320,7 @@ const AreaChartComponent = ({ >
({ }, [dataKeys, icons, colors, transformedKeys]); const id = useId(); + + const exportData = useExportChartData({ + type: "area", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + }); const gradientID = useMemo(() => `area-chart-condensed-gradient-${id}`, [id]); const chartMargin = useMemo( @@ -279,6 +291,7 @@ const AreaChartCondensedComponent = ({ >
({ return getLegendItems(dataKeys, colors, icons); }, [dataKeys, colors, icons]); + const exportData = useExportChartData({ + type: "bar", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + }); + const id = useId(); const yAxis = useMemo(() => { @@ -428,6 +444,7 @@ const BarChartComponent = ({ >
({ const id = useId(); + const exportData = useExportChartData({ + type: "bar", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + }); + const chartMargin = useMemo( () => ({ top: 10, @@ -378,6 +390,7 @@ const BarChartCondensedComponent = ({ >
({ return getLegendItems(dataKeys, colors, icons); }, [dataKeys, colors, icons]); + const exportData = useExportChartData({ + type: "bar", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + extraOptions: { + barDir: "bar", + }, + }); + const id = useId(); const xAxis = useMemo(() => { @@ -347,7 +361,10 @@ const HorizontalBarChartComponent = ({ data={sideBarTooltipData} setData={setSideBarTooltipData} > -
+
({ return getLegendItems(dataKeys, colors, icons); }, [dataKeys, colors, icons]); + const exportData = useExportChartData({ + type: "line", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + extraOptions: { + lineSize: strokeWidth, + }, + }); + const id = useId(); const onLineClick = useCallback( @@ -314,6 +333,7 @@ export const LineChart = ({ >
({ const id = useId(); + const exportData = useExportChartData({ + type: "line", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + extraOptions: { + lineSize: strokeWidth, + }, + }); + const chartMargin = useMemo( () => ({ top: 10, @@ -279,6 +294,7 @@ const LineChartCondensedComponent = ({ >
({ dataLength: sortedProcessedData.length, }); + const exportData = useExportChartData({ + type: "pie", + data: sortedProcessedData, + categoryKey: categoryKey as string, + dataKeys: [dataKey as string], + colors, + legend, + }); + // Memoize expensive data transformations and configurations const transformedData = useMemo( () => transformDataWithPercentages(sortedProcessedData as T, dataKey), @@ -445,7 +454,12 @@ const PieChartComponent = ({ }, [width, height]); return ( -
+
diff --git a/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx b/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx index a418534c..6f8579bf 100644 --- a/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx @@ -10,7 +10,7 @@ import { import { usePrintContext } from "../../../context/PrintContext"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarTooltipProvider } from "../context/SideBarTooltipContext"; -import { useTransformedKeys } from "../hooks/useTransformKey"; +import { useExportChartData, useTransformedKeys } from "../hooks"; import { ActiveDot, CustomTooltipContent, DefaultLegend } from "../shared"; import { LegendItem } from "../types"; import { useChartPalette } from "../utils/PalletUtils"; @@ -77,6 +77,15 @@ const RadarChartComponent = ({ return getLegendItems(dataKeys, colors, icons); }, [dataKeys, colors, icons]); + const exportData = useExportChartData({ + type: "radar", + data, + categoryKey: categoryKey as string, + dataKeys, + colors, + legend, + }); + const [isLegendExpanded, setIsLegendExpanded] = useState(false); const wrapperRef = useRef(null); const [wrapperRect, setWrapperRect] = useState({ width: 0, height: 0 }); @@ -195,7 +204,12 @@ const RadarChartComponent = ({ data={undefined} setData={() => {}} > -
+
diff --git a/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx b/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx index c12ec93d..930490e0 100644 --- a/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx +++ b/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Cell, PolarGrid, RadialBar, RadialBarChart, ResponsiveContainer } from "recharts"; import { usePrintContext } from "../../../context/PrintContext"; import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../Charts"; -import { useTransformedKeys } from "../hooks"; +import { useExportChartData, useTransformedKeys } from "../hooks"; import { DefaultLegend } from "../shared/DefaultLegend/DefaultLegend"; import { StackedLegend } from "../shared/StackedLegend/StackedLegend"; import { LegendItem } from "../types/Legend"; @@ -150,6 +150,15 @@ export const RadialChart = ({ dataLength: sortedProcessedData.length, }); + const exportData = useExportChartData({ + type: "pie", + data: sortedProcessedData, + categoryKey: categoryKey as string, + dataKeys: [dataKey as string], + colors, + legend, + }); + // Memoize expensive data transformations and configurations const transformedData = useMemo( () => transformRadialDataWithPercentages(sortedProcessedData as T, dataKey, colors), @@ -332,6 +341,7 @@ export const RadialChart = ({ className={wrapperClassName} style={wrapperStyle} aria-description="radial-chart-wrapper" + data-crayon-chart={exportData} >
diff --git a/js/packages/react-ui/src/components/Charts/ScatterChart/ScatterChart.tsx b/js/packages/react-ui/src/components/Charts/ScatterChart/ScatterChart.tsx index 027b957d..9c78aa8c 100644 --- a/js/packages/react-ui/src/components/Charts/ScatterChart/ScatterChart.tsx +++ b/js/packages/react-ui/src/components/Charts/ScatterChart/ScatterChart.tsx @@ -5,7 +5,7 @@ import { usePrintContext } from "../../../context/PrintContext"; import { useId } from "../../../polyfills"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; -import { useYAxisLabelWidth } from "../hooks"; +import { useExportChartData, useYAxisLabelWidth } from "../hooks"; import { CustomTooltipContent, DefaultLegend, @@ -189,6 +189,21 @@ export const ScatterChart = ({ return getLegendItems(datasets, colors); }, [datasets, colors]); + const exportData = useExportChartData({ + type: "scatter", + data, + colors, + legend, + xAxisLabel, + yAxisLabel, + customDataTransform: () => + data.map((dataset) => ({ + name: dataset.name, + x: dataset.data.map((p) => p[xAxisDataKey] as number), + y: dataset.data.map((p) => p[yAxisDataKey] as number), + })), + }); + const id = useId(); const xAxis = useMemo(() => { @@ -299,6 +314,7 @@ export const ScatterChart = ({ >
({ [segments, colors], ); + const printContext = usePrintContext(); + animated = printContext ? false : animated; + + const exportData = useExportChartData({ + type: "bar", + data, + categoryKey: categoryKey as string, + dataKeys: [dataKey as string], + colors, + legend, + extraOptions: { + barDir: "bar", + barGrouping: "stacked", + }, + }); + // Handle legend item hover with tooltip positioning const handleLegendItemHover = useCallback( (hoverIndex: number | null) => { @@ -142,6 +160,7 @@ export const SingleStackedBar = ({ "crayon-single-stacked-bar-chart-container-gap": legend && legendVariant === "default", })} style={style} + data-crayon-chart={exportData} >
{segments.map((segment, index) => { diff --git a/js/packages/react-ui/src/components/Charts/hooks/index.ts b/js/packages/react-ui/src/components/Charts/hooks/index.ts index ad182fdf..e465f700 100644 --- a/js/packages/react-ui/src/components/Charts/hooks/index.ts +++ b/js/packages/react-ui/src/components/Charts/hooks/index.ts @@ -1,5 +1,6 @@ export * from "./useAutoAngleCalculation"; export * from "./useCanvasContextForLabelSize"; +export * from "./useExportChartData"; export * from "./useMaxLabelHeight"; export * from "./useMaxLabelWidth"; export * from "./useTransformKey"; diff --git a/js/packages/react-ui/src/components/Charts/hooks/useExportChartData.ts b/js/packages/react-ui/src/components/Charts/hooks/useExportChartData.ts new file mode 100644 index 00000000..c86ba39e --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/hooks/useExportChartData.ts @@ -0,0 +1,77 @@ +import { useMemo } from "react"; +import { usePrintContext } from "../../../context/PrintContext"; +import { ExportChartData } from "../Charts"; + +export interface UseExportChartDataProps { + type: ExportChartData["type"]; + data: any[]; + categoryKey?: string; + dataKeys?: string[]; + colors: string[]; + legend?: boolean; + xAxisLabel?: React.ReactNode; + yAxisLabel?: React.ReactNode; + extraOptions?: Omit< + NonNullable, + "chartColors" | "showLegend" | "catAxisTitle" | "valAxisTitle" + >; + // For specialized charts like scatter + customDataTransform?: () => ExportChartData["data"]; +} + +export const useExportChartData = ({ + type, + data, + categoryKey, + dataKeys, + colors, + legend, + xAxisLabel, + yAxisLabel, + extraOptions, + customDataTransform, +}: UseExportChartDataProps): string | undefined => { + const printContext = usePrintContext(); + + return useMemo(() => { + if (!printContext) { + return undefined; + } + + const chartData = customDataTransform + ? customDataTransform() + : (dataKeys || []).map((key) => ({ + name: key, + labels: data.map((item) => (categoryKey ? String(item[categoryKey]) : "")), + values: data.map((item) => Number(item[key])), + })); + + const exportData: ExportChartData = { + type, + data: chartData, + options: { + chartColors: colors, + showLegend: legend, + catAxisTitle: typeof xAxisLabel === "string" ? xAxisLabel : undefined, + showCatAxisTitle: typeof xAxisLabel === "string", + valAxisTitle: typeof yAxisLabel === "string" ? yAxisLabel : undefined, + showValAxisTitle: typeof yAxisLabel === "string", + ...extraOptions, + }, + }; + + return JSON.stringify(exportData); + }, [ + type, + data, + dataKeys, + categoryKey, + colors, + legend, + xAxisLabel, + yAxisLabel, + extraOptions, + customDataTransform, + printContext, + ]); +}; diff --git a/js/packages/react-ui/src/index.ts b/js/packages/react-ui/src/index.ts index c854d5a2..8249cab4 100644 --- a/js/packages/react-ui/src/index.ts +++ b/js/packages/react-ui/src/index.ts @@ -7,6 +7,7 @@ export * from "./components/Card"; export * from "./components/CardHeader"; export * from "./components/Carousel"; export * from "./components/Charts"; +export type { ExportChartData } from "./components/Charts/Charts"; export * from "./components/CheckBoxGroup"; export * from "./components/CheckBoxItem"; export * from "./components/CodeBlock";