@samitouri / QOS-React-2 / commits / 260353e977

Added support for synthetic animation/transition events.

Miles Johnson committed Feb 8, 2016 at 21:31 UTC 260353e977848c202e267ba2b9a328075650f52b
8 files changed +287 -43
docs/docs/ref-05-events.md
+37
@@ -139,6 +139,7 @@ DOMEventTarget relatedTarget
139
140 These focus events work on all elements in the React DOM, not just form elements.
141
142 +
143 ### Form Events
144
145 Event names:
@@ -246,6 +247,7 @@ number deltaY
247 number deltaZ
248 ```
249
250 +
251 ### Media Events
252
253 Event names:
@@ -254,6 +256,7 @@ Event names:
256 onAbort onCanPlay onCanPlayThrough onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting
257 ```
258
259 +
260 ### Image Events
261
262 Event names:
@@ -261,3 +264,37 @@ Event names:
264 ```
265 onLoad onError
266 ```
267 +
268 +
269 +### Animation Events
270 +
271 +Event names:
272 +
273 +```
274 +onAnimationStart onAnimationEnd onAnimationIteration
275 +```
276 +
277 +Properties:
278 +
279 +```javascript
280 +string animationName
281 +string pseudoElement
282 +float elapsedTime
283 +```
284 +
285 +
286 +### Transition Events
287 +
288 +Event names:
289 +
290 +```
291 +onTransitionEnd
292 +```
293 +
294 +Properties:
295 +
296 +```javascript
297 +string propertyName
298 +string pseudoElement
299 +float elapsedTime
300 +```
src/addons/transitions/ReactTransitionEvents.js
+7 -43
@@ -13,56 +13,20 @@
13
14 var ExecutionEnvironment = require('ExecutionEnvironment');
15
16 -/**
17 - * EVENT_NAME_MAP is used to determine which event fired when a
18 - * transition/animation ends, based on the style property used to
19 - * define that event.
20 - */
21 -var EVENT_NAME_MAP = {
22 - transitionend: {
23 - 'transition': 'transitionend',
24 - 'WebkitTransition': 'webkitTransitionEnd',
25 - 'MozTransition': 'mozTransitionEnd',
26 - 'OTransition': 'oTransitionEnd',
27 - 'msTransition': 'MSTransitionEnd',
28 - },
29 -
30 - animationend: {
31 - 'animation': 'animationend',
32 - 'WebkitAnimation': 'webkitAnimationEnd',
33 - 'MozAnimation': 'mozAnimationEnd',
34 - 'OAnimation': 'oAnimationEnd',
35 - 'msAnimation': 'MSAnimationEnd',
36 - },
37 -};
16 +var getVendorPrefixedEventName = require('getVendorPrefixedEventName');
17
18 var endEvents = [];
19
20 function detectEvents() {
42 - var testEl = document.createElement('div');
43 - var style = testEl.style;
44 -
45 - // On some platforms, in particular some releases of Android 4.x,
46 - // the un-prefixed "animation" and "transition" properties are defined on the
47 - // style object but the events that fire will still be prefixed, so we need
48 - // to check if the un-prefixed events are useable, and if not remove them
49 - // from the map
50 - if (!('AnimationEvent' in window)) {
51 - delete EVENT_NAME_MAP.animationend.animation;
52 - }
21 + var animEnd = getVendorPrefixedEventName('animationend');
22 + var transEnd = getVendorPrefixedEventName('transitionend');
23
54 - if (!('TransitionEvent' in window)) {
55 - delete EVENT_NAME_MAP.transitionend.transition;
24 + if (animEnd) {
25 + endEvents.push(animEnd);
26 }
27
58 - for (var baseEventName in EVENT_NAME_MAP) {
59 - var baseEvents = EVENT_NAME_MAP[baseEventName];
60 - for (var styleName in baseEvents) {
61 - if (styleName in style) {
62 - endEvents.push(baseEvents[styleName]);
63 - break;
64 - }
65 - }
28 + if (transEnd) {
29 + endEvents.push(transEnd);
30 }
31 }
32
src/renderers/dom/client/ReactBrowserEventEmitter.js
+5
@@ -17,6 +17,7 @@ var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
17 var ViewportMetrics = require('ViewportMetrics');
18
19 var assign = require('Object.assign');
20 +var getVendorPrefixedEventName = require('getVendorPrefixedEventName');
21 var isEventSupported = require('isEventSupported');
22
23 /**
@@ -83,6 +84,9 @@ var reactTopListenersCounter = 0;
84 // events so we don't include them here
85 var topEventMapping = {
86 topAbort: 'abort',
87 + topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend',
88 + topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration',
89 + topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart',
90 topBlur: 'blur',
91 topCanPlay: 'canplay',
92 topCanPlayThrough: 'canplaythrough',
@@ -139,6 +143,7 @@ var topEventMapping = {
143 topTouchEnd: 'touchend',
144 topTouchMove: 'touchmove',
145 topTouchStart: 'touchstart',
146 + topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend',
147 topVolumeChange: 'volumechange',
148 topWaiting: 'waiting',
149 topWheel: 'wheel',
src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js
+38
@@ -15,6 +15,7 @@ var EventConstants = require('EventConstants');
15 var EventListener = require('EventListener');
16 var EventPropagators = require('EventPropagators');
17 var ReactDOMComponentTree = require('ReactDOMComponentTree');
18 +var SyntheticAnimationEvent = require('SyntheticAnimationEvent');
19 var SyntheticClipboardEvent = require('SyntheticClipboardEvent');
20 var SyntheticEvent = require('SyntheticEvent');
21 var SyntheticFocusEvent = require('SyntheticFocusEvent');
@@ -22,6 +23,7 @@ var SyntheticKeyboardEvent = require('SyntheticKeyboardEvent');
23 var SyntheticMouseEvent = require('SyntheticMouseEvent');
24 var SyntheticDragEvent = require('SyntheticDragEvent');
25 var SyntheticTouchEvent = require('SyntheticTouchEvent');
26 +var SyntheticTransitionEvent = require('SyntheticTransitionEvent');
27 var SyntheticUIEvent = require('SyntheticUIEvent');
28 var SyntheticWheelEvent = require('SyntheticWheelEvent');
29
@@ -39,6 +41,24 @@ var eventTypes = {
41 captured: keyOf({onAbortCapture: true}),
42 },
43 },
44 + animationEnd: {
45 + phasedRegistrationNames: {
46 + bubbled: keyOf({onAnimationEnd: true}),
47 + captured: keyOf({onAnimationEndCapture: true}),
48 + },
49 + },
50 + animationIteration: {
51 + phasedRegistrationNames: {
52 + bubbled: keyOf({onAnimationIteration: true}),
53 + captured: keyOf({onAnimationIterationCapture: true}),
54 + },
55 + },
56 + animationStart: {
57 + phasedRegistrationNames: {
58 + bubbled: keyOf({onAnimationStart: true}),
59 + captured: keyOf({onAnimationStartCapture: true}),
60 + },
61 + },
62 blur: {
63 phasedRegistrationNames: {
64 bubbled: keyOf({onBlur: true}),
@@ -365,6 +385,12 @@ var eventTypes = {
385 captured: keyOf({onTouchStartCapture: true}),
386 },
387 },
388 + transitionEnd: {
389 + phasedRegistrationNames: {
390 + bubbled: keyOf({onTransitionEnd: true}),
391 + captured: keyOf({onTransitionEndCapture: true}),
392 + },
393 + },
394 volumeChange: {
395 phasedRegistrationNames: {
396 bubbled: keyOf({onVolumeChange: true}),
@@ -387,6 +413,9 @@ var eventTypes = {
413
414 var topLevelEventsToDispatchConfig = {
415 topAbort: eventTypes.abort,
416 + topAnimationEnd: eventTypes.animationEnd,
417 + topAnimationIteration: eventTypes.animationIteration,
418 + topAnimationStart: eventTypes.animationStart,
419 topBlur: eventTypes.blur,
420 topCanPlay: eventTypes.canPlay,
421 topCanPlayThrough: eventTypes.canPlayThrough,
@@ -441,6 +470,7 @@ var topLevelEventsToDispatchConfig = {
470 topTouchEnd: eventTypes.touchEnd,
471 topTouchMove: eventTypes.touchMove,
472 topTouchStart: eventTypes.touchStart,
473 + topTransitionEnd: eventTypes.transitionEnd,
474 topVolumeChange: eventTypes.volumeChange,
475 topWaiting: eventTypes.waiting,
476 topWheel: eventTypes.wheel,
@@ -549,6 +579,14 @@ var SimpleEventPlugin = {
579 case topLevelTypes.topTouchStart:
580 EventConstructor = SyntheticTouchEvent;
581 break;
582 + case topLevelTypes.topAnimationEnd:
583 + case topLevelTypes.topAnimationIteration:
584 + case topLevelTypes.topAnimationStart:
585 + EventConstructor = SyntheticAnimationEvent;
586 + break;
587 + case topLevelTypes.topTransitionEnd:
588 + EventConstructor = SyntheticTransitionEvent;
589 + break;
590 case topLevelTypes.topScroll:
591 EventConstructor = SyntheticUIEvent;
592 break;
src/renderers/dom/client/syntheticEvents/SyntheticAnimationEvent.js new
+47
@@ -0,0 +1,47 @@
1 +/**
2 + * Copyright 2013-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 SyntheticAnimationEvent
10 + */
11 +
12 +'use strict';
13 +
14 +var SyntheticEvent = require('SyntheticEvent');
15 +
16 +/**
17 + * @interface Event
18 + * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
19 + * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
20 + */
21 +var AnimationEventInterface = {
22 + animationName: null,
23 + elapsedTime: null,
24 + pseudoElement: null,
25 +};
26 +
27 +/**
28 + * @param {object} dispatchConfig Configuration used to dispatch this event.
29 + * @param {string} dispatchMarker Marker identifying the event target.
30 + * @param {object} nativeEvent Native browser event.
31 + * @extends {SyntheticEvent}
32 + */
33 +function SyntheticAnimationEvent(
34 + dispatchConfig,
35 + dispatchMarker,
36 + nativeEvent,
37 + nativeEventTarget
38 +) {
39 + return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
40 +}
41 +
42 +SyntheticEvent.augmentClass(
43 + SyntheticAnimationEvent,
44 + AnimationEventInterface
45 +);
46 +
47 +module.exports = SyntheticAnimationEvent;
src/renderers/dom/client/syntheticEvents/SyntheticTransitionEvent.js new
+47
@@ -0,0 +1,47 @@
1 +/**
2 + * Copyright 2013-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 SyntheticTransitionEvent
10 + */
11 +
12 +'use strict';
13 +
14 +var SyntheticEvent = require('SyntheticEvent');
15 +
16 +/**
17 + * @interface Event
18 + * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
19 + * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
20 + */
21 +var TransitionEventInterface = {
22 + propertyName: null,
23 + elapsedTime: null,
24 + pseudoElement: null,
25 +};
26 +
27 +/**
28 + * @param {object} dispatchConfig Configuration used to dispatch this event.
29 + * @param {string} dispatchMarker Marker identifying the event target.
30 + * @param {object} nativeEvent Native browser event.
31 + * @extends {SyntheticEvent}
32 + */
33 +function SyntheticTransitionEvent(
34 + dispatchConfig,
35 + dispatchMarker,
36 + nativeEvent,
37 + nativeEventTarget
38 +) {
39 + return SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget);
40 +}
41 +
42 +SyntheticEvent.augmentClass(
43 + SyntheticTransitionEvent,
44 + TransitionEventInterface
45 +);
46 +
47 +module.exports = SyntheticTransitionEvent;
src/renderers/dom/client/utils/getVendorPrefixedEventName.js new
+102
@@ -0,0 +1,102 @@
1 +/**
2 + * Copyright 2013-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 getVendorPrefixedEventName
10 + */
11 +
12 +'use strict';
13 +
14 +var ExecutionEnvironment = require('ExecutionEnvironment');
15 +
16 +/**
17 + * Generate a mapping of standard vendor prefixes using the defined style property and event name.
18 + *
19 + * @param {string} styleProp
20 + * @param {string} eventName
21 + * @returns {object}
22 + */
23 +function makePrefixMap(styleProp, eventName) {
24 + var prefixes = {};
25 +
26 + prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
27 + prefixes['Webkit' + styleProp] = 'webkit' + eventName;
28 + prefixes['Moz' + styleProp] = 'moz' + eventName;
29 + prefixes['ms' + styleProp] = 'MS' + eventName;
30 + prefixes['O' + styleProp] = 'o' + eventName.toLowerCase();
31 +
32 + return prefixes;
33 +}
34 +
35 +/**
36 + * A list of event names to a configurable list of vendor prefixes.
37 + */
38 +var vendorPrefixes = {
39 + animationend: makePrefixMap('Animation', 'AnimationEnd'),
40 + animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
41 + animationstart: makePrefixMap('Animation', 'AnimationStart'),
42 + transitionend: makePrefixMap('Transition', 'TransitionEnd'),
43 +};
44 +
45 +/**
46 + * Event names that have already been detected and prefixed (if applicable).
47 + */
48 +var prefixedEventNames = {};
49 +
50 +/**
51 + * Element to check for prefixes on.
52 + */
53 +var style = {};
54 +
55 +/**
56 + * Bootstrap if a DOM exists.
57 + */
58 +if (ExecutionEnvironment.canUseDOM) {
59 + style = document.createElement('div').style;
60 +
61 + // On some platforms, in particular some releases of Android 4.x,
62 + // the un-prefixed "animation" and "transition" properties are defined on the
63 + // style object but the events that fire will still be prefixed, so we need
64 + // to check if the un-prefixed events are usable, and if not remove them from the map.
65 + if (!('AnimationEvent' in window)) {
66 + delete vendorPrefixes.animationend.animation;
67 + delete vendorPrefixes.animationiteration.animation;
68 + delete vendorPrefixes.animationstart.animation;
69 + }
70 +
71 + // Same as above
72 + if (!('TransitionEvent' in window)) {
73 + delete vendorPrefixes.transitionend.transition;
74 + }
75 +}
76 +
77 +/**
78 + * Attempts to determine the correct vendor prefixed event name.
79 + *
80 + * @param {string} eventName
81 + * @returns {string}
82 + */
83 +function getVendorPrefixedEventName(eventName) {
84 + if (prefixedEventNames[eventName]) {
85 + return prefixedEventNames[eventName];
86 +
87 + } else if (!vendorPrefixes[eventName]) {
88 + return eventName;
89 + }
90 +
91 + var prefixMap = vendorPrefixes[eventName];
92 +
93 + for (var styleProp in prefixMap) {
94 + if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
95 + return prefixedEventNames[eventName] = prefixMap[styleProp];
96 + }
97 + }
98 +
99 + return '';
100 +}
101 +
102 +module.exports = getVendorPrefixedEventName;
src/renderers/shared/event/EventConstants.js
+4
@@ -20,6 +20,9 @@ var PropagationPhases = keyMirror({bubbled: null, captured: null});
20 */
21 var topLevelTypes = keyMirror({
22 topAbort: null,
23 + topAnimationEnd: null,
24 + topAnimationIteration: null,
25 + topAnimationStart: null,
26 topBlur: null,
27 topCanPlay: null,
28 topCanPlayThrough: null,
@@ -80,6 +83,7 @@ var topLevelTypes = keyMirror({
83 topTouchEnd: null,
84 topTouchMove: null,
85 topTouchStart: null,
86 + topTransitionEnd: null,
87 topVolumeChange: null,
88 topWaiting: null,
89 topWheel: null,