UNPKG

react-jsonform-shape

Version:

A mini React library that abstracts the generation of forms from a JSON.

226 lines (196 loc) 5.47 kB
import React from "react"; const { forwardRef, Fragment } = React; const omitFields = (target, choices) => { const newObj = {}; Object.keys(target).forEach(key => { if (choices[key] === true) return; newObj[key] = target[key]; }); return newObj; }; const sortContents = arr => { return arr.sort((a, b) => { if (a.container.length === b.container.length) { return a.container > b.container; } return a.container.length < b.container.length; }); }; const sortStringKey = (a, b) => { if (a.length === b.length) { return a > b; } return a.length < b.length; }; const isControlComponentDefined = (Settings = {}, type) => { if (!Settings[type]) { throw Error( `Missing "${type}" component. "<AntdDynamicForm ControlComponents={{ ${type}..."` ); } }; const getControlByType = (Settings, item) => { switch (item.type) { case "slider": isControlComponentDefined(Settings, "Slider"); return forwardRef((props, ref) => { return <Settings.Slider {...item.propsItem} />; }); case "button": isControlComponentDefined(Settings, "Button"); return forwardRef((props, ref) => <Settings.Button {...{...props, ref}} />); default: isControlComponentDefined(Settings, "Input"); return forwardRef((props, ref) => { return <Settings.Input {...{...props, ref}} /> }); } }; const Render = Settings => { if (!Settings.Form) { throw Error( `Missing "Form" component. "<AntdDynamicForm ControlComponents={{ Form..."` ); } if (!Settings.RenderItem) { throw Error( `Missing "renderItem". "<AntdDynamicForm renderItem={(item) => {...` ); } return ( <Settings.Form {...Settings.formProps}> {Settings.items.map(field => { const arg = { ...field, ControlType: Settings.getControlByType(Settings, field) }; // TODO: finalise requiring "key" instead const key = field.propsItem.name ? field.propsItem.name : field.propsItem.id; return <Settings.RenderItem key={key} {...arg} />; })} </Settings.Form> ); }; const RenderWithTemplate = Settings => { let templateContents = [...Settings.templateContents]; const toRender = sortContents(templateContents).reduce((acc, content) => { const containerKey = content.container; const contentsArr = Settings.items.filter( item => item.container === containerKey ); if (contentsArr.length) { if (!content.renderItem || !content.render) { let msg = ""; try { msg = ` ${JSON.stringify(content)}`; } catch (e) {} throw Error(`Missing render or renderItem.${msg}`); } acc[containerKey] = () => { return content.render({ getContents() { return contentsArr.map(field => { const arg = { ...field, ControlType: Settings.getControlByType(Settings, field) }; return content.renderItem(arg); }); } }); }; return acc; } acc[containerKey] = () => { return content.render({ getContents() { const r = Object.keys(acc) .sort(sortStringKey) .filter( childKey => childKey.indexOf(containerKey) === 0 && childKey.length - 1 === containerKey.length ) .reduce((nodes, currentNode) => { if ( currentNode.indexOf(containerKey) === 0 && currentNode.length - 1 === containerKey.length ) { nodes = nodes.concat([ <Fragment key={currentNode}>{acc[currentNode]()}</Fragment> ]); } return nodes; }, []); if (!r.length) { return <></>; } return r; } }); }; return acc; }, {}); if (toRender) { return Object.keys(toRender) .filter(k => k.length === 1) .sort(sortStringKey) .map(container => { return <Fragment key={container}>{toRender[container]()}</Fragment>; }); } return <></>; }; const AntdDynamicForm = props => { const { ControlComponents = {}, items = [] } = props; if (ControlComponents.Form) { ControlComponents.formProps = omitFields(props, { ControlComponents: true, items: true, renderItem: true, render: true, template: true }); } const Settings = { ...ControlComponents, items, RenderItem: props.renderItem, render: props.render ? props.render : params => { return <Render {...Settings} />; }, getControlByType, templateContents: props.template && props.template.contents && props.template.contents.length ? props.template.contents : [] }; if (Settings.templateContents.length) { return ( <> {Settings.render({ ...Settings, getContents(ppp) { return <RenderWithTemplate {...Settings} />; } })} </> ); } return ( <> {Settings.render({ ...Settings, getContents: ppp => Settings.items.map(field => { return <Settings.RenderItem {...field} />; }) })} </> ); }; export default AntdDynamicForm;