@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,16 @@
1
+ // @flow
2
+ import type { DragImpact, DraggableLocation, Combine } from '../types';
3
+
4
+ export function tryGetDestination(impact: DragImpact): ?DraggableLocation {
5
+ if (impact.at && impact.at.type === 'REORDER') {
6
+ return impact.at.destination;
7
+ }
8
+ return null;
9
+ }
10
+
11
+ export function tryGetCombine(impact: DragImpact): ?Combine {
12
+ if (impact.at && impact.at.type === 'COMBINE') {
13
+ return impact.at.combine;
14
+ }
15
+ return null;
16
+ }
@@ -0,0 +1,11 @@
1
+ // @flow
2
+ import type { DisplacementGroups, DraggableId } from '../types';
3
+
4
+ type Args = {|
5
+ displaced: DisplacementGroups,
6
+ id: DraggableId,
7
+ |};
8
+
9
+ export default function getIsDisplaced({ displaced, id }: Args): boolean {
10
+ return Boolean(displaced.visible[id] || displaced.invisible[id]);
11
+ }
@@ -0,0 +1,82 @@
1
+ // @flow
2
+ import { invariant } from '../invariant';
3
+ import getHomeLocation from './get-home-location';
4
+ import type {
5
+ DraggableDimension,
6
+ DroppableDimension,
7
+ DraggableDimensionMap,
8
+ DragImpact,
9
+ DisplacedBy,
10
+ Viewport,
11
+ DraggableIdMap,
12
+ DisplacementGroups,
13
+ LiftEffect,
14
+ } from '../types';
15
+ import getDraggablesInsideDroppable from './get-draggables-inside-droppable';
16
+ import getDisplacedBy from './get-displaced-by';
17
+ import getDisplacementGroups from './get-displacement-groups';
18
+
19
+ type Args = {|
20
+ draggable: DraggableDimension,
21
+ home: DroppableDimension,
22
+ draggables: DraggableDimensionMap,
23
+ viewport: Viewport,
24
+ |};
25
+
26
+ type Result = {|
27
+ afterCritical: LiftEffect,
28
+ impact: DragImpact,
29
+ |};
30
+
31
+ export default ({ draggable, home, draggables, viewport }: Args): Result => {
32
+ const displacedBy: DisplacedBy = getDisplacedBy(
33
+ home.axis,
34
+ draggable.displaceBy,
35
+ );
36
+
37
+ const insideHome: DraggableDimension[] = getDraggablesInsideDroppable(
38
+ home.descriptor.id,
39
+ draggables,
40
+ );
41
+
42
+ // in a list that does not start at 0 the descriptor.index might be different from the index in the list
43
+ // eg a list could be: [2,3,4]. A descriptor.index of '2' would actually be in index '0' of the list
44
+ const rawIndex: number = insideHome.indexOf(draggable);
45
+ invariant(rawIndex !== -1, 'Expected draggable to be inside home list');
46
+
47
+ const afterDragging: DraggableDimension[] = insideHome.slice(rawIndex + 1);
48
+ const effected: DraggableIdMap = afterDragging.reduce(
49
+ (previous: DraggableIdMap, item: DraggableDimension): DraggableIdMap => {
50
+ previous[item.descriptor.id] = true;
51
+ return previous;
52
+ },
53
+ {},
54
+ );
55
+ const afterCritical: LiftEffect = {
56
+ inVirtualList: home.descriptor.mode === 'virtual',
57
+ displacedBy,
58
+ effected,
59
+ };
60
+
61
+ const displaced: DisplacementGroups = getDisplacementGroups({
62
+ afterDragging,
63
+ destination: home,
64
+ displacedBy,
65
+ last: null,
66
+ viewport: viewport.frame,
67
+ // originally we do not want any animation as we want
68
+ // everything to be fixed in the same position that
69
+ // it started in
70
+ forceShouldAnimate: false,
71
+ });
72
+
73
+ const impact: DragImpact = {
74
+ displaced,
75
+ displacedBy,
76
+ at: {
77
+ type: 'REORDER',
78
+ destination: getHomeLocation(draggable.descriptor),
79
+ },
80
+ };
81
+ return { impact, afterCritical };
82
+ };
@@ -0,0 +1,30 @@
1
+ // @flow
2
+ import { type Position } from 'css-box-model';
3
+ import { subtract } from './position';
4
+
5
+ type Args = {|
6
+ scrollHeight: number,
7
+ scrollWidth: number,
8
+ height: number,
9
+ width: number,
10
+ |};
11
+ export default ({
12
+ scrollHeight,
13
+ scrollWidth,
14
+ height,
15
+ width,
16
+ }: Args): Position => {
17
+ const maxScroll: Position = subtract(
18
+ // full size
19
+ { x: scrollWidth, y: scrollHeight },
20
+ // viewport size
21
+ { x: width, y: height },
22
+ );
23
+
24
+ const adjustedMaxScroll: Position = {
25
+ x: Math.max(0, maxScroll.x),
26
+ y: Math.max(0, maxScroll.y),
27
+ };
28
+
29
+ return adjustedMaxScroll;
30
+ };
@@ -0,0 +1,6 @@
1
+ // @flow
2
+ import type { State } from '../types';
3
+ // Using function declaration as arrow function does not play well with the %checks syntax
4
+ export default function isMovementAllowed(state: State): boolean %checks {
5
+ return state.phase === 'DRAGGING' || state.phase === 'COLLECTING';
6
+ }
@@ -0,0 +1,9 @@
1
+ // @flow
2
+
3
+ // is a value between two other values
4
+
5
+ export default (
6
+ lowerBound: number,
7
+ upperBound: number,
8
+ ): ((number) => boolean) => (value: number): boolean =>
9
+ lowerBound <= value && value <= upperBound;
@@ -0,0 +1,38 @@
1
+ // @flow
2
+ import { invariant } from '../../invariant';
3
+ import type { AutoScroller } from '../auto-scroller/auto-scroller-types';
4
+ import type { Action, Dispatch, MiddlewareStore } from '../store-types';
5
+ import type { State } from '../../types';
6
+
7
+ const shouldStop = (action: Action): boolean =>
8
+ action.type === 'DROP_COMPLETE' ||
9
+ action.type === 'DROP_ANIMATE' ||
10
+ action.type === 'FLUSH';
11
+
12
+ export default (autoScroller: AutoScroller) => (store: MiddlewareStore) => (
13
+ next: Dispatch,
14
+ ) => (action: Action): any => {
15
+ if (shouldStop(action)) {
16
+ autoScroller.stop();
17
+ next(action);
18
+ return;
19
+ }
20
+
21
+ if (action.type === 'INITIAL_PUBLISH') {
22
+ // letting the action go first to hydrate the state
23
+ next(action);
24
+ const state: State = store.getState();
25
+ invariant(
26
+ state.phase === 'DRAGGING',
27
+ 'Expected phase to be DRAGGING after INITIAL_PUBLISH',
28
+ );
29
+ autoScroller.start(state);
30
+ return;
31
+ }
32
+
33
+ // auto scroll happens in response to state changes
34
+ // releasing all actions to the reducer first
35
+ next(action);
36
+
37
+ autoScroller.scroll(store.getState());
38
+ };
@@ -0,0 +1,20 @@
1
+ // @flow
2
+ import type { Action, Dispatch } from '../store-types';
3
+ import type { DimensionMarshal } from '../dimension-marshal/dimension-marshal-types';
4
+
5
+ export default (marshal: DimensionMarshal) => () => (next: Dispatch) => (
6
+ action: Action,
7
+ ): any => {
8
+ // Not stopping a collection on a 'DROP' as we want a collection to continue
9
+ if (
10
+ // drag is finished
11
+ action.type === 'DROP_COMPLETE' ||
12
+ action.type === 'FLUSH' ||
13
+ // no longer accepting changes once the drop has started
14
+ action.type === 'DROP_ANIMATE'
15
+ ) {
16
+ marshal.stopPublishing();
17
+ }
18
+
19
+ next(action);
20
+ };
@@ -0,0 +1,21 @@
1
+ // @flow
2
+ import { invariant } from '../../../invariant';
3
+ import { completeDrop } from '../../action-creators';
4
+ import type { State } from '../../../types';
5
+ import type { MiddlewareStore, Action, Dispatch } from '../../store-types';
6
+
7
+ export default (store: MiddlewareStore) => (next: Dispatch) => (
8
+ action: Action,
9
+ ): any => {
10
+ if (action.type !== 'DROP_ANIMATION_FINISHED') {
11
+ next(action);
12
+ return;
13
+ }
14
+
15
+ const state: State = store.getState();
16
+ invariant(
17
+ state.phase === 'DROP_ANIMATING',
18
+ 'Cannot finish a drop animating when no drop is occurring',
19
+ );
20
+ store.dispatch(completeDrop({ completed: state.completed }));
21
+ };
@@ -0,0 +1,63 @@
1
+ // @flow
2
+ import { dropAnimationFinished } from '../../action-creators';
3
+ import type { State } from '../../../types';
4
+ import type { MiddlewareStore, Action, Dispatch } from '../../store-types';
5
+ import type { EventBinding } from '../../../view/event-bindings/event-types';
6
+ import bindEvents from '../../../view/event-bindings/bind-events';
7
+
8
+ export default (store: MiddlewareStore) => {
9
+ let unbind: ?() => void = null;
10
+ let frameId: ?AnimationFrameID = null;
11
+
12
+ function clear() {
13
+ if (frameId) {
14
+ cancelAnimationFrame(frameId);
15
+ frameId = null;
16
+ }
17
+
18
+ if (unbind) {
19
+ unbind();
20
+ unbind = null;
21
+ }
22
+ }
23
+
24
+ return (next: Dispatch) => (action: Action): any => {
25
+ if (
26
+ action.type === 'FLUSH' ||
27
+ action.type === 'DROP_COMPLETE' ||
28
+ action.type === 'DROP_ANIMATION_FINISHED'
29
+ ) {
30
+ clear();
31
+ }
32
+
33
+ next(action);
34
+
35
+ if (action.type !== 'DROP_ANIMATE') {
36
+ return;
37
+ }
38
+
39
+ const binding: EventBinding = {
40
+ eventName: 'scroll',
41
+ // capture: true will catch all scroll events, event from scroll containers
42
+ // once: just in case, we only want to ever fire one
43
+ options: { capture: true, passive: false, once: true },
44
+ fn: function flushDropAnimation() {
45
+ const state: State = store.getState();
46
+ if (state.phase === 'DROP_ANIMATING') {
47
+ store.dispatch(dropAnimationFinished());
48
+ }
49
+ },
50
+ };
51
+
52
+ // The browser can batch a few scroll events in a single frame
53
+ // including the one that ended the drag.
54
+ // Binding after a requestAnimationFrame ensures that any scrolls caused
55
+ // by the auto scroller are finished
56
+ // TODO: why is a second window scroll being fired?
57
+ // It leads to funny drop positions :(
58
+ frameId = requestAnimationFrame(() => {
59
+ frameId = null;
60
+ unbind = bindEvents(window, [binding]);
61
+ });
62
+ };
63
+ };
@@ -0,0 +1,146 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import { invariant } from '../../../invariant';
4
+ import type {
5
+ State,
6
+ DropReason,
7
+ Critical,
8
+ DraggableLocation,
9
+ DropResult,
10
+ CompletedDrag,
11
+ Combine,
12
+ DimensionMap,
13
+ DraggableDimension,
14
+ } from '../../../types';
15
+ import type { MiddlewareStore, Dispatch, Action } from '../../store-types';
16
+ import {
17
+ animateDrop,
18
+ completeDrop,
19
+ dropPending,
20
+ type AnimateDropArgs,
21
+ } from '../../action-creators';
22
+ import { isEqual } from '../../position';
23
+ import getDropDuration from './get-drop-duration';
24
+ import getNewHomeClientOffset from './get-new-home-client-offset';
25
+ import getDropImpact, { type Result } from './get-drop-impact';
26
+ import { tryGetCombine, tryGetDestination } from '../../get-impact-location';
27
+
28
+ export default ({ getState, dispatch }: MiddlewareStore) => (
29
+ next: Dispatch,
30
+ ) => (action: Action): any => {
31
+ if (action.type !== 'DROP') {
32
+ next(action);
33
+ return;
34
+ }
35
+
36
+ const state: State = getState();
37
+ const reason: DropReason = action.payload.reason;
38
+
39
+ // Still waiting for a bulk collection to publish
40
+ // We are now shifting the application into the 'DROP_PENDING' phase
41
+ if (state.phase === 'COLLECTING') {
42
+ dispatch(dropPending({ reason }));
43
+ return;
44
+ }
45
+
46
+ // Could have occurred in response to an error
47
+ if (state.phase === 'IDLE') {
48
+ return;
49
+ }
50
+
51
+ // Still waiting for our drop pending to end
52
+ // TODO: should this throw?
53
+ const isWaitingForDrop: boolean =
54
+ state.phase === 'DROP_PENDING' && state.isWaiting;
55
+ invariant(
56
+ !isWaitingForDrop,
57
+ 'A DROP action occurred while DROP_PENDING and still waiting',
58
+ );
59
+
60
+ invariant(
61
+ state.phase === 'DRAGGING' || state.phase === 'DROP_PENDING',
62
+ `Cannot drop in phase: ${state.phase}`,
63
+ );
64
+ // We are now in the DRAGGING or DROP_PENDING phase
65
+
66
+ const critical: Critical = state.critical;
67
+ const dimensions: DimensionMap = state.dimensions;
68
+ const draggable: DraggableDimension =
69
+ dimensions.draggables[state.critical.draggable.id];
70
+ // Only keeping impact when doing a user drop - otherwise we are cancelling
71
+
72
+ const { impact, didDropInsideDroppable }: Result = getDropImpact({
73
+ reason,
74
+ lastImpact: state.impact,
75
+ afterCritical: state.afterCritical,
76
+ onLiftImpact: state.onLiftImpact,
77
+ home: state.dimensions.droppables[state.critical.droppable.id],
78
+ viewport: state.viewport,
79
+ draggables: state.dimensions.draggables,
80
+ });
81
+
82
+ // only populating destination / combine if 'didDropInsideDroppable' is true
83
+ const destination: ?DraggableLocation = didDropInsideDroppable
84
+ ? tryGetDestination(impact)
85
+ : null;
86
+ const combine: ?Combine = didDropInsideDroppable
87
+ ? tryGetCombine(impact)
88
+ : null;
89
+
90
+ const source: DraggableLocation = {
91
+ index: critical.draggable.index,
92
+ droppableId: critical.droppable.id,
93
+ };
94
+
95
+ const result: DropResult = {
96
+ draggableId: draggable.descriptor.id,
97
+ type: draggable.descriptor.type,
98
+ source,
99
+ reason,
100
+ mode: state.movementMode,
101
+ // destination / combine will be null if didDropInsideDroppable is true
102
+ destination,
103
+ combine,
104
+ };
105
+
106
+ const newHomeClientOffset: Position = getNewHomeClientOffset({
107
+ impact,
108
+ draggable,
109
+ dimensions,
110
+ viewport: state.viewport,
111
+ afterCritical: state.afterCritical,
112
+ });
113
+
114
+ const completed: CompletedDrag = {
115
+ critical: state.critical,
116
+ afterCritical: state.afterCritical,
117
+ result,
118
+ impact,
119
+ };
120
+
121
+ const isAnimationRequired: boolean =
122
+ // 1. not already in the right spot
123
+ !isEqual(state.current.client.offset, newHomeClientOffset) ||
124
+ // 2. doing a combine (we still want to animate the scale and opacity fade)
125
+ // looking at the result and not the impact as the combine impact is cleared
126
+ Boolean(result.combine);
127
+
128
+ if (!isAnimationRequired) {
129
+ dispatch(completeDrop({ completed }));
130
+ return;
131
+ }
132
+
133
+ const dropDuration: number = getDropDuration({
134
+ current: state.current.client.offset,
135
+ destination: newHomeClientOffset,
136
+ reason,
137
+ });
138
+
139
+ const args: AnimateDropArgs = {
140
+ newHomeClientOffset,
141
+ dropDuration,
142
+ completed,
143
+ };
144
+
145
+ dispatch(animateDrop(args));
146
+ };
@@ -0,0 +1,47 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import { distance as getDistance } from '../../position';
4
+ import { timings } from '../../../animation';
5
+ import type { DropReason } from '../../../types';
6
+
7
+ type GetDropDurationArgs = {|
8
+ current: Position,
9
+ destination: Position,
10
+ reason: DropReason,
11
+ |};
12
+
13
+ const { minDropTime, maxDropTime } = timings;
14
+ const dropTimeRange: number = maxDropTime - minDropTime;
15
+ const maxDropTimeAtDistance: number = 1500;
16
+ // will bring a time lower - which makes it faster
17
+ const cancelDropModifier: number = 0.6;
18
+
19
+ export default ({
20
+ current,
21
+ destination,
22
+ reason,
23
+ }: GetDropDurationArgs): number => {
24
+ const distance: number = getDistance(current, destination);
25
+ // even if there is no distance to travel, we might still need to animate opacity
26
+ if (distance <= 0) {
27
+ return minDropTime;
28
+ }
29
+
30
+ if (distance >= maxDropTimeAtDistance) {
31
+ return maxDropTime;
32
+ }
33
+
34
+ // * range from:
35
+ // 0px = 0.33s
36
+ // 1500px and over = 0.55s
37
+ // * If reason === 'CANCEL' then speeding up the animation
38
+ // * round to 2 decimal points
39
+
40
+ const percentage: number = distance / maxDropTimeAtDistance;
41
+ const duration: number = minDropTime + dropTimeRange * percentage;
42
+
43
+ const withDuration: number =
44
+ reason === 'CANCEL' ? duration * cancelDropModifier : duration;
45
+ // To two decimal points by converting to string and back
46
+ return Number(withDuration.toFixed(2));
47
+ };
@@ -0,0 +1,77 @@
1
+ // @flow
2
+ import type {
3
+ DropReason,
4
+ DragImpact,
5
+ Viewport,
6
+ DroppableDimension,
7
+ DraggableDimensionMap,
8
+ LiftEffect,
9
+ } from '../../../types';
10
+ import recompute from '../../update-displacement-visibility/recompute';
11
+ import { emptyGroups } from '../../no-impact';
12
+
13
+ type Args = {|
14
+ draggables: DraggableDimensionMap,
15
+ home: DroppableDimension,
16
+ reason: DropReason,
17
+ lastImpact: DragImpact,
18
+ onLiftImpact: DragImpact,
19
+ viewport: Viewport,
20
+ afterCritical: LiftEffect,
21
+ |};
22
+
23
+ export type Result = {|
24
+ impact: DragImpact,
25
+ didDropInsideDroppable: boolean,
26
+ |};
27
+
28
+ export default ({
29
+ draggables,
30
+ reason,
31
+ lastImpact,
32
+ home,
33
+ viewport,
34
+ onLiftImpact,
35
+ }: Args): Result => {
36
+ if (!lastImpact.at || reason !== 'DROP') {
37
+ // Dropping outside of a list or the drag was cancelled
38
+
39
+ // Going to use the on lift impact
40
+ // Need to recompute the visibility of the original impact
41
+ // What is visible can be different to when the drag started
42
+
43
+ const recomputedHomeImpact: DragImpact = recompute({
44
+ draggables,
45
+ impact: onLiftImpact,
46
+ destination: home,
47
+ viewport,
48
+ // We need the draggables to animate back to their positions
49
+ forceShouldAnimate: true,
50
+ });
51
+
52
+ return {
53
+ impact: recomputedHomeImpact,
54
+ didDropInsideDroppable: false,
55
+ };
56
+ }
57
+
58
+ // use the existing impact
59
+ if (lastImpact.at.type === 'REORDER') {
60
+ return {
61
+ impact: lastImpact,
62
+ didDropInsideDroppable: true,
63
+ };
64
+ }
65
+
66
+ // When merging we remove the movement so that everything
67
+ // will animate closed
68
+ const withoutMovement: DragImpact = {
69
+ ...lastImpact,
70
+ displaced: emptyGroups,
71
+ };
72
+
73
+ return {
74
+ impact: withoutMovement,
75
+ didDropInsideDroppable: true,
76
+ };
77
+ };
@@ -0,0 +1,54 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import type {
4
+ DroppableDimension,
5
+ Viewport,
6
+ DragImpact,
7
+ DimensionMap,
8
+ DraggableDimension,
9
+ DroppableId,
10
+ LiftEffect,
11
+ } from '../../../types';
12
+ import whatIsDraggedOver from '../../droppable/what-is-dragged-over';
13
+ import { subtract } from '../../position';
14
+ import getClientBorderBoxCenter from '../../get-center-from-impact/get-client-border-box-center';
15
+
16
+ type Args = {|
17
+ impact: DragImpact,
18
+ draggable: DraggableDimension,
19
+ dimensions: DimensionMap,
20
+ viewport: Viewport,
21
+ afterCritical: LiftEffect,
22
+ |};
23
+
24
+ export default ({
25
+ impact,
26
+ draggable,
27
+ dimensions,
28
+ viewport,
29
+ afterCritical,
30
+ }: Args): Position => {
31
+ const { draggables, droppables } = dimensions;
32
+ const droppableId: ?DroppableId = whatIsDraggedOver(impact);
33
+ const destination: ?DroppableDimension = droppableId
34
+ ? droppables[droppableId]
35
+ : null;
36
+ const home: DroppableDimension = droppables[draggable.descriptor.droppableId];
37
+
38
+ const newClientCenter: Position = getClientBorderBoxCenter({
39
+ impact,
40
+ draggable,
41
+ draggables,
42
+ // if there is no destination, then we will be dropping back into the home
43
+ afterCritical,
44
+ droppable: destination || home,
45
+ viewport,
46
+ });
47
+
48
+ const offset: Position = subtract(
49
+ newClientCenter,
50
+ draggable.client.borderBox.center,
51
+ );
52
+
53
+ return offset;
54
+ };
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './drop-middleware';
@@ -0,0 +1,42 @@
1
+ // @flow
2
+ import type { DropResult } from '../../types';
3
+ import type { Action, Dispatch } from '../store-types';
4
+ import type { FocusMarshal } from '../../view/use-focus-marshal/focus-marshal-types';
5
+
6
+ export default (marshal: FocusMarshal) => {
7
+ let isWatching: boolean = false;
8
+
9
+ return () => (next: Dispatch) => (action: Action): any => {
10
+ if (action.type === 'INITIAL_PUBLISH') {
11
+ isWatching = true;
12
+
13
+ marshal.tryRecordFocus(action.payload.critical.draggable.id);
14
+ next(action);
15
+ marshal.tryRestoreFocusRecorded();
16
+ return;
17
+ }
18
+
19
+ next(action);
20
+
21
+ if (!isWatching) {
22
+ return;
23
+ }
24
+
25
+ if (action.type === 'FLUSH') {
26
+ isWatching = false;
27
+ marshal.tryRestoreFocusRecorded();
28
+ return;
29
+ }
30
+
31
+ if (action.type === 'DROP_COMPLETE') {
32
+ isWatching = false;
33
+ const result: DropResult = action.payload.completed.result;
34
+
35
+ // give focus to the combine target when combining
36
+ if (result.combine) {
37
+ marshal.tryShiftRecord(result.draggableId, result.combine.draggableId);
38
+ }
39
+ marshal.tryRestoreFocusRecorded();
40
+ }
41
+ };
42
+ };