Skip to content

Commit 9ac40a5

Browse files
author
Nedyalko Nikolov
committed
Fixed sorting issue with Css Selectors with same specificity.
1 parent 58be979 commit 9ac40a5

File tree

4 files changed

+58
-5
lines changed

4 files changed

+58
-5
lines changed

tests/app/ui/styling/style-tests.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import types = require("utils/types");
1212
import viewModule = require("ui/core/view");
1313
import styleModule = require("ui/styling/style");
1414
import dependencyObservableModule = require("ui/core/dependency-observable");
15+
import {StyleScope} from 'ui/styling/style-scope';
1516

1617
export function test_css_dataURI_is_applied_to_backgroundImageSource() {
1718
var stack = new stackModule.StackLayout();
@@ -1443,6 +1444,17 @@ export function test_CascadingClassNamesAppliesAfterPageLoad() {
14431444
helper.assertViewBackgroundColor(stack, "#FF0000");
14441445
});
14451446
}
1447+
1448+
export function test_SortingOfCssSelectorsWithSameSpecificity() {
1449+
let scope = new StyleScope();
1450+
scope.css = ".button { border-color: #b2b2b2; background-color: hotpink; color: #444; margin: 5; padding: 7 2; border-width: 1; border-style: solid; border-radius: 2; text-align: center; font-size: 18; line-height: 42; } .button-small { background-color: salmon; } .button-large { font-size: 26; } .button-light { border-color: #ddd; background-color: #fff; color: #444; } .button-stable { border-color: #b2b2b2; background-color: #f8f8f8; color: #444; } .button-positive { border-color: #0c60ee; background-color: #387ef5; color: #fff; } .button-calm { border-color: #0a9dc7;background-color: #11c1f3; color: #fff; } .button-balanced { border-color: #28a54c; background-color: #33cd5f; color: #fff; } .button-energized { border-color: #e6b500; background-color: #ffc900; color: #fff; } .button-assertive { border-color: #e42112; background-color: #ef473a; color: #fff; } .button-royal { border-color: #6b46e5; background-color: #886aea; color: #fff; } .button-dark { border-color: #111; background-color: #444; color: #fff; }";
1451+
scope.ensureSelectors();
1452+
let expressions = [];
1453+
(<any>scope)._mergedCssSelectors.forEach((v) => {
1454+
expressions.push(v.expression);
1455+
});
1456+
TKUnit.assertTrue(expressions.indexOf('button') < expressions.indexOf('button-calm'), "button class selector should be before button-calm selector.");
1457+
}
14461458
// <snippet module="ui/styling" title="styling">
14471459
// For information and example how to use style properties please refer to special [**Styling**](../../../styling.md) topic.
14481460
// </snippet>

tns-core-modules/ui/styling/style-scope.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ export class StyleScope {
191191
mergedResult.push.apply(mergedResult, arrays[i]);
192192
}
193193
}
194-
mergedResult.sort((a, b) => a.specificity - b.specificity);
194+
ensureUtils();
195+
mergedResult = utils.mergeSort(mergedResult, (a, b) => { return a.specificity - b.specificity; });
195196

196197
return mergedResult;
197198
}
@@ -200,9 +201,9 @@ export class StyleScope {
200201
this.ensureSelectors();
201202

202203
view.style._beginUpdate();
203-
let i,
204-
selector: cssSelector.CssSelector,
205-
matchedStateSelectors = new Array<cssSelector.CssVisualStateSelector>()
204+
let i;
205+
let selector: cssSelector.CssSelector;
206+
let matchedStateSelectors = new Array<cssSelector.CssVisualStateSelector>();
206207

207208
// Go trough all selectors - and directly apply all non-state selectors
208209
for (i = 0; i < this._mergedCssSelectors.length; i++) {

tns-core-modules/utils/utils-common.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,36 @@ export function isDataURI(uri: string): boolean {
134134

135135
return firstSegment && firstSegment.indexOf("data:") === 0 && firstSegment.indexOf('base64') >= 0;
136136
}
137+
138+
export function mergeSort(arr, compareFunc) {
139+
if (arr.length < 2) {
140+
return arr;
141+
}
142+
143+
let middle = arr.length / 2;
144+
let left = arr.slice(0, middle);
145+
let right = arr.slice(middle, arr.length);
146+
147+
return merge(mergeSort(left, compareFunc), mergeSort(right, compareFunc), compareFunc);
148+
}
149+
150+
export function merge(left, right, compareFunc) {
151+
let result = [];
152+
while(left.length && right.length) {
153+
if (compareFunc(left[0], right[0]) <= 0) {
154+
result.push(left.shift());
155+
} else {
156+
result.push(right.shift());
157+
}
158+
}
159+
160+
while (left.length) {
161+
result.push(left.shift());
162+
}
163+
164+
while (right.length) {
165+
result.push(right.shift());
166+
}
167+
168+
return result;
169+
}

tns-core-modules/utils/utils.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,5 +249,12 @@
249249
* Converts string value to number or boolean.
250250
* @param value The original value.
251251
*/
252-
export function convertString(value: any): any
252+
export function convertString(value: any): any
253+
254+
/**
255+
* Sorts an array by using merge sort algoritm (which ensures stable sort since the built-in Array.sort() does not promise a stable sort).
256+
* @param arr - array to be sorted
257+
* @param compareFunc - function that will be used to compare two elements of the array
258+
*/
259+
export function mergeSort(arr: Array<any>, compareFunc: (a: any, b: any) => number): Array<any>
253260
}

0 commit comments

Comments
 (0)