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" ;
5
12
6
13
const DESCRIPTION_COL_STYLE = {
7
- paddingBottom : ' 8px' ,
14
+ paddingBottom : " 8px" ,
8
15
} ;
9
16
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
+
10
31
const ObjectFieldTemplate = ( props : ObjectFieldTemplateProps ) => {
11
32
const {
12
33
title,
@@ -22,80 +43,204 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
22
43
registry,
23
44
} = props ;
24
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
+ } , [ ] ) ;
74
+
25
75
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 ) ;
28
78
const {
29
79
ButtonTemplates : { AddButton } ,
30
80
} = registry . templates ;
31
81
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
39
86
} ;
40
87
41
- const { rowGutter = 4 , colSpan = defaultResponsiveColSpan } = uiSchema ?. [ ' ui:props' ] || { } ;
88
+ const { rowGutter = 4 } = uiSchema ?. [ " ui:props" ] || { } ;
42
89
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 } => {
47
91
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 ;
49
95
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 } ;
52
118
} ;
53
119
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 }
67
171
</ 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
+ />
77
220
</ 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
+ />
85
237
</ Col >
86
- ) )
238
+ </ Row >
87
239
) }
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 >
99
244
) ;
100
245
} ;
101
246
0 commit comments