UNPKG

@web-atoms/core-docs

Version:
572 lines 22.7 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "../App", "../core/AtomBridge", "../core/types", "../di/Inject", "./AtomDisposableList", "./AtomOnce", "./AtomWatcher", "./Bind", "./InheritedProperty", "./PropertyMap", "./XNode"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PropertyBinding = exports.AtomComponent = void 0; const App_1 = require("../App"); const AtomBridge_1 = require("../core/AtomBridge"); // tslint:disable-next-line:import-spacing const types_1 = require("../core/types"); const Inject_1 = require("../di/Inject"); const AtomDisposableList_1 = require("./AtomDisposableList"); const AtomOnce_1 = require("./AtomOnce"); const AtomWatcher_1 = require("./AtomWatcher"); const Bind_1 = require("./Bind"); const InheritedProperty_1 = require("./InheritedProperty"); const PropertyMap_1 = require("./PropertyMap"); const XNode_1 = require("./XNode"); const objectHasOwnProperty = Object.prototype.hasOwnProperty; const localBindSymbol = Bind_1.bindSymbol; const localXNodeSymbol = XNode_1.xnodeSymbol; const localBridge = AtomBridge_1.AtomBridge; let AtomComponent = class AtomComponent { constructor(app, element = null) { this.app = app; this.mInvalidated = 0; this.mPendingPromises = {}; this.disposables = new AtomDisposableList_1.AtomDisposableList(); this.bindings = []; this.eventHandlers = []; this.element = element; // AtomBridge.instance.attachControl(this.element, this as any); this.element.atomControl = this; const a = this.beginEdit(); this.preCreate(); this.create(); app.callLater(() => a.dispose()); } /** Do not ever use, only available as intellisense feature for * vs code editor. */ get vsProps() { return undefined; } bind(element, name, path, twoWays, valueFunc, source) { // remove existing binding if any // let binding = this.bindings.find( (x) => x.name === name && (element ? x.element === element : true)); // if (binding) { // binding.dispose(); // ArrayHelper.remove(this.bindings, (x) => x === binding); // } const binding = new PropertyBinding(this, element, name, path, twoWays, valueFunc, source); this.bindings.push(binding); return { dispose: () => { binding.dispose(); types_1.ArrayHelper.remove(this.bindings, (x) => x === binding); } }; } /** * Remove all bindings associated with given element and optional name * @param element T * @param name string */ unbind(element, name) { const toDelete = this.bindings.filter((x) => x.element === element && (!name || (x.name === name))); for (const iterator of toDelete) { iterator.dispose(); types_1.ArrayHelper.remove(this.bindings, (x) => x === iterator); } } bindEvent(element, name, method, key) { if (!element) { return; } if (!method) { return; } const be = { element, name, handler: method }; if (key) { be.key = key; } be.disposable = AtomBridge_1.AtomBridge.instance.addEventHandler(element, name, method, false); this.eventHandlers.push(be); return { dispose: () => { be.disposable.dispose(); types_1.ArrayHelper.remove(this.eventHandlers, (e) => e.disposable === be.disposable); } }; } unbindEvent(element, name, method, key) { const deleted = []; for (const be of this.eventHandlers) { if (element && be.element !== element) { return; } if (key && be.key !== key) { return; } if (name && be.name !== name) { return; } if (method && be.handler !== method) { return; } be.disposable.dispose(); be.handler = null; be.element = null; be.name = null; be.key = null; deleted.push(() => this.eventHandlers.remove(be)); } for (const iterator of deleted) { iterator(); } } /** * Control checks if property is declared on the control or not. * Since TypeScript no longer creates enumerable properties, we have * to inspect name and PropertyMap which is generated by `@BindableProperty` * or the value is not set to undefined. * @param name name of Property */ hasProperty(name) { if (/^(data|viewModel|localViewModel|element)$/.test(name)) { return true; } const map = PropertyMap_1.PropertyMap.from(this); if (map.map[name]) { return true; } if (this[name] !== undefined) { return true; } return false; } /** * Use this method if you want to set attribute on HTMLElement immediately but * defer atom control property * @param element HTMLElement * @param name string * @param value any */ setPrimitiveValue(element, name, value) { const p = value; if (p && p.then && p.catch) { this.mPendingPromises[name] = p; p.then((r) => { if (this.mPendingPromises[name] !== p) { return; } this.mPendingPromises[name] = null; this.setPrimitiveValue(element, name, r); }).catch((e) => { if (this.mPendingPromises[name] !== p) { return; } this.mPendingPromises[name] = null; // tslint:disable-next-line:no-console console.error(e); }); return; } if (/^(viewModel|localViewModel)$/.test(name)) { this[name] = value; return; } if ((!element || element === this.element) && this.hasProperty(name)) { this.runAfterInit(() => { this[name] = value; }); } else { this.setElementValue(element, name, value); } } setLocalValue(element, name, value) { // if value is a promise const p = value; if (p && p.then && p.catch) { this.mPendingPromises[name] = p; p.then((r) => { if (this.mPendingPromises[name] !== p) { return; } this.mPendingPromises[name] = null; this.setLocalValue(element, name, r); }).catch((e) => { if (this.mPendingPromises[name] !== p) { return; } this.mPendingPromises[name] = null; // tslint:disable-next-line:no-console console.error(e); }); return; } if ((!element || element === this.element) && this.hasProperty(name)) { this[name] = value; } else { this.setElementValue(element, name, value); } } dispose(e) { if (this.mInvalidated) { clearTimeout(this.mInvalidated); this.mInvalidated = 0; } AtomBridge_1.AtomBridge.instance.visitDescendents(e || this.element, (ex, ac) => { if (ac) { ac.dispose(); return false; } return true; }); if (!e) { this.unbindEvent(null, null, null); for (const binding of this.bindings) { binding.dispose(); } this.bindings.length = 0; this.bindings = null; // AtomBridge.instance.dispose(this.element); const e1 = this.element; if (typeof e1.dispose === "function") { e1.dispose(); } this.element = null; const lvm = this.localViewModel; if (lvm && lvm.dispose) { lvm.dispose(); this.localViewModel = null; } const vm = this.viewModel; if (vm && vm.dispose) { vm.dispose(); this.viewModel = null; } this.disposables.dispose(); this.pendingInits = null; } } // tslint:disable-next-line:no-empty onPropertyChanged(name) { } beginEdit() { this.pendingInits = []; const a = this.pendingInits; return { dispose: () => { if (this.pendingInits == null) { // case where current control is disposed... return; } this.pendingInits = null; if (a) { for (const iterator of a) { iterator(); } } this.invalidate(); } }; } invalidate() { if (this.mInvalidated) { clearTimeout(this.mInvalidated); } this.mInvalidated = setTimeout(() => { this.mInvalidated = 0; this.app.callLater(() => { this.onUpdateUI(); }); }, 5); } onUpdateUI() { // for implementors.. } runAfterInit(f) { if (this.pendingInits) { this.pendingInits.push(f); } else { f(); } } registerDisposable(d) { return this.disposables.add(d); } render(node, e, creator) { creator = creator || this; const app = this.app; const renderFirst = AtomBridge_1.AtomBridge.platform === "xf"; e = e || this.element; const attr = node.attributes; if (attr) { for (const key in attr) { if (attr.hasOwnProperty(key)) { const item = attr[key]; const isObject = typeof item === "object"; if (isObject) { if (objectHasOwnProperty.call(item, localBindSymbol)) { item[localBindSymbol](key, this, e, creator); continue; } if (objectHasOwnProperty.call(item, localXNodeSymbol)) { if (item.isTemplate) { this.setLocalValue(e, key, AtomBridge_1.AtomBridge.toTemplate(app, item, creator)); continue; } const child = AtomBridge_1.AtomBridge.createNode(item, app); this.setLocalValue(e, key, child.element); continue; } } this.setLocalValue(e, key, item); } } } for (const iterator of node.children) { if (iterator === void 0) { continue; } if (typeof iterator === "string") { e.appendChild(document.createTextNode(iterator)); continue; } if (iterator.isTemplate) { if (iterator.isProperty) { this.setLocalValue(e, iterator.name, AtomBridge_1.AtomBridge.toTemplate(app, iterator.children[0], creator)); } else { e.appendChild(AtomBridge_1.AtomBridge.toTemplate(app, iterator, creator)); } continue; } if (iterator.isProperty) { for (const child of iterator.children) { const pc = AtomBridge_1.AtomBridge.createNode(child, app); (pc.control || this).render(child, pc.element, creator); // in Xamarin.Forms certain properties are required to be // set in advance, so we append the element after setting // all children properties localBridge.instance.append(e, iterator.name, pc.element); } continue; } const t = iterator.attributes && iterator.attributes.template; if (t) { this.setLocalValue(e, t, AtomBridge_1.AtomBridge.toTemplate(app, iterator, creator)); continue; } const c = AtomBridge_1.AtomBridge.createNode(iterator, app); if (renderFirst) { (c.control || this).render(iterator, c.element, creator); } if (this.element === e) { this.append(c.control || c.element); } else { e.appendChild(c.element); } if (!renderFirst) { (c.control || this).render(iterator, c.element, creator); } } } // tslint:disable-next-line:no-empty create() { } // tslint:disable-next-line:no-empty preCreate() { } setElementValue(element, name, value) { AtomBridge_1.AtomBridge.instance.setValue(element, name, value); } resolve(c, selfName) { const result = this.app.resolve(c, true); if (selfName) { if (typeof selfName === "function") { // this is required as parent is not available // in items control so binding becomes difficult this.runAfterInit(() => { const v = selfName(); if (v) { for (const key in v) { if (v.hasOwnProperty(key)) { const element = v[key]; result[key] = element; } } } }); } else { result[selfName] = this; } } return result; } }; AtomComponent.isControl = true; __decorate([ InheritedProperty_1.InheritedProperty, __metadata("design:type", Object) ], AtomComponent.prototype, "data", void 0); __decorate([ InheritedProperty_1.InheritedProperty, __metadata("design:type", Object) ], AtomComponent.prototype, "viewModel", void 0); __decorate([ InheritedProperty_1.InheritedProperty, __metadata("design:type", Object) ], AtomComponent.prototype, "localViewModel", void 0); AtomComponent = __decorate([ __param(0, Inject_1.Inject), __metadata("design:paramtypes", [App_1.App, Object]) ], AtomComponent); exports.AtomComponent = AtomComponent; class PropertyBinding { constructor(target, element, name, path, twoWays, valueFunc, source) { this.target = target; this.element = element; this.name = name; this.twoWays = twoWays; this.source = source; this.isTwoWaySetup = false; this.name = name; this.twoWays = twoWays; this.target = target; this.element = element; this.updaterOnce = new AtomOnce_1.AtomOnce(); if (valueFunc) { if (typeof valueFunc !== "function") { this.fromSourceToTarget = valueFunc.fromSource; this.fromTargetToSource = valueFunc.fromTarget; } else { this.fromSourceToTarget = valueFunc; } } this.watcher = new AtomWatcher_1.AtomWatcher(target, path, (...v) => { this.updaterOnce.run(() => { if (this.disposed) { return; } // set value for (const iterator of v) { if (iterator === undefined) { return; } } const cv = this.fromSourceToTarget ? this.fromSourceToTarget.apply(this, v) : v[0]; if (this.target instanceof AtomComponent) { this.target.setLocalValue(this.element, this.name, cv); } else { this.target[name] = cv; } }); }, source); this.path = this.watcher.path; if (this.target instanceof AtomComponent) { this.target.runAfterInit(() => { if (!this.watcher) { // this is disposed ... return; } this.watcher.init(true); if (twoWays) { this.setupTwoWayBinding(); } }); } else { this.watcher.init(true); if (twoWays) { this.setupTwoWayBinding(); } } } setupTwoWayBinding() { if (this.target instanceof AtomComponent) { if (this.element && (this.element !== this.target.element || !this.target.hasProperty(this.name))) { // most likely it has change event.. let events = []; if (typeof this.twoWays !== "boolean") { events = this.twoWays; } this.twoWaysDisposable = AtomBridge_1.AtomBridge.instance.watchProperty(this.element, this.name, events, (v) => { this.setInverseValue(v); }); return; } } const watcher = new AtomWatcher_1.AtomWatcher(this.target, [[this.name]], (...values) => { if (this.isTwoWaySetup) { this.setInverseValue(values[0]); } }); watcher.init(true); this.isTwoWaySetup = true; this.twoWaysDisposable = watcher; } setInverseValue(value) { if (!this.twoWays) { throw new Error("This Binding is not two ways."); } this.updaterOnce.run(() => { if (this.disposed) { return; } const first = this.path[0]; const length = first.length; let v = this.target; let i = 0; let name; for (i = 0; i < length - 1; i++) { name = first[i].name; if (name === "this") { v = this.source || this.target; } else { v = v[name]; } if (!v) { return; } } name = first[i].name; v[name] = this.fromTargetToSource ? this.fromTargetToSource.call(this, value) : value; }); } dispose() { if (this.twoWaysDisposable) { this.twoWaysDisposable.dispose(); this.twoWaysDisposable = null; } this.watcher.dispose(); this.disposed = true; this.watcher = null; } } exports.PropertyBinding = PropertyBinding; }); //# sourceMappingURL=AtomComponent.js.map