Skip to content

Commit c7bddbf

Browse files
author
FalkWolsky
committed
Work in Progress for Responsive Layout
1 parent 9618206 commit c7bddbf

File tree

2 files changed

+214
-229
lines changed

2 files changed

+214
-229
lines changed

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

Lines changed: 128 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,14 @@
11
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";
127

138
const DESCRIPTION_COL_STYLE = {
14-
paddingBottom: "8px",
9+
paddingBottom: '8px',
1510
};
1611

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-
3112
const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
3213
const {
3314
title,
@@ -42,59 +23,62 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
4223
readonly,
4324
registry,
4425
} = 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();
7427

7528
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);
7831
const {
7932
ButtonTemplates: { AddButton },
8033
} = registry.templates;
8134

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
8642
};
8743

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+
}
9175

76+
// Fallback to default colSpan or ui:props.colSpan
9277
const uiSchemaProps = getUiOptions(element.content.props.uiSchema)?.["ui:props"] as
9378
| { colSpan?: Record<string, number> | number }
9479
| undefined;
9580

9681
const uiSchemaColSpan = uiSchemaProps?.colSpan;
97-
const defaultSpan = containerWidth > 1200 ? 8 : containerWidth > 768 ? 12 : 24;
9882

9983
if (uiSchemaColSpan) {
10084
if (typeof uiSchemaColSpan === "number") {
@@ -114,133 +98,94 @@ const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
11498
}
11599
}
116100

101+
// Default responsive behavior
102+
const defaultSpan = containerWidth > 1200 ? 8 : containerWidth > 768 ? 12 : 24;
117103
return { span: defaultSpan };
118104
};
119105

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
144108
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+
})}
151136
</Row>
152137
);
153138
};
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+
192140
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>
244189
);
245190
};
246191

0 commit comments

Comments
 (0)