Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9618206

Browse files
author
FalkWolsky
committed
Extending the JSON Schema Forms to alow responsive view in the container
1 parent1658c18 commit9618206

File tree

3 files changed

+238
-63
lines changed

3 files changed

+238
-63
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
importReactfrom"react";
2+
importObjectFieldTemplatefrom"./ObjectFieldTemplate";// Import the existing ObjectFieldTemplate
3+
4+
exportconstLayoutFieldTemplate=(props:any)=>{
5+
const{ schema, uiSchema, children, ...rest}=props;// Spread to include all props
6+
7+
// Handle custom layouts
8+
switch(schema.type){
9+
case"Group":
10+
return(
11+
<divstyle={{border:"1px solid #ccc",padding:"15px",marginBottom:"10px"}}>
12+
<h3>{schema.label||"Group"}</h3>
13+
{children}
14+
</div>
15+
);
16+
case"HorizontalLayout":
17+
return<divstyle={{display:"flex",gap:"10px"}}>{children}</div>;
18+
case"VerticalLayout":
19+
return<divstyle={{display:"flex",flexDirection:"column",gap:"10px"}}>{children}</div>;
20+
default:
21+
// Delegate to the existing ObjectFieldTemplate, ensuring all props are passed
22+
return<ObjectFieldTemplateschema={schema}uiSchema={uiSchema}{...rest}/>;
23+
}
24+
};
25+
26+
exportdefaultLayoutFieldTemplate;

‎client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/ObjectFieldTemplate.tsx‎

Lines changed: 208 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
1-
importReactfrom'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+
importReact,{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

613
constDESCRIPTION_COL_STYLE={
7-
paddingBottom:'8px',
14+
paddingBottom:"8px",
815
};
916

17+
interfaceColSpan{
18+
xs:number;
19+
sm:number;
20+
md:number;
21+
lg:number;
22+
xl:number;
23+
}
24+
25+
interfaceUiOptions{
26+
colSpan:ColSpan;
27+
rowGutter:number;
28+
// other properties...
29+
}
30+
1031
constObjectFieldTemplate=(props:ObjectFieldTemplateProps)=>{
1132
const{
1233
title,
@@ -22,80 +43,204 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
2243
registry,
2344
}=props;
2445

46+
constcontainerRef=useRef<HTMLDivElement>(null);
47+
const[containerWidth,setContainerWidth]=useState(0);
48+
49+
// Monitor the container's width
50+
useEffect(()=>{
51+
constupdateWidth=()=>{
52+
if(containerRef.current){
53+
setContainerWidth(containerRef.current.offsetWidth);
54+
}
55+
};
56+
57+
// Create a ResizeObserver to watch for width changes
58+
constresizeObserver=newResizeObserver(()=>{
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
constuiOptions=getUiOptions(uiSchema);
26-
constTitleFieldTemplate=getTemplate('TitleFieldTemplate',registry,uiOptions);
27-
constDescriptionFieldTemplate=getTemplate('DescriptionFieldTemplate',registry,uiOptions);
76+
constTitleFieldTemplate=getTemplate("TitleFieldTemplate",registry,uiOptions);
77+
constDescriptionFieldTemplate=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-
constdefaultResponsiveColSpan={
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+
constdefaultResponsiveColSpan=(width:number)=>{
83+
if(width>1200)return8;// Wide screens
84+
if(width>768)return12;// Tablets
85+
return24;// 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-
constcalculateResponsiveColSpan=(element:any)=>{
45-
const{ type}=element.content.props.schema;
46-
constwidget=getUiOptions(element.content.props.uiSchema).widget;
90+
constcalculateResponsiveColSpan=(element:any):{span:number}=>{
4791

48-
constdefaultSpan=widget==='textarea'||type==='object'||type==='array' ?24 :colSpan;
92+
constuiSchemaProps=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-
returntypeofdefaultSpan==='object' ?defaultSpan :{span:defaultSpan};
96+
constuiSchemaColSpan=uiSchemaProps?.colSpan;
97+
constdefaultSpan=containerWidth>1200 ?8 :containerWidth>768 ?12 :24;
98+
99+
if(uiSchemaColSpan){
100+
if(typeofuiSchemaColSpan==="number"){
101+
return{span:uiSchemaColSpan};
102+
}elseif(typeofuiSchemaColSpan==="object"){
103+
if(containerWidth>1200&&uiSchemaColSpan.xl!==undefined){
104+
return{span:uiSchemaColSpan.xl};
105+
}elseif(containerWidth>992&&uiSchemaColSpan.lg!==undefined){
106+
return{span:uiSchemaColSpan.lg};
107+
}elseif(containerWidth>768&&uiSchemaColSpan.md!==undefined){
108+
return{span:uiSchemaColSpan.md};
109+
}elseif(containerWidth>576&&uiSchemaColSpan.sm!==undefined){
110+
return{span:uiSchemaColSpan.sm};
111+
}elseif(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-
<fieldsetid={idSchema.$id}className="form-section">
58-
<Rowgutter={rowGutter}>
59-
{schema.type==='object'&&title&&(
60-
<legend>
61-
<TitleFieldTemplateid={titleId(idSchema)}title={title}required={props.required}schema={schema}uiSchema={uiSchema}registry={registry}/>
62-
</legend>
63-
)}
64-
{description&&(
65-
<Colspan={24}style={DESCRIPTION_COL_STYLE}>
66-
<DescriptionFieldTemplateid={descriptionId(idSchema)}description={description}schema={schema}uiSchema={uiSchema}registry={registry}/>
120+
constrenderSectionLayout=(properties:any[],uiGrid:any,section:string)=>{
121+
122+
if(uiGrid&&Array.isArray(uiGrid)){
123+
return(
124+
<Rowgutter={rowGutter}key={section}>
125+
{uiGrid.map((ui_row:Record<string,any>)=>
126+
Object.keys(ui_row).map((row_item)=>{
127+
constelement=properties.find((p)=>p.name===row_item);
128+
if(element){
129+
constspan=calculateResponsiveColSpan(element).span;
130+
return(
131+
<Colkey={element.name}span={span}>
132+
{element.content}
133+
</Col>
134+
);
135+
}
136+
returnnull;
137+
})
138+
)}
139+
</Row>
140+
);
141+
}
142+
143+
// Default layout if no grid is provided
144+
return(
145+
<Rowgutter={rowGutter}key={section}>
146+
{properties.map((element)=>(
147+
<Colkey={element.name}{...calculateResponsiveColSpan(element)}>
148+
{element.content}
149+
</Col>
150+
))}
151+
</Row>
152+
);
153+
};
154+
155+
constrenderCustomLayout=()=>{
156+
constschemaType=schema.typeasstring;
157+
switch(schemaType){
158+
case"Group":
159+
return(
160+
<divstyle={{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+
<Rowgutter={rowGutter}style={{display:"flex",gap:"10px"}}>
168+
{properties.map((element)=>(
169+
<Colkey={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-
returnObject.keys(ui_row).map((row_item)=>{
72-
constelement=properties.find((p)=>p.name===row_item);
73-
returnelement ?(
74-
// Pass responsive colSpan props using the calculated values
75-
<Colkey={element.name}{...ui_row[row_item]}>
76-
{element.content}
172+
))}
173+
</Row>
174+
);
175+
case"VerticalLayout":
176+
return(
177+
<divstyle={{display:"flex",flexDirection:"column",gap:"10px"}}>
178+
{properties.map((element)=>(
179+
<divkey={element.name}>{element.content}</div>
180+
))}
181+
</div>
182+
);
183+
default:
184+
returnnull;// Fall back to default rendering if no match
185+
}
186+
};
187+
188+
// Check if the schema is a custom layout type
189+
constschemaType=schema.typeasstring;// Extract schema type safely
190+
constisCustomLayout=["Group","HorizontalLayout","VerticalLayout"].includes(schemaType);
191+
192+
return(
193+
<divref={containerRef}>
194+
<ConfigConsumer>
195+
{(configProps)=>(
196+
<fieldsetid={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+
<Colspan={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-
<Colkey={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+
<Rowjustify="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-
<Rowjustify="end"style={{marginTop:'24px'}}>
91-
<Col>
92-
<AddButtonclassName="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

‎client/packages/lowcoder/src/comps/comps/jsonSchemaFormComp/jsonSchemaFormComp.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { useContext, useEffect } from "react";
2626
import{EditorContext}from"comps/editorState";
2727
importObjectFieldTemplatefrom'./ObjectFieldTemplate';
2828
importArrayFieldTemplatefrom'./ArrayFieldTemplate';
29+
// import { LayoutFieldTemplate } from "./LayoutFieldTemplate";
2930
import{Select}from'antd';
3031
importTitlefrom'antd/es/typography/Title';
3132

@@ -82,6 +83,8 @@ const Container = styled.div<{
8283
.help-block {
8384
margin-bottom: 0px;
8485
}
86+
87+
8588
`;
8689

8790
functionconvertData(schema?:JSONSchema7,data?:any){
@@ -262,6 +265,7 @@ let FormBasicComp = (function () {
262265
templates={{
263266
ObjectFieldTemplate:ObjectFieldTemplate,
264267
ArrayFieldTemplate:ArrayFieldTemplate,
268+
// FieldTemplate: LayoutFieldTemplate,
265269
}}
266270
widgets={{searchableSelect:SearchableSelectWidget}}
267271
// ErrorList={ErrorList}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp