mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-07 20:52:28 +03:00
improve initialization and types
This commit is contained in:
@@ -27,14 +27,10 @@ const createOutputWithMinifiedVersion = (output, esm, buildMinifiedVersion) =>
|
||||
rollupTerser({
|
||||
ecma: esm ? 2015 : 5,
|
||||
safari10: true,
|
||||
mangle: {
|
||||
safari10: true,
|
||||
properties: {
|
||||
regex: /^_/,
|
||||
},
|
||||
},
|
||||
compress: {
|
||||
evaluate: false,
|
||||
module: !!esm,
|
||||
passes: 3,
|
||||
},
|
||||
}),
|
||||
],
|
||||
@@ -63,6 +59,30 @@ module.exports = (esm, options, { declarationFiles = false, outputStyle = false
|
||||
format: esm ? 'esm' : 'umd',
|
||||
generatedCode: esm ? 'es2015' : 'es5',
|
||||
file: path.resolve(distPath, `${file}${esm ? '.esm' : ''}.js`),
|
||||
plugins: [
|
||||
rollupTerser({
|
||||
ecma: esm ? 2015 : 5,
|
||||
safari10: true,
|
||||
|
||||
mangle: {
|
||||
safari10: true,
|
||||
keep_fnames: true, // eslint-disable-line camelcase
|
||||
properties: {
|
||||
regex: /^_/,
|
||||
},
|
||||
},
|
||||
compress: {
|
||||
defaults: false,
|
||||
hoist_funs: true, // eslint-disable-line camelcase
|
||||
},
|
||||
format: {
|
||||
beautify: true,
|
||||
max_line_len: 80, // eslint-disable-line camelcase
|
||||
braces: true,
|
||||
indent_level: 2, // eslint-disable-line camelcase
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
esm,
|
||||
buildMinifiedVersion
|
||||
|
||||
+3
-3
@@ -55,11 +55,11 @@
|
||||
"rollup-plugin-scss": "^3.0.0",
|
||||
"rollup-plugin-serve": "^1.1.0",
|
||||
"rollup-plugin-styles": "^3.10.0",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"rollup-plugin-ts": "^3.0.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-ts": "^3.0.2",
|
||||
"should": "^13.2.3",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "^4.7.3",
|
||||
"typescript": "^4.7.4",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
+1775
-2094
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2117
-2109
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,81 +27,31 @@ import {
|
||||
classNameEnvironmentFlexboxGlueMax,
|
||||
classNameViewportScrollbarStyling,
|
||||
} from 'classnames';
|
||||
import { OSOptions, defaultOptions } from 'options';
|
||||
import { OSTargetElement, PartialOptions } from 'typings';
|
||||
|
||||
type StructureInitializationStrategyElementFn<T> =
|
||||
| ((target: OSTargetElement) => HTMLElement | T)
|
||||
| T;
|
||||
|
||||
type ScrollbarsInitializationStrategyElementFn<T> =
|
||||
| ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T)
|
||||
| T;
|
||||
|
||||
/**
|
||||
* A Static element is an element which MUST be generated.
|
||||
* If null or undefined (or the returned result is null or undefined), the initialization function is generatig the element, otherwise
|
||||
* the element returned by the function acts as the generated element.
|
||||
*/
|
||||
export type StructureInitializationStrategyStaticElement = StructureInitializationStrategyElementFn<
|
||||
null | undefined
|
||||
>;
|
||||
|
||||
/**
|
||||
* A Dynamic element is an element which CAN be generated.
|
||||
* If boolean (or the returned result is boolean), the generation of the element is forced (or not).
|
||||
* If the function returns and element, the element returned by the function acts as the generated element.
|
||||
*/
|
||||
export type StructureInitializationStrategyDynamicElement =
|
||||
StructureInitializationStrategyElementFn<boolean>;
|
||||
|
||||
export interface StructureInitializationStrategy {
|
||||
_host: StructureInitializationStrategyStaticElement;
|
||||
_viewport: StructureInitializationStrategyStaticElement;
|
||||
_padding: StructureInitializationStrategyDynamicElement;
|
||||
_content: StructureInitializationStrategyDynamicElement;
|
||||
}
|
||||
|
||||
export interface ScrollbarsInitializationStrategy {
|
||||
/**
|
||||
* The scrollbars slot. If null or undefined (or the returned result is null or undefined), the initialization function is deciding the element, otherwise
|
||||
* the element returned by the function acts as the scrollbars slot.
|
||||
*/
|
||||
_scrollbarsSlot: ScrollbarsInitializationStrategyElementFn<null | undefined>;
|
||||
}
|
||||
|
||||
export interface InitializationStrategy
|
||||
extends StructureInitializationStrategy,
|
||||
ScrollbarsInitializationStrategy {}
|
||||
|
||||
export type DefaultInitializationStrategy = {
|
||||
[K in keyof InitializationStrategy]: Extract<
|
||||
InitializationStrategy[K],
|
||||
boolean | null | undefined
|
||||
>;
|
||||
};
|
||||
import { Options, defaultOptions } from 'options';
|
||||
import { PartialOptions } from 'typings';
|
||||
import { InitializationStrategy } from 'initialization';
|
||||
|
||||
export interface EnvironmentListenersNameArgsMap {
|
||||
_: undefined;
|
||||
}
|
||||
|
||||
export interface Environment {
|
||||
export interface InternalEnvironment {
|
||||
readonly _nativeScrollbarSize: XY;
|
||||
readonly _nativeScrollbarIsOverlaid: XY<boolean>;
|
||||
readonly _nativeScrollbarStyling: boolean;
|
||||
readonly _rtlScrollBehavior: { n: boolean; i: boolean };
|
||||
readonly _flexboxGlue: boolean;
|
||||
readonly _cssCustomProperties: boolean;
|
||||
readonly _defaultInitializationStrategy: DefaultInitializationStrategy;
|
||||
readonly _defaultDefaultOptions: OSOptions;
|
||||
readonly _defaultInitializationStrategy: InitializationStrategy;
|
||||
readonly _defaultDefaultOptions: Options;
|
||||
_addListener(listener: EventListener<EnvironmentListenersNameArgsMap, '_'>): () => void;
|
||||
_getInitializationStrategy(): InitializationStrategy;
|
||||
_setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
|
||||
_getDefaultOptions(): OSOptions;
|
||||
_setDefaultOptions(newDefaultOptions: PartialOptions<OSOptions>): void;
|
||||
_getDefaultOptions(): Options;
|
||||
_setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
|
||||
}
|
||||
|
||||
let environmentInstance: Environment;
|
||||
let environmentInstance: InternalEnvironment;
|
||||
const { abs, round } = Math;
|
||||
|
||||
const diffBiggerThanOne = (valOne: number, valTwo: number): boolean => {
|
||||
@@ -199,15 +149,12 @@ const getWindowDPR = (): number => {
|
||||
|
||||
const getDefaultInitializationStrategy = (
|
||||
nativeScrollbarStyling: boolean
|
||||
): DefaultInitializationStrategy => ({
|
||||
_host: null,
|
||||
_viewport: null,
|
||||
): InitializationStrategy => ({
|
||||
_padding: !nativeScrollbarStyling,
|
||||
_content: false,
|
||||
_scrollbarsSlot: null,
|
||||
});
|
||||
|
||||
const createEnvironment = (): Environment => {
|
||||
const createEnvironment = (): InternalEnvironment => {
|
||||
const { body } = document;
|
||||
const envDOM = createDOM(`<div class="${classNameEnvironment}"><div></div></div>`);
|
||||
const envElm = envDOM[0] as HTMLElement;
|
||||
@@ -226,7 +173,7 @@ const createEnvironment = (): Environment => {
|
||||
const initializationStrategy = getDefaultInitializationStrategy(nativeScrollbarStyling);
|
||||
const defaultDefaultOptions = assignDeep({}, defaultOptions);
|
||||
|
||||
const env: Environment = {
|
||||
const env: InternalEnvironment = {
|
||||
_nativeScrollbarSize: nativeScrollbarSize,
|
||||
_nativeScrollbarIsOverlaid: nativeScrollbarIsOverlaid,
|
||||
_nativeScrollbarStyling: nativeScrollbarStyling,
|
||||
@@ -242,11 +189,7 @@ const createEnvironment = (): Environment => {
|
||||
_setInitializationStrategy(newInitializationStrategy) {
|
||||
assignDeep(initializationStrategy, newInitializationStrategy);
|
||||
},
|
||||
_getDefaultOptions: assignDeep<OSOptions, OSOptions>.bind(
|
||||
0,
|
||||
{} as OSOptions,
|
||||
defaultDefaultOptions
|
||||
),
|
||||
_getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, defaultDefaultOptions),
|
||||
_setDefaultOptions(newDefaultOptions) {
|
||||
assignDeep(defaultDefaultOptions, newDefaultOptions);
|
||||
},
|
||||
@@ -305,9 +248,11 @@ const createEnvironment = (): Environment => {
|
||||
return env;
|
||||
};
|
||||
|
||||
export const getEnvironment = (): Environment => {
|
||||
const getEnvironment = (): InternalEnvironment => {
|
||||
if (!environmentInstance) {
|
||||
environmentInstance = createEnvironment();
|
||||
}
|
||||
return environmentInstance;
|
||||
};
|
||||
|
||||
export { getEnvironment };
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { OSOptions } from 'options';
|
||||
import { createEventListenerHub } from 'support';
|
||||
import { PartialOptions } from 'typings';
|
||||
import type {
|
||||
InitialEventListeners,
|
||||
AddEventListener,
|
||||
RemoveEventListener,
|
||||
TriggerEventListener,
|
||||
EventListener,
|
||||
} from 'support/eventListeners';
|
||||
/*
|
||||
onScrollStart : null,
|
||||
onScroll : null,
|
||||
onScrollStop : null,
|
||||
onOverflowChanged : null,
|
||||
onOverflowAmountChanged : null, // fusion with onOverflowChanged
|
||||
onDirectionChanged : null, // gone
|
||||
onContentSizeChanged : null, // gone
|
||||
onHostSizeChanged : null, // gone
|
||||
*/
|
||||
|
||||
export interface OnUpdatedEventListenerArgs {
|
||||
updateHints: {
|
||||
sizeChanged: boolean;
|
||||
directionChanged: boolean;
|
||||
heightIntrinsicChanged: boolean;
|
||||
overflowAmountChanged: boolean;
|
||||
overflowStyleChanged: boolean;
|
||||
hostMutation: boolean;
|
||||
contentMutation: boolean;
|
||||
};
|
||||
changedOptions: PartialOptions<OSOptions>;
|
||||
force: boolean;
|
||||
}
|
||||
|
||||
export interface OSEventListenersNameArgsMap {
|
||||
initialized: undefined;
|
||||
initializationWithdrawn: undefined;
|
||||
updated: OnUpdatedEventListenerArgs;
|
||||
destroyed: undefined;
|
||||
}
|
||||
|
||||
export type OSEventListener<
|
||||
N extends Extract<keyof OSEventListenersNameArgsMap, string> = Extract<
|
||||
keyof OSEventListenersNameArgsMap,
|
||||
string
|
||||
>
|
||||
> = EventListener<OSEventListenersNameArgsMap, N>;
|
||||
|
||||
export type AddOSEventListener = AddEventListener<OSEventListenersNameArgsMap>;
|
||||
|
||||
export type RemoveOSEventListener = RemoveEventListener<OSEventListenersNameArgsMap>;
|
||||
|
||||
export type TriggerOSEventListener = TriggerEventListener<OSEventListenersNameArgsMap>;
|
||||
|
||||
export type InitialOSEventListeners = InitialEventListeners<OSEventListenersNameArgsMap>;
|
||||
|
||||
export const createOSEventListenerHub = (initialEventListeners?: InitialOSEventListeners) =>
|
||||
createEventListenerHub(initialEventListeners);
|
||||
@@ -0,0 +1,87 @@
|
||||
import { isFunction, isBoolean, isNull, isUndefined } from 'support';
|
||||
import type {
|
||||
StructureInitialization,
|
||||
StructureInitializationStrategy,
|
||||
} from 'setups/structureSetup';
|
||||
import type {
|
||||
ScrollbarsInitialization,
|
||||
ScrollbarsInitializationStrategy,
|
||||
} from 'setups/scrollbarsSetup';
|
||||
|
||||
type StaticInitialization = HTMLElement | null | undefined;
|
||||
type DynamicInitialization = HTMLElement | boolean | null | undefined;
|
||||
|
||||
export type InitializationTargetElement = HTMLElement | HTMLTextAreaElement;
|
||||
|
||||
export type InitializationTargetObject = StructureInitialization & ScrollbarsInitialization;
|
||||
|
||||
export type InitializationTarget = InitializationTargetElement | InitializationTargetObject;
|
||||
|
||||
export type InitializationStrategy = StructureInitializationStrategy &
|
||||
ScrollbarsInitializationStrategy;
|
||||
|
||||
/**
|
||||
* Static elements MUST be present.
|
||||
* Null or undefined behave like if this element wasn't specified during initialization.
|
||||
*/
|
||||
export type StaticInitializationElement<Args extends any[]> =
|
||||
| ((...args: Args) => StaticInitialization)
|
||||
| StaticInitialization;
|
||||
|
||||
/**
|
||||
* Dynamic element CAN be present.
|
||||
* If its a element the element will be handled as the repsective element.
|
||||
* True means that the respective dynamic element is forced to be generated.
|
||||
* False means that the respective dynamic element is forced NOT to be generated.
|
||||
* Null or undefined behave like if this element wasn't specified during initialization.
|
||||
*/
|
||||
export type DynamicInitializationElement<Args extends any[]> =
|
||||
| ((...args: Args) => DynamicInitialization)
|
||||
| DynamicInitialization;
|
||||
|
||||
export type InitializtationElementStrategy<InitElm> = Exclude<InitElm, HTMLElement>;
|
||||
|
||||
export type DefaultInitializtationElementStrategy<
|
||||
InitElm extends StaticInitializationElement<any> | DynamicInitializationElement<any>
|
||||
> = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any
|
||||
? (...args: P) => HTMLElement
|
||||
: never;
|
||||
|
||||
const staticInitializationElement = <T extends StaticInitializationElement<any>>(
|
||||
args: Parameters<Extract<T, (...args: any[]) => any>>,
|
||||
defaultStaticInitializationElement: DefaultInitializtationElementStrategy<T>,
|
||||
staticInitializationElementStrategy?: InitializtationElementStrategy<T>,
|
||||
staticInitializationElementValue?: T | false
|
||||
): HTMLElement => {
|
||||
const result =
|
||||
staticInitializationElementValue ||
|
||||
(isFunction(staticInitializationElementStrategy)
|
||||
? staticInitializationElementStrategy.apply(0, args)
|
||||
: staticInitializationElementStrategy);
|
||||
|
||||
return (
|
||||
(isFunction(result) ? result.apply(0, args) : result) ||
|
||||
defaultStaticInitializationElement.apply(0, args)
|
||||
);
|
||||
};
|
||||
|
||||
const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>(
|
||||
args: Parameters<Extract<T, (...args: any[]) => any>>,
|
||||
defaultDynamicInitializationElement: DefaultInitializtationElementStrategy<T>,
|
||||
dynamicInitializationElementStrategy?: InitializtationElementStrategy<T>,
|
||||
dynamicInitializationElementValue?: T | false
|
||||
): HTMLElement | false => {
|
||||
const takeInitializationValue =
|
||||
isBoolean(dynamicInitializationElementValue) || !!dynamicInitializationElementValue;
|
||||
const result = takeInitializationValue
|
||||
? (dynamicInitializationElementValue as boolean | HTMLElement)
|
||||
: isFunction(dynamicInitializationElementStrategy)
|
||||
? dynamicInitializationElementStrategy.apply(0, args)
|
||||
: dynamicInitializationElementStrategy;
|
||||
|
||||
return result === true || isNull(result) || isUndefined(result) || isFunction(result)
|
||||
? defaultDynamicInitializationElement.apply(0, args)
|
||||
: result;
|
||||
};
|
||||
|
||||
export { staticInitializationElement, dynamicInitializationElement };
|
||||
@@ -1,7 +1,7 @@
|
||||
import { assignDeep, each, isObject, keys, isArray, hasOwnProperty, isFunction } from 'support';
|
||||
import { PartialOptions, ReadonlyOptions } from 'typings';
|
||||
|
||||
const stringify = (value: any) =>
|
||||
const opsStringify = (value: any) =>
|
||||
JSON.stringify(value, (_, val) => {
|
||||
if (isFunction(val)) {
|
||||
throw new Error();
|
||||
@@ -38,7 +38,7 @@ export type SizeChangedCallback = (this: any, args?: SizeChangedArgs) => void;
|
||||
|
||||
export type UpdatedCallback = (this: any, args?: UpdatedArgs) => void;
|
||||
|
||||
export interface OSOptions {
|
||||
export interface Options {
|
||||
paddingAbsolute: boolean;
|
||||
updating: {
|
||||
elementEvents: Array<[elementSelector: string, eventNames: string]> | null;
|
||||
@@ -64,7 +64,7 @@ export interface OSOptions {
|
||||
};
|
||||
}
|
||||
|
||||
export type ReadonlyOSOptions = ReadonlyOptions<OSOptions>;
|
||||
export type ReadonlyOSOptions = ReadonlyOptions<Options>;
|
||||
|
||||
export interface OverflowChangedArgs {
|
||||
x: boolean;
|
||||
@@ -93,7 +93,7 @@ export interface UpdatedArgs {
|
||||
forced: boolean;
|
||||
}
|
||||
|
||||
export const defaultOptions: OSOptions = {
|
||||
export const defaultOptions: Options = {
|
||||
// resize: 'none', // none || both || horizontal || vertical || n || b || h || v
|
||||
paddingAbsolute: false, // true || false
|
||||
updating: {
|
||||
@@ -145,7 +145,7 @@ export const getOptionsDiff = <T>(
|
||||
|
||||
if (isArray(currOptionValue) || isArray(newOptionValue)) {
|
||||
try {
|
||||
if (stringify(currOptionValue) === stringify(newOptionValue)) {
|
||||
if (opsStringify(currOptionValue) === opsStringify(newOptionValue)) {
|
||||
isDiff = false;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { OSTarget, OSInitializationObject, PartialOptions, OverflowStyle } from 'typings';
|
||||
import {
|
||||
assignDeep,
|
||||
isEmptyObject,
|
||||
@@ -8,10 +7,11 @@ import {
|
||||
isHTMLElement,
|
||||
XY,
|
||||
TRBL,
|
||||
createEventListenerHub,
|
||||
} from 'support';
|
||||
import { createStructureSetup, createScrollbarsSetup } from 'setups';
|
||||
import { getOptionsDiff, OSOptions, ReadonlyOSOptions } from 'options';
|
||||
import { DefaultInitializationStrategy, getEnvironment, InitializationStrategy } from 'environment';
|
||||
import { getOptionsDiff, Options, ReadonlyOSOptions } from 'options';
|
||||
import { getEnvironment } from 'environment';
|
||||
import {
|
||||
getPlugins,
|
||||
addPlugin,
|
||||
@@ -20,41 +20,56 @@ import {
|
||||
OptionsValidationPluginInstance,
|
||||
} from 'plugins';
|
||||
import { addInstance, getInstance, removeInstance } from 'instances';
|
||||
import {
|
||||
createOSEventListenerHub,
|
||||
InitialOSEventListeners,
|
||||
AddOSEventListener,
|
||||
RemoveOSEventListener,
|
||||
} from 'eventListeners';
|
||||
import type { PartialOptions, OverflowStyle } from 'typings';
|
||||
import type {
|
||||
InitializationTarget,
|
||||
InitializationTargetObject,
|
||||
InitializationStrategy,
|
||||
} from 'initialization';
|
||||
import type {
|
||||
InitialEventListeners as GeneralInitialEventListeners,
|
||||
EventListener as GeneralEventListener,
|
||||
} from 'support/eventListeners';
|
||||
|
||||
/*
|
||||
onScrollStart : null,
|
||||
onScroll : null,
|
||||
onScrollStop : null,
|
||||
onOverflowChanged : null,
|
||||
onOverflowAmountChanged : null, // fusion with onOverflowChanged
|
||||
onDirectionChanged : null, // gone
|
||||
onContentSizeChanged : null, // gone
|
||||
onHostSizeChanged : null, // gone
|
||||
*/
|
||||
|
||||
export interface OverlayScrollbarsStatic {
|
||||
(
|
||||
target: OSTarget | OSInitializationObject,
|
||||
options?: PartialOptions<OSOptions>,
|
||||
eventListeners?: InitialOSEventListeners
|
||||
target: InitializationTarget | InitializationTargetObject,
|
||||
options?: PartialOptions<Options>,
|
||||
eventListeners?: GeneralInitialEventListeners<EventListenerMap>
|
||||
): OverlayScrollbars;
|
||||
|
||||
plugin(osPlugin: OSPlugin | OSPlugin[]): void;
|
||||
env(): OverlayScrollbarsEnv;
|
||||
env(): Environment;
|
||||
}
|
||||
|
||||
export interface OverlayScrollbarsEnv {
|
||||
export interface Environment {
|
||||
scrollbarSize: XY<number>;
|
||||
scrollbarIsOverlaid: XY<boolean>;
|
||||
scrollbarStyling: boolean;
|
||||
rtlScrollBehavior: { n: boolean; i: boolean };
|
||||
flexboxGlue: boolean;
|
||||
cssCustomProperties: boolean;
|
||||
defaultInitializationStrategy: DefaultInitializationStrategy;
|
||||
defaultDefaultOptions: OSOptions;
|
||||
defaultInitializationStrategy: InitializationStrategy;
|
||||
defaultDefaultOptions: Options;
|
||||
|
||||
getInitializationStrategy(): InitializationStrategy;
|
||||
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
|
||||
getDefaultOptions(): OSOptions;
|
||||
setDefaultOptions(newDefaultOptions: PartialOptions<OSOptions>): void;
|
||||
getDefaultOptions(): Options;
|
||||
setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
|
||||
}
|
||||
|
||||
export interface OverlayScrollbarsState {
|
||||
export interface State {
|
||||
padding: TRBL;
|
||||
paddingAbsolute: boolean;
|
||||
overflowAmount: XY<number>;
|
||||
@@ -62,7 +77,7 @@ export interface OverlayScrollbarsState {
|
||||
hasOverflow: XY<boolean>;
|
||||
}
|
||||
|
||||
export interface OverlayScrollbarsElements {
|
||||
export interface Elements {
|
||||
target: HTMLElement;
|
||||
host: HTMLElement;
|
||||
padding: HTMLElement;
|
||||
@@ -70,18 +85,51 @@ export interface OverlayScrollbarsElements {
|
||||
content: HTMLElement;
|
||||
}
|
||||
|
||||
export interface OnUpdatedEventListenerArgs {
|
||||
updateHints: {
|
||||
sizeChanged: boolean;
|
||||
directionChanged: boolean;
|
||||
heightIntrinsicChanged: boolean;
|
||||
overflowAmountChanged: boolean;
|
||||
overflowStyleChanged: boolean;
|
||||
hostMutation: boolean;
|
||||
contentMutation: boolean;
|
||||
};
|
||||
changedOptions: PartialOptions<Options>;
|
||||
force: boolean;
|
||||
}
|
||||
|
||||
export interface EventListenerMap {
|
||||
initialized: undefined;
|
||||
initializationWithdrawn: undefined;
|
||||
updated: OnUpdatedEventListenerArgs;
|
||||
destroyed: undefined;
|
||||
}
|
||||
|
||||
export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>;
|
||||
|
||||
export type EventListener<Name extends keyof EventListenerMap> = GeneralEventListener<
|
||||
EventListenerMap,
|
||||
Name
|
||||
>;
|
||||
|
||||
export interface OverlayScrollbars {
|
||||
options(): OSOptions;
|
||||
options(newOptions?: PartialOptions<OSOptions>): OSOptions;
|
||||
options(): Options;
|
||||
options(newOptions?: PartialOptions<Options>): Options;
|
||||
|
||||
update(force?: boolean): void;
|
||||
|
||||
destroy(): void;
|
||||
|
||||
state(): OverlayScrollbarsState;
|
||||
elements(): OverlayScrollbarsElements;
|
||||
state(): State;
|
||||
|
||||
on: AddOSEventListener;
|
||||
off: RemoveOSEventListener;
|
||||
elements(): Elements;
|
||||
|
||||
on<Name extends keyof EventListenerMap>(name: Name, listener: EventListener<Name>): () => void;
|
||||
on<Name extends keyof EventListenerMap>(name: Name, listener: EventListener<Name>[]): () => void;
|
||||
|
||||
off<Name extends keyof EventListenerMap>(name: Name, listener?: EventListener<Name>): void;
|
||||
off<Name extends keyof EventListenerMap>(name: Name, listener?: EventListener<Name>[]): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +157,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||
const optionsValidationPlugin = plugins[
|
||||
optionsValidationPluginName
|
||||
] as OptionsValidationPluginInstance;
|
||||
const validateOptions = (newOptions?: PartialOptions<OSOptions>) => {
|
||||
const validateOptions = (newOptions?: PartialOptions<Options>) => {
|
||||
const opts = newOptions || {};
|
||||
const validate = optionsValidationPlugin && optionsValidationPlugin._;
|
||||
return validate ? validate(opts, true) : opts;
|
||||
@@ -119,7 +167,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||
_getDefaultOptions(),
|
||||
validateOptions(options)
|
||||
);
|
||||
const [addEvent, removeEvent, triggerEvent] = createOSEventListenerHub(eventListeners);
|
||||
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub(eventListeners);
|
||||
|
||||
if (
|
||||
_nativeScrollbarIsOverlaid.x &&
|
||||
@@ -139,7 +187,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||
structureState._elements
|
||||
);
|
||||
|
||||
const update = (changedOptions: PartialOptions<OSOptions>, force?: boolean) => {
|
||||
const update = (changedOptions: PartialOptions<Options>, force?: boolean) => {
|
||||
updateStructure(changedOptions, force);
|
||||
updateScrollbars(changedOptions, force);
|
||||
};
|
||||
@@ -173,7 +221,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||
});
|
||||
|
||||
const instance: OverlayScrollbars = {
|
||||
options(newOptions?: PartialOptions<OSOptions>) {
|
||||
options(newOptions?: PartialOptions<Options>) {
|
||||
if (newOptions) {
|
||||
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { OSPlugin } from 'plugins';
|
||||
import { OSOptions, OverflowBehavior, VisibilityBehavior, AutoHideBehavior } from 'options';
|
||||
import { Options, OverflowBehavior, VisibilityBehavior, AutoHideBehavior } from 'options';
|
||||
import {
|
||||
validateOptions,
|
||||
OptionsTemplate,
|
||||
@@ -18,7 +18,7 @@ const scrollbarsVisibilityAllowedValues: OptionsTemplateValue<VisibilityBehavior
|
||||
const scrollbarsAutoHideAllowedValues: OptionsTemplateValue<AutoHideBehavior> =
|
||||
'never scroll leavemove';
|
||||
|
||||
const optionsTemplate: OptionsTemplate<OSOptions> = {
|
||||
const optionsTemplate: OptionsTemplate<Options> = {
|
||||
// resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b ||
|
||||
paddingAbsolute: booleanAllowedValues, // true || false
|
||||
updating: {
|
||||
@@ -53,7 +53,7 @@ const optionsTemplate: OptionsTemplate<OSOptions> = {
|
||||
};
|
||||
|
||||
export type OptionsValidationPluginInstance = {
|
||||
_: (options: PartialOptions<OSOptions>, doWriteErrors?: boolean) => PartialOptions<OSOptions>;
|
||||
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => PartialOptions<Options>;
|
||||
};
|
||||
|
||||
export const optionsValidationPluginName = '__osOptionsValidationPlugin';
|
||||
@@ -61,7 +61,7 @@ export const optionsValidationPluginName = '__osOptionsValidationPlugin';
|
||||
export const optionsValidationPlugin: OSPlugin<OptionsValidationPluginInstance> = [
|
||||
optionsValidationPluginName,
|
||||
{
|
||||
_: (options: PartialOptions<OSOptions>, doWriteErrors?: boolean) => {
|
||||
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => {
|
||||
const [validated, foreign] = validateOptions(optionsTemplate, options, doWriteErrors);
|
||||
return { ...foreign, ...validated };
|
||||
},
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from 'setups/scrollbarsSetup/scrollbarsSetup';
|
||||
export * from 'setups/scrollbarsSetup/scrollbarsSetup.initialization';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { appendChildren, createDiv, removeElements, isFunction } from 'support';
|
||||
import { appendChildren, createDiv, removeElements } from 'support';
|
||||
import {
|
||||
classNameScrollbar,
|
||||
classNameScrollbarHorizontal,
|
||||
@@ -6,9 +6,15 @@ import {
|
||||
classNameScrollbarTrack,
|
||||
classNameScrollbarHandle,
|
||||
} from 'classnames';
|
||||
import { getEnvironment, ScrollbarsInitializationStrategy } from 'environment';
|
||||
import { OSTarget, ScrollbarsInitialization } from 'typings';
|
||||
import { getEnvironment } from 'environment';
|
||||
import { dynamicInitializationElement as generalDynamicInitializationElement } from 'initialization';
|
||||
import type { InitializationTarget } from 'initialization';
|
||||
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
|
||||
import type {
|
||||
ScrollbarsInitialization,
|
||||
ScrollbarsInitializationStrategy,
|
||||
ScrollbarsDynamicInitializationElement,
|
||||
} from 'setups/scrollbarsSetup/scrollbarsSetup.initialization';
|
||||
|
||||
export interface ScrollbarStructure {
|
||||
_scrollbar: HTMLElement;
|
||||
@@ -39,7 +45,7 @@ const generateScrollbarDOM = (scrollbarClassName: string): ScrollbarStructure =>
|
||||
};
|
||||
|
||||
export const createScrollbarsSetupElements = (
|
||||
target: OSTarget,
|
||||
target: InitializationTarget,
|
||||
structureSetupElements: StructureSetupElementsObj
|
||||
): ScrollbarsSetupElements => {
|
||||
const { _getInitializationStrategy } = getEnvironment();
|
||||
@@ -48,15 +54,13 @@ export const createScrollbarsSetupElements = (
|
||||
const { _target, _host, _viewport, _targetIsElm } = structureSetupElements;
|
||||
const initializationScrollbarSlot =
|
||||
!_targetIsElm && (target as ScrollbarsInitialization).scrollbarsSlot;
|
||||
const initializationScrollbarSlotResult = isFunction(initializationScrollbarSlot)
|
||||
? initializationScrollbarSlot(_target, _host, _viewport)
|
||||
: initializationScrollbarSlot;
|
||||
const evaluatedScrollbarSlot =
|
||||
initializationScrollbarSlotResult ||
|
||||
(isFunction(environmentScrollbarSlot)
|
||||
? environmentScrollbarSlot(_target, _host, _viewport)
|
||||
: environmentScrollbarSlot) ||
|
||||
_host;
|
||||
generalDynamicInitializationElement<ScrollbarsDynamicInitializationElement>(
|
||||
[_target, _host, _viewport],
|
||||
() => _host,
|
||||
environmentScrollbarSlot,
|
||||
initializationScrollbarSlot
|
||||
);
|
||||
|
||||
const horizontalScrollbarStructure = generateScrollbarDOM(classNameScrollbarHorizontal);
|
||||
const verticalScrollbarStructure = generateScrollbarDOM(classNameScrollbarVertical);
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import type {
|
||||
InitializationTargetElement,
|
||||
InitializtationElementStrategy,
|
||||
DynamicInitializationElement,
|
||||
} from 'initialization';
|
||||
|
||||
export type ScrollbarsDynamicInitializationElement = DynamicInitializationElement<
|
||||
[target: InitializationTargetElement, host: HTMLElement, viewport: HTMLElement]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Null or Undefined means that the environment initialization strategy is used.
|
||||
*/
|
||||
export interface ScrollbarsInitialization {
|
||||
scrollbarsSlot?: ScrollbarsDynamicInitializationElement;
|
||||
}
|
||||
|
||||
export type ScrollbarsInitializationStrategy = {
|
||||
[K in keyof ScrollbarsInitialization as `_${K}`]: InitializtationElementStrategy<
|
||||
ScrollbarsInitialization[K]
|
||||
>;
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
|
||||
import type { ReadonlyOSOptions } from 'options';
|
||||
import type { Setup } from 'setups';
|
||||
import type { OSTarget } from 'typings';
|
||||
import type { InitializationTarget } from 'initialization';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface ScrollbarsSetupState {}
|
||||
@@ -16,7 +16,7 @@ export interface ScrollbarsSetupStaticState {
|
||||
}
|
||||
|
||||
export const createScrollbarsSetup = (
|
||||
target: OSTarget,
|
||||
target: InitializationTarget,
|
||||
options: ReadonlyOSOptions,
|
||||
structureSetupElements: StructureSetupElementsObj
|
||||
): Setup<ScrollbarsSetupState, ScrollbarsSetupStaticState> => {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { assignDeep, hasOwnProperty } from 'support';
|
||||
import type { OSOptions, ReadonlyOSOptions } from 'options';
|
||||
import type { Options, ReadonlyOSOptions } from 'options';
|
||||
import type { PartialOptions } from 'typings';
|
||||
|
||||
export type SetupElements<T extends Record<string, any>> = [elements: T, destroy: () => void];
|
||||
|
||||
export type SetupUpdate<T = void> = (
|
||||
changedOptions: PartialOptions<OSOptions>,
|
||||
force?: boolean
|
||||
) => T;
|
||||
export type SetupUpdate<T = void> = (changedOptions: PartialOptions<Options>, force?: boolean) => T;
|
||||
|
||||
export type SetupUpdateCheckOption = <T>(path: string) => [value: T, changed: boolean];
|
||||
|
||||
@@ -36,7 +33,7 @@ const getPropByPath = <T>(obj: any, path: string): T =>
|
||||
export const createOptionCheck =
|
||||
(
|
||||
options: ReadonlyOSOptions,
|
||||
changedOptions: PartialOptions<OSOptions>,
|
||||
changedOptions: PartialOptions<Options>,
|
||||
force?: boolean
|
||||
): SetupUpdateCheckOption =>
|
||||
(path: string) =>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from 'setups/structureSetup/structureSetup';
|
||||
export * from 'setups/structureSetup/structureSetup.initialization';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
isHTMLElement,
|
||||
appendChildren,
|
||||
is,
|
||||
createDiv,
|
||||
is,
|
||||
contents,
|
||||
insertAfter,
|
||||
addClass,
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
runEach,
|
||||
insertBefore,
|
||||
attr,
|
||||
isBoolean,
|
||||
isFunction,
|
||||
keys,
|
||||
removeAttr,
|
||||
attrClass,
|
||||
@@ -33,17 +31,22 @@ import {
|
||||
classNameContent,
|
||||
classNameViewportScrollbarStyling,
|
||||
} from 'classnames';
|
||||
import { getEnvironment } from 'environment';
|
||||
import {
|
||||
getEnvironment,
|
||||
StructureInitializationStrategyStaticElement,
|
||||
StructureInitializationStrategyDynamicElement,
|
||||
} from 'environment';
|
||||
import { OSTarget, OSTargetElement, StructureInitialization } from 'typings';
|
||||
staticInitializationElement as generalStaticInitializationElement,
|
||||
dynamicInitializationElement as generalDynamicInitializationElement,
|
||||
} from 'initialization';
|
||||
import type { InitializationTarget, InitializationTargetElement } from 'initialization';
|
||||
import type {
|
||||
StructureDynamicInitializationElement,
|
||||
StructureInitialization,
|
||||
StructureStaticInitializationElement,
|
||||
} from 'setups/structureSetup/structureSetup.initialization';
|
||||
|
||||
export type StructureSetupElements = [targetObj: StructureSetupElementsObj, destroy: () => void];
|
||||
|
||||
export interface StructureSetupElementsObj {
|
||||
_target: OSTargetElement;
|
||||
_target: InitializationTargetElement;
|
||||
_host: HTMLElement;
|
||||
_viewport: HTMLElement;
|
||||
_padding: HTMLElement | false;
|
||||
@@ -64,6 +67,8 @@ export interface StructureSetupElementsObj {
|
||||
|
||||
let contentArrangeCounter = 0;
|
||||
|
||||
const createNewDiv = createDiv.bind(0, '');
|
||||
|
||||
const unwrap = (elm: HTMLElement | false | null | undefined) => {
|
||||
appendChildren(parent(elm), contents(elm));
|
||||
removeElements(elm);
|
||||
@@ -87,38 +92,14 @@ const createUniqueViewportArrangeElement = (): HTMLStyleElement | false => {
|
||||
return result;
|
||||
};
|
||||
|
||||
const staticCreationFromStrategy = (
|
||||
target: OSTargetElement,
|
||||
initializationValue?: HTMLElement | undefined,
|
||||
strategy?: StructureInitializationStrategyStaticElement
|
||||
): HTMLElement => {
|
||||
const result =
|
||||
initializationValue ||
|
||||
(isFunction(strategy) ? strategy(target) : (strategy as null | undefined));
|
||||
return result || createDiv();
|
||||
};
|
||||
|
||||
const dynamicCreationFromStrategy = (
|
||||
target: OSTargetElement,
|
||||
initializationValue: HTMLElement | boolean | undefined,
|
||||
strategy: StructureInitializationStrategyDynamicElement
|
||||
): HTMLElement | false => {
|
||||
const takeInitializationValue = isBoolean(initializationValue) || initializationValue;
|
||||
const result = takeInitializationValue
|
||||
? (initializationValue as boolean | HTMLElement)
|
||||
: isFunction(strategy)
|
||||
? strategy(target)
|
||||
: strategy;
|
||||
|
||||
return result === true ? createDiv() : result;
|
||||
};
|
||||
|
||||
const addDataAttrHost = (elm: HTMLElement, value?: string | false | null | undefined) => {
|
||||
attr(elm, dataAttributeHost, value || '');
|
||||
return removeAttr.bind(0, elm, dataAttributeHost);
|
||||
};
|
||||
|
||||
export const createStructureSetupElements = (target: OSTarget): StructureSetupElements => {
|
||||
export const createStructureSetupElements = (
|
||||
target: InitializationTarget
|
||||
): StructureSetupElements => {
|
||||
const { _getInitializationStrategy, _nativeScrollbarStyling } = getEnvironment();
|
||||
const {
|
||||
_host: hostInitializationStrategy,
|
||||
@@ -129,7 +110,7 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
|
||||
const targetIsElm = isHTMLElement(target);
|
||||
const targetStructureInitialization = target as StructureInitialization;
|
||||
const targetElement = targetIsElm
|
||||
? (target as OSTargetElement)
|
||||
? (target as InitializationTargetElement)
|
||||
: targetStructureInitialization.target;
|
||||
const isTextarea = is(targetElement, 'textarea');
|
||||
const isBody = !isTextarea && is(targetElement, 'body');
|
||||
@@ -137,36 +118,44 @@ export const createStructureSetupElements = (target: OSTarget): StructureSetupEl
|
||||
const bodyElm = ownerDocument.body as HTMLBodyElement;
|
||||
const wnd = ownerDocument.defaultView as Window;
|
||||
const singleElmSupport = !!ResizeObserverConstructor && _nativeScrollbarStyling;
|
||||
const potentialViewportElement = staticCreationFromStrategy(
|
||||
targetElement,
|
||||
targetStructureInitialization.viewport,
|
||||
viewportInitializationStrategy
|
||||
const staticInitializationElement =
|
||||
generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [
|
||||
targetElement,
|
||||
]);
|
||||
const dynamicInitializationElement =
|
||||
generalDynamicInitializationElement<StructureDynamicInitializationElement>.bind(0, [
|
||||
targetElement,
|
||||
]);
|
||||
const potentialViewportElement = staticInitializationElement(
|
||||
createNewDiv,
|
||||
viewportInitializationStrategy,
|
||||
targetStructureInitialization.viewport
|
||||
);
|
||||
const potentiallySingleElm = potentialViewportElement === targetElement;
|
||||
const viewportIsTarget = singleElmSupport && potentiallySingleElm;
|
||||
const viewportElement =
|
||||
potentiallySingleElm && !viewportIsTarget
|
||||
? staticCreationFromStrategy(targetElement)
|
||||
? staticInitializationElement(createNewDiv)
|
||||
: potentialViewportElement;
|
||||
const evaluatedTargetObj: StructureSetupElementsObj = {
|
||||
_target: targetElement,
|
||||
_host: isTextarea
|
||||
? staticCreationFromStrategy(
|
||||
targetElement,
|
||||
targetStructureInitialization.host,
|
||||
hostInitializationStrategy
|
||||
? staticInitializationElement(
|
||||
createNewDiv,
|
||||
hostInitializationStrategy,
|
||||
targetStructureInitialization.host
|
||||
)
|
||||
: (targetElement as HTMLElement),
|
||||
_viewport: viewportElement,
|
||||
_padding: dynamicCreationFromStrategy(
|
||||
targetElement,
|
||||
targetStructureInitialization.padding,
|
||||
paddingInitializationStrategy
|
||||
_padding: dynamicInitializationElement(
|
||||
createNewDiv,
|
||||
paddingInitializationStrategy,
|
||||
targetStructureInitialization.padding
|
||||
),
|
||||
_content: dynamicCreationFromStrategy(
|
||||
targetElement,
|
||||
targetStructureInitialization.content,
|
||||
contentInitializationStrategy
|
||||
_content: dynamicInitializationElement(
|
||||
createNewDiv,
|
||||
contentInitializationStrategy,
|
||||
targetStructureInitialization.content
|
||||
),
|
||||
_viewportArrange: !viewportIsTarget && createUniqueViewportArrangeElement(),
|
||||
_windowElm: wnd,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type {
|
||||
InitializationTargetElement,
|
||||
StaticInitializationElement,
|
||||
DynamicInitializationElement,
|
||||
InitializtationElementStrategy,
|
||||
} from 'initialization';
|
||||
|
||||
export type StructureStaticInitializationElement = StaticInitializationElement<
|
||||
[target: InitializationTargetElement]
|
||||
>;
|
||||
|
||||
export type StructureDynamicInitializationElement = DynamicInitializationElement<
|
||||
[target: InitializationTargetElement]
|
||||
>;
|
||||
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* Target is always required, if element is not provided or undefined it will be generated.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Null or Undefined means that the environment initialization strategy is used.
|
||||
*/
|
||||
export interface StructureInitialization {
|
||||
target: InitializationTargetElement;
|
||||
host?: StructureStaticInitializationElement; // only relevant for textarea
|
||||
viewport?: StructureStaticInitializationElement;
|
||||
padding?: StructureDynamicInitializationElement;
|
||||
content?: StructureDynamicInitializationElement;
|
||||
}
|
||||
|
||||
export type StructureInitializationStrategy = {
|
||||
[K in keyof Omit<StructureInitialization, 'target'> as `_${K}`]: InitializtationElementStrategy<
|
||||
StructureInitialization[K]
|
||||
>;
|
||||
};
|
||||
@@ -6,9 +6,10 @@ import { createStructureSetupObservers } from 'setups/structureSetup/structureSe
|
||||
import type { StructureSetupUpdateHints } from 'setups/structureSetup/structureSetup.update';
|
||||
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
|
||||
import type { TRBL, XY } from 'support';
|
||||
import type { OSOptions, ReadonlyOSOptions } from 'options';
|
||||
import type { Options, ReadonlyOSOptions } from 'options';
|
||||
import type { Setup } from 'setups';
|
||||
import type { OSTarget, PartialOptions, StyleObject, OverflowStyle } from 'typings';
|
||||
import type { InitializationTarget } from 'initialization';
|
||||
import type { PartialOptions, StyleObject, OverflowStyle } from 'typings';
|
||||
|
||||
export interface StructureSetupState {
|
||||
_padding: TRBL;
|
||||
@@ -28,7 +29,7 @@ export interface StructureSetupStaticState {
|
||||
|
||||
export type OnUpdatedListener = (
|
||||
updateHints: StructureSetupUpdateHints,
|
||||
changedOptions: PartialOptions<OSOptions>,
|
||||
changedOptions: PartialOptions<Options>,
|
||||
force: boolean
|
||||
) => void;
|
||||
|
||||
@@ -66,7 +67,7 @@ const initialStructureSetupUpdateState: StructureSetupState = {
|
||||
};
|
||||
|
||||
export const createStructureSetup = (
|
||||
target: OSTarget,
|
||||
target: InitializationTarget,
|
||||
options: ReadonlyOSOptions
|
||||
): Setup<StructureSetupState, StructureSetupStaticState> => {
|
||||
const checkOptionsFallback = createOptionCheck(options, {});
|
||||
@@ -75,7 +76,7 @@ export const createStructureSetup = (
|
||||
const [getState] = state;
|
||||
const runOnUpdatedListeners = (
|
||||
updateHints: StructureSetupUpdateHints,
|
||||
changedOptions?: PartialOptions<OSOptions>,
|
||||
changedOptions?: PartialOptions<Options>,
|
||||
force?: boolean
|
||||
) => {
|
||||
runEach(onUpdatedListeners, [updateHints, changedOptions || {}, !!force]);
|
||||
|
||||
@@ -12,11 +12,11 @@ type NodeCollection = ArrayLike<Node> | Node | false | null | undefined;
|
||||
*/
|
||||
const before = (
|
||||
parentElm: Node | false | null | undefined,
|
||||
preferredAnchor: Node | null | undefined,
|
||||
preferredAnchor: Node | false | null | undefined,
|
||||
insertedElms: NodeCollection
|
||||
): void => {
|
||||
if (insertedElms) {
|
||||
let anchor: Node | null | undefined = preferredAnchor;
|
||||
let anchor: Node | false | null | undefined = preferredAnchor;
|
||||
let fragment: DocumentFragment | Node | null | undefined;
|
||||
|
||||
// parent must be defined
|
||||
@@ -54,7 +54,10 @@ const before = (
|
||||
* @param node The Node to which the children shall be appended.
|
||||
* @param children The Nodes which shall be appended.
|
||||
*/
|
||||
export const appendChildren = (node: Node | null | undefined, children: NodeCollection): void => {
|
||||
export const appendChildren = (
|
||||
node: Node | false | null | undefined,
|
||||
children: NodeCollection
|
||||
): void => {
|
||||
before(node, null, children);
|
||||
};
|
||||
|
||||
@@ -63,7 +66,10 @@ export const appendChildren = (node: Node | null | undefined, children: NodeColl
|
||||
* @param node The Node to which the children shall be prepended.
|
||||
* @param children The Nodes which shall be prepended.
|
||||
*/
|
||||
export const prependChildren = (node: Node | null | undefined, children: NodeCollection): void => {
|
||||
export const prependChildren = (
|
||||
node: Node | false | null | undefined,
|
||||
children: NodeCollection
|
||||
): void => {
|
||||
before(node, node && node.firstChild, children);
|
||||
};
|
||||
|
||||
@@ -73,7 +79,7 @@ export const prependChildren = (node: Node | null | undefined, children: NodeCol
|
||||
* @param insertedNodes The Nodes which shall be inserted.
|
||||
*/
|
||||
export const insertBefore = (
|
||||
node: Node | null | undefined,
|
||||
node: Node | false | null | undefined,
|
||||
insertedNodes: NodeCollection
|
||||
): void => {
|
||||
before(parent(node), node, insertedNodes);
|
||||
@@ -84,7 +90,10 @@ export const insertBefore = (
|
||||
* @param node The Node after which the given Nodes shall be inserted.
|
||||
* @param insertedNodes The Nodes which shall be inserted.
|
||||
*/
|
||||
export const insertAfter = (node: Node | null | undefined, insertedNodes: NodeCollection): void => {
|
||||
export const insertAfter = (
|
||||
node: Node | false | null | undefined,
|
||||
insertedNodes: NodeCollection
|
||||
): void => {
|
||||
before(parent(node), node && node.nextSibling, insertedNodes);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,96 +3,102 @@ import { keys } from 'support/utils/object';
|
||||
import { each, from } from 'support/utils/array';
|
||||
|
||||
export type EventListener<
|
||||
NameArgsMap extends Record<string, any>,
|
||||
Name extends Extract<keyof NameArgsMap, string> = Extract<keyof NameArgsMap, string>
|
||||
> = (...args: NameArgsMap[Name] extends undefined ? [] : [args: NameArgsMap[Name]]) => void;
|
||||
EventMap extends Record<string, any>,
|
||||
Name extends keyof EventMap = keyof EventMap
|
||||
> = (...args: EventMap[Name] extends undefined ? [] : [args: EventMap[Name]]) => void;
|
||||
|
||||
export type EventListenerGroup<
|
||||
NameArgsMap extends Record<string, any>,
|
||||
Name extends Extract<keyof NameArgsMap, string> = Extract<keyof NameArgsMap, string>
|
||||
> = EventListener<NameArgsMap, Name> | EventListener<NameArgsMap, Name>[];
|
||||
|
||||
export type AddEventListener<NameArgsMap extends Record<string, any>> = <
|
||||
Name extends Extract<keyof NameArgsMap, string>
|
||||
>(
|
||||
name: Name,
|
||||
listener: EventListenerGroup<NameArgsMap, Name>
|
||||
) => () => void;
|
||||
|
||||
export type RemoveEventListener<NameArgsMap extends Record<string, any>> = <
|
||||
Name extends Extract<keyof NameArgsMap, string>
|
||||
>(
|
||||
name?: Name,
|
||||
listener?: EventListenerGroup<NameArgsMap, Name>
|
||||
) => void;
|
||||
|
||||
export type TriggerEventListener<NameArgsMap extends Record<string, any>> = <
|
||||
Name extends Extract<keyof NameArgsMap, string>
|
||||
>(
|
||||
name: Name,
|
||||
...args: NameArgsMap[Name] extends undefined ? [] : [args: NameArgsMap[Name]]
|
||||
) => void;
|
||||
|
||||
export type InitialEventListeners<NameArgsMap extends Record<string, any>> = {
|
||||
[K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>;
|
||||
export type InitialEventListeners<EventMap extends Record<string, any>> = {
|
||||
[K in keyof EventMap]?: EventListener<EventMap> | EventListener<EventMap>[];
|
||||
};
|
||||
|
||||
const manageListener = <NameArgsMap extends Record<string, any>>(
|
||||
callback: (listener?: EventListener<NameArgsMap>) => void,
|
||||
listener?: EventListener<NameArgsMap> | EventListener<NameArgsMap>[]
|
||||
const manageListener = <EventMap extends Record<string, any>>(
|
||||
callback: (listener?: EventListener<EventMap>) => void,
|
||||
listener?: EventListener<EventMap> | EventListener<EventMap>[]
|
||||
) => {
|
||||
each(isArray(listener) ? listener : [listener], callback);
|
||||
};
|
||||
|
||||
export const createEventListenerHub = <NameArgsMap extends Record<string, any>>(
|
||||
initialEventListeners?: InitialEventListeners<NameArgsMap>
|
||||
): [
|
||||
AddEventListener<NameArgsMap>,
|
||||
RemoveEventListener<NameArgsMap>,
|
||||
TriggerEventListener<NameArgsMap>
|
||||
] => {
|
||||
const events = new Map<Extract<keyof NameArgsMap, string>, Set<EventListener<NameArgsMap>>>();
|
||||
const removeEvent: RemoveEventListener<NameArgsMap> = (name?, listener?) => {
|
||||
export const createEventListenerHub = <EventMap extends Record<string, any>>(
|
||||
initialEventListeners?: InitialEventListeners<EventMap>
|
||||
) => {
|
||||
type EventListener<Name extends keyof EventMap = keyof EventMap> = (
|
||||
...args: EventMap[Name] extends undefined ? [] : [args: EventMap[Name]]
|
||||
) => void;
|
||||
|
||||
const events = new Map<keyof EventMap, Set<EventListener>>();
|
||||
|
||||
function removeEvent<Name extends keyof EventMap>(
|
||||
name?: Name,
|
||||
listener?: EventListener<Name>
|
||||
): void;
|
||||
function removeEvent<Name extends keyof EventMap>(
|
||||
name?: Name,
|
||||
listener?: EventListener<Name>[]
|
||||
): void;
|
||||
function removeEvent<Name extends keyof EventMap>(
|
||||
name?: Name,
|
||||
listener?: EventListener<Name> | EventListener<Name>[]
|
||||
): void {
|
||||
if (name) {
|
||||
const eventSet = events.get(name);
|
||||
manageListener((currListener) => {
|
||||
if (eventSet) {
|
||||
eventSet[currListener ? 'delete' : 'clear'](currListener!);
|
||||
}
|
||||
}, listener as EventListenerGroup<NameArgsMap> | undefined);
|
||||
}, listener as any);
|
||||
} else {
|
||||
events.forEach((eventSet) => {
|
||||
eventSet.clear();
|
||||
});
|
||||
events.clear();
|
||||
}
|
||||
};
|
||||
const addEvent: AddEventListener<NameArgsMap> = (name, listener) => {
|
||||
}
|
||||
|
||||
function addEvent<Name extends keyof EventMap>(
|
||||
name: Name,
|
||||
listener: EventListener<Name>
|
||||
): () => void;
|
||||
function addEvent<Name extends keyof EventMap>(
|
||||
name: Name,
|
||||
listener: EventListener<Name>[]
|
||||
): () => void;
|
||||
function addEvent<Name extends keyof EventMap>(
|
||||
name: Name,
|
||||
listener: EventListener<Name> | EventListener<Name>[]
|
||||
): () => void {
|
||||
const eventSet = events.get(name) || new Set();
|
||||
events.set(name, eventSet);
|
||||
|
||||
manageListener((currListener) => {
|
||||
currListener && eventSet.add(currListener);
|
||||
}, listener as EventListenerGroup<NameArgsMap>);
|
||||
}, listener as any);
|
||||
|
||||
return removeEvent.bind(0, name as any, listener as EventListenerGroup<NameArgsMap>);
|
||||
};
|
||||
const triggerEvent: TriggerEventListener<NameArgsMap> = (name, args?) => {
|
||||
return removeEvent.bind(0, name as any, listener as any);
|
||||
}
|
||||
|
||||
function triggerEvent<Name extends keyof EventMap>(
|
||||
name: Name,
|
||||
...args: EventMap[Name] extends undefined ? [] : [args: EventMap[Name]]
|
||||
): void {
|
||||
const eventSet = events.get(name);
|
||||
|
||||
each(from(eventSet), (event) => {
|
||||
if (args) {
|
||||
(event as (args: NameArgsMap[Extract<keyof NameArgsMap, string>]) => void)(args as any);
|
||||
(event as (args: EventMap[keyof EventMap]) => void)(args as any);
|
||||
} else {
|
||||
(event as () => void)();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const initialListenerKeys = keys(initialEventListeners) as Extract<keyof NameArgsMap, string>[];
|
||||
const initialListenerKeys = keys(initialEventListeners) as Extract<keyof EventMap, string>[];
|
||||
each(initialListenerKeys, (key) => {
|
||||
addEvent(key, initialEventListeners![key] as any);
|
||||
});
|
||||
|
||||
return [addEvent, removeEvent, triggerEvent];
|
||||
return [addEvent, removeEvent, triggerEvent] as [
|
||||
typeof addEvent,
|
||||
typeof removeEvent,
|
||||
typeof triggerEvent
|
||||
];
|
||||
};
|
||||
|
||||
@@ -14,59 +14,6 @@ export type StyleObject<CustomCssProps = ''> = {
|
||||
| number;
|
||||
};
|
||||
|
||||
export type InternalVersionOf<T> = {
|
||||
[K in keyof T as `_${Uncapitalize<string & K>}`]: T[K];
|
||||
};
|
||||
|
||||
export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
||||
|
||||
/**
|
||||
* Static elements MUST be present.
|
||||
*/
|
||||
type StructureInitializationStaticElement = HTMLElement;
|
||||
|
||||
/**
|
||||
* Dynamic element CAN be present.
|
||||
* If its a element the element will be handled as the repsective element.
|
||||
* True means that the respective dynamic element is forced to be generated.
|
||||
* False means that the respective dynamic element is forced NOT to be generated.
|
||||
*/
|
||||
type StructureInitializationDynamicElement = HTMLElement | boolean;
|
||||
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* Target is always required, if element is not provided or undefined it will be generated.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Undefined means that the environment initialization strategy for the respective element is used.
|
||||
*/
|
||||
export interface StructureInitialization {
|
||||
target: OSTargetElement;
|
||||
host?: StructureInitializationStaticElement; // only relevant for textarea
|
||||
viewport?: StructureInitializationStaticElement;
|
||||
padding?: StructureInitializationDynamicElement;
|
||||
content?: StructureInitializationDynamicElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* scrollbarsSlot is the element to which the scrollbars are applied to. If null or undefined the plugin decides by itself whats the scrollbars slot.
|
||||
*/
|
||||
export interface ScrollbarsInitialization {
|
||||
scrollbarsSlot?:
|
||||
| null
|
||||
| HTMLElement
|
||||
| ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => null | HTMLElement);
|
||||
}
|
||||
|
||||
export interface OSInitializationObject extends StructureInitialization, ScrollbarsInitialization {}
|
||||
|
||||
export type OSTarget = OSTargetElement | OSInitializationObject;
|
||||
|
||||
export type OverflowStyle = 'scroll' | 'hidden' | 'visible';
|
||||
|
||||
/*
|
||||
|
||||
+20
-23
@@ -1,13 +1,14 @@
|
||||
import {
|
||||
Environment,
|
||||
StructureInitializationStrategyStaticElement,
|
||||
StructureInitializationStrategyDynamicElement,
|
||||
} from 'environment';
|
||||
import { OSTarget, StructureInitialization } from 'typings';
|
||||
import { InternalEnvironment } from 'environment';
|
||||
import {
|
||||
createStructureSetupElements,
|
||||
StructureSetupElementsObj,
|
||||
} from 'setups/structureSetup/structureSetup.elements';
|
||||
import type { InitializationTarget, InitializtationElementStrategy } from 'initialization';
|
||||
import type {
|
||||
StructureInitialization,
|
||||
StructureStaticInitializationElement,
|
||||
StructureDynamicInitializationElement,
|
||||
} from 'setups/structureSetup/structureSetup.initialization';
|
||||
import { isHTMLElement } from 'support';
|
||||
|
||||
const mockGetEnvironment = jest.fn();
|
||||
@@ -16,7 +17,7 @@ jest.mock('environment', () => ({
|
||||
}));
|
||||
|
||||
interface StructureSetupElementsProxy {
|
||||
input: OSTarget;
|
||||
input: InitializationTarget;
|
||||
elements: StructureSetupElementsObj;
|
||||
destroy: () => void;
|
||||
}
|
||||
@@ -95,7 +96,7 @@ const assertCorrectDOMStructure = (textarea?: boolean) => {
|
||||
};
|
||||
|
||||
const createStructureSetupProxy = (
|
||||
target: OSTarget | StructureInitialization
|
||||
target: InitializationTarget | StructureInitialization
|
||||
): StructureSetupElementsProxy => {
|
||||
const [elements, destroy] = createStructureSetupElements(target);
|
||||
return {
|
||||
@@ -108,7 +109,7 @@ const createStructureSetupProxy = (
|
||||
const assertCorrectSetupElements = (
|
||||
textarea: boolean,
|
||||
setupElementsProxy: StructureSetupElementsProxy,
|
||||
environment: Environment
|
||||
environment: InternalEnvironment
|
||||
): [StructureSetupElementsObj, () => void] => {
|
||||
const { input, elements, destroy } = setupElementsProxy;
|
||||
const { _target, _host, _padding, _viewport, _content } = elements;
|
||||
@@ -165,11 +166,11 @@ const assertCorrectSetupElements = (
|
||||
const styleElm = document.querySelector('style');
|
||||
const checkStrategyDependendElements = (
|
||||
elm: Element | null,
|
||||
input: HTMLElement | boolean | undefined,
|
||||
input: StructureStaticInitializationElement | StructureDynamicInitializationElement,
|
||||
isStaticStrategy: boolean,
|
||||
strategy:
|
||||
| StructureInitializationStrategyStaticElement
|
||||
| StructureInitializationStrategyDynamicElement
|
||||
| InitializtationElementStrategy<StructureStaticInitializationElement>
|
||||
| InitializtationElementStrategy<StructureDynamicInitializationElement>
|
||||
) => {
|
||||
if (input) {
|
||||
expect(elm).toBeTruthy();
|
||||
@@ -179,7 +180,8 @@ const assertCorrectSetupElements = (
|
||||
}
|
||||
if (input === undefined) {
|
||||
if (isStaticStrategy) {
|
||||
strategy = strategy as StructureInitializationStrategyStaticElement;
|
||||
strategy =
|
||||
strategy as InitializtationElementStrategy<StructureStaticInitializationElement>;
|
||||
if (typeof strategy === 'function') {
|
||||
const result = strategy(target);
|
||||
if (result) {
|
||||
@@ -191,14 +193,13 @@ const assertCorrectSetupElements = (
|
||||
expect(elm).toBeTruthy();
|
||||
}
|
||||
} else {
|
||||
strategy = strategy as StructureInitializationStrategyDynamicElement;
|
||||
strategy =
|
||||
strategy as InitializtationElementStrategy<StructureDynamicInitializationElement>;
|
||||
expect(strategy).not.toBe(null);
|
||||
expect(strategy).not.toBe(undefined);
|
||||
if (typeof strategy === 'function') {
|
||||
const result = strategy(target);
|
||||
const resultIsBoolean = typeof result === 'boolean';
|
||||
expect(result).not.toBe(null);
|
||||
expect(result).not.toBe(undefined);
|
||||
if (resultIsBoolean) {
|
||||
if (result) {
|
||||
expect(elm).toBeTruthy();
|
||||
@@ -265,7 +266,7 @@ const assertCorrectDestroy = (snapshot: string, destroy: () => void) => {
|
||||
expect(snapshot).toBe(getSnapshot());
|
||||
};
|
||||
|
||||
const env: Environment = jest.requireActual('environment').getEnvironment();
|
||||
const env: InternalEnvironment = jest.requireActual('environment').getEnvironment();
|
||||
const envDefault = {
|
||||
name: 'default',
|
||||
env,
|
||||
@@ -317,12 +318,8 @@ const envInitStrategyAssigned = {
|
||||
_getInitializationStrategy: () => ({
|
||||
_host: () => document.querySelector('#host1') as HTMLElement,
|
||||
_viewport: (target: HTMLElement) => target.querySelector('#viewport') as HTMLElement,
|
||||
_content: (target: HTMLElement) =>
|
||||
target.querySelector<HTMLElement>('#content') ||
|
||||
env._defaultInitializationStrategy._content,
|
||||
_padding: (target: HTMLElement) =>
|
||||
target.querySelector<HTMLElement>('#padding') ||
|
||||
env._defaultInitializationStrategy._padding,
|
||||
_content: (target: HTMLElement) => target.querySelector<HTMLElement>('#content'),
|
||||
_padding: (target: HTMLElement) => target.querySelector<HTMLElement>('#padding'),
|
||||
_scrollbarsSlot: null,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// @ts-ignore
|
||||
import { playwrightRollup } from '@/playwright/rollup';
|
||||
import { test } from '@playwright/test';
|
||||
import { Environment } from 'environment';
|
||||
import { InternalEnvironment } from 'environment';
|
||||
|
||||
playwrightRollup();
|
||||
|
||||
test.describe('Environment', () => {
|
||||
test('page should be titled "Environment"', async ({ page }) => {
|
||||
// @ts-ignore
|
||||
const a: Environment = await page.evaluate(() => window.environment.envInstance);
|
||||
const a: InternalEnvironment = await page.evaluate(() => window.environment.envInstance);
|
||||
console.log(a);
|
||||
await expect(page.title()).resolves.toMatch('environment');
|
||||
});
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ import { resize } from '@/testing-browser/Resize';
|
||||
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
|
||||
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
|
||||
import { timeout } from '@/testing-browser/timeout';
|
||||
import { OSOptions } from 'options';
|
||||
import { Options } from 'options';
|
||||
import { PartialOptions } from 'typings';
|
||||
|
||||
interface Metrics {
|
||||
@@ -572,7 +572,7 @@ const iterateMinMax = async (afterEach?: () => any) => {
|
||||
await iterate(containerMinMaxSelect, afterEach);
|
||||
};
|
||||
|
||||
const overflowTest = async (osOptions?: PartialOptions<OSOptions>) => {
|
||||
const overflowTest = async (osOptions?: PartialOptions<Options>) => {
|
||||
const additiveOverflow = () => {
|
||||
if (isFractionalPixelRatio()) {
|
||||
return 1;
|
||||
|
||||
+119
-127
@@ -1,46 +1,6 @@
|
||||
type PartialOptions<T> = {
|
||||
[P in keyof T]?: T[P] extends Record<string, unknown> ? PartialOptions<T[P]> : T[P];
|
||||
};
|
||||
type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
||||
/**
|
||||
* Static elements MUST be present.
|
||||
*/
|
||||
type StructureInitializationStaticElement = HTMLElement;
|
||||
/**
|
||||
* Dynamic element CAN be present.
|
||||
* If its a element the element will be handled as the repsective element.
|
||||
* True means that the respective dynamic element is forced to be generated.
|
||||
* False means that the respective dynamic element is forced NOT to be generated.
|
||||
*/
|
||||
type StructureInitializationDynamicElement = HTMLElement | boolean;
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* Target is always required, if element is not provided or undefined it will be generated.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Undefined means that the environment initialization strategy for the respective element is used.
|
||||
*/
|
||||
interface StructureInitialization {
|
||||
target: OSTargetElement;
|
||||
host?: StructureInitializationStaticElement; // only relevant for textarea
|
||||
viewport?: StructureInitializationStaticElement;
|
||||
padding?: StructureInitializationDynamicElement;
|
||||
content?: StructureInitializationDynamicElement;
|
||||
}
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* scrollbarsSlot is the element to which the scrollbars are applied to. If null or undefined the plugin decides by itself whats the scrollbars slot.
|
||||
*/
|
||||
interface ScrollbarsInitialization {
|
||||
scrollbarsSlot?: null | HTMLElement | ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => null | HTMLElement);
|
||||
}
|
||||
interface OSInitializationObject extends StructureInitialization, ScrollbarsInitialization {
|
||||
}
|
||||
type OSTarget = OSTargetElement | OSInitializationObject;
|
||||
type OverflowStyle = "scroll" | "hidden" | "visible";
|
||||
interface TRBL {
|
||||
t: number;
|
||||
@@ -52,20 +12,17 @@ interface XY<T> {
|
||||
x: T;
|
||||
y: T;
|
||||
}
|
||||
type EventListener<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = (...args: NameArgsMap[Name] extends undefined ? [
|
||||
type EventListener<EventMap extends Record<string, any>, Name extends keyof EventMap> = (...args: EventMap[Name] extends undefined ? [
|
||||
] : [
|
||||
args: NameArgsMap[Name]
|
||||
args: EventMap[Name]
|
||||
]) => void;
|
||||
type EventListenerGroup<NameArgsMap extends Record<string, any>, Name extends Extract<keyof NameArgsMap, string>> = EventListener<NameArgsMap, Name> | EventListener<NameArgsMap, Name>[];
|
||||
type AddEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name: Name, listener: EventListenerGroup<NameArgsMap, Name>) => () => void;
|
||||
type RemoveEventListener<NameArgsMap extends Record<string, any>> = <Name extends Extract<keyof NameArgsMap, string>>(name?: Name, listener?: EventListenerGroup<NameArgsMap, Name>) => void;
|
||||
type InitialEventListeners<NameArgsMap extends Record<string, any>> = {
|
||||
[K in Extract<keyof NameArgsMap, string>]?: EventListenerGroup<NameArgsMap, K>;
|
||||
type InitialEventListeners<EventMap extends Record<string, any>> = {
|
||||
[K in keyof EventMap]?: EventListener<EventMap> | EventListener<EventMap>[];
|
||||
};
|
||||
type OverflowBehavior = "hidden" | "scroll" | "visible" | "visible-hidden" | "visible-scroll";
|
||||
type VisibilityBehavior = "visible" | "hidden" | "auto";
|
||||
type AutoHideBehavior = "never" | "scroll" | "leave" | "move";
|
||||
interface OSOptions {
|
||||
interface Options {
|
||||
paddingAbsolute: boolean;
|
||||
updating: {
|
||||
elementEvents: Array<[
|
||||
@@ -96,43 +53,78 @@ interface OSOptions {
|
||||
initialize: boolean;
|
||||
};
|
||||
}
|
||||
type StructureInitializationStrategyElementFn<T> = ((target: OSTargetElement) => HTMLElement | T) | T;
|
||||
type ScrollbarsInitializationStrategyElementFn<T> = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T;
|
||||
/**
|
||||
* A Static element is an element which MUST be generated.
|
||||
* If null or undefined (or the returned result is null or undefined), the initialization function is generatig the element, otherwise
|
||||
* the element returned by the function acts as the generated element.
|
||||
*/
|
||||
type StructureInitializationStrategyStaticElement = StructureInitializationStrategyElementFn<null | undefined>;
|
||||
/**
|
||||
* A Dynamic element is an element which CAN be generated.
|
||||
* If boolean (or the returned result is boolean), the generation of the element is forced (or not).
|
||||
* If the function returns and element, the element returned by the function acts as the generated element.
|
||||
*/
|
||||
type StructureInitializationStrategyDynamicElement = StructureInitializationStrategyElementFn<boolean>;
|
||||
interface StructureInitializationStrategy {
|
||||
_host: StructureInitializationStrategyStaticElement;
|
||||
_viewport: StructureInitializationStrategyStaticElement;
|
||||
_padding: StructureInitializationStrategyDynamicElement;
|
||||
_content: StructureInitializationStrategyDynamicElement;
|
||||
}
|
||||
interface ScrollbarsInitializationStrategy {
|
||||
/**
|
||||
* The scrollbars slot. If null or undefined (or the returned result is null or undefined), the initialization function is deciding the element, otherwise
|
||||
* the element returned by the function acts as the scrollbars slot.
|
||||
*/
|
||||
_scrollbarsSlot: ScrollbarsInitializationStrategyElementFn<null | undefined>;
|
||||
}
|
||||
interface InitializationStrategy extends StructureInitializationStrategy, ScrollbarsInitializationStrategy {
|
||||
}
|
||||
type DefaultInitializationStrategy = {
|
||||
[K in keyof InitializationStrategy]: Extract<InitializationStrategy[K], boolean | null | undefined>;
|
||||
};
|
||||
type OSPluginInstance = Record<string, unknown> | ((staticObj: OverlayScrollbarsStatic, instanceObj: OverlayScrollbars) => void);
|
||||
type OSPlugin<T extends OSPluginInstance> = [
|
||||
string,
|
||||
T
|
||||
];
|
||||
type ScrollbarsDynamicInitializationElement = DynamicInitializationElement<[
|
||||
target: InitializationTargetElement,
|
||||
host: HTMLElement,
|
||||
viewport: HTMLElement
|
||||
]>;
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Null or Undefined means that the environment initialization strategy is used.
|
||||
*/
|
||||
interface ScrollbarsInitialization {
|
||||
scrollbarsSlot?: ScrollbarsDynamicInitializationElement;
|
||||
}
|
||||
type ScrollbarsInitializationStrategy = {
|
||||
[K in keyof ScrollbarsInitialization as `_${K}`]: InitializtationElementStrategy<ScrollbarsInitialization[K]>;
|
||||
};
|
||||
type StructureStaticInitializationElement = StaticInitializationElement<[
|
||||
target: InitializationTargetElement
|
||||
]>;
|
||||
type StructureDynamicInitializationElement = DynamicInitializationElement<[
|
||||
target: InitializationTargetElement
|
||||
]>;
|
||||
/**
|
||||
* Object for special initialization.
|
||||
*
|
||||
* Target is always required, if element is not provided or undefined it will be generated.
|
||||
*
|
||||
* If element is provided, the provided element takes all its responsibilities.
|
||||
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||
*
|
||||
* Null or Undefined means that the environment initialization strategy is used.
|
||||
*/
|
||||
interface StructureInitialization {
|
||||
target: InitializationTargetElement;
|
||||
host?: StructureStaticInitializationElement; // only relevant for textarea
|
||||
viewport?: StructureStaticInitializationElement;
|
||||
padding?: StructureDynamicInitializationElement;
|
||||
content?: StructureDynamicInitializationElement;
|
||||
}
|
||||
type StructureInitializationStrategy = {
|
||||
[K in keyof Omit<StructureInitialization, "target"> as `_${K}`]: InitializtationElementStrategy<StructureInitialization[K]>;
|
||||
};
|
||||
type StaticInitialization = HTMLElement | null | undefined;
|
||||
type DynamicInitialization = HTMLElement | boolean | null | undefined;
|
||||
type InitializationTargetElement = HTMLElement | HTMLTextAreaElement;
|
||||
type InitializationTargetObject = StructureInitialization & ScrollbarsInitialization;
|
||||
type InitializationTarget = InitializationTargetElement | InitializationTargetObject;
|
||||
type InitializationStrategy = StructureInitializationStrategy & ScrollbarsInitializationStrategy;
|
||||
/**
|
||||
* Static elements MUST be present.
|
||||
* Null or undefined behave like if this element wasn't specified during initialization.
|
||||
*/
|
||||
type StaticInitializationElement<Args extends any[]> = ((...args: Args) => StaticInitialization) | StaticInitialization;
|
||||
/**
|
||||
* Dynamic element CAN be present.
|
||||
* If its a element the element will be handled as the repsective element.
|
||||
* True means that the respective dynamic element is forced to be generated.
|
||||
* False means that the respective dynamic element is forced NOT to be generated.
|
||||
* Null or undefined behave like if this element wasn't specified during initialization.
|
||||
*/
|
||||
type DynamicInitializationElement<Args extends any[]> = ((...args: Args) => DynamicInitialization) | DynamicInitialization;
|
||||
type InitializtationElementStrategy<InitElm> = Exclude<InitElm, HTMLElement>;
|
||||
type GeneralInitialEventListeners = InitialEventListeners;
|
||||
type GeneralEventListener = EventListener;
|
||||
/*
|
||||
onScrollStart : null,
|
||||
onScroll : null,
|
||||
@@ -143,6 +135,42 @@ onDirectionChanged : null, // gone
|
||||
onContentSizeChanged : null, // gone
|
||||
onHostSizeChanged : null, // gone
|
||||
*/
|
||||
interface OverlayScrollbarsStatic {
|
||||
(target: InitializationTarget | InitializationTargetObject, options?: PartialOptions<Options>, eventListeners?: GeneralInitialEventListeners<EventListenerMap>): OverlayScrollbars;
|
||||
plugin(osPlugin: OSPlugin | OSPlugin[]): void;
|
||||
env(): Environment;
|
||||
}
|
||||
interface Environment {
|
||||
scrollbarSize: XY<number>;
|
||||
scrollbarIsOverlaid: XY<boolean>;
|
||||
scrollbarStyling: boolean;
|
||||
rtlScrollBehavior: {
|
||||
n: boolean;
|
||||
i: boolean;
|
||||
};
|
||||
flexboxGlue: boolean;
|
||||
cssCustomProperties: boolean;
|
||||
defaultInitializationStrategy: InitializationStrategy;
|
||||
defaultDefaultOptions: Options;
|
||||
getInitializationStrategy(): InitializationStrategy;
|
||||
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
|
||||
getDefaultOptions(): Options;
|
||||
setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
|
||||
}
|
||||
interface State {
|
||||
padding: TRBL;
|
||||
paddingAbsolute: boolean;
|
||||
overflowAmount: XY<number>;
|
||||
overflowStyle: XY<OverflowStyle>;
|
||||
hasOverflow: XY<boolean>;
|
||||
}
|
||||
interface Elements {
|
||||
target: HTMLElement;
|
||||
host: HTMLElement;
|
||||
padding: HTMLElement;
|
||||
viewport: HTMLElement;
|
||||
content: HTMLElement;
|
||||
}
|
||||
interface OnUpdatedEventListenerArgs {
|
||||
updateHints: {
|
||||
sizeChanged: boolean;
|
||||
@@ -153,63 +181,27 @@ interface OnUpdatedEventListenerArgs {
|
||||
hostMutation: boolean;
|
||||
contentMutation: boolean;
|
||||
};
|
||||
changedOptions: PartialOptions<OSOptions>;
|
||||
changedOptions: PartialOptions<Options>;
|
||||
force: boolean;
|
||||
}
|
||||
interface OSEventListenersNameArgsMap {
|
||||
interface EventListenerMap {
|
||||
initialized: undefined;
|
||||
initializationWithdrawn: undefined;
|
||||
updated: OnUpdatedEventListenerArgs;
|
||||
destroyed: undefined;
|
||||
}
|
||||
type AddOSEventListener = AddEventListener<OSEventListenersNameArgsMap>;
|
||||
type RemoveOSEventListener = RemoveEventListener<OSEventListenersNameArgsMap>;
|
||||
type InitialOSEventListeners = InitialEventListeners<OSEventListenersNameArgsMap>;
|
||||
interface OverlayScrollbarsStatic {
|
||||
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, eventListeners?: InitialOSEventListeners): OverlayScrollbars;
|
||||
plugin(osPlugin: OSPlugin | OSPlugin[]): void;
|
||||
env(): OverlayScrollbarsEnv;
|
||||
}
|
||||
interface OverlayScrollbarsEnv {
|
||||
scrollbarSize: XY<number>;
|
||||
scrollbarIsOverlaid: XY<boolean>;
|
||||
scrollbarStyling: boolean;
|
||||
rtlScrollBehavior: {
|
||||
n: boolean;
|
||||
i: boolean;
|
||||
};
|
||||
flexboxGlue: boolean;
|
||||
cssCustomProperties: boolean;
|
||||
defaultInitializationStrategy: DefaultInitializationStrategy;
|
||||
defaultDefaultOptions: OSOptions;
|
||||
getInitializationStrategy(): InitializationStrategy;
|
||||
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
|
||||
getDefaultOptions(): OSOptions;
|
||||
setDefaultOptions(newDefaultOptions: PartialOptions<OSOptions>): void;
|
||||
}
|
||||
interface OverlayScrollbarsState {
|
||||
padding: TRBL;
|
||||
paddingAbsolute: boolean;
|
||||
overflowAmount: XY<number>;
|
||||
overflowStyle: XY<OverflowStyle>;
|
||||
hasOverflow: XY<boolean>;
|
||||
}
|
||||
interface OverlayScrollbarsElements {
|
||||
target: HTMLElement;
|
||||
host: HTMLElement;
|
||||
padding: HTMLElement;
|
||||
viewport: HTMLElement;
|
||||
content: HTMLElement;
|
||||
}
|
||||
type EventListener$0<Name extends keyof EventListenerMap> = GeneralEventListener<EventListenerMap, Name>;
|
||||
interface OverlayScrollbars {
|
||||
options(): OSOptions;
|
||||
options(newOptions?: PartialOptions<OSOptions>): OSOptions;
|
||||
options(): Options;
|
||||
options(newOptions?: PartialOptions<Options>): Options;
|
||||
update(force?: boolean): void;
|
||||
destroy(): void;
|
||||
state(): OverlayScrollbarsState;
|
||||
elements(): OverlayScrollbarsElements;
|
||||
on: AddOSEventListener;
|
||||
off: RemoveOSEventListener;
|
||||
state(): State;
|
||||
elements(): Elements;
|
||||
on<Name extends keyof EventListenerMap>(name: Name, listener: EventListener$0<Name>): () => void;
|
||||
on<Name extends keyof EventListenerMap>(name: Name, listener: EventListener$0<Name>[]): () => void;
|
||||
off<Name extends keyof EventListenerMap>(name: Name, listener?: EventListener$0<Name>): void;
|
||||
off<Name extends keyof EventListenerMap>(name: Name, listener?: EventListener$0<Name>[]): void;
|
||||
}
|
||||
/**
|
||||
* Notes:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.4"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3":
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
|
||||
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
|
||||
@@ -1248,6 +1248,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
|
||||
integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
|
||||
|
||||
"@jridgewell/source-map@^0.3.2":
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
|
||||
integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||
version "1.4.13"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
|
||||
@@ -4784,7 +4792,7 @@ jest-watcher@^28.1.1:
|
||||
jest-util "^28.1.1"
|
||||
string-length "^4.0.1"
|
||||
|
||||
jest-worker@^26.0.0:
|
||||
jest-worker@^26.2.1:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
|
||||
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
|
||||
@@ -6574,17 +6582,17 @@ rollup-plugin-styles@^3.10.0:
|
||||
source-map "^0.7.3"
|
||||
tslib "^2.1.0"
|
||||
|
||||
rollup-plugin-terser@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz#071866585aea104bfbb9dd1019ac523e63c81e45"
|
||||
integrity sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==
|
||||
rollup-plugin-terser@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
|
||||
integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.8.3"
|
||||
jest-worker "^26.0.0"
|
||||
serialize-javascript "^3.0.0"
|
||||
terser "^4.7.0"
|
||||
"@babel/code-frame" "^7.10.4"
|
||||
jest-worker "^26.2.1"
|
||||
serialize-javascript "^4.0.0"
|
||||
terser "^5.0.0"
|
||||
|
||||
rollup-plugin-ts@^3.0.1:
|
||||
rollup-plugin-ts@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-ts/-/rollup-plugin-ts-3.0.2.tgz#ee1a3f9ffe202ceff0b4d2f725fa268fa0c921bf"
|
||||
integrity sha512-67qi2QTHewhLyKDG6fX3jpohWpmUPPIT/xJ7rsYK46X6MqmoWy64Ti0y8ygPfLv8mXDCdRZUofM3mTxDfCswRA==
|
||||
@@ -6688,10 +6696,10 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
serialize-javascript@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea"
|
||||
integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==
|
||||
serialize-javascript@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
|
||||
integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
@@ -6845,7 +6853,7 @@ source-map-support@0.5.13:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map-support@~0.5.12:
|
||||
source-map-support@~0.5.20:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
|
||||
@@ -7145,14 +7153,15 @@ terminal-link@^2.0.0:
|
||||
ansi-escapes "^4.2.1"
|
||||
supports-hyperlinks "^2.0.0"
|
||||
|
||||
terser@^4.7.0:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
|
||||
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
|
||||
terser@^5.0.0:
|
||||
version "5.14.1"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca"
|
||||
integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.2"
|
||||
acorn "^8.5.0"
|
||||
commander "^2.20.0"
|
||||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.12"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
test-exclude@^6.0.0:
|
||||
version "6.0.0"
|
||||
@@ -7326,7 +7335,7 @@ type-fest@^0.8.1:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
typescript@^4.5.4, typescript@^4.7.3:
|
||||
typescript@^4.5.4, typescript@^4.7.4:
|
||||
version "4.7.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
|
||||
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
|
||||
|
||||
Reference in New Issue
Block a user