SideEffect.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import { __spreadArray } from "tslib";
  2. import * as React from 'react';
  3. import { RemoveScrollBar } from 'react-remove-scroll-bar';
  4. import { styleSingleton } from 'react-style-singleton';
  5. import { nonPassive } from './aggresiveCapture';
  6. import { handleScroll, locationCouldBeScrolled } from './handleScroll';
  7. export var getTouchXY = function (event) {
  8. return 'changedTouches' in event ? [event.changedTouches[0].clientX, event.changedTouches[0].clientY] : [0, 0];
  9. };
  10. export var getDeltaXY = function (event) { return [event.deltaX, event.deltaY]; };
  11. var extractRef = function (ref) {
  12. return ref && 'current' in ref ? ref.current : ref;
  13. };
  14. var deltaCompare = function (x, y) { return x[0] === y[0] && x[1] === y[1]; };
  15. var generateStyle = function (id) { return "\n .block-interactivity-".concat(id, " {pointer-events: none;}\n .allow-interactivity-").concat(id, " {pointer-events: all;}\n"); };
  16. var idCounter = 0;
  17. var lockStack = [];
  18. export function RemoveScrollSideCar(props) {
  19. var shouldPreventQueue = React.useRef([]);
  20. var touchStartRef = React.useRef([0, 0]);
  21. var activeAxis = React.useRef();
  22. var id = React.useState(idCounter++)[0];
  23. var Style = React.useState(function () { return styleSingleton(); })[0];
  24. var lastProps = React.useRef(props);
  25. React.useEffect(function () {
  26. lastProps.current = props;
  27. }, [props]);
  28. React.useEffect(function () {
  29. if (props.inert) {
  30. document.body.classList.add("block-interactivity-".concat(id));
  31. var allow_1 = __spreadArray([props.lockRef.current], (props.shards || []).map(extractRef), true).filter(Boolean);
  32. allow_1.forEach(function (el) { return el.classList.add("allow-interactivity-".concat(id)); });
  33. return function () {
  34. document.body.classList.remove("block-interactivity-".concat(id));
  35. allow_1.forEach(function (el) { return el.classList.remove("allow-interactivity-".concat(id)); });
  36. };
  37. }
  38. return;
  39. }, [props.inert, props.lockRef.current, props.shards]);
  40. var shouldCancelEvent = React.useCallback(function (event, parent) {
  41. if ('touches' in event && event.touches.length === 2) {
  42. return !lastProps.current.allowPinchZoom;
  43. }
  44. var touch = getTouchXY(event);
  45. var touchStart = touchStartRef.current;
  46. var deltaX = 'deltaX' in event ? event.deltaX : touchStart[0] - touch[0];
  47. var deltaY = 'deltaY' in event ? event.deltaY : touchStart[1] - touch[1];
  48. var currentAxis;
  49. var target = event.target;
  50. var moveDirection = Math.abs(deltaX) > Math.abs(deltaY) ? 'h' : 'v';
  51. // allow horizontal touch move on Range inputs. They will not cause any scroll
  52. if ('touches' in event && moveDirection === 'h' && target.type === 'range') {
  53. return false;
  54. }
  55. var canBeScrolledInMainDirection = locationCouldBeScrolled(moveDirection, target);
  56. if (!canBeScrolledInMainDirection) {
  57. return true;
  58. }
  59. if (canBeScrolledInMainDirection) {
  60. currentAxis = moveDirection;
  61. }
  62. else {
  63. currentAxis = moveDirection === 'v' ? 'h' : 'v';
  64. canBeScrolledInMainDirection = locationCouldBeScrolled(moveDirection, target);
  65. // other axis might be not scrollable
  66. }
  67. if (!canBeScrolledInMainDirection) {
  68. return false;
  69. }
  70. if (!activeAxis.current && 'changedTouches' in event && (deltaX || deltaY)) {
  71. activeAxis.current = currentAxis;
  72. }
  73. if (!currentAxis) {
  74. return true;
  75. }
  76. var cancelingAxis = activeAxis.current || currentAxis;
  77. return handleScroll(cancelingAxis, parent, event, cancelingAxis === 'h' ? deltaX : deltaY, true);
  78. }, []);
  79. var shouldPrevent = React.useCallback(function (_event) {
  80. var event = _event;
  81. if (!lockStack.length || lockStack[lockStack.length - 1] !== Style) {
  82. // not the last active
  83. return;
  84. }
  85. var delta = 'deltaY' in event ? getDeltaXY(event) : getTouchXY(event);
  86. var sourceEvent = shouldPreventQueue.current.filter(function (e) { return e.name === event.type && e.target === event.target && deltaCompare(e.delta, delta); })[0];
  87. // self event, and should be canceled
  88. if (sourceEvent && sourceEvent.should) {
  89. if (event.cancelable) {
  90. event.preventDefault();
  91. }
  92. return;
  93. }
  94. // outside or shard event
  95. if (!sourceEvent) {
  96. var shardNodes = (lastProps.current.shards || [])
  97. .map(extractRef)
  98. .filter(Boolean)
  99. .filter(function (node) { return node.contains(event.target); });
  100. var shouldStop = shardNodes.length > 0 ? shouldCancelEvent(event, shardNodes[0]) : !lastProps.current.noIsolation;
  101. if (shouldStop) {
  102. if (event.cancelable) {
  103. event.preventDefault();
  104. }
  105. }
  106. }
  107. }, []);
  108. var shouldCancel = React.useCallback(function (name, delta, target, should) {
  109. var event = { name: name, delta: delta, target: target, should: should };
  110. shouldPreventQueue.current.push(event);
  111. setTimeout(function () {
  112. shouldPreventQueue.current = shouldPreventQueue.current.filter(function (e) { return e !== event; });
  113. }, 1);
  114. }, []);
  115. var scrollTouchStart = React.useCallback(function (event) {
  116. touchStartRef.current = getTouchXY(event);
  117. activeAxis.current = undefined;
  118. }, []);
  119. var scrollWheel = React.useCallback(function (event) {
  120. shouldCancel(event.type, getDeltaXY(event), event.target, shouldCancelEvent(event, props.lockRef.current));
  121. }, []);
  122. var scrollTouchMove = React.useCallback(function (event) {
  123. shouldCancel(event.type, getTouchXY(event), event.target, shouldCancelEvent(event, props.lockRef.current));
  124. }, []);
  125. React.useEffect(function () {
  126. lockStack.push(Style);
  127. props.setCallbacks({
  128. onScrollCapture: scrollWheel,
  129. onWheelCapture: scrollWheel,
  130. onTouchMoveCapture: scrollTouchMove,
  131. });
  132. document.addEventListener('wheel', shouldPrevent, nonPassive);
  133. document.addEventListener('touchmove', shouldPrevent, nonPassive);
  134. document.addEventListener('touchstart', scrollTouchStart, nonPassive);
  135. return function () {
  136. lockStack = lockStack.filter(function (inst) { return inst !== Style; });
  137. document.removeEventListener('wheel', shouldPrevent, nonPassive);
  138. document.removeEventListener('touchmove', shouldPrevent, nonPassive);
  139. document.removeEventListener('touchstart', scrollTouchStart, nonPassive);
  140. };
  141. }, []);
  142. var removeScrollBar = props.removeScrollBar, inert = props.inert;
  143. return (React.createElement(React.Fragment, null,
  144. inert ? React.createElement(Style, { styles: generateStyle(id) }) : null,
  145. removeScrollBar ? React.createElement(RemoveScrollBar, { gapMode: "margin" }) : null));
  146. }