@object-ui/plugin-charts 0.3.0 → 0.5.0

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,6 +1,6 @@
1
1
  import { ChartConfig } from './ChartContainerImpl';
2
2
  export interface AdvancedChartImplProps {
3
- chartType?: 'bar' | 'line' | 'area' | 'pie';
3
+ chartType?: 'bar' | 'line' | 'area' | 'pie' | 'donut' | 'radar' | 'scatter';
4
4
  data?: Array<Record<string, any>>;
5
5
  config?: ChartConfig;
6
6
  xAxisKey?: string;
@@ -1,4 +1,11 @@
1
1
  import { ResponsiveContainer, Tooltip, Legend } from 'recharts';
2
+ /**
3
+ * ObjectUI
4
+ * Copyright (c) 2024-present ObjectStack Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
2
9
  import * as React from "react";
3
10
  declare const THEMES: {
4
11
  readonly light: "";
@@ -1,3 +1,10 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
1
8
  export interface ChartImplProps {
2
9
  data?: Array<Record<string, any>>;
3
10
  dataKey?: string;
@@ -0,0 +1,35 @@
1
+ import { default as React } from 'react';
2
+ export interface ChartBarRendererProps {
3
+ schema: {
4
+ type: string;
5
+ id?: string;
6
+ className?: string;
7
+ data?: Array<Record<string, any>>;
8
+ dataKey?: string;
9
+ xAxisKey?: string;
10
+ height?: number;
11
+ color?: string;
12
+ };
13
+ }
14
+ /**
15
+ * ChartBarRenderer - The public API for the bar chart component
16
+ */
17
+ export declare const ChartBarRenderer: React.FC<ChartBarRendererProps>;
18
+ export interface ChartRendererProps {
19
+ schema: {
20
+ type: string;
21
+ id?: string;
22
+ className?: string;
23
+ chartType?: 'bar' | 'line' | 'area';
24
+ data?: Array<Record<string, any>>;
25
+ config?: Record<string, any>;
26
+ xAxisKey?: string;
27
+ series?: Array<{
28
+ dataKey: string;
29
+ }>;
30
+ };
31
+ }
32
+ /**
33
+ * ChartRenderer - The public API for the advanced chart component
34
+ */
35
+ export declare const ChartRenderer: React.FC<ChartRendererProps>;
@@ -0,0 +1 @@
1
+ export declare const ObjectChart: (props: any) => import("react/jsx-runtime").JSX.Element;
@@ -1,42 +1,8 @@
1
- import { default as React } from 'react';
1
+ import { ChartBarRenderer, ChartRenderer } from './ChartRenderer';
2
2
  export type { BarChartSchema } from './types';
3
- export interface ChartBarRendererProps {
4
- schema: {
5
- type: string;
6
- id?: string;
7
- className?: string;
8
- data?: Array<Record<string, any>>;
9
- dataKey?: string;
10
- xAxisKey?: string;
11
- height?: number;
12
- color?: string;
13
- };
14
- }
15
- /**
16
- * ChartBarRenderer - The public API for the bar chart component
17
- * This wrapper handles lazy loading internally using React.Suspense
18
- */
19
- export declare const ChartBarRenderer: React.FC<ChartBarRendererProps>;
20
- export interface ChartRendererProps {
21
- schema: {
22
- type: string;
23
- id?: string;
24
- className?: string;
25
- chartType?: 'bar' | 'line' | 'area';
26
- data?: Array<Record<string, any>>;
27
- config?: Record<string, any>;
28
- xAxisKey?: string;
29
- series?: Array<{
30
- dataKey: string;
31
- }>;
32
- };
33
- }
34
- /**
35
- * ChartRenderer - The public API for the advanced chart component
36
- * Supports multiple chart types (bar, line, area) with full configuration
37
- */
38
- export declare const ChartRenderer: React.FC<ChartRendererProps>;
3
+ export { ChartBarRenderer, ChartRenderer };
4
+ export { ObjectChart } from './ObjectChart';
39
5
  export declare const chartComponents: {
40
- 'chart-bar': React.FC<ChartBarRendererProps>;
41
- chart: React.FC<ChartRendererProps>;
6
+ 'bar-chart': import('react').FC<import('./ChartRenderer').ChartBarRendererProps>;
7
+ chart: import('react').FC<import('./ChartRenderer').ChartRendererProps>;
42
8
  };
@@ -1,4 +1,4 @@
1
- import { BaseSchema } from '@object-ui/types';
1
+ import { BaseSchema } from '../../types/src';
2
2
  /**
3
3
  * Bar Chart component schema.
4
4
  * Renders a bar chart using Recharts library.
@@ -8,7 +8,7 @@ import { BaseSchema } from '@object-ui/types';
8
8
  * import type { BarChartSchema } from '@object-ui/plugin-charts';
9
9
  *
10
10
  * const chartSchema: BarChartSchema = {
11
- * type: 'chart-bar',
11
+ * type: 'bar-chart',
12
12
  * data: [
13
13
  * { name: 'Jan', value: 400 },
14
14
  * { name: 'Feb', value: 300 }
@@ -19,7 +19,7 @@ import { BaseSchema } from '@object-ui/types';
19
19
  * ```
20
20
  */
21
21
  export interface BarChartSchema extends BaseSchema {
22
- type: 'chart-bar';
22
+ type: 'bar-chart';
23
23
  /**
24
24
  * Array of data points to display in the chart.
25
25
  */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ /**
10
+ * Example: Using all new chart types
11
+ *
12
+ * This example demonstrates the new Pie, Donut, Radar, and Scatter chart types
13
+ */
14
+
15
+ export const pieChartExample = {
16
+ type: 'pie-chart',
17
+ data: [
18
+ { name: 'Chrome', value: 65 },
19
+ { name: 'Firefox', value: 20 },
20
+ { name: 'Safari', value: 10 },
21
+ { name: 'Edge', value: 5 },
22
+ ],
23
+ xAxisKey: 'name',
24
+ series: [{ dataKey: 'value' }],
25
+ config: {
26
+ Chrome: { label: 'Chrome', color: 'hsl(var(--chart-1))' },
27
+ Firefox: { label: 'Firefox', color: 'hsl(var(--chart-2))' },
28
+ Safari: { label: 'Safari', color: 'hsl(var(--chart-3))' },
29
+ Edge: { label: 'Edge', color: 'hsl(var(--chart-4))' },
30
+ }
31
+ };
32
+
33
+ export const donutChartExample = {
34
+ type: 'donut-chart',
35
+ data: [
36
+ { category: 'Electronics', revenue: 45000 },
37
+ { category: 'Clothing', revenue: 32000 },
38
+ { category: 'Food', revenue: 28000 },
39
+ { category: 'Books', revenue: 15000 },
40
+ ],
41
+ xAxisKey: 'category',
42
+ series: [{ dataKey: 'revenue' }]
43
+ };
44
+
45
+ export const radarChartExample = {
46
+ type: 'radar-chart',
47
+ data: [
48
+ { skill: 'React', score: 90 },
49
+ { skill: 'TypeScript', score: 85 },
50
+ { skill: 'Node.js', score: 80 }
51
+ ],
52
+ xAxisKey: 'skill',
53
+ series: [{ dataKey: 'score' }]
54
+ };
package/package.json CHANGED
@@ -1,8 +1,18 @@
1
1
  {
2
2
  "name": "@object-ui/plugin-charts",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
+ "description": "Chart components plugin for Object UI, powered by Recharts",
7
+ "homepage": "https://www.objectui.org",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/objectstack-ai/objectui.git",
11
+ "directory": "packages/plugin-charts"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/objectstack-ai/objectui/issues"
15
+ },
6
16
  "main": "dist/index.umd.cjs",
7
17
  "module": "dist/index.js",
8
18
  "types": "dist/index.d.ts",
@@ -14,20 +24,20 @@
14
24
  }
15
25
  },
16
26
  "dependencies": {
17
- "recharts": "^2.15.0",
18
- "@object-ui/components": "0.3.0",
19
- "@object-ui/core": "0.3.0",
20
- "@object-ui/react": "0.3.0",
21
- "@object-ui/types": "0.3.0"
27
+ "recharts": "^3.7.0",
28
+ "@object-ui/components": "0.5.0",
29
+ "@object-ui/core": "0.5.0",
30
+ "@object-ui/react": "0.5.0",
31
+ "@object-ui/types": "0.5.0"
22
32
  },
23
33
  "peerDependencies": {
24
34
  "react": "^18.0.0 || ^19.0.0",
25
35
  "react-dom": "^18.0.0 || ^19.0.0"
26
36
  },
27
37
  "devDependencies": {
28
- "@types/react": "^18.3.12",
29
- "@types/react-dom": "^18.3.1",
30
- "@vitejs/plugin-react": "^4.2.1",
38
+ "@types/react": "^19.2.10",
39
+ "@types/react-dom": "^19.2.3",
40
+ "@vitejs/plugin-react": "^5.1.3",
31
41
  "typescript": "^5.9.3",
32
42
  "vite": "^7.3.1",
33
43
  "vite-plugin-dts": "^4.5.4"
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
1
9
  import * as React from "react"
2
10
  import {
3
11
  Bar,
@@ -8,6 +16,14 @@ import {
8
16
  AreaChart,
9
17
  Pie,
10
18
  PieChart,
19
+ Radar,
20
+ RadarChart,
21
+ PolarGrid,
22
+ PolarAngleAxis,
23
+ PolarRadiusAxis,
24
+ Scatter,
25
+ ScatterChart,
26
+ ZAxis,
11
27
  Cell,
12
28
  XAxis,
13
29
  YAxis,
@@ -54,7 +70,7 @@ const TW_COLORS: Record<string, string> = {
54
70
  const resolveColor = (color: string) => TW_COLORS[color] || color;
55
71
 
56
72
  export interface AdvancedChartImplProps {
57
- chartType?: 'bar' | 'line' | 'area' | 'pie';
73
+ chartType?: 'bar' | 'line' | 'area' | 'pie' | 'donut' | 'radar' | 'scatter';
58
74
  data?: Array<Record<string, any>>;
59
75
  config?: ChartConfig;
60
76
  xAxisKey?: string;
@@ -79,11 +95,25 @@ export default function AdvancedChartImpl({
79
95
  line: LineChart,
80
96
  area: AreaChart,
81
97
  pie: PieChart,
98
+ donut: PieChart,
99
+ radar: RadarChart,
100
+ scatter: ScatterChart,
82
101
  }[chartType] || BarChart;
83
102
 
84
103
  console.log('📈 Rendering Chart:', { chartType, dataLength: data.length, config, series, xAxisKey });
85
104
 
86
- if (chartType === 'pie') {
105
+ // Helper function to get color palette
106
+ const getPalette = () => [
107
+ 'hsl(var(--chart-1))',
108
+ 'hsl(var(--chart-2))',
109
+ 'hsl(var(--chart-3))',
110
+ 'hsl(var(--chart-4))',
111
+ 'hsl(var(--chart-5))'
112
+ ];
113
+
114
+ // Pie and Donut charts
115
+ if (chartType === 'pie' || chartType === 'donut') {
116
+ const innerRadius = chartType === 'donut' ? 60 : 0;
87
117
  return (
88
118
  <ChartContainer config={config} className={className}>
89
119
  <PieChart>
@@ -92,7 +122,7 @@ export default function AdvancedChartImpl({
92
122
  data={data}
93
123
  dataKey={series[0]?.dataKey || 'value'}
94
124
  nameKey={xAxisKey || 'name'}
95
- innerRadius={60}
125
+ innerRadius={innerRadius}
96
126
  strokeWidth={5}
97
127
  paddingAngle={2}
98
128
  outerRadius={80}
@@ -101,22 +131,9 @@ export default function AdvancedChartImpl({
101
131
  // 1. Try config by nameKey (category)
102
132
  let c = config[entry[xAxisKey]]?.color;
103
133
 
104
- // 2. Try series config (if only 1 series defined with color list, logic fails here usually)
134
+ // 2. Fallback to palette
105
135
  if (!c) {
106
- // Fallback: If 'colors' array was passed in schema, my adapter put it in config[seriesKey]?
107
- // Actually my adapter logic in index.tsx was: config[seriesKey].color = colors[idx]
108
- // But here we are iterating DATA items, not SERIES.
109
- // So we need a cycling palette.
110
- // Let's assume the user didn't provide per-category config here, so we cycle default colors.
111
- const palette = [
112
- 'hsl(var(--chart-1))',
113
- 'hsl(var(--chart-2))',
114
- 'hsl(var(--chart-3))',
115
- 'hsl(var(--chart-4))',
116
- 'hsl(var(--chart-5))'
117
- ];
118
- // Check if we can get colors from the first series config?
119
- // No, let's just use the palette or resolveColor from entry if provided in data.
136
+ const palette = getPalette();
120
137
  c = palette[index % palette.length];
121
138
  }
122
139
 
@@ -129,6 +146,73 @@ export default function AdvancedChartImpl({
129
146
  );
130
147
  }
131
148
 
149
+ // Radar chart
150
+ if (chartType === 'radar') {
151
+ return (
152
+ <ChartContainer config={config} className={className}>
153
+ <RadarChart data={data}>
154
+ <PolarGrid />
155
+ <PolarAngleAxis dataKey={xAxisKey} />
156
+ <PolarRadiusAxis />
157
+ <ChartTooltip content={<ChartTooltipContent />} />
158
+ <ChartLegend content={<ChartLegendContent />} />
159
+ {series.map((s: any) => {
160
+ const color = resolveColor(config[s.dataKey]?.color || DEFAULT_CHART_COLOR);
161
+ return (
162
+ <Radar
163
+ key={s.dataKey}
164
+ dataKey={s.dataKey}
165
+ stroke={color}
166
+ fill={color}
167
+ fillOpacity={0.6}
168
+ />
169
+ );
170
+ })}
171
+ </RadarChart>
172
+ </ChartContainer>
173
+ );
174
+ }
175
+
176
+ // Scatter chart
177
+ if (chartType === 'scatter') {
178
+ return (
179
+ <ChartContainer config={config} className={className}>
180
+ <ScatterChart>
181
+ <CartesianGrid vertical={false} />
182
+ <XAxis
183
+ type="number"
184
+ dataKey={xAxisKey}
185
+ name={String(config[xAxisKey]?.label || xAxisKey)}
186
+ tickLine={false}
187
+ axisLine={false}
188
+ />
189
+ <YAxis
190
+ type="number"
191
+ dataKey={series[0]?.dataKey || 'value'}
192
+ name={String(config[series[0]?.dataKey]?.label || series[0]?.dataKey)}
193
+ tickLine={false}
194
+ axisLine={false}
195
+ />
196
+ <ZAxis type="number" range={[60, 400]} />
197
+ <ChartTooltip content={<ChartTooltipContent />} />
198
+ <ChartLegend content={<ChartLegendContent />} />
199
+ {series.map((s: any, index: number) => {
200
+ const palette = getPalette();
201
+ const color = resolveColor(config[s.dataKey]?.color || palette[index % palette.length]);
202
+ return (
203
+ <Scatter
204
+ key={s.dataKey}
205
+ name={config[s.dataKey]?.label || s.dataKey}
206
+ data={data}
207
+ fill={color}
208
+ />
209
+ );
210
+ })}
211
+ </ScatterChart>
212
+ </ChartContainer>
213
+ );
214
+ }
215
+
132
216
  return (
133
217
  <ChartContainer config={config} className={className}>
134
218
  <ChartComponent data={data}>
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
1
9
  "use client"
2
10
 
3
11
  import * as React from "react"
package/src/ChartImpl.tsx CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
1
9
  import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
2
10
 
3
11
  export interface ChartImplProps {
@@ -0,0 +1,112 @@
1
+
2
+ import React, { Suspense } from 'react';
3
+ import { Skeleton } from '@object-ui/components';
4
+ import type { ChartConfig } from './ChartContainerImpl';
5
+
6
+ // 🚀 Lazy load the implementation files
7
+ const LazyChart = React.lazy(() => import('./ChartImpl'));
8
+ const LazyAdvancedChart = React.lazy(() => import('./AdvancedChartImpl'));
9
+
10
+ export interface ChartBarRendererProps {
11
+ schema: {
12
+ type: string;
13
+ id?: string;
14
+ className?: string;
15
+ data?: Array<Record<string, any>>;
16
+ dataKey?: string;
17
+ xAxisKey?: string;
18
+ height?: number;
19
+ color?: string;
20
+ };
21
+ }
22
+
23
+ /**
24
+ * ChartBarRenderer - The public API for the bar chart component
25
+ */
26
+ export const ChartBarRenderer: React.FC<ChartBarRendererProps> = ({ schema }) => {
27
+ return (
28
+ <Suspense fallback={<Skeleton className="w-full h-[400px]" />}>
29
+ <LazyChart
30
+ data={schema.data}
31
+ dataKey={schema.dataKey}
32
+ xAxisKey={schema.xAxisKey}
33
+ height={schema.height}
34
+ className={schema.className}
35
+ color={schema.color}
36
+ />
37
+ </Suspense>
38
+ );
39
+ };
40
+
41
+ export interface ChartRendererProps {
42
+ schema: {
43
+ type: string;
44
+ id?: string;
45
+ className?: string;
46
+ chartType?: 'bar' | 'line' | 'area';
47
+ data?: Array<Record<string, any>>;
48
+ config?: Record<string, any>;
49
+ xAxisKey?: string;
50
+ series?: Array<{ dataKey: string }>;
51
+ };
52
+ }
53
+
54
+ /**
55
+ * ChartRenderer - The public API for the advanced chart component
56
+ */
57
+ export const ChartRenderer: React.FC<ChartRendererProps> = ({ schema }) => {
58
+ // ⚡️ Adapter: Normalize JSON schema to Recharts Props
59
+ const props = React.useMemo(() => {
60
+ // 1. Defaults
61
+ let series = schema.series;
62
+ let xAxisKey = schema.xAxisKey;
63
+ let config = schema.config;
64
+
65
+ // 2. Adapt Tremor/Simple format (categories -> series, index -> xAxisKey)
66
+ if (!xAxisKey) {
67
+ if ((schema as any).index) xAxisKey = (schema as any).index;
68
+ else if ((schema as any).category) xAxisKey = (schema as any).category; // Support Pie/Donut category
69
+ }
70
+
71
+ if (!series) {
72
+ if ((schema as any).categories) {
73
+ series = (schema as any).categories.map((cat: string) => ({ dataKey: cat }));
74
+ } else if ((schema as any).value) {
75
+ // Single value adapter (for Pie/Simple charts)
76
+ series = [{ dataKey: (schema as any).value }];
77
+ }
78
+ }
79
+
80
+ // 3. Auto-generate config/colors if missing
81
+ if (!config && series) {
82
+ const colors = (schema as any).colors || ['hsl(var(--chart-1))', 'hsl(var(--chart-2))', 'hsl(var(--chart-3))'];
83
+ const newConfig: ChartConfig = {};
84
+ series.forEach((s: any, idx: number) => {
85
+ newConfig[s.dataKey] = { label: s.dataKey, color: colors[idx % colors.length] };
86
+ });
87
+ config = newConfig;
88
+ }
89
+
90
+ return {
91
+ chartType: schema.chartType,
92
+ data: schema.data,
93
+ config,
94
+ xAxisKey,
95
+ series,
96
+ className: schema.className
97
+ };
98
+ }, [schema]);
99
+
100
+ return (
101
+ <Suspense fallback={<Skeleton className="w-full h-[400px]" />}>
102
+ <LazyAdvancedChart
103
+ chartType={props.chartType}
104
+ data={props.data}
105
+ config={props.config}
106
+ xAxisKey={props.xAxisKey}
107
+ series={props.series}
108
+ className={props.className}
109
+ />
110
+ </Suspense>
111
+ );
112
+ };
@@ -0,0 +1,74 @@
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import { useDataScope, useSchemaContext } from '@object-ui/react';
4
+ import { ChartRenderer } from './ChartRenderer';
5
+ import { ComponentRegistry } from '@object-ui/core';
6
+
7
+ export const ObjectChart = (props: any) => {
8
+ const { schema } = props;
9
+ const context = useSchemaContext();
10
+ const dataSource = props.dataSource || context.dataSource;
11
+ const boundData = useDataScope(schema.bind);
12
+
13
+ const [fetchedData, setFetchedData] = useState<any[]>([]);
14
+ const [loading, setLoading] = useState(false);
15
+
16
+ useEffect(() => {
17
+ let isMounted = true;
18
+ const fetchData = async () => {
19
+ if (!dataSource || !schema.objectName) return;
20
+ if (isMounted) setLoading(true);
21
+ try {
22
+ // Apply filtering?
23
+ const results = await dataSource.find(schema.objectName, {
24
+ $filter: schema.filter
25
+ });
26
+
27
+ let data: any[] = [];
28
+ if (Array.isArray(results)) {
29
+ data = results;
30
+ } else if (results && typeof results === 'object') {
31
+ if (Array.isArray((results as any).value)) {
32
+ data = (results as any).value;
33
+ }
34
+ }
35
+
36
+ if (isMounted) {
37
+ setFetchedData(data);
38
+ }
39
+ } catch (e) {
40
+ console.error('[ObjectChart] Fetch error:', e);
41
+ } finally {
42
+ if (isMounted) setLoading(false);
43
+ }
44
+ };
45
+
46
+ if (schema.objectName && !boundData && !schema.data) {
47
+ fetchData();
48
+ }
49
+ return () => { isMounted = false; };
50
+ }, [schema.objectName, dataSource, boundData, schema.data, schema.filter]);
51
+
52
+ const finalData = boundData || schema.data || fetchedData || [];
53
+
54
+ // Merge data if not provided in schema
55
+ const finalSchema = {
56
+ ...schema,
57
+ data: finalData
58
+ };
59
+
60
+ if (loading && finalData.length === 0) {
61
+ // Return skeleton or loading state?
62
+ // ChartRenderer has suspense/skeleton handling but needs to be triggered.
63
+ // We pass empty data but it might render empty chart.
64
+ }
65
+
66
+ return <ChartRenderer {...props} schema={finalSchema} />;
67
+ };
68
+
69
+ // Register it
70
+ ComponentRegistry.register('object-chart', ObjectChart, {
71
+ namespace: 'plugin-charts',
72
+ label: 'Object Chart',
73
+ category: 'view'
74
+ });