@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.
- package/CHANGELOG.md +35 -0
- package/LICENSE +13 -0
- package/README.md +178 -0
- package/dist/react-beautiful-dnd.cjs.js +8728 -0
- package/dist/react-beautiful-dnd.cjs.js.flow +3 -0
- package/dist/react-beautiful-dnd.esm.js +8715 -0
- package/dist/react-beautiful-dnd.js +11726 -0
- package/dist/react-beautiful-dnd.min.js +1 -0
- package/package.json +155 -0
- package/src/animation.js +75 -0
- package/src/debug/middleware/action-timing-average.js +52 -0
- package/src/debug/middleware/action-timing.js +16 -0
- package/src/debug/middleware/log.js +26 -0
- package/src/debug/middleware/user-timing.js +16 -0
- package/src/debug/timings.js +86 -0
- package/src/dev-warning.js +50 -0
- package/src/empty.js +6 -0
- package/src/index.js +67 -0
- package/src/invariant.js +33 -0
- package/src/native-with-fallback.js +69 -0
- package/src/screen-reader-message-preset.js +134 -0
- package/src/state/action-creators.js +328 -0
- package/src/state/auto-scroller/auto-scroller-types.js +8 -0
- package/src/state/auto-scroller/can-scroll.js +160 -0
- package/src/state/auto-scroller/fluid-scroller/config.js +25 -0
- package/src/state/auto-scroller/fluid-scroller/did-start-in-scrollable-area.js +1 -0
- package/src/state/auto-scroller/fluid-scroller/get-best-scrollable-droppable.js +77 -0
- package/src/state/auto-scroller/fluid-scroller/get-droppable-scroll-change.js +50 -0
- package/src/state/auto-scroller/fluid-scroller/get-percentage.js +25 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/adjust-for-size-limits.js +30 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/calc-axis-scroll-conditions.js +68 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/get-scroll-conditions.js +56 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/buffer-thresholds/index.js +66 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/dampen-value-by-time.js +48 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-distance-thresholds.js +31 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-value-from-distance.js +67 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/get-value.js +51 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/index.js +50 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/get-scroll-on-axis/min-scroll.js +4 -0
- package/src/state/auto-scroller/fluid-scroller/get-scroll/index.js +139 -0
- package/src/state/auto-scroller/fluid-scroller/get-window-scroll-change.js +43 -0
- package/src/state/auto-scroller/fluid-scroller/index.js +99 -0
- package/src/state/auto-scroller/fluid-scroller/scroll.js +80 -0
- package/src/state/auto-scroller/index.js +59 -0
- package/src/state/auto-scroller/jump-scroller.js +139 -0
- package/src/state/axis.js +26 -0
- package/src/state/calculate-drag-impact/calculate-reorder-impact.js +136 -0
- package/src/state/can-start-drag.js +29 -0
- package/src/state/create-store.js +97 -0
- package/src/state/did-start-after-critical.js +9 -0
- package/src/state/dimension-marshal/dimension-marshal-types.js +46 -0
- package/src/state/dimension-marshal/dimension-marshal.js +218 -0
- package/src/state/dimension-marshal/get-initial-publish.js +66 -0
- package/src/state/dimension-marshal/while-dragging-publisher.js +146 -0
- package/src/state/dimension-structures.js +35 -0
- package/src/state/droppable/get-droppable.js +101 -0
- package/src/state/droppable/is-home-of.js +7 -0
- package/src/state/droppable/scroll-droppable.js +53 -0
- package/src/state/droppable/should-use-placeholder.js +7 -0
- package/src/state/droppable/util/clip.js +17 -0
- package/src/state/droppable/util/get-subject.js +63 -0
- package/src/state/droppable/what-is-dragged-over-from-result.js +16 -0
- package/src/state/droppable/what-is-dragged-over.js +16 -0
- package/src/state/droppable/with-placeholder.js +174 -0
- package/src/state/get-center-from-impact/get-client-border-box-center/get-client-from-page-border-box-center.js +29 -0
- package/src/state/get-center-from-impact/get-client-border-box-center/index.js +44 -0
- package/src/state/get-center-from-impact/get-page-border-box-center/index.js +70 -0
- package/src/state/get-center-from-impact/get-page-border-box-center/when-combining.js +39 -0
- package/src/state/get-center-from-impact/get-page-border-box-center/when-reordering.js +112 -0
- package/src/state/get-center-from-impact/move-relative-to.js +66 -0
- package/src/state/get-combined-item-displacement.js +34 -0
- package/src/state/get-displaced-by.js +17 -0
- package/src/state/get-displacement-groups.js +130 -0
- package/src/state/get-drag-impact/get-combine-impact.js +130 -0
- package/src/state/get-drag-impact/get-reorder-impact.js +143 -0
- package/src/state/get-drag-impact/index.js +93 -0
- package/src/state/get-draggables-inside-droppable.js +31 -0
- package/src/state/get-droppable-over.js +158 -0
- package/src/state/get-frame.js +10 -0
- package/src/state/get-home-location.js +7 -0
- package/src/state/get-impact-location.js +16 -0
- package/src/state/get-is-displaced.js +11 -0
- package/src/state/get-lift-effect.js +82 -0
- package/src/state/get-max-scroll.js +30 -0
- package/src/state/is-movement-allowed.js +6 -0
- package/src/state/is-within.js +9 -0
- package/src/state/middleware/auto-scroll.js +38 -0
- package/src/state/middleware/dimension-marshal-stopper.js +20 -0
- package/src/state/middleware/drop/drop-animation-finish-middleware.js +21 -0
- package/src/state/middleware/drop/drop-animation-flush-on-scroll-middleware.js +63 -0
- package/src/state/middleware/drop/drop-middleware.js +146 -0
- package/src/state/middleware/drop/get-drop-duration.js +47 -0
- package/src/state/middleware/drop/get-drop-impact.js +77 -0
- package/src/state/middleware/drop/get-new-home-client-offset.js +54 -0
- package/src/state/middleware/drop/index.js +2 -0
- package/src/state/middleware/focus.js +42 -0
- package/src/state/middleware/lift.js +66 -0
- package/src/state/middleware/pending-drop.js +37 -0
- package/src/state/middleware/responders/async-marshal.js +55 -0
- package/src/state/middleware/responders/expiring-announce.js +44 -0
- package/src/state/middleware/responders/index.js +2 -0
- package/src/state/middleware/responders/is-equal.js +57 -0
- package/src/state/middleware/responders/publisher.js +253 -0
- package/src/state/middleware/responders/responders-middleware.js +75 -0
- package/src/state/middleware/scroll-listener.js +31 -0
- package/src/state/middleware/style.js +22 -0
- package/src/state/middleware/util/validate-dimensions.js +71 -0
- package/src/state/move-in-direction/index.js +84 -0
- package/src/state/move-in-direction/move-cross-axis/get-best-cross-axis-droppable.js +168 -0
- package/src/state/move-in-direction/move-cross-axis/get-closest-draggable.js +79 -0
- package/src/state/move-in-direction/move-cross-axis/index.js +109 -0
- package/src/state/move-in-direction/move-cross-axis/move-to-new-droppable.js +121 -0
- package/src/state/move-in-direction/move-cross-axis/without-starting-displacement.js +31 -0
- package/src/state/move-in-direction/move-in-direction-types.js +9 -0
- package/src/state/move-in-direction/move-to-next-place/index.js +132 -0
- package/src/state/move-in-direction/move-to-next-place/is-totally-visible-in-new-location.js +52 -0
- package/src/state/move-in-direction/move-to-next-place/move-to-next-combine/index.js +97 -0
- package/src/state/move-in-direction/move-to-next-place/move-to-next-index/from-combine.js +52 -0
- package/src/state/move-in-direction/move-to-next-place/move-to-next-index/from-reorder.js +43 -0
- package/src/state/move-in-direction/move-to-next-place/move-to-next-index/index.js +86 -0
- package/src/state/no-impact.js +33 -0
- package/src/state/patch-dimension-map.js +11 -0
- package/src/state/patch-droppable-map.js +10 -0
- package/src/state/position.js +58 -0
- package/src/state/post-reducer/when-moving/refresh-snap.js +67 -0
- package/src/state/post-reducer/when-moving/update.js +120 -0
- package/src/state/publish-while-dragging-in-virtual/adjust-additions-for-scroll-changes.js +58 -0
- package/src/state/publish-while-dragging-in-virtual/index.js +158 -0
- package/src/state/publish-while-dragging-in-virtual/offset-draggable.js +35 -0
- package/src/state/recompute-placeholders.js +99 -0
- package/src/state/rect.js +7 -0
- package/src/state/reducer.js +457 -0
- package/src/state/registry/create-registry.js +162 -0
- package/src/state/registry/registry-types.js +98 -0
- package/src/state/registry/use-registry.js +20 -0
- package/src/state/remove-draggable-from-list.js +13 -0
- package/src/state/scroll-viewport.js +34 -0
- package/src/state/spacing.js +45 -0
- package/src/state/store-types.js +16 -0
- package/src/state/update-displacement-visibility/recompute.js +54 -0
- package/src/state/update-displacement-visibility/speculatively-increase.js +111 -0
- package/src/state/visibility/is-partially-visible-through-frame.js +60 -0
- package/src/state/visibility/is-position-in-frame.js +12 -0
- package/src/state/visibility/is-totally-visible-through-frame-on-axis.js +19 -0
- package/src/state/visibility/is-totally-visible-through-frame.js +18 -0
- package/src/state/visibility/is-visible.js +102 -0
- package/src/state/with-scroll-change/with-all-displacement.js +15 -0
- package/src/state/with-scroll-change/with-droppable-displacement.js +13 -0
- package/src/state/with-scroll-change/with-droppable-scroll.js +13 -0
- package/src/state/with-scroll-change/with-viewport-displacement.js +7 -0
- package/src/types.js +542 -0
- package/src/view/animate-in-out/animate-in-out.jsx +95 -0
- package/src/view/animate-in-out/index.js +2 -0
- package/src/view/check-is-valid-inner-ref.js +15 -0
- package/src/view/context/app-context.js +19 -0
- package/src/view/context/droppable-context.js +11 -0
- package/src/view/context/store-context.js +5 -0
- package/src/view/data-attributes.js +37 -0
- package/src/view/drag-drop-context/app.jsx +273 -0
- package/src/view/drag-drop-context/check-doctype.js +39 -0
- package/src/view/drag-drop-context/check-react-version.js +71 -0
- package/src/view/drag-drop-context/drag-drop-context-types.js +7 -0
- package/src/view/drag-drop-context/drag-drop-context.jsx +68 -0
- package/src/view/drag-drop-context/error-boundary.jsx +88 -0
- package/src/view/drag-drop-context/index.js +2 -0
- package/src/view/drag-drop-context/use-startup-validation.js +13 -0
- package/src/view/drag-drop-context/use-unique-context-id.js +13 -0
- package/src/view/draggable/connected-draggable.js +372 -0
- package/src/view/draggable/draggable-api.jsx +48 -0
- package/src/view/draggable/draggable-types.js +191 -0
- package/src/view/draggable/draggable.jsx +171 -0
- package/src/view/draggable/get-style.js +109 -0
- package/src/view/draggable/index.js +2 -0
- package/src/view/draggable/use-validation.js +70 -0
- package/src/view/droppable/connected-droppable.js +280 -0
- package/src/view/droppable/droppable-types.js +91 -0
- package/src/view/droppable/droppable.jsx +167 -0
- package/src/view/droppable/index.js +2 -0
- package/src/view/droppable/use-validation.js +101 -0
- package/src/view/event-bindings/bind-events.js +39 -0
- package/src/view/event-bindings/event-types.js +14 -0
- package/src/view/get-body-element.js +8 -0
- package/src/view/get-border-box-center-position.js +5 -0
- package/src/view/get-document-element.js +8 -0
- package/src/view/get-elements/find-drag-handle.js +38 -0
- package/src/view/get-elements/find-draggable.js +30 -0
- package/src/view/is-strict-equal.js +2 -0
- package/src/view/is-type-of-element/is-element.js +6 -0
- package/src/view/is-type-of-element/is-html-element.js +6 -0
- package/src/view/is-type-of-element/is-svg-element.js +12 -0
- package/src/view/key-codes.js +13 -0
- package/src/view/placeholder/index.js +2 -0
- package/src/view/placeholder/placeholder-types.js +16 -0
- package/src/view/placeholder/placeholder.jsx +198 -0
- package/src/view/scroll-listener.js +72 -0
- package/src/view/throw-if-invalid-inner-ref.js +15 -0
- package/src/view/use-announcer/index.js +2 -0
- package/src/view/use-announcer/use-announcer.js +80 -0
- package/src/view/use-dev-setup-warning.js +22 -0
- package/src/view/use-dev.js +9 -0
- package/src/view/use-draggable-publisher/get-dimension.js +44 -0
- package/src/view/use-draggable-publisher/index.js +2 -0
- package/src/view/use-draggable-publisher/use-draggable-publisher.js +87 -0
- package/src/view/use-droppable-publisher/check-for-nested-scroll-container.js +27 -0
- package/src/view/use-droppable-publisher/get-closest-scrollable.js +95 -0
- package/src/view/use-droppable-publisher/get-dimension.js +139 -0
- package/src/view/use-droppable-publisher/get-env.js +31 -0
- package/src/view/use-droppable-publisher/get-listener-options.js +12 -0
- package/src/view/use-droppable-publisher/get-scroll.js +7 -0
- package/src/view/use-droppable-publisher/index.js +2 -0
- package/src/view/use-droppable-publisher/is-in-fixed-container.js +21 -0
- package/src/view/use-droppable-publisher/use-droppable-publisher.js +283 -0
- package/src/view/use-focus-marshal/focus-marshal-types.js +13 -0
- package/src/view/use-focus-marshal/index.js +2 -0
- package/src/view/use-focus-marshal/use-focus-marshal.js +129 -0
- package/src/view/use-hidden-text-element/index.js +2 -0
- package/src/view/use-hidden-text-element/use-hidden-text-element.js +60 -0
- package/src/view/use-isomorphic-layout-effect.js +18 -0
- package/src/view/use-previous-ref.js +14 -0
- package/src/view/use-required-context.js +9 -0
- package/src/view/use-sensor-marshal/closest.js +50 -0
- package/src/view/use-sensor-marshal/find-closest-draggable-id-from-event.js +50 -0
- package/src/view/use-sensor-marshal/index.js +5 -0
- package/src/view/use-sensor-marshal/is-event-in-interactive-element.js +66 -0
- package/src/view/use-sensor-marshal/lock.js +48 -0
- package/src/view/use-sensor-marshal/sensors/use-keyboard-sensor.js +243 -0
- package/src/view/use-sensor-marshal/sensors/use-mouse-sensor.js +386 -0
- package/src/view/use-sensor-marshal/sensors/use-touch-sensor.js +461 -0
- package/src/view/use-sensor-marshal/sensors/util/prevent-standard-key-events.js +19 -0
- package/src/view/use-sensor-marshal/sensors/util/supported-page-visibility-event-name.js +29 -0
- package/src/view/use-sensor-marshal/use-sensor-marshal.js +495 -0
- package/src/view/use-sensor-marshal/use-validate-sensor-hooks.js +20 -0
- package/src/view/use-style-marshal/get-styles.js +170 -0
- package/src/view/use-style-marshal/index.js +2 -0
- package/src/view/use-style-marshal/style-marshal-types.js +8 -0
- package/src/view/use-style-marshal/use-style-marshal.js +126 -0
- package/src/view/use-unique-id.js +25 -0
- package/src/view/visually-hidden-style.js +16 -0
- package/src/view/window/get-max-window-scroll.js +19 -0
- package/src/view/window/get-viewport.js +49 -0
- package/src/view/window/get-window-from-el.js +3 -0
- package/src/view/window/get-window-scroll.js +30 -0
- 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
|
+
}
|