@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,60 @@
1
+ // @flow
2
+ import { useEffect } from 'react';
3
+ import { useMemo } from 'use-memo-one';
4
+ import type { ContextId, ElementId } from '../../types';
5
+ import getBodyElement from '../get-body-element';
6
+ import useUniqueId from '../use-unique-id';
7
+
8
+ type GetIdArgs = {|
9
+ contextId: ContextId,
10
+ uniqueId: string,
11
+ |};
12
+
13
+ export function getElementId({ contextId, uniqueId }: GetIdArgs): ElementId {
14
+ return `rbd-hidden-text-${contextId}-${uniqueId}`;
15
+ }
16
+
17
+ type Args = {|
18
+ contextId: ContextId,
19
+ text: string,
20
+ |};
21
+
22
+ export default function useHiddenTextElement({
23
+ contextId,
24
+ text,
25
+ }: Args): ElementId {
26
+ const uniqueId: string = useUniqueId('hidden-text', { separator: '-' });
27
+ const id: ElementId = useMemo(() => getElementId({ contextId, uniqueId }), [
28
+ uniqueId,
29
+ contextId,
30
+ ]);
31
+
32
+ useEffect(
33
+ function mount() {
34
+ const el: HTMLElement = document.createElement('div');
35
+
36
+ // identifier
37
+ el.id = id;
38
+
39
+ // add the description text
40
+ el.textContent = text;
41
+
42
+ // Using `display: none` prevent screen readers from reading this element in the document flow
43
+ el.style.display = 'none';
44
+
45
+ // Add to body
46
+ getBodyElement().appendChild(el);
47
+
48
+ return function unmount() {
49
+ // checking if element exists as the body might have been changed by things like 'turbolinks'
50
+ const body: HTMLBodyElement = getBodyElement();
51
+ if (body.contains(el)) {
52
+ body.removeChild(el);
53
+ }
54
+ };
55
+ },
56
+ [id, text],
57
+ );
58
+
59
+ return id;
60
+ }
@@ -0,0 +1,18 @@
1
+ // @flow
2
+ // eslint-disable-next-line no-restricted-imports
3
+ import { useLayoutEffect, useEffect } from 'react';
4
+
5
+ // https://github.com/reduxjs/react-redux/blob/v7-beta/src/components/connectAdvanced.js#L35
6
+ // React currently throws a warning when using useLayoutEffect on the server.
7
+ // To get around it, we can conditionally useEffect on the server (no-op) and
8
+ // useLayoutEffect in the browser. We need useLayoutEffect because we want
9
+ // `connect` to perform sync updates to a ref to save the latest props after
10
+ // a render is actually committed to the DOM.
11
+ const useIsomorphicLayoutEffect =
12
+ typeof window !== 'undefined' &&
13
+ typeof window.document !== 'undefined' &&
14
+ typeof window.document.createElement !== 'undefined'
15
+ ? useLayoutEffect
16
+ : useEffect;
17
+
18
+ export default useIsomorphicLayoutEffect;
@@ -0,0 +1,14 @@
1
+ // @flow
2
+ import { useRef, useEffect } from 'react';
3
+
4
+ export default function usePrevious<T>(current: T): {| current: T |} {
5
+ const ref = useRef<T>(current);
6
+
7
+ // will be updated on the next render
8
+ useEffect(() => {
9
+ ref.current = current;
10
+ });
11
+
12
+ // return the existing current (pre render)
13
+ return ref;
14
+ }
@@ -0,0 +1,9 @@
1
+ // @flow
2
+ import { useContext, type Context as ContextType } from 'react';
3
+ import { invariant } from '../invariant';
4
+
5
+ export default function useRequiredContext<T>(Context: ContextType<?T>): T {
6
+ const result: ?T = useContext(Context);
7
+ invariant(result, 'Could not find required context');
8
+ return result;
9
+ }
@@ -0,0 +1,50 @@
1
+ // @flow
2
+ import { find } from '../../native-with-fallback';
3
+
4
+ const supportedMatchesName: string = ((): string => {
5
+ const base: string = 'matches';
6
+
7
+ // Server side rendering
8
+ if (typeof document === 'undefined') {
9
+ return base;
10
+ }
11
+
12
+ // See https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
13
+ const candidates: string[] = [
14
+ base,
15
+ 'msMatchesSelector',
16
+ 'webkitMatchesSelector',
17
+ ];
18
+
19
+ const value: ?string = find(
20
+ candidates,
21
+ (name: string): boolean => name in Element.prototype,
22
+ );
23
+
24
+ return value || base;
25
+ })();
26
+
27
+ function closestPonyfill(el: ?Element, selector: string) {
28
+ if (el == null) {
29
+ return null;
30
+ }
31
+
32
+ // Element.prototype.matches is supported in ie11 with a different name
33
+ // https://caniuse.com/#feat=matchesselector
34
+ // $FlowFixMe - dynamic property
35
+ if (el[supportedMatchesName](selector)) {
36
+ return el;
37
+ }
38
+
39
+ // recursively look up the tree
40
+ return closestPonyfill(el.parentElement, selector);
41
+ }
42
+
43
+ export default function closest(el: Element, selector: string): ?Element {
44
+ // Using native closest for maximum speed where we can
45
+ if (el.closest) {
46
+ return el.closest(selector);
47
+ }
48
+ // ie11: damn you!
49
+ return closestPonyfill(el, selector);
50
+ }
@@ -0,0 +1,50 @@
1
+ // @flow
2
+ import type { ContextId, DraggableId } from '../../types';
3
+ import * as attributes from '../data-attributes';
4
+ import isElement from '../is-type-of-element/is-element';
5
+ import isHtmlElement from '../is-type-of-element/is-html-element';
6
+ import closest from './closest';
7
+ import { warning } from '../../dev-warning';
8
+
9
+ function getSelector(contextId: ContextId): string {
10
+ return `[${attributes.dragHandle.contextId}="${contextId}"]`;
11
+ }
12
+
13
+ function findClosestDragHandleFromEvent(
14
+ contextId: ContextId,
15
+ event: Event,
16
+ ): ?HTMLElement {
17
+ const target: ?EventTarget = event.target;
18
+
19
+ if (!isElement(target)) {
20
+ warning('event.target must be a Element');
21
+ return null;
22
+ }
23
+
24
+ const selector: string = getSelector(contextId);
25
+ const handle: ?Element = closest(target, selector);
26
+
27
+ if (!handle) {
28
+ return null;
29
+ }
30
+
31
+ if (!isHtmlElement(handle)) {
32
+ warning('drag handle must be a HTMLElement');
33
+ return null;
34
+ }
35
+
36
+ return handle;
37
+ }
38
+
39
+ export default function tryGetClosestDraggableIdFromEvent(
40
+ contextId: ContextId,
41
+ event: Event,
42
+ ): ?DraggableId {
43
+ const handle: ?HTMLElement = findClosestDragHandleFromEvent(contextId, event);
44
+
45
+ if (!handle) {
46
+ return null;
47
+ }
48
+
49
+ return handle.getAttribute(attributes.dragHandle.draggableId);
50
+ }
@@ -0,0 +1,5 @@
1
+ // @flow
2
+ export { default } from './use-sensor-marshal';
3
+ export { default as useMouseSensor } from './sensors/use-mouse-sensor';
4
+ export { default as useTouchSensor } from './sensors/use-touch-sensor';
5
+ export { default as useKeyboardSensor } from './sensors/use-keyboard-sensor';
@@ -0,0 +1,66 @@
1
+ // @flow
2
+ import isHtmlElement from '../is-type-of-element/is-html-element';
3
+
4
+ export type TagNameMap = {
5
+ [tagName: string]: true,
6
+ };
7
+
8
+ export const interactiveTagNames: TagNameMap = {
9
+ input: true,
10
+ button: true,
11
+ textarea: true,
12
+ select: true,
13
+ option: true,
14
+ optgroup: true,
15
+ video: true,
16
+ audio: true,
17
+ };
18
+
19
+ function isAnInteractiveElement(parent: Element, current: ?Element) {
20
+ if (current == null) {
21
+ return false;
22
+ }
23
+
24
+ // Most interactive elements cannot have children. However, some can such as 'button'.
25
+ // See 'Permitted content' on https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button
26
+ // Rather than having two different functions we can consolidate our checks into this single
27
+ // function to keep things simple.
28
+ // There is no harm checking if the parent has an interactive tag name even if it cannot have
29
+ // any children. We need to perform this loop anyway to check for the contenteditable attribute
30
+ const hasAnInteractiveTag: boolean = Boolean(
31
+ interactiveTagNames[current.tagName.toLowerCase()],
32
+ );
33
+
34
+ if (hasAnInteractiveTag) {
35
+ return true;
36
+ }
37
+
38
+ // contenteditable="true" or contenteditable="" are valid ways
39
+ // of creating a contenteditable container
40
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable
41
+ const attribute: ?string = current.getAttribute('contenteditable');
42
+ if (attribute === 'true' || attribute === '') {
43
+ return true;
44
+ }
45
+
46
+ // nothing more can be done and no results found
47
+ if (current === parent) {
48
+ return false;
49
+ }
50
+
51
+ // recursion to check parent
52
+ return isAnInteractiveElement(parent, current.parentElement);
53
+ }
54
+
55
+ export default function isEventInInteractiveElement(
56
+ draggable: Element,
57
+ event: Event,
58
+ ): boolean {
59
+ const target: EventTarget = event.target;
60
+
61
+ if (!isHtmlElement(target)) {
62
+ return false;
63
+ }
64
+
65
+ return isAnInteractiveElement(draggable, target);
66
+ }
@@ -0,0 +1,48 @@
1
+ // @flow
2
+ import { invariant } from '../../invariant';
3
+
4
+ export type Lock = {|
5
+ abandon: () => void,
6
+ |};
7
+
8
+ export type LockAPI = {|
9
+ isClaimed: () => boolean,
10
+ isActive: (lock: Lock) => boolean,
11
+ claim: (abandon: () => void) => Lock,
12
+ release: () => void,
13
+ tryAbandon: () => void,
14
+ |};
15
+
16
+ export default function create(): LockAPI {
17
+ let lock: ?Lock = null;
18
+
19
+ function isClaimed(): boolean {
20
+ return Boolean(lock);
21
+ }
22
+
23
+ function isActive(value: Lock): boolean {
24
+ return value === lock;
25
+ }
26
+
27
+ function claim(abandon: () => void): Lock {
28
+ invariant(!lock, 'Cannot claim lock as it is already claimed');
29
+ const newLock: Lock = { abandon };
30
+ // update singleton
31
+ lock = newLock;
32
+ // return lock
33
+ return newLock;
34
+ }
35
+ function release() {
36
+ invariant(lock, 'Cannot release lock when there is no lock');
37
+ lock = null;
38
+ }
39
+
40
+ function tryAbandon() {
41
+ if (lock) {
42
+ lock.abandon();
43
+ release();
44
+ }
45
+ }
46
+
47
+ return { isClaimed, isActive, claim, release, tryAbandon };
48
+ }
@@ -0,0 +1,243 @@
1
+ // @flow
2
+ import { useRef } from 'react';
3
+ import { useMemo, useCallback } from 'use-memo-one';
4
+ import { invariant } from '../../../invariant';
5
+ import type {
6
+ SensorAPI,
7
+ PreDragActions,
8
+ SnapDragActions,
9
+ DraggableId,
10
+ } from '../../../types';
11
+ import type {
12
+ EventBinding,
13
+ EventOptions,
14
+ } from '../../event-bindings/event-types';
15
+ import * as keyCodes from '../../key-codes';
16
+ import bindEvents from '../../event-bindings/bind-events';
17
+ import preventStandardKeyEvents from './util/prevent-standard-key-events';
18
+ import supportedPageVisibilityEventName from './util/supported-page-visibility-event-name';
19
+ import useLayoutEffect from '../../use-isomorphic-layout-effect';
20
+
21
+ function noop() {}
22
+
23
+ type KeyMap = {
24
+ [key: number]: true,
25
+ };
26
+
27
+ const scrollJumpKeys: KeyMap = {
28
+ [keyCodes.pageDown]: true,
29
+ [keyCodes.pageUp]: true,
30
+ [keyCodes.home]: true,
31
+ [keyCodes.end]: true,
32
+ };
33
+
34
+ function getDraggingBindings(
35
+ actions: SnapDragActions,
36
+ stop: () => void,
37
+ ): EventBinding[] {
38
+ function cancel() {
39
+ stop();
40
+ actions.cancel();
41
+ }
42
+
43
+ function drop() {
44
+ stop();
45
+ actions.drop();
46
+ }
47
+
48
+ return [
49
+ {
50
+ eventName: 'keydown',
51
+ fn: (event: KeyboardEvent) => {
52
+ if (event.keyCode === keyCodes.escape) {
53
+ event.preventDefault();
54
+ cancel();
55
+ return;
56
+ }
57
+
58
+ // Dropping
59
+ if (event.keyCode === keyCodes.space) {
60
+ // need to stop parent Draggable's thinking this is a lift
61
+ event.preventDefault();
62
+ drop();
63
+ return;
64
+ }
65
+
66
+ // Movement
67
+
68
+ if (event.keyCode === keyCodes.arrowDown) {
69
+ event.preventDefault();
70
+ actions.moveDown();
71
+ return;
72
+ }
73
+
74
+ if (event.keyCode === keyCodes.arrowUp) {
75
+ event.preventDefault();
76
+ actions.moveUp();
77
+ return;
78
+ }
79
+
80
+ if (event.keyCode === keyCodes.arrowRight) {
81
+ event.preventDefault();
82
+ actions.moveRight();
83
+ return;
84
+ }
85
+
86
+ if (event.keyCode === keyCodes.arrowLeft) {
87
+ event.preventDefault();
88
+ actions.moveLeft();
89
+ return;
90
+ }
91
+
92
+ // preventing scroll jumping at this time
93
+ if (scrollJumpKeys[event.keyCode]) {
94
+ event.preventDefault();
95
+ return;
96
+ }
97
+
98
+ preventStandardKeyEvents(event);
99
+ },
100
+ },
101
+ // any mouse actions kills a drag
102
+ {
103
+ eventName: 'mousedown',
104
+ fn: cancel,
105
+ },
106
+ {
107
+ eventName: 'mouseup',
108
+ fn: cancel,
109
+ },
110
+ {
111
+ eventName: 'click',
112
+ fn: cancel,
113
+ },
114
+ {
115
+ eventName: 'touchstart',
116
+ fn: cancel,
117
+ },
118
+ // resizing the browser kills a drag
119
+ {
120
+ eventName: 'resize',
121
+ fn: cancel,
122
+ },
123
+ // kill if the user is using the mouse wheel
124
+ // We are not supporting wheel / trackpad scrolling with keyboard dragging
125
+ {
126
+ eventName: 'wheel',
127
+ fn: cancel,
128
+ // chrome says it is a violation for this to not be passive
129
+ // it is fine for it to be passive as we just cancel as soon as we get
130
+ // any event
131
+ options: { passive: true },
132
+ },
133
+ // Cancel on page visibility change
134
+ {
135
+ eventName: supportedPageVisibilityEventName,
136
+ fn: cancel,
137
+ },
138
+ ];
139
+ }
140
+
141
+ export default function useKeyboardSensor(api: SensorAPI) {
142
+ const unbindEventsRef = useRef<() => void>(noop);
143
+
144
+ const startCaptureBinding: EventBinding = useMemo(
145
+ () => ({
146
+ eventName: 'keydown',
147
+ fn: function onKeyDown(event: KeyboardEvent) {
148
+ // Event already used
149
+ if (event.defaultPrevented) {
150
+ return;
151
+ }
152
+
153
+ // Need to start drag with a spacebar press
154
+ if (event.keyCode !== keyCodes.space) {
155
+ return;
156
+ }
157
+
158
+ const draggableId: ?DraggableId = api.findClosestDraggableId(event);
159
+
160
+ if (!draggableId) {
161
+ return;
162
+ }
163
+
164
+ const preDrag: ?PreDragActions = api.tryGetLock(
165
+ draggableId,
166
+ // abort function not defined yet
167
+ // eslint-disable-next-line no-use-before-define
168
+ stop,
169
+ { sourceEvent: event },
170
+ );
171
+
172
+ // Cannot start capturing at this time
173
+ if (!preDrag) {
174
+ return;
175
+ }
176
+
177
+ // we are consuming the event
178
+ event.preventDefault();
179
+ let isCapturing: boolean = true;
180
+
181
+ // There is no pending period for a keyboard drag
182
+ // We can lift immediately
183
+ const actions: SnapDragActions = preDrag.snapLift();
184
+
185
+ // unbind this listener
186
+ unbindEventsRef.current();
187
+
188
+ // setup our function to end everything
189
+ function stop() {
190
+ invariant(
191
+ isCapturing,
192
+ 'Cannot stop capturing a keyboard drag when not capturing',
193
+ );
194
+ isCapturing = false;
195
+
196
+ // unbind dragging bindings
197
+ unbindEventsRef.current();
198
+ // start listening for capture again
199
+ // eslint-disable-next-line no-use-before-define
200
+ listenForCapture();
201
+ }
202
+
203
+ // bind dragging listeners
204
+ unbindEventsRef.current = bindEvents(
205
+ window,
206
+ getDraggingBindings(actions, stop),
207
+ { capture: true, passive: false },
208
+ );
209
+ },
210
+ }),
211
+ // not including startPendingDrag as it is not defined initially
212
+ // eslint-disable-next-line react-hooks/exhaustive-deps
213
+ [api],
214
+ );
215
+
216
+ const listenForCapture = useCallback(
217
+ function tryStartCapture() {
218
+ const options: EventOptions = {
219
+ passive: false,
220
+ capture: true,
221
+ };
222
+
223
+ unbindEventsRef.current = bindEvents(
224
+ window,
225
+ [startCaptureBinding],
226
+ options,
227
+ );
228
+ },
229
+ [startCaptureBinding],
230
+ );
231
+
232
+ useLayoutEffect(
233
+ function mount() {
234
+ listenForCapture();
235
+
236
+ // kill any pending window events when unmounting
237
+ return function unmount() {
238
+ unbindEventsRef.current();
239
+ };
240
+ },
241
+ [listenForCapture],
242
+ );
243
+ }