mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-24 00:20:36 +03:00
add debounce and updating options
This commit is contained in:
@@ -1,28 +1,11 @@
|
|||||||
import {
|
import { XY, WH, TRBL, CacheValues, PartialOptions, each, hasOwnProperty, isNumber, scrollLeft, scrollTop, assignDeep } from 'support';
|
||||||
XY,
|
|
||||||
WH,
|
|
||||||
TRBL,
|
|
||||||
CacheValues,
|
|
||||||
PartialOptions,
|
|
||||||
each,
|
|
||||||
hasOwnProperty,
|
|
||||||
isNumber,
|
|
||||||
scrollLeft,
|
|
||||||
scrollTop,
|
|
||||||
assignDeep,
|
|
||||||
liesBetween,
|
|
||||||
diffClass,
|
|
||||||
} from 'support';
|
|
||||||
import { OSOptions } from 'options';
|
import { OSOptions } from 'options';
|
||||||
import { classNameHost, classNameViewport, classNameContent } from 'classnames';
|
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
import { StructureSetup } from 'setups/structureSetup';
|
import { StructureSetup } from 'setups/structureSetup';
|
||||||
|
import { lifecycleHubOservers } from 'lifecycles/lifecycleHubObservers';
|
||||||
import { createTrinsicLifecycle } from 'lifecycles/trinsicLifecycle';
|
import { createTrinsicLifecycle } from 'lifecycles/trinsicLifecycle';
|
||||||
import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle';
|
import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle';
|
||||||
import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle';
|
import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle';
|
||||||
import { createSizeObserver } from 'observers/sizeObserver';
|
|
||||||
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
|
||||||
import { StyleObject } from 'typings';
|
import { StyleObject } from 'typings';
|
||||||
|
|
||||||
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
|
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
|
||||||
@@ -82,28 +65,7 @@ export interface LifecycleHub {
|
|||||||
const getPropByPath = <T>(obj: any, path: string): T =>
|
const getPropByPath = <T>(obj: any, path: string): T =>
|
||||||
obj ? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj) : undefined;
|
obj ? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj) : undefined;
|
||||||
|
|
||||||
// TODO: observer textarea attrs if textarea
|
const booleanCacheValuesFallback: CacheValues<boolean> = {
|
||||||
// TODO: tabindex, open etc.
|
|
||||||
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
|
|
||||||
// TODO: test _ignoreTargetChange for target dom observer
|
|
||||||
const ignorePrefix = 'os-';
|
|
||||||
const hostSelector = `.${classNameHost}`;
|
|
||||||
const viewportSelector = `.${classNameViewport}`;
|
|
||||||
const contentSelector = `.${classNameContent}`;
|
|
||||||
const attrs = ['id', 'class', 'style', 'open'];
|
|
||||||
const ignoreTargetChange = (target: Node, attrName: string, oldValue: string | null, newValue: string | null) => {
|
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
|
||||||
const diff = diffClass(oldValue, newValue);
|
|
||||||
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const directionIsRTLCacheValuesFallback: CacheValues<boolean> = {
|
|
||||||
_value: false,
|
|
||||||
_previous: false,
|
|
||||||
_changed: false,
|
|
||||||
};
|
|
||||||
const heightIntrinsicCacheValuesFallback: CacheValues<boolean> = {
|
|
||||||
_value: false,
|
_value: false,
|
||||||
_previous: false,
|
_previous: false,
|
||||||
_changed: false,
|
_changed: false,
|
||||||
@@ -139,7 +101,7 @@ const lifecycleCommunicationFallback: LifecycleCommunication = {
|
|||||||
|
|
||||||
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup): LifecycleHubInstance => {
|
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup): LifecycleHubInstance => {
|
||||||
let lifecycleCommunication = lifecycleCommunicationFallback;
|
let lifecycleCommunication = lifecycleCommunicationFallback;
|
||||||
const { _host, _viewport, _content } = structureSetup._targetObj;
|
const { _viewport } = structureSetup._targetObj;
|
||||||
const {
|
const {
|
||||||
_nativeScrollbarStyling,
|
_nativeScrollbarStyling,
|
||||||
_nativeScrollbarIsOverlaid,
|
_nativeScrollbarIsOverlaid,
|
||||||
@@ -168,10 +130,11 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
_contentMutation = force || false,
|
_contentMutation = force || false,
|
||||||
_paddingStyleChanged = force || false,
|
_paddingStyleChanged = force || false,
|
||||||
} = updateHints || {};
|
} = updateHints || {};
|
||||||
|
|
||||||
const finalDirectionIsRTL =
|
const finalDirectionIsRTL =
|
||||||
_directionIsRTL || (sizeObserver ? sizeObserver._getCurrentCacheValues(force)._directionIsRTL : directionIsRTLCacheValuesFallback);
|
_directionIsRTL || (_sizeObserver ? _sizeObserver._getCurrentCacheValues(force)._directionIsRTL : booleanCacheValuesFallback);
|
||||||
const finalHeightIntrinsic =
|
const finalHeightIntrinsic =
|
||||||
_heightIntrinsic || (trinsicObserver ? trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : heightIntrinsicCacheValuesFallback);
|
_heightIntrinsic || (_trinsicObserver ? _trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic : booleanCacheValuesFallback);
|
||||||
const checkOption: LifecycleCheckOption = (path) => ({
|
const checkOption: LifecycleCheckOption = (path) => ({
|
||||||
_value: getPropByPath(options, path),
|
_value: getPropByPath(options, path),
|
||||||
_changed: force || getPropByPath(changedOptions, path) !== undefined,
|
_changed: force || getPropByPath(changedOptions, path) !== undefined,
|
||||||
@@ -180,6 +143,11 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
|
||||||
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
|
||||||
|
|
||||||
|
// place before updating lifecycles because of possible flushing of debounce
|
||||||
|
if (_updateObserverOptions) {
|
||||||
|
_updateObserverOptions(checkOption);
|
||||||
|
}
|
||||||
|
|
||||||
each(lifecycles, (lifecycle) => {
|
each(lifecycles, (lifecycle) => {
|
||||||
const {
|
const {
|
||||||
_sizeChanged: adaptiveSizeChanged,
|
_sizeChanged: adaptiveSizeChanged,
|
||||||
@@ -217,58 +185,7 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
options.callbacks.onUpdated();
|
options.callbacks.onUpdated();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const { _sizeObserver, _trinsicObserver, _updateObserverOptions } = lifecycleHubOservers(instance, updateLifecycles);
|
||||||
const onSizeChanged = (directionIsRTL?: CacheValues<boolean>) => {
|
|
||||||
const sizeChanged = !directionIsRTL;
|
|
||||||
updateLifecycles({
|
|
||||||
_directionIsRTL: directionIsRTL,
|
|
||||||
_sizeChanged: sizeChanged,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const onTrinsicChanged = (heightIntrinsic: CacheValues<boolean>) => {
|
|
||||||
updateLifecycles({
|
|
||||||
_heightIntrinsic: heightIntrinsic,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const onHostMutation = () => {
|
|
||||||
// TODO: rAF only here because IE
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
updateLifecycles({
|
|
||||||
_hostMutation: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const onContentMutation = () => {
|
|
||||||
// TODO: rAF only here because IE
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
updateLifecycles({
|
|
||||||
_contentMutation: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
|
||||||
const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling });
|
|
||||||
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
|
|
||||||
_styleChangingAttributes: attrs,
|
|
||||||
_attributes: attrs,
|
|
||||||
_ignoreTargetChange: ignoreTargetChange,
|
|
||||||
});
|
|
||||||
const contentMutationObserver = createDOMObserver(_content || _viewport, true, onContentMutation, {
|
|
||||||
_styleChangingAttributes: attrs,
|
|
||||||
_attributes: attrs,
|
|
||||||
_eventContentChange: options!.updating!.elementEvents,
|
|
||||||
_nestedTargetSelector: hostSelector,
|
|
||||||
_ignoreContentChange: (mutation, isNestedTarget) => {
|
|
||||||
const { target, attributeName } = mutation;
|
|
||||||
return isNestedTarget
|
|
||||||
? false
|
|
||||||
: attributeName
|
|
||||||
? liesBetween(target as Element, hostSelector, viewportSelector) || liesBetween(target as Element, hostSelector, contentSelector)
|
|
||||||
: false;
|
|
||||||
},
|
|
||||||
_ignoreNestedTargetChange: ignoreTargetChange,
|
|
||||||
});
|
|
||||||
|
|
||||||
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) => {
|
const update = (changedOptions?: Partial<OSOptions> | null, force?: boolean) => {
|
||||||
updateLifecycles(null, changedOptions, force);
|
updateLifecycles(null, changedOptions, force);
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { CacheValues, diffClass, debounce, isArray, isNumber } from 'support';
|
||||||
|
import { getEnvironment } from 'environment';
|
||||||
|
import { createSizeObserver, SizeObserverCallbackParams } from 'observers/sizeObserver';
|
||||||
|
import { createTrinsicObserver } from 'observers/trinsicObserver';
|
||||||
|
import { createDOMObserver, DOMObserver } from 'observers/domObserver';
|
||||||
|
import { LifecycleHub, LifecycleCheckOption, LifecycleUpdateHints } from 'lifecycles/lifecycleHub';
|
||||||
|
|
||||||
|
//const hostSelector = `.${classNameHost}`;
|
||||||
|
|
||||||
|
// TODO: observer textarea attrs if textarea
|
||||||
|
// TODO: tabindex, etc. attributes for viewport
|
||||||
|
// TODO: test _ignoreContentChange & _ignoreNestedTargetChange for content dom observer
|
||||||
|
// TODO: test _ignoreTargetChange for target dom observer
|
||||||
|
|
||||||
|
//const viewportSelector = `.${classNameViewport}`;
|
||||||
|
//const contentSelector = `.${classNameContent}`;
|
||||||
|
const ignorePrefix = 'os-';
|
||||||
|
const viewportAttrsFromTarget = ['tabindex'];
|
||||||
|
const baseStyleChangingAttrsTextarea = ['wrap', 'cols', 'rows'];
|
||||||
|
const baseStyleChangingAttrs = ['id', 'class', 'style', 'open'];
|
||||||
|
|
||||||
|
const ignoreTargetChange = (target: Node, attrName: string, oldValue: string | null, newValue: string | null) => {
|
||||||
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
|
const diff = diffClass(oldValue, newValue);
|
||||||
|
return !!diff.find((addedOrRemovedClass) => addedOrRemovedClass.indexOf(ignorePrefix) !== 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const lifecycleHubOservers = (instance: LifecycleHub, updateLifecycles: (updateHints?: Partial<LifecycleUpdateHints> | null) => unknown) => {
|
||||||
|
let debounceTimeout: number | false | undefined;
|
||||||
|
let debounceMaxDelay: number | false | undefined;
|
||||||
|
const { _structureSetup } = instance;
|
||||||
|
const { _targetObj, _targetCtx } = _structureSetup;
|
||||||
|
const { _host, _viewport, _content } = _targetObj;
|
||||||
|
const { _isTextarea } = _targetCtx;
|
||||||
|
const { _nativeScrollbarStyling, _flexboxGlue } = getEnvironment();
|
||||||
|
const contentMutationObserverAttr = _isTextarea ? baseStyleChangingAttrsTextarea : baseStyleChangingAttrs.concat(baseStyleChangingAttrsTextarea);
|
||||||
|
const updateLifecyclesWithDebouncedAdaptiveUpdateHints = debounce(updateLifecycles as (updateHints: Partial<LifecycleUpdateHints>) => any, {
|
||||||
|
_timeout: () => debounceTimeout,
|
||||||
|
_maxDelay: () => debounceMaxDelay,
|
||||||
|
_mergeParams(prev, curr) {
|
||||||
|
const { _sizeChanged: prevSizeChanged, _hostMutation: prevHostMutation, _contentMutation: prevContentMutation } = prev[0];
|
||||||
|
const { _sizeChanged: currSizeChanged, _hostMutation: currvHostMutation, _contentMutation: currContentMutation } = curr[0];
|
||||||
|
const merged: [Partial<LifecycleUpdateHints>] = [
|
||||||
|
{
|
||||||
|
_sizeChanged: prevSizeChanged || currSizeChanged,
|
||||||
|
_hostMutation: prevHostMutation || currvHostMutation,
|
||||||
|
_contentMutation: prevContentMutation || currContentMutation,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onTrinsicChanged = (heightIntrinsic: CacheValues<boolean>) => {
|
||||||
|
updateLifecycles({
|
||||||
|
_heightIntrinsic: heightIntrinsic,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onSizeChanged = ({ _sizeChanged, _directionIsRTLCache, _appear }: SizeObserverCallbackParams) => {
|
||||||
|
const updateFn = !_sizeChanged || _appear ? updateLifecycles : updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
||||||
|
updateFn({
|
||||||
|
_sizeChanged,
|
||||||
|
_directionIsRTL: _directionIsRTLCache,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onContentMutation = (contentChangedTroughEvent: boolean) => {
|
||||||
|
// if contentChangedTroughEvent is true its already debounced
|
||||||
|
const updateFn = contentChangedTroughEvent ? updateLifecycles : updateLifecyclesWithDebouncedAdaptiveUpdateHints;
|
||||||
|
updateFn({
|
||||||
|
_contentMutation: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onHostMutation = updateLifecyclesWithDebouncedAdaptiveUpdateHints.bind(0, {
|
||||||
|
_hostMutation: true,
|
||||||
|
}) as () => any;
|
||||||
|
|
||||||
|
const trinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged);
|
||||||
|
const sizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling });
|
||||||
|
const hostMutationObserver = createDOMObserver(_host, false, onHostMutation, {
|
||||||
|
_styleChangingAttributes: baseStyleChangingAttrs,
|
||||||
|
_attributes: baseStyleChangingAttrs,
|
||||||
|
_ignoreTargetChange: ignoreTargetChange,
|
||||||
|
});
|
||||||
|
let contentMutationObserver: DOMObserver | undefined;
|
||||||
|
|
||||||
|
const updateOptions = (checkOption: LifecycleCheckOption) => {
|
||||||
|
const { _value: elementEvents, _changed: elementEventsChanged } = checkOption<Array<[string, string]> | null>('updating.elementEvents');
|
||||||
|
const { _value: attributes, _changed: attributesChanged } = checkOption<string[] | null>('updating.attributes');
|
||||||
|
const { _value: debounce, _changed: debounceChanged } = checkOption<Array<number> | number | null>('updating.debounce');
|
||||||
|
const updateContentMutationObserver = elementEventsChanged || attributesChanged;
|
||||||
|
|
||||||
|
if (updateContentMutationObserver) {
|
||||||
|
if (contentMutationObserver) {
|
||||||
|
contentMutationObserver._update();
|
||||||
|
contentMutationObserver._destroy();
|
||||||
|
}
|
||||||
|
contentMutationObserver = createDOMObserver(_content || _viewport, true, onContentMutation, {
|
||||||
|
_styleChangingAttributes: contentMutationObserverAttr.concat(attributes || []),
|
||||||
|
_attributes: contentMutationObserverAttr.concat(attributes || []),
|
||||||
|
_eventContentChange: elementEvents,
|
||||||
|
_ignoreNestedTargetChange: ignoreTargetChange,
|
||||||
|
//_nestedTargetSelector: hostSelector,
|
||||||
|
/*
|
||||||
|
_ignoreContentChange: (mutation, isNestedTarget) => {
|
||||||
|
const { target, attributeName } = mutation;
|
||||||
|
return isNestedTarget
|
||||||
|
? false
|
||||||
|
: attributeName
|
||||||
|
? liesBetween(target as Element, hostSelector, viewportSelector) || liesBetween(target as Element, hostSelector, contentSelector)
|
||||||
|
: false;
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debounceChanged) {
|
||||||
|
updateLifecyclesWithDebouncedAdaptiveUpdateHints._flush();
|
||||||
|
if (isArray(debounce)) {
|
||||||
|
const timeout = debounce[0];
|
||||||
|
const maxWait = debounce[1];
|
||||||
|
debounceTimeout = isNumber(timeout) ? timeout : false;
|
||||||
|
debounceMaxDelay = isNumber(maxWait) ? maxWait : false;
|
||||||
|
} else if (isNumber(debounce)) {
|
||||||
|
debounceTimeout = debounce;
|
||||||
|
debounceMaxDelay = false;
|
||||||
|
} else {
|
||||||
|
debounceTimeout = false;
|
||||||
|
debounceMaxDelay = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
_trinsicObserver: trinsicObserver,
|
||||||
|
_sizeObserver: sizeObserver,
|
||||||
|
_updateObserverOptions: updateOptions,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -13,12 +13,11 @@ import {
|
|||||||
find,
|
find,
|
||||||
push,
|
push,
|
||||||
isUndefined,
|
isUndefined,
|
||||||
isFunction,
|
|
||||||
} from 'support';
|
} from 'support';
|
||||||
|
|
||||||
type StringNullUndefined = string | null | undefined;
|
type StringNullUndefined = string | null | undefined;
|
||||||
|
|
||||||
type DOMContentObserverCallback = (contentChanged: boolean) => any;
|
type DOMContentObserverCallback = (contentChangedTroughEvent: boolean) => any;
|
||||||
|
|
||||||
type DOMTargetObserverCallback = (targetChangedAttrs: string[], targetStyleChanged: boolean) => any;
|
type DOMTargetObserverCallback = (targetChangedAttrs: string[], targetStyleChanged: boolean) => any;
|
||||||
|
|
||||||
@@ -38,18 +37,7 @@ interface DOMTargetObserverOptions extends DOMObserverOptionsBase {
|
|||||||
_ignoreTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed if it returns true
|
_ignoreTargetChange?: DOMObserverIgnoreTargetChange; // a function which will prevent marking certain attributes as changed if it returns true
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DOMObserverBase {
|
type ContentChangeArrayItem = [StringNullUndefined, StringNullUndefined] | null | undefined;
|
||||||
_destroy: () => void;
|
|
||||||
_update: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DOMContentObserver extends DOMObserverBase {
|
|
||||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DOMTargetObserver extends DOMObserverBase {}
|
|
||||||
|
|
||||||
type ContentChangeArrayItem = [StringNullUndefined, ((elms: Node[]) => StringNullUndefined) | StringNullUndefined] | null | undefined;
|
|
||||||
|
|
||||||
export type DOMObserverEventContentChange = Array<ContentChangeArrayItem> | false | null | undefined;
|
export type DOMObserverEventContentChange = Array<ContentChangeArrayItem> | false | null | undefined;
|
||||||
|
|
||||||
@@ -57,7 +45,7 @@ export type DOMObserverIgnoreContentChange = (
|
|||||||
mutation: MutationRecord,
|
mutation: MutationRecord,
|
||||||
isNestedTarget: boolean,
|
isNestedTarget: boolean,
|
||||||
domObserverTarget: HTMLElement,
|
domObserverTarget: HTMLElement,
|
||||||
domObserverOptions: DOMContentObserverOptions | undefined
|
domObserverOptions?: DOMContentObserverOptions
|
||||||
) => boolean;
|
) => boolean;
|
||||||
|
|
||||||
export type DOMObserverIgnoreTargetChange = (
|
export type DOMObserverIgnoreTargetChange = (
|
||||||
@@ -73,10 +61,10 @@ export type DOMObserverCallback<ContentObserver extends boolean> = ContentObserv
|
|||||||
|
|
||||||
export type DOMObserverOptions<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserverOptions : DOMTargetObserverOptions;
|
export type DOMObserverOptions<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserverOptions : DOMTargetObserverOptions;
|
||||||
|
|
||||||
export type DOMObserver<ContentObserver extends boolean> = ContentObserver extends true ? DOMContentObserver : DOMTargetObserver;
|
export interface DOMObserver {
|
||||||
|
_destroy: () => void;
|
||||||
// const styleChangingAttributes = ['id', 'class', 'style', 'open'];
|
_update: () => void;
|
||||||
// const mutationObserverAttrsTextarea = ['wrap', 'cols', 'rows'];
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a set of helper functions to observe events of elements inside the target element.
|
* Creates a set of helper functions to observe events of elements inside the target element.
|
||||||
@@ -87,7 +75,6 @@ export type DOMObserver<ContentObserver extends boolean> = ContentObserver exten
|
|||||||
*/
|
*/
|
||||||
const createEventContentChange = (target: Element, eventContentChange: DOMObserverEventContentChange, callback: (...args: any) => any) => {
|
const createEventContentChange = (target: Element, eventContentChange: DOMObserverEventContentChange, callback: (...args: any) => any) => {
|
||||||
let map: Map<Node, string> | undefined;
|
let map: Map<Node, string> | undefined;
|
||||||
let eventContentChangeRef: DOMObserverEventContentChange;
|
|
||||||
const _destroy = () => {
|
const _destroy = () => {
|
||||||
if (map) {
|
if (map) {
|
||||||
map.forEach((eventName: string, elm: Node) => off(elm, eventName, callback));
|
map.forEach((eventName: string, elm: Node) => off(elm, eventName, callback));
|
||||||
@@ -95,16 +82,15 @@ const createEventContentChange = (target: Element, eventContentChange: DOMObserv
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const _updateElements = (getElements?: (selector: string) => Node[]) => {
|
const _updateElements = (getElements?: (selector: string) => Node[]) => {
|
||||||
if (map && eventContentChangeRef) {
|
if (map && eventContentChange) {
|
||||||
const eventElmList = eventContentChangeRef.reduce<Array<[Node[], string]>>((arr, item) => {
|
const eventElmList = eventContentChange.reduce<Array<[Node[], string]>>((arr, item) => {
|
||||||
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));
|
||||||
const parsedEventNames = isFunction(eventNames) ? eventNames(elements) : eventNames;
|
|
||||||
|
|
||||||
if (elements && elements.length && parsedEventNames && isString(parsedEventNames)) {
|
if (elements && elements.length && eventNames && isString(eventNames)) {
|
||||||
push(arr, [elements, parsedEventNames.trim()], true);
|
push(arr, [elements, eventNames.trim()], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
@@ -128,21 +114,16 @@ const createEventContentChange = (target: Element, eventContentChange: DOMObserv
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const _updateEventContentChange = (newEventContentChange: DOMObserverEventContentChange) => {
|
|
||||||
map = map || new Map<Node, string>();
|
|
||||||
eventContentChangeRef = newEventContentChange;
|
|
||||||
_destroy();
|
|
||||||
_updateElements();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (eventContentChange) {
|
if (eventContentChange) {
|
||||||
_updateEventContentChange(eventContentChange);
|
map = map || new Map<Node, string>();
|
||||||
|
_destroy();
|
||||||
|
_updateElements();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_destroy,
|
_destroy,
|
||||||
_updateElements,
|
_updateElements,
|
||||||
_updateEventContentChange,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -159,7 +140,7 @@ export const createDOMObserver = <ContentObserver extends boolean>(
|
|||||||
isContentObserver: ContentObserver,
|
isContentObserver: ContentObserver,
|
||||||
callback: DOMObserverCallback<ContentObserver>,
|
callback: DOMObserverCallback<ContentObserver>,
|
||||||
options?: DOMObserverOptions<ContentObserver>
|
options?: DOMObserverOptions<ContentObserver>
|
||||||
): DOMObserver<ContentObserver> => {
|
): DOMObserver => {
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
const {
|
const {
|
||||||
_attributes,
|
_attributes,
|
||||||
@@ -170,18 +151,17 @@ export const createDOMObserver = <ContentObserver extends boolean>(
|
|||||||
_ignoreNestedTargetChange,
|
_ignoreNestedTargetChange,
|
||||||
_ignoreContentChange,
|
_ignoreContentChange,
|
||||||
} = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {};
|
} = (options as DOMContentObserverOptions & DOMTargetObserverOptions) || {};
|
||||||
const {
|
const { _destroy: destroyEventContentChange, _updateElements: updateEventContentChangeElements } = createEventContentChange(
|
||||||
_destroy: destroyEventContentChange,
|
|
||||||
_updateElements: updateEventContentChangeElements,
|
|
||||||
_updateEventContentChange: updateEventContentChange,
|
|
||||||
} = createEventContentChange(
|
|
||||||
target,
|
target,
|
||||||
isContentObserver && _eventContentChange,
|
isContentObserver && _eventContentChange,
|
||||||
debounce(() => {
|
debounce(
|
||||||
if (isConnected) {
|
() => {
|
||||||
(callback as DOMContentObserverCallback)(true);
|
if (isConnected) {
|
||||||
}
|
(callback as DOMContentObserverCallback)(true);
|
||||||
}, 84)
|
}
|
||||||
|
},
|
||||||
|
{ _timeout: 33, _maxDelay: 99 }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// MutationObserver
|
// MutationObserver
|
||||||
@@ -243,7 +223,7 @@ export const createDOMObserver = <ContentObserver extends boolean>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isContentObserver) {
|
if (isContentObserver) {
|
||||||
contentChanged && (callback as DOMContentObserverCallback)(contentChanged);
|
contentChanged && (callback as DOMContentObserverCallback)(false);
|
||||||
} else if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged) {
|
} else if (!isEmptyArray(targetChangedAttrs) || targetStyleChanged) {
|
||||||
(callback as DOMTargetObserverCallback)(targetChangedAttrs, targetStyleChanged);
|
(callback as DOMTargetObserverCallback)(targetChangedAttrs, targetStyleChanged);
|
||||||
}
|
}
|
||||||
@@ -269,13 +249,10 @@ export const createDOMObserver = <ContentObserver extends boolean>(
|
|||||||
isConnected = false;
|
isConnected = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_updateEventContentChange: (newEventContentChange?: DOMObserverEventContentChange) => {
|
|
||||||
updateEventContentChange(isConnected && isContentObserver && newEventContentChange);
|
|
||||||
},
|
|
||||||
_update: () => {
|
_update: () => {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
observerCallback(mutationObserver.takeRecords());
|
observerCallback(mutationObserver.takeRecords());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
} as DOMObserver<ContentObserver>;
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,16 @@ import {
|
|||||||
classNameSizeObserverListenerItemFinal,
|
classNameSizeObserverListenerItemFinal,
|
||||||
} from 'classnames';
|
} from 'classnames';
|
||||||
|
|
||||||
export type SizeObserverOptions = { _direction?: boolean; _appear?: boolean };
|
export interface SizeObserverOptions {
|
||||||
|
_direction?: boolean;
|
||||||
|
_appear?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SizeObserverCallbackParams {
|
||||||
|
_sizeChanged: boolean;
|
||||||
|
_directionIsRTLCache?: CacheValues<boolean>;
|
||||||
|
_appear?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SizeObserver {
|
export interface SizeObserver {
|
||||||
_destroy(): void;
|
_destroy(): void;
|
||||||
@@ -73,7 +82,7 @@ const domRectHasDimensions = (rect?: DOMRectReadOnly) => rect && (rect.height ||
|
|||||||
*/
|
*/
|
||||||
export const createSizeObserver = (
|
export const createSizeObserver = (
|
||||||
target: HTMLElement,
|
target: HTMLElement,
|
||||||
onSizeChangedCallback: (directionIsRTLCache?: CacheValues<boolean>) => any,
|
onSizeChangedCallback: (params: SizeObserverCallbackParams) => any,
|
||||||
options?: SizeObserverOptions
|
options?: SizeObserverOptions
|
||||||
): SizeObserver => {
|
): SizeObserver => {
|
||||||
const { _direction: observeDirectionChange = false, _appear: observeAppearChange = false } = options || {};
|
const { _direction: observeDirectionChange = false, _appear: observeAppearChange = false } = options || {};
|
||||||
@@ -90,31 +99,44 @@ export const createSizeObserver = (
|
|||||||
(!domRectHasDimensions(currVal) && domRectHasDimensions(newVal))
|
(!domRectHasDimensions(currVal) && domRectHasDimensions(newVal))
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
const onSizeChangedCallbackProxy = (sizeChangedContext?: CacheValues<boolean> | ResizeObserverEntry[] | Event) => {
|
const onSizeChangedCallbackProxy = (sizeChangedContext?: CacheValues<boolean> | ResizeObserverEntry[] | Event | boolean) => {
|
||||||
const hasDirectionCache = sizeChangedContext && isBoolean((sizeChangedContext as CacheValues<boolean>)._value);
|
const hasDirectionCache = sizeChangedContext && isBoolean((sizeChangedContext as CacheValues<boolean>)._value);
|
||||||
|
|
||||||
let skip = false;
|
let skip = false;
|
||||||
|
let appear: boolean | number | undefined = false;
|
||||||
let doDirectionScroll = true; // always true if sizeChangedContext is Event (appear callback or RO. Polyfill)
|
let doDirectionScroll = true; // always true if sizeChangedContext is Event (appear callback or RO. Polyfill)
|
||||||
|
|
||||||
// if triggered from RO.
|
// if triggered from RO.
|
||||||
if (isArray(sizeChangedContext) && sizeChangedContext.length > 0) {
|
if (isArray(sizeChangedContext) && sizeChangedContext.length > 0) {
|
||||||
const { _previous, _value, _changed } = updateResizeObserverContentRectCache(0, sizeChangedContext.pop()!.contentRect);
|
const { _previous, _value } = updateResizeObserverContentRectCache(0, sizeChangedContext.pop()!.contentRect);
|
||||||
skip = !_previous || !domRectHasDimensions(_value); // skip on initial RO. call or if display is none
|
const hasDimensions = domRectHasDimensions(_value);
|
||||||
doDirectionScroll = !skip && _changed; // direction scroll when not skipping and changing from display: none to block, false otherwise
|
const hadDimensions = domRectHasDimensions(_previous);
|
||||||
|
skip = !_previous || !hasDimensions; // skip on initial RO. call or if display is none
|
||||||
|
appear = !hadDimensions && hasDimensions;
|
||||||
|
|
||||||
|
doDirectionScroll = !skip; // direction scroll when not skipping
|
||||||
}
|
}
|
||||||
// else if its triggered with DirectionCache
|
// else if its triggered with DirectionCache
|
||||||
else if (hasDirectionCache) {
|
else if (hasDirectionCache) {
|
||||||
doDirectionScroll = (sizeChangedContext as CacheValues<boolean>)._changed; // direction scroll when DirectionCache changed, false otherwise
|
doDirectionScroll = (sizeChangedContext as CacheValues<boolean>)._changed; // direction scroll when DirectionCache changed, false otherwise
|
||||||
}
|
}
|
||||||
|
// else if it triggered with appear from polyfill
|
||||||
|
else {
|
||||||
|
appear = sizeChangedContext === true;
|
||||||
|
}
|
||||||
|
|
||||||
if (observeDirectionChange) {
|
if (observeDirectionChange && doDirectionScroll) {
|
||||||
const rtl = hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>)._value : directionIsRTL(sizeObserver);
|
const rtl = hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>)._value : directionIsRTL(sizeObserver);
|
||||||
scrollLeft(sizeObserver, rtl ? (rtlScrollBehavior.n ? -scrollAmount : rtlScrollBehavior.i ? 0 : scrollAmount) : scrollAmount);
|
scrollLeft(sizeObserver, rtl ? (rtlScrollBehavior.n ? -scrollAmount : rtlScrollBehavior.i ? 0 : scrollAmount) : scrollAmount);
|
||||||
scrollTop(sizeObserver, scrollAmount);
|
scrollTop(sizeObserver, scrollAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skip) {
|
if (!skip) {
|
||||||
onSizeChangedCallback(hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>) : undefined);
|
onSizeChangedCallback({
|
||||||
|
_sizeChanged: !hasDirectionCache,
|
||||||
|
_directionIsRTLCache: hasDirectionCache ? (sizeChangedContext as CacheValues<boolean>) : undefined,
|
||||||
|
_appear: !!appear,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const offListeners: (() => void)[] = [];
|
const offListeners: (() => void)[] = [];
|
||||||
@@ -147,11 +169,11 @@ export const createSizeObserver = (
|
|||||||
scrollLeft(shrinkElement, scrollAmount);
|
scrollLeft(shrinkElement, scrollAmount);
|
||||||
scrollTop(shrinkElement, scrollAmount);
|
scrollTop(shrinkElement, scrollAmount);
|
||||||
};
|
};
|
||||||
const onResized = () => {
|
const onResized = (appear?: unknown) => {
|
||||||
rAFId = 0;
|
rAFId = 0;
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
cacheSize = currSize;
|
cacheSize = currSize;
|
||||||
onSizeChangedCallbackProxy();
|
onSizeChangedCallbackProxy(appear === true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onScroll = (scrollEvent?: Event | false) => {
|
const onScroll = (scrollEvent?: Event | false) => {
|
||||||
@@ -166,7 +188,7 @@ export const createSizeObserver = (
|
|||||||
rAFId = rAF!(onResized);
|
rAFId = rAF!(onResized);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onResized();
|
onResized(scrollEvent === false);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|||||||
@@ -35,9 +35,8 @@ export interface OSOptions {
|
|||||||
paddingAbsolute: boolean;
|
paddingAbsolute: boolean;
|
||||||
updating: {
|
updating: {
|
||||||
elementEvents: Array<[string, string]> | null;
|
elementEvents: Array<[string, string]> | null;
|
||||||
contentMutationDebounce: number;
|
attributes: string[] | null;
|
||||||
hostMutationDebounce: number;
|
debounce: number | [number, number] | null;
|
||||||
resizeDebounce: number;
|
|
||||||
};
|
};
|
||||||
overflow: {
|
overflow: {
|
||||||
x: OverflowBehavior;
|
x: OverflowBehavior;
|
||||||
@@ -109,6 +108,7 @@ export interface UpdatedArgs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
|
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
|
||||||
|
const arrayNullValues: OptionsTemplateValue<Array<unknown> | null> = [oTypes.array, oTypes.null];
|
||||||
const stringArrayNullAllowedValues: OptionsTemplateValue<string | ReadonlyArray<string> | null> = [oTypes.string, oTypes.array, oTypes.null];
|
const stringArrayNullAllowedValues: OptionsTemplateValue<string | ReadonlyArray<string> | null> = [oTypes.string, oTypes.array, oTypes.null];
|
||||||
const booleanTrueTemplate: OptionsWithOptionsTemplateValue<boolean> = [true, oTypes.boolean];
|
const booleanTrueTemplate: OptionsWithOptionsTemplateValue<boolean> = [true, oTypes.boolean];
|
||||||
const booleanFalseTemplate: OptionsWithOptionsTemplateValue<boolean> = [false, oTypes.boolean];
|
const booleanFalseTemplate: OptionsWithOptionsTemplateValue<boolean> = [false, oTypes.boolean];
|
||||||
@@ -137,10 +137,12 @@ const defaultOptionsWithTemplate: OptionsWithOptionsTemplate<OSOptions> = {
|
|||||||
resize: ['none', resizeAllowedValues], // none || both || horizontal || vertical || n || b || h || v
|
resize: ['none', resizeAllowedValues], // none || both || horizontal || vertical || n || b || h || v
|
||||||
paddingAbsolute: booleanFalseTemplate, // true || false
|
paddingAbsolute: booleanFalseTemplate, // true || false
|
||||||
updating: {
|
updating: {
|
||||||
elementEvents: [[['img', 'load']], [oTypes.array, oTypes.null]], // array of tuples || null
|
elementEvents: [[['img', 'load']], arrayNullValues], // array of tuples || null
|
||||||
contentMutationDebounce: [80, numberAllowedValues], // number
|
attributes: [null, arrayNullValues],
|
||||||
hostMutationDebounce: [0, numberAllowedValues], // number
|
debounce: [
|
||||||
resizeDebounce: [0, numberAllowedValues], // number
|
[0, 33],
|
||||||
|
[oTypes.number, oTypes.array, oTypes.null],
|
||||||
|
], // number || number array || null
|
||||||
},
|
},
|
||||||
overflow: {
|
overflow: {
|
||||||
x: ['scroll', overflowAllowedValues], // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
|
x: ['scroll', overflowAllowedValues], // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
|
||||||
|
|||||||
@@ -1,36 +1,103 @@
|
|||||||
import { isNumber } from 'support/utils/types';
|
import { isNumber, isFunction } from 'support/utils/types';
|
||||||
import { cAF, rAF } from 'support/compatibility/apis';
|
import { rAF, cAF } from 'support/compatibility/apis';
|
||||||
|
|
||||||
|
const setT = window.setTimeout;
|
||||||
|
const clearTimeouts = (id: number | undefined) => {
|
||||||
|
id && window.clearTimeout(id);
|
||||||
|
id && cAF!(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
type DebounceTiming = number | false | null | undefined;
|
||||||
|
|
||||||
|
export interface DebounceOptions<FunctionToDebounce extends (...args: any) => any> {
|
||||||
|
/**
|
||||||
|
* The timeout for debouncing. If null, no debounce is applied.
|
||||||
|
*/
|
||||||
|
_timeout?: DebounceTiming | (() => DebounceTiming);
|
||||||
|
/**
|
||||||
|
* A maximum amount of ms. before the function will be called even with debounce.
|
||||||
|
*/
|
||||||
|
_maxDelay?: DebounceTiming | (() => DebounceTiming);
|
||||||
|
/**
|
||||||
|
* Function which merges parameters for each canceled debounce.
|
||||||
|
* If parameters can't be merged the function will return null, otherwise it returns the merged parameters.
|
||||||
|
*/
|
||||||
|
_mergeParams?: (
|
||||||
|
prev: Parameters<FunctionToDebounce>,
|
||||||
|
curr: Parameters<FunctionToDebounce>
|
||||||
|
) => Parameters<FunctionToDebounce> | false | null | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Debounced<FunctionToDebounce extends (...args: any) => any> {
|
||||||
|
(...args: Parameters<FunctionToDebounce>): ReturnType<FunctionToDebounce>;
|
||||||
|
_flush(): void;
|
||||||
|
}
|
||||||
|
|
||||||
export const noop = () => {}; // eslint-disable-line
|
export const noop = () => {}; // eslint-disable-line
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Debounces the given function either with a timeout or a animation frame.
|
* Debounces the given function either with a timeout or a animation frame.
|
||||||
* @param functionToDebounce The function which shall be debounced.
|
* @param functionToDebounce The function which shall be debounced.
|
||||||
* @param timeout The timeout for debouncing. If 0 or lower animation frame is used for debouncing, a timeout otherwise.
|
* @param options Options for debouncing.
|
||||||
* @param maxWait A maximum amount of ms. before the function will be called even with debounce.
|
|
||||||
*/
|
*/
|
||||||
export const debounce = (functionToDebounce: (...args: any) => any, timeout?: number, maxWait?: number) => {
|
export const debounce = <FunctionToDebounce extends (...args: any) => any>(
|
||||||
let timeoutId: number | void;
|
functionToDebounce: FunctionToDebounce,
|
||||||
let lastCallTime: number;
|
options: DebounceOptions<FunctionToDebounce>
|
||||||
const hasTimeout = isNumber(timeout) && timeout > 0;
|
): Debounced<FunctionToDebounce> => {
|
||||||
const hasMaxWait = isNumber(maxWait) && maxWait > 0;
|
let timeoutId: number | undefined;
|
||||||
const cancel = hasTimeout ? window.clearTimeout : cAF!;
|
let maxTimeoutId: number | undefined;
|
||||||
const set = hasTimeout ? window.setTimeout : rAF!;
|
let prevArguments: Parameters<FunctionToDebounce> | null | undefined;
|
||||||
const setFn = function (args: IArguments) {
|
let latestArguments: Parameters<FunctionToDebounce> | null | undefined;
|
||||||
lastCallTime = hasMaxWait ? performance.now() : 0;
|
const { _timeout, _maxDelay, _mergeParams } = options;
|
||||||
timeoutId && cancel(timeoutId);
|
|
||||||
|
const invokeFunctionToDebounce = function (args: IArguments) {
|
||||||
|
clearTimeouts(timeoutId);
|
||||||
|
clearTimeouts(maxTimeoutId);
|
||||||
|
maxTimeoutId = timeoutId = prevArguments = undefined;
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
functionToDebounce.apply(this, args);
|
functionToDebounce.apply(this, args);
|
||||||
};
|
};
|
||||||
|
|
||||||
return function () {
|
const mergeParms = (curr: Parameters<FunctionToDebounce>): Parameters<FunctionToDebounce> | false | null | undefined =>
|
||||||
// eslint-disable-next-line
|
_mergeParams && prevArguments ? _mergeParams(prevArguments, curr) : curr;
|
||||||
// @ts-ignore
|
|
||||||
const boundSetFn = setFn.bind(this, arguments); // eslint-disable-line
|
|
||||||
const forceCall = hasMaxWait ? performance.now() - lastCallTime >= maxWait! : false;
|
|
||||||
|
|
||||||
timeoutId && cancel(timeoutId);
|
const flush = () => {
|
||||||
timeoutId = forceCall ? boundSetFn() : (set(boundSetFn, timeout!) as number);
|
if (timeoutId) {
|
||||||
|
invokeFunctionToDebounce(mergeParms(latestArguments!) || latestArguments!);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const debouncedFn = function () {
|
||||||
|
const args: Parameters<FunctionToDebounce> = arguments as Parameters<FunctionToDebounce>;
|
||||||
|
const finalTimeout = isFunction(_timeout) ? _timeout() : _timeout;
|
||||||
|
const hasTimeout = isNumber(finalTimeout) && finalTimeout >= 0;
|
||||||
|
|
||||||
|
if (hasTimeout) {
|
||||||
|
const finalMaxWait = isFunction(_maxDelay) ? _maxDelay() : _maxDelay;
|
||||||
|
const hasMaxWait = isNumber(finalMaxWait) && finalMaxWait >= 0;
|
||||||
|
const setTimeoutFn = finalTimeout! > 0 ? setT : rAF!;
|
||||||
|
const mergeParamsResult = mergeParms(args);
|
||||||
|
const invokedArgs = mergeParamsResult || args;
|
||||||
|
const boundInvoke = invokeFunctionToDebounce.bind(0, invokedArgs);
|
||||||
|
|
||||||
|
if (!mergeParamsResult) {
|
||||||
|
invokeFunctionToDebounce(prevArguments || args);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimeouts(timeoutId);
|
||||||
|
timeoutId = setTimeoutFn(boundInvoke, finalTimeout as number) as number;
|
||||||
|
|
||||||
|
if (hasMaxWait && !maxTimeoutId) {
|
||||||
|
maxTimeoutId = setT(flush, finalMaxWait as number);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevArguments = latestArguments = invokedArgs;
|
||||||
|
} else {
|
||||||
|
invokeFunctionToDebounce(args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debouncedFn._flush = flush;
|
||||||
|
|
||||||
|
return debouncedFn as Debounced<FunctionToDebounce>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,24 +4,14 @@ import should from 'should';
|
|||||||
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
import { generateSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||||
import { timeout } from '@/testing-browser/timeout';
|
import { timeout } from '@/testing-browser/timeout';
|
||||||
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
||||||
import {
|
import { appendChildren, createDiv, removeElements, children, isArray, isNumber, liesBetween, addClass, removeClass, diffClass, on } from 'support';
|
||||||
appendChildren,
|
|
||||||
createDiv,
|
|
||||||
removeElements,
|
|
||||||
children,
|
|
||||||
isArray,
|
|
||||||
isNumber,
|
|
||||||
liesBetween,
|
|
||||||
hasClass,
|
|
||||||
addClass,
|
|
||||||
removeClass,
|
|
||||||
diffClass,
|
|
||||||
on,
|
|
||||||
} from 'support';
|
|
||||||
|
|
||||||
import { createDOMObserver } from 'observers/domObserver';
|
import { createDOMObserver } from 'observers/domObserver';
|
||||||
|
|
||||||
type DOMContentObserverResult = boolean;
|
type DOMContentObserverResult = {
|
||||||
|
contentChange: boolean;
|
||||||
|
troughEvent: boolean;
|
||||||
|
};
|
||||||
type DOMTargetObserverResult = {
|
type DOMTargetObserverResult = {
|
||||||
changedTargetAttrs: string[];
|
changedTargetAttrs: string[];
|
||||||
styleChanged: boolean;
|
styleChanged: boolean;
|
||||||
@@ -34,6 +24,7 @@ interface SeparateChangeThrough {
|
|||||||
const targetChangesCountSlot: HTMLElement | null = document.querySelector('#targetChanges');
|
const targetChangesCountSlot: HTMLElement | null = document.querySelector('#targetChanges');
|
||||||
const contentChangesCountSlot: HTMLElement | null = document.querySelector('#contentChanges');
|
const contentChangesCountSlot: HTMLElement | null = document.querySelector('#contentChanges');
|
||||||
const targetElm: HTMLElement | null = document.querySelector('#target');
|
const targetElm: HTMLElement | null = document.querySelector('#target');
|
||||||
|
const trargetContentElm: HTMLElement | null = document.querySelector('#target .content');
|
||||||
const targetElmContentElm: HTMLElement | null = document.querySelector('#content-host');
|
const targetElmContentElm: HTMLElement | null = document.querySelector('#content-host');
|
||||||
const contentElmAttrChange: HTMLElement | null = document.querySelector('#target .content-nest');
|
const contentElmAttrChange: HTMLElement | null = document.querySelector('#target .content-nest');
|
||||||
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector('#content-host .padding-nest-item');
|
const contentBetweenElmAttrChange: HTMLElement | null = document.querySelector('#content-host .padding-nest-item');
|
||||||
@@ -67,7 +58,7 @@ const startBtn: HTMLButtonElement | null = document.querySelector('#start');
|
|||||||
const hostSelector = '.host';
|
const hostSelector = '.host';
|
||||||
const ignorePrefix = 'ignore';
|
const ignorePrefix = 'ignore';
|
||||||
const attrs = ['id', 'class', 'style', 'open'];
|
const attrs = ['id', 'class', 'style', 'open'];
|
||||||
const contentChangeArr: Array<[string, string | ((elms: Node[]) => string)]> = [['img', 'load']];
|
const contentChangeArr: Array<[string, string]> = [['img', 'load']];
|
||||||
const domTargetObserverObservations: DOMTargetObserverResult[] = [];
|
const domTargetObserverObservations: DOMTargetObserverResult[] = [];
|
||||||
const domContentObserverObservations: DOMContentObserverResult[] = [];
|
const domContentObserverObservations: DOMContentObserverResult[] = [];
|
||||||
|
|
||||||
@@ -115,44 +106,48 @@ const targetDomObserver = createDOMObserver(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentDomObserver = createDOMObserver(
|
const createContentDomOserver = (eventContentChange: Array<[string | null | undefined, string | null | undefined] | null | undefined>) => {
|
||||||
document.querySelector('#target .content')!,
|
return createDOMObserver(
|
||||||
true,
|
trargetContentElm!,
|
||||||
(contentChanged: boolean) => {
|
true,
|
||||||
should.equal(typeof contentChanged, 'boolean', 'The contentChanged parameter in a content dom observer must be a boolean.');
|
(contentChangedTroughEvent: boolean) => {
|
||||||
|
should.equal(typeof contentChangedTroughEvent, 'boolean', 'The contentChanged parameter in a content dom observer must be a boolean.');
|
||||||
|
|
||||||
domContentObserverObservations.push(contentChanged);
|
domContentObserverObservations.push({ contentChange: true, troughEvent: contentChangedTroughEvent });
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (contentChangesCountSlot) {
|
if (contentChangesCountSlot) {
|
||||||
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
|
contentChangesCountSlot.textContent = `${domContentObserverObservations.length}`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
_styleChangingAttributes: attrs,
|
|
||||||
_attributes: attrs,
|
|
||||||
_eventContentChange: contentChangeArr,
|
|
||||||
_nestedTargetSelector: hostSelector,
|
|
||||||
_ignoreContentChange: (mutation, isNestedTarget) => {
|
|
||||||
const { target, attributeName } = mutation;
|
|
||||||
return isNestedTarget ? false : attributeName ? liesBetween(target as Element, hostSelector, '.content') : false;
|
|
||||||
},
|
},
|
||||||
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
|
{
|
||||||
if (attrName === 'class' && oldValue && newValue) {
|
_styleChangingAttributes: attrs,
|
||||||
const diff = diffClass(oldValue, newValue);
|
_attributes: attrs,
|
||||||
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
_eventContentChange: eventContentChange,
|
||||||
return ignore;
|
_nestedTargetSelector: hostSelector,
|
||||||
}
|
_ignoreContentChange: (mutation, isNestedTarget) => {
|
||||||
return false;
|
const { target, attributeName } = mutation;
|
||||||
},
|
return isNestedTarget ? false : attributeName ? liesBetween(target as Element, hostSelector, '.content') : false;
|
||||||
// @ts-ignore
|
},
|
||||||
_ignoreTargetChange: () => {
|
_ignoreNestedTargetChange: (target, attrName, oldValue, newValue) => {
|
||||||
// if param: isContentObserver = true, this function should never be called.
|
if (attrName === 'class' && oldValue && newValue) {
|
||||||
should.ok(false, 'A content dom observer must not call the _ignoreTargetChange method.');
|
const diff = diffClass(oldValue, newValue);
|
||||||
return true;
|
const ignore = diff.length === 1 && diff[0].startsWith(ignorePrefix);
|
||||||
},
|
return ignore;
|
||||||
}
|
}
|
||||||
);
|
return false;
|
||||||
|
},
|
||||||
|
// @ts-ignore
|
||||||
|
_ignoreTargetChange: () => {
|
||||||
|
// if param: isContentObserver = true, this function should never be called.
|
||||||
|
should.ok(false, 'A content dom observer must not call the _ignoreTargetChange method.');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let contentDomObserver = createContentDomOserver(contentChangeArr);
|
||||||
|
|
||||||
const getTotalObservations = () => domTargetObserverObservations.length + domContentObserverObservations.length;
|
const getTotalObservations = () => domTargetObserverObservations.length + domContentObserverObservations.length;
|
||||||
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
const getLast = <T>(arr: T[], indexFromLast = 0): T => arr[arr.length - 1 - indexFromLast] || ({} as T);
|
||||||
@@ -277,7 +272,7 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
|
|||||||
if (addChangeThrough) {
|
if (addChangeThrough) {
|
||||||
const contentChanged = getLast(addChangeThrough);
|
const contentChanged = getLast(addChangeThrough);
|
||||||
await waitForOrFailTest(() => {
|
await waitForOrFailTest(() => {
|
||||||
should.equal(contentChanged, true, 'Adding an content element must result in a content change.');
|
should.deepEqual(contentChanged, { contentChange: true, troughEvent: false }, 'Adding an content element must result in a content change.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -297,7 +292,11 @@ const addRemoveElementsTest = async (slot: Element | null, changeThrough?: DOMCo
|
|||||||
|
|
||||||
if (removeChangeThrough) {
|
if (removeChangeThrough) {
|
||||||
const contentChanged = getLast(removeChangeThrough);
|
const contentChanged = getLast(removeChangeThrough);
|
||||||
should.equal(contentChanged, true, 'Removing an content element must result in a content change.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'Removing an content element must result in a content change.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -361,10 +360,14 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
compare(2);
|
compare(2);
|
||||||
|
|
||||||
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
||||||
should.equal(previousContentChanged, true, 'Adding an content image must result in a content change.');
|
should.deepEqual(
|
||||||
|
previousContentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'Adding an content image must result in a content change.'
|
||||||
|
);
|
||||||
|
|
||||||
const lastContentChanged = getLast(domContentObserverObservations);
|
const lastContentChanged = getLast(domContentObserverObservations);
|
||||||
should.equal(lastContentChanged, true, 'The images load event must result in a content change.');
|
should.deepEqual(lastContentChanged, { contentChange: true, troughEvent: true }, 'The images load event must result in a content change.');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -375,21 +378,20 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
// test event content change debounce
|
// test event content change debounce
|
||||||
const addMultiple = async () => {
|
const addMultiple = async () => {
|
||||||
const { before, after, compare } = changedThrough(domContentObserverObservations);
|
const { before, after, compare } = changedThrough(domContentObserverObservations);
|
||||||
const addMultipleItem = () => {
|
const genImage = () => {
|
||||||
const img = new Image(1, 1);
|
const img = new Image(1, 1);
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
|
|
||||||
const imgHolder = createDiv('img');
|
const imgHolder = createDiv('img');
|
||||||
appendChildren(imgHolder, img);
|
appendChildren(imgHolder, img);
|
||||||
|
|
||||||
appendChildren(imgElmsSlot, imgHolder);
|
return imgHolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
before();
|
await timeout(250);
|
||||||
|
|
||||||
addMultipleItem();
|
before();
|
||||||
addMultipleItem();
|
appendChildren(imgElmsSlot, [genImage(), genImage(), genImage()]);
|
||||||
addMultipleItem();
|
|
||||||
|
|
||||||
await timeout(250);
|
await timeout(250);
|
||||||
|
|
||||||
@@ -398,20 +400,27 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
compare(2);
|
compare(2);
|
||||||
|
|
||||||
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
const previousContentChanged = getLast(domContentObserverObservations, 1);
|
||||||
should.equal(previousContentChanged, true, 'Adding mutliple content images must result in a single content change. (debounced)');
|
should.deepEqual(
|
||||||
|
previousContentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'Adding mutliple content images must result in a single content change. (debounced)'
|
||||||
|
);
|
||||||
|
|
||||||
const lastContentChanged = getLast(domContentObserverObservations);
|
const lastContentChanged = getLast(domContentObserverObservations);
|
||||||
should.equal(lastContentChanged, true, 'Multiple images load events must result in a single cintent change. (debounced)');
|
should.deepEqual(
|
||||||
|
lastContentChanged,
|
||||||
|
{ contentChange: true, troughEvent: true },
|
||||||
|
'Multiple images load events must result in a single cintent change. (debounced)'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
await addMultiple();
|
await addMultiple();
|
||||||
|
|
||||||
// remove load event from image test
|
// remove load event from image test
|
||||||
const addChanged = async (
|
const addChanged = async (newEventContentChange: Array<[string | null | undefined, string | null | undefined] | null | undefined>) => {
|
||||||
newEventContentChange: Array<[string | null | undefined, (() => string | null | undefined) | string | null | undefined] | null | undefined>
|
contentDomObserver._destroy();
|
||||||
) => {
|
contentDomObserver = createContentDomOserver(newEventContentChange);
|
||||||
contentDomObserver._updateEventContentChange(newEventContentChange);
|
|
||||||
|
|
||||||
const img = new Image(1, 1);
|
const img = new Image(1, 1);
|
||||||
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
img.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
|
||||||
@@ -430,7 +439,8 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
compare(1);
|
compare(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
contentDomObserver._updateEventContentChange(contentChangeArr);
|
contentDomObserver._destroy();
|
||||||
|
contentDomObserver = createContentDomOserver(contentChangeArr);
|
||||||
};
|
};
|
||||||
|
|
||||||
await addChanged([
|
await addChanged([
|
||||||
@@ -440,9 +450,6 @@ const addRemoveImgElmsFn = async () => {
|
|||||||
['img', undefined],
|
['img', undefined],
|
||||||
[null, null],
|
[null, null],
|
||||||
[undefined, undefined],
|
[undefined, undefined],
|
||||||
['img', () => 'hi'],
|
|
||||||
['img', () => null],
|
|
||||||
['img', () => undefined],
|
|
||||||
null,
|
null,
|
||||||
undefined,
|
undefined,
|
||||||
]);
|
]);
|
||||||
@@ -471,7 +478,11 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
compareTransition(expectTransitionEndContentChange ? 2 : 1); // 2 because 1: added class mutation and 2: transition end event
|
compareTransition(expectTransitionEndContentChange ? 2 : 1); // 2 because 1: added class mutation and 2: transition end event
|
||||||
|
|
||||||
const contentChanged = getLast(domContentObserverObservations);
|
const contentChanged = getLast(domContentObserverObservations);
|
||||||
should.equal(contentChanged, true, 'The transitionend event must trigger a event content change.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: expectTransitionEndContentChange },
|
||||||
|
'The transitionend event must trigger a event content change.'
|
||||||
|
);
|
||||||
resolve(1);
|
resolve(1);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -495,11 +506,16 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
compare(1);
|
compare(1);
|
||||||
|
|
||||||
const contentChanged = getLast(domContentObserverObservations);
|
const contentChanged = getLast(domContentObserverObservations);
|
||||||
should.equal(contentChanged, true, 'Adding an content element (transition) must result in a content change.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'Adding an content element (transition) must result in a content change.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
await startTransition(elm, expectTransitionEndContentChange && true);
|
await startTransition(elm, expectTransitionEndContentChange && true);
|
||||||
contentDomObserver._updateEventContentChange(contentChangeArr);
|
contentDomObserver._destroy();
|
||||||
|
contentDomObserver = createContentDomOserver(contentChangeArr);
|
||||||
await startTransition(elm, expectTransitionEndContentChange && false);
|
await startTransition(elm, expectTransitionEndContentChange && false);
|
||||||
|
|
||||||
removeElements(elm);
|
removeElements(elm);
|
||||||
@@ -509,19 +525,8 @@ const addRemoveTransitionElmsFn = async () => {
|
|||||||
|
|
||||||
await add(false);
|
await add(false);
|
||||||
|
|
||||||
contentDomObserver._updateEventContentChange(
|
contentDomObserver._destroy();
|
||||||
contentChangeArr.concat([
|
contentDomObserver = createContentDomOserver(contentChangeArr.concat([['.transition', 'transitionend']]));
|
||||||
[
|
|
||||||
'.transition',
|
|
||||||
(elms) => {
|
|
||||||
elms.forEach((elm) => {
|
|
||||||
should.equal(hasClass(elm as Element, 'transition'), true, 'Every checked element must match the correpsonding selector.'); // in this case "".transition"
|
|
||||||
});
|
|
||||||
return 'transitionend';
|
|
||||||
},
|
|
||||||
],
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
await add(true);
|
await add(true);
|
||||||
};
|
};
|
||||||
@@ -562,7 +567,11 @@ const iterateTargetAttrChange = async () => {
|
|||||||
const iterateContentAttrChange = async () => {
|
const iterateContentAttrChange = async () => {
|
||||||
await iterateAttrChange(setContentAttr, domContentObserverObservations, (observation) => {
|
await iterateAttrChange(setContentAttr, domContentObserverObservations, (observation) => {
|
||||||
const contentChanged = observation;
|
const contentChanged = observation;
|
||||||
should.equal(contentChanged, true, 'A attribute change inside the content must trigger a content change for a DOMContentObserver.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'A attribute change inside the content must trigger a content change for a DOMContentObserver.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredContentAttr);
|
await iterateAttrChange(setFilteredContentAttr);
|
||||||
};
|
};
|
||||||
@@ -573,7 +582,11 @@ const iterateContentBetweenAttrChange = async () => {
|
|||||||
const iterateContentHostElmAttrChange = async () => {
|
const iterateContentHostElmAttrChange = async () => {
|
||||||
await iterateAttrChange(setContentHostElmAttr, domContentObserverObservations, (observation) => {
|
await iterateAttrChange(setContentHostElmAttr, domContentObserverObservations, (observation) => {
|
||||||
const contentChanged = observation;
|
const contentChanged = observation;
|
||||||
should.equal(contentChanged, true, 'A attribute change for a nested target must trigger a content change for a DOMContentObserver.');
|
should.deepEqual(
|
||||||
|
contentChanged,
|
||||||
|
{ contentChange: true, troughEvent: false },
|
||||||
|
'A attribute change for a nested target must trigger a content change for a DOMContentObserver.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
await iterateAttrChange(setFilteredContentHostElmAttr);
|
await iterateAttrChange(setFilteredContentHostElmAttr);
|
||||||
};
|
};
|
||||||
@@ -626,11 +639,9 @@ const start = async () => {
|
|||||||
targetDomObserver._destroy();
|
targetDomObserver._destroy();
|
||||||
targetDomObserver._update();
|
targetDomObserver._update();
|
||||||
|
|
||||||
contentDomObserver._updateEventContentChange([]);
|
|
||||||
contentDomObserver._update();
|
contentDomObserver._update();
|
||||||
contentDomObserver._destroy();
|
contentDomObserver._destroy();
|
||||||
contentDomObserver._destroy();
|
contentDomObserver._destroy();
|
||||||
contentDomObserver._updateEventContentChange([]);
|
|
||||||
contentDomObserver._update();
|
contentDomObserver._update();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ const preInitChildren = targetElm?.children.length;
|
|||||||
|
|
||||||
const sizeObserver = createSizeObserver(
|
const sizeObserver = createSizeObserver(
|
||||||
targetElm as HTMLElement,
|
targetElm as HTMLElement,
|
||||||
(directionIsRTLCache?: any) => {
|
({ _directionIsRTLCache, _sizeChanged, _appear }) => {
|
||||||
if (directionIsRTLCache) {
|
if (_sizeChanged) {
|
||||||
directionIterations += 1;
|
|
||||||
} else {
|
|
||||||
sizeIterations += 1;
|
sizeIterations += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_directionIsRTLCache) {
|
||||||
|
directionIterations += 1;
|
||||||
|
}
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (resizesSlot) {
|
if (resizesSlot) {
|
||||||
resizesSlot.textContent = (directionIterations + sizeIterations).toString();
|
resizesSlot.textContent = (directionIterations + sizeIterations).toString();
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ const rollupConfig = (config = {}, { project = process.cwd(), overwrite = {}, si
|
|||||||
plugins: [
|
plugins: [
|
||||||
...(output.plugins || []),
|
...(output.plugins || []),
|
||||||
rollupTerser({
|
rollupTerser({
|
||||||
ecma: 8,
|
ecma: esm ? 2015 : 5,
|
||||||
safari10: true,
|
safari10: true,
|
||||||
mangle: {
|
mangle: {
|
||||||
safari10: true,
|
safari10: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user