feat: Scoped Packages (#7911)

* chore: move tns-core-modules to nativescript-core

* chore: preparing compat generate script

* chore: add missing definitions

* chore: no need for http-request to be private

* chore: packages chore

* test: generate tests for tns-core-modules

* chore: add anroid module for consistency

* chore: add .npmignore

* chore: added privateModulesWhitelist

* chore(webpack): added bundle-entry-points

* chore: scripts

* chore: tests changed to use @ns/core

* test: add scoped-packages test project

* test: fix types

* test: update test project

* chore: build scripts

* chore: update build script

* chore: npm scripts cleanup

* chore: make the compat pgk work with old wp config

* test: generate diff friendly tests

* chore: create barrel exports

* chore: move files after rebase

* chore: typedoc config

* chore: compat mode

* chore: review of barrels

* chore: remove tns-core-modules import after rebase

* chore: dev workflow setup

* chore: update developer-workflow

* docs: experiment with API extractor

* chore: api-extractor and barrel exports

* chore: api-extractor configs

* chore: generate d.ts rollup with api-extractor

* refactor: move methods inside Frame

* chore: fic tests to use Frame static methods

* refactor: create Builder class

* refactor: use Builder class in tests

* refactor: include Style in ui barrel

* chore: separate compat build script

* chore: fix tslint errors

* chore: update NATIVESCRIPT_CORE_ARGS

* chore: fix compat pack

* chore: fix ui-test-app build with linked modules

* chore: Application, ApplicationSettings, Connectivity and Http

* chore: export Trace, Profiling and Utils

* refactor: Static create methods for ImageSource

* chore: fix deprecated usages of ImageSource

* chore: move Span and FormattedString to ui

* chore: add events-args and ImageSource to index files

* chore: check for CLI >= 6.2 when building for IOS

* chore: update travis build

* chore: copy Pod file to compat package

* chore: update error msg ui-tests-app

* refactor: Apply suggestions from code review

Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com>

* chore: typings and refs

* chore: add missing d.ts files for public API

* chore: adress code review FB

* chore: update api-report

* chore: dev-workflow for other apps

* chore: api update

* chore: update api-report
This commit is contained in:
Alexander Vakrilov
2019-10-17 00:45:33 +03:00
committed by GitHub
parent 6c7139477e
commit cc97a16800
880 changed files with 9090 additions and 2104 deletions

View File

@@ -0,0 +1,71 @@
/**
* @module "ui/core/bindable"
*/ /** */
import { ViewBase } from "../view-base";
import { Observable, WrappedValue, PropertyChangeData, EventData } from "../../../data/observable";
import {
isEnabled as traceEnabled,
write as traceWrite,
error as traceError,
categories as traceCategories,
notifyEvent as traceNotifyEvent,
messageType as traceMessageType,
isCategorySet
} from "../../../trace";
export {
Observable, WrappedValue, PropertyChangeData, EventData,
traceEnabled, traceWrite, traceError, traceCategories, traceNotifyEvent,
traceMessageType, isCategorySet
};
/**
* The options object used in the Bindable.bind method.
*/
export interface BindingOptions {
/**
* The property name of the source object (typically the ViewModel) to bind to.
*/
sourceProperty: string;
/**
* The property name of the target object (that is the Bindable instance) to bind the source property to.
*/
targetProperty: string;
/**
* True to establish a two-way binding, false otherwise. A two-way binding will synchronize both the source and the target property values regardless of which one initiated the change.
*/
twoWay?: boolean;
/**
* An expression used for calculations (convertions) based on the value of the property.
*/
expression?: string;
}
/**
* An interface which defines methods need to create binding value converter.
*/
export interface ValueConverter {
/**
* A method that will be executed when a value (of the binding property) should be converted to the observable model.
* For example: user types in a text field, but our business logic requires to store data in a different manner (e.g. in lower case).
* @param params - An array of parameters where first element is the value of the property and next elements are parameters send to converter.
*/
toModel: (...params: any[]) => any;
/**
* A method that will be executed when a value should be converted to the UI view. For example we have a date object which should be displayed to the end user in a specific date format.
* @param params - An array of parameters where first element is the value of the property and next elements are parameters send to converter.
*/
toView: (...params: any[]) => any;
}
export class Binding {
constructor(target: ViewBase, options: BindingOptions);
public bind(source: Object): void;
public unbind();
public sourceIsBindingContext: boolean;
public updateTwoWay(value: any);
}
export function getEventOrGestureName(name: string): string;
export function isEventOrGesture(name: string, view: ViewBase): boolean;

View File

@@ -0,0 +1,628 @@
import { BindingOptions } from ".";
import { ViewBase } from "../view-base";
import { unsetValue } from "../properties";
import { Observable, WrappedValue, PropertyChangeData, EventData } from "../../../data/observable";
import { addWeakEventListener, removeWeakEventListener } from "../weak-event-listener";
import { bindingConstants, parentsRegex } from "../../builder/binding-builder";
import { escapeRegexSymbols } from "../../../utils/utils";
import {
isEnabled as traceEnabled,
write as traceWrite,
error as traceError,
categories as traceCategories,
notifyEvent as traceNotifyEvent,
isCategorySet,
messageType as traceMessageType
} from "../../../trace";
import * as types from "../../../utils/types";
import * as applicationCommon from "../../../application/application-common";
import * as polymerExpressions from "../../../js-libs/polymer-expressions";
export {
Observable, WrappedValue, PropertyChangeData, EventData,
traceEnabled, traceWrite, traceError, traceCategories, traceNotifyEvent, traceMessageType, isCategorySet
};
const contextKey = "context";
// this regex is used to get parameters inside [] for example:
// from $parents['ListView'] will return 'ListView'
// from $parents[1] will return 1
const paramsRegex = /\[\s*(['"])*(\w*)\1\s*\]/;
const bc = bindingConstants;
const emptyArray = [];
const propertiesCache = {};
function getProperties(property: string): Array<string> {
if (!property) {
return emptyArray;
}
let result: Array<string> = propertiesCache[property];
if (result) {
return result;
}
// first replace all '$parents[..]' with a safe string
// second removes all ] since they are not important for property access and not needed
// then split properties either on '.' or '['
const parentsMatches = property.match(parentsRegex);
result = property.replace(parentsRegex, "parentsMatch")
.replace(/\]/g, "")
.split(/\.|\[/);
let parentsMatchesCounter = 0;
for (let i = 0, resultLength = result.length; i < resultLength; i++) {
if (result[i] === "parentsMatch") {
result[i] = parentsMatches[parentsMatchesCounter++];
}
}
propertiesCache[property] = result;
return result;
}
export function getEventOrGestureName(name: string): string {
return name.indexOf("on") === 0 ? name.substr(2, name.length - 2) : name;
}
// NOTE: method fromString from "ui/gestures";
export function isGesture(eventOrGestureName: string): boolean {
let t = eventOrGestureName.trim().toLowerCase();
return t === "tap"
|| t === "doubletap"
|| t === "pinch"
|| t === "pan"
|| t === "swipe"
|| t === "rotation"
|| t === "longpress"
|| t === "touch";
}
// TODO: Make this instance function so that we dont need public statc tapEvent = "tap"
// in controls. They will just override this one and provide their own event support.
export function isEventOrGesture(name: string, view: ViewBase): boolean {
if (typeof name === "string") {
let eventOrGestureName = getEventOrGestureName(name);
let evt = `${eventOrGestureName}Event`;
return view.constructor && evt in view.constructor || isGesture(eventOrGestureName.toLowerCase());
}
return false;
}
export class Binding {
private source: WeakRef<Object>;
// TODO: target should be hard reference!
public target: WeakRef<ViewBase>;
private sourceOptions: { instance: WeakRef<any>; property: string };
private targetOptions: { instance: WeakRef<Object>; property: string };
private sourceProperties: Array<string>;
private propertyChangeListeners: Map<string, Observable> = new Map<string, Observable>();
public updating: boolean;
public sourceIsBindingContext: boolean;
public options: BindingOptions;
constructor(target: ViewBase, options: BindingOptions) {
this.target = new WeakRef(target);
this.options = options;
this.sourceProperties = getProperties(options.sourceProperty);
this.targetOptions = this.resolveOptions(target, getProperties(options.targetProperty));
if (!this.targetOptions) {
throw new Error(`Invalid property: ${options.targetProperty} for target: ${target}`);
}
if (options.twoWay) {
const target = this.targetOptions.instance.get();
if (target instanceof Observable) {
target.on(`${this.targetOptions.property}Change`, this.onTargetPropertyChanged, this);
}
}
}
private onTargetPropertyChanged(data: PropertyChangeData): void {
this.updateTwoWay(data.value);
}
public loadedHandlerVisualTreeBinding(args) {
let target = args.object;
target.off("loaded", this.loadedHandlerVisualTreeBinding, this);
const context = target.bindingContext;
if (context !== undefined && context !== null) {
this.update(context);
}
}
public clearSource(): void {
this.propertyChangeListeners.forEach((observable, index, map) => {
removeWeakEventListener(
observable,
Observable.propertyChangeEvent,
this.onSourcePropertyChanged,
this);
});
this.propertyChangeListeners.clear();
if (this.source) {
this.source.clear();
}
if (this.sourceOptions) {
this.sourceOptions.instance.clear();
this.sourceOptions = undefined;
}
}
private sourceAsObject(source: any): any {
/* tslint:disable */
let objectType = typeof source;
if (objectType === "number") {
source = new Number(source);
}
else if (objectType === "boolean") {
source = new Boolean(source);
}
else if (objectType === "string") {
source = new String(source);
}
/* tslint:enable */
return source;
}
private bindingContextChanged(data: PropertyChangeData): void {
const target = this.targetOptions.instance.get();
if (!target) {
this.unbind();
return;
}
const value = data.value;
if (value !== null && value !== undefined) {
this.update(value);
} else {
// TODO: Is this correct?
// What should happen when bindingContext is null/undefined?
this.clearBinding();
}
// TODO: if oneWay - call target.unbind();
}
public bind(source: any): void {
const target = this.targetOptions.instance.get();
if (this.sourceIsBindingContext && target instanceof Observable && this.targetOptions.property !== "bindingContext") {
target.on("bindingContextChange", this.bindingContextChanged, this);
}
this.update(source);
}
private update(source: any): void {
this.clearSource();
source = this.sourceAsObject(source);
if (!types.isNullOrUndefined(source)) {
// TODO: if oneWay - call target.unbind();
this.source = new WeakRef(source);
this.sourceOptions = this.resolveOptions(source, this.sourceProperties);
let sourceValue = this.getSourcePropertyValue();
this.updateTarget(sourceValue);
this.addPropertyChangeListeners(this.source, this.sourceProperties);
} else if (!this.sourceIsBindingContext) {
// TODO: if oneWay - call target.unbind();
let sourceValue = this.getSourcePropertyValue();
this.updateTarget(sourceValue ? sourceValue : source);
}
}
public unbind() {
const target = this.targetOptions.instance.get();
if (target instanceof Observable) {
if (this.options.twoWay) {
target.off(`${this.targetOptions.property}Change`, this.onTargetPropertyChanged, this);
}
if (this.sourceIsBindingContext && this.targetOptions.property !== "bindingContext") {
target.off("bindingContextChange", this.bindingContextChanged, this);
}
}
if (this.targetOptions) {
this.targetOptions = undefined;
}
this.sourceProperties = undefined;
if (!this.source) {
return;
}
this.clearSource();
}
// Consider returning single {} instead of array for performance.
private resolveObjectsAndProperties(source: Object, properties: Array<string>): Array<{ instance: Object; property: string }> {
let result = [];
let currentObject = source;
let currentObjectChanged = false;
for (let i = 0, propsArrayLength = properties.length; i < propsArrayLength; i++) {
let property = properties[i];
if (property === bc.bindingValueKey) {
currentObjectChanged = true;
}
if (property === bc.parentValueKey || property.indexOf(bc.parentsValueKey) === 0) {
let parentView = this.getParentView(this.target.get(), property).view;
if (parentView) {
currentObject = parentView.bindingContext;
} else {
let targetInstance = this.target.get();
targetInstance.off("loaded", this.loadedHandlerVisualTreeBinding, this);
targetInstance.on("loaded", this.loadedHandlerVisualTreeBinding, this);
}
currentObjectChanged = true;
}
if (currentObject) {
result.push({ instance: currentObject, property: property });
} else {
break;
}
// do not need to dive into last object property getter on binding stage will handle it
if (!currentObjectChanged && (i < propsArrayLength - 1)) {
currentObject = currentObject ? currentObject[properties[i]] : null;
}
currentObjectChanged = false;
}
return result;
}
private addPropertyChangeListeners(source: WeakRef<Object>, sourceProperty: Array<string>, parentProperies?: string) {
let objectsAndProperties = this.resolveObjectsAndProperties(source.get(), sourceProperty);
let prop = parentProperies || "";
for (let i = 0, length = objectsAndProperties.length; i < length; i++) {
const propName = objectsAndProperties[i].property;
prop += "$" + propName;
let currentObject = objectsAndProperties[i].instance;
if (!this.propertyChangeListeners.has(prop) && currentObject instanceof Observable && currentObject._isViewBase) {
// Add listener for properties created with after 3.0 version
addWeakEventListener(currentObject, `${propName}Change`, this.onSourcePropertyChanged, this);
addWeakEventListener(currentObject, Observable.propertyChangeEvent, this.onSourcePropertyChanged, this);
this.propertyChangeListeners.set(prop, currentObject);
} else if (!this.propertyChangeListeners.has(prop) && currentObject instanceof Observable) {
addWeakEventListener(currentObject, Observable.propertyChangeEvent, this.onSourcePropertyChanged, this);
this.propertyChangeListeners.set(prop, currentObject);
}
}
}
private prepareExpressionForUpdate(): string {
// this regex is used to create a valid RegExp object from a string that has some special regex symbols like [,(,$ and so on.
// Basically this method replaces all matches of 'source property' in expression with '$newPropertyValue'.
// For example: with an expression similar to:
// text="{{ sourceProperty = $parents['ListView'].test, expression = $parents['ListView'].test + 2}}"
// update expression will be '$newPropertyValue + 2'
// then on expression execution the new value will be taken and target property will be updated with the value of the expression.
let escapedSourceProperty = escapeRegexSymbols(this.options.sourceProperty);
let expRegex = new RegExp(escapedSourceProperty, "g");
let resultExp = this.options.expression.replace(expRegex, bc.newPropertyValueKey);
return resultExp;
}
private updateTwoWay(value: any) {
if (this.updating || !this.options.twoWay) {
return;
}
let newValue = value;
if (this.options.expression) {
let changedModel = {};
changedModel[bc.bindingValueKey] = value;
changedModel[bc.newPropertyValueKey] = value;
let sourcePropertyName = "";
if (this.sourceOptions) {
sourcePropertyName = this.sourceOptions.property;
}
else if (typeof this.options.sourceProperty === "string" && this.options.sourceProperty.indexOf(".") === -1) {
sourcePropertyName = this.options.sourceProperty;
}
if (sourcePropertyName !== "") {
changedModel[sourcePropertyName] = value;
}
let updateExpression = this.prepareExpressionForUpdate();
this.prepareContextForExpression(changedModel, updateExpression, undefined);
let expressionValue = this._getExpressionValue(updateExpression, true, changedModel);
if (expressionValue instanceof Error) {
traceWrite((<Error>expressionValue).message, traceCategories.Binding, traceMessageType.error);
}
newValue = expressionValue;
}
this.updateSource(newValue);
}
private _getExpressionValue(expression: string, isBackConvert: boolean, changedModel: any): any {
try {
let exp = polymerExpressions.PolymerExpressions.getExpression(expression);
if (exp) {
let context = this.source && this.source.get && this.source.get() || global;
let model = {};
let addedProps = [];
const resources = applicationCommon.getResources();
for (let prop in resources) {
if (resources.hasOwnProperty(prop) && !context.hasOwnProperty(prop)) {
context[prop] = resources[prop];
addedProps.push(prop);
}
}
this.prepareContextForExpression(context, expression, addedProps);
model[contextKey] = context;
let result = exp.getValue(model, isBackConvert, changedModel ? changedModel : model);
// clear added props
let addedPropsLength = addedProps.length;
for (let i = 0; i < addedPropsLength; i++) {
delete context[addedProps[i]];
}
addedProps.length = 0;
return result;
}
return new Error(expression + " is not a valid expression.");
}
catch (e) {
let errorMessage = "Run-time error occured in file: " + e.sourceURL + " at line: " + e.line + " and column: " + e.column;
return new Error(errorMessage);
}
}
public onSourcePropertyChanged(data: PropertyChangeData) {
const sourceProps = this.sourceProperties;
const sourcePropsLength = sourceProps.length;
let changedPropertyIndex = sourceProps.indexOf(data.propertyName);
let parentProps = "";
if (changedPropertyIndex > -1) {
parentProps = "$" + sourceProps.slice(0, changedPropertyIndex + 1).join("$");
while (this.propertyChangeListeners.get(parentProps) !== data.object) {
changedPropertyIndex += sourceProps.slice(changedPropertyIndex + 1).indexOf(data.propertyName) + 1;
parentProps = "$" + sourceProps.slice(0, changedPropertyIndex + 1).join("$");
}
}
if (this.options.expression) {
const expressionValue = this._getExpressionValue(this.options.expression, false, undefined);
if (expressionValue instanceof Error) {
traceWrite(expressionValue.message, traceCategories.Binding, traceMessageType.error);
} else {
this.updateTarget(expressionValue);
}
} else {
if (changedPropertyIndex > -1) {
const props = sourceProps.slice(changedPropertyIndex + 1);
const propsLength = props.length;
if (propsLength > 0) {
let value = data.value;
for (let i = 0; i < propsLength; i++) {
value = value[props[i]];
}
this.updateTarget(value);
} else if (data.propertyName === this.sourceOptions.property) {
this.updateTarget(data.value);
}
}
}
// we need to do this only if nested objects are used as source and some middle object has changed.
if (changedPropertyIndex > -1 && changedPropertyIndex < sourcePropsLength - 1) {
const probablyChangedObject = this.propertyChangeListeners.get(parentProps);
if (probablyChangedObject &&
probablyChangedObject !== data.object[sourceProps[changedPropertyIndex]]) {
// remove all weakevent listeners after change, because changed object replaces object that is hooked for
// propertyChange event
for (let i = sourcePropsLength - 1; i > changedPropertyIndex; i--) {
const prop = "$" + sourceProps.slice(0, i + 1).join("$");
if (this.propertyChangeListeners.has(prop)) {
removeWeakEventListener(
this.propertyChangeListeners.get(prop),
Observable.propertyChangeEvent,
this.onSourcePropertyChanged,
this);
this.propertyChangeListeners.delete(prop);
}
}
const newProps = sourceProps.slice(changedPropertyIndex + 1);
// add new weak event listeners
const newObject = data.object[sourceProps[changedPropertyIndex]];
if (!types.isNullOrUndefined(newObject) && typeof newObject === "object") {
this.addPropertyChangeListeners(new WeakRef(newObject), newProps, parentProps);
}
}
}
}
private prepareContextForExpression(model: Object, expression: string, newProps: Array<string>) {
let parentViewAndIndex: { view: ViewBase, index: number };
let parentView;
let addedProps = newProps || [];
if (expression.indexOf(bc.bindingValueKey) > -1) {
model[bc.bindingValueKey] = model;
addedProps.push(bc.bindingValueKey);
}
if (expression.indexOf(bc.parentValueKey) > -1) {
parentView = this.getParentView(this.target.get(), bc.parentValueKey).view;
if (parentView) {
model[bc.parentValueKey] = parentView.bindingContext;
addedProps.push(bc.parentValueKey);
}
}
let parentsArray = expression.match(parentsRegex);
if (parentsArray) {
for (let i = 0; i < parentsArray.length; i++) {
parentViewAndIndex = this.getParentView(this.target.get(), parentsArray[i]);
if (parentViewAndIndex.view) {
model[bc.parentsValueKey] = model[bc.parentsValueKey] || {};
model[bc.parentsValueKey][parentViewAndIndex.index] = parentViewAndIndex.view.bindingContext;
addedProps.push(bc.parentsValueKey);
}
}
}
}
private getSourcePropertyValue() {
if (this.options.expression) {
let changedModel = {};
changedModel[bc.bindingValueKey] = this.source ? this.source.get() : undefined;
let expressionValue = this._getExpressionValue(this.options.expression, false, changedModel);
if (expressionValue instanceof Error) {
traceWrite((<Error>expressionValue).message, traceCategories.Binding, traceMessageType.error);
}
else {
return expressionValue;
}
}
if (this.sourceOptions) {
let sourceOptionsInstance = this.sourceOptions.instance.get();
if (this.sourceOptions.property === bc.bindingValueKey) {
return sourceOptionsInstance;
} else if ((sourceOptionsInstance instanceof Observable) && (this.sourceOptions.property && this.sourceOptions.property !== "")) {
return sourceOptionsInstance.get(this.sourceOptions.property);
} else if (sourceOptionsInstance && this.sourceOptions.property && this.sourceOptions.property !== "" &&
this.sourceOptions.property in sourceOptionsInstance) {
return sourceOptionsInstance[this.sourceOptions.property];
} else {
traceWrite("Property: '" + this.sourceOptions.property + "' is invalid or does not exist. SourceProperty: '" + this.options.sourceProperty + "'", traceCategories.Binding, traceMessageType.error);
}
}
return null;
}
public clearBinding() {
this.clearSource();
this.updateTarget(unsetValue);
}
private updateTarget(value: any) {
if (this.updating) {
return;
}
this.updateOptions(this.targetOptions, types.isNullOrUndefined(value) ? unsetValue : value);
}
private updateSource(value: any) {
if (this.updating || !this.source || !this.source.get()) {
return;
}
this.updateOptions(this.sourceOptions, value);
}
private getParentView(target: any, property: string): { view: ViewBase, index: number } {
if (!target) {
return { view: null, index: null };
}
let result: ViewBase;
if (property === bc.parentValueKey) {
result = target.parent;
}
let index = null;
if (property.indexOf(bc.parentsValueKey) === 0) {
result = target.parent;
let indexParams = paramsRegex.exec(property);
if (indexParams && indexParams.length > 1) {
index = indexParams[2];
}
if (!isNaN(index)) {
let indexAsInt = parseInt(index);
while (indexAsInt > 0) {
result = result.parent;
indexAsInt--;
}
} else if (types.isString(index)) {
while (result && result.typeName !== index) {
result = result.parent;
}
}
}
return { view: result, index: index };
}
private resolveOptions(obj: Object, properties: Array<string>): { instance: WeakRef<Object>; property: any } {
let objectsAndProperties = this.resolveObjectsAndProperties(obj, properties);
if (objectsAndProperties.length > 0) {
let resolvedObj = objectsAndProperties[objectsAndProperties.length - 1].instance;
let prop = objectsAndProperties[objectsAndProperties.length - 1].property;
return {
instance: new WeakRef(this.sourceAsObject(resolvedObj)),
property: prop
};
}
return null;
}
private updateOptions(options: { instance: WeakRef<any>; property: string }, value: any) {
let optionsInstance;
if (options && options.instance) {
optionsInstance = options.instance.get();
}
if (!optionsInstance) {
return;
}
this.updating = true;
try {
if (isEventOrGesture(options.property, <any>optionsInstance) &&
types.isFunction(value)) {
// calling off method with null as handler will remove all handlers for options.property event
optionsInstance.off(options.property, null, optionsInstance.bindingContext);
optionsInstance.on(options.property, value, optionsInstance.bindingContext);
} else if (optionsInstance instanceof Observable) {
optionsInstance.set(options.property, value);
} else {
optionsInstance[options.property] = value;
}
} catch (ex) {
traceWrite("Binding error while setting property " + options.property + " of " + optionsInstance + ": " + ex,
traceCategories.Binding,
traceMessageType.error);
}
this.updating = false;
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "bindable",
"main": "bindable",
"types": "bindable.d.ts"
}

View File

@@ -0,0 +1,11 @@
/* tslint:disable:no-unused-variable */
/* tslint:disable:no-empty */
import { ControlStateChangeListener as ControlStateChangeListenerDefinition } from "./control-state-change";
export class ControlStateChangeListener implements ControlStateChangeListenerDefinition {
constructor(control: any /* UIControl */, callback: (state: string) => void) {
console.log("ControlStateChangeListener is intended for IOS usage only.");
}
public start() { }
public stop() { }
}

View File

@@ -0,0 +1,20 @@
/**
* @module "ui/core/control-state-change"
*/ /** */
/**
* An utility class used for supporting styling infrastructure.
* WARNING: This class is intended for IOS only.
*/
export class ControlStateChangeListener {
/**
* Initializes an instance of ControlStateChangeListener class.
* @param control An instance of the UIControl which state will be watched.
* @param callback A callback called when a visual state of the UIControl is changed.
*/
constructor(control: any /* UIControl */, callback: (state: string) => void);
start();
stop();
}

View File

@@ -0,0 +1,68 @@
/* tslint:disable:no-unused-variable */
import { ControlStateChangeListener as ControlStateChangeListenerDefinition } from ".";
class ObserverClass extends NSObject {
// NOTE: Refactor this - use Typescript property instead of strings....
observeValueForKeyPathOfObjectChangeContext(path: string) {
if (path === "selected") {
this["_owner"]._onSelectedChanged();
} else if (path === "enabled") {
this["_owner"]._onEnabledChanged();
} else if (path === "highlighted") {
this["_owner"]._onHighlightedChanged();
}
}
}
export class ControlStateChangeListener implements ControlStateChangeListenerDefinition {
private _observer: NSObject;
private _control: UIControl;
private _observing: boolean = false;
private _callback: (state: string) => void;
constructor(control: UIControl, callback: (state: string) => void) {
this._observer = ObserverClass.alloc();
this._observer["_owner"] = this;
this._control = control;
this._callback = callback;
}
public start() {
if (!this._observing) {
this._control.addObserverForKeyPathOptionsContext(this._observer, "highlighted", NSKeyValueObservingOptions.New, null);
this._observing = true;
this._updateState();
}
}
public stop() {
if (this._observing) {
this._observing = false;
this._control.removeObserverForKeyPath(this._observer, "highlighted");
}
}
//@ts-ignore
private _onEnabledChanged() {
this._updateState();
}
//@ts-ignore
private _onSelectedChanged() {
this._updateState();
}
//@ts-ignore
private _onHighlightedChanged() {
this._updateState();
}
private _updateState() {
let state = "normal";
if (this._control.highlighted) {
state = "highlighted";
}
this._callback(state);
}
}

View File

@@ -0,0 +1,5 @@
{
"name": "control-state-change",
"main": "control-state-change",
"types": "control-state-change.d.ts"
}

View File

@@ -0,0 +1,5 @@
{
"name": "properties",
"main": "properties",
"types": "properties.d.ts"
}

View File

@@ -0,0 +1,165 @@
/**
* @module "ui/core/properties"
*/ /** */
import { ViewBase } from "../view-base";
import { Style } from "../../styling/style";
export { Style };
/**
* Value specifing that Property should be set to its initial value.
*/
export const unsetValue: any;
export interface PropertyOptions<T, U> {
readonly name: string;
readonly defaultValue?: U;
readonly affectsLayout?: boolean;
readonly equalityComparer?: (x: U, y: U) => boolean;
readonly valueChanged?: (target: T, oldValue: U, newValue: U) => void;
readonly valueConverter?: (value: string) => U;
}
export interface CoerciblePropertyOptions<T, U> extends PropertyOptions<T, U> {
readonly coerceValue: (t: T, u: U) => U;
}
export interface CssPropertyOptions<T extends Style, U> extends PropertyOptions<T, U> {
readonly cssName: string;
}
export interface ShorthandPropertyOptions<P> {
readonly name: string,
readonly cssName: string;
readonly converter: (value: string | P) => [CssProperty<any, any> | CssAnimationProperty<any, any>, any][],
readonly getter: (this: Style) => string | P
}
export interface CssAnimationPropertyOptions<T, U> {
readonly name: string;
readonly cssName?: string;
readonly defaultValue?: U;
readonly equalityComparer?: (x: U, y: U) => boolean;
readonly valueChanged?: (target: T, oldValue: U, newValue: U) => void;
readonly valueConverter?: (value: string) => U;
}
export class Property<T extends ViewBase, U> {
constructor(options: PropertyOptions<T, U>);
public readonly getDefault: symbol;
public readonly setNative: symbol;
public readonly defaultValue: U;
public register(cls: { prototype: T }): void;
public nativeValueChange(owner: T, value: U): void;
public isSet(instance: T): boolean;
}
export interface Property<T extends ViewBase, U> extends TypedPropertyDescriptor<U> {
}
export class CoercibleProperty<T extends ViewBase, U> extends Property<T, U> {
constructor(options: CoerciblePropertyOptions<T, U>);
public readonly coerce: (target: T) => void;
}
export interface CoercibleProperty<T extends ViewBase, U> extends TypedPropertyDescriptor<U> {
}
export class InheritedProperty<T extends ViewBase, U> extends Property<T, U> {
constructor(options: PropertyOptions<T, U>);
}
export class CssProperty<T extends Style, U> {
constructor(options: CssPropertyOptions<T, U>);
public readonly getDefault: symbol;
public readonly setNative: symbol;
public readonly name: string;
public readonly cssName: string;
public readonly cssLocalName: string;
public readonly defaultValue: U;
public register(cls: { prototype: T }): void;
public isSet(instance: T): boolean;
}
export class InheritedCssProperty<T extends Style, U> extends CssProperty<T, U> {
constructor(options: CssPropertyOptions<T, U>);
}
export class ShorthandProperty<T extends Style, P> {
constructor(options: ShorthandPropertyOptions<P>);
public readonly name: string;
public readonly cssName: string;
public register(cls: typeof Style): void;
}
export class CssAnimationProperty<T extends Style, U> {
constructor(options: CssAnimationPropertyOptions<T, U>);
public readonly getDefault: symbol;
public readonly setNative: symbol;
public readonly name: string;
public readonly cssName: string;
public readonly cssLocalName: string;
readonly keyframe: string;
public readonly defaultValue: U;
public register(cls: { prototype: T }): void;
public isSet(instance: T): boolean;
/**
* @private
*/
public _initDefaultNativeValue(target: T): void;
/**
* @private
*/
public _valueConverter?: (value: string) => any;
/**
* @private
*/
public static _getByCssName(name: string): CssAnimationProperty<any, any>;
/**
* @private
*/
public static _getPropertyNames(): string[];
}
export function initNativeView(view: ViewBase): void;
export function resetNativeView(view: ViewBase): void;
export function resetCSSProperties(style: Style): void;
export function propagateInheritableProperties(view: ViewBase, childView: ViewBase): void;
export function propagateInheritableCssProperties(parentStyle: Style, childStyle: Style): void;
export function clearInheritedProperties(view: ViewBase): void;
export function makeValidator<T>(...values: T[]): (value: any) => value is T;
export function makeParser<T>(isValid: (value: any) => boolean): (value: any) => T;
export function getSetProperties(view: ViewBase): [string, any][];
export function getComputedCssValues(view: ViewBase): [string, any][];
export function isCssVariable(property: string): boolean;
export function isCssCalcExpression(value: string): boolean;
export function isCssVariableExpression(value: string): boolean;
//@private
/**
* @private get all properties defined on ViewBase
*/
export function _getProperties(): Property<any, any>[];
/**
* @private get all properties defined on Style
*/
export function _getStyleProperties(): CssProperty<any, any>[];
export function _evaluateCssVariableExpression(view: ViewBase, cssName: string, value: string): string;
export function _evaluateCssCalcExpression(value: string): string;
//@endprivate

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
{
"name": "view-base",
"main": "view-base",
"types": "view-base.d.ts"
}

View File

@@ -0,0 +1,486 @@
/**
* @module "ui/core/view-base"
*/ /** */
import { Property, CssProperty, CssAnimationProperty, InheritedProperty, Style } from "../properties";
import { BindingOptions, Observable } from "../bindable";
import { SelectorCore } from "../../styling/css-selector";
import { isIOS, isAndroid } from "../../../platform";
import { KeyframeAnimation } from "../../animation/keyframe-animation";
import { Page } from "../../page";
import { layout } from "../../../utils/utils";
import { Color } from "../../../color";
import { Order, FlexGrow, FlexShrink, FlexWrapBefore, AlignSelf } from "../../layouts/flexbox-layout";
import { Length } from "../../styling/style-properties";
import { DOMNode } from "../../../debugger/dom-node";
export { isIOS, isAndroid, layout, Color };
export * from "../properties";
export * from "../bindable";
/**
* Iterates through all child views (via visual tree) and executes a function.
* @param view - Starting view (parent container).
* @param callback - A function to execute on every child. If function returns false it breaks the iteration.
*/
export function eachDescendant(view: ViewBase, callback: (child: ViewBase) => boolean);
/**
* Gets an ancestor from a given type.
* @param view - Starting view (child view).
* @param criterion - The type of ancestor view we are looking for. Could be a string containing a class name or an actual type.
* Returns an instance of a view (if found), otherwise undefined.
*/
export function getAncestor(view: ViewBase, criterion: string | Function): ViewBase;
export function isEventOrGesture(name: string, view: ViewBase): boolean;
/**
* Gets a child view by id.
* @param view - The parent (container) view of the view to look for.
* @param id - The id of the view to look for.
* Returns an instance of a view (if found), otherwise undefined.
*/
export function getViewById(view: ViewBase, id: string): ViewBase;
export interface ShowModalOptions {
/**
* Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
*/
context: any;
/**
* A function that will be called when the view is closed. Any arguments provided when calling ShownModallyData.closeCallback will be available here.
*/
closeCallback: Function;
/**
* An optional parameter specifying whether to show the modal view in full-screen mode.
*/
fullscreen?: boolean;
/**
* An optional parameter specifying whether to show the modal view with animation.
*/
animated?: boolean;
/**
* An optional parameter specifying whether to stretch the modal view when not in full-screen mode.
*/
stretched?: boolean;
/**
* An optional parameter that specify options specific to iOS as an object.
*/
ios?: {
/**
* The UIModalPresentationStyle to be used when showing the dialog in iOS .
*/
presentationStyle: any /* UIModalPresentationStyle */
}
android?: {
/**
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
*/
cancelable?: boolean
}
}
export abstract class ViewBase extends Observable {
// Dynamic properties.
left: Length;
top: Length;
effectiveLeft: number;
effectiveTop: number;
dock: "left" | "top" | "right" | "bottom";
row: number;
col: number;
/**
* Setting `column` property is the same as `col`
*/
column: number;
rowSpan: number;
colSpan: number;
/**
* Setting `columnSpan` property is the same as `colSpan`
*/
columnSpan: number;
domNode: DOMNode;
order: Order;
flexGrow: FlexGrow;
flexShrink: FlexShrink;
flexWrapBefore: FlexWrapBefore;
alignSelf: AlignSelf;
/**
* @private
* Module name when the view is a module root. Otherwise, it is undefined.
*/
_moduleName?: string;
//@private
/**
* @private
*/
_oldLeft: number;
/**
* @private
*/
_oldTop: number;
/**
* @private
*/
_oldRight: number;
/**
* @private
*/
_oldBottom: number;
/**
* @private
*/
_defaultPaddingTop: number;
/**
* @private
*/
_defaultPaddingRight: number;
/**
* @private
*/
_defaultPaddingBottom: number;
/**
* @private
*/
_defaultPaddingLeft: number;
/**
* A property bag holding suspended native updates.
* Native setters that had to execute while there was no native view,
* or the view was detached from the visual tree etc. will accumulate in this object,
* and will be applied when all prerequisites are met.
* @private
*/
_suspendedUpdates: { [propertyName: string]: Property<ViewBase, any> | CssProperty<Style, any> | CssAnimationProperty<Style, any> };
//@endprivate
/**
* Shows the View contained in moduleName as a modal view.
* @param moduleName - The name of the module to load starting from the application root.
* @param modalOptions - A ShowModalOptions instance
*/
showModal(moduleName: string, modalOptions: ShowModalOptions): ViewBase;
/**
* Shows the view passed as parameter as a modal view.
* @param view - View instance to be shown modally.
* @param modalOptions - A ShowModalOptions instance
*/
showModal(view: ViewBase, modalOptions: ShowModalOptions): ViewBase;
/**
* Closes the current modal view that this page is showing.
* @param context - Any context you want to pass back to the host when closing the modal view.
*/
closeModal(context?: any): void;
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;
/**
* String value used when hooking to loaded event.
*/
public static loadedEvent: string;
/**
* String value used when hooking to unloaded event.
*/
public static unloadedEvent: string;
public ios: any;
public android: any;
/**
* returns the native UIViewController.
*/
public viewController: any;
/**
* read-only. If you want to set out-of-band the nativeView use the setNativeView method.
*/
public nativeViewProtected: any;
public nativeView: any;
public bindingContext: any;
/**
* Gets the name of the constructor function for this instance. E.g. for a Button class this will return "Button".
*/
public typeName: string;
/**
* Gets the parent view. This property is read-only.
*/
public readonly parent: ViewBase;
/**
* Gets the template parent or the native parent. Sets the template parent.
*/
public parentNode: ViewBase;
/**
* Gets or sets the id for this view.
*/
public id: string;
/**
* Gets or sets the CSS class name for this view.
*/
public className: string;
/**
* Gets owner page. This is a read-only property.
*/
public readonly page: Page;
/**
* Gets the style object associated to this view.
*/
public readonly style: Style;
/**
* Returns true if visibility is set to 'collapse'.
* Readonly property
*/
public isCollapsed: boolean;
public readonly isLoaded: boolean;
/**
* Returns the child view with the specified id.
*/
public getViewById<T extends ViewBase>(id: string): T;
/**
* Load view.
* @param view to load.
*/
public loadView(view: ViewBase): void;
/**
* Unload view.
* @param view to unload.
*/
public unloadView(view: ViewBase): void;
public onLoaded(): void;
public onUnloaded(): void;
public onResumeNativeUpdates(): void;
public bind(options: BindingOptions, source?: Object): void;
public unbind(property: string): void;
/**
* Invalidates the layout of the view and triggers a new layout pass.
*/
public requestLayout(): void;
/**
* Iterates over children of type ViewBase.
* @param callback Called for each child of type ViewBase. Iteration stops if this method returns falsy value.
*/
public eachChild(callback: (child: ViewBase) => boolean): void;
public _addView(view: ViewBase, atIndex?: number): void;
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _addViewCore(view: ViewBase, atIndex?: number): void;
public _removeView(view: ViewBase): void;
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _removeViewCore(view: ViewBase): void;
public _parentChanged(oldParent: ViewBase): void;
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _dialogClosed(): void;
/**
* Method is intended to be overridden by inheritors and used as "protected"
*/
public _onRootViewReset(): void;
_domId: number;
_cssState: any /* "ui/styling/style-scope" */;
/**
* @private
* Notifies each child's css state for change, recursively.
* Either the style scope, className or id properties were changed.
*/
_onCssStateChange(): void;
public cssClasses: Set<string>;
public cssPseudoClasses: Set<string>;
public _goToVisualState(state: string): void;
public setInlineStyle(style: string): void;
_context: any /* android.content.Context */;
/**
* Setups the UI for ViewBase and all its children recursively.
* This method should *not* be overridden by derived views.
*/
_setupUI(context: any /* android.content.Context */, atIndex?: number): void;
/**
* Tears down the UI for ViewBase and all its children recursively.
* This method should *not* be overridden by derived views.
*/
_tearDownUI(force?: boolean): void;
/**
* Creates a native view.
* Returns either android.view.View or UIView.
*/
createNativeView(): Object;
/**
* Initializes properties/listeners of the native view.
*/
initNativeView(): void;
/**
* Clean up references to the native view.
*/
disposeNativeView(): void;
/**
* Resets properties/listeners set to the native view.
*/
resetNativeView(): void;
/**
* Set the nativeView field performing extra checks and updates to the native properties on the new view.
* Use in cases where the createNativeView is not suitable.
* As an example use in item controls where the native parent view will create the native views for child items.
*/
setNativeView(view: any): void;
_isAddedToNativeVisualTree: boolean;
/**
* Performs the core logic of adding a child view to the native visual tree. Returns true if the view's native representation has been successfully added, false otherwise.
*/
_addViewToNativeVisualTree(view: ViewBase, atIndex?: number): boolean;
_removeViewFromNativeVisualTree(view: ViewBase): void;
_childIndexToNativeChildIndex(index?: number): number;
/**
* @protected
* @unstable
* A widget can call this method to add a matching css pseudo class.
*/
public addPseudoClass(name: string): void;
/**
* @protected
* @unstable
* A widget can call this method to discard matching css pseudo class.
*/
public deletePseudoClass(name: string): void;
/**
* @unstable
* Ensures a dom-node for this view.
*/
public ensureDomNode();
//@private
/**
* @private
*/
public recycleNativeView: "always" | "never" | "auto";
/**
* @private
*/
public _isPaddingRelative: boolean;
public _styleScope: any;
/**
* @private
*/
public _automaticallyAdjustsScrollViewInsets: boolean;
/**
* @private
*/
_isStyleScopeHost: boolean;
/**
* Determines the depth of suspended updates.
* When the value is 0 the current property updates are not batched nor scoped and must be immediately applied.
* If the value is 1 or greater, the current updates are batched and does not have to provide immediate update.
* Do not set this field, the _batchUpdate method is responsible to keep the count up to date,
* as well as adding/rmoving the view to/from the visual tree.
*/
public _suspendNativeUpdatesCount: number;
/**
* Allow multiple updates to be performed on the instance at once.
*/
public _batchUpdate<T>(callback: () => T): T;
/**
* @private
*/
_setupAsRootView(context: any): void;
/**
* @private
*/
_inheritStyleScope(styleScope: any /* StyleScope */): void;
/**
* @private
*/
callLoaded(): void;
/**
* @private
*/
callUnloaded(): void;
//@endprivate
}
export class Binding {
constructor(target: ViewBase, options: BindingOptions);
public bind(source: Object): void;
public unbind();
}
export const idProperty: Property<ViewBase, string>;
export const classNameProperty: Property<ViewBase, string>;
export const bindingContextProperty: InheritedProperty<ViewBase, any>;
/**
* Converts string into boolean value.
* Throws error if value is not 'true' or 'false'.
*/
export function booleanConverter(v: string): boolean;

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
{
"name": "view",
"main": "view",
"types": "view.d.ts"
}

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

867
nativescript-core/ui/core/view/view.d.ts vendored Normal file
View File

@@ -0,0 +1,867 @@
/**
* @module "ui/core/view"
*/ /** */
/// <reference path="../../../tns-core-modules.d.ts" />
import { ViewBase, Property, InheritedProperty, EventData, Color } from "../view-base";
import { Animation, AnimationDefinition, AnimationPromise } from "../../animation";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
import { GestureTypes, GestureEventData, GesturesObserver } from "../../gestures";
import { LinearGradient } from "../../styling/gradient";
export * from "../view-base";
export * from "../../styling/style-properties";
export { LinearGradient };
export function PseudoClassHandler(...pseudoClasses: string[]): MethodDecorator;
/**
* Specifies the type name for the instances of this View class,
* that is used when matching CSS type selectors.
*
* Usage:
* ```
* @CSSType("Button")
* class Button extends View {
* }
* ```
*
* Internally the decorator set `Button.prototype.cssType = "Button"`.
* @param type The type name, e. g. "Button", "Label", etc.
*/
export function CSSType(type: string): ClassDecorator;
/**
*
* @param view The view
* @param context The ModuleType
* @param type Type of the ModuleType to be matched
*/
export function viewMatchesModuleContext(
view: View,
context: ModuleContext,
type: ModuleType[]): boolean;
/**
* Denotes a length number that is in device independent pixel units.
*/
export type dip = number;
/**
* Denotes a length number that is in physical device pixels.
*/
export type px = number;
/**
* Denotes a normalized percent number.
* 0% is represented as 0
* 50% is represented as 0.5
* 100% is represented as 1
*/
export type percent = number;
/**
* The Point interface describes a two dimensional location.
* It has two properties x and y, representing the x and y coordinate of the location.
*/
export interface Point {
/**
* Represents the x coordinate of the location.
*/
x: number;
/**
* Represents the y coordinate of the location.
*/
y: number;
}
/**
* The Size interface describes abstract dimensions in two dimensional space.
* It has two properties width and height, representing the width and height values of the size.
*/
export interface Size {
/**
* Represents the width of the size.
*/
width: number;
/**
* Represents the height of the size.
*/
height: number;
}
/**
* Defines the data for the shownModally event.
*/
export interface ShownModallyData extends EventData {
/**
* The context (optional, may be undefined) passed to the view when shown modally.
*/
context?: any;
/**
* A callback to call when you want to close the modally shown view.
* Pass in any kind of arguments and you will receive when the callback parameter
* of View.showModal is executed.
*/
closeCallback?: Function;
}
/**
* This class is the base class for all UI components.
* A View occupies a rectangular area on the screen and is responsible for drawing and layouting of all UI components within.
*/
export abstract class View extends ViewBase {
/**
* String value used when hooking to layoutChanged event.
*/
public static layoutChangedEvent: string;
/**
* String value used when hooking to showingModally event.
*/
public static showingModallyEvent: string;
/**
* String value used when hooking to shownModally event.
*/
public static shownModallyEvent: string;
/**
* Gets the android-specific native instance that lies behind this proxy. Will be available if running on an Android platform.
*/
public android: any;
/**
* Gets the ios-specific native instance that lies behind this proxy. Will be available if running on an iOS platform.
*/
public ios: any;
/**
* Gets or sets the binding context of this instance. This object is used as a source for each Binding that does not have a source object specified.
*/
bindingContext: any;
/**
* Gets or sets the border color of the view.
*/
borderColor: string | Color;
/**
* Gets or sets the top border color of the view.
*/
borderTopColor: Color;
/**
* Gets or sets the right border color of the view.
*/
borderRightColor: Color;
/**
* Gets or sets the bottom border color of the view.
*/
borderBottomColor: Color;
/**
* Gets or sets the left border color of the view.
*/
borderLeftColor: Color;
/**
* Gets or sets the border width of the view.
*/
borderWidth: string | Length;
/**
* Gets or sets the top border width of the view.
*/
borderTopWidth: Length;
/**
* Gets or sets the right border width of the view.
*/
borderRightWidth: Length;
/**
* Gets or sets the bottom border width of the view.
*/
borderBottomWidth: Length;
/**
* Gets or sets the left border width of the view.
*/
borderLeftWidth: Length;
/**
* Gets or sets the border radius of the view.
*/
borderRadius: string | Length;
/**
* Gets or sets the top left border radius of the view.
*/
borderTopLeftRadius: Length;
/**
* Gets or sets the top right border radius of the view.
*/
borderTopRightRadius: Length;
/**
* Gets or sets the bottom right border radius of the view.
*/
borderBottomRightRadius: Length;
/**
* Gets or sets the bottom left border radius of the view.
*/
borderBottomLeftRadius: Length;
/**
* Gets or sets the color of the view.
*/
color: Color;
/**
* Gets or sets the elevation of the android view.
*/
androidElevation: number;
/**
* Gets or sets the dynamic elevation offset of the android view.
*/
androidDynamicElevationOffset: number;
/**
* Gets or sets the background style property.
*/
background: string;
/**
* Gets or sets the background color of the view.
*/
backgroundColor: string | Color;
/**
* Gets or sets the background image of the view.
*/
backgroundImage: string | LinearGradient;
/**
* Gets or sets the minimum width the view may grow to.
*/
minWidth: Length;
/**
* Gets or sets the minimum height the view may grow to.
*/
minHeight: Length;
/**
* Gets or sets the desired width of the view.
*/
width: PercentLength;
/**
* Gets or sets the desired height of the view.
*/
height: PercentLength;
/**
* Gets or sets margin style property.
*/
margin: string | PercentLength;
/**
* Specifies extra space on the left side of this view.
*/
marginLeft: PercentLength;
/**
* Specifies extra space on the top side of this view.
*/
marginTop: PercentLength;
/**
* Specifies extra space on the right side of this view.
*/
marginRight: PercentLength;
/**
* Specifies extra space on the bottom side of this view.
*/
marginBottom: PercentLength;
/**
* Gets or sets the alignment of this view within its parent along the Horizontal axis.
*/
horizontalAlignment: HorizontalAlignment;
/**
* Gets or sets the alignment of this view within its parent along the Vertical axis.
*/
verticalAlignment: VerticalAlignment;
/**
* Gets or sets the visibility of the view.
*/
visibility: Visibility;
/**
* Gets or sets the opacity style property.
*/
opacity: number;
/**
* Gets or sets the rotate affine transform of the view.
*/
rotate: number;
/**
* Gets or sets the translateX affine transform of the view in device independent pixels.
*/
translateX: dip;
/**
* Gets or sets the translateY affine transform of the view in device independent pixels.
*/
translateY: dip;
/**
* Gets or sets the scaleX affine transform of the view.
*/
scaleX: number;
/**
* Gets or sets the scaleY affine transform of the view.
*/
scaleY: number;
//END Style property shortcuts
/**
* Gets or sets the automation text of the view.
*/
automationText: string;
/**
* Gets or sets the X component of the origin point around which the view will be transformed. The deafault value is 0.5 representing the center of the view.
*/
originX: number;
/**
* Gets or sets the Y component of the origin point around which the view will be transformed. The deafault value is 0.5 representing the center of the view.
*/
originY: number;
/**
* Gets or sets a value indicating whether the the view is enabled. This affects the appearance of the view.
*/
isEnabled: boolean;
/**
* Gets or sets a value indicating whether the user can interact with the view. This does not affect the appearance of the view.
*/
isUserInteractionEnabled: boolean;
/**
* Instruct container view to expand beyond the safe area. This property is iOS specific. Default value: false
*/
iosOverflowSafeArea: boolean;
/**
* Enables or disables the iosOverflowSafeArea property for all children. This property is iOS specific. Default value: true
*/
iosOverflowSafeAreaEnabled: boolean;
/**
* Gets is layout is valid. This is a read-only property.
*/
isLayoutValid: boolean;
/**
* Gets the CSS fully qualified type name.
* Using this as element type should allow for PascalCase and kebap-case selectors, when fully qualified, to match the element.
*/
cssType: string;
cssClasses: Set<string>;
cssPseudoClasses: Set<string>;
/**
* This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters.
* The actual measurement work of a view is performed in onMeasure(int, int), called by this method. Therefore, only onMeasure(int, int) can and must be overridden by subclasses.
* @param widthMeasureSpec Horizontal space requirements as imposed by the parent
* @param heightMeasureSpec Vertical space requirements as imposed by the parent
*/
public measure(widthMeasureSpec: number, heightMeasureSpec: number): void;
/**
* Assign a size and position to a view and all of its descendants
* This is the second phase of the layout mechanism. (The first is measuring). In this phase, each parent calls layout on all of its children to position them. This is typically done using the child measurements that were stored in the measure pass().
* Derived classes should not override this method. Derived classes with children should override onLayout. In that method, they should call layout on each of their children.
* @param l Left position, relative to parent
* @param t Top position, relative to parent
* @param r Right position, relative to parent
* @param b Bottom position, relative to parent
*/
public layout(left: number, top: number, right: number, bottom: number, setFrame?: boolean): void;
/**
* Returns the raw width component.
*/
public getMeasuredWidth(): number;
/**
* Returns the raw height component.
*/
public getMeasuredHeight(): number;
public getMeasuredState(): number;
/**
* Measure the view and its content to determine the measured width and the measured height. This method is invoked by measure(int, int) and should be overriden by subclasses to provide accurate and efficient measurement of their contents.
* When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height of this view. Failure to do so will trigger an exception, thrown by measure(int, int).
* @param widthMeasureSpec horizontal space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.
* @param heightMeasureSpec vertical space requirements as imposed by the parent. The requirements are encoded with View.MeasureSpec.
*/
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void;
/**
* Called from layout when this view should assign a size and position to each of its children. Derived classes with children should override this method and call layout on each of their children.
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
public onLayout(left: number, top: number, right: number, bottom: number): void;
/**
* This method must be called by onMeasure(int, int) to store the measured width and measured height. Failing to do so will trigger an exception at measurement time.
* @param measuredWidth The measured width of this view. May be a complex bit mask as defined by MEASURED_SIZE_MASK and MEASURED_STATE_TOO_SMALL.
* @param measuredHeight The measured height of this view. May be a complex bit mask as defined by MEASURED_SIZE_MASK and MEASURED_STATE_TOO_SMALL.
*/
public setMeasuredDimension(measuredWidth: number, measuredHeight: number): void;
/**
* Called from onLayout when native view position is about to be changed.
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
public layoutNativeView(left: number, top: number, right: number, bottom: number): void;
/**
* Measure a child by taking into account its margins and a given measureSpecs.
* @param parent This parameter is not used. You can pass null.
* @param child The view to be measured.
* @param measuredWidth The measured width that the parent layout specifies for this view.
* @param measuredHeight The measured height that the parent layout specifies for this view.
*/
public static measureChild(parent: View, child: View, widthMeasureSpec: number, heightMeasureSpec: number): { measuredWidth: number; measuredHeight: number };
/**
* Layout a child by taking into account its margins, horizontal and vertical alignments and a given bounds.
* @param parent This parameter is not used. You can pass null.
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
public static layoutChild(parent: View, child: View, left: number, top: number, right: number, bottom: number): void;
/**
* Utility to reconcile a desired size and state, with constraints imposed
* by a MeasureSpec. Will take the desired size, unless a different size
* is imposed by the constraints. The returned value is a compound integer,
* with the resolved size in the MEASURED_SIZE_MASK bits and
* optionally the bit MEASURED_STATE_TOO_SMALL set if the resulting
* size is smaller than the size the view wants to be.
*/
public static resolveSizeAndState(size: number, specSize: number, specMode: number, childMeasuredState: number): number;
public static combineMeasuredStates(curState: number, newState): number;
/**
* Tries to focus the view.
* Returns a value indicating whether this view or one of its descendants actually took focus.
*/
public focus(): boolean;
public getGestureObservers(type: GestureTypes): Array<GesturesObserver>;
/**
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change") or you can use gesture types.
* @param callback - Callback function which will be executed when event is raised.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/
on(eventNames: string | GestureTypes, callback: (args: EventData) => void, thisArg?: any);
/**
* Removes listener(s) for the specified event name.
* @param eventNames Comma delimited names of the events or gesture types the specified listener is associated with.
* @param callback An optional parameter pointing to a specific listener. If not defined, all listeners for the event names will be removed.
* @param thisArg An optional parameter which when set will be used to refine search of the correct callback which will be removed as event listener.
*/
off(eventNames: string | GestureTypes, callback?: (args: EventData) => void, thisArg?: any);
/**
* Raised when a loaded event occurs.
*/
on(event: "loaded", callback: (args: EventData) => void, thisArg?: any);
/**
* Raised when an unloaded event occurs.
*/
on(event: "unloaded", callback: (args: EventData) => void, thisArg?: any);
/**
* Raised when a back button is pressed.
* This event is raised only for android.
*/
on(event: "androidBackPressed", callback: (args: EventData) => void, thisArg?: any);
/**
* Raised before the view is shown as a modal dialog.
*/
on(event: "showingModally", callback: (args: ShownModallyData) => void, thisArg?: any): void;
/**
* Raised after the view is shown as a modal dialog.
*/
on(event: "shownModally", callback: (args: ShownModallyData) => void, thisArg?: any);
/**
* Returns the current modal view that this page is showing (is parent of), if any.
*/
modal: View;
/**
* Animates one or more properties of the view based on the supplied options.
*/
public animate(options: AnimationDefinition): AnimationPromise;
/**
* Creates an Animation object based on the supplied options.
*/
public createAnimation(options: AnimationDefinition): Animation;
/**
* Returns the iOS safe area insets of this view.
*/
public getSafeAreaInsets(): { left, top, right, bottom };
/**
* Returns the location of this view in the window coordinate system.
*/
public getLocationInWindow(): Point;
/**
* Returns the location of this view in the screen coordinate system.
*/
public getLocationOnScreen(): Point;
/**
* Returns the location of this view in the otherView's coordinate system.
*/
public getLocationRelativeTo(otherView: View): Point;
/**
* Returns the actual size of the view in device-independent pixels.
*/
public getActualSize(): Size;
/**
* Derived classes can override this method to handle Android back button press.
*/
onBackPressed(): boolean;
/**
* @private
* A valid css string which will be applied for all nested UI components (based on css rules).
*/
css: string;
/**
* @private
* Adds a new values to current css.
* @param cssString - A valid css which will be added to current css.
*/
addCss(cssString: string): void;
/**
* @private
* Adds the content of the file to the current css.
* @param cssFileName - A valid file name (from the application root) which contains a valid css.
*/
addCssFile(cssFileName: string): void;
/**
* @private
* Changes the current css to the content of the file.
* @param cssFileName - A valid file name (from the application root) which contains a valid css.
*/
changeCssFile(cssFileName: string): void;
// Lifecycle events
_getNativeViewsCount(): number;
/**
* Internal method:
* Closes all modal views. Should be used by plugins like `nativescript-angular` which implement their own `modal views` service.
*/
_closeAllModalViewsInternal(): boolean;
/**
* Internal method:
* Gets all modal views of the current view.
*/
_getRootModalViews(): Array<ViewBase>
_eachLayoutView(callback: (View) => void): void;
/**
* Iterates over children of type View.
* @param callback Called for each child of type View. Iteration stops if this method returns falsy value.
*/
public eachChildView(callback: (view: View) => boolean): void;
//@private
/**
* @private
*/
_modalParent?: View;
/**
* @private
*/
isLayoutRequired: boolean;
/**
* @private
*/
_gestureObservers: any;
/**
* @private
* androidx.fragment.app.FragmentManager
*/
_manager: any;
/**
* @private
*/
_setNativeClipToBounds(): void;
/**
* Called by measure method to cache measureSpecs.
* @private
*/
_setCurrentMeasureSpecs(widthMeasureSpec: number, heightMeasureSpec: number): boolean;
/**
* Called by layout method to cache view bounds.
* @private
*/
_setCurrentLayoutBounds(left: number, top: number, right: number, bottom: number): { boundsChanged: boolean, sizeChanged: boolean };
/**
* Return view bounds.
* @private
*/
_getCurrentLayoutBounds(): { left: number; top: number; right: number; bottom: number };
/**
* @private
*/
_goToVisualState(state: string);
/**
* @private
*/
_setNativeViewFrame(nativeView: any, frame: any): void;
// _onStylePropertyChanged(property: dependencyObservable.Property): void;
/**
* @private
*/
_updateEffectiveLayoutValues(
parentWidthMeasureSize: number,
parentWidthMeasureMode: number,
parentHeightMeasureSize: number,
parentHeightMeasureMode: number): void
/**
* @private
*/
_currentWidthMeasureSpec: number;
/**
* @private
*/
_currentHeightMeasureSpec: number;
/**
* @private
*/
_setMinWidthNative(value: Length): void;
/**
* @private
*/
_setMinHeightNative(value: Length): void;
/**
* @private
*/
_redrawNativeBackground(value: any): void;
/**
* @private
*/
_removeAnimation(animation: Animation): boolean;
/**
* @private
*/
_onLivesync(context?: { type: string, path: string }): boolean;
/**
* @private
*/
_getFragmentManager(): any; /* androidx.fragment.app.FragmentManager */
_handleLivesync(context?: { type: string, path: string }): boolean;
/**
* Updates styleScope or create new styleScope.
* @param cssFileName
* @param cssString
* @param css
*/
_updateStyleScope(cssFileName?: string, cssString?: string, css?: string): void;
/**
* Called in android when native view is attached to window.
*/
_onAttachedToWindow(): void;
/**
* Called in android when native view is dettached from window.
*/
_onDetachedFromWindow(): void;
/**
* Checks whether the current view has specific view for an ancestor.
*/
_hasAncestorView(ancestorView: View): boolean;
//@endprivate
/**
* __Obsolete:__ There is a new property system that does not rely on _getValue.
*/
_getValue(property: any): never;
/**
* __Obsolete:__ There is a new property system that does not rely on _setValue.
*/
_setValue(property: any, value: any): never;
}
/**
* Base class for all UI components that are containers.
*/
export class ContainerView extends View {
/**
* Instruct container view to expand beyond the safe area. This property is iOS specific. Default value: true
*/
public iosOverflowSafeArea: boolean;
}
/**
* Base class for all UI components that implement custom layouts.
*/
export class CustomLayoutView extends ContainerView {
//@private
/**
* @private
*/
_updateNativeLayoutParams(child: View): void;
/**
* @private
*/
_setChildMinWidthNative(child: View): void;
/**
* @private
*/
_setChildMinHeightNative(child: View): void;
//@endprivate
}
/**
* Defines an interface for a View factory function.
* Commonly used to specify the visualization of data objects.
*/
export interface Template {
/**
* Call signature of the factory function.
* Returns a new View instance.
*/
(): View;
}
/**
* Defines an interface for Template with a key.
*/
export interface KeyedTemplate {
/**
* The unique key of the template.
*/
key: string;
/**
* The function that creates the view.
*/
createView: Template;
}
/**
* Defines an interface for adding arrays declared in xml.
*/
export interface AddArrayFromBuilder {
/**
* A function that is called when an array declaration is found in xml.
* @param name - Name of the array.
* @param value - The actual value of the array.
*/
_addArrayFromBuilder(name: string, value: Array<any>): void;
}
/**
* Defines an interface for adding a child element declared in xml.
*/
export interface AddChildFromBuilder {
/**
* Called for every child element declared in xml.
* This method will add a child element (value) to current element.
* @param name - Name of the element.
* @param value - Value of the element.
*/
_addChildFromBuilder(name: string, value: any): void;
}
export const automationTextProperty: Property<View, string>;
export const originXProperty: Property<View, number>;
export const originYProperty: Property<View, number>;
export const isEnabledProperty: Property<View, boolean>;
export const isUserInteractionEnabledProperty: Property<View, boolean>;
export const iosOverflowSafeAreaProperty: Property<View, boolean>;
export const iosOverflowSafeAreaEnabledProperty: InheritedProperty<View, boolean>;
export namespace ios {
/**
* String value used when hooking to traitCollectionColorAppearanceChangedEvent event.
*/
export const traitCollectionColorAppearanceChangedEvent: string;
/**
* Returns a view with viewController or undefined if no such found along the view's parent chain.
* @param view The view form which to start the search.
*/
export function getParentWithViewController(view: View): View
export function updateAutoAdjustScrollInsets(controller: any /* UIViewController */, owner: View): void
export function updateConstraints(controller: any /* UIViewController */, owner: View): void;
export function layoutView(controller: any /* UIViewController */, owner: View): void;
export function getPositionFromFrame(frame: any /* CGRect */): { left, top, right, bottom };
export function getFrameFromPosition(position: { left, top, right, bottom }, insets?: { left, top, right, bottom }): any /* CGRect */;
export function shrinkToSafeArea(view: View, frame: any /* CGRect */): any /* CGRect */;
export function expandBeyondSafeArea(view: View, frame: any /* CGRect */): any /* CGRect */;
export class UILayoutViewController {
public static initWithOwner(owner: WeakRef<View>): UILayoutViewController;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
{
"name": "weak-event-listener",
"main": "weak-event-listener",
"types": "weak-event-listener.d.ts"
}

View File

@@ -0,0 +1,23 @@
/**
* @module "ui/core/weak-event-listener"
*/ /** */
import { Observable, EventData } from "../../../data/observable";
/**
* Attaches a WeakEventListener.
* @param source Observable class which emits the event.
* @param eventName The event name.
* @param handler The function which should be called when event occurs.
* @param target Subscriber (target) of the event listener. It will be used as a thisArg in the handler function.
*/
export function addWeakEventListener(source: Observable, eventName: string, handler: (eventData: EventData) => void, target: any): void;
/**
* Removes a WeakEventListener.
* @param source Observable class which emits the event.
* @param eventName The event name.
* @param handler The function which should be called when event occurs.
* @param target Subscriber (target) of the event listener. It will be used as a thisArg in the handler function.
*/
export function removeWeakEventListener(source: Observable, eventName: string, handler: (eventData: EventData) => void, target: any): void;

View File

@@ -0,0 +1,151 @@
import { Observable, EventData } from "../../../data/observable";
const handlersForEventName = new Map<string, (eventData: EventData) => void>();
const sourcesMap = new WeakMap<Observable, Map<string, Array<TargetHandlerPair>>>();
class TargetHandlerPair {
tagetRef: WeakRef<Object>;
handler: (eventData: EventData) => void;
constructor(target: Object, handler: (eventData: EventData) => void) {
this.tagetRef = new WeakRef(target);
this.handler = handler;
}
}
function getHandlerForEventName(eventName: string): (eventData: EventData) => void {
let handler = handlersForEventName.get(eventName);
if (!handler) {
handler = function (eventData: EventData) {
const source = eventData.object;
const sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
// There is no event map for this source - it is safe to detach the listener;
source.removeEventListener(eventName, handlersForEventName.get(eventName));
return;
}
const targetHandlerPairList = sourceEventMap.get(eventName);
if (!targetHandlerPairList) {
return;
}
const deadPairsIndexes = [];
let pair;
let target;
for (let i = 0; i < targetHandlerPairList.length; i++) {
pair = targetHandlerPairList[i];
target = pair.tagetRef.get();
if (target) {
pair.handler.call(target, eventData);
}
else {
deadPairsIndexes.push(i);
}
}
if (deadPairsIndexes.length === targetHandlerPairList.length) {
// There are no alive targets for this event - unsubscribe
source.removeEventListener(eventName, handlersForEventName.get(eventName));
sourceEventMap.delete(eventName);
}
else {
for (let j = deadPairsIndexes.length - 1; j >= 0; j--) {
targetHandlerPairList.splice(deadPairsIndexes[j], 1);
}
}
};
handlersForEventName.set(eventName, handler);
}
return handler;
}
function validateArgs(source: Observable, eventName: string, handler: (eventData: EventData) => void, target: any) {
if (!source) {
throw new Error("source is null or undefined");
}
if (!target) {
throw new Error("target is null or undefined");
}
if (typeof eventName !== "string") {
throw new Error("eventName is not a string");
}
if (typeof handler !== "function") {
throw new Error("handler is not a function");
}
}
export function addWeakEventListener(source: Observable, eventName: string, handler: (eventData: EventData) => void, target: any) {
validateArgs(source, eventName, handler, target);
let shouldAttach: boolean = false;
let sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
sourceEventMap = new Map<string, Array<TargetHandlerPair>>();
sourcesMap.set(source, sourceEventMap);
shouldAttach = true;
}
let pairList = sourceEventMap.get(eventName);
if (!pairList) {
pairList = new Array<TargetHandlerPair>();
sourceEventMap.set(eventName, pairList);
shouldAttach = true;
}
pairList.push(new TargetHandlerPair(target, handler));
if (shouldAttach) {
source.addEventListener(eventName, getHandlerForEventName(eventName));
}
}
export function removeWeakEventListener(source: Observable, eventName: string, handler: (eventData: EventData) => void, target: any) {
validateArgs(source, eventName, handler, target);
const handlerForEventWithName = handlersForEventName.get(eventName);
if (!handlerForEventWithName) {
// We have never created handler for event with this name;
return;
}
const sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
return;
}
const targetHandlerPairList = sourceEventMap.get(eventName);
if (!targetHandlerPairList) {
return;
}
// Remove all pairs that match given target and handler or have a dead target
const targetHandlerPairsToRemove = [];
let pair;
let registeredTarget;
for (let i = 0; i < targetHandlerPairList.length; i++) {
pair = targetHandlerPairList[i];
registeredTarget = pair.tagetRef.get();
if (!registeredTarget || (registeredTarget === target && handler === pair.handler)) {
targetHandlerPairsToRemove.push(i);
}
}
if (targetHandlerPairsToRemove.length === targetHandlerPairList.length) {
// There are no alive targets for this event - unsubscribe
source.removeEventListener(eventName, handlerForEventWithName);
sourceEventMap.delete(eventName);
}
else {
for (let j = targetHandlerPairsToRemove.length - 1; j >= 0; j--) {
targetHandlerPairList.splice(targetHandlerPairsToRemove[j], 1);
}
}
}