Skip to content

Commit 28d99cd

Browse files
committed
Add icon and chart renderers to component library
Introduces new 'icon' and 'chart' renderers to the component registry, enabling sidebar menu items to display icons and supporting interactive chart rendering in the UI. Updates the prototype schema to use these features for improved sidebar and chart display.
1 parent a85fa02 commit 28d99cd

5 files changed

Lines changed: 182 additions & 10 deletions

File tree

examples/prototype/src/App.tsx

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,30 @@ const schema = {
4343
body: {
4444
type: 'sidebar-menu-button',
4545
active: true,
46-
body: { type: 'span', body: { type: 'text', content: 'Dashboard' } }
46+
body: [
47+
{ type: 'icon', name: 'SquareTerminal' },
48+
{ type: 'span', body: { type: 'text', content: 'Dashboard' } }
49+
]
4750
}
4851
},
4952
{
5053
type: 'sidebar-menu-item',
5154
body: {
5255
type: 'sidebar-menu-button',
53-
body: { type: 'span', body: { type: 'text', content: 'Projects' } }
56+
body: [
57+
{ type: 'icon', name: 'Frame' },
58+
{ type: 'span', body: { type: 'text', content: 'Projects' } }
59+
]
5460
}
5561
},
5662
{
5763
type: 'sidebar-menu-item',
5864
body: {
5965
type: 'sidebar-menu-button',
60-
body: { type: 'span', body: { type: 'text', content: 'Tasks' } }
66+
body: [
67+
{ type: 'icon', name: 'Map' },
68+
{ type: 'span', body: { type: 'text', content: 'Tasks' } }
69+
]
6170
}
6271
}
6372
]
@@ -75,14 +84,20 @@ const schema = {
7584
type: 'sidebar-menu-item',
7685
body: {
7786
type: 'sidebar-menu-button',
78-
body: { type: 'span', body: { type: 'text', content: 'Profile' } }
87+
body: [
88+
{ type: 'icon', name: 'User' },
89+
{ type: 'span', body: { type: 'text', content: 'Profile' } }
90+
]
7991
}
8092
},
8193
{
8294
type: 'sidebar-menu-item',
8395
body: {
8496
type: 'sidebar-menu-button',
85-
body: { type: 'span', body: { type: 'text', content: 'Billing' } }
97+
body: [
98+
{ type: 'icon', name: 'CreditCard' },
99+
{ type: 'span', body: { type: 'text', content: 'Billing' } }
100+
]
86101
}
87102
}
88103
]
@@ -94,9 +109,36 @@ const schema = {
94109
{
95110
type: 'sidebar-footer',
96111
body: {
97-
type: 'div',
98-
className: 'p-4 text-xs text-muted-foreground border-t',
99-
body: { type: 'text', content: 'v1.0.0' }
112+
type: 'sidebar-menu',
113+
body: {
114+
type: 'sidebar-menu-item',
115+
body: {
116+
type: 'sidebar-menu-button',
117+
size: 'lg',
118+
className: 'data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground',
119+
body: {
120+
type: 'div',
121+
className: 'flex items-center gap-2 text-left leading-tight',
122+
body: [
123+
{
124+
type: 'avatar',
125+
src: 'https://github.com/shadcn.png',
126+
alt: '@shadcn',
127+
fallback: 'SC',
128+
className: 'h-8 w-8 rounded-lg'
129+
},
130+
{
131+
type: 'div',
132+
className: 'grid flex-1 text-left text-sm leading-tight',
133+
body: [
134+
{ type: 'span', className: 'truncate font-semibold', body: {type: 'text', content: 'Troy Su'} },
135+
{ type: 'span', className: 'truncate text-xs', body: {type: 'text', content: 'troy@object-ui.com'} }
136+
]
137+
}
138+
]
139+
}
140+
}
141+
}
100142
}
101143
}
102144
]
@@ -239,8 +281,35 @@ const schema = {
239281
title: 'Interactive Chart',
240282
body: {
241283
type: 'div',
242-
className: 'h-[350px] flex items-center justify-center bg-muted/50 rounded-md border border-dashed text-muted-foreground m-6',
243-
body: { type: 'text', content: 'Chart Area Placeholder' }
284+
className: 'p-6',
285+
body: {
286+
type: 'chart',
287+
chartType: 'bar',
288+
className: "aspect-auto h-[350px] w-full",
289+
data: [
290+
{ month: "January", desktop: 186, mobile: 80 },
291+
{ month: "February", desktop: 305, mobile: 200 },
292+
{ month: "March", desktop: 237, mobile: 120 },
293+
{ month: "April", desktop: 73, mobile: 190 },
294+
{ month: "May", desktop: 209, mobile: 130 },
295+
{ month: "June", desktop: 214, mobile: 140 },
296+
],
297+
config: {
298+
desktop: {
299+
label: "Desktop",
300+
color: "hsl(var(--primary))",
301+
},
302+
mobile: {
303+
label: "Mobile",
304+
color: "hsl(var(--primary)/0.5)",
305+
},
306+
},
307+
xAxisKey: "month",
308+
series: [
309+
{ dataKey: "desktop" },
310+
{ dataKey: "mobile" }
311+
]
312+
}
244313
}
245314
},
246315
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ComponentRegistry } from '@object-ui/core';
2+
import { icons } from 'lucide-react';
3+
4+
ComponentRegistry.register('icon',
5+
({ schema, className, ...props }) => {
6+
const Icon = (icons as any)[schema.name || schema.icon];
7+
if (!Icon) return null;
8+
return <Icon className={className} {...props} />;
9+
},
10+
{
11+
label: 'Icon',
12+
type: 'basic',
13+
inputs: [
14+
{ name: 'name', type: 'string', label: 'Icon Name' },
15+
{ name: 'className', type: 'string', label: 'CSS Class' }
16+
]
17+
}
18+
);

packages/components/src/renderers/basic/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ import './text';
33
import './span';
44
import './separator';
55
import './image';
6+
import './icon';
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { ComponentRegistry } from '@object-ui/core';
2+
import {
3+
ChartContainer,
4+
ChartTooltip,
5+
ChartTooltipContent,
6+
ChartLegend,
7+
ChartLegendContent
8+
} from '@/ui/chart';
9+
import {
10+
Bar,
11+
BarChart,
12+
Line,
13+
LineChart,
14+
Area,
15+
AreaChart,
16+
XAxis,
17+
YAxis,
18+
CartesianGrid,
19+
ResponsiveContainer
20+
} from 'recharts';
21+
22+
ComponentRegistry.register('chart',
23+
({ schema, className }) => {
24+
const { chartType = 'bar', data = [], config = {}, xAxisKey, series = [] } = schema;
25+
26+
const ChartComponent = {
27+
bar: BarChart,
28+
line: LineChart,
29+
area: AreaChart
30+
}[chartType as string] || BarChart;
31+
32+
return (
33+
<ChartContainer config={config} className={className}>
34+
<ChartComponent data={data}>
35+
<CartesianGrid vertical={false} />
36+
<XAxis
37+
dataKey={xAxisKey}
38+
tickLine={false}
39+
tickMargin={10}
40+
axisLine={false}
41+
tickFormatter={(value) => value.slice(0, 3)}
42+
/>
43+
<ChartTooltip content={<ChartTooltipContent />} />
44+
<ChartLegend content={<ChartLegendContent />} />
45+
{series.map((s: any) => {
46+
const color = config[s.dataKey]?.color || 'hsl(var(--chart-1))';
47+
48+
if (chartType === 'bar') {
49+
return <Bar key={s.dataKey} dataKey={s.dataKey} fill={color} radius={4} />;
50+
}
51+
if (chartType === 'line') {
52+
return <Line key={s.dataKey} type="monotone" dataKey={s.dataKey} stroke={color} strokeWidth={2} dot={false} />;
53+
}
54+
if (chartType === 'area') {
55+
return <Area key={s.dataKey} type="monotone" dataKey={s.dataKey} fill={color} stroke={color} fillOpacity={0.4} />;
56+
}
57+
return null;
58+
})}
59+
</ChartComponent>
60+
</ChartContainer>
61+
);
62+
},
63+
{
64+
label: 'Chart',
65+
type: 'data-display',
66+
inputs: [
67+
{
68+
name: 'chartType',
69+
type: 'select',
70+
label: 'Chart Type',
71+
options: [
72+
{ label: 'Bar', value: 'bar' },
73+
{ label: 'Line', value: 'line' },
74+
{ label: 'Area', value: 'area' }
75+
]
76+
},
77+
{ name: 'data', type: 'code', label: 'Data (JSON)' },
78+
{ name: 'config', type: 'code', label: 'Config (JSON)' },
79+
{ name: 'xAxisKey', type: 'string', label: 'X Axis Key' },
80+
{ name: 'className', type: 'string', label: 'CSS Class' }
81+
]
82+
}
83+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import './badge';
22
import './avatar';
33
import './alert';
4+
import './chart';

0 commit comments

Comments
 (0)