From 4d4532f74dee8f1a67e94b69f0f7e966e359b982 Mon Sep 17 00:00:00 2001 From: Rene Haas Date: Wed, 27 Jul 2022 01:11:05 +0200 Subject: [PATCH] add drag scrolling & improve code --- .../scrollbarsSetup.calculations.ts | 27 ++++ .../scrollbarsSetup.elements.ts | 87 ++++++------- .../scrollbarsSetup/scrollbarsSetup.events.ts | 116 ++++++++++++++++++ .../setups/scrollbarsSetup/scrollbarsSetup.ts | 106 +++++++--------- .../structureSetup/structureSetup.elements.ts | 4 + .../src/styles/scrollbars.scss | 3 +- 6 files changed, 233 insertions(+), 110 deletions(-) create mode 100644 packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.calculations.ts create mode 100644 packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.events.ts diff --git a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.calculations.ts b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.calculations.ts new file mode 100644 index 0000000..38a602f --- /dev/null +++ b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.calculations.ts @@ -0,0 +1,27 @@ +import type { StructureSetupState } from 'setups'; + +const { min, max } = Math; +export const getScrollbarHandleLengthRatio = ( + structureSetupState: StructureSetupState, + isHorizontal?: boolean +) => { + const { _overflowAmount, _overflowEdge } = structureSetupState; + const axis = isHorizontal ? 'x' : 'y'; + const viewportSize = _overflowEdge[axis]; + const overflowAmount = _overflowAmount[axis]; + return max(0, min(1, viewportSize / (viewportSize + overflowAmount))); +}; +export const getScrollbarHandleOffsetRatio = ( + structureSetupState: StructureSetupState, + scrollOffsetElement: HTMLElement, + isHorizontal?: boolean +) => { + const axis = isHorizontal ? 'x' : 'y'; + const scrollLeftTop = isHorizontal ? 'Left' : 'Top'; + const lengthRatio = getScrollbarHandleLengthRatio(structureSetupState, isHorizontal); + const scrollPosition = scrollOffsetElement[`scroll${scrollLeftTop}`] as number; + const scrollPositionMax = Math.floor(structureSetupState._overflowAmount[axis]); + const scrollPercent = min(1, scrollPosition / scrollPositionMax); + + return (1 / lengthRatio) * (1 - lengthRatio) * scrollPercent; +}; diff --git a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.elements.ts b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.elements.ts index 672a85b..0f0e905 100644 --- a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.elements.ts +++ b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.elements.ts @@ -3,15 +3,13 @@ import { appendChildren, createDiv, each, + isBoolean, isEmptyArray, - noop, - on, push, removeClass, removeElements, runEachAndClear, setT, - stopPropagation, style, } from 'support'; import { @@ -20,18 +18,18 @@ import { classNameScrollbarVertical, classNameScrollbarTrack, classNameScrollbarHandle, - classNamesScrollbarInteraction, classNamesScrollbarTransitionless, } from 'classnames'; import { getEnvironment } from 'environment'; import { dynamicInitializationElement as generalDynamicInitializationElement } from 'initialization'; import type { InitializationTarget } from 'initialization'; import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements'; +import type { ScrollbarsSetupEvents } from 'setups/scrollbarsSetup/scrollbarsSetup.events'; import type { ScrollbarsInitialization, ScrollbarsDynamicInitializationElement, } from 'setups/scrollbarsSetup/scrollbarsSetup.initialization'; -import { StyleObject } from 'typings'; +import type { StyleObject } from 'typings'; export interface ScrollbarStructure { _scrollbar: HTMLElement; @@ -42,24 +40,19 @@ export interface ScrollbarStructure { export interface ScrollbarsSetupElement { _scrollbarStructures: ScrollbarStructure[]; _clone: () => ScrollbarStructure; - _addRemoveClass: ( - classNames: string | false | null | undefined, - add?: boolean, - elm?: (scrollbarStructure: ScrollbarStructure) => HTMLElement | false | null | undefined - ) => void; _handleStyle: ( elmStyle: ( scrollbarStructure: ScrollbarStructure ) => [HTMLElement | false | null | undefined, StyleObject] ) => void; - // _removeClass: (classNames: string) => void; - /* - _addEventListener: () => void; - _removeEventListener: () => void; - */ } export interface ScrollbarsSetupElementsObj { + _scrollbarsAddRemoveClass: ( + classNames: string | false | null | undefined, + add?: boolean, + isHorizontal?: boolean + ) => void; _horizontal: ScrollbarsSetupElement; _vertical: ScrollbarsSetupElement; } @@ -70,23 +63,15 @@ export type ScrollbarsSetupElements = [ destroy: () => void ]; -const interactionStartEventNames = 'touchstart mouseenter'; -const interactionEndEventNames = 'touchend touchcancel mouseleave'; -const stopRootClickPropagation = (scrollbar: HTMLElement, documentElm: Document) => - on( - scrollbar, - 'mousedown', - on.bind(0, documentElm, 'click', stopPropagation, { _once: true, _capture: true }), - { _capture: true } - ); - export const createScrollbarsSetupElements = ( target: InitializationTarget, - structureSetupElements: StructureSetupElementsObj + structureSetupElements: StructureSetupElementsObj, + scrollbarsSetupEvents: ScrollbarsSetupEvents ): ScrollbarsSetupElements => { const { _getDefaultInitialization } = getEnvironment(); const { scrollbarsSlot: defaultScrollbarsSlot } = _getDefaultInitialization(); - const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements; + const { _documentElm, _target, _host, _viewport, _targetIsElm, _scrollOffsetElement } = + structureSetupElements; const { scrollbarsSlot } = (_targetIsElm ? {} : target) as ScrollbarsInitialization; const evaluatedScrollbarSlot = generalDynamicInitializationElement( @@ -95,15 +80,14 @@ export const createScrollbarsSetupElements = ( defaultScrollbarsSlot, scrollbarsSlot ); - const scrollbarsAddRemoveClass = ( + const scrollbarStructureAddRemoveClass = ( scrollbarStructures: ScrollbarStructure[], classNames: string | false | null | undefined, - add?: boolean, - elm?: (scrollbarStructure: ScrollbarStructure) => HTMLElement | false | null | undefined + add?: boolean ) => { const action = add ? addClass : removeClass; each(scrollbarStructures, (scrollbarStructure) => { - action((elm || noop)(scrollbarStructure) || scrollbarStructure._scrollbar, classNames); + action(scrollbarStructure._scrollbar, classNames); }); }; const scrollbarsHandleStyle = ( @@ -121,13 +105,22 @@ export const createScrollbarsSetupElements = ( const horizontalScrollbars: ScrollbarStructure[] = []; const verticalScrollbars: ScrollbarStructure[] = []; - const addRemoveClassHorizontal = scrollbarsAddRemoveClass.bind(0, horizontalScrollbars); - const addRemoveClassVertical = scrollbarsAddRemoveClass.bind(0, verticalScrollbars); - const generateScrollbarDOM = (horizontal?: boolean): ScrollbarStructure => { - const scrollbarClassName = horizontal + const scrollbarsAddRemoveClass = ( + className: string | false | null | undefined, + add?: boolean, + onlyHorizontal?: boolean + ) => { + const singleAxis = isBoolean(onlyHorizontal); + const runHorizontal = singleAxis ? onlyHorizontal : true; + const runVertical = singleAxis ? !onlyHorizontal : true; + runHorizontal && scrollbarStructureAddRemoveClass(horizontalScrollbars, className, add); + runVertical && scrollbarStructureAddRemoveClass(verticalScrollbars, className, add); + }; + const generateScrollbarDOM = (isHorizontal?: boolean): ScrollbarStructure => { + const scrollbarClassName = isHorizontal ? classNameScrollbarHorizontal : classNameScrollbarVertical; - const arrToPush = horizontal ? horizontalScrollbars : verticalScrollbars; + const arrToPush = isHorizontal ? horizontalScrollbars : verticalScrollbars; const transitionlessClass = isEmptyArray(arrToPush) ? classNamesScrollbarTransitionless : ''; const scrollbar = createDiv( `${classNameScrollbar} ${scrollbarClassName} ${transitionlessClass}` @@ -146,15 +139,13 @@ export const createScrollbarsSetupElements = ( push(arrToPush, result); push(destroyFns, [ removeElements.bind(0, scrollbar), - on(scrollbar, interactionStartEventNames, () => { - addRemoveClassHorizontal(classNamesScrollbarInteraction, true); - addRemoveClassVertical(classNamesScrollbarInteraction, true); - }), - on(scrollbar, interactionEndEventNames, () => { - addRemoveClassHorizontal(classNamesScrollbarInteraction); - addRemoveClassVertical(classNamesScrollbarInteraction); - }), - stopRootClickPropagation(scrollbar, _documentElm), + scrollbarsSetupEvents( + result, + scrollbarsAddRemoveClass, + _documentElm, + _scrollOffsetElement, + isHorizontal + ), ]); return result; @@ -166,8 +157,7 @@ export const createScrollbarsSetupElements = ( appendChildren(evaluatedScrollbarSlot, verticalScrollbars[0]._scrollbar); setT(() => { - addRemoveClassHorizontal(classNamesScrollbarTransitionless); - addRemoveClassVertical(classNamesScrollbarTransitionless); + scrollbarsAddRemoveClass(classNamesScrollbarTransitionless); }, 300); }; @@ -176,16 +166,15 @@ export const createScrollbarsSetupElements = ( return [ { + _scrollbarsAddRemoveClass: scrollbarsAddRemoveClass, _horizontal: { _scrollbarStructures: horizontalScrollbars, _clone: generateHorizontalScrollbarStructure, - _addRemoveClass: addRemoveClassHorizontal, _handleStyle: scrollbarsHandleStyle.bind(0, horizontalScrollbars), }, _vertical: { _scrollbarStructures: verticalScrollbars, _clone: generateVerticalScrollbarStructure, - _addRemoveClass: addRemoveClassVertical, _handleStyle: scrollbarsHandleStyle.bind(0, verticalScrollbars), }, }, diff --git a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.events.ts b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.events.ts new file mode 100644 index 0000000..67bea74 --- /dev/null +++ b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.events.ts @@ -0,0 +1,116 @@ +import { + getBoundingClientRect, + offsetSize, + on, + preventDefault, + runEachAndClear, + stopPropagation, + XY, +} from 'support'; +import { classNamesScrollbarInteraction } from 'classnames'; +import { getScrollbarHandleOffsetRatio } from 'setups/scrollbarsSetup/scrollbarsSetup.calculations'; +import type { StructureSetupState } from 'setups'; +import type { + ScrollbarsSetupElementsObj, + ScrollbarStructure, +} from 'setups/scrollbarsSetup/scrollbarsSetup.elements'; + +export type ScrollbarsSetupEvents = ( + scrollbarStructure: ScrollbarStructure, + scrollbarsAddRemoveClass: ScrollbarsSetupElementsObj['_scrollbarsAddRemoveClass'], + documentElm: Document, + scrollOffsetElm: HTMLElement, + isHorizontal?: boolean +) => () => void; + +const getPageOffset = (event: PointerEvent): XY => ({ + x: event.pageX, + y: event.pageY, +}); +const getInvertedScale = (element: HTMLElement): XY => { + const { width, height } = getBoundingClientRect(element); + const { w, h } = offsetSize(element); + return { + x: 1 / (Math.round(width) / w) || 1, + y: 1 / (Math.round(height) / h) || 1, + }; +}; +const createRootClickStopPropagationEvents = (scrollbar: HTMLElement, documentElm: Document) => + on( + scrollbar, + 'mousedown', + on.bind(0, documentElm, 'click', stopPropagation, { _once: true, _capture: true }), + { _capture: true } + ); +const createDragScrollingEvents = ( + doc: Document, + scrollbarHandle: HTMLElement, + scrollOffsetElement: HTMLElement, + structureSetupState: () => StructureSetupState, + isHorizontal?: boolean +) => { + const scrollOffsetKey = `scroll${isHorizontal ? 'Left' : 'Top'}`; + const xyKey = `${isHorizontal ? 'x' : 'y'}`; + const createOnPointerMoveHandler = + (mouseDownScroll: number, mouseDownPageOffset: number, mouseDownInvertedScale: number) => + (event: PointerEvent) => { + const movement = (getPageOffset(event)[xyKey] - mouseDownPageOffset) * mouseDownInvertedScale; + const handleLengthRatio = + 1 / getScrollbarHandleOffsetRatio(structureSetupState(), scrollOffsetElement, isHorizontal); + scrollOffsetElement[scrollOffsetKey] = mouseDownScroll + movement * handleLengthRatio; + // if (_isRTL && isHorizontal && !_rtlScrollBehavior.i) scrollDelta *= -1; + }; + + return on(scrollbarHandle, 'pointerdown', (pointerDownEvent: PointerEvent) => { + const { button, isPrimary, pointerId } = pointerDownEvent; + + if (button === 0 && isPrimary) { + const mouseDownScroll = scrollOffsetElement[scrollOffsetKey] || 0; + const mouseDownPageOffset = getPageOffset(pointerDownEvent)[xyKey]; + const mouseDownInvertedScale = getInvertedScale(scrollOffsetElement)[xyKey]; + const offSelectStart = on(doc, 'selectstart', (event: Event) => preventDefault(event), { + _passive: false, + }); + const offPointerMove = on( + scrollbarHandle, + 'pointermove', + createOnPointerMoveHandler(mouseDownScroll, mouseDownPageOffset, mouseDownInvertedScale) + ); + + on( + scrollbarHandle, + 'pointerup', + (pointerUpEvent: PointerEvent) => { + offSelectStart(); + offPointerMove(); + scrollbarHandle.releasePointerCapture(pointerUpEvent.pointerId); + }, + { _once: true } + ); + scrollbarHandle.setPointerCapture(pointerId); + } + }); +}; + +export const createScrollbarsSetupEvents = + (structureSetupState: () => StructureSetupState): ScrollbarsSetupEvents => + (scrollbarStructure, scrollbarsAddRemoveClass, documentElm, scrollOffsetElm, isHorizontal) => { + const { _scrollbar, _handle } = scrollbarStructure; + + return runEachAndClear.bind(0, [ + on(_scrollbar, 'pointerenter', () => { + scrollbarsAddRemoveClass(classNamesScrollbarInteraction, true); + }), + on(_scrollbar, 'pointerleave pointercancel', () => { + scrollbarsAddRemoveClass(classNamesScrollbarInteraction); + }), + createRootClickStopPropagationEvents(_scrollbar, documentElm), + createDragScrollingEvents( + documentElm, + _handle, + scrollOffsetElm, + structureSetupState, + isHorizontal + ), + ]); + }; diff --git a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.ts b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.ts index 5adda66..3c56edf 100644 --- a/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.ts +++ b/packages/overlayscrollbars/src/setups/scrollbarsSetup/scrollbarsSetup.ts @@ -11,6 +11,11 @@ import { scrollTop, } from 'support'; import { createState, createOptionCheck } from 'setups/setups'; +import { + getScrollbarHandleLengthRatio, + getScrollbarHandleOffsetRatio, +} from 'setups/scrollbarsSetup/scrollbarsSetup.calculations'; +import { createScrollbarsSetupEvents } from 'setups/scrollbarsSetup/scrollbarsSetup.events'; import { createScrollbarsSetupElements, ScrollbarsSetupElement, @@ -40,7 +45,6 @@ export interface ScrollbarsSetupStaticState { _appendElements: () => void; } -const { min } = Math; const createSelfCancelTimeout = (timeout?: number | (() => number)) => { let id: number; const setTFn = timeout ? setT : rAF!; @@ -55,17 +59,6 @@ const createSelfCancelTimeout = (timeout?: number | (() => number)) => { ] as [timeout: (callback: () => any) => void, clear: () => void]; }; -const getScrollbarHandleRatio = ( - structureSetupState: StructureSetupState, - isHorizontal?: boolean -) => { - const { _overflowAmount, _overflowEdge } = structureSetupState; - const axis = isHorizontal ? 'x' : 'y'; - const viewportSize = _overflowEdge[axis]; - const overflowAmount = _overflowAmount[axis]; - return min(1, viewportSize / (viewportSize + overflowAmount)); -}; - const refreshScrollbarHandleLength = ( setStyleFn: ScrollbarsSetupElement['_handleStyle'], structureSetupState: StructureSetupState, @@ -75,7 +68,7 @@ const refreshScrollbarHandleLength = ( structure._handle, { [isHorizontal ? 'width' : 'height']: `${( - getScrollbarHandleRatio(structureSetupState, isHorizontal) * 100 + getScrollbarHandleLengthRatio(structureSetupState, isHorizontal) * 100 ).toFixed(3)}%`, }, ]); @@ -83,28 +76,23 @@ const refreshScrollbarHandleLength = ( const refreshScrollbarHandleOffset = ( setStyleFn: ScrollbarsSetupElement['_handleStyle'], structureSetupState: StructureSetupState, - viewport: HTMLElement, + scrollOffsetElement: HTMLElement, isHorizontal?: boolean ) => { - const axis = isHorizontal ? 'x' : 'y'; const translateAxis = isHorizontal ? 'X' : 'Y'; - const scrollLeftTop = isHorizontal ? 'Left' : 'Top'; - const handleRatio = getScrollbarHandleRatio(structureSetupState, isHorizontal); - const scrollPosition = viewport[`scroll${scrollLeftTop}`] as number; - const scrollPositionMax = - (viewport[`scroll${scrollLeftTop}Max`] as number) || - Math.floor(structureSetupState._overflowAmount[axis]); + const offsetRatio = getScrollbarHandleOffsetRatio( + structureSetupState, + scrollOffsetElement, + isHorizontal + ); + // eslint-disable-next-line no-self-compare + const validOffsetRatio = offsetRatio === offsetRatio; // is false when offset is NaN setStyleFn((structure) => [ structure._handle, { - transform: scrollPositionMax - ? `translate${translateAxis}(${( - (1 / handleRatio) * - (1 - handleRatio) * - (scrollPosition / scrollPositionMax) * - 100 - ).toFixed(3)}%)` + transform: validOffsetRatio + ? `translate${translateAxis}(${(offsetRatio * 100).toFixed(3)}%)` : '', }, ]); @@ -131,34 +119,38 @@ export const createScrollbarsSetup = ( const [auotHideTimeout, clearAutoTimeout] = createSelfCancelTimeout(() => globalAutoHideDelay); const [elements, appendElements, destroyElements] = createScrollbarsSetupElements( target, - structureSetupState._elements + structureSetupState._elements, + createScrollbarsSetupEvents(structureSetupState) ); - const { _host, _viewport, _viewportIsTarget, _isBody, _documentElm } = - structureSetupState._elements; - const scrollOffsetElement = _isBody ? _documentElm.documentElement : _viewport; - const { _horizontal, _vertical } = elements; - const { _addRemoveClass: addRemoveClassHorizontal, _handleStyle: styleHorizontal } = _horizontal; - const { _addRemoveClass: addRemoveClassVertical, _handleStyle: styleVertical } = _vertical; + const { + _host, + _viewport, + _scrollOffsetElement, + _scrollEventElement, + _viewportIsTarget, + _isBody, + } = structureSetupState._elements; + const { _horizontal, _vertical, _scrollbarsAddRemoveClass: scrollbarsAddRemoveClass } = elements; + const { _handleStyle: styleHorizontal } = _horizontal; + const { _handleStyle: styleVertical } = _vertical; const styleScrollbarPosition = (structure: ScrollbarStructure) => { const { _scrollbar } = structure; const elm = _viewportIsTarget && !_isBody && parent(_scrollbar) === _viewport && _scrollbar; return [ elm, { - transform: elm ? `translate(${scrollLeft(_viewport)}px, ${scrollTop(_viewport)}px)` : '', + transform: elm + ? `translate(${scrollLeft(_scrollOffsetElement)}px, ${scrollTop(_scrollOffsetElement)}px)` + : '', }, ] as [HTMLElement | false, StyleObject]; }; const manageScrollbarsAutoHide = (removeAutoHide: boolean, delayless?: boolean) => { clearAutoTimeout(); if (removeAutoHide) { - addRemoveClassHorizontal(classNamesScrollbarAutoHidden); - addRemoveClassVertical(classNamesScrollbarAutoHidden); + scrollbarsAddRemoveClass(classNamesScrollbarAutoHidden); } else { - const hide = () => { - addRemoveClassHorizontal(classNamesScrollbarAutoHidden, true); - addRemoveClassVertical(classNamesScrollbarAutoHidden, true); - }; + const hide = () => scrollbarsAddRemoveClass(classNamesScrollbarAutoHidden, true); if (globalAutoHideDelay > 0 && !delayless) { auotHideTimeout(hide); } else { @@ -195,11 +187,11 @@ export const createScrollbarsSetup = ( }); }); }), - on(_isBody ? _documentElm : _viewport, 'scroll', () => { + on(_scrollEventElement, 'scroll', () => { requestScrollAnimationFrame(() => { const structureState = structureSetupState(); - refreshScrollbarHandleOffset(styleHorizontal, structureState, scrollOffsetElement, true); - refreshScrollbarHandleOffset(styleVertical, structureState, scrollOffsetElement); + refreshScrollbarHandleOffset(styleHorizontal, structureState, _scrollOffsetElement, true); + refreshScrollbarHandleOffset(styleVertical, structureState, _scrollOffsetElement); autoHideNotNever && manageScrollbarsAutoHide(true); scrollTimeout(() => { @@ -237,13 +229,10 @@ export const createScrollbarsSetup = ( const updateHandle = _overflowEdgeChanged || _overflowAmountChanged; const updateVisibility = _overflowStyleChanged || visibilityChanged; - const setScrollbarVisibility = ( - overflowStyle: OverflowStyle, - addRemoveClass: (classNames: string, add?: boolean) => void - ) => { + const setScrollbarVisibility = (overflowStyle: OverflowStyle, isHorizontal: boolean) => { const isVisible = visibility === 'visible' || (visibility === 'auto' && overflowStyle === 'scroll'); - addRemoveClass(classNamesScrollbarVisible, isVisible); + scrollbarsAddRemoveClass(classNamesScrollbarVisible, isVisible, isHorizontal); return isVisible; }; @@ -252,19 +241,16 @@ export const createScrollbarsSetup = ( if (updateVisibility) { const { _overflowStyle } = currStructureSetupState; - const xVisible = setScrollbarVisibility(_overflowStyle.x, addRemoveClassHorizontal); - const yVisible = setScrollbarVisibility(_overflowStyle.y, addRemoveClassVertical); + const xVisible = setScrollbarVisibility(_overflowStyle.x, true); + const yVisible = setScrollbarVisibility(_overflowStyle.y, false); const hasCorner = xVisible && yVisible; - addRemoveClassHorizontal(classNamesScrollbarCornerless, !hasCorner); - addRemoveClassVertical(classNamesScrollbarCornerless, !hasCorner); + scrollbarsAddRemoveClass(classNamesScrollbarCornerless, !hasCorner); } if (themeChanged) { - addRemoveClassHorizontal(prevTheme); - addRemoveClassVertical(prevTheme); + scrollbarsAddRemoveClass(prevTheme); + scrollbarsAddRemoveClass(theme, true); - addRemoveClassHorizontal(theme, true); - addRemoveClassVertical(theme, true); prevTheme = theme; } if (autoHideChanged) { @@ -280,10 +266,10 @@ export const createScrollbarsSetup = ( refreshScrollbarHandleOffset( styleHorizontal, currStructureSetupState, - scrollOffsetElement, + _scrollOffsetElement, true ); - refreshScrollbarHandleOffset(styleVertical, currStructureSetupState, scrollOffsetElement); + refreshScrollbarHandleOffset(styleVertical, currStructureSetupState, _scrollOffsetElement); } }, scrollbarsSetupState, diff --git a/packages/overlayscrollbars/src/setups/structureSetup/structureSetup.elements.ts b/packages/overlayscrollbars/src/setups/structureSetup/structureSetup.elements.ts index 650ad08..84f66f9 100644 --- a/packages/overlayscrollbars/src/setups/structureSetup/structureSetup.elements.ts +++ b/packages/overlayscrollbars/src/setups/structureSetup/structureSetup.elements.ts @@ -60,6 +60,8 @@ export interface StructureSetupElementsObj { _padding: HTMLElement | false; _content: HTMLElement | false; _viewportArrange: HTMLStyleElement | false | null | undefined; + _scrollOffsetElement: HTMLElement; + _scrollEventElement: HTMLElement | Document; // ctx ---- _isTextarea: boolean; _isBody: boolean; @@ -152,6 +154,8 @@ export const createStructureSetupElements = ( !_nativeScrollbarsHiding && createUniqueViewportArrangeElement && createUniqueViewportArrangeElement(env), + _scrollOffsetElement: isBody ? ownerDocument.documentElement : viewportElement, + _scrollEventElement: isBody ? ownerDocument : viewportElement, _windowElm: wnd, _documentElm: ownerDocument, _isTextarea: isTextarea, diff --git a/packages/overlayscrollbars/src/styles/scrollbars.scss b/packages/overlayscrollbars/src/styles/scrollbars.scss index de10aa3..212b97c 100644 --- a/packages/overlayscrollbars/src/styles/scrollbars.scss +++ b/packages/overlayscrollbars/src/styles/scrollbars.scss @@ -25,7 +25,7 @@ body > .os-scrollbar { border: none !important; } .os-scrollbar-handle { - pointer-events: none; + pointer-events: auto; position: absolute; width: 100%; height: 100%; @@ -33,6 +33,7 @@ body > .os-scrollbar { .os-scrollbar-handle-interactive, .os-scrollbar-track-interactive { pointer-events: auto; + touch-action: none; } .os-scrollbar-unusable, .os-scrollbar-unusable * {