1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. import {
    
  13.   buttonType,
    
  14.   buttonsType,
    
  15.   defaultPointerSize,
    
  16.   defaultBrowserChromeSize,
    
  17. } from './constants';
    
  18. 
    
  19. /**
    
  20.  * Native event object mocks for higher-level events.
    
  21.  *
    
  22.  * 1. Each event type defines the exact object that it accepts. This ensures
    
  23.  * that no arbitrary properties can be assigned to events, and the properties
    
  24.  * that don't exist on specific event types (e.g., 'pointerType') are not added
    
  25.  * to the respective native event.
    
  26.  *
    
  27.  * 2. Properties that cannot be relied on due to inconsistent browser support (e.g., 'x' and 'y') are not
    
  28.  * added to the native event. Others that shouldn't be arbitrarily customized (e.g., 'screenX')
    
  29.  * are automatically inferred from associated values.
    
  30.  *
    
  31.  * 3. PointerEvent and TouchEvent fields are normalized (e.g., 'rotationAngle' -> 'twist')
    
  32.  */
    
  33. 
    
  34. function emptyFunction() {}
    
  35. 
    
  36. function createEvent(type, data = {}) {
    
  37.   const event = document.createEvent('CustomEvent');
    
  38.   event.initCustomEvent(type, true, true);
    
  39.   if (data != null) {
    
  40.     Object.keys(data).forEach(key => {
    
  41.       const value = data[key];
    
  42.       if (key === 'timeStamp' && !value) {
    
  43.         return;
    
  44.       }
    
  45.       Object.defineProperty(event, key, {value});
    
  46.     });
    
  47.   }
    
  48.   return event;
    
  49. }
    
  50. 
    
  51. function createGetModifierState(keyArg, data) {
    
  52.   if (keyArg === 'Alt') {
    
  53.     return data.altKey || false;
    
  54.   }
    
  55.   if (keyArg === 'Control') {
    
  56.     return data.ctrlKey || false;
    
  57.   }
    
  58.   if (keyArg === 'Meta') {
    
  59.     return data.metaKey || false;
    
  60.   }
    
  61.   if (keyArg === 'Shift') {
    
  62.     return data.shiftKey || false;
    
  63.   }
    
  64. }
    
  65. 
    
  66. function createPointerEvent(
    
  67.   type,
    
  68.   {
    
  69.     altKey = false,
    
  70.     button = buttonType.none,
    
  71.     buttons = buttonsType.none,
    
  72.     ctrlKey = false,
    
  73.     detail = 1,
    
  74.     height,
    
  75.     metaKey = false,
    
  76.     movementX = 0,
    
  77.     movementY = 0,
    
  78.     offsetX = 0,
    
  79.     offsetY = 0,
    
  80.     pageX,
    
  81.     pageY,
    
  82.     pointerId,
    
  83.     pressure = 0,
    
  84.     preventDefault = emptyFunction,
    
  85.     pointerType = 'mouse',
    
  86.     screenX,
    
  87.     screenY,
    
  88.     shiftKey = false,
    
  89.     tangentialPressure = 0,
    
  90.     tiltX = 0,
    
  91.     tiltY = 0,
    
  92.     timeStamp,
    
  93.     twist = 0,
    
  94.     width,
    
  95.     x = 0,
    
  96.     y = 0,
    
  97.   } = {},
    
  98. ) {
    
  99.   const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
    
  100.   const isMouse = pointerType === 'mouse';
    
  101. 
    
  102.   return createEvent(type, {
    
  103.     altKey,
    
  104.     button,
    
  105.     buttons,
    
  106.     clientX: x,
    
  107.     clientY: y,
    
  108.     ctrlKey,
    
  109.     detail,
    
  110.     getModifierState(keyArg) {
    
  111.       return createGetModifierState(keyArg, modifierState);
    
  112.     },
    
  113.     height: isMouse ? 1 : height != null ? height : defaultPointerSize,
    
  114.     metaKey,
    
  115.     movementX,
    
  116.     movementY,
    
  117.     offsetX,
    
  118.     offsetY,
    
  119.     pageX: pageX || x,
    
  120.     pageY: pageY || y,
    
  121.     pointerId,
    
  122.     pointerType,
    
  123.     pressure,
    
  124.     preventDefault,
    
  125.     releasePointerCapture: emptyFunction,
    
  126.     screenX: screenX === 0 ? screenX : x,
    
  127.     screenY: screenY === 0 ? screenY : y + defaultBrowserChromeSize,
    
  128.     setPointerCapture: emptyFunction,
    
  129.     shiftKey,
    
  130.     tangentialPressure,
    
  131.     tiltX,
    
  132.     tiltY,
    
  133.     timeStamp,
    
  134.     twist,
    
  135.     width: isMouse ? 1 : width != null ? width : defaultPointerSize,
    
  136.   });
    
  137. }
    
  138. 
    
  139. function createKeyboardEvent(
    
  140.   type,
    
  141.   {
    
  142.     altKey = false,
    
  143.     ctrlKey = false,
    
  144.     isComposing = false,
    
  145.     key = '',
    
  146.     metaKey = false,
    
  147.     preventDefault = emptyFunction,
    
  148.     shiftKey = false,
    
  149.   } = {},
    
  150. ) {
    
  151.   const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
    
  152. 
    
  153.   return createEvent(type, {
    
  154.     altKey,
    
  155.     ctrlKey,
    
  156.     getModifierState(keyArg) {
    
  157.       return createGetModifierState(keyArg, modifierState);
    
  158.     },
    
  159.     isComposing,
    
  160.     key,
    
  161.     metaKey,
    
  162.     preventDefault,
    
  163.     shiftKey,
    
  164.   });
    
  165. }
    
  166. 
    
  167. function createMouseEvent(
    
  168.   type,
    
  169.   {
    
  170.     altKey = false,
    
  171.     button = buttonType.none,
    
  172.     buttons = buttonsType.none,
    
  173.     ctrlKey = false,
    
  174.     detail = 1,
    
  175.     metaKey = false,
    
  176.     movementX = 0,
    
  177.     movementY = 0,
    
  178.     offsetX = 0,
    
  179.     offsetY = 0,
    
  180.     pageX,
    
  181.     pageY,
    
  182.     preventDefault = emptyFunction,
    
  183.     screenX,
    
  184.     screenY,
    
  185.     shiftKey = false,
    
  186.     timeStamp,
    
  187.     x = 0,
    
  188.     y = 0,
    
  189.   } = {},
    
  190. ) {
    
  191.   const modifierState = {altKey, ctrlKey, metaKey, shiftKey};
    
  192. 
    
  193.   return createEvent(type, {
    
  194.     altKey,
    
  195.     button,
    
  196.     buttons,
    
  197.     clientX: x,
    
  198.     clientY: y,
    
  199.     ctrlKey,
    
  200.     detail,
    
  201.     getModifierState(keyArg) {
    
  202.       return createGetModifierState(keyArg, modifierState);
    
  203.     },
    
  204.     metaKey,
    
  205.     movementX,
    
  206.     movementY,
    
  207.     offsetX,
    
  208.     offsetY,
    
  209.     pageX: pageX || x,
    
  210.     pageY: pageY || y,
    
  211.     preventDefault,
    
  212.     screenX: screenX === 0 ? screenX : x,
    
  213.     screenY: screenY === 0 ? screenY : y + defaultBrowserChromeSize,
    
  214.     shiftKey,
    
  215.     timeStamp,
    
  216.   });
    
  217. }
    
  218. 
    
  219. function createTouchEvent(type, payload) {
    
  220.   return createEvent(type, {
    
  221.     ...payload,
    
  222.     detail: 0,
    
  223.     sourceCapabilities: {
    
  224.       firesTouchEvents: true,
    
  225.     },
    
  226.   });
    
  227. }
    
  228. 
    
  229. /**
    
  230.  * Mock event objects
    
  231.  */
    
  232. 
    
  233. export function blur({relatedTarget} = {}) {
    
  234.   return new FocusEvent('blur', {relatedTarget});
    
  235. }
    
  236. 
    
  237. export function focusOut({relatedTarget} = {}) {
    
  238.   return new FocusEvent('focusout', {relatedTarget, bubbles: true});
    
  239. }
    
  240. 
    
  241. export function click(payload) {
    
  242.   return createMouseEvent('click', {
    
  243.     button: buttonType.primary,
    
  244.     ...payload,
    
  245.   });
    
  246. }
    
  247. 
    
  248. export function contextmenu(payload) {
    
  249.   return createMouseEvent('contextmenu', {
    
  250.     ...payload,
    
  251.     detail: 0,
    
  252.   });
    
  253. }
    
  254. 
    
  255. export function dragstart(payload) {
    
  256.   return createMouseEvent('dragstart', {
    
  257.     ...payload,
    
  258.     detail: 0,
    
  259.   });
    
  260. }
    
  261. 
    
  262. export function focus({relatedTarget} = {}) {
    
  263.   return new FocusEvent('focus', {relatedTarget});
    
  264. }
    
  265. 
    
  266. export function focusIn({relatedTarget} = {}) {
    
  267.   return new FocusEvent('focusin', {relatedTarget, bubbles: true});
    
  268. }
    
  269. 
    
  270. export function scroll() {
    
  271.   return createEvent('scroll');
    
  272. }
    
  273. 
    
  274. export function virtualclick(payload) {
    
  275.   return createMouseEvent('click', {
    
  276.     button: 0,
    
  277.     ...payload,
    
  278.     buttons: 0,
    
  279.     detail: 0,
    
  280.     height: 1,
    
  281.     pageX: 0,
    
  282.     pageY: 0,
    
  283.     pressure: 0,
    
  284.     screenX: 0,
    
  285.     screenY: 0,
    
  286.     width: 1,
    
  287.     x: 0,
    
  288.     y: 0,
    
  289.   });
    
  290. }
    
  291. 
    
  292. /**
    
  293.  * Key events
    
  294.  */
    
  295. 
    
  296. export function keydown(payload) {
    
  297.   return createKeyboardEvent('keydown', payload);
    
  298. }
    
  299. 
    
  300. export function keyup(payload) {
    
  301.   return createKeyboardEvent('keyup', payload);
    
  302. }
    
  303. 
    
  304. /**
    
  305.  * Pointer events
    
  306.  */
    
  307. 
    
  308. export function gotpointercapture(payload) {
    
  309.   return createPointerEvent('gotpointercapture', payload);
    
  310. }
    
  311. 
    
  312. export function lostpointercapture(payload) {
    
  313.   return createPointerEvent('lostpointercapture', payload);
    
  314. }
    
  315. 
    
  316. export function pointercancel(payload) {
    
  317.   return createPointerEvent('pointercancel', {
    
  318.     ...payload,
    
  319.     buttons: 0,
    
  320.     detail: 0,
    
  321.     height: 1,
    
  322.     pageX: 0,
    
  323.     pageY: 0,
    
  324.     pressure: 0,
    
  325.     screenX: 0,
    
  326.     screenY: 0,
    
  327.     width: 1,
    
  328.     x: 0,
    
  329.     y: 0,
    
  330.   });
    
  331. }
    
  332. 
    
  333. export function pointerdown(payload) {
    
  334.   const isTouch = payload != null && payload.pointerType === 'touch';
    
  335.   return createPointerEvent('pointerdown', {
    
  336.     button: buttonType.primary,
    
  337.     buttons: buttonsType.primary,
    
  338.     pressure: isTouch ? 1 : 0.5,
    
  339.     ...payload,
    
  340.   });
    
  341. }
    
  342. 
    
  343. export function pointerenter(payload) {
    
  344.   return createPointerEvent('pointerenter', payload);
    
  345. }
    
  346. 
    
  347. export function pointerleave(payload) {
    
  348.   return createPointerEvent('pointerleave', payload);
    
  349. }
    
  350. 
    
  351. export function pointermove(payload) {
    
  352.   return createPointerEvent('pointermove', {
    
  353.     ...payload,
    
  354.     button: buttonType.none,
    
  355.   });
    
  356. }
    
  357. 
    
  358. export function pointerout(payload) {
    
  359.   return createPointerEvent('pointerout', payload);
    
  360. }
    
  361. 
    
  362. export function pointerover(payload) {
    
  363.   return createPointerEvent('pointerover', payload);
    
  364. }
    
  365. 
    
  366. export function pointerup(payload) {
    
  367.   return createPointerEvent('pointerup', {
    
  368.     button: buttonType.primary,
    
  369.     ...payload,
    
  370.     buttons: buttonsType.none,
    
  371.     pressure: 0,
    
  372.   });
    
  373. }
    
  374. 
    
  375. /**
    
  376.  * Mouse events
    
  377.  */
    
  378. 
    
  379. export function mousedown(payload) {
    
  380.   // The value of 'button' and 'buttons' for 'mousedown' must not be none.
    
  381.   const button =
    
  382.     payload == null || payload.button === buttonType.none
    
  383.       ? buttonType.primary
    
  384.       : payload.button;
    
  385.   const buttons =
    
  386.     payload == null || payload.buttons === buttonsType.none
    
  387.       ? buttonsType.primary
    
  388.       : payload.buttons;
    
  389.   return createMouseEvent('mousedown', {
    
  390.     ...payload,
    
  391.     button,
    
  392.     buttons,
    
  393.   });
    
  394. }
    
  395. 
    
  396. export function mouseenter(payload) {
    
  397.   return createMouseEvent('mouseenter', payload);
    
  398. }
    
  399. 
    
  400. export function mouseleave(payload) {
    
  401.   return createMouseEvent('mouseleave', payload);
    
  402. }
    
  403. 
    
  404. export function mousemove(payload) {
    
  405.   return createMouseEvent('mousemove', payload);
    
  406. }
    
  407. 
    
  408. export function mouseout(payload) {
    
  409.   return createMouseEvent('mouseout', payload);
    
  410. }
    
  411. 
    
  412. export function mouseover(payload) {
    
  413.   return createMouseEvent('mouseover', payload);
    
  414. }
    
  415. 
    
  416. export function mouseup(payload) {
    
  417.   return createMouseEvent('mouseup', {
    
  418.     button: buttonType.primary,
    
  419.     ...payload,
    
  420.     buttons: buttonsType.none,
    
  421.   });
    
  422. }
    
  423. 
    
  424. /**
    
  425.  * Touch events
    
  426.  */
    
  427. 
    
  428. export function touchcancel(payload) {
    
  429.   return createTouchEvent('touchcancel', payload);
    
  430. }
    
  431. 
    
  432. export function touchend(payload) {
    
  433.   return createTouchEvent('touchend', payload);
    
  434. }
    
  435. 
    
  436. export function touchmove(payload) {
    
  437.   return createTouchEvent('touchmove', payload);
    
  438. }
    
  439. 
    
  440. export function touchstart(payload) {
    
  441.   return createTouchEvent('touchstart', payload);
    
  442. }