UNPKG

@jsonforms/react

Version:

React module of JSON Forms

410 lines (402 loc) 23.6 kB
import React, { Component, useRef, useState, useEffect, useReducer, useCallback, useMemo, useContext } from 'react'; import maxBy from 'lodash/maxBy'; import { defaultMiddleware, Actions, coreReducer, configReducer, i18nReducer, mapStateToArrayLayoutProps, mapStateToArrayControlProps, mapStateToLayoutProps, mapStateToControlProps, mapStateToEnumControlProps, mapStateToOneOfEnumControlProps, mapStateToMultiEnumControlProps, mapStateToControlWithDetailProps, mapStateToAllOfProps, mapDispatchToControlProps, mapStateToAnyOfProps, mapStateToOneOfProps, mapStateToJsonFormsRendererProps, mapDispatchToArrayControlProps, mapStateToMasterListItemProps, mapStateToCellProps, defaultMapStateToEnumCellProps, mapStateToOneOfEnumCellProps, mapStateToDispatchCellProps, mapDispatchToMultiEnumProps, mapStateToLabelProps, defaultJsonFormsI18nState, getArrayTranslations, arrayDefaultTranslations, isControl, createId, removeId, Generate } from '@jsonforms/core'; import debounce from 'lodash/debounce'; class RendererComponent extends React.Component { constructor(props) { super(props); } } class Control extends RendererComponent { constructor(props) { super(props); this.handleChange = (value) => { this.setState({ value }); this.updateData(value); }; this.onFocus = () => { this.setState({ isFocused: true }); }; this.onBlur = () => { this.setState({ isFocused: false }); }; this.updateData = (value) => { this.props.handleChange(this.props.path, value); }; this.state = { value: props.data ? props.data : '', isFocused: false, }; } } class UnknownRenderer extends Component { render() { return (React.createElement("div", { style: { color: 'red' } }, "No applicable ", this.props.type, " found.")); } } const initialCoreState = { data: {}, schema: {}, uischema: undefined, errors: [], additionalErrors: [], validator: undefined, ajv: undefined, }; const JsonFormsContext = React.createContext({ core: initialCoreState, renderers: [], }); const useEffectAfterFirstRender = (effect, dependencies) => { const firstExecution = useRef(true); useEffect(() => { if (firstExecution.current) { firstExecution.current = false; return; } effect(); }, dependencies); }; const JsonFormsStateProvider = ({ children, initState, onChange, middleware, }) => { const { data, schema, uischema, ajv, validationMode, additionalErrors } = initState.core; const middlewareRef = useRef(middleware ?? defaultMiddleware); middlewareRef.current = middleware ?? defaultMiddleware; const [core, setCore] = useState(() => middlewareRef.current(initState.core, Actions.init(data, schema, uischema, { ajv, validationMode, additionalErrors, }), coreReducer)); useEffect(() => setCore((currentCore) => middlewareRef.current(currentCore, Actions.updateCore(data, schema, uischema, { ajv, validationMode, additionalErrors, }), coreReducer)), [data, schema, uischema, ajv, validationMode, additionalErrors]); const [config, configDispatch] = useReducer(configReducer, undefined, () => configReducer(undefined, Actions.setConfig(initState.config))); useEffectAfterFirstRender(() => { configDispatch(Actions.setConfig(initState.config)); }, [initState.config]); const [i18n, i18nDispatch] = useReducer(i18nReducer, undefined, () => i18nReducer(initState.i18n, Actions.updateI18n(initState.i18n?.locale, initState.i18n?.translate, initState.i18n?.translateError))); useEffect(() => { i18nDispatch(Actions.updateI18n(initState.i18n?.locale, initState.i18n?.translate, initState.i18n?.translateError)); }, [ initState.i18n?.locale, initState.i18n?.translate, initState.i18n?.translateError, ]); const dispatch = useCallback((action) => { setCore((currentCore) => middlewareRef.current(currentCore, action, coreReducer)); }, []); const contextValue = useMemo(() => ({ core, renderers: initState.renderers, cells: initState.cells, config: config, uischemas: initState.uischemas, readonly: initState.readonly, i18n: i18n, dispatch: dispatch, }), [ core, initState.renderers, initState.cells, config, initState.uischemas, initState.readonly, i18n, ]); const onChangeRef = useRef(onChange); useEffect(() => { onChangeRef.current = onChange; }, [onChange]); const debouncedEmit = useCallback(debounce((...args) => onChangeRef.current?.(...args), 10), []); useEffect(() => { debouncedEmit({ data: core.data, errors: core.errors }); }, [core.data, core.errors]); return (React.createElement(JsonFormsContext.Provider, { value: contextValue }, children)); }; const useJsonForms = () => useContext(JsonFormsContext); const ctxToArrayLayoutProps = (ctx, props) => mapStateToArrayLayoutProps({ jsonforms: { ...ctx } }, props); const ctxToArrayControlProps = (ctx, props) => mapStateToArrayControlProps({ jsonforms: { ...ctx } }, props); const ctxToLayoutProps = (ctx, props) => mapStateToLayoutProps({ jsonforms: { ...ctx } }, props); const ctxToControlProps = (ctx, props) => mapStateToControlProps({ jsonforms: { ...ctx } }, props); const ctxToEnumControlProps = (ctx, props) => { const enumProps = mapStateToEnumControlProps({ jsonforms: { ...ctx } }, props); const options = useMemo(() => enumProps.options, [props.options, enumProps.schema, ctx.i18n?.translate]); return { ...enumProps, options }; }; const ctxToOneOfEnumControlProps = (ctx, props) => { const enumProps = mapStateToOneOfEnumControlProps({ jsonforms: { ...ctx } }, props); const options = useMemo(() => enumProps.options, [props.options, enumProps.schema, ctx.i18n?.translate]); return { ...enumProps, options }; }; const ctxToMultiEnumControlProps = (ctx, props) => { const enumProps = mapStateToMultiEnumControlProps({ jsonforms: { ...ctx } }, props); const options = useMemo(() => enumProps.options, [enumProps.schema, ctx.i18n?.translate]); return { ...enumProps, options }; }; const ctxToControlWithDetailProps = (ctx, props) => mapStateToControlWithDetailProps({ jsonforms: { ...ctx } }, props); const ctxToAllOfProps = (ctx, ownProps) => { const props = mapStateToAllOfProps({ jsonforms: { ...ctx } }, ownProps); return { ...props, }; }; const ctxDispatchToControlProps = (dispatch) => useMemo(() => mapDispatchToControlProps(dispatch), [dispatch]); const ctxToAnyOfProps = (ctx, ownProps) => { const props = mapStateToAnyOfProps({ jsonforms: { ...ctx } }, ownProps); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return { ...props, ...dispatchProps, }; }; const ctxToOneOfProps = (ctx, ownProps) => { const props = mapStateToOneOfProps({ jsonforms: { ...ctx } }, ownProps); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return { ...props, ...dispatchProps, }; }; const ctxToJsonFormsRendererProps = (ctx, ownProps) => mapStateToJsonFormsRendererProps({ jsonforms: { ...ctx } }, ownProps); const ctxDispatchToArrayControlProps = (dispatch) => ({ ...ctxDispatchToControlProps(dispatch), ...useMemo(() => mapDispatchToArrayControlProps(dispatch), [dispatch]), }); const ctxToMasterListItemProps = (ctx, ownProps) => mapStateToMasterListItemProps({ jsonforms: { ...ctx } }, ownProps); const ctxToCellProps = (ctx, ownProps) => { return mapStateToCellProps({ jsonforms: { ...ctx } }, ownProps); }; const ctxToEnumCellProps = (ctx, ownProps) => { const cellProps = defaultMapStateToEnumCellProps({ jsonforms: { ...ctx } }, ownProps); const options = useMemo(() => cellProps.options, [ownProps.options, cellProps.schema, ctx.i18n?.translate]); return { ...cellProps, options }; }; const ctxToOneOfEnumCellProps = (ctx, props) => { const enumCellProps = mapStateToOneOfEnumCellProps({ jsonforms: { ...ctx } }, props); const options = useMemo(() => enumCellProps.options, [props.options, enumCellProps.schema, ctx.i18n?.translate]); return { ...enumCellProps, options }; }; const ctxToDispatchCellProps = (ctx, ownProps) => { return mapStateToDispatchCellProps({ jsonforms: { ...ctx } }, ownProps); }; const ctxDispatchToMultiEnumProps = (dispatch) => ({ ...ctxDispatchToControlProps(dispatch), ...useMemo(() => mapDispatchToMultiEnumProps(dispatch), [dispatch]), }); const ctxToLabelProps = (ctx, ownProps) => { return mapStateToLabelProps({ jsonforms: { ...ctx } }, ownProps); }; const withJsonFormsContext = (Component) => function WithJsonFormsContext(props) { const ctx = useJsonForms(); return React.createElement(Component, { ctx: ctx, props: props }); }; const withContextToJsonFormsRendererProps = (Component) => function WithContextToJsonFormsRendererProps({ ctx, props, }) { const contextProps = ctxToJsonFormsRendererProps(ctx, props); return React.createElement(Component, { ...props, ...contextProps }); }; const withContextToControlProps = (Component) => function WithContextToControlProps({ ctx, props, }) { const controlProps = ctxToControlProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...controlProps, ...dispatchProps }); }; const withContextToLayoutProps = (Component) => function WithContextToLayoutProps({ ctx, props, }) { const layoutProps = ctxToLayoutProps(ctx, props); return React.createElement(Component, { ...props, ...layoutProps }); }; const withContextToOneOfProps = (Component) => function WithContextToOneOfProps({ ctx, props, }) { const oneOfProps = ctxToOneOfProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...oneOfProps, ...dispatchProps }); }; const withContextToAnyOfProps = (Component) => function WithContextToAnyOfProps({ ctx, props, }) { const oneOfProps = ctxToAnyOfProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...oneOfProps, ...dispatchProps }); }; const withContextToAllOfProps = (Component) => function WithContextToAllOfProps({ ctx, props, }) { const allOfProps = ctxToAllOfProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...allOfProps, ...dispatchProps }); }; const withContextToDetailProps = (Component) => function WithContextToDetailProps({ ctx, props, }) { const detailProps = ctxToControlWithDetailProps(ctx, props); return React.createElement(Component, { ...props, ...detailProps }); }; const withContextToArrayLayoutProps = (Component) => function WithContextToArrayLayoutProps({ ctx, props, }) { const arrayLayoutProps = ctxToArrayLayoutProps(ctx, props); const dispatchProps = ctxDispatchToArrayControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...arrayLayoutProps, ...dispatchProps }); }; const withContextToArrayControlProps = (Component) => function WithContextToArrayControlProps({ ctx, props, }) { const stateProps = ctxToArrayControlProps(ctx, props); const dispatchProps = ctxDispatchToArrayControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...stateProps, ...dispatchProps }); }; const withContextToMasterListItemProps = (Component) => function WithContextToMasterListItemProps({ ctx, props, }) { const stateProps = ctxToMasterListItemProps(ctx, props); return React.createElement(Component, { ...props, ...stateProps }); }; const withContextToCellProps = (Component) => function WithContextToCellProps({ ctx, props, }) { const cellProps = ctxToCellProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...cellProps }); }; const withContextToDispatchCellProps = (Component) => function WithContextToDispatchCellProps({ ctx, props, }) { const cellProps = ctxToDispatchCellProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...cellProps }); }; const withContextToEnumCellProps = (Component) => function WithContextToEnumCellProps({ ctx, props, }) { const cellProps = ctxToEnumCellProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...cellProps }); }; const withContextToEnumProps = (Component) => function WithContextToEnumProps({ ctx, props, }) { const stateProps = ctxToEnumControlProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...stateProps }); }; const withContextToOneOfEnumCellProps = (Component) => function WithContextToOneOfEnumCellProps({ ctx, props, }) { const cellProps = ctxToOneOfEnumCellProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...cellProps }); }; const withContextToOneOfEnumProps = (Component) => function WithContextToOneOfEnumProps({ ctx, props, }) { const stateProps = ctxToOneOfEnumControlProps(ctx, props); const dispatchProps = ctxDispatchToControlProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...stateProps }); }; const withContextToMultiEnumProps = (Component) => function WithContextToMultiEnumProps({ ctx, props, }) { const stateProps = ctxToMultiEnumControlProps(ctx, props); const dispatchProps = ctxDispatchToMultiEnumProps(ctx.dispatch); return React.createElement(Component, { ...props, ...dispatchProps, ...stateProps }); }; const withContextToLabelProps = (Component) => function WithContextToLabelProps({ ctx, props, }) { const stateProps = ctxToLabelProps(ctx, props); return React.createElement(Component, { ...props, ...stateProps }); }; const withJsonFormsRendererProps = (Component, memoize = true) => withJsonFormsContext(withContextToJsonFormsRendererProps(memoize ? React.memo(Component) : Component)); const withJsonFormsControlProps = (Component, memoize = true) => withJsonFormsContext(withContextToControlProps(memoize ? React.memo(Component) : Component)); const withJsonFormsLayoutProps = (Component, memoize = true) => withJsonFormsContext(withContextToLayoutProps(memoize ? React.memo(Component) : Component)); const withJsonFormsOneOfProps = (Component, memoize = true) => withJsonFormsContext(withContextToOneOfProps(memoize ? React.memo(Component) : Component)); const withJsonFormsAnyOfProps = (Component, memoize = true) => withJsonFormsContext(withContextToAnyOfProps(memoize ? React.memo(Component) : Component)); const withJsonFormsAllOfProps = (Component, memoize = true) => withJsonFormsContext(withContextToAllOfProps(memoize ? React.memo(Component) : Component)); const withJsonFormsDetailProps = (Component, memoize = true) => withJsonFormsContext(withContextToDetailProps(memoize ? React.memo(Component) : Component)); const withJsonFormsArrayLayoutProps = (Component, memoize = true) => withJsonFormsContext(withContextToArrayLayoutProps(memoize ? React.memo(Component) : Component)); const withJsonFormsArrayControlProps = (Component, memoize = true) => withJsonFormsContext(withContextToArrayControlProps(memoize ? React.memo(Component) : Component)); const withJsonFormsMasterListItemProps = (Component, memoize = true) => withJsonFormsContext(withContextToMasterListItemProps(memoize ? React.memo(Component) : Component)); const withJsonFormsCellProps = (Component, memoize = true) => withJsonFormsContext(withContextToCellProps(memoize ? React.memo(Component) : Component)); const withJsonFormsDispatchCellProps = (Component, memoize = true) => withJsonFormsContext(withContextToDispatchCellProps(memoize ? React.memo(Component) : Component)); const withJsonFormsEnumCellProps = (Component, memoize = true) => withJsonFormsContext(withContextToEnumCellProps(memoize ? React.memo(Component) : Component)); const withJsonFormsEnumProps = (Component, memoize = true) => withJsonFormsContext(withContextToEnumProps(memoize ? React.memo(Component) : Component)); const withJsonFormsOneOfEnumCellProps = (Component, memoize = true) => withJsonFormsContext(withContextToOneOfEnumCellProps(memoize ? React.memo(Component) : Component)); const withJsonFormsOneOfEnumProps = (Component, memoize = true) => withJsonFormsContext(withContextToOneOfEnumProps(memoize ? React.memo(Component) : Component)); const withJsonFormsMultiEnumProps = (Component, memoize = true) => withJsonFormsContext(withContextToMultiEnumProps(memoize ? React.memo(Component) : Component)); const withJsonFormsLabelProps = (Component, memoize = true) => withJsonFormsContext(withContextToLabelProps(memoize ? React.memo(Component) : Component)); const withTranslateProps = (Component) => function WithTranslateProps(props) { const ctx = useJsonForms(); const locale = ctx.i18n?.locale ?? defaultJsonFormsI18nState.locale; const t = ctx.i18n?.translate ?? defaultJsonFormsI18nState.translate; return React.createElement(Component, { ...props, locale: locale, t: t }); }; const withArrayTranslationProps = (Component) => function withArrayTranslatationProps(props) { const translations = useMemo(() => getArrayTranslations(props.t, arrayDefaultTranslations, props.i18nKeyPrefix, props.label), [props.t, props.i18nKeyPrefix, props.label]); return React.createElement(Component, { ...props, translations: translations }); }; class JsonFormsDispatchRenderer extends React.Component { constructor(props) { super(props); this.state = { id: isControl(props.uischema) ? createId(props.uischema.scope) : undefined, }; } componentWillUnmount() { if (isControl(this.props.uischema)) { removeId(this.state.id); } } componentDidUpdate(prevProps) { if (prevProps.schema !== this.props.schema) { removeId(this.state.id); this.setState({ id: isControl(this.props.uischema) ? createId(this.props.uischema.scope) : undefined, }); } } render() { const { schema, rootSchema, uischema, path, enabled, renderers, cells, config, } = this.props; return (React.createElement(TestAndRender, { uischema: uischema, schema: schema, rootSchema: rootSchema, path: path, enabled: enabled, renderers: renderers, cells: cells, id: this.state.id, config: config })); } } const TestAndRender = React.memo(function TestAndRender(props) { const testerContext = useMemo(() => ({ rootSchema: props.rootSchema, config: props.config, }), [props.rootSchema, props.config]); const renderer = useMemo(() => maxBy(props.renderers, (r) => r.tester(props.uischema, props.schema, testerContext)), [props.renderers, props.uischema, props.schema, testerContext]); if (renderer === undefined || renderer.tester(props.uischema, props.schema, testerContext) === -1) { return React.createElement(UnknownRenderer, { type: 'renderer' }); } else { const Render = renderer.renderer; return (React.createElement(Render, { uischema: props.uischema, schema: props.schema, path: props.path, enabled: props.enabled, renderers: props.renderers, cells: props.cells, id: props.id })); } }); class ResolvedJsonFormsDispatchRenderer extends JsonFormsDispatchRenderer { constructor(props) { super(props); } } const JsonFormsDispatch = withJsonFormsRendererProps(JsonFormsDispatchRenderer); const ResolvedJsonFormsDispatch = withJsonFormsRendererProps(ResolvedJsonFormsDispatchRenderer); const JsonForms = (props) => { const { ajv, data, schema, uischema, renderers, cells, onChange, config, uischemas, readonly, validationMode, i18n, additionalErrors, middleware, } = props; const schemaToUse = useMemo(() => (schema !== undefined ? schema : Generate.jsonSchema(data)), [schema, data]); const uischemaToUse = useMemo(() => typeof uischema === 'object' ? uischema : Generate.uiSchema(schemaToUse, undefined, undefined, schemaToUse), [uischema, schemaToUse]); return (React.createElement(JsonFormsStateProvider, { initState: { core: { ajv, data, schema: schemaToUse, uischema: uischemaToUse, validationMode: validationMode, additionalErrors: additionalErrors, }, config, uischemas, renderers, cells, readonly, i18n, }, onChange: onChange, middleware: middleware }, React.createElement(JsonFormsDispatch, null))); }; const Dispatch = ({ uischema, schema, rootSchema, path, cells, id, enabled, renderers, config, }) => { const testerContext = useMemo(() => ({ rootSchema: rootSchema, config: config, }), [rootSchema, config]); const cell = useMemo(() => maxBy(cells, (r) => r.tester(uischema, schema, testerContext)), [cells, uischema, schema, testerContext]); if (cell === undefined || cell.tester(uischema, schema, testerContext) === -1) { return React.createElement(UnknownRenderer, { type: 'cell' }); } else { const Cell = cell.cell; return (React.createElement(Cell, { uischema: uischema, schema: schema, enabled: enabled, path: path, id: id, renderers: renderers, cells: cells })); } }; const DispatchCell = withJsonFormsDispatchCellProps(Dispatch); export { Control, Dispatch, DispatchCell, JsonForms, JsonFormsContext, JsonFormsDispatch, JsonFormsDispatchRenderer, JsonFormsStateProvider, RendererComponent, ResolvedJsonFormsDispatch, ResolvedJsonFormsDispatchRenderer, UnknownRenderer, ctxDispatchToArrayControlProps, ctxDispatchToControlProps, ctxDispatchToMultiEnumProps, ctxToAllOfProps, ctxToAnyOfProps, ctxToArrayControlProps, ctxToArrayLayoutProps, ctxToCellProps, ctxToControlProps, ctxToControlWithDetailProps, ctxToDispatchCellProps, ctxToEnumCellProps, ctxToEnumControlProps, ctxToJsonFormsRendererProps, ctxToLabelProps, ctxToLayoutProps, ctxToMasterListItemProps, ctxToMultiEnumControlProps, ctxToOneOfEnumCellProps, ctxToOneOfEnumControlProps, ctxToOneOfProps, useJsonForms, withArrayTranslationProps, withContextToJsonFormsRendererProps, withJsonFormsAllOfProps, withJsonFormsAnyOfProps, withJsonFormsArrayControlProps, withJsonFormsArrayLayoutProps, withJsonFormsCellProps, withJsonFormsContext, withJsonFormsControlProps, withJsonFormsDetailProps, withJsonFormsDispatchCellProps, withJsonFormsEnumCellProps, withJsonFormsEnumProps, withJsonFormsLabelProps, withJsonFormsLayoutProps, withJsonFormsMasterListItemProps, withJsonFormsMultiEnumProps, withJsonFormsOneOfEnumCellProps, withJsonFormsOneOfEnumProps, withJsonFormsOneOfProps, withJsonFormsRendererProps, withTranslateProps }; //# sourceMappingURL=jsonforms-react.esm.js.map