@@ -15,12 +15,15 @@ import '@object-ui/fields'; // Register field widgets
1515import '@object-ui/plugin-dashboard' ; // Register dashboard components
1616import '@object-ui/plugin-grid' ; // Register grid components
1717
18- // Manually re-register basic text component to override field widget
19- // This is necessary because @object -ui/fields has @object-ui/components as a dependency,
20- // so components gets loaded BEFORE fields registers its widgets, causing fields to overwrite.
18+ // Manually re-register UI components to override field widgets and plugin components
19+ // This is necessary because @object -ui/fields and plugins have @object-ui/components as a dependency,
20+ // so components gets loaded BEFORE fields/plugins register their widgets, causing them to overwrite.
2121import { ComponentRegistry } from '@object-ui/core' ;
22- import type { TextSchema } from '@object-ui/types' ;
22+ import type { TextSchema , InputSchema , TextareaSchema , ImageSchema , HtmlSchema , AvatarSchema , SelectSchema , SliderSchema , GridSchema } from '@object-ui/types' ;
23+ import { Input , Textarea , Label , Avatar , AvatarImage , AvatarFallback , Select , SelectTrigger , SelectValue , SelectContent , SelectItem , Slider } from '@object-ui/components' ;
24+ import { cn , renderChildren } from '@object-ui/components' ;
2325
26+ // Re-register text component
2427ComponentRegistry . register ( 'text' ,
2528 ( { schema, ...props } : { schema : TextSchema ; [ key : string ] : any } ) => {
2629 const {
@@ -52,6 +55,354 @@ ComponentRegistry.register('text',
5255 }
5356) ;
5457
58+ // Re-register email component
59+ const InputRenderer = ( { schema, className, onChange, value, ...props } : { schema : InputSchema ; className ?: string ; onChange ?: ( val : any ) => void ; value ?: any ; [ key : string ] : any } ) => {
60+ const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
61+ if ( onChange ) {
62+ onChange ( e . target . value ) ;
63+ }
64+ } ;
65+
66+ const {
67+ 'data-obj-id' : dataObjId ,
68+ 'data-obj-type' : dataObjType ,
69+ style,
70+ ...inputProps
71+ } = props ;
72+
73+ return (
74+ < div
75+ className = { cn ( "grid w-full items-center gap-1.5" , schema . wrapperClass ) }
76+ data-obj-id = { dataObjId }
77+ data-obj-type = { dataObjType }
78+ style = { style }
79+ >
80+ { schema . label && < Label htmlFor = { schema . id } className = { cn ( schema . required && "text-destructive after:content-['*'] after:ml-0.5" ) } > { schema . label } </ Label > }
81+ < Input
82+ type = { schema . inputType || 'text' }
83+ id = { schema . id }
84+ name = { schema . name }
85+ placeholder = { schema . placeholder }
86+ className = { className }
87+ required = { schema . required }
88+ disabled = { schema . disabled }
89+ readOnly = { schema . readOnly }
90+ value = { value ?? schema . value ?? '' }
91+ defaultValue = { value === undefined ? schema . defaultValue : undefined }
92+ onChange = { handleChange }
93+ { ...inputProps }
94+ />
95+ </ div >
96+ ) ;
97+ } ;
98+
99+ ComponentRegistry . register ( 'email' ,
100+ ( props : any ) => < InputRenderer { ...props } schema = { { ...props . schema , inputType : 'email' } } /> ,
101+ {
102+ namespace : 'ui' ,
103+ label : 'Email Input' ,
104+ }
105+ ) ;
106+
107+ // Re-register password component
108+ ComponentRegistry . register ( 'password' ,
109+ ( props : any ) => < InputRenderer { ...props } schema = { { ...props . schema , inputType : 'password' } } /> ,
110+ {
111+ namespace : 'ui' ,
112+ label : 'Password Input' ,
113+ }
114+ ) ;
115+
116+ // Re-register textarea component
117+ ComponentRegistry . register ( 'textarea' ,
118+ ( { schema, className, onChange, value, ...props } : { schema : TextareaSchema ; className ?: string ; onChange ?: ( val : any ) => void ; value ?: any ; [ key : string ] : any } ) => {
119+ const handleChange = ( e : React . ChangeEvent < HTMLTextAreaElement > ) => {
120+ if ( onChange ) {
121+ onChange ( e . target . value ) ;
122+ }
123+ } ;
124+
125+ const {
126+ 'data-obj-id' : dataObjId ,
127+ 'data-obj-type' : dataObjType ,
128+ style,
129+ ...inputProps
130+ } = props ;
131+
132+ return (
133+ < div
134+ className = { cn ( "grid w-full gap-1.5" , schema . wrapperClass ) }
135+ data-obj-id = { dataObjId }
136+ data-obj-type = { dataObjType }
137+ style = { style }
138+ >
139+ { schema . label && < Label htmlFor = { schema . id } className = { cn ( schema . required && "text-destructive after:content-['*'] after:ml-0.5" ) } > { schema . label } </ Label > }
140+ < Textarea
141+ id = { schema . id }
142+ name = { schema . name }
143+ placeholder = { schema . placeholder }
144+ className = { className }
145+ disabled = { schema . disabled }
146+ readOnly = { schema . readOnly }
147+ required = { schema . required }
148+ rows = { schema . rows }
149+ value = { value ?? schema . value ?? '' }
150+ defaultValue = { value === undefined ? schema . defaultValue : undefined }
151+ onChange = { handleChange }
152+ { ...inputProps }
153+ />
154+ </ div >
155+ ) ;
156+ } ,
157+ {
158+ namespace : 'ui' ,
159+ label : 'Textarea' ,
160+ }
161+ ) ;
162+
163+ // Re-register image component
164+ ComponentRegistry . register ( 'image' ,
165+ ( { schema, className, ...props } : { schema : ImageSchema ; className ?: string ; [ key : string ] : any } ) => {
166+ const {
167+ 'data-obj-id' : dataObjId ,
168+ 'data-obj-type' : dataObjType ,
169+ style,
170+ ...imgProps
171+ } = props ;
172+
173+ return (
174+ < img
175+ src = { schema . src }
176+ alt = { schema . alt || '' }
177+ className = { className }
178+ { ...imgProps }
179+ data-obj-id = { dataObjId }
180+ data-obj-type = { dataObjType }
181+ style = { style }
182+ />
183+ ) ;
184+ } ,
185+ {
186+ namespace : 'ui' ,
187+ label : 'Image' ,
188+ }
189+ ) ;
190+
191+ // Re-register html component
192+ ComponentRegistry . register ( 'html' ,
193+ ( { schema, className, ...props } : { schema : HtmlSchema ; className ?: string ; [ key : string ] : any } ) => {
194+ const {
195+ 'data-obj-id' : dataObjId ,
196+ 'data-obj-type' : dataObjType ,
197+ style,
198+ ...htmlProps
199+ } = props ;
200+
201+ return (
202+ < div
203+ className = { cn ( "prose prose-sm max-w-none dark:prose-invert" , className ) }
204+ dangerouslySetInnerHTML = { { __html : schema . html } }
205+ { ...htmlProps }
206+ data-obj-id = { dataObjId }
207+ data-obj-type = { dataObjType }
208+ style = { style }
209+ />
210+ ) ;
211+ } ,
212+ {
213+ namespace : 'ui' ,
214+ label : 'HTML Content' ,
215+ }
216+ ) ;
217+
218+ // Re-register avatar component
219+ ComponentRegistry . register ( 'avatar' ,
220+ ( { schema, className, ...props } : { schema : AvatarSchema ; className ?: string ; [ key : string ] : any } ) => (
221+ < Avatar className = { className } { ...props } >
222+ < AvatarImage src = { schema . src } alt = { schema . alt } />
223+ < AvatarFallback > { schema . fallback } </ AvatarFallback >
224+ </ Avatar >
225+ ) ,
226+ {
227+ namespace : 'ui' ,
228+ label : 'Avatar' ,
229+ }
230+ ) ;
231+
232+ // Re-register select component
233+ ComponentRegistry . register ( 'select' ,
234+ ( { schema, className, onChange, value, ...props } : { schema : SelectSchema ; className ?: string ; onChange ?: ( val : any ) => void ; value ?: any ; [ key : string ] : any } ) => {
235+ const {
236+ 'data-obj-id' : dataObjId ,
237+ 'data-obj-type' : dataObjType ,
238+ style,
239+ ...selectProps
240+ } = props ;
241+
242+ const handleValueChange = ( newValue : string ) => {
243+ if ( onChange ) {
244+ onChange ( newValue ) ;
245+ }
246+ } ;
247+
248+ return (
249+ < div
250+ className = { cn ( "grid w-full items-center gap-1.5" , schema . wrapperClass ) }
251+ data-obj-id = { dataObjId }
252+ data-obj-type = { dataObjType }
253+ style = { style }
254+ >
255+ { schema . label && < Label className = { cn ( schema . required && "text-destructive after:content-['*'] after:ml-0.5" ) } > { schema . label } </ Label > }
256+ < Select
257+ defaultValue = { value === undefined ? schema . defaultValue : undefined }
258+ value = { value ?? schema . value }
259+ onValueChange = { handleValueChange }
260+ disabled = { schema . disabled }
261+ required = { schema . required }
262+ name = { schema . name }
263+ { ...selectProps }
264+ >
265+ < SelectTrigger className = { className } >
266+ < SelectValue placeholder = { schema . placeholder } />
267+ </ SelectTrigger >
268+ < SelectContent >
269+ { schema . options ?. map ( ( opt ) => (
270+ < SelectItem key = { opt . value } value = { opt . value } disabled = { opt . disabled } > { opt . label } </ SelectItem >
271+ ) ) }
272+ </ SelectContent >
273+ </ Select >
274+ </ div >
275+ ) ;
276+ } ,
277+ {
278+ namespace : 'ui' ,
279+ label : 'Select' ,
280+ }
281+ ) ;
282+
283+ // Re-register slider component
284+ ComponentRegistry . register ( 'slider' ,
285+ ( { schema, className, ...props } : { schema : SliderSchema ; className ?: string ; [ key : string ] : any } ) => {
286+ const {
287+ 'data-obj-id' : dataObjId ,
288+ 'data-obj-type' : dataObjType ,
289+ style,
290+ ...sliderProps
291+ } = props ;
292+
293+ const defaultValue = Array . isArray ( schema . defaultValue )
294+ ? schema . defaultValue
295+ : schema . defaultValue !== undefined
296+ ? [ schema . defaultValue ]
297+ : undefined ;
298+
299+ return (
300+ < Slider
301+ defaultValue = { defaultValue }
302+ max = { schema . max }
303+ min = { schema . min }
304+ step = { schema . step }
305+ className = { className }
306+ { ...sliderProps }
307+ { ...{ 'data-obj-id' : dataObjId , 'data-obj-type' : dataObjType , style } }
308+ />
309+ ) ;
310+ } ,
311+ {
312+ namespace : 'ui' ,
313+ label : 'Slider' ,
314+ }
315+ ) ;
316+
317+ // Re-register grid component to override plugin-grid's registration
318+ ComponentRegistry . register ( 'grid' ,
319+ ( { schema, className, ...props } : { schema : GridSchema & { smColumns ?: number , mdColumns ?: number , lgColumns ?: number , xlColumns ?: number } ; className ?: string ; [ key : string ] : any } ) => {
320+ let baseCols = 2 ;
321+ let smCols , mdCols , lgCols , xlCols ;
322+
323+ if ( typeof schema . columns === 'number' ) {
324+ baseCols = schema . columns ;
325+ } else if ( typeof schema . columns === 'object' && schema . columns !== null ) {
326+ baseCols = schema . columns . xs ?? 1 ;
327+ smCols = schema . columns . sm ;
328+ mdCols = schema . columns . md ;
329+ lgCols = schema . columns . lg ;
330+ xlCols = schema . columns . xl ;
331+ }
332+
333+ if ( schema . smColumns ) smCols = schema . smColumns ;
334+ if ( schema . mdColumns ) mdCols = schema . mdColumns ;
335+ if ( schema . lgColumns ) lgCols = schema . lgColumns ;
336+ if ( schema . xlColumns ) xlCols = schema . xlColumns ;
337+
338+ const gap = schema . gap ?? 4 ;
339+
340+ const GRID_COLS : Record < number , string > = {
341+ 1 : 'grid-cols-1' , 2 : 'grid-cols-2' , 3 : 'grid-cols-3' , 4 : 'grid-cols-4' ,
342+ 5 : 'grid-cols-5' , 6 : 'grid-cols-6' , 7 : 'grid-cols-7' , 8 : 'grid-cols-8' ,
343+ 9 : 'grid-cols-9' , 10 : 'grid-cols-10' , 11 : 'grid-cols-11' , 12 : 'grid-cols-12'
344+ } ;
345+ const GRID_COLS_SM : Record < number , string > = {
346+ 1 : 'sm:grid-cols-1' , 2 : 'sm:grid-cols-2' , 3 : 'sm:grid-cols-3' , 4 : 'sm:grid-cols-4' ,
347+ 5 : 'sm:grid-cols-5' , 6 : 'sm:grid-cols-6' , 7 : 'sm:grid-cols-7' , 8 : 'sm:grid-cols-8' ,
348+ 9 : 'sm:grid-cols-9' , 10 : 'sm:grid-cols-10' , 11 : 'sm:grid-cols-11' , 12 : 'sm:grid-cols-12'
349+ } ;
350+ const GRID_COLS_MD : Record < number , string > = {
351+ 1 : 'md:grid-cols-1' , 2 : 'md:grid-cols-2' , 3 : 'md:grid-cols-3' , 4 : 'md:grid-cols-4' ,
352+ 5 : 'md:grid-cols-5' , 6 : 'md:grid-cols-6' , 7 : 'md:grid-cols-7' , 8 : 'md:grid-cols-8' ,
353+ 9 : 'md:grid-cols-9' , 10 : 'md:grid-cols-10' , 11 : 'md:grid-cols-11' , 12 : 'md:grid-cols-12'
354+ } ;
355+ const GRID_COLS_LG : Record < number , string > = {
356+ 1 : 'lg:grid-cols-1' , 2 : 'lg:grid-cols-2' , 3 : 'lg:grid-cols-3' , 4 : 'lg:grid-cols-4' ,
357+ 5 : 'lg:grid-cols-5' , 6 : 'lg:grid-cols-6' , 7 : 'lg:grid-cols-7' , 8 : 'lg:grid-cols-8' ,
358+ 9 : 'lg:grid-cols-9' , 10 : 'lg:grid-cols-10' , 11 : 'lg:grid-cols-11' , 12 : 'lg:grid-cols-12'
359+ } ;
360+ const GRID_COLS_XL : Record < number , string > = {
361+ 1 : 'xl:grid-cols-1' , 2 : 'xl:grid-cols-2' , 3 : 'xl:grid-cols-3' , 4 : 'xl:grid-cols-4' ,
362+ 5 : 'xl:grid-cols-5' , 6 : 'xl:grid-cols-6' , 7 : 'xl:grid-cols-7' , 8 : 'xl:grid-cols-8' ,
363+ 9 : 'xl:grid-cols-9' , 10 : 'xl:grid-cols-10' , 11 : 'xl:grid-cols-11' , 12 : 'xl:grid-cols-12'
364+ } ;
365+ const GAPS : Record < number , string > = {
366+ 0 : 'gap-0' , 1 : 'gap-1' , 2 : 'gap-2' , 3 : 'gap-3' , 4 : 'gap-4' ,
367+ 5 : 'gap-5' , 6 : 'gap-6' , 8 : 'gap-8' , 10 : 'gap-10' , 12 : 'gap-12'
368+ } ;
369+
370+ const gridClass = cn (
371+ 'grid' ,
372+ GRID_COLS [ baseCols ] || 'grid-cols-2' ,
373+ smCols && GRID_COLS_SM [ smCols ] ,
374+ mdCols && GRID_COLS_MD [ mdCols ] ,
375+ lgCols && GRID_COLS_LG [ lgCols ] ,
376+ xlCols && GRID_COLS_XL [ xlCols ] ,
377+ GAPS [ gap ] || `gap-[${ gap * 0.25 } rem]` ,
378+ className
379+ ) ;
380+
381+ const {
382+ 'data-obj-id' : dataObjId ,
383+ 'data-obj-type' : dataObjType ,
384+ style,
385+ ...gridProps
386+ } = props ;
387+
388+ return (
389+ < div
390+ className = { gridClass }
391+ { ...gridProps }
392+ data-obj-id = { dataObjId }
393+ data-obj-type = { dataObjType }
394+ style = { style }
395+ >
396+ { schema . children && renderChildren ( schema . children ) }
397+ </ div >
398+ ) ;
399+ } ,
400+ {
401+ namespace : 'ui' ,
402+ label : 'Grid Layout' ,
403+ }
404+ ) ;
405+
55406// Polyfill ResizeObserver for Radix UI (Shadcn) components
56407global . ResizeObserver = class ResizeObserver {
57408 observe ( ) { }
0 commit comments