mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(HMR): apply changes in application styles at runtime
Expose `HmrContext` interface. Apply changes in `app.css` instantly. Avoid navigation on livesync when changes in `app.css` have been made. Apply changes in `app.css` on back navigation.
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
_updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType
|
||||
} from "./fragment.transitions";
|
||||
|
||||
import { loadCss } from "../styling/style-scope";
|
||||
import { profile } from "../../profiling";
|
||||
|
||||
// TODO: Remove this and get it from global to decouple builder for angular
|
||||
@@ -82,13 +83,24 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
|
||||
return attachStateChangeListener;
|
||||
}
|
||||
|
||||
export function reloadPage(): void {
|
||||
export function reloadPage(context?: HmrContext): void {
|
||||
const activity = application.android.foregroundActivity;
|
||||
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
|
||||
const rootView: View = callbacks.getRootView();
|
||||
|
||||
if (!rootView || !rootView._onLivesync()) {
|
||||
callbacks.resetActivityContent(activity);
|
||||
let executeLivesync = true;
|
||||
// HMR has context, livesync does not
|
||||
if (context) {
|
||||
if (context.module === application.getCssFileName()) {
|
||||
loadCss(context.module);
|
||||
rootView._onCssStateChange();
|
||||
executeLivesync = false;
|
||||
}
|
||||
}
|
||||
if (executeLivesync) {
|
||||
if (!rootView || !rootView._onLivesync()) {
|
||||
callbacks.resetActivityContent(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,19 +481,19 @@ export class Frame extends FrameBase {
|
||||
switch (this.actionBarVisibility) {
|
||||
case "never":
|
||||
return false;
|
||||
|
||||
|
||||
case "always":
|
||||
return true;
|
||||
|
||||
|
||||
default:
|
||||
if (page.actionBarHidden !== undefined) {
|
||||
return !page.actionBarHidden;
|
||||
}
|
||||
|
||||
|
||||
if (this._android && this._android.showActionBar !== undefined) {
|
||||
return this._android.showActionBar;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -846,14 +858,14 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
// 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) {
|
||||
const parentView = nativeView.getParent();
|
||||
if (nativeView != null) {
|
||||
const parentView = nativeView.getParent();
|
||||
if (parentView instanceof android.view.ViewGroup) {
|
||||
if (parentView.getChildCount() === 0) {
|
||||
parentView.addViewInLayout(nativeView, -1, new org.nativescript.widgets.CommonLayoutParams());
|
||||
}
|
||||
|
||||
parentView.removeView(nativeView);
|
||||
parentView.removeView(nativeView);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,17 +17,17 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
public static navigatedToEvent = "navigatedTo";
|
||||
public static navigatingFromEvent = "navigatingFrom";
|
||||
public static navigatedFromEvent = "navigatedFrom";
|
||||
|
||||
|
||||
private _navigationContext: any;
|
||||
private _actionBar: ActionBar;
|
||||
|
||||
public _frame: Frame;
|
||||
|
||||
|
||||
public actionBarHidden: boolean;
|
||||
public enableSwipeBackNavigation: boolean;
|
||||
public backgroundSpanUnderStatusBar: boolean;
|
||||
public hasActionBar: boolean;
|
||||
|
||||
|
||||
get navigationContext(): any {
|
||||
return this._navigationContext;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
const frame = this.parent;
|
||||
return frame instanceof Frame ? frame : undefined;
|
||||
}
|
||||
|
||||
|
||||
private createNavigatedData(eventName: string, isBackNavigation: boolean): NavigatedData {
|
||||
return {
|
||||
eventName: eventName,
|
||||
@@ -103,6 +103,10 @@ export class PageBase extends ContentView implements PageDefinition {
|
||||
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
|
||||
this._navigationContext = context;
|
||||
|
||||
if (!this._cssState.isSelectorsLatestVersionApplied()) {
|
||||
this._onCssStateChange();
|
||||
}
|
||||
|
||||
//https://github.com/NativeScript/NativeScript/issues/731
|
||||
if (!isBackNavigation && bindingContext !== undefined && bindingContext !== null) {
|
||||
this.bindingContext = bindingContext;
|
||||
|
||||
8
tns-core-modules/ui/styling/style-scope.d.ts
vendored
8
tns-core-modules/ui/styling/style-scope.d.ts
vendored
@@ -19,6 +19,11 @@ export class CssState {
|
||||
* Gets the static selectors that match the view and the dynamic selectors that may potentially match the view.
|
||||
*/
|
||||
public changeMap: ChangeMap<ViewBase>;
|
||||
|
||||
/**
|
||||
* Checks whether style scope and CSS state selectors are in sync.
|
||||
*/
|
||||
public isSelectorsLatestVersionApplied(): boolean
|
||||
}
|
||||
|
||||
export class StyleScope {
|
||||
@@ -29,6 +34,9 @@ export class StyleScope {
|
||||
public static createSelectorsFromImports(tree: SyntaxTree, keyframes: Object): RuleSet[];
|
||||
public ensureSelectors(): number;
|
||||
|
||||
public isApplicationCssSelectorsLatestVersionApplied(): boolean;
|
||||
public isLocalCssSelectorsLatestVersionApplied(): boolean;
|
||||
|
||||
public applySelectors(view: ViewBase): void
|
||||
public query(options: Node): SelectorCore[];
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ export function removeTaggedAdditionalCSS(tag: String | Number): Boolean {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) { mergeCssSelectors(); }
|
||||
if (changed) { mergeCssSelectors(); }
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ function onLiveSync(args: applicationCommon.CssChangedEventData): void {
|
||||
loadCss(applicationCommon.getCssFileName());
|
||||
}
|
||||
|
||||
const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
|
||||
export const loadCss = profile(`"style-scope".loadCss`, (cssFile: string) => {
|
||||
if (!cssFile) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -343,6 +343,7 @@ export class CssState {
|
||||
_appliedChangeMap: Readonly<ChangeMap<ViewBase>>;
|
||||
_appliedPropertyValues: Readonly<{}>;
|
||||
_appliedAnimations: ReadonlyArray<kam.KeyframeAnimation>;
|
||||
_appliedSelectorsVersion: number;
|
||||
|
||||
_match: SelectorsMatch<ViewBase>;
|
||||
_matchInvalid: boolean;
|
||||
@@ -367,6 +368,15 @@ export class CssState {
|
||||
}
|
||||
}
|
||||
|
||||
public isSelectorsLatestVersionApplied(): boolean {
|
||||
if (this._appliedSelectorsVersion && this.view._styleScope) {
|
||||
this.view._styleScope.ensureSelectors();
|
||||
return this.view._styleScope._getSelectorsVersion() === this._appliedSelectorsVersion;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public onLoaded(): void {
|
||||
if (this._matchInvalid) {
|
||||
this.updateMatch();
|
||||
@@ -381,6 +391,7 @@ export class CssState {
|
||||
|
||||
@profile
|
||||
private updateMatch() {
|
||||
this._appliedSelectorsVersion = this.view._styleScope._getSelectorsVersion();
|
||||
this._match = this.view._styleScope ? this.view._styleScope.matchSelectors(this.view) : CssState.emptyMatch;
|
||||
this._matchInvalid = false;
|
||||
}
|
||||
@@ -597,8 +608,8 @@ export class StyleScope {
|
||||
}
|
||||
|
||||
public ensureSelectors(): number {
|
||||
if (this._applicationCssSelectorsAppliedVersion !== applicationCssSelectorVersion ||
|
||||
this._localCssSelectorVersion !== this._localCssSelectorsAppliedVersion ||
|
||||
if (!this.isApplicationCssSelectorsLatestVersionApplied() ||
|
||||
!this.isLocalCssSelectorsLatestVersionApplied() ||
|
||||
!this._mergedCssSelectors) {
|
||||
|
||||
this._createSelectors();
|
||||
@@ -607,6 +618,14 @@ export class StyleScope {
|
||||
return this._getSelectorsVersion();
|
||||
}
|
||||
|
||||
public isApplicationCssSelectorsLatestVersionApplied(): boolean {
|
||||
return this._applicationCssSelectorsAppliedVersion === applicationCssSelectorVersion;
|
||||
}
|
||||
|
||||
public isLocalCssSelectorsLatestVersionApplied(): boolean {
|
||||
return this._localCssSelectorsAppliedVersion === this._localCssSelectorVersion;
|
||||
}
|
||||
|
||||
@profile
|
||||
private _createSelectors() {
|
||||
let toMerge: RuleSet[][] = [];
|
||||
|
||||
Reference in New Issue
Block a user