You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.1 KiB
102 lines
3.1 KiB
import React, { memo } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { isEqual } from 'lodash';
|
|
import { safeComponentList } from './safe-component-list';
|
|
|
|
function getElement(section) {
|
|
const { element } = section;
|
|
const Element = safeComponentList[element];
|
|
if (!Element) {
|
|
throw new Error(
|
|
`${element} is not in the safe component list for MetaMask template renderer`,
|
|
);
|
|
}
|
|
return Element;
|
|
}
|
|
|
|
const MetaMaskTemplateRenderer = ({ sections }) => {
|
|
if (!sections) {
|
|
// If sections is null eject early by returning null
|
|
return null;
|
|
} else if (typeof sections === 'string') {
|
|
// React can render strings directly, so return the string
|
|
return sections;
|
|
} else if (
|
|
sections &&
|
|
typeof sections === 'object' &&
|
|
!Array.isArray(sections)
|
|
) {
|
|
// If dealing with a single entry, then render a single object without key
|
|
const Element = getElement(sections);
|
|
return (
|
|
<Element {...sections.props}>
|
|
{typeof sections.children === 'object' ? (
|
|
<MetaMaskTemplateRenderer sections={sections.children} />
|
|
) : (
|
|
sections?.children
|
|
)}
|
|
</Element>
|
|
);
|
|
}
|
|
|
|
// The last case is dealing with an array of objects
|
|
return (
|
|
<>
|
|
{sections.reduce((allChildren, child) => {
|
|
if (typeof child === 'string') {
|
|
// React can render strings directly, so push them into the accumulator
|
|
allChildren.push(child);
|
|
} else {
|
|
// If the entry in array is not a string, then it must be a Section.
|
|
// Sections are handled by the main function, but must
|
|
// be provided a key when a part of an array.
|
|
if (!child.key) {
|
|
throw new Error(
|
|
'When using array syntax in MetaMask Template Language, you must specify a key for each child of the array',
|
|
);
|
|
}
|
|
if (typeof child?.children === 'object') {
|
|
// If this child has its own children, check if children is an
|
|
// object, and in that case use recursion to render.
|
|
allChildren.push(
|
|
<MetaMaskTemplateRenderer sections={child} key={child.key} />,
|
|
);
|
|
} else {
|
|
// Otherwise render the element.
|
|
const Element = getElement(child);
|
|
allChildren.push(
|
|
<Element key={child.key} {...child.props}>
|
|
{child?.children}
|
|
</Element>,
|
|
);
|
|
}
|
|
}
|
|
return allChildren;
|
|
}, [])}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export const SectionShape = {
|
|
props: PropTypes.object,
|
|
element: PropTypes.oneOf(Object.keys(safeComponentList)).isRequired,
|
|
key: PropTypes.string,
|
|
};
|
|
|
|
const ValidChildren = PropTypes.oneOfType([
|
|
PropTypes.string,
|
|
PropTypes.shape(SectionShape),
|
|
PropTypes.arrayOf(
|
|
PropTypes.oneOfType([PropTypes.shape(SectionShape), PropTypes.string]),
|
|
),
|
|
]);
|
|
|
|
SectionShape.children = ValidChildren;
|
|
|
|
MetaMaskTemplateRenderer.propTypes = {
|
|
sections: ValidChildren,
|
|
};
|
|
|
|
export default memo(MetaMaskTemplateRenderer, (prevProps, nextProps) => {
|
|
return isEqual(prevProps.sections, nextProps.sections);
|
|
});
|
|
|