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