@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,457 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import { invariant } from '../invariant';
4
+ import type {
5
+ DimensionMap,
6
+ State,
7
+ StateWhenUpdatesAllowed,
8
+ DraggableDimension,
9
+ DroppableDimension,
10
+ IdleState,
11
+ DraggingState,
12
+ DragPositions,
13
+ ClientPositions,
14
+ CollectingState,
15
+ DropAnimatingState,
16
+ DropPendingState,
17
+ Viewport,
18
+ DropReason,
19
+ } from '../types';
20
+ import type { Action } from './store-types';
21
+ import type { PublicResult as MoveInDirectionResult } from './move-in-direction/move-in-direction-types';
22
+ import scrollDroppable from './droppable/scroll-droppable';
23
+ import moveInDirection from './move-in-direction';
24
+ import { add, isEqual, origin } from './position';
25
+ import scrollViewport from './scroll-viewport';
26
+ import isMovementAllowed from './is-movement-allowed';
27
+ import { toDroppableList } from './dimension-structures';
28
+ import update from './post-reducer/when-moving/update';
29
+ import refreshSnap from './post-reducer/when-moving/refresh-snap';
30
+ import getLiftEffect from './get-lift-effect';
31
+ import patchDimensionMap from './patch-dimension-map';
32
+ import publishWhileDraggingInVirtual from './publish-while-dragging-in-virtual';
33
+
34
+ const isSnapping = (state: StateWhenUpdatesAllowed): boolean =>
35
+ state.movementMode === 'SNAP';
36
+
37
+ const postDroppableChange = (
38
+ state: StateWhenUpdatesAllowed,
39
+ updated: DroppableDimension,
40
+ isEnabledChanging: boolean,
41
+ ): StateWhenUpdatesAllowed => {
42
+ const dimensions: DimensionMap = patchDimensionMap(state.dimensions, updated);
43
+
44
+ // if the enabled state is changing, we need to force a update
45
+ if (!isSnapping(state) || isEnabledChanging) {
46
+ return update({
47
+ state,
48
+ dimensions,
49
+ });
50
+ }
51
+
52
+ return refreshSnap({
53
+ state,
54
+ dimensions,
55
+ });
56
+ };
57
+
58
+ function removeScrollJumpRequest(state: State): State {
59
+ if (state.isDragging && state.movementMode === 'SNAP') {
60
+ return {
61
+ // will be overwritten by spread
62
+ // needed for flow
63
+ phase: 'DRAGGING',
64
+ ...state,
65
+ scrollJumpRequest: null,
66
+ };
67
+ }
68
+ return state;
69
+ }
70
+
71
+ const idle: IdleState = { phase: 'IDLE', completed: null, shouldFlush: false };
72
+
73
+ export default (state: State = idle, action: Action): State => {
74
+ if (action.type === 'FLUSH') {
75
+ return {
76
+ ...idle,
77
+ shouldFlush: true,
78
+ };
79
+ }
80
+
81
+ if (action.type === 'INITIAL_PUBLISH') {
82
+ invariant(
83
+ state.phase === 'IDLE',
84
+ 'INITIAL_PUBLISH must come after a IDLE phase',
85
+ );
86
+ const {
87
+ critical,
88
+ clientSelection,
89
+ viewport,
90
+ dimensions,
91
+ movementMode,
92
+ } = action.payload;
93
+
94
+ const draggable: DraggableDimension =
95
+ dimensions.draggables[critical.draggable.id];
96
+ const home: DroppableDimension =
97
+ dimensions.droppables[critical.droppable.id];
98
+
99
+ const client: ClientPositions = {
100
+ selection: clientSelection,
101
+ borderBoxCenter: draggable.client.borderBox.center,
102
+ offset: origin,
103
+ };
104
+
105
+ const initial: DragPositions = {
106
+ client,
107
+ page: {
108
+ selection: add(client.selection, viewport.scroll.initial),
109
+ borderBoxCenter: add(client.selection, viewport.scroll.initial),
110
+ offset: add(client.selection, viewport.scroll.diff.value),
111
+ },
112
+ };
113
+
114
+ // Can only auto scroll the window if every list is not fixed on the page
115
+ const isWindowScrollAllowed: boolean = toDroppableList(
116
+ dimensions.droppables,
117
+ ).every((item: DroppableDimension) => !item.isFixedOnPage);
118
+
119
+ const { impact, afterCritical } = getLiftEffect({
120
+ draggable,
121
+ home,
122
+ draggables: dimensions.draggables,
123
+ viewport,
124
+ });
125
+
126
+ const result: DraggingState = {
127
+ phase: 'DRAGGING',
128
+ isDragging: true,
129
+ critical,
130
+ movementMode,
131
+ dimensions,
132
+ initial,
133
+ current: initial,
134
+ isWindowScrollAllowed,
135
+ impact,
136
+ afterCritical,
137
+ onLiftImpact: impact,
138
+ viewport,
139
+ scrollJumpRequest: null,
140
+ forceShouldAnimate: null,
141
+ };
142
+
143
+ return result;
144
+ }
145
+
146
+ if (action.type === 'COLLECTION_STARTING') {
147
+ // A collection might have restarted. We do not care as we are already in the right phase
148
+ // TODO: remove?
149
+ if (state.phase === 'COLLECTING' || state.phase === 'DROP_PENDING') {
150
+ return state;
151
+ }
152
+
153
+ invariant(
154
+ state.phase === 'DRAGGING',
155
+ `Collection cannot start from phase ${state.phase}`,
156
+ );
157
+
158
+ const result: CollectingState = {
159
+ // putting phase first to appease flow
160
+ phase: 'COLLECTING',
161
+ ...state,
162
+ // eslint-disable-next-line
163
+ phase: 'COLLECTING',
164
+ };
165
+
166
+ return result;
167
+ }
168
+
169
+ if (action.type === 'PUBLISH_WHILE_DRAGGING') {
170
+ // Unexpected bulk publish
171
+ invariant(
172
+ state.phase === 'COLLECTING' || state.phase === 'DROP_PENDING',
173
+ `Unexpected ${action.type} received in phase ${state.phase}`,
174
+ );
175
+
176
+ return publishWhileDraggingInVirtual({
177
+ state,
178
+ published: action.payload,
179
+ });
180
+ }
181
+
182
+ if (action.type === 'MOVE') {
183
+ // Not allowing any more movements
184
+ if (state.phase === 'DROP_PENDING') {
185
+ return state;
186
+ }
187
+
188
+ invariant(
189
+ isMovementAllowed(state),
190
+ `${action.type} not permitted in phase ${state.phase}`,
191
+ );
192
+
193
+ const { client: clientSelection } = action.payload;
194
+
195
+ // nothing needs to be done
196
+ if (isEqual(clientSelection, state.current.client.selection)) {
197
+ return state;
198
+ }
199
+
200
+ return update({
201
+ state,
202
+ clientSelection,
203
+ // If we are snap moving - manual movements should not update the impact
204
+ impact: isSnapping(state) ? state.impact : null,
205
+ });
206
+ }
207
+
208
+ if (action.type === 'UPDATE_DROPPABLE_SCROLL') {
209
+ // Not allowing changes while a drop is pending
210
+ // Cannot get this during a DROP_ANIMATING as the dimension
211
+ // marshal will cancel any pending scroll updates
212
+ if (state.phase === 'DROP_PENDING') {
213
+ return removeScrollJumpRequest(state);
214
+ }
215
+
216
+ // We will be updating the scroll in response to dynamic changes
217
+ // manually on the droppable so we can ignore this change
218
+ if (state.phase === 'COLLECTING') {
219
+ return removeScrollJumpRequest(state);
220
+ }
221
+
222
+ invariant(
223
+ isMovementAllowed(state),
224
+ `${action.type} not permitted in phase ${state.phase}`,
225
+ );
226
+
227
+ const { id, newScroll } = action.payload;
228
+ const target: ?DroppableDimension = state.dimensions.droppables[id];
229
+
230
+ // This is possible if a droppable has been asked to watch scroll but
231
+ // the dimension has not been published yet
232
+ if (!target) {
233
+ return state;
234
+ }
235
+
236
+ const scrolled: DroppableDimension = scrollDroppable(target, newScroll);
237
+ return postDroppableChange(state, scrolled, false);
238
+ }
239
+
240
+ if (action.type === 'UPDATE_DROPPABLE_IS_ENABLED') {
241
+ // Things are locked at this point
242
+ if (state.phase === 'DROP_PENDING') {
243
+ return state;
244
+ }
245
+
246
+ invariant(
247
+ isMovementAllowed(state),
248
+ `Attempting to move in an unsupported phase ${state.phase}`,
249
+ );
250
+
251
+ const { id, isEnabled } = action.payload;
252
+ const target: ?DroppableDimension = state.dimensions.droppables[id];
253
+
254
+ invariant(
255
+ target,
256
+ `Cannot find Droppable[id: ${id}] to toggle its enabled state`,
257
+ );
258
+
259
+ invariant(
260
+ target.isEnabled !== isEnabled,
261
+ `Trying to set droppable isEnabled to ${String(isEnabled)}
262
+ but it is already ${String(target.isEnabled)}`,
263
+ );
264
+
265
+ const updated: DroppableDimension = {
266
+ ...target,
267
+ isEnabled,
268
+ };
269
+
270
+ return postDroppableChange(state, updated, true);
271
+ }
272
+
273
+ if (action.type === 'UPDATE_DROPPABLE_IS_COMBINE_ENABLED') {
274
+ // Things are locked at this point
275
+ if (state.phase === 'DROP_PENDING') {
276
+ return state;
277
+ }
278
+
279
+ invariant(
280
+ isMovementAllowed(state),
281
+ `Attempting to move in an unsupported phase ${state.phase}`,
282
+ );
283
+
284
+ const { id, isCombineEnabled } = action.payload;
285
+ const target: ?DroppableDimension = state.dimensions.droppables[id];
286
+
287
+ invariant(
288
+ target,
289
+ `Cannot find Droppable[id: ${id}] to toggle its isCombineEnabled state`,
290
+ );
291
+
292
+ invariant(
293
+ target.isCombineEnabled !== isCombineEnabled,
294
+ `Trying to set droppable isCombineEnabled to ${String(isCombineEnabled)}
295
+ but it is already ${String(target.isCombineEnabled)}`,
296
+ );
297
+
298
+ const updated: DroppableDimension = {
299
+ ...target,
300
+ isCombineEnabled,
301
+ };
302
+
303
+ return postDroppableChange(state, updated, true);
304
+ }
305
+
306
+ if (action.type === 'MOVE_BY_WINDOW_SCROLL') {
307
+ // No longer accepting changes
308
+ if (state.phase === 'DROP_PENDING' || state.phase === 'DROP_ANIMATING') {
309
+ return state;
310
+ }
311
+
312
+ invariant(
313
+ isMovementAllowed(state),
314
+ `Cannot move by window in phase ${state.phase}`,
315
+ );
316
+
317
+ invariant(
318
+ state.isWindowScrollAllowed,
319
+ 'Window scrolling is currently not supported for fixed lists',
320
+ );
321
+
322
+ const newScroll: Position = action.payload.newScroll;
323
+
324
+ // nothing needs to be done
325
+ if (isEqual(state.viewport.scroll.current, newScroll)) {
326
+ return removeScrollJumpRequest(state);
327
+ }
328
+
329
+ const viewport: Viewport = scrollViewport(state.viewport, newScroll);
330
+
331
+ if (isSnapping(state)) {
332
+ return refreshSnap({
333
+ state,
334
+ viewport,
335
+ });
336
+ }
337
+
338
+ return update({
339
+ state,
340
+ viewport,
341
+ });
342
+ }
343
+
344
+ if (action.type === 'UPDATE_VIEWPORT_MAX_SCROLL') {
345
+ // Could occur if a transitionEnd occurs after a drag ends
346
+ if (!isMovementAllowed(state)) {
347
+ return state;
348
+ }
349
+
350
+ const maxScroll: Position = action.payload.maxScroll;
351
+
352
+ if (isEqual(maxScroll, state.viewport.scroll.max)) {
353
+ return state;
354
+ }
355
+
356
+ const withMaxScroll: Viewport = {
357
+ ...state.viewport,
358
+ scroll: {
359
+ ...state.viewport.scroll,
360
+ max: maxScroll,
361
+ },
362
+ };
363
+
364
+ // don't need to recalc any updates
365
+ return {
366
+ // phase will be overridden - appeasing flow
367
+ phase: 'DRAGGING',
368
+ ...state,
369
+ viewport: withMaxScroll,
370
+ };
371
+ }
372
+ if (
373
+ action.type === 'MOVE_UP' ||
374
+ action.type === 'MOVE_DOWN' ||
375
+ action.type === 'MOVE_LEFT' ||
376
+ action.type === 'MOVE_RIGHT'
377
+ ) {
378
+ // Not doing keyboard movements during these phases
379
+ if (state.phase === 'COLLECTING' || state.phase === 'DROP_PENDING') {
380
+ return state;
381
+ }
382
+
383
+ invariant(
384
+ state.phase === 'DRAGGING',
385
+ `${action.type} received while not in DRAGGING phase`,
386
+ );
387
+
388
+ const result: ?MoveInDirectionResult = moveInDirection({
389
+ state,
390
+ type: action.type,
391
+ });
392
+
393
+ // cannot move in that direction
394
+ if (!result) {
395
+ return state;
396
+ }
397
+
398
+ return update({
399
+ state,
400
+ impact: result.impact,
401
+ clientSelection: result.clientSelection,
402
+ scrollJumpRequest: result.scrollJumpRequest,
403
+ });
404
+ }
405
+
406
+ if (action.type === 'DROP_PENDING') {
407
+ const reason: DropReason = action.payload.reason;
408
+ invariant(
409
+ state.phase === 'COLLECTING',
410
+ 'Can only move into the DROP_PENDING phase from the COLLECTING phase',
411
+ );
412
+
413
+ const newState: DropPendingState = {
414
+ // appeasing flow
415
+ phase: 'DROP_PENDING',
416
+ ...state,
417
+ // eslint-disable-next-line
418
+ phase: 'DROP_PENDING',
419
+ isWaiting: true,
420
+ reason,
421
+ };
422
+ return newState;
423
+ }
424
+
425
+ if (action.type === 'DROP_ANIMATE') {
426
+ const { completed, dropDuration, newHomeClientOffset } = action.payload;
427
+ invariant(
428
+ state.phase === 'DRAGGING' || state.phase === 'DROP_PENDING',
429
+ `Cannot animate drop from phase ${state.phase}`,
430
+ );
431
+
432
+ // Moving into a new phase
433
+ const result: DropAnimatingState = {
434
+ phase: 'DROP_ANIMATING',
435
+ completed,
436
+ dropDuration,
437
+ newHomeClientOffset,
438
+ dimensions: state.dimensions,
439
+ };
440
+
441
+ return result;
442
+ }
443
+
444
+ // Action will be used by responders to call consumers
445
+ // We can simply return to the idle state
446
+ if (action.type === 'DROP_COMPLETE') {
447
+ const { completed } = action.payload;
448
+
449
+ return {
450
+ phase: 'IDLE',
451
+ completed,
452
+ shouldFlush: false,
453
+ };
454
+ }
455
+
456
+ return state;
457
+ };
@@ -0,0 +1,162 @@
1
+ // @flow
2
+ import { invariant } from '../../invariant';
3
+ import type { TypeId, DraggableId, DroppableId } from '../../types';
4
+ import type {
5
+ Registry,
6
+ DraggableAPI,
7
+ DroppableAPI,
8
+ DraggableEntry,
9
+ DroppableEntry,
10
+ RegistryEvent,
11
+ Subscriber,
12
+ Unsubscribe,
13
+ DraggableEntryMap,
14
+ DroppableEntryMap,
15
+ } from './registry-types';
16
+ import { values } from '../../native-with-fallback';
17
+
18
+ type EntryMap = {
19
+ draggables: DraggableEntryMap,
20
+ droppables: DroppableEntryMap,
21
+ };
22
+
23
+ export default function createRegistry(): Registry {
24
+ const entries: EntryMap = {
25
+ draggables: {},
26
+ droppables: {},
27
+ };
28
+
29
+ const subscribers: Subscriber[] = [];
30
+
31
+ function subscribe(cb: Subscriber): Unsubscribe {
32
+ subscribers.push(cb);
33
+
34
+ return function unsubscribe(): void {
35
+ const index: number = subscribers.indexOf(cb);
36
+
37
+ // might have been removed by a clean
38
+ if (index === -1) {
39
+ return;
40
+ }
41
+
42
+ subscribers.splice(index, 1);
43
+ };
44
+ }
45
+
46
+ function notify(event: RegistryEvent) {
47
+ if (subscribers.length) {
48
+ subscribers.forEach((cb) => cb(event));
49
+ }
50
+ }
51
+
52
+ function findDraggableById(id: DraggableId): ?DraggableEntry {
53
+ return entries.draggables[id] || null;
54
+ }
55
+
56
+ function getDraggableById(id: DraggableId): DraggableEntry {
57
+ const entry: ?DraggableEntry = findDraggableById(id);
58
+ invariant(entry, `Cannot find draggable entry with id [${id}]`);
59
+ return entry;
60
+ }
61
+
62
+ const draggableAPI: DraggableAPI = {
63
+ register: (entry: DraggableEntry) => {
64
+ entries.draggables[entry.descriptor.id] = entry;
65
+ notify({ type: 'ADDITION', value: entry });
66
+ },
67
+ update: (entry: DraggableEntry, last: DraggableEntry) => {
68
+ const current: ?DraggableEntry = entries.draggables[last.descriptor.id];
69
+
70
+ // item already removed
71
+ if (!current) {
72
+ return;
73
+ }
74
+
75
+ // id already used for another mount
76
+ if (current.uniqueId !== entry.uniqueId) {
77
+ return;
78
+ }
79
+
80
+ // We are safe to delete the old entry and add a new one
81
+ delete entries.draggables[last.descriptor.id];
82
+ entries.draggables[entry.descriptor.id] = entry;
83
+ },
84
+ unregister: (entry: DraggableEntry) => {
85
+ const draggableId: DraggableId = entry.descriptor.id;
86
+ const current: ?DraggableEntry = findDraggableById(draggableId);
87
+
88
+ // can occur if cleaned before unregistration
89
+ if (!current) {
90
+ return;
91
+ }
92
+
93
+ // outdated uniqueId
94
+ if (entry.uniqueId !== current.uniqueId) {
95
+ return;
96
+ }
97
+
98
+ delete entries.draggables[draggableId];
99
+ notify({ type: 'REMOVAL', value: entry });
100
+ },
101
+ getById: getDraggableById,
102
+ findById: findDraggableById,
103
+ exists: (id: DraggableId): boolean => Boolean(findDraggableById(id)),
104
+ getAllByType: (type: TypeId): DraggableEntry[] =>
105
+ values(entries.draggables).filter(
106
+ (entry: DraggableEntry): boolean => entry.descriptor.type === type,
107
+ ),
108
+ };
109
+
110
+ function findDroppableById(id: DroppableId): ?DroppableEntry {
111
+ return entries.droppables[id] || null;
112
+ }
113
+
114
+ function getDroppableById(id: DroppableId): DroppableEntry {
115
+ const entry: ?DroppableEntry = findDroppableById(id);
116
+ invariant(entry, `Cannot find droppable entry with id [${id}]`);
117
+ return entry;
118
+ }
119
+
120
+ const droppableAPI: DroppableAPI = {
121
+ register: (entry: DroppableEntry) => {
122
+ entries.droppables[entry.descriptor.id] = entry;
123
+ },
124
+ unregister: (entry: DroppableEntry) => {
125
+ const current: ?DroppableEntry = findDroppableById(entry.descriptor.id);
126
+
127
+ // can occur if cleaned before an unregistry
128
+ if (!current) {
129
+ return;
130
+ }
131
+
132
+ // already changed
133
+ if (entry.uniqueId !== current.uniqueId) {
134
+ return;
135
+ }
136
+
137
+ delete entries.droppables[entry.descriptor.id];
138
+ },
139
+ getById: getDroppableById,
140
+ findById: findDroppableById,
141
+ exists: (id: DroppableId): boolean => Boolean(findDroppableById(id)),
142
+ getAllByType: (type: TypeId): DroppableEntry[] =>
143
+ values(entries.droppables).filter(
144
+ (entry: DroppableEntry): boolean => entry.descriptor.type === type,
145
+ ),
146
+ };
147
+
148
+ function clean(): void {
149
+ // kill entries
150
+ entries.draggables = {};
151
+ entries.droppables = {};
152
+ // remove all subscribers
153
+ subscribers.length = 0;
154
+ }
155
+
156
+ return {
157
+ draggable: draggableAPI,
158
+ droppable: droppableAPI,
159
+ subscribe,
160
+ clean,
161
+ };
162
+ }
@@ -0,0 +1,98 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import type {
4
+ Id,
5
+ DraggableId,
6
+ DraggableDescriptor,
7
+ DraggableOptions,
8
+ DraggableDimension,
9
+ DroppableId,
10
+ ScrollOptions,
11
+ DroppableDescriptor,
12
+ DroppableDimension,
13
+ TypeId,
14
+ } from '../../types';
15
+
16
+ export type GetDraggableDimensionFn = (
17
+ windowScroll: Position,
18
+ ) => DraggableDimension;
19
+
20
+ export type DraggableEntry = {|
21
+ uniqueId: Id,
22
+ descriptor: DraggableDescriptor,
23
+ options: DraggableOptions,
24
+ getDimension: GetDraggableDimensionFn,
25
+ |};
26
+
27
+ export type DraggableAPI = {|
28
+ register: (entry: DraggableEntry) => void,
29
+ update: (entry: DraggableEntry, last: DraggableEntry) => void,
30
+ unregister: (entry: DraggableEntry) => void,
31
+ exists: (id: DraggableId) => boolean,
32
+ getById: (id: DraggableId) => DraggableEntry,
33
+ findById: (id: DraggableId) => ?DraggableEntry,
34
+ getAllByType: (type: TypeId) => DraggableEntry[],
35
+ |};
36
+
37
+ export type GetDroppableDimensionFn = (
38
+ windowScroll: Position,
39
+ options: ScrollOptions,
40
+ ) => DroppableDimension;
41
+
42
+ export type RecollectDroppableOptions = {|
43
+ withoutPlaceholder: boolean,
44
+ |};
45
+
46
+ export type DroppableCallbacks = {|
47
+ // a drag is starting
48
+ getDimensionAndWatchScroll: GetDroppableDimensionFn,
49
+ getScrollWhileDragging: () => Position,
50
+ // scroll a droppable
51
+ scroll: (change: Position) => void,
52
+ // If the Droppable is listening for scroll events - it needs to stop!
53
+ // Can be called on droppables that have not been asked to watch scroll
54
+ dragStopped: () => void,
55
+ |};
56
+
57
+ export type DroppableEntry = {|
58
+ uniqueId: Id,
59
+ descriptor: DroppableDescriptor,
60
+ callbacks: DroppableCallbacks,
61
+ |};
62
+
63
+ export type DraggableEntryMap = {
64
+ [id: DraggableId]: DraggableEntry,
65
+ };
66
+
67
+ export type DroppableEntryMap = {
68
+ [id: DroppableId]: DroppableEntry,
69
+ };
70
+
71
+ export type DroppableAPI = {|
72
+ register: (entry: DroppableEntry) => void,
73
+ unregister: (entry: DroppableEntry) => void,
74
+ exists: (id: DraggableId) => boolean,
75
+ getById: (id: DroppableId) => DroppableEntry,
76
+ findById: (id: DroppableId) => ?DroppableEntry,
77
+ getAllByType: (type: TypeId) => DroppableEntry[],
78
+ |};
79
+
80
+ export type RegistryEvent =
81
+ | {|
82
+ type: 'ADDITION',
83
+ value: DraggableEntry,
84
+ |}
85
+ | {|
86
+ type: 'REMOVAL',
87
+ value: DraggableEntry,
88
+ |};
89
+
90
+ export type Subscriber = (event: RegistryEvent) => void;
91
+ export type Unsubscribe = () => void;
92
+
93
+ export type Registry = {|
94
+ draggable: DraggableAPI,
95
+ droppable: DroppableAPI,
96
+ subscribe: (cb: Subscriber) => Unsubscribe,
97
+ clean: () => void,
98
+ |};