UNPKG

@winged/core

Version:

Morden webapp framekwork made only for ts developers. (UNDER DEVELOPMENT, PLEASE DO NOT USE)

156 lines (146 loc) 5.23 kB
import { StateDependencies, ViewState } from '../types' import { utils } from '../utils' import { Renderable } from './Renderable' import { AtAttr } from './vAttribute/AtAttr' import { AtRef } from './vAttribute/AtRef' import { AtValue } from './vAttribute/AtValue' import { DataAttr } from './vAttribute/DataAttr' import { NormalDomAttr } from './vAttribute/NormalDomAttr' import { OnEventAttr } from './vAttribute/OnEventAttr' import { VAttribute } from './vAttribute/VAttribute' import { AttrDict, ContentStructList, PrevSibling, vDomFactory, VDomStruct, VNodeRegister } from './vdom' import { VAttrVerifyError } from './vdomErrors' import { vdomUtils } from './vdomUtils' export class VElement extends Renderable { public static vAttributeClasses = [ AtAttr, AtRef, AtValue, DataAttr, OnEventAttr, NormalDomAttr ] public tagName: string public element?: HTMLElement public vAttributes: { [key: string]: VAttribute } = {} private children: Renderable[] = [] constructor(tagName: string, attrDict: AttrDict, children: ContentStructList, register: VNodeRegister) { super() this.tagName = tagName const copiedAttrDict = utils.clone(attrDict, false) for (const VAttrClass of VElement.vAttributeClasses) { for (const attrName in copiedAttrDict) { const attr = copiedAttrDict[attrName] let vAttr: VAttribute | undefined if (typeof VAttrClass.matcher === 'string' && VAttrClass.matcher === attrName) { vAttr = new VAttrClass(attrName, attr, this, register) } else if (VAttrClass.matcher instanceof RegExp && VAttrClass.matcher.test(attrName)) { vAttr = new VAttrClass(attrName, attr, this, register) } if (vAttr) { if (vAttr.created) { this.vAttributes[attrName] = vAttr delete copiedAttrDict[attrName] } else { vAttr.destroy() } } } } // handling unresolved attrs for (const attrName in copiedAttrDict) { throw new VAttrVerifyError( attrName, `Unsupported vAttribute: '${attrName}' in VElement '${tagName}' of ${register.viewClassName}, please check!` ) } for (const struct of children as Array<string | VDomStruct>) { this.children.push(vDomFactory.createVChildren(struct, 'common', register)) } this.stateDependencies = this.initStateDependencies() } public destroyDomElement() { super.destroyDomElement() if (!this.element) { return } for (const key in this.vAttributes) { this.vAttributes[key].onElementDestroy(this.element) } if (this.element.parentElement) { this.element.parentElement.removeChild(this.element) } this.element = undefined } public render(state: ViewState, modifiedState: ViewState, container: HTMLElement, prevSibling: PrevSibling) { if (!this.stateDependencies) { console.error(this) throw new Error('Can\'t render VElement: stateDependencies is null') } // console.log("do render"); if (!this.element) { this.createDomElement(container, prevSibling) this.doRender(state, modifiedState, prevSibling) } else { if (vdomUtils.checkStateDependencies(modifiedState, this.stateDependencies)) { this.doRender(state, modifiedState, prevSibling) } } // if (prevSibling.node === this.element) { // prevSibling.node = this.element.nextSibling; // } prevSibling.node = this.element } public skipRender(prevSibling: PrevSibling) { if (!this.element) { return } super.skipRender(prevSibling) prevSibling.node = this.element } public destroy() { this.destroyDomElement() for (const key in this.vAttributes) { this.vAttributes[key].destroy() } for (const child of this.children) { child.destroy() } delete this.vAttributes delete this.children super.destroy() } public forEachChildren(fn: Renderable.ChildrenIter) { for (const child of this.children) { if (fn(child) === true) { return } } } protected initStateDependencies() { const deps: StateDependencies = {} for (const key in this.vAttributes) { const vAttr = this.vAttributes[key] vdomUtils.mergeStateDependenciesN(deps, vAttr.stateDependencies) } vdomUtils.mergeStateDependenciesWithChildren(deps, this.children) return deps } private createDomElement(container: HTMLElement, prevSibling: PrevSibling) { this.element = document.createElement(this.tagName) if (prevSibling.node) { container.insertBefore(this.element, prevSibling.node.nextSibling) } else { container.insertBefore(this.element, container.firstChild) } for (const key in this.vAttributes) { this.vAttributes[key].onElementCreate(this.element) } } private doRender(state: ViewState, modifiedState: ViewState, prevSibling: PrevSibling) { for (const key in this.vAttributes) { this.vAttributes[key].onRender(state, modifiedState) } prevSibling.node = undefined for (const child of this.children) { child.render(state, modifiedState, this.element!, prevSibling) } } }