From abcd1aa2d5b162576ffc389f89a306bedc038556 Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Mon, 21 Dec 2020 18:11:20 +0100 Subject: [PATCH 1/3] [WIP] Refactor layout --- app/src/App.tsx | 2 +- jsonforms-editor/src/JsonFormsEditor.tsx | 36 ++-- .../src/core/components/Header.tsx | 4 +- .../src/core/components/Layout.tsx | 101 +++++++++-- jsonforms-editor/src/core/context/context.ts | 10 ++ jsonforms-editor/src/core/dnd/types.ts | 2 +- jsonforms-editor/src/core/util/hooks.ts | 39 +--- .../src/editor/components/EditorElement.tsx | 2 +- .../src/editor/components/EditorPanel.tsx | 53 +----- .../preview/ReactMaterialPreview.tsx | 1 + jsonforms-editor/src/editor/index.ts | 5 +- .../palette-panel/components/PalletePanel.tsx | 169 +++++++++++++----- .../properties/components/PropertiesPanel.tsx | 52 ++++-- 13 files changed, 307 insertions(+), 169 deletions(-) diff --git a/app/src/App.tsx b/app/src/App.tsx index 5029e65..2eb6e07 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -22,7 +22,7 @@ export const App = () => ( schemaService={schemaService} schemaProviders={defaultSchemaProviders} schemaDecorators={defaultSchemaDecorators} - editorTabs={[ + previewTabs={[ { name: 'Preview (React)', Component: ReactMaterialPreview }, { name: 'Preview (Angular)', Component: AngularMaterialPreview }, ]} diff --git a/jsonforms-editor/src/JsonFormsEditor.tsx b/jsonforms-editor/src/JsonFormsEditor.tsx index 2d18c43..48861af 100644 --- a/jsonforms-editor/src/JsonFormsEditor.tsx +++ b/jsonforms-editor/src/JsonFormsEditor.tsx @@ -27,10 +27,10 @@ import { SelectedElement } from './core/selection'; import { tryFindByUUID } from './core/util/schemasUtil'; import { defaultEditorRenderers, - defaultEditorTabs, + defaultPreviewTabs, EditorPanel, } from './editor'; -import { EditorTab } from './editor/components/EditorPanel'; +import { PreviewTab } from './editor/components/EditorPanel'; import { defaultPalettePanelTabs, PalettePanel, @@ -58,6 +58,7 @@ const useStyles = makeStyles((theme) => ({ reflexContainer: { flex: '1', alignItems: 'stretch', + overflow: 'auto', }, })); @@ -65,7 +66,7 @@ interface JsonFormsEditorProps { schemaService?: SchemaService; schemaProviders: PropertySchemasProvider[]; schemaDecorators: PropertySchemasDecorator[]; - editorTabs?: EditorTab[] | null; + previewTabs?: PreviewTab[] | null; paletteService?: PaletteService; paletteTabs?: PaletteTab[] | null; editorRenderers?: JsonFormsRendererRegistryEntry[]; @@ -92,7 +93,7 @@ export const JsonFormsEditor: React.FC = ({ schemaProviders, schemaDecorators, editorRenderers = defaultEditorRenderers, - editorTabs: editorTabsProp = defaultEditorTabs, + previewTabs: previewTabsProp = defaultPreviewTabs, paletteTabs = defaultPalettePanelTabs, propertyRenderers = defaultPropertyRenderers, header = Header, @@ -103,7 +104,7 @@ export const JsonFormsEditor: React.FC = ({ const [propertiesService] = useState( propertiesServiceProvider(schemaProviders, schemaDecorators) ); - const editorTabs = editorTabsProp ?? undefined; + const previewTabs = previewTabsProp ?? undefined; const headerComponent = header ?? undefined; const footerComponent = footer ?? undefined; @@ -144,7 +145,7 @@ export const JsonFormsEditor: React.FC = ({ = ({ }; interface JsonFormsEditorUiProps { - editorTabs?: EditorTab[]; + previewTabs?: PreviewTab[]; editorRenderers: JsonFormsRendererRegistryEntry[]; propertyRenderers: JsonFormsRendererRegistryEntry[]; header?: ComponentType; @@ -164,7 +165,7 @@ interface JsonFormsEditorUiProps { paletteTabs?: PaletteTab[]; } const JsonFormsEditorUi: React.FC = ({ - editorTabs, + previewTabs, editorRenderers, propertyRenderers, header, @@ -173,29 +174,24 @@ const JsonFormsEditorUi: React.FC = ({ }) => { const classes = useStyles(); return ( - + } + > - -
- -
-
-
- +
- +
diff --git a/jsonforms-editor/src/core/components/Header.tsx b/jsonforms-editor/src/core/components/Header.tsx index 59e94e3..1b9ccce 100644 --- a/jsonforms-editor/src/core/components/Header.tsx +++ b/jsonforms-editor/src/core/components/Header.tsx @@ -33,7 +33,7 @@ export const Header: React.FC = () => { const openDownloadDialog = () => setOpen(true); return ( - + <> { uiSchema={uiSchema} /> )} - + ); }; diff --git a/jsonforms-editor/src/core/components/Layout.tsx b/jsonforms-editor/src/core/components/Layout.tsx index 45d8e0d..ab61414 100644 --- a/jsonforms-editor/src/core/components/Layout.tsx +++ b/jsonforms-editor/src/core/components/Layout.tsx @@ -5,46 +5,127 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ -import { makeStyles } from '@material-ui/core'; +import AppBar from '@material-ui/core/AppBar'; +import Divider from '@material-ui/core/Divider'; +import Drawer from '@material-ui/core/Drawer'; +import IconButton from '@material-ui/core/IconButton'; +import { makeStyles } from '@material-ui/core/styles'; +import Toolbar from '@material-ui/core/Toolbar'; +import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; import React from 'react'; +import { DrawerContextInstance } from '../context'; + +const footerHeight = '40px'; +const drawerWidth = '400px'; + const useStyles = makeStyles((theme) => ({ + appBar: { + zIndex: theme.zIndex.drawer + 1, + }, main: { - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2), minHeight: 0, + flexGrow: 1, + display: 'flex', + flexDirection: 'column', }, container: { - display: 'grid', height: '100vh', - gridTemplateAreas: 'header content footer', - gridTemplateColumns: '1fr', - gridTemplateRows: 'auto 1fr auto', + display: 'flex', }, footer: { - padding: theme.spacing(2, 2), + zIndex: theme.zIndex.drawer + 1, + padding: theme.spacing(1, 1), backgroundColor: theme.palette.type === 'light' ? theme.palette.grey[200] : theme.palette.grey[800], + height: footerHeight, + bottom: 0, + left: 'auto', + right: 0, + position: 'fixed', + width: '100%', + }, + fakeFooter: { + marginBottom: footerHeight, + }, + drawer: { + width: drawerWidth, + flexShrink: 0, + whiteSpace: 'nowrap', + }, + drawerOpen: { + width: drawerWidth, + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerClose: { + transition: theme.transitions.create('width', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + overflowX: 'hidden', + width: theme.spacing(7) + 1, + [theme.breakpoints.up('sm')]: { + width: theme.spacing(9) + 1, + }, }, })); interface LayoutProps { HeaderComponent?: React.ComponentType; FooterComponent?: React.ComponentType; + drawerContent?: React.ReactNode; } export const Layout: React.FC = ({ HeaderComponent, FooterComponent, + drawerContent, children, }) => { + const [open, setOpen] = React.useState(true); + + // const toggleDrawerClose = () => { + // setOpen(!open); + // }; + const openDrawer = () => { + setOpen(true); + }; const classes = useStyles(); + const classNameOpen = open ? classes.drawerOpen : classes.drawerClose; + return (
-
{HeaderComponent ? : null}
-
{children}
+ + {HeaderComponent ? : null} + + + + + {/* + {open ? : } + + */} + {drawerContent} +
+ + +
+ + {children} +
+
{FooterComponent ? : null}
diff --git a/jsonforms-editor/src/core/context/context.ts b/jsonforms-editor/src/core/context/context.ts index 31ce140..b722bcc 100644 --- a/jsonforms-editor/src/core/context/context.ts +++ b/jsonforms-editor/src/core/context/context.ts @@ -35,6 +35,16 @@ export const EditorContextInstance = React.createContext( export const useEditorContext = (): EditorContext => useContext(EditorContextInstance); +export interface DrawerContext { + open: boolean; + openDrawer: () => void; +} +export const DrawerContextInstance = React.createContext( + defaultContext +); + +export const useDrawerContext = (): DrawerContext => + useContext(DrawerContextInstance); export const useGitLabService = (): SchemaService => { const { schemaService } = useEditorContext(); diff --git a/jsonforms-editor/src/core/dnd/types.ts b/jsonforms-editor/src/core/dnd/types.ts index f09dbbf..2d09970 100644 --- a/jsonforms-editor/src/core/dnd/types.ts +++ b/jsonforms-editor/src/core/dnd/types.ts @@ -39,7 +39,7 @@ const newUISchemaElement = ( export interface MoveUISchemaElement { type: 'moveUiSchemaElement'; uiSchemaElement: EditorUISchemaElement; - schema?: SchemaElement; + // schema?: SchemaElement; } const moveUISchemaElement = ( diff --git a/jsonforms-editor/src/core/util/hooks.ts b/jsonforms-editor/src/core/util/hooks.ts index 7d1e079..80547fc 100644 --- a/jsonforms-editor/src/core/util/hooks.ts +++ b/jsonforms-editor/src/core/util/hooks.ts @@ -22,7 +22,8 @@ const doBuildUiSchema = (uiSchema: EditorUISchemaElement | undefined) => */ export const useExportSchema = () => { const schema = useSchema(); - return useTransform(schema, doBuildJsonSchema); + // return useTransform(schema, doBuildJsonSchema); + return doBuildJsonSchema(schema); }; /** @@ -30,40 +31,8 @@ export const useExportSchema = () => { */ export const useExportUiSchema = () => { const uiSchema = useUiSchema(); - return useTransform(uiSchema, doBuildUiSchema); -}; - -/** - * Transforms the given element whenever it changes. - */ -export const useTransform = ( - element: T1, - transform: (el: T1) => T2 -) => { - const [transformedElement, setTransformedElement] = useState( - transform(element) - ); - useEffectAfterInit(() => setTransformedElement(transform(element)), [ - element, - transform, - ]); - return transformedElement; -}; - -/** - * Hook similar to `useEffect` with the difference that the effect - * is only executed from the second call onwards. - */ -const useEffectAfterInit = (effect: () => void, dependencies: Array) => { - const firstExecution = useRef(true); - useEffect(() => { - if (firstExecution.current) { - firstExecution.current = false; - return; - } - effect(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [...dependencies]); + // return useTransform(uiSchema, doBuildUiSchema); + return doBuildUiSchema(uiSchema); }; /** Force a rerender */ diff --git a/jsonforms-editor/src/editor/components/EditorElement.tsx b/jsonforms-editor/src/editor/components/EditorElement.tsx index 9f7dfea..a39c24a 100644 --- a/jsonforms-editor/src/editor/components/EditorElement.tsx +++ b/jsonforms-editor/src/editor/components/EditorElement.tsx @@ -79,7 +79,7 @@ export const EditorElement: React.FC = ({ wrappedElement.linkedSchemaElement ); const [{ isDragging }, drag] = useDrag({ - item: DndItems.moveUISchemaElement(wrappedElement, elementSchema), + item: DndItems.moveUISchemaElement(wrappedElement), collect: (monitor) => ({ isDragging: !!monitor.isDragging(), }), diff --git a/jsonforms-editor/src/editor/components/EditorPanel.tsx b/jsonforms-editor/src/editor/components/EditorPanel.tsx index 41ab3a0..54ae245 100644 --- a/jsonforms-editor/src/editor/components/EditorPanel.tsx +++ b/jsonforms-editor/src/editor/components/EditorPanel.tsx @@ -6,63 +6,28 @@ * --------------------------------------------------------------------- */ import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; -import { makeStyles, Tab, Tabs } from '@material-ui/core'; -import React, { useState } from 'react'; +import Typography from '@material-ui/core/Typography'; +import React from 'react'; -import { TabContent } from '../../core/components'; import { Editor } from './Editor'; -const useStyles = makeStyles(() => ({ - editorPanel: { - height: '100%', - display: 'grid', - gridTemplateColumns: '1fr', - gridTemplateRows: 'auto 1fr ', - }, -})); - -export interface EditorTab { +export interface PreviewTab { name: string; Component: React.ComponentType; } interface EditorPanelProps { - editorTabs?: EditorTab[]; editorRenderers: JsonFormsRendererRegistryEntry[]; } export const EditorPanel: React.FC = ({ - editorTabs, editorRenderers, }) => { - const [selectedTab, setSelectedTab] = useState(0); - const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => { - setSelectedTab(newValue); - }; - const classes = useStyles(); return ( -
- - - {editorTabs - ? editorTabs.map((tab) => ( - - )) - : null} - - - - - {editorTabs - ? editorTabs.map((tab, index) => ( - - - - )) - : null} -
+ <> + + Editor + + + ); }; diff --git a/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx b/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx index 656a061..e831a5b 100644 --- a/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx +++ b/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx @@ -19,6 +19,7 @@ import { useExportSchema, useExportUiSchema } from '../../../core/util/hooks'; import { previewOptions } from './options'; export const ReactMaterialPreview: React.FC = () => { + console.log('IM RENDERER'); const schema = useExportSchema(); const uischema = useExportUiSchema(); const editorSchema = useSchema(); diff --git a/jsonforms-editor/src/editor/index.ts b/jsonforms-editor/src/editor/index.ts index 221a0a6..16cf0c8 100644 --- a/jsonforms-editor/src/editor/index.ts +++ b/jsonforms-editor/src/editor/index.ts @@ -15,13 +15,14 @@ import { DroppableHorizontalLayoutRegistration, DroppableVerticalLayoutRegistration, } from '../core/renderers/DroppableLayout'; -import { EditorTab } from './components/EditorPanel'; +import { PreviewTab } from './components/EditorPanel'; import { ReactMaterialPreview } from './components/preview'; export * from './components/EditorPanel'; export { EditorElement } from './components/EditorElement'; -export const defaultEditorTabs: EditorTab[] = [ +export type { PreviewTab } from './components/EditorPanel'; +export const defaultPreviewTabs: PreviewTab[] = [ { name: 'Preview', Component: ReactMaterialPreview }, ]; diff --git a/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx b/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx index 342186d..ef7627b 100644 --- a/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx +++ b/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx @@ -5,18 +5,42 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ -import { makeStyles, Tab, Tabs } from '@material-ui/core'; +import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; +import { makeStyles } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import BallotIcon from '@material-ui/icons/Ballot'; +import ChatIcon from '@material-ui/icons/Chat'; +import CodeIcon from '@material-ui/icons/Code'; +import SettingsIcon from '@material-ui/icons/Settings'; import React, { useState } from 'react'; -import { TabContent } from '../../core/components'; +import { + useDispatch, + useDrawerContext, + usePaletteService, + useSchema, + useUiSchema, +} from '../../core/context'; import { usePaletteService, useSchema } from '../../core/context'; import { SchemaElement } from '../../core/model'; import { JsonSchemaPanel } from './JsonSchemaPanel'; +import { Properties } from '../../properties/components/Properties'; import { SchemaTreeView } from './SchemaTree'; import { UIElementsTree } from './UIElementsTree'; import { UISchemaPanel } from './UISchemaPanel'; +const toText = (object: any) => JSON.stringify(object, null, 2); + const useStyles = makeStyles((theme) => ({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper, + display: 'flex', + height: 224, + }, uiElementsTree: { marginBottom: theme.spacing(1), }, @@ -25,17 +49,24 @@ const useStyles = makeStyles((theme) => ({ display: 'flex', flexDirection: 'column', }, + menu: { + margin: theme.spacing(1), + width: theme.spacing(7) + 1, + maxWidth: theme.spacing(7) + 1, + [theme.breakpoints.up('sm')]: { + width: theme.spacing(9) + 1, + maxWidth: theme.spacing(9) + 1, + }, + }, })); - export interface PaletteTab { name: string; Component: React.ReactElement; } - -export interface PalettePanelProps { +interface PalettePanelProps { + propertyRenderers: JsonFormsRendererRegistryEntry[]; paletteTabs?: PaletteTab[]; } - export const defaultPalettePanelTabs: PaletteTab[] = [ { name: 'JSON Schema', @@ -43,47 +74,103 @@ export const defaultPalettePanelTabs: PaletteTab[] = [ }, { name: 'UI Schema', Component: }, ]; - -export const PalettePanel: React.FC = ({ paletteTabs }) => { +export const PalettePanel = ({ propertyRenderers }: PalettePanelProps) => { + const classes = useStyles(); + const { open, openDrawer } = useDrawerContext(); const [selectedTab, setSelectedTab] = useState(0); - const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => { + const handleTabChange = (newValue: number) => { setSelectedTab(newValue); + if (!open) { + openDrawer(); + } }; const schema: SchemaElement | undefined = useSchema(); const paletteService = usePaletteService(); - const classes = useStyles(); return ( -
- - - {paletteTabs - ? paletteTabs.map((tab) => ( - + + + handleTabChange(0)} + selected={selectedTab === 0} + > + + + + + handleTabChange(1)} + selected={selectedTab === 1} + > + + + + + handleTabChange(2)} + selected={selectedTab === 2} + > + + + + + handleTabChange(3)} + selected={selectedTab === 3} + > + + + + + + + + {open && ( + + {selectedTab === 0 && ( + <> + - )) - : null} - - - - - - {paletteTabs - ? paletteTabs.map((tab, index) => ( - - {tab.Component} - - )) - : null} -
+ {' '} + + )} + {selectedTab === 1 && ( + + )} + {selectedTab === 2 && ( + + )} + {selectedTab === 3 && ( + + )} + + )} + ); }; diff --git a/jsonforms-editor/src/properties/components/PropertiesPanel.tsx b/jsonforms-editor/src/properties/components/PropertiesPanel.tsx index 419623e..9f14ccd 100644 --- a/jsonforms-editor/src/properties/components/PropertiesPanel.tsx +++ b/jsonforms-editor/src/properties/components/PropertiesPanel.tsx @@ -5,24 +5,52 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ -import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; -import { Typography } from '@material-ui/core'; -import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Tab from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; +import React, { useState } from 'react'; -import { Properties } from './Properties'; +import { TabContent } from '../../core/components'; +import { PreviewTab } from '../../editor'; +const useStyles = makeStyles((theme) => ({ + palettePanel: { + height: '100%', + display: 'flex', + flexDirection: 'column', + }, +})); export interface PropertiesPanelProps { - propertyRenderers: JsonFormsRendererRegistryEntry[]; + previewTabs?: PreviewTab[]; } export const PropertiesPanel: React.FC = ({ - propertyRenderers, + previewTabs, }) => { + const [selectedTab, setSelectedTab] = useState(0); + const handleTabChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setSelectedTab(newValue); + }; + const classes = useStyles(); return ( - <> - - Properties - - - +
+ + {previewTabs + ? previewTabs.map((tab) => ( + + )) + : null} + + {previewTabs + ? previewTabs.map((tab, index) => ( + + + + )) + : null} +
); }; From 62a7d8db80e0bcfd79ba8cb289753c5f8cc79b68 Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Sat, 27 Feb 2021 17:24:22 +0100 Subject: [PATCH 2/3] Add inline button for add operation --- jsonforms-editor/src/JsonFormsEditor.tsx | 17 +- .../src/core/components/Layout.tsx | 62 +++++-- jsonforms-editor/src/core/context/context.ts | 17 +- .../src/core/renderers/DroppableLayout.tsx | 89 ++++++++- .../src/editor/components/EditorPanel.tsx | 5 - .../src/editor/components/EmptyEditor.tsx | 47 ++++- .../preview/ReactMaterialPreview.tsx | 1 - jsonforms-editor/src/editor/index.ts | 4 +- jsonforms-editor/src/editor/interface.ts | 16 ++ jsonforms-editor/src/env.ts | 2 +- jsonforms-editor/src/index.ts | 1 - .../components/JsonSchemaPanel.tsx | 2 +- .../palette-panel/components/PalletePanel.tsx | 173 ++++++++---------- .../components/UISchemaPanel.tsx | 2 +- jsonforms-editor/src/palette-panel/index.ts | 1 - 15 files changed, 277 insertions(+), 162 deletions(-) create mode 100644 jsonforms-editor/src/editor/interface.ts diff --git a/jsonforms-editor/src/JsonFormsEditor.tsx b/jsonforms-editor/src/JsonFormsEditor.tsx index 48861af..3b23b73 100644 --- a/jsonforms-editor/src/JsonFormsEditor.tsx +++ b/jsonforms-editor/src/JsonFormsEditor.tsx @@ -29,13 +29,10 @@ import { defaultEditorRenderers, defaultPreviewTabs, EditorPanel, -} from './editor'; -import { PreviewTab } from './editor/components/EditorPanel'; -import { - defaultPalettePanelTabs, - PalettePanel, PaletteTab, -} from './palette-panel'; +} from './editor'; +import { PreviewTab } from './editor'; +import { defaultPalettePanelTabs, PalettePanel } from './palette-panel'; import { defaultPropertyRenderers, PropertiesPanel } from './properties'; import { PropertiesService, @@ -140,13 +137,13 @@ export const JsonFormsEditor: React.FC = ({ schemaService, paletteService, propertiesService, + propertyRenderers, }} > = ({ interface JsonFormsEditorUiProps { previewTabs?: PreviewTab[]; editorRenderers: JsonFormsRendererRegistryEntry[]; - propertyRenderers: JsonFormsRendererRegistryEntry[]; header?: ComponentType; footer?: ComponentType; paletteTabs?: PaletteTab[]; @@ -167,7 +163,6 @@ interface JsonFormsEditorUiProps { const JsonFormsEditorUi: React.FC = ({ previewTabs, editorRenderers, - propertyRenderers, header, footer, paletteTabs, @@ -177,7 +172,7 @@ const JsonFormsEditorUi: React.FC = ({ } + paletteTabs={paletteTabs} > = ({
- +
diff --git a/jsonforms-editor/src/core/components/Layout.tsx b/jsonforms-editor/src/core/components/Layout.tsx index ab61414..6b1e929 100644 --- a/jsonforms-editor/src/core/components/Layout.tsx +++ b/jsonforms-editor/src/core/components/Layout.tsx @@ -5,17 +5,20 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ +import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; import AppBar from '@material-ui/core/AppBar'; import Divider from '@material-ui/core/Divider'; import Drawer from '@material-ui/core/Drawer'; import IconButton from '@material-ui/core/IconButton'; import { makeStyles } from '@material-ui/core/styles'; import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; import ChevronRightIcon from '@material-ui/icons/ChevronRight'; -import React from 'react'; +import React, { useCallback } from 'react'; -import { DrawerContextInstance } from '../context'; +import { PaletteTab } from '../../editor'; +import { PalettePanel } from '../../palette-panel'; const footerHeight = '40px'; const drawerWidth = '400px'; @@ -33,6 +36,7 @@ const useStyles = makeStyles((theme) => ({ container: { height: '100vh', display: 'flex', + overflow: 'hidden', }, footer: { zIndex: theme.zIndex.drawer + 1, @@ -74,29 +78,41 @@ const useStyles = makeStyles((theme) => ({ width: theme.spacing(9) + 1, }, }, + drawerContainer: { + overflow: 'auto', + }, + drawerTitle: (props: { isOpen: boolean }) => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: props.isOpen ? 'flex-start' : 'center', + }), })); interface LayoutProps { HeaderComponent?: React.ComponentType; FooterComponent?: React.ComponentType; - drawerContent?: React.ReactNode; + paletteTabs?: PaletteTab[]; } export const Layout: React.FC = ({ HeaderComponent, FooterComponent, - drawerContent, + paletteTabs, children, }) => { const [open, setOpen] = React.useState(true); - - // const toggleDrawerClose = () => { - // setOpen(!open); - // }; + const [selectedTabName, setSelectedTabName] = React.useState( + paletteTabs ? paletteTabs[0].name : '' + ); + const toggleDrawerClose = () => { + setOpen(!open); + }; const openDrawer = () => { setOpen(true); }; - const classes = useStyles(); + const onSelected = useCallback((tabName) => setSelectedTabName(tabName), []); + const classes = useStyles({ isOpen: open }); const classNameOpen = open ? classes.drawerOpen : classes.drawerClose; return ( @@ -111,15 +127,25 @@ export const Layout: React.FC = ({ paper: classNameOpen, }} > - - - {/* - {open ? : } - - */} - {drawerContent} -
- + +
+
+ + {open ? : } + + {open ? ( + {selectedTabName} + ) : null} +
+ + +
+
diff --git a/jsonforms-editor/src/core/context/context.ts b/jsonforms-editor/src/core/context/context.ts index b722bcc..eb7b125 100644 --- a/jsonforms-editor/src/core/context/context.ts +++ b/jsonforms-editor/src/core/context/context.ts @@ -5,6 +5,7 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ +import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; import React, { useContext } from 'react'; import { PropertiesService } from '../../properties/propertiesService'; @@ -24,6 +25,7 @@ export interface EditorContext { dispatch: (action: EditorAction) => void; selection: SelectedElement; setSelection: (selection: SelectedElement) => void; + propertyRenderers: JsonFormsRendererRegistryEntry[]; } /**We always use a provider so default can be undefined*/ @@ -35,16 +37,6 @@ export const EditorContextInstance = React.createContext( export const useEditorContext = (): EditorContext => useContext(EditorContextInstance); -export interface DrawerContext { - open: boolean; - openDrawer: () => void; -} -export const DrawerContextInstance = React.createContext( - defaultContext -); - -export const useDrawerContext = (): DrawerContext => - useContext(DrawerContextInstance); export const useGitLabService = (): SchemaService => { const { schemaService } = useEditorContext(); @@ -83,3 +75,8 @@ export const usePropertiesService = (): PropertiesService => { const { propertiesService } = useEditorContext(); return propertiesService; }; + +export const usePropertyRenderers = (): JsonFormsRendererRegistryEntry[] => { + const { propertyRenderers } = useEditorContext(); + return propertyRenderers; +}; diff --git a/jsonforms-editor/src/core/renderers/DroppableLayout.tsx b/jsonforms-editor/src/core/renderers/DroppableLayout.tsx index b8434dc..75f3228 100644 --- a/jsonforms-editor/src/core/renderers/DroppableLayout.tsx +++ b/jsonforms-editor/src/core/renderers/DroppableLayout.tsx @@ -18,10 +18,14 @@ import { withJsonFormsLayoutProps, } from '@jsonforms/react'; import { Grid, makeStyles } from '@material-ui/core'; +import EditIcon from '@material-ui/icons/Edit'; +import SpeedDial from '@material-ui/lab/SpeedDial'; +import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; +import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; import React from 'react'; import { useDrop } from 'react-dnd'; -import { useDispatch, useSchema } from '../context'; +import { useDispatch, usePaletteService, useSchema } from '../context'; import { canDropIntoLayout, canMoveSchemaElementTo, @@ -73,12 +77,12 @@ export const DroppableLayout: React.FC = ({ spacing={direction === 'row' ? 2 : 0} wrap='nowrap' > - + {layout.elements.map((child, index) => ( - + @@ -95,7 +99,7 @@ export const DroppableLayout: React.FC = ({ ))} @@ -103,6 +107,62 @@ export const DroppableLayout: React.FC = ({ ); }; +const useActionBarStyles = makeStyles((theme) => ({ + speedDial: { + bottom: theme.spacing(2), + right: theme.spacing(2), + }, +})); +interface InlineActionBarProps { + layout: EditorLayout; + index: number; +} +const InlineActionBar = ({ layout, index }: InlineActionBarProps) => { + const classes = useActionBarStyles(); + const paletteService = usePaletteService(); + const dispatch = useDispatch(); + const [open, setOpen] = React.useState(false); + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + return ( + } />} + onClose={handleClose} + onOpen={handleOpen} + open={open} + direction={layout.type === 'HorizontalLayout' ? 'down' : 'right'} + > + {paletteService + .getPaletteElements() + .map(({ type, label, icon, uiSchemaElementProvider }) => ( + { + dispatch( + Actions.addUnscopedElementToLayout( + uiSchemaElementProvider(), + layout.uuid, + index + ) + ); + handleClose(); + }} + /> + ))} + + ); +}; + interface DropPointProps { layout: EditorLayout; index: number; @@ -116,10 +176,16 @@ const useDropPointStyles = makeStyles((theme) => ({ : 'none', backgroundSize: 'calc(10 * 1px) calc(10 * 1px)', backgroundClip: 'content-box', - minWidth: '2em', - minHeight: props.isOver ? '8em' : '2em', - maxWidth: props.fillWidth || props.isOver ? 'inherit' : '2em', + minWidth: '3em', + minHeight: props.isOver ? '8em' : '3em', + maxWidth: props.fillWidth ? 'inherit' : '3em', }), + actions: { + opacity: 0, + '&:hover': { + opacity: 1, + }, + }, })); const DropPoint: React.FC = ({ layout, index }) => { @@ -195,8 +261,13 @@ const DropPoint: React.FC = ({ layout, index }) => { ref={drop} className={classes.dropPointGridItem} data-cy={`${getDataPath(layout)}-drop-${index}`} + alignItems='stretch' xs - > + > + + + + ); }; diff --git a/jsonforms-editor/src/editor/components/EditorPanel.tsx b/jsonforms-editor/src/editor/components/EditorPanel.tsx index 54ae245..65accf9 100644 --- a/jsonforms-editor/src/editor/components/EditorPanel.tsx +++ b/jsonforms-editor/src/editor/components/EditorPanel.tsx @@ -11,11 +11,6 @@ import React from 'react'; import { Editor } from './Editor'; -export interface PreviewTab { - name: string; - Component: React.ComponentType; -} - interface EditorPanelProps { editorRenderers: JsonFormsRendererRegistryEntry[]; } diff --git a/jsonforms-editor/src/editor/components/EmptyEditor.tsx b/jsonforms-editor/src/editor/components/EmptyEditor.tsx index 81caee6..6ffff1b 100644 --- a/jsonforms-editor/src/editor/components/EmptyEditor.tsx +++ b/jsonforms-editor/src/editor/components/EmptyEditor.tsx @@ -7,21 +7,29 @@ */ import { Typography } from '@material-ui/core'; import { makeStyles } from '@material-ui/core/styles'; +import EditIcon from '@material-ui/icons/Edit'; +import SpeedDial from '@material-ui/lab/SpeedDial'; +import SpeedDialAction from '@material-ui/lab/SpeedDialAction'; +import SpeedDialIcon from '@material-ui/lab/SpeedDialIcon'; import React from 'react'; import { useDrop } from 'react-dnd'; -import { useDispatch } from '../../core/context'; +import { useDispatch, usePaletteService } from '../../core/context'; import { NEW_UI_SCHEMA_ELEMENT } from '../../core/dnd'; import { Actions } from '../../core/model'; -const useStyles = makeStyles({ +const useStyles = makeStyles((theme) => ({ root: (props: any) => ({ padding: 10, fontSize: props.isOver ? '1.1em' : '1em', border: props.isOver ? '1px solid #D3D3D3' : 'none', height: '100%', }), -}); + speedDial: { + bottom: theme.spacing(2), + right: theme.spacing(2), + }, +})); export const EmptyEditor: React.FC = () => { const dispatch = useDispatch(); @@ -35,12 +43,45 @@ export const EmptyEditor: React.FC = () => { dispatch(Actions.setUiSchema(uiSchemaElement)); }, }); + const paletteService = usePaletteService(); + const [open, setOpen] = React.useState(false); + const handleOpen = () => { + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; const classes = useStyles({ isOver }); return (
Drag and drop an element from the Palette to begin. + } />} + onClose={handleClose} + onOpen={handleOpen} + open={open} + direction={'right'} + > + {paletteService + .getPaletteElements() + .map(({ type, label, icon, uiSchemaElementProvider }) => ( + { + dispatch(Actions.setUiSchema(uiSchemaElementProvider())); + handleClose(); + }} + /> + ))} +
); }; diff --git a/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx b/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx index e831a5b..656a061 100644 --- a/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx +++ b/jsonforms-editor/src/editor/components/preview/ReactMaterialPreview.tsx @@ -19,7 +19,6 @@ import { useExportSchema, useExportUiSchema } from '../../../core/util/hooks'; import { previewOptions } from './options'; export const ReactMaterialPreview: React.FC = () => { - console.log('IM RENDERER'); const schema = useExportSchema(); const uischema = useExportUiSchema(); const editorSchema = useSchema(); diff --git a/jsonforms-editor/src/editor/index.ts b/jsonforms-editor/src/editor/index.ts index 16cf0c8..9fead8e 100644 --- a/jsonforms-editor/src/editor/index.ts +++ b/jsonforms-editor/src/editor/index.ts @@ -15,13 +15,13 @@ import { DroppableHorizontalLayoutRegistration, DroppableVerticalLayoutRegistration, } from '../core/renderers/DroppableLayout'; -import { PreviewTab } from './components/EditorPanel'; import { ReactMaterialPreview } from './components/preview'; +import { PreviewTab } from './interface'; export * from './components/EditorPanel'; export { EditorElement } from './components/EditorElement'; -export type { PreviewTab } from './components/EditorPanel'; +export * from './interface'; export const defaultPreviewTabs: PreviewTab[] = [ { name: 'Preview', Component: ReactMaterialPreview }, ]; diff --git a/jsonforms-editor/src/editor/interface.ts b/jsonforms-editor/src/editor/interface.ts new file mode 100644 index 0000000..99961f3 --- /dev/null +++ b/jsonforms-editor/src/editor/interface.ts @@ -0,0 +1,16 @@ +/** + * --------------------------------------------------------------------- + * Copyright (c) 2020 EclipseSource Munich + * Licensed under MIT + * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE + * --------------------------------------------------------------------- + */ +export interface PreviewTab { + name: string; + Component: React.ComponentType; +} +export interface PaletteTab { + name: string; + Component: React.ComponentType; + icon: React.ReactElement; +} diff --git a/jsonforms-editor/src/env.ts b/jsonforms-editor/src/env.ts index aa886af..b903e6a 100644 --- a/jsonforms-editor/src/env.ts +++ b/jsonforms-editor/src/env.ts @@ -7,5 +7,5 @@ */ export const env = () => { const { REACT_APP_DEBUG: DEBUG = 'false', NODE_ENV } = process.env; - return { NODE_ENV, DEBUG }; + return { NODE_ENV, DEBUG, IS_DEBUG: DEBUG === 'true' }; }; diff --git a/jsonforms-editor/src/index.ts b/jsonforms-editor/src/index.ts index 9503bbd..58bde55 100644 --- a/jsonforms-editor/src/index.ts +++ b/jsonforms-editor/src/index.ts @@ -20,5 +20,4 @@ export * from './core/util'; export * from './editor/components/preview'; export * from './editor'; export * from './text-editor'; -export * from './palette-panel'; export default JsonFormsEditor; diff --git a/jsonforms-editor/src/palette-panel/components/JsonSchemaPanel.tsx b/jsonforms-editor/src/palette-panel/components/JsonSchemaPanel.tsx index c0c1579..612033a 100644 --- a/jsonforms-editor/src/palette-panel/components/JsonSchemaPanel.tsx +++ b/jsonforms-editor/src/palette-panel/components/JsonSchemaPanel.tsx @@ -28,7 +28,7 @@ export const JsonSchemaPanel: React.FC = ({ const dispatch = useDispatch(); const exportSchema = useExportSchema(); const schema: SchemaElement | undefined = useSchema(); - const showDebugSchema = env().DEBUG === 'true'; + const showDebugSchema = env().IS_DEBUG; const handleSchemaUpdate = (newSchema: string): UpdateResult => { try { const newSchemaObject = JSON.parse(newSchema); diff --git a/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx b/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx index ef7627b..e431a4d 100644 --- a/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx +++ b/jsonforms-editor/src/palette-panel/components/PalletePanel.tsx @@ -5,12 +5,11 @@ * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE * --------------------------------------------------------------------- */ -import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; import { makeStyles } from '@material-ui/core'; import Grid from '@material-ui/core/Grid'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; +import Tooltip from '@material-ui/core/Tooltip'; import BallotIcon from '@material-ui/icons/Ballot'; import ChatIcon from '@material-ui/icons/Chat'; import CodeIcon from '@material-ui/icons/Code'; @@ -18,22 +17,18 @@ import SettingsIcon from '@material-ui/icons/Settings'; import React, { useState } from 'react'; import { - useDispatch, - useDrawerContext, usePaletteService, + usePropertyRenderers, useSchema, - useUiSchema, } from '../../core/context'; -import { usePaletteService, useSchema } from '../../core/context'; import { SchemaElement } from '../../core/model'; -import { JsonSchemaPanel } from './JsonSchemaPanel'; +import { PaletteTab } from '../../editor'; import { Properties } from '../../properties/components/Properties'; +import { JsonSchemaPanel } from './JsonSchemaPanel'; import { SchemaTreeView } from './SchemaTree'; import { UIElementsTree } from './UIElementsTree'; import { UISchemaPanel } from './UISchemaPanel'; -const toText = (object: any) => JSON.stringify(object, null, 2); - const useStyles = makeStyles((theme) => ({ root: { flexGrow: 1, @@ -41,9 +36,6 @@ const useStyles = makeStyles((theme) => ({ display: 'flex', height: 224, }, - uiElementsTree: { - marginBottom: theme.spacing(1), - }, palettePanel: { height: '100%', display: 'flex', @@ -59,116 +51,101 @@ const useStyles = makeStyles((theme) => ({ }, }, })); -export interface PaletteTab { - name: string; - Component: React.ReactElement; -} +const usePaletteStyles = makeStyles((theme) => ({ + uiElementsTree: { + marginBottom: theme.spacing(1), + }, +})); interface PalettePanelProps { - propertyRenderers: JsonFormsRendererRegistryEntry[]; paletteTabs?: PaletteTab[]; + open: boolean; + openDrawer: () => void; + onSelected: (tabName: string) => void; } export const defaultPalettePanelTabs: PaletteTab[] = [ + { + name: 'Palette', + Component: () => { + const classes = usePaletteStyles(); + const schema: SchemaElement | undefined = useSchema(); + const paletteService = usePaletteService(); + return ( + <> + + {' '} + + ); + }, + icon: , + }, + { + name: 'Properties', + Component: () => { + const propertyRenderers = usePropertyRenderers(); + return ; + }, + icon: , + }, { name: 'JSON Schema', - Component: , + Component: () => , + icon: , + }, + { + name: 'UI Schema', + Component: () => , + icon: , }, - { name: 'UI Schema', Component: }, ]; -export const PalettePanel = ({ propertyRenderers }: PalettePanelProps) => { +export const PalettePanel = ({ + paletteTabs, + open, + openDrawer, + onSelected, +}: PalettePanelProps) => { const classes = useStyles(); - const { open, openDrawer } = useDrawerContext(); const [selectedTab, setSelectedTab] = useState(0); - const handleTabChange = (newValue: number) => { + const handleTabChange = (newValue: number, name: string) => { setSelectedTab(newValue); if (!open) { openDrawer(); } + onSelected(name); }; - const schema: SchemaElement | undefined = useSchema(); - const paletteService = usePaletteService(); + return ( - handleTabChange(0)} - selected={selectedTab === 0} - > - - - - - handleTabChange(1)} - selected={selectedTab === 1} - > - - - - - handleTabChange(2)} - selected={selectedTab === 2} - > - - - - - handleTabChange(3)} - selected={selectedTab === 3} - > - - - - + {paletteTabs?.map((pt, i) => { + return ( + handleTabChange(i, pt.name)} + selected={selectedTab === i} + key={`${pt.name.toLowerCase()}_tab`} + > + + {pt.icon} + + + ); + })} {open && ( - {selectedTab === 0 && ( - <> - - {' '} - - )} - {selectedTab === 1 && ( - - )} - {selectedTab === 2 && ( - - )} - {selectedTab === 3 && ( - - )} + {paletteTabs?.map((pt, i) => { + if (selectedTab === i) + return ( + + ); + return undefined; + })} )} diff --git a/jsonforms-editor/src/palette-panel/components/UISchemaPanel.tsx b/jsonforms-editor/src/palette-panel/components/UISchemaPanel.tsx index 2d0add0..e692c9e 100644 --- a/jsonforms-editor/src/palette-panel/components/UISchemaPanel.tsx +++ b/jsonforms-editor/src/palette-panel/components/UISchemaPanel.tsx @@ -27,7 +27,7 @@ export const UISchemaPanel: React.FC = ({ const dispatch = useDispatch(); const exportUiSchema = useExportUiSchema(); const uiSchema = useUiSchema(); - const showDebugSchema = env().DEBUG === 'true'; + const showDebugSchema = env().IS_DEBUG; const handleUiSchemaUpdate = (newUiSchema: string): UpdateResult => { try { const newUiSchemaObject = JSON.parse(newUiSchema); diff --git a/jsonforms-editor/src/palette-panel/index.ts b/jsonforms-editor/src/palette-panel/index.ts index ea2b5b7..f627018 100644 --- a/jsonforms-editor/src/palette-panel/index.ts +++ b/jsonforms-editor/src/palette-panel/index.ts @@ -6,7 +6,6 @@ * --------------------------------------------------------------------- */ -export type { PaletteTab } from './components/PalletePanel'; export * from './components/PalletePanel'; export { JsonSchemaPanel } from './components/JsonSchemaPanel'; export * from './components/UISchemaPanel'; From 722dbc5771a8270d93afe3b359ed0a17d4c44690 Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Sun, 7 Mar 2021 12:16:58 +0100 Subject: [PATCH 3/3] Add support for inline editing - allow to modify scope of controls - allow to modify label if controls - allow to modify label of groups - allow to modify label of labels --- .../src/core/api/paletteService.tsx | 20 +++- .../src/core/components/EditableControl.tsx | 95 +++++++++++++++++++ .../src/core/components/Layout.tsx | 2 +- jsonforms-editor/src/core/components/index.ts | 1 + jsonforms-editor/src/core/model/actions.ts | 19 +++- jsonforms-editor/src/core/model/reducer.ts | 20 ++++ .../core/renderers/DroppableArrayControl.tsx | 39 ++++---- .../src/core/renderers/DroppableControl.tsx | 28 ++++++ .../core/renderers/DroppableGroupLayout.tsx | 18 ++++ .../src/core/renderers/DroppableLabel.tsx | 42 ++++++++ .../src/core/util/generators/uiSchema.ts | 9 ++ jsonforms-editor/src/editor/index.ts | 4 + 12 files changed, 277 insertions(+), 20 deletions(-) create mode 100644 jsonforms-editor/src/core/components/EditableControl.tsx create mode 100644 jsonforms-editor/src/core/renderers/DroppableControl.tsx create mode 100644 jsonforms-editor/src/core/renderers/DroppableLabel.tsx diff --git a/jsonforms-editor/src/core/api/paletteService.tsx b/jsonforms-editor/src/core/api/paletteService.tsx index 6076110..56ddb8b 100644 --- a/jsonforms-editor/src/core/api/paletteService.tsx +++ b/jsonforms-editor/src/core/api/paletteService.tsx @@ -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[]; @@ -48,6 +58,12 @@ const paletteElements: PaletteElement[] = [ icon: , uiSchemaElementProvider: () => createLabel(), }, + { + type: 'Control', + label: 'Control', + icon: , + uiSchemaElementProvider: () => createEmptyControl(), + }, ]; export class DefaultPaletteService implements PaletteService { diff --git a/jsonforms-editor/src/core/components/EditableControl.tsx b/jsonforms-editor/src/core/components/EditableControl.tsx new file mode 100644 index 0000000..b3856ce --- /dev/null +++ b/jsonforms-editor/src/core/components/EditableControl.tsx @@ -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 = ({ + uischema, +}) => { + const dispatch = useDispatch(); + const baseSchema = useSchema() as SchemaElement; + const handleLabelChange = (event: React.ChangeEvent) => { + dispatch( + Actions.updateUISchemaElement(uischema.uuid, { + label: event.target.value, + }) + ); + }; + const handleScopeChange = (event: React.ChangeEvent) => { + 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 ( + + + + + + + {scopes.map((scope) => ( + + {scope.scope} + + ))} + + + + ); +}; diff --git a/jsonforms-editor/src/core/components/Layout.tsx b/jsonforms-editor/src/core/components/Layout.tsx index 6b1e929..32e4d7a 100644 --- a/jsonforms-editor/src/core/components/Layout.tsx +++ b/jsonforms-editor/src/core/components/Layout.tsx @@ -101,7 +101,7 @@ export const Layout: React.FC = ({ paletteTabs, children, }) => { - const [open, setOpen] = React.useState(true); + const [open, setOpen] = React.useState(false); const [selectedTabName, setSelectedTabName] = React.useState( paletteTabs ? paletteTabs[0].name : '' ); diff --git a/jsonforms-editor/src/core/components/index.ts b/jsonforms-editor/src/core/components/index.ts index d454b75..8472164 100644 --- a/jsonforms-editor/src/core/components/index.ts +++ b/jsonforms-editor/src/core/components/index.ts @@ -14,3 +14,4 @@ export * from './ErrorDialog'; export * from './OkCancelDialog'; export * from './ExportDialog'; export * from './ShowMoreLess'; +export * from './EditableControl'; diff --git a/jsonforms-editor/src/core/model/actions.ts b/jsonforms-editor/src/core/model/actions.ts index c72c2f4..6dfa957 100644 --- a/jsonforms-editor/src/core/model/actions.ts +++ b/jsonforms-editor/src/core/model/actions.ts @@ -16,7 +16,8 @@ export type CombinedAction = | AddScopedElementToLayout | MoveUiSchemaElement | RemoveUiSchemaElement - | AddDetail; + | AddDetail + | ChangeControlScope; export type EditorAction = UiSchemaAction | CombinedAction; @@ -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'; @@ -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; @@ -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, @@ -177,4 +193,5 @@ export const Actions = { removeUiSchemaElement, updateUISchemaElement, addDetail, + changeControlScope, }; diff --git a/jsonforms-editor/src/core/model/reducer.ts b/jsonforms-editor/src/core/model/reducer.ts index ce4fb56..1c90b17 100644 --- a/jsonforms-editor/src/core/model/reducer.ts +++ b/jsonforms-editor/src/core/model/reducer.ts @@ -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, @@ -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, @@ -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: diff --git a/jsonforms-editor/src/core/renderers/DroppableArrayControl.tsx b/jsonforms-editor/src/core/renderers/DroppableArrayControl.tsx index 1c8c8ad..e7daa46 100644 --- a/jsonforms-editor/src/core/renderers/DroppableArrayControl.tsx +++ b/jsonforms-editor/src/core/renderers/DroppableArrayControl.tsx @@ -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, @@ -96,22 +97,28 @@ const DroppableArrayControl: React.FC = ({ return renderers && [...renderers, DroppableElementRegistration]; }, [renderers]); - if (!uischema.options?.detail) { - return ( - - Default array layout. Drag and drop an item here to customize array - layout. - - ); - } return ( - + + + + + + {!uischema.options?.detail ? ( + + Default array layout. Drag and drop an item here to customize array + layout. + + ) : ( + + )} + + ); }; diff --git a/jsonforms-editor/src/core/renderers/DroppableControl.tsx b/jsonforms-editor/src/core/renderers/DroppableControl.tsx new file mode 100644 index 0000000..644adc3 --- /dev/null +++ b/jsonforms-editor/src/core/renderers/DroppableControl.tsx @@ -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 = ({ uischema }) => { + return ; +}; + +export const DroppableControlRegistration = { + tester: rankWith(40, isControl), // less than DroppableElement + renderer: withJsonFormsControlProps( + DroppableControl as React.FC + ), +}; diff --git a/jsonforms-editor/src/core/renderers/DroppableGroupLayout.tsx b/jsonforms-editor/src/core/renderers/DroppableGroupLayout.tsx index 1530c81..d39d626 100644 --- a/jsonforms-editor/src/core/renderers/DroppableGroupLayout.tsx +++ b/jsonforms-editor/src/core/renderers/DroppableGroupLayout.tsx @@ -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'; @@ -39,6 +42,14 @@ const Group: React.FC = (props) => { const { uischema } = props; const groupLayout = uischema as GroupLayout & EditorLayout; const classes = useStyles(); + const dispatch = useDispatch(); + const handleLabelChange = (event: React.ChangeEvent) => { + dispatch( + Actions.updateUISchemaElement(groupLayout.uuid, { + label: event.target.value, + }) + ); + }; return ( = (props) => { )} > + diff --git a/jsonforms-editor/src/core/renderers/DroppableLabel.tsx b/jsonforms-editor/src/core/renderers/DroppableLabel.tsx new file mode 100644 index 0000000..8cbd679 --- /dev/null +++ b/jsonforms-editor/src/core/renderers/DroppableLabel.tsx @@ -0,0 +1,42 @@ +/** + * --------------------------------------------------------------------- + * Copyright (c) 2020 EclipseSource Munich + * Licensed under MIT + * https://github.com/eclipsesource/jsonforms-editor/blob/master/LICENSE + * --------------------------------------------------------------------- + */ +import { LabelElement, LayoutProps, rankWith, uiTypeIs } from '@jsonforms/core'; +import { withJsonFormsLayoutProps } from '@jsonforms/react'; +import { TextField } from '@material-ui/core'; +import React from 'react'; + +import { useDispatch } from '../context'; +import { Actions } from '../model'; +import { EditorUISchemaElement } from '../model/uischema'; + +const DroppableLabel: React.FC = (props) => { + const { uischema } = props; + const labelElement = uischema as LabelElement & EditorUISchemaElement; + const dispatch = useDispatch(); + const handleLabelChange = (event: React.ChangeEvent) => { + dispatch( + Actions.updateUISchemaElement(labelElement.uuid, { + text: event.target.value, + }) + ); + }; + return ( + + ); +}; + +export const DroppableLabelRegistration = { + tester: rankWith(45, uiTypeIs('Label')), + renderer: withJsonFormsLayoutProps(DroppableLabel), +}; diff --git a/jsonforms-editor/src/core/util/generators/uiSchema.ts b/jsonforms-editor/src/core/util/generators/uiSchema.ts index b0f55f9..6191520 100644 --- a/jsonforms-editor/src/core/util/generators/uiSchema.ts +++ b/jsonforms-editor/src/core/util/generators/uiSchema.ts @@ -44,3 +44,12 @@ export const createLabel = ( uuid: uuid(), } as LabelElement & EditorUISchemaElement; }; + +export const createEmptyControl = (): ControlElement & + EditorUISchemaElement => { + return { + type: 'Control', + scope: '', + uuid: uuid(), + } as ControlElement & EditorUISchemaElement; +}; diff --git a/jsonforms-editor/src/editor/index.ts b/jsonforms-editor/src/editor/index.ts index 9fead8e..6d94016 100644 --- a/jsonforms-editor/src/editor/index.ts +++ b/jsonforms-editor/src/editor/index.ts @@ -9,8 +9,10 @@ import { JsonFormsRendererRegistryEntry } from '@jsonforms/core'; import { materialRenderers } from '@jsonforms/material-renderers'; import { DroppableArrayControlRegistration } from '../core/renderers/DroppableArrayControl'; +import { DroppableControlRegistration } from '../core/renderers/DroppableControl'; import { DroppableElementRegistration } from '../core/renderers/DroppableElement'; import { DroppableGroupLayoutRegistration } from '../core/renderers/DroppableGroupLayout'; +import { DroppableLabelRegistration } from '../core/renderers/DroppableLabel'; import { DroppableHorizontalLayoutRegistration, DroppableVerticalLayoutRegistration, @@ -33,4 +35,6 @@ export const defaultEditorRenderers: JsonFormsRendererRegistryEntry[] = [ DroppableElementRegistration, DroppableGroupLayoutRegistration, DroppableArrayControlRegistration, + DroppableControlRegistration, + DroppableLabelRegistration, ];