@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,66 @@
1
+ // @flow
2
+ import { invariant } from '../../invariant';
3
+ import type { DimensionMarshal } from '../dimension-marshal/dimension-marshal-types';
4
+ import type { State, ScrollOptions, LiftRequest } from '../../types';
5
+ import type { MiddlewareStore, Action, Dispatch } from '../store-types';
6
+ import {
7
+ completeDrop,
8
+ initialPublish,
9
+ flush,
10
+ beforeInitialCapture,
11
+ } from '../action-creators';
12
+ import validateDimensions from './util/validate-dimensions';
13
+
14
+ export default (marshal: DimensionMarshal) => ({
15
+ getState,
16
+ dispatch,
17
+ }: MiddlewareStore) => (next: Dispatch) => (action: Action): any => {
18
+ if (action.type !== 'LIFT') {
19
+ next(action);
20
+ return;
21
+ }
22
+ const { id, clientSelection, movementMode } = action.payload;
23
+ const initial: State = getState();
24
+
25
+ // flush dropping animation if needed
26
+ // this can change the descriptor of the dragging item
27
+ // Will call the onDragEnd responders
28
+
29
+ if (initial.phase === 'DROP_ANIMATING') {
30
+ dispatch(completeDrop({ completed: initial.completed }));
31
+ }
32
+
33
+ invariant(getState().phase === 'IDLE', 'Unexpected phase to start a drag');
34
+
35
+ // Removing any placeholders before we capture any starting dimensions
36
+ dispatch(flush());
37
+
38
+ // Let consumers know we are just about to publish
39
+ // We are only publishing a small amount of information as
40
+ // things might change as a result of the onBeforeCapture callback
41
+ dispatch(beforeInitialCapture({ draggableId: id, movementMode }));
42
+
43
+ // will communicate with the marshal to start requesting dimensions
44
+ const scrollOptions: ScrollOptions = {
45
+ shouldPublishImmediately: movementMode === 'SNAP',
46
+ };
47
+ const request: LiftRequest = {
48
+ draggableId: id,
49
+ scrollOptions,
50
+ };
51
+ // Let's get the marshal started!
52
+ const { critical, dimensions, viewport } = marshal.startPublishing(request);
53
+
54
+ validateDimensions(critical, dimensions);
55
+
56
+ // Okay, we are good to start dragging now
57
+ dispatch(
58
+ initialPublish({
59
+ critical,
60
+ dimensions,
61
+ clientSelection,
62
+ movementMode,
63
+ viewport,
64
+ }),
65
+ );
66
+ };
@@ -0,0 +1,37 @@
1
+ // @flow
2
+ import { drop } from '../action-creators';
3
+ import type { State } from '../../types';
4
+ import type { MiddlewareStore, Dispatch, Action } from '../store-types';
5
+
6
+ export default (store: MiddlewareStore) => (next: Dispatch) => (
7
+ action: Action,
8
+ ): any => {
9
+ // Always let the action go through first
10
+ next(action);
11
+
12
+ if (action.type !== 'PUBLISH_WHILE_DRAGGING') {
13
+ return;
14
+ }
15
+
16
+ // A bulk replace occurred - check if
17
+ // 1. there is a pending drop
18
+ // 2. that the pending drop is no longer waiting
19
+
20
+ const postActionState: State = store.getState();
21
+
22
+ // no pending drop after the publish
23
+ if (postActionState.phase !== 'DROP_PENDING') {
24
+ return;
25
+ }
26
+
27
+ // the pending drop is still waiting for completion
28
+ if (postActionState.isWaiting) {
29
+ return;
30
+ }
31
+
32
+ store.dispatch(
33
+ drop({
34
+ reason: postActionState.reason,
35
+ }),
36
+ );
37
+ };
@@ -0,0 +1,55 @@
1
+ // @flow
2
+ import { invariant } from '../../../invariant';
3
+ import { findIndex } from '../../../native-with-fallback';
4
+
5
+ type Entry = {|
6
+ timerId: TimeoutID,
7
+ callback: Function,
8
+ |};
9
+
10
+ export type AsyncMarshal = {|
11
+ add: (fn: Function) => void,
12
+ flush: () => void,
13
+ |};
14
+
15
+ export default () => {
16
+ const entries: Entry[] = [];
17
+
18
+ const execute = (timerId: TimeoutID) => {
19
+ const index: number = findIndex(
20
+ entries,
21
+ (item: Entry): boolean => item.timerId === timerId,
22
+ );
23
+ invariant(index !== -1, 'Could not find timer');
24
+ // delete in place
25
+ const [entry] = entries.splice(index, 1);
26
+ entry.callback();
27
+ };
28
+
29
+ const add = (fn: Function) => {
30
+ const timerId: TimeoutID = setTimeout(() => execute(timerId));
31
+ const entry: Entry = {
32
+ timerId,
33
+ callback: fn,
34
+ };
35
+ entries.push(entry);
36
+ };
37
+
38
+ const flush = () => {
39
+ // nothing to flush
40
+ if (!entries.length) {
41
+ return;
42
+ }
43
+
44
+ const shallow: Entry[] = [...entries];
45
+ // clearing entries in case a callback adds some more callbacks
46
+ entries.length = 0;
47
+
48
+ shallow.forEach((entry: Entry) => {
49
+ clearTimeout(entry.timerId);
50
+ entry.callback();
51
+ });
52
+ };
53
+
54
+ return { add, flush };
55
+ };
@@ -0,0 +1,44 @@
1
+ // @flow
2
+ import type { Announce } from '../../../types';
3
+ import { warning } from '../../../dev-warning';
4
+
5
+ export type ExpiringAnnounce = Announce & {
6
+ wasCalled: () => boolean,
7
+ };
8
+
9
+ export default (announce: Announce): ExpiringAnnounce => {
10
+ let wasCalled: boolean = false;
11
+ let isExpired: boolean = false;
12
+
13
+ // not allowing async announcements
14
+ const timeoutId: TimeoutID = setTimeout(() => {
15
+ isExpired = true;
16
+ });
17
+
18
+ const result = (message: string): void => {
19
+ if (wasCalled) {
20
+ warning('Announcement already made. Not making a second announcement');
21
+
22
+ return;
23
+ }
24
+
25
+ if (isExpired) {
26
+ warning(`
27
+ Announcements cannot be made asynchronously.
28
+ Default message has already been announced.
29
+ `);
30
+ return;
31
+ }
32
+
33
+ wasCalled = true;
34
+ announce(message);
35
+ clearTimeout(timeoutId);
36
+ };
37
+
38
+ // getter for isExpired
39
+ // using this technique so that a consumer cannot
40
+ // set the isExpired or wasCalled flags
41
+ result.wasCalled = (): boolean => wasCalled;
42
+
43
+ return result;
44
+ };
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './responders-middleware';
@@ -0,0 +1,57 @@
1
+ // @flow
2
+ import type { Critical, DraggableLocation, Combine } from '../../../types';
3
+
4
+ export const areLocationsEqual = (
5
+ first: ?DraggableLocation,
6
+ second: ?DraggableLocation,
7
+ ): boolean => {
8
+ // if both are null - we are equal
9
+ if (first == null && second == null) {
10
+ return true;
11
+ }
12
+
13
+ // if one is null - then they are not equal
14
+ if (first == null || second == null) {
15
+ return false;
16
+ }
17
+
18
+ // compare their actual values
19
+ return (
20
+ first.droppableId === second.droppableId && first.index === second.index
21
+ );
22
+ };
23
+
24
+ export const isCombineEqual = (first: ?Combine, second: ?Combine): boolean => {
25
+ // if both are null - we are equal
26
+ if (first == null && second == null) {
27
+ return true;
28
+ }
29
+
30
+ // only one is null
31
+ if (first == null || second == null) {
32
+ return false;
33
+ }
34
+
35
+ return (
36
+ first.draggableId === second.draggableId &&
37
+ first.droppableId === second.droppableId
38
+ );
39
+ };
40
+
41
+ export const isCriticalEqual = (first: Critical, second: Critical): boolean => {
42
+ if (first === second) {
43
+ return true;
44
+ }
45
+
46
+ const isDraggableEqual: boolean =
47
+ first.draggable.id === second.draggable.id &&
48
+ first.draggable.droppableId === second.draggable.droppableId &&
49
+ first.draggable.type === second.draggable.type &&
50
+ first.draggable.index === second.draggable.index;
51
+
52
+ const isDroppableEqual: boolean =
53
+ first.droppable.id === second.droppable.id &&
54
+ first.droppable.type === second.droppable.type;
55
+
56
+ return isDraggableEqual && isDroppableEqual;
57
+ };
@@ -0,0 +1,253 @@
1
+ // @flow
2
+ import { invariant } from '../../../invariant';
3
+ import messagePreset from '../../../screen-reader-message-preset';
4
+ import * as timings from '../../../debug/timings';
5
+ import getExpiringAnnounce, {
6
+ type ExpiringAnnounce,
7
+ } from './expiring-announce';
8
+ import getAsyncMarshal, { type AsyncMarshal } from './async-marshal';
9
+ import type {
10
+ DropResult,
11
+ Responders,
12
+ ResponderProvided,
13
+ Critical,
14
+ BeforeCapture,
15
+ DragImpact,
16
+ DraggableLocation,
17
+ Combine,
18
+ DragStart,
19
+ Announce,
20
+ DragUpdate,
21
+ MovementMode,
22
+ DraggableId,
23
+ OnBeforeCaptureResponder,
24
+ OnBeforeDragStartResponder,
25
+ OnDragStartResponder,
26
+ OnDragUpdateResponder,
27
+ OnDragEndResponder,
28
+ } from '../../../types';
29
+ import { isCombineEqual, isCriticalEqual, areLocationsEqual } from './is-equal';
30
+ import { tryGetDestination, tryGetCombine } from '../../get-impact-location';
31
+
32
+ const withTimings = (key: string, fn: Function) => {
33
+ timings.start(key);
34
+ fn();
35
+ timings.finish(key);
36
+ };
37
+
38
+ const getDragStart = (critical: Critical, mode: MovementMode): DragStart => ({
39
+ draggableId: critical.draggable.id,
40
+ type: critical.droppable.type,
41
+ source: {
42
+ droppableId: critical.droppable.id,
43
+ index: critical.draggable.index,
44
+ },
45
+ mode,
46
+ });
47
+
48
+ type AnyPrimaryResponderFn =
49
+ | OnDragStartResponder
50
+ | OnDragUpdateResponder
51
+ | OnDragEndResponder;
52
+ type AnyResponderData = DragStart | DragUpdate | DropResult;
53
+
54
+ const execute = (
55
+ responder: ?AnyPrimaryResponderFn,
56
+ data: AnyResponderData,
57
+ announce: Announce,
58
+ getDefaultMessage: (data: any) => string,
59
+ ) => {
60
+ if (!responder) {
61
+ announce(getDefaultMessage(data));
62
+ return;
63
+ }
64
+
65
+ const willExpire: ExpiringAnnounce = getExpiringAnnounce(announce);
66
+ const provided: ResponderProvided = {
67
+ announce: willExpire,
68
+ };
69
+
70
+ // Casting because we are not validating which data type is going into which responder
71
+ responder((data: any), provided);
72
+
73
+ if (!willExpire.wasCalled()) {
74
+ announce(getDefaultMessage(data));
75
+ }
76
+ };
77
+
78
+ type WhileDragging = {|
79
+ mode: MovementMode,
80
+ lastCritical: Critical,
81
+ lastCombine: ?Combine,
82
+ lastLocation: ?DraggableLocation,
83
+ |};
84
+
85
+ export default (getResponders: () => Responders, announce: Announce) => {
86
+ const asyncMarshal: AsyncMarshal = getAsyncMarshal();
87
+ let dragging: ?WhileDragging = null;
88
+
89
+ const beforeCapture = (draggableId: DraggableId, mode: MovementMode) => {
90
+ invariant(
91
+ !dragging,
92
+ 'Cannot fire onBeforeCapture as a drag start has already been published',
93
+ );
94
+ withTimings('onBeforeCapture', () => {
95
+ // No use of screen reader for this responder
96
+ const fn: ?OnBeforeCaptureResponder = getResponders().onBeforeCapture;
97
+ if (fn) {
98
+ const before: BeforeCapture = {
99
+ draggableId,
100
+ mode,
101
+ };
102
+ fn(before);
103
+ }
104
+ });
105
+ };
106
+
107
+ const beforeStart = (critical: Critical, mode: MovementMode) => {
108
+ invariant(
109
+ !dragging,
110
+ 'Cannot fire onBeforeDragStart as a drag start has already been published',
111
+ );
112
+ withTimings('onBeforeDragStart', () => {
113
+ // No use of screen reader for this responder
114
+ const fn: ?OnBeforeDragStartResponder = getResponders().onBeforeDragStart;
115
+ if (fn) {
116
+ fn(getDragStart(critical, mode));
117
+ }
118
+ });
119
+ };
120
+
121
+ const start = (critical: Critical, mode: MovementMode) => {
122
+ invariant(
123
+ !dragging,
124
+ 'Cannot fire onBeforeDragStart as a drag start has already been published',
125
+ );
126
+ const data: DragStart = getDragStart(critical, mode);
127
+ dragging = {
128
+ mode,
129
+ lastCritical: critical,
130
+ lastLocation: data.source,
131
+ lastCombine: null,
132
+ };
133
+
134
+ // we will flush this frame if we receive any responder updates
135
+ asyncMarshal.add(() => {
136
+ withTimings('onDragStart', () =>
137
+ execute(
138
+ getResponders().onDragStart,
139
+ data,
140
+ announce,
141
+ messagePreset.onDragStart,
142
+ ),
143
+ );
144
+ });
145
+ };
146
+
147
+ // Passing in the critical location again as it can change during a drag
148
+ const update = (critical: Critical, impact: DragImpact) => {
149
+ const location: ?DraggableLocation = tryGetDestination(impact);
150
+ const combine: ?Combine = tryGetCombine(impact);
151
+
152
+ invariant(
153
+ dragging,
154
+ 'Cannot fire onDragMove when onDragStart has not been called',
155
+ );
156
+
157
+ // Has the critical changed? Will result in a source change
158
+ const hasCriticalChanged: boolean = !isCriticalEqual(
159
+ critical,
160
+ dragging.lastCritical,
161
+ );
162
+ if (hasCriticalChanged) {
163
+ dragging.lastCritical = critical;
164
+ }
165
+
166
+ // Has the location changed? Will result in a destination change
167
+ const hasLocationChanged: boolean = !areLocationsEqual(
168
+ dragging.lastLocation,
169
+ location,
170
+ );
171
+ if (hasLocationChanged) {
172
+ dragging.lastLocation = location;
173
+ }
174
+ const hasGroupingChanged: boolean = !isCombineEqual(
175
+ dragging.lastCombine,
176
+ combine,
177
+ );
178
+ if (hasGroupingChanged) {
179
+ dragging.lastCombine = combine;
180
+ }
181
+
182
+ // Nothing has changed - no update needed
183
+ if (!hasCriticalChanged && !hasLocationChanged && !hasGroupingChanged) {
184
+ return;
185
+ }
186
+
187
+ const data: DragUpdate = {
188
+ ...getDragStart(critical, dragging.mode),
189
+ combine,
190
+ destination: location,
191
+ };
192
+
193
+ asyncMarshal.add(() => {
194
+ withTimings('onDragUpdate', () =>
195
+ execute(
196
+ getResponders().onDragUpdate,
197
+ data,
198
+ announce,
199
+ messagePreset.onDragUpdate,
200
+ ),
201
+ );
202
+ });
203
+ };
204
+
205
+ const flush = () => {
206
+ invariant(dragging, 'Can only flush responders while dragging');
207
+ asyncMarshal.flush();
208
+ };
209
+
210
+ const drop = (result: DropResult) => {
211
+ invariant(
212
+ dragging,
213
+ 'Cannot fire onDragEnd when there is no matching onDragStart',
214
+ );
215
+ dragging = null;
216
+ // not adding to frame marshal - we want this to be done in the same render pass
217
+ // we also want the consumers reorder logic to be in the same render pass
218
+ withTimings('onDragEnd', () =>
219
+ execute(
220
+ getResponders().onDragEnd,
221
+ result,
222
+ announce,
223
+ messagePreset.onDragEnd,
224
+ ),
225
+ );
226
+ };
227
+
228
+ // A non user initiated cancel
229
+ const abort = () => {
230
+ // aborting can happen defensively
231
+ if (!dragging) {
232
+ return;
233
+ }
234
+
235
+ const result: DropResult = {
236
+ ...getDragStart(dragging.lastCritical, dragging.mode),
237
+ combine: null,
238
+ destination: null,
239
+ reason: 'CANCEL',
240
+ };
241
+ drop(result);
242
+ };
243
+
244
+ return {
245
+ beforeCapture,
246
+ beforeStart,
247
+ start,
248
+ update,
249
+ flush,
250
+ drop,
251
+ abort,
252
+ };
253
+ };
@@ -0,0 +1,75 @@
1
+ // @flow
2
+ import getPublisher from './publisher';
3
+ import type {
4
+ State,
5
+ DropResult,
6
+ Responders,
7
+ Critical,
8
+ Announce,
9
+ } from '../../../types';
10
+ import type {
11
+ Action,
12
+ Middleware,
13
+ MiddlewareStore,
14
+ Dispatch,
15
+ } from '../../store-types';
16
+
17
+ export default (
18
+ getResponders: () => Responders,
19
+ announce: Announce,
20
+ ): Middleware => {
21
+ const publisher = getPublisher(
22
+ (getResponders: () => Responders),
23
+ (announce: Announce),
24
+ );
25
+
26
+ return (store: MiddlewareStore) => (next: Dispatch) => (
27
+ action: Action,
28
+ ): any => {
29
+ if (action.type === 'BEFORE_INITIAL_CAPTURE') {
30
+ publisher.beforeCapture(
31
+ action.payload.draggableId,
32
+ action.payload.movementMode,
33
+ );
34
+ return;
35
+ }
36
+
37
+ if (action.type === 'INITIAL_PUBLISH') {
38
+ const critical: Critical = action.payload.critical;
39
+ publisher.beforeStart(critical, action.payload.movementMode);
40
+ next(action);
41
+ publisher.start(critical, action.payload.movementMode);
42
+ return;
43
+ }
44
+
45
+ // Drag end
46
+ if (action.type === 'DROP_COMPLETE') {
47
+ // it is important that we use the result and not the last impact
48
+ // the last impact might be different to the result for visual reasons
49
+ const result: DropResult = action.payload.completed.result;
50
+ // flushing all pending responders before snapshots are updated
51
+ publisher.flush();
52
+ next(action);
53
+ publisher.drop(result);
54
+ return;
55
+ }
56
+
57
+ // All other responders can fire after we have updated our connected components
58
+ next(action);
59
+
60
+ // Drag state resetting - need to check if
61
+ // we should fire a onDragEnd responder
62
+ if (action.type === 'FLUSH') {
63
+ publisher.abort();
64
+ return;
65
+ }
66
+
67
+ // ## Perform drag updates
68
+ // impact of action has already been reduced
69
+
70
+ const state: State = store.getState();
71
+ if (state.phase === 'DRAGGING') {
72
+ publisher.update(state.critical, state.impact);
73
+ }
74
+ };
75
+ };
@@ -0,0 +1,31 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import { moveByWindowScroll } from '../action-creators';
4
+ import type { MiddlewareStore, Action, Dispatch } from '../store-types';
5
+ import getScrollListener from '../../view/scroll-listener';
6
+
7
+ // TODO: this is taken from auto-scroll. Let's make it a util
8
+ const shouldEnd = (action: Action): boolean =>
9
+ action.type === 'DROP_COMPLETE' ||
10
+ action.type === 'DROP_ANIMATE' ||
11
+ action.type === 'FLUSH';
12
+
13
+ export default (store: MiddlewareStore) => {
14
+ const listener = getScrollListener({
15
+ onWindowScroll: (newScroll: Position) => {
16
+ store.dispatch(moveByWindowScroll({ newScroll }));
17
+ },
18
+ });
19
+
20
+ return (next: Dispatch) => (action: Action): any => {
21
+ if (!listener.isActive() && action.type === 'INITIAL_PUBLISH') {
22
+ listener.start();
23
+ }
24
+
25
+ if (listener.isActive() && shouldEnd(action)) {
26
+ listener.stop();
27
+ }
28
+
29
+ next(action);
30
+ };
31
+ };
@@ -0,0 +1,22 @@
1
+ // @flow
2
+ import type { Action, Dispatch } from '../store-types';
3
+ import type { StyleMarshal } from '../../view/use-style-marshal/style-marshal-types';
4
+
5
+ export default (marshal: StyleMarshal) => () => (next: Dispatch) => (
6
+ action: Action,
7
+ ): any => {
8
+ if (action.type === 'INITIAL_PUBLISH') {
9
+ marshal.dragging();
10
+ }
11
+
12
+ if (action.type === 'DROP_ANIMATE') {
13
+ marshal.dropping(action.payload.completed.result.reason);
14
+ }
15
+
16
+ // this will clear any styles immediately before a reorder
17
+ if (action.type === 'FLUSH' || action.type === 'DROP_COMPLETE') {
18
+ marshal.resting();
19
+ }
20
+
21
+ next(action);
22
+ };