From 20e6d60b229ee209153e02eebbf1c15e41608222 Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Thu, 18 Jul 2019 19:54:11 +0200 Subject: [PATCH] Add support to JSX --- src/dom_components/model/Components.js | 43 +++++++++++++++++++++++- src/parser/model/ParserHtml.js | 46 ++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/dom_components/model/Components.js b/src/dom_components/model/Components.js index 86da3c4eb..fb54073e5 100644 --- a/src/dom_components/model/Components.js +++ b/src/dom_components/model/Components.js @@ -1,5 +1,5 @@ import Backbone from 'backbone'; -import { isEmpty, isArray, isString } from 'underscore'; +import { isEmpty, isArray, isString, each, includes } from 'underscore'; let Component; @@ -71,9 +71,50 @@ export default Backbone.Collection.extend({ }); } + (isArray(models) ? models : [models]) + .filter(i => i) + .forEach(model => this.processDef(model)); + return Backbone.Collection.prototype.add.apply(this, [models, opt]); }, + /** + * Process component definition. + */ + processDef(model) { + // React JSX + if (model.$$typeof && typeof model.props == 'object') { + const { em } = this; + const domc = em.get('DomComponents'); + const parser = em.get('Parser'); + const { parserHtml } = parser; + + each(model, (value, key) => { + if (!includes(['props', 'type'], key)) delete model[key]; + }); + const { props } = model; + const comps = props.children; + delete props.children; + delete model.props; + const res = parserHtml.splitPropsFromAttr(props); + model.attributes = res.attrs; + + if (comps) { + model.components = comps; + } + if (!model.type) { + model.type = 'textnode'; + } else if (!domc.getType(model.type)) { + model.tagName = model.type; + delete model.type; + } + + each(res.props, (val, key) => (model[key] = val)); + } + + return model; + }, + onAdd(model, c, opts = {}) { const em = this.em; const style = model.getStyle(); diff --git a/src/parser/model/ParserHtml.js b/src/parser/model/ParserHtml.js index fe1c44493..ab04760eb 100644 --- a/src/parser/model/ParserHtml.js +++ b/src/parser/model/ParserHtml.js @@ -1,3 +1,5 @@ +import { each, isString } from 'underscore'; + export default config => { var TEXT_NODE = 'span'; var c = config; @@ -6,6 +8,50 @@ export default config => { return { compTypes: '', + modelAttrStart, + + /** + * Extract component props from an attribute object + * @param {Object} attr + * @returns {Object} An object containing props and attributes without them + */ + splitPropsFromAttr(attr = {}) { + const props = {}; + const attrs = {}; + + each(attr, (value, key) => { + if (key.indexOf(this.modelAttrStart) === 0) { + const modelAttr = key.replace(modelAttrStart, ''); + const valueLen = value.length; + const valStr = value && isString(value); + const firstChar = valStr && value.substr(0, 1); + const lastChar = valStr && value.substr(valueLen - 1); + value = value === 'true' ? true : value; + value = value === 'false' ? false : value; + + // Try to parse JSON where it's possible + // I can get false positive here (eg. a selector '[data-attr]') + // so put it under try/catch and let fail silently + try { + value = + (firstChar == '{' && lastChar == '}') || + (firstChar == '[' && lastChar == ']') + ? JSON.parse(value) + : value; + } catch (e) {} + + props[modelAttr] = value; + } else { + attrs[key] = value; + } + }); + + return { + props, + attrs + }; + }, + /** * Parse style string to object * @param {string} str