keet
Version:
Minimalist view layer for the web
158 lines (146 loc) • 4.33 kB
JavaScript
//
// Keetjs v4.2.4 Alpha release: https://github.com/keetjs/keet
// Minimalist view layer for the web
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Keetjs >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//
// Copyright 2018, Shahrul Nizam Selamat
// Released under the MIT License.
//
/**
* @module keet
* @example
* import Keet from 'keet'
*
* class App extends Keet {
* contructor() {
* super()
* // props
* }
* // new extended method
* myMethod(...args){
* //
* }
* }
*
* const app = new App()
*/
import parseStr from './src/components/parseStr'
import { updateContext, morpher } from './src/components/genElement'
import { genId, assert, html, childLike } from './utils'
import CreateModel from './src/base/createModel'
import mount from './src/base/mount'
/**
* The main constructor of Keet
* @param {Boolean} localize - Use local inhertance for sub-components
* instead using global referance
*/
class Keet {
constructor (localize) {
if (localize) {
this.LOCAL = true
}
this.ID = Keet.indentity
// mount vtree from render arguments
this.autoRender()
}
// Auto rendered on class constructor instantiation
async autoRender () {
await this.el
if (typeof this.render === 'function') {
const vtree = this.render()
this.mount(vtree)
// ensure parsing only done by root component
// check constructor if it decorated with childLike
const proto = Object.getPrototypeOf(this)
if (this.IS_STUB || (proto && proto.constructor.IS_STUB)) {
return
}
this.cycleVirtualDomTree()
}
}
// generate ID for the component
static get indentity () {
return genId()
}
/**
* Mount an instance of html/string template
* @param {Object|string} instance - the html/string template
*/
mount (instance) {
if (!this.LOCAL) {
if (this.el) {
this.storeRef(this.el)
} else {
assert(false, `Component has no unique identifier.`)
}
}
return mount.call(this, instance)
}
/**
* Parse this component to the DOM
* @param {Boolean} stub - set as true if this a child component
*/
cycleVirtualDomTree (stub) {
// life-cycle method before rendering the component
if (this.componentWillMount && typeof this.componentWillMount === 'function') {
this.componentWillMount()
}
// Render this component to the target DOM
if (stub) {
this.IS_STUB = true
}
parseStr.call(this, stub)
}
/**
* Recheck all states if anything changed, diffing will occurs.
* this method is ***asynchronous*** and ***trottled***, you can call it from a loop and
* only trigger diffing when the loop end
*/
callBatchPoolUpdate () {
updateContext.call(this, morpher, 1)
}
/**
* Another component can subscribe to changes on this component.
* This is the subscribe method
* @param {Function} fn - the callback function for the subscribe
*/
subscribe (fn) {
this.exec = this.exec || []
this.exec = this.exec.concat(fn)
}
/**
* Another component can subscribe to changes on this component.
* This is the publish method
* @param {...*} value - one or more parameters to publish to subscribers
*/
inform (...args) {
if (this.exec.length) {
this.exec.map(fn => fn.apply(null, args))
}
}
/**
* Store referance in the global space, with this the parent component do need
* to store/assign it as a property while still be able to look for the sub-component
* to initialize it
* @param {string} name - Identifier for the component, should be unique to avoid conflict
*/
storeRef (name) {
window.__keetGlobalComponentRef__ = window.__keetGlobalComponentRef__ || []
let isExist = window.__keetGlobalComponentRef__.map(c => c.identifier).indexOf(name)
if (~isExist) {
assert(false, `The component name: ${name} already exist in the global pool.`)
} else {
window.__keetGlobalComponentRef__ = window.__keetGlobalComponentRef__.concat({
identifier: name,
component: this
})
}
}
}
export {
Keet as default,
html,
CreateModel,
childLike
}