diff --git a/app/ui-tests-app/issues/issue-4450.xml b/app/ui-tests-app/issues/issue-4450.xml
new file mode 100644
index 000000000..ebdc91e3d
--- /dev/null
+++ b/app/ui-tests-app/issues/issue-4450.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/apps/app/ui-tests-app/issues/issue-4450.css b/apps/app/ui-tests-app/issues/issue-4450.css
new file mode 100644
index 000000000..be0be4b54
--- /dev/null
+++ b/apps/app/ui-tests-app/issues/issue-4450.css
@@ -0,0 +1,3 @@
+.styled > StackLayout > * {
+ background-color: red;
+}
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/issues/issue-4450.ts b/apps/app/ui-tests-app/issues/issue-4450.ts
new file mode 100644
index 000000000..8ddd06f57
--- /dev/null
+++ b/apps/app/ui-tests-app/issues/issue-4450.ts
@@ -0,0 +1,7 @@
+export function set(args) {
+ args.object.page.className = "styled";
+}
+
+export function clear(args) {
+ args.object.page.className = "";
+}
\ No newline at end of file
diff --git a/apps/app/ui-tests-app/issues/issue-4450.xml b/apps/app/ui-tests-app/issues/issue-4450.xml
new file mode 100644
index 000000000..ebdc91e3d
--- /dev/null
+++ b/apps/app/ui-tests-app/issues/issue-4450.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/apps/app/ui-tests-app/issues/main-page.ts b/apps/app/ui-tests-app/issues/main-page.ts
index 0fd0458ac..04569c27f 100644
--- a/apps/app/ui-tests-app/issues/main-page.ts
+++ b/apps/app/ui-tests-app/issues/main-page.ts
@@ -24,6 +24,7 @@ export function loadExamples() {
examples.set("1657-ios", "issues/issue-1657-ios");
examples.set("tabview-with-scrollview_4022","issues/tabview-with-scrollview_4022");
examples.set("3354-ios", "issues/issue-3354");
+ examples.set("4450", "issues/issue-4450");
return examples;
}
\ No newline at end of file
diff --git a/tns-core-modules/ui/core/properties/properties.ts b/tns-core-modules/ui/core/properties/properties.ts
index 8e03d0813..9409fb20f 100644
--- a/tns-core-modules/ui/core/properties/properties.ts
+++ b/tns-core-modules/ui/core/properties/properties.ts
@@ -658,6 +658,7 @@ export class CssAnimationProperty {
public readonly keyframe: string;
public readonly defaultValueKey: symbol;
public readonly key: symbol;
+ private readonly source: symbol;
public readonly defaultValue: U;
@@ -699,6 +700,7 @@ export class CssAnimationProperty {
const computedValue = Symbol("computed-value:" + propertyName);
this.key = computedValue;
const computedSource = Symbol("computed-source:" + propertyName);
+ this.source = computedSource;
this.getDefault = Symbol(propertyName + ":getDefault");
const getDefault = this.getDefault;
@@ -712,7 +714,11 @@ export class CssAnimationProperty {
enumerable, configurable,
get: getsComputed ? function (this: T) { return this[computedValue]; } : function (this: T) { return this[symbol]; },
set(this: T, boxedValue: U) {
- let oldValue = this[computedValue];
+
+ const oldValue = this[computedValue];
+ const oldSource = this[computedSource];
+ const wasSet = oldSource !== ValueSource.Default;
+
if (boxedValue === unsetValue) {
this[symbol] = unsetValue;
if (this[computedSource] === propertySource) {
@@ -724,8 +730,8 @@ export class CssAnimationProperty {
this[computedSource] = ValueSource.Css;
this[computedValue] = this[cssValue];
} else {
- this[computedSource] = ValueSource.Default;
- this[computedValue] = defaultValueKey in this ? this[defaultValueKey] : defaultValue;
+ delete this[computedSource];
+ delete this[computedValue];
}
}
} else {
@@ -738,29 +744,41 @@ export class CssAnimationProperty {
this[computedValue] = boxedValue;
}
}
- let value = this[computedValue];
- if (oldValue !== value && (!equalityComparer || !equalityComparer(oldValue, value))) {
- if (valueChanged) {
- valueChanged(this, oldValue, value);
- }
- const view = this.view;
- if (view[setNative]) {
- if (view._suspendNativeUpdatesCount) {
- if (view._suspendedUpdates) {
- view._suspendedUpdates[propertyName] = property;
- }
- } else {
- if (!(defaultValueKey in this)) {
+ const value = this[computedValue];
+ const source = this[computedSource];
+ const isSet = source !== ValueSource.Default;
+
+ const computedValueChanged = oldValue !== value && (!equalityComparer || !equalityComparer(oldValue, value));
+
+ if (computedValueChanged && valueChanged) {
+ valueChanged(this, oldValue, value);
+ }
+
+ const view = this.view;
+ if (view[setNative] && (computedValueChanged || isSet !== wasSet)) {
+ if (view._suspendNativeUpdatesCount) {
+ if (view._suspendedUpdates) {
+ view._suspendedUpdates[propertyName] = property;
+ }
+ } else {
+ if (isSet) {
+ if (!wasSet && !(defaultValueKey in this)) {
this[defaultValueKey] = view[getDefault] ? view[getDefault]() : defaultValue;
}
-
view[setNative](value);
+ } else if (wasSet) {
+ if (defaultValueKey in this) {
+ view[setNative](this[defaultValueKey]);
+ } else {
+ view[setNative](defaultValue);
+ }
}
}
- if (this.hasListeners(eventName)) {
- this.notify({ object: this, eventName, propertyName, value, oldValue });
- }
+ }
+
+ if (computedValueChanged && this.hasListeners(eventName)) {
+ this.notify({ object: this, eventName, propertyName, value, oldValue });
}
}
}
@@ -807,7 +825,7 @@ export class CssAnimationProperty {
}
public isSet(instance: T): boolean {
- return instance[this.key] !== unsetValue;
+ return instance[this.source] !== ValueSource.Default;
}
}
CssAnimationProperty.prototype.isStyleProperty = true;