@react-aria/utils
Version:
Spectrum UI components in React
97 lines (84 loc) • 3.07 kB
text/typescript
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {FocusableElement} from '@react-types/shared';
// This is a polyfill for element.focus({preventScroll: true});
// Currently necessary for Safari and old Edge:
// https://caniuse.com/#feat=mdn-api_htmlelement_focus_preventscroll_option
// See https://bugs.webkit.org/show_bug.cgi?id=178583
//
// Original licensing for the following methods can be found in the
// NOTICE file in the root directory of this source tree.
// See https://github.com/calvellido/focus-options-polyfill
interface ScrollableElement {
element: HTMLElement,
scrollTop: number,
scrollLeft: number
}
export function focusWithoutScrolling(element: FocusableElement) {
if (supportsPreventScroll()) {
element.focus({preventScroll: true});
} else {
let scrollableElements = getScrollableElements(element);
element.focus();
restoreScrollPosition(scrollableElements);
}
}
let supportsPreventScrollCached: boolean = null;
function supportsPreventScroll() {
if (supportsPreventScrollCached == null) {
supportsPreventScrollCached = false;
try {
var focusElem = document.createElement('div');
focusElem.focus({
get preventScroll() {
supportsPreventScrollCached = true;
return true;
}
});
} catch (e) {
// Ignore
}
}
return supportsPreventScrollCached;
}
function getScrollableElements(element: FocusableElement): ScrollableElement[] {
var parent = element.parentNode;
var scrollableElements: ScrollableElement[] = [];
var rootScrollingElement = document.scrollingElement || document.documentElement;
while (parent instanceof HTMLElement && parent !== rootScrollingElement) {
if (
parent.offsetHeight < parent.scrollHeight ||
parent.offsetWidth < parent.scrollWidth
) {
scrollableElements.push({
element: parent,
scrollTop: parent.scrollTop,
scrollLeft: parent.scrollLeft
});
}
parent = parent.parentNode;
}
if (rootScrollingElement instanceof HTMLElement) {
scrollableElements.push({
element: rootScrollingElement,
scrollTop: rootScrollingElement.scrollTop,
scrollLeft: rootScrollingElement.scrollLeft
});
}
return scrollableElements;
}
function restoreScrollPosition(scrollableElements: ScrollableElement[]) {
for (let {element, scrollTop, scrollLeft} of scrollableElements) {
element.scrollTop = scrollTop;
element.scrollLeft = scrollLeft;
}
}