UNPKG

bd-core

Version:

a JavaScript library for building browser-hosted user interfaces

149 lines (135 loc) 6.93 kB
import {getPostProcessingFunction} from './postProcessingCatalog.js'; function flattenChildren(children) { // children can be falsey, single children (of type Element or string), or arrays of single children, arbitrarily deep const result = []; function flatten_(child) { if (Array.isArray(child)) { child.forEach(flatten_); } else if (child) { result.push(child); } } flatten_(children); return result; } export class Element { constructor(type, props, ...children) { if (type instanceof Element) { // copy constructor this.type = type.type; type.isComponentType && (this.isComponentType = type.isComponentType); type.ctorProps && (this.ctorProps = type.ctorProps); type.ppFuncs && (this.ppFuncs = type.ppFuncs); type.children && (this.children = type.children); } else { // type must either be a constructor (a function) or a string; guarantee that as follows... if (type instanceof Function) { this.isComponentType = true; this.type = type; } else if (type) { // leave this.isComponentType === undefined this.type = Array.isArray(type) ? type : `${type}`; } else { throw new Error('type is required'); } // if the second arg is an Object and not an Element or and Array, then it is props... if (props) { if (props instanceof Element || Array.isArray(props)) { children.unshift(props); this.ctorProps = {}; } else if (props instanceof Object) { const ctorProps = {}; const ppFuncs = {}; let ppFuncsExist = false; let match, ppf; const setPpFuncs = (ppKey, value) => { if (ppFuncs[ppKey]) { const dest = ppFuncs[ppKey]; Reflect.ownKeys(value).forEach(k => (dest[k] = value[k])); } else { ppFuncsExist = true; ppFuncs[ppKey] = value; } }; Reflect.ownKeys(props).forEach(k => { if ((ppf = getPostProcessingFunction(k))) { const value = ppf.bdTransform(null, props[k]); setPpFuncs(k, value); } else if ((match = k.match(/^([A-Za-z0-9$]+)_(.+)$/)) && (ppf = getPostProcessingFunction(match[1]))) { const ppKey = match[1]; const value = ppf.bdTransform(match[2], props[k]); setPpFuncs(ppKey, value); } else { ctorProps[k] = props[k]; } }); this.ctorProps = Object.freeze(ctorProps); if (ppFuncsExist) { this.ppFuncs = Object.freeze(ppFuncs); } } else { children.unshift(props); this.ctorProps = {}; } } else { this.ctorProps = {}; } const flattenedChildren = flattenChildren(children); if (flattenedChildren.length === 1) { const child = flattenedChildren[0]; this.children = child instanceof Element ? child : `${child}`; } else if (flattenedChildren.length) { this.children = flattenedChildren.map(child => (child instanceof Element ? child : `${child}`)); Object.freeze(this.children); }// else children.length===0; therefore, no children } Object.freeze(this); } } export function element(type, props, ...children) { // make elements without having to use new return new Element(type, props, children); } element.addElementType = function addElementType(type) { // type is either a constructor (a function) or a string if (typeof type === 'function') { if (type.name in element) { // eslint-disable-next-line no-console console.error(type.name, 'already in element'); } else { element[type.name] = (props, ...children) => new Element(type, props, children); } } else { // eslint-disable-next-line no-lonely-if if (type in element) { // eslint-disable-next-line no-console console.error(type, 'already in element'); } else { element[type] = (props, ...children) => new Element(type, props, children); } } }; 'a.abbr.address.area.article.aside.audio.base.bdi.bdo.blockquote.br.button.canvas.caption.cite.code.col.colgroup.data.datalist.dd.del.details.dfn.div.dl.dt.em.embed.fieldset.figcaption.figure.footer.form.h1.h2.h3.h4.h5.h6.h7.head.header.hr.html.i.iframe.img.input.ins.kbd.label.legend.li.link.main.map.mark.meta.meter.nav.noscript.object.ol.optgroup.option.output.p.param.picture.pre.progress.q.rb.rp.rt.rtc.ruby.s.samp.script.section.select.slot.small.source.span.strong.style.sub.summary.sup.table.tbody.td.template.textarea.tfoot.th.thead.time.title.tr.track.u.ul.var.video.wbr'.split('.').forEach(element.addElementType); export function div(props, ...children) { // eslint-disable-next-line no-console console.warn('deprecated: use e.div'); return new Element('div', props, children); } const SVG = Object.create(null, { toString: { value: () => 'http://www.w3.org/2000/svg' } }); Object.freeze(SVG); export function svg(type, props, ...children) { if (typeof type !== 'string') { children.unshift(props); props = type; type = 'svg'; } return new Element([SVG, type], props, children); } 'altGlyph.altGlyphDef.altGlyphItem.animate.animateColor.animateMotion.animateTransform.circle.clipPath.colorprofile.cursor.defs.desc.ellipse.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feDistantLight.feFlood.feFuncA.feFuncB.feFuncG.feFuncR.feGaussianBlur.feImage.feMerge.feMergeNode.feMorphology.feOffset.fePointLight.feSpecularLighting.feSpotLight.feTile.feTurbulence.filter.font.fontface.fontfaceformat.fontfacename.fontfacesrc.fontfaceuri.foreignObject.g.glyph.glyphRef.hkern.image.line.linearGradient.marker.mask.metadata.missingglyph.mpath.path.pattern.polygon.polyline.radialGradient.rect.script.set.stop.style.svg.switch.symbol.text.textPath.title.tref.tspan.use.view.vkern'.split('.').forEach(tag => { svg[tag] = (props, ...children) => svg(tag, props, children); });