@planningcenter/react-beautiful-dnd 13.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +13 -0
  3. package/README.md +178 -0
  4. package/dist/react-beautiful-dnd.cjs.js +8728 -0
  5. package/dist/react-beautiful-dnd.cjs.js.flow +3 -0
  6. package/dist/react-beautiful-dnd.esm.js +8715 -0
  7. package/dist/react-beautiful-dnd.js +11726 -0
  8. package/dist/react-beautiful-dnd.min.js +1 -0
  9. package/package.json +155 -0
  10. package/src/animation.js +75 -0
  11. package/src/debug/middleware/action-timing-average.js +52 -0
  12. package/src/debug/middleware/action-timing.js +16 -0
  13. package/src/debug/middleware/log.js +26 -0
  14. package/src/debug/middleware/user-timing.js +16 -0
  15. package/src/debug/timings.js +86 -0
  16. package/src/dev-warning.js +50 -0
  17. package/src/empty.js +6 -0
  18. package/src/index.js +67 -0
  19. package/src/invariant.js +33 -0
  20. package/src/native-with-fallback.js +69 -0
  21. package/src/screen-reader-message-preset.js +134 -0
  22. package/src/state/action-creators.js +328 -0
  23. package/src/state/auto-scroller/auto-scroller-types.js +8 -0
  24. package/src/state/auto-scroller/can-scroll.js +160 -0
  25. package/src/state/auto-scroller/fluid-scroller/config.js +25 -0
  26. package/src/state/auto-scroller/fluid-scroller/did-start-in-scrollable-area.js +1 -0
  27. package/src/state/auto-scroller/fluid-scroller/get-best-scrollable-droppable.js +77 -0
  28. package/src/state/auto-scroller/fluid-scroller/get-droppable-scroll-change.js +50 -0
  29. package/src/state/auto-scroller/fluid-scroller/get-percentage.js +25 -0
  30. package/src/state/auto-scroller/fluid-scroller/get-scroll/adjust-for-size-limits.js +30 -0
  31. package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/calc-axis-scroll-conditions.js +68 -0
  32. package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/get-scroll-conditions.js +56 -0
  33. package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/index.js +66 -0
  34. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/dampen-value-by-time.js +48 -0
  35. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-distance-thresholds.js +31 -0
  36. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-value-from-distance.js +67 -0
  37. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-value.js +51 -0
  38. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/index.js +50 -0
  39. package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/min-scroll.js +4 -0
  40. package/src/state/auto-scroller/fluid-scroller/get-scroll/index.js +139 -0
  41. package/src/state/auto-scroller/fluid-scroller/get-window-scroll-change.js +43 -0
  42. package/src/state/auto-scroller/fluid-scroller/index.js +99 -0
  43. package/src/state/auto-scroller/fluid-scroller/scroll.js +80 -0
  44. package/src/state/auto-scroller/index.js +59 -0
  45. package/src/state/auto-scroller/jump-scroller.js +139 -0
  46. package/src/state/axis.js +26 -0
  47. package/src/state/calculate-drag-impact/calculate-reorder-impact.js +136 -0
  48. package/src/state/can-start-drag.js +29 -0
  49. package/src/state/create-store.js +97 -0
  50. package/src/state/did-start-after-critical.js +9 -0
  51. package/src/state/dimension-marshal/dimension-marshal-types.js +46 -0
  52. package/src/state/dimension-marshal/dimension-marshal.js +218 -0
  53. package/src/state/dimension-marshal/get-initial-publish.js +66 -0
  54. package/src/state/dimension-marshal/while-dragging-publisher.js +146 -0
  55. package/src/state/dimension-structures.js +35 -0
  56. package/src/state/droppable/get-droppable.js +101 -0
  57. package/src/state/droppable/is-home-of.js +7 -0
  58. package/src/state/droppable/scroll-droppable.js +53 -0
  59. package/src/state/droppable/should-use-placeholder.js +7 -0
  60. package/src/state/droppable/util/clip.js +17 -0
  61. package/src/state/droppable/util/get-subject.js +63 -0
  62. package/src/state/droppable/what-is-dragged-over-from-result.js +16 -0
  63. package/src/state/droppable/what-is-dragged-over.js +16 -0
  64. package/src/state/droppable/with-placeholder.js +174 -0
  65. package/src/state/get-center-from-impact/get-client-border-box-center/get-client-from-page-border-box-center.js +29 -0
  66. package/src/state/get-center-from-impact/get-client-border-box-center/index.js +44 -0
  67. package/src/state/get-center-from-impact/get-page-border-box-center/index.js +70 -0
  68. package/src/state/get-center-from-impact/get-page-border-box-center/when-combining.js +39 -0
  69. package/src/state/get-center-from-impact/get-page-border-box-center/when-reordering.js +112 -0
  70. package/src/state/get-center-from-impact/move-relative-to.js +66 -0
  71. package/src/state/get-combined-item-displacement.js +34 -0
  72. package/src/state/get-displaced-by.js +17 -0
  73. package/src/state/get-displacement-groups.js +130 -0
  74. package/src/state/get-drag-impact/get-combine-impact.js +130 -0
  75. package/src/state/get-drag-impact/get-reorder-impact.js +143 -0
  76. package/src/state/get-drag-impact/index.js +93 -0
  77. package/src/state/get-draggables-inside-droppable.js +31 -0
  78. package/src/state/get-droppable-over.js +158 -0
  79. package/src/state/get-frame.js +10 -0
  80. package/src/state/get-home-location.js +7 -0
  81. package/src/state/get-impact-location.js +16 -0
  82. package/src/state/get-is-displaced.js +11 -0
  83. package/src/state/get-lift-effect.js +82 -0
  84. package/src/state/get-max-scroll.js +30 -0
  85. package/src/state/is-movement-allowed.js +6 -0
  86. package/src/state/is-within.js +9 -0
  87. package/src/state/middleware/auto-scroll.js +38 -0
  88. package/src/state/middleware/dimension-marshal-stopper.js +20 -0
  89. package/src/state/middleware/drop/drop-animation-finish-middleware.js +21 -0
  90. package/src/state/middleware/drop/drop-animation-flush-on-scroll-middleware.js +63 -0
  91. package/src/state/middleware/drop/drop-middleware.js +146 -0
  92. package/src/state/middleware/drop/get-drop-duration.js +47 -0
  93. package/src/state/middleware/drop/get-drop-impact.js +77 -0
  94. package/src/state/middleware/drop/get-new-home-client-offset.js +54 -0
  95. package/src/state/middleware/drop/index.js +2 -0
  96. package/src/state/middleware/focus.js +42 -0
  97. package/src/state/middleware/lift.js +66 -0
  98. package/src/state/middleware/pending-drop.js +37 -0
  99. package/src/state/middleware/responders/async-marshal.js +55 -0
  100. package/src/state/middleware/responders/expiring-announce.js +44 -0
  101. package/src/state/middleware/responders/index.js +2 -0
  102. package/src/state/middleware/responders/is-equal.js +57 -0
  103. package/src/state/middleware/responders/publisher.js +253 -0
  104. package/src/state/middleware/responders/responders-middleware.js +75 -0
  105. package/src/state/middleware/scroll-listener.js +31 -0
  106. package/src/state/middleware/style.js +22 -0
  107. package/src/state/middleware/util/validate-dimensions.js +71 -0
  108. package/src/state/move-in-direction/index.js +84 -0
  109. package/src/state/move-in-direction/move-cross-axis/get-best-cross-axis-droppable.js +168 -0
  110. package/src/state/move-in-direction/move-cross-axis/get-closest-draggable.js +79 -0
  111. package/src/state/move-in-direction/move-cross-axis/index.js +109 -0
  112. package/src/state/move-in-direction/move-cross-axis/move-to-new-droppable.js +121 -0
  113. package/src/state/move-in-direction/move-cross-axis/without-starting-displacement.js +31 -0
  114. package/src/state/move-in-direction/move-in-direction-types.js +9 -0
  115. package/src/state/move-in-direction/move-to-next-place/index.js +132 -0
  116. package/src/state/move-in-direction/move-to-next-place/is-totally-visible-in-new-location.js +52 -0
  117. package/src/state/move-in-direction/move-to-next-place/move-to-next-combine/index.js +97 -0
  118. package/src/state/move-in-direction/move-to-next-place/move-to-next-index/from-combine.js +52 -0
  119. package/src/state/move-in-direction/move-to-next-place/move-to-next-index/from-reorder.js +43 -0
  120. package/src/state/move-in-direction/move-to-next-place/move-to-next-index/index.js +86 -0
  121. package/src/state/no-impact.js +33 -0
  122. package/src/state/patch-dimension-map.js +11 -0
  123. package/src/state/patch-droppable-map.js +10 -0
  124. package/src/state/position.js +58 -0
  125. package/src/state/post-reducer/when-moving/refresh-snap.js +67 -0
  126. package/src/state/post-reducer/when-moving/update.js +120 -0
  127. package/src/state/publish-while-dragging-in-virtual/adjust-additions-for-scroll-changes.js +58 -0
  128. package/src/state/publish-while-dragging-in-virtual/index.js +158 -0
  129. package/src/state/publish-while-dragging-in-virtual/offset-draggable.js +35 -0
  130. package/src/state/recompute-placeholders.js +99 -0
  131. package/src/state/rect.js +7 -0
  132. package/src/state/reducer.js +457 -0
  133. package/src/state/registry/create-registry.js +162 -0
  134. package/src/state/registry/registry-types.js +98 -0
  135. package/src/state/registry/use-registry.js +20 -0
  136. package/src/state/remove-draggable-from-list.js +13 -0
  137. package/src/state/scroll-viewport.js +34 -0
  138. package/src/state/spacing.js +45 -0
  139. package/src/state/store-types.js +16 -0
  140. package/src/state/update-displacement-visibility/recompute.js +54 -0
  141. package/src/state/update-displacement-visibility/speculatively-increase.js +111 -0
  142. package/src/state/visibility/is-partially-visible-through-frame.js +60 -0
  143. package/src/state/visibility/is-position-in-frame.js +12 -0
  144. package/src/state/visibility/is-totally-visible-through-frame-on-axis.js +19 -0
  145. package/src/state/visibility/is-totally-visible-through-frame.js +18 -0
  146. package/src/state/visibility/is-visible.js +102 -0
  147. package/src/state/with-scroll-change/with-all-displacement.js +15 -0
  148. package/src/state/with-scroll-change/with-droppable-displacement.js +13 -0
  149. package/src/state/with-scroll-change/with-droppable-scroll.js +13 -0
  150. package/src/state/with-scroll-change/with-viewport-displacement.js +7 -0
  151. package/src/types.js +542 -0
  152. package/src/view/animate-in-out/animate-in-out.jsx +95 -0
  153. package/src/view/animate-in-out/index.js +2 -0
  154. package/src/view/check-is-valid-inner-ref.js +15 -0
  155. package/src/view/context/app-context.js +19 -0
  156. package/src/view/context/droppable-context.js +11 -0
  157. package/src/view/context/store-context.js +5 -0
  158. package/src/view/data-attributes.js +37 -0
  159. package/src/view/drag-drop-context/app.jsx +273 -0
  160. package/src/view/drag-drop-context/check-doctype.js +39 -0
  161. package/src/view/drag-drop-context/check-react-version.js +71 -0
  162. package/src/view/drag-drop-context/drag-drop-context-types.js +7 -0
  163. package/src/view/drag-drop-context/drag-drop-context.jsx +68 -0
  164. package/src/view/drag-drop-context/error-boundary.jsx +88 -0
  165. package/src/view/drag-drop-context/index.js +2 -0
  166. package/src/view/drag-drop-context/use-startup-validation.js +13 -0
  167. package/src/view/drag-drop-context/use-unique-context-id.js +13 -0
  168. package/src/view/draggable/connected-draggable.js +372 -0
  169. package/src/view/draggable/draggable-api.jsx +48 -0
  170. package/src/view/draggable/draggable-types.js +191 -0
  171. package/src/view/draggable/draggable.jsx +171 -0
  172. package/src/view/draggable/get-style.js +109 -0
  173. package/src/view/draggable/index.js +2 -0
  174. package/src/view/draggable/use-validation.js +70 -0
  175. package/src/view/droppable/connected-droppable.js +280 -0
  176. package/src/view/droppable/droppable-types.js +91 -0
  177. package/src/view/droppable/droppable.jsx +167 -0
  178. package/src/view/droppable/index.js +2 -0
  179. package/src/view/droppable/use-validation.js +101 -0
  180. package/src/view/event-bindings/bind-events.js +39 -0
  181. package/src/view/event-bindings/event-types.js +14 -0
  182. package/src/view/get-body-element.js +8 -0
  183. package/src/view/get-border-box-center-position.js +5 -0
  184. package/src/view/get-document-element.js +8 -0
  185. package/src/view/get-elements/find-drag-handle.js +38 -0
  186. package/src/view/get-elements/find-draggable.js +30 -0
  187. package/src/view/is-strict-equal.js +2 -0
  188. package/src/view/is-type-of-element/is-element.js +6 -0
  189. package/src/view/is-type-of-element/is-html-element.js +6 -0
  190. package/src/view/is-type-of-element/is-svg-element.js +12 -0
  191. package/src/view/key-codes.js +13 -0
  192. package/src/view/placeholder/index.js +2 -0
  193. package/src/view/placeholder/placeholder-types.js +16 -0
  194. package/src/view/placeholder/placeholder.jsx +198 -0
  195. package/src/view/scroll-listener.js +72 -0
  196. package/src/view/throw-if-invalid-inner-ref.js +15 -0
  197. package/src/view/use-announcer/index.js +2 -0
  198. package/src/view/use-announcer/use-announcer.js +80 -0
  199. package/src/view/use-dev-setup-warning.js +22 -0
  200. package/src/view/use-dev.js +9 -0
  201. package/src/view/use-draggable-publisher/get-dimension.js +44 -0
  202. package/src/view/use-draggable-publisher/index.js +2 -0
  203. package/src/view/use-draggable-publisher/use-draggable-publisher.js +87 -0
  204. package/src/view/use-droppable-publisher/check-for-nested-scroll-container.js +27 -0
  205. package/src/view/use-droppable-publisher/get-closest-scrollable.js +95 -0
  206. package/src/view/use-droppable-publisher/get-dimension.js +139 -0
  207. package/src/view/use-droppable-publisher/get-env.js +31 -0
  208. package/src/view/use-droppable-publisher/get-listener-options.js +12 -0
  209. package/src/view/use-droppable-publisher/get-scroll.js +7 -0
  210. package/src/view/use-droppable-publisher/index.js +2 -0
  211. package/src/view/use-droppable-publisher/is-in-fixed-container.js +21 -0
  212. package/src/view/use-droppable-publisher/use-droppable-publisher.js +283 -0
  213. package/src/view/use-focus-marshal/focus-marshal-types.js +13 -0
  214. package/src/view/use-focus-marshal/index.js +2 -0
  215. package/src/view/use-focus-marshal/use-focus-marshal.js +129 -0
  216. package/src/view/use-hidden-text-element/index.js +2 -0
  217. package/src/view/use-hidden-text-element/use-hidden-text-element.js +60 -0
  218. package/src/view/use-isomorphic-layout-effect.js +18 -0
  219. package/src/view/use-previous-ref.js +14 -0
  220. package/src/view/use-required-context.js +9 -0
  221. package/src/view/use-sensor-marshal/closest.js +50 -0
  222. package/src/view/use-sensor-marshal/find-closest-draggable-id-from-event.js +50 -0
  223. package/src/view/use-sensor-marshal/index.js +5 -0
  224. package/src/view/use-sensor-marshal/is-event-in-interactive-element.js +66 -0
  225. package/src/view/use-sensor-marshal/lock.js +48 -0
  226. package/src/view/use-sensor-marshal/sensors/use-keyboard-sensor.js +243 -0
  227. package/src/view/use-sensor-marshal/sensors/use-mouse-sensor.js +386 -0
  228. package/src/view/use-sensor-marshal/sensors/use-touch-sensor.js +461 -0
  229. package/src/view/use-sensor-marshal/sensors/util/prevent-standard-key-events.js +19 -0
  230. package/src/view/use-sensor-marshal/sensors/util/supported-page-visibility-event-name.js +29 -0
  231. package/src/view/use-sensor-marshal/use-sensor-marshal.js +495 -0
  232. package/src/view/use-sensor-marshal/use-validate-sensor-hooks.js +20 -0
  233. package/src/view/use-style-marshal/get-styles.js +170 -0
  234. package/src/view/use-style-marshal/index.js +2 -0
  235. package/src/view/use-style-marshal/style-marshal-types.js +8 -0
  236. package/src/view/use-style-marshal/use-style-marshal.js +126 -0
  237. package/src/view/use-unique-id.js +25 -0
  238. package/src/view/visually-hidden-style.js +16 -0
  239. package/src/view/window/get-max-window-scroll.js +19 -0
  240. package/src/view/window/get-viewport.js +49 -0
  241. package/src/view/window/get-window-from-el.js +3 -0
  242. package/src/view/window/get-window-scroll.js +30 -0
  243. package/src/view/window/scroll-window.js +7 -0
@@ -0,0 +1,461 @@
1
+ // @flow
2
+ import { useRef } from 'react';
3
+ import { useCallback, useMemo } from 'use-memo-one';
4
+ import type { Position } from 'css-box-model';
5
+ import { invariant } from '../../../invariant';
6
+ import type {
7
+ DraggableId,
8
+ SensorAPI,
9
+ PreDragActions,
10
+ FluidDragActions,
11
+ } from '../../../types';
12
+ import type {
13
+ EventBinding,
14
+ EventOptions,
15
+ } from '../../event-bindings/event-types';
16
+ import bindEvents from '../../event-bindings/bind-events';
17
+ import * as keyCodes from '../../key-codes';
18
+ import supportedPageVisibilityEventName from './util/supported-page-visibility-event-name';
19
+ import { noop } from '../../../empty';
20
+ import useLayoutEffect from '../../use-isomorphic-layout-effect';
21
+
22
+ type TouchWithForce = Touch & {
23
+ force: number,
24
+ };
25
+
26
+ type Idle = {|
27
+ type: 'IDLE',
28
+ |};
29
+
30
+ type Pending = {|
31
+ type: 'PENDING',
32
+ point: Position,
33
+ actions: PreDragActions,
34
+ longPressTimerId: TimeoutID,
35
+ |};
36
+
37
+ type Dragging = {|
38
+ type: 'DRAGGING',
39
+ actions: FluidDragActions,
40
+ hasMoved: boolean,
41
+ |};
42
+
43
+ type Phase = Idle | Pending | Dragging;
44
+
45
+ const idle: Idle = { type: 'IDLE' };
46
+ // Decreased from 150 as a work around for an issue for forcepress on iOS
47
+ // https://github.com/atlassian/react-beautiful-dnd/issues/1401
48
+ export const timeForLongPress: number = 120;
49
+ export const forcePressThreshold: number = 0.15;
50
+
51
+ type GetBindingArgs = {|
52
+ cancel: () => void,
53
+ completed: () => void,
54
+ getPhase: () => Phase,
55
+ |};
56
+
57
+ function getWindowBindings({
58
+ cancel,
59
+ getPhase,
60
+ }: GetBindingArgs): EventBinding[] {
61
+ return [
62
+ // If the orientation of the device changes - kill the drag
63
+ // https://davidwalsh.name/orientation-change
64
+ {
65
+ eventName: 'orientationchange',
66
+ fn: cancel,
67
+ },
68
+ // some devices fire resize if the orientation changes
69
+ {
70
+ eventName: 'resize',
71
+ fn: cancel,
72
+ },
73
+ // Long press can bring up a context menu
74
+ // need to opt out of this behavior
75
+ {
76
+ eventName: 'contextmenu',
77
+ fn: (event: Event) => {
78
+ // always opting out of context menu events
79
+ event.preventDefault();
80
+ },
81
+ },
82
+ // On some devices it is possible to have a touch interface with a keyboard.
83
+ // On any keyboard event we cancel a touch drag
84
+ {
85
+ eventName: 'keydown',
86
+ fn: (event: KeyboardEvent) => {
87
+ if (getPhase().type !== 'DRAGGING') {
88
+ cancel();
89
+ return;
90
+ }
91
+
92
+ // direct cancel: we are preventing the default action
93
+ // indirect cancel: we are not preventing the default action
94
+
95
+ // escape is a direct cancel
96
+ if (event.keyCode === keyCodes.escape) {
97
+ event.preventDefault();
98
+ }
99
+ cancel();
100
+ },
101
+ },
102
+ // Cancel on page visibility change
103
+ {
104
+ eventName: supportedPageVisibilityEventName,
105
+ fn: cancel,
106
+ },
107
+ ];
108
+ }
109
+
110
+ // All of the touch events get applied to the drag handle of the touch interaction
111
+ // This plays well with the event.target being unmounted during a drag
112
+ function getHandleBindings({
113
+ cancel,
114
+ completed,
115
+ getPhase,
116
+ }: GetBindingArgs): EventBinding[] {
117
+ return [
118
+ {
119
+ eventName: 'touchmove',
120
+ // Opting out of passive touchmove (default) so as to prevent scrolling while moving
121
+ // Not worried about performance as effect of move is throttled in requestAnimationFrame
122
+ // Using `capture: false` due to a recent horrible firefox bug: https://twitter.com/alexandereardon/status/1125904207184187393
123
+ options: { capture: false },
124
+ fn: (event: TouchEvent) => {
125
+ const phase: Phase = getPhase();
126
+ // Drag has not yet started and we are waiting for a long press.
127
+ if (phase.type !== 'DRAGGING') {
128
+ cancel();
129
+ return;
130
+ }
131
+
132
+ // At this point we are dragging
133
+ phase.hasMoved = true;
134
+
135
+ const { clientX, clientY } = event.touches[0];
136
+
137
+ const point: Position = {
138
+ x: clientX,
139
+ y: clientY,
140
+ };
141
+
142
+ // We need to prevent the default event in order to block native scrolling
143
+ // Also because we are using it as part of a drag we prevent the default action
144
+ // as a sign that we are using the event
145
+ event.preventDefault();
146
+ phase.actions.move(point);
147
+ },
148
+ },
149
+ {
150
+ eventName: 'touchend',
151
+ fn: (event: TouchEvent) => {
152
+ const phase: Phase = getPhase();
153
+ // drag had not started yet - do not prevent the default action
154
+ if (phase.type !== 'DRAGGING') {
155
+ cancel();
156
+ return;
157
+ }
158
+
159
+ // ending the drag
160
+ event.preventDefault();
161
+ phase.actions.drop({ shouldBlockNextClick: true });
162
+ completed();
163
+ },
164
+ },
165
+ {
166
+ eventName: 'touchcancel',
167
+ fn: (event: TouchEvent) => {
168
+ // drag had not started yet - do not prevent the default action
169
+ if (getPhase().type !== 'DRAGGING') {
170
+ cancel();
171
+ return;
172
+ }
173
+
174
+ // already dragging - this event is directly ending a drag
175
+ event.preventDefault();
176
+ cancel();
177
+ },
178
+ },
179
+ // Need to opt out of dragging if the user is a force press
180
+ // Only for webkit which has decided to introduce its own custom way of doing things
181
+ // https://developer.apple.com/library/content/documentation/AppleApplications/Conceptual/SafariJSProgTopics/RespondingtoForceTouchEventsfromJavaScript.html
182
+ {
183
+ eventName: 'touchforcechange',
184
+ fn: (event: TouchEvent) => {
185
+ const phase: Phase = getPhase();
186
+
187
+ // needed to use phase.actions
188
+ invariant(phase.type !== 'IDLE');
189
+
190
+ // This is not fantastic logic, but it is done to account for
191
+ // and issue with forcepress on iOS
192
+ // Calling event.preventDefault() will currently opt out of scrolling and clicking
193
+ // https://github.com/atlassian/react-beautiful-dnd/issues/1401
194
+
195
+ const touch: ?TouchWithForce = (event.touches[0]: any);
196
+
197
+ if (!touch) {
198
+ return;
199
+ }
200
+
201
+ const isForcePress: boolean = touch.force >= forcePressThreshold;
202
+
203
+ if (!isForcePress) {
204
+ return;
205
+ }
206
+
207
+ const shouldRespect: boolean = phase.actions.shouldRespectForcePress();
208
+
209
+ if (phase.type === 'PENDING') {
210
+ if (shouldRespect) {
211
+ cancel();
212
+ }
213
+ // If not respecting we just let the event go through
214
+ // It will not have an impact on the browser until
215
+ // there has been a sufficient time ellapsed
216
+ return;
217
+ }
218
+
219
+ // 'DRAGGING'
220
+
221
+ if (shouldRespect) {
222
+ if (phase.hasMoved) {
223
+ // After the user has moved we do not allow the dragging item to be force pressed
224
+ // This prevents strange behaviour such as a link preview opening mid drag
225
+ event.preventDefault();
226
+ return;
227
+ }
228
+ // indirect cancel
229
+ cancel();
230
+ return;
231
+ }
232
+
233
+ // not respecting during a drag
234
+ event.preventDefault();
235
+ },
236
+ },
237
+ // Cancel on page visibility change
238
+ {
239
+ eventName: supportedPageVisibilityEventName,
240
+ fn: cancel,
241
+ },
242
+ // Not adding a cancel on touchstart as this handler will pick up the initial touchstart event
243
+ ];
244
+ }
245
+
246
+ export default function useTouchSensor(api: SensorAPI) {
247
+ const phaseRef = useRef<Phase>(idle);
248
+ const unbindEventsRef = useRef<() => void>(noop);
249
+
250
+ const getPhase = useCallback(function getPhase(): Phase {
251
+ return phaseRef.current;
252
+ }, []);
253
+
254
+ const setPhase = useCallback(function setPhase(phase: Phase) {
255
+ phaseRef.current = phase;
256
+ }, []);
257
+
258
+ const startCaptureBinding: EventBinding = useMemo(
259
+ () => ({
260
+ eventName: 'touchstart',
261
+ fn: function onTouchStart(event: TouchEvent) {
262
+ // Event already used by something else
263
+ if (event.defaultPrevented) {
264
+ return;
265
+ }
266
+
267
+ // We need to NOT call event.preventDefault() so as to maintain as much standard
268
+ // browser interactions as possible.
269
+ // This includes navigation on anchors which we want to preserve
270
+
271
+ const draggableId: ?DraggableId = api.findClosestDraggableId(event);
272
+
273
+ if (!draggableId) {
274
+ return;
275
+ }
276
+
277
+ const actions: ?PreDragActions = api.tryGetLock(
278
+ draggableId,
279
+ // eslint-disable-next-line no-use-before-define
280
+ stop,
281
+ { sourceEvent: event },
282
+ );
283
+
284
+ // could not start a drag
285
+ if (!actions) {
286
+ return;
287
+ }
288
+
289
+ const touch: Touch = event.touches[0];
290
+ const { clientX, clientY } = touch;
291
+ const point: Position = {
292
+ x: clientX,
293
+ y: clientY,
294
+ };
295
+
296
+ // unbind this event handler
297
+ unbindEventsRef.current();
298
+
299
+ // eslint-disable-next-line no-use-before-define
300
+ startPendingDrag(actions, point);
301
+ },
302
+ }),
303
+ // not including stop or startPendingDrag as it is not defined initially
304
+ // eslint-disable-next-line react-hooks/exhaustive-deps
305
+ [api],
306
+ );
307
+
308
+ const listenForCapture = useCallback(
309
+ function listenForCapture() {
310
+ const options: EventOptions = {
311
+ capture: true,
312
+ passive: false,
313
+ };
314
+
315
+ unbindEventsRef.current = bindEvents(
316
+ window,
317
+ [startCaptureBinding],
318
+ options,
319
+ );
320
+ },
321
+ [startCaptureBinding],
322
+ );
323
+
324
+ const stop = useCallback(() => {
325
+ const current: Phase = phaseRef.current;
326
+ if (current.type === 'IDLE') {
327
+ return;
328
+ }
329
+
330
+ // aborting any pending drag
331
+ if (current.type === 'PENDING') {
332
+ clearTimeout(current.longPressTimerId);
333
+ }
334
+
335
+ setPhase(idle);
336
+ unbindEventsRef.current();
337
+
338
+ listenForCapture();
339
+ }, [listenForCapture, setPhase]);
340
+
341
+ const cancel = useCallback(() => {
342
+ const phase: Phase = phaseRef.current;
343
+ stop();
344
+ if (phase.type === 'DRAGGING') {
345
+ phase.actions.cancel({ shouldBlockNextClick: true });
346
+ }
347
+ if (phase.type === 'PENDING') {
348
+ phase.actions.abort();
349
+ }
350
+ }, [stop]);
351
+
352
+ const bindCapturingEvents = useCallback(
353
+ function bindCapturingEvents() {
354
+ const options: EventOptions = { capture: true, passive: false };
355
+ const args: GetBindingArgs = {
356
+ cancel,
357
+ completed: stop,
358
+ getPhase,
359
+ };
360
+
361
+ // In prior versions of iOS it was required that touch listeners be added
362
+ // to the handle to work correctly (even if the handle got removed in a portal / clone)
363
+ // In the latest version it appears to be the opposite: for reparenting to work
364
+ // the events need to be attached to the window.
365
+ // For now i'll keep these two functions seperate in case we need to swap it back again
366
+ // Old behaviour:
367
+ // https://gist.github.com/parris/dda613e3ae78f14eb2dc9fa0f4bfce3d
368
+ // https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed
369
+ const unbindTarget = bindEvents(window, getHandleBindings(args), options);
370
+ const unbindWindow = bindEvents(window, getWindowBindings(args), options);
371
+
372
+ unbindEventsRef.current = function unbindAll() {
373
+ unbindTarget();
374
+ unbindWindow();
375
+ };
376
+ },
377
+ [cancel, getPhase, stop],
378
+ );
379
+
380
+ const startDragging = useCallback(
381
+ function startDragging() {
382
+ const phase: Phase = getPhase();
383
+ invariant(
384
+ phase.type === 'PENDING',
385
+ `Cannot start dragging from phase ${phase.type}`,
386
+ );
387
+
388
+ const actions: FluidDragActions = phase.actions.fluidLift(phase.point);
389
+
390
+ setPhase({
391
+ type: 'DRAGGING',
392
+ actions,
393
+ hasMoved: false,
394
+ });
395
+ },
396
+ [getPhase, setPhase],
397
+ );
398
+
399
+ const startPendingDrag = useCallback(
400
+ function startPendingDrag(actions: PreDragActions, point: Position) {
401
+ invariant(
402
+ getPhase().type === 'IDLE',
403
+ 'Expected to move from IDLE to PENDING drag',
404
+ );
405
+
406
+ const longPressTimerId: TimeoutID = setTimeout(
407
+ startDragging,
408
+ timeForLongPress,
409
+ );
410
+
411
+ setPhase({
412
+ type: 'PENDING',
413
+ point,
414
+ actions,
415
+ longPressTimerId,
416
+ });
417
+
418
+ bindCapturingEvents();
419
+ },
420
+ [bindCapturingEvents, getPhase, setPhase, startDragging],
421
+ );
422
+
423
+ useLayoutEffect(
424
+ function mount() {
425
+ listenForCapture();
426
+
427
+ return function unmount() {
428
+ // remove any existing listeners
429
+ unbindEventsRef.current();
430
+
431
+ // need to kill any pending drag start timer
432
+ const phase: Phase = getPhase();
433
+ if (phase.type === 'PENDING') {
434
+ clearTimeout(phase.longPressTimerId);
435
+ setPhase(idle);
436
+ }
437
+ };
438
+ },
439
+ [getPhase, listenForCapture, setPhase],
440
+ );
441
+
442
+ // This is needed for safari
443
+ // Simply adding a non capture, non passive 'touchmove' listener.
444
+ // This forces event.preventDefault() in dynamically added
445
+ // touchmove event handlers to actually work
446
+ // https://github.com/atlassian/react-beautiful-dnd/issues/1374
447
+ useLayoutEffect(function webkitHack() {
448
+ const unbind = bindEvents(window, [
449
+ {
450
+ eventName: 'touchmove',
451
+ // using a new noop function for each usage as a single `removeEventListener()`
452
+ // call will remove all handlers with the same reference
453
+ // https://codesandbox.io/s/removing-multiple-handlers-with-same-reference-fxe15
454
+ fn: () => {},
455
+ options: { capture: false, passive: false },
456
+ },
457
+ ]);
458
+
459
+ return unbind;
460
+ }, []);
461
+ }
@@ -0,0 +1,19 @@
1
+ // @flow
2
+ import * as keyCodes from '../../../key-codes';
3
+
4
+ type KeyMap = {
5
+ [key: number]: true,
6
+ };
7
+
8
+ const preventedKeys: KeyMap = {
9
+ // submission
10
+ [keyCodes.enter]: true,
11
+ // tabbing
12
+ [keyCodes.tab]: true,
13
+ };
14
+
15
+ export default (event: KeyboardEvent) => {
16
+ if (preventedKeys[event.keyCode]) {
17
+ event.preventDefault();
18
+ }
19
+ };
@@ -0,0 +1,29 @@
1
+ // @flow
2
+ import { find } from '../../../../native-with-fallback';
3
+
4
+ const supportedEventName: string = ((): string => {
5
+ const base: string = 'visibilitychange';
6
+
7
+ // Server side rendering
8
+ if (typeof document === 'undefined') {
9
+ return base;
10
+ }
11
+
12
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
13
+ const candidates: string[] = [
14
+ base,
15
+ `ms${base}`,
16
+ `webkit${base}`,
17
+ `moz${base}`,
18
+ `o${base}`,
19
+ ];
20
+
21
+ const supported: ?string = find(
22
+ candidates,
23
+ (eventName: string): boolean => `on${eventName}` in document,
24
+ );
25
+
26
+ return supported || base;
27
+ })();
28
+
29
+ export default supportedEventName;