180 lines
6.7 KiB
JavaScript
180 lines
6.7 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.findScrollableParent = exports.getScrollbarWidth = exports.enableBodyScroll = exports.disableBodyScroll = exports.allowOverscrollOnElement = exports.allowScrollOnElement = exports.DATA_IS_SCROLLABLE_ATTRIBUTE = void 0;
|
|
var getDocument_1 = require("./dom/getDocument");
|
|
var merge_styles_1 = require("@fluentui/merge-styles");
|
|
var getWindow_1 = require("./dom/getWindow");
|
|
var _scrollbarWidth;
|
|
var _bodyScrollDisabledCount = 0;
|
|
var DisabledScrollClassName = (0, merge_styles_1.mergeStyles)({
|
|
overflow: 'hidden !important',
|
|
});
|
|
/**
|
|
* Placing this attribute on scrollable divs optimizes detection to know
|
|
* if the div is scrollable or not (given we can avoid expensive operations
|
|
* like getComputedStyle.)
|
|
*
|
|
* @public
|
|
*/
|
|
exports.DATA_IS_SCROLLABLE_ATTRIBUTE = 'data-is-scrollable';
|
|
/**
|
|
* Allows the user to scroll within a element,
|
|
* while preventing the user from scrolling the body
|
|
*/
|
|
var allowScrollOnElement = function (element, events) {
|
|
var window = (0, getWindow_1.getWindow)(element);
|
|
if (!element || !window) {
|
|
return;
|
|
}
|
|
var _previousClientY = 0;
|
|
var _element = null;
|
|
var computedStyles = window.getComputedStyle(element);
|
|
// remember the clientY for future calls of _preventOverscrolling
|
|
var _saveClientY = function (event) {
|
|
if (event.targetTouches.length === 1) {
|
|
_previousClientY = event.targetTouches[0].clientY;
|
|
}
|
|
};
|
|
// prevent the body from scrolling when the user attempts
|
|
// to scroll past the top or bottom of the element
|
|
var _preventOverscrolling = function (event) {
|
|
// only respond to a single-finger touch
|
|
if (event.targetTouches.length !== 1) {
|
|
return;
|
|
}
|
|
// prevent the body touchmove handler from firing
|
|
// so that scrolling is allowed within the element
|
|
event.stopPropagation();
|
|
if (!_element) {
|
|
return;
|
|
}
|
|
var clientY = event.targetTouches[0].clientY - _previousClientY;
|
|
var scrollableParent = findScrollableParent(event.target);
|
|
if (scrollableParent && _element !== scrollableParent) {
|
|
_element = scrollableParent;
|
|
computedStyles = window.getComputedStyle(_element);
|
|
}
|
|
var scrollTop = _element.scrollTop;
|
|
var isColumnReverse = (computedStyles === null || computedStyles === void 0 ? void 0 : computedStyles.flexDirection) === 'column-reverse';
|
|
// if the element is scrolled to the top,
|
|
// prevent the user from scrolling up
|
|
if (scrollTop === 0 && (isColumnReverse ? clientY < 0 : clientY > 0)) {
|
|
event.preventDefault();
|
|
}
|
|
// if the element is scrolled to the bottom,
|
|
// prevent the user from scrolling down
|
|
if (_element.scrollHeight - Math.abs(Math.ceil(scrollTop)) <= _element.clientHeight &&
|
|
(isColumnReverse ? clientY > 0 : clientY < 0)) {
|
|
event.preventDefault();
|
|
}
|
|
};
|
|
events.on(element, 'touchstart', _saveClientY, { passive: false });
|
|
events.on(element, 'touchmove', _preventOverscrolling, { passive: false });
|
|
_element = element;
|
|
};
|
|
exports.allowScrollOnElement = allowScrollOnElement;
|
|
/**
|
|
* Same as allowScrollOnElement but does not prevent overscrolling.
|
|
*/
|
|
var allowOverscrollOnElement = function (element, events) {
|
|
if (!element) {
|
|
return;
|
|
}
|
|
var _allowElementScroll = function (event) {
|
|
event.stopPropagation();
|
|
};
|
|
events.on(element, 'touchmove', _allowElementScroll, { passive: false });
|
|
};
|
|
exports.allowOverscrollOnElement = allowOverscrollOnElement;
|
|
var _disableIosBodyScroll = function (event) {
|
|
event.preventDefault();
|
|
};
|
|
/**
|
|
* Disables the body scrolling.
|
|
*
|
|
* @public
|
|
*/
|
|
function disableBodyScroll() {
|
|
var doc = (0, getDocument_1.getDocument)();
|
|
if (doc && doc.body && !_bodyScrollDisabledCount) {
|
|
doc.body.classList.add(DisabledScrollClassName);
|
|
doc.body.addEventListener('touchmove', _disableIosBodyScroll, { passive: false, capture: false });
|
|
}
|
|
_bodyScrollDisabledCount++;
|
|
}
|
|
exports.disableBodyScroll = disableBodyScroll;
|
|
/**
|
|
* Enables the body scrolling.
|
|
*
|
|
* @public
|
|
*/
|
|
function enableBodyScroll() {
|
|
if (_bodyScrollDisabledCount > 0) {
|
|
var doc = (0, getDocument_1.getDocument)();
|
|
if (doc && doc.body && _bodyScrollDisabledCount === 1) {
|
|
doc.body.classList.remove(DisabledScrollClassName);
|
|
doc.body.removeEventListener('touchmove', _disableIosBodyScroll);
|
|
}
|
|
_bodyScrollDisabledCount--;
|
|
}
|
|
}
|
|
exports.enableBodyScroll = enableBodyScroll;
|
|
/**
|
|
* Calculates the width of a scrollbar for the browser/os.
|
|
*
|
|
* @public
|
|
*/
|
|
function getScrollbarWidth(doc) {
|
|
if (_scrollbarWidth === undefined) {
|
|
var theDoc = doc !== null && doc !== void 0 ? doc : (0, getDocument_1.getDocument)();
|
|
var scrollDiv = theDoc.createElement('div');
|
|
scrollDiv.style.setProperty('width', '100px');
|
|
scrollDiv.style.setProperty('height', '100px');
|
|
scrollDiv.style.setProperty('overflow', 'scroll');
|
|
scrollDiv.style.setProperty('position', 'absolute');
|
|
scrollDiv.style.setProperty('top', '-9999px');
|
|
theDoc.body.appendChild(scrollDiv);
|
|
// Get the scrollbar width
|
|
_scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
// Delete the DIV
|
|
theDoc.body.removeChild(scrollDiv);
|
|
}
|
|
return _scrollbarWidth;
|
|
}
|
|
exports.getScrollbarWidth = getScrollbarWidth;
|
|
/**
|
|
* Traverses up the DOM for the element with the data-is-scrollable=true attribute, or returns
|
|
* document.body.
|
|
*
|
|
* @public
|
|
*/
|
|
function findScrollableParent(startingElement) {
|
|
var el = startingElement;
|
|
var doc = (0, getDocument_1.getDocument)(startingElement);
|
|
// First do a quick scan for the scrollable attribute.
|
|
while (el && el !== doc.body) {
|
|
if (el.getAttribute(exports.DATA_IS_SCROLLABLE_ATTRIBUTE) === 'true') {
|
|
return el;
|
|
}
|
|
el = el.parentElement;
|
|
}
|
|
// If we haven't found it, the use the slower method: compute styles to evaluate if overflow is set.
|
|
el = startingElement;
|
|
while (el && el !== doc.body) {
|
|
if (el.getAttribute(exports.DATA_IS_SCROLLABLE_ATTRIBUTE) !== 'false') {
|
|
var computedStyles = getComputedStyle(el);
|
|
var overflowY = computedStyles ? computedStyles.getPropertyValue('overflow-y') : '';
|
|
if (overflowY && (overflowY === 'scroll' || overflowY === 'auto')) {
|
|
return el;
|
|
}
|
|
}
|
|
el = el.parentElement;
|
|
}
|
|
// Fall back to window scroll.
|
|
if (!el || el === doc.body) {
|
|
el = (0, getWindow_1.getWindow)(startingElement);
|
|
}
|
|
return el;
|
|
}
|
|
exports.findScrollableParent = findScrollableParent;
|
|
//# sourceMappingURL=scroll.js.map
|