start with events

This commit is contained in:
Rene
2022-06-23 11:21:25 +02:00
parent 0a3eac283f
commit 0585e40572
13 changed files with 381 additions and 50 deletions
+107 -15
View File
@@ -108,14 +108,22 @@ const push = (array, items, arrayIsSingleItem) => {
return array;
};
const from = arr => {
if (Array.from) {
if (Array.from && arr) {
return Array.from(arr);
}
const result = [];
each(arr, elm => {
push(result, elm);
});
if (arr instanceof Set) {
arr.forEach(value => {
push(result, value);
});
} else {
each(arr, elm => {
push(result, elm);
});
}
return result;
};
const isEmptyArray = array => !!array && array.length === 0;
@@ -933,8 +941,8 @@ const createUniqueViewportArrangeElement = () => {
};
const staticCreationFromStrategy = (target, initializationValue, strategy, elementClass) => {
const result = initializationValue ? initializationValue : isFunction(strategy) ? strategy(target) : strategy;
return result ? result : createDiv(elementClass);
const result = initializationValue || (isFunction(strategy) ? strategy(target) : strategy);
return result || createDiv(elementClass);
};
const dynamicCreationFromStrategy = (target, initializationValue, strategy, elementClass, defaultValue) => {
@@ -2219,7 +2227,7 @@ const lifecycleCommunicationFallback = {
paddingLeft: 0
}
};
const createLifecycleHub = (options, structureSetup, scrollbarsSetup) => {
const createLifecycleHub = (options, triggerEvent, structureSetup, scrollbarsSetup) => {
let lifecycleCommunication = lifecycleCommunicationFallback;
const {
_viewport
@@ -2295,9 +2303,17 @@ const createLifecycleHub = (options, structureSetup, scrollbarsSetup) => {
scrollTop(_viewport, scrollOffsetY);
}
if (options.callbacks.onUpdated) {
options.callbacks.onUpdated();
}
triggerEvent('updated', {
updateHints: {
sizeChanged: _sizeChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation,
directionChanged: finalDirectionIsRTL[1],
heightIntrinsicChanged: finalHeightIntrinsic[1]
},
changedOptions: changedOptions || {},
force: !!force
});
};
const {
@@ -2385,21 +2401,89 @@ const scrollbarsAutoHideAllowedValues = 'never scroll leavemove';
});
const optionsValidationPluginName = '__osOptionsValidationPlugin';
const targets = new Set();
const targetInstanceMap = new WeakMap();
const addInstance = (target, osInstance) => {
targetInstanceMap.set(target, osInstance);
targets.add(target);
};
const removeInstance = target => {
targetInstanceMap.delete(target);
targets.delete(target);
};
const getInstance = target => targetInstanceMap.get(target);
const manageListener = (callback, listener) => {
each(isArray(listener) ? listener : [listener], callback);
};
const createEventHub = () => {
const events = new Map();
const removeEvent = (name, listener) => {
if (name) {
const eventSet = events.get(name);
manageListener(currListener => {
if (eventSet) {
eventSet[currListener ? 'delete' : 'clear'](currListener);
}
}, listener);
} else {
events.forEach(eventSet => {
eventSet.clear();
});
events.clear();
}
};
const addEvent = (name, listener) => {
const eventSet = events.get(name) || new Set();
events.set(name, eventSet);
manageListener(currListener => {
eventSet.add(currListener);
}, listener);
return removeEvent.bind(0, name, listener);
};
const triggerEvent = (name, args) => {
const eventSet = events.get(name);
each(from(eventSet), event => {
event(args);
});
};
return [addEvent, removeEvent, triggerEvent];
};
const OverlayScrollbars = (target, options) => {
const instanceTarget = isHTMLElement(target) ? target : target.target;
const potentialInstance = getInstance(instanceTarget);
if (potentialInstance) {
return potentialInstance;
}
const {
_getDefaultOptions
} = getEnvironment();
const plugins = getPlugins();
const optionsValidationPlugin = plugins[optionsValidationPluginName];
const validateOptions = optionsValidationPlugin && optionsValidationPlugin._;
const currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions ? validateOptions(options || {}, true) : options);
const validateOptions = newOptions => {
const opts = newOptions || {};
const validate = optionsValidationPlugin && optionsValidationPlugin._;
return validate ? validate(opts, true) : opts;
};
const [addEvent, removeEvent, triggerEvent] = createEventHub();
const currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
const structureSetup = createStructureSetup(target);
const scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
const lifecycleHub = createLifecycleHub(currentOptions, structureSetup, scrollbarsSetup);
const lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
const instance = {
options(newOptions) {
if (newOptions) {
const changedOptions = getOptionsDiff(currentOptions, validateOptions ? validateOptions(newOptions, true) : newOptions);
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
if (!isEmptyObject(changedOptions)) {
assignDeep(currentOptions, changedOptions);
@@ -2411,13 +2495,20 @@ const OverlayScrollbars = (target, options) => {
return currentOptions;
},
on: addEvent,
off: removeEvent,
state: () => lifecycleHub._state(),
update(force) {
lifecycleHub._update({}, force);
},
destroy: () => lifecycleHub._destroy()
destroy: () => {
lifecycleHub._destroy();
removeInstance(instanceTarget);
removeEvent();
}
};
each(keys(plugins), pluginName => {
const pluginInstance = plugins[pluginName];
@@ -2427,6 +2518,7 @@ const OverlayScrollbars = (target, options) => {
}
});
instance.update(true);
addInstance(instanceTarget, instance);
return instance;
};
OverlayScrollbars.extend = addPlugin;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+111 -15
View File
@@ -121,14 +121,22 @@
return array;
};
var from = function from(arr) {
if (Array.from) {
if (Array.from && arr) {
return Array.from(arr);
}
var result = [];
each(arr, function (elm) {
push(result, elm);
});
if (arr instanceof Set) {
arr.forEach(function (value) {
push(result, value);
});
} else {
each(arr, function (elm) {
push(result, elm);
});
}
return result;
};
var isEmptyArray = function isEmptyArray(array) {
@@ -1005,8 +1013,8 @@
};
var staticCreationFromStrategy = function staticCreationFromStrategy(target, initializationValue, strategy, elementClass) {
var result = initializationValue ? initializationValue : isFunction(strategy) ? strategy(target) : strategy;
return result ? result : createDiv(elementClass);
var result = initializationValue || (isFunction(strategy) ? strategy(target) : strategy);
return result || createDiv(elementClass);
};
var dynamicCreationFromStrategy = function dynamicCreationFromStrategy(target, initializationValue, strategy, elementClass, defaultValue) {
@@ -2311,7 +2319,7 @@
paddingLeft: 0
}
};
var createLifecycleHub = function createLifecycleHub(options, structureSetup, scrollbarsSetup) {
var createLifecycleHub = function createLifecycleHub(options, triggerEvent, structureSetup, scrollbarsSetup) {
var lifecycleCommunication = lifecycleCommunicationFallback;
var _viewport = structureSetup._targetObj._viewport;
@@ -2392,9 +2400,17 @@
scrollTop(_viewport, scrollOffsetY);
}
if (options.callbacks.onUpdated) {
options.callbacks.onUpdated();
}
triggerEvent('updated', {
updateHints: {
sizeChanged: _sizeChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation,
directionChanged: finalDirectionIsRTL[1],
heightIntrinsicChanged: finalHeightIntrinsic[1]
},
changedOptions: changedOptions || {},
force: !!force
});
};
var _lifecycleHubOservers = lifecycleHubOservers(instance, updateLifecycles),
@@ -2487,21 +2503,95 @@
});
var optionsValidationPluginName = '__osOptionsValidationPlugin';
var targets = new Set();
var targetInstanceMap = new WeakMap();
var addInstance = function addInstance(target, osInstance) {
targetInstanceMap.set(target, osInstance);
targets.add(target);
};
var removeInstance = function removeInstance(target) {
targetInstanceMap.delete(target);
targets.delete(target);
};
var getInstance = function getInstance(target) {
return targetInstanceMap.get(target);
};
var manageListener = function manageListener(callback, listener) {
each(isArray(listener) ? listener : [listener], callback);
};
var createEventHub = function createEventHub() {
var events = new Map();
var removeEvent = function removeEvent(name, listener) {
if (name) {
var eventSet = events.get(name);
manageListener(function (currListener) {
if (eventSet) {
eventSet[currListener ? 'delete' : 'clear'](currListener);
}
}, listener);
} else {
events.forEach(function (eventSet) {
eventSet.clear();
});
events.clear();
}
};
var addEvent = function addEvent(name, listener) {
var eventSet = events.get(name) || new Set();
events.set(name, eventSet);
manageListener(function (currListener) {
eventSet.add(currListener);
}, listener);
return removeEvent.bind(0, name, listener);
};
var triggerEvent = function triggerEvent(name, args) {
var eventSet = events.get(name);
each(from(eventSet), function (event) {
event(args);
});
};
return [addEvent, removeEvent, triggerEvent];
};
var OverlayScrollbars = function OverlayScrollbars(target, options) {
var instanceTarget = isHTMLElement(target) ? target : target.target;
var potentialInstance = getInstance(instanceTarget);
if (potentialInstance) {
return potentialInstance;
}
var _getEnvironment = getEnvironment(),
_getDefaultOptions = _getEnvironment._getDefaultOptions;
var plugins = getPlugins();
var optionsValidationPlugin = plugins[optionsValidationPluginName];
var validateOptions = optionsValidationPlugin && optionsValidationPlugin._;
var currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions ? validateOptions(options || {}, true) : options);
var validateOptions = function validateOptions(newOptions) {
var opts = newOptions || {};
var validate = optionsValidationPlugin && optionsValidationPlugin._;
return validate ? validate(opts, true) : opts;
};
var _createEventHub = createEventHub(),
addEvent = _createEventHub[0],
removeEvent = _createEventHub[1],
triggerEvent = _createEventHub[2];
var currentOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
var structureSetup = createStructureSetup(target);
var scrollbarsSetup = createScrollbarsSetup(target, structureSetup);
var lifecycleHub = createLifecycleHub(currentOptions, structureSetup, scrollbarsSetup);
var lifecycleHub = createLifecycleHub(currentOptions, triggerEvent, structureSetup, scrollbarsSetup);
var instance = {
options: function options(newOptions) {
if (newOptions) {
var changedOptions = getOptionsDiff(currentOptions, validateOptions ? validateOptions(newOptions, true) : newOptions);
var changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
if (!isEmptyObject(changedOptions)) {
assignDeep(currentOptions, changedOptions);
@@ -2512,6 +2602,8 @@
return currentOptions;
},
on: addEvent,
off: removeEvent,
state: function state() {
return lifecycleHub._state();
},
@@ -2519,7 +2611,10 @@
lifecycleHub._update({}, force);
},
destroy: function destroy() {
return lifecycleHub._destroy();
lifecycleHub._destroy();
removeInstance(instanceTarget);
removeEvent();
}
};
each(keys(plugins), function (pluginName) {
@@ -2530,6 +2625,7 @@
}
});
instance.update(true);
addInstance(instanceTarget, instance);
return instance;
};
OverlayScrollbars.extend = addPlugin;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+81
View File
@@ -0,0 +1,81 @@
import { OSOptions } from 'options';
import { each, from, isArray } from 'support';
import { PartialOptions } from 'typings';
export interface onUpdatedEventArgs {
updateHints: {
sizeChanged: boolean;
hostMutation: boolean;
contentMutation: boolean;
directionChanged: boolean;
heightIntrinsicChanged: boolean;
};
changedOptions: PartialOptions<OSOptions>;
force: boolean;
}
export interface EventArgsMap {
updated: onUpdatedEventArgs;
}
export type OSEventListener<N extends keyof EventArgsMap> = (args: EventArgsMap[N]) => void;
export type AddEvent = <N extends keyof EventArgsMap>(
name: N,
listener: OSEventListener<N> | OSEventListener<N>[]
) => () => void;
export type RemoveEvent = <N extends keyof EventArgsMap>(
name?: N,
listener?: OSEventListener<N> | OSEventListener<N>[]
) => void;
export type TriggerEvent = <N extends keyof EventArgsMap>(name: N, args: EventArgsMap[N]) => void;
export type EventHub = [AddEvent, RemoveEvent, TriggerEvent];
const manageListener = <N extends keyof EventArgsMap>(
callback: (listener?: OSEventListener<any>) => void,
listener?: OSEventListener<N> | OSEventListener<N>[]
) => {
each(isArray(listener) ? listener : [listener], callback);
};
export const createEventHub = (): EventHub => {
const events = new Map<string, Set<OSEventListener<any>>>();
const removeEvent: RemoveEvent = (name?, listener?) => {
if (name) {
const eventSet = events.get(name);
manageListener((currListener) => {
if (eventSet) {
eventSet[currListener ? 'delete' : 'clear'](currListener!);
}
}, listener);
} else {
events.forEach((eventSet) => {
eventSet.clear();
});
events.clear();
}
};
const addEvent: AddEvent = (name, listener) => {
const eventSet = events.get(name) || new Set();
events.set(name, eventSet);
manageListener((currListener) => {
eventSet.add(currListener!);
}, listener);
return removeEvent.bind(0, name, listener as any);
};
const triggerEvent: TriggerEvent = (name, args) => {
const eventSet = events.get(name);
each(from(eventSet), (event) => {
event(args);
});
};
return [addEvent, removeEvent, triggerEvent];
};
+3 -2
View File
@@ -26,11 +26,12 @@ export const removeInstance = (target: Element): void => {
* Gets the OverlayScrollbars from the given element or undefined if it doesn't have one.
* @param target The element of which its OverlayScrollbars instance shall be get.
*/
export const getInstance = (target: Element): OverlayScrollbars | undefined => targetInstanceMap.get(target);
export const getInstance = (target: Element): OverlayScrollbars | undefined =>
targetInstanceMap.get(target);
/**
* Gets a Map which represents all active OverayScrollbars instances.
* The Key is the ekement and the value is the instance.
* The Key is the element and the value is the instance.
*/
export const allInstances = (): ReadonlyMap<Element, OverlayScrollbars> => {
const validTargetInstanceMap: Map<Element, OverlayScrollbars> = new Map();
@@ -19,6 +19,7 @@ import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle';
import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle';
import { StyleObject, PartialOptions } from 'typings';
import { ScrollbarsSetup } from 'setups/scrollbarsSetup';
import { TriggerEvent } from '../events';
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
@@ -108,6 +109,7 @@ const lifecycleCommunicationFallback: LifecycleCommunication = {
export const createLifecycleHub = (
options: OSOptions,
triggerEvent: TriggerEvent,
structureSetup: StructureSetup,
scrollbarsSetup: ScrollbarsSetup
): LifecycleHubInstance => {
@@ -208,9 +210,17 @@ export const createLifecycleHub = (
scrollTop(_viewport, scrollOffsetY);
}
if (options.callbacks.onUpdated) {
options.callbacks.onUpdated();
}
triggerEvent('updated', {
updateHints: {
sizeChanged: _sizeChanged,
contentMutation: _contentMutation,
hostMutation: _hostMutation,
directionChanged: finalDirectionIsRTL[1],
heightIntrinsicChanged: finalHeightIntrinsic[1],
},
changedOptions: changedOptions || {},
force: !!force,
});
};
const {
_sizeObserver,
@@ -12,7 +12,8 @@ import {
OSPlugin,
OptionsValidationPluginInstance,
} from 'plugins';
import { addInstance, getInstance } from 'instances';
import { addInstance, getInstance, removeInstance } from 'instances';
import { createEventHub, AddEvent, RemoveEvent } from './events';
export interface OverlayScrollbarsStatic {
(
@@ -32,13 +33,17 @@ export interface OverlayScrollbars {
destroy(): void;
state(): any;
on: AddEvent;
off: RemoveEvent;
}
export const OverlayScrollbars: OverlayScrollbarsStatic = (
target: OSTarget | OSInitializationObject,
options?: PartialOptions<OSOptions>
): OverlayScrollbars => {
const potentialInstance = getInstance(isHTMLElement(target) ? target : target.target);
const instanceTarget = isHTMLElement(target) ? target : target.target;
const potentialInstance = getInstance(instanceTarget);
if (potentialInstance) {
return potentialInstance;
}
@@ -53,10 +58,16 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
const validate = optionsValidationPlugin && optionsValidationPlugin._;
return validate ? validate(opts, true) : opts;
};
const [addEvent, removeEvent, triggerEvent] = createEventHub();
const currentOptions: OSOptions = assignDeep({}, _getDefaultOptions(), validateOptions(options));
const structureSetup: StructureSetup = createStructureSetup(target);
const scrollbarsSetup: ScrollbarsSetup = createScrollbarsSetup(target, structureSetup);
const lifecycleHub = createLifecycleHub(currentOptions, structureSetup, scrollbarsSetup);
const lifecycleHub = createLifecycleHub(
currentOptions,
triggerEvent,
structureSetup,
scrollbarsSetup
);
const instance: OverlayScrollbars = {
options(newOptions?: PartialOptions<OSOptions>) {
@@ -70,11 +81,17 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
}
return currentOptions;
},
on: addEvent,
off: removeEvent,
state: () => lifecycleHub._state(),
update(force?: boolean) {
lifecycleHub._update({}, force);
},
destroy: () => lifecycleHub._destroy(),
destroy: () => {
lifecycleHub._destroy();
removeInstance(instanceTarget);
removeEvent();
},
};
each(keys(plugins), (pluginName) => {
@@ -86,7 +103,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
instance.update(true);
addInstance(structureSetup._targetObj._target, instance);
addInstance(instanceTarget, instance);
return instance;
};
@@ -75,15 +75,21 @@ export const push = <T>(array: T[], items: T | ArrayLike<T>, arrayIsSingleItem?:
* Creates a shallow-copied Array instance from an array-like or iterable object.
* @param arr The object from which the array instance shall be created.
*/
export const from = <T = any>(arr: ArrayLike<T> | Set<T>) => {
if (Array.from) {
export const from = <T = any>(arr?: ArrayLike<T> | Set<T>) => {
if (Array.from && arr) {
return Array.from(arr);
}
const result: T[] = [];
each(arr, (elm) => {
push(result, elm);
});
if (arr instanceof Set) {
arr.forEach((value) => {
push(result, value);
});
} else {
each(arr, (elm) => {
push(result, elm);
});
}
return result;
};
@@ -278,6 +278,15 @@ describe('array utilities', () => {
expect(fromChildNodes).toEqual(Array.from(document.body.childNodes));
document.body.innerHTML = '';
});
test('fallback with Set', () => {
const arrFrom = Array.from;
// @ts-ignore
Array.from = undefined;
const fromResult = from(new Set([1, 2, 3]));
Array.from = arrFrom;
expect(fromResult).toEqual([1, 2, 3]);
});
});
describe('runEach', () => {
+20 -1
View File
@@ -85,10 +85,27 @@ interface OSOptions {
};
}
type OSPluginInstance = Record<string, unknown> | ((staticObj: OverlayScrollbarsStatic, instanceObj: OverlayScrollbars) => void);
type OSPlugin<T extends OSPluginInstance = OSPluginInstance> = [
type OSPlugin<T extends OSPluginInstance> = [
string,
T
];
interface onUpdatedEventArgs {
updateHints: {
sizeChanged: boolean;
hostMutation: boolean;
contentMutation: boolean;
directionChanged: boolean;
heightIntrinsicChanged: boolean;
};
changedOptions: PartialOptions<OSOptions>;
force: boolean;
}
interface EventArgsMap {
updated: onUpdatedEventArgs;
}
type OSEventListener<N extends keyof EventArgsMap> = (args: EventArgsMap[N]) => void;
type AddEvent = <N extends keyof EventArgsMap>(name: N, listener: OSEventListener<N> | OSEventListener<N>[]) => () => void;
type RemoveEvent = <N extends keyof EventArgsMap>(name?: N, listener?: OSEventListener<N> | OSEventListener<N>[]) => void;
interface OverlayScrollbarsStatic {
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, extensions?: any): OverlayScrollbars;
extend(osPlugin: OSPlugin | OSPlugin[]): void;
@@ -99,6 +116,8 @@ interface OverlayScrollbars {
update(force?: boolean): void;
destroy(): void;
state(): any;
on: AddEvent;
off: RemoveEvent;
}
declare const OverlayScrollbars: OverlayScrollbarsStatic;
export { OverlayScrollbars as default };