UNPKG

spyne

Version:

Reactive Real-DOM Framework for Advanced Javascript applications

310 lines (284 loc) 7.55 kB
import { baseCoreMixins } from '../utils/mixins/base-core-mixins.js' import { DomElementTemplate } from './dom-element-template.js' import { deepMerge } from '../utils/deep-merge.js' import { is, defaultTo, pick, mapObjIndexed, forEachObjIndexed, pipe } from 'ramda' class DomElement { /** * @module DomElement * @type util * * @desc * <p>This is the ViewStream rendering engine.</p> * <p>This is the recommended process for creating HTMLElements that do not require the logic and overhead of a ViewStream instance.</p> * <button class='modal-btn btn btn-blue-ref' data-type='modal-window' data-num=800 data-value='attributes'>View Attributes</button> * * @constructor * @param {string} tagName the tagname for this dom element. * @param {object} attributes any domElement attribute (except for class ) * @param {string|object} data string for text tags and json for templates * @param {template} template * @property {String} props.tagName - = 'div'; Default for tagName. * @property {Object} props.attributes - = {}; This can be any valid HTML attribute for the given tagName. * @property {String|Object} props.data = undefined; This is either a String for an element or JSON data object for a template. * @property {String|HTML} props.template = undefined; If a template is defined, the DomElement will use it. * */ constructor(props = {}, testMode = false) { const checkDefault = (dflt, val) => defaultTo(dflt)(val) props.tagName = checkDefault('div', props.tagName) props.attributes = props.attributes !== undefined ? props.attributes : this.getDomAttributes(props) props.attrs = this.updateAttrs(props.attributes) props.fragment = document.createDocumentFragment() this.testMode = testMode this.props = props this.addMixins() } setProp(key, val) { this.props[key] = val } getProp(val) { return this.props[val] } get el() { return this.props.el } setElAttrs(el, params) { const addAttributes = (val, key) => { const addToDataset = (val, key) => { el.dataset[key] = val } if (key === 'dataset') { forEachObjIndexed(addToDataset, val) } else { el.setAttribute(key, val) } } this.getProp('attrs').forEach(addAttributes) return el } updateAttrs(params, m) { const theMap = m !== undefined ? m : new Map() const addAttributes = (val, key) => theMap.set(key, val) mapObjIndexed(addAttributes, params) return theMap } addTemplate(el) { const template = this.getProp('template') const addTmpl = (template) => { let data = this.getProp('data') data = is(Object, data) ? data : {} const frag = new DomElementTemplate(template, data, { testMode: this?.testMode }).renderDocFrag() const fragIsString = is(String, frag) fragIsString ? el.innerHTML = frag : el.appendChild(frag) return el } const doNothing = (el) => el return template !== undefined ? addTmpl(template) : doNothing(el) } createElement(tagName = 'div') { return document.createElement(tagName) } addContent(el) { const text = (this.getProp('data')) const isText = is(String, text) if (isText === true) { const txt = document.createTextNode(text) el.appendChild(txt) } return el } execute() { const el = pipe( this.createElement.bind(this), this.setElAttrs.bind(this), this.addTemplate.bind(this), this.addContent.bind(this) )(this.getProp('tagName')) // this.getProp('fragment').appendChild(el); this.props.el = el } /** * This method will render the HTML Element * @returns {HTMLElement} HTMLElement */ render() { this.execute() this.props.template = undefined this.props.data = undefined this.props.attributes = undefined return this.getProp('el') } renderToHTMLString() { this.execute() this.props.template = undefined this.props.data = undefined this.props.attributes = undefined return this.getProp('el').outerHTML } returnIfDefined(obj, val) { if (val !== undefined) { const isObj = typeof (val) === 'undefined' isObj === false ? obj[val] = val : obj[val] = deepMerge(obj[val], val) } } updateprops(val) { this.returnIfDefined(this.props, val) return this } updatepropsAndRun(val) { this.updateprops(val) this.execute() return this.getProp('fragment') } unmount() { if (this.props !== undefined) { this.getProp('el').remove() this.props = undefined this.gc() } } updateTag(tagName = 'div') { this.updateprops(tagName) } updateAttributes(attrs = {}) { this.updateprops(attrs) } updateTemplate(template) { this.updateprops(template) } updateData(data = {}) { this.updateprops(data) } addTagAndRender(tagName = 'div') { this.updatepropsAndRun(tagName) } addAttrsibutesAndRender(attrs = {}) { this.updatepropsAndRun(attrs) } addTemplateAndRender(template) { this.updatepropsAndRun(template) } addDataAndRender(data = {}) { this.updatepropsAndRun(data) } // ================================== // BASE CORE MIXINS // ================================== addMixins() { const coreMixins = baseCoreMixins() this.gc = coreMixins.gc.bind(this) } getDomAttributes(props) { return pick(this.attributesArray, props) } get attributesArray() { return [ 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'allow', 'alt', 'async', 'autocapitalize', 'autocomplete', 'autofocus', 'autoplay', 'bgcolor', 'border', 'buffered', 'challenge', 'charset', 'checked', 'cite', 'class', 'code', 'codebase', 'color', 'cols', 'colspan', 'content', 'contenteditable', 'contextmenu', 'controls', 'coords', 'crossorigin', 'csp', 'dataset', 'datetime', 'decoding', 'default', 'defer', 'dir', 'dirname', 'disabled', 'download', 'draggable', 'dropzone', 'enctype', 'for', 'form', 'formaction', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'http-equiv', 'icon', 'id', 'importance', 'integrity', 'ismap', 'itemprop', 'keytype', 'kind', 'label', 'lang', 'language', 'lazyload', 'list', 'loop', 'low', 'manifest', 'max', 'maxlength', 'minlength', 'media', 'method', 'min', 'multiple', 'muted', 'name', 'novalidate', 'open', 'optimum', 'pattern', 'ping', 'placeholder', 'poster', 'preload', 'radiogroup', 'readonly', 'referrerpolicy', 'rel', 'required', 'reversed', 'rows', 'rowspan', 'sandbox', 'scope', 'scoped', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'style', 'summary', 'tabindex', 'target', 'title', 'translate', 'type', 'usemap', 'value', 'width', 'wrap' ] } } const DomEl = DomElement export { DomElement, DomEl }