@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,495 @@
1
+ // @flow
2
+ import rafSchd from 'raf-schd';
3
+ import { useState } from 'react';
4
+ import { useCallback, useMemo } from 'use-memo-one';
5
+ import type { Position } from 'css-box-model';
6
+ import { invariant } from '../../invariant';
7
+ import type {
8
+ ContextId,
9
+ State,
10
+ Sensor,
11
+ StopDragOptions,
12
+ PreDragActions,
13
+ FluidDragActions,
14
+ SnapDragActions,
15
+ DraggableId,
16
+ SensorAPI,
17
+ TryGetLock,
18
+ TryGetLockOptions,
19
+ DraggableOptions,
20
+ } from '../../types';
21
+ import create, { type Lock, type LockAPI } from './lock';
22
+ import type { Store, Action } from '../../state/store-types';
23
+ import canStartDrag from '../../state/can-start-drag';
24
+ import {
25
+ move as moveAction,
26
+ moveUp as moveUpAction,
27
+ moveRight as moveRightAction,
28
+ moveDown as moveDownAction,
29
+ moveLeft as moveLeftAction,
30
+ drop as dropAction,
31
+ lift as liftAction,
32
+ type LiftArgs as LiftActionArgs,
33
+ flush,
34
+ } from '../../state/action-creators';
35
+ import type {
36
+ Registry,
37
+ DraggableEntry,
38
+ } from '../../state/registry/registry-types';
39
+ import useMouseSensor from './sensors/use-mouse-sensor';
40
+ import useKeyboardSensor from './sensors/use-keyboard-sensor';
41
+ import useTouchSensor from './sensors/use-touch-sensor';
42
+ import useValidateSensorHooks from './use-validate-sensor-hooks';
43
+ import isEventInInteractiveElement from './is-event-in-interactive-element';
44
+ import getBorderBoxCenterPosition from '../get-border-box-center-position';
45
+ import { warning } from '../../dev-warning';
46
+ import useLayoutEffect from '../use-isomorphic-layout-effect';
47
+ import { noop } from '../../empty';
48
+ import findClosestDraggableIdFromEvent from './find-closest-draggable-id-from-event';
49
+ import findDraggable from '../get-elements/find-draggable';
50
+ import bindEvents from '../event-bindings/bind-events';
51
+
52
+ function preventDefault(event: Event) {
53
+ event.preventDefault();
54
+ }
55
+
56
+ type LockPhase = 'PRE_DRAG' | 'DRAGGING' | 'COMPLETED';
57
+
58
+ type IsActiveArgs = {|
59
+ expected: LockPhase,
60
+ phase: LockPhase,
61
+ isLockActive: () => boolean,
62
+ shouldWarn: boolean,
63
+ |};
64
+
65
+ function isActive({
66
+ expected,
67
+ phase,
68
+ isLockActive,
69
+ shouldWarn,
70
+ }: IsActiveArgs): boolean {
71
+ // lock is no longer active
72
+ if (!isLockActive()) {
73
+ if (shouldWarn) {
74
+ warning(`
75
+ Cannot perform action.
76
+ The sensor no longer has an action lock.
77
+
78
+ Tips:
79
+
80
+ - Throw away your action handlers when forceStop() is called
81
+ - Check actions.isActive() if you really need to
82
+ `);
83
+ }
84
+ return false;
85
+ }
86
+ // wrong phase
87
+ if (expected !== phase) {
88
+ if (shouldWarn) {
89
+ warning(`
90
+ Cannot perform action.
91
+ The actions you used belong to an outdated phase
92
+
93
+ Current phase: ${expected}
94
+ You called an action from outdated phase: ${phase}
95
+
96
+ Tips:
97
+
98
+ - Do not use preDragActions actions after calling preDragActions.lift()
99
+ `);
100
+ }
101
+ return false;
102
+ }
103
+ return true;
104
+ }
105
+
106
+ type CanStartArgs = {|
107
+ lockAPI: LockAPI,
108
+ registry: Registry,
109
+ store: Store,
110
+ draggableId: DraggableId,
111
+ |};
112
+
113
+ function canStart({
114
+ lockAPI,
115
+ store,
116
+ registry,
117
+ draggableId,
118
+ }: CanStartArgs): boolean {
119
+ // lock is already claimed - cannot start
120
+ if (lockAPI.isClaimed()) {
121
+ return false;
122
+ }
123
+
124
+ const entry: ?DraggableEntry = registry.draggable.findById(draggableId);
125
+
126
+ if (!entry) {
127
+ warning(`Unable to find draggable with id: ${draggableId}`);
128
+ return false;
129
+ }
130
+
131
+ // draggable is not enabled - cannot start
132
+ if (!entry.options.isEnabled) {
133
+ return false;
134
+ }
135
+
136
+ // Application might now allow dragging right now
137
+ if (!canStartDrag(store.getState(), draggableId)) {
138
+ return false;
139
+ }
140
+
141
+ return true;
142
+ }
143
+
144
+ type TryStartArgs = {|
145
+ lockAPI: LockAPI,
146
+ contextId: ContextId,
147
+ registry: Registry,
148
+ store: Store,
149
+ draggableId: DraggableId,
150
+ forceSensorStop: ?() => void,
151
+ sourceEvent: ?Event,
152
+ |};
153
+
154
+ function tryStart({
155
+ lockAPI,
156
+ contextId,
157
+ store,
158
+ registry,
159
+ draggableId,
160
+ forceSensorStop,
161
+ sourceEvent,
162
+ }: TryStartArgs): ?PreDragActions {
163
+ const shouldStart: boolean = canStart({
164
+ lockAPI,
165
+ store,
166
+ registry,
167
+ draggableId,
168
+ });
169
+
170
+ if (!shouldStart) {
171
+ return null;
172
+ }
173
+
174
+ const entry: DraggableEntry = registry.draggable.getById(draggableId);
175
+ const el: ?HTMLElement = findDraggable(contextId, entry.descriptor.id);
176
+
177
+ if (!el) {
178
+ warning(`Unable to find draggable element with id: ${draggableId}`);
179
+ return null;
180
+ }
181
+
182
+ // Do not allow dragging from interactive elements
183
+ if (
184
+ sourceEvent &&
185
+ !entry.options.canDragInteractiveElements &&
186
+ isEventInInteractiveElement(el, sourceEvent)
187
+ ) {
188
+ return null;
189
+ }
190
+
191
+ // claiming lock
192
+ const lock: Lock = lockAPI.claim(forceSensorStop || noop);
193
+ let phase: LockPhase = 'PRE_DRAG';
194
+
195
+ function getShouldRespectForcePress(): boolean {
196
+ // not looking up the entry as it might have been removed in a virtual list
197
+ return entry.options.shouldRespectForcePress;
198
+ }
199
+
200
+ function isLockActive(): boolean {
201
+ return lockAPI.isActive(lock);
202
+ }
203
+
204
+ function tryDispatch(expected: LockPhase, getAction: () => Action): void {
205
+ if (isActive({ expected, phase, isLockActive, shouldWarn: true })) {
206
+ store.dispatch(getAction());
207
+ }
208
+ }
209
+
210
+ const tryDispatchWhenDragging = tryDispatch.bind(null, 'DRAGGING');
211
+
212
+ type LiftArgs = {|
213
+ liftActionArgs: LiftActionArgs,
214
+ cleanup: () => void,
215
+ actions: Object,
216
+ |};
217
+
218
+ function lift(args: LiftArgs) {
219
+ function completed() {
220
+ lockAPI.release();
221
+ phase = 'COMPLETED';
222
+ }
223
+ // Double lift = bad
224
+ if (phase !== 'PRE_DRAG') {
225
+ completed();
226
+ invariant(phase === 'PRE_DRAG', `Cannot lift in phase ${phase}`);
227
+ }
228
+
229
+ store.dispatch(liftAction(args.liftActionArgs));
230
+
231
+ // We are now in the DRAGGING phase
232
+ phase = 'DRAGGING';
233
+
234
+ function finish(
235
+ reason: 'CANCEL' | 'DROP',
236
+ options?: StopDragOptions = { shouldBlockNextClick: false },
237
+ ) {
238
+ args.cleanup();
239
+
240
+ // block next click if requested
241
+ if (options.shouldBlockNextClick) {
242
+ const unbind = bindEvents(window, [
243
+ {
244
+ eventName: 'click',
245
+ fn: preventDefault,
246
+ options: {
247
+ // only blocking a single click
248
+ once: true,
249
+ passive: false,
250
+ capture: true,
251
+ },
252
+ },
253
+ ]);
254
+ // Sometimes the click is swallowed, such as when there is reparenting
255
+ // The click event (in the message queue) will occur before the next setTimeout expiry
256
+ // https://codesandbox.io/s/click-behaviour-pkfk2
257
+ setTimeout(unbind);
258
+ }
259
+
260
+ // releasing
261
+ completed();
262
+ store.dispatch(dropAction({ reason }));
263
+ }
264
+
265
+ return {
266
+ isActive: () =>
267
+ isActive({
268
+ expected: 'DRAGGING',
269
+ phase,
270
+ isLockActive,
271
+ // Do not want to want warnings for boolean checks
272
+ shouldWarn: false,
273
+ }),
274
+ shouldRespectForcePress: getShouldRespectForcePress,
275
+ drop: (options?: StopDragOptions) => finish('DROP', options),
276
+ cancel: (options?: StopDragOptions) => finish('CANCEL', options),
277
+ ...args.actions,
278
+ };
279
+ }
280
+
281
+ function fluidLift(clientSelection: Position): FluidDragActions {
282
+ const move = rafSchd((client: Position) => {
283
+ tryDispatchWhenDragging(() => moveAction({ client }));
284
+ });
285
+
286
+ const api = lift({
287
+ liftActionArgs: {
288
+ id: draggableId,
289
+ clientSelection,
290
+ movementMode: 'FLUID',
291
+ },
292
+ cleanup: () => move.cancel(),
293
+ actions: { move },
294
+ });
295
+
296
+ return {
297
+ ...api,
298
+ move,
299
+ };
300
+ }
301
+
302
+ function snapLift(): SnapDragActions {
303
+ const actions = {
304
+ moveUp: () => tryDispatchWhenDragging(moveUpAction),
305
+ moveRight: () => tryDispatchWhenDragging(moveRightAction),
306
+ moveDown: () => tryDispatchWhenDragging(moveDownAction),
307
+ moveLeft: () => tryDispatchWhenDragging(moveLeftAction),
308
+ };
309
+
310
+ return lift({
311
+ liftActionArgs: {
312
+ id: draggableId,
313
+ clientSelection: getBorderBoxCenterPosition(el),
314
+ movementMode: 'SNAP',
315
+ },
316
+ cleanup: noop,
317
+ actions,
318
+ });
319
+ }
320
+
321
+ function abortPreDrag() {
322
+ const shouldRelease: boolean = isActive({
323
+ expected: 'PRE_DRAG',
324
+ phase,
325
+ isLockActive,
326
+ shouldWarn: true,
327
+ });
328
+
329
+ if (shouldRelease) {
330
+ lockAPI.release();
331
+ }
332
+ }
333
+
334
+ const preDrag: PreDragActions = {
335
+ isActive: () =>
336
+ isActive({
337
+ expected: 'PRE_DRAG',
338
+ phase,
339
+ isLockActive,
340
+ // Do not want to want warnings for boolean checks
341
+ shouldWarn: false,
342
+ }),
343
+ shouldRespectForcePress: getShouldRespectForcePress,
344
+ fluidLift,
345
+ snapLift,
346
+ abort: abortPreDrag,
347
+ };
348
+
349
+ return preDrag;
350
+ }
351
+
352
+ type SensorMarshalArgs = {|
353
+ contextId: ContextId,
354
+ registry: Registry,
355
+ store: Store,
356
+ customSensors: ?(Sensor[]),
357
+ enableDefaultSensors: boolean,
358
+ |};
359
+
360
+ // default sensors are now exported to library consumers
361
+ const defaultSensors: Sensor[] = [
362
+ useMouseSensor,
363
+ useKeyboardSensor,
364
+ useTouchSensor,
365
+ ];
366
+
367
+ export default function useSensorMarshal({
368
+ contextId,
369
+ store,
370
+ registry,
371
+ customSensors,
372
+ enableDefaultSensors,
373
+ }: SensorMarshalArgs) {
374
+ const useSensors: Sensor[] = [
375
+ ...(enableDefaultSensors ? defaultSensors : []),
376
+ ...(customSensors || []),
377
+ ];
378
+ const lockAPI: LockAPI = useState(() => create())[0];
379
+
380
+ const tryAbandonLock = useCallback(
381
+ function tryAbandonLock(previous: State, current: State) {
382
+ if (previous.isDragging && !current.isDragging) {
383
+ lockAPI.tryAbandon();
384
+ }
385
+ },
386
+ [lockAPI],
387
+ );
388
+
389
+ // We need to abort any capturing if there is no longer a drag
390
+ useLayoutEffect(
391
+ function listenToStore() {
392
+ let previous: State = store.getState();
393
+ const unsubscribe = store.subscribe(() => {
394
+ const current: State = store.getState();
395
+ tryAbandonLock(previous, current);
396
+ previous = current;
397
+ });
398
+
399
+ // unsubscribe from store when unmounting
400
+ return unsubscribe;
401
+ },
402
+ [lockAPI, store, tryAbandonLock],
403
+ );
404
+
405
+ // abort any lock on unmount
406
+ useLayoutEffect(() => {
407
+ return lockAPI.tryAbandon;
408
+ }, [lockAPI.tryAbandon]);
409
+
410
+ const canGetLock = useCallback(
411
+ (draggableId: DraggableId): boolean => {
412
+ return canStart({
413
+ lockAPI,
414
+ registry,
415
+ store,
416
+ draggableId,
417
+ });
418
+ },
419
+ [lockAPI, registry, store],
420
+ );
421
+
422
+ const tryGetLock: TryGetLock = useCallback(
423
+ (
424
+ draggableId: DraggableId,
425
+ forceStop?: () => void,
426
+ options?: TryGetLockOptions,
427
+ ): ?PreDragActions =>
428
+ tryStart({
429
+ lockAPI,
430
+ registry,
431
+ contextId,
432
+ store,
433
+ draggableId,
434
+ forceSensorStop: forceStop,
435
+ sourceEvent:
436
+ options && options.sourceEvent ? options.sourceEvent : null,
437
+ }),
438
+ [contextId, lockAPI, registry, store],
439
+ );
440
+
441
+ const findClosestDraggableId = useCallback(
442
+ (event: Event): ?DraggableId =>
443
+ findClosestDraggableIdFromEvent(contextId, event),
444
+ [contextId],
445
+ );
446
+
447
+ const findOptionsForDraggable = useCallback(
448
+ (id: DraggableId): ?DraggableOptions => {
449
+ const entry: ?DraggableEntry = registry.draggable.findById(id);
450
+ return entry ? entry.options : null;
451
+ },
452
+ [registry.draggable],
453
+ );
454
+
455
+ const tryReleaseLock = useCallback(
456
+ function tryReleaseLock() {
457
+ if (!lockAPI.isClaimed()) {
458
+ return;
459
+ }
460
+
461
+ lockAPI.tryAbandon();
462
+
463
+ if (store.getState().phase !== 'IDLE') {
464
+ store.dispatch(flush());
465
+ }
466
+ },
467
+ [lockAPI, store],
468
+ );
469
+ const isLockClaimed = useCallback(lockAPI.isClaimed, [lockAPI]);
470
+
471
+ const api: SensorAPI = useMemo(
472
+ () => ({
473
+ canGetLock,
474
+ tryGetLock,
475
+ findClosestDraggableId,
476
+ findOptionsForDraggable,
477
+ tryReleaseLock,
478
+ isLockClaimed,
479
+ }),
480
+ [
481
+ canGetLock,
482
+ tryGetLock,
483
+ findClosestDraggableId,
484
+ findOptionsForDraggable,
485
+ tryReleaseLock,
486
+ isLockClaimed,
487
+ ],
488
+ );
489
+
490
+ // Bad ass
491
+ useValidateSensorHooks(useSensors);
492
+ for (let i = 0; i < useSensors.length; i++) {
493
+ useSensors[i](api);
494
+ }
495
+ }
@@ -0,0 +1,20 @@
1
+ // @flow
2
+ /* eslint-disable react-hooks/rules-of-hooks */
3
+ import { invariant } from '../../invariant';
4
+ import type { Sensor } from '../../types';
5
+ import usePreviousRef from '../use-previous-ref';
6
+ import useDevSetupWarning from '../use-dev-setup-warning';
7
+ import useDev from '../use-dev';
8
+
9
+ export default function useValidateSensorHooks(sensorHooks: Sensor[]) {
10
+ useDev(() => {
11
+ const previousRef = usePreviousRef<Sensor[]>(sensorHooks);
12
+
13
+ useDevSetupWarning(() => {
14
+ invariant(
15
+ previousRef.current.length === sensorHooks.length,
16
+ 'Cannot change the amount of sensor hooks after mounting',
17
+ );
18
+ });
19
+ });
20
+ }
@@ -0,0 +1,170 @@
1
+ // @flow
2
+ import type { ContextId } from '../../types';
3
+ import { transitions } from '../../animation';
4
+ import * as attributes from '../data-attributes';
5
+
6
+ export type Styles = {|
7
+ always: string,
8
+ dragging: string,
9
+ resting: string,
10
+ dropAnimating: string,
11
+ userCancel: string,
12
+ |};
13
+
14
+ type Rule = {|
15
+ selector: string,
16
+ styles: {|
17
+ always?: string,
18
+ resting?: string,
19
+ dragging?: string,
20
+ dropAnimating?: string,
21
+ userCancel?: string,
22
+ |},
23
+ |};
24
+
25
+ const makeGetSelector = (context: string) => (attribute: string) =>
26
+ `[${attribute}="${context}"]`;
27
+
28
+ const getStyles = (rules: Rule[], property: string): string =>
29
+ rules
30
+ .map((rule: Rule): string => {
31
+ const value: ?string = rule.styles[property];
32
+ if (!value) {
33
+ return '';
34
+ }
35
+
36
+ return `${rule.selector} { ${value} }`;
37
+ })
38
+ .join(' ');
39
+
40
+ const noPointerEvents: string = 'pointer-events: none;';
41
+
42
+ export default (contextId: ContextId): Styles => {
43
+ const getSelector = makeGetSelector(contextId);
44
+
45
+ // ## Drag handle styles
46
+
47
+ // -webkit-touch-callout
48
+ // A long press on anchors usually pops a content menu that has options for
49
+ // the link such as 'Open in new tab'. Because long press is used to start
50
+ // a drag we need to opt out of this behavior
51
+
52
+ // -webkit-tap-highlight-color
53
+ // Webkit based browsers add a grey overlay to anchors when they are active.
54
+ // We remove this tap overlay as it is confusing for users
55
+ // https://css-tricks.com/snippets/css/remove-gray-highlight-when-tapping-links-in-mobile-safari/
56
+
57
+ // touch-action: manipulation
58
+ // Avoid the *pull to refresh action* and *delayed anchor focus* on Android Chrome
59
+
60
+ // cursor: grab
61
+ // We apply this by default for an improved user experience. It is such a common default that we
62
+ // bake it right in. Consumers can opt out of this by adding a selector with higher specificity
63
+ // The cursor will not apply when pointer-events is set to none
64
+
65
+ // pointer-events: none
66
+ // this is used to prevent pointer events firing on draggables during a drag
67
+ // Reasons:
68
+ // 1. performance: it stops the other draggables from processing mouse events
69
+ // 2. scrolling: it allows the user to scroll through the current draggable
70
+ // to scroll the list behind
71
+ // 3.* function: it blocks other draggables from starting. This is not relied on though as there
72
+ // is a function on the context (canLift) which is a more robust way of controlling this
73
+
74
+ const dragHandle: Rule = (() => {
75
+ const grabCursor = `
76
+ cursor: -webkit-grab;
77
+ cursor: grab;
78
+ `;
79
+ return {
80
+ selector: getSelector(attributes.dragHandle.contextId),
81
+ styles: {
82
+ always: `
83
+ -webkit-touch-callout: none;
84
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
85
+ touch-action: manipulation;
86
+ `,
87
+ resting: grabCursor,
88
+ dragging: noPointerEvents,
89
+ // it is fine for users to start dragging another item when a drop animation is occurring
90
+ dropAnimating: grabCursor,
91
+ // Not applying grab cursor during a user cancel as it is not possible for users to reorder
92
+ // items during a cancel
93
+ },
94
+ };
95
+ })();
96
+
97
+ // ## Draggable styles
98
+
99
+ // transition: transform
100
+ // This controls the animation of draggables that are moving out of the way
101
+ // The main draggable is controlled by react-motion.
102
+
103
+ const draggable: Rule = (() => {
104
+ const transition: string = `
105
+ transition: ${transitions.outOfTheWay};
106
+ `;
107
+ return {
108
+ selector: getSelector(attributes.draggable.contextId),
109
+ styles: {
110
+ dragging: transition,
111
+ dropAnimating: transition,
112
+ userCancel: transition,
113
+ },
114
+ };
115
+ })();
116
+
117
+ // ## Droppable styles
118
+
119
+ // overflow-anchor: none;
120
+ // Opting out of the browser feature which tries to maintain
121
+ // the scroll position when the DOM changes above the fold.
122
+ // This does not work well with reordering DOM nodes.
123
+ // When we drop a Draggable it already has the correct scroll applied.
124
+
125
+ const droppable: Rule = {
126
+ selector: getSelector(attributes.droppable.contextId),
127
+ styles: {
128
+ always: `overflow-anchor: none;`,
129
+ // need pointer events on the droppable to allow manual scrolling
130
+ },
131
+ };
132
+
133
+ // ## Body styles
134
+
135
+ // cursor: grab
136
+ // We apply this by default for an improved user experience. It is such a common default that we
137
+ // bake it right in. Consumers can opt out of this by adding a selector with higher specificity
138
+
139
+ // user-select: none
140
+ // This prevents the user from selecting text on the page while dragging
141
+
142
+ // overflow-anchor: none
143
+ // We are in control and aware of all of the window scrolls that occur
144
+ // we do not want the browser to have behaviors we do not expect
145
+
146
+ const body: Rule = {
147
+ selector: 'body',
148
+ styles: {
149
+ dragging: `
150
+ cursor: grabbing;
151
+ cursor: -webkit-grabbing;
152
+ user-select: none;
153
+ -webkit-user-select: none;
154
+ -moz-user-select: none;
155
+ -ms-user-select: none;
156
+ overflow-anchor: none;
157
+ `,
158
+ },
159
+ };
160
+
161
+ const rules: Rule[] = [draggable, dragHandle, droppable, body];
162
+
163
+ return {
164
+ always: getStyles(rules, 'always'),
165
+ resting: getStyles(rules, 'resting'),
166
+ dragging: getStyles(rules, 'dragging'),
167
+ dropAnimating: getStyles(rules, 'dropAnimating'),
168
+ userCancel: getStyles(rules, 'userCancel'),
169
+ };
170
+ };
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './use-style-marshal';
@@ -0,0 +1,8 @@
1
+ // @flow
2
+ import type { DropReason } from '../../types';
3
+
4
+ export type StyleMarshal = {|
5
+ dragging: () => void,
6
+ dropping: (reason: DropReason) => void,
7
+ resting: () => void,
8
+ |};