1- import React from 'react' ;
2- import { Row , Col } from 'antd' ;
3- import { ObjectFieldTemplateProps , getTemplate , getUiOptions , descriptionId , titleId , canExpand } from '@rjsf/utils' ;
4- import { ConfigConsumer } from 'antd/es/config-provider/context' ;
1+ import React , { useEffect , useRef , useState } from "react" ;
2+ import { Row , Col } from "antd" ;
3+ import {
4+ ObjectFieldTemplateProps ,
5+ getTemplate ,
6+ getUiOptions ,
7+ descriptionId ,
8+ titleId ,
9+ canExpand ,
10+ } from "@rjsf/utils" ;
11+ import { ConfigConsumer } from "antd/es/config-provider/context" ;
512
613const DESCRIPTION_COL_STYLE = {
7- paddingBottom : ' 8px' ,
14+ paddingBottom : " 8px" ,
815} ;
916
17+ interface ColSpan {
18+ xs : number ;
19+ sm : number ;
20+ md : number ;
21+ lg : number ;
22+ xl : number ;
23+ }
24+
25+ interface UiOptions {
26+ colSpan : ColSpan ;
27+ rowGutter : number ;
28+ // other properties...
29+ }
30+
1031const ObjectFieldTemplate = ( props : ObjectFieldTemplateProps ) => {
1132 const {
1233 title,
@@ -22,80 +43,204 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
2243 registry,
2344 } = props ;
2445
46+ const containerRef = useRef < HTMLDivElement > ( null ) ;
47+ const [ containerWidth , setContainerWidth ] = useState ( 0 ) ;
48+
49+ // Monitor the container's width
50+ useEffect ( ( ) => {
51+ const updateWidth = ( ) => {
52+ if ( containerRef . current ) {
53+ setContainerWidth ( containerRef . current . offsetWidth ) ;
54+ }
55+ } ;
56+
57+ // Create a ResizeObserver to watch for width changes
58+ const resizeObserver = new ResizeObserver ( ( ) => {
59+ updateWidth ( ) ;
60+ } ) ;
61+
62+ if ( containerRef . current ) {
63+ resizeObserver . observe ( containerRef . current ) ;
64+ }
65+
66+ // Initial update
67+ updateWidth ( ) ;
68+
69+ // Cleanup observer on unmount
70+ return ( ) => {
71+ resizeObserver . disconnect ( ) ;
72+ } ;
73+ } , [ ] ) ;
74+
2575 const uiOptions = getUiOptions ( uiSchema ) ;
26- const TitleFieldTemplate = getTemplate ( ' TitleFieldTemplate' , registry , uiOptions ) ;
27- const DescriptionFieldTemplate = getTemplate ( ' DescriptionFieldTemplate' , registry , uiOptions ) ;
76+ const TitleFieldTemplate = getTemplate ( " TitleFieldTemplate" , registry , uiOptions ) ;
77+ const DescriptionFieldTemplate = getTemplate ( " DescriptionFieldTemplate" , registry , uiOptions ) ;
2878 const {
2979 ButtonTemplates : { AddButton } ,
3080 } = registry . templates ;
3181
32- // Define responsive column spans based on the ui:props or fallback to defaults
33- const defaultResponsiveColSpan = {
34- xs : 24 , // Extra small devices
35- sm : 24 , // Small devices
36- md : 12 , // Medium devices
37- lg : 12 , // Large devices
38- xl : 8 , // Extra large devices
82+ const defaultResponsiveColSpan = ( width : number ) => {
83+ if ( width > 1200 ) return 8 ; // Wide screens
84+ if ( width > 768 ) return 12 ; // Tablets
85+ return 24 ; // Mobile
3986 } ;
4087
41- const { rowGutter = 4 , colSpan = defaultResponsiveColSpan } = uiSchema ?. [ ' ui:props' ] || { } ;
88+ const { rowGutter = 4 } = uiSchema ?. [ " ui:props" ] || { } ;
4289
43- // Generate responsive colSpan props for each element
44- const calculateResponsiveColSpan = ( element : any ) => {
45- const { type } = element . content . props . schema ;
46- const widget = getUiOptions ( element . content . props . uiSchema ) . widget ;
90+ const calculateResponsiveColSpan = ( element : any ) : { span : number } => {
4791
48- const defaultSpan = widget === 'textarea' || type === 'object' || type === 'array' ? 24 : colSpan ;
92+ const uiSchemaProps = getUiOptions ( element . content . props . uiSchema ) ?. [ "ui:props" ] as
93+ | { colSpan ?: Record < string , number > | number }
94+ | undefined ;
4995
50- // Ensure the returned object is properly formatted for AntD responsive properties
51- return typeof defaultSpan === 'object' ? defaultSpan : { span : defaultSpan } ;
96+ const uiSchemaColSpan = uiSchemaProps ?. colSpan ;
97+ const defaultSpan = containerWidth > 1200 ? 8 : containerWidth > 768 ? 12 : 24 ;
98+
99+ if ( uiSchemaColSpan ) {
100+ if ( typeof uiSchemaColSpan === "number" ) {
101+ return { span : uiSchemaColSpan } ;
102+ } else if ( typeof uiSchemaColSpan === "object" ) {
103+ if ( containerWidth > 1200 && uiSchemaColSpan . xl !== undefined ) {
104+ return { span : uiSchemaColSpan . xl } ;
105+ } else if ( containerWidth > 992 && uiSchemaColSpan . lg !== undefined ) {
106+ return { span : uiSchemaColSpan . lg } ;
107+ } else if ( containerWidth > 768 && uiSchemaColSpan . md !== undefined ) {
108+ return { span : uiSchemaColSpan . md } ;
109+ } else if ( containerWidth > 576 && uiSchemaColSpan . sm !== undefined ) {
110+ return { span : uiSchemaColSpan . sm } ;
111+ } else if ( uiSchemaColSpan . xs !== undefined ) {
112+ return { span : uiSchemaColSpan . xs } ;
113+ }
114+ }
115+ }
116+
117+ return { span : defaultSpan } ;
52118 } ;
53119
54- return (
55- < ConfigConsumer >
56- { ( configProps ) => (
57- < fieldset id = { idSchema . $id } className = "form-section" >
58- < Row gutter = { rowGutter } >
59- { schema . type === 'object' && title && (
60- < legend >
61- < TitleFieldTemplate id = { titleId ( idSchema ) } title = { title } required = { props . required } schema = { schema } uiSchema = { uiSchema } registry = { registry } />
62- </ legend >
63- ) }
64- { description && (
65- < Col span = { 24 } style = { DESCRIPTION_COL_STYLE } >
66- < DescriptionFieldTemplate id = { descriptionId ( idSchema ) } description = { description } schema = { schema } uiSchema = { uiSchema } registry = { registry } />
120+ const renderSectionLayout = ( properties : any [ ] , uiGrid : any , section : string ) => {
121+
122+ if ( uiGrid && Array . isArray ( uiGrid ) ) {
123+ return (
124+ < Row gutter = { rowGutter } key = { section } >
125+ { uiGrid . map ( ( ui_row : Record < string , any > ) =>
126+ Object . keys ( ui_row ) . map ( ( row_item ) => {
127+ const element = properties . find ( ( p ) => p . name === row_item ) ;
128+ if ( element ) {
129+ const span = calculateResponsiveColSpan ( element ) . span ;
130+ return (
131+ < Col key = { element . name } span = { span } >
132+ { element . content }
133+ </ Col >
134+ ) ;
135+ }
136+ return null ;
137+ } )
138+ ) }
139+ </ Row >
140+ ) ;
141+ }
142+
143+ // Default layout if no grid is provided
144+ return (
145+ < Row gutter = { rowGutter } key = { section } >
146+ { properties . map ( ( element ) => (
147+ < Col key = { element . name } { ...calculateResponsiveColSpan ( element ) } >
148+ { element . content }
149+ </ Col >
150+ ) ) }
151+ </ Row >
152+ ) ;
153+ } ;
154+
155+ const renderCustomLayout = ( ) => {
156+ const schemaType = schema . type as string ;
157+ switch ( schemaType ) {
158+ case "Group" :
159+ return (
160+ < div style = { { border : "1px solid #ccc" , padding : "15px" , marginBottom : "10px" } } >
161+ < h3 > { schema . label || "Group" } </ h3 >
162+ { renderSectionLayout ( properties , uiSchema ?. [ "ui:grid" ] , schema . label ) }
163+ </ div >
164+ ) ;
165+ case "HorizontalLayout" :
166+ return (
167+ < Row gutter = { rowGutter } style = { { display : "flex" , gap : "10px" } } >
168+ { properties . map ( ( element ) => (
169+ < Col key = { element . name } { ...calculateResponsiveColSpan ( element ) } >
170+ { element . content }
67171 </ Col >
68- ) }
69- { uiSchema ?. [ 'ui:grid' ] && Array . isArray ( uiSchema [ 'ui:grid' ] ) ? (
70- uiSchema [ 'ui:grid' ] . map ( ( ui_row : Record < string , any > ) => {
71- return Object . keys ( ui_row ) . map ( ( row_item ) => {
72- const element = properties . find ( ( p ) => p . name === row_item ) ;
73- return element ? (
74- // Pass responsive colSpan props using the calculated values
75- < Col key = { element . name } { ...ui_row [ row_item ] } >
76- { element . content }
172+ ) ) }
173+ </ Row >
174+ ) ;
175+ case "VerticalLayout" :
176+ return (
177+ < div style = { { display : "flex" , flexDirection : "column" , gap : "10px" } } >
178+ { properties . map ( ( element ) => (
179+ < div key = { element . name } > { element . content } </ div >
180+ ) ) }
181+ </ div >
182+ ) ;
183+ default :
184+ return null ; // Fall back to default rendering if no match
185+ }
186+ } ;
187+
188+ // Check if the schema is a custom layout type
189+ const schemaType = schema . type as string ; // Extract schema type safely
190+ const isCustomLayout = [ "Group" , "HorizontalLayout" , "VerticalLayout" ] . includes ( schemaType ) ;
191+
192+ return (
193+ < div ref = { containerRef } >
194+ < ConfigConsumer >
195+ { ( configProps ) => (
196+ < fieldset id = { idSchema . $id } className = "form-section" >
197+ { ! isCustomLayout && (
198+ < >
199+ { schema . type === "object" && title && (
200+ < legend >
201+ < TitleFieldTemplate
202+ id = { titleId ( idSchema ) }
203+ title = { title }
204+ required = { props . required }
205+ schema = { schema }
206+ uiSchema = { uiSchema }
207+ registry = { registry }
208+ />
209+ </ legend >
210+ ) }
211+ { description && (
212+ < Col span = { 24 } style = { DESCRIPTION_COL_STYLE } >
213+ < DescriptionFieldTemplate
214+ id = { descriptionId ( idSchema ) }
215+ description = { description }
216+ schema = { schema }
217+ uiSchema = { uiSchema }
218+ registry = { registry }
219+ />
77220 </ Col >
78- ) : null ;
79- } ) ;
80- } )
81- ) : (
82- properties . map ( ( element ) => (
83- < Col key = { element . name } { ...calculateResponsiveColSpan ( element ) } >
84- { element . content }
221+ ) }
222+ { renderSectionLayout ( properties , uiSchema ?. [ "ui:grid" ] , "root" ) }
223+ </ >
224+ ) }
225+
226+ { isCustomLayout && renderCustomLayout ( ) }
227+
228+ { canExpand ( schema , uiSchema , formData ) && (
229+ < Row justify = "end" style = { { marginTop : "24px" } } >
230+ < Col >
231+ < AddButton
232+ className = "object-property-expand"
233+ onClick = { onAddClick ( schema ) }
234+ disabled = { disabled || readonly }
235+ registry = { registry }
236+ />
85237 </ Col >
86- ) )
238+ </ Row >
87239 ) }
88- </ Row >
89- { canExpand ( schema , uiSchema , formData ) && (
90- < Row justify = "end" style = { { marginTop : '24px' } } >
91- < Col >
92- < AddButton className = "object-property-expand" onClick = { onAddClick ( schema ) } disabled = { disabled || readonly } registry = { registry } />
93- </ Col >
94- </ Row >
95- ) }
96- </ fieldset >
97- ) }
98- </ ConfigConsumer >
240+ </ fieldset >
241+ ) }
242+ </ ConfigConsumer >
243+ </ div >
99244 ) ;
100245} ;
101246
0 commit comments