diff --git a/packages/overlayscrollbars/src/classnames.ts b/packages/overlayscrollbars/src/classnames.ts index 958e111..924774f 100644 --- a/packages/overlayscrollbars/src/classnames.ts +++ b/packages/overlayscrollbars/src/classnames.ts @@ -5,8 +5,8 @@ export const classNameEnvironmentFlexboxGlueMax = `${classNameEnvironmentFlexbox export const classNameHost = 'os-host'; export const classNamePadding = 'os-padding'; export const classNameViewport = 'os-viewport'; +export const classNameViewportArrange = `${classNameViewport}-arrange`; export const classNameContent = 'os-content'; -export const classNameContentArrange = `${classNameContent}-arrange`; export const classNameViewportScrollbarStyling = `${classNameViewport}-scrollbar-styled`; export const classNameSizeObserver = 'os-size-observer'; diff --git a/packages/overlayscrollbars/src/environment/environment.ts b/packages/overlayscrollbars/src/environment/environment.ts index 0763ef9..352cabf 100644 --- a/packages/overlayscrollbars/src/environment/environment.ts +++ b/packages/overlayscrollbars/src/environment/environment.ts @@ -30,6 +30,7 @@ export interface Environment { _nativeScrollbarStyling: boolean; _rtlScrollBehavior: { n: boolean; i: boolean }; _flexboxGlue: boolean; + _cssCustomProperties: boolean; _addListener(listener: OnEnvironmentChanged): void; _removeListener(listener: OnEnvironmentChanged): void; } @@ -135,6 +136,7 @@ const createEnvironment = (): Environment => { _nativeScrollbarSize: nativeScrollbarSize, _nativeScrollbarIsOverlaid: nativeScrollbarIsOverlaid, _nativeScrollbarStyling: nativeScrollbarStyling, + _cssCustomProperties: style(envElm, 'zIndex') === '-1', _rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm), _flexboxGlue: getFlexboxGlue(envElm, envChildElm), _addListener(listener: OnEnvironmentChanged): void { @@ -145,6 +147,8 @@ const createEnvironment = (): Environment => { }, }; + console.log(env); + removeAttr(envElm, 'style'); removeElements(envElm); diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts index 40e8d37..efd73ec 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts @@ -11,6 +11,11 @@ import { StyleObject } from 'typings'; export type LifecycleCheckOption = (path: string) => LifecycleOptionInfo; +export interface PaddingInfo { + _absolute: boolean; + _padding: TRBL; +} + export interface LifecycleOptionInfo { readonly _value: T; _changed: boolean; @@ -42,8 +47,8 @@ export interface LifecycleHubInstance { export interface LifecycleHub { _options: Options; _structureSetup: StructureSetup; - _getPadding(): TRBL; - _setPadding(newPadding?: TRBL | null): void; + _getPaddingInfo(): PaddingInfo; + _setPaddingInfo(newPadding?: PaddingInfo | null): void; _getPaddingStyle(): StyleObject; _setPaddingStyle(newPaddingStlye?: StyleObject | null): void; _getViewportOverflowScroll(): XY; @@ -54,7 +59,15 @@ const getPropByPath = (obj: any, path: string): T => obj && path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj); const attrs = ['id', 'class', 'style', 'open']; -const paddingFallback: TRBL = { t: 0, r: 0, b: 0, l: 0 }; +const paddingInfoFallback: PaddingInfo = { + _absolute: false, + _padding: { + t: 0, + r: 0, + b: 0, + l: 0, + }, +}; const viewportPaddingStyleFallback: StyleObject = { marginTop: 0, marginRight: 0, @@ -77,7 +90,7 @@ const heightIntrinsicCacheValuesFallback: CacheValues = { }; export const createLifecycleHub = (options: Options, structureSetup: StructureSetup): LifecycleHubInstance => { - let padding = paddingFallback; + let paddingInfo = paddingInfoFallback; let viewportPaddingStyle = viewportPaddingStyleFallback; let viewportOverflowScroll = viewportOverflowScrollFallback; const { _host, _viewport, _content, _contentArrange } = structureSetup._targetObj; @@ -91,9 +104,9 @@ export const createLifecycleHub = (options: Options, structureSetup: StructureSe const instance: LifecycleHub = { _options: options, _structureSetup: structureSetup, - _getPadding: () => padding, - _setPadding(newPadding) { - padding = newPadding || paddingFallback; + _getPaddingInfo: () => paddingInfo, + _setPaddingInfo(newPaddingInfo) { + paddingInfo = newPaddingInfo || paddingInfoFallback; }, _getPaddingStyle: () => viewportPaddingStyle, _setPaddingStyle(newPaddingStlye) { diff --git a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts index ec25bd3..981b0d4 100644 --- a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts @@ -1,5 +1,6 @@ import { createCache, + attr, WH, XY, equalXY, @@ -17,7 +18,7 @@ import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub'; import { getEnvironment } from 'environment'; import { OverflowBehavior } from 'options'; import { StyleObject } from 'typings'; -import { classNameViewportScrollbarStyling } from 'classnames'; +import { classNameViewport, classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames'; interface ContentScrollSizeCacheContext { _viewportRect: DOMRect; @@ -44,7 +45,7 @@ const overlaidScrollbarsHideOffset = 42; const overlaidScrollbarsHideBorderStyle = `${overlaidScrollbarsHideOffset}px solid transparent`; export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => { - const { _structureSetup, _getPaddingStyle } = lifecycleHub; + const { _structureSetup, _getPaddingStyle, _getPaddingInfo } = lifecycleHub; const { _host, _padding, _viewport, _content, _contentArrange } = _structureSetup._targetObj; const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache< WH, @@ -76,14 +77,16 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = }); if (heightIntrinsic) { + const { _absolute: paddingAbsolute, _padding: padding } = _getPaddingInfo(); const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState; const hostBCR = getBoundingClientRect(_host); const hostOffsetSize = offsetSize(_host); const hostClientSize = clientSize(_host); + const paddingAbsoluteVertical = paddingAbsolute ? padding.b + padding.t : 0; const clientSizeWithoutRounding = hostClientSize.h + (hostBCR.height - hostOffsetSize.h); style(_viewport, { - maxHeight: clientSizeWithoutRounding + (_overflowScroll.x ? _scrollbarsHideOffset.x : 0), + maxHeight: clientSizeWithoutRounding + (_overflowScroll.x ? _scrollbarsHideOffset.x : 0) - paddingAbsoluteVertical, }); } }; @@ -92,7 +95,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = const { _nativeScrollbarSize, _nativeScrollbarIsOverlaid, _nativeScrollbarStyling } = getEnvironment(); const { x: overlaidX, y: overlaidY } = _nativeScrollbarIsOverlaid; const determineOverflow = !viewportStyleObj; - const arrangeHideOffset = _content && !_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0; + const arrangeHideOffset = !_nativeScrollbarStyling && !showNativeOverlaidScrollbars ? overlaidScrollbarsHideOffset : 0; const styleObj = determineOverflow ? style(_viewport, ['overflowX', 'overflowY']) : viewportStyleObj; const scroll = { x: styleObj!.overflowX === 'scroll', @@ -150,23 +153,42 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = directionIsRTL: boolean, contentStyleObj?: StyleObject ) => { - const { _scrollbarsHideOffset } = viewportOverflowState; - const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset; - const horizontalBorderKey = directionIsRTL ? 'borderLeft' : 'borderRight'; + const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment(); + if ((_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) && !_nativeScrollbarStyling) { + const { _scrollbarsHideOffset } = viewportOverflowState; + const { _absolute: paddingAbsolute, _padding: padding } = _getPaddingInfo(); + const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset; + const horizontalPaddingKey = directionIsRTL ? 'paddingLeft' : 'paddingRight'; + const horizontalPaddingValue = paddingAbsolute ? 0 : directionIsRTL ? padding.l : padding.r; + const verticalPaddingValue = paddingAbsolute ? 0 : padding.b; - if (_contentArrange && contentStyleObj) { - // horizontal - contentStyleObj[horizontalBorderKey] = hideOffsetY ? overlaidScrollbarsHideBorderStyle : ''; + style(_viewport, { + [horizontalPaddingKey]: horizontalPaddingValue + hideOffsetY, + paddingBottom: verticalPaddingValue + hideOffsetX, + }); - // vertical - contentStyleObj.borderBottom = hideOffsetX ? overlaidScrollbarsHideBorderStyle : ''; + // adjust content arrange / before element + if (_contentArrange) { + const { sheet } = _contentArrange; + if (sheet) { + const { cssRules } = sheet; + if (cssRules) { + if (!cssRules.length) { + sheet.insertRule(`#${attr(_contentArrange, 'id')} + .${classNameViewportArrange}::before {}`, 0); + } + + // @ts-ignore + const ruleStyle = cssRules[0].style; + + ruleStyle.width = hideOffsetY ? `${contentScrollSize.w}px` : '0px'; + ruleStyle.height = hideOffsetX ? `${contentScrollSize.h}px` : '0px'; + + addClass(_viewport, classNameViewportArrange); + } + } + } else { + } } - - // adjust content arrange (content arrange doesn't exist if its not needed) - style(_contentArrange, { - width: hideOffsetY ? hideOffsetY + contentScrollSize.w : '', - height: hideOffsetX ? hideOffsetX + contentScrollSize.h : '', - }); }; const hideNativeScrollbars = (viewportOverflowState: ViewportOverflowState, directionIsRTL: boolean, viewportStyleObj: StyleObject) => { @@ -220,37 +242,59 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = fixFlexboxGlue(preMeasureViewportOverflowState, !!heightIntrinsic); } - if (_sizeChanged || _contentMutation) { + if (_sizeChanged || _contentMutation || directionChanged) { + removeClass(_viewport, classNameViewportArrange); + style(_viewport, { + paddingRight: _getPaddingInfo()._padding.r, + paddingBottom: _getPaddingInfo()._padding.b, + marginRight: -_getPaddingInfo()._padding.r - _getPaddingInfo()._padding.l, + marginBottom: -_getPaddingInfo()._padding.b - _getPaddingInfo()._padding.t, + }); + const viewportRect = getBoundingClientRect(_viewport); const viewportOffsetSize = offsetSize(_viewport); - const contentClientSize = clientSize(_content || _viewport); // needs to be client Size because applied border for content arrange on content + const contentClientSize = clientSize(_viewport); // needs to be client Size because applied border for content arrange on content let viewportScrollSize = fixScrollSizeRounding(scrollSize(_viewport), viewportOffsetSize, viewportRect); let viewportClientSize = clientSize(_viewport); - const { _value: contentScrollSize, _changed: contentScrollSizeChanged } = (contentScrollSizeCache = updateContentScrollSizeCache(force, { + let { _value: contentScrollSize, _changed: contentScrollSizeChanged } = (contentScrollSizeCache = updateContentScrollSizeCache(force, { _viewportRect: viewportRect, _viewportOffsetSize: viewportOffsetSize, _viewportScrollSize: viewportScrollSize, })); // re measure is only required if we rely on content arrange to hide native scrollbars (no native scrollbar styling and overlaid scrollbars) - const reMeasureRequired = contentScrollSizeChanged && !showNativeOverlaidScrollbars && _contentArrange; + const reMeasureRequired = contentScrollSizeChanged && !showNativeOverlaidScrollbars; - if (reMeasureRequired) { - setContentArrange( - preMeasureViewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars), - contentScrollSize!, - directionIsRTL! - ); + if (true) { + const viewportStyle: StyleObject = { + overflowY: '', + overflowX: '', + marginTop: '', + marginRight: '', + marginBottom: '', + marginLeft: '', + maxWidth: '', + }; + setContentArrange(getViewportOverflowState(showNativeOverlaidScrollbars), contentScrollSize!, directionIsRTL!); + hideNativeScrollbars(getViewportOverflowState(showNativeOverlaidScrollbars), directionIsRTL!, viewportStyle); + + style(_viewport, viewportStyle); viewportClientSize = clientSize(_viewport); viewportScrollSize = fixScrollSizeRounding(scrollSize(_viewport), offsetSize(_viewport), getBoundingClientRect(_viewport)); + + ({ _value: contentScrollSize, _changed: contentScrollSizeChanged } = contentScrollSizeCache = updateContentScrollSizeCache(force, { + _viewportRect: viewportRect, + _viewportOffsetSize: viewportOffsetSize, + _viewportScrollSize: viewportScrollSize, + })); } - const contentArrangeOffsetSize = clientSize(_contentArrange); + //const contentArrangeOffsetSize = clientSize(_contentArrange); overflowAmuntCache = updateOverflowAmountCache(force, { _contentScrollSize: { - w: Math.max(contentScrollSize!.w, viewportScrollSize.w, contentArrangeOffsetSize.w), - h: Math.max(contentScrollSize!.h, viewportScrollSize.h, contentArrangeOffsetSize.h), + w: Math.max(contentScrollSize!.w, viewportScrollSize.w), + h: Math.max(contentScrollSize!.h, viewportScrollSize.h), }, _viewportSize: { w: viewportClientSize.w + Math.max(0, contentClientSize.w - contentScrollSize!.w), diff --git a/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts index 7ccce4d..1217d01 100644 --- a/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts @@ -76,7 +76,10 @@ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => style(_padding || _viewport, paddingStyle); style(_viewport, viewportStyle); - lifecycleHub._setPadding(padding); + lifecycleHub._setPaddingInfo({ + _absolute: !paddingRelative, + _padding: padding!, + }); lifecycleHub._setPaddingStyle(!_padding ? paddingStyle : null); } diff --git a/packages/overlayscrollbars/src/overlayscrollbars.scss b/packages/overlayscrollbars/src/overlayscrollbars.scss index 9751d73..8a7479e 100644 --- a/packages/overlayscrollbars/src/overlayscrollbars.scss +++ b/packages/overlayscrollbars/src/overlayscrollbars.scss @@ -3,12 +3,14 @@ @import './structurelifecycle.scss'; .os-environment { + --css-custom-prop: -1; position: fixed; opacity: 0; visibility: hidden; overflow: scroll; height: 200px; width: 200px; + z-index: var(--css-custom-prop); div { width: 200%; @@ -68,7 +70,7 @@ } .os-environment, .os-viewport { - -ms-overflow-style: scrollbar !important; + -ms-overflow-style: -ms-autohiding-scrollbar !important; } .os-viewport-scrollbar-styled.os-environment, .os-viewport-scrollbar-styled.os-viewport { @@ -85,10 +87,15 @@ background: transparent !important; } -.os-content-arrange { - min-width: 1px; - min-height: 1px; - z-index: -1; +.os-viewport-arrange::before { + content: ''; position: absolute; pointer-events: none; + z-index: -1; + min-width: 1px; + min-height: 1px; +} + +.os-host { + padding: 5px 50px 15px 20px; } diff --git a/packages/overlayscrollbars/src/setups/structureSetup.ts b/packages/overlayscrollbars/src/setups/structureSetup.ts index 1efcb01..5b019bd 100644 --- a/packages/overlayscrollbars/src/setups/structureSetup.ts +++ b/packages/overlayscrollbars/src/setups/structureSetup.ts @@ -12,14 +12,15 @@ import { removeClass, push, runEach, - prependChildren, + insertBefore, + attr, } from 'support'; import { classNameHost, classNamePadding, classNameViewport, + classNameViewportArrange, classNameContent, - classNameContentArrange, classNameViewportScrollbarStyling, } from 'classnames'; import { getEnvironment } from 'environment'; @@ -36,7 +37,7 @@ export interface OSTargetContext { export interface PreparedOSTargetObject extends Required> { _host: HTMLElement; - _contentArrange: HTMLElement | null; + _contentArrange: HTMLStyleElement | null; } export interface StructureSetup { @@ -50,6 +51,16 @@ const unwrap = (elm: HTMLElement | null | undefined) => { removeElements(elm); }; +let contentArrangeCounter = 0; +const createUniqueContentArrangeElement = () => { + const elm = document.createElement('style'); + + attr(elm, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`); + contentArrangeCounter++; + + return elm; +}; + export const createStructureSetup = (target: OSTarget | OSTargetObject): StructureSetup => { const targetIsElm = isHTMLElement(target); const osTargetObj: InternalVersionOf = targetIsElm @@ -160,14 +171,14 @@ export const createStructureSetup = (target: OSTarget | OSTargetObject): Structu _host, }; - const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment(); + const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment(); if (_nativeScrollbarStyling) { push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); } else if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) { - if (obj._content) { - const contentArrangeElm = createDiv(classNameContentArrange); + if (true) { + const contentArrangeElm = createUniqueContentArrangeElement(); - prependChildren(_viewport, contentArrangeElm); + insertBefore(_viewport, contentArrangeElm); push(destroyFns, removeElements.bind(0, contentArrangeElm)); obj._contentArrange = contentArrangeElm; diff --git a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts index 761d423..6d670e2 100644 --- a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts +++ b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.browser.ts @@ -4,7 +4,7 @@ import { createDiv, appendChildren, parent, style, on, off, addClass, WH, XY, cl import { OverlayScrollbars } from 'overlayscrollbars/OverlayScrollbars'; const targetElm = document.querySelector('#target') as HTMLElement; -window.os = OverlayScrollbars({ target: targetElm, padding: null }); +window.os = OverlayScrollbars({ target: targetElm, padding: null, content: null }); export const resize = (element: HTMLElement) => { const strMouseTouchDownEvent = 'mousedown touchstart'; diff --git a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss index ca5627f..57693f2 100644 --- a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss +++ b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss @@ -57,6 +57,7 @@ body { border: 1px solid black; padding: 10px; margin: 10px; + display: none; } #end::before {