mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-05-25 11:34:06 +03:00
content element independency
This commit is contained in:
@@ -147,8 +147,6 @@ const createEnvironment = (): Environment => {
|
||||
},
|
||||
};
|
||||
|
||||
console.log(env);
|
||||
|
||||
removeAttr(envElm, 'style');
|
||||
removeElements(envElm);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { XY, TRBL, CacheValues, each, push, OptionsValidated, hasOwnProperty, isNumber, scrollLeft, scrollTop } from 'support';
|
||||
import { XY, TRBL, CacheValues, each, push, keys, OptionsValidated, hasOwnProperty, isNumber, scrollLeft, scrollTop } from 'support';
|
||||
import { Options } from 'options';
|
||||
import { getEnvironment } from 'environment';
|
||||
import { StructureSetup } from 'setups/structureSetup';
|
||||
@@ -47,10 +47,11 @@ export interface LifecycleHubInstance {
|
||||
export interface LifecycleHub {
|
||||
_options: Options;
|
||||
_structureSetup: StructureSetup;
|
||||
_doViewportArrange: boolean;
|
||||
_getPaddingInfo(): PaddingInfo;
|
||||
_setPaddingInfo(newPadding?: PaddingInfo | null): void;
|
||||
_getPaddingStyle(): StyleObject;
|
||||
_setPaddingStyle(newPaddingStlye?: StyleObject | null): void;
|
||||
_getViewportPaddingStyle(): StyleObject;
|
||||
_setViewportPaddingStyle(newPaddingStlye?: StyleObject | null): void;
|
||||
_getViewportOverflowScroll(): XY<boolean>;
|
||||
_setViewportOverflowScroll(newViewportOverflowScroll: XY<boolean>): void;
|
||||
}
|
||||
@@ -58,6 +59,16 @@ export interface LifecycleHub {
|
||||
const getPropByPath = <T>(obj: any, path: string): T =>
|
||||
obj && path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj);
|
||||
|
||||
const emptyStylePropsToZero = (stlyeObj: StyleObject, baseStyle?: StyleObject) =>
|
||||
keys(stlyeObj).reduce(
|
||||
(obj, key) => {
|
||||
const value = stlyeObj[key];
|
||||
obj[key] = value === '' ? 0 : value;
|
||||
return obj;
|
||||
},
|
||||
{ ...baseStyle }
|
||||
);
|
||||
|
||||
const attrs = ['id', 'class', 'style', 'open'];
|
||||
const paddingInfoFallback: PaddingInfo = {
|
||||
_absolute: false,
|
||||
@@ -73,6 +84,10 @@ const viewportPaddingStyleFallback: StyleObject = {
|
||||
marginRight: 0,
|
||||
marginBottom: 0,
|
||||
marginLeft: 0,
|
||||
paddingTop: 0,
|
||||
paddingRight: 0,
|
||||
paddingBottom: 0,
|
||||
paddingLeft: 0,
|
||||
};
|
||||
const viewportOverflowScrollFallback: XY<boolean> = {
|
||||
x: false,
|
||||
@@ -93,24 +108,27 @@ export const createLifecycleHub = (options: Options, structureSetup: StructureSe
|
||||
let paddingInfo = paddingInfoFallback;
|
||||
let viewportPaddingStyle = viewportPaddingStyleFallback;
|
||||
let viewportOverflowScroll = viewportOverflowScrollFallback;
|
||||
const { _host, _viewport, _content, _contentArrange } = structureSetup._targetObj;
|
||||
const { _host, _viewport, _content } = structureSetup._targetObj;
|
||||
const {
|
||||
_nativeScrollbarStyling,
|
||||
_nativeScrollbarIsOverlaid,
|
||||
_flexboxGlue,
|
||||
_addListener: addEnvironmentListener,
|
||||
_removeListener: removeEnvironmentListener,
|
||||
} = getEnvironment();
|
||||
const doViewportArrange = !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
|
||||
const lifecycles: Lifecycle[] = [];
|
||||
const instance: LifecycleHub = {
|
||||
_options: options,
|
||||
_structureSetup: structureSetup,
|
||||
_doViewportArrange: doViewportArrange,
|
||||
_getPaddingInfo: () => paddingInfo,
|
||||
_setPaddingInfo(newPaddingInfo) {
|
||||
paddingInfo = newPaddingInfo || paddingInfoFallback;
|
||||
},
|
||||
_getPaddingStyle: () => viewportPaddingStyle,
|
||||
_setPaddingStyle(newPaddingStlye) {
|
||||
viewportPaddingStyle = newPaddingStlye || viewportPaddingStyleFallback;
|
||||
_getViewportPaddingStyle: () => viewportPaddingStyle,
|
||||
_setViewportPaddingStyle(newPaddingStlye) {
|
||||
viewportPaddingStyle = newPaddingStlye ? emptyStylePropsToZero(newPaddingStlye, viewportPaddingStyleFallback) : viewportPaddingStyleFallback;
|
||||
},
|
||||
_getViewportOverflowScroll: () => viewportOverflowScroll,
|
||||
_setViewportOverflowScroll(newViewportOverflowScroll) {
|
||||
@@ -143,7 +161,7 @@ export const createLifecycleHub = (options: Options, structureSetup: StructureSe
|
||||
_value: getPropByPath(options, path),
|
||||
_changed: force || getPropByPath(changedOptions, path) !== undefined,
|
||||
});
|
||||
const adjustScrollOffset = _contentArrange || !_flexboxGlue;
|
||||
const adjustScrollOffset = doViewportArrange || !_flexboxGlue;
|
||||
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
||||
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
createCache,
|
||||
keys,
|
||||
attr,
|
||||
WH,
|
||||
XY,
|
||||
@@ -13,12 +14,13 @@ import {
|
||||
clientSize,
|
||||
offsetSize,
|
||||
getBoundingClientRect,
|
||||
noop,
|
||||
} from 'support';
|
||||
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
|
||||
import { getEnvironment } from 'environment';
|
||||
import { OverflowBehavior } from 'options';
|
||||
import { StyleObject } from 'typings';
|
||||
import { classNameViewport, classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames';
|
||||
import { classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames';
|
||||
|
||||
interface ContentScrollSizeCacheContext {
|
||||
_viewportRect: DOMRect;
|
||||
@@ -41,20 +43,23 @@ interface OverflowOption {
|
||||
y: OverflowBehavior;
|
||||
}
|
||||
|
||||
interface ViewportArrangeCustomCssProps {
|
||||
'--viewport-arrange-width': string;
|
||||
'--viewport-arrange-height': string;
|
||||
}
|
||||
|
||||
const overlaidScrollbarsHideOffset = 42;
|
||||
const overlaidScrollbarsHideBorderStyle = `${overlaidScrollbarsHideOffset}px solid transparent`;
|
||||
|
||||
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
||||
const { _structureSetup, _getPaddingStyle, _getPaddingInfo } = lifecycleHub;
|
||||
const { _host, _padding, _viewport, _content, _contentArrange } = _structureSetup._targetObj;
|
||||
const { _structureSetup, _doViewportArrange, _getViewportPaddingStyle, _getPaddingInfo } = lifecycleHub;
|
||||
const { _host, _padding, _viewport, _viewportArrange } = _structureSetup._targetObj;
|
||||
const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache<
|
||||
WH<number>,
|
||||
ContentScrollSizeCacheContext
|
||||
>(
|
||||
(ctx) => {
|
||||
const { _viewportOffsetSize, _viewportScrollSize, _viewportRect } = ctx;
|
||||
const contentViewportScrollSize = _content ? scrollSize(_content) : _viewportScrollSize;
|
||||
return fixScrollSizeRounding(contentViewportScrollSize, _viewportOffsetSize, _viewportRect);
|
||||
return fixScrollSizeRounding(_viewportScrollSize, _viewportOffsetSize, _viewportRect);
|
||||
},
|
||||
{ _equal: equalWH }
|
||||
);
|
||||
@@ -67,8 +72,8 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
);
|
||||
|
||||
const fixScrollSizeRounding = (scrollSize: WH<number>, viewportOffsetSize: WH<number>, viewportRect: DOMRect): WH<number> => ({
|
||||
w: scrollSize.w - Math.ceil(Math.max(0, viewportRect.width - viewportOffsetSize.w)),
|
||||
h: scrollSize.h - Math.ceil(Math.max(0, viewportRect.height - viewportOffsetSize.h)),
|
||||
w: scrollSize.w - Math.round(Math.max(0, viewportRect.width - viewportOffsetSize.w)),
|
||||
h: scrollSize.h - Math.round(Math.max(0, viewportRect.height - viewportOffsetSize.h)),
|
||||
});
|
||||
|
||||
const fixFlexboxGlue = (viewportOverflowState: ViewportOverflowState, heightIntrinsic: boolean) => {
|
||||
@@ -151,42 +156,54 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
viewportOverflowState: ViewportOverflowState,
|
||||
contentScrollSize: WH<number>,
|
||||
directionIsRTL: boolean,
|
||||
contentStyleObj?: StyleObject
|
||||
viewportStyleObj?: StyleObject
|
||||
) => {
|
||||
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;
|
||||
|
||||
style(_viewport, {
|
||||
[horizontalPaddingKey]: horizontalPaddingValue + hideOffsetY,
|
||||
paddingBottom: verticalPaddingValue + hideOffsetX,
|
||||
});
|
||||
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` : '',
|
||||
};
|
||||
|
||||
// adjust content arrange / before element
|
||||
if (_contentArrange) {
|
||||
const { sheet } = _contentArrange;
|
||||
if (_viewportArrange) {
|
||||
const { sheet } = _viewportArrange;
|
||||
if (sheet) {
|
||||
const { cssRules } = sheet;
|
||||
if (cssRules) {
|
||||
if (!cssRules.length) {
|
||||
sheet.insertRule(`#${attr(_contentArrange, 'id')} + .${classNameViewportArrange}::before {}`, 0);
|
||||
sheet.insertRule(`#${attr(_viewportArrange, '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);
|
||||
ruleStyle.width = arrangeSize.w;
|
||||
ruleStyle.height = arrangeSize.h;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
style<ViewportArrangeCustomCssProps>(_viewport, {
|
||||
'--viewport-arrange-width': arrangeSize.w,
|
||||
'--viewport-arrange-height': arrangeSize.h,
|
||||
});
|
||||
}
|
||||
|
||||
styleObj[viewportHorizontalPaddingKey] = viewportHorizontalPaddingValue + hideOffsetY;
|
||||
styleObj.paddingBottom = viewportVerticalPaddingValue + hideOffsetX;
|
||||
|
||||
if (!viewportStyleObj) {
|
||||
style(_viewport, styleObj);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -195,16 +212,17 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
const { _nativeScrollbarStyling } = getEnvironment();
|
||||
const { _overflowScroll, _scrollbarsHideOffset } = viewportOverflowState;
|
||||
const { x: scrollX, y: scrollY } = _overflowScroll;
|
||||
const paddingStyle = _getPaddingStyle();
|
||||
const paddingStyle = _getViewportPaddingStyle();
|
||||
const horizontalMarginKey = directionIsRTL ? 'marginLeft' : 'marginRight';
|
||||
const horizontalPaddingValue = paddingStyle[horizontalMarginKey] as number;
|
||||
const verticalPaddingValue = paddingStyle.marginBottom as number;
|
||||
|
||||
// horizontal
|
||||
viewportStyleObj.maxWidth = `calc(100% + ${_scrollbarsHideOffset.y + horizontalPaddingValue * -1}px)`;
|
||||
viewportStyleObj[horizontalMarginKey] = -_scrollbarsHideOffset.y + horizontalPaddingValue;
|
||||
|
||||
// vertical
|
||||
viewportStyleObj.marginBottom = -_scrollbarsHideOffset.x + (paddingStyle.marginBottom as number);
|
||||
viewportStyleObj.marginBottom = -_scrollbarsHideOffset.x + verticalPaddingValue;
|
||||
|
||||
// hide overflowing scrollbars if there are any
|
||||
if (!_nativeScrollbarStyling) {
|
||||
@@ -214,6 +232,25 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
}
|
||||
};
|
||||
|
||||
const undoOverlaidScrollbarsHiding = () => {
|
||||
const paddingStyle = _getViewportPaddingStyle();
|
||||
const viewportStyle = {
|
||||
marginTop: '',
|
||||
marginRight: '',
|
||||
marginBottom: '',
|
||||
marginLeft: '',
|
||||
...paddingStyle,
|
||||
};
|
||||
const prevStyle = style(_viewport, keys(viewportStyle));
|
||||
removeClass(_viewport, classNameViewportArrange);
|
||||
style(_viewport, viewportStyle);
|
||||
|
||||
return () => {
|
||||
style(_viewport, prevStyle);
|
||||
addClass(_viewport, classNameViewportArrange);
|
||||
};
|
||||
};
|
||||
|
||||
return (updateHints, checkOption, force) => {
|
||||
const { _directionIsRTL, _heightIntrinsic, _sizeChanged, _hostMutation, _contentMutation, _paddingStyleChanged } = updateHints;
|
||||
const { _flexboxGlue, _nativeScrollbarStyling, _nativeScrollbarIsOverlaid } = getEnvironment();
|
||||
@@ -222,9 +259,10 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
const { _value: showNativeOverlaidScrollbarsOption, _changed: showNativeOverlaidScrollbarsChanged } = checkOption<boolean>(
|
||||
'nativeScrollbarsOverlaid.show'
|
||||
);
|
||||
const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
|
||||
const viewportArrange = _doViewportArrange && !showNativeOverlaidScrollbars;
|
||||
const adjustFlexboxGlue =
|
||||
!_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || heightIntrinsicChanged);
|
||||
const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
|
||||
let overflowAmuntCache: CacheValues<XY<number>> = getCurrentOverflowAmountCache(force);
|
||||
let contentScrollSizeCache: CacheValues<WH<number>> = getCurrentContentScrollSizeCache(force);
|
||||
let preMeasureViewportOverflowState: ViewportOverflowState | undefined;
|
||||
@@ -242,63 +280,43 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
fixFlexboxGlue(preMeasureViewportOverflowState, !!heightIntrinsic);
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
if (_sizeChanged || _paddingStyleChanged || _contentMutation || directionChanged) {
|
||||
const redoOverlaidScrollbarsHiding = viewportArrange ? undoOverlaidScrollbarsHiding() : noop;
|
||||
const contentSize = clientSize(_viewport);
|
||||
const viewportRect = getBoundingClientRect(_viewport);
|
||||
const viewportOffsetSize = offsetSize(_viewport);
|
||||
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);
|
||||
|
||||
let { _value: contentScrollSize, _changed: contentScrollSizeChanged } = (contentScrollSizeCache = updateContentScrollSizeCache(force, {
|
||||
let viewportScrollSize = scrollSize(_viewport);
|
||||
let viewportClientSize = contentSize;
|
||||
const { _value: contentScrollSize, _changed: contentScrollSizeChanged } = (contentScrollSizeCache = updateContentScrollSizeCache(force, {
|
||||
_viewportRect: viewportRect,
|
||||
_viewportOffsetSize: viewportOffsetSize,
|
||||
_viewportScrollSize: viewportScrollSize,
|
||||
}));
|
||||
|
||||
redoOverlaidScrollbarsHiding();
|
||||
|
||||
// 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;
|
||||
const reMeasureRequired = viewportArrange && contentScrollSizeChanged && !showNativeOverlaidScrollbars;
|
||||
|
||||
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);
|
||||
if (reMeasureRequired) {
|
||||
setContentArrange(
|
||||
preMeasureViewportOverflowState || getViewportOverflowState(showNativeOverlaidScrollbars),
|
||||
contentScrollSize!,
|
||||
directionIsRTL!
|
||||
);
|
||||
|
||||
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);
|
||||
overflowAmuntCache = updateOverflowAmountCache(force, {
|
||||
_contentScrollSize: {
|
||||
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),
|
||||
h: viewportClientSize.h + Math.max(0, contentClientSize.h - contentScrollSize!.h),
|
||||
w: viewportClientSize.w + Math.max(0, contentSize.w - contentScrollSize!.w),
|
||||
h: viewportClientSize.h + Math.max(0, contentSize.h - contentScrollSize!.h),
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -325,16 +343,13 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
marginLeft: '',
|
||||
maxWidth: '',
|
||||
};
|
||||
const contentStyle: StyleObject = {
|
||||
borderTop: '',
|
||||
borderRight: '',
|
||||
borderBottom: '',
|
||||
borderLeft: '',
|
||||
};
|
||||
|
||||
const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, overflowAmount!, overflow, viewportStyle);
|
||||
hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportStyle);
|
||||
setContentArrange(viewportOverflowState, contentScrollSize!, directionIsRTL!, contentStyle);
|
||||
|
||||
if (_doViewportArrange) {
|
||||
setContentArrange(viewportOverflowState, contentScrollSize!, directionIsRTL!, viewportStyle);
|
||||
}
|
||||
|
||||
if (adjustFlexboxGlue) {
|
||||
fixFlexboxGlue(viewportOverflowState, !!heightIntrinsic);
|
||||
@@ -349,7 +364,6 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
|
||||
// TODO: remove lifecycleHub get set padding if not needed
|
||||
|
||||
style(_viewport, viewportStyle);
|
||||
style(_content, contentStyle);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@ import { StyleObject } from 'typings';
|
||||
import { getEnvironment } from 'environment';
|
||||
|
||||
export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
|
||||
const { _host, _padding, _viewport } = lifecycleHub._structureSetup._targetObj;
|
||||
const { _setPaddingInfo, _setViewportPaddingStyle, _structureSetup } = lifecycleHub;
|
||||
const { _host, _padding, _viewport } = _structureSetup._targetObj;
|
||||
const { _update: updatePaddingCache, _current: currentPaddingCache } = createCache(() => topRightBottomLeft(_host, 'padding'), {
|
||||
_equal: equalTRBL,
|
||||
});
|
||||
@@ -76,11 +77,18 @@ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =>
|
||||
style(_padding || _viewport, paddingStyle);
|
||||
style(_viewport, viewportStyle);
|
||||
|
||||
lifecycleHub._setPaddingInfo({
|
||||
_setPaddingInfo({
|
||||
_absolute: !paddingRelative,
|
||||
_padding: padding!,
|
||||
});
|
||||
lifecycleHub._setPaddingStyle(!_padding ? paddingStyle : null);
|
||||
_setViewportPaddingStyle(
|
||||
_padding
|
||||
? viewportStyle
|
||||
: {
|
||||
...paddingStyle,
|
||||
...viewportStyle,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@import './sizeobserver.scss';
|
||||
@import './trinsicobserver.scss';
|
||||
@import './structurelifecycle.scss';
|
||||
|
||||
.os-environment {
|
||||
--css-custom-prop: -1;
|
||||
@@ -70,7 +69,7 @@
|
||||
}
|
||||
.os-environment,
|
||||
.os-viewport {
|
||||
-ms-overflow-style: -ms-autohiding-scrollbar !important;
|
||||
-ms-overflow-style: scrollbar !important;
|
||||
}
|
||||
.os-viewport-scrollbar-styled.os-environment,
|
||||
.os-viewport-scrollbar-styled.os-viewport {
|
||||
@@ -87,6 +86,34 @@
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.os-host,
|
||||
.os-padding {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.os-padding,
|
||||
.os-viewport {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
flex: auto;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
max-width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.os-viewport {
|
||||
--viewport-arrange-width: 0;
|
||||
--viewport-arrange-height: 0;
|
||||
}
|
||||
|
||||
.os-viewport-arrange::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
@@ -94,8 +121,6 @@
|
||||
z-index: -1;
|
||||
min-width: 1px;
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.os-host {
|
||||
padding: 5px 50px 15px 20px;
|
||||
width: var(--viewport-arrange-width);
|
||||
height: var(--viewport-arrange-height);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface OSTargetContext {
|
||||
|
||||
export interface PreparedOSTargetObject extends Required<InternalVersionOf<OSTargetObject>> {
|
||||
_host: HTMLElement;
|
||||
_contentArrange: HTMLStyleElement | null;
|
||||
_viewportArrange: HTMLStyleElement | null;
|
||||
}
|
||||
|
||||
export interface StructureSetup {
|
||||
@@ -52,7 +52,7 @@ const unwrap = (elm: HTMLElement | null | undefined) => {
|
||||
};
|
||||
|
||||
let contentArrangeCounter = 0;
|
||||
const createUniqueContentArrangeElement = () => {
|
||||
const createUniqueViewportArrangeElement = () => {
|
||||
const elm = document.createElement('style');
|
||||
|
||||
attr(elm, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`);
|
||||
@@ -174,15 +174,13 @@ export const createStructureSetup = (target: OSTarget | OSTargetObject): Structu
|
||||
const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment();
|
||||
if (_nativeScrollbarStyling) {
|
||||
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
|
||||
} else if (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y) {
|
||||
if (true) {
|
||||
const contentArrangeElm = createUniqueContentArrangeElement();
|
||||
} else if (!_cssCustomProperties && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y)) {
|
||||
const viewportArrangeElm = createUniqueViewportArrangeElement();
|
||||
|
||||
insertBefore(_viewport, contentArrangeElm);
|
||||
push(destroyFns, removeElements.bind(0, contentArrangeElm));
|
||||
insertBefore(_viewport, viewportArrangeElm);
|
||||
push(destroyFns, removeElements.bind(0, viewportArrangeElm));
|
||||
|
||||
obj._contentArrange = contentArrangeElm;
|
||||
}
|
||||
obj._viewportArrange = viewportArrangeElm;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
.os-host,
|
||||
.os-padding {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.os-padding,
|
||||
.os-viewport {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
flex: auto;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
max-width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.os-content {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { each, keys } from 'support/utils';
|
||||
import { isString, isNumber, isArray } from 'support/utils/types';
|
||||
import { isString, isNumber, isArray, isUndefined } from 'support/utils/types';
|
||||
import { PlainObject, StyleObject } from 'typings';
|
||||
|
||||
export interface TRBL {
|
||||
@@ -37,8 +37,13 @@ const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: s
|
||||
computedStyle != null ? computedStyle[prop] || computedStyle.getPropertyValue(prop) : elm.style[prop];
|
||||
const setCSSVal = (elm: HTMLElement | null | undefined, prop: string, val: string | number): void => {
|
||||
try {
|
||||
if (elm && elm.style[prop] !== undefined) {
|
||||
elm.style[prop] = adaptCSSVal(prop, val);
|
||||
if (elm) {
|
||||
const { style } = elm;
|
||||
if (!isUndefined(style[prop])) {
|
||||
style[prop] = adaptCSSVal(prop, val);
|
||||
} else {
|
||||
style.setProperty(prop, val as string);
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
};
|
||||
@@ -48,10 +53,13 @@ const setCSSVal = (elm: HTMLElement | null | undefined, prop: string, val: strin
|
||||
* @param elm The element to which the styles shall be applied to / be read from.
|
||||
* @param styles The styles which shall be set or read.
|
||||
*/
|
||||
export function style(elm: HTMLElement | null | undefined, styles: StyleObject): void;
|
||||
export function style(elm: HTMLElement | null | undefined, styles: string): string;
|
||||
export function style(elm: HTMLElement | null | undefined, styles: Array<string> | string): { [key: string]: string };
|
||||
export function style(elm: HTMLElement | null | undefined, styles: StyleObject | Array<string> | string): { [key: string]: string } | string | void {
|
||||
export function style<CustomCssProps>(elm: HTMLElement | null | undefined, styles: StyleObject<CustomCssProps>): void;
|
||||
export function style<CustomCssProps>(elm: HTMLElement | null | undefined, styles: string): string;
|
||||
export function style<CustomCssProps>(elm: HTMLElement | null | undefined, styles: Array<string> | string): { [key: string]: string };
|
||||
export function style<CustomCssProps>(
|
||||
elm: HTMLElement | null | undefined,
|
||||
styles: StyleObject<CustomCssProps> | Array<string> | string
|
||||
): { [key: string]: string } | string | void {
|
||||
const getSingleStyle = isString(styles);
|
||||
const getStyles = isArray(styles) || getSingleStyle;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export type PlainObject<T = any> = { [name: string]: T };
|
||||
|
||||
export type StyleObject = {
|
||||
[K in keyof CSSStyleDeclaration]?: string | number;
|
||||
export type StyleObject<CustomCssProps = {}> = {
|
||||
[K in keyof (CSSStyleDeclaration & CustomCssProps)]?: string | number;
|
||||
}
|
||||
|
||||
export type InternalVersionOf<T> = {
|
||||
|
||||
+1
-1
@@ -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, content: null });
|
||||
window.os = OverlayScrollbars({ target: targetElm, padding: null });
|
||||
|
||||
export const resize = (element: HTMLElement) => {
|
||||
const strMouseTouchDownEvent = 'mousedown touchstart';
|
||||
|
||||
@@ -35,6 +35,7 @@ body {
|
||||
min-width: 200px;
|
||||
max-height: 300px;
|
||||
max-width: 320px;
|
||||
//padding: 5px 50px 15px 20px;
|
||||
}
|
||||
|
||||
#resize {
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
import { optionsTemplateTypes as oTypes } from 'support';
|
||||
import { createLifecycleBase } from 'lifecycles/lifecycleBase';
|
||||
|
||||
interface TestLifecycleOptions {
|
||||
number?: number;
|
||||
string?: string;
|
||||
nested?: {
|
||||
boolean?: boolean;
|
||||
number?: number;
|
||||
};
|
||||
}
|
||||
|
||||
const createLifecycle = (initalOptions?: TestLifecycleOptions, updateFn?: (...args: any) => any) =>
|
||||
createLifecycleBase<TestLifecycleOptions>(
|
||||
{
|
||||
number: [0, oTypes.number],
|
||||
string: ['hi', oTypes.string],
|
||||
nested: {
|
||||
boolean: [false, oTypes.boolean],
|
||||
number: [0, oTypes.number],
|
||||
},
|
||||
},
|
||||
initalOptions,
|
||||
updateFn || (() => {})
|
||||
);
|
||||
|
||||
describe('lifecycleBase', () => {
|
||||
describe('options', () => {
|
||||
test('correct default options', () => {
|
||||
const { _options } = createLifecycle();
|
||||
|
||||
const defaultOptions = _options();
|
||||
expect(defaultOptions.number).toBe(0);
|
||||
expect(defaultOptions.string).toBe('hi');
|
||||
expect(defaultOptions.nested?.boolean).toBe(false);
|
||||
expect(defaultOptions.nested?.number).toBe(0);
|
||||
});
|
||||
|
||||
test('correct initial options', () => {
|
||||
const { _options } = createLifecycle({ number: 1, nested: { boolean: true } });
|
||||
|
||||
const initOptions = _options();
|
||||
expect(initOptions.number).toBe(1);
|
||||
expect(initOptions.string).toBe('hi');
|
||||
expect(initOptions.nested?.boolean).toBe(true);
|
||||
expect(initOptions.nested?.number).toBe(0);
|
||||
});
|
||||
|
||||
test('correct options change', () => {
|
||||
const { _options } = createLifecycle();
|
||||
|
||||
const options = _options();
|
||||
expect(options.number).toBe(0);
|
||||
expect(options.string).toBe('hi');
|
||||
expect(options.nested?.boolean).toBe(false);
|
||||
expect(options.nested?.number).toBe(0);
|
||||
|
||||
const changedOptions = _options({ number: 2, nested: { number: 3 } });
|
||||
expect(changedOptions.number).toBe(2);
|
||||
expect(changedOptions.string).toBe('hi');
|
||||
expect(changedOptions.nested?.boolean).toBe(false);
|
||||
expect(changedOptions.nested?.number).toBe(3);
|
||||
});
|
||||
|
||||
test('correct options validation', () => {
|
||||
const originalWarn = console.warn;
|
||||
const mockWarn = jest.fn();
|
||||
console.warn = mockWarn;
|
||||
|
||||
// @ts-ignore
|
||||
const { _options } = createLifecycle({ string: 123 });
|
||||
expect(mockWarn).toBeCalledTimes(1);
|
||||
|
||||
const options = _options();
|
||||
expect(options.string).toBe('hi');
|
||||
|
||||
// @ts-ignore
|
||||
const changedOptions = _options({ number: 'string', nested: null });
|
||||
expect(mockWarn).toBeCalledTimes(2);
|
||||
expect(changedOptions.number).toBe(0);
|
||||
expect(changedOptions.string).toBe('hi');
|
||||
expect(changedOptions.nested?.boolean).toBe(false);
|
||||
expect(changedOptions.nested?.number).toBe(0);
|
||||
|
||||
console.warn = originalWarn;
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
test('initial call', () => {
|
||||
const updateFn = jest.fn();
|
||||
createLifecycle({}, updateFn);
|
||||
|
||||
expect(updateFn).toBeCalledTimes(1);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(true, expect.objectContaining({}));
|
||||
});
|
||||
|
||||
test('updates correctly on options change', () => {
|
||||
let checkOption = (...args: any): any => {}; // eslint-disable-line
|
||||
const updateFn = jest.fn();
|
||||
const update = (force: any, check: any): void => {
|
||||
updateFn(force, check);
|
||||
checkOption = check;
|
||||
};
|
||||
const { _options } = createLifecycle({}, update);
|
||||
|
||||
_options({ number: 5 });
|
||||
expect(updateFn).toBeCalledTimes(2);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
let { _value, _changed } = checkOption('number');
|
||||
expect(_value).toBe(5);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(false);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(false);
|
||||
|
||||
_options({ number: 5, string: 'test', nested: { number: 3 } });
|
||||
expect(updateFn).toBeCalledTimes(3);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
({ _value, _changed } = checkOption('number'));
|
||||
expect(_value).toBe(5);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('test');
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(false);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(3);
|
||||
expect(_changed).toBe(true);
|
||||
|
||||
_options({ string: 'test', nested: { number: 3 } });
|
||||
expect(updateFn).toBeCalledTimes(3);
|
||||
});
|
||||
|
||||
test('updates correctly on update call', () => {
|
||||
let checkOption = (...args: any): any => {}; // eslint-disable-line
|
||||
const updateFn = jest.fn();
|
||||
const update = (force: any, check: any): void => {
|
||||
updateFn(force, check);
|
||||
checkOption = check;
|
||||
};
|
||||
const { _update, _options } = createLifecycle({}, update);
|
||||
|
||||
_update();
|
||||
expect(updateFn).toBeCalledTimes(2);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
let { _value, _changed } = checkOption('number');
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(false);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(false);
|
||||
|
||||
_update(true);
|
||||
expect(updateFn).toBeCalledTimes(3);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(true, expect.objectContaining({}));
|
||||
({ _value, _changed } = checkOption('number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(false);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(true);
|
||||
|
||||
_options({ number: 3, nested: { boolean: true } });
|
||||
_update(true);
|
||||
expect(updateFn).toBeCalledTimes(5);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(true, expect.objectContaining({}));
|
||||
({ _value, _changed } = checkOption('number'));
|
||||
expect(_value).toBe(3);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(true);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(true);
|
||||
|
||||
_options({ number: 3, nested: { boolean: true } });
|
||||
_update();
|
||||
expect(updateFn).toBeCalledTimes(6);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
({ _value, _changed } = checkOption('number'));
|
||||
expect(_value).toBe(3);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(true);
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(false);
|
||||
|
||||
_options({ number: 4, nested: { boolean: false }, string: 'hi' });
|
||||
expect(updateFn).toBeCalledTimes(7);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
({ _value, _changed } = checkOption('number'));
|
||||
expect(_value).toBe(4);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('string'));
|
||||
expect(_value).toBe('hi');
|
||||
expect(_changed).toBe(false);
|
||||
({ _value, _changed } = checkOption('nested.boolean'));
|
||||
expect(_value).toBe(false);
|
||||
expect(_changed).toBe(true);
|
||||
({ _value, _changed } = checkOption('nested.number'));
|
||||
expect(_value).toBe(0);
|
||||
expect(_changed).toBe(false);
|
||||
|
||||
_update();
|
||||
expect(updateFn).toBeCalledTimes(8);
|
||||
expect(updateFn).toHaveBeenLastCalledWith(false, expect.objectContaining({}));
|
||||
|
||||
_options();
|
||||
expect(updateFn).toBeCalledTimes(8);
|
||||
|
||||
_options({ number: 4, nested: { boolean: false }, string: 'hi' });
|
||||
expect(updateFn).toBeCalledTimes(8);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isEmptyObject } from 'support/utils/object';
|
||||
import { isString, isPlainObject } from 'support/utils/types';
|
||||
import { style, hide, show, topRightBottomLeft } from 'support/dom/style';
|
||||
import { StyleObject } from 'typings';
|
||||
|
||||
describe('dom style', () => {
|
||||
afterEach(() => {
|
||||
@@ -34,6 +35,10 @@ describe('dom style', () => {
|
||||
expect(document.body.style.width).toBe('');
|
||||
style(document.body, { width: '123px' });
|
||||
expect(document.body.style.width).toBe('123px');
|
||||
|
||||
expect(document.body.style.getPropertyValue('--custom')).toBe('');
|
||||
style(document.body, { '--custom': '123px' });
|
||||
expect(document.body.style.getPropertyValue('--custom')).toBe('123px');
|
||||
});
|
||||
|
||||
test('single add px', () => {
|
||||
@@ -54,11 +59,13 @@ describe('dom style', () => {
|
||||
expect(document.body.style.opacity).toBe('');
|
||||
expect(document.body.style.zIndex).toBe('');
|
||||
expect(document.body.style.lineHeight).toBe('');
|
||||
style(document.body, { width: '123px', height: 321, opacity: '0.5', zIndex: 1 });
|
||||
expect(document.body.style.getPropertyValue('--custom')).toBe('');
|
||||
style(document.body, { width: '123px', height: 321, opacity: '0.5', zIndex: 1, '--custom': '123px' });
|
||||
expect(document.body.style.width).toBe('123px');
|
||||
expect(document.body.style.height).toBe('321px');
|
||||
expect(document.body.style.opacity).toBe('0.5');
|
||||
expect(document.body.style.zIndex).toBe('1');
|
||||
expect(document.body.style.getPropertyValue('--custom')).toBe('123px');
|
||||
});
|
||||
|
||||
test('null', () => {
|
||||
|
||||
Reference in New Issue
Block a user