@web-atoms/core-docs
Version:
572 lines • 22.7 kB
JavaScript
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