@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,139 @@
1
+ // @flow
2
+ import {
3
+ getBox,
4
+ withScroll,
5
+ createBox,
6
+ expand,
7
+ type BoxModel,
8
+ type Position,
9
+ type Spacing,
10
+ } from 'css-box-model';
11
+ import getDroppableDimension, {
12
+ type Closest,
13
+ } from '../../state/droppable/get-droppable';
14
+ import type { Env } from './get-env';
15
+ import type {
16
+ DroppableDimension,
17
+ DroppableDescriptor,
18
+ Direction,
19
+ ScrollSize,
20
+ } from '../../types';
21
+ import getScroll from './get-scroll';
22
+
23
+ const getClient = (
24
+ targetRef: HTMLElement,
25
+ closestScrollable: ?Element,
26
+ ): BoxModel => {
27
+ const base: BoxModel = getBox(targetRef);
28
+
29
+ // Droppable has no scroll parent
30
+ if (!closestScrollable) {
31
+ return base;
32
+ }
33
+
34
+ // Droppable is not the same as the closest scrollable
35
+ if (targetRef !== closestScrollable) {
36
+ return base;
37
+ }
38
+
39
+ // Droppable is scrollable
40
+
41
+ // Element.getBoundingClient() returns a clipped padding box:
42
+ // When not scrollable: the full size of the element
43
+ // When scrollable: the visible size of the element
44
+ // (which is not the full width of its scrollable content)
45
+ // So we recalculate the borderBox of a scrollable droppable to give
46
+ // it its full dimensions. This will be cut to the correct size by the frame
47
+
48
+ // Creating the paddingBox based on scrollWidth / scrollTop
49
+ // scrollWidth / scrollHeight are based on the paddingBox of an element
50
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight
51
+ const top: number = base.paddingBox.top - closestScrollable.scrollTop;
52
+ const left: number = base.paddingBox.left - closestScrollable.scrollLeft;
53
+ const bottom: number = top + closestScrollable.scrollHeight;
54
+ const right: number = left + closestScrollable.scrollWidth;
55
+
56
+ // unclipped padding box
57
+ const paddingBox: Spacing = {
58
+ top,
59
+ right,
60
+ bottom,
61
+ left,
62
+ };
63
+
64
+ // Creating the borderBox by adding the borders to the paddingBox
65
+ const borderBox: Spacing = expand(paddingBox, base.border);
66
+
67
+ // We are not accounting for scrollbars
68
+ // Adjusting for scrollbars is hard because:
69
+ // - they are different between browsers
70
+ // - scrollbars can be activated and removed during a drag
71
+ // We instead account for this slightly in our auto scroller
72
+
73
+ const client: BoxModel = createBox({
74
+ borderBox,
75
+ margin: base.margin,
76
+ border: base.border,
77
+ padding: base.padding,
78
+ });
79
+ return client;
80
+ };
81
+
82
+ type Args = {|
83
+ ref: HTMLElement,
84
+ descriptor: DroppableDescriptor,
85
+ env: Env,
86
+ windowScroll: Position,
87
+ direction: Direction,
88
+ isDropDisabled: boolean,
89
+ isCombineEnabled: boolean,
90
+ shouldClipSubject: boolean,
91
+ |};
92
+
93
+ export default ({
94
+ ref,
95
+ descriptor,
96
+ env,
97
+ windowScroll,
98
+ direction,
99
+ isDropDisabled,
100
+ isCombineEnabled,
101
+ shouldClipSubject,
102
+ }: Args): DroppableDimension => {
103
+ const closestScrollable: ?Element = env.closestScrollable;
104
+ const client: BoxModel = getClient(ref, closestScrollable);
105
+ const page: BoxModel = withScroll(client, windowScroll);
106
+
107
+ const closest: ?Closest = (() => {
108
+ if (!closestScrollable) {
109
+ return null;
110
+ }
111
+
112
+ const frameClient: BoxModel = getBox(closestScrollable);
113
+ const scrollSize: ScrollSize = {
114
+ scrollHeight: closestScrollable.scrollHeight,
115
+ scrollWidth: closestScrollable.scrollWidth,
116
+ };
117
+
118
+ return {
119
+ client: frameClient,
120
+ page: withScroll(frameClient, windowScroll),
121
+ scroll: getScroll(closestScrollable),
122
+ scrollSize,
123
+ shouldClipSubject,
124
+ };
125
+ })();
126
+
127
+ const dimension: DroppableDimension = getDroppableDimension({
128
+ descriptor,
129
+ isEnabled: !isDropDisabled,
130
+ isCombineEnabled,
131
+ isFixedOnPage: env.isFixedOnPage,
132
+ direction,
133
+ client,
134
+ page,
135
+ closest,
136
+ });
137
+
138
+ return dimension;
139
+ };
@@ -0,0 +1,31 @@
1
+ // @flow
2
+ import getClosestScrollable from './get-closest-scrollable';
3
+
4
+ export type Env = {|
5
+ closestScrollable: ?Element,
6
+ isFixedOnPage: boolean,
7
+ |};
8
+
9
+ // TODO: do this check at the same time as the closest scrollable
10
+ // in order to avoid double calling getComputedStyle
11
+ // Do this when we move to multiple scroll containers
12
+ const getIsFixed = (el: ?Element): boolean => {
13
+ if (!el) {
14
+ return false;
15
+ }
16
+ const style: CSSStyleDeclaration = window.getComputedStyle(el);
17
+ if (style.position === 'fixed') {
18
+ return true;
19
+ }
20
+ return getIsFixed(el.parentElement);
21
+ };
22
+
23
+ export default (start: Element): Env => {
24
+ const closestScrollable: ?Element = getClosestScrollable(start);
25
+ const isFixedOnPage: boolean = getIsFixed(start);
26
+
27
+ return {
28
+ closestScrollable,
29
+ isFixedOnPage,
30
+ };
31
+ };
@@ -0,0 +1,12 @@
1
+ // @flow
2
+ import type { ScrollOptions } from '../../types';
3
+
4
+ const immediate = {
5
+ passive: false,
6
+ };
7
+ const delayed = {
8
+ passive: true,
9
+ };
10
+
11
+ export default (options: ScrollOptions) =>
12
+ options.shouldPublishImmediately ? immediate : delayed;
@@ -0,0 +1,7 @@
1
+ // @flow
2
+ import type { Position } from 'css-box-model';
3
+
4
+ export default (el: Element): Position => ({
5
+ x: el.scrollLeft,
6
+ y: el.scrollTop,
7
+ });
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './use-droppable-publisher';
@@ -0,0 +1,21 @@
1
+ // @flow
2
+
3
+ const isElementFixed = (el: Element): boolean =>
4
+ window.getComputedStyle(el).position === 'fixed';
5
+
6
+ const find = (el: ?Element): boolean => {
7
+ // cannot do anything else!
8
+ if (el == null) {
9
+ return false;
10
+ }
11
+
12
+ // keep looking
13
+ if (!isElementFixed(el)) {
14
+ return find(el.parentElement);
15
+ }
16
+
17
+ // success!
18
+ return true;
19
+ };
20
+
21
+ export default find;
@@ -0,0 +1,283 @@
1
+ // @flow
2
+ import { useRef } from 'react';
3
+ import { type Position } from 'css-box-model';
4
+ import rafSchedule from 'raf-schd';
5
+ import { useMemo, useCallback } from 'use-memo-one';
6
+ import memoizeOne from 'memoize-one';
7
+ import { invariant } from '../../invariant';
8
+ import checkForNestedScrollContainers from './check-for-nested-scroll-container';
9
+ import * as dataAttr from '../data-attributes';
10
+ import { origin } from '../../state/position';
11
+ import getScroll from './get-scroll';
12
+ import type {
13
+ DroppableEntry,
14
+ DroppableCallbacks,
15
+ } from '../../state/registry/registry-types';
16
+ import getEnv, { type Env } from './get-env';
17
+ import type {
18
+ Id,
19
+ DroppableId,
20
+ TypeId,
21
+ DroppableDimension,
22
+ DroppableDescriptor,
23
+ Direction,
24
+ ScrollOptions,
25
+ DroppableMode,
26
+ } from '../../types';
27
+ import getDimension from './get-dimension';
28
+ import AppContext, { type AppContextValue } from '../context/app-context';
29
+ import { warning } from '../../dev-warning';
30
+ import getListenerOptions from './get-listener-options';
31
+ import useRequiredContext from '../use-required-context';
32
+ import usePreviousRef from '../use-previous-ref';
33
+ import useLayoutEffect from '../use-isomorphic-layout-effect';
34
+ import useUniqueId from '../use-unique-id';
35
+
36
+ type Props = {|
37
+ droppableId: DroppableId,
38
+ type: TypeId,
39
+ mode: DroppableMode,
40
+ direction: Direction,
41
+ isDropDisabled: boolean,
42
+ isCombineEnabled: boolean,
43
+ ignoreContainerClipping: boolean,
44
+ getDroppableRef: () => ?HTMLElement,
45
+ |};
46
+
47
+ type WhileDragging = {|
48
+ ref: HTMLElement,
49
+ descriptor: DroppableDescriptor,
50
+ env: Env,
51
+ scrollOptions: ScrollOptions,
52
+ |};
53
+
54
+ const getClosestScrollableFromDrag = (dragging: ?WhileDragging): ?Element =>
55
+ (dragging && dragging.env.closestScrollable) || null;
56
+
57
+ export default function useDroppablePublisher(args: Props) {
58
+ const whileDraggingRef = useRef<?WhileDragging>(null);
59
+ const appContext: AppContextValue = useRequiredContext(AppContext);
60
+ const uniqueId: Id = useUniqueId('droppable');
61
+ const { registry, marshal } = appContext;
62
+ const previousRef = usePreviousRef(args);
63
+
64
+ const descriptor = useMemo<DroppableDescriptor>(
65
+ () => ({
66
+ id: args.droppableId,
67
+ type: args.type,
68
+ mode: args.mode,
69
+ }),
70
+ [args.droppableId, args.mode, args.type],
71
+ );
72
+ const publishedDescriptorRef = useRef<DroppableDescriptor>(descriptor);
73
+
74
+ const memoizedUpdateScroll = useMemo(
75
+ () =>
76
+ memoizeOne((x: number, y: number) => {
77
+ invariant(
78
+ whileDraggingRef.current,
79
+ 'Can only update scroll when dragging',
80
+ );
81
+ const scroll: Position = { x, y };
82
+ marshal.updateDroppableScroll(descriptor.id, scroll);
83
+ }),
84
+ [descriptor.id, marshal],
85
+ );
86
+
87
+ const getClosestScroll = useCallback((): Position => {
88
+ const dragging: ?WhileDragging = whileDraggingRef.current;
89
+ if (!dragging || !dragging.env.closestScrollable) {
90
+ return origin;
91
+ }
92
+
93
+ return getScroll(dragging.env.closestScrollable);
94
+ }, []);
95
+
96
+ const updateScroll = useCallback(() => {
97
+ // reading scroll value when called so value will be the latest
98
+ const scroll: Position = getClosestScroll();
99
+ memoizedUpdateScroll(scroll.x, scroll.y);
100
+ }, [getClosestScroll, memoizedUpdateScroll]);
101
+
102
+ const scheduleScrollUpdate = useMemo(() => rafSchedule(updateScroll), [
103
+ updateScroll,
104
+ ]);
105
+
106
+ const onClosestScroll = useCallback(() => {
107
+ const dragging: ?WhileDragging = whileDraggingRef.current;
108
+ const closest: ?Element = getClosestScrollableFromDrag(dragging);
109
+
110
+ invariant(
111
+ dragging && closest,
112
+ 'Could not find scroll options while scrolling',
113
+ );
114
+ const options: ScrollOptions = dragging.scrollOptions;
115
+ if (options.shouldPublishImmediately) {
116
+ updateScroll();
117
+ return;
118
+ }
119
+ scheduleScrollUpdate();
120
+ }, [scheduleScrollUpdate, updateScroll]);
121
+
122
+ const getDimensionAndWatchScroll = useCallback(
123
+ (windowScroll: Position, options: ScrollOptions) => {
124
+ invariant(
125
+ !whileDraggingRef.current,
126
+ 'Cannot collect a droppable while a drag is occurring',
127
+ );
128
+ const previous: Props = previousRef.current;
129
+ const ref: ?HTMLElement = previous.getDroppableRef();
130
+ invariant(ref, 'Cannot collect without a droppable ref');
131
+ const env: Env = getEnv(ref);
132
+
133
+ const dragging: WhileDragging = {
134
+ ref,
135
+ descriptor,
136
+ env,
137
+ scrollOptions: options,
138
+ };
139
+ // side effect
140
+ whileDraggingRef.current = dragging;
141
+
142
+ const dimension: DroppableDimension = getDimension({
143
+ ref,
144
+ descriptor,
145
+ env,
146
+ windowScroll,
147
+ direction: previous.direction,
148
+ isDropDisabled: previous.isDropDisabled,
149
+ isCombineEnabled: previous.isCombineEnabled,
150
+ shouldClipSubject: !previous.ignoreContainerClipping,
151
+ });
152
+
153
+ const scrollable: ?Element = env.closestScrollable;
154
+
155
+ if (scrollable) {
156
+ scrollable.setAttribute(
157
+ dataAttr.scrollContainer.contextId,
158
+ appContext.contextId,
159
+ );
160
+
161
+ // bind scroll listener
162
+ scrollable.addEventListener(
163
+ 'scroll',
164
+ onClosestScroll,
165
+ getListenerOptions(dragging.scrollOptions),
166
+ );
167
+ // print a debug warning if using an unsupported nested scroll container setup
168
+ if (process.env.NODE_ENV !== 'production') {
169
+ checkForNestedScrollContainers(scrollable);
170
+ }
171
+ }
172
+
173
+ return dimension;
174
+ },
175
+ [appContext.contextId, descriptor, onClosestScroll, previousRef],
176
+ );
177
+
178
+ const getScrollWhileDragging = useCallback((): Position => {
179
+ const dragging: ?WhileDragging = whileDraggingRef.current;
180
+ const closest: ?Element = getClosestScrollableFromDrag(dragging);
181
+ invariant(
182
+ dragging && closest,
183
+ 'Can only recollect Droppable client for Droppables that have a scroll container',
184
+ );
185
+
186
+ return getScroll(closest);
187
+ }, []);
188
+
189
+ const dragStopped = useCallback(() => {
190
+ const dragging: ?WhileDragging = whileDraggingRef.current;
191
+ invariant(dragging, 'Cannot stop drag when no active drag');
192
+ const closest: ?Element = getClosestScrollableFromDrag(dragging);
193
+
194
+ // goodbye old friend
195
+ whileDraggingRef.current = null;
196
+
197
+ if (!closest) {
198
+ return;
199
+ }
200
+
201
+ // unwatch scroll
202
+ scheduleScrollUpdate.cancel();
203
+ closest.removeAttribute(dataAttr.scrollContainer.contextId);
204
+ closest.removeEventListener(
205
+ 'scroll',
206
+ onClosestScroll,
207
+ getListenerOptions(dragging.scrollOptions),
208
+ );
209
+ }, [onClosestScroll, scheduleScrollUpdate]);
210
+
211
+ const scroll = useCallback((change: Position) => {
212
+ // arrange
213
+ const dragging: ?WhileDragging = whileDraggingRef.current;
214
+ invariant(dragging, 'Cannot scroll when there is no drag');
215
+ const closest: ?Element = getClosestScrollableFromDrag(dragging);
216
+ invariant(closest, 'Cannot scroll a droppable with no closest scrollable');
217
+
218
+ // act
219
+ closest.scrollTop += change.y;
220
+ closest.scrollLeft += change.x;
221
+ }, []);
222
+
223
+ const callbacks: DroppableCallbacks = useMemo(() => {
224
+ return {
225
+ getDimensionAndWatchScroll,
226
+ getScrollWhileDragging,
227
+ dragStopped,
228
+ scroll,
229
+ };
230
+ }, [dragStopped, getDimensionAndWatchScroll, getScrollWhileDragging, scroll]);
231
+
232
+ const entry: DroppableEntry = useMemo(
233
+ () => ({
234
+ uniqueId,
235
+ descriptor,
236
+ callbacks,
237
+ }),
238
+ [callbacks, descriptor, uniqueId],
239
+ );
240
+
241
+ // Register with the marshal and let it know of:
242
+ // - any descriptor changes
243
+ // - when it unmounts
244
+ useLayoutEffect(() => {
245
+ publishedDescriptorRef.current = entry.descriptor;
246
+ registry.droppable.register(entry);
247
+
248
+ return () => {
249
+ if (whileDraggingRef.current) {
250
+ warning(
251
+ 'Unsupported: changing the droppableId or type of a Droppable during a drag',
252
+ );
253
+ dragStopped();
254
+ }
255
+
256
+ registry.droppable.unregister(entry);
257
+ };
258
+ }, [callbacks, descriptor, dragStopped, entry, marshal, registry.droppable]);
259
+
260
+ // update is enabled with the marshal
261
+ // only need to update when there is a drag
262
+ useLayoutEffect(() => {
263
+ if (!whileDraggingRef.current) {
264
+ return;
265
+ }
266
+ marshal.updateDroppableIsEnabled(
267
+ publishedDescriptorRef.current.id,
268
+ !args.isDropDisabled,
269
+ );
270
+ }, [args.isDropDisabled, marshal]);
271
+
272
+ // update is combine enabled with the marshal
273
+ // only need to update when there is a drag
274
+ useLayoutEffect(() => {
275
+ if (!whileDraggingRef.current) {
276
+ return;
277
+ }
278
+ marshal.updateDroppableIsCombineEnabled(
279
+ publishedDescriptorRef.current.id,
280
+ args.isCombineEnabled,
281
+ );
282
+ }, [args.isCombineEnabled, marshal]);
283
+ }
@@ -0,0 +1,13 @@
1
+ // @flow
2
+ import type { DraggableId } from '../../types';
3
+
4
+ export type Unregister = () => void;
5
+
6
+ export type Register = (id: DraggableId, focus: () => void) => Unregister;
7
+
8
+ export type FocusMarshal = {|
9
+ register: Register,
10
+ tryRecordFocus: (tryRecordFor: DraggableId) => void,
11
+ tryRestoreFocusRecorded: () => void,
12
+ tryShiftRecord: (previous: DraggableId, redirectTo: DraggableId) => void,
13
+ |};
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './use-focus-marshal';
@@ -0,0 +1,129 @@
1
+ // @flow
2
+ import { useRef } from 'react';
3
+ import { useMemo, useCallback } from 'use-memo-one';
4
+ import type { DraggableId, ContextId } from '../../types';
5
+ import type { FocusMarshal, Unregister } from './focus-marshal-types';
6
+ import { dragHandle as dragHandleAttr } from '../data-attributes';
7
+ import useLayoutEffect from '../use-isomorphic-layout-effect';
8
+ import findDragHandle from '../get-elements/find-drag-handle';
9
+
10
+ type Entry = {|
11
+ id: DraggableId,
12
+ focus: () => void,
13
+ |};
14
+
15
+ type EntryMap = {
16
+ [id: DraggableId]: Entry,
17
+ };
18
+
19
+ export default function useFocusMarshal(contextId: ContextId): FocusMarshal {
20
+ const entriesRef = useRef<EntryMap>({});
21
+ const recordRef = useRef<?DraggableId>(null);
22
+ const restoreFocusFrameRef = useRef<?AnimationFrameID>(null);
23
+ const isMountedRef = useRef<boolean>(false);
24
+
25
+ const register = useCallback(function register(
26
+ id: DraggableId,
27
+ focus: () => void,
28
+ ): Unregister {
29
+ const entry: Entry = { id, focus };
30
+ entriesRef.current[id] = entry;
31
+
32
+ return function unregister() {
33
+ const entries: EntryMap = entriesRef.current;
34
+ const current: Entry = entries[id];
35
+ // entry might have been overrided by another registration
36
+ if (current !== entry) {
37
+ delete entries[id];
38
+ }
39
+ };
40
+ },
41
+ []);
42
+
43
+ const tryGiveFocus = useCallback(
44
+ function tryGiveFocus(tryGiveFocusTo: DraggableId) {
45
+ const handle: ?HTMLElement = findDragHandle(contextId, tryGiveFocusTo);
46
+
47
+ if (handle && handle !== document.activeElement) {
48
+ handle.focus();
49
+ }
50
+ },
51
+ [contextId],
52
+ );
53
+
54
+ const tryShiftRecord = useCallback(function tryShiftRecord(
55
+ previous: DraggableId,
56
+ redirectTo: DraggableId,
57
+ ) {
58
+ if (recordRef.current === previous) {
59
+ recordRef.current = redirectTo;
60
+ }
61
+ },
62
+ []);
63
+
64
+ const tryRestoreFocusRecorded = useCallback(
65
+ function tryRestoreFocusRecorded() {
66
+ // frame already queued
67
+ if (restoreFocusFrameRef.current) {
68
+ return;
69
+ }
70
+
71
+ // cannot give focus if unmounted
72
+ // this code path is generally not hit expect for some hot-reloading flows
73
+ if (!isMountedRef.current) {
74
+ return;
75
+ }
76
+
77
+ restoreFocusFrameRef.current = requestAnimationFrame(() => {
78
+ restoreFocusFrameRef.current = null;
79
+ const record: ?DraggableId = recordRef.current;
80
+ if (record) {
81
+ tryGiveFocus(record);
82
+ }
83
+ });
84
+ },
85
+ [tryGiveFocus],
86
+ );
87
+
88
+ const tryRecordFocus = useCallback(function tryRecordFocus(id: DraggableId) {
89
+ // clear any existing record
90
+ recordRef.current = null;
91
+
92
+ const focused: ?Element = document.activeElement;
93
+
94
+ // no item focused so it cannot be our item
95
+ if (!focused) {
96
+ return;
97
+ }
98
+
99
+ // focused element is not a drag handle or does not have the right id
100
+ if (focused.getAttribute(dragHandleAttr.draggableId) !== id) {
101
+ return;
102
+ }
103
+
104
+ recordRef.current = id;
105
+ }, []);
106
+
107
+ useLayoutEffect(() => {
108
+ isMountedRef.current = true;
109
+ return function clearFrameOnUnmount() {
110
+ isMountedRef.current = false;
111
+ const frameId: ?AnimationFrameID = restoreFocusFrameRef.current;
112
+ if (frameId) {
113
+ cancelAnimationFrame(frameId);
114
+ }
115
+ };
116
+ }, []);
117
+
118
+ const marshal: FocusMarshal = useMemo(
119
+ () => ({
120
+ register,
121
+ tryRecordFocus,
122
+ tryRestoreFocusRecorded,
123
+ tryShiftRecord,
124
+ }),
125
+ [register, tryRecordFocus, tryRestoreFocusRecorded, tryShiftRecord],
126
+ );
127
+
128
+ return marshal;
129
+ }
@@ -0,0 +1,2 @@
1
+ // @flow
2
+ export { default } from './use-hidden-text-element';