Skip to content

Commit 9bebc2a

Browse files
committed
fix(animations): implement containsElement method for driver
1 parent f646bd4 commit 9bebc2a

File tree

1 file changed

+126
-51
lines changed

1 file changed

+126
-51
lines changed

Diff for: nativescript-angular/animations/animation-driver.ts

+126-51
Original file line numberDiff line numberDiff line change
@@ -12,77 +12,96 @@ import { animationsLog as traceLog } from "../trace";
1212

1313
import { createSelector, SelectorCore } from "tns-core-modules/ui/styling/css-selector";
1414

15-
export class NativeScriptAnimationDriver implements AnimationDriver {
16-
matchesElement(_element: any, _selector: string): boolean {
17-
// this method is never called since NG 4.2.5
18-
throw new Error("Method not implemented.");
19-
}
15+
interface ViewMatchResult {
16+
found: boolean;
17+
}
2018

21-
containsElement(elm1: NgView, elm2: NgView): boolean {
22-
traceLog(
23-
`NativeScriptAnimationDriver.containsElement ` +
24-
`element1: ${elm1}, element2: ${elm2}`
25-
);
19+
interface ViewMatchParams {
20+
originalView: NgView;
21+
}
2622

27-
let found = false;
28-
eachDescendant(elm1, child => {
29-
if (child === elm2) {
30-
found = true;
31-
}
23+
interface QueryParams {
24+
selector: Selector;
25+
multi: boolean;
26+
}
27+
28+
interface QueryResult {
29+
matches: NgView[];
30+
}
3231

33-
return !found;
34-
});
32+
class Selector {
33+
private nsSelectors: SelectorCore[];
34+
private classSelectors: string[];
3535

36-
return found;
36+
constructor(rawSelector: string) {
37+
this.parse(rawSelector);
3738
}
3839

39-
query(element: NgView, selector: string, multi: boolean): NgView[] {
40-
traceLog(
41-
`NativeScriptAnimationDriver.query ` +
42-
`element: ${element}, selector: ${selector} ` +
43-
`multi: ${multi}`
44-
);
40+
match(element: NgView): boolean {
41+
return this.nsSelectorMatch(element) || this.classSelectorsMatch(element);
42+
}
4543

46-
const selectors = selector.split(",").map(s => s.trim());
44+
private parse(rawSelector: string) {
45+
const selectors = rawSelector.split(",").map(s => s.trim());
4746

48-
const nsSelectors: SelectorCore[] = selectors.map(createSelector);
49-
const classSelectors = selectors
47+
this.nsSelectors = selectors.map(createSelector);
48+
this.classSelectors = selectors
5049
.filter(s => s.startsWith("."))
5150
.map(s => s.substring(1));
51+
}
5252

53-
return this.visitDescendants(element, nsSelectors, classSelectors, multi);
53+
private nsSelectorMatch(element: NgView) {
54+
return this.nsSelectors.some(s => s.match(element));
5455
}
5556

56-
private visitDescendants(
57-
element: NgView,
58-
nsSelectors: SelectorCore[],
59-
classSelectors: string[],
60-
multi: boolean): NgView[] {
57+
private classSelectorsMatch(element: NgView) {
58+
return this.classSelectors.some(s => this.hasClass(element, s));
59+
}
6160

62-
let results = [];
63-
eachDescendant(element, child => {
64-
if (child instanceof InvisibleNode) {
65-
return true;
66-
}
61+
// we're using that instead of match for classes
62+
// that are dynamically added by the animation engine
63+
// such as .ng-trigger, that's added for every :enter view
64+
private hasClass(element: NgView, cls: string) {
65+
return element && element["$$classes"] && element["$$classes"][cls];
66+
}
67+
}
6768

68-
if (nsSelectors.some(s => s.match(child)) ||
69-
classSelectors.some(s => this.hasClass(child, s))) {
69+
export class NativeScriptAnimationDriver implements AnimationDriver {
70+
matchesElement(element: NgView, rawSelector: string): boolean {
71+
traceLog(
72+
`NativeScriptAnimationDriver.matchesElement ` +
73+
`element: ${element}, selector: ${rawSelector}`
74+
);
7075

71-
results.push(child);
72-
return multi;
73-
}
76+
const selector = this.makeSelector(rawSelector);
77+
return selector.match(element);
78+
}
7479

75-
return true;
76-
});
7780

78-
return results;
81+
containsElement(elm1: NgView, elm2: NgView): boolean {
82+
traceLog(
83+
`NativeScriptAnimationDriver.containsElement ` +
84+
`element1: ${elm1}, element2: ${elm2}`
85+
);
86+
87+
const params: ViewMatchParams = { originalView: elm2 };
88+
const result: ViewMatchResult = this.visitDescendants(elm1, viewMatches, params);
89+
90+
return result.found;
7991
}
8092

81-
// we're using that instead of match for classes
82-
// that are dynamically added by the animation engine
83-
// such as .ng-trigger, that's added for every :enter view
84-
private hasClass(element: any, cls: string) {
85-
return element["$$classes"][cls];
93+
query(element: NgView, rawSelector: string, multi: boolean): NgView[] {
94+
traceLog(
95+
`NativeScriptAnimationDriver.query ` +
96+
`element: ${element}, selector: ${rawSelector} ` +
97+
`multi: ${multi}`
98+
);
99+
100+
const selector = this.makeSelector(rawSelector);
101+
const params: QueryParams = { selector, multi };
102+
const result: QueryResult = this.visitDescendants(element, queryDescendants, params);
103+
104+
return result.matches || [];
86105
}
87106

88107
computeStyle(element: NgView, prop: string): string {
@@ -112,4 +131,60 @@ export class NativeScriptAnimationDriver implements AnimationDriver {
112131
return new NativeScriptAnimationPlayer(
113132
element, keyframes, duration, delay, easing);
114133
}
134+
135+
private makeSelector(rawSelector: string): Selector {
136+
return new Selector(rawSelector);
137+
}
138+
139+
private visitDescendants(
140+
element: NgView,
141+
cb: (child: NgView, result: any, params: any) => boolean,
142+
cbParams: any): any {
143+
144+
const result = {};
145+
// fill the result obj with the result from the callback function
146+
eachDescendant(element, (child: NgView) => cb(child, result, cbParams));
147+
148+
return result;
149+
}
150+
}
151+
152+
function viewMatches(
153+
element: NgView,
154+
result: ViewMatchResult,
155+
params: ViewMatchParams
156+
): boolean {
157+
158+
if (element === params.originalView) {
159+
result.found = true;
160+
}
161+
162+
return !result.found;
163+
}
164+
165+
function queryDescendants(
166+
element: NgView,
167+
result: QueryResult,
168+
params: QueryParams
169+
): boolean {
170+
171+
if (!result.matches) {
172+
result.matches = [];
173+
}
174+
175+
const { selector, multi } = params;
176+
177+
// skip comment and text nodes
178+
// because they are not actual Views
179+
// and cannot be animated
180+
if (element instanceof InvisibleNode) {
181+
return true;
182+
}
183+
184+
if (selector.match(element)) {
185+
result.matches.push(element);
186+
return multi;
187+
}
188+
189+
return true;
115190
}

0 commit comments

Comments
 (0)