Files

1059 lines
32 KiB
TypeScript

// Definitions.
import { ViewBase as ViewBaseDefinition } from ".";
import { Page } from "../../page";
import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../layouts/flexbox-layout";
// Types.
import { Property, CssProperty, CssAnimationProperty, InheritedProperty, Style, clearInheritedProperties, propagateInheritableProperties, propagateInheritableCssProperties, initNativeView } from "../properties";
import { Source } from "../../../utils/debug";
import { Binding, BindingOptions, Observable, WrappedValue, PropertyChangeData, traceEnabled, traceWrite, traceCategories } from "../bindable";
import { isIOS, isAndroid } from "../../../platform";
import { layout } from "../../../utils/utils";
import { Length, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty } from "../../styling/style-properties";
// TODO: Remove this import!
import * as types from "../../../utils/types";
import { Color } from "../../../color";
import { profile } from "../../../profiling";
export { isIOS, isAndroid, layout, Color };
export * from "../bindable";
export * from "../properties";
import * as ssm from "../../styling/style-scope";
// import { DOMNode } from "../../../debugger/dom-node";
import * as dnm from "../../../debugger/dom-node";
let domNodeModule: typeof dnm;
function ensuredomNodeModule(): void {
if (!domNodeModule) {
domNodeModule = require("../../../debugger/dom-node");
}
}
let styleScopeModule: typeof ssm;
function ensureStyleScopeModule() {
if (!styleScopeModule) {
styleScopeModule = require("../../styling/style-scope");
}
}
let defaultBindingSource = {};
export function getAncestor(view: ViewBaseDefinition, criterion: string | Function): ViewBaseDefinition {
let matcher: (view: ViewBaseDefinition) => boolean = null;
if (typeof criterion === "string") {
matcher = (view: ViewBaseDefinition) => view.typeName === criterion;
} else {
matcher = (view: ViewBaseDefinition) => view instanceof criterion;
}
for (let parent = view.parent; parent != null; parent = parent.parent) {
if (matcher(parent)) {
return parent;
}
}
return null;
}
export function getViewById(view: ViewBaseDefinition, id: string): ViewBaseDefinition {
if (!view) {
return undefined;
}
if (view.id === id) {
return view;
}
let retVal: ViewBaseDefinition;
const descendantsCallback = function (child: ViewBaseDefinition): boolean {
if (child.id === id) {
retVal = child;
// break the iteration by returning false
return false;
}
return true;
};
eachDescendant(view, descendantsCallback);
return retVal;
}
export function eachDescendant(view: ViewBaseDefinition, callback: (child: ViewBaseDefinition) => boolean) {
if (!callback || !view) {
return;
}
let continueIteration: boolean;
let localCallback = function (child: ViewBaseDefinition): boolean {
continueIteration = callback(child);
if (continueIteration) {
child.eachChild(localCallback);
}
return continueIteration;
};
view.eachChild(localCallback);
}
let viewIdCounter = 1;
// const contextMap = new WeakMap<Object, Map<string, WeakRef<Object>[]>>();
// function getNativeView(context: Object, typeName: string): Object {
// let typeMap = contextMap.get(context);
// if (!typeMap) {
// typeMap = new Map<string, WeakRef<Object>[]>();
// contextMap.set(context, typeMap);
// return undefined;
// }
// const array = typeMap.get(typeName);
// if (array) {
// let nativeView;
// while (array.length > 0) {
// const weakRef = array.pop();
// nativeView = weakRef.get();
// if (nativeView) {
// return nativeView;
// }
// }
// }
// return undefined;
// }
// function putNativeView(context: Object, view: ViewBase): void {
// const typeMap = contextMap.get(context);
// const typeName = view.typeName;
// let list = typeMap.get(typeName);
// if (!list) {
// list = [];
// typeMap.set(typeName, list);
// }
// list.push(new WeakRef(view.nativeViewProtected));
// }
enum Flags {
superOnLoadedCalled = "Loaded",
superOnUnloadedCalled = "Unloaded",
}
enum SuspendType {
Incremental = 0,
Loaded = 1 << 20,
NativeView = 1 << 21,
UISetup = 1 << 22,
IncrementalCountMask = ~((1 << 20) + (1 << 21) + (1 << 22))
}
namespace SuspendType {
export function toString(type: SuspendType): string {
return (type ? "suspended" : "resumed") + "(" +
"Incremental: " + (type & SuspendType.IncrementalCountMask) + ", " +
"Loaded: " + !(type & SuspendType.Loaded) + ", " +
"NativeView: " + !(type & SuspendType.NativeView) + ", " +
"UISetup: " + !(type & SuspendType.UISetup) +
")";
}
}
export abstract class ViewBase extends Observable implements ViewBaseDefinition {
public static loadedEvent = "loaded";
public static unloadedEvent = "unloaded";
private _onLoadedCalled: boolean = false;
private _onUnloadedCalled: boolean = false;
private _iosView: Object;
private _androidView: Object;
private _style: Style;
private _isLoaded: boolean;
private _visualState: string;
private _templateParent: ViewBase;
private __nativeView: any;
// private _disableNativeViewRecycling: boolean;
public domNode: dnm.DOMNode;
public recycleNativeView: "always" | "never" | "auto";
public viewController: any;
public bindingContext: any;
public nativeViewProtected: any;
public parent: ViewBase;
public isCollapsed; // Default(false) set in prototype
public id: string;
public className: string;
public _domId: number;
public _context: any;
public _isAddedToNativeVisualTree: boolean;
public _cssState: ssm.CssState = new ssm.CssState(new WeakRef(this));
public _styleScope: ssm.StyleScope;
public _suspendedUpdates: { [propertyName: string]: Property<ViewBase, any> | CssProperty<Style, any> | CssAnimationProperty<Style, any> };
public _suspendNativeUpdatesCount: SuspendType;
public _isStyleScopeHost: boolean;
public _automaticallyAdjustsScrollViewInsets: boolean;
// Dynamic properties.
left: Length;
top: Length;
effectiveLeft: number;
effectiveTop: number;
dock: "left" | "top" | "right" | "bottom";
row: number;
col: number;
rowSpan: number;
colSpan: number;
order: Order;
flexGrow: FlexGrow;
flexShrink: FlexShrink;
flexWrapBefore: FlexWrapBefore;
alignSelf: AlignSelf;
_oldLeft: number;
_oldTop: number;
_oldRight: number;
_oldBottom: number;
public effectiveMinWidth: number;
public effectiveMinHeight: number;
public effectiveWidth: number;
public effectiveHeight: number;
public effectiveMarginTop: number;
public effectiveMarginRight: number;
public effectiveMarginBottom: number;
public effectiveMarginLeft: number;
public effectivePaddingTop: number;
public effectivePaddingRight: number;
public effectivePaddingBottom: number;
public effectivePaddingLeft: number;
public effectiveBorderTopWidth: number;
public effectiveBorderRightWidth: number;
public effectiveBorderBottomWidth: number;
public effectiveBorderLeftWidth: number;
public _defaultPaddingTop: number;
public _defaultPaddingRight: number;
public _defaultPaddingBottom: number;
public _defaultPaddingLeft: number;
public _isPaddingRelative: boolean;
public _moduleName: string;
constructor() {
super();
this._domId = viewIdCounter++;
this._style = new Style(new WeakRef(this));
}
// Used in Angular.
get parentNode() {
return this._templateParent || this.parent;
}
set parentNode(node: ViewBase) {
this._templateParent = node;
}
get nativeView(): any {
// this._disableNativeViewRecycling = true;
return this.nativeViewProtected;
}
set nativeView(value: any) {
this.setNativeView(value);
}
// TODO: Use Type.prototype.typeName instead.
get typeName(): string {
return types.getClass(this);
}
get style(): Style {
return this._style;
}
set style(inlineStyle: Style /* | string */) {
if (typeof inlineStyle === "string") {
this.setInlineStyle(inlineStyle);
} else {
throw new Error("View.style property is read-only.");
}
}
get android(): any {
// this._disableNativeViewRecycling = true;
return this._androidView;
}
get ios(): any {
// this._disableNativeViewRecycling = true;
return this._iosView;
}
get isLoaded(): boolean {
return this._isLoaded;
}
get class(): string {
return this.className;
}
set class(v: string) {
this.className = v;
}
getViewById<T extends ViewBaseDefinition>(id: string): T {
return <T>getViewById(this, id);
}
get page(): Page {
if (this.parent) {
return this.parent.page;
}
return null;
}
public ensureDomNode() {
if (!this.domNode) {
ensuredomNodeModule();
this.domNode = new domNodeModule.DOMNode(this);
}
}
// Overridden so we don't raise `poropertyChange`
// The property will raise its own event.
public set(name: string, value: any) {
this[name] = WrappedValue.unwrap(value);
}
@profile
public onLoaded() {
this.setFlag(Flags.superOnLoadedCalled, true);
if (this._isLoaded) {
return;
}
this._isLoaded = true;
this._cssState.onLoaded();
this._resumeNativeUpdates(SuspendType.Loaded);
this.eachChild(child => {
this.loadView(child);
return true;
});
this._emit("loaded");
}
@profile
public onUnloaded() {
this.setFlag(Flags.superOnUnloadedCalled, true);
if (!this._isLoaded) {
return;
}
this._suspendNativeUpdates(SuspendType.Loaded);
this.eachChild(child => {
this.unloadView(child);
return true;
});
this._isLoaded = false;
this._cssState.onUnloaded();
this._emit("unloaded");
}
public _suspendNativeUpdates(type: SuspendType): void {
if (type) {
this._suspendNativeUpdatesCount = this._suspendNativeUpdatesCount | type;
} else {
this._suspendNativeUpdatesCount++;
}
}
public _resumeNativeUpdates(type: SuspendType): void {
if (type) {
this._suspendNativeUpdatesCount = this._suspendNativeUpdatesCount & ~type;
} else {
if ((this._suspendNativeUpdatesCount & SuspendType.IncrementalCountMask) === 0) {
throw new Error(`Invalid call to ${this}._resumeNativeUpdates`);
}
this._suspendNativeUpdatesCount--;
}
if (!this._suspendNativeUpdatesCount) {
this.onResumeNativeUpdates();
}
}
public _batchUpdate<T>(callback: () => T): T {
try {
this._suspendNativeUpdates(SuspendType.Incremental);
return callback();
} finally {
this._resumeNativeUpdates(SuspendType.Incremental);
}
}
private setFlag(flag: Flags, value: boolean): void {
switch (flag) {
case Flags.superOnLoadedCalled:
this._onLoadedCalled = value;
break;
case Flags.superOnUnloadedCalled:
this._onUnloadedCalled = value;
break;
}
}
private isFlagSet(flag: Flags): boolean {
switch (flag) {
case Flags.superOnLoadedCalled:
return this._onLoadedCalled;
case Flags.superOnUnloadedCalled:
return this._onUnloadedCalled;
}
}
private callFunctionWithSuper(flag: Flags, func: () => void): void {
this.setFlag(flag, false);
func();
if (!this.isFlagSet(flag)) {
throw new Error(`super.${flag} not called in ${this}`);
}
}
public callLoaded(): void {
this.callFunctionWithSuper(Flags.superOnLoadedCalled, () => this.onLoaded());
}
public callUnloaded(): void {
this.callFunctionWithSuper(Flags.superOnUnloadedCalled, () => this.onUnloaded());
}
private notifyPseudoClassChanged(pseudoClass: string): void {
this.notify({ eventName: ":" + pseudoClass, object: this });
}
private pseudoClassAliases = {
"highlighted": [
"active",
"pressed"
]
};
public cssClasses: Set<string> = new Set();
public cssPseudoClasses: Set<string> = new Set();
private getAllAliasedStates(name: string): Array<string> {
const allStates = [];
allStates.push(name);
if (name in this.pseudoClassAliases) {
for (let i = 0; i < this.pseudoClassAliases[name].length; i++) {
allStates.push(this.pseudoClassAliases[name][i]);
}
}
return allStates;
}
@profile
public addPseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (!this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.add(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
@profile
public deletePseudoClass(name: string): void {
let allStates = this.getAllAliasedStates(name);
for (let i = 0; i < allStates.length; i++) {
if (this.cssPseudoClasses.has(allStates[i])) {
this.cssPseudoClasses.delete(allStates[i]);
this.notifyPseudoClassChanged(allStates[i]);
}
}
}
private bindingContextChanged(data: PropertyChangeData): void {
this.bindings.get("bindingContext").bind(data.value);
}
private bindings: Map<string, Binding>;
private shouldAddHandlerToParentBindingContextChanged: boolean;
private bindingContextBoundToParentBindingContextChanged: boolean;
public bind(options: BindingOptions, source: Object = defaultBindingSource): void {
const targetProperty = options.targetProperty;
this.unbind(targetProperty);
if (!this.bindings) {
this.bindings = new Map<string, Binding>();
}
const binding = new Binding(this, options);
this.bindings.set(targetProperty, binding);
let bindingSource = source;
if (bindingSource === defaultBindingSource) {
bindingSource = this.bindingContext;
binding.sourceIsBindingContext = true;
if (targetProperty === "bindingContext") {
this.bindingContextBoundToParentBindingContextChanged = true;
const parent = this.parent;
if (parent) {
parent.on("bindingContextChange", this.bindingContextChanged, this);
} else {
this.shouldAddHandlerToParentBindingContextChanged = true;
}
}
}
binding.bind(bindingSource);
}
public unbind(property: string): void {
const bindings = this.bindings;
if (!bindings) {
return;
}
const binding = bindings.get(property);
if (binding) {
binding.unbind();
bindings.delete(property);
if (binding.sourceIsBindingContext) {
if (property === "bindingContext") {
this.shouldAddHandlerToParentBindingContextChanged = false;
this.bindingContextBoundToParentBindingContextChanged = false;
const parent = this.parent;
if (parent) {
parent.off("bindingContextChange", this.bindingContextChanged, this);
}
}
}
}
}
@profile
public requestLayout(): void {
// Default implementation for non View instances (like TabViewItem).
const parent = this.parent;
if (parent) {
parent.requestLayout();
}
}
public eachChild(callback: (child: ViewBase) => boolean) {
//
}
@profile
public _addView(view: ViewBase, atIndex?: number) {
if (traceEnabled()) {
traceWrite(`${this}._addView(${view}, ${atIndex})`, traceCategories.ViewHierarchy);
}
if (!view) {
throw new Error("Expecting a valid View instance.");
}
if (!(view instanceof ViewBase)) {
throw new Error(view + " is not a valid View instance.");
}
if (view.parent) {
throw new Error("View already has a parent. View: " + view + " Parent: " + view.parent);
}
view.parent = this;
this._addViewCore(view, atIndex);
view._parentChanged(null);
if (this.domNode) {
this.domNode.onChildAdded(view);
}
}
public _addViewCore(view: ViewBase, atIndex?: number) {
propagateInheritableProperties(this, view);
view._inheritStyleScope(this._styleScope);
propagateInheritableCssProperties(this.style, view.style);
if (this._context) {
view._setupUI(this._context, atIndex);
}
if (this._isLoaded) {
this.loadView(view);
}
}
public loadView(view: ViewBase): void {
if (view && !view.isLoaded) {
view.callLoaded();
}
}
public unloadView(view: ViewBase): void {
if (view && view.isLoaded) {
view.callUnloaded();
}
}
/**
* Core logic for removing a child view from this instance. Used by the framework to handle lifecycle events more centralized. Do not use outside the UI Stack implementation.
*/
public _removeView(view: ViewBase) {
if (traceEnabled()) {
traceWrite(`${this}._removeView(${view})`, traceCategories.ViewHierarchy);
}
if (view.parent !== this) {
throw new Error("View not added to this instance. View: " + view + " CurrentParent: " + view.parent + " ExpectedParent: " + this);
}
if (this.domNode) {
this.domNode.onChildRemoved(view);
}
this._removeViewCore(view);
view.parent = undefined;
view._parentChanged(this);
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewBase) {
this.unloadView(view);
if (view._context) {
view._tearDownUI();
}
}
public createNativeView(): Object {
return undefined;
}
public disposeNativeView() {
//
}
public initNativeView(): void {
//
}
public resetNativeView(): void {
//
}
private resetNativeViewInternal(): void {
// const nativeView = this.nativeViewProtected;
// if (nativeView && isAndroid) {
// const recycle = this.recycleNativeView;
// if (recycle === "always" || (recycle === "auto" && !this._disableNativeViewRecycling)) {
// resetNativeView(this);
// if (this._isPaddingRelative) {
// nativeView.setPaddingRelative(this._defaultPaddingLeft, this._defaultPaddingTop, this._defaultPaddingRight, this._defaultPaddingBottom);
// } else {
// nativeView.setPadding(this._defaultPaddingLeft, this._defaultPaddingTop, this._defaultPaddingRight, this._defaultPaddingBottom);
// }
// this.resetNativeView();
// }
// }
// if (this._cssState) {
// this._cancelAllAnimations();
// }
}
_setupAsRootView(context: any): void {
this._setupUI(context);
}
@profile
public _setupUI(context: android.content.Context, atIndex?: number, parentIsLoaded?: boolean): void {
if (this._context === context) {
return;
} else if (this._context) {
this._tearDownUI(true);
}
this._context = context;
// This will account for nativeView that is created in createNativeView, recycled
// or for backward compatability - set before _setupUI in iOS contructor.
let nativeView = this.nativeViewProtected;
// if (isAndroid) {
// const recycle = this.recycleNativeView;
// if (recycle === "always" || (recycle === "auto" && !this._disableNativeViewRecycling)) {
// nativeView = <android.view.View>getNativeView(context, this.typeName);
// }
// }
if (!nativeView) {
nativeView = this.createNativeView();
}
if (isAndroid) {
this._androidView = nativeView;
if (nativeView) {
if (this._isPaddingRelative === undefined) {
this._isPaddingRelative = nativeView.isPaddingRelative();
}
let result: any /* android.graphics.Rect */ = (<any>nativeView).defaultPaddings;
if (result === undefined) {
result = org.nativescript.widgets.ViewHelper.getPadding(nativeView);
(<any>nativeView).defaultPaddings = result;
}
this._defaultPaddingTop = result.top;
this._defaultPaddingRight = result.right;
this._defaultPaddingBottom = result.bottom;
this._defaultPaddingLeft = result.left;
const style = this.style;
if (!paddingTopProperty.isSet(style)) {
this.effectivePaddingTop = this._defaultPaddingTop;
}
if (!paddingRightProperty.isSet(style)) {
this.effectivePaddingRight = this._defaultPaddingRight;
}
if (!paddingBottomProperty.isSet(style)) {
this.effectivePaddingBottom = this._defaultPaddingBottom;
}
if (!paddingLeftProperty.isSet(style)) {
this.effectivePaddingLeft = this._defaultPaddingLeft;
}
}
} else {
this._iosView = nativeView;
}
this.setNativeView(nativeView);
if (this.parent) {
const nativeIndex = this.parent._childIndexToNativeChildIndex(atIndex);
this._isAddedToNativeVisualTree = this.parent._addViewToNativeVisualTree(this, nativeIndex);
}
this._resumeNativeUpdates(SuspendType.UISetup);
this.eachChild((child) => {
child._setupUI(context);
return true;
});
}
setNativeView(value: any): void {
if (this.__nativeView === value) {
return;
}
if (this.__nativeView) {
this._suspendNativeUpdates(SuspendType.NativeView);
// We may do a `this.resetNativeView()` here?
}
this.__nativeView = this.nativeViewProtected = value;
if (this.__nativeView) {
this._suspendedUpdates = undefined;
this.initNativeView();
this._resumeNativeUpdates(SuspendType.NativeView);
}
}
@profile
public _tearDownUI(force?: boolean): void {
// No context means we are already teared down.
if (!this._context) {
return;
}
this.resetNativeViewInternal();
this.eachChild((child) => {
child._tearDownUI(force);
return true;
});
if (this.parent) {
this.parent._removeViewFromNativeVisualTree(this);
}
// const nativeView = this.nativeViewProtected;
// if (nativeView && isAndroid) {
// const recycle = this.recycleNativeView;
// let shouldRecycle = false;
// if (recycle === "always") {
// shouldRecycle = true;
// } else if (recycle === "auto" && !this._disableNativeViewRecycling) {
// const propertiesSet = Object.getOwnPropertySymbols(this).length + Object.getOwnPropertySymbols(this.style).length / 2;
// shouldRecycle = propertiesSet <= this.recyclePropertyCounter;
// }
// // const nativeParent = isAndroid ? (<android.view.View>nativeView).getParent() : (<UIView>nativeView).superview;
// const nativeParent = (<android.view.View>nativeView).getParent();
// const animation = (<android.view.View>nativeView).getAnimation();
// if (shouldRecycle && !nativeParent && !animation) {
// putNativeView(this._context, this);
// }
// }
this.disposeNativeView();
this._suspendNativeUpdates(SuspendType.UISetup);
if (isAndroid) {
this.setNativeView(null);
this._androidView = null;
}
// this._iosView = null;
this._context = null;
if (this.domNode) {
this.domNode.dispose();
this.domNode = undefined;
}
}
_childIndexToNativeChildIndex(index?: number): number {
return index;
}
/**
* Method is intended to be overridden by inheritors and used as "protected".
*/
public _addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean {
if (view._isAddedToNativeVisualTree) {
throw new Error("Child already added to the native visual tree.");
}
return true;
}
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewFromNativeVisualTree(view: ViewBase) {
view._isAddedToNativeVisualTree = false;
}
public _goToVisualState(state: string) {
if (traceEnabled()) {
traceWrite(this + " going to state: " + state, traceCategories.Style);
}
if (state === this._visualState) {
return;
}
this.deletePseudoClass(this._visualState);
this._visualState = state;
this.addPseudoClass(state);
}
/**
* @deprecated
*
* This used to be the way to set attribute values in early {N} versions.
* Now attributes are expected to be set as plain properties on the view instances.
*/
public _applyXmlAttribute(attribute: string, value: string): boolean {
console.log("ViewBase._applyXmlAttribute(...) is deprecated; set attributes as plain properties instead");
if (attribute === "style" || attribute === "rows" || attribute === "columns" || attribute === "fontAttributes") {
this[attribute] = value;
return true;
}
return false;
}
public setInlineStyle(style: string): void {
if (typeof style !== "string") {
throw new Error("Parameter should be valid CSS string!");
}
ensureStyleScopeModule();
styleScopeModule.applyInlineStyle(this, style);
}
public _parentChanged(oldParent: ViewBase): void {
const newParent = this.parent;
//Overridden
if (oldParent) {
clearInheritedProperties(this);
if (this.bindingContextBoundToParentBindingContextChanged) {
oldParent.off("bindingContextChange", this.bindingContextChanged, this);
}
} else if (this.shouldAddHandlerToParentBindingContextChanged) {
newParent.on("bindingContextChange", this.bindingContextChanged, this);
this.bindings.get("bindingContext").bind(newParent.bindingContext);
}
}
public onResumeNativeUpdates(): void {
// Apply native setters...
initNativeView(this);
}
public toString(): string {
let str = this.typeName;
if (this.id) {
str += `<${this.id}>`;
} else {
str += `(${this._domId})`;
}
let source = Source.get(this);
if (source) {
str += `@${source};`;
}
return str;
}
_onCssStateChange(): void {
this._cssState.onChange();
eachDescendant(this, (child: ViewBase) => {
child._cssState.onChange();
return true;
});
}
_inheritStyleScope(styleScope: ssm.StyleScope): void {
// If we are styleScope don't inherit parent stylescope.
// TODO: Consider adding parent scope and merge selectors.
if (this._isStyleScopeHost) {
return;
}
if (this._styleScope !== styleScope) {
this._styleScope = styleScope;
this._onCssStateChange();
this.eachChild(child => {
child._inheritStyleScope(styleScope);
return true;
});
}
}
public showModal(...args): ViewBase {
const parent = this.parent;
return parent && parent.showModal(...args);
}
public closeModal(...args): void {
const parent = this.parent;
if (parent) {
parent.closeModal(...args);
}
}
public _dialogClosed(): void {
eachDescendant(this, (child: ViewBase) => {
child._dialogClosed();
return true;
});
}
public _onRootViewReset(): void {
eachDescendant(this, (child: ViewBase) => {
child._onRootViewReset();
return true;
});
}
}
ViewBase.prototype.isCollapsed = false;
ViewBase.prototype._oldLeft = 0;
ViewBase.prototype._oldTop = 0;
ViewBase.prototype._oldRight = 0;
ViewBase.prototype._oldBottom = 0;
ViewBase.prototype.effectiveMinWidth = 0;
ViewBase.prototype.effectiveMinHeight = 0;
ViewBase.prototype.effectiveWidth = 0;
ViewBase.prototype.effectiveHeight = 0;
ViewBase.prototype.effectiveMarginTop = 0;
ViewBase.prototype.effectiveMarginRight = 0;
ViewBase.prototype.effectiveMarginBottom = 0;
ViewBase.prototype.effectiveMarginLeft = 0;
ViewBase.prototype.effectivePaddingTop = 0;
ViewBase.prototype.effectivePaddingRight = 0;
ViewBase.prototype.effectivePaddingBottom = 0;
ViewBase.prototype.effectivePaddingLeft = 0;
ViewBase.prototype.effectiveBorderTopWidth = 0;
ViewBase.prototype.effectiveBorderRightWidth = 0;
ViewBase.prototype.effectiveBorderBottomWidth = 0;
ViewBase.prototype.effectiveBorderLeftWidth = 0;
ViewBase.prototype._defaultPaddingTop = 0;
ViewBase.prototype._defaultPaddingRight = 0;
ViewBase.prototype._defaultPaddingBottom = 0;
ViewBase.prototype._defaultPaddingLeft = 0;
ViewBase.prototype._isViewBase = true;
ViewBase.prototype.recycleNativeView = "never";
ViewBase.prototype._suspendNativeUpdatesCount =
SuspendType.Loaded |
SuspendType.NativeView |
SuspendType.UISetup;
export const bindingContextProperty = new InheritedProperty<ViewBase, any>({ name: "bindingContext" });
bindingContextProperty.register(ViewBase);
export const classNameProperty = new Property<ViewBase, string>({
name: "className",
valueChanged(view: ViewBase, oldValue: string, newValue: string) {
let classes = view.cssClasses;
classes.clear();
if (typeof newValue === "string" && newValue !== "") {
newValue.split(" ").forEach(c => classes.add(c));
}
view._onCssStateChange();
}
});
classNameProperty.register(ViewBase);
export const idProperty = new Property<ViewBase, string>({ name: "id", valueChanged: (view, oldValue, newValue) => view._onCssStateChange() });
idProperty.register(ViewBase);
export function booleanConverter(v: string): boolean {
const lowercase = (v + "").toLowerCase();
if (lowercase === "true") {
return true;
} else if (lowercase === "false") {
return false;
}
throw new Error(`Invalid boolean: ${v}`);
}