enables nested custom fields

This commit is contained in:
James
2020-06-03 12:56:43 -04:00
parent 14f1fc44ce
commit e15623539b
12 changed files with 153 additions and 10 deletions

View File

@@ -12,12 +12,46 @@ function stringify(obj) {
return `React.lazy(() => import('${obj}'))`;
}
module.exports = function (config) {
function recursivelyAddFieldComponents(fields) {
if (fields) {
return fields.reduce((allFields, field) => {
const subFields = recursivelyAddFieldComponents(field.fields);
if (!field.name && field.fields) {
return {
...allFields,
...subFields,
};
}
if (field.components || field.fields) {
const fieldComponents = {
...(field.components || {}),
};
if (field.fields) {
fieldComponents.fields = subFields;
}
return {
...allFields,
[field.name]: fieldComponents,
};
}
return allFields;
}, {});
}
return {};
}
function customComponents(config) {
const allCollectionComponents = config.collections.reduce((components, collection) => {
const newComponents = { ...components };
newComponents[collection.slug] = {
fields: {},
fields: recursivelyAddFieldComponents(collection.fields),
...(collection.components || {}),
};
@@ -30,8 +64,26 @@ module.exports = function (config) {
return newComponents;
}, {});
const allGlobalComponents = config.globals ? config.globals.reduce((globals, global) => {
const newComponents = { ...globals };
newComponents[global.slug] = {
fields: recursivelyAddFieldComponents(global.fields),
...(global.components || {}),
};
global.fields.forEach((field) => {
if (field.components) {
newComponents[global.slug].fields[field.name] = field.components;
}
});
return newComponents;
}, {}) : {};
const string = stringify({
...(allCollectionComponents || {}),
...(allGlobalComponents || {}),
...(config.components || {}),
}).replace(/\\/g, '\\\\');
@@ -41,4 +93,6 @@ module.exports = function (config) {
module.exports = ${string};
`,
};
};
}
module.exports = customComponents;

View File

@@ -26,7 +26,9 @@ const DraggableSection = (props) => {
singularLabel,
blockType,
fieldTypes,
customComponentsPath,
} = props;
const draggableRef = useRef(null);
const handleCollapseClick = () => {
@@ -104,6 +106,7 @@ const DraggableSection = (props) => {
duration={0}
>
<RenderFields
customComponentsPath={customComponentsPath}
fieldTypes={fieldTypes}
key={rowIndex}
fieldSchema={fieldSchema.map((field) => {
@@ -129,6 +132,7 @@ DraggableSection.defaultProps = {
collapsibleStates: [],
singularLabel: '',
blockType: '',
customComponentsPath: '',
};
DraggableSection.propTypes = {
@@ -144,6 +148,7 @@ DraggableSection.propTypes = {
collapsibleStates: PropTypes.arrayOf(PropTypes.bool),
blockType: PropTypes.string,
fieldTypes: PropTypes.shape({}).isRequired,
customComponentsPath: PropTypes.string,
};
export default DraggableSection;

View File

@@ -1,15 +1,25 @@
import React from 'react';
import React, { createContext, useContext } from 'react';
import PropTypes from 'prop-types';
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
import './index.scss';
const RenderedFieldContext = createContext({});
export const useRenderedFields = () => useContext(RenderedFieldContext);
const RenderFields = ({
fieldSchema, initialData, customComponentsPath, fieldTypes, filter,
fieldSchema, initialData, customComponentsPath: customComponentsPathFromProps, fieldTypes, filter,
}) => {
const { customComponentsPath: customComponentsPathFromContext } = useRenderedFields();
const customComponentsPath = customComponentsPathFromProps || customComponentsPathFromContext;
// console.log(customComponentsPath);
if (fieldSchema) {
return (
<>
<RenderedFieldContext.Provider value={{ customComponentsPath }}>
{fieldSchema.map((field, i) => {
if (field?.hidden !== 'api' && field?.hidden !== true) {
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
@@ -27,7 +37,7 @@ const RenderFields = ({
return (
<RenderCustomComponent
key={field.name || `field-${i}`}
path={`${customComponentsPath}${field.name}.field`}
path={`${customComponentsPath}${field.name ? `${field.name.split('.').pop()}.field` : ''}`}
DefaultComponent={FieldComponent}
componentProps={{
...field,
@@ -57,7 +67,7 @@ const RenderFields = ({
return null;
})}
</>
</RenderedFieldContext.Provider>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import RenderFields from '../../RenderFields';
import RenderFields, { useRenderedFields } from '../../RenderFields';
import withCondition from '../../withCondition';
import './index.scss';
@@ -10,11 +10,14 @@ const Group = (props) => {
label, fields, name, defaultValue, fieldTypes,
} = props;
const { customComponentsPath } = useRenderedFields();
return (
<div className="field-type group">
<h3>{label}</h3>
<RenderFields
fieldTypes={fieldTypes}
customComponentsPath={`${customComponentsPath}${name}.fields.`}
fieldSchema={fields.map((subField) => {
return {
...subField,

View File

@@ -10,6 +10,7 @@ import Button from '../../../elements/Button';
import FormContext from '../../Form/Context';
import DraggableSection from '../../DraggableSection';
import collapsibleReducer from './reducer';
import { useRenderedFields } from '../../RenderFields';
import './index.scss';
@@ -22,6 +23,7 @@ const Repeater = (props) => {
const [rowCount, setRowCount] = useState(0);
const [lastModified, setLastModified] = useState(null);
const { fields: fieldState, dispatchFields, countRows } = formContext;
const { customComponentsPath } = useRenderedFields();
const {
name,
@@ -120,6 +122,7 @@ const Repeater = (props) => {
defaultValue={lastModified ? undefined : defaultValue[rowIndex]}
dispatchCollapsibleStates={dispatchCollapsibleStates}
collapsibleStates={collapsibleStates}
customComponentsPath={customComponentsPath}
/>
);
})

View File

@@ -8,6 +8,12 @@ const RenderCustomComponent = (props) => {
if (path) {
const CustomComponent = path.split('.').reduce((res, prop) => {
const potentialRowIndex = parseInt(prop, 10);
if (!Number.isNaN(potentialRowIndex) && res.fields) {
return res.fields;
}
if (res) {
return res[prop];
}

View File

@@ -69,7 +69,7 @@ const DefaultEditView = (props) => {
{!isEditing
&& (
<h1>
Create New
New
{' '}
{singularLabel}
</h1>