1
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" ;
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' ;
5
+ import { useContainerWidth } from "./jsonSchemaFormComp" ;
6
+ import styled from "styled-components" ;
12
7
13
8
const DESCRIPTION_COL_STYLE = {
14
- paddingBottom : " 8px" ,
9
+ paddingBottom : ' 8px' ,
15
10
} ;
16
11
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
-
31
12
const ObjectFieldTemplate = ( props : ObjectFieldTemplateProps ) => {
32
13
const {
33
14
title,
@@ -42,59 +23,62 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
42
23
readonly,
43
24
registry,
44
25
} = props ;
45
-
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
- } , [ ] ) ;
26
+ const containerWidth = useContainerWidth ( ) ;
74
27
75
28
const uiOptions = getUiOptions ( uiSchema ) ;
76
- const TitleFieldTemplate = getTemplate ( " TitleFieldTemplate" , registry , uiOptions ) ;
77
- const DescriptionFieldTemplate = getTemplate ( " DescriptionFieldTemplate" , registry , uiOptions ) ;
29
+ const TitleFieldTemplate = getTemplate ( ' TitleFieldTemplate' , registry , uiOptions ) ;
30
+ const DescriptionFieldTemplate = getTemplate ( ' DescriptionFieldTemplate' , registry , uiOptions ) ;
78
31
const {
79
32
ButtonTemplates : { AddButton } ,
80
33
} = registry . templates ;
81
34
82
- const defaultResponsiveColSpan = ( width : number ) => {
83
- if ( width > 1200 ) return 8 ; // Wide screens
84
- if ( width > 768 ) return 12 ; // Tablets
85
- return 24 ; // Mobile
35
+ // Define responsive column spans based on the ui:props or fallback to defaults
36
+ const defaultResponsiveColSpan = {
37
+ xs : 24 , // Extra small devices
38
+ sm : 24 , // Small devices
39
+ md : 12 , // Medium devices
40
+ lg : 12 , // Large devices
41
+ xl : 8 , // Extra large devices
86
42
} ;
87
43
88
- const { rowGutter = 4 } = uiSchema ?. [ "ui:props" ] || { } ;
89
-
90
- const calculateResponsiveColSpan = ( element : any ) : { span : number } => {
44
+ const { rowGutter = 4 } = uiSchema ?. [ 'ui:props' ] || { } ;
45
+
46
+ const calculateResponsiveColSpan = ( element : any , level : number ) : { span : number } => {
47
+
48
+ console . log ( "Calculating span for" , element . name , "at level" , level ) ;
49
+
50
+ // root level
51
+ if ( level === 0 ) return { span : 24 } ;
52
+
53
+ // Check if the element has a layout definition in ui:grid
54
+ const gridColSpan = uiSchema ?. [ 'ui:grid' ]
55
+ ?. find ( ( row : Record < string , any > ) => row [ element . name ] )
56
+ ?. [ element . name ] ;
57
+
58
+ if ( gridColSpan ) {
59
+ if ( typeof gridColSpan === "number" ) {
60
+ return { span : gridColSpan } ;
61
+ } else if ( typeof gridColSpan === "object" ) {
62
+ if ( containerWidth > 1200 && gridColSpan . xl !== undefined ) {
63
+ return { span : gridColSpan . xl } ;
64
+ } else if ( containerWidth > 992 && gridColSpan . lg !== undefined ) {
65
+ return { span : gridColSpan . lg } ;
66
+ } else if ( containerWidth > 768 && gridColSpan . md !== undefined ) {
67
+ return { span : gridColSpan . md } ;
68
+ } else if ( containerWidth > 576 && gridColSpan . sm !== undefined ) {
69
+ return { span : gridColSpan . sm } ;
70
+ } else if ( gridColSpan . xs !== undefined ) {
71
+ return { span : gridColSpan . xs } ;
72
+ }
73
+ }
74
+ }
91
75
76
+ // Fallback to default colSpan or ui:props.colSpan
92
77
const uiSchemaProps = getUiOptions ( element . content . props . uiSchema ) ?. [ "ui:props" ] as
93
78
| { colSpan ?: Record < string , number > | number }
94
79
| undefined ;
95
80
96
81
const uiSchemaColSpan = uiSchemaProps ?. colSpan ;
97
- const defaultSpan = containerWidth > 1200 ? 8 : containerWidth > 768 ? 12 : 24 ;
98
82
99
83
if ( uiSchemaColSpan ) {
100
84
if ( typeof uiSchemaColSpan === "number" ) {
@@ -114,133 +98,94 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
114
98
}
115
99
}
116
100
101
+ // Default responsive behavior
102
+ const defaultSpan = containerWidth > 1200 ? 8 : containerWidth > 768 ? 12 : 24 ;
117
103
return { span : defaultSpan } ;
118
104
} ;
119
105
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
106
+ const renderProperties = ( properties : any [ ] , level : number ) => {
107
+ console . log ( "Rendering level:" , level ) ; // Debugging level
144
108
return (
145
- < Row gutter = { rowGutter } key = { section } >
146
- { properties . map ( ( element ) => (
147
- < Col key = { element . name } { ...calculateResponsiveColSpan ( element ) } >
148
- { element . content }
149
- </ Col >
150
- ) ) }
109
+ < Row
110
+ gutter = { rowGutter }
111
+ style = { level === 0 ? { width : "100%" } : { marginLeft : - 8 , marginRight : - 8 } }
112
+ >
113
+ { properties . map ( ( element ) => {
114
+ const span = calculateResponsiveColSpan ( element , level ) ;
115
+
116
+ // Check if the element is an object or array and has nested properties
117
+ if ( element . content ?. props ?. schema ?. type === "object" && element . content . props . properties ) {
118
+ // Render nested objects with an incremented level
119
+ return (
120
+ < Col key = { element . name } span = { 24 } >
121
+ < fieldset >
122
+ < legend > { element . content . props . title || element . name } </ legend >
123
+ { renderProperties ( element . content . props . properties , level + 1 ) }
124
+ </ fieldset >
125
+ </ Col >
126
+ ) ;
127
+ }
128
+
129
+ // Render normal elements
130
+ return (
131
+ < Col key = { element . name } span = { span . span } >
132
+ { element . content }
133
+ </ Col >
134
+ ) ;
135
+ } ) }
151
136
</ Row >
152
137
) ;
153
138
} ;
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 }
171
- </ Col >
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
-
139
+
192
140
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
- />
220
- </ Col >
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
- />
237
- </ Col >
238
- </ Row >
239
- ) }
240
- </ fieldset >
241
- ) }
242
- </ ConfigConsumer >
243
- </ div >
141
+ < ConfigConsumer >
142
+ { ( configProps ) => (
143
+ < fieldset id = { idSchema . $id } className = "form-section" >
144
+ { /* Render Title */ }
145
+ { schema . type === "object" && title && (
146
+ < legend >
147
+ < TitleFieldTemplate
148
+ id = { titleId ( idSchema ) }
149
+ title = { title }
150
+ required = { props . required }
151
+ schema = { schema }
152
+ uiSchema = { uiSchema }
153
+ registry = { registry }
154
+ />
155
+ </ legend >
156
+ ) }
157
+ { /* Render Description */ }
158
+ { description && (
159
+ < Row >
160
+ < Col span = { 24 } style = { DESCRIPTION_COL_STYLE } >
161
+ < DescriptionFieldTemplate
162
+ id = { descriptionId ( idSchema ) }
163
+ description = { description }
164
+ schema = { schema }
165
+ uiSchema = { uiSchema }
166
+ registry = { registry }
167
+ />
168
+ </ Col >
169
+ </ Row >
170
+ ) }
171
+ { /* Render Properties */ }
172
+ { renderProperties ( properties , 0 ) }
173
+ { /* Expand Button */ }
174
+ { canExpand ( schema , uiSchema , formData ) && (
175
+ < Row justify = "end" style = { { width : "100%" , marginTop : "24px" } } >
176
+ < Col >
177
+ < AddButton
178
+ className = "object-property-expand"
179
+ onClick = { onAddClick ( schema ) }
180
+ disabled = { disabled || readonly }
181
+ registry = { registry }
182
+ />
183
+ </ Col >
184
+ </ Row >
185
+ ) }
186
+ </ fieldset >
187
+ ) }
188
+ </ ConfigConsumer >
244
189
) ;
245
190
} ;
246
191
0 commit comments