@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,461 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { useCallback, useMemo } from 'use-memo-one';
|
|
4
|
+
import type { Position } from 'css-box-model';
|
|
5
|
+
import { invariant } from '../../../invariant';
|
|
6
|
+
import type {
|
|
7
|
+
DraggableId,
|
|
8
|
+
SensorAPI,
|
|
9
|
+
PreDragActions,
|
|
10
|
+
FluidDragActions,
|
|
11
|
+
} from '../../../types';
|
|
12
|
+
import type {
|
|
13
|
+
EventBinding,
|
|
14
|
+
EventOptions,
|
|
15
|
+
} from '../../event-bindings/event-types';
|
|
16
|
+
import bindEvents from '../../event-bindings/bind-events';
|
|
17
|
+
import * as keyCodes from '../../key-codes';
|
|
18
|
+
import supportedPageVisibilityEventName from './util/supported-page-visibility-event-name';
|
|
19
|
+
import { noop } from '../../../empty';
|
|
20
|
+
import useLayoutEffect from '../../use-isomorphic-layout-effect';
|
|
21
|
+
|
|
22
|
+
type TouchWithForce = Touch & {
|
|
23
|
+
force: number,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type Idle = {|
|
|
27
|
+
type: 'IDLE',
|
|
28
|
+
|};
|
|
29
|
+
|
|
30
|
+
type Pending = {|
|
|
31
|
+
type: 'PENDING',
|
|
32
|
+
point: Position,
|
|
33
|
+
actions: PreDragActions,
|
|
34
|
+
longPressTimerId: TimeoutID,
|
|
35
|
+
|};
|
|
36
|
+
|
|
37
|
+
type Dragging = {|
|
|
38
|
+
type: 'DRAGGING',
|
|
39
|
+
actions: FluidDragActions,
|
|
40
|
+
hasMoved: boolean,
|
|
41
|
+
|};
|
|
42
|
+
|
|
43
|
+
type Phase = Idle | Pending | Dragging;
|
|
44
|
+
|
|
45
|
+
const idle: Idle = { type: 'IDLE' };
|
|
46
|
+
// Decreased from 150 as a work around for an issue for forcepress on iOS
|
|
47
|
+
// https://github.com/atlassian/react-beautiful-dnd/issues/1401
|
|
48
|
+
export const timeForLongPress: number = 120;
|
|
49
|
+
export const forcePressThreshold: number = 0.15;
|
|
50
|
+
|
|
51
|
+
type GetBindingArgs = {|
|
|
52
|
+
cancel: () => void,
|
|
53
|
+
completed: () => void,
|
|
54
|
+
getPhase: () => Phase,
|
|
55
|
+
|};
|
|
56
|
+
|
|
57
|
+
function getWindowBindings({
|
|
58
|
+
cancel,
|
|
59
|
+
getPhase,
|
|
60
|
+
}: GetBindingArgs): EventBinding[] {
|
|
61
|
+
return [
|
|
62
|
+
// If the orientation of the device changes - kill the drag
|
|
63
|
+
// https://davidwalsh.name/orientation-change
|
|
64
|
+
{
|
|
65
|
+
eventName: 'orientationchange',
|
|
66
|
+
fn: cancel,
|
|
67
|
+
},
|
|
68
|
+
// some devices fire resize if the orientation changes
|
|
69
|
+
{
|
|
70
|
+
eventName: 'resize',
|
|
71
|
+
fn: cancel,
|
|
72
|
+
},
|
|
73
|
+
// Long press can bring up a context menu
|
|
74
|
+
// need to opt out of this behavior
|
|
75
|
+
{
|
|
76
|
+
eventName: 'contextmenu',
|
|
77
|
+
fn: (event: Event) => {
|
|
78
|
+
// always opting out of context menu events
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
// On some devices it is possible to have a touch interface with a keyboard.
|
|
83
|
+
// On any keyboard event we cancel a touch drag
|
|
84
|
+
{
|
|
85
|
+
eventName: 'keydown',
|
|
86
|
+
fn: (event: KeyboardEvent) => {
|
|
87
|
+
if (getPhase().type !== 'DRAGGING') {
|
|
88
|
+
cancel();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// direct cancel: we are preventing the default action
|
|
93
|
+
// indirect cancel: we are not preventing the default action
|
|
94
|
+
|
|
95
|
+
// escape is a direct cancel
|
|
96
|
+
if (event.keyCode === keyCodes.escape) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
}
|
|
99
|
+
cancel();
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
// Cancel on page visibility change
|
|
103
|
+
{
|
|
104
|
+
eventName: supportedPageVisibilityEventName,
|
|
105
|
+
fn: cancel,
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// All of the touch events get applied to the drag handle of the touch interaction
|
|
111
|
+
// This plays well with the event.target being unmounted during a drag
|
|
112
|
+
function getHandleBindings({
|
|
113
|
+
cancel,
|
|
114
|
+
completed,
|
|
115
|
+
getPhase,
|
|
116
|
+
}: GetBindingArgs): EventBinding[] {
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
eventName: 'touchmove',
|
|
120
|
+
// Opting out of passive touchmove (default) so as to prevent scrolling while moving
|
|
121
|
+
// Not worried about performance as effect of move is throttled in requestAnimationFrame
|
|
122
|
+
// Using `capture: false` due to a recent horrible firefox bug: https://twitter.com/alexandereardon/status/1125904207184187393
|
|
123
|
+
options: { capture: false },
|
|
124
|
+
fn: (event: TouchEvent) => {
|
|
125
|
+
const phase: Phase = getPhase();
|
|
126
|
+
// Drag has not yet started and we are waiting for a long press.
|
|
127
|
+
if (phase.type !== 'DRAGGING') {
|
|
128
|
+
cancel();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// At this point we are dragging
|
|
133
|
+
phase.hasMoved = true;
|
|
134
|
+
|
|
135
|
+
const { clientX, clientY } = event.touches[0];
|
|
136
|
+
|
|
137
|
+
const point: Position = {
|
|
138
|
+
x: clientX,
|
|
139
|
+
y: clientY,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// We need to prevent the default event in order to block native scrolling
|
|
143
|
+
// Also because we are using it as part of a drag we prevent the default action
|
|
144
|
+
// as a sign that we are using the event
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
phase.actions.move(point);
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
eventName: 'touchend',
|
|
151
|
+
fn: (event: TouchEvent) => {
|
|
152
|
+
const phase: Phase = getPhase();
|
|
153
|
+
// drag had not started yet - do not prevent the default action
|
|
154
|
+
if (phase.type !== 'DRAGGING') {
|
|
155
|
+
cancel();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ending the drag
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
phase.actions.drop({ shouldBlockNextClick: true });
|
|
162
|
+
completed();
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
eventName: 'touchcancel',
|
|
167
|
+
fn: (event: TouchEvent) => {
|
|
168
|
+
// drag had not started yet - do not prevent the default action
|
|
169
|
+
if (getPhase().type !== 'DRAGGING') {
|
|
170
|
+
cancel();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// already dragging - this event is directly ending a drag
|
|
175
|
+
event.preventDefault();
|
|
176
|
+
cancel();
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
// Need to opt out of dragging if the user is a force press
|
|
180
|
+
// Only for webkit which has decided to introduce its own custom way of doing things
|
|
181
|
+
// https://developer.apple.com/library/content/documentation/AppleApplications/Conceptual/SafariJSProgTopics/RespondingtoForceTouchEventsfromJavaScript.html
|
|
182
|
+
{
|
|
183
|
+
eventName: 'touchforcechange',
|
|
184
|
+
fn: (event: TouchEvent) => {
|
|
185
|
+
const phase: Phase = getPhase();
|
|
186
|
+
|
|
187
|
+
// needed to use phase.actions
|
|
188
|
+
invariant(phase.type !== 'IDLE');
|
|
189
|
+
|
|
190
|
+
// This is not fantastic logic, but it is done to account for
|
|
191
|
+
// and issue with forcepress on iOS
|
|
192
|
+
// Calling event.preventDefault() will currently opt out of scrolling and clicking
|
|
193
|
+
// https://github.com/atlassian/react-beautiful-dnd/issues/1401
|
|
194
|
+
|
|
195
|
+
const touch: ?TouchWithForce = (event.touches[0]: any);
|
|
196
|
+
|
|
197
|
+
if (!touch) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const isForcePress: boolean = touch.force >= forcePressThreshold;
|
|
202
|
+
|
|
203
|
+
if (!isForcePress) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const shouldRespect: boolean = phase.actions.shouldRespectForcePress();
|
|
208
|
+
|
|
209
|
+
if (phase.type === 'PENDING') {
|
|
210
|
+
if (shouldRespect) {
|
|
211
|
+
cancel();
|
|
212
|
+
}
|
|
213
|
+
// If not respecting we just let the event go through
|
|
214
|
+
// It will not have an impact on the browser until
|
|
215
|
+
// there has been a sufficient time ellapsed
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 'DRAGGING'
|
|
220
|
+
|
|
221
|
+
if (shouldRespect) {
|
|
222
|
+
if (phase.hasMoved) {
|
|
223
|
+
// After the user has moved we do not allow the dragging item to be force pressed
|
|
224
|
+
// This prevents strange behaviour such as a link preview opening mid drag
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// indirect cancel
|
|
229
|
+
cancel();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// not respecting during a drag
|
|
234
|
+
event.preventDefault();
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
// Cancel on page visibility change
|
|
238
|
+
{
|
|
239
|
+
eventName: supportedPageVisibilityEventName,
|
|
240
|
+
fn: cancel,
|
|
241
|
+
},
|
|
242
|
+
// Not adding a cancel on touchstart as this handler will pick up the initial touchstart event
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default function useTouchSensor(api: SensorAPI) {
|
|
247
|
+
const phaseRef = useRef<Phase>(idle);
|
|
248
|
+
const unbindEventsRef = useRef<() => void>(noop);
|
|
249
|
+
|
|
250
|
+
const getPhase = useCallback(function getPhase(): Phase {
|
|
251
|
+
return phaseRef.current;
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
const setPhase = useCallback(function setPhase(phase: Phase) {
|
|
255
|
+
phaseRef.current = phase;
|
|
256
|
+
}, []);
|
|
257
|
+
|
|
258
|
+
const startCaptureBinding: EventBinding = useMemo(
|
|
259
|
+
() => ({
|
|
260
|
+
eventName: 'touchstart',
|
|
261
|
+
fn: function onTouchStart(event: TouchEvent) {
|
|
262
|
+
// Event already used by something else
|
|
263
|
+
if (event.defaultPrevented) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// We need to NOT call event.preventDefault() so as to maintain as much standard
|
|
268
|
+
// browser interactions as possible.
|
|
269
|
+
// This includes navigation on anchors which we want to preserve
|
|
270
|
+
|
|
271
|
+
const draggableId: ?DraggableId = api.findClosestDraggableId(event);
|
|
272
|
+
|
|
273
|
+
if (!draggableId) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const actions: ?PreDragActions = api.tryGetLock(
|
|
278
|
+
draggableId,
|
|
279
|
+
// eslint-disable-next-line no-use-before-define
|
|
280
|
+
stop,
|
|
281
|
+
{ sourceEvent: event },
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
// could not start a drag
|
|
285
|
+
if (!actions) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const touch: Touch = event.touches[0];
|
|
290
|
+
const { clientX, clientY } = touch;
|
|
291
|
+
const point: Position = {
|
|
292
|
+
x: clientX,
|
|
293
|
+
y: clientY,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// unbind this event handler
|
|
297
|
+
unbindEventsRef.current();
|
|
298
|
+
|
|
299
|
+
// eslint-disable-next-line no-use-before-define
|
|
300
|
+
startPendingDrag(actions, point);
|
|
301
|
+
},
|
|
302
|
+
}),
|
|
303
|
+
// not including stop or startPendingDrag as it is not defined initially
|
|
304
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
305
|
+
[api],
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const listenForCapture = useCallback(
|
|
309
|
+
function listenForCapture() {
|
|
310
|
+
const options: EventOptions = {
|
|
311
|
+
capture: true,
|
|
312
|
+
passive: false,
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
unbindEventsRef.current = bindEvents(
|
|
316
|
+
window,
|
|
317
|
+
[startCaptureBinding],
|
|
318
|
+
options,
|
|
319
|
+
);
|
|
320
|
+
},
|
|
321
|
+
[startCaptureBinding],
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const stop = useCallback(() => {
|
|
325
|
+
const current: Phase = phaseRef.current;
|
|
326
|
+
if (current.type === 'IDLE') {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// aborting any pending drag
|
|
331
|
+
if (current.type === 'PENDING') {
|
|
332
|
+
clearTimeout(current.longPressTimerId);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
setPhase(idle);
|
|
336
|
+
unbindEventsRef.current();
|
|
337
|
+
|
|
338
|
+
listenForCapture();
|
|
339
|
+
}, [listenForCapture, setPhase]);
|
|
340
|
+
|
|
341
|
+
const cancel = useCallback(() => {
|
|
342
|
+
const phase: Phase = phaseRef.current;
|
|
343
|
+
stop();
|
|
344
|
+
if (phase.type === 'DRAGGING') {
|
|
345
|
+
phase.actions.cancel({ shouldBlockNextClick: true });
|
|
346
|
+
}
|
|
347
|
+
if (phase.type === 'PENDING') {
|
|
348
|
+
phase.actions.abort();
|
|
349
|
+
}
|
|
350
|
+
}, [stop]);
|
|
351
|
+
|
|
352
|
+
const bindCapturingEvents = useCallback(
|
|
353
|
+
function bindCapturingEvents() {
|
|
354
|
+
const options: EventOptions = { capture: true, passive: false };
|
|
355
|
+
const args: GetBindingArgs = {
|
|
356
|
+
cancel,
|
|
357
|
+
completed: stop,
|
|
358
|
+
getPhase,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// In prior versions of iOS it was required that touch listeners be added
|
|
362
|
+
// to the handle to work correctly (even if the handle got removed in a portal / clone)
|
|
363
|
+
// In the latest version it appears to be the opposite: for reparenting to work
|
|
364
|
+
// the events need to be attached to the window.
|
|
365
|
+
// For now i'll keep these two functions seperate in case we need to swap it back again
|
|
366
|
+
// Old behaviour:
|
|
367
|
+
// https://gist.github.com/parris/dda613e3ae78f14eb2dc9fa0f4bfce3d
|
|
368
|
+
// https://stackoverflow.com/questions/33298828/touch-move-event-dont-fire-after-touch-start-target-is-removed
|
|
369
|
+
const unbindTarget = bindEvents(window, getHandleBindings(args), options);
|
|
370
|
+
const unbindWindow = bindEvents(window, getWindowBindings(args), options);
|
|
371
|
+
|
|
372
|
+
unbindEventsRef.current = function unbindAll() {
|
|
373
|
+
unbindTarget();
|
|
374
|
+
unbindWindow();
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
[cancel, getPhase, stop],
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const startDragging = useCallback(
|
|
381
|
+
function startDragging() {
|
|
382
|
+
const phase: Phase = getPhase();
|
|
383
|
+
invariant(
|
|
384
|
+
phase.type === 'PENDING',
|
|
385
|
+
`Cannot start dragging from phase ${phase.type}`,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
const actions: FluidDragActions = phase.actions.fluidLift(phase.point);
|
|
389
|
+
|
|
390
|
+
setPhase({
|
|
391
|
+
type: 'DRAGGING',
|
|
392
|
+
actions,
|
|
393
|
+
hasMoved: false,
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
[getPhase, setPhase],
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
const startPendingDrag = useCallback(
|
|
400
|
+
function startPendingDrag(actions: PreDragActions, point: Position) {
|
|
401
|
+
invariant(
|
|
402
|
+
getPhase().type === 'IDLE',
|
|
403
|
+
'Expected to move from IDLE to PENDING drag',
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const longPressTimerId: TimeoutID = setTimeout(
|
|
407
|
+
startDragging,
|
|
408
|
+
timeForLongPress,
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
setPhase({
|
|
412
|
+
type: 'PENDING',
|
|
413
|
+
point,
|
|
414
|
+
actions,
|
|
415
|
+
longPressTimerId,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
bindCapturingEvents();
|
|
419
|
+
},
|
|
420
|
+
[bindCapturingEvents, getPhase, setPhase, startDragging],
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
useLayoutEffect(
|
|
424
|
+
function mount() {
|
|
425
|
+
listenForCapture();
|
|
426
|
+
|
|
427
|
+
return function unmount() {
|
|
428
|
+
// remove any existing listeners
|
|
429
|
+
unbindEventsRef.current();
|
|
430
|
+
|
|
431
|
+
// need to kill any pending drag start timer
|
|
432
|
+
const phase: Phase = getPhase();
|
|
433
|
+
if (phase.type === 'PENDING') {
|
|
434
|
+
clearTimeout(phase.longPressTimerId);
|
|
435
|
+
setPhase(idle);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
},
|
|
439
|
+
[getPhase, listenForCapture, setPhase],
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
// This is needed for safari
|
|
443
|
+
// Simply adding a non capture, non passive 'touchmove' listener.
|
|
444
|
+
// This forces event.preventDefault() in dynamically added
|
|
445
|
+
// touchmove event handlers to actually work
|
|
446
|
+
// https://github.com/atlassian/react-beautiful-dnd/issues/1374
|
|
447
|
+
useLayoutEffect(function webkitHack() {
|
|
448
|
+
const unbind = bindEvents(window, [
|
|
449
|
+
{
|
|
450
|
+
eventName: 'touchmove',
|
|
451
|
+
// using a new noop function for each usage as a single `removeEventListener()`
|
|
452
|
+
// call will remove all handlers with the same reference
|
|
453
|
+
// https://codesandbox.io/s/removing-multiple-handlers-with-same-reference-fxe15
|
|
454
|
+
fn: () => {},
|
|
455
|
+
options: { capture: false, passive: false },
|
|
456
|
+
},
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
return unbind;
|
|
460
|
+
}, []);
|
|
461
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as keyCodes from '../../../key-codes';
|
|
3
|
+
|
|
4
|
+
type KeyMap = {
|
|
5
|
+
[key: number]: true,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const preventedKeys: KeyMap = {
|
|
9
|
+
// submission
|
|
10
|
+
[keyCodes.enter]: true,
|
|
11
|
+
// tabbing
|
|
12
|
+
[keyCodes.tab]: true,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default (event: KeyboardEvent) => {
|
|
16
|
+
if (preventedKeys[event.keyCode]) {
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { find } from '../../../../native-with-fallback';
|
|
3
|
+
|
|
4
|
+
const supportedEventName: string = ((): string => {
|
|
5
|
+
const base: string = 'visibilitychange';
|
|
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
|
+
`ms${base}`,
|
|
16
|
+
`webkit${base}`,
|
|
17
|
+
`moz${base}`,
|
|
18
|
+
`o${base}`,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const supported: ?string = find(
|
|
22
|
+
candidates,
|
|
23
|
+
(eventName: string): boolean => `on${eventName}` in document,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
return supported || base;
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
export default supportedEventName;
|