Skip to content

Commit

Permalink
Add support for inline editing
Browse files Browse the repository at this point in the history
- allow to modify scope of controls
- allow to modify label if controls
- allow to modify label of groups
- allow to modify label of labels
  • Loading branch information
eneufeld committed Mar 7, 2021
1 parent 62a7d8d commit 722dbc5
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 20 deletions.
20 changes: 18 additions & 2 deletions jsonforms-editor/src/core/api/paletteService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,19 @@

import React from 'react';

import { GroupIcon, HorizontalIcon, LabelIcon, VerticalIcon } from '../icons';
import {
ControlIcon,
GroupIcon,
HorizontalIcon,
LabelIcon,
VerticalIcon,
} from '../icons';
import { EditorUISchemaElement } from '../model/uischema';
import { createLabel, createLayout } from '../util/generators/uiSchema';
import {
createEmptyControl,
createLabel,
createLayout,
} from '../util/generators/uiSchema';

export interface PaletteService {
getPaletteElements(): PaletteElement[];
Expand Down Expand Up @@ -48,6 +58,12 @@ const paletteElements: PaletteElement[] = [
icon: <LabelIcon />,
uiSchemaElementProvider: () => createLabel(),
},
{
type: 'Control',
label: 'Control',
icon: <ControlIcon />,
uiSchemaElementProvider: () => createEmptyControl(),
},
];

export class DefaultPaletteService implements PaletteService {
Expand Down
95 changes: 95 additions & 0 deletions jsonforms-editor/src/core/components/EditableControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* ---------------------------------------------------------------------
* Copyright (c) 2020 EclipseSource Munich
* Licensed under MIT
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
* ---------------------------------------------------------------------
*/

import { Grid, TextField } from '@material-ui/core';
import MenuItem from '@material-ui/core/MenuItem';
import React from 'react';

import { useDispatch, useSchema } from '../context';
import {
Actions,
getChildren,
getScope,
isArrayElement,
isObjectElement,
SchemaElement,
} from '../model';
import { EditorControl } from '../model/uischema';

interface EditableControlProps {
uischema: EditorControl;
}
export const EditableControl: React.FC<EditableControlProps> = ({
uischema,
}) => {
const dispatch = useDispatch();
const baseSchema = useSchema() as SchemaElement;
const handleLabelChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
Actions.updateUISchemaElement(uischema.uuid, {
label: event.target.value,
})
);
};
const handleScopeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
Actions.changeControlScope(
uischema.uuid,
(event.target.value as any).scope,
(event.target.value as any).uuid
)
);
};
const scopes = getChildren(baseSchema)
.flatMap((child) => {
// if the child is the only item of an array, use its children instead
if (
(isObjectElement(child) &&
isArrayElement(child.parent) &&
child.parent.items === child) ||
isObjectElement(child)
) {
return getChildren(child);
}
return [child];
})
.map((e) => ({ scope: `#${getScope(e)}`, uuid: e.uuid }));
const scopeValues = scopes.filter((s) => s.scope.endsWith(uischema.scope));
const scopeValue =
scopeValues && scopeValues.length === 1 ? scopeValues[0] : '';
return (
<Grid container direction={'column'}>
<Grid item xs>
<TextField
id='filled-name'
label='Label'
value={uischema.label ?? ''}
onChange={handleLabelChange}
fullWidth
/>
</Grid>
<Grid item xs>
<TextField
id='standard-select-currency'
select
label='Scope'
value={scopeValue}
onChange={handleScopeChange}
fullWidth
helperText='Please select your scope'
>
{scopes.map((scope) => (
<MenuItem key={scope.scope} value={scope as any}>
{scope.scope}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
);
};
2 changes: 1 addition & 1 deletion jsonforms-editor/src/core/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const Layout: React.FC<LayoutProps> = ({
paletteTabs,
children,
}) => {
const [open, setOpen] = React.useState(true);
const [open, setOpen] = React.useState(false);
const [selectedTabName, setSelectedTabName] = React.useState<string>(
paletteTabs ? paletteTabs[0].name : ''
);
Expand Down
1 change: 1 addition & 0 deletions jsonforms-editor/src/core/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from './ErrorDialog';
export * from './OkCancelDialog';
export * from './ExportDialog';
export * from './ShowMoreLess';
export * from './EditableControl';
19 changes: 18 additions & 1 deletion jsonforms-editor/src/core/model/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export type CombinedAction =
| AddScopedElementToLayout
| MoveUiSchemaElement
| RemoveUiSchemaElement
| AddDetail;
| AddDetail
| ChangeControlScope;

export type EditorAction = UiSchemaAction | CombinedAction;

Expand All @@ -38,6 +39,8 @@ export const UPDATE_UISCHEMA_ELEMENT: 'jsonforms-editor/UPDATE_UISCHEMA_ELEMENT'
'jsonforms-editor/UPDATE_UISCHEMA_ELEMENT';
export const ADD_DETAIL: 'jsonforms-editor/ADD_DETAIL' =
'jsonforms-editor/ADD_DETAIL';
export const CHANGE_CONTROL_SCOPE: 'jsonforms-editor/CHANGE_CONTROL_SCOPE' =
'jsonforms-editor/CHANGE_CONTROL_SCOPE';

export interface SetSchemaAction {
type: 'jsonforms-editor/SET_SCHEMA';
Expand Down Expand Up @@ -89,6 +92,13 @@ export interface UpdateUiSchemaElement {
changedProperties: { [key: string]: any };
}

export interface ChangeControlScope {
type: 'jsonforms-editor/CHANGE_CONTROL_SCOPE';
elementUUID: string;
scope: string;
schemaUUID: string;
}

export interface AddDetail {
type: 'jsonforms-editor/ADD_DETAIL';
uiSchemaElementId: string;
Expand Down Expand Up @@ -167,6 +177,12 @@ const addDetail = (
detail,
});

const changeControlScope = (
elementUUID: string,
scope: string,
schemaUUID: string
) => ({ type: CHANGE_CONTROL_SCOPE, elementUUID, scope, schemaUUID });

export const Actions = {
setSchema,
setUiSchema,
Expand All @@ -177,4 +193,5 @@ export const Actions = {
removeUiSchemaElement,
updateUISchemaElement,
addDetail,
changeControlScope,
};
20 changes: 20 additions & 0 deletions jsonforms-editor/src/core/model/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ADD_DETAIL,
ADD_SCOPED_ELEMENT_TO_LAYOUT,
ADD_UNSCOPED_ELEMENT_TO_LAYOUT,
CHANGE_CONTROL_SCOPE,
CombinedAction,
EditorAction,
MOVE_UISCHEMA_ELEMENT,
Expand Down Expand Up @@ -118,6 +119,24 @@ export const combinedReducer = (state: EditorState, action: CombinedAction) => {
buildSchemaTree(action.schema),
buildEditorUiSchemaTree(action.uiSchema)
);
case CHANGE_CONTROL_SCOPE:
return withCloneTrees(
state.uiSchema,
action.elementUUID,
state.schema,
action.schemaUUID,
state,
(newUiSchema, newSchema) => {
if (!newSchema || !newUiSchema || !isEditorControl(newUiSchema))
return state;
linkElements(newUiSchema, newSchema);
newUiSchema.scope = action.scope;
return {
schema: getRoot(newSchema),
uiSchema: getRoot(newUiSchema as EditorUISchemaElement),
};
}
);
case ADD_SCOPED_ELEMENT_TO_LAYOUT:
return withCloneTrees(
state.uiSchema,
Expand Down Expand Up @@ -361,6 +380,7 @@ export const editorReducer = (state: EditorState, action: EditorAction) => {
schema: state.schema,
uiSchema: uiSchemaReducer(state.uiSchema, action),
};
case CHANGE_CONTROL_SCOPE:
case SET_SCHEMA:
case SET_UISCHEMA:
case SET_SCHEMAS:
Expand Down
39 changes: 23 additions & 16 deletions jsonforms-editor/src/core/renderers/DroppableArrayControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
JsonFormsDispatch,
withJsonFormsArrayControlProps,
} from '@jsonforms/react';
import { makeStyles, Typography } from '@material-ui/core';
import { Grid, makeStyles, Typography } from '@material-ui/core';
import React, { useMemo } from 'react';
import { useDrop } from 'react-dnd';

import { EditableControl } from '../components';
import { useDispatch, useSchema } from '../context';
import {
canDropIntoScope,
Expand Down Expand Up @@ -96,22 +97,28 @@ const DroppableArrayControl: React.FC<DroppableArrayControlProps> = ({
return renderers && [...renderers, DroppableElementRegistration];
}, [renderers]);

if (!uischema.options?.detail) {
return (
<Typography ref={drop} className={classes.root}>
Default array layout. Drag and drop an item here to customize array
layout.
</Typography>
);
}
return (
<JsonFormsDispatch
schema={schema}
uischema={uischema.options.detail}
path={path}
renderers={renderersToUse}
cells={cells}
/>
<Grid container direction={'column'}>
<Grid item>
<EditableControl uischema={uischema} />
</Grid>
<Grid item>
{!uischema.options?.detail ? (
<Typography ref={drop} className={classes.root}>
Default array layout. Drag and drop an item here to customize array
layout.
</Typography>
) : (
<JsonFormsDispatch
schema={schema}
uischema={uischema.options.detail}
path={path}
renderers={renderersToUse}
cells={cells}
/>
)}
</Grid>
</Grid>
);
};

Expand Down
28 changes: 28 additions & 0 deletions jsonforms-editor/src/core/renderers/DroppableControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* ---------------------------------------------------------------------
* Copyright (c) 2020 EclipseSource Munich
* Licensed under MIT
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
* ---------------------------------------------------------------------
*/

import { ControlProps, isControl, rankWith } from '@jsonforms/core';
import { withJsonFormsControlProps } from '@jsonforms/react';
import React from 'react';

import { EditableControl } from '../components';
import { EditorControl } from '../model/uischema';

interface DroppableControlProps extends ControlProps {
uischema: EditorControl;
}
const DroppableControl: React.FC<DroppableControlProps> = ({ uischema }) => {
return <EditableControl uischema={uischema} />;
};

export const DroppableControlRegistration = {
tester: rankWith(40, isControl), // less than DroppableElement
renderer: withJsonFormsControlProps(
DroppableControl as React.FC<ControlProps>
),
};
18 changes: 18 additions & 0 deletions jsonforms-editor/src/core/renderers/DroppableGroupLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ import {
CardHeader,
Grid,
makeStyles,
TextField,
Typography,
} from '@material-ui/core';
import React from 'react';

import { useDispatch } from '../context';
import { Actions } from '../model';
import { EditorLayout } from '../model/uischema';
import { DroppableLayout } from './DroppableLayout';

Expand All @@ -39,6 +42,14 @@ const Group: React.FC<LayoutProps> = (props) => {
const { uischema } = props;
const groupLayout = uischema as GroupLayout & EditorLayout;
const classes = useStyles();
const dispatch = useDispatch();
const handleLabelChange = (event: React.ChangeEvent<HTMLInputElement>) => {
dispatch(
Actions.updateUISchemaElement(groupLayout.uuid, {
label: event.target.value,
})
);
};
return (
<Card>
<CardHeader
Expand Down Expand Up @@ -66,6 +77,13 @@ const Group: React.FC<LayoutProps> = (props) => {
)}
></CardHeader>
<CardContent>
<TextField
id='filled-name'
label='Label'
value={groupLayout.label ?? ''}
onChange={handleLabelChange}
fullWidth
/>
<DroppableLayout {...props} layout={groupLayout} direction={'column'} />
</CardContent>
</Card>
Expand Down
Loading

0 comments on commit 722dbc5

Please sign in to comment.