improve overflow lifecycle for fractional device pixel ratios

This commit is contained in:
Rene
2021-05-08 02:14:54 +02:00
parent d16d8f2f82
commit a109bdc6b1
3 changed files with 115 additions and 107 deletions
@@ -22,15 +22,10 @@ import { OverflowBehavior } from 'options';
import { StyleObject } from 'typings'; import { StyleObject } from 'typings';
import { classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames'; import { classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames';
interface ContentScrollSizeCacheContext {
_viewportRect: DOMRect;
_viewportOffsetSize: WH<number>;
_viewportScrollSize: WH<number>;
}
interface OverflowAmountCacheContext { interface OverflowAmountCacheContext {
_contentScrollSize: WH<number>; _viewportScrollSize: WH<number>;
_viewportSize: WH<number>; _viewportClientSize: WH<number>;
_viewportSizeFraction: WH<number>;
} }
interface ViewportOverflowState { interface ViewportOverflowState {
@@ -49,7 +44,20 @@ interface OverflowOption {
y: OverflowBehavior; y: OverflowBehavior;
} }
const { max, abs, round } = Math;
const overlaidScrollbarsHideOffset = 42; const overlaidScrollbarsHideOffset = 42;
const whCacheOptions = {
_equal: equalWH,
_initialValue: { w: 0, h: 0 },
};
const sizeFraction = (elm: HTMLElement): WH<number> => {
const viewportOffsetSize = offsetSize(elm);
const viewportRect = getBoundingClientRect(elm);
return {
w: viewportRect.width - viewportOffsetSize.w,
h: viewportRect.height - viewportOffsetSize.h,
};
};
/** /**
* Lifecycle with the responsibility to set the correct overflow and scrollbar hiding styles of the viewport element. * Lifecycle with the responsibility to set the correct overflow and scrollbar hiding styles of the viewport element.
@@ -59,41 +67,21 @@ const overlaidScrollbarsHideOffset = 42;
export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => { export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
const { _structureSetup, _doViewportArrange, _getLifecycleCommunication, _setLifecycleCommunication } = lifecycleHub; const { _structureSetup, _doViewportArrange, _getLifecycleCommunication, _setLifecycleCommunication } = lifecycleHub;
const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj; const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj;
const { _update: updateContentScrollSizeCache, _current: getCurrentContentScrollSizeCache } = createCache< const { _update: updateViewportSizeFraction, _current: getCurrentViewportSizeFraction } = createCache<WH<number>>(
WH<number>, () => sizeFraction(_viewport),
ContentScrollSizeCacheContext whCacheOptions
>((ctx) => fixScrollSizeRounding(ctx._viewportScrollSize, ctx._viewportOffsetSize, ctx._viewportRect), { _equal: equalWH }); );
const { _update: updateViewportScrollSizeCache, _current: getCurrentViewportScrollSizeCache } = createCache<WH<number>>(
() => scrollSize(_viewport),
whCacheOptions
);
const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache<WH<number>, OverflowAmountCacheContext>( const { _update: updateOverflowAmountCache, _current: getCurrentOverflowAmountCache } = createCache<WH<number>, OverflowAmountCacheContext>(
(ctx) => { ({ _viewportScrollSize, _viewportClientSize, _viewportSizeFraction }) => ({
// @ts-ignore w: round(max(0, _viewportScrollSize.w - _viewportClientSize.w) - max(0, _viewportSizeFraction.w)),
//const { scrollLeftMax, scrollTopMax } = _viewport; h: round(max(0, _viewportScrollSize.h - _viewportClientSize.h) - max(0, _viewportSizeFraction.h)),
//const multiplicatorW = (isNumber(scrollLeftMax) ? scrollLeftMax !== 0 : true) ? 1 : 0; }),
//const multiplicatorH = (isNumber(scrollTopMax) ? scrollTopMax !== 0 : true) ? 1 : 0; whCacheOptions
return {
w: Math.round(Math.max(0, ctx._contentScrollSize.w - ctx._viewportSize.w)),
h: Math.round(Math.max(0, ctx._contentScrollSize.h - ctx._viewportSize.h)),
};
},
{ _equal: equalWH, _initialValue: { w: 0, h: 0 } }
); );
/**
* 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> => {
const wFix = viewportRect.width - viewportOffsetSize.w;
const hFix = viewportRect.height - viewportOffsetSize.h;
return {
w: viewportScrollSize.w + (Math.abs(wFix) < 1 ? wFix : 0),
h: viewportScrollSize.h + (Math.abs(hFix) < 1 ? hFix : 0),
};
};
/** /**
* Applies a fixed height to the viewport so it can't overflow or underflow the host element. * Applies a fixed height to the viewport so it can't overflow or underflow the host element.
@@ -200,11 +188,16 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
/** /**
* Sets the styles of the viewport arrange element. * Sets the styles of the viewport arrange element.
* @param viewportOverflowState The viewport overflow state according to which the scrollbars shall be hidden. * @param viewportOverflowState The viewport overflow state according to which the scrollbars shall be hidden.
* @param contentScrollSize The content scroll size. * @param viewportScrollSize The content scroll size.
* @param directionIsRTL Whether the direction is RTL or not. * @param directionIsRTL Whether the direction is RTL or not.
* @returns A boolean which indicates whether the viewport arrange element was adjusted. * @returns A boolean which indicates whether the viewport arrange element was adjusted.
*/ */
const arrangeViewport = (viewportOverflowState: ViewportOverflowState, contentScrollSize: WH<number>, directionIsRTL: boolean) => { const arrangeViewport = (
viewportOverflowState: ViewportOverflowState,
viewportScrollSize: WH<number>,
viewportSizeFraction: WH<number>,
directionIsRTL: boolean
) => {
if (_doViewportArrange) { if (_doViewportArrange) {
const { _scrollbarsHideOffset, _scrollbarsHideOffsetArrange } = viewportOverflowState; const { _scrollbarsHideOffset, _scrollbarsHideOffsetArrange } = viewportOverflowState;
const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange; const { x: arrangeX, y: arrangeY } = _scrollbarsHideOffsetArrange;
@@ -213,9 +206,11 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const viewportArrangeHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingRight' : 'paddingLeft'; const viewportArrangeHorizontalPaddingKey: keyof StyleObject = directionIsRTL ? 'paddingRight' : 'paddingLeft';
const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[viewportArrangeHorizontalPaddingKey] as number; const viewportArrangeHorizontalPaddingValue = viewportPaddingStyle[viewportArrangeHorizontalPaddingKey] as number;
const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number; const viewportArrangeVerticalPaddingValue = viewportPaddingStyle.paddingTop as number;
const fractionalContentWidth = viewportScrollSize.w + (abs(viewportSizeFraction.w) < 1 ? viewportSizeFraction.w : 0);
const fractionalContenHeight = viewportScrollSize.h + (abs(viewportSizeFraction.h) < 1 ? viewportSizeFraction.h : 0);
const arrangeSize = { const arrangeSize = {
w: hideOffsetY && arrangeY ? `${hideOffsetY + contentScrollSize.w - viewportArrangeHorizontalPaddingValue}px` : '', w: hideOffsetY && arrangeY ? `${hideOffsetY + fractionalContentWidth - viewportArrangeHorizontalPaddingValue}px` : '',
h: hideOffsetX && arrangeX ? `${hideOffsetX + contentScrollSize.h - viewportArrangeVerticalPaddingValue}px` : '', h: hideOffsetX && arrangeX ? `${hideOffsetX + fractionalContenHeight - viewportArrangeVerticalPaddingValue}px` : '',
}; };
// adjust content arrange / before element // adjust content arrange / before element
@@ -349,8 +344,9 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y; const showNativeOverlaidScrollbars = showNativeOverlaidScrollbarsOption && _nativeScrollbarIsOverlaid.x && _nativeScrollbarIsOverlaid.y;
const adjustFlexboxGlue = const adjustFlexboxGlue =
!_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || heightIntrinsicChanged); !_flexboxGlue && (_sizeChanged || _contentMutation || _hostMutation || showNativeOverlaidScrollbarsChanged || heightIntrinsicChanged);
let viewportSizeFractionCache: CacheValues<WH<number>> = getCurrentViewportSizeFraction(force);
let viewportScrollSizeCache: CacheValues<WH<number>> = getCurrentViewportScrollSizeCache(force);
let overflowAmuntCache: CacheValues<WH<number>> = getCurrentOverflowAmountCache(force); let overflowAmuntCache: CacheValues<WH<number>> = getCurrentOverflowAmountCache(force);
let contentScrollSizeCache: CacheValues<WH<number>> = getCurrentContentScrollSizeCache(force);
let preMeasureViewportOverflowState: ViewportOverflowState | undefined; let preMeasureViewportOverflowState: ViewportOverflowState | undefined;
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) { if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarStyling) {
@@ -372,49 +368,47 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
directionIsRTL!, directionIsRTL!,
preMeasureViewportOverflowState preMeasureViewportOverflowState
); );
const contentSize = clientSize(_viewport); const { _value: viewportSizeFraction, _changed: viewportSizeFractionCahnged } = (viewportSizeFractionCache = updateViewportSizeFraction(force));
const viewportRect = getBoundingClientRect(_viewport); const { _value: viewportScrollSize, _changed: viewportScrollSizeChanged } = (viewportScrollSizeCache = updateViewportScrollSizeCache(force));
const viewportOffsetSize = offsetSize(_viewport); const viewportContentSize = clientSize(_viewport);
let viewportScrollSize = scrollSize(_viewport); let arrangedViewportScrollSize = viewportScrollSize!;
let viewportClientSize = contentSize; let arrangedViewportClientSize = viewportContentSize;
const { _value: contentScrollSize, _changed: contentScrollSizeChanged } = (contentScrollSizeCache = updateContentScrollSizeCache(force, {
_viewportRect: viewportRect,
_viewportOffsetSize: viewportOffsetSize,
_viewportScrollSize: viewportScrollSize,
}));
_redoViewportArrange(); _redoViewportArrange();
// if re measure is required (only required if content arrange strategy is used) // if re measure is required (only required if content arrange strategy is used)
if ( if (
(contentScrollSizeChanged || showNativeOverlaidScrollbarsChanged) && (viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) &&
undoViewportArrangeOverflowState && undoViewportArrangeOverflowState &&
!showNativeOverlaidScrollbars && !showNativeOverlaidScrollbars &&
arrangeViewport(undoViewportArrangeOverflowState, contentScrollSize!, directionIsRTL!) arrangeViewport(undoViewportArrangeOverflowState, viewportScrollSize!, viewportSizeFraction!, directionIsRTL!)
) { ) {
viewportClientSize = clientSize(_viewport); arrangedViewportClientSize = clientSize(_viewport);
viewportScrollSize = fixScrollSizeRounding(scrollSize(_viewport), offsetSize(_viewport), getBoundingClientRect(_viewport)); arrangedViewportScrollSize = scrollSize(_viewport);
} }
overflowAmuntCache = updateOverflowAmountCache(force, { overflowAmuntCache = updateOverflowAmountCache(force, {
_contentScrollSize: { _viewportSizeFraction: viewportSizeFraction!,
w: Math.max(contentScrollSize!.w, viewportScrollSize.w), _viewportScrollSize: {
h: Math.max(contentScrollSize!.h, viewportScrollSize.h), w: max(viewportScrollSize!.w, arrangedViewportScrollSize.w),
h: max(viewportScrollSize!.h, arrangedViewportScrollSize.h),
}, },
_viewportSize: { _viewportClientSize: {
w: viewportClientSize.w + Math.max(0, contentSize.w - contentScrollSize!.w), w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - viewportScrollSize!.w),
h: viewportClientSize.h + Math.max(0, contentSize.h - contentScrollSize!.h), h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - viewportScrollSize!.h),
}, },
}); });
} }
const { _value: overflow, _changed: overflowChanged } = checkOption<OverflowOption>('overflow'); const { _value: viewportSizeFraction, _changed: viewportSizeFractionChanged } = viewportSizeFractionCache;
const { _value: contentScrollSize, _changed: contentScrollSizeChanged } = contentScrollSizeCache; const { _value: viewportScrollSize, _changed: viewportScrollSizeChanged } = viewportScrollSizeCache;
const { _value: overflowAmount, _changed: overflowAmountChanged } = overflowAmuntCache; const { _value: overflowAmount, _changed: overflowAmountChanged } = overflowAmuntCache;
const { _value: overflow, _changed: overflowChanged } = checkOption<OverflowOption>('overflow');
if ( if (
_paddingStyleChanged || _paddingStyleChanged ||
contentScrollSizeChanged || viewportSizeFractionChanged ||
viewportScrollSizeChanged ||
overflowAmountChanged || overflowAmountChanged ||
overflowChanged || overflowChanged ||
showNativeOverlaidScrollbarsChanged || showNativeOverlaidScrollbarsChanged ||
@@ -432,7 +426,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
}; };
const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, overflowAmount!, overflow, viewportStyle); const viewportOverflowState = setViewportOverflowState(showNativeOverlaidScrollbars, overflowAmount!, overflow, viewportStyle);
const viewportArranged = arrangeViewport(viewportOverflowState, contentScrollSize!, directionIsRTL!); const viewportArranged = arrangeViewport(viewportOverflowState, viewportScrollSize!, viewportSizeFraction!, directionIsRTL!);
hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle); hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle);
if (adjustFlexboxGlue) { if (adjustFlexboxGlue) {
@@ -4,7 +4,7 @@ import './handleEnvironment';
import should from 'should'; import should from 'should';
import { resize } from '@/testing-browser/Resize'; import { resize } from '@/testing-browser/Resize';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult'; import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { generateClassChangeSelectCallback, iterateSelect, selectOption } from '@/testing-browser/Select'; import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout'; import { timeout } from '@/testing-browser/timeout';
import { OverlayScrollbars } from 'overlayscrollbars'; import { OverlayScrollbars } from 'overlayscrollbars';
import { assignDeep, clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support'; import { assignDeep, clientSize, from, getBoundingClientRect, style, parent, addClass, WH, removeAttr } from 'support';
@@ -57,19 +57,10 @@ const getMetrics = (elm: HTMLElement): Metrics => {
const hasOverflow = (elm: HTMLElement) => { const hasOverflow = (elm: HTMLElement) => {
const measure = scrollMeasure(elm); const measure = scrollMeasure(elm);
return {
elm.scrollLeft = 9999; x: measure.width > 0,
elm.scrollTop = 9999; y: measure.height > 0,
const hasOverflow = {
x: measure.width > 0 && elm.scrollLeft >= 1,
y: measure.height > 0 && elm.scrollTop >= 1,
}; };
elm.scrollLeft = 0;
elm.scrollTop = 0;
return hasOverflow;
}; };
return { return {
@@ -101,6 +92,8 @@ const metricsDimensionsEqual = (a: Metrics, b: Metrics) => {
return JSON.stringify(aDimensions) === JSON.stringify(bDimensions); return JSON.stringify(aDimensions) === JSON.stringify(bDimensions);
}; };
const isFractionalPixelRatio = () => window.devicePixelRatio % 1 !== 0;
const plusMinusArr = (original: number, plusMinus: number) => { const plusMinusArr = (original: number, plusMinus: number) => {
return [original, original + plusMinus, original - plusMinus]; return [original, original + plusMinus, original - plusMinus];
}; };
@@ -158,9 +151,13 @@ target!.querySelector('.os-viewport')?.addEventListener('scroll', (e) => {
comparison!.scrollTop = viewport.scrollTop; comparison!.scrollTop = viewport.scrollTop;
}); });
resize(target!).addResizeListener((width, height) => style(comparison, { width, height })); resize(target!).addResizeListener((width, height) => {
style(comparison, { width, height });
});
//resize(comparison!).addResizeListener((width, height) => style(target, { width, height })); //resize(comparison!).addResizeListener((width, height) => style(target, { width, height }));
resize(targetResize!).addResizeListener((width, height) => style(comparisonResize, { width, height })); resize(targetResize!).addResizeListener((width, height) => {
style(comparisonResize, { width, height });
});
//resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height })); //resize(comparisonRes!).addResizeListener((width, height) => style(targetRes, { width, height }));
const selectCallbackEnv = generateClassChangeSelectCallback(from(envElms)); const selectCallbackEnv = generateClassChangeSelectCallback(from(envElms));
@@ -219,19 +216,32 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
should.equal(targetMetrics.size.width, comparisonMetrics.size.width, 'Size width equality.'); should.equal(targetMetrics.size.width, comparisonMetrics.size.width, 'Size width equality.');
should.equal(targetMetrics.size.height, comparisonMetrics.size.height, 'Size height equality.'); should.equal(targetMetrics.size.height, comparisonMetrics.size.height, 'Size height equality.');
//should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.'); if (isFractionalPixelRatio()) {
//should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.'); should.ok(plusMinusArr(targetMetrics.scroll.width, 1).indexOf(comparisonMetrics.scroll.width) > -1, 'Scroll width equality. (+-1)');
should.ok(plusMinusArr(targetMetrics.scroll.height, 1).indexOf(comparisonMetrics.scroll.height) > -1, 'Scroll height equality. (+-1)');
//should.equal(osInstance.state()._overflowAmount.w, comparisonMetrics.scroll.width, 'Overflow amount width equality.'); should.ok(
//should.equal(osInstance.state()._overflowAmount.h, comparisonMetrics.scroll.height, 'Overflow amount height equality.'); plusMinusArr(osInstance.state()._overflowAmount.w, 1).indexOf(comparisonMetrics.scroll.width) > -1,
'Overflow amount width equality. (+-1)'
);
should.ok(
plusMinusArr(osInstance.state()._overflowAmount.h, 1).indexOf(comparisonMetrics.scroll.height) > -1,
'Overflow amount height equality. (+-1)'
);
} else {
should.equal(targetMetrics.scroll.width, comparisonMetrics.scroll.width, 'Scroll width equality.');
should.equal(targetMetrics.scroll.height, comparisonMetrics.scroll.height, 'Scroll height equality.');
should.equal(targetMetrics.hasOverflow.x, comparisonMetrics.hasOverflow.x, 'Has overflow x equality.'); should.equal(osInstance.state()._overflowAmount.w, comparisonMetrics.scroll.width, 'Overflow amount width equality.');
should.equal(targetMetrics.hasOverflow.y, comparisonMetrics.hasOverflow.y, 'Has overflow y equality.'); should.equal(osInstance.state()._overflowAmount.h, comparisonMetrics.scroll.height, 'Overflow amount height equality.');
}
//should.equal(targetMetrics.hasOverflow.x, comparisonMetrics.hasOverflow.x, 'Has overflow x equality.');
//should.equal(targetMetrics.hasOverflow.y, comparisonMetrics.hasOverflow.y, 'Has overflow y equality.');
if (targetMetrics.hasOverflow.x) { if (targetMetrics.hasOverflow.x) {
should.equal(style(targetViewport!, 'overflowX'), 'scroll', 'Overflow-X should result in scroll.'); should.equal(style(targetViewport!, 'overflowX'), 'scroll', 'Overflow-X should result in scroll.');
should.ok(osInstance.state()._overflowAmount.w > 0, 'Overflow amount width should be > 0 with overflow.'); should.ok(osInstance.state()._overflowAmount.w > 0, 'Overflow amount width should be > 0 with overflow.');
//should.ok(plusMinusArr(targetMetrics.scroll.width, 1).indexOf(comparisonMetrics.scroll.width) > -1, 'Scroll width equality. (+-1)');
} else { } else {
should.notEqual(style(targetViewport!, 'overflowX'), 'scroll', 'No Overflow-X shouldnt result in scroll.'); should.notEqual(style(targetViewport!, 'overflowX'), 'scroll', 'No Overflow-X shouldnt result in scroll.');
should.equal(osInstance.state()._overflowAmount.w, 0, 'Overflow amount width should be 0 without overflow.'); should.equal(osInstance.state()._overflowAmount.w, 0, 'Overflow amount width should be 0 without overflow.');
@@ -240,7 +250,6 @@ const checkMetrics = async (checkComparison: CheckComparisonObj) => {
if (targetMetrics.hasOverflow.y) { if (targetMetrics.hasOverflow.y) {
should.equal(style(targetViewport!, 'overflowY'), 'scroll', 'Overflow-Y should result in scroll.'); should.equal(style(targetViewport!, 'overflowY'), 'scroll', 'Overflow-Y should result in scroll.');
should.ok(osInstance.state()._overflowAmount.h > 0, 'Overflow amount height should be > 0 with overflow.'); should.ok(osInstance.state()._overflowAmount.h > 0, 'Overflow amount height should be > 0 with overflow.');
//should.ok(plusMinusArr(targetMetrics.scroll.height, 1).indexOf(comparisonMetrics.scroll.height) > -1, 'Scroll height equality. (+-1)');
} else { } else {
should.notEqual(style(targetViewport!, 'overflowY'), 'scroll', 'No Overflow-Y shouldnt result in scroll.'); should.notEqual(style(targetViewport!, 'overflowY'), 'scroll', 'No Overflow-Y shouldnt result in scroll.');
should.equal(osInstance.state()._overflowAmount.h, 0, 'Overflow amount height should be 0 without overflow.'); should.equal(osInstance.state()._overflowAmount.h, 0, 'Overflow amount height should be 0 without overflow.');
@@ -307,6 +316,12 @@ const iterateMinMax = async (afterEach?: () => any) => {
}; };
const overflowTest = async () => { const overflowTest = async () => {
const additiveOverflow = () => {
if (isFractionalPixelRatio()) {
return 1 + Math.max(1, Math.round(window.devicePixelRatio));
}
return 1;
};
const contentBox = (elm: HTMLElement | null): WH<number> => { const contentBox = (elm: HTMLElement | null): WH<number> => {
if (elm) { if (elm) {
const computedStyle = window.getComputedStyle(elm); const computedStyle = window.getComputedStyle(elm);
@@ -335,14 +350,15 @@ const overflowTest = async () => {
const { maxWidth, maxHeight } = style(comparison, ['maxWidth', 'maxHeight']); const { maxWidth, maxHeight } = style(comparison, ['maxWidth', 'maxHeight']);
if (maxWidth !== 'none' && maxHeight !== 'none') { if (maxWidth !== 'none' && maxHeight !== 'none') {
const addOverflow = additiveOverflow();
const before: CheckComparisonObj = { const before: CheckComparisonObj = {
updCount: updateCount, updCount: updateCount,
metrics: getMetrics(comparison!), metrics: getMetrics(comparison!),
}; };
const { paddingRight, paddingBottom } = style(comparison, ['paddingRight', 'paddingBottom']); const { paddingRight, paddingBottom } = style(comparison, ['paddingRight', 'paddingBottom']);
const comparisonContentBox = contentBox(comparison); const comparisonContentBox = contentBox(comparison);
const widthOverflow = width ? 1 : 0; const widthOverflow = width ? addOverflow : 0;
const heightOverflow = height ? 1 : 0; const heightOverflow = height ? addOverflow : 0;
const styleObj = { width: comparisonContentBox.w + widthOverflow, height: comparisonContentBox.h + heightOverflow }; const styleObj = { width: comparisonContentBox.w + widthOverflow, height: comparisonContentBox.h + heightOverflow };
style(comparisonResize, styleObj); style(comparisonResize, styleObj);
@@ -362,15 +378,15 @@ const overflowTest = async () => {
style(comparisonResize, styleObj); style(comparisonResize, styleObj);
if (width) { if (width) {
while (comparison!.scrollWidth - comparison!.clientWidth <= 0) { while (comparison!.scrollWidth - comparison!.clientWidth <= addOverflow - 1) {
styleObj.width += 1; styleObj.width += addOverflow;
style(comparisonResize, styleObj); style(comparisonResize, styleObj);
} }
} }
if (height) { if (height) {
while (comparison!.scrollHeight - comparison!.clientHeight <= 0) { while (comparison!.scrollHeight - comparison!.clientHeight <= addOverflow - 1) {
styleObj.height += 1; styleObj.height += addOverflow;
style(comparisonResize, styleObj); style(comparisonResize, styleObj);
} }
} }
@@ -381,13 +397,13 @@ const overflowTest = async () => {
}; };
if (width) { if (width) {
should.ok(overflowAmountCheck.width >= 1, 'Correct smallest possible overflow width.'); should.ok(overflowAmountCheck.width >= addOverflow, 'Correct smallest possible overflow width.');
} else { } else {
should.equal(overflowAmountCheck.width, 0, 'Correct smallest possible overflow width.'); should.equal(overflowAmountCheck.width, 0, 'Correct smallest possible overflow width.');
} }
if (height) { if (height) {
should.ok(overflowAmountCheck.height >= 1, 'Correct smallest possible overflow height.'); should.ok(overflowAmountCheck.height >= addOverflow, 'Correct smallest possible overflow height.');
} else { } else {
should.equal(overflowAmountCheck.height, 0, 'Correct smallest possible overflow height.'); should.equal(overflowAmountCheck.height, 0, 'Correct smallest possible overflow height.');
} }
@@ -472,5 +488,3 @@ const start = async () => {
}; };
startBtn?.addEventListener('click', start); startBtn?.addEventListener('click', start);
window.getMetrics = getMetrics;
@@ -105,10 +105,10 @@ body {
content: ''; content: '';
position: absolute; position: absolute;
display: block; display: block;
top: -11px; top: -10px;
right: -11px; right: -10px;
bottom: -11px; bottom: -10px;
left: -11px; left: -10px;
background: green; background: green;
z-index: -1; z-index: -1;
opacity: 0.5; opacity: 0.5;