enables nested custom fields
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
0
src/client/components/forms/RenderFields/context.js
Normal file
0
src/client/components/forms/RenderFields/context.js
Normal 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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ const DefaultEditView = (props) => {
|
||||
{!isEditing
|
||||
&& (
|
||||
<h1>
|
||||
Create New
|
||||
New
|
||||
{' '}
|
||||
{singularLabel}
|
||||
</h1>
|
||||
|
||||
Reference in New Issue
Block a user