@object-ui/components 0.3.1 → 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.
Files changed (299) hide show
  1. package/.turbo/turbo-build.log +47 -0
  2. package/README.md +13 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.js +34610 -24854
  5. package/dist/index.umd.cjs +53 -32
  6. package/dist/src/SchemaRenderer.d.ts +3 -0
  7. package/dist/src/{ui → custom}/button-group.d.ts +1 -1
  8. package/dist/src/custom/field.d.ts +19 -0
  9. package/dist/src/custom/index.d.ts +12 -0
  10. package/dist/src/custom/input-group.d.ts +14 -0
  11. package/dist/src/{ui → custom}/item.d.ts +1 -1
  12. package/dist/src/custom/native-select.d.ts +12 -0
  13. package/dist/src/custom/sort-builder.d.ts +22 -0
  14. package/dist/src/index.d.ts +1 -0
  15. package/dist/src/renderers/data-display/table.d.ts +1 -1
  16. package/dist/src/renderers/layout/page.d.ts +1 -1
  17. package/dist/src/renderers/placeholders.d.ts +1 -1
  18. package/dist/src/ui/accordion.d.ts +4 -4
  19. package/dist/src/ui/alert-dialog.d.ts +17 -11
  20. package/dist/src/ui/alert.d.ts +4 -5
  21. package/dist/src/ui/aspect-ratio.d.ts +1 -1
  22. package/dist/src/ui/avatar.d.ts +3 -3
  23. package/dist/src/ui/badge.d.ts +3 -3
  24. package/dist/src/ui/breadcrumb.d.ts +16 -8
  25. package/dist/src/ui/calendar.d.ts +7 -7
  26. package/dist/src/ui/card.d.ts +7 -8
  27. package/dist/src/ui/carousel.d.ts +5 -6
  28. package/dist/src/ui/chart.d.ts +62 -0
  29. package/dist/src/ui/checkbox.d.ts +1 -1
  30. package/dist/src/ui/collapsible.d.ts +3 -3
  31. package/dist/src/ui/command.d.ts +78 -16
  32. package/dist/src/ui/context-menu.d.ts +14 -12
  33. package/dist/src/ui/dialog.d.ts +17 -13
  34. package/dist/src/ui/drawer.d.ts +19 -10
  35. package/dist/src/ui/dropdown-menu.d.ts +20 -18
  36. package/dist/src/ui/form.d.ts +6 -7
  37. package/dist/src/ui/hover-card.d.ts +3 -3
  38. package/dist/src/ui/index.d.ts +2 -8
  39. package/dist/src/ui/input-otp.d.ts +30 -7
  40. package/dist/src/ui/label.d.ts +2 -1
  41. package/dist/src/ui/menubar.d.ts +19 -17
  42. package/dist/src/ui/navigation-menu.d.ts +9 -11
  43. package/dist/src/ui/pagination.d.ts +25 -10
  44. package/dist/src/ui/popover.d.ts +4 -5
  45. package/dist/src/ui/progress.d.ts +1 -1
  46. package/dist/src/ui/radio-group.d.ts +2 -2
  47. package/dist/src/ui/resizable.d.ts +5 -8
  48. package/dist/src/ui/scroll-area.d.ts +2 -2
  49. package/dist/src/ui/select.d.ts +11 -13
  50. package/dist/src/ui/sheet.d.ts +23 -11
  51. package/dist/src/ui/sidebar.d.ts +27 -29
  52. package/dist/src/ui/skeleton.d.ts +1 -1
  53. package/dist/src/ui/slider.d.ts +1 -1
  54. package/dist/src/ui/sonner.d.ts +2 -1
  55. package/dist/src/ui/switch.d.ts +2 -2
  56. package/dist/src/ui/tabs.d.ts +1 -1
  57. package/dist/src/ui/textarea.d.ts +1 -1
  58. package/dist/src/ui/toast.d.ts +22 -0
  59. package/dist/src/ui/toggle-group.d.ts +8 -3
  60. package/dist/src/ui/toggle.d.ts +4 -1
  61. package/dist/src/ui/tooltip.d.ts +4 -4
  62. package/dist/src/ui/typography.d.ts +21 -0
  63. package/package.json +17 -7
  64. package/shadcn-components.json +52 -47
  65. package/src/SchemaRenderer.tsx +28 -0
  66. package/src/__tests__/PageRendererRegions.test.tsx +59 -0
  67. package/src/__tests__/Registry.test.ts +21 -0
  68. package/src/__tests__/basic-renderers.test.tsx +1 -1
  69. package/src/__tests__/complex-disclosure-renderers.test.tsx +3 -2
  70. package/src/__tests__/feedback-overlay-renderers.test.tsx +1 -1
  71. package/src/__tests__/form-renderers.test.tsx +1 -1
  72. package/src/__tests__/layout-data-renderers.test.tsx +1 -1
  73. package/src/{ui → custom}/button-group.tsx +1 -1
  74. package/src/{ui → custom}/combobox.tsx +3 -3
  75. package/src/{ui → custom}/date-picker.tsx +3 -3
  76. package/src/custom/field.tsx +81 -0
  77. package/src/{ui → custom}/filter-builder.tsx +3 -3
  78. package/src/custom/index.ts +12 -0
  79. package/src/custom/input-group.tsx +53 -0
  80. package/src/{ui → custom}/item.tsx +1 -1
  81. package/src/custom/native-select.tsx +33 -0
  82. package/src/custom/sort-builder.tsx +129 -0
  83. package/src/index.css +20 -1
  84. package/src/index.ts +1 -0
  85. package/src/renderers/basic/button-group.tsx +1 -0
  86. package/src/renderers/basic/div.tsx +12 -1
  87. package/src/renderers/basic/html.tsx +1 -0
  88. package/src/renderers/basic/icon.tsx +1 -0
  89. package/src/renderers/basic/image.tsx +1 -0
  90. package/src/renderers/basic/navigation-menu.tsx +1 -0
  91. package/src/renderers/basic/pagination.tsx +31 -4
  92. package/src/renderers/basic/separator.tsx +1 -0
  93. package/src/renderers/basic/span.tsx +12 -1
  94. package/src/renderers/basic/text.tsx +4 -2
  95. package/src/renderers/complex/carousel.tsx +1 -0
  96. package/src/renderers/complex/data-table.tsx +134 -95
  97. package/src/renderers/complex/filter-builder.tsx +2 -1
  98. package/src/renderers/complex/resizable.tsx +2 -1
  99. package/src/renderers/complex/scroll-area.tsx +25 -7
  100. package/src/renderers/complex/table.tsx +1 -0
  101. package/src/renderers/data-display/alert.tsx +1 -0
  102. package/src/renderers/data-display/avatar.tsx +1 -0
  103. package/src/renderers/data-display/badge.tsx +1 -0
  104. package/src/renderers/data-display/breadcrumb.tsx +1 -0
  105. package/src/renderers/data-display/kbd.tsx +1 -0
  106. package/src/renderers/data-display/list.tsx +21 -49
  107. package/src/renderers/data-display/statistic.tsx +21 -5
  108. package/src/renderers/data-display/table.tsx +21 -11
  109. package/src/renderers/data-display/tree-view.tsx +7 -1
  110. package/src/renderers/disclosure/accordion.tsx +1 -0
  111. package/src/renderers/disclosure/collapsible.tsx +1 -0
  112. package/src/renderers/disclosure/toggle-group.tsx +2 -0
  113. package/src/renderers/feedback/empty.tsx +1 -0
  114. package/src/renderers/feedback/loading.tsx +2 -1
  115. package/src/renderers/feedback/progress.tsx +1 -0
  116. package/src/renderers/feedback/skeleton.tsx +1 -0
  117. package/src/renderers/feedback/sonner.tsx +1 -0
  118. package/src/renderers/feedback/spinner.tsx +1 -0
  119. package/src/renderers/feedback/toast.tsx +1 -0
  120. package/src/renderers/feedback/toaster.tsx +1 -0
  121. package/src/renderers/form/button.tsx +35 -1
  122. package/src/renderers/form/calendar.tsx +1 -0
  123. package/src/renderers/form/checkbox.tsx +38 -16
  124. package/src/renderers/form/combobox.tsx +2 -1
  125. package/src/renderers/form/command.tsx +1 -0
  126. package/src/renderers/form/date-picker.tsx +1 -0
  127. package/src/renderers/form/file-upload.tsx +1 -0
  128. package/src/renderers/form/form.tsx +92 -15
  129. package/src/renderers/form/input-otp.tsx +1 -0
  130. package/src/renderers/form/input.tsx +3 -0
  131. package/src/renderers/form/label.tsx +1 -0
  132. package/src/renderers/form/radio-group.tsx +1 -0
  133. package/src/renderers/form/select.tsx +35 -15
  134. package/src/renderers/form/slider.tsx +1 -0
  135. package/src/renderers/form/switch.tsx +1 -0
  136. package/src/renderers/form/textarea.tsx +50 -27
  137. package/src/renderers/form/toggle.tsx +3 -45
  138. package/src/renderers/layout/aspect-ratio.tsx +2 -1
  139. package/src/renderers/layout/card.tsx +10 -2
  140. package/src/renderers/layout/container.tsx +1 -0
  141. package/src/renderers/layout/flex.tsx +1 -0
  142. package/src/renderers/layout/grid.tsx +23 -8
  143. package/src/renderers/layout/page.tsx +35 -23
  144. package/src/renderers/layout/semantic.tsx +1 -0
  145. package/src/renderers/layout/stack.tsx +2 -1
  146. package/src/renderers/layout/tabs.tsx +43 -17
  147. package/src/renderers/navigation/header-bar.tsx +1 -0
  148. package/src/renderers/navigation/sidebar.tsx +5 -0
  149. package/src/renderers/overlay/alert-dialog.tsx +1 -0
  150. package/src/renderers/overlay/context-menu.tsx +1 -0
  151. package/src/renderers/overlay/dialog.tsx +1 -0
  152. package/src/renderers/overlay/drawer.tsx +1 -0
  153. package/src/renderers/overlay/dropdown-menu.tsx +1 -0
  154. package/src/renderers/overlay/hover-card.tsx +1 -0
  155. package/src/renderers/overlay/menubar.tsx +1 -0
  156. package/src/renderers/overlay/popover.tsx +1 -0
  157. package/src/renderers/overlay/sheet.tsx +1 -0
  158. package/src/renderers/overlay/tooltip.tsx +1 -0
  159. package/src/renderers/placeholders.tsx +2 -2
  160. package/src/stories/CRMApp.stories.tsx +706 -0
  161. package/src/stories/Guide.mdx +55 -0
  162. package/src/stories/Introduction.mdx +34 -0
  163. package/src/stories/MockedData.stories.tsx +71 -0
  164. package/src/stories/assets/accessibility.png +0 -0
  165. package/src/stories/assets/accessibility.svg +1 -0
  166. package/src/stories/assets/addon-library.png +0 -0
  167. package/src/stories/assets/assets.png +0 -0
  168. package/src/stories/assets/avif-test-image.avif +0 -0
  169. package/src/stories/assets/context.png +0 -0
  170. package/src/stories/assets/discord.svg +1 -0
  171. package/src/stories/assets/docs.png +0 -0
  172. package/src/stories/assets/figma-plugin.png +0 -0
  173. package/src/stories/assets/github.svg +1 -0
  174. package/src/stories/assets/share.png +0 -0
  175. package/src/stories/assets/styling.png +0 -0
  176. package/src/stories/assets/testing.png +0 -0
  177. package/src/stories/assets/theming.png +0 -0
  178. package/src/stories/assets/tutorials.svg +1 -0
  179. package/src/stories/assets/youtube.svg +1 -0
  180. package/src/stories/button.css +30 -0
  181. package/src/stories/header.css +32 -0
  182. package/src/stories/page.css +68 -0
  183. package/src/stories-json/accordion.stories.tsx +43 -0
  184. package/src/stories-json/aggrid.stories.tsx +103 -0
  185. package/src/stories-json/alert.stories.tsx +39 -0
  186. package/src/stories-json/aspect-ratio.stories.tsx +34 -0
  187. package/src/stories-json/avatar.stories.tsx +38 -0
  188. package/src/stories-json/badge.stories.tsx +53 -0
  189. package/src/stories-json/breadcrumb.stories.tsx +30 -0
  190. package/src/stories-json/button-group.stories.tsx +43 -0
  191. package/src/stories-json/button.stories.tsx +73 -0
  192. package/src/stories-json/calendar.stories.tsx +85 -0
  193. package/src/stories-json/card.stories.tsx +48 -0
  194. package/src/stories-json/carousel.stories.tsx +33 -0
  195. package/src/stories-json/charts.stories.tsx +195 -0
  196. package/src/stories-json/chatbot.stories.tsx +248 -0
  197. package/src/stories-json/code-editor.stories.tsx +92 -0
  198. package/src/stories-json/collapsible.stories.tsx +40 -0
  199. package/src/stories-json/controls.stories.tsx +36 -0
  200. package/src/stories-json/dashboard.stories.tsx +318 -0
  201. package/src/stories-json/data-table.stories.tsx +60 -0
  202. package/src/stories-json/data_display_extras.stories.tsx +102 -0
  203. package/src/stories-json/date-picker.stories.tsx +28 -0
  204. package/src/stories-json/detail-view.stories.tsx +258 -0
  205. package/src/stories-json/dialog.stories.tsx +43 -0
  206. package/src/stories-json/feedback_extras.stories.tsx +40 -0
  207. package/src/stories-json/feedback_others.stories.tsx +46 -0
  208. package/src/stories-json/form_advanced.stories.tsx +117 -0
  209. package/src/stories-json/form_extras.stories.tsx +123 -0
  210. package/src/stories-json/grid.stories.tsx +56 -0
  211. package/src/stories-json/icon.stories.tsx +36 -0
  212. package/src/stories-json/input.stories.tsx +52 -0
  213. package/src/stories-json/kanban.stories.tsx +295 -0
  214. package/src/stories-json/layout_extended.stories.tsx +76 -0
  215. package/src/stories-json/layout_flex.stories.tsx +107 -0
  216. package/src/stories-json/list-view.stories.tsx +97 -0
  217. package/src/stories-json/markdown.stories.tsx +129 -0
  218. package/src/stories-json/menus.stories.tsx +63 -0
  219. package/src/stories-json/metric-card.stories.tsx +143 -0
  220. package/src/stories-json/navigation-menu.stories.tsx +37 -0
  221. package/src/stories-json/object-aggrid.stories.tsx +252 -0
  222. package/src/stories-json/object-form.stories.tsx +130 -0
  223. package/src/stories-json/object-gantt.stories.tsx +114 -0
  224. package/src/stories-json/object-grid.stories.tsx +157 -0
  225. package/src/stories-json/object-map.stories.tsx +116 -0
  226. package/src/stories-json/object-view.stories.tsx +118 -0
  227. package/src/stories-json/overlay_extras.stories.tsx +113 -0
  228. package/src/stories-json/overlay_others.stories.tsx +76 -0
  229. package/src/stories-json/page.stories.tsx +55 -0
  230. package/src/stories-json/reports.stories.tsx +163 -0
  231. package/src/stories-json/resizable.stories.tsx +44 -0
  232. package/src/stories-json/select.stories.tsx +34 -0
  233. package/src/stories-json/separator.stories.tsx +41 -0
  234. package/src/stories-json/sidebar.stories.tsx +147 -0
  235. package/src/stories-json/statistic.stories.tsx +44 -0
  236. package/src/stories-json/tabs.stories.tsx +51 -0
  237. package/src/stories-json/timeline.stories.tsx +188 -0
  238. package/src/stories-json/typography.stories.tsx +45 -0
  239. package/src/ui/accordion.tsx +47 -53
  240. package/src/ui/alert-dialog.tsx +103 -117
  241. package/src/ui/alert.tsx +35 -36
  242. package/src/ui/aspect-ratio.tsx +1 -5
  243. package/src/ui/avatar.tsx +41 -42
  244. package/src/ui/badge.tsx +6 -15
  245. package/src/ui/breadcrumb.tsx +81 -75
  246. package/src/ui/button.tsx +10 -11
  247. package/src/ui/calendar.tsx +178 -51
  248. package/src/ui/card.tsx +51 -110
  249. package/src/ui/carousel.tsx +136 -113
  250. package/src/ui/chart.tsx +367 -0
  251. package/src/ui/checkbox.tsx +20 -22
  252. package/src/ui/collapsible.tsx +5 -25
  253. package/src/ui/command.tsx +106 -135
  254. package/src/ui/context-menu.tsx +69 -116
  255. package/src/ui/dialog.tsx +94 -113
  256. package/src/ui/drawer.tsx +82 -99
  257. package/src/ui/dropdown-menu.tsx +134 -188
  258. package/src/ui/form.tsx +51 -40
  259. package/src/ui/hover-card.tsx +18 -33
  260. package/src/ui/index.ts +2 -8
  261. package/src/ui/input-otp.tsx +42 -52
  262. package/src/ui/input.tsx +13 -15
  263. package/src/ui/label.tsx +17 -15
  264. package/src/ui/menubar.tsx +188 -206
  265. package/src/ui/navigation-menu.tsx +96 -136
  266. package/src/ui/pagination.tsx +86 -96
  267. package/src/ui/popover.tsx +24 -41
  268. package/src/ui/progress.tsx +21 -22
  269. package/src/ui/radio-group.tsx +19 -20
  270. package/src/ui/resizable.tsx +32 -42
  271. package/src/ui/scroll-area.tsx +38 -48
  272. package/src/ui/select.tsx +129 -157
  273. package/src/ui/separator.tsx +2 -2
  274. package/src/ui/sheet.tsx +110 -107
  275. package/src/ui/sidebar.tsx +442 -408
  276. package/src/ui/skeleton.tsx +6 -11
  277. package/src/ui/slider.tsx +19 -54
  278. package/src/ui/sonner.tsx +19 -1
  279. package/src/ui/switch.tsx +19 -21
  280. package/src/ui/tabs.tsx +6 -37
  281. package/src/ui/textarea.tsx +8 -4
  282. package/src/ui/toast.tsx +137 -0
  283. package/src/ui/toggle-group.tsx +28 -37
  284. package/src/ui/toggle.tsx +19 -19
  285. package/src/ui/tooltip.tsx +21 -52
  286. package/src/ui/typography.tsx +85 -0
  287. package/tsconfig.json +1 -1
  288. package/vite.config.ts +9 -1
  289. package/vitest.config.ts +5 -0
  290. package/ISSUES_FOUND.md +0 -128
  291. /package/dist/src/{ui → custom}/combobox.d.ts +0 -0
  292. /package/dist/src/{ui → custom}/date-picker.d.ts +0 -0
  293. /package/dist/src/{ui → custom}/empty.d.ts +0 -0
  294. /package/dist/src/{ui → custom}/filter-builder.d.ts +0 -0
  295. /package/dist/src/{ui → custom}/kbd.d.ts +0 -0
  296. /package/dist/src/{ui → custom}/spinner.d.ts +0 -0
  297. /package/src/{ui → custom}/empty.tsx +0 -0
  298. /package/src/{ui → custom}/kbd.tsx +0 -0
  299. /package/src/{ui → custom}/spinner.tsx +0 -0
@@ -14,6 +14,7 @@ import { Button } from '../../ui/button';
14
14
  import { Input } from '../../ui/input';
15
15
  import { Textarea } from '../../ui/textarea';
16
16
  import { Checkbox } from '../../ui/checkbox';
17
+ import { Switch } from '../../ui/switch';
17
18
  import {
18
19
  Select,
19
20
  SelectTrigger,
@@ -36,10 +37,12 @@ ComponentRegistry.register('form',
36
37
  submitLabel = 'Submit',
37
38
  cancelLabel = 'Cancel',
38
39
  showCancel = false,
40
+ showSubmit = true,
39
41
  layout = 'vertical',
40
42
  columns = 1,
41
43
  onSubmit: onSubmitProp,
42
44
  onChange: onChangeProp,
45
+ onCancel: onCancelProp,
43
46
  resetOnSubmit = false,
44
47
  validationMode = 'onSubmit',
45
48
  disabled = false,
@@ -54,6 +57,11 @@ ComponentRegistry.register('form',
54
57
  const [isSubmitting, setIsSubmitting] = React.useState(false);
55
58
  const [submitError, setSubmitError] = React.useState<string | null>(null);
56
59
 
60
+ // React to defaultValues changes
61
+ React.useEffect(() => {
62
+ form.reset(defaultValues);
63
+ }, [defaultValues]);
64
+
57
65
  // Watch for form changes - only track changes when onAction is available
58
66
  React.useEffect(() => {
59
67
  if (onAction) {
@@ -73,12 +81,34 @@ ComponentRegistry.register('form',
73
81
  setIsSubmitting(true);
74
82
  setSubmitError(null);
75
83
 
84
+ // Defensive check: If data is an Event, use getValues()
85
+ let formData = data;
86
+ // Check for Event-like properties
87
+ const isEvent = data && (
88
+ (data as any).nativeEvent ||
89
+ typeof (data as any).preventDefault === 'function' ||
90
+ typeof (data as any).stopPropagation === 'function' ||
91
+ (data as any).target ||
92
+ (data as any).bubbles
93
+ );
94
+
95
+ if (isEvent) {
96
+ // This should not happen with RHF handleSubmit, but just in case
97
+ formData = form.getValues();
98
+ } else if (!formData || Object.keys(formData).length === 0) {
99
+ // Fallback: if data is empty check getValues(), in case RHF failed to pass it for some reason
100
+ const values = form.getValues();
101
+ if (values && Object.keys(values).length > 0) {
102
+ formData = values;
103
+ }
104
+ }
105
+
76
106
  try {
77
107
  if (onAction) {
78
108
  const result = await onAction({
79
109
  type: 'form_submit',
80
- data,
81
- formData: data,
110
+ data: formData,
111
+ formData: formData,
82
112
  }) as any;
83
113
 
84
114
  // Check if submission returned an error
@@ -89,7 +119,7 @@ ComponentRegistry.register('form',
89
119
  }
90
120
 
91
121
  if (onSubmitProp && typeof onSubmitProp === 'function') {
92
- await onSubmitProp(data);
122
+ await onSubmitProp(formData);
93
123
  }
94
124
 
95
125
  if (resetOnSubmit) {
@@ -117,6 +147,11 @@ ComponentRegistry.register('form',
117
147
  // Handle cancel
118
148
  const handleCancel = () => {
119
149
  form.reset();
150
+
151
+ if (onCancelProp && typeof onCancelProp === 'function') {
152
+ onCancelProp();
153
+ }
154
+
120
155
  if (onAction) {
121
156
  onAction({
122
157
  type: 'form_cancel',
@@ -137,11 +172,21 @@ ComponentRegistry.register('form',
137
172
  ? cn('grid gap-4', gridColsClass)
138
173
  : 'space-y-4';
139
174
 
140
- // Extract designer-related props
175
+ // Extract designer-related props and conflicting handlers
141
176
  const {
142
177
  'data-obj-id': dataObjId,
143
178
  'data-obj-type': dataObjType,
144
- style,
179
+ style,
180
+ onSubmit: _ignoredOnSubmit, // Prevent overwriting our handleSubmit
181
+ onChange: _ignoredOnChange, // Prevent overwriting our onChange
182
+ // Extract schema props that should not be spread to DOM (handled separately by schema destructuring above)
183
+ submitLabel: _submitLabel,
184
+ cancelLabel: _cancelLabel,
185
+ showSubmit: _showSubmit,
186
+ showCancel: _showCancel,
187
+ resetOnSubmit: _resetOnSubmit,
188
+ defaultValues: _defaultValues,
189
+ inputType: _inputType,
145
190
  ...formProps
146
191
  } = props;
147
192
 
@@ -241,6 +286,9 @@ ComponentRegistry.register('form',
241
286
  {/* Render the actual field component based on type */}
242
287
  {renderFieldComponent(type, {
243
288
  ...fieldProps,
289
+ // specialized fields needs raw metadata, but we should traverse down if it exists
290
+ // field is the field configuration loop variable
291
+ field: (field as any).field || field,
244
292
  ...formField,
245
293
  inputType: fieldProps.inputType,
246
294
  options: fieldProps.options,
@@ -273,6 +321,7 @@ ComponentRegistry.register('form',
273
321
  {cancelLabel}
274
322
  </Button>
275
323
  )}
324
+ {showSubmit && (
276
325
  <Button
277
326
  type="submit"
278
327
  disabled={isSubmitting || disabled}
@@ -280,6 +329,7 @@ ComponentRegistry.register('form',
280
329
  {isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
281
330
  {submitLabel}
282
331
  </Button>
332
+ )}
283
333
  </div>
284
334
  )}
285
335
  </form>
@@ -287,6 +337,7 @@ ComponentRegistry.register('form',
287
337
  );
288
338
  },
289
339
  {
340
+ namespace: 'ui',
290
341
  label: 'Form',
291
342
  inputs: [
292
343
  {
@@ -372,26 +423,52 @@ interface RenderFieldProps {
372
423
  }
373
424
 
374
425
  function renderFieldComponent(type: string, props: RenderFieldProps) {
426
+ // 1. Try to resolve specialized field widget from registry first
427
+ // We prioritize registry components (e.g., 'field.currency', 'field.date')
428
+ const RegisteredComponent = ComponentRegistry.get(type);
429
+
430
+ if (RegisteredComponent) {
431
+ // For specialized fields (e.g. fields package), they expect 'field' prop.
432
+ // Ensure we pass all props.
433
+ // Also pass 'schema' for standard renderers that expect it
434
+ return <RegisteredComponent schema={props} {...props} />;
435
+ }
436
+
375
437
  const { inputType, options = [], placeholder, ...fieldProps } = props;
376
438
 
377
439
  switch (type) {
378
440
  case 'input':
379
- return <Input type={inputType || 'text'} placeholder={placeholder} {...fieldProps} />;
380
-
441
+ if (inputType === 'file') {
442
+ // File inputs cannot be controlled with value prop
443
+ const { value, ...fileProps } = fieldProps;
444
+ return <Input type="file" placeholder={placeholder} {...fileProps} />;
445
+ }
446
+ return <Input type={inputType || 'text'} placeholder={placeholder} {...fieldProps} value={fieldProps.value ?? ''} />;
447
+
381
448
  case 'textarea':
382
- return <Textarea placeholder={placeholder} {...fieldProps} />;
449
+ return <Textarea placeholder={placeholder} {...fieldProps} value={fieldProps.value ?? ''} />;
383
450
 
384
451
  case 'checkbox': {
385
452
  // For checkbox, we need to handle the value differently
386
453
  const { value, onChange, ...checkboxProps } = fieldProps;
387
454
  return (
388
- <div className="flex items-center space-x-2">
389
- <Checkbox
390
- checked={value}
391
- onCheckedChange={onChange}
392
- {...checkboxProps}
393
- />
394
- </div>
455
+ <Checkbox
456
+ checked={value}
457
+ onCheckedChange={onChange}
458
+ {...checkboxProps}
459
+ />
460
+ );
461
+ }
462
+
463
+ case 'switch': {
464
+ // For switch, we need to handle the value differently (same as checkbox)
465
+ const { value, onChange, ...switchProps } = fieldProps;
466
+ return (
467
+ <Switch
468
+ checked={value}
469
+ onCheckedChange={onChange}
470
+ {...switchProps}
471
+ />
395
472
  );
396
473
  }
397
474
 
@@ -38,6 +38,7 @@ ComponentRegistry.register('input-otp',
38
38
  );
39
39
  },
40
40
  {
41
+ namespace: 'ui',
41
42
  label: 'Input OTP',
42
43
  inputs: [
43
44
  { name: 'maxLength', type: 'number', label: 'Max Length', defaultValue: 6 },
@@ -63,6 +63,7 @@ const InputRenderer = ({ schema, className, onChange, value, ...props }: { schem
63
63
  };
64
64
 
65
65
  ComponentRegistry.register('input', InputRenderer, {
66
+ namespace: 'ui',
66
67
  label: 'Input Field',
67
68
  inputs: [
68
69
  { name: 'label', type: 'string', label: 'Label' },
@@ -88,6 +89,7 @@ ComponentRegistry.register('input', InputRenderer, {
88
89
  ComponentRegistry.register('email',
89
90
  (props: any) => <InputRenderer {...props} schema={{ ...props.schema, inputType: 'email' }} />,
90
91
  {
92
+ namespace: 'ui',
91
93
  label: 'Email Input',
92
94
  icon: 'mail',
93
95
  inputs: [
@@ -104,6 +106,7 @@ ComponentRegistry.register('email',
104
106
  ComponentRegistry.register('password',
105
107
  (props: any) => <InputRenderer {...props} schema={{ ...props.schema, inputType: 'password' }} />,
106
108
  {
109
+ namespace: 'ui',
107
110
  label: 'Password Input',
108
111
  icon: 'lock',
109
112
  inputs: [
@@ -32,6 +32,7 @@ ComponentRegistry.register('label',
32
32
  );
33
33
  },
34
34
  {
35
+ namespace: 'ui',
35
36
  label: 'Label',
36
37
  inputs: [
37
38
  { name: 'text', type: 'string', label: 'Text', required: true },
@@ -38,6 +38,7 @@ ComponentRegistry.register('radio-group',
38
38
  );
39
39
  },
40
40
  {
41
+ namespace: 'ui',
41
42
  label: 'Radio Group',
42
43
  inputs: [
43
44
  { name: 'defaultValue', type: 'string', label: 'Default Value' },
@@ -16,44 +16,64 @@ import {
16
16
  SelectItem,
17
17
  Label
18
18
  } from '../../ui';
19
+ import { cn } from '../../lib/utils';
20
+ import React from 'react';
19
21
 
20
- ComponentRegistry.register('select',
21
- ({ schema, className, ...props }: { schema: SelectSchema; className?: string; [key: string]: any }) => {
22
- // Extract designer-related props
23
- const {
24
- 'data-obj-id': dataObjId,
25
- 'data-obj-type': dataObjType,
26
- style,
27
- ...selectProps
28
- } = props;
22
+ const SelectRenderer = ({ schema, className, onChange, value, ...props }: { schema: SelectSchema; className?: string; onChange?: (val: any) => void; value?: any; [key: string]: any }) => {
23
+ // Extract designer-related props
24
+ const {
25
+ 'data-obj-id': dataObjId,
26
+ 'data-obj-type': dataObjType,
27
+ style,
28
+ ...selectProps
29
+ } = props;
29
30
 
30
- return (
31
+ const handleValueChange = (newValue: string) => {
32
+ if (onChange) {
33
+ onChange(newValue);
34
+ }
35
+ };
36
+
37
+ return (
31
38
  <div
32
- className={`grid w-full max-w-sm items-center gap-1.5 ${schema.wrapperClass || ''}`}
39
+ className={cn("grid w-full items-center gap-1.5", schema.wrapperClass)}
33
40
  data-obj-id={dataObjId}
34
41
  data-obj-type={dataObjType}
35
42
  style={style}
36
43
  >
37
- {schema.label && <Label>{schema.label}</Label>}
38
- <Select defaultValue={schema.defaultValue} {...selectProps}>
44
+ {schema.label && <Label className={cn(schema.required && "text-destructive after:content-['*'] after:ml-0.5")}>{schema.label}</Label>}
45
+ <Select
46
+ defaultValue={value === undefined ? schema.defaultValue : undefined}
47
+ value={value ?? schema.value}
48
+ onValueChange={handleValueChange}
49
+ disabled={schema.disabled}
50
+ required={schema.required}
51
+ name={schema.name}
52
+ {...selectProps}
53
+ >
39
54
  <SelectTrigger className={className}>
40
55
  <SelectValue placeholder={schema.placeholder} />
41
56
  </SelectTrigger>
42
57
  <SelectContent>
43
58
  {schema.options?.map((opt) => (
44
- <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
59
+ <SelectItem key={opt.value} value={opt.value} disabled={opt.disabled}>{opt.label}</SelectItem>
45
60
  ))}
46
61
  </SelectContent>
47
62
  </Select>
48
63
  </div>
49
64
  );
50
- },
65
+ };
66
+
67
+ ComponentRegistry.register('select', SelectRenderer,
51
68
  {
69
+ namespace: 'ui',
52
70
  label: 'Select',
53
71
  inputs: [
54
72
  { name: 'label', type: 'string', label: 'Label' },
55
73
  { name: 'placeholder', type: 'string', label: 'Placeholder' },
56
74
  { name: 'defaultValue', type: 'string', label: 'Default Value' },
75
+ { name: 'required', type: 'boolean', label: 'Required' },
76
+ { name: 'disabled', type: 'boolean', label: 'Disabled' },
57
77
  {
58
78
  name: 'options',
59
79
  type: 'array',
@@ -41,6 +41,7 @@ ComponentRegistry.register('slider',
41
41
  );
42
42
  },
43
43
  {
44
+ namespace: 'ui',
44
45
  label: 'Slider',
45
46
  inputs: [
46
47
  { name: 'defaultValue', type: 'array', label: 'Default Value', defaultValue: [50] },
@@ -33,6 +33,7 @@ ComponentRegistry.register('switch',
33
33
  );
34
34
  },
35
35
  {
36
+ namespace: 'ui',
36
37
  label: 'Switch',
37
38
  inputs: [
38
39
  { name: 'label', type: 'string', label: 'Label', required: true },
@@ -9,45 +9,68 @@
9
9
  import { ComponentRegistry } from '@object-ui/core';
10
10
  import type { TextareaSchema } from '@object-ui/types';
11
11
  import { Textarea, Label } from '../../ui';
12
+ import { cn } from '../../lib/utils';
13
+ import React from 'react';
12
14
 
13
- ComponentRegistry.register('textarea',
14
- ({ schema, className, ...props }: { schema: TextareaSchema; className?: string; [key: string]: any }) => {
15
- // Extract designer-related props
16
- const {
17
- 'data-obj-id': dataObjId,
18
- 'data-obj-type': dataObjType,
19
- style,
20
- ...inputProps
21
- } = props;
15
+ const TextareaRenderer = ({ schema, className, onChange, value, ...props }: { schema: TextareaSchema; className?: string; onChange?: (val: any) => void; value?: any; [key: string]: any }) => {
16
+ // Handle change for both raw inputs and form-bound inputs
17
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
18
+ if (onChange) {
19
+ onChange(e.target.value);
20
+ }
21
+ };
22
+
23
+ // Extract designer-related props
24
+ const {
25
+ 'data-obj-id': dataObjId,
26
+ 'data-obj-type': dataObjType,
27
+ style,
28
+ ...inputProps
29
+ } = props;
30
+
31
+ return (
32
+ <div
33
+ className={cn("grid w-full gap-1.5", schema.wrapperClass)}
34
+ data-obj-id={dataObjId}
35
+ data-obj-type={dataObjType}
36
+ style={style}
37
+ >
38
+ {schema.label && <Label htmlFor={schema.id} className={cn(schema.required && "text-destructive after:content-['*'] after:ml-0.5")}>{schema.label}</Label>}
39
+ <Textarea
40
+ id={schema.id}
41
+ name={schema.name}
42
+ placeholder={schema.placeholder}
43
+ className={className}
44
+ disabled={schema.disabled}
45
+ readOnly={schema.readOnly}
46
+ required={schema.required}
47
+ rows={schema.rows}
48
+ value={value ?? schema.value ?? ''}
49
+ defaultValue={value === undefined ? schema.defaultValue : undefined}
50
+ onChange={handleChange}
51
+ {...inputProps}
52
+ />
53
+ </div>
54
+ );
55
+ };
22
56
 
23
- return (
24
- <div
25
- className={`grid w-full gap-1.5 ${schema.wrapperClass || ''}`}
26
- data-obj-id={dataObjId}
27
- data-obj-type={dataObjType}
28
- style={style}
29
- >
30
- {schema.label && <Label htmlFor={schema.id}>{schema.label}</Label>}
31
- <Textarea
32
- id={schema.id}
33
- placeholder={schema.placeholder}
34
- className={className}
35
- {...inputProps}
36
- />
37
- </div>
38
- );
39
- },
57
+ ComponentRegistry.register('textarea', TextareaRenderer,
40
58
  {
59
+ namespace: 'ui',
41
60
  label: 'Textarea',
42
61
  inputs: [
43
62
  { name: 'label', type: 'string', label: 'Label' },
44
63
  { name: 'placeholder', type: 'string', label: 'Placeholder' },
64
+ { name: 'rows', type: 'number', label: 'Rows' },
65
+ { name: 'required', type: 'boolean', label: 'Required' },
66
+ { name: 'disabled', type: 'boolean', label: 'Disabled' },
45
67
  { name: 'id', type: 'string', label: 'ID', required: true }
46
68
  ],
47
69
  defaultProps: {
48
70
  label: 'Textarea label',
49
71
  placeholder: 'Enter text here...',
50
- id: 'textarea-field' // Will be made unique by designer's ensureNodeIds
72
+ rows: 3,
73
+ id: 'textarea-field'
51
74
  }
52
75
  }
53
76
  );
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { ComponentRegistry } from '@object-ui/core';
10
10
  import type { ToggleSchema } from '@object-ui/types';
11
- import { Toggle, ToggleGroup, ToggleGroupItem } from '../../ui';
11
+ import { Toggle } from '../../ui';
12
12
  import { renderChildren } from '../../lib/utils';
13
13
 
14
14
  ComponentRegistry.register('toggle',
@@ -24,13 +24,14 @@ ComponentRegistry.register('toggle',
24
24
  </Toggle>
25
25
  ),
26
26
  {
27
+ namespace: 'ui',
27
28
  label: 'Toggle',
28
29
  inputs: [
29
30
  { name: 'label', type: 'string', label: 'Label' },
30
31
  { name: 'pressed', type: 'boolean', label: 'Pressed' },
31
32
  { name: 'variant', type: 'enum', enum: ['default', 'outline'], defaultValue: 'default', label: 'Variant' },
32
33
  { name: 'size', type: 'enum', enum: ['default', 'sm', 'lg'], defaultValue: 'default', label: 'Size' },
33
- { name: 'ariaLabel', type: 'string', label: 'Aria Label' }
34
+ { name: 'ariaLabel', type: 'string', label: 'Aria Label' }
34
35
  ],
35
36
  defaultProps: {
36
37
  label: 'Toggle',
@@ -39,46 +40,3 @@ ComponentRegistry.register('toggle',
39
40
  }
40
41
  }
41
42
  );
42
-
43
- ComponentRegistry.register('toggle-group',
44
- ({ schema, className, ...props }) => (
45
- <ToggleGroup
46
- type={schema.groupType || 'single'}
47
- variant={schema.variant}
48
- size={schema.size}
49
- className={className}
50
- {...props}
51
- >
52
- {schema.items?.map((item: any) => (
53
- <ToggleGroupItem key={item.value} value={item.value} aria-label={item.label}>
54
- {item.icon || item.label}
55
- </ToggleGroupItem>
56
- ))}
57
- </ToggleGroup>
58
- ),
59
- {
60
- label: 'Toggle Group',
61
- inputs: [
62
- { name: 'groupType', type: 'enum', enum: ['single', 'multiple'], defaultValue: 'single', label: 'Type' },
63
- { name: 'variant', type: 'enum', enum: ['default', 'outline'], defaultValue: 'default', label: 'Variant' },
64
- { name: 'size', type: 'enum', enum: ['default', 'sm', 'lg'], defaultValue: 'default', label: 'Size' },
65
- {
66
- name: 'items',
67
- type: 'array',
68
- label: 'Items',
69
- description: 'Array of {label, value, icon?} objects'
70
- },
71
- { name: 'className', type: 'string', label: 'CSS Class' }
72
- ],
73
- defaultProps: {
74
- groupType: 'single',
75
- variant: 'default',
76
- size: 'default',
77
- items: [
78
- { label: 'A', value: 'a' },
79
- { label: 'B', value: 'b' },
80
- { label: 'C', value: 'c' }
81
- ]
82
- }
83
- }
84
- );
@@ -30,12 +30,13 @@ ComponentRegistry.register('aspect-ratio',
30
30
  {schema.image ? (
31
31
  <img src={schema.image} alt={schema.alt || ''} className="rounded-md object-cover w-full h-full" />
32
32
  ) : (
33
- renderChildren(schema.body)
33
+ renderChildren(schema.children || schema.body)
34
34
  )}
35
35
  </AspectRatio>
36
36
  );
37
37
  },
38
38
  {
39
+ namespace: 'ui',
39
40
  label: 'Aspect Ratio',
40
41
  inputs: [
41
42
  { name: 'ratio', type: 'number', label: 'Ratio', defaultValue: 16/9 },
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { ComponentRegistry } from '@object-ui/core';
10
10
  import type { CardSchema } from '@object-ui/types';
11
- import { renderChildren } from '../../lib/utils';
11
+ import { renderChildren, cn } from '../../lib/utils';
12
12
  import {
13
13
  Card,
14
14
  CardHeader,
@@ -28,11 +28,18 @@ const CardRenderer = forwardRef<HTMLDivElement, { schema: CardSchema; className?
28
28
  style,
29
29
  ...cardProps
30
30
  } = props;
31
+
32
+ const isClickable = schema.clickable || !!props.onClick;
33
+ const isHoverable = schema.hoverable || isClickable;
31
34
 
32
35
  return (
33
36
  <Card
34
37
  ref={ref}
35
- className={className}
38
+ className={cn(
39
+ className,
40
+ isHoverable && "transition-colors hover:bg-muted/50",
41
+ isClickable && "cursor-pointer active:bg-muted"
42
+ )}
36
43
  {...cardProps}
37
44
  // Apply designer props
38
45
  {...{ 'data-obj-id': dataObjId, 'data-obj-type': dataObjType, style }}
@@ -54,6 +61,7 @@ const CardRenderer = forwardRef<HTMLDivElement, { schema: CardSchema; className?
54
61
  ComponentRegistry.register('card',
55
62
  CardRenderer,
56
63
  {
64
+ namespace: 'ui',
57
65
  label: 'Card',
58
66
  inputs: [
59
67
  { name: 'title', type: 'string', label: 'Title' },
@@ -77,6 +77,7 @@ const ContainerRenderer = forwardRef<HTMLDivElement, { schema: ContainerSchema;
77
77
  ComponentRegistry.register('container',
78
78
  ContainerRenderer,
79
79
  {
80
+ namespace: 'ui',
80
81
  label: 'Container',
81
82
  inputs: [
82
83
  {
@@ -76,6 +76,7 @@ ComponentRegistry.register('flex',
76
76
  );
77
77
  },
78
78
  {
79
+ namespace: 'ui',
79
80
  label: 'Flex Layout',
80
81
  inputs: [
81
82
  {
@@ -50,13 +50,27 @@ const GAPS: Record<number, string> = {
50
50
  ComponentRegistry.register('grid',
51
51
  ({ schema, className, ...props }: { schema: GridSchema & { smColumns?: number, mdColumns?: number, lgColumns?: number, xlColumns?: number }; className?: string; [key: string]: any }) => {
52
52
  // Determine columns configuration
53
- // Supports direct number or responsive object logic if schema allows,
54
- // but here we primarily handle the flat properties supported by the designer inputs
55
- const baseCols = typeof schema.columns === 'number' ? schema.columns : 2;
56
- const smCols = schema.smColumns;
57
- const mdCols = schema.mdColumns;
58
- const lgCols = schema.lgColumns;
59
- const xlCols = schema.xlColumns;
53
+ // Supports detailed object configuration from schema
54
+ let baseCols = 2;
55
+ let smCols, mdCols, lgCols, xlCols;
56
+
57
+ if (typeof schema.columns === 'number') {
58
+ baseCols = schema.columns;
59
+ } else if (typeof schema.columns === 'object' && schema.columns !== null) {
60
+ // Handle responsive object: { xs: 1, sm: 2, md: 3, lg: 4 }
61
+ // Note: 'xs' corresponds to base (mobile-first)
62
+ baseCols = schema.columns.xs ?? 1;
63
+ smCols = schema.columns.sm;
64
+ mdCols = schema.columns.md;
65
+ lgCols = schema.columns.lg;
66
+ xlCols = schema.columns.xl;
67
+ }
68
+
69
+ // Fallback to legacy flat props if provided (from designer)
70
+ if (schema.smColumns) smCols = schema.smColumns;
71
+ if (schema.mdColumns) mdCols = schema.mdColumns;
72
+ if (schema.lgColumns) lgCols = schema.lgColumns;
73
+ if (schema.xlColumns) xlCols = schema.xlColumns;
60
74
 
61
75
  const gap = schema.gap ?? 4;
62
76
 
@@ -71,7 +85,7 @@ ComponentRegistry.register('grid',
71
85
  lgCols && GRID_COLS_LG[lgCols],
72
86
  xlCols && GRID_COLS_XL[xlCols],
73
87
  // Gap
74
- GAPS[gap] || 'gap-4',
88
+ GAPS[gap] || `gap-[${gap * 0.25}rem]`, // Fallback for arbitrary values if not in map
75
89
  className
76
90
  );
77
91
 
@@ -97,6 +111,7 @@ ComponentRegistry.register('grid',
97
111
  );
98
112
  },
99
113
  {
114
+ namespace: 'ui',
100
115
  label: 'Grid Layout',
101
116
  inputs: [
102
117
  {