diff --git a/src/browser/ReactEventEmitter.js b/src/browser/ReactEventEmitter.js index c09ed460dec31..27e4c039cab83 100644 --- a/src/browser/ReactEventEmitter.js +++ b/src/browser/ReactEventEmitter.js @@ -103,16 +103,24 @@ var topEventMapping = { topDragStart: 'dragstart', topDrop: 'drop', topFocus: 'focus', + topGotPointerCapture: 'gotpointercapture', topInput: 'input', topKeyDown: 'keydown', topKeyPress: 'keypress', topKeyUp: 'keyup', + topLostPointerCapture: 'lostpointercapture', topMouseDown: 'mousedown', topMouseMove: 'mousemove', topMouseOut: 'mouseout', topMouseOver: 'mouseover', topMouseUp: 'mouseup', topPaste: 'paste', + topPointerCancel: 'pointercancel', + topPointerDown: 'pointerdown', + topPointerMove: 'pointermove', + topPointerOut: 'pointerout', + topPointerOver: 'pointerover', + topPointerUp: 'pointerup', topScroll: 'scroll', topSelectionChange: 'selectionchange', topTextInput: 'textInput', diff --git a/src/browser/eventPlugins/EnterLeaveEventPlugin.js b/src/browser/eventPlugins/EnterLeaveEventPlugin.js index 6dbfaa4399566..97b7be2df6086 100644 --- a/src/browser/eventPlugins/EnterLeaveEventPlugin.js +++ b/src/browser/eventPlugins/EnterLeaveEventPlugin.js @@ -22,6 +22,7 @@ var EventConstants = require('EventConstants'); var EventPropagators = require('EventPropagators'); var SyntheticMouseEvent = require('SyntheticMouseEvent'); +var SyntheticPointerEvent = require('SyntheticPointerEvent'); var ReactMount = require('ReactMount'); var keyOf = require('keyOf'); @@ -43,6 +44,20 @@ var eventTypes = { topLevelTypes.topMouseOut, topLevelTypes.topMouseOver ] + }, + pointerEnter: { + registrationName: keyOf({onPointerEnter: null}), + dependencies: [ + topLevelTypes.topPointerOut, + topLevelTypes.topPointerOver + ] + }, + pointerLeave: { + registrationName: keyOf({onPointerLeave: null}), + dependencies: [ + topLevelTypes.topPointerOut, + topLevelTypes.topPointerOver + ] } }; @@ -71,13 +86,20 @@ var EnterLeaveEventPlugin = { topLevelTarget, topLevelTargetID, nativeEvent) { - if (topLevelType === topLevelTypes.topMouseOver && - (nativeEvent.relatedTarget || nativeEvent.fromElement)) { + var isOverEvent = ( + topLevelType === topLevelTypes.topMouseOver || + topLevelType === topLevelTypes.topPointerOver + ); + var isOutEvent = ( + topLevelType === topLevelTypes.topMouseOut || + topLevelType === topLevelTypes.topPointerOut + ); + + if (!isOverEvent && !isOutEvent) { + // Must not be a mouse/pointer in or out - ignoring. return null; } - if (topLevelType !== topLevelTypes.topMouseOut && - topLevelType !== topLevelTypes.topMouseOver) { - // Must not be a mouse in or mouse out - ignoring. + if (isOverEvent && (nativeEvent.relatedTarget || nativeEvent.fromElement)) { return null; } @@ -88,19 +110,16 @@ var EnterLeaveEventPlugin = { } else { // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8. var doc = topLevelTarget.ownerDocument; - if (doc) { - win = doc.defaultView || doc.parentWindow; - } else { - win = window; - } + win = doc ? doc.defaultView || doc.parentWindow : window; } var from, to; - if (topLevelType === topLevelTypes.topMouseOut) { + if (isOutEvent) { from = topLevelTarget; - to = + to = ( getFirstReactDOM(nativeEvent.relatedTarget || nativeEvent.toElement) || - win; + win + ); } else { from = win; to = topLevelTarget; @@ -111,24 +130,32 @@ var EnterLeaveEventPlugin = { return null; } + var eventInterface, leaveEventType, enterEventType, eventTypePrefix; + + if (topLevelType === topLevelTypes.topMouseOut || + topLevelType === topLevelTypes.topMouseOver) { + eventInterface = SyntheticMouseEvent; + leaveEventType = eventTypes.mouseLeave; + enterEventType = eventTypes.mouseEnter; + eventTypePrefix = 'mouse'; + } else if (topLevelType === topLevelTypes.topPointerOut || + topLevelType === topLevelTypes.topPointerOver) { + eventInterface = SyntheticPointerEvent; + leaveEventType = eventTypes.pointerLeave; + enterEventType = eventTypes.pointerEnter; + eventTypePrefix = 'pointer'; + } + var fromID = from ? ReactMount.getID(from) : ''; var toID = to ? ReactMount.getID(to) : ''; - var leave = SyntheticMouseEvent.getPooled( - eventTypes.mouseLeave, - fromID, - nativeEvent - ); - leave.type = 'mouseleave'; + var leave = eventInterface.getPooled(leaveEventType, fromID, nativeEvent); + leave.type = eventTypePrefix + 'leave'; leave.target = from; leave.relatedTarget = to; - var enter = SyntheticMouseEvent.getPooled( - eventTypes.mouseEnter, - toID, - nativeEvent - ); - enter.type = 'mouseenter'; + var enter = eventInterface.getPooled(enterEventType, fromID, nativeEvent); + enter.type = eventTypePrefix + 'enter'; enter.target = to; enter.relatedTarget = from; diff --git a/src/browser/eventPlugins/SimpleEventPlugin.js b/src/browser/eventPlugins/SimpleEventPlugin.js index c714793338359..2ce7acd6753ad 100644 --- a/src/browser/eventPlugins/SimpleEventPlugin.js +++ b/src/browser/eventPlugins/SimpleEventPlugin.js @@ -30,6 +30,7 @@ var SyntheticDragEvent = require('SyntheticDragEvent'); var SyntheticTouchEvent = require('SyntheticTouchEvent'); var SyntheticUIEvent = require('SyntheticUIEvent'); var SyntheticWheelEvent = require('SyntheticWheelEvent'); +var SyntheticPointerEvent = require('SyntheticPointerEvent'); var invariant = require('invariant'); var keyOf = require('keyOf'); @@ -127,6 +128,12 @@ var eventTypes = { captured: keyOf({onFocusCapture: true}) } }, + gotPointerCapture: { + phasedRegistrationNames: { + bubbled: keyOf({onGotPointerCapture: true}), + captured: keyOf({onGotPointerCaptureCapture: true}) + } + }, input: { phasedRegistrationNames: { bubbled: keyOf({onInput: true}), @@ -157,6 +164,12 @@ var eventTypes = { captured: keyOf({onLoadCapture: true}) } }, + lostPointerCapture: { + phasedRegistrationNames: { + bubbled: keyOf({onLostPointerCapture: true}), + captured: keyOf({onLostPointerCaptureCapture: true}) + } + }, error: { phasedRegistrationNames: { bubbled: keyOf({onError: true}), @@ -201,6 +214,42 @@ var eventTypes = { captured: keyOf({onPasteCapture: true}) } }, + pointerCancel: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerCancel: true}), + captured: keyOf({onPointerCancelCapture: true}) + } + }, + pointerDown: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerDown: true}), + captured: keyOf({onPointerDownCapture: true}) + } + }, + pointerMove: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerMove: true}), + captured: keyOf({onPointerMoveCapture: true}) + } + }, + pointerOut: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerOut: true}), + captured: keyOf({onPointerOutCapture: true}) + } + }, + pointerOver: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerOver: true}), + captured: keyOf({onPointerOverCapture: true}) + } + }, + pointerUp: { + phasedRegistrationNames: { + bubbled: keyOf({onPointerUp: true}), + captured: keyOf({onPointerUpCapture: true}) + } + }, reset: { phasedRegistrationNames: { bubbled: keyOf({onReset: true}), @@ -252,41 +301,49 @@ var eventTypes = { }; var topLevelEventsToDispatchConfig = { - topBlur: eventTypes.blur, - topClick: eventTypes.click, + topBlur: eventTypes.blur, + topClick: eventTypes.click, topContextMenu: eventTypes.contextMenu, - topCopy: eventTypes.copy, - topCut: eventTypes.cut, + topCopy: eventTypes.copy, + topCut: eventTypes.cut, topDoubleClick: eventTypes.doubleClick, - topDrag: eventTypes.drag, - topDragEnd: eventTypes.dragEnd, - topDragEnter: eventTypes.dragEnter, - topDragExit: eventTypes.dragExit, - topDragLeave: eventTypes.dragLeave, - topDragOver: eventTypes.dragOver, - topDragStart: eventTypes.dragStart, - topDrop: eventTypes.drop, - topError: eventTypes.error, - topFocus: eventTypes.focus, - topInput: eventTypes.input, - topKeyDown: eventTypes.keyDown, - topKeyPress: eventTypes.keyPress, - topKeyUp: eventTypes.keyUp, - topLoad: eventTypes.load, - topMouseDown: eventTypes.mouseDown, - topMouseMove: eventTypes.mouseMove, - topMouseOut: eventTypes.mouseOut, - topMouseOver: eventTypes.mouseOver, - topMouseUp: eventTypes.mouseUp, - topPaste: eventTypes.paste, - topReset: eventTypes.reset, - topScroll: eventTypes.scroll, - topSubmit: eventTypes.submit, + topDrag: eventTypes.drag, + topDragEnd: eventTypes.dragEnd, + topDragEnter: eventTypes.dragEnter, + topDragExit: eventTypes.dragExit, + topDragLeave: eventTypes.dragLeave, + topDragOver: eventTypes.dragOver, + topDragStart: eventTypes.dragStart, + topDrop: eventTypes.drop, + topError: eventTypes.error, + topFocus: eventTypes.focus, + topGotPointerCapture: eventTypes.gotPointerCapture, + topInput: eventTypes.input, + topKeyDown: eventTypes.keyDown, + topKeyPress: eventTypes.keyPress, + topKeyUp: eventTypes.keyUp, + topLoad: eventTypes.load, + topLostPointerCapture: eventTypes.lostPointerCapture, + topMouseDown: eventTypes.mouseDown, + topMouseMove: eventTypes.mouseMove, + topMouseOut: eventTypes.mouseOut, + topMouseOver: eventTypes.mouseOver, + topMouseUp: eventTypes.mouseUp, + topPaste: eventTypes.paste, + topPointerCancel: eventTypes.pointerCancel, + topPointerDown: eventTypes.pointerDown, + topPointerMove: eventTypes.pointerMove, + topPointerOut: eventTypes.pointerOut, + topPointerOver: eventTypes.pointerOver, + topPointerUp: eventTypes.pointerUp, + topReset: eventTypes.reset, + topScroll: eventTypes.scroll, + topSubmit: eventTypes.submit, topTouchCancel: eventTypes.touchCancel, - topTouchEnd: eventTypes.touchEnd, - topTouchMove: eventTypes.touchMove, - topTouchStart: eventTypes.touchStart, - topWheel: eventTypes.wheel + topTouchEnd: eventTypes.touchEnd, + topTouchMove: eventTypes.touchMove, + topTouchStart: eventTypes.touchStart, + topWheel: eventTypes.wheel }; for (var topLevelType in topLevelEventsToDispatchConfig) { @@ -366,6 +423,16 @@ var SimpleEventPlugin = { case topLevelTypes.topMouseUp: EventConstructor = SyntheticMouseEvent; break; + case topLevelTypes.topPointerCancel: + case topLevelTypes.topPointerDown: + case topLevelTypes.topPointerMove: + case topLevelTypes.topPointerOut: + case topLevelTypes.topPointerOver: + case topLevelTypes.topPointerUp: + case topLevelTypes.topGotPointerCapture: + case topLevelTypes.topLostPointerCapture: + EventConstructor = SyntheticPointerEvent; + break; case topLevelTypes.topDrag: case topLevelTypes.topDragEnd: case topLevelTypes.topDragEnter: diff --git a/src/browser/eventPlugins/TapEventPlugin.js b/src/browser/eventPlugins/TapEventPlugin.js index 6db72af8a9602..72f8408b517a6 100644 --- a/src/browser/eventPlugins/TapEventPlugin.js +++ b/src/browser/eventPlugins/TapEventPlugin.js @@ -66,7 +66,11 @@ function getDistance(coords, nativeEvent) { var dependencies = [ topLevelTypes.topMouseDown, topLevelTypes.topMouseMove, - topLevelTypes.topMouseUp + topLevelTypes.topMouseUp, + topLevelTypes.topPointerCancel, + topLevelTypes.topPointerDown, + topLevelTypes.topPointerMove, + topLevelTypes.topPointerUp ]; if (EventPluginUtils.useTouchEvents) { diff --git a/src/browser/syntheticEvents/SyntheticPointerEvent.js b/src/browser/syntheticEvents/SyntheticPointerEvent.js new file mode 100644 index 0000000000000..7b03956610c7e --- /dev/null +++ b/src/browser/syntheticEvents/SyntheticPointerEvent.js @@ -0,0 +1,51 @@ +/** + * Copyright 2013-2014 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @providesModule SyntheticPointerEvent + * @typechecks static-only + */ + +"use strict"; + +var SyntheticMouseEvent = require('SyntheticMouseEvent'); + +/** + * @interface PointerEvent + * @see http://www.w3.org/TR/pointerevents/ + */ +var PointerEventInterface = { + pointerId: null, + width: null, + height: null, + pressure: null, + tiltX: null, + tiltY: null, + pointerType: null, + isPrimary: null +}; + +/** + * @param {object} dispatchConfig Configuration used to dispatch this event. + * @param {string} dispatchMarker Marker identifying the event target. + * @param {object} nativeEvent Native browser event. + * @extends {SyntheticMouseEvent} + */ +function SyntheticPointerEvent(dispatchConfig, dispatchMarker, nativeEvent) { + SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent); +} + +SyntheticMouseEvent.augmentClass(SyntheticPointerEvent, PointerEventInterface); + +module.exports = SyntheticPointerEvent; diff --git a/src/event/EventConstants.js b/src/event/EventConstants.js index ee8508340f13c..286e68f9f3a38 100644 --- a/src/event/EventConstants.js +++ b/src/event/EventConstants.js @@ -46,17 +46,27 @@ var topLevelTypes = keyMirror({ topDrop: null, topError: null, topFocus: null, + topGotPointerCapture: null, topInput: null, topKeyDown: null, topKeyPress: null, topKeyUp: null, topLoad: null, + topLostPointerCapture: null, topMouseDown: null, topMouseMove: null, topMouseOut: null, topMouseOver: null, topMouseUp: null, topPaste: null, + topPointerCancel: null, + topPointerDown: null, + topPointerEnter: null, + topPointerLeave: null, + topPointerMove: null, + topPointerOut: null, + topPointerOver: null, + topPointerUp: null, topReset: null, topScroll: null, topSelectionChange: null, diff --git a/src/event/EventPluginUtils.js b/src/event/EventPluginUtils.js index 5f2715ab9a724..70b0441b43033 100644 --- a/src/event/EventPluginUtils.js +++ b/src/event/EventPluginUtils.js @@ -48,16 +48,20 @@ var topLevelTypes = EventConstants.topLevelTypes; function isEndish(topLevelType) { return topLevelType === topLevelTypes.topMouseUp || + topLevelType === topLevelTypes.topPointerUp || + topLevelType === topLevelTypes.topPointerCancel || topLevelType === topLevelTypes.topTouchEnd || topLevelType === topLevelTypes.topTouchCancel; } function isMoveish(topLevelType) { return topLevelType === topLevelTypes.topMouseMove || + topLevelType === topLevelTypes.topPointerMove || topLevelType === topLevelTypes.topTouchMove; } function isStartish(topLevelType) { return topLevelType === topLevelTypes.topMouseDown || + topLevelType === topLevelTypes.topPointerDown || topLevelType === topLevelTypes.topTouchStart; }