improve code

This commit is contained in:
Rene
2021-04-03 23:30:19 +02:00
parent 01228ff790
commit 8bdfd0b12b
5 changed files with 129 additions and 86 deletions
@@ -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<boolean>;
@@ -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,
@@ -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<number>,
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<XY<number>, 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<number>, viewportOffsetSize: WH<number>, viewportRect: DOMRect): WH<number> => ({
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<number>, viewportOffsetSize: WH<number>, viewportRect: DOMRect): WH<number> => ({
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<number>,
@@ -147,24 +172,21 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
return getViewportOverflowState(showNativeOverlaidScrollbars, viewportStyleObj);
};
const setContentArrange = (
viewportOverflowState: ViewportOverflowState,
contentScrollSize: WH<number>,
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<number>, 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<XY<number>> = 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);
}
};
};
@@ -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<boolean>) => {
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();
@@ -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;
@@ -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 {