Skip to content

Commit 768d1c5

Browse files
author
FalkWolsky
committedOct 3, 2024·
Updating React JSON Schema Form - and implement Grid System + Select input
1 parent cd35441 commit 768d1c5

File tree

3 files changed

+202
-1
lines changed

3 files changed

+202
-1
lines changed
 
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React from 'react';
2+
import { Button, Row, Col } from 'antd';
3+
import { ArrayFieldTemplateProps } from '@rjsf/utils';
4+
import { ArrowDownOutlined, ArrowUpOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
5+
6+
const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
7+
const { items, canAdd, onAddClick, title } = props;
8+
9+
return (
10+
<fieldset>
11+
{title && <legend>{title}</legend>}
12+
<Row gutter={[0, 0]}>
13+
{items.map((element: any) => (
14+
<Col key={element.index} span={24} style={{ display: 'flex', alignItems: 'center' }}>
15+
{/* Content container for the array item */}
16+
<div style={{ flexGrow: 1 }}>
17+
{element.children}
18+
</div>
19+
20+
{/* Container for the control buttons with vertical alignment */}
21+
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'flex-end', paddingTop: "38px" }}>
22+
{/* Move down button */}
23+
{element.hasMoveDown && (
24+
<Button
25+
type="default"
26+
icon={<ArrowDownOutlined />}
27+
onClick={element.onReorderClick(element.index, element.index + 1)}
28+
style={{ marginLeft: '4px' }}
29+
/>
30+
)}
31+
32+
{/* Move up button */}
33+
{element.hasMoveUp && (
34+
<Button
35+
type="default"
36+
icon={<ArrowUpOutlined />}
37+
onClick={element.onReorderClick(element.index, element.index - 1)}
38+
style={{ marginLeft: '4px' }}
39+
/>
40+
)}
41+
42+
{/* Remove button */}
43+
{element.hasRemove && (
44+
<Button
45+
type="default"
46+
icon={<DeleteOutlined />}
47+
danger
48+
onClick={element.onDropIndexClick(element.index)}
49+
style={{ marginLeft: '4px' }}
50+
/>
51+
)}
52+
</div>
53+
</Col>
54+
))}
55+
{/* Add button for the array */}
56+
{canAdd && (
57+
<Col span={24} style={{ textAlign: 'center' }}>
58+
<Button type="dashed" onClick={onAddClick} icon={<PlusOutlined />}>
59+
Add Item
60+
</Button>
61+
</Col>
62+
)}
63+
</Row>
64+
</fieldset>
65+
);
66+
};
67+
68+
export default ArrayFieldTemplate;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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';
5+
6+
const DESCRIPTION_COL_STYLE = {
7+
paddingBottom: '8px',
8+
};
9+
10+
const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
11+
const {
12+
title,
13+
description,
14+
properties,
15+
schema,
16+
uiSchema,
17+
formData,
18+
idSchema,
19+
onAddClick,
20+
disabled,
21+
readonly,
22+
registry,
23+
} = props;
24+
25+
const uiOptions = getUiOptions(uiSchema);
26+
const TitleFieldTemplate = getTemplate('TitleFieldTemplate', registry, uiOptions);
27+
const DescriptionFieldTemplate = getTemplate('DescriptionFieldTemplate', registry, uiOptions);
28+
const {
29+
ButtonTemplates: { AddButton },
30+
} = registry.templates;
31+
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
39+
};
40+
41+
const { rowGutter = 24, colSpan = defaultResponsiveColSpan } = uiSchema?.['ui:props'] || {};
42+
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;
47+
48+
const defaultSpan = widget === 'textarea' || type === 'object' || type === 'array' ? 24 : colSpan;
49+
50+
// Ensure the returned object is properly formatted for AntD responsive properties
51+
return typeof defaultSpan === 'object' ? defaultSpan : { span: defaultSpan };
52+
};
53+
54+
return (
55+
<ConfigConsumer>
56+
{(configProps) => (
57+
<fieldset id={idSchema.$id}>
58+
<Row gutter={rowGutter}>
59+
{title && (
60+
<Col span={24}>
61+
<TitleFieldTemplate id={titleId(idSchema)} title={title} required={props.required} schema={schema} uiSchema={uiSchema} registry={registry} />
62+
</Col>
63+
)}
64+
{description && (
65+
<Col span={24} style={DESCRIPTION_COL_STYLE}>
66+
<DescriptionFieldTemplate id={descriptionId(idSchema)} description={description} schema={schema} uiSchema={uiSchema} registry={registry} />
67+
</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}
77+
</Col>
78+
) : null;
79+
});
80+
})
81+
) : (
82+
properties.map((element) => (
83+
<Col key={element.name} {...calculateResponsiveColSpan(element)}>
84+
{element.content}
85+
</Col>
86+
))
87+
)}
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>
99+
);
100+
};
101+
102+
export default ObjectFieldTemplate;

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { withTheme } from '@rjsf/core';
22
import type { RJSFValidationError, ErrorListProps, UISchemaSubmitButtonOptions } from "@rjsf/utils";
33
import validator from "@rjsf/validator-ajv8";
4-
// import Ajv from "@rjsf/validator-ajv8";
54
import { default as Button } from "antd/es/button";
65
import { BoolControl } from "comps/controls/boolControl";
76
import { jsonObjectExposingStateControl } from "comps/controls/codeStateControl";
@@ -25,6 +24,9 @@ import { hiddenPropertyView } from "comps/utils/propertyUtils";
2524
import { AutoHeightControl } from "../../controls/autoHeightControl";
2625
import { useContext, useEffect } from "react";
2726
import { EditorContext } from "comps/editorState";
27+
import ObjectFieldTemplate from './ObjectFieldTemplate';
28+
import ArrayFieldTemplate from './ArrayFieldTemplate';
29+
import { Select } from 'antd';
2830

2931
Theme.widgets.DateWidget = DateWidget(false);
3032
Theme.widgets.DateTimeWidget = DateWidget(true);
@@ -177,6 +179,30 @@ function ErrorList(props: ErrorListProps) {
177179
);
178180
}
179181

182+
const SearchableSelectWidget = (props : any) => {
183+
const { options, value, required, disabled, readonly, autofocus, onChange } = props;
184+
const { enumOptions } = options;
185+
186+
return (
187+
<Select
188+
showSearch
189+
optionFilterProp="children"
190+
value={value || undefined}
191+
disabled={disabled || readonly}
192+
autoFocus={autofocus}
193+
onChange={(val) => onChange(val)}
194+
style={{ width: '100%' }}
195+
placeholder={props.placeholder}
196+
>
197+
{enumOptions.map((option : any) => (
198+
<Select.Option key={option.value} value={option.value}>
199+
{option.label}
200+
</Select.Option>
201+
))}
202+
</Select>
203+
);
204+
};
205+
180206
function onSubmit(props: {
181207
resetAfterSubmit: boolean;
182208
data: { reset: () => void };
@@ -227,6 +253,11 @@ let FormBasicComp = (function () {
227253
onSubmit={() => onSubmit(props)}
228254
onChange={(e) => props.data.onChange(e.formData)}
229255
transformErrors={(errors) => transformErrors(errors)}
256+
templates={{
257+
ObjectFieldTemplate: ObjectFieldTemplate,
258+
ArrayFieldTemplate: ArrayFieldTemplate,
259+
}}
260+
widgets={{ searchableSelect: SearchableSelectWidget }}
230261
// ErrorList={ErrorList}
231262
children={
232263
<Button

0 commit comments

Comments
 (0)
Please sign in to comment.