improve cache code and prettier

This commit is contained in:
Rene
2022-06-15 22:22:45 +02:00
parent 3084e25385
commit 1fbc761483
31 changed files with 660 additions and 506 deletions
+7
View File
@@ -83,11 +83,18 @@ module.exports = (esm, options, declarationFiles = false) => {
rollupBabelInputPlugin({
...(esm ? babelConfigEsm : babelConfigUmd),
assumptions: {
enumerableModuleMeta: false,
constantReexports: true,
iterableIsArray: true,
objectRestNoSymbols: true,
noNewArrows: true,
noClassCalls: true,
ignoreToPrimitiveHint: true,
ignoreFunctionLength: true,
// privateFieldsAsProperties: true,
// setPublicClassFields: true,
setSpreadProperties: true,
pureGetters: true,
},
plugins: [
'@babel/plugin-transform-runtime',
+59 -65
View File
@@ -1,16 +1,16 @@
const createCache = (update, options) => {
function createCache(options, update) {
const {
_initialValue,
_equal,
_alwaysUpdateValues
} = options || {};
} = options;
let _value = _initialValue;
let _previous;
const cacheUpdate = (force, context) => {
const cacheUpdateContextual = (newValue, force) => {
const curr = _value;
const newVal = update ? update(context, _value, _previous) : context;
const newVal = newValue;
const changed = force || (_equal ? !_equal(curr, newVal) : curr !== newVal);
if (changed || _alwaysUpdateValues) {
@@ -21,8 +21,12 @@ const createCache = (update, options) => {
return [_value, changed, _previous];
};
return [cacheUpdate, force => [_value, !!force, _previous]];
};
const cacheUpdateIsolated = force => cacheUpdateContextual(update(_value, _previous), force);
const getCurrentCache = force => [_value, !!force, _previous];
return [update ? cacheUpdateIsolated : cacheUpdateContextual, getCurrentCache];
}
const ElementNodeType = Node.ELEMENT_NODE;
const {
@@ -35,9 +39,7 @@ function isUndefined(obj) {
function isNull(obj) {
return obj === null;
}
const type = obj => {
return isUndefined(obj) || isNull(obj) ? `${obj}` : toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
};
const type = obj => isUndefined(obj) || isNull(obj) ? `${obj}` : toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
function isNumber(obj) {
return typeof obj === 'number';
}
@@ -100,7 +102,7 @@ function each(source, callback) {
return source;
}
const indexOf = (arr, item, fromIndex) => arr.indexOf(item, fromIndex);
const indexOf = (arr, item, fromIndex) => isArray(arr) ? arr.indexOf(item, fromIndex) : -1;
const push = (array, items, arrayIsSingleItem) => {
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
return array;
@@ -116,7 +118,7 @@ const from = arr => {
});
return result;
};
const isEmptyArray = array => array && array.length === 0;
const isEmptyArray = array => !!array && array.length === 0;
const runEach = (arr, p1) => {
const runFn = fn => fn && fn(p1);
@@ -464,8 +466,6 @@ const equalWH = (a, b) => equal(a, b, ['w', 'h']);
const equalTRBL = (a, b) => equal(a, b, ['t', 'r', 'b', 'l']);
const equalBCRWH = (a, b, round) => equal(a, b, ['width', 'height'], round && (value => Math.round(value)));
const setT = window.setTimeout;
const clearTimeouts = id => {
id && window.clearTimeout(id);
id && cAF(id);
@@ -481,7 +481,8 @@ const debounce = (functionToDebounce, options) => {
_timeout,
_maxDelay,
_mergeParams
} = options;
} = options || {};
const setT = window.setTimeout;
const invokeFunctionToDebounce = function invokeFunctionToDebounce(args) {
clearTimeouts(timeoutId);
@@ -499,7 +500,7 @@ const debounce = (functionToDebounce, options) => {
};
const debouncedFn = function debouncedFn() {
const args = arguments;
const args = from(arguments);
const finalTimeout = isFunction(_timeout) ? _timeout() : _timeout;
const hasTimeout = isNumber(finalTimeout) && finalTimeout >= 0;
@@ -510,11 +511,6 @@ const debounce = (functionToDebounce, options) => {
const mergeParamsResult = mergeParms(args);
const invokedArgs = mergeParamsResult || args;
const boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs);
if (!mergeParamsResult) {
invokeFunctionToDebounce(prevArguments || args);
}
clearTimeouts(timeoutId);
timeoutId = setTimeoutFn(boundInvoke, finalTimeout);
@@ -550,13 +546,13 @@ const setCSSVal = (elm, prop, val) => {
try {
if (elm) {
const {
style
style: elmStyle
} = elm;
if (!isUndefined(style[prop])) {
style[prop] = adaptCSSVal(prop, val);
if (!isUndefined(elmStyle[prop])) {
elmStyle[prop] = adaptCSSVal(prop, val);
} else {
style.setProperty(prop, val);
elmStyle.setProperty(prop, val);
}
}
} catch (e) {}
@@ -717,9 +713,7 @@ const validateRecursive = (options, template, optionsDiff, doWriteErrors, propPa
};
};
const validateOptions = (options, template, optionsDiff, doWriteErrors) => {
return validateRecursive(options, template, optionsDiff || {}, doWriteErrors || false);
};
const validateOptions = (options, template, optionsDiff, doWriteErrors) => validateRecursive(options, template, optionsDiff || {}, doWriteErrors || false);
const transformOptions = optionsWithOptionsTemplate => {
const result = {
@@ -1224,7 +1218,7 @@ const createSizeObserver = (target, onSizeChangedCallback, options) => {
const sizeObserver = baseElements[0];
const listenerElement = sizeObserver.firstChild;
const getIsDirectionRTL = getElmDirectionIsRTL.bind(0, sizeObserver);
const [updateResizeObserverContentRectCache] = createCache(0, {
const [updateResizeObserverContentRectCache] = createCache({
_initialValue: undefined,
_alwaysUpdateValues: true,
_equal: (currVal, newVal) => !(!currVal || !domRectHasDimensions(currVal) && domRectHasDimensions(newVal))
@@ -1238,7 +1232,7 @@ const createSizeObserver = (target, onSizeChangedCallback, options) => {
let doDirectionScroll = true;
if (isResizeObserverCall) {
const [currRContentRect,, prevContentRect] = updateResizeObserverContentRectCache(0, sizeChangedContext.pop().contentRect);
const [currRContentRect,, prevContentRect] = updateResizeObserverContentRectCache(sizeChangedContext.pop().contentRect);
const hasDimensions = domRectHasDimensions(currRContentRect);
const hadDimensions = domRectHasDimensions(prevContentRect);
skip = !prevContentRect || !hasDimensions;
@@ -1332,9 +1326,9 @@ const createSizeObserver = (target, onSizeChangedCallback, options) => {
}
if (observeDirectionChange) {
directionIsRTLCache = createCache(getIsDirectionRTL, {
directionIsRTLCache = createCache({
_initialValue: !getIsDirectionRTL()
});
}, getIsDirectionRTL);
const [updateDirectionIsRTLCache] = directionIsRTLCache;
push(offListeners, on(sizeObserver, scrollEventName, event => {
const directionIsRTLCacheValues = updateDirectionIsRTLCache();
@@ -1379,16 +1373,18 @@ const createSizeObserver = (target, onSizeChangedCallback, options) => {
};
};
const isHeightIntrinsic = ioEntryOrSize => ioEntryOrSize.h === 0 || ioEntryOrSize.isIntersecting || ioEntryOrSize.intersectionRatio > 0;
const createTrinsicObserver = (target, onTrinsicChangedCallback) => {
const trinsicObserver = createDiv(classNameTrinsicObserver);
const offListeners = [];
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache(ioEntryOrSize => ioEntryOrSize.h === 0 || ioEntryOrSize.isIntersecting || ioEntryOrSize.intersectionRatio > 0, {
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache({
_initialValue: false
});
const triggerOnTrinsicChangedCallback = updateValue => {
if (updateValue) {
const heightIntrinsic = updateHeightIntrinsicCache(0, updateValue);
const heightIntrinsic = updateHeightIntrinsicCache(isHeightIntrinsic(updateValue));
const [, heightIntrinsicChanged] = heightIntrinsic;
if (heightIntrinsicChanged) {
@@ -1819,10 +1815,10 @@ const createPaddingLifecycle = lifecycleHub => {
_padding,
_viewport
} = _structureSetup._targetObj;
const [updatePaddingCache, currentPaddingCache] = createCache(topRightBottomLeft.bind(0, _host, 'padding'), {
const [updatePaddingCache, currentPaddingCache] = createCache({
_equal: equalTRBL,
_initialValue: topRightBottomLeft()
});
}, topRightBottomLeft.bind(0, _host, 'padding', ''));
return (updateHints, checkOption, force) => {
let [padding, paddingChanged] = currentPaddingCache(force);
const {
@@ -1927,6 +1923,11 @@ const setAxisOverflowStyle = (horizontal, overflowAmount, behavior, styleObj) =>
};
};
const getOverflowAmount = (viewportScrollSize, viewportClientSize, viewportSizeFraction) => ({
w: max(0, round(max(0, viewportScrollSize.w - viewportClientSize.w) - (fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.w)))),
h: max(0, round(max(0, viewportScrollSize.h - viewportClientSize.h) - (fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.h))))
});
const createOverflowLifecycle = lifecycleHub => {
const {
_structureSetup,
@@ -1939,16 +1940,9 @@ const createOverflowLifecycle = lifecycleHub => {
_viewport,
_viewportArrange
} = _structureSetup._targetObj;
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache(sizeFraction.bind(0, _viewport), whCacheOptions);
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache(scrollSize.bind(0, _viewport), whCacheOptions);
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache(({
_viewportScrollSize,
_viewportClientSize,
_viewportSizeFraction
}) => ({
w: max(0, round(max(0, _viewportScrollSize.w - _viewportClientSize.w) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.w)))),
h: max(0, round(max(0, _viewportScrollSize.h - _viewportClientSize.h) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.h))))
}), whCacheOptions);
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache(whCacheOptions, sizeFraction.bind(0, _viewport));
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache(whCacheOptions, scrollSize.bind(0, _viewport));
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache(whCacheOptions);
const fixFlexboxGlue = (viewportOverflowState, heightIntrinsic) => {
style(_viewport, {
@@ -1998,9 +1992,13 @@ const createOverflowLifecycle = lifecycleHub => {
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll'
};
const nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y
};
const scrollbarsHideOffset = {
x: scroll.x && !_nativeScrollbarStyling ? overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y : 0
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0
};
return {
_overflowScroll: scroll,
@@ -2223,30 +2221,26 @@ const createOverflowLifecycle = lifecycleHub => {
_redoViewportArrange,
_viewportOverflowState: undoViewportArrangeOverflowState
} = undoViewportArrange(showNativeOverlaidScrollbars, directionIsRTL, preMeasureViewportOverflowState);
const [_viewportSizeFraction2, viewportSizeFractionCahnged] = viewportSizeFractionCache = updateViewportSizeFraction(force);
const [_viewportScrollSize2, _viewportScrollSizeChanged] = viewportScrollSizeCache = updateViewportScrollSizeCache(force);
const [_viewportSizeFraction, viewportSizeFractionCahnged] = viewportSizeFractionCache = updateViewportSizeFraction(force);
const [_viewportScrollSize, _viewportScrollSizeChanged] = viewportScrollSizeCache = updateViewportScrollSizeCache(force);
const viewportContentSize = clientSize(_viewport);
let arrangedViewportScrollSize = _viewportScrollSize2;
let arrangedViewportScrollSize = _viewportScrollSize;
let arrangedViewportClientSize = viewportContentSize;
_redoViewportArrange();
if ((_viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) && undoViewportArrangeOverflowState && !showNativeOverlaidScrollbars && arrangeViewport(undoViewportArrangeOverflowState, _viewportScrollSize2, _viewportSizeFraction2, directionIsRTL)) {
if ((_viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) && undoViewportArrangeOverflowState && !showNativeOverlaidScrollbars && arrangeViewport(undoViewportArrangeOverflowState, _viewportScrollSize, _viewportSizeFraction, directionIsRTL)) {
arrangedViewportClientSize = clientSize(_viewport);
arrangedViewportScrollSize = scrollSize(_viewport);
}
overflowAmuntCache = updateOverflowAmountCache(force, {
_viewportSizeFraction: _viewportSizeFraction2,
_viewportScrollSize: {
w: max(_viewportScrollSize2.w, arrangedViewportScrollSize.w),
h: max(_viewportScrollSize2.h, arrangedViewportScrollSize.h)
},
_viewportClientSize: {
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - _viewportScrollSize2.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - _viewportScrollSize2.h)
}
});
overflowAmuntCache = updateOverflowAmountCache(getOverflowAmount({
w: max(_viewportScrollSize.w, arrangedViewportScrollSize.w),
h: max(_viewportScrollSize.h, arrangedViewportScrollSize.h)
}, {
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - _viewportScrollSize.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - _viewportScrollSize.h)
}, _viewportSizeFraction), force);
}
const [viewportSizeFraction, viewportSizeFractionChanged] = viewportSizeFractionCache;
@@ -2400,9 +2394,9 @@ const createLifecycleHub = (options, structureSetup, scrollbarsSetup) => {
_destroy: destroyObservers
} = lifecycleHubOservers(instance, updateLifecycles);
const update = (changedOptions, force) => updateLifecycles(null, changedOptions, force);
const update = (changedOptions, force) => updateLifecycles({}, changedOptions, force);
const envUpdateListener = update.bind(null, null, true);
const envUpdateListener = update.bind(0, {}, true);
addEnvironmentListener(envUpdateListener);
console.log(getEnvironment());
return {
@@ -2451,7 +2445,7 @@ const OverlayScrollbars = (target, options, extensions) => {
state: () => lifecycleHub._state(),
update(force) {
lifecycleHub._update(null, force);
lifecycleHub._update({}, force);
},
destroy: () => lifecycleHub._destroy()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+76 -74
View File
@@ -4,19 +4,17 @@
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.OverlayScrollbars = factory());
})(this, (function () { 'use strict';
var createCache = function createCache(update, options) {
var _ref = options || {},
_initialValue = _ref._initialValue,
_equal = _ref._equal,
_alwaysUpdateValues = _ref._alwaysUpdateValues;
function createCache(options, update) {
var _initialValue = options._initialValue,
_equal = options._equal,
_alwaysUpdateValues = options._alwaysUpdateValues;
var _value = _initialValue;
var _previous;
var cacheUpdate = function cacheUpdate(force, context) {
var cacheUpdateContextual = function cacheUpdateContextual(newValue, force) {
var curr = _value;
var newVal = update ? update(context, _value, _previous) : context;
var newVal = newValue;
var changed = force || (_equal ? !_equal(curr, newVal) : curr !== newVal);
if (changed || _alwaysUpdateValues) {
@@ -27,10 +25,16 @@
return [_value, changed, _previous];
};
return [cacheUpdate, function (force) {
var cacheUpdateIsolated = function cacheUpdateIsolated(force) {
return cacheUpdateContextual(update(_value, _previous), force);
};
var getCurrentCache = function getCurrentCache(force) {
return [_value, !!force, _previous];
}];
};
};
return [update ? cacheUpdateIsolated : cacheUpdateContextual, getCurrentCache];
}
var ElementNodeType = Node.ELEMENT_NODE;
var _Object$prototype = Object.prototype,
@@ -110,7 +114,7 @@
return source;
}
var indexOf = function indexOf(arr, item, fromIndex) {
return arr.indexOf(item, fromIndex);
return isArray(arr) ? arr.indexOf(item, fromIndex) : -1;
};
var push = function push(array, items, arrayIsSingleItem) {
!arrayIsSingleItem && !isString(items) && isArrayLike(items) ? Array.prototype.push.apply(array, items) : array.push(items);
@@ -128,7 +132,7 @@
return result;
};
var isEmptyArray = function isEmptyArray(array) {
return array && array.length === 0;
return !!array && array.length === 0;
};
var runEach = function runEach(arr, p1) {
var runFn = function runFn(fn) {
@@ -529,8 +533,6 @@
});
};
var setT = window.setTimeout;
var clearTimeouts = function clearTimeouts(id) {
id && window.clearTimeout(id);
id && cAF(id);
@@ -542,9 +544,13 @@
var maxTimeoutId;
var prevArguments;
var latestArguments;
var _timeout = options._timeout,
_maxDelay = options._maxDelay,
_mergeParams = options._mergeParams;
var _ref = options || {},
_timeout = _ref._timeout,
_maxDelay = _ref._maxDelay,
_mergeParams = _ref._mergeParams;
var setT = window.setTimeout;
var invokeFunctionToDebounce = function invokeFunctionToDebounce(args) {
clearTimeouts(timeoutId);
@@ -564,7 +570,7 @@
};
var debouncedFn = function debouncedFn() {
var args = arguments;
var args = from(arguments);
var finalTimeout = isFunction(_timeout) ? _timeout() : _timeout;
var hasTimeout = isNumber(finalTimeout) && finalTimeout >= 0;
@@ -575,11 +581,6 @@
var mergeParamsResult = mergeParms(args);
var invokedArgs = mergeParamsResult || args;
var boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs);
if (!mergeParamsResult) {
invokeFunctionToDebounce(prevArguments || args);
}
clearTimeouts(timeoutId);
timeoutId = setTimeoutFn(boundInvoke, finalTimeout);
@@ -618,12 +619,12 @@
var setCSSVal = function setCSSVal(elm, prop, val) {
try {
if (elm) {
var _style = elm.style;
var elmStyle = elm.style;
if (!isUndefined(_style[prop])) {
_style[prop] = adaptCSSVal(prop, val);
if (!isUndefined(elmStyle[prop])) {
elmStyle[prop] = adaptCSSVal(prop, val);
} else {
_style.setProperty(prop, val);
elmStyle.setProperty(prop, val);
}
}
} catch (e) {}
@@ -1288,7 +1289,7 @@
var listenerElement = sizeObserver.firstChild;
var getIsDirectionRTL = getElmDirectionIsRTL.bind(0, sizeObserver);
var _createCache = createCache(0, {
var _createCache = createCache({
_initialValue: undefined,
_alwaysUpdateValues: true,
_equal: function _equal(currVal, newVal) {
@@ -1305,7 +1306,7 @@
var doDirectionScroll = true;
if (isResizeObserverCall) {
var _updateResizeObserver = updateResizeObserverContentRectCache(0, sizeChangedContext.pop().contentRect),
var _updateResizeObserver = updateResizeObserverContentRectCache(sizeChangedContext.pop().contentRect),
currRContentRect = _updateResizeObserver[0],
prevContentRect = _updateResizeObserver[2];
@@ -1402,9 +1403,9 @@
}
if (observeDirectionChange) {
directionIsRTLCache = createCache(getIsDirectionRTL, {
directionIsRTLCache = createCache({
_initialValue: !getIsDirectionRTL()
});
}, getIsDirectionRTL);
var _directionIsRTLCache = directionIsRTLCache,
updateDirectionIsRTLCache = _directionIsRTLCache[0];
push(offListeners, on(sizeObserver, scrollEventName, function (event) {
@@ -1449,13 +1450,15 @@
};
};
var isHeightIntrinsic = function isHeightIntrinsic(ioEntryOrSize) {
return ioEntryOrSize.h === 0 || ioEntryOrSize.isIntersecting || ioEntryOrSize.intersectionRatio > 0;
};
var createTrinsicObserver = function createTrinsicObserver(target, onTrinsicChangedCallback) {
var trinsicObserver = createDiv(classNameTrinsicObserver);
var offListeners = [];
var _createCache = createCache(function (ioEntryOrSize) {
return ioEntryOrSize.h === 0 || ioEntryOrSize.isIntersecting || ioEntryOrSize.intersectionRatio > 0;
}, {
var _createCache = createCache({
_initialValue: false
}),
updateHeightIntrinsicCache = _createCache[0],
@@ -1463,7 +1466,7 @@
var triggerOnTrinsicChangedCallback = function triggerOnTrinsicChangedCallback(updateValue) {
if (updateValue) {
var heightIntrinsic = updateHeightIntrinsicCache(0, updateValue);
var heightIntrinsic = updateHeightIntrinsicCache(isHeightIntrinsic(updateValue));
var heightIntrinsicChanged = heightIntrinsic[1];
if (heightIntrinsicChanged) {
@@ -1889,10 +1892,10 @@
_padding = _structureSetup$_targ._padding,
_viewport = _structureSetup$_targ._viewport;
var _createCache = createCache(topRightBottomLeft.bind(0, _host, 'padding'), {
var _createCache = createCache({
_equal: equalTRBL,
_initialValue: topRightBottomLeft()
}),
}, topRightBottomLeft.bind(0, _host, 'padding', '')),
updatePaddingCache = _createCache[0],
currentPaddingCache = _createCache[1];
@@ -2009,6 +2012,13 @@
};
};
var getOverflowAmount = function getOverflowAmount(viewportScrollSize, viewportClientSize, viewportSizeFraction) {
return {
w: max(0, round(max(0, viewportScrollSize.w - viewportClientSize.w) - (fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.w)))),
h: max(0, round(max(0, viewportScrollSize.h - viewportClientSize.h) - (fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.h))))
};
};
var createOverflowLifecycle = function createOverflowLifecycle(lifecycleHub) {
var _structureSetup = lifecycleHub._structureSetup,
_doViewportArrange = lifecycleHub._doViewportArrange,
@@ -2019,23 +2029,15 @@
_viewport = _structureSetup$_targ._viewport,
_viewportArrange = _structureSetup$_targ._viewportArrange;
var _createCache = createCache(sizeFraction.bind(0, _viewport), whCacheOptions),
var _createCache = createCache(whCacheOptions, sizeFraction.bind(0, _viewport)),
updateViewportSizeFraction = _createCache[0],
getCurrentViewportSizeFraction = _createCache[1];
var _createCache2 = createCache(scrollSize.bind(0, _viewport), whCacheOptions),
var _createCache2 = createCache(whCacheOptions, scrollSize.bind(0, _viewport)),
updateViewportScrollSizeCache = _createCache2[0],
getCurrentViewportScrollSizeCache = _createCache2[1];
var _createCache3 = createCache(function (_ref) {
var _viewportScrollSize = _ref._viewportScrollSize,
_viewportClientSize = _ref._viewportClientSize,
_viewportSizeFraction = _ref._viewportSizeFraction;
return {
w: max(0, round(max(0, _viewportScrollSize.w - _viewportClientSize.w) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.w)))),
h: max(0, round(max(0, _viewportScrollSize.h - _viewportClientSize.h) - (fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.h))))
};
}, whCacheOptions),
var _createCache3 = createCache(whCacheOptions),
updateOverflowAmountCache = _createCache3[0],
getCurrentOverflowAmountCache = _createCache3[1];
@@ -2081,9 +2083,13 @@
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll'
};
var nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y
};
var scrollbarsHideOffset = {
x: scroll.x && !_nativeScrollbarStyling ? overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y : 0
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0
};
return {
_overflowScroll: scroll,
@@ -2289,41 +2295,37 @@
_redoViewportArrange = _undoViewportArrange._redoViewportArrange,
undoViewportArrangeOverflowState = _undoViewportArrange._viewportOverflowState;
var _viewportSizeFraction3 = viewportSizeFractionCache = updateViewportSizeFraction(force),
_viewportSizeFraction2 = _viewportSizeFraction3[0],
viewportSizeFractionCahnged = _viewportSizeFraction3[1];
var _viewportSizeFraction2 = viewportSizeFractionCache = updateViewportSizeFraction(force),
_viewportSizeFraction = _viewportSizeFraction2[0],
viewportSizeFractionCahnged = _viewportSizeFraction2[1];
var _viewportScrollSizeCa = viewportScrollSizeCache = updateViewportScrollSizeCache(force),
_viewportScrollSize2 = _viewportScrollSizeCa[0],
_viewportScrollSize = _viewportScrollSizeCa[0],
_viewportScrollSizeChanged = _viewportScrollSizeCa[1];
var viewportContentSize = clientSize(_viewport);
var arrangedViewportScrollSize = _viewportScrollSize2;
var arrangedViewportScrollSize = _viewportScrollSize;
var arrangedViewportClientSize = viewportContentSize;
_redoViewportArrange();
if ((_viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) && undoViewportArrangeOverflowState && !showNativeOverlaidScrollbars && arrangeViewport(undoViewportArrangeOverflowState, _viewportScrollSize2, _viewportSizeFraction2, directionIsRTL)) {
if ((_viewportScrollSizeChanged || viewportSizeFractionCahnged || showNativeOverlaidScrollbarsChanged) && undoViewportArrangeOverflowState && !showNativeOverlaidScrollbars && arrangeViewport(undoViewportArrangeOverflowState, _viewportScrollSize, _viewportSizeFraction, directionIsRTL)) {
arrangedViewportClientSize = clientSize(_viewport);
arrangedViewportScrollSize = scrollSize(_viewport);
}
overflowAmuntCache = updateOverflowAmountCache(force, {
_viewportSizeFraction: _viewportSizeFraction2,
_viewportScrollSize: {
w: max(_viewportScrollSize2.w, arrangedViewportScrollSize.w),
h: max(_viewportScrollSize2.h, arrangedViewportScrollSize.h)
},
_viewportClientSize: {
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - _viewportScrollSize2.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - _viewportScrollSize2.h)
}
});
overflowAmuntCache = updateOverflowAmountCache(getOverflowAmount({
w: max(_viewportScrollSize.w, arrangedViewportScrollSize.w),
h: max(_viewportScrollSize.h, arrangedViewportScrollSize.h)
}, {
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - _viewportScrollSize.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - _viewportScrollSize.h)
}, _viewportSizeFraction), force);
}
var _viewportSizeFraction4 = viewportSizeFractionCache,
viewportSizeFraction = _viewportSizeFraction4[0],
viewportSizeFractionChanged = _viewportSizeFraction4[1];
var _viewportSizeFraction3 = viewportSizeFractionCache,
viewportSizeFraction = _viewportSizeFraction3[0],
viewportSizeFractionChanged = _viewportSizeFraction3[1];
var _viewportScrollSizeCa2 = viewportScrollSizeCache,
viewportScrollSize = _viewportScrollSizeCa2[0],
viewportScrollSizeChanged = _viewportScrollSizeCa2[1];
@@ -2490,10 +2492,10 @@
destroyObservers = _lifecycleHubOservers._destroy;
var update = function update(changedOptions, force) {
return updateLifecycles(null, changedOptions, force);
return updateLifecycles({}, changedOptions, force);
};
var envUpdateListener = update.bind(null, null, true);
var envUpdateListener = update.bind(0, {}, true);
addEnvironmentListener(envUpdateListener);
console.log(getEnvironment());
return {
@@ -2541,7 +2543,7 @@
return lifecycleHub._state();
},
update: function update(force) {
lifecycleHub._update(null, force);
lifecycleHub._update({}, force);
},
destroy: function destroy() {
return lifecycleHub._destroy();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -23,7 +23,7 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
const updateLoopInterval = () => {
loopInterval = isEmptyArray(intervals) ? defaultLoopInterval : Math.min.apply(null, intervals);
};
const { _update: updateTimeCache } = createCache<number, number | undefined>((ctx) => ctx || performance.now(), {
const [updateTimeCache] = createCache<number>({
_initialValue: performance.now(),
_equal: (currTime, newTime) => {
const delta = newTime! - currTime!;
@@ -34,9 +34,9 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
/* istanbul ignore next */
if (!isEmptyArray(loopFunctions) && loopIsRunning) {
loopId = rAF!(loop);
const { _changed, _value, _previous } = updateTimeCache(0, newTime);
if (_changed) {
runEach(loopFunctions, _value! - _previous!);
const [value, changed, previous] = updateTimeCache(newTime || performance.now());
if (changed) {
runEach(loopFunctions, value - previous!);
}
}
};
@@ -60,9 +60,9 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
push(loopFunctions, fn);
if (!loopIsRunning && !isEmptyArray(loopFunctions)) {
//getEnvironment()._autoUpdateLoop = loopIsRunning = true;
// getEnvironment()._autoUpdateLoop = loopIsRunning = true;
updateTimeCache(true);
updateTimeCache(performance.now(), true);
loop();
}
@@ -70,7 +70,7 @@ const createAutoUpdateLoop = (): AutoUpdateLoop => {
loopFunctions.splice(indexOf(loopFunctions, fn), 1);
if (isEmptyArray(loopFunctions) && loopIsRunning) {
//getEnvironment()._autoUpdateLoop = loopIsRunning = false;
// getEnvironment()._autoUpdateLoop = loopIsRunning = false;
cAF!(loopId!);
loopId = undefined;
@@ -58,7 +58,7 @@ export interface LifecycleHubState {
}
export interface LifecycleHubInstance {
_update(changedOptions?: PartialOptions<OSOptions> | null, force?: boolean): void;
_update(changedOptions: PartialOptions<OSOptions>, force?: boolean): void;
_state(): LifecycleHubState;
_destroy(): void;
}
@@ -139,8 +139,8 @@ export const createLifecycleHub = (
];
const updateLifecycles = (
updateHints?: Partial<LifecycleUpdateHints> | null,
changedOptions?: Partial<OSOptions> | null,
updateHints: Partial<LifecycleUpdateHints>,
changedOptions?: Partial<OSOptions>,
force?: boolean
) => {
let {
@@ -221,9 +221,9 @@ export const createLifecycleHub = (
_destroy: destroyObservers,
} = lifecycleHubOservers(instance, updateLifecycles);
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) =>
updateLifecycles(null, changedOptions, force);
const envUpdateListener = update.bind(null, null, true);
const update = (changedOptions: Partial<OSOptions>, force?: boolean) =>
updateLifecycles({}, changedOptions, force);
const envUpdateListener = update.bind(0, {}, true);
addEnvironmentListener(envUpdateListener);
console.log(getEnvironment());
@@ -44,7 +44,7 @@ const ignoreTargetChange = (
export const lifecycleHubOservers = (
instance: LifecycleHub,
updateLifecycles: (updateHints?: Partial<LifecycleUpdateHints> | null) => unknown
updateLifecycles: (updateHints: Partial<LifecycleUpdateHints>) => unknown
) => {
let debounceTimeout: number | false | undefined;
let debounceMaxDelay: number | false | undefined;
@@ -57,34 +57,31 @@ export const lifecycleHubOservers = (
const contentMutationObserverAttr = _isTextarea
? baseStyleChangingAttrsTextarea
: baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
const updateLifecyclesWithDebouncedAdaptiveUpdateHints = debounce(
updateLifecycles as (updateHints: Partial<LifecycleUpdateHints>) => any,
{
_timeout: () => debounceTimeout,
_maxDelay: () => debounceMaxDelay,
_mergeParams(prev, curr) {
const {
_sizeChanged: prevSizeChanged,
_hostMutation: prevHostMutation,
_contentMutation: prevContentMutation,
} = prev[0];
const {
_sizeChanged: currSizeChanged,
_hostMutation: currvHostMutation,
_contentMutation: currContentMutation,
} = curr[0];
const merged: [Partial<LifecycleUpdateHints>] = [
{
_sizeChanged: prevSizeChanged || currSizeChanged,
_hostMutation: prevHostMutation || currvHostMutation,
_contentMutation: prevContentMutation || currContentMutation,
},
];
const updateLifecyclesWithDebouncedAdaptiveUpdateHints = debounce(updateLifecycles, {
_timeout: () => debounceTimeout,
_maxDelay: () => debounceMaxDelay,
_mergeParams(prev, curr) {
const {
_sizeChanged: prevSizeChanged,
_hostMutation: prevHostMutation,
_contentMutation: prevContentMutation,
} = prev[0];
const {
_sizeChanged: currSizeChanged,
_hostMutation: currvHostMutation,
_contentMutation: currContentMutation,
} = curr[0];
const merged: [Partial<LifecycleUpdateHints>] = [
{
_sizeChanged: prevSizeChanged || currSizeChanged,
_hostMutation: prevHostMutation || currvHostMutation,
_contentMutation: prevContentMutation || currContentMutation,
},
];
return merged;
},
}
);
return merged;
},
});
const updateViewportAttrsFromHost = (attributes?: string[]) => {
each(attributes || viewportAttrsFromTarget, (attribute) => {
@@ -22,12 +22,6 @@ import { OverflowBehavior } from 'options';
import { StyleObject } from 'typings';
import { classNameViewportArrange, classNameViewportScrollbarStyling } from 'classnames';
interface OverflowAmountCacheContext {
_viewportScrollSize: WH<number>;
_viewportClientSize: WH<number>;
_viewportSizeFraction: WH<number>;
}
interface ViewportOverflowState {
_scrollbarsHideOffset: XY<number>;
_scrollbarsHideOffsetArrange: XY<boolean>;
@@ -84,6 +78,27 @@ const setAxisOverflowStyle = (
};
};
const getOverflowAmount = (
viewportScrollSize: WH<number>,
viewportClientSize: WH<number>,
viewportSizeFraction: WH<number>
) => ({
w: max(
0,
round(
max(0, viewportScrollSize.w - viewportClientSize.w) -
(fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.w))
)
),
h: max(
0,
round(
max(0, viewportScrollSize.h - viewportClientSize.h) -
(fractionalPixelRatioTollerance() || max(0, viewportSizeFraction.h))
)
),
});
/**
* Lifecycle with the responsibility to set the correct overflow and scrollbar hiding styles of the viewport element.
* @param lifecycleHub
@@ -100,34 +115,15 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const { _host, _viewport, _viewportArrange } = _structureSetup._targetObj;
const [updateViewportSizeFraction, getCurrentViewportSizeFraction] = createCache<WH<number>>(
sizeFraction.bind(0, _viewport),
whCacheOptions
whCacheOptions,
sizeFraction.bind(0, _viewport)
);
const [updateViewportScrollSizeCache, getCurrentViewportScrollSizeCache] = createCache<
WH<number>
>(scrollSize.bind(0, _viewport), whCacheOptions);
>(whCacheOptions, scrollSize.bind(0, _viewport));
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache<
WH<number>,
OverflowAmountCacheContext
>(
({ _viewportScrollSize, _viewportClientSize, _viewportSizeFraction }) => ({
w: max(
0,
round(
max(0, _viewportScrollSize.w - _viewportClientSize.w) -
(fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.w))
)
),
h: max(
0,
round(
max(0, _viewportScrollSize.h - _viewportClientSize.h) -
(fractionalPixelRatioTollerance() || max(0, _viewportSizeFraction.h))
)
),
}),
const [updateOverflowAmountCache, getCurrentOverflowAmountCache] = createCache<WH<number>>(
whCacheOptions
);
@@ -195,19 +191,13 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
x: styleObj.overflowX === 'scroll',
y: styleObj.overflowY === 'scroll',
};
const nonScrollbarStylingHideOffset = {
x: overlaidX ? arrangeHideOffset : _nativeScrollbarSize.x,
y: overlaidY ? arrangeHideOffset : _nativeScrollbarSize.y,
};
const scrollbarsHideOffset = {
x:
scroll.x && !_nativeScrollbarStyling
? overlaidX
? arrangeHideOffset
: _nativeScrollbarSize.x
: 0,
y:
scroll.y && !_nativeScrollbarStyling
? overlaidY
? arrangeHideOffset
: _nativeScrollbarSize.y
: 0,
x: scroll.x && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.x : 0,
y: scroll.y && !_nativeScrollbarStyling ? nonScrollbarStylingHideOffset.y : 0,
};
return {
@@ -486,7 +476,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
_viewportOverflowState: undoViewportArrangeOverflowState,
} = undoViewportArrange(
showNativeOverlaidScrollbars,
directionIsRTL!,
directionIsRTL,
preMeasureViewportOverflowState
);
const [
@@ -498,7 +488,7 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
viewportScrollSizeChanged,
] = (viewportScrollSizeCache = updateViewportScrollSizeCache(force));
const viewportContentSize = clientSize(_viewport);
let arrangedViewportScrollSize = viewportScrollSize!;
let arrangedViewportScrollSize = viewportScrollSize;
let arrangedViewportClientSize = viewportContentSize;
_redoViewportArrange();
@@ -512,26 +502,29 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
!showNativeOverlaidScrollbars &&
arrangeViewport(
undoViewportArrangeOverflowState,
viewportScrollSize!,
viewportSizeFraction!,
directionIsRTL!
viewportScrollSize,
viewportSizeFraction,
directionIsRTL
)
) {
arrangedViewportClientSize = clientSize(_viewport);
arrangedViewportScrollSize = scrollSize(_viewport);
}
overflowAmuntCache = updateOverflowAmountCache(force, {
_viewportSizeFraction: viewportSizeFraction!,
_viewportScrollSize: {
w: max(viewportScrollSize.w, arrangedViewportScrollSize.w),
h: max(viewportScrollSize.h, arrangedViewportScrollSize.h),
},
_viewportClientSize: {
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - viewportScrollSize.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - viewportScrollSize.h),
},
});
overflowAmuntCache = updateOverflowAmountCache(
getOverflowAmount(
{
w: max(viewportScrollSize.w, arrangedViewportScrollSize.w),
h: max(viewportScrollSize.h, arrangedViewportScrollSize.h),
}, // scroll size
{
w: arrangedViewportClientSize.w + max(0, viewportContentSize.w - viewportScrollSize.w),
h: arrangedViewportClientSize.h + max(0, viewportContentSize.h - viewportScrollSize.h),
}, // client size
viewportSizeFraction
),
force
);
}
const [viewportSizeFraction, viewportSizeFractionChanged] = viewportSizeFractionCache;
@@ -560,17 +553,17 @@ export const createOverflowLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =
const viewportOverflowState = setViewportOverflowState(
showNativeOverlaidScrollbars,
overflowAmount!,
overflowAmount,
overflow,
viewportStyle
);
const viewportArranged = arrangeViewport(
viewportOverflowState,
viewportScrollSize!,
viewportSizeFraction!,
directionIsRTL!
viewportScrollSize,
viewportSizeFraction,
directionIsRTL
);
hideNativeScrollbars(viewportOverflowState, directionIsRTL!, viewportArranged, viewportStyle);
hideNativeScrollbars(viewportOverflowState, directionIsRTL, viewportArranged, viewportStyle);
if (adjustFlexboxGlue) {
fixFlexboxGlue(viewportOverflowState, !!heightIntrinsic);
@@ -1,4 +1,4 @@
import { createCache, topRightBottomLeft, equalTRBL, style, TRBL } from 'support';
import { createCache, topRightBottomLeft, equalTRBL, style } from 'support';
import { LifecycleHub, Lifecycle } from 'lifecycles/lifecycleHub';
import { StyleObject } from 'typings';
import { getEnvironment } from 'environment';
@@ -11,12 +11,12 @@ import { getEnvironment } from 'environment';
export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle => {
const { _structureSetup, _setLifecycleCommunication } = lifecycleHub;
const { _host, _padding, _viewport } = _structureSetup._targetObj;
const [updatePaddingCache, currentPaddingCache] = createCache<TRBL>(
topRightBottomLeft.bind(0, _host, 'padding'),
const [updatePaddingCache, currentPaddingCache] = createCache(
{
_equal: equalTRBL,
_initialValue: topRightBottomLeft(),
}
},
topRightBottomLeft.bind(0, _host, 'padding', '')
);
return (updateHints, checkOption, force) => {
@@ -62,7 +62,7 @@ export const createPaddingLifecycle = (lifecycleHub: LifecycleHub): Lifecycle =>
_setLifecycleCommunication({
_paddingInfo: {
_absolute: !paddingRelative,
_padding: padding!,
_padding: padding,
},
_viewportPaddingStyle: _padding
? viewportStyle
@@ -1,4 +1,17 @@
import { each, noop, debounce, indexOf, isString, MutationObserverConstructor, isEmptyArray, on, attr, is, find, push } from 'support';
import {
each,
noop,
debounce,
indexOf,
isString,
MutationObserverConstructor,
isEmptyArray,
on,
attr,
is,
find,
push,
} from 'support';
type DOMContentObserverCallback = (contentChangedTroughEvent: boolean) => any;
@@ -22,7 +35,11 @@ interface DOMTargetObserverOptions extends DOMObserverOptionsBase {
type ContentChangeArrayItem = [string?, string?] | null | undefined;
export type DOMObserverEventContentChange = Array<ContentChangeArrayItem> | false | null | undefined;
export type DOMObserverEventContentChange =
| Array<ContentChangeArrayItem>
| false
| null
| undefined;
export type DOMObserverIgnoreContentChange = (
mutation: MutationRecord,
@@ -42,7 +59,9 @@ export type DOMObserverCallback<ContentObserver extends boolean> = ContentObserv
? DOMContentObserverCallback
: DOMTargetObserverCallback;
export type DOMObserverOptions<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserverOptions : DOMTargetObserverOptions;
export type DOMObserverOptions<ContentObserver extends boolean> = ContentObserver extends true
? DOMContentObserverOptions
: DOMTargetObserverOptions;
export interface DOMObserver {
_destroy: () => void;
@@ -56,7 +75,11 @@ export interface DOMObserver {
* @param callback Callback which is called if one of the elements emits the corresponding event.
* @returns A object which contains a set of helper functions to destroy and update the observation of elements.
*/
const createEventContentChange = (target: Element, callback: (...args: any) => any, eventContentChange?: DOMObserverEventContentChange) => {
const createEventContentChange = (
target: Element,
callback: (...args: any) => any,
eventContentChange?: DOMObserverEventContentChange
) => {
let map: WeakMap<Node, [string, () => any]> | undefined; // weak map to prevent memory leak for detached elements
let destroyed = false;
const _destroy = () => {
@@ -68,7 +91,10 @@ const createEventContentChange = (target: Element, callback: (...args: any) => a
if (item) {
const selector = item[0];
const eventNames = item[1];
const elements = eventNames && selector && (getElements ? getElements(selector) : find(selector, target));
const elements =
eventNames &&
selector &&
(getElements ? getElements(selector) : find(selector, target));
if (elements && elements.length && eventNames && isString(eventNames)) {
push(arr, [elements, eventNames.trim()], true);
@@ -141,7 +167,10 @@ export const createDOMObserver = <ContentObserver extends boolean>(
_ignoreNestedTargetChange,
_ignoreContentChange,
} = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {};
const { _destroy: destroyEventContentChange, _updateElements: updateEventContentChangeElements } = createEventContentChange(
const {
_destroy: destroyEventContentChange,
_updateElements: updateEventContentChangeElements,
} = createEventContentChange(
target,
debounce(
() => {
@@ -159,7 +188,8 @@ export const createDOMObserver = <ContentObserver extends boolean>(
const finalStyleChangingAttributes = _styleChangingAttributes || [];
const observedAttributes = finalAttributes.concat(finalStyleChangingAttributes);
const observerCallback = (mutations: MutationRecord[]) => {
const ignoreTargetChange = (isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop;
const ignoreTargetChange =
(isContentObserver ? _ignoreNestedTargetChange : _ignoreTargetChange) || noop;
const ignoreContentChange = _ignoreContentChange || noop;
const targetChangedAttrs: string[] = [];
const totalAddedNodes: Node[] = [];
@@ -171,19 +201,25 @@ export const createDOMObserver = <ContentObserver extends boolean>(
const isAttributesType = type === 'attributes';
const isChildListType = type === 'childList';
const targetIsMutationTarget = target === mutationTarget;
const attributeValue = isAttributesType && isString(attributeName) ? attr(mutationTarget as HTMLElement, attributeName!) : 0;
const attributeValue =
isAttributesType && isString(attributeName)
? attr(mutationTarget as HTMLElement, attributeName!)
: 0;
const attributeChanged = attributeValue !== 0 && oldValue !== attributeValue;
const styleChangingAttrChanged = indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
const styleChangingAttrChanged =
indexOf(finalStyleChangingAttributes, attributeName) > -1 && attributeChanged;
// if is content observer and something changed in children
if (isContentObserver && !targetIsMutationTarget) {
const notOnlyAttrChanged = !isAttributesType;
const contentAttrChanged = isAttributesType && styleChangingAttrChanged;
const isNestedTarget = contentAttrChanged && _nestedTargetSelector && is(mutationTarget, _nestedTargetSelector);
const isNestedTarget =
contentAttrChanged && _nestedTargetSelector && is(mutationTarget, _nestedTargetSelector);
const baseAssertion = isNestedTarget
? !ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null)
? !ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue)
: notOnlyAttrChanged || contentAttrChanged;
const contentFinalChanged = baseAssertion && !ignoreContentChange(mutation, !!isNestedTarget, target, options);
const contentFinalChanged =
baseAssertion && !ignoreContentChange(mutation, !!isNestedTarget, target, options);
push(totalAddedNodes, addedNodes);
@@ -195,7 +231,7 @@ export const createDOMObserver = <ContentObserver extends boolean>(
!isContentObserver &&
targetIsMutationTarget &&
attributeChanged &&
!ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue as string | null)
!ignoreTargetChange(mutationTarget, attributeName!, oldValue, attributeValue)
) {
push(targetChangedAttrs, attributeName!);
targetStyleChanged = targetStyleChanged || styleChangingAttrChanged;
@@ -81,10 +81,7 @@ export const createSizeObserver = (
const sizeObserver = baseElements[0] as HTMLElement;
const listenerElement = sizeObserver.firstChild as HTMLElement;
const getIsDirectionRTL = getElmDirectionIsRTL.bind(0, sizeObserver);
const [updateResizeObserverContentRectCache] = createCache<
DOMRectReadOnly | undefined,
DOMRectReadOnly
>(0, {
const [updateResizeObserverContentRectCache] = createCache<DOMRectReadOnly | undefined>({
_initialValue: undefined,
_alwaysUpdateValues: true,
_equal: (currVal, newVal) =>
@@ -112,7 +109,6 @@ export const createSizeObserver = (
// if triggered from RO.
if (isResizeObserverCall) {
const [currRContentRect, , prevContentRect] = updateResizeObserverContentRectCache(
0,
(sizeChangedContext as ResizeObserverEntry[]).pop()!.contentRect
);
const hasDimensions = domRectHasDimensions(currRContentRect);
@@ -234,9 +230,12 @@ export const createSizeObserver = (
}
if (observeDirectionChange) {
directionIsRTLCache = createCache(getIsDirectionRTL, {
_initialValue: !getIsDirectionRTL(), // invert current value to trigger initial change
});
directionIsRTLCache = createCache(
{
_initialValue: !getIsDirectionRTL(), // invert current value to trigger initial change
},
getIsDirectionRTL
);
const [updateDirectionIsRTLCache] = directionIsRTLCache;
push(
@@ -22,6 +22,11 @@ export interface TrinsicObserver {
};
}
const isHeightIntrinsic = (ioEntryOrSize: IntersectionObserverEntry | WH<number>): boolean =>
(ioEntryOrSize as WH<number>).h === 0 ||
(ioEntryOrSize as IntersectionObserverEntry).isIntersecting ||
(ioEntryOrSize as IntersectionObserverEntry).intersectionRatio > 0;
/**
* Creates a trinsic observer which observes changes to intrinsic or extrinsic sizing for the height of the target element.
* @param target The element which shall be observed.
@@ -34,24 +39,15 @@ export const createTrinsicObserver = (
): TrinsicObserver => {
const trinsicObserver = createDiv(classNameTrinsicObserver);
const offListeners: (() => void)[] = [];
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache<
boolean,
IntersectionObserverEntry | WH<number>
>(
(ioEntryOrSize: IntersectionObserverEntry | WH<number>) =>
(ioEntryOrSize! as WH<number>).h === 0 ||
(ioEntryOrSize! as IntersectionObserverEntry).isIntersecting ||
(ioEntryOrSize! as IntersectionObserverEntry).intersectionRatio > 0,
{
_initialValue: false,
}
);
const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache({
_initialValue: false,
});
const triggerOnTrinsicChangedCallback = (
updateValue?: IntersectionObserverEntry | WH<number>
) => {
if (updateValue) {
const heightIntrinsic = updateHeightIntrinsicCache(0, updateValue);
const heightIntrinsic = updateHeightIntrinsicCache(isHeightIntrinsic(updateValue));
const [, heightIntrinsicChanged] = heightIntrinsic;
if (heightIntrinsicChanged) {
@@ -59,7 +59,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
},
state: () => lifecycleHub._state(),
update(force?: boolean) {
lifecycleHub._update(null, force);
lifecycleHub._update({}, force);
},
destroy: () => lifecycleHub._destroy(),
};
+44 -41
View File
@@ -1,49 +1,49 @@
export type CacheValues<T> = [
T, // value
boolean, // changed
T | undefined // previous
];
export type Cache<Value, Ctx = undefined> = [
CacheUpdate<Value, Ctx>,
(force?: boolean) => CacheValues<Value> // getCurrent
];
export interface CacheOptions<T> {
export interface CacheOptions<Value> {
// initial value of _value.
_initialValue: T;
_initialValue: Value;
// Custom comparison function if shallow compare isn't enough. Returns true if nothing changed.
_equal?: EqualCachePropFunction<T>;
_equal?: EqualCachePropFunction<Value>;
// If true always updates _value and _previous, otherwise they update only when they changed.
_alwaysUpdateValues?: boolean;
}
export type CacheUpdate<T, C> = undefined extends C
? (force?: boolean | 0, context?: C) => CacheValues<T>
: (force: boolean | 0, context: C) => CacheValues<T>;
export type CacheValues<T> = [
T, // value
boolean, // changed
T? // previous
];
export type UpdateCachePropFunction<Value, Ctx> = undefined extends Ctx
? (context?: Ctx, current?: Value, previous?: Value) => Value
: Ctx extends Value
? ((context: Ctx, current?: Value, previous?: Value) => Value) | 0
: (context: Ctx, current?: Value, previous?: Value) => Value;
export type EqualCachePropFunction<Value> = (currentVal: Value, newVal: Value) => boolean;
export type EqualCachePropFunction<T> = (currentVal?: T, newVal?: T) => boolean;
export type CacheUpdater<Value> = (current: Value, previous?: Value) => Value;
export const createCache = <Value, Ctx = undefined>(
update: UpdateCachePropFunction<Value, Ctx>,
options: CacheOptions<Value>
): Cache<Value, Ctx> => {
export type UpdateCacheContextual<Value> = (newValue: Value, force?: boolean) => CacheValues<Value>;
export type UpdateCache<Value> = (force?: boolean) => CacheValues<Value>;
export type GetCurrentCache<Value> = (force?: boolean) => CacheValues<Value>;
export type Cache<Value> = [UpdateCache<Value>, GetCurrentCache<Value>];
export type CacheContextual<Value> = [UpdateCacheContextual<Value>, GetCurrentCache<Value>];
export function createCache<Value>(options: CacheOptions<Value>): CacheContextual<Value>;
export function createCache<Value>(
options: CacheOptions<Value>,
update: CacheUpdater<Value>
): Cache<Value>;
export function createCache<Value>(
options: CacheOptions<Value>,
update?: CacheUpdater<Value>
): CacheContextual<Value> | Cache<Value> {
const { _initialValue, _equal, _alwaysUpdateValues } = options;
let _value: Value = _initialValue;
let _previous: Value | undefined;
const cacheUpdate = ((force?: boolean | 0, context?: Ctx) => {
const cacheUpdateContextual: UpdateCacheContextual<Value> = (newValue, force?) => {
const curr = _value;
// @ts-ignore
// update can only not be a function if C extends T as described in "UpdateCachePropFunction" type definition
// if C extends T the cast (context as T) is perfectly valid
const newVal = update ? update(context, _value, _previous) : (context as T);
const newVal = newValue;
const changed = force || (_equal ? !_equal(curr, newVal) : curr !== newVal);
if (changed || _alwaysUpdateValues) {
@@ -52,14 +52,17 @@ export const createCache = <Value, Ctx = undefined>(
}
return [_value, changed, _previous];
}) as CacheUpdate<Value, Ctx>;
};
const cacheUpdateIsolated: UpdateCache<Value> = (force?) =>
cacheUpdateContextual(update!(_value, _previous), force);
return [
cacheUpdate,
(force?: boolean) => [
_value,
!!force, // changed
_previous,
],
const getCurrentCache: GetCurrentCache<Value> = (force?: boolean) => [
_value,
!!force, // changed
_previous,
];
};
return [update ? cacheUpdateIsolated : cacheUpdateContextual, getCurrentCache] as
| CacheContextual<Value>
| Cache<Value>;
}
@@ -1,7 +1,9 @@
import { jsAPI } from 'support/compatibility/vendors';
export const MutationObserverConstructor = jsAPI<typeof MutationObserver>('MutationObserver');
export const IntersectionObserverConstructor = jsAPI<typeof IntersectionObserver>('IntersectionObserver');
export const IntersectionObserverConstructor = jsAPI<typeof IntersectionObserver>(
'IntersectionObserver'
);
export const ResizeObserverConstructor = jsAPI<typeof ResizeObserver>('ResizeObserver');
export const cAF = jsAPI<typeof cancelAnimationFrame>('cancelAnimationFrame');
export const rAF = jsAPI<typeof requestAnimationFrame>('requestAnimationFrame');
@@ -23,7 +23,11 @@ function getSetProp(
*/
export function attr(elm: HTMLElement | null, attrName: string): string | null;
export function attr(elm: HTMLElement | null, attrName: string, value: string): void;
export function attr(elm: HTMLElement | null, attrName: string, value?: string): string | null | void {
export function attr(
elm: HTMLElement | null,
attrName: string,
value?: string
): string | null | void {
if (isUndefined(value)) {
return elm ? elm.getAttribute(attrName) : null;
}
@@ -53,7 +53,10 @@ export const removeClass = (elm: Element | false | null | undefined, className:
* @param classNameA ClassName A.
* @param classNameB ClassName B.
*/
export const diffClass = (classNameA: string | null | undefined, classNameB: string | null | undefined) => {
export const diffClass = (
classNameA: string | null | undefined,
classNameB: string | null | undefined
) => {
const classNameASplit = classNameA && classNameA.split(' ');
const classNameBSplit = classNameB && classNameB.split(' ');
const tempObj = {};
@@ -3,7 +3,8 @@ export interface WH<T = number> {
h: T;
}
const elementHasDimensions = (elm: HTMLElement): boolean => !!(elm.offsetWidth || elm.offsetHeight || elm.getClientRects().length);
const elementHasDimensions = (elm: HTMLElement): boolean =>
!!(elm.offsetWidth || elm.offsetHeight || elm.getClientRects().length);
const zeroObj: WH = {
w: 0,
h: 0,
@@ -63,4 +64,5 @@ export const getBoundingClientRect = (elm: HTMLElement): DOMRect => elm.getBound
* Determines whether the passed element has any dimensions.
* @param elm The element.
*/
export const hasDimensions = (elm: HTMLElement | null | undefined): boolean => (elm ? elementHasDimensions(elm as HTMLElement) : false);
export const hasDimensions = (elm: HTMLElement | null | undefined): boolean =>
elm ? elementHasDimensions(elm as HTMLElement) : false;
@@ -37,7 +37,12 @@ export interface OnOptions {
* @param listener The listener which shall be removed.
* @param capture The options of the removed listener.
*/
export const off = <T extends Event = Event>(target: EventTarget, eventNames: string, listener: (event: T) => any, capture?: boolean): void => {
export const off = <T extends Event = Event>(
target: EventTarget,
eventNames: string,
listener: (event: T) => any,
capture?: boolean
): void => {
each(splitEventNames(eventNames), (eventName) => {
target.removeEventListener(eventName, listener as EventListener, capture);
});
@@ -99,4 +104,5 @@ export const preventDefault = (evt: Event): void => evt.preventDefault();
* Shorthand for the stopPropagation and preventDefault event Method.
* @param evt The event of which the stopPropagation and preventDefault methods shall be called.
*/
export const stopAndPrevent = (evt: Event): void => (stopPropagation(evt) as undefined) || (preventDefault(evt) as undefined);
export const stopAndPrevent = (evt: Event): void =>
(stopPropagation(evt) as undefined) || (preventDefault(evt) as undefined);
@@ -10,7 +10,11 @@ type NodeCollection = ArrayLike<Node> | Node | false | null | undefined;
* @param preferredAnchor The element before which the Nodes shall be inserted or null if the elements shall be appended at the end.
* @param insertedElms The Nodes which shall be inserted.
*/
const before = (parentElm: Node | false | null | undefined, preferredAnchor: Node | null | undefined, insertedElms: NodeCollection): void => {
const before = (
parentElm: Node | false | null | undefined,
preferredAnchor: Node | null | undefined,
insertedElms: NodeCollection
): void => {
if (insertedElms) {
let anchor: Node | null | undefined = preferredAnchor;
let fragment: DocumentFragment | Node | null | undefined;
@@ -68,7 +72,10 @@ export const prependChildren = (node: Node | null | undefined, children: NodeCol
* @param node The Node before which the given Nodes shall be inserted.
* @param insertedNodes The Nodes which shall be inserted.
*/
export const insertBefore = (node: Node | null | undefined, insertedNodes: NodeCollection): void => {
export const insertBefore = (
node: Node | null | undefined,
insertedNodes: NodeCollection
): void => {
before(parent(node), node, insertedNodes);
};
@@ -10,19 +10,19 @@ export interface TRBL {
}
const cssNumber = {
//animationiterationcount: 1,
//columncount: 1,
//fillopacity: 1,
//flexgrow: 1,
//flexshrink: 1,
//fontweight: 1,
//lineheight: 1,
// animationiterationcount: 1,
// columncount: 1,
// fillopacity: 1,
// flexgrow: 1,
// flexshrink: 1,
// fontweight: 1,
// lineheight: 1,
// order: 1,
// orphans: 1,
// widows: 1,
// zoom: 1,
opacity: 1,
//order: 1,
//orphans: 1,
//widows: 1,
zindex: 1,
//zoom: 1,
};
const parseToZeroOrNumber = (value: string, toFloat?: boolean): number => {
@@ -31,18 +31,25 @@ const parseToZeroOrNumber = (value: string, toFloat?: boolean): number => {
/* istanbul ignore next */
return Number.isNaN(num) ? 0 : num;
};
const adaptCSSVal = (prop: string, val: string | number): string | number => (!cssNumber[prop.toLowerCase()] && isNumber(val) ? `${val}px` : val);
const adaptCSSVal = (prop: string, val: string | number): string | number =>
!cssNumber[prop.toLowerCase()] && isNumber(val) ? `${val}px` : val;
const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: string): string =>
/* istanbul ignore next */
computedStyle != null ? computedStyle[prop] || computedStyle.getPropertyValue(prop) : elm.style[prop];
const setCSSVal = (elm: HTMLElement | false | null | undefined, prop: string, val: string | number): void => {
computedStyle != null
? computedStyle[prop] || computedStyle.getPropertyValue(prop)
: elm.style[prop];
const setCSSVal = (
elm: HTMLElement | false | null | undefined,
prop: string,
val: string | number
): void => {
try {
if (elm) {
const { style } = elm;
if (!isUndefined(style[prop])) {
style[prop] = adaptCSSVal(prop, val);
const { style: elmStyle } = elm;
if (!isUndefined(elmStyle[prop])) {
elmStyle[prop] = adaptCSSVal(prop, val);
} else {
style.setProperty(prop, val as string);
elmStyle.setProperty(prop, val as string);
}
}
} catch (e) {}
@@ -53,9 +60,18 @@ const setCSSVal = (elm: HTMLElement | false | null | undefined, prop: string, va
* @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<CustomCssProps>(elm: HTMLElement | false | null | undefined, styles: StyleObject<CustomCssProps>): void;
export function style<CustomCssProps>(elm: HTMLElement | false | null | undefined, styles: string): string;
export function style<CustomCssProps>(elm: HTMLElement | false | null | undefined, styles: Array<string> | string): { [key: string]: string };
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: StyleObject<CustomCssProps>
): void;
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: string
): string;
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: Array<string> | string
): { [key: string]: string };
export function style<CustomCssProps>(
elm: HTMLElement | false | null | undefined,
styles: StyleObject<CustomCssProps> | Array<string> | string
@@ -101,7 +117,11 @@ export const show = (elm: HTMLElement | false | null | undefined): void => {
* @param propertyPrefix The css property prefix. (e.g. "border")
* @param propertySuffix The css property suffix. (e.g. "width")
*/
export const topRightBottomLeft = (elm?: HTMLElement | false | null | undefined, propertyPrefix?: string, propertySuffix?: string): TRBL => {
export const topRightBottomLeft = (
elm?: HTMLElement | false | null | undefined,
propertyPrefix?: string,
propertySuffix?: string
): TRBL => {
const finalPrefix = propertyPrefix ? `${propertyPrefix}-` : '';
const finalSuffix = propertySuffix ? `-${propertySuffix}` : '';
const top = `${finalPrefix}top${finalSuffix}`;
@@ -65,7 +65,8 @@ const children = (elm: InputElementType, selector?: string): ReadonlyArray<Eleme
* Returns the childNodes (incl. text-nodes or comments etc.) of the passed element. An empty array is returned if the passed element is null.
* @param elm The element of which the childNodes shall be returned.
*/
const contents = (elm: InputElementType): ReadonlyArray<ChildNode> => (elm ? from(elm.childNodes) : []);
const contents = (elm: InputElementType): ReadonlyArray<ChildNode> =>
elm ? from(elm.childNodes) : [];
/**
* Returns the parent element of the passed element, or null if the passed element is null.
@@ -97,7 +98,11 @@ const closest = (elm: InputElementType, selector: string): OutputElementType =>
* @param highBoundarySelector The high boundary selector.
* @param deepBoundarySelector The deep boundary selector.
*/
const liesBetween = (elm: InputElementType, highBoundarySelector: string, deepBoundarySelector: string): boolean => {
const liesBetween = (
elm: InputElementType,
highBoundarySelector: string,
deepBoundarySelector: string
): boolean => {
const closestHighBoundaryElm = elm && closest(elm, highBoundarySelector);
const closestDeepBoundaryElm = elm && findFirst(deepBoundarySelector, closestHighBoundaryElm);
@@ -14,7 +14,10 @@ export interface OptionsWithOptionsTemplateTransformation<T> {
_options: T;
}
export type OptionsWithOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [T, OptionsTemplateValue<T>];
export type OptionsWithOptionsTemplateValue<T extends OptionsTemplateNativeTypes> = [
T,
OptionsTemplateValue<T>
];
export type OptionsWithOptionsTemplate<T> = {
[P in keyof T]: T[P] extends OptionsObjectType
@@ -29,14 +32,17 @@ export type OptionsWithOptionsTemplate<T> = {
* @param optionsWithOptionsTemplate The OptionsWithOptionsTemplate<T> object which shall be converted.
* @param toTemplate True if the given OptionsWithOptionsTemplate<T> shall be converted to its corresponding Template object.
*/
export const transformOptions = <T>(optionsWithOptionsTemplate: OptionsWithOptionsTemplate<T>): OptionsWithOptionsTemplateTransformation<T> => {
export const transformOptions = <T>(
optionsWithOptionsTemplate: OptionsWithOptionsTemplate<T>
): OptionsWithOptionsTemplateTransformation<T> => {
const result: any = {
_template: {},
_options: {},
};
each(keys(optionsWithOptionsTemplate), (key: Extract<keyof T, string>) => {
const val: PlainObject | OptionsTemplateTypes | Array<OptionsTemplateTypes> = optionsWithOptionsTemplate[key];
const val: PlainObject | OptionsTemplateTypes | Array<OptionsTemplateTypes> =
optionsWithOptionsTemplate[key];
if (isArray(val)) {
result._template[key] = val[1];
@@ -1,10 +1,21 @@
import { each, hasOwnProperty, keys, push, isEmptyObject } from 'support/utils';
import { type, isArray, isUndefined, isPlainObject, isString, isNumber, isBoolean } from 'support/utils/types';
import {
type,
isArray,
isUndefined,
isPlainObject,
isString,
isNumber,
isBoolean,
} from 'support/utils/types';
import { PlainObject } from 'typings';
export type OptionsObjectType = Record<string, unknown>;
export type OptionsFunctionType = (this: unknown, ...args: unknown[]) => unknown;
export type OptionsTemplateType<T extends OptionsTemplateNativeTypes> = ExtractPropsKey<OptionsTemplateTypeMap, T>;
export type OptionsTemplateType<T extends OptionsTemplateNativeTypes> = ExtractPropsKey<
OptionsTemplateTypeMap,
T
>;
export type OptionsTemplateTypes = keyof OptionsTemplateTypeMap;
export type OptionsTemplateNativeTypes = OptionsTemplateTypeMap[keyof OptionsTemplateTypeMap];
@@ -71,13 +82,18 @@ const templateTypePrefixSuffix: readonly [string, string] = ['__TPL_', '_TYPE__'
* Key = normal type string
* value = template type string
*/
const optionsTemplateTypes: OptionsTemplateTypesDictionary = ['boolean', 'number', 'string', 'array', 'object', 'function', 'null'].reduce(
(result, item) => {
result[item] = templateTypePrefixSuffix[0] + item + templateTypePrefixSuffix[1];
return result;
},
{} as OptionsTemplateTypesDictionary
);
const optionsTemplateTypes: OptionsTemplateTypesDictionary = [
'boolean',
'number',
'string',
'array',
'object',
'function',
'null',
].reduce((result, item) => {
result[item] = templateTypePrefixSuffix[0] + item + templateTypePrefixSuffix[1];
return result;
}, {} as OptionsTemplateTypesDictionary);
/**
* Validates the given options object according to the given template object and returns a object which looks like:
@@ -111,13 +127,20 @@ const validateRecursive = <T extends PlainObject>(
each(props, (prop: Extract<keyof T, string>) => {
const optionsDiffValue: any = isUndefined(optionsDiff[prop]) ? {} : optionsDiff[prop];
const optionsValue: any = options[prop];
const templateValue: PlainObject | string | OptionsTemplateTypes | Array<OptionsTemplateTypes> = template[prop];
const templateValue: PlainObject | string | OptionsTemplateTypes | Array<OptionsTemplateTypes> =
template[prop];
const templateIsComplex = isPlainObject(templateValue);
const propPrefix = propPath ? `${propPath}.` : '';
// if the template has a object as value, it means that the options are complex (verschachtelt)
if (templateIsComplex && isPlainObject(optionsValue)) {
const validatedResult = validateRecursive(optionsValue, templateValue as T, optionsDiffValue, doWriteErrors, propPrefix + prop);
const validatedResult = validateRecursive(
optionsValue,
templateValue as T,
optionsDiffValue,
doWriteErrors,
propPrefix + prop
);
validatedOptions[prop] = validatedResult._validated as any;
optionsCopy[prop] = validatedResult._foreign as any;
@@ -163,9 +186,15 @@ const validateRecursive = <T extends PlainObject>(
});
if (isValid) {
const isPrimitiveArr = isArray(optionsValue) && !optionsValue.some((val) => !isNumber(val) && !isString(val) && !isBoolean(val));
const isPrimitiveArr =
isArray(optionsValue) &&
!optionsValue.some((val) => !isNumber(val) && !isString(val) && !isBoolean(val));
const doStringifyComparison = isPrimitiveArr || isPlainObject(optionsValue);
if (doStringifyComparison ? stringify(optionsValue) !== stringify(optionsDiffValue) : optionsValue !== optionsDiffValue) {
if (
doStringifyComparison
? stringify(optionsValue) !== stringify(optionsDiffValue)
: optionsValue !== optionsDiffValue
) {
validatedOptions[prop] = optionsValue;
}
} else if (doWriteErrors) {
@@ -173,7 +202,11 @@ const validateRecursive = <T extends PlainObject>(
`${
`The option "${propPrefix}${prop}" wasn't set, because it doesn't accept the type [ ${optionsValueType.toUpperCase()} ] with the value of "${optionsValue}".\r\n` +
`Accepted types are: [ ${errorPossibleTypes.join(', ').toUpperCase()} ].\r\n`
}${errorEnumStrings.length > 0 ? `\r\nValid strings are: [ ${errorEnumStrings.join(', ')} ].` : ''}`
}${
errorEnumStrings.length > 0
? `\r\nValid strings are: [ ${errorEnumStrings.join(', ')} ].`
: ''
}`
);
}
@@ -209,7 +242,7 @@ const validateOptions = <T extends PlainObject>(
template: OptionsTemplate<T>,
optionsDiff?: T | null,
doWriteErrors?: boolean
): OptionsValidationResult<T> => {
): OptionsValidationResult<T> =>
/*
if (!isEmptyObject(foreign) && doWriteErrors)
console.warn(`The following options are discarded due to invalidity:\r\n ${window.JSON.stringify(foreign, null, 2)}`);
@@ -219,7 +252,6 @@ const validateOptions = <T extends PlainObject>(
Object.assign(result.validated, foreign);
}
*/
return validateRecursive<T>(options, template, optionsDiff || ({} as T), doWriteErrors || false);
};
validateRecursive<T>(options, template, optionsDiff || ({} as T), doWriteErrors || false);
export { validateOptions, optionsTemplateTypes };
@@ -1,4 +1,4 @@
import { isArrayLike, isString } from 'support/utils/types';
import { isArrayLike, isString, isArray } from 'support/utils/types';
import { PlainObject } from 'typings';
type RunEachItem = ((...args: any) => any | any[]) | null | undefined;
@@ -56,21 +56,17 @@ export function each<T>(
* @param item The item.
* @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
*/
export const indexOf = <T = any>(arr: Array<T>, item: T, fromIndex?: number): number =>
arr.indexOf(item, fromIndex);
export const indexOf = <T = any>(arr: T[], item: T, fromIndex?: number): number =>
isArray(arr) ? arr.indexOf(item, fromIndex) : -1;
/**
* Pushesh all given items into the given array and returns it.
* @param array The array the items shall be pushed into.
* @param items The items which shall be pushed into the array.
*/
export const push = <T>(
array: Array<T>,
items: T | ArrayLike<T>,
arrayIsSingleItem?: boolean
): Array<T> => {
export const push = <T>(array: T[], items: T | ArrayLike<T>, arrayIsSingleItem?: boolean): T[] => {
!arrayIsSingleItem && !isString(items) && isArrayLike(items)
? Array.prototype.push.apply(array, items as Array<T>)
? Array.prototype.push.apply(array, items as T[])
: array.push(items as T);
return array;
};
@@ -83,7 +79,7 @@ export const from = <T = any>(arr: ArrayLike<T>) => {
if (Array.from) {
return Array.from(arr);
}
const result: Array<T> = [];
const result: T[] = [];
each(arr, (elm) => {
push(result, elm);
@@ -96,7 +92,7 @@ export const from = <T = any>(arr: ArrayLike<T>) => {
* Check whether the passed array is empty.
* @param array The array which shall be checked.
*/
export const isEmptyArray = (array: Array<any> | null | undefined): boolean =>
export const isEmptyArray = (array: any[] | null | undefined): boolean =>
!!array && array.length === 0;
/**
@@ -6,7 +6,8 @@ import { each } from 'support/utils/array';
* @param obj The object.
* @param prop The name of the property.
*/
export const hasOwnProperty = (obj: any, prop: string | number | symbol): boolean => Object.prototype.hasOwnProperty.call(obj, prop);
export const hasOwnProperty = (obj: any, prop: string | number | symbol): boolean =>
Object.prototype.hasOwnProperty.call(obj, prop);
/**
* Returns the names of the enumerable string properties and methods of an object.
@@ -17,9 +18,27 @@ export const keys = (obj: any): Array<string> => (obj ? Object.keys(obj) : []);
// https://github.com/jquery/jquery/blob/master/src/core.js#L116
export function assignDeep<T, U>(target: T, object1: U): T & U;
export function assignDeep<T, U, V>(target: T, object1: U, object2: V): T & U & V;
export function assignDeep<T, U, V, W>(target: T, object1: U, object2: V, object3: W): T & U & V & W;
export function assignDeep<T, U, V, W, X>(target: T, object1: U, object2: V, object3: W, object4: X): T & U & V & W & X;
export function assignDeep<T, U, V, W, X, Y>(target: T, object1: U, object2: V, object3: W, object4: X, object5: Y): T & U & V & W & X & Y;
export function assignDeep<T, U, V, W>(
target: T,
object1: U,
object2: V,
object3: W
): T & U & V & W;
export function assignDeep<T, U, V, W, X>(
target: T,
object1: U,
object2: V,
object3: W,
object4: X
): T & U & V & W & X;
export function assignDeep<T, U, V, W, X, Y>(
target: T,
object1: U,
object2: V,
object3: W,
object4: X,
object5: Y
): T & U & V & W & X & Y;
export function assignDeep<T, U, V, W, X, Y, Z>(
target: T,
object1?: U,
@@ -11,14 +11,13 @@ export function isNull(obj: any): obj is null {
return obj === null;
}
export const type: (obj: any) => string = (obj) => {
return isUndefined(obj) || isNull(obj)
export const type: (obj: any) => string = (obj) =>
isUndefined(obj) || isNull(obj)
? `${obj}`
: toString
.call(obj)
.replace(/^\[object (.+)\]$/, '$1')
.toLowerCase();
};
export function isNumber(obj: any): obj is number {
return typeof obj === 'number';
@@ -52,7 +51,11 @@ export function isArrayLike<T extends PlainObject = any>(obj: any): obj is Array
const length = !!obj && obj.length;
const lengthCorrectFormat = isNumber(length) && length > -1 && length % 1 == 0; // eslint-disable-line eqeqeq
return isArray(obj) || (!isFunction(obj) && lengthCorrectFormat) ? (length > 0 && isObject(obj) ? length - 1 in obj : true) : false;
return isArray(obj) || (!isFunction(obj) && lengthCorrectFormat)
? length > 0 && isObject(obj)
? length - 1 in obj
: true
: false;
}
/**
@@ -88,7 +91,11 @@ export function isPlainObject<T = any>(obj: any): obj is PlainObject<T> {
*/
export function isHTMLElement(obj: any): obj is HTMLElement {
const instanceofObj = window.HTMLElement;
return obj ? (instanceofObj ? obj instanceof instanceofObj : obj.nodeType === ElementNodeType) : false;
return obj
? instanceofObj
? obj instanceof instanceofObj
: obj.nodeType === ElementNodeType
: false;
}
/**
@@ -97,5 +104,9 @@ export function isHTMLElement(obj: any): obj is HTMLElement {
*/
export function isElement(obj: any): obj is Element {
const instanceofObj = window.Element;
return obj ? (instanceofObj ? obj instanceof instanceofObj : obj.nodeType === ElementNodeType) : false;
return obj
? instanceofObj
? obj instanceof instanceofObj
: obj.nodeType === ElementNodeType
: false;
}
@@ -1,128 +1,122 @@
import { createCache } from 'support/cache';
const createUpdater = <T, C = unknown>(updaterReturn: (i: number) => T) => {
const fn = jest.fn();
const createUpdater = <T>(updaterReturn: (i: number) => T) => {
let index = 0;
const update = (context?: C, curr?: T, prev?: T): T => {
fn(context, curr, prev);
index += 1;
return updaterReturn(index);
};
const updater = jest.fn(
(): T => {
index += 1;
return updaterReturn(index);
}
);
return [fn, update];
return updater;
};
describe('cache', () => {
test('creates and updates cache', () => {
const [fn, updater] = createUpdater((i) => `${i}`);
const updater = createUpdater((i) => `${i}`);
const _initialValue = '';
const [updateCache, getCurrentCache] = createCache<string>(updater, {
_initialValue,
});
const [updateCache, getCurrentCache] = createCache<string>(
{
_initialValue,
},
updater
);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, _initialValue, undefined);
expect(updater).toHaveBeenLastCalledWith(_initialValue, undefined);
expect(value).toBe('1');
expect(previous).toBe(_initialValue);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, '1', _initialValue);
expect(updater).toHaveBeenLastCalledWith('1', _initialValue);
expect(value).toBe('2');
expect(previous).toBe('1');
expect(changed).toBe(true);
});
describe('context', () => {
test('creates and updates cache with context', () => {
describe('contextual cache', () => {
test('creates and updates contextual cache', () => {
interface ContextObj {
test: string;
even: number;
}
const _initialValue = false;
const updateFn = jest.fn();
const updater = (context?: ContextObj, current?: boolean, previous?: boolean) => {
updateFn(context, current, previous);
return context!.test === 'test' || context!.even % 2 === 0;
};
const [updateCache, getCurrentCache] = createCache(updater, { _initialValue });
const updater = (context: ContextObj) => context!.test === 'test' || context!.even % 2 === 0;
const [updateCache, getCurrentCache] = createCache({ _initialValue });
const firstCtx = { test: 'test', even: 2 };
let [value, changed, previous] = updateCache(0, firstCtx);
let [value, changed, previous] = updateCache(updater(firstCtx));
expect([value, false, previous]).toEqual(getCurrentCache());
expect(updateFn).toHaveBeenLastCalledWith(firstCtx, _initialValue, undefined);
expect(value).toBe(true);
expect(previous).toBe(_initialValue);
expect(changed).toBe(true);
expect([value, false, previous]).toEqual(getCurrentCache());
[value, changed, previous] = updateCache(0, firstCtx);
[value, changed, previous] = updateCache(updater(firstCtx));
expect([value, false, previous]).toEqual(getCurrentCache());
expect(updateFn).toHaveBeenLastCalledWith(firstCtx, true, _initialValue);
expect(value).toBe(true);
expect(previous).toBe(_initialValue);
expect(changed).toBe(false);
const scndCtx = { test: 'nah', even: 1 };
[value, changed, previous] = updateCache(0, scndCtx);
[value, changed, previous] = updateCache(updater(scndCtx));
expect([value, false, previous]).toEqual(getCurrentCache());
expect(updateFn).toHaveBeenLastCalledWith(scndCtx, true, _initialValue);
expect(value).toBe(false);
expect(previous).toBe(true);
expect(changed).toBe(true);
[value, changed, previous] = updateCache(0, scndCtx);
[value, changed, previous] = updateCache(updater(scndCtx));
expect([value, false, previous]).toEqual(getCurrentCache());
expect(updateFn).toHaveBeenLastCalledWith(scndCtx, false, true);
expect(value).toBe(false);
expect(previous).toBe(true);
expect(changed).toBe(false);
[value, changed, previous] = updateCache(true, scndCtx);
[value, changed, previous] = updateCache(updater(scndCtx), true);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(updateFn).toHaveBeenLastCalledWith(scndCtx, false, true);
expect(value).toBe(false);
expect(previous).toBe(false);
expect(changed).toBe(true);
});
test('creates and updates cache with context shorthand', () => {
test('creates and updates contextual cache with direct passing', () => {
interface ContextObj {
test: string;
even: number;
}
const _initialValue = undefined;
const firstCtx = { test: 'test', even: 2 };
const [_update] = createCache<ContextObj | undefined, ContextObj>(0, {
const [updateCache] = createCache<ContextObj | undefined>({
_initialValue,
});
let [value, changed, previous] = _update(0, firstCtx);
let [value, changed, previous] = updateCache(firstCtx);
expect(value).toBe(firstCtx);
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = _update(0, firstCtx);
[value, changed, previous] = updateCache(firstCtx);
expect(value).toBe(firstCtx);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
const scndCtx = { test: 'nah', even: 1 };
[value, changed, previous] = _update(0, scndCtx);
[value, changed, previous] = updateCache(scndCtx);
expect(value).toBe(scndCtx);
expect(previous).toBe(firstCtx);
expect(changed).toBe(true);
[value, changed, previous] = _update(0, scndCtx);
[value, changed, previous] = updateCache(scndCtx);
expect(value).toBe(scndCtx);
expect(previous).toBe(firstCtx);
expect(changed).toBe(false);
[value, changed, previous] = _update(true, scndCtx);
[value, changed, previous] = updateCache(scndCtx, true);
expect(value).toBe(scndCtx);
expect(previous).toBe(scndCtx);
expect(changed).toBe(true);
@@ -131,65 +125,73 @@ describe('cache', () => {
describe('equal', () => {
test('with equal always true', () => {
const [fn, updater] = createUpdater((i) => i);
const [updateCache, getCurrentCache] = createCache<number | undefined>(updater, {
_initialValue: undefined,
_equal: () => true,
});
const updater = createUpdater<number | undefined>((i) => i);
const [updateCache, getCurrentCache] = createCache<number | undefined>(
{
_initialValue: undefined,
_equal: () => true,
},
updater
);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe(undefined);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe(undefined);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
});
test('with equal always false', () => {
const [fn, updater] = createUpdater(() => 1);
const [updateCache, getCurrentCache] = createCache<number | undefined>(updater, {
_initialValue: undefined,
_equal: () => false,
});
const updater = createUpdater<number | undefined>(() => 1);
const [updateCache, getCurrentCache] = createCache<number | undefined>(
{
_initialValue: undefined,
_equal: () => false,
},
updater
);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe(1);
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 1, undefined);
expect(updater).toHaveBeenLastCalledWith(1, undefined);
expect(value).toBe(1);
expect(previous).toBe(1);
expect(changed).toBe(true);
});
test('with object equal', () => {
const obj = { a: -1, b: -1 };
const [fn, updater] = createUpdater((i) => ({ a: i, b: i + 1 }));
const [updateCache] = createCache<typeof obj | undefined>(updater, {
_initialValue: undefined,
_equal: (a, b) => a?.a === b?.a && a?.b === b?.b,
});
const updater = createUpdater((i) => ({ a: i, b: i + 1 }));
const [updateCache] = createCache(
{
_initialValue: undefined,
_equal: (a, b) => a?.a === b?.a && a?.b === b?.b,
},
updater
);
let [value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toEqual({ a: 1, b: 2 });
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, { a: 1, b: 2 }, undefined);
expect(updater).toHaveBeenLastCalledWith({ a: 1, b: 2 }, undefined);
expect(value).toEqual({ a: 2, b: 3 });
expect(previous).toEqual({ a: 1, b: 2 });
expect(changed).toBe(true);
@@ -198,19 +200,19 @@ describe('cache', () => {
describe('inital value', () => {
test('creates and updates cache with initialValue', () => {
const [fn, updater] = createUpdater((i) => i);
const [updateCache, getCurrentCache] = createCache<number>(updater, { _initialValue: 0 });
const updater = createUpdater((i) => i);
const [updateCache, getCurrentCache] = createCache<number>({ _initialValue: 0 }, updater);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 0, undefined);
expect(updater).toHaveBeenLastCalledWith(0, undefined);
expect(value).toBe(1);
expect(previous).toBe(0);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 1, 0);
expect(updater).toHaveBeenLastCalledWith(1, 0);
expect(value).toBe(2);
expect(previous).toBe(1);
expect(changed).toBe(true);
@@ -218,20 +220,23 @@ describe('cache', () => {
test('creates and updates cache with initialValue and equal', () => {
const obj = { a: -1, b: -1 };
const [fn, updater] = createUpdater((i) => ({ a: i, b: i + 1 }));
const [updateCache] = createCache<typeof obj>(updater, {
_initialValue: obj,
_equal: (a, b) => a?.a === b?.a && a?.b === b?.b,
});
const updater = createUpdater((i) => ({ a: i, b: i + 1 }));
const [updateCache] = createCache(
{
_initialValue: obj,
_equal: (a, b) => a?.a === b?.a && a?.b === b?.b,
},
updater
);
let [value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, obj, undefined);
expect(updater).toHaveBeenLastCalledWith(obj, undefined);
expect(value).toEqual({ a: 1, b: 2 });
expect(previous).toBe(obj);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, { a: 1, b: 2 }, obj);
expect(updater).toHaveBeenLastCalledWith({ a: 1, b: 2 }, obj);
expect(value).toEqual({ a: 2, b: 3 });
expect(previous).toEqual({ a: 1, b: 2 });
expect(changed).toBe(true);
@@ -240,44 +245,47 @@ describe('cache', () => {
describe('always update values', () => {
test('creates and updates cache with alwaysUpdateValues and equal always true', () => {
const [fn, updater] = createUpdater((i) => i);
const [updateCache] = createCache<number | undefined>(updater, {
_initialValue: undefined,
_alwaysUpdateValues: true,
_equal: () => true,
});
const updater = createUpdater<number | undefined>((i) => i);
const [updateCache] = createCache<number | undefined>(
{
_initialValue: undefined,
_alwaysUpdateValues: true,
_equal: () => true,
},
updater
);
let [value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe(1);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
[value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, 1, undefined);
expect(updater).toHaveBeenLastCalledWith(1, undefined);
expect(value).toBe(2);
expect(previous).toBe(1);
expect(changed).toBe(false);
});
test('creates and updates cache with context shorthand and alwaysUpdateValues', () => {
test('creates and updates contextual cache with alwaysUpdateValues', () => {
interface ContextObj {
test: string;
even: number;
}
const [updateCache, getCurrentCache] = createCache<ContextObj | undefined, ContextObj>(0, {
const [updateCache, getCurrentCache] = createCache<ContextObj | undefined>({
_initialValue: undefined,
_alwaysUpdateValues: true,
});
const firstCtx = { test: 'test', even: 2 };
let [value, changed, previous] = updateCache(0, firstCtx);
let [value, changed, previous] = updateCache(firstCtx);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(value).toBe(firstCtx);
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = updateCache(0, firstCtx);
[value, changed, previous] = updateCache(firstCtx);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(value).toBe(firstCtx);
expect(previous).toBe(firstCtx);
@@ -285,19 +293,19 @@ describe('cache', () => {
const scndCtx = { test: 'nah', even: 1 };
[value, changed, previous] = updateCache(0, scndCtx);
[value, changed, previous] = updateCache(scndCtx);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(value).toBe(scndCtx);
expect(previous).toBe(firstCtx);
expect(changed).toBe(true);
[value, changed, previous] = updateCache(0, scndCtx);
[value, changed, previous] = updateCache(scndCtx);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(value).toBe(scndCtx);
expect(previous).toBe(scndCtx);
expect(changed).toBe(false);
[value, changed, previous] = updateCache(true, scndCtx);
[value, changed, previous] = updateCache(scndCtx, true);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(value).toBe(scndCtx);
expect(previous).toBe(scndCtx);
@@ -307,21 +315,24 @@ describe('cache', () => {
describe('constant', () => {
test('updates constant initially without intial value', () => {
const [fn, updater] = createUpdater(() => true);
const [updateCache, getCurrentCache] = createCache<boolean | undefined>(updater, {
_initialValue: undefined,
});
const updater = createUpdater<boolean | undefined>(() => true);
const [updateCache, getCurrentCache] = createCache<boolean | undefined>(
{
_initialValue: undefined,
},
updater
);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe(true);
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, true, undefined);
expect(updater).toHaveBeenLastCalledWith(true, undefined);
expect(value).toBe(true);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
@@ -329,52 +340,55 @@ describe('cache', () => {
test('doesnt update constant with initial value', () => {
const obj = { constant: true };
const [fn, updater] = createUpdater(() => obj);
const [updateCache] = createCache<typeof obj>(updater, { _initialValue: obj });
const updater = createUpdater(() => obj);
const [updateCache] = createCache({ _initialValue: obj }, updater);
let [value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, obj, undefined);
expect(updater).toHaveBeenLastCalledWith(obj, undefined);
expect(value).toBe(obj);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
[value, changed, previous] = updateCache();
expect(fn).toHaveBeenLastCalledWith(undefined, obj, undefined);
expect(updater).toHaveBeenLastCalledWith(obj, undefined);
expect(value).toBe(obj);
expect(previous).toBe(undefined);
expect(changed).toBe(false);
});
test('updates constant with force', () => {
const [fn, updater] = createUpdater(() => 'constant');
const [updateCache, getCurrentCache] = createCache<string | undefined>(updater, {
_initialValue: undefined,
});
const updater = createUpdater<string | undefined>(() => 'constant');
const [updateCache, getCurrentCache] = createCache<string | undefined>(
{
_initialValue: undefined,
},
updater
);
let [value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, undefined, undefined);
expect(updater).toHaveBeenLastCalledWith(undefined, undefined);
expect(value).toBe('constant');
expect(previous).toBe(undefined);
expect(changed).toBe(true);
[value, changed, previous] = updateCache(true);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 'constant', undefined);
expect(updater).toHaveBeenLastCalledWith('constant', undefined);
expect(value).toBe('constant');
expect(previous).toBe('constant');
expect(changed).toBe(true);
[value, changed, previous] = updateCache(false);
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 'constant', 'constant');
expect(updater).toHaveBeenLastCalledWith('constant', 'constant');
expect(value).toBe('constant');
expect(previous).toBe('constant');
expect(changed).toBe(false);
[value, changed, previous] = updateCache();
expect([value, false, previous]).toEqual(getCurrentCache());
expect(fn).toHaveBeenLastCalledWith(undefined, 'constant', 'constant');
expect(updater).toHaveBeenLastCalledWith('constant', 'constant');
expect(value).toBe('constant');
expect(previous).toBe('constant');
expect(changed).toBe(false);