From 8bdfd0b12b27d7d0d889e59e733a267fc3c6ccfc Mon Sep 17 00:00:00 2001 From: Rene Date: Sat, 3 Apr 2021 23:30:19 +0200 Subject: [PATCH] improve code --- .../src/lifecycles/lifecycleHub.ts | 5 +- .../src/lifecycles/overflowLifecycle.ts | 189 +++++++++++------- .../src/lifecycles/paddingLifecycle.ts | 14 +- .../src/lifecycles/trinsicLifecycle.ts | 5 + .../lifecycles/structureLifecycle/index.scss | 2 - 5 files changed, 129 insertions(+), 86 deletions(-) diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts index bc6f423..bb761ef 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts @@ -48,9 +48,11 @@ export interface LifecycleHubInstance { export interface LifecycleHub { _options: Options; _structureSetup: StructureSetup; + // whether the "viewport arrange" strategy must be used (true if no native scrollbar hiding and scrollbars are overlaid) _doViewportArrange: boolean; _getPaddingInfo(): PaddingInfo; _setPaddingInfo(newPadding?: PaddingInfo | null): void; + // padding related styles applied to the viewport element _getViewportPaddingStyle(): StyleObject; _setViewportPaddingStyle(newPaddingStlye?: StyleObject | null): void; _getViewportOverflowScroll(): XY; @@ -70,6 +72,7 @@ const emptyStylePropsToZero = (stlyeObj: StyleObject, baseStyle?: StyleObject) = { ...baseStyle } ); +// TODO: tabindex, open etc. const attrs = ['id', 'class', 'style', 'open']; const paddingInfoFallback: PaddingInfo = { _absolute: false, @@ -229,7 +232,7 @@ export const createLifecycleHub = (options: Options, structureSetup: StructureSe }); }; - const trinsicObserver = _content ? createTrinsicObserver(_host, onTrinsicChanged) : null; + const trinsicObserver = _content && createTrinsicObserver(_host, onTrinsicChanged); const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling }); const hostMutationObserver = createDOMObserver(_host, onHostMutation, { _styleChangingAttributes: attrs, diff --git a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts index 72b0667..e9db73f 100644 --- a/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/overflowLifecycle.ts @@ -45,19 +45,18 @@ interface OverflowOption { const overlaidScrollbarsHideOffset = 42; +/** + * Lifecycle with the responsibility to set the correct overflow and scrollbar hiding styles of the viewport element. + * @param lifecycleHub + * @returns + */ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => { - const { _structureSetup, _doViewportArrange, _getViewportPaddingStyle, _getPaddingInfo } = lifecycleHub; - const { _host, _padding, _viewport, _content, _viewportArrange } = _structureSetup._targetObj; + const { _structureSetup, _doViewportArrange, _getViewportPaddingStyle, _getPaddingInfo, _setViewportOverflowScroll } = lifecycleHub; + const { _host, _padding, _viewport, _viewportArrange } = _structureSetup._targetObj; const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache< WH, ContentScrollSizeCacheContext - >( - (ctx) => { - const { _viewportOffsetSize, _viewportScrollSize, _viewportRect } = ctx; - return fixScrollSizeRounding(_viewportScrollSize, _viewportOffsetSize, _viewportRect); - }, - { _equal: equalWH } - ); + >((ctx) => fixScrollSizeRounding(ctx._viewportScrollSize, ctx._viewportOffsetSize, ctx._viewportRect), { _equal: equalWH }); const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache, OverflowAmountCacheContext>( (ctx) => ({ x: Math.max(0, ctx._contentScrollSize.w - ctx._viewportSize.w), @@ -66,11 +65,23 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = { _equal: equalXY, _initialValue: { x: 0, y: 0 } } ); - const fixScrollSizeRounding = (scrollSize: WH, viewportOffsetSize: WH, viewportRect: DOMRect): WH => ({ - w: scrollSize.w - Math.round(Math.max(0, viewportRect.width - viewportOffsetSize.w)), - h: scrollSize.h - Math.round(Math.max(0, viewportRect.height - viewportOffsetSize.h)), + /** + * Fixes incorrect roundng of scroll size. + * @param viewportScrollSize The potential incorrect viewport scroll size. + * @param viewportOffsetSize The viewport offset size. + * @param viewportRect The viewport bounding client rect. + * @returns The passed scroll size without rounding errors. + */ + const fixScrollSizeRounding = (viewportScrollSize: WH, viewportOffsetSize: WH, viewportRect: DOMRect): WH => ({ + w: viewportScrollSize.w - Math.round(Math.max(0, viewportRect.width - viewportOffsetSize.w)), + h: viewportScrollSize.h - Math.round(Math.max(0, viewportRect.height - viewportOffsetSize.h)), }); + /** + * Applies a fixed height to the viewport so it can't overflow or underflow the host element. + * @param viewportOverflowState The current overflow state. + * @param heightIntrinsic Whether the host height is intrinsic or not. + */ const fixFlexboxGlue = (viewportOverflowState: ViewportOverflowState, heightIntrinsic: boolean) => { style(_viewport, { height: '', @@ -91,6 +102,12 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = } }; + /** + * Gets the current overflow state of the viewport. + * @param showNativeOverlaidScrollbars Whether native overlaid scrollbars are shown instead of hidden. + * @param viewportStyleObj The viewport style object where the overflow scroll property can be read of, or undefined if shall be determined. + * @returns A object which contains informations about the current overflow state. + */ const getViewportOverflowState = (showNativeOverlaidScrollbars: boolean, viewportStyleObj?: StyleObject): ViewportOverflowState => { const { _nativeScrollbarSize, _nativeScrollbarIsOverlaid, _nativeScrollbarStyling } = getEnvironment(); const { x: overlaidX, y: overlaidY } = _nativeScrollbarIsOverlaid; @@ -112,6 +129,14 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = }; }; + /** + * Sets the overflow property of the viewport and calculates the a overflow state according to the new parameters. + * @param showNativeOverlaidScrollbars Whether to show natively overlaid scrollbars. + * @param overflowAmount The overflow amount. + * @param overflow The overflow behavior according to the options. + * @param viewportStyleObj The viewport style object to which the overflow style shall be applied. + * @returns A object which represents the newly set overflow state. + */ const setViewportOverflowState = ( showNativeOverlaidScrollbars: boolean, overflowAmount: XY, @@ -147,24 +172,21 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = return getViewportOverflowState(showNativeOverlaidScrollbars, viewportStyleObj); }; - const setContentArrange = ( - viewportOverflowState: ViewportOverflowState, - contentScrollSize: WH, - directionIsRTL: boolean, - viewportStyleObj?: StyleObject - ) => { - const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment(); - if ((_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) && !_nativeScrollbarStyling) { + /** + * Sets the styles of the viewport arrange element. + * @param viewportOverflowState The viewport overflow state according to which the scrollbars shall be hidden. + * @param contentScrollSize The content scroll size. + * @param directionIsRTL Whether the direction is RTL or not. + * @returns A boolean which indicates whether the viewport arrange element was adjusted. + */ + const arrangeViewport = (viewportOverflowState: ViewportOverflowState, contentScrollSize: WH, directionIsRTL: boolean) => { + if (_doViewportArrange) { const { _scrollbarsHideOffset } = viewportOverflowState; const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset; const viewportPaddingStyle = _getViewportPaddingStyle(); - const viewportHorizontalPaddingKey = directionIsRTL ? 'paddingLeft' : 'paddingRight'; - const viewportHorizontalPaddingValue = viewportPaddingStyle[viewportHorizontalPaddingKey] as number; - const viewportVerticalPaddingValue = viewportPaddingStyle.paddingBottom as number; const viewportArrangeHorizontalPaddingKey = directionIsRTL ? 'paddingRight' : 'paddingLeft'; const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[viewportArrangeHorizontalPaddingKey] as number; const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number; - const styleObj: StyleObject = viewportStyleObj || {}; const arrangeSize = { w: hideOffsetY ? `${hideOffsetY + contentScrollSize.w - viewportArrangeHorizontalPaddingValue}px` : '', h: hideOffsetX ? `${hideOffsetX + contentScrollSize.h - viewportArrangeVerticalPaddingValue}px` : '', @@ -193,58 +215,83 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = '--viewport-arrange-height': arrangeSize.h, }); } - - styleObj[viewportHorizontalPaddingKey] = viewportHorizontalPaddingValue + hideOffsetY; - styleObj.paddingBottom = viewportVerticalPaddingValue + hideOffsetX; - - if (!viewportStyleObj) { - style(_viewport, styleObj); - } } + + return _doViewportArrange; }; - const hideNativeScrollbars = (viewportOverflowState: ViewportOverflowState, directionIsRTL: boolean, viewportStyleObj: StyleObject) => { + /** + * Hides the native scrollbars according to the passed parameters. + * @param viewportOverflowState The viewport overflow state. + * @param directionIsRTL Whether the direction is RTL or not. + * @param viewportArrange Whether special styles related to the viewport arrange strategy shall be applied. + * @param viewportStyleObj The viewport style object to which the needed styles shall be applied. + */ + const hideNativeScrollbars = ( + viewportOverflowState: ViewportOverflowState, + directionIsRTL: boolean, + viewportArrange: boolean, + viewportStyleObj: StyleObject + ) => { const { _nativeScrollbarStyling } = getEnvironment(); const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState; + const { x: hideOffsetX, y: hideOffsetY } = _scrollbarsHideOffset; const { x: scrollX, y: scrollY } = _overflowScroll; const paddingStyle = _getViewportPaddingStyle(); const horizontalMarginKey = directionIsRTL ? 'marginLeft' : 'marginRight'; - const horizontalPaddingValue = paddingStyle[horizontalMarginKey] as number; - const verticalPaddingValue = paddingStyle.marginBottom as number; + const horizontalMarginValue = paddingStyle[horizontalMarginKey] as number; + const verticalMarginValue = paddingStyle.marginBottom as number; + const viewportHorizontalPaddingKey = directionIsRTL ? 'paddingLeft' : 'paddingRight'; + const horizontalPaddingValue = paddingStyle[viewportHorizontalPaddingKey] as number; + const verticalPaddingValue = paddingStyle.paddingBottom as number; // horizontal - viewportStyleObj.maxWidth = `calc(100% + ${_scrollbarsHideOffset.y + horizontalPaddingValue * -1}px)`; - viewportStyleObj[horizontalMarginKey] = -_scrollbarsHideOffset.y + horizontalPaddingValue; + viewportStyleObj.maxWidth = `calc(100% + ${hideOffsetY + horizontalMarginValue * -1}px)`; + viewportStyleObj[horizontalMarginKey] = -hideOffsetY + horizontalMarginValue; // vertical - viewportStyleObj.marginBottom = -_scrollbarsHideOffset.x + verticalPaddingValue; + viewportStyleObj.marginBottom = -hideOffsetX + verticalMarginValue; + + // viewport arrange additional styles + if (viewportArrange) { + viewportStyleObj[viewportHorizontalPaddingKey] = horizontalPaddingValue + hideOffsetY; + viewportStyleObj.paddingBottom = verticalPaddingValue + hideOffsetX; + } // hide overflowing scrollbars if there are any if (!_nativeScrollbarStyling) { - style(_padding, { - overflow: scrollX || scrollY ? 'hidden' : 'visible', + style(_padding || _host, { + overflow: scrollX || scrollY ? 'hidden' : '', }); } }; - const undoOverlaidScrollbarsHiding = (adjustFlexboxGlue?: boolean) => { - let paddingStyle = _getViewportPaddingStyle(); + /** + * Removes all styles applied because of the viewport arrange strategy. + * @returns A function which again applies all the removed styles. + */ + const undoViewportArrange = () => { + if (_doViewportArrange) { + const { _flexboxGlue } = getEnvironment(); + let paddingStyle = _getViewportPaddingStyle(); - if (adjustFlexboxGlue) { - paddingStyle = { - ...paddingStyle, - height: '', + if (!_flexboxGlue) { + paddingStyle = { + ...paddingStyle, + height: '', + }; + } + + const prevStyle = style(_viewport, keys(paddingStyle)); + removeClass(_viewport, classNameViewportArrange); + style(_viewport, paddingStyle); + + return () => { + style(_viewport, prevStyle); + addClass(_viewport, classNameViewportArrange); }; } - - const prevStyle = style(_viewport, keys(paddingStyle)); - removeClass(_viewport, classNameViewportArrange); - style(_viewport, paddingStyle); - - return () => { - style(_viewport, prevStyle); - addClass(_viewport, classNameViewportArrange); - }; + return noop; }; return (updateHints, checkOption, force) => { @@ -256,7 +303,6 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = 'nativeScrollbarsOverlaid.show' ); const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y; - const viewportArrange = _doViewportArrange && !showNativeOverlaidScrollbars; const adjustFlexboxGlue = !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || heightIntrinsicChanged); let overflowAmuntCache: CacheValues> = getCurrentOverflowAmountCache(force); @@ -276,8 +322,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = fixFlexboxGlue(preMeasureViewportOverflowState, !!heightIntrinsic); } - if (_sizeChanged || _paddingStyleChanged || _contentMutation || directionChanged) { - const redoOverlaidScrollbarsHiding = viewportArrange ? undoOverlaidScrollbarsHiding(adjustFlexboxGlue) : noop; + if (_sizeChanged || _paddingStyleChanged || _contentMutation || showNativeOverlaidScrollbarsChanged || directionChanged) { + const redoViewportArrange = undoViewportArrange(); const contentSize = clientSize(_viewport); const viewportRect = getBoundingClientRect(_viewport); const viewportOffsetSize = offsetSize(_viewport); @@ -289,20 +335,19 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = _viewportScrollSize: viewportScrollSize, })); - redoOverlaidScrollbarsHiding(); + redoViewportArrange(); - // re measure is only required if we rely on content arrange to hide native scrollbars (no native scrollbar styling and overlaid scrollbars) - const reMeasureRequired = viewportArrange && contentScrollSizeChanged && !showNativeOverlaidScrollbars; - - if (reMeasureRequired) { - setContentArrange( + if ((contentScrollSizeChanged || showNativeOverlaidScrollbarsChanged) && !showNativeOverlaidScrollbars) { + const arranged = arrangeViewport( preMeasureViewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars), contentScrollSize!, directionIsRTL! ); - viewportClientSize = clientSize(_viewport); - viewportScrollSize = fixScrollSizeRounding(scrollSize(_viewport), offsetSize(_viewport), getBoundingClientRect(_viewport)); + if (arranged) { + viewportClientSize = clientSize(_viewport); + viewportScrollSize = fixScrollSizeRounding(scrollSize(_viewport), offsetSize(_viewport), getBoundingClientRect(_viewport)); + } } overflowAmuntCache = updateOverflowAmountCache(force, { @@ -341,25 +386,21 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle = }; const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, overflowAmount!, overflow, viewportStyle); - hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportStyle); - - if (_doViewportArrange) { - setContentArrange(viewportOverflowState, contentScrollSize!, directionIsRTL!, viewportStyle); - } + const viewportArranged = arrangeViewport(viewportOverflowState, contentScrollSize!, directionIsRTL!); + hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle); if (adjustFlexboxGlue) { fixFlexboxGlue(viewportOverflowState, !!heightIntrinsic); } // TODO: enlargen viewport if div too small for firefox scrollbar hiding behavior + // TODO: hide host overflow if scroll x or y and no padding element there // TODO: Test without content // TODO: Test without padding - // TODO: hide host || padding overflow if scroll x or y - // TODO: add trinsic lifecycle - // TODO: IE max-width fix not always working - // TODO: remove lifecycleHub get set padding if not needed style(_viewport, viewportStyle); + + _setViewportOverflowScroll(viewportOverflowState._overflowScroll); } }; }; diff --git a/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts index f01e491..fe81c89 100644 --- a/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/paddingLifecycle.ts @@ -3,6 +3,11 @@ import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub'; import { StyleObject } from 'typings'; import { getEnvironment } from 'environment'; +/** + * Lifecycle with the responsibility to adjust the padding styling of the padding and viewport element. + * @param lifecycleHub + * @returns + */ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => { const { _setPaddingInfo, _setViewportPaddingStyle, _structureSetup } = lifecycleHub; const { _host, _padding, _viewport } = _structureSetup._targetObj; @@ -10,15 +15,6 @@ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => _equal: equalTRBL, }); - /* - const onTrinsicChanged = (heightIntrinsic: CacheValues) => { - const { _changed, _value } = heightIntrinsic; - if (_changed) { - style(_content, { height: _value ? 'auto' : '100%' }); - } - }; - */ - return (updateHints, checkOption, force) => { let { _value: padding, _changed: paddingChanged } = currentPaddingCache(force); const { _nativeScrollbarStyling } = getEnvironment(); diff --git a/packages/overlayscrollbars/src/lifecycles/trinsicLifecycle.ts b/packages/overlayscrollbars/src/lifecycles/trinsicLifecycle.ts index f8be7b8..cb37138 100644 --- a/packages/overlayscrollbars/src/lifecycles/trinsicLifecycle.ts +++ b/packages/overlayscrollbars/src/lifecycles/trinsicLifecycle.ts @@ -1,6 +1,11 @@ import { style } from 'support'; import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub'; +/** + * Lifecycle with the responsibility to adjust the trinsic behavior of the content element. + * @param lifecycleHub + * @returns + */ export const createTrinsicLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => { const { _structureSetup } = lifecycleHub; const { _content } = _structureSetup._targetObj; diff --git a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss index 64e94b1..128161a 100644 --- a/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss +++ b/packages/overlayscrollbars/tests/browser/lifecycles/structureLifecycle/index.scss @@ -28,7 +28,6 @@ body { } #target { - overflow: hidden; position: relative; border: 2px solid red; min-height: 100px; @@ -138,7 +137,6 @@ body { .resizer { position: relative; - overflow: hidden; } .resizeBtn {