From 096815f89b5698089a02b15cf456687d57b73ead Mon Sep 17 00:00:00 2001 From: Eugen Neufeld Date: Wed, 6 Oct 2021 15:08:02 +0200 Subject: [PATCH] Remove custom areEqual methods Due to previous refactorings the data is already only changing where needed. All other control properties are also memoized in some way. So the manual areEqual check is not needed, instead plain memo is used. Also add named memos to missing renderers. Change all material renderers to be functional components. --- MIGRATION.md | 30 +++ packages/core/src/util/renderer.ts | 2 - packages/examples/src/arrays.ts | 7 +- .../src/additional/MaterialLabelRenderer.tsx | 3 +- .../src/cells/MaterialBooleanCell.tsx | 3 +- .../src/cells/MaterialBooleanToggleCell.tsx | 3 +- .../material/src/cells/MaterialDateCell.tsx | 3 +- .../material/src/cells/MaterialEnumCell.tsx | 3 +- .../src/cells/MaterialIntegerCell.tsx | 3 +- .../material/src/cells/MaterialNumberCell.tsx | 4 +- .../src/cells/MaterialNumberFormatCell.tsx | 3 +- .../src/cells/MaterialOneOfEnumCell.tsx | 3 +- .../material/src/cells/MaterialTextCell.tsx | 3 +- .../material/src/cells/MaterialTimeCell.tsx | 4 +- .../src/complex/MaterialAllOfRenderer.tsx | 4 +- .../src/complex/MaterialAnyOfRenderer.tsx | 4 +- .../complex/MaterialArrayControlRenderer.tsx | 3 +- .../src/complex/MaterialEnumArrayRenderer.tsx | 3 +- .../src/complex/MaterialObjectRenderer.tsx | 4 +- .../src/complex/MaterialOneOfRenderer.tsx | 8 +- .../src/complex/MaterialTableControl.tsx | 60 +++-- .../src/controls/MaterialBooleanControl.tsx | 4 +- .../controls/MaterialBooleanToggleControl.tsx | 3 +- .../src/controls/MaterialDateControl.tsx | 166 ++++++------ .../src/controls/MaterialDateTimeControl.tsx | 170 ++++++------ .../src/controls/MaterialEnumControl.tsx | 3 +- .../src/controls/MaterialInputControl.tsx | 129 +++++----- .../src/controls/MaterialIntegerControl.tsx | 4 +- .../src/controls/MaterialNativeControl.tsx | 108 ++++---- .../src/controls/MaterialNumberControl.tsx | 3 +- .../src/controls/MaterialOneOfEnumControl.tsx | 3 +- .../MaterialOneOfRadioGroupControl.tsx | 3 +- .../src/controls/MaterialRadioGroup.tsx | 134 +++++----- .../controls/MaterialRadioGroupControl.tsx | 5 +- .../src/controls/MaterialSliderControl.tsx | 185 ++++++------- .../src/controls/MaterialTextControl.tsx | 4 +- .../src/controls/MaterialTimeControl.tsx | 156 ++++++----- .../MaterialAutocompleteEnumControl.tsx | 3 +- .../MaterialAutocompleteOneOfEnumControl.tsx | 3 +- .../material/src/extended/MuiAutocomplete.tsx | 5 +- .../src/layouts/ExpandPanelRenderer.tsx | 24 +- .../src/layouts/MaterialArrayLayout.tsx | 155 ++++++----- .../layouts/MaterialCategorizationLayout.tsx | 115 ++++----- .../MaterialCategorizationStepperLayout.tsx | 166 ++++++------ .../src/layouts/MaterialGroupLayout.tsx | 3 +- .../src/layouts/MaterialHorizontalLayout.tsx | 3 +- .../src/layouts/MaterialVerticalLayout.tsx | 3 +- .../material/src/mui-controls/MuiCheckbox.tsx | 5 +- .../src/mui-controls/MuiInputInteger.tsx | 7 +- .../src/mui-controls/MuiInputNumber.tsx | 3 +- .../src/mui-controls/MuiInputNumberFormat.tsx | 7 +- .../src/mui-controls/MuiInputText.tsx | 3 +- .../src/mui-controls/MuiInputTime.tsx | 5 +- .../material/src/mui-controls/MuiSelect.tsx | 5 +- .../material/src/mui-controls/MuiToggle.tsx | 5 +- packages/material/src/util/debounce.ts | 24 ++ packages/material/src/util/focus.ts | 32 +++ packages/material/src/util/index.ts | 1 + packages/material/src/util/layout.tsx | 6 +- packages/react/src/DispatchCell.tsx | 52 ++-- packages/react/src/JsonFormsContext.tsx | 243 ++++++++---------- 61 files changed, 1087 insertions(+), 1031 deletions(-) create mode 100644 packages/material/src/util/focus.ts diff --git a/MIGRATION.md b/MIGRATION.md index 1b42bee504..641a8fc716 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -2,6 +2,36 @@ ## Migrating to JSON Forms 3.0 for React users +### Removal of Class Components in React Material + +With Version 3.0 of JSON Forms we removed all class components. +Please check whether you extended any of our base renderers in your adaptation. + +### Memoization changes in React bindings + +Previously components wrapped in our bindings (e.g. "`withJsonFormsControlProps`") were automatically memoized using a custom comparison function ("`areEqual`"). +The comparison function was rather expensive, invoking deep comparisons on some of the props. +Also automatically applying "React.memo" on all renderers is counter productive when a user might add renderers which always rerender anyway. +Enabled by [previous refactorings](https://github.com/eclipsesource/jsonforms/issues/1715) we now removed the custom comparison function as well as the automatic memoization. +Thus memoization can now achieved by simply using `React.memo`. +If your component relied on our bindings, e.g. `withJsonFormsControlProps` to be memoized, you now need to do it yourself: + +```ts +import React from 'react'; +import { ControlProps, rankWith } from '@jsonforms/core'; +import { withJsonFormsControlProps } from '@jsonforms/react'; + +const MyControlComponent = ({data}: ControlProps) => { + return ({data}); +}; + +export const myControlTester = rankWith(2, true); +export const MyControl = React.memo(MyControlComponent); +export default withJsonFormsControlProps(MyControl); +``` + +### Removal of JSON Schema $Ref Parser + With version 3.0 of JSON Forms, we removed the `json-schema-ref-parser` dependency within the core package. This change only affects users of the React variant, Vue and Angular users are not affected. diff --git a/packages/core/src/util/renderer.ts b/packages/core/src/util/renderer.ts index 0677029a21..2a492ac6d2 100644 --- a/packages/core/src/util/renderer.ts +++ b/packages/core/src/util/renderer.ts @@ -1002,8 +1002,6 @@ export const mapStateToArrayLayoutProps = ( }; }; -export type CombinatorProps = StatePropsOfCombinator & DispatchPropsOfControl; - /** * Props of an array control. */ diff --git a/packages/examples/src/arrays.ts b/packages/examples/src/arrays.ts index 6ff2714b18..bfc627ed2f 100644 --- a/packages/examples/src/arrays.ts +++ b/packages/examples/src/arrays.ts @@ -46,13 +46,18 @@ export const schema = { } } } - } + }, + foo:{type:'string'} } }; export const uischema = { type: 'VerticalLayout', elements: [ + { + type: 'Control', + scope: '#/properties/foo' + }, { type: 'Control', scope: '#/properties/comments' diff --git a/packages/material/src/additional/MaterialLabelRenderer.tsx b/packages/material/src/additional/MaterialLabelRenderer.tsx index de48acd282..42aa1435d3 100644 --- a/packages/material/src/additional/MaterialLabelRenderer.tsx +++ b/packages/material/src/additional/MaterialLabelRenderer.tsx @@ -45,7 +45,7 @@ export const materialLabelRendererTester: RankedTester = rankWith(1, uiTypeIs('L /** * Default renderer for a label. */ -export const MaterialLabelRenderer = ({ uischema, visible }: OwnPropsOfRenderer) => { +const MaterialLabelRendererComponent = ({ uischema, visible }: OwnPropsOfRenderer) => { const labelElement: LabelElement = uischema as LabelElement; return ( @@ -56,4 +56,5 @@ export const MaterialLabelRenderer = ({ uischema, visible }: OwnPropsOfRenderer) ); }; +export const MaterialLabelRenderer = React.memo(MaterialLabelRendererComponent); export default withJsonFormsLayoutProps(MaterialLabelRenderer); diff --git a/packages/material/src/cells/MaterialBooleanCell.tsx b/packages/material/src/cells/MaterialBooleanCell.tsx index fe4f8ed0ec..1a2c682755 100644 --- a/packages/material/src/cells/MaterialBooleanCell.tsx +++ b/packages/material/src/cells/MaterialBooleanCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiCheckbox } from '../mui-controls/MuiCheckbox'; -export const MaterialBooleanCell = (props: CellProps & WithClassname) => { +const MaterialBooleanCellComponent = (props: CellProps & WithClassname) => { return ; }; @@ -42,4 +42,5 @@ export const materialBooleanCellTester: RankedTester = rankWith( isBooleanControl ); +export const MaterialBooleanCell = React.memo(MaterialBooleanCellComponent); export default withJsonFormsCellProps(MaterialBooleanCell); diff --git a/packages/material/src/cells/MaterialBooleanToggleCell.tsx b/packages/material/src/cells/MaterialBooleanToggleCell.tsx index c2ef93adeb..0e9df6fcc4 100644 --- a/packages/material/src/cells/MaterialBooleanToggleCell.tsx +++ b/packages/material/src/cells/MaterialBooleanToggleCell.tsx @@ -35,7 +35,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiToggle } from '../mui-controls/MuiToggle'; -export const MaterialBooleanToggleCell = (props: CellProps & WithClassname) => { +const MaterialBooleanToggleCellComponent = (props: CellProps & WithClassname) => { return ; }; @@ -44,4 +44,5 @@ export const materialBooleanToggleCellTester: RankedTester = rankWith( and(isBooleanControl, optionIs('toggle', true)) );; +export const MaterialBooleanToggleCell = React.memo(MaterialBooleanToggleCellComponent); export default withJsonFormsCellProps(MaterialBooleanToggleCell); diff --git a/packages/material/src/cells/MaterialDateCell.tsx b/packages/material/src/cells/MaterialDateCell.tsx index 747a9b5705..325f9682f5 100644 --- a/packages/material/src/cells/MaterialDateCell.tsx +++ b/packages/material/src/cells/MaterialDateCell.tsx @@ -34,7 +34,7 @@ import { withJsonFormsCellProps } from '@jsonforms/react'; import Input from '@material-ui/core/Input'; import merge from 'lodash/merge'; -export const MaterialDateCell = (props: CellProps & WithClassname) => { +const MaterialDateCellComponent = (props: CellProps & WithClassname) => { const { data, className, @@ -63,4 +63,5 @@ export const MaterialDateCell = (props: CellProps & WithClassname) => { }; export const materialDateCellTester: RankedTester = rankWith(2, isDateControl); +export const MaterialDateCell = React.memo(MaterialDateCellComponent); export default withJsonFormsCellProps(MaterialDateCell); diff --git a/packages/material/src/cells/MaterialEnumCell.tsx b/packages/material/src/cells/MaterialEnumCell.tsx index 0107df9a64..b59cd395ea 100644 --- a/packages/material/src/cells/MaterialEnumCell.tsx +++ b/packages/material/src/cells/MaterialEnumCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsEnumCellProps } from '@jsonforms/react'; import { MuiSelect } from '../mui-controls/MuiSelect'; -export const MaterialEnumCell = (props: EnumCellProps & WithClassname) => ( +const MaterialEnumCellComponent = (props: EnumCellProps & WithClassname) => ( ); @@ -43,4 +43,5 @@ export const MaterialEnumCell = (props: EnumCellProps & WithClassname) => ( */ export const materialEnumCellTester: RankedTester = rankWith(2, isEnumControl); +export const MaterialEnumCell = React.memo(MaterialEnumCellComponent); export default withJsonFormsEnumCellProps(MaterialEnumCell); diff --git a/packages/material/src/cells/MaterialIntegerCell.tsx b/packages/material/src/cells/MaterialIntegerCell.tsx index 2c038ddfde..a2e86cea73 100644 --- a/packages/material/src/cells/MaterialIntegerCell.tsx +++ b/packages/material/src/cells/MaterialIntegerCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiInputInteger } from '../mui-controls/MuiInputInteger'; -export const MaterialIntegerCell = (props: CellProps & WithClassname) => ( +const MaterialIntegerCellComponent = (props: CellProps & WithClassname) => ( ); export const materialIntegerCellTester: RankedTester = rankWith( @@ -41,4 +41,5 @@ export const materialIntegerCellTester: RankedTester = rankWith( isIntegerControl ); +export const MaterialIntegerCell = React.memo(MaterialIntegerCellComponent); export default withJsonFormsCellProps(MaterialIntegerCell); diff --git a/packages/material/src/cells/MaterialNumberCell.tsx b/packages/material/src/cells/MaterialNumberCell.tsx index 892c5f163e..e52c6f12b9 100644 --- a/packages/material/src/cells/MaterialNumberCell.tsx +++ b/packages/material/src/cells/MaterialNumberCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiInputNumber } from '../mui-controls/MuiInputNumber'; -export const MaterialNumberCell = (props: CellProps & WithClassname) => ( +const MaterialNumberCellComponent = (props: CellProps & WithClassname) => ( ); /** @@ -44,4 +44,6 @@ export const materialNumberCellTester: RankedTester = rankWith( 2, isNumberControl ); + +export const MaterialNumberCell = React.memo(MaterialNumberCellComponent); export default withJsonFormsCellProps(MaterialNumberCell); diff --git a/packages/material/src/cells/MaterialNumberFormatCell.tsx b/packages/material/src/cells/MaterialNumberFormatCell.tsx index d98801731c..70a51fcfd4 100644 --- a/packages/material/src/cells/MaterialNumberFormatCell.tsx +++ b/packages/material/src/cells/MaterialNumberFormatCell.tsx @@ -34,7 +34,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiInputNumberFormat } from '../mui-controls/MuiInputNumberFormat'; -export const MaterialNumberFormatCell = ( +const MaterialNumberFormatCellComponent = ( props: CellProps & WithClassname & Formatted ) => ; /** @@ -46,4 +46,5 @@ export const materialNumberFormatCellTester: RankedTester = rankWith( isNumberFormatControl ); +export const MaterialNumberFormatCell = React.memo(MaterialNumberFormatCellComponent); export default withJsonFormsCellProps(MaterialNumberFormatCell); diff --git a/packages/material/src/cells/MaterialOneOfEnumCell.tsx b/packages/material/src/cells/MaterialOneOfEnumCell.tsx index b3ff1bf8ff..e807af6899 100644 --- a/packages/material/src/cells/MaterialOneOfEnumCell.tsx +++ b/packages/material/src/cells/MaterialOneOfEnumCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsOneOfEnumCellProps } from '@jsonforms/react'; import { MuiSelect } from '../mui-controls/MuiSelect'; -export const MaterialOneOfEnumCell = (props: EnumCellProps & WithClassname) => ( +const MaterialOneOfEnumCellComponent = (props: EnumCellProps & WithClassname) => ( ); @@ -43,4 +43,5 @@ export const MaterialOneOfEnumCell = (props: EnumCellProps & WithClassname) => ( */ export const materialOneOfEnumCellTester: RankedTester = rankWith(2, isOneOfEnumControl); +export const MaterialOneOfEnumCell = React.memo(MaterialOneOfEnumCellComponent); export default withJsonFormsOneOfEnumCellProps(MaterialOneOfEnumCell); diff --git a/packages/material/src/cells/MaterialTextCell.tsx b/packages/material/src/cells/MaterialTextCell.tsx index 485728d700..af3bd7bd36 100644 --- a/packages/material/src/cells/MaterialTextCell.tsx +++ b/packages/material/src/cells/MaterialTextCell.tsx @@ -33,7 +33,7 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiInputText } from '../mui-controls/MuiInputText'; -export const MaterialTextCell = (props: CellProps & WithClassname) => ( +const MaterialTextCellComponent = (props: CellProps & WithClassname) => ( ); @@ -46,4 +46,5 @@ export const materialTextCellTester: RankedTester = rankWith( isStringControl ); +export const MaterialTextCell = React.memo(MaterialTextCellComponent); export default withJsonFormsCellProps(MaterialTextCell); diff --git a/packages/material/src/cells/MaterialTimeCell.tsx b/packages/material/src/cells/MaterialTimeCell.tsx index bc8eace446..345aec8bf0 100644 --- a/packages/material/src/cells/MaterialTimeCell.tsx +++ b/packages/material/src/cells/MaterialTimeCell.tsx @@ -33,8 +33,10 @@ import { import { withJsonFormsCellProps } from '@jsonforms/react'; import { MuiInputTime } from '../mui-controls/MuiInputTime'; -export const MaterialTimeCell = (props: CellProps & WithClassname) => ( +const MaterialTimeCellComponent = (props: CellProps & WithClassname) => ( ); export const materialTimeCellTester: RankedTester = rankWith(2, isTimeControl); + +export const MaterialTimeCell = React.memo(MaterialTimeCellComponent); export default withJsonFormsCellProps(MaterialTimeCell); diff --git a/packages/material/src/complex/MaterialAllOfRenderer.tsx b/packages/material/src/complex/MaterialAllOfRenderer.tsx index f146c7ee26..4e8686baf3 100644 --- a/packages/material/src/complex/MaterialAllOfRenderer.tsx +++ b/packages/material/src/complex/MaterialAllOfRenderer.tsx @@ -37,7 +37,7 @@ import { } from '@jsonforms/core'; import { JsonFormsDispatch, withJsonFormsAllOfProps } from '@jsonforms/react'; -const MaterialAllOfRenderer = ({ +const MaterialAllOfRendererComponent = ({ schema, rootSchema, visible, @@ -95,4 +95,6 @@ export const materialAllOfControlTester: RankedTester = rankWith( 3, isAllOfControl ); + +export const MaterialAllOfRenderer = React.memo(MaterialAllOfRendererComponent); export default withJsonFormsAllOfProps(MaterialAllOfRenderer); diff --git a/packages/material/src/complex/MaterialAnyOfRenderer.tsx b/packages/material/src/complex/MaterialAnyOfRenderer.tsx index 6e675a30b3..638ba9e8be 100644 --- a/packages/material/src/complex/MaterialAnyOfRenderer.tsx +++ b/packages/material/src/complex/MaterialAnyOfRenderer.tsx @@ -37,7 +37,7 @@ import { JsonFormsDispatch, withJsonFormsAnyOfProps } from '@jsonforms/react'; import { Hidden, Tab, Tabs } from '@material-ui/core'; import CombinatorProperties from './CombinatorProperties'; -const MaterialAnyOfRenderer = ({ +const MaterialAnyOfRendererComponent = ({ schema, rootSchema, indexOfFittingSchema, @@ -97,4 +97,6 @@ export const materialAnyOfControlTester: RankedTester = rankWith( 3, isAnyOfControl ); + +export const MaterialAnyOfRenderer = React.memo(MaterialAnyOfRendererComponent); export default withJsonFormsAnyOfProps(MaterialAnyOfRenderer); diff --git a/packages/material/src/complex/MaterialArrayControlRenderer.tsx b/packages/material/src/complex/MaterialArrayControlRenderer.tsx index 17ba04d645..0656bcce4f 100644 --- a/packages/material/src/complex/MaterialArrayControlRenderer.tsx +++ b/packages/material/src/complex/MaterialArrayControlRenderer.tsx @@ -29,7 +29,7 @@ import { MaterialTableControl } from './MaterialTableControl'; import { Hidden } from '@material-ui/core'; import { DeleteDialog } from './DeleteDialog'; -export const MaterialArrayControlRenderer = (props: ArrayLayoutProps) => { +export const MaterialArrayControlRendererComponent = (props: ArrayLayoutProps) => { const [open, setOpen] = useState(false); const [path, setPath] = useState(undefined); const [rowData, setRowData] = useState(undefined); @@ -64,4 +64,5 @@ export const MaterialArrayControlRenderer = (props: ArrayLayoutProps) => { ); }; +export const MaterialArrayControlRenderer = React.memo(MaterialArrayControlRendererComponent); export default withJsonFormsArrayLayoutProps(MaterialArrayControlRenderer); diff --git a/packages/material/src/complex/MaterialEnumArrayRenderer.tsx b/packages/material/src/complex/MaterialEnumArrayRenderer.tsx index 37d6f8c5d6..ca6a7263f6 100644 --- a/packages/material/src/complex/MaterialEnumArrayRenderer.tsx +++ b/packages/material/src/complex/MaterialEnumArrayRenderer.tsx @@ -26,7 +26,7 @@ import { startCase } from 'lodash'; import { isEmpty } from 'lodash'; import React from 'react'; -export const MaterialEnumArrayRenderer = ({ +const MaterialEnumArrayRendererComponent = ({ schema, visible, errors, @@ -108,4 +108,5 @@ export const materialEnumArrayRendererTester: RankedTester = rankWith( ) ); +export const MaterialEnumArrayRenderer = React.memo(MaterialEnumArrayRendererComponent); export default withJsonFormsMultiEnumProps(MaterialEnumArrayRenderer); diff --git a/packages/material/src/complex/MaterialObjectRenderer.tsx b/packages/material/src/complex/MaterialObjectRenderer.tsx index 158de93cef..600ee6dacc 100644 --- a/packages/material/src/complex/MaterialObjectRenderer.tsx +++ b/packages/material/src/complex/MaterialObjectRenderer.tsx @@ -35,7 +35,7 @@ import { JsonFormsDispatch, withJsonFormsDetailProps } from '@jsonforms/react'; import { Hidden } from '@material-ui/core'; import React, { useMemo } from 'react'; -const MaterialObjectRenderer = ({ +const MaterialObjectRendererComponent = ({ renderers, cells, uischemas, @@ -84,4 +84,6 @@ export const materialObjectControlTester: RankedTester = rankWith( 2, isObjectControl ); + +export const MaterialObjectRenderer = React.memo(MaterialObjectRendererComponent); export default withJsonFormsDetailProps(MaterialObjectRenderer); diff --git a/packages/material/src/complex/MaterialOneOfRenderer.tsx b/packages/material/src/complex/MaterialOneOfRenderer.tsx index b581cbf76e..72b7e08d75 100644 --- a/packages/material/src/complex/MaterialOneOfRenderer.tsx +++ b/packages/material/src/complex/MaterialOneOfRenderer.tsx @@ -26,7 +26,7 @@ import React, { useCallback, useState } from 'react'; import isEmpty from 'lodash/isEmpty'; import { - CombinatorProps, + CombinatorRendererProps, createCombinatorRenderInfos, createDefaultValue, isOneOfControl, @@ -58,8 +58,8 @@ export interface OwnOneOfProps extends OwnPropsOfControl { } const oneOf = 'oneOf'; -const MaterialOneOfRenderer = - ({ handleChange, schema, path, renderers, cells, rootSchema, id, visible, indexOfFittingSchema, uischema, uischemas, data }: CombinatorProps) => { +const MaterialOneOfRendererComponent = + ({ handleChange, schema, path, renderers, cells, rootSchema, id, visible, indexOfFittingSchema, uischema, uischemas, data }: CombinatorRendererProps) => { const [open, setOpen] = useState(false); const [selectedIndex, setSelectedIndex] = useState(indexOfFittingSchema || 0); const [newSelectedIndex, setNewSelectedIndex] = useState(0); @@ -150,4 +150,6 @@ const MaterialOneOfRenderer = }; export const materialOneOfControlTester: RankedTester = rankWith(3, isOneOfControl); + +export const MaterialOneOfRenderer = React.memo(MaterialOneOfRendererComponent); export default withJsonFormsOneOfProps(MaterialOneOfRenderer); diff --git a/packages/material/src/complex/MaterialTableControl.tsx b/packages/material/src/complex/MaterialTableControl.tsx index 86e253a18b..c0e936e030 100644 --- a/packages/material/src/complex/MaterialTableControl.tsx +++ b/packages/material/src/complex/MaterialTableControl.tsx @@ -31,7 +31,7 @@ import { } from '@jsonforms/react'; import startCase from 'lodash/startCase'; import range from 'lodash/range'; -import React, { Fragment } from 'react'; +import React, { Fragment, useMemo } from 'react'; import { FormHelperText, Grid, @@ -195,20 +195,19 @@ const controlWithoutLabel = (scope: string): ControlElement => ({ label: false }); -const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => { - const ctx = useJsonForms(); - const { - path, - propName, - schema, - rootSchema, - errors, - enabled, - renderers, - cells - } = ctxToNonEmptyCellProps(ctx, ownProps); +interface NonEmptyCellComponentProps { + path: string, + propName?: string, + schema: JsonSchema, + rootSchema: JsonSchema, + errors: string, + enabled: boolean, + renderers?: JsonFormsRendererRegistryEntry[], + cells?: JsonFormsCellRendererRegistryEntry[], + isValid: boolean +} +const NonEmptyCellComponent = React.memo(({path, propName, schema,rootSchema, errors, enabled, renderers, cells, isValid}:NonEmptyCellComponentProps) => { - const isValid = isEmpty(errors); return ( {schema.properties ? ( @@ -237,35 +236,47 @@ const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => { {!isValid && errors} ); +}); + +const NonEmptyCell = (ownProps: OwnPropsOfNonEmptyCell) => { + const ctx = useJsonForms(); + const emptyCellProps = ctxToNonEmptyCellProps(ctx, ownProps); + + const isValid = isEmpty(emptyCellProps.errors); + return }; interface NonEmptyRowProps { childPath: string; schema: JsonSchema; rowIndex: number; - moveUp: () => void; - moveDown: () => void; + moveUpCreator: (path:string, position: number)=> ()=> void; + moveDownCreator: (path:string, position: number)=> ()=> void; enableUp: boolean; enableDown: boolean; showSortButtons: boolean; enabled: boolean; cells?: JsonFormsCellRendererRegistryEntry[]; + path: string; } -const NonEmptyRow = React.memo( +const NonEmptyRowComponent = ({ childPath, schema, rowIndex, openDeleteDialog, - moveUp, - moveDown, + moveUpCreator, + moveDownCreator, enableUp, enableDown, showSortButtons, enabled, - cells + cells, + path }: NonEmptyRowProps & WithDeleteDialogSupport) => { + const moveUp = useMemo(() => moveUpCreator(path, rowIndex),[moveUpCreator, path, rowIndex]); + const moveDown = useMemo(() => moveDownCreator(path, rowIndex),[moveDownCreator, path, rowIndex]); return ( {generateCells(NonEmptyCell, schema, childPath, enabled, cells)} @@ -314,8 +325,8 @@ const NonEmptyRow = React.memo( ) : null} ); - } -); + }; +export const NonEmptyRow = React.memo(NonEmptyRowComponent); interface TableRowsProp { data: number; path: string; @@ -359,13 +370,14 @@ const TableRows = ({ rowIndex={index} schema={schema} openDeleteDialog={openDeleteDialog} - moveUp={moveUp(path, index)} - moveDown={moveDown(path, index)} + moveUpCreator={moveUp} + moveDownCreator={moveDown} enableUp={index !== 0} enableDown={index !== data - 1} showSortButtons={appliedUiSchemaOptions.showSortButtons} enabled={enabled} cells={cells} + path={path} /> ); })} diff --git a/packages/material/src/controls/MaterialBooleanControl.tsx b/packages/material/src/controls/MaterialBooleanControl.tsx index 691d618996..b52867cab9 100644 --- a/packages/material/src/controls/MaterialBooleanControl.tsx +++ b/packages/material/src/controls/MaterialBooleanControl.tsx @@ -34,7 +34,7 @@ import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormControlLabel, Hidden } from '@material-ui/core'; import { MuiCheckbox } from '../mui-controls/MuiCheckbox'; -export const MaterialBooleanControl = ({ +const MaterialBooleanControlComponent = ({ data, visible, label, @@ -78,4 +78,6 @@ export const materialBooleanControlTester: RankedTester = rankWith( 2, isBooleanControl ); + +export const MaterialBooleanControl = React.memo(MaterialBooleanControlComponent); export default withJsonFormsControlProps(MaterialBooleanControl); diff --git a/packages/material/src/controls/MaterialBooleanToggleControl.tsx b/packages/material/src/controls/MaterialBooleanToggleControl.tsx index a4d762eabb..62cda85cdd 100644 --- a/packages/material/src/controls/MaterialBooleanToggleControl.tsx +++ b/packages/material/src/controls/MaterialBooleanToggleControl.tsx @@ -36,7 +36,7 @@ import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormControlLabel, Hidden } from '@material-ui/core'; import { MuiToggle } from '../mui-controls/MuiToggle'; -export const MaterialBooleanToggleControl = ({ +const MaterialBooleanToggleControlComponent = ({ data, visible, label, @@ -81,4 +81,5 @@ export const materialBooleanToggleControlTester: RankedTester = rankWith( and(isBooleanControl, optionIs('toggle', true)) ); +export const MaterialBooleanToggleControl = React.memo(MaterialBooleanToggleControlComponent); export default withJsonFormsControlProps(MaterialBooleanToggleControl); diff --git a/packages/material/src/controls/MaterialDateControl.tsx b/packages/material/src/controls/MaterialDateControl.tsx index c5a9d67b03..c6c772b24b 100644 --- a/packages/material/src/controls/MaterialDateControl.tsx +++ b/packages/material/src/controls/MaterialDateControl.tsx @@ -23,17 +23,15 @@ THE SOFTWARE. */ import merge from 'lodash/merge'; -import React from 'react'; +import React, { useMemo } from 'react'; import { - ControlState, - DispatchPropsOfControl, + ControlProps, isDateControl, isDescriptionHidden, RankedTester, rankWith, - StatePropsOfControl } from '@jsonforms/core'; -import { Control, withJsonFormsControlProps } from '@jsonforms/react'; +import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormHelperText, Hidden } from '@material-ui/core'; import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'; @@ -43,94 +41,92 @@ import { MuiPickersUtilsProvider } from '@material-ui/pickers'; import DayJsUtils from '@date-io/dayjs'; -import { createOnChangeHandler, getData } from '../util'; +import { createOnChangeHandler, getData, useFocus } from '../util'; -export class MaterialDateControl extends Control< - StatePropsOfControl & DispatchPropsOfControl, - ControlState -> { - render() { - const { - description, - id, - errors, - label, - uischema, - visible, - enabled, - required, - path, - handleChange, - data, - config - } = this.props; - const isValid = errors.length === 0; - const appliedUiSchemaOptions = merge({}, config, uischema.options); - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); +const MaterialDateControlComponent = (props: ControlProps)=> { + const [focused, onFocus, onBlur] = useFocus(); + const { + description, + id, + errors, + label, + uischema, + visible, + enabled, + required, + path, + handleChange, + data, + config + } = props; + const isValid = errors.length === 0; + const appliedUiSchemaOptions = merge({}, config, uischema.options); + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); - const format = appliedUiSchemaOptions.dateFormat ?? 'YYYY-MM-DD'; - const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? 'YYYY-MM-DD'; + const format = appliedUiSchemaOptions.dateFormat ?? 'YYYY-MM-DD'; + const saveFormat = appliedUiSchemaOptions.dateSaveFormat ?? 'YYYY-MM-DD'; - const firstFormHelperText = showDescription - ? description - : !isValid - ? errors - : null; - const secondFormHelperText = showDescription && !isValid ? errors : null; + const firstFormHelperText = showDescription + ? description + : !isValid + ? errors + : null; + const secondFormHelperText = showDescription && !isValid ? errors : null; + const onChange = useMemo(() => createOnChangeHandler( + path, + handleChange, + saveFormat + ),[path, handleChange, saveFormat]); - return ( - - - } - rightArrowIcon={} - keyboardIcon={} - invalidDateMessage={null} - maxDateMessage={null} - minDateMessage={null} - /> - - {firstFormHelperText} - - - {secondFormHelperText} - - - - ); - } -} + return ( + + + } + rightArrowIcon={} + keyboardIcon={} + invalidDateMessage={null} + maxDateMessage={null} + minDateMessage={null} + /> + + {firstFormHelperText} + + + {secondFormHelperText} + + + + ); +}; export const materialDateControlTester: RankedTester = rankWith( 4, isDateControl ); +export const MaterialDateControl = React.memo(MaterialDateControlComponent); export default withJsonFormsControlProps(MaterialDateControl); diff --git a/packages/material/src/controls/MaterialDateTimeControl.tsx b/packages/material/src/controls/MaterialDateTimeControl.tsx index 3251c682a3..d013dcd1d4 100644 --- a/packages/material/src/controls/MaterialDateTimeControl.tsx +++ b/packages/material/src/controls/MaterialDateTimeControl.tsx @@ -22,17 +22,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, { useMemo } from 'react'; import merge from 'lodash/merge'; import { ControlProps, - ControlState, isDateTimeControl, isDescriptionHidden, RankedTester, rankWith } from '@jsonforms/core'; -import { Control, withJsonFormsControlProps } from '@jsonforms/react'; +import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormHelperText, Hidden } from '@material-ui/core'; import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'; @@ -44,98 +43,97 @@ import { MuiPickersUtilsProvider } from '@material-ui/pickers'; import DayjsUtils from '@date-io/dayjs'; -import { createOnChangeHandler, getData } from '../util'; +import { createOnChangeHandler, getData, useFocus } from '../util'; -export class MaterialDateTimeControl extends Control< - ControlProps, - ControlState -> { - render() { - const { - id, - description, - errors, - label, - uischema, - visible, - enabled, - required, - path, - handleChange, - data, - config - } = this.props; - const appliedUiSchemaOptions = merge({}, config, uischema.options); - const isValid = errors.length === 0; +export const MaterialDateTimeControlComponent = (props: ControlProps) => { + const [focused, onFocus, onBlur] = useFocus(); + const { + id, + description, + errors, + label, + uischema, + visible, + enabled, + required, + path, + handleChange, + data, + config + } = props; + const appliedUiSchemaOptions = merge({}, config, uischema.options); + const isValid = errors.length === 0; + + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); + const format = appliedUiSchemaOptions.dateTimeFormat ?? 'YYYY-MM-DD HH:mm'; + const saveFormat = appliedUiSchemaOptions.dateTimeSaveFormat ?? undefined; - const format = appliedUiSchemaOptions.dateTimeFormat ?? 'YYYY-MM-DD HH:mm'; - const saveFormat = appliedUiSchemaOptions.dateTimeSaveFormat ?? undefined; + const firstFormHelperText = showDescription + ? description + : !isValid + ? errors + : null; + const secondFormHelperText = showDescription && !isValid ? errors : null; - const firstFormHelperText = showDescription - ? description - : !isValid - ? errors - : null; - const secondFormHelperText = showDescription && !isValid ? errors : null; + const onChange = useMemo(() => createOnChangeHandler( + path, + handleChange, + saveFormat + ),[path, handleChange, saveFormat]); - return ( - - - } - rightArrowIcon={} - dateRangeIcon={} - keyboardIcon={} - timeIcon={} - invalidDateMessage={null} - maxDateMessage={null} - minDateMessage={null} - /> - - {firstFormHelperText} - - - {secondFormHelperText} - - - - ); - } -} + return ( + + + } + rightArrowIcon={} + dateRangeIcon={} + keyboardIcon={} + timeIcon={} + invalidDateMessage={null} + maxDateMessage={null} + minDateMessage={null} + /> + + {firstFormHelperText} + + + {secondFormHelperText} + + + + ); +}; export const materialDateTimeControlTester: RankedTester = rankWith( 2, isDateTimeControl ); +export const MaterialDateTimeControl = React.memo(MaterialDateTimeControlComponent); export default withJsonFormsControlProps(MaterialDateTimeControl); diff --git a/packages/material/src/controls/MaterialEnumControl.tsx b/packages/material/src/controls/MaterialEnumControl.tsx index 1aea1ef245..298fb785bf 100644 --- a/packages/material/src/controls/MaterialEnumControl.tsx +++ b/packages/material/src/controls/MaterialEnumControl.tsx @@ -34,7 +34,7 @@ import { withJsonFormsEnumProps } from '@jsonforms/react'; import { MuiSelect } from '../mui-controls/MuiSelect'; import { MaterialInputControl } from './MaterialInputControl'; -export const MaterialEnumControl = (props: ControlProps & OwnPropsOfEnum) => ( +const MaterialEnumControlComponent = (props: ControlProps & OwnPropsOfEnum) => ( ); @@ -43,4 +43,5 @@ export const materialEnumControlTester: RankedTester = rankWith( isEnumControl ); +export const MaterialEnumControl = React.memo(MaterialEnumControlComponent); export default withJsonFormsEnumProps(MaterialEnumControl); \ No newline at end of file diff --git a/packages/material/src/controls/MaterialInputControl.tsx b/packages/material/src/controls/MaterialInputControl.tsx index 1b3a1cb747..19b6f7aeb1 100644 --- a/packages/material/src/controls/MaterialInputControl.tsx +++ b/packages/material/src/controls/MaterialInputControl.tsx @@ -1,7 +1,7 @@ /* The MIT License - Copyright (c) 2017-2019 EclipseSource Munich + Copyright (c) 2017-2021 EclipseSource Munich https://github.com/eclipsesource/jsonforms Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,83 +26,80 @@ import React from 'react'; import { showAsRequired, ControlProps, - ControlState, isDescriptionHidden, } from '@jsonforms/core'; -import { Control } from '@jsonforms/react'; import { Hidden, InputLabel } from '@material-ui/core'; import { FormControl, FormHelperText } from '@material-ui/core'; import merge from 'lodash/merge'; +import { useFocus } from '../util'; export interface WithInput { input: any; } -export abstract class MaterialInputControl extends Control< - ControlProps & WithInput, - ControlState -> { - render() { - const { - id, - description, - errors, - label, - uischema, - visible, - required, - config, - input - } = this.props; - const isValid = errors.length === 0; - const appliedUiSchemaOptions = merge({}, config, uischema.options); +const MaterialInputControlComponent = (props: ControlProps & WithInput) => { + const [focused, onFocus, onBlur] = useFocus(); + const { + id, + description, + errors, + label, + uischema, + visible, + required, + config, + input + } = props; + const isValid = errors.length === 0; + const appliedUiSchemaOptions = merge({}, config, uischema.options); - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); - const firstFormHelperText = showDescription - ? description - : !isValid - ? errors - : null; - const secondFormHelperText = showDescription && !isValid ? errors : null; - const InnerComponent = input; + const firstFormHelperText = showDescription + ? description + : !isValid + ? errors + : null; + const secondFormHelperText = showDescription && !isValid ? errors : null; + const InnerComponent = input; - return ( - - + + - - {label} - - - - {firstFormHelperText} - - - {secondFormHelperText} - - - - ); - } -} + {label} + + + + {firstFormHelperText} + + + {secondFormHelperText} + + + + ); +}; + +export const MaterialInputControl = React.memo(MaterialInputControlComponent); \ No newline at end of file diff --git a/packages/material/src/controls/MaterialIntegerControl.tsx b/packages/material/src/controls/MaterialIntegerControl.tsx index edef3745ed..376c8b398c 100644 --- a/packages/material/src/controls/MaterialIntegerControl.tsx +++ b/packages/material/src/controls/MaterialIntegerControl.tsx @@ -33,11 +33,13 @@ import { MuiInputInteger } from '../mui-controls/MuiInputInteger'; import { MaterialInputControl } from './MaterialInputControl'; import { withJsonFormsControlProps } from '@jsonforms/react'; -export const MaterialIntegerControl = (props: ControlProps) => ( +const MaterialIntegerControlComponent = (props: ControlProps) => ( ); export const materialIntegerControlTester: RankedTester = rankWith( 2, isIntegerControl ); + +export const MaterialIntegerControl = React.memo(MaterialIntegerControlComponent); export default withJsonFormsControlProps(MaterialIntegerControl); diff --git a/packages/material/src/controls/MaterialNativeControl.tsx b/packages/material/src/controls/MaterialNativeControl.tsx index 6f1bab7331..0fed69ad82 100644 --- a/packages/material/src/controls/MaterialNativeControl.tsx +++ b/packages/material/src/controls/MaterialNativeControl.tsx @@ -25,7 +25,6 @@ import React from 'react'; import { ControlProps, - ControlState, showAsRequired, isDateControl, isDescriptionHidden, @@ -35,67 +34,68 @@ import { rankWith } from '@jsonforms/core'; import { Hidden } from '@material-ui/core'; -import { Control, withJsonFormsControlProps } from '@jsonforms/react'; +import { withJsonFormsControlProps } from '@jsonforms/react'; import TextField from '@material-ui/core/TextField'; import merge from 'lodash/merge'; +import { useDebouncedChange, useFocus } from '../util'; -export class MaterialNativeControl extends Control { - render() { - const { - id, - errors, - label, - schema, - description, - enabled, - visible, - required, - path, - handleChange, - data, - config - } = this.props; - const isValid = errors.length === 0; - const appliedUiSchemaOptions = merge( - {}, - config, - this.props.uischema.options - ); - const onChange = (ev: any) => handleChange(path, ev.target.value); - const fieldType = appliedUiSchemaOptions.format ?? schema.format; - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); +const MaterialNativeControlComponent = (props: ControlProps) => { + const [focused, onFocus, onBlur] = useFocus(); + const { + id, + errors, + label, + schema, + description, + enabled, + visible, + required, + path, + handleChange, + data, + config + } = props; + const isValid = errors.length === 0; + const appliedUiSchemaOptions = merge( + {}, + config, + props.uischema.options + ); + const [inputValue, onChange] = useDebouncedChange(handleChange, '', data, path); + const fieldType = appliedUiSchemaOptions.format ?? schema.format; + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); - return ( - - - - ); - } -} + return ( + + + + ); +}; export const materialNativeControlTester: RankedTester = rankWith( 2, or(isDateControl, isTimeControl) ); +export const MaterialNativeControl = React.memo(MaterialNativeControlComponent); export default withJsonFormsControlProps(MaterialNativeControl); diff --git a/packages/material/src/controls/MaterialNumberControl.tsx b/packages/material/src/controls/MaterialNumberControl.tsx index 85fceb9be8..c014450aa4 100644 --- a/packages/material/src/controls/MaterialNumberControl.tsx +++ b/packages/material/src/controls/MaterialNumberControl.tsx @@ -33,7 +33,7 @@ import { MuiInputNumber } from '../mui-controls/MuiInputNumber'; import { MaterialInputControl } from './MaterialInputControl'; import { withJsonFormsControlProps } from '@jsonforms/react'; -export const MaterialNumberControl = (props: ControlProps) => ( +const MaterialNumberControlComponent = (props: ControlProps) => ( ); @@ -42,4 +42,5 @@ export const materialNumberControlTester: RankedTester = rankWith( isNumberControl ); +export const MaterialNumberControl = React.memo(MaterialNumberControlComponent); export default withJsonFormsControlProps(MaterialNumberControl); diff --git a/packages/material/src/controls/MaterialOneOfEnumControl.tsx b/packages/material/src/controls/MaterialOneOfEnumControl.tsx index 65c20a28e1..cf21c41f10 100644 --- a/packages/material/src/controls/MaterialOneOfEnumControl.tsx +++ b/packages/material/src/controls/MaterialOneOfEnumControl.tsx @@ -34,7 +34,7 @@ import { withJsonFormsOneOfEnumProps } from '@jsonforms/react'; import { MuiSelect } from '../mui-controls/MuiSelect'; import { MaterialInputControl } from './MaterialInputControl'; -export const MaterialOneOfEnumControl = (props: ControlProps & OwnPropsOfEnum) => ( +const MaterialOneOfEnumControlComponent = (props: ControlProps & OwnPropsOfEnum) => ( ); @@ -43,4 +43,5 @@ export const materialOneOfEnumControlTester: RankedTester = rankWith( isOneOfEnumControl ); +export const MaterialOneOfEnumControl = React.memo(MaterialOneOfEnumControlComponent); export default withJsonFormsOneOfEnumProps(MaterialOneOfEnumControl); diff --git a/packages/material/src/controls/MaterialOneOfRadioGroupControl.tsx b/packages/material/src/controls/MaterialOneOfRadioGroupControl.tsx index 42aafc5aa0..4c8084b965 100644 --- a/packages/material/src/controls/MaterialOneOfRadioGroupControl.tsx +++ b/packages/material/src/controls/MaterialOneOfRadioGroupControl.tsx @@ -35,7 +35,7 @@ import { import { withJsonFormsOneOfEnumProps } from '@jsonforms/react'; import { MaterialRadioGroup } from './MaterialRadioGroup'; -export const MaterialOneOfRadioGroupControl = (props: ControlProps & OwnPropsOfEnum) => { +const MaterialOneOfRadioGroupControlComponent = (props: ControlProps & OwnPropsOfEnum) => { return ; }; @@ -44,4 +44,5 @@ export const materialOneOfRadioGroupControlTester: RankedTester = rankWith( and(isOneOfEnumControl, optionIs('format', 'radio')) ); +export const MaterialOneOfRadioGroupControl = React.memo(MaterialOneOfRadioGroupControlComponent); export default withJsonFormsOneOfEnumProps(MaterialOneOfRadioGroupControl); diff --git a/packages/material/src/controls/MaterialRadioGroup.tsx b/packages/material/src/controls/MaterialRadioGroup.tsx index 417da51e22..c27db1e564 100644 --- a/packages/material/src/controls/MaterialRadioGroup.tsx +++ b/packages/material/src/controls/MaterialRadioGroup.tsx @@ -26,12 +26,10 @@ import merge from 'lodash/merge'; import React from 'react'; import { ControlProps, - ControlState, showAsRequired, isDescriptionHidden, OwnPropsOfEnum } from '@jsonforms/core'; -import { Control } from '@jsonforms/react'; import Radio from '@material-ui/core/Radio'; import RadioGroup from '@material-ui/core/RadioGroup'; import { @@ -41,71 +39,75 @@ import { FormLabel, Hidden } from '@material-ui/core'; +import { useFocus } from '../util'; -export class MaterialRadioGroup extends Control< - ControlProps & OwnPropsOfEnum, - ControlState -> { - render() { - const { - config, - id, - label, - required, - description, - errors, - data, - visible, - options - } = this.props; - const isValid = errors.length === 0; - const appliedUiSchemaOptions = merge( - {}, - config, - this.props.uischema.options - ); - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); +const MaterialRadioGroupComponent = (props: ControlProps & OwnPropsOfEnum) => { + const [focused, onFocus, onBlur] = useFocus(); + const { + config, + id, + label, + required, + description, + errors, + data, + visible, + options, + handleChange, + path + } = props; + const isValid = errors.length === 0; + const appliedUiSchemaOptions = merge( + {}, + config, + props.uischema.options + ); + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); + const onChange = (_ev:any, value:any) => handleChange(path, value); - return ( - - + + - - {label} - + {label} + - this.handleChange(value)} - row={true} - > - {options.map(option => ( - } - label={option.label} - /> - ))} - - - {!isValid ? errors : showDescription ? description : null} - - - - ); - } -} + + {options.map(option => ( + } + label={option.label} + /> + ))} + + + {!isValid ? errors : showDescription ? description : null} + + + + ); +}; + +export const MaterialRadioGroup = React.memo(MaterialRadioGroupComponent); \ No newline at end of file diff --git a/packages/material/src/controls/MaterialRadioGroupControl.tsx b/packages/material/src/controls/MaterialRadioGroupControl.tsx index d5729ab119..2221893cc9 100644 --- a/packages/material/src/controls/MaterialRadioGroupControl.tsx +++ b/packages/material/src/controls/MaterialRadioGroupControl.tsx @@ -31,7 +31,8 @@ import { } from '@jsonforms/core'; import { withJsonFormsEnumProps } from '@jsonforms/react'; import { MaterialRadioGroup } from './MaterialRadioGroup'; -export const MaterialRadioGroupControl = (props: ControlProps & OwnPropsOfEnum) => { + +const MaterialRadioGroupControlComponent = (props: ControlProps & OwnPropsOfEnum) => { return ; }; @@ -39,4 +40,6 @@ export const materialRadioGroupControlTester: RankedTester = rankWith( 20, and(isEnumControl, optionIs('format', 'radio')) ); + +export const MaterialRadioGroupControl = React.memo(MaterialRadioGroupControlComponent); export default withJsonFormsEnumProps(MaterialRadioGroupControl); diff --git a/packages/material/src/controls/MaterialSliderControl.tsx b/packages/material/src/controls/MaterialSliderControl.tsx index 9a319581e4..8ad9003421 100644 --- a/packages/material/src/controls/MaterialSliderControl.tsx +++ b/packages/material/src/controls/MaterialSliderControl.tsx @@ -22,17 +22,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, {useCallback} from 'react'; import { ControlProps, - ControlState, showAsRequired, isDescriptionHidden, isRangeControl, RankedTester, rankWith } from '@jsonforms/core'; -import { Control, withJsonFormsControlProps } from '@jsonforms/react'; +import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormControl, @@ -43,101 +42,103 @@ import { Typography } from '@material-ui/core'; import merge from 'lodash/merge'; +import { useFocus } from '../util'; -export class MaterialSliderControl extends Control { - render() { - const { - id, - data, - description, - enabled, - errors, - label, - schema, - handleChange, - visible, - path, - required, - config - } = this.props; - const isValid = errors.length === 0; - const appliedUiSchemaOptions = merge( - {}, - config, - this.props.uischema.options - ); - const labelStyle: { [x: string]: any } = { - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - width: '100%' - }; - const rangeContainerStyle: { [x: string]: any } = { - display: 'flex' - }; - const rangeItemStyle: { [x: string]: any } = { - flexGrow: '1' - }; - const sliderStyle: { [x: string]: any } = { - marginTop: '7px' - }; +const MaterialSliderControlComponent = (props: ControlProps) => { + const [focused, onFocus, onBlur] = useFocus(); + const { + id, + data, + description, + enabled, + errors, + label, + schema, + handleChange, + visible, + path, + required, + config + } = props; + const isValid = errors.length === 0; + const appliedUiSchemaOptions = merge( + {}, + config, + props.uischema.options + ); + const labelStyle: { [x: string]: any } = { + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + width: '100%' + }; + const rangeContainerStyle: { [x: string]: any } = { + display: 'flex' + }; + const rangeItemStyle: { [x: string]: any } = { + flexGrow: '1' + }; + const sliderStyle: { [x: string]: any } = { + marginTop: '7px' + }; - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); - return ( - - handleChange(path, Number(value)), [path, handleChange]); + + return ( + + + - - - {label} - - -
- - {schema.minimum} - - - {schema.maximum} - -
- { - handleChange(path, Number(value)); - }} - id={id + '-input'} - disabled={!enabled} - step={schema.multipleOf || 1} - /> - - {!isValid ? errors : showDescription ? description : null} - -
-
- ); - } -} + + {label} + + +
+ + {schema.minimum} + + + {schema.maximum} + +
+ + + {!isValid ? errors : showDescription ? description : null} + +
+
+ ); +}; export const materialSliderControlTester: RankedTester = rankWith( 4, isRangeControl ); +export const MaterialSliderControl = React.memo(MaterialSliderControlComponent) export default withJsonFormsControlProps(MaterialSliderControl); diff --git a/packages/material/src/controls/MaterialTextControl.tsx b/packages/material/src/controls/MaterialTextControl.tsx index 2a0ee1e6d6..94dfb2f3bb 100644 --- a/packages/material/src/controls/MaterialTextControl.tsx +++ b/packages/material/src/controls/MaterialTextControl.tsx @@ -33,7 +33,7 @@ import { withJsonFormsControlProps } from '@jsonforms/react'; import { MuiInputText } from '../mui-controls/MuiInputText'; import { MaterialInputControl } from './MaterialInputControl'; -export const MaterialTextControl = (props: ControlProps) => ( +const MaterialTextControlComponent = (props: ControlProps) => ( ); @@ -41,4 +41,6 @@ export const materialTextControlTester: RankedTester = rankWith( 1, isStringControl ); + +export const MaterialTextControl = React.memo(MaterialTextControlComponent); export default withJsonFormsControlProps(MaterialTextControl); diff --git a/packages/material/src/controls/MaterialTimeControl.tsx b/packages/material/src/controls/MaterialTimeControl.tsx index 21698b36c5..2fd7ef7f98 100644 --- a/packages/material/src/controls/MaterialTimeControl.tsx +++ b/packages/material/src/controls/MaterialTimeControl.tsx @@ -22,110 +22,108 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, { useMemo } from 'react'; import merge from 'lodash/merge'; import { ControlProps, - ControlState, isTimeControl, isDescriptionHidden, RankedTester, rankWith } from '@jsonforms/core'; -import { Control, withJsonFormsControlProps } from '@jsonforms/react'; +import { withJsonFormsControlProps } from '@jsonforms/react'; import { FormHelperText, Hidden } from '@material-ui/core'; import { KeyboardTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; import DayjsUtils from '@date-io/dayjs'; -import { createOnChangeHandler, getData } from '../util'; +import { createOnChangeHandler, getData, useFocus } from '../util'; -export class MaterialTimeControl extends Control< - ControlProps, - ControlState -> { - render() { +const MaterialTimeControlComponent = (props: ControlProps) => { + const [focused, onFocus, onBlur] = useFocus(); const { - id, - description, - errors, - label, - uischema, - visible, - enabled, - required, - path, - handleChange, - data, - config - } = this.props; - const appliedUiSchemaOptions = merge({}, config, uischema.options); - const isValid = errors.length === 0; + id, + description, + errors, + label, + uischema, + visible, + enabled, + required, + path, + handleChange, + data, + config + } = props; + const appliedUiSchemaOptions = merge({}, config, uischema.options); + const isValid = errors.length === 0; - const showDescription = !isDescriptionHidden( - visible, - description, - this.state.isFocused, - appliedUiSchemaOptions.showUnfocusedDescription - ); + const showDescription = !isDescriptionHidden( + visible, + description, + focused, + appliedUiSchemaOptions.showUnfocusedDescription + ); - const format = appliedUiSchemaOptions.timeFormat ?? 'HH:mm'; + const format = appliedUiSchemaOptions.timeFormat ?? 'HH:mm'; const saveFormat = appliedUiSchemaOptions.timeSaveFormat ?? 'HH:mm:ss'; - const firstFormHelperText = showDescription - ? description - : !isValid - ? errors - : null; - const secondFormHelperText = showDescription && !isValid ? errors : null; + const firstFormHelperText = showDescription + ? description + : !isValid + ? errors + : null; + const secondFormHelperText = showDescription && !isValid ? errors : null; + + const onChange = useMemo(() => createOnChangeHandler( + path, + handleChange, + saveFormat + ),[path, handleChange, saveFormat]); - return ( - - - - - {firstFormHelperText} - - - {secondFormHelperText} - - - - ); - } -} + return ( + + + + + {firstFormHelperText} + + + {secondFormHelperText} + + + + ); +}; export const materialTimeControlTester: RankedTester = rankWith( 4, isTimeControl ); +export const MaterialTimeControl = React.memo(MaterialTimeControlComponent); export default withJsonFormsControlProps(MaterialTimeControl); diff --git a/packages/material/src/extended/MaterialAutocompleteEnumControl.tsx b/packages/material/src/extended/MaterialAutocompleteEnumControl.tsx index 18b0c25512..0a5b868409 100644 --- a/packages/material/src/extended/MaterialAutocompleteEnumControl.tsx +++ b/packages/material/src/extended/MaterialAutocompleteEnumControl.tsx @@ -36,7 +36,7 @@ import merge from 'lodash/merge'; import { MaterialInputControl } from '../controls/MaterialInputControl'; import { MuiAutocomplete, WithOptionLabel } from './MuiAutocomplete'; -export const MaterialAutocompleteEnumControl = (props: ControlProps & OwnPropsOfEnum & WithOptionLabel) => { +const MaterialAutocompleteEnumControlComponent = (props: ControlProps & OwnPropsOfEnum & WithOptionLabel) => { const {config, uischema} = props; const appliedUiSchemaOptions = merge({}, config, uischema.options); return ( @@ -52,4 +52,5 @@ export const materialAutocompleteEnumControlTester: RankedTester = rankWith( isEnumControl ); +export const MaterialAutocompleteEnumControl = React.memo(MaterialAutocompleteEnumControlComponent); export default withJsonFormsEnumProps(MaterialAutocompleteEnumControl); diff --git a/packages/material/src/extended/MaterialAutocompleteOneOfEnumControl.tsx b/packages/material/src/extended/MaterialAutocompleteOneOfEnumControl.tsx index 220ea15316..92e71ad11e 100644 --- a/packages/material/src/extended/MaterialAutocompleteOneOfEnumControl.tsx +++ b/packages/material/src/extended/MaterialAutocompleteOneOfEnumControl.tsx @@ -36,7 +36,7 @@ import { MuiSelect } from '../mui-controls/MuiSelect'; import { MaterialInputControl } from '../controls/MaterialInputControl'; import merge from 'lodash/merge'; -export const MaterialAutocompleteOneOfEnumControl = (props: ControlProps & OwnPropsOfEnum & WithOptionLabel) => { +const MaterialAutocompleteOneOfEnumControlComponent = (props: ControlProps & OwnPropsOfEnum & WithOptionLabel) => { const {config, uischema} = props; const appliedUiSchemaOptions = merge({}, config, uischema.options); return ( @@ -52,4 +52,5 @@ export const materialAutocompleteOneOfEnumControlTester: RankedTester = rankWith isOneOfEnumControl ); +export const MaterialAutocompleteOneOfEnumControl = React.memo(MaterialAutocompleteOneOfEnumControlComponent); export default withJsonFormsOneOfEnumProps(MaterialAutocompleteOneOfEnumControl); diff --git a/packages/material/src/extended/MuiAutocomplete.tsx b/packages/material/src/extended/MuiAutocomplete.tsx index acde0e2605..60b9cbced5 100644 --- a/packages/material/src/extended/MuiAutocomplete.tsx +++ b/packages/material/src/extended/MuiAutocomplete.tsx @@ -27,7 +27,6 @@ import { EnumCellProps, EnumOption, WithClassname } from '@jsonforms/core'; import Input from '@material-ui/core/Input'; import Autocomplete, { AutocompleteRenderOptionState } from '@material-ui/lab/Autocomplete'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; import { FilterOptionsState } from '@material-ui/lab/useAutocomplete'; @@ -37,7 +36,7 @@ export interface WithOptionLabel { filterOptions?(options: EnumOption[], state: FilterOptionsState) : EnumOption[]; } -export const MuiAutocomplete = React.memo((props: EnumCellProps & WithClassname & WithOptionLabel) => { +export const MuiAutocomplete = (props: EnumCellProps & WithClassname & WithOptionLabel) => { const { data, className, @@ -90,4 +89,4 @@ export const MuiAutocomplete = React.memo((props: EnumCellProps & WithClassname filterOptions={filterOptions} /> ); -}, areEqual); +}; diff --git a/packages/material/src/layouts/ExpandPanelRenderer.tsx b/packages/material/src/layouts/ExpandPanelRenderer.tsx index 4535d48af2..f9a0ef8b9d 100644 --- a/packages/material/src/layouts/ExpandPanelRenderer.tsx +++ b/packages/material/src/layouts/ExpandPanelRenderer.tsx @@ -1,8 +1,7 @@ import merge from 'lodash/merge'; import get from 'lodash/get'; -import React, { ComponentType, Dispatch, Fragment, ReducerAction, useMemo, useState, useEffect } from 'react'; +import React, { ComponentType, Dispatch, Fragment, ReducerAction, useMemo, useState, useEffect, useCallback } from 'react'; import { - areEqual, JsonFormsDispatch, JsonFormsStateContext, withJsonFormsContext @@ -210,7 +209,7 @@ const ExpandPanelRenderer = (props: ExpandPanelProps) => { export const ctxDispatchToExpandPanelProps: ( dispatch: Dispatch> ) => DispatchPropsOfExpandPanel = dispatch => ({ - removeItems: (path: string, toDelete: number[]) => (event: any): void => { + removeItems: useCallback((path: string, toDelete: number[]) => (event: any): void => { event.stopPropagation(); dispatch( update(path, array => { @@ -221,8 +220,8 @@ export const ctxDispatchToExpandPanelProps: ( return array; }) ); - }, - moveUp: (path: string, toMove: number) => (event: any): void => { + }, [dispatch]), + moveUp: useCallback((path: string, toMove: number) => (event: any): void => { event.stopPropagation(); dispatch( update(path, array => { @@ -230,8 +229,8 @@ export const ctxDispatchToExpandPanelProps: ( return array; }) ); - }, - moveDown: (path: string, toMove: number) => (event: any): void => { + }, [dispatch]), + moveDown: useCallback((path: string, toMove: number) => (event: any): void => { event.stopPropagation(); dispatch( update(path, array => { @@ -239,7 +238,7 @@ export const ctxDispatchToExpandPanelProps: ( return array; }) ); - } + }, [dispatch]) }); /** @@ -277,13 +276,6 @@ export const withJsonFormsExpandPanelProps = ( Component: ComponentType ): ComponentType => withJsonFormsContext( - withContextToExpandPanelProps( - React.memo( - Component, - (prevProps: ExpandPanelProps, nextProps: ExpandPanelProps) => - areEqual(prevProps, nextProps) - ) - ) - ); + withContextToExpandPanelProps(Component)); export default withJsonFormsExpandPanelProps(ExpandPanelRenderer); diff --git a/packages/material/src/layouts/MaterialArrayLayout.tsx b/packages/material/src/layouts/MaterialArrayLayout.tsx index 48a471863f..f50b3e31f4 100644 --- a/packages/material/src/layouts/MaterialArrayLayout.tsx +++ b/packages/material/src/layouts/MaterialArrayLayout.tsx @@ -23,7 +23,7 @@ THE SOFTWARE. */ import range from 'lodash/range'; -import React from 'react'; +import React, {useState, useCallback} from 'react'; import { ArrayLayoutProps, composePaths, @@ -35,87 +35,78 @@ import { ArrayLayoutToolbar } from './ArrayToolbar'; import ExpandPanelRenderer from './ExpandPanelRenderer'; import merge from 'lodash/merge'; -interface MaterialArrayLayoutState { - expanded: string | boolean; -} -export class MaterialArrayLayout extends React.PureComponent< - ArrayLayoutProps, - MaterialArrayLayoutState -> { - state: MaterialArrayLayoutState = { - expanded: null - }; - innerCreateDefaultValue = () => createDefaultValue(this.props.schema); - handleChange = (panel: string) => (_event: any, expanded: boolean) => { - this.setState({ - expanded: expanded ? panel : false - }); - }; - isExpanded = (index: number) => - this.state.expanded === composePaths(this.props.path, `${index}`); - render() { - const { - data, - path, - schema, - uischema, - errors, - addItem, - renderers, - cells, - label, - required, - rootSchema, - config, - uischemas - } = this.props; - const appliedUiSchemaOptions = merge( - {}, - config, - this.props.uischema.options - ); +const MaterialArrayLayoutComponent = (props: ArrayLayoutProps)=> { + const [expanded, setExpanded] = useState(false); + const innerCreateDefaultValue = useCallback(() => createDefaultValue(props.schema), [props.schema]); + const handleChange = useCallback((panel: string) => (_event: any, expandedPanel: boolean) => { + setExpanded(expandedPanel ? panel : false) + }, []); + const isExpanded = (index: number) => + expanded === composePaths(props.path, `${index}`); + + const { + data, + path, + schema, + uischema, + errors, + addItem, + renderers, + cells, + label, + required, + rootSchema, + config, + uischemas + } = props; + const appliedUiSchemaOptions = merge( + {}, + config, + props.uischema.options + ); - return ( + return ( +
+
- -
- {data > 0 ? ( - map(range(data), index => { - return ( - - ); - }) - ) : ( -

No data

- )} -
+ {data > 0 ? ( + map(range(data), index => { + return ( + + ); + }) + ) : ( +

No data

+ )}
- ); - } -} +
+ ); +}; + +export const MaterialArrayLayout = React.memo(MaterialArrayLayoutComponent); \ No newline at end of file diff --git a/packages/material/src/layouts/MaterialCategorizationLayout.tsx b/packages/material/src/layouts/MaterialCategorizationLayout.tsx index 2f369f866e..b74c18c12e 100644 --- a/packages/material/src/layouts/MaterialCategorizationLayout.tsx +++ b/packages/material/src/layouts/MaterialCategorizationLayout.tsx @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, {useState} from 'react'; import { Hidden, Tab, Tabs } from '@material-ui/core'; import AppBar from '@material-ui/core/AppBar'; import { @@ -37,7 +37,7 @@ import { UISchemaElement, uiTypeIs } from '@jsonforms/core'; -import { RendererComponent, withJsonFormsLayoutProps } from '@jsonforms/react'; +import { withJsonFormsLayoutProps } from '@jsonforms/react'; import { AjvProps, MaterialLayoutRenderer, @@ -76,71 +76,56 @@ export interface MaterialCategorizationLayoutRendererProps onChange?(selected: number, prevSelected: number): void; } -export class MaterialCategorizationLayoutRenderer extends RendererComponent< - MaterialCategorizationLayoutRendererProps, - CategorizationState -> { - state = { - activeCategory: 0 +const MaterialCategorizationLayoutRendererComponent = (props: MaterialCategorizationLayoutRendererProps) => { + const { + data, + path, + renderers, + cells, + schema, + uischema, + visible, + enabled, + selected, + onChange, + ajv + } = props; + const categorization = uischema as Categorization; + const [activeCategory, setActiveCategory]= useState(selected??0); + const childProps: MaterialLayoutRendererProps = { + elements: categorization.elements[activeCategory].elements, + schema, + path, + direction: 'column', + enabled, + visible, + renderers, + cells }; - - render() { - const { - data, - path, - renderers, - cells, - schema, - uischema, - visible, - enabled, - selected, - ajv - } = this.props; - const categorization = uischema as Categorization; - const value = this.hasOwnState() ? this.state.activeCategory : selected; - const childProps: MaterialLayoutRendererProps = { - elements: categorization.elements[value].elements, - schema, - path, - direction: 'column', - enabled, - visible, - renderers, - cells - }; - const categories = categorization.elements.filter((category: Category) => - isVisible(category, data, undefined, ajv) - ); - return ( - - - - {categories.map((e: Category, idx: number) => ( - - ))} - - -
- -
-
- ); - } - - hasOwnState = () => { - return this.props.ownState !== undefined ? this.props.ownState : true; - }; - - private handleChange = (_event: any, value: any) => { - if (this.props.onChange) { - this.props.onChange(value, this.state.activeCategory); - } - const hasOwnState = this.hasOwnState(); - if (hasOwnState) { - this.setState({ activeCategory: value }); + const categories = categorization.elements.filter((category: Category) => + isVisible(category, data, undefined, ajv) + ); + const onTabChange = (_event: any, value: any) => { + if (onChange) { + onChange(value, activeCategory); } + setActiveCategory(value); }; -} + return ( + + + + {categories.map((e: Category, idx: number) => ( + + ))} + + +
+ +
+
+ ); +}; +export const MaterialCategorizationLayoutRenderer = React.memo(MaterialCategorizationLayoutRendererComponent); export default withJsonFormsLayoutProps(withAjvProps(MaterialCategorizationLayoutRenderer)); diff --git a/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx b/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx index b2b122933b..9ceacdc32b 100644 --- a/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx +++ b/packages/material/src/layouts/MaterialCategorizationStepperLayout.tsx @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React from 'react'; +import React, {useState} from 'react'; import merge from 'lodash/merge'; import { Button, Hidden, Step, StepButton, Stepper } from '@material-ui/core'; import { @@ -37,7 +37,7 @@ import { StatePropsOfLayout, uiTypeIs } from '@jsonforms/core'; -import { RendererComponent, withJsonFormsLayoutProps } from '@jsonforms/react'; +import { withJsonFormsLayoutProps } from '@jsonforms/react'; import { AjvProps, MaterialLayoutRenderer, @@ -63,94 +63,88 @@ export interface MaterialCategorizationStepperLayoutRendererProps data: any; } -export class MaterialCategorizationStepperLayoutRenderer extends RendererComponent< - MaterialCategorizationStepperLayoutRendererProps, - CategorizationStepperState -> { - state = { - activeCategory: 0 +const MaterialCategorizationStepperLayoutRendererComponent = (props: MaterialCategorizationStepperLayoutRendererProps)=> { + const [activeCategory, setActiveCategory] = useState(0); + + const handleStep = (step: number) => { + setActiveCategory( step); }; - handleStep = (step: number) => { - this.setState({ activeCategory: step }); + const { + data, + path, + renderers, + schema, + uischema, + visible, + cells, + config, + ajv + } = props; + const categorization = uischema as Categorization; + const appliedUiSchemaOptions = merge({}, config, uischema.options); + const buttonWrapperStyle = { + textAlign: 'right' as 'right', + width: '100%', + margin: '1em auto' + }; + const buttonNextStyle = { + float: 'right' as 'right' + }; + const buttonStyle = { + marginRight: '1em' }; + const childProps: MaterialLayoutRendererProps = { + elements: categorization.elements[activeCategory].elements, + schema, + path, + direction: 'column', + visible, + renderers, + cells + }; + const categories = categorization.elements.filter((category: Category) => + isVisible(category, data, undefined, ajv) + ); + return ( + + + {categories.map((e: Category, idx: number) => ( + + handleStep(idx)}> + {e.label} + + + ))} + +
+ +
+ { !!appliedUiSchemaOptions.showNavButtons ? (
+ + +
) : (<>)} +
+ ); +}; - render() { - const { - data, - path, - renderers, - schema, - uischema, - visible, - cells, - config, - ajv - } = this.props; - const categorization = uischema as Categorization; - const activeCategory = this.state.activeCategory; - const appliedUiSchemaOptions = merge({}, config, uischema.options); - const buttonWrapperStyle = { - textAlign: 'right' as 'right', - width: '100%', - margin: '1em auto' - }; - const buttonNextStyle = { - float: 'right' as 'right' - }; - const buttonStyle = { - marginRight: '1em' - }; - const childProps: MaterialLayoutRendererProps = { - elements: categorization.elements[activeCategory].elements, - schema, - path, - direction: 'column', - visible, - renderers, - cells - }; - const categories = categorization.elements.filter((category: Category) => - isVisible(category, data, undefined, ajv) - ); - return ( - - - {categories.map((e: Category, idx: number) => ( - - this.handleStep(idx)}> - {e.label} - - - ))} - -
- -
- { !!appliedUiSchemaOptions.showNavButtons ? (
- - -
) : (<>)} -
- ); - } -} +export const MaterialCategorizationStepperLayoutRenderer = React.memo(MaterialCategorizationStepperLayoutRendererComponent); export default withJsonFormsLayoutProps(withAjvProps( MaterialCategorizationStepperLayoutRenderer diff --git a/packages/material/src/layouts/MaterialGroupLayout.tsx b/packages/material/src/layouts/MaterialGroupLayout.tsx index ea45d0a82e..4123111f33 100644 --- a/packages/material/src/layouts/MaterialGroupLayout.tsx +++ b/packages/material/src/layouts/MaterialGroupLayout.tsx @@ -58,7 +58,7 @@ const GroupComponent = React.memo(({ visible, enabled, uischema, ...props }: Mat ); }); -export const MaterializedGroupLayoutRenderer = ({ uischema, schema, path, visible, enabled, renderers, cells, direction }: LayoutProps) => { +const MaterializedGroupLayoutRendererComponent = ({ uischema, schema, path, visible, enabled, renderers, cells, direction }: LayoutProps) => { const groupLayout = uischema as GroupLayout; return ( @@ -76,6 +76,7 @@ export const MaterializedGroupLayoutRenderer = ({ uischema, schema, path, visibl ); }; +export const MaterializedGroupLayoutRenderer = React.memo(MaterializedGroupLayoutRendererComponent); export default withJsonFormsLayoutProps(MaterializedGroupLayoutRenderer); export const materialGroupTester: RankedTester = withIncreasedRank( diff --git a/packages/material/src/layouts/MaterialHorizontalLayout.tsx b/packages/material/src/layouts/MaterialHorizontalLayout.tsx index a451030ca1..0302d4a4cb 100644 --- a/packages/material/src/layouts/MaterialHorizontalLayout.tsx +++ b/packages/material/src/layouts/MaterialHorizontalLayout.tsx @@ -45,7 +45,7 @@ export const materialHorizontalLayoutTester: RankedTester = rankWith( uiTypeIs('HorizontalLayout') ); -export const MaterialHorizontalLayoutRenderer = ({ uischema, renderers, cells, schema, path, enabled, visible }: LayoutProps) => { +const MaterialHorizontalLayoutRendererComponent = ({ uischema, renderers, cells, schema, path, enabled, visible }: LayoutProps) => { const layout = uischema as HorizontalLayout; const childProps: MaterialLayoutRendererProps = { elements: layout.elements, @@ -59,4 +59,5 @@ export const MaterialHorizontalLayoutRenderer = ({ uischema, renderers, cells, s return ; }; +export const MaterialHorizontalLayoutRenderer = React.memo(MaterialHorizontalLayoutRendererComponent); export default withJsonFormsLayoutProps(MaterialHorizontalLayoutRenderer); diff --git a/packages/material/src/layouts/MaterialVerticalLayout.tsx b/packages/material/src/layouts/MaterialVerticalLayout.tsx index 5c0d841e16..7425775f5c 100644 --- a/packages/material/src/layouts/MaterialVerticalLayout.tsx +++ b/packages/material/src/layouts/MaterialVerticalLayout.tsx @@ -45,7 +45,7 @@ export const materialVerticalLayoutTester: RankedTester = rankWith( uiTypeIs('VerticalLayout') ); -export const MaterialVerticalLayoutRenderer = ({ uischema, schema, path, enabled, visible, renderers, cells }: LayoutProps) => { +const MaterialVerticalLayoutRendererComponent = ({ uischema, schema, path, enabled, visible, renderers, cells }: LayoutProps) => { const verticalLayout = uischema as VerticalLayout; const childProps: MaterialLayoutRendererProps = { elements: verticalLayout.elements, @@ -59,4 +59,5 @@ export const MaterialVerticalLayoutRenderer = ({ uischema, schema, path, enabled return ; }; +export const MaterialVerticalLayoutRenderer = React.memo(MaterialVerticalLayoutRendererComponent); export default withJsonFormsLayoutProps(MaterialVerticalLayoutRenderer); diff --git a/packages/material/src/mui-controls/MuiCheckbox.tsx b/packages/material/src/mui-controls/MuiCheckbox.tsx index c5bf4c2236..bbf39e57a2 100644 --- a/packages/material/src/mui-controls/MuiCheckbox.tsx +++ b/packages/material/src/mui-controls/MuiCheckbox.tsx @@ -25,10 +25,9 @@ import React from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; import Checkbox from '@material-ui/core/Checkbox'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; -export const MuiCheckbox = React.memo((props: CellProps & WithClassname) => { +export const MuiCheckbox = (props: CellProps & WithClassname) => { const { data, className, @@ -54,4 +53,4 @@ export const MuiCheckbox = React.memo((props: CellProps & WithClassname) => { inputProps={inputProps} /> ); -}, areEqual); +}; diff --git a/packages/material/src/mui-controls/MuiInputInteger.tsx b/packages/material/src/mui-controls/MuiInputInteger.tsx index e1826ef5eb..c51011d0a5 100644 --- a/packages/material/src/mui-controls/MuiInputInteger.tsx +++ b/packages/material/src/mui-controls/MuiInputInteger.tsx @@ -25,7 +25,6 @@ import React from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; import Input from '@material-ui/core/Input'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; import { useDebouncedChange } from '../util'; @@ -33,7 +32,7 @@ const toNumber = (value: string) => value === '' ? undefined : parseInt(value, 10); const eventToValue = (ev:any) => toNumber(ev.target.value); -export const MuiInputInteger = React.memo( +export const MuiInputInteger = (props: CellProps & WithClassname) => { const { data, @@ -64,6 +63,4 @@ export const MuiInputInteger = React.memo( fullWidth={true} /> ); - }, - areEqual -); + }; diff --git a/packages/material/src/mui-controls/MuiInputNumber.tsx b/packages/material/src/mui-controls/MuiInputNumber.tsx index 6a5e4e738e..4e1fe9e54b 100644 --- a/packages/material/src/mui-controls/MuiInputNumber.tsx +++ b/packages/material/src/mui-controls/MuiInputNumber.tsx @@ -25,7 +25,6 @@ import React from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; import Input from '@material-ui/core/Input'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; import {useDebouncedChange} from '../util'; @@ -61,4 +60,4 @@ export const MuiInputNumber = React.memo((props: CellProps & WithClassname) => { fullWidth={true} /> ); -}, areEqual); +}); diff --git a/packages/material/src/mui-controls/MuiInputNumberFormat.tsx b/packages/material/src/mui-controls/MuiInputNumberFormat.tsx index c4fba22013..1bc7b92d2c 100644 --- a/packages/material/src/mui-controls/MuiInputNumberFormat.tsx +++ b/packages/material/src/mui-controls/MuiInputNumberFormat.tsx @@ -25,11 +25,10 @@ import React, {useCallback} from 'react'; import { CellProps, Formatted, WithClassname } from '@jsonforms/core'; import Input from '@material-ui/core/Input'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; import { useDebouncedChange } from '../util'; -export const MuiInputNumberFormat = React.memo( +export const MuiInputNumberFormat = (props: CellProps & WithClassname & Formatted) => { const { className, @@ -71,6 +70,4 @@ export const MuiInputNumberFormat = React.memo( error={!isValid} /> ); - }, - areEqual -); + }; diff --git a/packages/material/src/mui-controls/MuiInputText.tsx b/packages/material/src/mui-controls/MuiInputText.tsx index abca13fdcd..eb6848a3c2 100644 --- a/packages/material/src/mui-controls/MuiInputText.tsx +++ b/packages/material/src/mui-controls/MuiInputText.tsx @@ -24,7 +24,6 @@ */ import React, { useState } from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; -import { areEqual } from '@jsonforms/react'; import Input, { InputProps } from '@material-ui/core/Input'; import merge from 'lodash/merge'; import IconButton from '@material-ui/core/IconButton'; @@ -115,4 +114,4 @@ export const MuiInputText = React.memo((props: CellProps & WithClassname & MuiTe inputComponent={inputComponent} /> ); -}, areEqual); +}); diff --git a/packages/material/src/mui-controls/MuiInputTime.tsx b/packages/material/src/mui-controls/MuiInputTime.tsx index 9c82b3f1a3..7a7f1ba8be 100644 --- a/packages/material/src/mui-controls/MuiInputTime.tsx +++ b/packages/material/src/mui-controls/MuiInputTime.tsx @@ -25,11 +25,10 @@ import React from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; import Input from '@material-ui/core/Input'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; import { useDebouncedChange } from '../util'; -export const MuiInputTime = React.memo((props: CellProps & WithClassname) => { +export const MuiInputTime = (props: CellProps & WithClassname) => { const { data, className, @@ -55,4 +54,4 @@ export const MuiInputTime = React.memo((props: CellProps & WithClassname) => { fullWidth={true} /> ); -}, areEqual); +}; diff --git a/packages/material/src/mui-controls/MuiSelect.tsx b/packages/material/src/mui-controls/MuiSelect.tsx index 73db3c005c..7d2a706de0 100644 --- a/packages/material/src/mui-controls/MuiSelect.tsx +++ b/packages/material/src/mui-controls/MuiSelect.tsx @@ -27,10 +27,9 @@ import { EnumCellProps, WithClassname } from '@jsonforms/core'; import Select from '@material-ui/core/Select'; import { MenuItem } from '@material-ui/core'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; -export const MuiSelect = React.memo((props: EnumCellProps & WithClassname) => { +export const MuiSelect = (props: EnumCellProps & WithClassname) => { const { data, className, @@ -63,4 +62,4 @@ export const MuiSelect = React.memo((props: EnumCellProps & WithClassname) => { )} ); -}, areEqual); +}; diff --git a/packages/material/src/mui-controls/MuiToggle.tsx b/packages/material/src/mui-controls/MuiToggle.tsx index 1000c0e52e..294f670405 100644 --- a/packages/material/src/mui-controls/MuiToggle.tsx +++ b/packages/material/src/mui-controls/MuiToggle.tsx @@ -25,10 +25,9 @@ import React from 'react'; import { CellProps, WithClassname } from '@jsonforms/core'; import Switch from '@material-ui/core/Switch'; -import { areEqual } from '@jsonforms/react'; import merge from 'lodash/merge'; -export const MuiToggle = React.memo((props: CellProps & WithClassname) => { +export const MuiToggle = (props: CellProps & WithClassname) => { const { data, className, @@ -53,4 +52,4 @@ export const MuiToggle = React.memo((props: CellProps & WithClassname) => { inputProps={inputProps} /> ); -}, areEqual); +}; diff --git a/packages/material/src/util/debounce.ts b/packages/material/src/util/debounce.ts index c5b2991718..c5c7739fab 100644 --- a/packages/material/src/util/debounce.ts +++ b/packages/material/src/util/debounce.ts @@ -1,3 +1,27 @@ +/* + The MIT License + + Copyright (c) 2021 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ import { debounce } from 'lodash'; import { useState, useCallback, useEffect } from 'react' diff --git a/packages/material/src/util/focus.ts b/packages/material/src/util/focus.ts new file mode 100644 index 0000000000..5b7921d917 --- /dev/null +++ b/packages/material/src/util/focus.ts @@ -0,0 +1,32 @@ +/* + The MIT License + + Copyright (c) 2021 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +import {useState, useCallback} from 'react'; + +export const useFocus = ():[boolean, () => void, () => void] => { + const [focused, setFocused] = useState(false); + const onFocus = useCallback(() => setFocused(true), []); + const onBlur = useCallback(() => setFocused(false), []); + return [focused, onFocus, onBlur]; +}; \ No newline at end of file diff --git a/packages/material/src/util/index.ts b/packages/material/src/util/index.ts index 0999bc9e25..474e83318f 100644 --- a/packages/material/src/util/index.ts +++ b/packages/material/src/util/index.ts @@ -26,3 +26,4 @@ export * from './datejs'; export * from './layout'; export * from './theme'; export * from './debounce'; +export * from './focus'; diff --git a/packages/material/src/util/layout.tsx b/packages/material/src/util/layout.tsx index 02755d88c3..72fe05ba2e 100644 --- a/packages/material/src/util/layout.tsx +++ b/packages/material/src/util/layout.tsx @@ -34,7 +34,7 @@ import { OwnPropsOfRenderer, UISchemaElement } from '@jsonforms/core'; -import { areEqual, JsonFormsDispatch, useJsonForms } from '@jsonforms/react'; +import { JsonFormsDispatch, useJsonForms } from '@jsonforms/react'; import { Grid, Hidden } from '@material-ui/core'; export const renderLayoutElements = ( @@ -96,9 +96,7 @@ export const MaterialLayoutRenderer = React.memo( ); } - }, - areEqual -); + }); export interface AjvProps { ajv: Ajv; diff --git a/packages/react/src/DispatchCell.tsx b/packages/react/src/DispatchCell.tsx index 21bffd5547..a3606d2243 100644 --- a/packages/react/src/DispatchCell.tsx +++ b/packages/react/src/DispatchCell.tsx @@ -23,7 +23,7 @@ THE SOFTWARE. */ import maxBy from 'lodash/maxBy'; -import React from 'react'; +import React, { useMemo } from 'react'; import { UnknownRenderer } from './UnknownRenderer'; import { DispatchCellProps } from '@jsonforms/core'; import { withJsonFormsDispatchCellProps } from './JsonFormsContext'; @@ -31,37 +31,25 @@ import { withJsonFormsDispatchCellProps } from './JsonFormsContext'; /** * Dispatch renderer component for cells. */ -class Dispatch extends React.Component { - render() { - const { - uischema, - schema, - path, - cells, - id, - enabled, - renderers - } = this.props; - const cell = maxBy(cells, r => r.tester(uischema, schema)); - if (cell === undefined || cell.tester(uischema, schema) === -1) { - return ; - } else { - const Cell = cell.cell; - return ( - - - - ); - } +const DispatchComponent = ({uischema, schema, path, cells, id, enabled, renderers}:DispatchCellProps) => { + const cell = useMemo(() => maxBy(cells, r => r.tester(uischema, schema)), [cells, uischema, schema]); + if (cell === undefined || cell.tester(uischema, schema) === -1) { + return ; + } else { + const Cell = cell.cell; + return ( + + ); } -} +}; +export const Dispatch = React.memo(DispatchComponent); export const DispatchCell = withJsonFormsDispatchCellProps(Dispatch); diff --git a/packages/react/src/JsonFormsContext.tsx b/packages/react/src/JsonFormsContext.tsx index 93fad5cf82..046beb27a5 100644 --- a/packages/react/src/JsonFormsContext.tsx +++ b/packages/react/src/JsonFormsContext.tsx @@ -28,7 +28,7 @@ import { ArrayControlProps, ArrayLayoutProps, CellProps, - CombinatorProps, + CombinatorRendererProps, ControlProps, defaultMapStateToEnumCellProps, DispatchCellProps, @@ -44,13 +44,10 @@ import { OwnPropsOfJsonFormsRenderer, OwnPropsOfLayout, OwnPropsOfMasterListItem, - OwnPropsOfRenderer, - StatePropsOfCombinator, StatePropsOfControlWithDetail, StatePropsOfMasterItem, configReducer, coreReducer, - mapDispatchToArrayControlProps, mapStateToAllOfProps, mapStateToAnyOfProps, mapStateToArrayControlProps, @@ -69,14 +66,12 @@ import { mapDispatchToMultiEnumProps, update, mapStateToMultiEnumControlProps, - DispatchPropsOfMultiEnumControl + DispatchPropsOfMultiEnumControl, + moveUp, + moveDown } from '@jsonforms/core'; import React, { ComponentType, Dispatch, ReducerAction, useCallback, useContext, useEffect, useMemo, useReducer, useRef } from 'react'; -import get from 'lodash/get'; -import isEqual from 'lodash/isEqual'; -import omit from 'lodash/omit'; - const initialCoreState: JsonFormsCore = { data: {}, schema: {}, @@ -188,8 +183,20 @@ export const ctxToLayoutProps = (ctx: JsonFormsStateContext, props: OwnPropsOfLa export const ctxToControlProps = (ctx: JsonFormsStateContext, props: OwnPropsOfControl) => mapStateToControlProps({ jsonforms: { ...ctx } }, props); -export const ctxToEnumControlProps = (ctx: JsonFormsStateContext, props: OwnPropsOfControl) => - mapStateToEnumControlProps({ jsonforms: { ...ctx } }, props); +/** + * Create the props for an enum control. + * Make sure, that options are memoized as otherwise the cell will rerender for every change, + * as the options array is recreated every time. + * + * @param ctx The @link JsonFormsStateContext + * @param ownProps The @link EnumCellProps + * @returns The props of the cell. + */ +export const ctxToEnumControlProps = (ctx: JsonFormsStateContext, props: OwnPropsOfEnum) => { + const enumProps = mapStateToEnumControlProps({ jsonforms: { ...ctx } }, props); + const options = useMemo(() => enumProps.options, [props.options, enumProps.schema.enum, enumProps.schema.const]); + return {...enumProps, options} +} export const ctxToOneOfEnumControlProps = (ctx: JsonFormsStateContext, props: OwnPropsOfControl) => mapStateToOneOfEnumControlProps({ jsonforms: { ...ctx } }, props); @@ -224,24 +231,24 @@ export const ctxDispatchToControlProps = (dispatch: Dispatch> export const ctxToAnyOfProps = ( ctx: JsonFormsStateContext, ownProps: OwnPropsOfControl -): CombinatorProps => { +): CombinatorRendererProps => { const props = mapStateToAnyOfProps({ jsonforms: { ...ctx } }, ownProps); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return { ...props, - handleChange + ...dispatchProps }; }; export const ctxToOneOfProps = ( ctx: JsonFormsStateContext, ownProps: OwnPropsOfControl -): CombinatorProps => { +): CombinatorRendererProps => { const props = mapStateToOneOfProps({ jsonforms: { ...ctx } }, ownProps); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return { ...props, - handleChange + ...dispatchProps }; }; @@ -250,8 +257,47 @@ export const ctxToJsonFormsDispatchProps = ( ownProps: OwnPropsOfJsonFormsRenderer ) => mapStateToJsonFormsRendererProps({ jsonforms: { ...ctx } }, ownProps); -export const ctxDispatchToArrayControlProps = (dispatch: Dispatch>) => - mapDispatchToArrayControlProps(dispatch as any); +export const ctxDispatchToArrayControlProps = (dispatch: Dispatch>) => ({ + addItem: useCallback((path: string, value: any) => () => { + dispatch( + update(path, array => { + if (array === undefined || array === null) { + return [value]; + } + + array.push(value); + return array; + }) + ); + }, [dispatch]), + removeItems: useCallback((path: string, toDelete: number[]) => () => { + dispatch( + update(path, array => { + toDelete + .sort() + .reverse() + .forEach(s => array.splice(s, 1)); + return array; + }) + ); + }, [dispatch]), + moveUp: useCallback((path: string, toMove: number) => () => { + dispatch( + update(path, array => { + moveUp(array, toMove); + return array; + }) + ); + }, [dispatch]), + moveDown: useCallback((path: string, toMove: number) => () => { + dispatch( + update(path, array => { + moveDown(array, toMove); + return array; + }) + ); + }, [dispatch]) +}); export const ctxToMasterListItemProps = ( ctx: JsonFormsStateContext, @@ -265,11 +311,22 @@ export const ctxToCellProps = ( return mapStateToCellProps({ jsonforms: { ...ctx } }, ownProps); }; +/** + * Create the props for an enum control. + * Make sure, that options are memoized as otherwise the cell will rerender for every change, + * as the options array is recreated every time. + * + * @param ctx The @link JsonFormsStateContext + * @param ownProps The @link EnumCellProps + * @returns The props of the cell. + */ export const ctxToEnumCellProps = ( ctx: JsonFormsStateContext, - ownProps: OwnPropsOfCell + ownProps: EnumCellProps ) => { - return defaultMapStateToEnumCellProps({ jsonforms: { ...ctx } }, ownProps); + const cellProps = defaultMapStateToEnumCellProps({ jsonforms: { ...ctx } }, ownProps); + const options = useMemo(() => cellProps.options, [ownProps.options, cellProps.schema.enum, cellProps.schema.const]); + return {...cellProps, options} }; export const ctxToOneOfEnumCellProps = ( @@ -306,8 +363,8 @@ const withContextToControlProps = (Component: ComponentType): ComponentType => ({ ctx, props }: JsonFormsStateContext & ControlProps) => { const controlProps = ctxToControlProps(ctx, props); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); - return (); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); + return (); }; const withContextToLayoutProps = @@ -318,27 +375,27 @@ const withContextToLayoutProps = }; const withContextToOneOfProps = - (Component: ComponentType): ComponentType => - ({ ctx, props }: JsonFormsStateContext & CombinatorProps) => { + (Component: ComponentType): ComponentType => + ({ ctx, props }: JsonFormsStateContext & CombinatorRendererProps) => { const oneOfProps = ctxToOneOfProps(ctx, props); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); - return (); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); + return (); }; const withContextToAnyOfProps = - (Component: ComponentType): ComponentType => - ({ ctx, props }: JsonFormsStateContext & CombinatorProps) => { + (Component: ComponentType): ComponentType => + ({ ctx, props }: JsonFormsStateContext & CombinatorRendererProps) => { const oneOfProps = ctxToAnyOfProps(ctx, props); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); - return (); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); + return (); }; const withContextToAllOfProps = - (Component: ComponentType): ComponentType => - ({ ctx, props }: JsonFormsStateContext & CombinatorProps) => { + (Component: ComponentType): ComponentType => + ({ ctx, props }: JsonFormsStateContext & CombinatorRendererProps) => { const allOfProps = ctxToAllOfProps(ctx, props); - const { handleChange } = ctxDispatchToControlProps(ctx.dispatch); - return (); + const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); + return (); }; const withContextToDetailProps = @@ -398,8 +455,7 @@ const withContextToEnumCellProps = ({ ctx, props }: JsonFormsStateContext & EnumCellProps) => { const cellProps = ctxToEnumCellProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); - - return (); + return (); }; const withContextToEnumProps = @@ -407,7 +463,8 @@ const withContextToEnumProps = ({ ctx, props }: JsonFormsStateContext & ControlProps & OwnPropsOfEnum) => { const stateProps = ctxToEnumControlProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); - return (); + + return (); }; const withContextToOneOfEnumCellProps = @@ -437,144 +494,72 @@ const withContextToMultiEnumProps = // -- -type JsonFormsPropTypes = ControlProps | CombinatorProps | LayoutProps | CellProps | ArrayLayoutProps | StatePropsOfControlWithDetail | OwnPropsOfRenderer; - -export const areEqual = (prevProps: JsonFormsPropTypes, nextProps: JsonFormsPropTypes) => { - const prev = omit(prevProps, ['schema', 'uischema', 'handleChange', 'renderers', 'cells', 'uischemas']); - const next = omit(nextProps, ['schema', 'uischema', 'handleChange', 'renderers', 'cells', 'uischemas']); - return isEqual(prev, next) - && get(prevProps, 'renderers') === get(nextProps, 'renderers') - && get(prevProps, 'cells') === get(nextProps, 'cells') - && get(prevProps, 'uischemas') === get(nextProps, 'uischemas') - && get(prevProps, 'schema') === get(nextProps, 'schema') - && get(prevProps, 'uischema') === get(nextProps, 'uischema'); -}; - // top level HOCs -- export const withJsonFormsControlProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToControlProps(React.memo( - Component, - (prevProps: ControlProps, nextProps: ControlProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToControlProps(Component)); export const withJsonFormsLayoutProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToLayoutProps(React.memo( - Component, - (prevProps: LayoutProps, nextProps: LayoutProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToLayoutProps(Component)); export const withJsonFormsOneOfProps = - (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToOneOfProps(React.memo( - Component, - (prevProps: CombinatorProps, nextProps: CombinatorProps) => areEqual(prevProps, nextProps) - ))); + (Component: ComponentType): ComponentType => + withJsonFormsContext(withContextToOneOfProps(Component)); export const withJsonFormsAnyOfProps = - (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToAnyOfProps(React.memo( - Component, - (prevProps: CombinatorProps, nextProps: CombinatorProps) => areEqual(prevProps, nextProps) - ))); + (Component: ComponentType): ComponentType => + withJsonFormsContext(withContextToAnyOfProps(Component)); export const withJsonFormsAllOfProps = - (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToAllOfProps(React.memo( - Component, - (prevProps: CombinatorProps, nextProps: CombinatorProps) => areEqual(prevProps, nextProps) - ))); + (Component: ComponentType): ComponentType => + withJsonFormsContext(withContextToAllOfProps(Component)); export const withJsonFormsDetailProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToDetailProps(React.memo( - Component, - (prevProps: StatePropsOfControlWithDetail, nextProps: StatePropsOfControlWithDetail) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToDetailProps(Component)); export const withJsonFormsArrayLayoutProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToArrayLayoutProps(React.memo( - Component, - (prevProps: ArrayLayoutProps, nextProps: ArrayLayoutProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToArrayLayoutProps(Component)); export const withJsonFormsArrayControlProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToArrayControlProps(React.memo( - Component, - (prevProps: ArrayControlProps, nextProps: ArrayControlProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToArrayControlProps(Component)); export const withJsonFormsMasterListItemProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToMasterListItemProps(React.memo( - Component, - (prevProps: StatePropsOfMasterItem, nextProps: StatePropsOfMasterItem) => - areEqual(omit(prevProps, ['handleSelect', 'removeItem']), omit(nextProps, ['handleSelect', 'removeItem'])) - ))); + withJsonFormsContext(withContextToMasterListItemProps(Component)); export const withJsonFormsCellProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToCellProps(React.memo( - Component, - (prevProps: CellProps, nextProps: CellProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToCellProps(Component)); export const withJsonFormsDispatchCellProps = ( Component: ComponentType ): ComponentType => - withJsonFormsContext( - withContextToDispatchCellProps( - React.memo(Component, (prevProps: DispatchCellProps, nextProps: DispatchCellProps) => - areEqual(prevProps, nextProps) - ) - ) - ); + withJsonFormsContext(withContextToDispatchCellProps(Component)); export const withJsonFormsEnumCellProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToEnumCellProps(React.memo( - Component, - (prevProps: EnumCellProps, nextProps: EnumCellProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToEnumCellProps(Component)); export const withJsonFormsEnumProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToEnumProps(React.memo( - Component, - (prevProps: ControlProps & OwnPropsOfEnum, nextProps: ControlProps & OwnPropsOfEnum) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToEnumProps(Component)); export const withJsonFormsOneOfEnumCellProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToOneOfEnumCellProps(React.memo( - Component, - (prevProps: EnumCellProps, nextProps: EnumCellProps) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToOneOfEnumCellProps(Component)); export const withJsonFormsOneOfEnumProps = (Component: ComponentType): ComponentType => - withJsonFormsContext(withContextToOneOfEnumProps(React.memo( - Component, - (prevProps: ControlProps & OwnPropsOfEnum, nextProps: ControlProps & OwnPropsOfEnum) => areEqual(prevProps, nextProps) - ))); + withJsonFormsContext(withContextToOneOfEnumProps(Component)); export const withJsonFormsMultiEnumProps = ( Component: ComponentType ): ComponentType => - withJsonFormsContext( - withContextToMultiEnumProps( - React.memo( - Component, - ( - prevProps: ControlProps & OwnPropsOfEnum & DispatchPropsOfMultiEnumControl, - nextProps: ControlProps & OwnPropsOfEnum & DispatchPropsOfMultiEnumControl - ) => areEqual(prevProps, nextProps) - ) - ) - ); + withJsonFormsContext(withContextToMultiEnumProps(Component)); // --