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