Skip to content
This repository was archived by the owner on Nov 19, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions jsonforms-editor/src/core/dnd/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
* ---------------------------------------------------------------------
*/
import { JsonSchema } from '@jsonforms/core';

import { getArrayContainer, SchemaElement } from '../model';
import {
containsControls,
Expand All @@ -18,8 +20,27 @@ import { getHierarchy } from '../util/tree';
export const NEW_UI_SCHEMA_ELEMENT: 'newUiSchemaElement' = 'newUiSchemaElement';
export const MOVE_UI_SCHEMA_ELEMENT: 'moveUiSchemaElement' =
'moveUiSchemaElement';
export const NEW_SCHEMA_ELEMENT: 'newSchemaElement' = 'newSchemaElement';

export type DndType =
| NewUISchemaElement
| MoveUISchemaElement
| NewSchemaElement;

export type DndType = NewUISchemaElement | MoveUISchemaElement;
export interface NewSchemaElement {
type: 'newSchemaElement';
uiSchemaElement: EditorUISchemaElement;
schema: JsonSchema;
}

const newSchemaElement = (
uiSchemaElement: EditorUISchemaElement,
schema: JsonSchema
) => ({
type: NEW_SCHEMA_ELEMENT,
uiSchemaElement,
schema,
});

export interface NewUISchemaElement {
type: 'newUiSchemaElement';
Expand Down Expand Up @@ -51,7 +72,11 @@ const moveUISchemaElement = (
schema,
});

export const DndItems = { newUISchemaElement, moveUISchemaElement };
export const DndItems = {
newUISchemaElement,
moveUISchemaElement,
newSchemaElement,
};

export const canDropIntoLayout = (
item: NewUISchemaElement,
Expand Down
27 changes: 27 additions & 0 deletions jsonforms-editor/src/core/model/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
* ---------------------------------------------------------------------
*/
import { JsonSchema } from '@jsonforms/core';

import { EditorUISchemaElement } from './uischema';

export type UiSchemaAction = AddUnscopedElementToLayout | UpdateUiSchemaElement;
Expand All @@ -14,6 +16,7 @@ export type CombinedAction =
| SetSchemaAction
| SetSchemasAction
| AddScopedElementToLayout
| AddSchemaElementToLayout
| MoveUiSchemaElement
| RemoveUiSchemaElement
| AddDetail;
Expand All @@ -26,6 +29,8 @@ export const SET_UISCHEMA: 'jsonforms-editor/SET_UISCHEMA' =
'jsonforms-editor/SET_UISCHEMA';
export const SET_SCHEMAS: 'jsonforms-editor/SET_SCHEMAS' =
'jsonforms-editor/SET_SCHEMAS';
export const ADD_SCHEMA_ELEMENT_TO_LAYOUT: 'jsonforms-editor/ADD_SCHEMA_ELEMENT_TO_LAYOUT' =
'jsonforms-editor/ADD_SCHEMA_ELEMENT_TO_LAYOUT';
export const ADD_SCOPED_ELEMENT_TO_LAYOUT: 'jsonforms-editor/ADD_SCOPED_ELEMENT_TO_LAYOUT' =
'jsonforms-editor/ADD_SCOPED_ELEMENT_TO_LAYOUT';
export const ADD_UNSCOPED_ELEMENT_TO_LAYOUT: 'jsonforms-editor/ADD_UNSCOPED_ELEMENT_TO_LAYOUT' =
Expand Down Expand Up @@ -55,6 +60,14 @@ export interface SetSchemasAction {
uiSchema: any;
}

export interface AddSchemaElementToLayout {
type: 'jsonforms-editor/ADD_SCHEMA_ELEMENT_TO_LAYOUT';
uiSchemaElement: EditorUISchemaElement;
layoutUUID: string;
schema: JsonSchema;
index: number;
}

export interface AddScopedElementToLayout {
type: 'jsonforms-editor/ADD_SCOPED_ELEMENT_TO_LAYOUT';
uiSchemaElement: EditorUISchemaElement;
Expand Down Expand Up @@ -111,6 +124,19 @@ const setSchemas = (schema: any, uiSchema: any) => ({
uiSchema,
});

const addSchemaElementToLayout = (
uiSchemaElement: EditorUISchemaElement,
layoutUUID: string,
index: number,
schema: JsonSchema
) => ({
type: ADD_SCHEMA_ELEMENT_TO_LAYOUT,
uiSchemaElement,
layoutUUID,
index,
schema,
});

const addScopedElementToLayout = (
uiSchemaElement: EditorUISchemaElement,
layoutUUID: string,
Expand Down Expand Up @@ -177,4 +203,5 @@ export const Actions = {
removeUiSchemaElement,
updateUISchemaElement,
addDetail,
addSchemaElementToLayout,
};
56 changes: 55 additions & 1 deletion jsonforms-editor/src/core/model/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { assign } from 'lodash';
import { withCloneTree, withCloneTrees } from '../util/clone';
import {
findByUUID,
getPathString,
getRoot,
isUUIDError,
linkElements,
Expand All @@ -18,6 +19,7 @@ import {
} from '../util/schemasUtil';
import {
ADD_DETAIL,
ADD_SCHEMA_ELEMENT_TO_LAYOUT,
ADD_SCOPED_ELEMENT_TO_LAYOUT,
ADD_UNSCOPED_ELEMENT_TO_LAYOUT,
CombinedAction,
Expand All @@ -30,7 +32,13 @@ import {
UiSchemaAction,
UPDATE_UISCHEMA_ELEMENT,
} from './actions';
import { buildSchemaTree, cleanLinkedElements, SchemaElement } from './schema';
import {
buildJsonSchema,
buildSchemaTree,
cleanLinkedElements,
isObjectElement,
SchemaElement,
} from './schema';
import {
buildEditorUiSchemaTree,
cleanUiSchemaLinks,
Expand Down Expand Up @@ -118,6 +126,51 @@ export const combinedReducer = (state: EditorState, action: CombinedAction) => {
buildSchemaTree(action.schema),
buildEditorUiSchemaTree(action.uiSchema)
);
case ADD_SCHEMA_ELEMENT_TO_LAYOUT:
return withCloneTree(
state.uiSchema,
action.layoutUUID,
state,
(newUiSchema) => {
const newUIElement = action.uiSchemaElement;
newUIElement.parent = newUiSchema;
(newUiSchema as EditorLayout).elements.splice(
action.index,
0,
newUIElement
);
const currentJsonSchema = buildJsonSchema(state.schema!);
const maxPropNumber =
Object.keys(currentJsonSchema.properties!)
.filter((propName) => propName.startsWith('prop_'))
.map((propName) => Number.parseInt(propName.substr(5)))
.reduce(
(maxValue, propNumber) => Math.max(maxValue, propNumber),
0
) + 1;
const propName = `prop_${maxPropNumber}`;
currentJsonSchema.properties![propName] = action.schema;
const newSchema = buildSchemaTree(currentJsonSchema);
if (!isObjectElement(newSchema)) {
return state;
}

if (
!linkElements(newUIElement, newSchema.properties.get(propName)!)
) {
console.error('Could not add new UI element', newUIElement);
return state;
}
(newUIElement as any).scope = `#/${getPathString(
newSchema.properties.get(propName)
)}`;

return {
schema: getRoot(newSchema as SchemaElement),
uiSchema: getRoot(newUiSchema),
};
}
);
case ADD_SCOPED_ELEMENT_TO_LAYOUT:
return withCloneTrees(
state.uiSchema,
Expand Down Expand Up @@ -364,6 +417,7 @@ export const editorReducer = (state: EditorState, action: EditorAction) => {
case SET_SCHEMA:
case SET_UISCHEMA:
case SET_SCHEMAS:
case ADD_SCHEMA_ELEMENT_TO_LAYOUT:
case ADD_SCOPED_ELEMENT_TO_LAYOUT:
case MOVE_UISCHEMA_ELEMENT:
case REMOVE_UISCHEMA_ELEMENT:
Expand Down
16 changes: 14 additions & 2 deletions jsonforms-editor/src/core/renderers/DroppableLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
canMoveSchemaElementTo,
MOVE_UI_SCHEMA_ELEMENT,
MoveUISchemaElement,
NEW_SCHEMA_ELEMENT,
NEW_UI_SCHEMA_ELEMENT,
NewUISchemaElement,
} from '../dnd';
Expand Down Expand Up @@ -125,10 +126,11 @@ const useDropPointStyles = makeStyles((theme) => ({
const DropPoint: React.FC<DropPointProps> = ({ layout, index }) => {
const dispatch = useDispatch();
const rootSchema = useSchema();
const [{ isOver, uiSchemaElement, schemaUUID }, drop] = useDrop({
accept: [NEW_UI_SCHEMA_ELEMENT, MOVE_UI_SCHEMA_ELEMENT],
const [{ isOver, uiSchemaElement, schemaUUID, schema }, drop] = useDrop({
accept: [NEW_UI_SCHEMA_ELEMENT, MOVE_UI_SCHEMA_ELEMENT, NEW_SCHEMA_ELEMENT],
canDrop: (item, monitor) => {
switch (item.type) {
case NEW_SCHEMA_ELEMENT:
case NEW_UI_SCHEMA_ELEMENT:
return canDropIntoLayout(
item as NewUISchemaElement,
Expand All @@ -149,6 +151,7 @@ const DropPoint: React.FC<DropPointProps> = ({ layout, index }) => {
isOver: !!mon.isOver() && mon.canDrop(),
uiSchemaElement: mon.getItem()?.uiSchemaElement,
schemaUUID: mon.getItem()?.schemaUUID,
schema: mon.getItem()?.schema,
}),
drop: (item) => {
switch (item.type) {
Expand Down Expand Up @@ -180,6 +183,15 @@ const DropPoint: React.FC<DropPointProps> = ({ layout, index }) => {
)
);
break;
case NEW_SCHEMA_ELEMENT:
dispatch(
Actions.addSchemaElementToLayout(
uiSchemaElement,
layout.uuid,
index,
schema
)
);
}
},
});
Expand Down
80 changes: 80 additions & 0 deletions jsonforms-editor/src/palette-panel/components/CreateSchemaTree.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* ---------------------------------------------------------------------
* Copyright (c) 2020 EclipseSource Munich
* Licensed under MIT
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
* ---------------------------------------------------------------------
*/
import { JsonSchema } from '@jsonforms/core';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { useDrag } from 'react-dnd';
import { v4 as uuid } from 'uuid';

import { DndItems } from '../../core/dnd';
import { StyledTreeItem, StyledTreeView } from './Tree';

interface CreateSchemaTreeProps {
schema: JsonSchema;
label: string;
icon?: React.ReactNode;
}

const CreateSchemaTreeItem: React.FC<CreateSchemaTreeProps> = ({
schema,
label,
icon,
}) => {
const uiSchemaElement = {
type: 'Control',
uuid: uuid(),
};
const [{ isDragging }, drag] = useDrag({
item: DndItems.newSchemaElement(uiSchemaElement, schema),
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
}),
});
if (!schema.type || Array.isArray(schema.type)) {
return <></>;
}
return (
<div ref={drag} data-cy={`${schema.type}-source`}>
<StyledTreeItem
key={schema.type}
nodeId={schema.type}
label={label}
icon={icon}
isDragging={isDragging}
></StyledTreeItem>
</div>
);
};

export const CreateSchemaTree: React.FC<{}> = () => {
return (
<div>
<Typography variant='h6' color='inherit' noWrap>
Create Schema
</Typography>
<StyledTreeView defaultExpanded={['']}>
<CreateSchemaTreeItem
schema={{ type: 'string' }}
label='String'
></CreateSchemaTreeItem>
<CreateSchemaTreeItem
schema={{ type: 'boolean' }}
label='Boolean'
></CreateSchemaTreeItem>
<CreateSchemaTreeItem
schema={{ type: 'integer' }}
label='Integer'
></CreateSchemaTreeItem>
<CreateSchemaTreeItem
schema={{ type: 'number' }}
label='Number'
></CreateSchemaTreeItem>
</StyledTreeView>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Actions, SchemaElement, toPrintableObject } from '../../core/model';
import { buildDebugUISchema } from '../../core/model/uischema';
import { useExportSchema, useExportUiSchema } from '../../core/util/hooks';
import { env } from '../../env';
import { CreateSchemaTree } from './CreateSchemaTree';
import { SchemaJson, UpdateResult } from './SchemaJson';
import { SchemaTreeView } from './SchemaTree';
import { UIElementsTree } from './UIElementsTree';
Expand Down Expand Up @@ -101,6 +102,7 @@ export const PalettePanel = () => {
className={classes.uiElementsTree}
elements={paletteService.getPaletteElements()}
/>
<CreateSchemaTree></CreateSchemaTree>
<SchemaTreeView schema={schema} />
</TabContent>
<TabContent index={1} currentIndex={selectedTab}>
Expand Down