@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,71 @@
1
+ // @flow
2
+ import type {
3
+ Critical,
4
+ DimensionMap,
5
+ DraggableDimension,
6
+ } from '../../../types';
7
+ import getDraggablesInsideDroppable from '../../get-draggables-inside-droppable';
8
+ import { warning } from '../../../dev-warning';
9
+
10
+ type ErrorMap = {
11
+ [index: number]: true,
12
+ };
13
+
14
+ function checkIndexes(insideDestination: DraggableDimension[]) {
15
+ // no point running if there are 1 or less items
16
+ if (insideDestination.length <= 1) {
17
+ return;
18
+ }
19
+
20
+ const indexes: number[] = insideDestination.map(
21
+ (d: DraggableDimension): number => d.descriptor.index,
22
+ );
23
+
24
+ const errors: ErrorMap = {};
25
+
26
+ for (let i = 1; i < indexes.length; i++) {
27
+ const current: number = indexes[i];
28
+ const previous: number = indexes[i - 1];
29
+
30
+ // this will be an error if:
31
+ // 1. index is not consecutive
32
+ // 2. index is duplicated (which is true if #1 is not passed)
33
+ if (current !== previous + 1) {
34
+ errors[current] = true;
35
+ }
36
+ }
37
+
38
+ if (!Object.keys(errors).length) {
39
+ return;
40
+ }
41
+
42
+ const formatted: string = indexes
43
+ .map((index: number): string => {
44
+ const hasError: boolean = Boolean(errors[index]);
45
+
46
+ return hasError ? `[🔥${index}]` : `${index}`;
47
+ })
48
+ .join(', ');
49
+
50
+ warning(`
51
+ Detected non-consecutive <Draggable /> indexes.
52
+
53
+ (This can cause unexpected bugs)
54
+
55
+ ${formatted}
56
+ `);
57
+ }
58
+
59
+ export default function validateDimensions(
60
+ critical: Critical,
61
+ dimensions: DimensionMap,
62
+ ): void {
63
+ // wrapping entire block for better minification
64
+ if (process.env.NODE_ENV !== 'production') {
65
+ const insideDestination: DraggableDimension[] = getDraggablesInsideDroppable(
66
+ critical.droppable.id,
67
+ dimensions.draggables,
68
+ );
69
+ checkIndexes(insideDestination);
70
+ }
71
+ }
@@ -0,0 +1,84 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import type { PublicResult } from './move-in-direction-types';
4
+ import type {
5
+ DroppableId,
6
+ DraggingState,
7
+ Direction,
8
+ DroppableDimension,
9
+ DraggableDimension,
10
+ DroppableDimensionMap,
11
+ DragImpact,
12
+ } from '../../types';
13
+ import moveToNextPlace from './move-to-next-place';
14
+ import moveCrossAxis from './move-cross-axis';
15
+ import whatIsDraggedOver from '../droppable/what-is-dragged-over';
16
+
17
+ type Args = {|
18
+ state: DraggingState,
19
+ type: 'MOVE_UP' | 'MOVE_RIGHT' | 'MOVE_DOWN' | 'MOVE_LEFT',
20
+ |};
21
+
22
+ const getDroppableOver = (
23
+ impact: DragImpact,
24
+ droppables: DroppableDimensionMap,
25
+ ): ?DroppableDimension => {
26
+ const id: ?DroppableId = whatIsDraggedOver(impact);
27
+ return id ? droppables[id] : null;
28
+ };
29
+
30
+ export default ({ state, type }: Args): ?PublicResult => {
31
+ const isActuallyOver: ?DroppableDimension = getDroppableOver(
32
+ state.impact,
33
+ state.dimensions.droppables,
34
+ );
35
+ const isMainAxisMovementAllowed: boolean = Boolean(isActuallyOver);
36
+ const home: DroppableDimension =
37
+ state.dimensions.droppables[state.critical.droppable.id];
38
+ // use home when not actually over a droppable (can happen when move is disabled)
39
+ const isOver: DroppableDimension = isActuallyOver || home;
40
+
41
+ const direction: Direction = isOver.axis.direction;
42
+ const isMovingOnMainAxis: boolean =
43
+ (direction === 'vertical' &&
44
+ (type === 'MOVE_UP' || type === 'MOVE_DOWN')) ||
45
+ (direction === 'horizontal' &&
46
+ (type === 'MOVE_LEFT' || type === 'MOVE_RIGHT'));
47
+
48
+ // This movement is not permitted right now
49
+ if (isMovingOnMainAxis && !isMainAxisMovementAllowed) {
50
+ return null;
51
+ }
52
+
53
+ const isMovingForward: boolean =
54
+ type === 'MOVE_DOWN' || type === 'MOVE_RIGHT';
55
+
56
+ const draggable: DraggableDimension =
57
+ state.dimensions.draggables[state.critical.draggable.id];
58
+ const previousPageBorderBoxCenter: Position =
59
+ state.current.page.borderBoxCenter;
60
+ const { draggables, droppables } = state.dimensions;
61
+
62
+ return isMovingOnMainAxis
63
+ ? moveToNextPlace({
64
+ isMovingForward,
65
+ previousPageBorderBoxCenter,
66
+ draggable,
67
+ destination: isOver,
68
+ draggables,
69
+ viewport: state.viewport,
70
+ previousClientSelection: state.current.client.selection,
71
+ previousImpact: state.impact,
72
+ afterCritical: state.afterCritical,
73
+ })
74
+ : moveCrossAxis({
75
+ isMovingForward,
76
+ previousPageBorderBoxCenter,
77
+ draggable,
78
+ isOver,
79
+ draggables,
80
+ droppables,
81
+ viewport: state.viewport,
82
+ afterCritical: state.afterCritical,
83
+ });
84
+ };
@@ -0,0 +1,168 @@
1
+ // @flow
2
+ import { type Position, type Rect } from 'css-box-model';
3
+ import { invariant } from '../../../invariant';
4
+ import { closest } from '../../position';
5
+ import isWithin from '../../is-within';
6
+ import { getCorners } from '../../spacing';
7
+ import isPartiallyVisibleThroughFrame from '../../visibility/is-partially-visible-through-frame';
8
+ import { toDroppableList } from '../../dimension-structures';
9
+ import type {
10
+ Axis,
11
+ DroppableDimension,
12
+ DroppableDimensionMap,
13
+ Viewport,
14
+ } from '../../../types';
15
+
16
+ type GetBestDroppableArgs = {|
17
+ isMovingForward: boolean,
18
+ // the current position of the dragging item
19
+ pageBorderBoxCenter: Position,
20
+ // the home of the draggable
21
+ source: DroppableDimension,
22
+ // all the droppables in the system
23
+ droppables: DroppableDimensionMap,
24
+ viewport: Viewport,
25
+ |};
26
+
27
+ const getKnownActive = (droppable: DroppableDimension): Rect => {
28
+ const rect: ?Rect = droppable.subject.active;
29
+
30
+ invariant(rect, 'Cannot get clipped area from droppable');
31
+
32
+ return rect;
33
+ };
34
+
35
+ export default ({
36
+ isMovingForward,
37
+ pageBorderBoxCenter,
38
+ source,
39
+ droppables,
40
+ viewport,
41
+ }: GetBestDroppableArgs): ?DroppableDimension => {
42
+ const active: ?Rect = source.subject.active;
43
+
44
+ if (!active) {
45
+ return null;
46
+ }
47
+
48
+ const axis: Axis = source.axis;
49
+ const isBetweenSourceClipped = isWithin(active[axis.start], active[axis.end]);
50
+ const candidates: DroppableDimension[] = toDroppableList(droppables)
51
+ // Remove the source droppable from the list
52
+ .filter((droppable: DroppableDimension): boolean => droppable !== source)
53
+ // Remove any options that are not enabled
54
+ .filter((droppable: DroppableDimension): boolean => droppable.isEnabled)
55
+ // Remove any droppables that do not have a visible subject
56
+ .filter((droppable: DroppableDimension): boolean =>
57
+ Boolean(droppable.subject.active),
58
+ )
59
+ // Remove any that are not visible in the window
60
+ .filter((droppable: DroppableDimension): boolean =>
61
+ isPartiallyVisibleThroughFrame(viewport.frame)(getKnownActive(droppable)),
62
+ )
63
+ .filter((droppable: DroppableDimension): boolean => {
64
+ const activeOfTarget: Rect = getKnownActive(droppable);
65
+
66
+ // is the target in front of the source on the cross axis?
67
+ if (isMovingForward) {
68
+ return active[axis.crossAxisEnd] < activeOfTarget[axis.crossAxisEnd];
69
+ }
70
+ // is the target behind the source on the cross axis?
71
+ return activeOfTarget[axis.crossAxisStart] < active[axis.crossAxisStart];
72
+ })
73
+ // Must have some overlap on the main axis
74
+ .filter((droppable: DroppableDimension): boolean => {
75
+ const activeOfTarget: Rect = getKnownActive(droppable);
76
+
77
+ const isBetweenDestinationClipped = isWithin(
78
+ activeOfTarget[axis.start],
79
+ activeOfTarget[axis.end],
80
+ );
81
+
82
+ return (
83
+ isBetweenSourceClipped(activeOfTarget[axis.start]) ||
84
+ isBetweenSourceClipped(activeOfTarget[axis.end]) ||
85
+ isBetweenDestinationClipped(active[axis.start]) ||
86
+ isBetweenDestinationClipped(active[axis.end])
87
+ );
88
+ })
89
+ // Sort on the cross axis
90
+ .sort((a: DroppableDimension, b: DroppableDimension) => {
91
+ const first: number = getKnownActive(a)[axis.crossAxisStart];
92
+ const second: number = getKnownActive(b)[axis.crossAxisStart];
93
+
94
+ if (isMovingForward) {
95
+ return first - second;
96
+ }
97
+ return second - first;
98
+ })
99
+ // Find the droppables that have the same cross axis value as the first item
100
+ .filter(
101
+ (
102
+ droppable: DroppableDimension,
103
+ index: number,
104
+ array: DroppableDimension[],
105
+ ): boolean =>
106
+ getKnownActive(droppable)[axis.crossAxisStart] ===
107
+ getKnownActive(array[0])[axis.crossAxisStart],
108
+ );
109
+
110
+ // no possible candidates
111
+ if (!candidates.length) {
112
+ return null;
113
+ }
114
+
115
+ // only one result - all done!
116
+ if (candidates.length === 1) {
117
+ return candidates[0];
118
+ }
119
+
120
+ // At this point we have a number of candidates that
121
+ // all have the same axis.crossAxisStart value.
122
+
123
+ // Check to see if the center position is within the size of a Droppable on the main axis
124
+ const contains: DroppableDimension[] = candidates.filter(
125
+ (droppable: DroppableDimension) => {
126
+ const isWithinDroppable = isWithin(
127
+ getKnownActive(droppable)[axis.start],
128
+ getKnownActive(droppable)[axis.end],
129
+ );
130
+ return isWithinDroppable(pageBorderBoxCenter[axis.line]);
131
+ },
132
+ );
133
+
134
+ if (contains.length === 1) {
135
+ return contains[0];
136
+ }
137
+
138
+ // The center point of the draggable falls on the boundary between two droppables
139
+ if (contains.length > 1) {
140
+ // sort on the main axis and choose the first
141
+ return contains.sort(
142
+ (a: DroppableDimension, b: DroppableDimension): number =>
143
+ getKnownActive(a)[axis.start] - getKnownActive(b)[axis.start],
144
+ )[0];
145
+ }
146
+
147
+ // The center is not contained within any droppable
148
+ // 1. Find the candidate that has the closest corner
149
+ // 2. If there is a tie - choose the one that is first on the main axis
150
+ return candidates.sort(
151
+ (a: DroppableDimension, b: DroppableDimension): number => {
152
+ const first = closest(pageBorderBoxCenter, getCorners(getKnownActive(a)));
153
+ const second = closest(
154
+ pageBorderBoxCenter,
155
+ getCorners(getKnownActive(b)),
156
+ );
157
+
158
+ // if the distances are not equal - choose the shortest
159
+ if (first !== second) {
160
+ return first - second;
161
+ }
162
+
163
+ // They both have the same distance -
164
+ // choose the one that is first on the main axis
165
+ return getKnownActive(a)[axis.start] - getKnownActive(b)[axis.start];
166
+ },
167
+ )[0];
168
+ };
@@ -0,0 +1,79 @@
1
+ // @flow
2
+ import { type Position } from 'css-box-model';
3
+ import type {
4
+ Viewport,
5
+ DraggableDimension,
6
+ DroppableDimension,
7
+ LiftEffect,
8
+ } from '../../../types';
9
+ import { distance } from '../../position';
10
+ import { isTotallyVisible } from '../../visibility/is-visible';
11
+ import withDroppableDisplacement from '../../with-scroll-change/with-droppable-displacement';
12
+ import {
13
+ getCurrentPageBorderBox,
14
+ getCurrentPageBorderBoxCenter,
15
+ } from './without-starting-displacement';
16
+
17
+ type Args = {|
18
+ pageBorderBoxCenter: Position,
19
+ viewport: Viewport,
20
+ // the droppable that is being moved to
21
+ destination: DroppableDimension,
22
+ // the droppables inside the destination
23
+ insideDestination: DraggableDimension[],
24
+ afterCritical: LiftEffect,
25
+ |};
26
+
27
+ export default ({
28
+ pageBorderBoxCenter,
29
+ viewport,
30
+ destination,
31
+ insideDestination,
32
+ afterCritical,
33
+ }: Args): ?DraggableDimension => {
34
+ const sorted: DraggableDimension[] = insideDestination
35
+ .filter((draggable: DraggableDimension): boolean =>
36
+ // Allowing movement to draggables that are not visible in the viewport
37
+ // but must be visible in the droppable
38
+ // We can improve this, but this limitation is easier for now
39
+ isTotallyVisible({
40
+ target: getCurrentPageBorderBox(draggable, afterCritical),
41
+ destination,
42
+ viewport: viewport.frame,
43
+ withDroppableDisplacement: true,
44
+ }),
45
+ )
46
+ .sort((a: DraggableDimension, b: DraggableDimension): number => {
47
+ // Need to consider the change in scroll in the destination
48
+ const distanceToA = distance(
49
+ pageBorderBoxCenter,
50
+ withDroppableDisplacement(
51
+ destination,
52
+ getCurrentPageBorderBoxCenter(a, afterCritical),
53
+ ),
54
+ );
55
+ const distanceToB = distance(
56
+ pageBorderBoxCenter,
57
+ withDroppableDisplacement(
58
+ destination,
59
+ getCurrentPageBorderBoxCenter(b, afterCritical),
60
+ ),
61
+ );
62
+
63
+ // if a is closer - return a
64
+ if (distanceToA < distanceToB) {
65
+ return -1;
66
+ }
67
+
68
+ // if b is closer - return b
69
+ if (distanceToB < distanceToA) {
70
+ return 1;
71
+ }
72
+
73
+ // if the distance to a and b are the same:
74
+ // return the one with the lower index (it will be higher on the main axis)
75
+ return a.descriptor.index - b.descriptor.index;
76
+ });
77
+
78
+ return sorted[0] || null;
79
+ };
@@ -0,0 +1,109 @@
1
+ // @flow
2
+ import { type Position } from 'css-box-model';
3
+ import type { PublicResult } from '../move-in-direction-types';
4
+ import type {
5
+ DroppableDimension,
6
+ DraggableDimension,
7
+ DraggableDimensionMap,
8
+ DroppableDimensionMap,
9
+ DragImpact,
10
+ Viewport,
11
+ LiftEffect,
12
+ } from '../../../types';
13
+ import getBestCrossAxisDroppable from './get-best-cross-axis-droppable';
14
+ import getClosestDraggable from './get-closest-draggable';
15
+ // import moveToNewDroppable from './move-to-new-droppable';
16
+ import getDraggablesInsideDroppable from '../../get-draggables-inside-droppable';
17
+ import getClientFromPageBorderBoxCenter from '../../get-center-from-impact/get-client-border-box-center/get-client-from-page-border-box-center';
18
+ import getPageBorderBoxCenter from '../../get-center-from-impact/get-page-border-box-center';
19
+ import moveToNewDroppable from './move-to-new-droppable';
20
+
21
+ type Args = {|
22
+ isMovingForward: boolean,
23
+ // the current page center of the dragging item
24
+ previousPageBorderBoxCenter: Position,
25
+ // the dragging item
26
+ draggable: DraggableDimension,
27
+ // the droppable the dragging item is in
28
+ isOver: DroppableDimension,
29
+ // all the dimensions in the system
30
+ draggables: DraggableDimensionMap,
31
+ droppables: DroppableDimensionMap,
32
+ // the current viewport
33
+ viewport: Viewport,
34
+ afterCritical: LiftEffect,
35
+ |};
36
+
37
+ export default ({
38
+ isMovingForward,
39
+ previousPageBorderBoxCenter,
40
+ draggable,
41
+ isOver,
42
+ draggables,
43
+ droppables,
44
+ viewport,
45
+ afterCritical,
46
+ }: Args): ?PublicResult => {
47
+ // not considering the container scroll changes as container scrolling cancels a keyboard drag
48
+
49
+ const destination: ?DroppableDimension = getBestCrossAxisDroppable({
50
+ isMovingForward,
51
+ pageBorderBoxCenter: previousPageBorderBoxCenter,
52
+ source: isOver,
53
+ droppables,
54
+ viewport,
55
+ });
56
+
57
+ // nothing available to move to
58
+ if (!destination) {
59
+ return null;
60
+ }
61
+
62
+ const insideDestination: DraggableDimension[] = getDraggablesInsideDroppable(
63
+ destination.descriptor.id,
64
+ draggables,
65
+ );
66
+
67
+ const moveRelativeTo: ?DraggableDimension = getClosestDraggable({
68
+ pageBorderBoxCenter: previousPageBorderBoxCenter,
69
+ viewport,
70
+ destination,
71
+ insideDestination,
72
+ afterCritical,
73
+ });
74
+
75
+ const impact: ?DragImpact = moveToNewDroppable({
76
+ previousPageBorderBoxCenter,
77
+ destination,
78
+ draggable,
79
+ draggables,
80
+ moveRelativeTo,
81
+ insideDestination,
82
+ viewport,
83
+ afterCritical,
84
+ });
85
+
86
+ if (!impact) {
87
+ return null;
88
+ }
89
+
90
+ const pageBorderBoxCenter: Position = getPageBorderBoxCenter({
91
+ impact,
92
+ draggable,
93
+ droppable: destination,
94
+ draggables,
95
+ afterCritical,
96
+ });
97
+
98
+ const clientSelection: Position = getClientFromPageBorderBoxCenter({
99
+ pageBorderBoxCenter,
100
+ draggable,
101
+ viewport,
102
+ });
103
+
104
+ return {
105
+ clientSelection,
106
+ impact,
107
+ scrollJumpRequest: null,
108
+ };
109
+ };
@@ -0,0 +1,121 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import type {
4
+ DragImpact,
5
+ DraggableDimension,
6
+ DraggableDimensionMap,
7
+ DroppableDimension,
8
+ Viewport,
9
+ DisplacedBy,
10
+ LiftEffect,
11
+ } from '../../../types';
12
+ import getDisplacedBy from '../../get-displaced-by';
13
+ import { emptyGroups, noDisplacedBy } from '../../no-impact';
14
+ import getPageBorderBoxCenter from '../../get-center-from-impact/get-page-border-box-center';
15
+ import isTotallyVisibleInNewLocation from '../move-to-next-place/is-totally-visible-in-new-location';
16
+ import { addPlaceholder } from '../../droppable/with-placeholder';
17
+ import isHomeOf from '../../droppable/is-home-of';
18
+ import calculateReorderImpact from '../../calculate-drag-impact/calculate-reorder-impact';
19
+
20
+ type Args = {|
21
+ previousPageBorderBoxCenter: Position,
22
+ moveRelativeTo: ?DraggableDimension,
23
+ insideDestination: DraggableDimension[],
24
+ draggable: DraggableDimension,
25
+ draggables: DraggableDimensionMap,
26
+ destination: DroppableDimension,
27
+ viewport: Viewport,
28
+ afterCritical: LiftEffect,
29
+ |};
30
+
31
+ export default ({
32
+ previousPageBorderBoxCenter,
33
+ moveRelativeTo,
34
+ insideDestination,
35
+ draggable,
36
+ draggables,
37
+ destination,
38
+ viewport,
39
+ afterCritical,
40
+ }: Args): ?DragImpact => {
41
+ if (!moveRelativeTo) {
42
+ // Draggables available, but none are candidates for movement
43
+ if (insideDestination.length) {
44
+ return null;
45
+ }
46
+
47
+ // Try move to top of empty list if it is visible
48
+ const proposed: DragImpact = {
49
+ displaced: emptyGroups,
50
+ displacedBy: noDisplacedBy,
51
+ at: {
52
+ type: 'REORDER',
53
+ destination: {
54
+ droppableId: destination.descriptor.id,
55
+ index: 0,
56
+ },
57
+ },
58
+ };
59
+ const proposedPageBorderBoxCenter: Position = getPageBorderBoxCenter({
60
+ impact: proposed,
61
+ draggable,
62
+ droppable: destination,
63
+ draggables,
64
+ afterCritical,
65
+ });
66
+
67
+ // need to add room for a placeholder in a foreign list
68
+ const withPlaceholder: DroppableDimension = isHomeOf(draggable, destination)
69
+ ? destination
70
+ : addPlaceholder(destination, draggable, draggables);
71
+
72
+ const isVisibleInNewLocation: boolean = isTotallyVisibleInNewLocation({
73
+ draggable,
74
+ destination: withPlaceholder,
75
+ newPageBorderBoxCenter: proposedPageBorderBoxCenter,
76
+ viewport: viewport.frame,
77
+ // already taken into account by getPageBorderBoxCenter
78
+ withDroppableDisplacement: false,
79
+ onlyOnMainAxis: true,
80
+ });
81
+
82
+ return isVisibleInNewLocation ? proposed : null;
83
+ }
84
+
85
+ const isGoingBeforeTarget: boolean = Boolean(
86
+ // Using <= as we optimise slightly for moving before items in a new list
87
+ // This is nicer in lists with fixed height items
88
+ previousPageBorderBoxCenter[destination.axis.line] <=
89
+ moveRelativeTo.page.borderBox.center[destination.axis.line],
90
+ );
91
+
92
+ const proposedIndex: number = (() => {
93
+ const relativeTo: number = moveRelativeTo.descriptor.index;
94
+
95
+ if (moveRelativeTo.descriptor.id === draggable.descriptor.id) {
96
+ return relativeTo;
97
+ }
98
+
99
+ if (isGoingBeforeTarget) {
100
+ return relativeTo;
101
+ }
102
+
103
+ return relativeTo + 1;
104
+ })();
105
+
106
+ const displacedBy: DisplacedBy = getDisplacedBy(
107
+ destination.axis,
108
+ draggable.displaceBy,
109
+ );
110
+
111
+ return calculateReorderImpact({
112
+ draggable,
113
+ insideDestination,
114
+ destination,
115
+ viewport,
116
+ displacedBy,
117
+ // last groups won't be relevant
118
+ last: emptyGroups,
119
+ index: proposedIndex,
120
+ });
121
+ };
@@ -0,0 +1,31 @@
1
+ // @flow
2
+ import type { Position, Rect, Spacing } from 'css-box-model';
3
+ import type { DraggableDimension, LiftEffect } from '../../../types';
4
+ import { negate, subtract } from '../../position';
5
+ import { offsetByPosition } from '../../spacing';
6
+ import didStartAfterCritical from '../../did-start-after-critical';
7
+
8
+ export const getCurrentPageBorderBoxCenter = (
9
+ draggable: DraggableDimension,
10
+ afterCritical: LiftEffect,
11
+ ): Position => {
12
+ // If an item started displaced it is now resting
13
+ // in a non-displaced location
14
+ const original: Position = draggable.page.borderBox.center;
15
+ return didStartAfterCritical(draggable.descriptor.id, afterCritical)
16
+ ? subtract(original, afterCritical.displacedBy.point)
17
+ : original;
18
+ };
19
+
20
+ export const getCurrentPageBorderBox = (
21
+ draggable: DraggableDimension,
22
+ afterCritical: LiftEffect,
23
+ ): Spacing => {
24
+ // If an item started displaced it is now resting
25
+ // in a non-displaced location
26
+ const original: Rect = draggable.page.borderBox;
27
+
28
+ return didStartAfterCritical(draggable.descriptor.id, afterCritical)
29
+ ? offsetByPosition(original, negate(afterCritical.displacedBy.point))
30
+ : original;
31
+ };
@@ -0,0 +1,9 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+ import type { DragImpact } from '../../types';
4
+
5
+ export type PublicResult = {|
6
+ clientSelection: Position,
7
+ impact: DragImpact,
8
+ scrollJumpRequest: ?Position,
9
+ |};