@react-aria/utils 3.14.1 → 3.15.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/dist/import.mjs +1051 -0
- package/dist/main.js +445 -185
- package/dist/main.js.map +1 -1
- package/dist/module.js +444 -186
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -6
- package/src/getScrollParent.ts +5 -1
- package/src/index.ts +2 -2
- package/src/scrollIntoView.ts +52 -6
package/src/scrollIntoView.ts
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import {getScrollParent} from './';
|
|
14
|
+
|
|
15
|
+
interface ScrollIntoViewportOpts {
|
|
16
|
+
/** The optional containing element of the target to be centered in the viewport. */
|
|
17
|
+
containingElement?: Element
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
* Scrolls `scrollView` so that `element` is visible.
|
|
15
22
|
* Similar to `element.scrollIntoView({block: 'nearest'})` (not supported in Edge),
|
|
@@ -22,20 +29,25 @@ export function scrollIntoView(scrollView: HTMLElement, element: HTMLElement) {
|
|
|
22
29
|
let height = element.offsetHeight;
|
|
23
30
|
let x = scrollView.scrollLeft;
|
|
24
31
|
let y = scrollView.scrollTop;
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
|
|
33
|
+
// Account for top/left border offsetting the scroll top/Left
|
|
34
|
+
let {borderTopWidth, borderLeftWidth} = getComputedStyle(scrollView);
|
|
35
|
+
let borderAdjustedX = scrollView.scrollLeft + parseInt(borderLeftWidth, 10);
|
|
36
|
+
let borderAdjustedY = scrollView.scrollTop + parseInt(borderTopWidth, 10);
|
|
37
|
+
// Ignore end/bottom border via clientHeight/Width instead of offsetHeight/Width
|
|
38
|
+
let maxX = borderAdjustedX + scrollView.clientWidth;
|
|
39
|
+
let maxY = borderAdjustedY + scrollView.clientHeight;
|
|
27
40
|
|
|
28
41
|
if (offsetX <= x) {
|
|
29
|
-
x = offsetX;
|
|
42
|
+
x = offsetX - parseInt(borderLeftWidth, 10);
|
|
30
43
|
} else if (offsetX + width > maxX) {
|
|
31
44
|
x += offsetX + width - maxX;
|
|
32
45
|
}
|
|
33
|
-
if (offsetY <=
|
|
34
|
-
y = offsetY;
|
|
46
|
+
if (offsetY <= borderAdjustedY) {
|
|
47
|
+
y = offsetY - parseInt(borderTopWidth, 10);
|
|
35
48
|
} else if (offsetY + height > maxY) {
|
|
36
49
|
y += offsetY + height - maxY;
|
|
37
50
|
}
|
|
38
|
-
|
|
39
51
|
scrollView.scrollLeft = x;
|
|
40
52
|
scrollView.scrollTop = y;
|
|
41
53
|
}
|
|
@@ -63,3 +75,37 @@ function relativeOffset(ancestor: HTMLElement, child: HTMLElement, axis: 'left'|
|
|
|
63
75
|
}
|
|
64
76
|
return sum;
|
|
65
77
|
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Scrolls the `targetElement` so it is visible in the viewport. Accepts an optional `opts.containingElement`
|
|
81
|
+
* that will be centered in the viewport prior to scrolling the targetElement into view. If scrolling is prevented on
|
|
82
|
+
* the body (e.g. targetElement is in a popover), this will only scroll the scroll parents of the targetElement up to but not including the body itself.
|
|
83
|
+
*/
|
|
84
|
+
export function scrollIntoViewport(targetElement: Element, opts?: ScrollIntoViewportOpts) {
|
|
85
|
+
if (document.contains(targetElement)) {
|
|
86
|
+
let root = document.scrollingElement || document.documentElement;
|
|
87
|
+
let isScrollPrevented = window.getComputedStyle(root).overflow === 'hidden';
|
|
88
|
+
// If scrolling is not currently prevented then we aren’t in a overlay nor is a overlay open, just use element.scrollIntoView to bring the element into view
|
|
89
|
+
if (!isScrollPrevented) {
|
|
90
|
+
let {left: originalLeft, top: originalTop} = targetElement.getBoundingClientRect();
|
|
91
|
+
|
|
92
|
+
// use scrollIntoView({block: 'nearest'}) instead of .focus to check if the element is fully in view or not since .focus()
|
|
93
|
+
// won't cause a scroll if the element is already focused and doesn't behave consistently when an element is partially out of view horizontally vs vertically
|
|
94
|
+
targetElement?.scrollIntoView?.({block: 'nearest'});
|
|
95
|
+
let {left: newLeft, top: newTop} = targetElement.getBoundingClientRect();
|
|
96
|
+
// Account for sub pixel differences from rounding
|
|
97
|
+
if ((Math.abs(originalLeft - newLeft) > 1) || (Math.abs(originalTop - newTop) > 1)) {
|
|
98
|
+
opts?.containingElement?.scrollIntoView?.({block: 'center', inline: 'center'});
|
|
99
|
+
targetElement.scrollIntoView?.({block: 'nearest'});
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
let scrollParent = getScrollParent(targetElement);
|
|
103
|
+
// If scrolling is prevented, we don't want to scroll the body since it might move the overlay partially offscreen and the user can't scroll it back into view.
|
|
104
|
+
while (targetElement && scrollParent && targetElement !== root && scrollParent !== root) {
|
|
105
|
+
scrollIntoView(scrollParent as HTMLElement, targetElement as HTMLElement);
|
|
106
|
+
targetElement = scrollParent;
|
|
107
|
+
scrollParent = getScrollParent(targetElement);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|