Source: templates/Template.js

import React, { useMemo } from "react";
import { useSelector, } from "react-redux";
import { InputOqt, processOptions, reactorParser, useHtmlParser } from "../..";
import { TagWithVariable } from "./";
import { ReactorLink, GeolocationOqt } from "../";
import { InputCheckBoxOqt } from "../input";
import { components, regexp, makeComponent, anyReplaceAttribs, resolveAttribs, anyVariableAttribs, matchText, resolverItem, getTypes } from "./Template.helpers";
import { useEffect } from "react";

const objectPath = require("object-path");

/**
 * Componente que renderiza un objeto llamando a un template en memoria o un string como html.
 * @name Reactor.Components.Templates.Template
 * @param {Object|string} content Contenido que va a renderizarse
 * @class
 * @example
<template content='{"template":"NombreDeTemplate", "titulo": "{{titulo}}"}' />
 */

export const Template = ({content}) => {

  const { parse, domToReact } = useHtmlParser();
  const templates = useSelector(state => state.app.templates);
  const contentType = useMemo(() => typeof content, [content]);
  const noTemplate = useMemo(() => !templates || templates.length === 0, [templates]);

  const template = useMemo(()=>{
    let template;
    if (!noTemplate && (content && content.template && typeof content.template === "string")){
      template = templates[content.template.toLowerCase()];
    }else if(content){
      template = {body: content.template};
    }
    return template;
  }, [noTemplate, templates, content]);

  const options = useMemo(()=>({

    replace: ({children, attribs, name, data, ...params}) => {


      if(params.type === "tag" && name === "a"){
        //const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content) : attribs;
        const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content) : attribs;
        if(anyVariableAttribs(attribs)){
          return makeComponent(TagWithVariable, {...resolveAttribs(attribs, content), title: attributes.title, tag: ReactorLink, children: children && domToReact(children, options)})
        }else{
          return makeComponent(ReactorLink, {...attributes, children: children && domToReact(children, options)})
        }
      }

      if(params.type === "tag" && name==="option"){
        const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content, false) : attribs;
        //console.log({attribs, attributes})
        return <option {...attributes}>{matchText(children[0].data, content)}</option>;
      }
      
      if(params.type === "tag" && name==="textarea"){
        const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content, false) : attribs;
        return <textarea {...attributes}>{children[0] && matchText(children[0].data, content) || ""}</textarea>;
      }

      if(params.type === "tag" && (name === "for_each" || name === "live")){
        const Component = components[name]();
        if(anyVariableAttribs(attribs)){
          return makeComponent(TagWithVariable, {...resolveAttribs(attribs, content), tag: Component, children: children && domToReact(children, options)})
        }else{
          const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content) : attribs;
          return makeComponent(Component, {...attributes, template: attributes.template && attributes.template.replace("[[", "{{").replace("]]", "}}"), children: children && domToReact(children, options)})
        }
      }

      if(params.type === "tag"){
        if( components[name] ){
          const Component = components[name]();
          if(anyVariableAttribs(attribs)){
            return makeComponent(TagWithVariable, {...resolveAttribs(attribs, content), tag: Component, children: children && domToReact(children, options)})
          }else{
            const attributes = anyReplaceAttribs(attribs) ? resolveAttribs(attribs, content) : attribs;
            return makeComponent(Component, {...attributes, children: children && domToReact(children, options)})
          }
        }
      }
      
      if(params.type === "tag" && name === "img" && anyReplaceAttribs(attribs)){
        const attributes = resolveAttribs(attribs, content);
        return <img {...attributes} />;      
      }

      if(params.type === "tag" && name === "geolocation" ){
        const attributes = resolveAttribs(attribs, content);
        return <GeolocationOqt {...attributes}>{domToReact(children, options)}</GeolocationOqt>;
      }

      if(params.type === "tag" && name === "input" && anyReplaceAttribs(attribs)){
        const attributes = resolveAttribs(attribs, content);
        //return attributes.type === "checkbox" ? <InputCheckBoxOqt {...attributes} /> : <InputOqt {...attributes} />;
        if(anyVariableAttribs(attribs)){
          return makeComponent(TagWithVariable, {...attributes, tag: attributes.type === "checkbox" ? InputCheckBoxOqt : InputOqt, children: undefined})
        }else{
          return makeComponent(attributes.type === "checkbox" ? InputCheckBoxOqt : InputOqt, {...attributes, children: undefined})
        }
      }

      if(params.type === "tag" && anyReplaceAttribs(attribs)){
        const attributes = resolveAttribs(attribs, content);
        const Tag = name;
        if(anyVariableAttribs(attribs)){
          return makeComponent(TagWithVariable, {...attributes, tag: Tag, children: children && domToReact(children, options)})
        }else{
          return makeComponent(Tag, {...attributes, children: children && domToReact(children, options)})
        }
      }

      if(params.type === "tag" && name === "object"){
        const attributes = resolveAttribs(attribs, content);
        const Tag2 = attributes.tag;
        const newOptions = attributes.options ? JSON.parse(attributes.options, reactorParser) : {};
        return <Tag2 {...newOptions}>{domToReact(children, options)}</Tag2>;      
      }

      // Función para procesar el template y generar el JSX
      if(params.type === "text"){
        //return generarJSX(data, content)
        const matches = data.match(regexp);
        //console.log({params, data, matches})
        if(matches){
          return ( 
            <>
              { 
                matches
                  .map(x => x.replace("{{", "").replace("}}", ""))
                  .map( match => {
                    const [ splitMatch, defaultTemplate] = match.split("|");
                    const targetContent = content;
                    const childContent = objectPath.get(targetContent, `${splitMatch}`.trim());
                    //console.log("resolverItem", {childContent, defaultTemplate})
                    return resolverItem(childContent, defaultTemplate);
                  })
              }
            </>
          );
        }
      }
    }
  }), [content]);
  
  const types = useMemo(() => getTypes(options, template, parse), [options, template]);

  const result = useMemo(() => {
    return noTemplate 
        ? <></> 
        : (types[contentType] ? types[contentType](content) : types.default(content));
  }, [noTemplate, contentType, types, content]);

  return result;
}

const generarJSX = (template, content) => {
  const partes = template.split(/({{[^{}]+}})/g);
  const jsx = partes.map((parte, index) => {
    if (parte.match(/({{[^{}]+}})/)) {
      
      const clave = parte.slice(2, -2);
      const [ data, defaultTemplate ] = clave.split("|");
      
      const dataProcess = processOptions(data.trim(), "", data);
      
      if(typeof dataProcess === "string"){
        console.log("string", dataProcess)
        return resolverItem(objectPath.get(content, dataProcess), defaultTemplate);
      }else if(typeof dataProcess === "object"){
        console.log("object", dataProcess)
        return resolverItem({...dataProcess, template: defaultTemplate});
      }
      console.log("otra cosa", dataProcess)
        
      //const clave = parte.slice(2, -2);
      //return <span key={index}>{datos[clave]}</span>;
    } else {
      //console.log(parte)
      return parte;
    }
  });
  //console.log(jsx);
  return jsx;
}