@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,198 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import React, { useState, useRef, useEffect, type Node } from 'react';
|
|
3
|
+
import { useCallback } from 'use-memo-one';
|
|
4
|
+
import type { Spacing } from 'css-box-model';
|
|
5
|
+
import type {
|
|
6
|
+
Placeholder as PlaceholderType,
|
|
7
|
+
InOutAnimationMode,
|
|
8
|
+
ContextId,
|
|
9
|
+
} from '../../types';
|
|
10
|
+
import { transitions } from '../../animation';
|
|
11
|
+
import { noSpacing } from '../../state/spacing';
|
|
12
|
+
|
|
13
|
+
function noop() {}
|
|
14
|
+
|
|
15
|
+
export type PlaceholderStyle = {|
|
|
16
|
+
display: string,
|
|
17
|
+
boxSizing: 'border-box',
|
|
18
|
+
width: number,
|
|
19
|
+
height: number,
|
|
20
|
+
marginTop: number,
|
|
21
|
+
marginRight: number,
|
|
22
|
+
marginBottom: number,
|
|
23
|
+
marginLeft: number,
|
|
24
|
+
flexShrink: '0',
|
|
25
|
+
flexGrow: '0',
|
|
26
|
+
pointerEvents: 'none',
|
|
27
|
+
transition: ?string,
|
|
28
|
+
|};
|
|
29
|
+
export type Props = {|
|
|
30
|
+
placeholder: PlaceholderType,
|
|
31
|
+
animate: InOutAnimationMode,
|
|
32
|
+
onClose: () => void,
|
|
33
|
+
innerRef?: () => ?HTMLElement,
|
|
34
|
+
onTransitionEnd: () => void,
|
|
35
|
+
contextId: ContextId,
|
|
36
|
+
|};
|
|
37
|
+
|
|
38
|
+
type Size = {|
|
|
39
|
+
width: number,
|
|
40
|
+
height: number,
|
|
41
|
+
// Need to animate in/out animation as well as size
|
|
42
|
+
margin: Spacing,
|
|
43
|
+
|};
|
|
44
|
+
|
|
45
|
+
type HelperArgs = {|
|
|
46
|
+
isAnimatingOpenOnMount: boolean,
|
|
47
|
+
placeholder: PlaceholderType,
|
|
48
|
+
animate: InOutAnimationMode,
|
|
49
|
+
|};
|
|
50
|
+
|
|
51
|
+
const empty: Size = {
|
|
52
|
+
width: 0,
|
|
53
|
+
height: 0,
|
|
54
|
+
margin: noSpacing,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getSize = ({
|
|
58
|
+
isAnimatingOpenOnMount,
|
|
59
|
+
placeholder,
|
|
60
|
+
animate,
|
|
61
|
+
}: HelperArgs): Size => {
|
|
62
|
+
if (isAnimatingOpenOnMount) {
|
|
63
|
+
return empty;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (animate === 'close') {
|
|
67
|
+
return empty;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
height: placeholder.client.borderBox.height,
|
|
72
|
+
width: placeholder.client.borderBox.width,
|
|
73
|
+
margin: placeholder.client.margin,
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getStyle = ({
|
|
78
|
+
isAnimatingOpenOnMount,
|
|
79
|
+
placeholder,
|
|
80
|
+
animate,
|
|
81
|
+
}: HelperArgs): PlaceholderStyle => {
|
|
82
|
+
const size: Size = getSize({ isAnimatingOpenOnMount, placeholder, animate });
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
display: placeholder.display,
|
|
86
|
+
// ## Recreating the box model
|
|
87
|
+
// We created the borderBox and then apply the margins directly
|
|
88
|
+
// this is to maintain any margin collapsing behaviour
|
|
89
|
+
|
|
90
|
+
// creating borderBox
|
|
91
|
+
// background: 'green',
|
|
92
|
+
boxSizing: 'border-box',
|
|
93
|
+
width: size.width,
|
|
94
|
+
height: size.height,
|
|
95
|
+
// creating marginBox
|
|
96
|
+
marginTop: size.margin.top,
|
|
97
|
+
marginRight: size.margin.right,
|
|
98
|
+
marginBottom: size.margin.bottom,
|
|
99
|
+
marginLeft: size.margin.left,
|
|
100
|
+
|
|
101
|
+
// ## Avoiding collapsing
|
|
102
|
+
// Avoiding the collapsing or growing of this element when pushed by flex child siblings.
|
|
103
|
+
// We have already taken a snapshot the current dimensions we do not want this element
|
|
104
|
+
// to recalculate its dimensions
|
|
105
|
+
// It is okay for these properties to be applied on elements that are not flex children
|
|
106
|
+
flexShrink: '0',
|
|
107
|
+
flexGrow: '0',
|
|
108
|
+
// Just a little performance optimisation: avoiding the browser needing
|
|
109
|
+
// to worry about pointer events for this element
|
|
110
|
+
pointerEvents: 'none',
|
|
111
|
+
|
|
112
|
+
// Animate the placeholder size and margin
|
|
113
|
+
transition: animate !== 'none' ? transitions.placeholder : null,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function Placeholder(props: Props): Node {
|
|
118
|
+
const animateOpenTimerRef = useRef<?TimeoutID>(null);
|
|
119
|
+
|
|
120
|
+
const tryClearAnimateOpenTimer = useCallback(() => {
|
|
121
|
+
if (!animateOpenTimerRef.current) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
clearTimeout(animateOpenTimerRef.current);
|
|
125
|
+
animateOpenTimerRef.current = null;
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
const { animate, onTransitionEnd, onClose, contextId } = props;
|
|
129
|
+
const [isAnimatingOpenOnMount, setIsAnimatingOpenOnMount] = useState<boolean>(
|
|
130
|
+
props.animate === 'open',
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Will run after a render is flushed
|
|
134
|
+
// Still need to wait a timeout to ensure that the
|
|
135
|
+
// update is completely applied to the DOM
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
// No need to do anything
|
|
138
|
+
if (!isAnimatingOpenOnMount) {
|
|
139
|
+
return noop;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// might need to clear the timer
|
|
143
|
+
if (animate !== 'open') {
|
|
144
|
+
tryClearAnimateOpenTimer();
|
|
145
|
+
setIsAnimatingOpenOnMount(false);
|
|
146
|
+
return noop;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// timer already pending
|
|
150
|
+
if (animateOpenTimerRef.current) {
|
|
151
|
+
return noop;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
animateOpenTimerRef.current = setTimeout(() => {
|
|
155
|
+
animateOpenTimerRef.current = null;
|
|
156
|
+
setIsAnimatingOpenOnMount(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// clear the timer if needed
|
|
160
|
+
return tryClearAnimateOpenTimer;
|
|
161
|
+
}, [animate, isAnimatingOpenOnMount, tryClearAnimateOpenTimer]);
|
|
162
|
+
|
|
163
|
+
const onSizeChangeEnd = useCallback(
|
|
164
|
+
(event: TransitionEvent) => {
|
|
165
|
+
// We transition height, width and margin
|
|
166
|
+
// each of those transitions will independently call this callback
|
|
167
|
+
// Because they all have the same duration we can just respond to one of them
|
|
168
|
+
// 'height' was chosen for no particular reason :D
|
|
169
|
+
if (event.propertyName !== 'height') {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
onTransitionEnd();
|
|
174
|
+
|
|
175
|
+
if (animate === 'close') {
|
|
176
|
+
onClose();
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
[animate, onClose, onTransitionEnd],
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const style: PlaceholderStyle = getStyle({
|
|
183
|
+
isAnimatingOpenOnMount,
|
|
184
|
+
animate: props.animate,
|
|
185
|
+
placeholder: props.placeholder,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return React.createElement(props.placeholder.tagName, {
|
|
189
|
+
style,
|
|
190
|
+
'data-rbd-placeholder-context-id': contextId,
|
|
191
|
+
onTransitionEnd: onSizeChangeEnd,
|
|
192
|
+
ref: props.innerRef,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default React.memo<Props>(Placeholder);
|
|
197
|
+
// enzyme does not work well with memo, so exporting the non-memo version
|
|
198
|
+
export const WithoutMemo = Placeholder;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type { Position } from 'css-box-model';
|
|
3
|
+
import rafSchd from 'raf-schd';
|
|
4
|
+
import { invariant } from '../invariant';
|
|
5
|
+
import bindEvents from './event-bindings/bind-events';
|
|
6
|
+
import type { EventBinding } from './event-bindings/event-types';
|
|
7
|
+
import getWindowScroll from './window/get-window-scroll';
|
|
8
|
+
import { noop } from '../empty';
|
|
9
|
+
|
|
10
|
+
type OnWindowScroll = (newScroll: Position) => void;
|
|
11
|
+
|
|
12
|
+
type Args = {|
|
|
13
|
+
onWindowScroll: OnWindowScroll,
|
|
14
|
+
|};
|
|
15
|
+
|
|
16
|
+
type Result = {|
|
|
17
|
+
start: () => void,
|
|
18
|
+
stop: () => void,
|
|
19
|
+
isActive: () => boolean,
|
|
20
|
+
|};
|
|
21
|
+
|
|
22
|
+
function getWindowScrollBinding(update: () => void): EventBinding {
|
|
23
|
+
return {
|
|
24
|
+
eventName: 'scroll',
|
|
25
|
+
// ## Passive: true
|
|
26
|
+
// Eventual consistency is fine because we use position: fixed on the item
|
|
27
|
+
// ## Capture: false
|
|
28
|
+
// Scroll events on elements do not bubble, but they go through the capture phase
|
|
29
|
+
// https://twitter.com/alexandereardon/status/985994224867819520
|
|
30
|
+
// Using capture: false here as we want to avoid intercepting droppable scroll requests
|
|
31
|
+
options: { passive: true, capture: false },
|
|
32
|
+
fn: (event: UIEvent) => {
|
|
33
|
+
// IE11 fix
|
|
34
|
+
// All scrollable events still bubble up and are caught by this handler in ie11.
|
|
35
|
+
// On a window scroll the event.target should be the window or the document.
|
|
36
|
+
// If this is not the case then it is not a 'window' scroll event and can be ignored
|
|
37
|
+
if (event.target !== window && event.target !== window.document) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
update();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default function getScrollListener({ onWindowScroll }: Args): Result {
|
|
47
|
+
function updateScroll() {
|
|
48
|
+
// letting the update function read the latest scroll when called
|
|
49
|
+
onWindowScroll(getWindowScroll());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const scheduled = rafSchd(updateScroll);
|
|
53
|
+
const binding: EventBinding = getWindowScrollBinding(scheduled);
|
|
54
|
+
let unbind: () => void = noop;
|
|
55
|
+
|
|
56
|
+
function isActive(): boolean {
|
|
57
|
+
return unbind !== noop;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function start() {
|
|
61
|
+
invariant(!isActive(), 'Cannot start scroll listener when already active');
|
|
62
|
+
unbind = bindEvents(window, [binding]);
|
|
63
|
+
}
|
|
64
|
+
function stop() {
|
|
65
|
+
invariant(isActive(), 'Cannot stop scroll listener when not active');
|
|
66
|
+
scheduled.cancel();
|
|
67
|
+
unbind();
|
|
68
|
+
unbind = noop;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { start, stop, isActive };
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { invariant } from '../invariant';
|
|
3
|
+
import isHtmlElement from './is-type-of-element/is-html-element';
|
|
4
|
+
|
|
5
|
+
export default (ref: ?mixed) => {
|
|
6
|
+
invariant(
|
|
7
|
+
ref && isHtmlElement(ref),
|
|
8
|
+
`
|
|
9
|
+
provided.innerRef has not been provided with a HTMLElement.
|
|
10
|
+
|
|
11
|
+
You can find a guide on using the innerRef callback functions at:
|
|
12
|
+
https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/using-inner-ref.md
|
|
13
|
+
`,
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
import { useMemo, useCallback } from 'use-memo-one';
|
|
4
|
+
import type { Announce, ContextId } from '../../types';
|
|
5
|
+
import { warning } from '../../dev-warning';
|
|
6
|
+
import getBodyElement from '../get-body-element';
|
|
7
|
+
import visuallyHidden from '../visually-hidden-style';
|
|
8
|
+
|
|
9
|
+
export const getId = (contextId: ContextId): string =>
|
|
10
|
+
`rbd-announcement-${contextId}`;
|
|
11
|
+
|
|
12
|
+
export default function useAnnouncer(contextId: ContextId): Announce {
|
|
13
|
+
const id: string = useMemo(() => getId(contextId), [contextId]);
|
|
14
|
+
const ref = useRef<?HTMLElement>(null);
|
|
15
|
+
|
|
16
|
+
useEffect(
|
|
17
|
+
function setup() {
|
|
18
|
+
const el: HTMLElement = document.createElement('div');
|
|
19
|
+
// storing reference for usage in announce
|
|
20
|
+
ref.current = el;
|
|
21
|
+
|
|
22
|
+
// identifier
|
|
23
|
+
el.id = id;
|
|
24
|
+
|
|
25
|
+
// Aria live region
|
|
26
|
+
|
|
27
|
+
// will force itself to be read
|
|
28
|
+
el.setAttribute('aria-live', 'assertive');
|
|
29
|
+
// must read the whole thing every time
|
|
30
|
+
el.setAttribute('aria-atomic', 'true');
|
|
31
|
+
|
|
32
|
+
// hide the element visually
|
|
33
|
+
Object.assign(el.style, visuallyHidden);
|
|
34
|
+
|
|
35
|
+
// Add to body
|
|
36
|
+
getBodyElement().appendChild(el);
|
|
37
|
+
|
|
38
|
+
return function cleanup() {
|
|
39
|
+
// Not clearing the ref as it might be used by announce before the timeout expires
|
|
40
|
+
|
|
41
|
+
// unmounting after a timeout to let any announcements
|
|
42
|
+
// during a mount be published
|
|
43
|
+
setTimeout(function remove() {
|
|
44
|
+
// checking if element exists as the body might have been changed by things like 'turbolinks'
|
|
45
|
+
const body: HTMLBodyElement = getBodyElement();
|
|
46
|
+
if (body.contains(el)) {
|
|
47
|
+
body.removeChild(el);
|
|
48
|
+
}
|
|
49
|
+
// if el was the current ref - clear it so that
|
|
50
|
+
// we can get a warning if announce is called
|
|
51
|
+
if (el === ref.current) {
|
|
52
|
+
ref.current = null;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
[id],
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const announce: Announce = useCallback((message: string): void => {
|
|
61
|
+
const el: ?HTMLElement = ref.current;
|
|
62
|
+
if (el) {
|
|
63
|
+
el.textContent = message;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
warning(`
|
|
68
|
+
A screen reader message was trying to be announced but it was unable to do so.
|
|
69
|
+
This can occur if you unmount your <DragDropContext /> in your onDragEnd.
|
|
70
|
+
Consider calling provided.announce() before the unmount so that the instruction will
|
|
71
|
+
not be lost for users relying on a screen reader.
|
|
72
|
+
|
|
73
|
+
Message not passed to screen reader:
|
|
74
|
+
|
|
75
|
+
"${message}"
|
|
76
|
+
`);
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
return announce;
|
|
80
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { error } from '../dev-warning';
|
|
4
|
+
import useDev from './use-dev';
|
|
5
|
+
|
|
6
|
+
export default function useDevSetupWarning(fn: () => void, inputs?: mixed[]) {
|
|
7
|
+
useDev(() => {
|
|
8
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
try {
|
|
11
|
+
fn();
|
|
12
|
+
} catch (e) {
|
|
13
|
+
error(`
|
|
14
|
+
A setup problem was encountered.
|
|
15
|
+
|
|
16
|
+
> ${e.message}
|
|
17
|
+
`);
|
|
18
|
+
}
|
|
19
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
20
|
+
}, inputs);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {
|
|
3
|
+
type BoxModel,
|
|
4
|
+
type Position,
|
|
5
|
+
calculateBox,
|
|
6
|
+
withScroll,
|
|
7
|
+
} from 'css-box-model';
|
|
8
|
+
import type {
|
|
9
|
+
DraggableDescriptor,
|
|
10
|
+
DraggableDimension,
|
|
11
|
+
Placeholder,
|
|
12
|
+
} from '../../types';
|
|
13
|
+
import { origin } from '../../state/position';
|
|
14
|
+
|
|
15
|
+
export default function getDimension(
|
|
16
|
+
descriptor: DraggableDescriptor,
|
|
17
|
+
el: HTMLElement,
|
|
18
|
+
windowScroll?: Position = origin,
|
|
19
|
+
): DraggableDimension {
|
|
20
|
+
const computedStyles: CSSStyleDeclaration = window.getComputedStyle(el);
|
|
21
|
+
const borderBox: ClientRect = el.getBoundingClientRect();
|
|
22
|
+
const client: BoxModel = calculateBox(borderBox, computedStyles);
|
|
23
|
+
const page: BoxModel = withScroll(client, windowScroll);
|
|
24
|
+
|
|
25
|
+
const placeholder: Placeholder = {
|
|
26
|
+
client,
|
|
27
|
+
tagName: el.tagName.toLowerCase(),
|
|
28
|
+
display: computedStyles.display,
|
|
29
|
+
};
|
|
30
|
+
const displaceBy: Position = {
|
|
31
|
+
x: client.marginBox.width,
|
|
32
|
+
y: client.marginBox.height,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const dimension: DraggableDimension = {
|
|
36
|
+
descriptor,
|
|
37
|
+
placeholder,
|
|
38
|
+
displaceBy,
|
|
39
|
+
client,
|
|
40
|
+
page,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return dimension;
|
|
44
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { type Position } from 'css-box-model';
|
|
3
|
+
import { useMemo, useCallback } from 'use-memo-one';
|
|
4
|
+
import { useRef } from 'react';
|
|
5
|
+
import { invariant } from '../../invariant';
|
|
6
|
+
import type {
|
|
7
|
+
DraggableDescriptor,
|
|
8
|
+
DraggableDimension,
|
|
9
|
+
Id,
|
|
10
|
+
DraggableOptions,
|
|
11
|
+
} from '../../types';
|
|
12
|
+
import type {
|
|
13
|
+
Registry,
|
|
14
|
+
DraggableEntry,
|
|
15
|
+
} from '../../state/registry/registry-types';
|
|
16
|
+
import makeDimension from './get-dimension';
|
|
17
|
+
import useLayoutEffect from '../use-isomorphic-layout-effect';
|
|
18
|
+
import useUniqueId from '../use-unique-id';
|
|
19
|
+
|
|
20
|
+
export type Args = {|
|
|
21
|
+
descriptor: DraggableDescriptor,
|
|
22
|
+
getDraggableRef: () => ?HTMLElement,
|
|
23
|
+
registry: Registry,
|
|
24
|
+
...DraggableOptions,
|
|
25
|
+
|};
|
|
26
|
+
|
|
27
|
+
export default function useDraggablePublisher(args: Args) {
|
|
28
|
+
const uniqueId: Id = useUniqueId('draggable');
|
|
29
|
+
|
|
30
|
+
const {
|
|
31
|
+
descriptor,
|
|
32
|
+
registry,
|
|
33
|
+
getDraggableRef,
|
|
34
|
+
canDragInteractiveElements,
|
|
35
|
+
shouldRespectForcePress,
|
|
36
|
+
isEnabled,
|
|
37
|
+
} = args;
|
|
38
|
+
|
|
39
|
+
const options: DraggableOptions = useMemo(
|
|
40
|
+
() => ({
|
|
41
|
+
canDragInteractiveElements,
|
|
42
|
+
shouldRespectForcePress,
|
|
43
|
+
isEnabled,
|
|
44
|
+
}),
|
|
45
|
+
[canDragInteractiveElements, isEnabled, shouldRespectForcePress],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const getDimension = useCallback(
|
|
49
|
+
(windowScroll?: Position): DraggableDimension => {
|
|
50
|
+
const el: ?HTMLElement = getDraggableRef();
|
|
51
|
+
invariant(el, 'Cannot get dimension when no ref is set');
|
|
52
|
+
return makeDimension(descriptor, el, windowScroll);
|
|
53
|
+
},
|
|
54
|
+
[descriptor, getDraggableRef],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const entry: DraggableEntry = useMemo(
|
|
58
|
+
() => ({
|
|
59
|
+
uniqueId,
|
|
60
|
+
descriptor,
|
|
61
|
+
options,
|
|
62
|
+
getDimension,
|
|
63
|
+
}),
|
|
64
|
+
[descriptor, getDimension, options, uniqueId],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const publishedRef = useRef<DraggableEntry>(entry);
|
|
68
|
+
const isFirstPublishRef = useRef<boolean>(true);
|
|
69
|
+
|
|
70
|
+
// mounting and unmounting
|
|
71
|
+
useLayoutEffect(() => {
|
|
72
|
+
registry.draggable.register(publishedRef.current);
|
|
73
|
+
return () => registry.draggable.unregister(publishedRef.current);
|
|
74
|
+
}, [registry.draggable]);
|
|
75
|
+
|
|
76
|
+
// updates while mounted
|
|
77
|
+
useLayoutEffect(() => {
|
|
78
|
+
if (isFirstPublishRef.current) {
|
|
79
|
+
isFirstPublishRef.current = false;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const last: DraggableEntry = publishedRef.current;
|
|
84
|
+
publishedRef.current = entry;
|
|
85
|
+
registry.draggable.update(entry, last);
|
|
86
|
+
}, [entry, registry.draggable]);
|
|
87
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import getClosestScrollable from './get-closest-scrollable';
|
|
3
|
+
import { warning } from '../../dev-warning';
|
|
4
|
+
|
|
5
|
+
// We currently do not support nested scroll containers
|
|
6
|
+
// But will hopefully support this soon!
|
|
7
|
+
export default (scrollable: ?Element) => {
|
|
8
|
+
if (!scrollable) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const anotherScrollParent: ?Element = getClosestScrollable(
|
|
13
|
+
scrollable.parentElement,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!anotherScrollParent) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
warning(`
|
|
21
|
+
Droppable: unsupported nested scroll container detected.
|
|
22
|
+
A Droppable can only have one scroll parent (which can be itself)
|
|
23
|
+
Nested scroll containers are currently not supported.
|
|
24
|
+
|
|
25
|
+
We hope to support nested scroll containers soon: https://github.com/atlassian/react-beautiful-dnd/issues/131
|
|
26
|
+
`);
|
|
27
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import { invariant } from '../../invariant';
|
|
3
|
+
import { warning } from '../../dev-warning';
|
|
4
|
+
import getBodyElement from '../get-body-element';
|
|
5
|
+
|
|
6
|
+
type Overflow = {|
|
|
7
|
+
overflowX: string,
|
|
8
|
+
overflowY: string,
|
|
9
|
+
|};
|
|
10
|
+
|
|
11
|
+
const isEqual = (base: string) => (value: string): boolean => base === value;
|
|
12
|
+
const isScroll = isEqual('scroll');
|
|
13
|
+
const isAuto = isEqual('auto');
|
|
14
|
+
const isVisible = isEqual('visible');
|
|
15
|
+
const isEither = (overflow: Overflow, fn: (value: string) => boolean) =>
|
|
16
|
+
fn(overflow.overflowX) || fn(overflow.overflowY);
|
|
17
|
+
const isBoth = (overflow: Overflow, fn: (value: string) => boolean) =>
|
|
18
|
+
fn(overflow.overflowX) && fn(overflow.overflowY);
|
|
19
|
+
|
|
20
|
+
const isElementScrollable = (el: Element): boolean => {
|
|
21
|
+
const style: CSSStyleDeclaration = window.getComputedStyle(el);
|
|
22
|
+
const overflow: Overflow = {
|
|
23
|
+
overflowX: style.overflowX,
|
|
24
|
+
overflowY: style.overflowY,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return isEither(overflow, isScroll) || isEither(overflow, isAuto);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Special case for a body element
|
|
31
|
+
// Playground: https://codepen.io/alexreardon/pen/ZmyLgX?editors=1111
|
|
32
|
+
const isBodyScrollable = (): boolean => {
|
|
33
|
+
// Because we always return false for now, we can skip any actual processing in production
|
|
34
|
+
if (process.env.NODE_ENV === 'production') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const body: HTMLBodyElement = getBodyElement();
|
|
39
|
+
const html: ?HTMLElement = document.documentElement;
|
|
40
|
+
invariant(html);
|
|
41
|
+
|
|
42
|
+
// 1. The `body` has `overflow-[x|y]: auto | scroll`
|
|
43
|
+
if (!isElementScrollable(body)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const htmlStyle: CSSStyleDeclaration = window.getComputedStyle(html);
|
|
48
|
+
const htmlOverflow: Overflow = {
|
|
49
|
+
overflowX: htmlStyle.overflowX,
|
|
50
|
+
overflowY: htmlStyle.overflowY,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (isBoth(htmlOverflow, isVisible)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
warning(`
|
|
58
|
+
We have detected that your <body> element might be a scroll container.
|
|
59
|
+
We have found no reliable way of detecting whether the <body> element is a scroll container.
|
|
60
|
+
Under most circumstances a <body> scroll bar will be on the <html> element (document.documentElement)
|
|
61
|
+
|
|
62
|
+
Because we cannot determine if the <body> is a scroll container, and generally it is not one,
|
|
63
|
+
we will be treating the <body> as *not* a scroll container
|
|
64
|
+
|
|
65
|
+
More information: https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/how-we-detect-scroll-containers.md
|
|
66
|
+
`);
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const getClosestScrollable = (el: ?Element): ?Element => {
|
|
71
|
+
// cannot do anything else!
|
|
72
|
+
if (el == null) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// not allowing us to go higher then body
|
|
77
|
+
if (el === document.body) {
|
|
78
|
+
return isBodyScrollable() ? el : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Should never get here, but just being safe
|
|
82
|
+
if (el === document.documentElement) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!isElementScrollable(el)) {
|
|
87
|
+
// keep recursing
|
|
88
|
+
return getClosestScrollable(el.parentElement);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// success!
|
|
92
|
+
return el;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default getClosestScrollable;
|