framework-entersol-web
Version:
Framework based on bootstrap 5
160 lines (149 loc) • 5.81 kB
JSX
import React from "react";
import { NavLink, Link } from "react-router-dom";
import parseReact, { domToReact, attributesToProps } from "html-react-parser";
import deepMerge from "./functions/deep-merge";
import Icons from "./media/icons";
import COMPONENTS from "./components";
/**
* Clase utilizada para generar contenido dinámico en React a partir de una estructura de datos JSON.
*
* @class JsonRender
*/
export default class JsonRender {
/**
* Opciones para el análisis del contenido HTML.
* @type {Object}
*/
parseOpts = {
replace: domNode => {
let C7tReplace;
switch (domNode.name) {
case 'navlink':
C7tReplace = NavLink;
break;
case 'a':
if (!domNode.attribs.to && domNode.attribs.href) return;
C7tReplace = Link;
break;
case 'icons':
C7tReplace = Icons;
domNode.attribs.inline = domNode.attribs.inline === 'false' ? false : true;
break;
case 'textarea':
case 'input':
domNode.defaultValue = domNode.value;
domNode.defaultChecked = domNode.checked;
delete domNode.value;
delete domNode.checked;
default:
return;
}
Object.keys(domNode).forEach(k => {
if (k.match(/^on[A-Z]/)) {
domNode[k] = this.props[k];
}
});
return <C7tReplace
{...attributesToProps(domNode.attribs)}
children={domToReact(domNode.children, this.parseOpts)}
/>;
}
}
/**
* Crea una instancia de JsonRender.
* @param {Object} props - Las propiedades del componente.
* @param {Object} mutations - Las mutaciones para las secciones.
*/
constructor(props, mutations) {
this.props = props;
this.mutations = mutations;
this.sections = this.sections.bind(this);
this.buildContent = this.buildContent.bind(this);
}
/**
* Construye el contenido basado en la estructura de datos proporcionada.
* @param {any} content - El contenido a construir.
* @param {number} index - El índice del contenido.
* @returns {React.Component|React.Fragment|boolean} - El componente construido.
*/
buildContent(content, index) {
if (!content) return false;
if (typeof content === 'number') {
return content;
} else if (typeof content === 'string') {
return (<React.Fragment key={content.name || index}>
{parseReact(content, this.parseOpts)}
</React.Fragment>);
} else if (React.isValidElement(content)) {
try {
content.key = content.name || index;
} catch (error) {
}
return content;
} else if (Array.isArray(content)) return content.map(this.buildContent);
if (Array.isArray(content.name)) content.name = content.name.join('-');
if (typeof content === 'object' && typeof content.name !== 'string')
return Object.keys(content)
.map((name, i) => this.buildContent({ name, ...content[name] }, i));
return this.sections(content, index);
}
/**
* Construye una sección basada en la información proporcionada.
* @param {Object} sectionRaw - Los datos de la sección.
* @param {number} i - El índice de la sección.
* @returns {React.Component|boolean} - El componente de la sección construido.
*/
sections(sectionRaw, i) {
if (typeof this.mutations === 'function') {
const m = this.mutations(sectionRaw.name, sectionRaw) || {};
//Si la mutacion contiene elementos react, hacer un deepMerge truena con loop infinito
Object.assign(sectionRaw, m);
}
if (typeof sectionRaw.active === 'boolean' && !sectionRaw.active) return false;
const { component: componentName, content, placeholder,
label, message, errorMessage, managerName, wrapperClasses, ...section } = sectionRaw;
const { location, match, childrenIn = this.childrenIn, history, children } = this.props;
const Component = COMPONENTS[componentName] || (COMPONENTS.Component);
const componentProps = {
...section,
managerName: managerName || this.props.name,
label: this.buildContent(label),
placeholder: this.buildContent(placeholder),
message: this.buildContent(message),
errorMessage: this.buildContent(errorMessage),
location,
match,
history
}
if (Component.dontBuildContent) componentProps.content = content;
if (!Component.dontBuildContent && content && (childrenIn === section.name)) {
componentProps.children = <>
{this.buildContent(content)}
{children}
</>
} else if (!Component.dontBuildContent && content) {
componentProps.children = this.buildContent(content);
} else if (childrenIn === section.name) {
componentProps.children = children;
}
const cnSection = [componentProps.name + '-section'];
if (this.props.test) cnSection.push('test-section-wrapper');
if (this.props.wrapperClasses) cnSection.push(this.props.wrapperClasses);
if (wrapperClasses) cnSection.push(wrapperClasses);
const exclusionSec = ['NavLink', 'Image', 'Link', 'Icons', 'Action',
'DropdownButtonContainer', 'ModalButtonContainer', 'DropdownItem']
.includes(componentName);
const Wrapper = componentProps.wrapper === false ? false :
componentProps.wrapper || Component.wrapper || 'section';
if (!Wrapper || exclusionSec || componentProps.tag) {
if (this.props.test) {
if (!componentProps.style) componentProps.style = {};
componentProps.style.border = '1px solid yellow';
}
return <Component key={componentProps.name || i} {...componentProps} />
}
return (<Wrapper key={componentProps.name || i} className={cnSection.flat().join(' ')}>
<Component {...componentProps} />
</Wrapper>);
}
}