Skip to content

Commit

Permalink
Merge pull request #5 from amir78729/feat/container
Browse files Browse the repository at this point in the history
fix: enhance UI
  • Loading branch information
amir78729 authored Jun 18, 2024
2 parents 1a436f5 + 1517d31 commit b303454
Show file tree
Hide file tree
Showing 11 changed files with 612 additions and 140 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@lit/react": "^1.0.5",
"@mui/icons-material": "^5.15.20",
"@mui/material": "^5.15.19",
"@rjsf/core": "^5.18.4",
"@rjsf/mui": "^5.18.4",
Expand Down
13 changes: 8 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions src/builder/JsonSchemaBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,27 @@ export class JsonSchemaBuilder {
return this;
}

deleteProperty(name: string): JsonSchemaBuilder {
if (this.schema.properties && this.schema.properties[name]) {
delete this.schema.properties[name];
}
return this;
}

deleteRequired(name: string): JsonSchemaBuilder {
if (this.schema.required) {
this.schema.required = this.schema.required.filter((req: string) => req !== name);
}
return this;
}

editProperty(name: string, propSchema: JsonSchema): JsonSchemaBuilder {
if (this.schema.properties && this.schema.properties[name]) {
this.schema.properties[name] = propSchema;
}
return this;
}

build(): JsonSchema {
return this.schema;
}
Expand Down
5 changes: 3 additions & 2 deletions src/components/AddFieldModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Box, Button, Dialog, FormControl, InputLabel, MenuItem, Stack, TextField,} from "@mui/material";
import {Box, Button, Dialog, FormControl, IconButton, InputLabel, MenuItem, Stack, TextField,} from "@mui/material";
import Form from "@rjsf/mui";

import validator from "@rjsf/validator-ajv8";
Expand All @@ -7,6 +7,7 @@ import {useSchema} from "../providers/SchemaProvider";
import {JsonSchemaType} from "../types";
import {JsonSchemaField} from "../fields/JsonSchemaField";
import Select from "@mui/material/Select";
import {Add} from "@mui/icons-material";

const AddFieldModal = () => {
const [open, setOpen] = React.useState<boolean>(false);
Expand Down Expand Up @@ -54,7 +55,7 @@ const AddFieldModal = () => {

return (
<>
<Button onClick={() => setOpen(true)}>Add Property</Button>
<IconButton onClick={() => setOpen(true)}><Add /></IconButton>
<Dialog fullWidth open={open} onClose={() => setOpen(false)}>
<Box p={3} key={JSON.stringify(fields)}>
<h1>{step !== 0 && <Button onClick={() => setStep(0)}>back</Button>}Adding Field</h1>
Expand Down
44 changes: 31 additions & 13 deletions src/components/SchemaBuilder.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
import Form from "@rjsf/mui";
import validator from "@rjsf/validator-ajv8";
import React from "react";
import React, {useState} from "react";
import AddFieldModal from "./AddFieldModal";
import {SchemaProvider, useSchema} from "../providers/SchemaProvider";
import {JsonSchemaField} from "../fields/JsonSchemaField";
import {FieldConfig} from "../types";
import {useSchema} from "../providers/SchemaProvider";
import {Button, CssBaseline, ButtonGroup, Tabs, Tab} from "@mui/material";
import SchemaPreview from "./SchemaPreview";


const SchemaBuilder = () => {
const {schema} = useSchema();

const [tab, setTab] = useState<number>(0)
const TABS: Record<'BUILDER' | "SCHEMA" | "FORM_PREVIEW", number> = {
'BUILDER': 0, "SCHEMA": 1, "FORM_PREVIEW": 2
}
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
setTab(newValue);
};
return (
<div>
<p>Form Preview:</p>
<Form schema={schema} validator={validator}/>
<p>Schema:</p>
<pre>{JSON.stringify(schema, null, 2)}</pre>
<hr/>
<AddFieldModal/>
</div>
<>
<CssBaseline/>
<div>
<Tabs value={tab} onChange={handleTabChange}>
<Tab label="Builder"></Tab>
<Tab label="Schema"></Tab>
<Tab label="Form Preview"></Tab>
</Tabs>

{tab === TABS.BUILDER && (
<SchemaPreview schema={schema} data={{}}/>
)}
{tab === TABS.SCHEMA && (
<pre>{JSON.stringify(schema, null, 2)}</pre>
)}
{tab === TABS.FORM_PREVIEW && (
<Form schema={schema} validator={validator}/>
)}
</div>
</>
);
};

Expand Down
221 changes: 221 additions & 0 deletions src/components/SchemaPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import React, {useState} from "react";
import {RJSFSchema} from "@rjsf/utils";
import AddFieldModal from "./AddFieldModal";
import Numbers from '@mui/icons-material/Numbers';
import {
Add,
DataArray,
DataObject,
Delete,
Edit,
ExpandLess,
ExpandMore,
TextSnippet,
ToggleOn
} from "@mui/icons-material";
import {
Box, Button, Card, Chip,
Collapse,
Dialog, DialogActions, DialogContent, DialogTitle,
IconButton,
ListItem,
ListItemIcon,
ListItemText,
Tooltip,
Typography
} from "@mui/material";
import {getFieldId, getSchemaFormatFromSchema} from "../utils";
import {DataVisualizationType} from "../types";
import {SchemaAction, useSchema} from "../providers/SchemaProvider";
import Form from "@rjsf/mui";
import validator from "@rjsf/validator-ajv8";

type Props = {
schema: RJSFSchema;
data: unknown;
name: string;
}


// TODO: refactor
const renderHeader = ({icon, schema, onDelete, name}: {
icon?: React.ReactNode,
schema: RJSFSchema,
name: string,
onDelete?: () => void,
collapse?: boolean;
onCollapse?: () => void
}) => {
const [showDeleteConfirmationModal, setShowDeleteConfirmationModal] = useState<boolean>(false);
const [showEditModal, setShowEditModal] = useState<boolean>(false);
const {fields, dispatch} = useSchema();
const SelectedFieldClass = fields.find(field => field.id === getFieldId(schema))?.Class

let field;
if (SelectedFieldClass) {
field = new SelectedFieldClass(name)
}
return (
<>
<Dialog open={showEditModal} onClose={() => setShowEditModal(false)}>
<DialogTitle>Edit <code>{name}</code> Field</DialogTitle>
<DialogContent>
<Form onSubmit={({formData}) => {
dispatch({type: "UPDATE_PROPERTY", payload: {name, schema: formData}})
setShowEditModal(false);
}} schema={field?.getBuilderSchema()} formData={schema} validator={validator}/>
</DialogContent>
</Dialog>
<Dialog open={showDeleteConfirmationModal} onClose={() => setShowDeleteConfirmationModal(false)}>
<DialogContent><Typography>Are you sure you want to delete this field?</Typography></DialogContent>
<DialogActions>
<Button fullWidth color="error"
onClick={() => setShowDeleteConfirmationModal(false)}>Cancel</Button>
<Button fullWidth variant="contained" color="error" onClick={() => {
onDelete?.();
setShowDeleteConfirmationModal(false)
}}>Delete</Button>
</DialogActions>
</Dialog>
<ListItem>
<ListItemText
primary={(
<>
<Typography variant="h6">{schema?.title} <Chip
size="small"
color="primary"
variant="outlined"
icon={icon}
label={`${schema?.type}${schema?.format ? `: ${schema?.format}` : ''}`}
/>
</Typography>
{schema?.description && (
<Typography variant="caption">{schema?.description}</Typography>
)}
</>
)}
/>
{onDelete && <IconButton color="error" onClick={() => setShowDeleteConfirmationModal(true)}><Delete
fontSize="small"/></IconButton>}
<IconButton
color="warning"
onClick={() => setShowEditModal(true)}
>
<Edit fontSize="small"/>
</IconButton>

</ListItem>
</>
)
}

const handleDelete = (dispatch: React.Dispatch<SchemaAction>, name: string) => {
dispatch({type: "DELETE_PROPERTY", payload: {name}});
dispatch({type: "DELETE_REQUIRED", payload: {name}});
}

const handleEdit = (dispatch: React.Dispatch<SchemaAction>, name: string, schema: RJSFSchema) => {
dispatch({type: "UPDATE_PROPERTY", payload: {name, schema}});
}

const SchemaPreview = ({schema, data, name}: Props) => {
const FormPreview = getSchemaFormatFromSchema(schema, SchemaPreview)
return (
<div>
<FormPreview {...{schema, data, name}} />
</div>
)
};

SchemaPreview.String = function String({schema, data, name}: DataVisualizationType) {
const {dispatch} = useSchema();
return (
<div>
{renderHeader({name, schema, icon: <TextSnippet/>, onDelete: () => handleDelete(dispatch, name)})}
</div>
);
};

SchemaPreview.Number = function Number({schema, name}: DataVisualizationType) {
const {dispatch} = useSchema();
return (
<div>
{renderHeader({name, schema, icon: <Numbers/>, onDelete: () => handleDelete(dispatch, name)})}
</div>
);
};

SchemaPreview.Boolean = function BooleanVisualization({schema, name}: DataVisualizationType) {
const {dispatch} = useSchema();
return (
<div>
{renderHeader({name, schema, icon: <ToggleOn/>, onDelete: () => handleDelete(dispatch, name)})}
</div>
);
};

SchemaPreview.Object = function ObjectVisualization({schema, data, name}: DataVisualizationType) {
const {dispatch} = useSchema();
const properties = Object.keys(schema?.properties || {})

const [open, setOpen] = React.useState(true);

const handleCollapse = () => {
setOpen(!open);
};

return (
<Card sx={{p: 2, m: 2}}>
{renderHeader({
name,
schema,
icon: <DataObject/>,
collapse: open,
onCollapse: handleCollapse,
onDelete: () => handleDelete(dispatch, name)
})}
<Card sx={{p: 2, m: 2}}>
<Box
px={2} display="flex" justifyContent="space-between">
<Typography flex={1}>Properties</Typography>
<AddFieldModal/>
{open !== undefined &&
<IconButton onClick={handleCollapse}>{!open ? <ExpandMore fontSize="small"/> :
<ExpandLess fontSize="small"/>}</IconButton>}
</Box>
<Collapse in={open} timeout="auto" unmountOnExit>
{properties?.length > 0 ? properties?.map((property) => (
<SchemaPreview
name={property}
schema={schema.properties[property]}
/>
)) : (
<Typography alignItems="center" textAlign="center" p={3}>
Click on <Add fontSize="small"/> button to add properties
</Typography>
)}
</Collapse>
</Card>
</Card>
);
};

SchemaPreview.Array = function ArrayVisualization({schema, data, name}: DataVisualizationType) {
const {dispatch} = useSchema();
return (
<>
<Card sx={{p: 2, m: 2}}>
{renderHeader({name, schema, icon: <DataArray/>, onDelete: () => handleDelete(dispatch, name)})}
<SchemaPreview
{...{schema: schema.items, name}}
/>
</Card>
</>
);
};

SchemaPreview.Unknown = function ArrayVisualization() {
return <></>;
};

export default SchemaPreview;
Loading

0 comments on commit b303454

Please sign in to comment.