UNPKG

oxe

Version:

A mighty tiny web components framework/library

283 lines (215 loc) 8.23 kB
import Methods from './methods.js'; import Binder from './binder.js'; import Loader from './loader.js'; import Model from './model.js'; import Style from './style.js'; import Utility from './utility.js'; import Definer from './definer.js'; export default { data: {}, async setup (options) { const self = this; options = options || {}; if (options.components) { return Promise.all(options.components.map(function (component) { if (typeof component === 'string') { return Loader.load(component).then(function (load) { return self.define(load.default); }); } else { return self.define(component); } })); } }, style (style, name) { style = style.replace(/\n|\r|\t/g, ''); style = style.replace(/:host/g, name); if (!window.CSS || !window.CSS.supports || !window.CSS.supports('(--t: black)')) { const matches = style.match(/--\w+(?:-+\w+)*:\s*.*?;/g) || []; for (let i = 0, l = matches.length; i < l; i++) { const match = matches[i]; const rule = match.match(/(--\w+(?:-+\w+)*):\s*(.*?);/); const pattern = new RegExp('var\\('+rule[1]+'\\)', 'g'); style = style.replace(rule[0], ''); style = style.replace(pattern, rule[2]); } } return style; }, slot (element, fragment) { const fragmentSlots = fragment.querySelectorAll('slot[name]'); const defaultSlot = fragment.querySelector('slot:not([name])'); for (let i = 0, l = fragmentSlots.length; i < l; i++) { const fragmentSlot = fragmentSlots[i]; const name = fragmentSlot.getAttribute('name'); const elementSlot = element.querySelector('[slot="'+ name + '"]'); if (elementSlot) { fragmentSlot.parentNode.replaceChild(elementSlot, fragmentSlot); } else { fragmentSlot.parentNode.removeChild(fragmentSlot); } } if (defaultSlot) { if (element.children.length) { while (element.firstChild) { defaultSlot.parentNode.insertBefore(element.firstChild, defaultSlot); } } defaultSlot.parentNode.removeChild(defaultSlot); } }, fragment (element, template, adopt) { const fragment = document.createDocumentFragment(); const clone = template.cloneNode(true); let child = clone.firstElementChild; while (child) { if (!adopt) { Binder.add(child, { container: element, scope: element.scope }); } fragment.appendChild(child); child = clone.firstElementChild; } return fragment; }, render (element, template, adopt, shadow) { if (!template) { return; } const fragment = this.fragment(element, template); let root; if (shadow && 'attachShadow' in document.body) { root = element.attachShadow({ mode: 'open' }) } else if (shadow && 'createShadowRoot' in document.body) { root = element.createShadowRoot(); } else { if (fragment) { this.slot(element, fragment); } root = element; } if (fragment) { root.appendChild(fragment); } if (adopt) { let child = root.firstElementChild; while (child) { Binder.add(child, { container: element, scope: element.scope }); child = child.nextElementSibling; } } }, define (options) { const self = this; if (typeof options !== 'object') { return console.warn('Oxe.component.define - invalid argument type'); } if (options.constructor === Array) { for (let i = 0, l = options.length; i < l; i++) { self.define(options[i]); } return; } if (!options.name) { return console.warn('Oxe.component.define - requires name'); } options.name = options.name.toLowerCase(); if (options.name in self.data) { console.log(options.name); return console.warn('Oxe.component.define - component defined'); } self.data[options.name] = options; options.count = 0; options.model = options.model || {}; options.adopt = options.adopt || false; options.methods = options.methods || {}; options.shadow = options.shadow || false; options.attributes = options.attributes || []; options.properties = options.properties || {}; if (options.style) { options.style = this.style(options.style, options.name); Style.append(options.style); } if (options.template && typeof options.template === 'string') { const data = document.createElement('div'); data.innerHTML = options.template; options.template = data; } options.properties.created = { get () { return this._created; } }; options.properties.scope = { get () { return this._scope; } }; options.properties.methods = { get () { return Methods.get(this.scope); } }; options.properties.model = { get () { return Model.get(this.scope); }, set (data) { return Model.set(this.scope, data && typeof data === 'object' ? data : {}); } }; options.properties.observedAttributes = { value: options.attributes }; options.properties.attributeChangedCallback = { value () { if (options.attributed) options.attributed.apply(this, arguments); } }; options.properties.adoptedCallback = { value () { if (options.adopted) options.adopted.apply(this, arguments); } }; options.properties.disconnectedCallback = { value () { if (options.detached) options.detached.call(this); } }; options.properties.connectedCallback = { value () { const instance = this; if (instance.created) { if (options.attached) { options.attached.call(instance); } } else { instance._created = true; self.render(instance, options.template, options.adopt, options.shadow); Promise.resolve().then(function () { if (options.created) { return options.created.call(instance); } }).then(function () { if (options.attached) { return options.attached.call(instance); } }); } } }; const constructor = function () { this._created = false; this._scope = options.name + '-' + options.count++; // Object.defineProperties(this, { // created: { // value: false, // enumerable: true, // configurable: true // }, // scope: { // enumerable: true, // value: scope // } // }); const methods = Utility.clone(options.methods); const model = Utility.clone(options.model); Methods.set(this.scope, methods); Model.set(this.scope, model); }; Object.defineProperties(constructor.prototype, options.properties); Definer.define(options.name, constructor); } };