diff --git a/CodingConvention.md b/CodingConvention.md index 826e49bd6..dc7cea973 100644 --- a/CodingConvention.md +++ b/CodingConvention.md @@ -205,7 +205,7 @@ let b = {"good": "code" ## Equality operator -Use the [strict comparison operators][comparisonoperators]. The triple equality operator helps to maintain data type integrity throughout code. +Use the [strict comparison operators][comparisonoperators]. The triple equality operator helps to maintain data type integrity throughout the code. *Right:* @@ -341,8 +341,8 @@ the last row of a big room can comfortably read. So don't count on them having perfect vision and limit yourself to 1/2 of your screen height per function (no screen rotation :). ## Return statements -There are few important considerations here: -+ To avoid deep nesting of if-statements, always return a functions value as early +There are a few important considerations here: ++ To avoid deep nesting of if-statements, always return a function's value as early as possible. In certain routines, once you know the answer, you want to return it to the calling routine immediately. If the routine is defined in such a way that it doesn't require any cleanup, not returning immediately means that you have to write more code. + Minimize the number of returns in each routine. It's harder to understand a routine if, reading it at the bottom, you're unaware of the possibility that it *return*ed somewhere above. @@ -396,7 +396,7 @@ function getSomething(val) { ## Arrow Functions -Use arrow functions over anonymous function expressions. Typescript will take care for `this`. +Use arrow functions over anonymous function expressions. Typescript will take care of `this`. *Right:* @@ -423,7 +423,7 @@ req.on("end", function () { Use the [JSDoc][JSDOC] convention for comments. When writing a comment always think how understandable will be for somebody who is new to this code. Even if it may look simple to you think how a guy that just joined will understand it. Always comment in the following cases: + When there is some non-trivial logic. -+ Some "external" knowledge is needed which is missing in the context - workaround for driver, module bug, special 'hack' because of a bug and so on; ++ Some "external" knowledge is needed which is missing in the context - workaround for a driver, module bug, special 'hack' because of a bug and so on; + When you are creating a new class + Public methods - include all the arguments and if possible the types {String}, {Number}. Optional arguments should be marked too. Check the [@param tag][param] @@ -432,7 +432,7 @@ Use the [JSDoc][JSDOC] convention for comments. When writing a comment always th ## File/module structure -Typical module should have the following structure: +A typical module should have the following structure: 1. required dependencies 2. module-private declarations - variables, functions, classes, etc. @@ -442,7 +442,7 @@ Typical module should have the following structure: For more information see [this file](https://github.com/telerik/xPlatCore/blob/master/JS/BCL/CreateNewModule.md) ## File naming -Use lower case for file names. Use dash to separate different words. +Use lower case for file names. Use a dash to separate different words. *Right:* file-system @@ -451,7 +451,7 @@ file-system FileSystem, fileSystem, file_system ## This, that, self -When you **need** to keep reference to **this** use **that** as the name of the variable. Additionally, if you use the TypeScript lambda support, the compiler will take care of this automatically. +When you **need** to keep a reference to **this** use **that** as the name of the variable. Additionally, if you use the TypeScript lambda support, the compiler will take care of this automatically. *Right:* ```TypeScript @@ -470,7 +470,7 @@ doSomething(function(){ ``` ## Private (hidden) variables and methods -Although there is the **private** keyword in TypeScript, it is only a syntax sugar. There is no such notation in JavaScript and everything is available to the users. Hence, always use underscore (**_**) to prefix private variables and methods. There are also methods which have the **public** visibility but they are meant to be used within our code ONLY. Such methods should also be prefixed with underscore. +Although there is the **private** keyword in TypeScript, it is only a syntax sugar. There is no such notation in JavaScript and everything is available to the users. Hence, always use underscore (**_**) to prefix private variables and methods. There are also methods which have the **public** visibility but they are meant to be used within our code ONLY. Such methods should also be prefixed with an underscore. *Right:* ```TypeScript diff --git a/WritingUnitTests.md b/WritingUnitTests.md index e2d791692..a6945bc1c 100644 --- a/WritingUnitTests.md +++ b/WritingUnitTests.md @@ -1,7 +1,7 @@ # Writing Unit Tests for NativeScript Core Modules Unit tests for NativeScript Modules are written and executed with a custom lightweight test-runner and assertion framework. -The purpose of this document is to get you familiar with it, so that you can unit-test your contributions to the NativeScript framework. +The purpose of this document is to get you familiar with it so that you can unit-test your contributions to the NativeScript framework. # Run Unit Tests Project @@ -19,7 +19,7 @@ tns run ios # Test Modules All unit tests are organized into test modules(bundles). -By default the test app will run all the tests from all registered test modules. This happens in [`runTests()`](/tests/app/app/mainPage.ts#L26-L28) method in the main page of the test-app. By modifying this method, you can configure the app to: +By default, the test app will run all the tests from all registered test modules. This happens in [`runTests()`](/tests/app/app/mainPage.ts#L26-L28) method in the main page of the test-app. By modifying this method, you can configure the app to: * **Execute only the tests from a specific test module**: @@ -50,8 +50,8 @@ The test modules are actually TypeScript modules which export unit tests and hoo * All exported functions with a `test` prefix are unit-tests. * The `setUpModule()` hook is called once - before all the tests in the module. -* The `setUp()` hook is called before each tests. -* The `tearDown()` hook called after each tests. +* The `setUp()` hook is called before each test. +* The `tearDown()` hook called after each test. * The `tearDownModule()` hook is called once - after all the tests in the module. # Asserting @@ -85,4 +85,4 @@ export function test_getJSON(done) { # Misc -When looking into the code of the existing tests, you might encounter strange comments looking like this `// >> animation-chaining`. These are markers for code snippets generated in the docs documetation. They are not related to testing so you don't need to add any of those in your tests. +When looking into the code of the existing tests, you might encounter strange comments looking like this `// >> animation-chaining`. These are markers for code snippets generated in the docs documentation. They are not related to testing so you don't need to add any of those in your tests. diff --git a/release-contribution-guide-schema.png b/release-contribution-guide-schema.png index 9ce519055..625cc83c2 100644 Binary files a/release-contribution-guide-schema.png and b/release-contribution-guide-schema.png differ diff --git a/tests/app/ui/styling/style-tests.ts b/tests/app/ui/styling/style-tests.ts index e9dc498d2..ffe9873ca 100644 --- a/tests/app/ui/styling/style-tests.ts +++ b/tests/app/ui/styling/style-tests.ts @@ -674,6 +674,23 @@ export function test_CSS_isAppliedOnPage_From_addCssFile() { }); } +export function test_CSS_isAppliedOnPage_From_changeCssFile() { + const testButton = new buttonModule.Button(); + testButton.text = "Test"; + + const testCss = "button { color: blue; }"; + + const testFunc = function (views: Array) { + helper.assertViewColor(testButton, "#0000FF"); + const page: pageModule.Page = views[1]; + page.changeCssFile("~/ui/styling/test.css"); + helper.assertViewBackgroundColor(page, "#FF0000"); + TKUnit.assert(testButton.style.color === undefined, "Color should not have a value"); + } + + helper.buildUIAndRunTest(testButton, testFunc, { pageCss: testCss }); +} + const invalidCSS = ".invalid { " + "color: invalidValue; " + "background-color: invalidValue; " + diff --git a/tns-core-modules/application/application-common.ts b/tns-core-modules/application/application-common.ts index f113902d8..b585578fa 100644 --- a/tns-core-modules/application/application-common.ts +++ b/tns-core-modules/application/application-common.ts @@ -39,7 +39,7 @@ import { iOSApplication, LoadAppCSSEventData, UnhandledErrorEventData, - DiscardedErrorEventData + DiscardedErrorEventData, } from "./application"; export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData }; @@ -82,19 +82,23 @@ export function setApplication(instance: iOSApplication | AndroidApplication): v export function livesync(rootView: View, context?: ModuleContext) { events.notify({ eventName: "livesync", object: app }); const liveSyncCore = global.__onLiveSyncCore; - let reapplyAppCss = false; + let reapplyAppStyles = false; + let reapplyLocalStyles = false; - if (context) { - const fullFileName = getCssFileName(); - const fileName = fullFileName.substring(0, fullFileName.lastIndexOf(".") + 1); + if (context && context.path) { const extensions = ["css", "scss"]; - reapplyAppCss = extensions.some(ext => context.path === fileName.concat(ext)); + const appStylesFullFileName = getCssFileName(); + const appStylesFileName = appStylesFullFileName.substring(0, appStylesFullFileName.lastIndexOf(".") + 1); + reapplyAppStyles = extensions.some(ext => context.path === appStylesFileName.concat(ext)); + if (!reapplyAppStyles) { + reapplyLocalStyles = extensions.some(ext => context.path.endsWith(ext)); + } } - if (reapplyAppCss && rootView) { + if (reapplyAppStyles && rootView) { rootView._onCssStateChange(); } else if (liveSyncCore) { - liveSyncCore(); + reapplyLocalStyles ? liveSyncCore(context) : liveSyncCore(); } } diff --git a/tns-core-modules/application/application.d.ts b/tns-core-modules/application/application.d.ts index 582a19786..16f45599e 100644 --- a/tns-core-modules/application/application.d.ts +++ b/tns-core-modules/application/application.d.ts @@ -3,7 +3,9 @@ * @module "application" */ /** */ -/// Include global typings +/// +/// + import { NavigationEntry, View, Observable, EventData } from "../ui/frame"; /** @@ -607,4 +609,4 @@ export function hasLaunched(): boolean; export interface LoadAppCSSEventData extends EventData { cssFile: string; -} \ No newline at end of file +} diff --git a/tns-core-modules/application/application.ios.ts b/tns-core-modules/application/application.ios.ts index 7362023a6..6601904a0 100644 --- a/tns-core-modules/application/application.ios.ts +++ b/tns-core-modules/application/application.ios.ts @@ -225,9 +225,9 @@ class IOSApplication implements IOSApplicationDefinition { } } - public _onLivesync(): void { + public _onLivesync(context?: ModuleContext): void { // If view can't handle livesync set window controller. - if (this._rootView && !this._rootView._onLivesync()) { + if (this._rootView && !this._rootView._onLivesync(context)) { this.setWindowContent(); } } @@ -264,8 +264,8 @@ exports.ios = iosApp; setApplication(iosApp); // attach on global, so it can be overwritten in NativeScript Angular -(global).__onLiveSyncCore = function () { - iosApp._onLivesync(); +(global).__onLiveSyncCore = function (context?: ModuleContext) { + iosApp._onLivesync(context); } let mainEntry: NavigationEntry; diff --git a/tns-core-modules/debugger/webinspector-css.ios.ts b/tns-core-modules/debugger/webinspector-css.ios.ts index e248a6d04..8e7c353f4 100644 --- a/tns-core-modules/debugger/webinspector-css.ios.ts +++ b/tns-core-modules/debugger/webinspector-css.ios.ts @@ -18,6 +18,10 @@ export class CSSDomainDebugger implements inspectorCommandTypes.CSSDomain.CSSDom this.commands = {}; attachCSSInspectorCommandCallbacks(this.commands); + + // By default start enabled because we can miss the "enable" event when + // running with `--debug-brk` -- the frontend will send it before we've been created + this.enable(); } get enabled(): boolean { diff --git a/tns-core-modules/debugger/webinspector-dom.ios.ts b/tns-core-modules/debugger/webinspector-dom.ios.ts index 038147be4..54f934e34 100644 --- a/tns-core-modules/debugger/webinspector-dom.ios.ts +++ b/tns-core-modules/debugger/webinspector-dom.ios.ts @@ -20,6 +20,10 @@ export class DOMDomainDebugger implements inspectorCommandTypes.DOMDomain.DOMDom attachDOMInspectorEventCallbacks(this.events); attachDOMInspectorCommandCallbacks(this.commands); + + // By default start enabled because we can miss the "enable event when + // running with `--debug-brk` -- the frontend will send it before we've been created + this.enable(); } get enabled(): boolean { diff --git a/tns-core-modules/debugger/webinspector-network.ios.ts b/tns-core-modules/debugger/webinspector-network.ios.ts index 769dbf402..bce24b9c7 100644 --- a/tns-core-modules/debugger/webinspector-network.ios.ts +++ b/tns-core-modules/debugger/webinspector-network.ios.ts @@ -64,7 +64,7 @@ export class Request { this._resourceType = "Other"; return; } - + this._mimeType = value; var resourceType = "Other"; @@ -112,19 +112,19 @@ export class Request { this._resourceType = value; } } - + public responseReceived(response: inspectorCommandTypes.NetworkDomain.Response): void { if (this._networkDomainDebugger.enabled) { this._networkDomainDebugger.events.responseReceived(this.requestID, frameId, loaderId, __inspectorTimestamp(), this.resourceType, response); } } - + public loadingFinished(): void { if (this._networkDomainDebugger.enabled) { this._networkDomainDebugger.events.loadingFinished(this.requestID, __inspectorTimestamp()); } } - + public requestWillBeSent(request: inspectorCommandTypes.NetworkDomain.Request): void { if (this._networkDomainDebugger.enabled) { this._networkDomainDebugger.events.requestWillBeSent(this.requestID, frameId, loaderId, request.url, request, __inspectorTimestamp(), { type: "Script" }); @@ -136,9 +136,13 @@ export class Request { export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomain.NetworkDomainDispatcher { private _enabled: boolean; public events: inspectorCommandTypes.NetworkDomain.NetworkFrontend; - + constructor() { this.events = new inspectorCommands.NetworkDomain.NetworkFrontend(); + + // By default start enabled because we can miss the "enable" event when + // running with `--debug-brk` -- the frontend will send it before we've been created + this.enable(); } get enabled(): boolean { @@ -156,7 +160,7 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai } this._enabled = true; } - + /** * Disables network tracking, prevents network events from being sent to the client. */ @@ -166,14 +170,14 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai } this._enabled = false; } - + /** * Specifies whether to always send extra HTTP headers with the requests from this page. */ setExtraHTTPHeaders(params: inspectorCommandTypes.NetworkDomain.SetExtraHTTPHeadersMethodArguments): void { // } - + /** * Returns content served for the given request. */ @@ -187,9 +191,9 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai body: body, base64Encoded: !resource_data.hasTextContent }; - } + } } - + /** * Tells whether clearing browser cache is supported. */ @@ -198,14 +202,14 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai result: false }; } - + /** * Clears browser cache. */ clearBrowserCache(): void { // } - + /** * Tells whether clearing browser cookies is supported. */ @@ -214,21 +218,21 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai result: false }; } - + /** * Clears browser cookies. */ clearBrowserCookies(): void { // } - + /** * Toggles ignoring cache for each request. If true, cache will not be used. */ setCacheDisabled(params: inspectorCommandTypes.NetworkDomain.SetCacheDisabledMethodArguments): void { // } - + /** * Loads a resource in the context of a frame on the inspected page without cross origin checks. */ @@ -245,7 +249,7 @@ export class NetworkDomainDebugger implements inspectorCommandTypes.NetworkDomai status: 200 } } - + public static idSequence: number = 0; create(): Request { let id = (++NetworkDomainDebugger.idSequence).toString(); @@ -264,4 +268,4 @@ export class RuntimeDomainDebugger { compileScript(): { scriptId?: string, exceptionDetails?: Object } { return {}; } -} \ No newline at end of file +} diff --git a/tns-core-modules/module.d.ts b/tns-core-modules/module.d.ts index 76f015c56..573831c1b 100644 --- a/tns-core-modules/module.d.ts +++ b/tns-core-modules/module.d.ts @@ -1,3 +1,4 @@ +/// declare var global: NodeJS.Global; interface ModuleResolver { @@ -52,7 +53,7 @@ declare namespace NodeJS { __inspector?: any; __extends: any; __onLiveSync: (context?: { type: string, path: string }) => void; - __onLiveSyncCore: () => void; + __onLiveSyncCore: (context?: { type: string, path: string }) => void; __onUncaughtError: (error: NativeScriptError) => void; __onDiscardedError: (error: NativeScriptError) => void; TNS_WEBPACK?: boolean; @@ -86,16 +87,6 @@ interface ModuleContext { path: string; } -/** - * An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code. - */ -interface NativeScriptError extends Error { - /** - * Represents the native error object. - */ - nativeError: any; -} - // Define a minimal subset of NodeRequire and NodeModule so user apps can compile without // installing @types/node diff --git a/tns-core-modules/nativescript-error.d.ts b/tns-core-modules/nativescript-error.d.ts new file mode 100644 index 000000000..0373f06a2 --- /dev/null +++ b/tns-core-modules/nativescript-error.d.ts @@ -0,0 +1,9 @@ +/** + * An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code. + */ +declare interface NativeScriptError extends Error { + /** + * Represents the native error object. + */ + nativeError: any; +} diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json index c04928cce..642d7903d 100644 --- a/tns-core-modules/package.json +++ b/tns-core-modules/package.json @@ -1,7 +1,7 @@ { "name": "tns-core-modules", "description": "Telerik NativeScript Core Modules", - "version": "5.2.2", + "version": "5.3.0", "homepage": "https://www.nativescript.org", "repository": { "type": "git", @@ -26,7 +26,7 @@ "license": "Apache-2.0", "typings": "tns-core-modules.d.ts", "dependencies": { - "tns-core-modules-widgets": "5.2.0", + "tns-core-modules-widgets": "next", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/tns-core-modules/tns-core-modules.d.ts b/tns-core-modules/tns-core-modules.d.ts index 495b0ee5b..1a9ec6d30 100644 --- a/tns-core-modules/tns-core-modules.d.ts +++ b/tns-core-modules/tns-core-modules.d.ts @@ -22,12 +22,15 @@ declare var console: Console; declare var require: NodeRequire; // Extend NodeRequire with the webpack's require context extension. +interface RequireContext { + keys(): string[]; + (id: string): any; + (id: string): T; + resolve(id: string): string; +} + interface NodeRequire { - context(root: string, recursive: boolean, filter: RegExp): { - (module: string): any; - id: number; - keys(): string[]; - } + context(path: string, deep?: boolean, filter?: RegExp): RequireContext; } declare var __dirname: string; diff --git a/tns-core-modules/ui/core/view/view-common.ts b/tns-core-modules/ui/core/view/view-common.ts index b52989865..f12fabdc3 100644 --- a/tns-core-modules/ui/core/view/view-common.ts +++ b/tns-core-modules/ui/core/view/view-common.ts @@ -105,6 +105,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition { this._updateStyleScope(cssFileName); } + public changeCssFile(cssFileName: string): void { + const scope = this._styleScope; + if (scope && cssFileName) { + scope.changeCssFile(cssFileName); + this._onCssStateChange(); + } + } + public _updateStyleScope(cssFileName?: string, cssString?: string, css?: string): void { let scope = this._styleScope; if (!scope) { diff --git a/tns-core-modules/ui/core/view/view.d.ts b/tns-core-modules/ui/core/view/view.d.ts index d638efc1e..04a310522 100644 --- a/tns-core-modules/ui/core/view/view.d.ts +++ b/tns-core-modules/ui/core/view/view.d.ts @@ -2,7 +2,8 @@ * @module "ui/core/view" */ /** */ -/// Include global typings +/// + 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"; @@ -18,14 +19,14 @@ 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. */ @@ -50,8 +51,8 @@ export type px = number; 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. + * 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 { /** @@ -66,8 +67,8 @@ export interface Point { } /** - * 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. + * 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 { /** @@ -99,8 +100,8 @@ export interface ShownModallyData extends EventData { } /** - * 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. + * 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 { /** @@ -475,13 +476,13 @@ export abstract class View extends ViewBase { * [Deprecated. Please use the on() instead.] Adds a gesture observer. * @param type - Type of the gesture. * @param callback - A function that will be executed when gesture is received. - * @param thisArg - An optional parameter which will be used as `this` context for callback execution. + * @param thisArg - An optional parameter which will be used as `this` context for callback execution. */ observe(type: GestureTypes, callback: (args: GestureEventData) => void, thisArg?: any); /** * 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 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. */ @@ -527,12 +528,12 @@ export abstract class View extends ViewBase { modal: View; /** - * Animates one or more properties of the view based on the supplied options. + * 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. + * Creates an Animation object based on the supplied options. */ public createAnimation(options: AnimationDefinition): Animation; @@ -562,7 +563,7 @@ export abstract class View extends ViewBase { public getActualSize(): Size; /** - * Derived classes can override this method to handle Android back button press. + * Derived classes can override this method to handle Android back button press. */ onBackPressed(): boolean; @@ -575,7 +576,7 @@ export abstract class View extends ViewBase { /** * @private * Adds a new values to current css. - * @param cssString - A valid css which will be added to current css. + * @param cssString - A valid css which will be added to current css. */ addCss(cssString: string): void; @@ -586,13 +587,20 @@ export abstract class View extends ViewBase { */ 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; _eachLayoutView(callback: (View) => void): void; - + /** - * Iterates over children of type View. + * 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; @@ -673,7 +681,7 @@ export abstract class View extends ViewBase { /** * @private */ - _onLivesync(): boolean; + _onLivesync(context?: { type: string, path: string }): boolean; /** * @private */ @@ -681,9 +689,9 @@ export abstract class View extends ViewBase { /** * Updates styleScope or create new styleScope. - * @param cssFileName - * @param cssString - * @param css + * @param cssFileName + * @param cssString + * @param css */ _updateStyleScope(cssFileName?: string, cssString?: string, css?: string): void; @@ -715,7 +723,7 @@ export abstract class View extends ViewBase { } /** - * Base class for all UI components that are containers. + * Base class for all UI components that are containers. */ export class ContainerView extends View { /** @@ -725,7 +733,7 @@ export class ContainerView extends View { } /** - * Base class for all UI components that implement custom layouts. + * Base class for all UI components that implement custom layouts. */ export class CustomLayoutView extends ContainerView { //@private diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index a92093ee8..c9f47b205 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -76,7 +76,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { if (backstackIndex !== -1) { backstack = backstackIndex; } else { - // NOTE: We don't search for entries in navigationQueue because there is no way for + // NOTE: We don't search for entries in navigationQueue because there is no way for // developer to get reference to BackstackEntry unless transition is completed. // At that point the entry is put in the backstack array. // If we start to return Backstack entry from navigate method then @@ -153,7 +153,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { // } // let currentPage = this._currentEntry.resolvedPage; - // let currentNavigationEntry = this._currentEntry.entry; + // let currentNavigationEntry = this._currentEntry.entry; // if (currentPage["isBiOrientational"] && currentNavigationEntry.moduleName) { // if (this.canGoBack()){ // this.goBack(); @@ -162,7 +162,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { // currentNavigationEntry.backstackVisible = false; // } // // Re-navigate to the same page so the other (.port or .land) xml is loaded. - // this.navigate(currentNavigationEntry); + // this.navigate(currentNavigationEntry); // } // } @@ -224,7 +224,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { newPage.onNavigatedTo(isBack); // Reset executing entry after NavigatedTo is raised; - // we do not want to execute two navigations in parallel in case + // we do not want to execute two navigations in parallel in case // additional navigation is triggered from the NavigatedTo handler. this._executingEntry = null; } @@ -259,7 +259,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { return true; } } - + return false; } @@ -563,7 +563,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { return result; } - public _onLivesync(): boolean { + public _onLivesync(context?: ModuleContext): boolean { super._onLivesync(); if (!this._currentEntry || !this._currentEntry.entry) { @@ -571,6 +571,17 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { } const currentEntry = this._currentEntry.entry; + if (context && context.path) { + // Use topmost instead of this to cover nested frames scenario + const topmostFrame = topmost(); + const moduleName = topmostFrame.currentEntry.moduleName; + const reapplyStyles = context.path.includes(moduleName); + if (reapplyStyles && moduleName) { + topmostFrame.currentPage.changeCssFile(context.path); + return true; + } + } + const newEntry: NavigationEntry = { animated: false, clearHistory: true, diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index fdde423ab..387c9b193 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -82,13 +82,13 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener { return attachStateChangeListener; } -export function reloadPage(): void { +export function reloadPage(context?: ModuleContext): void { const activity = application.android.foregroundActivity; const callbacks: AndroidActivityCallbacks = activity[CALLBACKS]; if (callbacks) { const rootView: View = callbacks.getRootView(); - if (!rootView || !rootView._onLivesync()) { + if (!rootView || !rootView._onLivesync(context)) { callbacks.resetActivityContent(activity); } } else { @@ -153,7 +153,7 @@ export class Frame extends FrameBase { // In case activity was destroyed because of back button pressed (e.g. app exit) // and application is restored from recent apps, current fragment isn't recreated. // In this case call _navigateCore in order to recreate the current fragment. - // Don't call navigate because it will fire navigation events. + // Don't call navigate because it will fire navigation events. // As JS instances are alive it is already done for the current page. if (!this.isLoaded || this._executingEntry || !this._attachedToWindow) { return; @@ -183,13 +183,13 @@ export class Frame extends FrameBase { const entry = this._currentEntry; if (entry && manager && !manager.findFragmentByTag(entry.fragmentTag)) { // Simulate first navigation (e.g. no animations or transitions) - // we need to cache the original animation settings so we can restore them later; otherwise as the - // simulated first navigation is not animated (it is actually a zero duration animator) the "popExit" animation + // we need to cache the original animation settings so we can restore them later; otherwise as the + // simulated first navigation is not animated (it is actually a zero duration animator) the "popExit" animation // is broken when transaction.setCustomAnimations(...) is used in a scenario with: // 1) forward navigation // 2) suspend / resume app - // 3) back navigation -- the exiting fragment is erroneously animated with the exit animator from the - // simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears; + // 3) back navigation -- the exiting fragment is erroneously animated with the exit animator from the + // simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears; // the user only sees the animation of the entering fragment as per its specific enter animation settings. // NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously this._cachedAnimatorState = getAnimatorState(this._currentEntry); @@ -727,7 +727,7 @@ function ensureFragmentClass() { return; } - // this require will apply the FragmentClass implementation + // this require will apply the FragmentClass implementation require("ui/frame/fragment"); if (!fragmentClass) { @@ -855,11 +855,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { entry.viewSavedState = null; } - // fixes 'java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first'. + // fixes 'java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first'. // on app resume in nested frame scenarios with support library version greater than 26.0.0 - // HACK: this whole code block shouldn't be necessary as the native view is supposedly removed from its parent - // right after onDestroyView(...) is called but for some reason the fragment view (page) still thinks it has a - // parent while its supposed parent believes it properly removed its children; in order to "force" the child to + // HACK: this whole code block shouldn't be necessary as the native view is supposedly removed from its parent + // right after onDestroyView(...) is called but for some reason the fragment view (page) still thinks it has a + // parent while its supposed parent believes it properly removed its children; in order to "force" the child to // lose its parent we temporarily add it to the parent, and then remove it (addViewInLayout doesn't trigger layout pass) const nativeView = page.nativeViewProtected; if (nativeView != null) { @@ -908,8 +908,8 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { } // [nested frames / fragments] see https://github.com/NativeScript/NativeScript/issues/6629 - // retaining reference to a destroyed fragment here somehow causes a cryptic - // "IllegalStateException: Failure saving state: active fragment has cleared index: -1" + // retaining reference to a destroyed fragment here somehow causes a cryptic + // "IllegalStateException: Failure saving state: active fragment has cleared index: -1" // in a specific mixed parent / nested frame navigation scenario entry.fragment = null; @@ -1019,15 +1019,15 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { } // NOTE: activity.onPostResume() is called when activity resume is complete and we can - // safely raise the application resume event; + // safely raise the application resume event; // onActivityResumed(...) lifecycle callback registered in application is called too early - // and raising the application resume event there causes issues like + // and raising the application resume event there causes issues like // https://github.com/NativeScript/NativeScript/issues/6708 if ((activity).isNativeScriptActivity) { const args = { eventName: application.resumeEvent, - object: application.android, - android: activity + object: application.android, + android: activity }; application.notify(args); application.android.paused = false; @@ -1151,7 +1151,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { // Paths that go trough this method: // 1. Application initial start - there is no rootView in callbacks. - // 2. Application revived after Activity is destroyed. this._rootView should have been restored by id in onCreate. + // 2. Application revived after Activity is destroyed. this._rootView should have been restored by id in onCreate. // 3. Livesync if rootView has no custom _onLivesync. this._rootView should have been cleared upfront. Launch event should not fired // 4. _resetRootView method. this._rootView should have been cleared upfront. Launch event should not fired private setActivityContent( diff --git a/tns-core-modules/ui/page/page.d.ts b/tns-core-modules/ui/page/page.d.ts index a9ace3677..e8f973e59 100644 --- a/tns-core-modules/ui/page/page.d.ts +++ b/tns-core-modules/ui/page/page.d.ts @@ -3,7 +3,8 @@ * @module "ui/page" */ /** */ -/// Include global typings +/// + import { ShownModallyData } from "../core/view"; import { ContentView, EventData, Property, Color, CssProperty, Style } from "../content-view"; import { Frame } from "../frame"; @@ -106,7 +107,7 @@ export class Page extends ContentView { /** * 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"). + * @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change"). * @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. */ @@ -198,4 +199,4 @@ export const statusBarStyleProperty: CssProperty; /** * Property backing androidStatusBarBackground. */ -export const androidStatusBarBackgroundProperty: CssProperty; \ No newline at end of file +export const androidStatusBarBackgroundProperty: CssProperty; diff --git a/tns-core-modules/ui/styling/style-scope.d.ts b/tns-core-modules/ui/styling/style-scope.d.ts index b5bf01684..eff3fe292 100644 --- a/tns-core-modules/ui/styling/style-scope.d.ts +++ b/tns-core-modules/ui/styling/style-scope.d.ts @@ -29,6 +29,7 @@ export class CssState { export class StyleScope { public css: string; public addCss(cssString: string, cssFileName: string): void; + public changeCssFile(cssFileName: string): void; public static createSelectorsFromCss(css: string, cssFileName: string, keyframes: Object): RuleSet[]; public static createSelectorsFromImports(tree: SyntaxTree, keyframes: Object): RuleSet[]; diff --git a/tns-core-modules/ui/styling/style-scope.ts b/tns-core-modules/ui/styling/style-scope.ts index a643f5a93..3c0823f41 100644 --- a/tns-core-modules/ui/styling/style-scope.ts +++ b/tns-core-modules/ui/styling/style-scope.ts @@ -569,6 +569,19 @@ export class StyleScope { this.appendCss(null, cssFileName); } + @profile + private changeCssFile(cssFileName: string): void { + if (!cssFileName) { + return; + } + + const cssSelectors = CSSSource.fromURI(cssFileName, this._keyframes); + this._css = cssSelectors.source; + this._localCssSelectors = cssSelectors.selectors; + this._localCssSelectorVersion++; + this.ensureSelectors(); + } + @profile private setCss(cssString: string, cssFileName?): void { this._css = cssString; diff --git a/tns-core-modules/ui/tab-view/tab-view.android.ts b/tns-core-modules/ui/tab-view/tab-view.android.ts index fb214f74e..f4cf6b789 100644 --- a/tns-core-modules/ui/tab-view/tab-view.android.ts +++ b/tns-core-modules/ui/tab-view/tab-view.android.ts @@ -643,11 +643,13 @@ export class TabView extends TabViewBase { } [selectedIndexProperty.setNative](value: number) { + const smoothScroll = this.androidTabsPosition === "top"; + if (traceEnabled()) { - traceWrite("TabView this._viewPager.setCurrentItem(" + value + ", true);", traceCategory); + traceWrite("TabView this._viewPager.setCurrentItem(" + value + ", " + smoothScroll + ");", traceCategory); } - this._viewPager.setCurrentItem(value, true); + this._viewPager.setCurrentItem(value, smoothScroll); } [itemsProperty.getDefault](): TabViewItem[] { diff --git a/tns-core-modules/ui/web-view/web-view.ios.ts b/tns-core-modules/ui/web-view/web-view.ios.ts index 67425f475..b350905a9 100644 --- a/tns-core-modules/ui/web-view/web-view.ios.ts +++ b/tns-core-modules/ui/web-view/web-view.ios.ts @@ -131,7 +131,8 @@ export class WebView extends WebViewBase { public _loadUrl(src: string) { if (src.startsWith("file:///")) { - this.ios.loadFileURLAllowingReadAccessToURL(NSURL.URLWithString(src), NSURL.URLWithString(src)); + var cachePath = src.substring(0, src.lastIndexOf("/")); + this.ios.loadFileURLAllowingReadAccessToURL(NSURL.URLWithString(src), NSURL.URLWithString(cachePath)); } else { this.ios.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(src))); } @@ -160,4 +161,4 @@ export class WebView extends WebViewBase { public reload() { this.ios.reload(); } -} \ No newline at end of file +} diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index 9daf6ddfb..e22938abe 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -1,6 +1,6 @@ { "name": "tns-platform-declarations", - "version": "5.2.2", + "version": "5.3.0", "description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects", "main": "", "scripts": {