improve sizeobserver with passive listeners, fix raf and setT code, move part of enviroment code to plugin

This commit is contained in:
Rene
2022-07-17 21:32:24 +02:00
parent 2704b66f69
commit d567cae275
15 changed files with 137 additions and 124 deletions
+18 -62
View File
@@ -11,7 +11,6 @@ import {
XY, XY,
removeAttr, removeAttr,
removeElements, removeElements,
windowSize,
equalBCRWH, equalBCRWH,
getBoundingClientRect, getBoundingClientRect,
assignDeep, assignDeep,
@@ -30,6 +29,7 @@ import {
import { Options, defaultOptions } from 'options'; import { Options, defaultOptions } from 'options';
import { PartialOptions } from 'typings'; import { PartialOptions } from 'typings';
import { InitializationStrategy } from 'initialization'; import { InitializationStrategy } from 'initialization';
import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins';
type EnvironmentEventMap = { type EnvironmentEventMap = {
_: []; _: [];
@@ -52,18 +52,12 @@ export interface InternalEnvironment {
} }
let environmentInstance: InternalEnvironment; let environmentInstance: InternalEnvironment;
const { abs, round } = Math;
const diffBiggerThanOne = (valOne: number, valTwo: number): boolean => {
const absValOne = abs(valOne);
const absValTwo = abs(valTwo);
return !(absValOne === absValTwo || absValOne + 1 === absValTwo || absValOne - 1 === absValTwo);
};
const getNativeScrollbarSize = ( const getNativeScrollbarSize = (
body: HTMLElement, body: HTMLElement,
measureElm: HTMLElement, measureElm: HTMLElement,
measureElmChild: HTMLElement measureElmChild: HTMLElement,
clear?: boolean
): XY => { ): XY => {
appendChildren(body, measureElm); appendChildren(body, measureElm);
@@ -71,6 +65,8 @@ const getNativeScrollbarSize = (
const oSize = offsetSize(measureElm); const oSize = offsetSize(measureElm);
const fSize = fractionalSize(measureElmChild); const fSize = fractionalSize(measureElmChild);
clear && removeElements(measureElm);
return { return {
x: oSize.h - cSize.h + fSize.h, x: oSize.h - cSize.h + fSize.h,
y: oSize.w - cSize.w + fSize.w, y: oSize.w - cSize.w + fSize.w,
@@ -137,26 +133,19 @@ const getFlexboxGlue = (parentElm: HTMLElement, childElm: HTMLElement): boolean
return supportsMin && supportsMax; return supportsMin && supportsMax;
}; };
const getWindowDPR = (): number => {
// eslint-disable-next-line
// @ts-ignore
const dDPI = window.screen.deviceXDPI || 0;
// eslint-disable-next-line
// @ts-ignore
const sDPI = window.screen.logicalXDPI || 1;
return window.devicePixelRatio || dDPI / sDPI;
};
const createEnvironment = (): InternalEnvironment => { const createEnvironment = (): InternalEnvironment => {
const { body } = document; const { body } = document;
const envDOM = createDOM(`<div class="${classNameEnvironment}"><div></div></div>`); const envDOM = createDOM(`<div class="${classNameEnvironment}"><div></div></div>`);
const envElm = envDOM[0] as HTMLElement; const envElm = envDOM[0] as HTMLElement;
const envChildElm = envElm.firstChild as HTMLElement; const envChildElm = envElm.firstChild as HTMLElement;
const [addEvent, , triggerEvent] = createEventListenerHub<EnvironmentEventMap>(); const [addEvent, , triggerEvent] = createEventListenerHub<EnvironmentEventMap>();
const [updateNativeScrollbarSizeCache, getNativeScrollbarSizeCache] = createCache({ const [updateNativeScrollbarSizeCache, getNativeScrollbarSizeCache] = createCache(
_initialValue: getNativeScrollbarSize(body, envElm, envChildElm), {
_equal: equalXY, _initialValue: getNativeScrollbarSize(body, envElm, envChildElm),
}); _equal: equalXY,
},
getNativeScrollbarSize.bind(0, body, envElm, envChildElm, true)
);
const [nativeScrollbarsSize] = getNativeScrollbarSizeCache(); const [nativeScrollbarsSize] = getNativeScrollbarSizeCache();
const nativeScrollbarsHiding = getNativeScrollbarsHiding(envElm); const nativeScrollbarsHiding = getNativeScrollbarsHiding(envElm);
const nativeScrollbarsOverlaid = { const nativeScrollbarsOverlaid = {
@@ -197,47 +186,14 @@ const createEnvironment = (): InternalEnvironment => {
removeElements(envElm); removeElements(envElm);
if (!nativeScrollbarsHiding && (!nativeScrollbarsOverlaid.x || !nativeScrollbarsOverlaid.y)) { if (!nativeScrollbarsHiding && (!nativeScrollbarsOverlaid.x || !nativeScrollbarsOverlaid.y)) {
let size = windowSize(); let resizeFn: undefined | ReturnType<ScrollbarsHidingPluginInstance['_envWindowZoom']>;
let dpr = getWindowDPR();
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
const sizeNew = windowSize(); const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as
const deltaSize = { | ScrollbarsHidingPluginInstance
w: sizeNew.w - size.w, | undefined;
h: sizeNew.h - size.h,
};
if (deltaSize.w === 0 && deltaSize.h === 0) return; resizeFn = resizeFn || (scrollbarsHidingPlugin && scrollbarsHidingPlugin._envWindowZoom());
resizeFn && resizeFn(env, updateNativeScrollbarSizeCache, triggerEvent.bind(0, '_'));
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h),
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0))),
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache(
getNativeScrollbarSize(body, envElm, envChildElm)
);
assignDeep(environmentInstance._nativeScrollbarsSize, scrollbarSize); // keep the object same!
removeElements(envElm);
if (scrollbarSizeChanged) {
triggerEvent('_');
}
}
size = sizeNew;
dpr = dprNew;
}); });
} }
@@ -10,7 +10,6 @@ import {
prependChildren, prependChildren,
removeElements, removeElements,
on, on,
stopAndPrevent,
addClass, addClass,
push, push,
ResizeObserverConstructor, ResizeObserverConstructor,
@@ -18,6 +17,7 @@ import {
isBoolean, isBoolean,
removeClass, removeClass,
isObject, isObject,
stopPropagation,
} from 'support'; } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { import {
@@ -190,7 +190,7 @@ export const createSizeObserver = (
onSizeChangedCallbackProxy(directionIsRTLCacheValues); onSizeChangedCallbackProxy(directionIsRTLCacheValues);
} }
stopAndPrevent(event); stopPropagation(event);
}) })
); );
} }
@@ -12,9 +12,10 @@ const pluginRegistry: Record<string, PluginInstance> = {};
export const getPlugins = () => assignDeep({}, pluginRegistry); export const getPlugins = () => assignDeep({}, pluginRegistry);
export const addPlugin = (addedPlugin: Plugin | Plugin[]) => export const addPlugin = (addedPlugin: Plugin | Plugin[]) => {
each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => { each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => {
each(keys(plugin), (pluginName) => { each(keys(plugin), (pluginName) => {
pluginRegistry[pluginName] = plugin[pluginName]; pluginRegistry[pluginName] = plugin[pluginName];
}); });
}); });
};
@@ -1,5 +1,17 @@
import { keys, attr, WH, style, addClass, removeClass, noop, each } from 'support'; import {
import { getEnvironment } from 'environment'; keys,
attr,
WH,
style,
addClass,
removeClass,
noop,
each,
assignDeep,
windowSize,
UpdateCache,
XY,
} from 'support';
import { classNameViewportArrange } from 'classnames'; import { classNameViewportArrange } from 'classnames';
import type { StyleObject } from 'typings'; import type { StyleObject } from 'typings';
import type { StructureSetupState } from 'setups/structureSetup'; import type { StructureSetupState } from 'setups/structureSetup';
@@ -8,6 +20,7 @@ import type {
GetViewportOverflowState, GetViewportOverflowState,
HideNativeScrollbars, HideNativeScrollbars,
} from 'setups/structureSetup/updateSegments/overflowUpdateSegment'; } from 'setups/structureSetup/updateSegments/overflowUpdateSegment';
import type { InternalEnvironment } from 'environment';
import type { Plugin } from 'plugins'; import type { Plugin } from 'plugins';
export type ArrangeViewport = ( export type ArrangeViewport = (
@@ -29,29 +42,51 @@ export type UndoArrangeViewport = (
) => UndoViewportArrangeResult; ) => UndoViewportArrangeResult;
export type ScrollbarsHidingPluginInstance = { export type ScrollbarsHidingPluginInstance = {
_createUniqueViewportArrangeElement(): HTMLStyleElement | false; _createUniqueViewportArrangeElement(env: InternalEnvironment): HTMLStyleElement | false;
_overflowUpdateSegment( _overflowUpdateSegment(
doViewportArrange: boolean, doViewportArrange: boolean,
flexboxGlue: boolean,
viewport: HTMLElement, viewport: HTMLElement,
viewportArrange: HTMLStyleElement | false | null | undefined, viewportArrange: HTMLStyleElement | false | null | undefined,
getState: () => StructureSetupState, getState: () => StructureSetupState,
getViewportOverflowState: GetViewportOverflowState, getViewportOverflowState: GetViewportOverflowState,
hideNativeScrollbars: HideNativeScrollbars hideNativeScrollbars: HideNativeScrollbars
): [ArrangeViewport, UndoArrangeViewport]; ): [ArrangeViewport, UndoArrangeViewport];
_envWindowZoom(): (
envInstance: InternalEnvironment,
updateNativeScrollbarSizeCache: UpdateCache<XY<number>>,
triggerEvent: () => void
) => void;
}; };
let contentArrangeCounter = 0; let contentArrangeCounter = 0;
const { round, abs } = Math;
const getWindowDPR = (): number => {
// eslint-disable-next-line
// @ts-ignore
const dDPI = window.screen.deviceXDPI || 0;
// eslint-disable-next-line
// @ts-ignore
const sDPI = window.screen.logicalXDPI || 1;
return window.devicePixelRatio || dDPI / sDPI;
};
const diffBiggerThanOne = (valOne: number, valTwo: number): boolean => {
const absValOne = abs(valOne);
const absValTwo = abs(valTwo);
return !(absValOne === absValTwo || absValOne + 1 === absValTwo || absValOne - 1 === absValTwo);
};
export const scrollbarsHidingPluginName = '__osScrollbarsHidingPlugin'; export const scrollbarsHidingPluginName = '__osScrollbarsHidingPlugin';
export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = { export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
[scrollbarsHidingPluginName]: { [scrollbarsHidingPluginName]: {
_createUniqueViewportArrangeElement: () => { _createUniqueViewportArrangeElement: (env: InternalEnvironment) => {
const { const {
_nativeScrollbarsHiding: _nativeScrollbarStyling, _nativeScrollbarsHiding: _nativeScrollbarStyling,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid, _nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_cssCustomProperties, _cssCustomProperties,
} = getEnvironment(); } = env;
const create = const create =
!_cssCustomProperties && !_cssCustomProperties &&
!_nativeScrollbarStyling && !_nativeScrollbarStyling &&
@@ -67,14 +102,13 @@ export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
}, },
_overflowUpdateSegment: ( _overflowUpdateSegment: (
doViewportArrange, doViewportArrange,
flexboxGlue,
viewport, viewport,
viewportArrange, viewportArrange,
getState, getState,
getViewportOverflowState, getViewportOverflowState,
hideNativeScrollbars hideNativeScrollbars
) => { ) => {
const { _flexboxGlue } = getEnvironment();
/** /**
* 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.
@@ -182,7 +216,7 @@ export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
removeClass(viewport, classNameViewportArrange); removeClass(viewport, classNameViewportArrange);
if (!_flexboxGlue) { if (!flexboxGlue) {
finalPaddingStyle.height = ''; finalPaddingStyle.height = '';
} }
@@ -207,5 +241,46 @@ export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
return [arrangeViewport, undoViewportArrange]; return [arrangeViewport, undoViewportArrange];
}, },
_envWindowZoom: () => {
let size = windowSize();
let dpr = getWindowDPR();
return (envInstance, updateNativeScrollbarSizeCache, triggerEvent) => {
const sizeNew = windowSize();
const deltaSize = {
w: sizeNew.w - size.w,
h: sizeNew.h - size.h,
};
if (deltaSize.w === 0 && deltaSize.h === 0) return;
const deltaAbsSize = {
w: abs(deltaSize.w),
h: abs(deltaSize.h),
};
const deltaAbsRatio = {
w: abs(round(sizeNew.w / (size.w / 100.0))),
h: abs(round(sizeNew.h / (size.h / 100.0))),
};
const dprNew = getWindowDPR();
const deltaIsBigger = deltaAbsSize.w > 2 && deltaAbsSize.h > 2;
const difference = !diffBiggerThanOne(deltaAbsRatio.w, deltaAbsRatio.h);
const dprChanged = dprNew !== dpr && dpr > 0;
const isZoom = deltaIsBigger && difference && dprChanged;
if (isZoom) {
const [scrollbarSize, scrollbarSizeChanged] = updateNativeScrollbarSizeCache();
assignDeep(envInstance._nativeScrollbarsSize, scrollbarSize); // keep the object same!
if (scrollbarSizeChanged) {
triggerEvent();
}
}
size = sizeNew;
dpr = dprNew;
};
},
}, },
}; };
@@ -6,12 +6,12 @@ import {
scrollLeft, scrollLeft,
scrollTop, scrollTop,
on, on,
stopAndPrevent,
addClass, addClass,
equalWH, equalWH,
push, push,
cAF, cAF,
rAF, rAF,
stopPropagation,
} from 'support'; } from 'support';
import { import {
classNameSizeObserverListenerScroll, classNameSizeObserverListenerScroll,
@@ -68,7 +68,7 @@ export const sizeObserverPlugin: Plugin<SizeObserverPluginInstance> = {
isDirty = !scrollEvent || !equalWH(currSize, cacheSize); isDirty = !scrollEvent || !equalWH(currSize, cacheSize);
if (scrollEvent) { if (scrollEvent) {
stopAndPrevent(scrollEvent); stopPropagation(scrollEvent);
if (isDirty && !rAFId) { if (isDirty && !rAFId) {
cAF!(rAFId); cAF!(rAFId);
@@ -9,6 +9,7 @@ import {
removeClass, removeClass,
removeElements, removeElements,
runEachAndClear, runEachAndClear,
setT,
stopPropagation, stopPropagation,
} from 'support'; } from 'support';
import { import {
@@ -143,7 +144,7 @@ export const createScrollbarsSetupElements = (
appendChildren(evaluatedScrollbarSlot, horizontalScrollbars[0]._scrollbar); appendChildren(evaluatedScrollbarSlot, horizontalScrollbars[0]._scrollbar);
appendChildren(evaluatedScrollbarSlot, verticalScrollbars[0]._scrollbar); appendChildren(evaluatedScrollbarSlot, verticalScrollbars[0]._scrollbar);
setTimeout(() => { setT(() => {
addRemoveClassHorizontal(classNamesScrollbarTransitionless); addRemoveClassHorizontal(classNamesScrollbarTransitionless);
addRemoveClassVertical(classNamesScrollbarTransitionless); addRemoveClassVertical(classNamesScrollbarTransitionless);
}, 300); }, 300);
@@ -1,4 +1,4 @@
import { rAF, cAF, isFunction, on, runEachAndClear } from 'support'; import { rAF, cAF, isFunction, on, runEachAndClear, setT, clearT } from 'support';
import { createState, createOptionCheck } from 'setups/setups'; import { createState, createOptionCheck } from 'setups/setups';
import { import {
createScrollbarsSetupElements, createScrollbarsSetupElements,
@@ -29,15 +29,15 @@ export interface ScrollbarsSetupStaticState {
const createSelfCancelTimeout = (timeout?: number | (() => number)) => { const createSelfCancelTimeout = (timeout?: number | (() => number)) => {
let id: number; let id: number;
const setT = timeout ? (setTimeout as (...args: any[]) => number) : rAF!; const setTFn = timeout ? setT : rAF!;
const clearT = timeout ? clearTimeout : cAF!; const clearTFn = timeout ? clearT : cAF!;
return [ return [
(callback: () => any) => { (callback: () => any) => {
clearT(id); clearTFn(id);
// @ts-ignore // @ts-ignore
id = setT(callback, isFunction(timeout) ? timeout() : timeout); id = setTFn(callback, isFunction(timeout) ? timeout() : timeout);
}, },
() => clearT(id), () => clearTFn(id),
] as [timeout: (callback: () => any) => void, clear: () => void]; ] as [timeout: (callback: () => any) => void, clear: () => void];
}; };
@@ -85,7 +85,8 @@ const addDataAttrHost = (elm: HTMLElement, value: string) => {
export const createStructureSetupElements = ( export const createStructureSetupElements = (
target: InitializationTarget target: InitializationTarget
): StructureSetupElements => { ): StructureSetupElements => {
const { _getInitializationStrategy, _nativeScrollbarsHiding } = getEnvironment(); const env = getEnvironment();
const { _getInitializationStrategy, _nativeScrollbarsHiding } = env;
const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as
| ScrollbarsHidingPluginInstance | ScrollbarsHidingPluginInstance
| undefined; | undefined;
@@ -156,7 +157,7 @@ export const createStructureSetupElements = (
!viewportIsTarget && !viewportIsTarget &&
!_nativeScrollbarsHiding && !_nativeScrollbarsHiding &&
createUniqueViewportArrangeElement && createUniqueViewportArrangeElement &&
createUniqueViewportArrangeElement(), createUniqueViewportArrangeElement(env),
_windowElm: wnd, _windowElm: wnd,
_documentElm: ownerDocument, _documentElm: ownerDocument,
_htmlElm: parent(bodyElm) as HTMLHtmlElement, _htmlElm: parent(bodyElm) as HTMLHtmlElement,
@@ -308,6 +308,7 @@ export const createOverflowUpdateSegment: CreateStructureUpdateSegment = (
const [arrangeViewport, undoViewportArrange] = scrollbarsHidingPlugin const [arrangeViewport, undoViewportArrange] = scrollbarsHidingPlugin
? scrollbarsHidingPlugin._overflowUpdateSegment( ? scrollbarsHidingPlugin._overflowUpdateSegment(
doViewportArrange, doViewportArrange,
_flexboxGlue,
_viewport, _viewport,
_viewportArrange, _viewportArrange,
getState, getState,
@@ -1,9 +1,10 @@
import { jsAPI } from 'support/compatibility/vendors'; import { jsAPI } from 'support/compatibility/vendors';
export const MutationObserverConstructor = jsAPI<typeof MutationObserver>('MutationObserver'); export const MutationObserverConstructor = jsAPI<typeof MutationObserver>('MutationObserver');
export const IntersectionObserverConstructor = jsAPI<typeof IntersectionObserver>( export const IntersectionObserverConstructor =
'IntersectionObserver' jsAPI<typeof IntersectionObserver>('IntersectionObserver');
);
export const ResizeObserverConstructor = jsAPI<typeof ResizeObserver>('ResizeObserver'); export const ResizeObserverConstructor = jsAPI<typeof ResizeObserver>('ResizeObserver');
export const cAF = jsAPI<typeof cancelAnimationFrame>('cancelAnimationFrame'); export const cAF = jsAPI<typeof cancelAnimationFrame>('cancelAnimationFrame');
export const rAF = jsAPI<typeof requestAnimationFrame>('requestAnimationFrame'); export const rAF = jsAPI<typeof requestAnimationFrame>('requestAnimationFrame');
export const setT = window.setTimeout as (handler: TimerHandler, timeout?: number) => number;
export const clearT = window.clearTimeout as (id?: number) => void;
@@ -1,11 +1,6 @@
import { isNumber, isFunction } from 'support/utils/types'; import { isNumber, isFunction } from 'support/utils/types';
import { from } from 'support/utils/array'; import { from } from 'support/utils/array';
import { rAF, cAF } from 'support/compatibility/apis'; import { rAF, cAF, setT, clearT } from 'support/compatibility/apis';
const clearTimeouts = (id: number | undefined) => {
id && clearTimeout(id);
id && cAF!(id);
};
type DebounceTiming = number | false | null | undefined; type DebounceTiming = number | false | null | undefined;
@@ -44,17 +39,17 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
functionToDebounce: FunctionToDebounce, functionToDebounce: FunctionToDebounce,
options?: DebounceOptions<FunctionToDebounce> options?: DebounceOptions<FunctionToDebounce>
): Debounced<FunctionToDebounce> => { ): Debounced<FunctionToDebounce> => {
let timeoutId: number | undefined;
let maxTimeoutId: number | undefined; let maxTimeoutId: number | undefined;
let prevArguments: Parameters<FunctionToDebounce> | null | undefined; let prevArguments: Parameters<FunctionToDebounce> | null | undefined;
let latestArguments: Parameters<FunctionToDebounce> | null | undefined; let latestArguments: Parameters<FunctionToDebounce> | null | undefined;
let clear: () => void = noop;
const { _timeout, _maxDelay, _mergeParams } = options || {}; const { _timeout, _maxDelay, _mergeParams } = options || {};
const setT = setTimeout as (...args: any[]) => number;
const invokeFunctionToDebounce = function (args: IArguments) { const invokeFunctionToDebounce = function (args: IArguments) {
clearTimeouts(timeoutId); clear();
clearTimeouts(maxTimeoutId); clearT(maxTimeoutId);
maxTimeoutId = timeoutId = prevArguments = undefined; maxTimeoutId = prevArguments = undefined;
clear = noop;
// eslint-disable-next-line // eslint-disable-next-line
// @ts-ignore // @ts-ignore
functionToDebounce.apply(this, args); functionToDebounce.apply(this, args);
@@ -67,7 +62,7 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
const flush = () => { const flush = () => {
/* istanbul ignore next */ /* istanbul ignore next */
if (timeoutId) { if (clear !== noop) {
invokeFunctionToDebounce(mergeParms(latestArguments!) || latestArguments!); invokeFunctionToDebounce(mergeParms(latestArguments!) || latestArguments!);
} }
}; };
@@ -82,6 +77,7 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
const finalMaxWait = isFunction(_maxDelay) ? _maxDelay() : _maxDelay; const finalMaxWait = isFunction(_maxDelay) ? _maxDelay() : _maxDelay;
const hasMaxWait = isNumber(finalMaxWait) && finalMaxWait >= 0; const hasMaxWait = isNumber(finalMaxWait) && finalMaxWait >= 0;
const setTimeoutFn = finalTimeout > 0 ? setT : rAF!; const setTimeoutFn = finalTimeout > 0 ? setT : rAF!;
const clearTimeoutFn = finalTimeout > 0 ? clearT : cAF!;
const mergeParamsResult = mergeParms(args); const mergeParamsResult = mergeParms(args);
const invokedArgs = mergeParamsResult || args; const invokedArgs = mergeParamsResult || args;
const boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs); const boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs);
@@ -90,9 +86,10 @@ export const debounce = <FunctionToDebounce extends (...args: any) => any>(
// invokeFunctionToDebounce(prevArguments || args); // invokeFunctionToDebounce(prevArguments || args);
// } // }
clearTimeouts(timeoutId); clear();
// @ts-ignore // @ts-ignore
timeoutId = setTimeoutFn(boundInvoke, finalTimeout as number) as number; const timeoutId = setTimeoutFn(boundInvoke, finalTimeout);
clear = () => clearTimeoutFn(timeoutId);
if (hasMaxWait && !maxTimeoutId) { if (hasMaxWait && !maxTimeoutId) {
maxTimeoutId = setT(flush, finalMaxWait as number); maxTimeoutId = setT(flush, finalMaxWait as number);
@@ -1,26 +1,15 @@
import { noop, debounce } from 'support/utils/function'; import { noop, debounce } from 'support/utils/function';
import { rAF } from 'support/compatibility/apis'; import { rAF, setT } from 'support/compatibility/apis';
jest.mock('support/compatibility/apis', () => { jest.mock('support/compatibility/apis', () => {
const originalModule = jest.requireActual('support/compatibility/apis'); const originalModule = jest.requireActual('support/compatibility/apis');
return { return {
...originalModule, ...originalModule,
rAF: jest.fn().mockImplementation((...args) => originalModule.rAF(...args)), rAF: jest.fn().mockImplementation((...args) => originalModule.rAF(...args)),
setT: jest.fn().mockImplementation((...args) => originalModule.setT(...args)),
}; };
}); });
const mockSetTimeout = () => {
const original = window.setTimeout;
// @ts-ignore
const setT = (window.setTimeout = jest.fn((...args) => original(...args)));
return [
setT,
() => {
window.setTimeout = original;
},
];
};
// eslint-disable-next-line no-return-await // eslint-disable-next-line no-return-await
const timeout = async (timeout = 100) => await new Promise((r) => setTimeout(r, timeout)); const timeout = async (timeout = 100) => await new Promise((r) => setTimeout(r, timeout));
@@ -34,7 +23,6 @@ describe('function', () => {
describe('timeout', () => { describe('timeout', () => {
test('without timeout', () => { test('without timeout', () => {
let i = 0; let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce(() => { const debouncedFn = debounce(() => {
i += 1; i += 1;
}); });
@@ -44,12 +32,10 @@ describe('function', () => {
expect(rAF).not.toHaveBeenCalled(); expect(rAF).not.toHaveBeenCalled();
expect(setT).not.toHaveBeenCalled(); expect(setT).not.toHaveBeenCalled();
expect(i).toBe(1); expect(i).toBe(1);
unmockSetTimeout();
}); });
test('with timeout 0', async () => { test('with timeout 0', async () => {
let i = 0; let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce( const debouncedFn = debounce(
() => { () => {
i += 1; i += 1;
@@ -67,12 +53,10 @@ describe('function', () => {
await timeout(); await timeout();
expect(i).toBe(1); expect(i).toBe(1);
unmockSetTimeout();
}); });
test('with timeout > 0', async () => { test('with timeout > 0', async () => {
let i = 0; let i = 0;
const [setT, unmockSetTimeout] = mockSetTimeout();
const debouncedFn = debounce( const debouncedFn = debounce(
() => { () => {
i += 1; i += 1;
@@ -89,8 +73,6 @@ describe('function', () => {
expect(i).toBe(0); expect(i).toBe(0);
await timeout(); await timeout();
expect(i).toBe(1); expect(i).toBe(1);
unmockSetTimeout();
}); });
test('with timeout > 0 and multiple calls', async () => { test('with timeout > 0 and multiple calls', async () => {
@@ -30,6 +30,7 @@ if (!window.ResizeObserver) {
addPlugin(sizeObserverPlugin); addPlugin(sizeObserverPlugin);
} }
if (!OverlayScrollbars.env().scrollbarsHiding) { if (!OverlayScrollbars.env().scrollbarsHiding) {
console.log('added');
addPlugin(scrollbarsHidingPlugin); addPlugin(scrollbarsHidingPlugin);
} }
@@ -104,8 +104,6 @@ body {
background: blue; background: blue;
border: 1px solid black; border: 1px solid black;
padding: 10px; padding: 10px;
height: 10000px;
width: 10000px;
} }
.percent { .percent {
@@ -1 +0,0 @@
export {};