pxengine 0.1.3 → 0.1.5
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.cjs +9057 -2405
- package/dist/index.d.cts +105 -10
- package/dist/index.d.ts +105 -10
- package/dist/index.js +9062 -2407
- package/dist/registry.json +1388 -4364
- package/package.json +4 -4
- package/src/atoms/CardAtom.tsx +7 -7
- package/src/atoms/FormInputAtom.tsx +97 -0
- package/src/atoms/FormSelectAtom.tsx +106 -0
- package/src/atoms/FormTextareaAtom.tsx +89 -0
- package/src/atoms/LayoutAtom.tsx +5 -3
- package/src/atoms/index.ts +4 -0
- package/src/components/ui/index.ts +54 -0
- package/src/render/PXEngineRenderer.tsx +265 -242
- package/src/types/atoms.ts +8 -1
|
@@ -1,272 +1,295 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { UIComponent, UISchema
|
|
2
|
+
import { UIComponent, UISchema } from "../types/schema";
|
|
3
3
|
import * as Atoms from "../atoms";
|
|
4
|
-
import * as Molecules from "../molecules/index";
|
|
4
|
+
import * as Molecules from "../molecules/index";
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
|
|
6
|
+
// Import all shadcn UI components
|
|
7
|
+
import * as UIComponents from "../components/ui/index";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Components that require specific React Context and cannot be rendered in isolation.
|
|
11
|
+
* These components will show helpful error messages if used directly in schemas.
|
|
12
|
+
*/
|
|
13
|
+
const CONTEXT_DEPENDENT_COMPONENTS = new Set([
|
|
14
|
+
// Form components - require FormField + FormItem context
|
|
15
|
+
"FormLabel",
|
|
16
|
+
"FormControl",
|
|
17
|
+
"FormDescription",
|
|
18
|
+
"FormMessage",
|
|
19
|
+
"FormItem",
|
|
20
|
+
"FormField",
|
|
21
|
+
// Select components - require Select parent
|
|
22
|
+
"SelectContent",
|
|
23
|
+
"SelectItem",
|
|
24
|
+
"SelectValue",
|
|
25
|
+
"SelectTrigger",
|
|
26
|
+
"SelectGroup",
|
|
27
|
+
"SelectLabel",
|
|
28
|
+
"SelectSeparator",
|
|
29
|
+
// Accordion components - require Accordion parent
|
|
30
|
+
"AccordionContent",
|
|
31
|
+
"AccordionItem",
|
|
32
|
+
"AccordionTrigger",
|
|
33
|
+
// Tabs components - require Tabs parent
|
|
34
|
+
"TabsContent",
|
|
35
|
+
"TabsList",
|
|
36
|
+
"TabsTrigger",
|
|
37
|
+
// Dialog components - require Dialog parent
|
|
38
|
+
"DialogContent",
|
|
39
|
+
"DialogHeader",
|
|
40
|
+
"DialogFooter",
|
|
41
|
+
"DialogTitle",
|
|
42
|
+
"DialogDescription",
|
|
43
|
+
"DialogClose",
|
|
44
|
+
// Sheet components - require Sheet parent
|
|
45
|
+
"SheetContent",
|
|
46
|
+
"SheetHeader",
|
|
47
|
+
"SheetFooter",
|
|
48
|
+
"SheetTitle",
|
|
49
|
+
"SheetDescription",
|
|
50
|
+
"SheetClose",
|
|
51
|
+
// AlertDialog components - require AlertDialog parent
|
|
52
|
+
"AlertDialogContent",
|
|
53
|
+
"AlertDialogHeader",
|
|
54
|
+
"AlertDialogFooter",
|
|
55
|
+
"AlertDialogTitle",
|
|
56
|
+
"AlertDialogDescription",
|
|
57
|
+
"AlertDialogAction",
|
|
58
|
+
"AlertDialogCancel",
|
|
59
|
+
// Dropdown components - require DropdownMenu parent
|
|
60
|
+
"DropdownMenuContent",
|
|
61
|
+
"DropdownMenuItem",
|
|
62
|
+
"DropdownMenuLabel",
|
|
63
|
+
"DropdownMenuSeparator",
|
|
64
|
+
"DropdownMenuCheckboxItem",
|
|
65
|
+
"DropdownMenuRadioItem",
|
|
66
|
+
"DropdownMenuRadioGroup",
|
|
67
|
+
// Popover components - require Popover parent
|
|
68
|
+
"PopoverContent",
|
|
69
|
+
// Tooltip components - require TooltipProvider parent
|
|
70
|
+
"TooltipContent",
|
|
71
|
+
// Context Menu components
|
|
72
|
+
"ContextMenuContent",
|
|
73
|
+
"ContextMenuItem",
|
|
74
|
+
// Navigation Menu components
|
|
75
|
+
"NavigationMenuContent",
|
|
76
|
+
"NavigationMenuItem",
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Mapping of problematic components to their Atom alternatives
|
|
81
|
+
*/
|
|
82
|
+
const COMPONENT_SUGGESTIONS: Record<string, string> = {
|
|
83
|
+
FormLabel: "FormInputAtom (with label prop)",
|
|
84
|
+
FormControl: "FormInputAtom",
|
|
85
|
+
FormItem: "FormInputAtom",
|
|
86
|
+
FormField: "FormInputAtom",
|
|
87
|
+
Select: "FormSelectAtom or InputAtom with inputType='select'",
|
|
88
|
+
SelectContent: "FormSelectAtom or InputAtom with inputType='select'",
|
|
89
|
+
SelectItem: "FormSelectAtom or InputAtom with inputType='select'",
|
|
90
|
+
Tabs: "TabsAtom",
|
|
91
|
+
TabsContent: "TabsAtom",
|
|
92
|
+
TabsList: "TabsAtom",
|
|
93
|
+
Dialog: "DialogAtom",
|
|
94
|
+
DialogContent: "DialogAtom",
|
|
95
|
+
Sheet: "SheetAtom",
|
|
96
|
+
SheetContent: "SheetAtom",
|
|
97
|
+
AlertDialog: "AlertDialogAtom",
|
|
98
|
+
AlertDialogContent: "AlertDialogAtom",
|
|
99
|
+
Accordion: "AccordionAtom",
|
|
100
|
+
AccordionItem: "AccordionAtom",
|
|
101
|
+
Input: "InputAtom",
|
|
102
|
+
Textarea: "InputAtom with inputType='textarea'",
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* PXEngineRenderer
|
|
107
|
+
*
|
|
108
|
+
* Handles both the full schema { version, root } and individual components.
|
|
109
|
+
* Dynamically resolves components from Atoms/Molecules/UI Components registry.
|
|
110
|
+
* Prevents rendering of context-dependent components to avoid React errors.
|
|
111
|
+
*/
|
|
9
112
|
interface PXEngineRendererProps {
|
|
10
113
|
schema: UISchema | UIComponent;
|
|
11
114
|
onAction?: (action: string, payload?: any) => void;
|
|
12
115
|
}
|
|
13
116
|
|
|
14
117
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* The core engine of the @pxengine-ui library.
|
|
18
|
-
* Recursively renders Atoms and Molecules based on the provided JSON schema.
|
|
118
|
+
* Renders an error message for context-dependent components
|
|
19
119
|
*/
|
|
120
|
+
const renderContextDependentError = (
|
|
121
|
+
componentName: string,
|
|
122
|
+
normalizedName: string,
|
|
123
|
+
key: string,
|
|
124
|
+
): React.ReactNode => {
|
|
125
|
+
const suggestion =
|
|
126
|
+
COMPONENT_SUGGESTIONS[normalizedName] ||
|
|
127
|
+
`${componentName}Atom (if available)`;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<div
|
|
131
|
+
key={key}
|
|
132
|
+
className="p-4 border-2 border-amber-500/50 rounded-lg bg-amber-50/80 space-y-2 my-2"
|
|
133
|
+
>
|
|
134
|
+
<div className="flex items-start gap-2">
|
|
135
|
+
<span className="text-amber-600 font-bold text-lg">⚠️</span>
|
|
136
|
+
<div className="flex-1">
|
|
137
|
+
<p className="text-sm font-semibold text-amber-900">
|
|
138
|
+
Invalid Component: {componentName}
|
|
139
|
+
</p>
|
|
140
|
+
<p className="text-xs text-amber-700 mt-1">
|
|
141
|
+
This component requires React Context and cannot be rendered
|
|
142
|
+
directly in schemas.
|
|
143
|
+
</p>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div className="bg-white/60 p-3 rounded border border-amber-200">
|
|
147
|
+
<p className="text-xs font-semibold text-gray-700 mb-1.5">
|
|
148
|
+
✓ Use instead:
|
|
149
|
+
</p>
|
|
150
|
+
<code className="text-xs text-blue-700 bg-blue-50 px-2 py-1 rounded">
|
|
151
|
+
{suggestion}
|
|
152
|
+
</code>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Renders an error message for components not found in any registry
|
|
160
|
+
*/
|
|
161
|
+
const renderNotFoundError = (
|
|
162
|
+
componentName: string,
|
|
163
|
+
key: string,
|
|
164
|
+
): React.ReactNode => {
|
|
165
|
+
return (
|
|
166
|
+
<div
|
|
167
|
+
key={key}
|
|
168
|
+
className="p-3 border border-dashed border-red-500/50 text-red-500 text-xs rounded bg-red-50/30 my-2"
|
|
169
|
+
>
|
|
170
|
+
<span className="font-semibold">❌ Unknown Component:</span>{" "}
|
|
171
|
+
{componentName}
|
|
172
|
+
<p className="text-[10px] text-red-400 mt-1">
|
|
173
|
+
Component not found in Atoms, Molecules, or UI Components registry.
|
|
174
|
+
</p>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
20
179
|
export const PXEngineRenderer: React.FC<PXEngineRendererProps> = ({
|
|
21
180
|
schema,
|
|
22
181
|
onAction,
|
|
23
182
|
}) => {
|
|
24
|
-
|
|
25
|
-
const root = "root" in schema ? schema.root : schema;
|
|
183
|
+
if (!schema) return null;
|
|
26
184
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
185
|
+
// Extract root if it's a full UISchema
|
|
186
|
+
const root = (schema as any).root || (schema as UIComponent);
|
|
187
|
+
|
|
188
|
+
const renderRecursive = (
|
|
189
|
+
component: UIComponent | string | any,
|
|
190
|
+
index?: number,
|
|
33
191
|
): React.ReactNode => {
|
|
34
|
-
|
|
192
|
+
// 1. Handle text nodes (string or number)
|
|
193
|
+
if (typeof component === "string" || typeof component === "number") {
|
|
194
|
+
return component;
|
|
195
|
+
}
|
|
35
196
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<Atoms.LayoutAtom
|
|
40
|
-
key={id}
|
|
41
|
-
{...(atom as any)}
|
|
42
|
-
renderComponent={renderComponent}
|
|
43
|
-
/>
|
|
44
|
-
);
|
|
45
|
-
case "card":
|
|
46
|
-
return (
|
|
47
|
-
<Atoms.CardAtom
|
|
48
|
-
key={id}
|
|
49
|
-
{...(atom as any)}
|
|
50
|
-
renderComponent={renderComponent}
|
|
51
|
-
/>
|
|
52
|
-
);
|
|
53
|
-
case "text":
|
|
54
|
-
return <Atoms.TextAtom key={id} {...(atom as any)} />;
|
|
55
|
-
case "button":
|
|
56
|
-
return (
|
|
57
|
-
<Atoms.ButtonAtom key={id} {...(atom as any)} onAction={onAction} />
|
|
58
|
-
);
|
|
59
|
-
case "input":
|
|
60
|
-
return <Atoms.InputAtom key={id} {...(atom as any)} />;
|
|
61
|
-
case "badge":
|
|
62
|
-
return <Atoms.BadgeAtom key={id} {...(atom as any)} />;
|
|
63
|
-
case "avatar":
|
|
64
|
-
return <Atoms.AvatarAtom key={id} {...(atom as any)} />;
|
|
65
|
-
case "progress":
|
|
66
|
-
return <Atoms.ProgressAtom key={id} {...(atom as any)} />;
|
|
67
|
-
case "skeleton":
|
|
68
|
-
return <Atoms.SkeletonAtom key={id} {...(atom as any)} />;
|
|
69
|
-
case "alert":
|
|
70
|
-
return <Atoms.AlertAtom key={id} {...(atom as any)} />;
|
|
71
|
-
case "separator":
|
|
72
|
-
return <Atoms.SeparatorAtom key={id} {...(atom as any)} />;
|
|
73
|
-
case "table":
|
|
74
|
-
return <Atoms.TableAtom key={id} {...(atom as any)} />;
|
|
75
|
-
case "tabs":
|
|
76
|
-
return (
|
|
77
|
-
<Atoms.TabsAtom
|
|
78
|
-
key={id}
|
|
79
|
-
{...(atom as any)}
|
|
80
|
-
renderComponent={renderComponent}
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
83
|
-
case "accordion":
|
|
84
|
-
return (
|
|
85
|
-
<Atoms.AccordionAtom
|
|
86
|
-
key={id}
|
|
87
|
-
{...(atom as any)}
|
|
88
|
-
renderComponent={renderComponent}
|
|
89
|
-
/>
|
|
90
|
-
);
|
|
91
|
-
case "scroll-area":
|
|
92
|
-
return (
|
|
93
|
-
<Atoms.ScrollAreaAtom
|
|
94
|
-
key={id}
|
|
95
|
-
{...(atom as any)}
|
|
96
|
-
renderComponent={renderComponent}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
99
|
-
case "carousel":
|
|
100
|
-
return (
|
|
101
|
-
<Atoms.CarouselAtom
|
|
102
|
-
key={id}
|
|
103
|
-
{...(atom as any)}
|
|
104
|
-
renderComponent={renderComponent}
|
|
105
|
-
/>
|
|
106
|
-
);
|
|
107
|
-
case "aspect-ratio":
|
|
108
|
-
return (
|
|
109
|
-
<Atoms.AspectRatioAtom
|
|
110
|
-
key={id}
|
|
111
|
-
{...(atom as any)}
|
|
112
|
-
renderComponent={renderComponent}
|
|
113
|
-
/>
|
|
114
|
-
);
|
|
115
|
-
case "collapsible":
|
|
116
|
-
return (
|
|
117
|
-
<Atoms.CollapsibleAtom
|
|
118
|
-
key={id}
|
|
119
|
-
{...(atom as any)}
|
|
120
|
-
renderComponent={renderComponent}
|
|
121
|
-
/>
|
|
122
|
-
);
|
|
123
|
-
case "tooltip":
|
|
124
|
-
return (
|
|
125
|
-
<Atoms.TooltipAtom
|
|
126
|
-
key={id}
|
|
127
|
-
{...(atom as any)}
|
|
128
|
-
renderComponent={renderComponent}
|
|
129
|
-
/>
|
|
130
|
-
);
|
|
131
|
-
case "popover":
|
|
132
|
-
return (
|
|
133
|
-
<Atoms.PopoverAtom
|
|
134
|
-
key={id}
|
|
135
|
-
{...(atom as any)}
|
|
136
|
-
renderComponent={renderComponent}
|
|
137
|
-
/>
|
|
138
|
-
);
|
|
139
|
-
case "dialog":
|
|
140
|
-
return (
|
|
141
|
-
<Atoms.DialogAtom
|
|
142
|
-
key={id}
|
|
143
|
-
{...(atom as any)}
|
|
144
|
-
renderComponent={renderComponent}
|
|
145
|
-
/>
|
|
146
|
-
);
|
|
147
|
-
case "sheet":
|
|
148
|
-
return (
|
|
149
|
-
<Atoms.SheetAtom
|
|
150
|
-
key={id}
|
|
151
|
-
{...(atom as any)}
|
|
152
|
-
renderComponent={renderComponent}
|
|
153
|
-
/>
|
|
154
|
-
);
|
|
155
|
-
case "alert-dialog":
|
|
156
|
-
return (
|
|
157
|
-
<Atoms.AlertDialogAtom
|
|
158
|
-
key={id}
|
|
159
|
-
{...(atom as any)}
|
|
160
|
-
onAction={onAction}
|
|
161
|
-
renderComponent={renderComponent}
|
|
162
|
-
/>
|
|
163
|
-
);
|
|
164
|
-
case "breadcrumb":
|
|
165
|
-
return <Atoms.BreadcrumbAtom key={id} {...(atom as any)} />;
|
|
166
|
-
case "spinner":
|
|
167
|
-
return <Atoms.SpinnerAtom key={id} {...(atom as any)} />;
|
|
168
|
-
case "calendar":
|
|
169
|
-
return <Atoms.CalendarAtom key={id} {...(atom as any)} />;
|
|
170
|
-
case "pagination":
|
|
171
|
-
return <Atoms.PaginationAtom key={id} {...(atom as any)} />;
|
|
172
|
-
case "command":
|
|
173
|
-
return <Atoms.CommandAtom key={id} {...(atom as any)} />;
|
|
174
|
-
default:
|
|
175
|
-
return null;
|
|
197
|
+
// 2. Handle already rendered React elements
|
|
198
|
+
if (React.isValidElement(component)) {
|
|
199
|
+
return component;
|
|
176
200
|
}
|
|
177
|
-
};
|
|
178
201
|
|
|
179
|
-
|
|
180
|
-
* Render Molecule components (Domain-specific / Complex)
|
|
181
|
-
*/
|
|
182
|
-
const renderMolecule = (molecule: UIMolecule): React.ReactNode => {
|
|
183
|
-
const { type, id } = molecule;
|
|
202
|
+
if (!component || typeof component !== "object") return null;
|
|
184
203
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
204
|
+
const { type, name, props = {}, children = [], id } = component;
|
|
205
|
+
|
|
206
|
+
// Determine the component name to search for
|
|
207
|
+
const componentName = name || type;
|
|
208
|
+
if (!componentName || typeof componentName !== "string") return null;
|
|
209
|
+
|
|
210
|
+
// Generate a unique key from id, index, or random
|
|
211
|
+
const uniqueKey =
|
|
212
|
+
id ||
|
|
213
|
+
`${componentName}-${index || Math.random().toString(36).substr(2, 9)}`;
|
|
214
|
+
|
|
215
|
+
// Normalize name to PascalCase
|
|
216
|
+
const normalizedName =
|
|
217
|
+
componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
218
|
+
const atomName = normalizedName.endsWith("Atom")
|
|
219
|
+
? normalizedName
|
|
220
|
+
: `${normalizedName}Atom`;
|
|
221
|
+
|
|
222
|
+
// 3. Resolve Component from registries (PRIORITY ORDER - safest first)
|
|
223
|
+
|
|
224
|
+
// Priority 1: Atoms (schema-safe, self-contained)
|
|
225
|
+
let TargetComponent =
|
|
226
|
+
(Atoms as any)[atomName] ||
|
|
227
|
+
(Atoms as any)[normalizedName] ||
|
|
228
|
+
(Atoms as any)[componentName];
|
|
229
|
+
|
|
230
|
+
// Priority 2: Molecules (schema-safe, composite)
|
|
231
|
+
if (!TargetComponent) {
|
|
232
|
+
TargetComponent =
|
|
233
|
+
(Molecules as any)[normalizedName] || (Molecules as any)[componentName];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Priority 3: UI Components (ONLY if NOT context-dependent)
|
|
237
|
+
if (!TargetComponent && !CONTEXT_DEPENDENT_COMPONENTS.has(normalizedName)) {
|
|
238
|
+
TargetComponent =
|
|
239
|
+
(UIComponents as any)[normalizedName] ||
|
|
240
|
+
(UIComponents as any)[componentName];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 4. Handle component not found or context-dependent
|
|
244
|
+
if (!TargetComponent) {
|
|
245
|
+
if (CONTEXT_DEPENDENT_COMPONENTS.has(normalizedName)) {
|
|
246
|
+
// Show helpful error for context-dependent components
|
|
247
|
+
if (process.env.NODE_ENV === "development") {
|
|
248
|
+
console.error(
|
|
249
|
+
`[PXEngineRenderer] Cannot render context-dependent component: ${componentName}. ` +
|
|
250
|
+
`Use ${COMPONENT_SUGGESTIONS[normalizedName] || `${componentName}Atom`} instead.`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return renderContextDependentError(
|
|
254
|
+
componentName,
|
|
255
|
+
normalizedName,
|
|
256
|
+
uniqueKey,
|
|
215
257
|
);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
{...(molecule as any)}
|
|
221
|
-
onProceed={() => onAction?.((molecule as any).action || "proceed")}
|
|
222
|
-
/>
|
|
258
|
+
} else {
|
|
259
|
+
// Show generic "not found" error
|
|
260
|
+
console.warn(
|
|
261
|
+
`[PXEngineRenderer] Component not found: ${componentName}`,
|
|
223
262
|
);
|
|
224
|
-
|
|
225
|
-
|
|
263
|
+
return renderNotFoundError(componentName, uniqueKey);
|
|
264
|
+
}
|
|
226
265
|
}
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const renderComponent = (component: UIComponent): React.ReactNode => {
|
|
230
|
-
const { type } = component;
|
|
231
266
|
|
|
232
|
-
//
|
|
233
|
-
const
|
|
234
|
-
"layout",
|
|
235
|
-
"card",
|
|
236
|
-
"text",
|
|
237
|
-
"button",
|
|
238
|
-
"input",
|
|
239
|
-
"badge",
|
|
240
|
-
"avatar",
|
|
241
|
-
"progress",
|
|
242
|
-
"skeleton",
|
|
243
|
-
"alert",
|
|
244
|
-
"separator",
|
|
245
|
-
"table",
|
|
246
|
-
"tabs",
|
|
247
|
-
"accordion",
|
|
248
|
-
"scroll-area",
|
|
249
|
-
"carousel",
|
|
250
|
-
"aspect-ratio",
|
|
251
|
-
"collapsible",
|
|
252
|
-
"tooltip",
|
|
253
|
-
"popover",
|
|
254
|
-
"dialog",
|
|
255
|
-
"sheet",
|
|
256
|
-
"alert-dialog",
|
|
257
|
-
"breadcrumb",
|
|
258
|
-
"spinner",
|
|
259
|
-
"calendar",
|
|
260
|
-
"pagination",
|
|
261
|
-
"command",
|
|
262
|
-
].includes(type);
|
|
267
|
+
// 4. Determine if component expects 'renderComponent' prop (for Atoms with children management)
|
|
268
|
+
const isAtomWithRenderProp = atomName in Atoms;
|
|
263
269
|
|
|
264
|
-
|
|
265
|
-
|
|
270
|
+
// 5. Render Component
|
|
271
|
+
if (isAtomWithRenderProp) {
|
|
272
|
+
// Atoms handle their own children via renderComponent
|
|
273
|
+
return (
|
|
274
|
+
<TargetComponent
|
|
275
|
+
key={uniqueKey}
|
|
276
|
+
{...props}
|
|
277
|
+
onAction={onAction}
|
|
278
|
+
renderComponent={renderRecursive}
|
|
279
|
+
children={children}
|
|
280
|
+
/>
|
|
281
|
+
);
|
|
282
|
+
} else {
|
|
283
|
+
// Standard shadcn components - pass children as React children
|
|
284
|
+
return (
|
|
285
|
+
<TargetComponent key={uniqueKey} {...props}>
|
|
286
|
+
{Array.isArray(children)
|
|
287
|
+
? children.map((child, idx) => renderRecursive(child, idx))
|
|
288
|
+
: children}
|
|
289
|
+
</TargetComponent>
|
|
290
|
+
);
|
|
266
291
|
}
|
|
267
|
-
|
|
268
|
-
return renderMolecule(component as UIMolecule);
|
|
269
292
|
};
|
|
270
293
|
|
|
271
|
-
return
|
|
294
|
+
return <div className="px-engine-root">{renderRecursive(root)}</div>;
|
|
272
295
|
};
|
package/src/types/atoms.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { FormInputAtomType } from "../atoms/FormInputAtom";
|
|
2
|
+
import type { FormSelectAtomType } from "../atoms/FormSelectAtom";
|
|
3
|
+
import type { FormTextareaAtomType } from "../atoms/FormTextareaAtom";
|
|
4
|
+
|
|
1
5
|
export type LayoutDirection = "vertical" | "horizontal" | "grid";
|
|
2
6
|
export type GapSize = "none" | "sm" | "md" | "lg" | "xl";
|
|
3
7
|
export type TextVariant = "h1" | "h2" | "h3" | "h4" | "p" | "small" | "muted" | "label";
|
|
@@ -291,4 +295,7 @@ export type UIAtom =
|
|
|
291
295
|
| BreadcrumbAtomType
|
|
292
296
|
| CalendarAtomType
|
|
293
297
|
| PaginationAtomType
|
|
294
|
-
| CommandAtomType
|
|
298
|
+
| CommandAtomType
|
|
299
|
+
| FormInputAtomType
|
|
300
|
+
| FormSelectAtomType
|
|
301
|
+
| FormTextareaAtomType;
|