Skip to content

Commit 0726310

Browse files
Merge branch 'master' into native-events-fix
2 parents 602cbd5 + dc22bd6 commit 0726310

98 files changed

Lines changed: 3969 additions & 2665 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/CODEOWNERS‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
docs/* @facebook/react-native
2-
blog/* @facebook/react-native
1+
docs/* @hramos
2+
blog/* @hramos
33
Libraries/Animated/* @janicduplessis
44
Libraries/NativeAnimation/* @janicduplessis
55
Libraries/Image/* @shergin
66
Libraries/Text/* @shergin
7-
React/Base/* @shergin @javache
8-
React/Views/* @shergin @javache
9-
React/Modules/* @shergin @javache
10-
React/CxxBridge/* @javache
11-
React/Executors/* @javache
7+
React/Base/* @shergin
8+
React/Views/* @shergin
9+
React/Modules/* @shergin
10+
React/CxxBridge/* @mhorowitz
1211
ReactAndroid/src/main/java/com/facebook/react/animated/* @janicduplessis
13-
ReactCommon/* @javache
14-
website/* @facebook/react-native
15-
website/showcase.json @facebook/react-native
16-
package.json @facebook/react-native
17-
website/package.json @facebook/react-native
12+
website/* @hramos
13+
website/showcase.json @hramos
14+
package.json @hramos
15+
website/package.json @hramos
1816
local-cli/core/* @grabbou @kureev
1917
local-cli/link/* @grabbou @kureev
2018
local-cli/unlink/* @grabbou @kureev
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @providesModule AnimatedEvent
10+
* @flow
11+
* @format
12+
*/
13+
'use strict';
14+
15+
const AnimatedValue = require('./nodes/AnimatedValue');
16+
const NativeAnimatedHelper = require('./NativeAnimatedHelper');
17+
const ReactNative = require('ReactNative');
18+
19+
const invariant = require('fbjs/lib/invariant');
20+
const {shouldUseNativeDriver} = require('./NativeAnimatedHelper');
21+
22+
export type Mapping = {[key: string]: Mapping} | AnimatedValue;
23+
export type EventConfig = {
24+
listener?: ?Function,
25+
useNativeDriver?: boolean,
26+
};
27+
28+
function attachNativeEvent(
29+
viewRef: any,
30+
eventName: string,
31+
argMapping: Array<?Mapping>,
32+
) {
33+
// Find animated values in `argMapping` and create an array representing their
34+
// key path inside the `nativeEvent` object. Ex.: ['contentOffset', 'x'].
35+
const eventMappings = [];
36+
37+
const traverse = (value, path) => {
38+
if (value instanceof AnimatedValue) {
39+
value.__makeNative();
40+
41+
eventMappings.push({
42+
nativeEventPath: path,
43+
animatedValueTag: value.__getNativeTag(),
44+
});
45+
} else if (typeof value === 'object') {
46+
for (const key in value) {
47+
traverse(value[key], path.concat(key));
48+
}
49+
}
50+
};
51+
52+
invariant(
53+
argMapping[0] && argMapping[0].nativeEvent,
54+
'Native driven events only support animated values contained inside `nativeEvent`.',
55+
);
56+
57+
// Assume that the event containing `nativeEvent` is always the first argument.
58+
traverse(argMapping[0].nativeEvent, []);
59+
60+
const viewTag = ReactNative.findNodeHandle(viewRef);
61+
62+
eventMappings.forEach(mapping => {
63+
NativeAnimatedHelper.API.addAnimatedEventToView(
64+
viewTag,
65+
eventName,
66+
mapping,
67+
);
68+
});
69+
70+
return {
71+
detach() {
72+
eventMappings.forEach(mapping => {
73+
NativeAnimatedHelper.API.removeAnimatedEventFromView(
74+
viewTag,
75+
eventName,
76+
mapping.animatedValueTag,
77+
);
78+
});
79+
},
80+
};
81+
}
82+
83+
class AnimatedEvent {
84+
_argMapping: Array<?Mapping>;
85+
_listeners: Array<Function> = [];
86+
_callListeners: Function;
87+
_attachedEvent: ?{
88+
detach: () => void,
89+
};
90+
__isNative: boolean;
91+
92+
constructor(argMapping: Array<?Mapping>, config?: EventConfig = {}) {
93+
this._argMapping = argMapping;
94+
if (config.listener) {
95+
this.__addListener(config.listener);
96+
}
97+
this._callListeners = this._callListeners.bind(this);
98+
this._attachedEvent = null;
99+
this.__isNative = shouldUseNativeDriver(config);
100+
101+
if (__DEV__) {
102+
this._validateMapping();
103+
}
104+
}
105+
106+
__addListener(callback: Function): void {
107+
this._listeners.push(callback);
108+
}
109+
110+
__removeListener(callback: Function): void {
111+
this._listeners = this._listeners.filter(listener => listener !== callback);
112+
}
113+
114+
__attach(viewRef: any, eventName: string) {
115+
invariant(
116+
this.__isNative,
117+
'Only native driven events need to be attached.',
118+
);
119+
120+
this._attachedEvent = attachNativeEvent(
121+
viewRef,
122+
eventName,
123+
this._argMapping,
124+
);
125+
}
126+
127+
__detach(viewTag: any, eventName: string) {
128+
invariant(
129+
this.__isNative,
130+
'Only native driven events need to be detached.',
131+
);
132+
133+
this._attachedEvent && this._attachedEvent.detach();
134+
}
135+
136+
__getHandler() {
137+
if (this.__isNative) {
138+
return this._callListeners;
139+
}
140+
141+
return (...args: any) => {
142+
const traverse = (recMapping, recEvt, key) => {
143+
if (typeof recEvt === 'number' && recMapping instanceof AnimatedValue) {
144+
recMapping.setValue(recEvt);
145+
} else if (typeof recMapping === 'object') {
146+
for (const mappingKey in recMapping) {
147+
/* $FlowFixMe(>=0.53.0 site=react_native_fb) This comment
148+
* suppresses an error found when Flow v0.53 was deployed. To see
149+
* the error delete this comment and run Flow. */
150+
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
151+
}
152+
}
153+
};
154+
155+
if (!this.__isNative) {
156+
this._argMapping.forEach((mapping, idx) => {
157+
traverse(mapping, args[idx], 'arg' + idx);
158+
});
159+
}
160+
this._callListeners(...args);
161+
};
162+
}
163+
164+
_callListeners(...args) {
165+
this._listeners.forEach(listener => listener(...args));
166+
}
167+
168+
_validateMapping() {
169+
const traverse = (recMapping, recEvt, key) => {
170+
if (typeof recEvt === 'number') {
171+
invariant(
172+
recMapping instanceof AnimatedValue,
173+
'Bad mapping of type ' +
174+
typeof recMapping +
175+
' for key ' +
176+
key +
177+
', event value must map to AnimatedValue',
178+
);
179+
return;
180+
}
181+
invariant(
182+
typeof recMapping === 'object',
183+
'Bad mapping of type ' + typeof recMapping + ' for key ' + key,
184+
);
185+
invariant(
186+
typeof recEvt === 'object',
187+
'Bad event of type ' + typeof recEvt + ' for key ' + key,
188+
);
189+
for (const mappingKey in recMapping) {
190+
traverse(recMapping[mappingKey], recEvt[mappingKey], mappingKey);
191+
}
192+
};
193+
}
194+
}
195+
196+
module.exports = {AnimatedEvent, attachNativeEvent};

0 commit comments

Comments
 (0)