123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- // Utilities
- import { effectScope, onScopeDispose, watchEffect } from 'vue';
- import { requestNewFrame } from "./requestNewFrame.mjs";
- import { convertToUnit, getScrollParents, hasScrollbar, IN_BROWSER, propsFactory } from "../../util/index.mjs"; // Types
- const scrollStrategies = {
- none: null,
- close: closeScrollStrategy,
- block: blockScrollStrategy,
- reposition: repositionScrollStrategy
- };
- export const makeScrollStrategyProps = propsFactory({
- scrollStrategy: {
- type: [String, Function],
- default: 'block',
- validator: val => typeof val === 'function' || val in scrollStrategies
- }
- }, 'VOverlay-scroll-strategies');
- export function useScrollStrategies(props, data) {
- if (!IN_BROWSER) return;
- let scope;
- watchEffect(async () => {
- scope?.stop();
- if (!(data.isActive.value && props.scrollStrategy)) return;
- scope = effectScope();
- await new Promise(resolve => setTimeout(resolve));
- scope.active && scope.run(() => {
- if (typeof props.scrollStrategy === 'function') {
- props.scrollStrategy(data, props, scope);
- } else {
- scrollStrategies[props.scrollStrategy]?.(data, props, scope);
- }
- });
- });
- onScopeDispose(() => {
- scope?.stop();
- });
- }
- function closeScrollStrategy(data) {
- function onScroll(e) {
- data.isActive.value = false;
- }
- bindScroll(data.targetEl.value ?? data.contentEl.value, onScroll);
- }
- function blockScrollStrategy(data, props) {
- const offsetParent = data.root.value?.offsetParent;
- const scrollElements = [...new Set([...getScrollParents(data.targetEl.value, props.contained ? offsetParent : undefined), ...getScrollParents(data.contentEl.value, props.contained ? offsetParent : undefined)])].filter(el => !el.classList.contains('v-overlay-scroll-blocked'));
- const scrollbarWidth = window.innerWidth - document.documentElement.offsetWidth;
- const scrollableParent = (el => hasScrollbar(el) && el)(offsetParent || document.documentElement);
- if (scrollableParent) {
- data.root.value.classList.add('v-overlay--scroll-blocked');
- }
- scrollElements.forEach((el, i) => {
- el.style.setProperty('--v-body-scroll-x', convertToUnit(-el.scrollLeft));
- el.style.setProperty('--v-body-scroll-y', convertToUnit(-el.scrollTop));
- if (el !== document.documentElement) {
- el.style.setProperty('--v-scrollbar-offset', convertToUnit(scrollbarWidth));
- }
- el.classList.add('v-overlay-scroll-blocked');
- });
- onScopeDispose(() => {
- scrollElements.forEach((el, i) => {
- const x = parseFloat(el.style.getPropertyValue('--v-body-scroll-x'));
- const y = parseFloat(el.style.getPropertyValue('--v-body-scroll-y'));
- const scrollBehavior = el.style.scrollBehavior;
- el.style.scrollBehavior = 'auto';
- el.style.removeProperty('--v-body-scroll-x');
- el.style.removeProperty('--v-body-scroll-y');
- el.style.removeProperty('--v-scrollbar-offset');
- el.classList.remove('v-overlay-scroll-blocked');
- el.scrollLeft = -x;
- el.scrollTop = -y;
- el.style.scrollBehavior = scrollBehavior;
- });
- if (scrollableParent) {
- data.root.value.classList.remove('v-overlay--scroll-blocked');
- }
- });
- }
- function repositionScrollStrategy(data, props, scope) {
- let slow = false;
- let raf = -1;
- let ric = -1;
- function update(e) {
- requestNewFrame(() => {
- const start = performance.now();
- data.updateLocation.value?.(e);
- const time = performance.now() - start;
- slow = time / (1000 / 60) > 2;
- });
- }
- ric = (typeof requestIdleCallback === 'undefined' ? cb => cb() : requestIdleCallback)(() => {
- scope.run(() => {
- bindScroll(data.targetEl.value ?? data.contentEl.value, e => {
- if (slow) {
- // If the position calculation is slow,
- // defer updates until scrolling is finished.
- // Browsers usually fire one scroll event per frame so
- // we just wait until we've got two frames without an event
- cancelAnimationFrame(raf);
- raf = requestAnimationFrame(() => {
- raf = requestAnimationFrame(() => {
- update(e);
- });
- });
- } else {
- update(e);
- }
- });
- });
- });
- onScopeDispose(() => {
- typeof cancelIdleCallback !== 'undefined' && cancelIdleCallback(ric);
- cancelAnimationFrame(raf);
- });
- }
- /** @private */
- function bindScroll(el, onScroll) {
- const scrollElements = [document, ...getScrollParents(el)];
- scrollElements.forEach(el => {
- el.addEventListener('scroll', onScroll, {
- passive: true
- });
- });
- onScopeDispose(() => {
- scrollElements.forEach(el => {
- el.removeEventListener('scroll', onScroll);
- });
- });
- }
- //# sourceMappingURL=scrollStrategies.mjs.map
|