@sqlrooms/vega 0.24.21 → 0.24.22

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VegaChartToolResult.d.ts","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAG7C,KAAK,wBAAwB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,iBAAiB,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,QAAQ,EACR,YAAY,GACb,EAAE,wBAAwB,2CAwB1B"}
1
+ {"version":3,"file":"VegaChartToolResult.d.ts","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAG7C,KAAK,wBAAwB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,iBAAiB,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,QAAQ,EACR,YAAY,GACb,EAAE,wBAAwB,2CAgC1B"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { QueryToolResult } from '@sqlrooms/ai';
3
+ import { useSql } from '@sqlrooms/duckdb';
3
4
  import { cn } from '@sqlrooms/ui';
4
- import { Suspense } from 'react';
5
5
  import { VegaLiteChart } from './VegaLiteChart';
6
6
  /**
7
7
  * Renders a chart tool call with visualization using Vega-Lite
@@ -9,6 +9,7 @@ import { VegaLiteChart } from './VegaLiteChart';
9
9
  * @returns {JSX.Element} The rendered chart tool call
10
10
  */
11
11
  export function VegaChartToolResult({ className, sqlQuery, vegaLiteSpec, }) {
12
- return (_jsx(_Fragment, { children: vegaLiteSpec && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(QueryToolResult, { title: "Query", sqlQuery: sqlQuery }), _jsx(Suspense, { fallback: _jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" }) }), children: _jsx(VegaLiteChart, { className: cn('max-w-[600px]', className), aspectRatio: 16 / 9, sqlQuery: sqlQuery, spec: vegaLiteSpec }) })] })) }));
12
+ const result = useSql({ query: sqlQuery });
13
+ return (_jsx(_Fragment, { children: vegaLiteSpec && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(QueryToolResult, { title: "", arrowTable: result.data?.arrowTable, sqlQuery: sqlQuery }), result.error ? (_jsx("div", { className: "whitespace-pre-wrap font-mono text-sm text-red-500", children: result.error?.message })) : result.isLoading ? (_jsxs("div", { className: "text-muted-foreground align-center flex gap-2 px-2", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" }), "Running query for chart data\u2026"] })) : (_jsx(VegaLiteChart.ArrowChart, { className: cn('max-w-[600px]', className), aspectRatio: 16 / 9, arrowTable: result.data?.arrowTable, spec: vegaLiteSpec }))] })) }));
13
14
  }
14
15
  //# sourceMappingURL=VegaChartToolResult.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaChartToolResult.js","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAChC,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAS9C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,SAAS,EACT,QAAQ,EACR,YAAY,GACa;IACzB,OAAO,CACL,4BACG,YAAY,IAAI,CACf,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,eAAe,IAAC,KAAK,EAAC,OAAO,EAAC,QAAQ,EAAE,QAAQ,GAAI,EACrD,KAAC,QAAQ,IACP,QAAQ,EACN,cAAK,SAAS,EAAC,gDAAgD,YAC7D,cAAK,SAAS,EAAC,8EAA8E,GAAO,GAChG,YAGR,KAAC,aAAa,IACZ,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,WAAW,EAAE,EAAE,GAAG,CAAC,EACnB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,YAAY,GAClB,GACO,IACP,CACP,GACA,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import {QueryToolResult} from '@sqlrooms/ai';\nimport {cn} from '@sqlrooms/ui';\nimport {Suspense} from 'react';\nimport {VisualizationSpec} from 'react-vega';\nimport {VegaLiteChart} from './VegaLiteChart';\n\ntype VegaChartToolResultProps = {\n className?: string;\n reasoning: string;\n sqlQuery: string;\n vegaLiteSpec: VisualizationSpec;\n};\n\n/**\n * Renders a chart tool call with visualization using Vega-Lite\n * @param {VegaChartToolResultProps} props - The component props\n * @returns {JSX.Element} The rendered chart tool call\n */\nexport function VegaChartToolResult({\n className,\n sqlQuery,\n vegaLiteSpec,\n}: VegaChartToolResultProps) {\n return (\n <>\n {vegaLiteSpec && (\n <div className=\"flex flex-col gap-2\">\n <QueryToolResult title=\"Query\" sqlQuery={sqlQuery} />\n <Suspense\n fallback={\n <div className=\"flex h-full w-full items-center justify-center\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600\"></div>\n </div>\n }\n >\n <VegaLiteChart\n className={cn('max-w-[600px]', className)}\n aspectRatio={16 / 9}\n sqlQuery={sqlQuery}\n spec={vegaLiteSpec}\n />\n </Suspense>\n </div>\n )}\n </>\n );\n}\n"]}
1
+ {"version":3,"file":"VegaChartToolResult.js","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAGhC,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAS9C;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,SAAS,EACT,QAAQ,EACR,YAAY,GACa;IACzB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAC,CAAC,CAAC;IACzC,OAAO,CACL,4BACG,YAAY,IAAI,CACf,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,eAAe,IACd,KAAK,EAAC,EAAE,EACR,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EACnC,QAAQ,EAAE,QAAQ,GAClB,EACD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CACd,cAAK,SAAS,EAAC,oDAAoD,YAChE,MAAM,CAAC,KAAK,EAAE,OAAO,GAClB,CACP,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CACrB,eAAK,SAAS,EAAC,oDAAoD,aACjE,cAAK,SAAS,EAAC,8EAA8E,GAAO,0CAEhG,CACP,CAAC,CAAC,CAAC,CACF,KAAC,aAAa,CAAC,UAAU,IACvB,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,WAAW,EAAE,EAAE,GAAG,CAAC,EACnB,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EACnC,IAAI,EAAE,YAAY,GAClB,CACH,IACG,CACP,GACA,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import {QueryToolResult} from '@sqlrooms/ai';\nimport {useSql} from '@sqlrooms/duckdb';\nimport {cn} from '@sqlrooms/ui';\nimport {Suspense} from 'react';\nimport {VisualizationSpec} from 'react-vega';\nimport {VegaLiteChart} from './VegaLiteChart';\n\ntype VegaChartToolResultProps = {\n className?: string;\n reasoning: string;\n sqlQuery: string;\n vegaLiteSpec: VisualizationSpec;\n};\n\n/**\n * Renders a chart tool call with visualization using Vega-Lite\n * @param {VegaChartToolResultProps} props - The component props\n * @returns {JSX.Element} The rendered chart tool call\n */\nexport function VegaChartToolResult({\n className,\n sqlQuery,\n vegaLiteSpec,\n}: VegaChartToolResultProps) {\n const result = useSql({query: sqlQuery});\n return (\n <>\n {vegaLiteSpec && (\n <div className=\"flex flex-col gap-2\">\n <QueryToolResult\n title=\"\"\n arrowTable={result.data?.arrowTable}\n sqlQuery={sqlQuery}\n />\n {result.error ? (\n <div className=\"whitespace-pre-wrap font-mono text-sm text-red-500\">\n {result.error?.message}\n </div>\n ) : result.isLoading ? (\n <div className=\"text-muted-foreground align-center flex gap-2 px-2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600\"></div>\n Running query for chart data…\n </div>\n ) : (\n <VegaLiteChart.ArrowChart\n className={cn('max-w-[600px]', className)}\n aspectRatio={16 / 9}\n arrowTable={result.data?.arrowTable}\n spec={vegaLiteSpec}\n />\n )}\n </div>\n )}\n </>\n );\n}\n"]}
@@ -1,60 +1,40 @@
1
+ import * as arrow from 'apache-arrow';
1
2
  import { VisualizationSpec } from 'react-vega';
2
- /**
3
- * A component that renders a Vega-Lite chart with SQL data and responsive sizing.
4
- *
5
- * The chart can be sized in multiple ways:
6
- * - Fixed dimensions: Provide both width and height as numbers
7
- * - Fixed width, proportional height: Provide width as number, height as 'auto'
8
- * - Fixed height, proportional width: Provide height as number, width as 'auto'
9
- * - Fully responsive: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio
10
- *
11
- * @param props - The component props
12
- * @param {number | 'auto'} [props.width='auto'] - The chart width in pixels, or 'auto' to use container width
13
- * @param {number | 'auto'} [props.height='auto'] - The chart height in pixels, or 'auto' to calculate from aspect ratio
14
- * @param {number} [props.aspectRatio=3/2] - The desired width-to-height ratio when dimensions are auto-calculated
15
- * @param {string} props.sqlQuery - The SQL query to fetch data for the chart
16
- * @param {string | VisualizationSpec} props.spec - The Vega-Lite specification for the chart.
17
- * Can be either a JSON string or a VisualizationSpec object.
18
- * The data and size properties will be overridden by the component.
19
- *
20
- * @returns The rendered chart component
21
- *
22
- * @example
23
- * // Fixed size chart
24
- * <VegaLiteChart
25
- * width={600}
26
- * height={400}
27
- * sqlQuery="SELECT category, count(*) as count FROM sales GROUP BY category"
28
- * spec={{
29
- * mark: 'bar',
30
- * encoding: {
31
- * x: {field: 'category', type: 'nominal'},
32
- * y: {field: 'count', type: 'quantitative'}
33
- * }
34
- * }}
35
- * />
36
- *
37
- * @example
38
- * // Responsive chart with 16:9 aspect ratio
39
- * <VegaLiteChart
40
- * className="max-w-[600px]"
41
- * aspectRatio={16/9}
42
- * sqlQuery="SELECT date, value FROM metrics"
43
- * spec={{
44
- * mark: 'line',
45
- * encoding: {
46
- * x: {field: 'date', type: 'temporal'},
47
- * y: {field: 'value', type: 'quantitative'}
48
- * }
49
- * }}
50
- * />
51
- */
52
- export declare const VegaLiteChart: React.FC<{
3
+ export declare const ArrowChart: React.FC<{
53
4
  className?: string;
54
5
  width?: number | 'auto';
55
6
  height?: number | 'auto';
56
7
  aspectRatio?: number;
57
- sqlQuery: string;
58
8
  spec: string | VisualizationSpec;
9
+ arrowTable: arrow.Table | undefined;
10
+ dataName?: string;
59
11
  }>;
12
+ export declare const VegaLiteChart: import("react").FC<{
13
+ className?: string;
14
+ width?: number | "auto";
15
+ height?: number | "auto";
16
+ aspectRatio?: number;
17
+ sqlQuery: string;
18
+ spec: string | VisualizationSpec;
19
+ dataName?: string;
20
+ }> & {
21
+ SqlChart: import("react").FC<{
22
+ className?: string;
23
+ width?: number | "auto";
24
+ height?: number | "auto";
25
+ aspectRatio?: number;
26
+ sqlQuery: string;
27
+ spec: string | VisualizationSpec;
28
+ dataName?: string;
29
+ }>;
30
+ ArrowChart: import("react").FC<{
31
+ className?: string;
32
+ width?: number | "auto";
33
+ height?: number | "auto";
34
+ aspectRatio?: number;
35
+ spec: string | VisualizationSpec;
36
+ arrowTable: arrow.Table | undefined;
37
+ dataName?: string;
38
+ }>;
39
+ };
60
40
  //# sourceMappingURL=VegaLiteChart.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaLiteChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAW,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAIvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;CAClC,CAuDA,CAAC"}
1
+ {"version":3,"file":"VegaLiteChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAW,iBAAiB,EAAC,MAAM,YAAY,CAAC;AA2HvD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACjC,UAAU,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAwEA,CAAC;AAEF,eAAO,MAAM,aAAa;gBAtJZ,MAAM;YACV,MAAM,GAAG,MAAM;aACd,MAAM,GAAG,MAAM;kBACV,MAAM;cACV,MAAM;UACV,MAAM,GAAG,iBAAiB;eACrB,MAAM;;;oBANL,MAAM;gBACV,MAAM,GAAG,MAAM;iBACd,MAAM,GAAG,MAAM;sBACV,MAAM;kBACV,MAAM;cACV,MAAM,GAAG,iBAAiB;mBACrB,MAAM;;;oBA+DL,MAAM;gBACV,MAAM,GAAG,MAAM;iBACd,MAAM,GAAG,MAAM;sBACV,MAAM;cACd,MAAM,GAAG,iBAAiB;oBACpB,KAAK,CAAC,KAAK,GAAG,SAAS;mBACxB,MAAM;;CA8EjB,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useSql } from '@sqlrooms/duckdb';
3
- import { AspectRatio, cn, useAspectRatioDimensions } from '@sqlrooms/ui';
2
+ import { ToolErrorMessage } from '@sqlrooms/ai';
3
+ import { arrowTableToJson, useSql } from '@sqlrooms/duckdb';
4
+ import { AspectRatio, cn, useAspectRatioDimensions, } from '@sqlrooms/ui';
4
5
  import { safeJsonParse } from '@sqlrooms/utils';
5
- import { useMemo, useRef } from 'react';
6
+ import { useEffect, useMemo, useRef, useState } from 'react';
6
7
  import { VegaLite } from 'react-vega';
7
- const DATA_NAME = 'queryResult';
8
+ const DEFAULT_DATA_NAME = 'queryResult';
8
9
  /**
9
10
  * A component that renders a Vega-Lite chart with SQL data and responsive sizing.
10
11
  *
@@ -55,7 +56,7 @@ const DATA_NAME = 'queryResult';
55
56
  * }}
56
57
  * />
57
58
  */
58
- export const VegaLiteChart = ({ className, width = 'auto', height = 'auto', aspectRatio = 3 / 2, sqlQuery, spec, }) => {
59
+ const VegaLiteSqlChart = ({ className, width = 'auto', height = 'auto', aspectRatio = 3 / 2, sqlQuery, spec, dataName = DEFAULT_DATA_NAME, }) => {
59
60
  const containerRef = useRef(null);
60
61
  const dimensions = useAspectRatioDimensions({
61
62
  containerRef,
@@ -69,7 +70,7 @@ export const VegaLiteChart = ({ className, width = 'auto', height = 'auto', aspe
69
70
  return null;
70
71
  return {
71
72
  ...parsed,
72
- data: { name: DATA_NAME },
73
+ data: { name: dataName },
73
74
  width: dimensions.width,
74
75
  height: dimensions.height,
75
76
  autosize: {
@@ -79,11 +80,50 @@ export const VegaLiteChart = ({ className, width = 'auto', height = 'auto', aspe
79
80
  };
80
81
  }, [spec, dimensions]);
81
82
  const result = useSql({ query: sqlQuery });
83
+ const arrowTable = result.data?.arrowTable;
84
+ return (_jsxs("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-2 overflow-hidden', className), children: [result.error && (_jsx("div", { className: "whitespace-pre-wrap font-mono text-sm text-red-500", children: result.error.message })), result.isLoading ? (_jsxs("div", { className: "text-muted-foreground align-center flex gap-2 px-2", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" }), "Running query for chart data\u2026"] })) : (refinedSpec &&
85
+ arrowTable && _jsx(ArrowChart, { spec: refinedSpec, arrowTable: arrowTable }))] }));
86
+ };
87
+ export const ArrowChart = ({ className, width = 'auto', height = 'auto', aspectRatio = 3 / 2, spec, arrowTable, dataName = DEFAULT_DATA_NAME, }) => {
88
+ const containerRef = useRef(null);
89
+ const [chartError, setChartError] = useState(null);
90
+ const dimensions = useAspectRatioDimensions({
91
+ containerRef,
92
+ width,
93
+ height,
94
+ aspectRatio,
95
+ });
96
+ const refinedSpec = useMemo(() => {
97
+ const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;
98
+ if (!parsed) {
99
+ setChartError(new Error('Invalid Vega-Lite specification'));
100
+ return null;
101
+ }
102
+ return {
103
+ ...parsed,
104
+ data: { name: dataName },
105
+ width: dimensions.width,
106
+ height: dimensions.height,
107
+ autosize: {
108
+ type: 'fit',
109
+ contains: 'padding',
110
+ },
111
+ };
112
+ }, [spec, dimensions]);
82
113
  const data = useMemo(() => {
83
- if (!result.data)
114
+ if (!arrowTable)
84
115
  return null;
85
- return { [DATA_NAME]: result.data.toArray() };
86
- }, [result.data]);
87
- return (_jsxs("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-2 overflow-hidden', className), children: [result.error && (_jsx("div", { className: "whitespace-pre-wrap font-mono text-sm text-red-500", children: result.error.message })), _jsx(AspectRatio, { ratio: aspectRatio, children: refinedSpec && data && _jsx(VegaLite, { spec: refinedSpec, data: data }) })] }));
116
+ return { queryResult: arrowTableToJson(arrowTable) };
117
+ }, [arrowTable]);
118
+ // Reset chart error whenever spec or data changes
119
+ useEffect(() => {
120
+ setChartError(null);
121
+ }, [spec, data]);
122
+ return (_jsx("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-2 overflow-hidden', className), children: chartError ? (_jsx(ToolErrorMessage, { error: chartError, triggerLabel: "Chart rendering failed", title: "Chart error", align: "start", details: spec })) : (refinedSpec &&
123
+ data && (_jsx(AspectRatio, { ratio: aspectRatio, children: _jsx(VegaLite, { spec: refinedSpec, data: data, onError: setChartError }) }))) }));
88
124
  };
125
+ export const VegaLiteChart = Object.assign(VegaLiteSqlChart, {
126
+ SqlChart: VegaLiteSqlChart,
127
+ ArrowChart: ArrowChart,
128
+ });
89
129
  //# sourceMappingURL=VegaLiteChart.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaLiteChart.js","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAC,WAAW,EAAE,EAAE,EAAE,wBAAwB,EAAC,MAAM,cAAc,CAAC;AACvE,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,OAAO,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AACtC,OAAO,EAAC,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAEvD,MAAM,SAAS,GAAG,aAAa,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,CAAC,MAAM,aAAa,GAOrB,CAAC,EACJ,SAAS,EACT,KAAK,GAAG,MAAM,EACd,MAAM,GAAG,MAAM,EACf,WAAW,GAAG,CAAC,GAAG,CAAC,EACnB,QAAQ,EACR,IAAI,GACL,EAAE,EAAE;IACH,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,wBAAwB,CAAC;QAC1C,YAAY;QACZ,KAAK;QACL,MAAM;QACN,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrE,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,OAAO;YACL,GAAG,MAAM;YACT,IAAI,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC;YACvB,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE;gBACR,IAAI,EAAE,KAAK;gBACX,QAAQ,EAAE,SAAS;aACpB;SACmB,CAAC;IACzB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAEvB,MAAM,MAAM,GAAG,MAAM,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAC,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;QACxB,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,EAAC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAC,CAAC;IAC9C,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAElB,OAAO,CACL,eACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,EAAE,CACX,mDAAmD,EACnD,SAAS,CACV,aAEA,MAAM,CAAC,KAAK,IAAI,CACf,cAAK,SAAS,EAAC,oDAAoD,YAChE,MAAM,CAAC,KAAK,CAAC,OAAO,GACjB,CACP,EACD,KAAC,WAAW,IAAC,KAAK,EAAE,WAAW,YAC5B,WAAW,IAAI,IAAI,IAAI,KAAC,QAAQ,IAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,GAAI,GACvD,IACV,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {useSql} from '@sqlrooms/duckdb';\nimport {AspectRatio, cn, useAspectRatioDimensions} from '@sqlrooms/ui';\nimport {safeJsonParse} from '@sqlrooms/utils';\nimport {useMemo, useRef} from 'react';\nimport {VegaLite, VisualizationSpec} from 'react-vega';\n\nconst DATA_NAME = 'queryResult';\n\n/**\n * A component that renders a Vega-Lite chart with SQL data and responsive sizing.\n *\n * The chart can be sized in multiple ways:\n * - Fixed dimensions: Provide both width and height as numbers\n * - Fixed width, proportional height: Provide width as number, height as 'auto'\n * - Fixed height, proportional width: Provide height as number, width as 'auto'\n * - Fully responsive: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio\n *\n * @param props - The component props\n * @param {number | 'auto'} [props.width='auto'] - The chart width in pixels, or 'auto' to use container width\n * @param {number | 'auto'} [props.height='auto'] - The chart height in pixels, or 'auto' to calculate from aspect ratio\n * @param {number} [props.aspectRatio=3/2] - The desired width-to-height ratio when dimensions are auto-calculated\n * @param {string} props.sqlQuery - The SQL query to fetch data for the chart\n * @param {string | VisualizationSpec} props.spec - The Vega-Lite specification for the chart.\n * Can be either a JSON string or a VisualizationSpec object.\n * The data and size properties will be overridden by the component.\n *\n * @returns The rendered chart component\n *\n * @example\n * // Fixed size chart\n * <VegaLiteChart\n * width={600}\n * height={400}\n * sqlQuery=\"SELECT category, count(*) as count FROM sales GROUP BY category\"\n * spec={{\n * mark: 'bar',\n * encoding: {\n * x: {field: 'category', type: 'nominal'},\n * y: {field: 'count', type: 'quantitative'}\n * }\n * }}\n * />\n *\n * @example\n * // Responsive chart with 16:9 aspect ratio\n * <VegaLiteChart\n * className=\"max-w-[600px]\"\n * aspectRatio={16/9}\n * sqlQuery=\"SELECT date, value FROM metrics\"\n * spec={{\n * mark: 'line',\n * encoding: {\n * x: {field: 'date', type: 'temporal'},\n * y: {field: 'value', type: 'quantitative'}\n * }\n * }}\n * />\n */\nexport const VegaLiteChart: React.FC<{\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n sqlQuery: string;\n spec: string | VisualizationSpec;\n}> = ({\n className,\n width = 'auto',\n height = 'auto',\n aspectRatio = 3 / 2,\n sqlQuery,\n spec,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n\n const refinedSpec = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) return null;\n return {\n ...parsed,\n data: {name: DATA_NAME},\n width: dimensions.width,\n height: dimensions.height,\n autosize: {\n type: 'fit',\n contains: 'padding',\n },\n } as VisualizationSpec;\n }, [spec, dimensions]);\n\n const result = useSql({query: sqlQuery});\n const data = useMemo(() => {\n if (!result.data) return null;\n return {[DATA_NAME]: result.data.toArray()};\n }, [result.data]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'flex h-full w-full flex-col gap-2 overflow-hidden',\n className,\n )}\n >\n {result.error && (\n <div className=\"whitespace-pre-wrap font-mono text-sm text-red-500\">\n {result.error.message}\n </div>\n )}\n <AspectRatio ratio={aspectRatio}>\n {refinedSpec && data && <VegaLite spec={refinedSpec} data={data} />}\n </AspectRatio>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"VegaLiteChart.js","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai';\nimport {arrowTableToJson, useSql} from '@sqlrooms/duckdb';\nimport {\n AspectRatio,\n Button,\n cn,\n Popover,\n PopoverContent,\n PopoverTrigger,\n useAspectRatioDimensions,\n} from '@sqlrooms/ui';\nimport {safeJsonParse} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {TriangleAlertIcon} from 'lucide-react';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {VegaLite, VisualizationSpec} from 'react-vega';\n\nconst DEFAULT_DATA_NAME = 'queryResult';\n\n/**\n * A component that renders a Vega-Lite chart with SQL data and responsive sizing.\n *\n * The chart can be sized in multiple ways:\n * - Fixed dimensions: Provide both width and height as numbers\n * - Fixed width, proportional height: Provide width as number, height as 'auto'\n * - Fixed height, proportional width: Provide height as number, width as 'auto'\n * - Fully responsive: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio\n *\n * @param props - The component props\n * @param {number | 'auto'} [props.width='auto'] - The chart width in pixels, or 'auto' to use container width\n * @param {number | 'auto'} [props.height='auto'] - The chart height in pixels, or 'auto' to calculate from aspect ratio\n * @param {number} [props.aspectRatio=3/2] - The desired width-to-height ratio when dimensions are auto-calculated\n * @param {string} props.sqlQuery - The SQL query to fetch data for the chart\n * @param {string | VisualizationSpec} props.spec - The Vega-Lite specification for the chart.\n * Can be either a JSON string or a VisualizationSpec object.\n * The data and size properties will be overridden by the component.\n *\n * @returns The rendered chart component\n *\n * @example\n * // Fixed size chart\n * <VegaLiteChart\n * width={600}\n * height={400}\n * sqlQuery=\"SELECT category, count(*) as count FROM sales GROUP BY category\"\n * spec={{\n * mark: 'bar',\n * encoding: {\n * x: {field: 'category', type: 'nominal'},\n * y: {field: 'count', type: 'quantitative'}\n * }\n * }}\n * />\n *\n * @example\n * // Responsive chart with 16:9 aspect ratio\n * <VegaLiteChart\n * className=\"max-w-[600px]\"\n * aspectRatio={16/9}\n * sqlQuery=\"SELECT date, value FROM metrics\"\n * spec={{\n * mark: 'line',\n * encoding: {\n * x: {field: 'date', type: 'temporal'},\n * y: {field: 'value', type: 'quantitative'}\n * }\n * }}\n * />\n */\nconst VegaLiteSqlChart: React.FC<{\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n sqlQuery: string;\n spec: string | VisualizationSpec;\n dataName?: string;\n}> = ({\n className,\n width = 'auto',\n height = 'auto',\n aspectRatio = 3 / 2,\n sqlQuery,\n spec,\n dataName = DEFAULT_DATA_NAME,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n\n const refinedSpec = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) return null;\n return {\n ...parsed,\n data: {name: dataName},\n width: dimensions.width,\n height: dimensions.height,\n autosize: {\n type: 'fit',\n contains: 'padding',\n },\n } as VisualizationSpec;\n }, [spec, dimensions]);\n\n const result = useSql({query: sqlQuery});\n const arrowTable = result.data?.arrowTable;\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'flex h-full w-full flex-col gap-2 overflow-hidden',\n className,\n )}\n >\n {result.error && (\n <div className=\"whitespace-pre-wrap font-mono text-sm text-red-500\">\n {result.error.message}\n </div>\n )}\n {result.isLoading ? (\n <div className=\"text-muted-foreground align-center flex gap-2 px-2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600\"></div>\n Running query for chart data…\n </div>\n ) : (\n refinedSpec &&\n arrowTable && <ArrowChart spec={refinedSpec} arrowTable={arrowTable} />\n )}\n </div>\n );\n};\n\nexport const ArrowChart: React.FC<{\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n spec: string | VisualizationSpec;\n arrowTable: arrow.Table | undefined;\n dataName?: string;\n}> = ({\n className,\n width = 'auto',\n height = 'auto',\n aspectRatio = 3 / 2,\n spec,\n arrowTable,\n dataName = DEFAULT_DATA_NAME,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [chartError, setChartError] = useState<Error | null>(null);\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n\n const refinedSpec = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) {\n setChartError(new Error('Invalid Vega-Lite specification'));\n return null;\n }\n return {\n ...parsed,\n data: {name: dataName},\n width: dimensions.width,\n height: dimensions.height,\n autosize: {\n type: 'fit',\n contains: 'padding',\n },\n } as VisualizationSpec;\n }, [spec, dimensions]);\n\n const data = useMemo(() => {\n if (!arrowTable) return null;\n return {queryResult: arrowTableToJson(arrowTable)};\n }, [arrowTable]);\n\n // Reset chart error whenever spec or data changes\n useEffect(() => {\n setChartError(null);\n }, [spec, data]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'flex h-full w-full flex-col gap-2 overflow-hidden',\n className,\n )}\n >\n {chartError ? (\n <ToolErrorMessage\n error={chartError}\n triggerLabel=\"Chart rendering failed\"\n title=\"Chart error\"\n align=\"start\"\n details={spec}\n />\n ) : (\n refinedSpec &&\n data && (\n <AspectRatio ratio={aspectRatio}>\n <VegaLite spec={refinedSpec} data={data} onError={setChartError} />\n </AspectRatio>\n )\n )}\n </div>\n );\n};\n\nexport const VegaLiteChart = Object.assign(VegaLiteSqlChart, {\n SqlChart: VegaLiteSqlChart,\n ArrowChart: ArrowChart,\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/vega",
3
- "version": "0.24.21",
3
+ "version": "0.24.22",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/index.js",
@@ -19,10 +19,10 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@openassistant/utils": "^0.5.13",
22
- "@sqlrooms/ai": "0.24.21",
23
- "@sqlrooms/duckdb": "0.24.21",
24
- "@sqlrooms/ui": "0.24.21",
25
- "@sqlrooms/utils": "0.24.21",
22
+ "@sqlrooms/ai": "0.24.22",
23
+ "@sqlrooms/duckdb": "0.24.22",
24
+ "@sqlrooms/ui": "0.24.22",
25
+ "@sqlrooms/utils": "0.24.22",
26
26
  "react-vega": "^7.6.0",
27
27
  "vega": "^5.33.0",
28
28
  "vega-lite": "^5.23.0",
@@ -39,5 +39,5 @@
39
39
  "typecheck": "tsc --noEmit",
40
40
  "typedoc": "typedoc"
41
41
  },
42
- "gitHead": "f9ab9f80a5362213c7cb257496c50e305ebd6a81"
42
+ "gitHead": "bc633cde7574270b679cb6c3d9ce9b496a634133"
43
43
  }