Skip to content
This repository was archived by the owner on Nov 19, 2025. It is now read-only.

Commit 62a7d8d

Browse files
committed
Add inline button for add operation
1 parent abcd1aa commit 62a7d8d

File tree

15 files changed

+277
-162
lines changed

15 files changed

+277
-162
lines changed

jsonforms-editor/src/JsonFormsEditor.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,10 @@ import {
2929
defaultEditorRenderers,
3030
defaultPreviewTabs,
3131
EditorPanel,
32-
} from './editor';
33-
import { PreviewTab } from './editor/components/EditorPanel';
34-
import {
35-
defaultPalettePanelTabs,
36-
PalettePanel,
3732
PaletteTab,
38-
} from './palette-panel';
33+
} from './editor';
34+
import { PreviewTab } from './editor';
35+
import { defaultPalettePanelTabs, PalettePanel } from './palette-panel';
3936
import { defaultPropertyRenderers, PropertiesPanel } from './properties';
4037
import {
4138
PropertiesService,
@@ -140,13 +137,13 @@ export const JsonFormsEditor: React.FC<JsonFormsEditorProps> = ({
140137
schemaService,
141138
paletteService,
142139
propertiesService,
140+
propertyRenderers,
143141
}}
144142
>
145143
<DndProvider backend={Backend}>
146144
<JsonFormsEditorUi
147145
editorRenderers={editorRenderers}
148146
previewTabs={previewTabs}
149-
propertyRenderers={propertyRenderers}
150147
header={headerComponent}
151148
footer={footerComponent}
152149
paletteTabs={paletteTabs ?? undefined}
@@ -159,15 +156,13 @@ export const JsonFormsEditor: React.FC<JsonFormsEditorProps> = ({
159156
interface JsonFormsEditorUiProps {
160157
previewTabs?: PreviewTab[];
161158
editorRenderers: JsonFormsRendererRegistryEntry[];
162-
propertyRenderers: JsonFormsRendererRegistryEntry[];
163159
header?: ComponentType;
164160
footer?: ComponentType;
165161
paletteTabs?: PaletteTab[];
166162
}
167163
const JsonFormsEditorUi: React.FC<JsonFormsEditorUiProps> = ({
168164
previewTabs,
169165
editorRenderers,
170-
propertyRenderers,
171166
header,
172167
footer,
173168
paletteTabs,
@@ -177,7 +172,7 @@ const JsonFormsEditorUi: React.FC<JsonFormsEditorUiProps> = ({
177172
<Layout
178173
HeaderComponent={header}
179174
FooterComponent={footer}
180-
drawerContent={<PalettePanel propertyRenderers={propertyRenderers} />}
175+
paletteTabs={paletteTabs}
181176
>
182177
<ReflexContainer
183178
orientation='vertical'
@@ -191,7 +186,7 @@ const JsonFormsEditorUi: React.FC<JsonFormsEditorUiProps> = ({
191186
<ReflexSplitter propagate />
192187
<ReflexElement minSize={200} flex={1}>
193188
<div className={`${classes.pane} ${classes.rightPane}`}>
194-
<PropertiesPanel previewTabs={previewTabs} paletteTabs={paletteTabs} />
189+
<PropertiesPanel previewTabs={previewTabs} />
195190
</div>
196191
</ReflexElement>
197192
</ReflexContainer>

jsonforms-editor/src/core/components/Layout.tsx

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@
55
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
66
* ---------------------------------------------------------------------
77
*/
8+
import { JsonFormsRendererRegistryEntry } from '@jsonforms/core';
89
import AppBar from '@material-ui/core/AppBar';
910
import Divider from '@material-ui/core/Divider';
1011
import Drawer from '@material-ui/core/Drawer';
1112
import IconButton from '@material-ui/core/IconButton';
1213
import { makeStyles } from '@material-ui/core/styles';
1314
import Toolbar from '@material-ui/core/Toolbar';
15+
import Typography from '@material-ui/core/Typography';
1416
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
1517
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
16-
import React from 'react';
18+
import React, { useCallback } from 'react';
1719

18-
import { DrawerContextInstance } from '../context';
20+
import { PaletteTab } from '../../editor';
21+
import { PalettePanel } from '../../palette-panel';
1922

2023
const footerHeight = '40px';
2124
const drawerWidth = '400px';
@@ -33,6 +36,7 @@ const useStyles = makeStyles((theme) => ({
3336
container: {
3437
height: '100vh',
3538
display: 'flex',
39+
overflow: 'hidden',
3640
},
3741
footer: {
3842
zIndex: theme.zIndex.drawer + 1,
@@ -74,29 +78,41 @@ const useStyles = makeStyles((theme) => ({
7478
width: theme.spacing(9) + 1,
7579
},
7680
},
81+
drawerContainer: {
82+
overflow: 'auto',
83+
},
84+
drawerTitle: (props: { isOpen: boolean }) => ({
85+
display: 'flex',
86+
flexDirection: 'row',
87+
alignItems: 'center',
88+
justifyContent: props.isOpen ? 'flex-start' : 'center',
89+
}),
7790
}));
7891

7992
interface LayoutProps {
8093
HeaderComponent?: React.ComponentType;
8194
FooterComponent?: React.ComponentType;
82-
drawerContent?: React.ReactNode;
95+
paletteTabs?: PaletteTab[];
8396
}
8497

8598
export const Layout: React.FC<LayoutProps> = ({
8699
HeaderComponent,
87100
FooterComponent,
88-
drawerContent,
101+
paletteTabs,
89102
children,
90103
}) => {
91104
const [open, setOpen] = React.useState(true);
92-
93-
// const toggleDrawerClose = () => {
94-
// setOpen(!open);
95-
// };
105+
const [selectedTabName, setSelectedTabName] = React.useState<string>(
106+
paletteTabs ? paletteTabs[0].name : ''
107+
);
108+
const toggleDrawerClose = () => {
109+
setOpen(!open);
110+
};
96111
const openDrawer = () => {
97112
setOpen(true);
98113
};
99-
const classes = useStyles();
114+
const onSelected = useCallback((tabName) => setSelectedTabName(tabName), []);
115+
const classes = useStyles({ isOpen: open });
100116
const classNameOpen = open ? classes.drawerOpen : classes.drawerClose;
101117

102118
return (
@@ -111,15 +127,25 @@ export const Layout: React.FC<LayoutProps> = ({
111127
paper: classNameOpen,
112128
}}
113129
>
114-
<DrawerContextInstance.Provider value={{ open, openDrawer }}>
115-
<Toolbar />
116-
{/* <IconButton onClick={toggleDrawerClose}>
117-
{open ? <ChevronLeftIcon /> : <ChevronRightIcon />}
118-
</IconButton>
119-
<Divider /> */}
120-
{drawerContent}
121-
<div className={classes.fakeFooter} />
122-
</DrawerContextInstance.Provider>
130+
<Toolbar />
131+
<div className={classes.drawerContainer}>
132+
<div className={classes.drawerTitle}>
133+
<IconButton onClick={toggleDrawerClose}>
134+
{open ? <ChevronLeftIcon /> : <ChevronRightIcon />}
135+
</IconButton>
136+
{open ? (
137+
<Typography variant='h5'>{selectedTabName}</Typography>
138+
) : null}
139+
</div>
140+
<Divider />
141+
<PalettePanel
142+
paletteTabs={paletteTabs}
143+
open={open}
144+
openDrawer={openDrawer}
145+
onSelected={onSelected}
146+
/>
147+
</div>
148+
<div className={classes.fakeFooter} />
123149
</Drawer>
124150
<main className={classes.main}>
125151
<Toolbar />

jsonforms-editor/src/core/context/context.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE
66
* ---------------------------------------------------------------------
77
*/
8+
import { JsonFormsRendererRegistryEntry } from '@jsonforms/core';
89
import React, { useContext } from 'react';
910

1011
import { PropertiesService } from '../../properties/propertiesService';
@@ -24,6 +25,7 @@ export interface EditorContext {
2425
dispatch: (action: EditorAction) => void;
2526
selection: SelectedElement;
2627
setSelection: (selection: SelectedElement) => void;
28+
propertyRenderers: JsonFormsRendererRegistryEntry[];
2729
}
2830

2931
/**We always use a provider so default can be undefined*/
@@ -35,16 +37,6 @@ export const EditorContextInstance = React.createContext<EditorContext>(
3537

3638
export const useEditorContext = (): EditorContext =>
3739
useContext(EditorContextInstance);
38-
export interface DrawerContext {
39-
open: boolean;
40-
openDrawer: () => void;
41-
}
42-
export const DrawerContextInstance = React.createContext<DrawerContext>(
43-
defaultContext
44-
);
45-
46-
export const useDrawerContext = (): DrawerContext =>
47-
useContext(DrawerContextInstance);
4840

4941
export const useGitLabService = (): SchemaService => {
5042
const { schemaService } = useEditorContext();
@@ -83,3 +75,8 @@ export const usePropertiesService = (): PropertiesService => {
8375
const { propertiesService } = useEditorContext();
8476
return propertiesService;
8577
};
78+
79+
export const usePropertyRenderers = (): JsonFormsRendererRegistryEntry[] => {
80+
const { propertyRenderers } = useEditorContext();
81+
return propertyRenderers;
82+
};

jsonforms-editor/src/core/renderers/DroppableLayout.tsx

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ import {
1818
withJsonFormsLayoutProps,
1919
} from '@jsonforms/react';
2020
import { Grid, makeStyles } from '@material-ui/core';
21+
import EditIcon from '@material-ui/icons/Edit';
22+
import SpeedDial from '@material-ui/lab/SpeedDial';
23+
import SpeedDialAction from '@material-ui/lab/SpeedDialAction';
24+
import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon';
2125
import React from 'react';
2226
import { useDrop } from 'react-dnd';
2327

24-
import { useDispatch, useSchema } from '../context';
28+
import { useDispatch, usePaletteService, useSchema } from '../context';
2529
import {
2630
canDropIntoLayout,
2731
canMoveSchemaElementTo,
@@ -73,12 +77,12 @@ export const DroppableLayout: React.FC<DroppableLayoutProps> = ({
7377
spacing={direction === 'row' ? 2 : 0}
7478
wrap='nowrap'
7579
>
76-
<DropPoint index={0} layout={layout} key={`${path}-${0}-drop`} />
80+
<DropPoint index={0} layout={layout} key={`${layout.uuid}-${0}-drop`} />
7781
{layout.elements.map((child, index) => (
78-
<React.Fragment key={`${path}-${index}-fragment`}>
82+
<React.Fragment key={`${layout.uuid}-${index}-fragment`}>
7983
<Grid
8084
item
81-
key={`${path}-${index}`}
85+
key={`${layout.uuid}-${index}`}
8286
className={classes.jsonformsGridItem}
8387
xs
8488
>
@@ -95,14 +99,70 @@ export const DroppableLayout: React.FC<DroppableLayoutProps> = ({
9599
<DropPoint
96100
index={index + 1}
97101
layout={layout}
98-
key={`${path}-${index + 1}-drop`}
102+
key={`${layout.uuid}-${index + 1}-drop`}
99103
/>
100104
</React.Fragment>
101105
))}
102106
</Grid>
103107
);
104108
};
105109

110+
const useActionBarStyles = makeStyles((theme) => ({
111+
speedDial: {
112+
bottom: theme.spacing(2),
113+
right: theme.spacing(2),
114+
},
115+
}));
116+
interface InlineActionBarProps {
117+
layout: EditorLayout;
118+
index: number;
119+
}
120+
const InlineActionBar = ({ layout, index }: InlineActionBarProps) => {
121+
const classes = useActionBarStyles();
122+
const paletteService = usePaletteService();
123+
const dispatch = useDispatch();
124+
const [open, setOpen] = React.useState(false);
125+
const handleOpen = () => {
126+
setOpen(true);
127+
};
128+
129+
const handleClose = () => {
130+
setOpen(false);
131+
};
132+
return (
133+
<SpeedDial
134+
ariaLabel='SpeedDial openIcon example'
135+
className={classes.speedDial}
136+
icon={<SpeedDialIcon openIcon={<EditIcon />} />}
137+
onClose={handleClose}
138+
onOpen={handleOpen}
139+
open={open}
140+
direction={layout.type === 'HorizontalLayout' ? 'down' : 'right'}
141+
>
142+
{paletteService
143+
.getPaletteElements()
144+
.map(({ type, label, icon, uiSchemaElementProvider }) => (
145+
<SpeedDialAction
146+
key={type}
147+
color='primary'
148+
icon={icon as any}
149+
tooltipTitle={label}
150+
onClick={() => {
151+
dispatch(
152+
Actions.addUnscopedElementToLayout(
153+
uiSchemaElementProvider(),
154+
layout.uuid,
155+
index
156+
)
157+
);
158+
handleClose();
159+
}}
160+
/>
161+
))}
162+
</SpeedDial>
163+
);
164+
};
165+
106166
interface DropPointProps {
107167
layout: EditorLayout;
108168
index: number;
@@ -116,10 +176,16 @@ const useDropPointStyles = makeStyles((theme) => ({
116176
: 'none',
117177
backgroundSize: 'calc(10 * 1px) calc(10 * 1px)',
118178
backgroundClip: 'content-box',
119-
minWidth: '2em',
120-
minHeight: props.isOver ? '8em' : '2em',
121-
maxWidth: props.fillWidth || props.isOver ? 'inherit' : '2em',
179+
minWidth: '3em',
180+
minHeight: props.isOver ? '8em' : '3em',
181+
maxWidth: props.fillWidth ? 'inherit' : '3em',
122182
}),
183+
actions: {
184+
opacity: 0,
185+
'&:hover': {
186+
opacity: 1,
187+
},
188+
},
123189
}));
124190

125191
const DropPoint: React.FC<DropPointProps> = ({ layout, index }) => {
@@ -195,8 +261,13 @@ const DropPoint: React.FC<DropPointProps> = ({ layout, index }) => {
195261
ref={drop}
196262
className={classes.dropPointGridItem}
197263
data-cy={`${getDataPath(layout)}-drop-${index}`}
264+
alignItems='stretch'
198265
xs
199-
></Grid>
266+
>
267+
<Grid className={classes.actions} item xs>
268+
<InlineActionBar layout={layout} index={index} />
269+
</Grid>
270+
</Grid>
200271
);
201272
};
202273

jsonforms-editor/src/editor/components/EditorPanel.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ import React from 'react';
1111

1212
import { Editor } from './Editor';
1313

14-
export interface PreviewTab {
15-
name: string;
16-
Component: React.ComponentType;
17-
}
18-
1914
interface EditorPanelProps {
2015
editorRenderers: JsonFormsRendererRegistryEntry[];
2116
}

0 commit comments

Comments
 (0)