@rpcbase/client 0.180.0 → 0.181.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.
@@ -0,0 +1,994 @@
1
+ /* @flow */
2
+ /* eslint-disable */
3
+ import * as React from "react"
4
+ import invariant from "invariant"
5
+
6
+ import Manager from "../Manager"
7
+ import {isSortableHandle} from "../SortableHandle"
8
+
9
+ import {
10
+ cloneNode,
11
+ closest,
12
+ events,
13
+ getScrollingParent,
14
+ getContainerGridGap,
15
+ getEdgeOffset,
16
+ getElementMargin,
17
+ getLockPixelOffsets,
18
+ getPosition,
19
+ isTouchEvent,
20
+ limit,
21
+ NodeType,
22
+ omit,
23
+ provideDisplayName,
24
+ setInlineStyles,
25
+ setTransitionDuration,
26
+ setTranslate3d,
27
+ getTargetIndex,
28
+ getScrollAdjustedBoundingClientRect,
29
+ } from "../utils"
30
+
31
+ import AutoScroller from "../AutoScroller"
32
+ import {defaultProps, omittedProps, propTypes, validateProps, defaultKeyCodes} from "./props"
33
+
34
+ export const SortableContext = React.createContext({
35
+ manager: {},
36
+ })
37
+
38
+ export default function SortableContainer(WrappedComponent, config = {withRef: false}) {
39
+ return class WithSortableContainer extends React.Component {
40
+ constructor(props) {
41
+ super(props)
42
+ const manager = new Manager()
43
+
44
+ validateProps(props)
45
+
46
+ this.manager = manager
47
+ this.wrappedInstance = React.createRef()
48
+ this.containerRef = React.createRef()
49
+ this.sortableContextValue = {manager}
50
+ this.events = {
51
+ end: this.handleEnd,
52
+ move: this.handleMove,
53
+ start: this.handleStart,
54
+ }
55
+ }
56
+
57
+ state = {}
58
+
59
+ static displayName = provideDisplayName("sortableList", WrappedComponent)
60
+ static defaultProps = defaultProps
61
+ static propTypes = propTypes
62
+
63
+ componentDidMount() {
64
+ const {useWindowAsScrollContainer} = this.props
65
+
66
+ const container = this.getContainer()
67
+
68
+ Promise.resolve(container).then((containerNode) => {
69
+
70
+ this.container = containerNode
71
+ this.document = this.container.ownerDocument || document
72
+
73
+ /*
74
+ * Set our own default rather than using defaultProps because Jest
75
+ * snapshots will serialize window, causing a RangeError
76
+ * https://github.com/clauderic/react-sortable-hoc/issues/249
77
+ */
78
+ const contentWindow = this.props.contentWindow || this.document.defaultView || window
79
+
80
+ this.contentWindow = typeof contentWindow === "function" ? contentWindow() : contentWindow
81
+
82
+ this.scrollContainer = useWindowAsScrollContainer
83
+ ? this.document.scrollingElement || this.document.documentElement
84
+ : getScrollingParent(this.container) || this.container
85
+
86
+ this.autoScroller = new AutoScroller(this.scrollContainer, this.onAutoScroll)
87
+
88
+ Object.keys(this.events).forEach((key) =>
89
+ events[key].forEach((eventName) =>
90
+ this.container.addEventListener(eventName, this.events[key], {passive: false}),
91
+ ),
92
+ )
93
+
94
+ this.container.addEventListener("keydown", this.handleKeyDown)
95
+ })
96
+ }
97
+
98
+ componentWillUnmount() {
99
+ if (this.helper && this.helper.parentNode) {
100
+ this.helper.parentNode.removeChild(this.helper)
101
+ }
102
+ if (!this.container) {
103
+ return
104
+ }
105
+
106
+ Object.keys(this.events).forEach((key) =>
107
+ events[key].forEach((eventName) =>
108
+ this.container.removeEventListener(eventName, this.events[key]),
109
+ ),
110
+ )
111
+ this.container.removeEventListener("keydown", this.handleKeyDown)
112
+ }
113
+
114
+ handleStart = (event) => {
115
+ const {distance, shouldCancelStart} = this.props
116
+
117
+ if (event.button === 2 || shouldCancelStart(event)) {
118
+ return
119
+ }
120
+
121
+ this.touched = true
122
+ this.position = getPosition(event)
123
+
124
+ const node = closest(event.target, (el) => el.sortableInfo != null)
125
+
126
+ if (node && node.sortableInfo && this.nodeIsChild(node) && !this.state.sorting) {
127
+ const {useDragHandle} = this.props
128
+ const {index, collection, disabled} = node.sortableInfo
129
+
130
+ if (disabled) {
131
+ return
132
+ }
133
+
134
+ if (useDragHandle && !closest(event.target, isSortableHandle)) {
135
+ return
136
+ }
137
+
138
+ this.manager.active = {collection, index}
139
+
140
+ /*
141
+ * Fixes a bug in Firefox where the :active state of anchor tags
142
+ * prevent subsequent 'mousemove' events from being fired
143
+ * (see https://github.com/clauderic/react-sortable-hoc/issues/118)
144
+ */
145
+ if (!isTouchEvent(event) && event.target.tagName === NodeType.Anchor) {
146
+ // console.log("event.preventDefault()")
147
+ event.preventDefault()
148
+ }
149
+
150
+ if (!distance) {
151
+ if (this.props.pressDelay === 0) {
152
+ this.handlePress(event)
153
+ } else {
154
+ this.pressTimer = setTimeout(() => this.handlePress(event), this.props.pressDelay)
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ nodeIsChild = (node) => {
161
+ return node.sortableInfo.manager === this.manager
162
+ }
163
+
164
+ handleMove = (event) => {
165
+ const {distance, pressThreshold} = this.props
166
+
167
+ if (!this.state.sorting && this.touched && !this._awaitingUpdateBeforeSortStart) {
168
+ const position = getPosition(event)
169
+ const delta = {
170
+ x: this.position.x - position.x,
171
+ y: this.position.y - position.y,
172
+ }
173
+ const combinedDelta = Math.abs(delta.x) + Math.abs(delta.y)
174
+
175
+ this.delta = delta
176
+
177
+ if (!distance && (!pressThreshold || combinedDelta >= pressThreshold)) {
178
+ clearTimeout(this.cancelTimer)
179
+ this.cancelTimer = setTimeout(this.cancel, 0)
180
+ } else if (distance && combinedDelta >= distance && this.manager.isActive()) {
181
+ this.handlePress(event)
182
+ }
183
+ }
184
+ }
185
+
186
+ handleEnd = () => {
187
+ this.touched = false
188
+ this.cancel()
189
+ }
190
+
191
+ cancel = () => {
192
+ const {distance} = this.props
193
+ const {sorting} = this.state
194
+
195
+ if (!sorting) {
196
+ if (!distance) {
197
+ clearTimeout(this.pressTimer)
198
+ }
199
+ this.manager.active = null
200
+ }
201
+ }
202
+
203
+ handlePress = async (event) => {
204
+ const active = this.manager.getActive()
205
+
206
+ if (active) {
207
+ const {
208
+ axis,
209
+ getHelperDimensions,
210
+ helperClass,
211
+ hideSortableGhost,
212
+ updateBeforeSortStart,
213
+ onSortStart,
214
+ useWindowAsScrollContainer,
215
+ } = this.props
216
+ const {node, collection} = active
217
+ const {isKeySorting} = this.manager
218
+
219
+ if (typeof updateBeforeSortStart === "function") {
220
+ this._awaitingUpdateBeforeSortStart = true
221
+
222
+ try {
223
+ const {index} = node.sortableInfo
224
+ await updateBeforeSortStart({collection, index, node, isKeySorting}, event)
225
+ } finally {
226
+ this._awaitingUpdateBeforeSortStart = false
227
+ }
228
+ }
229
+
230
+ // Need to get the latest value for `index` in case it changes during `updateBeforeSortStart`
231
+ const {index} = node.sortableInfo
232
+ const margin = getElementMargin(node)
233
+ const gridGap = getContainerGridGap(this.container)
234
+ const containerBoundingRect = this.scrollContainer.getBoundingClientRect()
235
+ const dimensions = getHelperDimensions({index, node, collection})
236
+
237
+ this.node = node
238
+ this.margin = margin
239
+ this.gridGap = gridGap
240
+ this.width = dimensions.width
241
+ this.height = dimensions.height
242
+ this.marginOffset = {
243
+ x: this.margin.left + this.margin.right + this.gridGap.x,
244
+ y: Math.max(this.margin.top, this.margin.bottom, this.gridGap.y),
245
+ }
246
+ this.boundingClientRect = node.getBoundingClientRect()
247
+ this.containerBoundingRect = containerBoundingRect
248
+ this.index = index
249
+ this.newIndex = index
250
+
251
+ this.axis = {
252
+ x: axis.indexOf("x") >= 0,
253
+ y: axis.indexOf("y") >= 0,
254
+ }
255
+ this.offsetEdge = getEdgeOffset(node, this.container)
256
+
257
+ if (isKeySorting) {
258
+ this.initialOffset = getPosition({
259
+ ...event,
260
+ pageX: this.boundingClientRect.left,
261
+ pageY: this.boundingClientRect.top,
262
+ })
263
+ } else {
264
+ this.initialOffset = getPosition(event)
265
+ }
266
+
267
+ this.initialScroll = {
268
+ left: this.scrollContainer.scrollLeft,
269
+ top: this.scrollContainer.scrollTop,
270
+ }
271
+
272
+ this.initialWindowScroll = {
273
+ left: window.pageXOffset,
274
+ top: window.pageYOffset,
275
+ }
276
+
277
+ this.helper = this.helperContainer.appendChild(cloneNode(node))
278
+
279
+ setInlineStyles(this.helper, {
280
+ boxSizing: "border-box",
281
+ height: `${this.height}px`,
282
+ left: `${this.boundingClientRect.left - margin.left}px`,
283
+ pointerEvents: "none",
284
+ position: "fixed",
285
+ top: `${this.boundingClientRect.top - margin.top}px`,
286
+ width: `${this.width}px`,
287
+ zIndex: 4000,
288
+ })
289
+
290
+ if (isKeySorting) {
291
+ this.helper.focus()
292
+ }
293
+
294
+ if (hideSortableGhost) {
295
+ this.sortableGhost = node
296
+
297
+ setInlineStyles(node, {
298
+ opacity: 0,
299
+ visibility: "hidden",
300
+ })
301
+ }
302
+
303
+ this.minTranslate = {}
304
+ this.maxTranslate = {}
305
+
306
+ if (isKeySorting) {
307
+ const {
308
+ top: containerTop,
309
+ left: containerLeft,
310
+ width: containerWidth,
311
+ height: containerHeight,
312
+ } = useWindowAsScrollContainer
313
+ ? {
314
+ top: 0,
315
+ left: 0,
316
+ width: this.contentWindow.innerWidth,
317
+ height: this.contentWindow.innerHeight,
318
+ }
319
+ : this.containerBoundingRect
320
+ const containerBottom = containerTop + containerHeight
321
+ const containerRight = containerLeft + containerWidth
322
+
323
+ if (this.axis.x) {
324
+ this.minTranslate.x = containerLeft - this.boundingClientRect.left
325
+ this.maxTranslate.x = containerRight - (this.boundingClientRect.left + this.width)
326
+ }
327
+
328
+ if (this.axis.y) {
329
+ this.minTranslate.y = containerTop - this.boundingClientRect.top
330
+ this.maxTranslate.y = containerBottom - (this.boundingClientRect.top + this.height)
331
+ }
332
+ } else {
333
+ if (this.axis.x) {
334
+ this.minTranslate.x =
335
+ (useWindowAsScrollContainer ? 0 : containerBoundingRect.left) -
336
+ this.boundingClientRect.left -
337
+ this.width / 2
338
+ this.maxTranslate.x =
339
+ (useWindowAsScrollContainer
340
+ ? this.contentWindow.innerWidth
341
+ : containerBoundingRect.left + containerBoundingRect.width) -
342
+ this.boundingClientRect.left -
343
+ this.width / 2
344
+ }
345
+
346
+ if (this.axis.y) {
347
+ this.minTranslate.y =
348
+ (useWindowAsScrollContainer ? 0 : containerBoundingRect.top) -
349
+ this.boundingClientRect.top -
350
+ this.height / 2
351
+ this.maxTranslate.y =
352
+ (useWindowAsScrollContainer
353
+ ? this.contentWindow.innerHeight
354
+ : containerBoundingRect.top + containerBoundingRect.height) -
355
+ this.boundingClientRect.top -
356
+ this.height / 2
357
+ }
358
+ }
359
+
360
+ if (helperClass) {
361
+ helperClass.split(" ").forEach((className) => this.helper.classList.add(className))
362
+ }
363
+
364
+ this.listenerNode = event.touches ? event.target : this.contentWindow
365
+
366
+ if (isKeySorting) {
367
+ this.listenerNode.addEventListener("wheel", this.handleKeyEnd, true)
368
+ this.listenerNode.addEventListener("mousedown", this.handleKeyEnd, true)
369
+ this.listenerNode.addEventListener("keydown", this.handleKeyDown)
370
+ } else {
371
+ events.move.forEach((eventName) =>
372
+ this.listenerNode.addEventListener(eventName, this.handleSortMove, false),
373
+ )
374
+ events.end.forEach((eventName) =>
375
+ this.listenerNode.addEventListener(eventName, this.handleSortEnd, false),
376
+ )
377
+ }
378
+
379
+ this.setState({
380
+ sorting: true,
381
+ sortingIndex: index,
382
+ })
383
+
384
+ if (onSortStart) {
385
+ onSortStart(
386
+ {
387
+ node,
388
+ index,
389
+ collection,
390
+ isKeySorting,
391
+ nodes: this.manager.getOrderedRefs(),
392
+ helper: this.helper,
393
+ },
394
+ event,
395
+ )
396
+ }
397
+
398
+ if (isKeySorting) {
399
+ // Readjust positioning in case re-rendering occurs onSortStart
400
+ this.keyMove(0)
401
+ }
402
+ }
403
+ }
404
+
405
+ handleSortMove = (event) => {
406
+ const {onSortMove} = this.props
407
+
408
+ // Prevent scrolling on mobile
409
+ if (typeof event.preventDefault === "function" && event.cancelable) {
410
+ event.preventDefault()
411
+ }
412
+
413
+ this.updateHelperPosition(event)
414
+ this.animateNodes()
415
+ this.autoscroll()
416
+
417
+ if (onSortMove) {
418
+ onSortMove(event)
419
+ }
420
+ }
421
+
422
+ handleSortEnd = (event) => {
423
+ const {hideSortableGhost, onSortEnd} = this.props
424
+ const {
425
+ active: {collection},
426
+ isKeySorting,
427
+ } = this.manager
428
+ const nodes = this.manager.getOrderedRefs()
429
+
430
+ // Remove the event listeners if the node is still in the DOM
431
+ if (this.listenerNode) {
432
+ if (isKeySorting) {
433
+ this.listenerNode.removeEventListener("wheel", this.handleKeyEnd, true)
434
+ this.listenerNode.removeEventListener("mousedown", this.handleKeyEnd, true)
435
+ this.listenerNode.removeEventListener("keydown", this.handleKeyDown)
436
+ } else {
437
+ events.move.forEach((eventName) =>
438
+ this.listenerNode.removeEventListener(eventName, this.handleSortMove),
439
+ )
440
+ events.end.forEach((eventName) =>
441
+ this.listenerNode.removeEventListener(eventName, this.handleSortEnd),
442
+ )
443
+ }
444
+ }
445
+
446
+ // Remove the helper from the DOM
447
+ this.helper.parentNode.removeChild(this.helper)
448
+
449
+ if (hideSortableGhost && this.sortableGhost) {
450
+ setInlineStyles(this.sortableGhost, {
451
+ opacity: "",
452
+ visibility: "",
453
+ })
454
+ }
455
+
456
+ for (let i = 0, len = nodes.length; i < len; i++) {
457
+ const node = nodes[i]
458
+ const el = node.node
459
+
460
+ // Clear the cached offset/boundingClientRect
461
+ node.edgeOffset = null
462
+ node.boundingClientRect = null
463
+
464
+ // Remove the transforms / transitions
465
+ setTranslate3d(el, null)
466
+ setTransitionDuration(el, null)
467
+ node.translate = null
468
+ }
469
+
470
+ // Stop autoscroll
471
+ this.autoScroller.clear()
472
+
473
+ // Update manager state
474
+ this.manager.active = null
475
+ this.manager.isKeySorting = false
476
+
477
+ this.setState({
478
+ sorting: false,
479
+ sortingIndex: null,
480
+ })
481
+
482
+ if (typeof onSortEnd === "function") {
483
+ onSortEnd(
484
+ {
485
+ collection,
486
+ newIndex: this.newIndex,
487
+ oldIndex: this.index,
488
+ isKeySorting,
489
+ nodes,
490
+ },
491
+ event,
492
+ )
493
+ }
494
+
495
+ this.touched = false
496
+ }
497
+
498
+ updateHelperPosition(event) {
499
+ const {
500
+ lockAxis,
501
+ lockOffset,
502
+ lockToContainerEdges,
503
+ transitionDuration,
504
+ keyboardSortingTransitionDuration = transitionDuration,
505
+ } = this.props
506
+ const {isKeySorting} = this.manager
507
+ const {ignoreTransition} = event
508
+
509
+ const offset = getPosition(event)
510
+ const translate = {
511
+ x: offset.x - this.initialOffset.x,
512
+ y: offset.y - this.initialOffset.y,
513
+ }
514
+
515
+ // Adjust for window scroll
516
+ translate.y -= window.pageYOffset - this.initialWindowScroll.top
517
+ translate.x -= window.pageXOffset - this.initialWindowScroll.left
518
+
519
+ this.translate = translate
520
+
521
+ if (lockToContainerEdges) {
522
+ const [minLockOffset, maxLockOffset] = getLockPixelOffsets({
523
+ height: this.height,
524
+ lockOffset,
525
+ width: this.width,
526
+ })
527
+ const minOffset = {
528
+ x: this.width / 2 - minLockOffset.x,
529
+ y: this.height / 2 - minLockOffset.y,
530
+ }
531
+ const maxOffset = {
532
+ x: this.width / 2 - maxLockOffset.x,
533
+ y: this.height / 2 - maxLockOffset.y,
534
+ }
535
+
536
+ translate.x = limit(
537
+ this.minTranslate.x + minOffset.x,
538
+ this.maxTranslate.x - maxOffset.x,
539
+ translate.x,
540
+ )
541
+ translate.y = limit(
542
+ this.minTranslate.y + minOffset.y,
543
+ this.maxTranslate.y - maxOffset.y,
544
+ translate.y,
545
+ )
546
+ }
547
+
548
+ if (lockAxis === "x") {
549
+ translate.y = 0
550
+ } else if (lockAxis === "y") {
551
+ translate.x = 0
552
+ }
553
+
554
+ if (isKeySorting && keyboardSortingTransitionDuration && !ignoreTransition) {
555
+ setTransitionDuration(this.helper, keyboardSortingTransitionDuration)
556
+ }
557
+
558
+ setTranslate3d(this.helper, translate)
559
+ }
560
+
561
+ animateNodes() {
562
+ const {transitionDuration, hideSortableGhost, onSortOver} = this.props
563
+ const {containerScrollDelta, windowScrollDelta} = this
564
+ const nodes = this.manager.getOrderedRefs()
565
+ const sortingOffset = {
566
+ left: this.offsetEdge.left + this.translate.x + containerScrollDelta.left,
567
+ top: this.offsetEdge.top + this.translate.y + containerScrollDelta.top,
568
+ }
569
+ const {isKeySorting} = this.manager
570
+
571
+ const prevIndex = this.newIndex
572
+ this.newIndex = null
573
+
574
+ for (let i = 0, len = nodes.length; i < len; i++) {
575
+ const {node} = nodes[i]
576
+ const {index} = node.sortableInfo
577
+ const width = node.offsetWidth
578
+ const height = node.offsetHeight
579
+ const offset = {
580
+ height: this.height > height ? height / 2 : this.height / 2,
581
+ width: this.width > width ? width / 2 : this.width / 2,
582
+ }
583
+
584
+ // For keyboard sorting, we want user input to dictate the position of the nodes
585
+ const mustShiftBackward = isKeySorting && index > this.index && index <= prevIndex
586
+ const mustShiftForward = isKeySorting && index < this.index && index >= prevIndex
587
+
588
+ const translate = {
589
+ x: 0,
590
+ y: 0,
591
+ }
592
+ let {edgeOffset} = nodes[i]
593
+
594
+ // If we haven't cached the node's offsetTop / offsetLeft value
595
+ if (!edgeOffset) {
596
+ edgeOffset = getEdgeOffset(node, this.container)
597
+ nodes[i].edgeOffset = edgeOffset
598
+ // While we're at it, cache the boundingClientRect, used during keyboard sorting
599
+ if (isKeySorting) {
600
+ nodes[i].boundingClientRect = getScrollAdjustedBoundingClientRect(
601
+ node,
602
+ containerScrollDelta,
603
+ )
604
+ }
605
+ }
606
+
607
+ // Get a reference to the next and previous node
608
+ const nextNode = i < nodes.length - 1 && nodes[i + 1]
609
+ const prevNode = i > 0 && nodes[i - 1]
610
+
611
+ // Also cache the next node's edge offset if needed.
612
+ // We need this for calculating the animation in a grid setup
613
+ if (nextNode && !nextNode.edgeOffset) {
614
+ nextNode.edgeOffset = getEdgeOffset(nextNode.node, this.container)
615
+ if (isKeySorting) {
616
+ nextNode.boundingClientRect = getScrollAdjustedBoundingClientRect(
617
+ nextNode.node,
618
+ containerScrollDelta,
619
+ )
620
+ }
621
+ }
622
+
623
+ // If the node is the one we're currently animating, skip it
624
+ if (index === this.index) {
625
+ if (hideSortableGhost) {
626
+ /*
627
+ * With windowing libraries such as `react-virtualized`, the sortableGhost
628
+ * node may change while scrolling down and then back up (or vice-versa),
629
+ * so we need to update the reference to the new node just to be safe.
630
+ */
631
+ this.sortableGhost = node
632
+
633
+ setInlineStyles(node, {
634
+ opacity: 0,
635
+ visibility: "hidden",
636
+ })
637
+ }
638
+ continue
639
+ }
640
+
641
+ if (transitionDuration) {
642
+ setTransitionDuration(node, transitionDuration)
643
+ }
644
+
645
+ if (this.axis.x) {
646
+ if (this.axis.y) {
647
+ // Calculations for a grid setup
648
+ if (
649
+ mustShiftForward ||
650
+ (index < this.index &&
651
+ ((sortingOffset.left + windowScrollDelta.left - offset.width <= edgeOffset.left &&
652
+ sortingOffset.top + windowScrollDelta.top <= edgeOffset.top + offset.height) ||
653
+ sortingOffset.top + windowScrollDelta.top + offset.height <= edgeOffset.top))
654
+ ) {
655
+ // If the current node is to the left on the same row, or above the node that's being dragged
656
+ // then move it to the right
657
+ translate.x = this.width + this.marginOffset.x
658
+ if (
659
+ edgeOffset.left + translate.x >
660
+ this.containerBoundingRect.width - offset.width * 2
661
+ ) {
662
+ // If it moves passed the right bounds, then animate it to the first position of the next row.
663
+ // We just use the offset of the next node to calculate where to move, because that node's original position
664
+ // is exactly where we want to go
665
+ if (nextNode) {
666
+ translate.x = nextNode.edgeOffset.left - edgeOffset.left
667
+ translate.y = nextNode.edgeOffset.top - edgeOffset.top
668
+ }
669
+ }
670
+ if (this.newIndex === null) {
671
+ this.newIndex = index
672
+ }
673
+ } else if (
674
+ mustShiftBackward ||
675
+ (index > this.index &&
676
+ ((sortingOffset.left + windowScrollDelta.left + offset.width >= edgeOffset.left &&
677
+ sortingOffset.top + windowScrollDelta.top + offset.height >= edgeOffset.top) ||
678
+ sortingOffset.top + windowScrollDelta.top + offset.height >=
679
+ edgeOffset.top + height))
680
+ ) {
681
+ // If the current node is to the right on the same row, or below the node that's being dragged
682
+ // then move it to the left
683
+ translate.x = -(this.width + this.marginOffset.x)
684
+ if (edgeOffset.left + translate.x < this.containerBoundingRect.left + offset.width) {
685
+ // If it moves passed the left bounds, then animate it to the last position of the previous row.
686
+ // We just use the offset of the previous node to calculate where to move, because that node's original position
687
+ // is exactly where we want to go
688
+ if (prevNode) {
689
+ translate.x = prevNode.edgeOffset.left - edgeOffset.left
690
+ translate.y = prevNode.edgeOffset.top - edgeOffset.top
691
+ }
692
+ }
693
+ this.newIndex = index
694
+ }
695
+ } else {
696
+ if (
697
+ mustShiftBackward ||
698
+ (index > this.index &&
699
+ sortingOffset.left + windowScrollDelta.left + offset.width >= edgeOffset.left)
700
+ ) {
701
+ translate.x = -(this.width + this.marginOffset.x)
702
+ this.newIndex = index
703
+ } else if (
704
+ mustShiftForward ||
705
+ (index < this.index &&
706
+ sortingOffset.left + windowScrollDelta.left <= edgeOffset.left + offset.width)
707
+ ) {
708
+ translate.x = this.width + this.marginOffset.x
709
+
710
+ if (this.newIndex == null) {
711
+ this.newIndex = index
712
+ }
713
+ }
714
+ }
715
+ } else if (this.axis.y) {
716
+ if (
717
+ mustShiftBackward ||
718
+ (index > this.index &&
719
+ sortingOffset.top + windowScrollDelta.top + offset.height >= edgeOffset.top)
720
+ ) {
721
+ translate.y = -(this.height + this.marginOffset.y)
722
+ this.newIndex = index
723
+ } else if (
724
+ mustShiftForward ||
725
+ (index < this.index &&
726
+ sortingOffset.top + windowScrollDelta.top <= edgeOffset.top + offset.height)
727
+ ) {
728
+ translate.y = this.height + this.marginOffset.y
729
+ if (this.newIndex == null) {
730
+ this.newIndex = index
731
+ }
732
+ }
733
+ }
734
+
735
+ setTranslate3d(node, translate)
736
+ nodes[i].translate = translate
737
+ }
738
+
739
+ if (this.newIndex == null) {
740
+ this.newIndex = this.index
741
+ }
742
+
743
+ if (isKeySorting) {
744
+ // If keyboard sorting, we want the user input to dictate index, not location of the helper
745
+ this.newIndex = prevIndex
746
+ }
747
+
748
+ const oldIndex = isKeySorting ? this.prevIndex : prevIndex
749
+ if (onSortOver && this.newIndex !== oldIndex) {
750
+ onSortOver({
751
+ collection: this.manager.active.collection,
752
+ index: this.index,
753
+ newIndex: this.newIndex,
754
+ oldIndex,
755
+ isKeySorting,
756
+ nodes,
757
+ helper: this.helper,
758
+ })
759
+ }
760
+ }
761
+
762
+ autoscroll = () => {
763
+ const {disableAutoscroll} = this.props
764
+ const {isKeySorting} = this.manager
765
+
766
+ if (disableAutoscroll) {
767
+ this.autoScroller.clear()
768
+ return
769
+ }
770
+
771
+ if (isKeySorting) {
772
+ const translate = {...this.translate}
773
+ let scrollX = 0
774
+ let scrollY = 0
775
+
776
+ if (this.axis.x) {
777
+ translate.x = Math.min(
778
+ this.maxTranslate.x,
779
+ Math.max(this.minTranslate.x, this.translate.x),
780
+ )
781
+ scrollX = this.translate.x - translate.x
782
+ }
783
+
784
+ if (this.axis.y) {
785
+ translate.y = Math.min(
786
+ this.maxTranslate.y,
787
+ Math.max(this.minTranslate.y, this.translate.y),
788
+ )
789
+ scrollY = this.translate.y - translate.y
790
+ }
791
+
792
+ this.translate = translate
793
+ setTranslate3d(this.helper, this.translate)
794
+ this.scrollContainer.scrollLeft += scrollX
795
+ this.scrollContainer.scrollTop += scrollY
796
+
797
+ return
798
+ }
799
+
800
+ this.autoScroller.update({
801
+ height: this.height,
802
+ maxTranslate: this.maxTranslate,
803
+ minTranslate: this.minTranslate,
804
+ translate: this.translate,
805
+ width: this.width,
806
+ })
807
+ }
808
+
809
+ onAutoScroll = (offset) => {
810
+ this.translate.x += offset.left
811
+ this.translate.y += offset.top
812
+
813
+ this.animateNodes()
814
+ }
815
+
816
+ getWrappedInstance() {
817
+ invariant(
818
+ config.withRef,
819
+ "To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableContainer() call",
820
+ )
821
+
822
+ return this.wrappedInstance.current
823
+ }
824
+
825
+ getContainer() {
826
+ const {getContainer} = this.props
827
+
828
+ if (typeof getContainer !== "function") {
829
+ return this.containerRef.current
830
+ }
831
+
832
+ return getContainer(config.withRef ? this.getWrappedInstance() : undefined);
833
+ }
834
+
835
+ handleKeyDown = (event) => {
836
+ const {keyCode} = event
837
+ const {shouldCancelStart, keyCodes: customKeyCodes = {}} = this.props
838
+
839
+ const keyCodes = {
840
+ ...defaultKeyCodes,
841
+ ...customKeyCodes,
842
+ }
843
+
844
+ if (
845
+ (this.manager.active && !this.manager.isKeySorting) ||
846
+ (!this.manager.active &&
847
+ (!keyCodes.lift.includes(keyCode) ||
848
+ shouldCancelStart(event) ||
849
+ !this.isValidSortingTarget(event)))
850
+ ) {
851
+ return
852
+ }
853
+
854
+ event.stopPropagation()
855
+ event.preventDefault()
856
+
857
+ if (keyCodes.lift.includes(keyCode) && !this.manager.active) {
858
+ this.keyLift(event)
859
+ } else if (keyCodes.drop.includes(keyCode) && this.manager.active) {
860
+ this.keyDrop(event)
861
+ } else if (keyCodes.cancel.includes(keyCode)) {
862
+ this.newIndex = this.manager.active.index
863
+ this.keyDrop(event)
864
+ } else if (keyCodes.up.includes(keyCode)) {
865
+ this.keyMove(-1)
866
+ } else if (keyCodes.down.includes(keyCode)) {
867
+ this.keyMove(1)
868
+ }
869
+ }
870
+
871
+ keyLift = (event) => {
872
+ const {target} = event
873
+ const node = closest(target, (el) => el.sortableInfo != null)
874
+ const {index, collection} = node.sortableInfo
875
+
876
+ this.initialFocusedNode = target
877
+
878
+ this.manager.isKeySorting = true
879
+ this.manager.active = {
880
+ index,
881
+ collection,
882
+ }
883
+
884
+ this.handlePress(event)
885
+ }
886
+
887
+ keyMove = (shift) => {
888
+ const nodes = this.manager.getOrderedRefs()
889
+ const {index: lastIndex} = nodes[nodes.length - 1].node.sortableInfo
890
+ const newIndex = this.newIndex + shift
891
+ const prevIndex = this.newIndex
892
+
893
+ if (newIndex < 0 || newIndex > lastIndex) {
894
+ return
895
+ }
896
+
897
+ this.prevIndex = prevIndex
898
+ this.newIndex = newIndex
899
+
900
+ const targetIndex = getTargetIndex(this.newIndex, this.prevIndex, this.index)
901
+ const target = nodes.find(({node}) => node.sortableInfo.index === targetIndex)
902
+ const {node: targetNode} = target
903
+
904
+ const scrollDelta = this.containerScrollDelta
905
+ const targetBoundingClientRect =
906
+ target.boundingClientRect || getScrollAdjustedBoundingClientRect(targetNode, scrollDelta)
907
+ const targetTranslate = target.translate || {x: 0, y: 0}
908
+
909
+ const targetPosition = {
910
+ top: targetBoundingClientRect.top + targetTranslate.y - scrollDelta.top,
911
+ left: targetBoundingClientRect.left + targetTranslate.x - scrollDelta.left,
912
+ }
913
+
914
+ const shouldAdjustForSize = prevIndex < newIndex
915
+ const sizeAdjustment = {
916
+ x: shouldAdjustForSize && this.axis.x ? targetNode.offsetWidth - this.width : 0,
917
+ y: shouldAdjustForSize && this.axis.y ? targetNode.offsetHeight - this.height : 0,
918
+ }
919
+
920
+ this.handleSortMove({
921
+ pageX: targetPosition.left + sizeAdjustment.x,
922
+ pageY: targetPosition.top + sizeAdjustment.y,
923
+ ignoreTransition: shift === 0,
924
+ })
925
+ }
926
+
927
+ keyDrop = (event) => {
928
+ this.handleSortEnd(event)
929
+
930
+ if (this.initialFocusedNode) {
931
+ this.initialFocusedNode.focus()
932
+ }
933
+ }
934
+
935
+ handleKeyEnd = (event) => {
936
+ if (this.manager.active) {
937
+ this.keyDrop(event)
938
+ }
939
+ }
940
+
941
+ isValidSortingTarget = (event) => {
942
+ const {useDragHandle} = this.props
943
+ const {target} = event
944
+ const node = closest(target, (el) => el.sortableInfo != null)
945
+
946
+ return (
947
+ node &&
948
+ node.sortableInfo &&
949
+ !node.sortableInfo.disabled &&
950
+ (useDragHandle ? isSortableHandle(target) : target.sortableInfo)
951
+ )
952
+ }
953
+
954
+ render() {
955
+ const ref = config.withRef ? this.wrappedInstance : this.containerRef; // Attach the ref
956
+
957
+ return (
958
+ <SortableContext.Provider value={this.sortableContextValue}>
959
+ <WrappedComponent ref={ref} {...omit(this.props, omittedProps)} />
960
+ </SortableContext.Provider>
961
+ )
962
+ }
963
+
964
+ get helperContainer() {
965
+ const {helperContainer} = this.props
966
+
967
+ if (typeof helperContainer === "function") {
968
+ return helperContainer()
969
+ }
970
+
971
+ return this.props.helperContainer || this.document.body
972
+ }
973
+
974
+ get containerScrollDelta() {
975
+ const {useWindowAsScrollContainer} = this.props
976
+
977
+ if (useWindowAsScrollContainer) {
978
+ return {left: 0, top: 0}
979
+ }
980
+
981
+ return {
982
+ left: this.scrollContainer.scrollLeft - this.initialScroll.left,
983
+ top: this.scrollContainer.scrollTop - this.initialScroll.top,
984
+ }
985
+ }
986
+
987
+ get windowScrollDelta() {
988
+ return {
989
+ left: this.contentWindow.pageXOffset - this.initialWindowScroll.left,
990
+ top: this.contentWindow.pageYOffset - this.initialWindowScroll.top,
991
+ }
992
+ }
993
+ }
994
+ }