improve code and public interface

This commit is contained in:
Rene Haas
2022-07-19 17:24:32 +02:00
parent a548b60484
commit 2e7f8b4df4
24 changed files with 226 additions and 937 deletions
+2 -2
View File
@@ -6,12 +6,12 @@ export const dataAttributeHost = 'data-overlayscrollbars';
export const dataAttributeHostOverflowX = `${dataAttributeHost}-overflow-x`; export const dataAttributeHostOverflowX = `${dataAttributeHost}-overflow-x`;
export const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`; export const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`;
export const dataValueHostOverflowVisible = 'overflowVisible'; export const dataValueHostOverflowVisible = 'overflowVisible';
export const dataValueHostViewportScrollbarStyling = 'viewportStyled'; export const dataValueHostScrollbarHidden = 'scrollbarHidden';
export const classNamePadding = 'os-padding'; export const classNamePadding = 'os-padding';
export const classNameViewport = 'os-viewport'; export const classNameViewport = 'os-viewport';
export const classNameViewportArrange = `${classNameViewport}-arrange`; export const classNameViewportArrange = `${classNameViewport}-arrange`;
export const classNameContent = 'os-content'; export const classNameContent = 'os-content';
export const classNameViewportScrollbarStyling = `${classNameViewport}-scrollbar-styled`; export const classNameViewportScrollbarHidden = `${classNameViewport}-scrollbar-hidden`;
export const classNameOverflowVisible = `os-overflow-visible`; export const classNameOverflowVisible = `os-overflow-visible`;
export const classNameSizeObserver = 'os-size-observer'; export const classNameSizeObserver = 'os-size-observer';
+21 -17
View File
@@ -24,11 +24,11 @@ import {
classNameEnvironment, classNameEnvironment,
classNameEnvironmentFlexboxGlue, classNameEnvironmentFlexboxGlue,
classNameEnvironmentFlexboxGlueMax, classNameEnvironmentFlexboxGlueMax,
classNameViewportScrollbarStyling, classNameViewportScrollbarHidden,
} from 'classnames'; } from 'classnames';
import { Options, defaultOptions } from 'options'; import { Options, defaultOptions } from 'options';
import { PartialOptions } from 'typings'; import { DeepPartial } from 'typings';
import { InitializationStrategy } from 'initialization'; import { DefaultInitialization } from 'initialization';
import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins'; import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins';
type EnvironmentEventMap = { type EnvironmentEventMap = {
@@ -42,13 +42,13 @@ export interface InternalEnvironment {
readonly _rtlScrollBehavior: { n: boolean; i: boolean }; readonly _rtlScrollBehavior: { n: boolean; i: boolean };
readonly _flexboxGlue: boolean; readonly _flexboxGlue: boolean;
readonly _cssCustomProperties: boolean; readonly _cssCustomProperties: boolean;
readonly _defaultInitializationStrategy: InitializationStrategy; readonly _staticDefaultInitialization: DefaultInitialization;
readonly _defaultDefaultOptions: Options; readonly _staticDefaultOptions: Options;
_addListener(listener: EventListener<EnvironmentEventMap, '_'>): () => void; _addListener(listener: EventListener<EnvironmentEventMap, '_'>): () => void;
_getInitializationStrategy(): InitializationStrategy; _getDefaultInitialization(): DefaultInitialization;
_setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void; _setDefaultInitialization(newInitialization: DeepPartial<DefaultInitialization>): void;
_getDefaultOptions(): Options; _getDefaultOptions(): Options;
_setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void; _setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
} }
let environmentInstance: InternalEnvironment; let environmentInstance: InternalEnvironment;
@@ -75,7 +75,7 @@ const getNativeScrollbarSize = (
const getNativeScrollbarsHiding = (testElm: HTMLElement): boolean => { const getNativeScrollbarsHiding = (testElm: HTMLElement): boolean => {
let result = false; let result = false;
const revertClass = addClass(testElm, classNameViewportScrollbarStyling); const revertClass = addClass(testElm, classNameViewportScrollbarHidden);
try { try {
result = result =
style(testElm, cssProperty('scrollbar-width')) === 'none' || style(testElm, cssProperty('scrollbar-width')) === 'none' ||
@@ -152,9 +152,13 @@ const createEnvironment = (): InternalEnvironment => {
x: nativeScrollbarsSize.x === 0, x: nativeScrollbarsSize.x === 0,
y: nativeScrollbarsSize.y === 0, y: nativeScrollbarsSize.y === 0,
}; };
const initializationStrategy = { const defaultInitialization = {
padding: !nativeScrollbarsHiding, padding: !nativeScrollbarsHiding,
content: false, content: false,
cancel: {
nativeScrollbarsOverlaid: true,
body: null,
},
}; };
const defaultDefaultOptions = assignDeep({}, defaultOptions); const defaultDefaultOptions = assignDeep({}, defaultOptions);
@@ -166,20 +170,20 @@ const createEnvironment = (): InternalEnvironment => {
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm), _rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm), _flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener: (listener) => addEvent('_', listener), _addListener: (listener) => addEvent('_', listener),
_getInitializationStrategy: assignDeep<InitializationStrategy, InitializationStrategy>.bind( _getDefaultInitialization: assignDeep<DefaultInitialization, DefaultInitialization>.bind(
0, 0,
{} as InitializationStrategy, {} as DefaultInitialization,
initializationStrategy defaultInitialization
), ),
_setInitializationStrategy(newInitializationStrategy) { _setDefaultInitialization(newInitializationStrategy) {
assignDeep(initializationStrategy, newInitializationStrategy); assignDeep(defaultInitialization, newInitializationStrategy);
}, },
_getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, defaultDefaultOptions), _getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, defaultDefaultOptions),
_setDefaultOptions(newDefaultOptions) { _setDefaultOptions(newDefaultOptions) {
assignDeep(defaultDefaultOptions, newDefaultOptions); assignDeep(defaultDefaultOptions, newDefaultOptions);
}, },
_defaultInitializationStrategy: assignDeep({}, initializationStrategy), _staticDefaultInitialization: assignDeep({}, defaultInitialization),
_defaultDefaultOptions: assignDeep({}, defaultDefaultOptions), _staticDefaultOptions: assignDeep({}, defaultDefaultOptions),
}; };
removeAttr(envElm, 'style'); removeAttr(envElm, 'style');
@@ -1,24 +1,37 @@
import { isFunction, isNull, isUndefined } from 'support'; import { isBoolean, isFunction, isNull, isUndefined } from 'support';
import type { import type {
StructureInitialization, StructureInitialization,
StructureInitializationStrategy, DefaultStructureInitialization,
} from 'setups/structureSetup'; } from 'setups/structureSetup';
import type { import type {
ScrollbarsInitialization, ScrollbarsInitialization,
ScrollbarsInitializationStrategy, DefaultScrollbarsInitialization,
} from 'setups/scrollbarsSetup'; } from 'setups/scrollbarsSetup';
import { getEnvironment } from 'environment';
import { DeepPartial } from 'typings';
import { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
type StaticInitialization = HTMLElement | null | undefined; type StaticInitialization = HTMLElement | null | undefined;
type DynamicInitialization = HTMLElement | boolean | null | undefined; type DynamicInitialization = HTMLElement | boolean | null | undefined;
export type CancelInitialization = {
cancel: {
nativeScrollbarsOverlaid: boolean | undefined;
body: boolean | null | undefined;
};
};
export type InitializationTargetElement = HTMLElement | HTMLTextAreaElement; export type InitializationTargetElement = HTMLElement | HTMLTextAreaElement;
export type InitializationTargetObject = StructureInitialization & ScrollbarsInitialization; export type InitializationTargetObject = StructureInitialization &
ScrollbarsInitialization &
DeepPartial<CancelInitialization>;
export type InitializationTarget = InitializationTargetElement | InitializationTargetObject; export type InitializationTarget = InitializationTargetElement | InitializationTargetObject;
export type InitializationStrategy = StructureInitializationStrategy & export type DefaultInitialization = DefaultStructureInitialization &
ScrollbarsInitializationStrategy; DefaultScrollbarsInitialization &
CancelInitialization;
/** /**
* Static elements MUST be present. * Static elements MUST be present.
@@ -39,9 +52,9 @@ export type DynamicInitializationElement<Args extends any[]> =
| ((...args: Args) => DynamicInitialization) | ((...args: Args) => DynamicInitialization)
| DynamicInitialization; | DynamicInitialization;
export type InitializtationElementStrategy<InitElm> = Exclude<InitElm, HTMLElement>; export type DefaultInitializtationElement<InitElm> = Exclude<InitElm, HTMLElement>;
export type DefaultInitializtationElementStrategy< export type FallbackInitializtationElement<
InitElm extends StaticInitializationElement<any> | DynamicInitializationElement<any> InitElm extends StaticInitializationElement<any> | DynamicInitializationElement<any>
> = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any > = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any
? (...args: P) => HTMLElement ? (...args: P) => HTMLElement
@@ -52,20 +65,20 @@ const resolveInitialization = <T>(value: any, args: any): T =>
const staticInitializationElement = <T extends StaticInitializationElement<any>>( const staticInitializationElement = <T extends StaticInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>, args: Parameters<Extract<T, (...args: any[]) => any>>,
defaultStaticInitializationElement: DefaultInitializtationElementStrategy<T>, fallbackStaticInitializationElement: FallbackInitializtationElement<T>,
staticInitializationElementStrategy?: InitializtationElementStrategy<T>, defaultStaticInitializationElementStrategy?: DefaultInitializtationElement<T>,
staticInitializationElementValue?: T | false staticInitializationElementValue?: T | false
): HTMLElement => ): HTMLElement =>
resolveInitialization<StaticInitialization>( resolveInitialization<StaticInitialization>(
staticInitializationElementValue || staticInitializationElementValue ||
resolveInitialization<StaticInitialization>(staticInitializationElementStrategy, args), resolveInitialization<StaticInitialization>(defaultStaticInitializationElementStrategy, args),
args args
) || defaultStaticInitializationElement.apply(0, args); ) || fallbackStaticInitializationElement.apply(0, args);
const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>( const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>, args: Parameters<Extract<T, (...args: any[]) => any>>,
defaultDynamicInitializationElement: DefaultInitializtationElementStrategy<T>, fallbackDynamicInitializationElement: FallbackInitializtationElement<T>,
dynamicInitializationElementStrategy?: InitializtationElementStrategy<T>, defaultDynamicInitializationElementStrategy?: DefaultInitializtationElement<T>,
dynamicInitializationElementValue?: T | false dynamicInitializationElementValue?: T | false
): HTMLElement | false => { ): HTMLElement | false => {
let result = resolveInitialization<DynamicInitialization>( let result = resolveInitialization<DynamicInitialization>(
@@ -75,14 +88,40 @@ const dynamicInitializationElement = <T extends DynamicInitializationElement<any
if (isNull(result) || isUndefined(result)) { if (isNull(result) || isUndefined(result)) {
result = resolveInitialization<DynamicInitialization>( result = resolveInitialization<DynamicInitialization>(
dynamicInitializationElementStrategy, defaultDynamicInitializationElementStrategy,
args args
); );
} }
return result === true || isNull(result) || isUndefined(result) return result === true || isNull(result) || isUndefined(result)
? defaultDynamicInitializationElement.apply(0, args) ? fallbackDynamicInitializationElement.apply(0, args)
: result; : result;
}; };
export { staticInitializationElement, dynamicInitializationElement }; const cancelInitialization = (
cancelInitializationValue: DeepPartial<CancelInitialization['cancel']> | false | null | undefined,
structureSetupElements: StructureSetupElementsObj
): boolean => {
const { nativeScrollbarsOverlaid, body } = cancelInitializationValue || {};
const { _isBody, _viewportIsTarget } = structureSetupElements;
const { _getDefaultInitialization, _nativeScrollbarsOverlaid } = getEnvironment();
const { nativeScrollbarsOverlaid: defaultNativeScrollbarsOverlaid, body: defaultbody } =
_getDefaultInitialization().cancel;
const resolvedNativeScrollbarsOverlaid =
nativeScrollbarsOverlaid ?? defaultNativeScrollbarsOverlaid;
const resolvedDocumentScrollingElement = isBoolean(body) || isNull(body) ? body : defaultbody;
const finalNativeScrollbarsOverlaid =
(_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y) &&
resolvedNativeScrollbarsOverlaid;
const finalDocumentScrollingElement =
_isBody &&
(isNull(resolvedDocumentScrollingElement)
? !_viewportIsTarget
: resolvedDocumentScrollingElement);
return !!finalNativeScrollbarsOverlaid || !!finalDocumentScrollingElement;
};
export { staticInitializationElement, dynamicInitializationElement, cancelInitialization };
+5 -14
View File
@@ -1,5 +1,5 @@
import { assignDeep, each, isObject, keys, isArray, hasOwnProperty, isFunction } from 'support'; import { assignDeep, each, isObject, keys, isArray, hasOwnProperty, isFunction } from 'support';
import { PartialOptions, ReadonlyOptions } from 'typings'; import { DeepPartial, ReadonlyOptions } from 'typings';
const opsStringify = (value: any) => const opsStringify = (value: any) =>
JSON.stringify(value, (_, val) => { JSON.stringify(value, (_, val) => {
@@ -40,6 +40,7 @@ export type UpdatedCallback = (this: any, args?: UpdatedArgs) => void;
export interface Options { export interface Options {
paddingAbsolute: boolean; paddingAbsolute: boolean;
showNativeOverlaidScrollbars: boolean;
updating: { updating: {
elementEvents: Array<[elementSelector: string, eventNames: string]> | null; elementEvents: Array<[elementSelector: string, eventNames: string]> | null;
attributes: string[] | null; attributes: string[] | null;
@@ -59,10 +60,6 @@ export interface Options {
clickScroll: boolean; clickScroll: boolean;
touch: boolean; touch: boolean;
}; };
nativeScrollbarsOverlaid: {
show: boolean;
initialize: boolean;
};
} }
export type ReadonlyOSOptions = ReadonlyOptions<Options>; export type ReadonlyOSOptions = ReadonlyOptions<Options>;
@@ -97,6 +94,7 @@ export interface UpdatedArgs {
export const defaultOptions: Options = { export const defaultOptions: Options = {
// resize: 'none', // none || both || horizontal || vertical || n || b || h || v // resize: 'none', // none || both || horizontal || vertical || n || b || h || v
paddingAbsolute: false, // true || false paddingAbsolute: false, // true || false
showNativeOverlaidScrollbars: false, // true || false
updating: { updating: {
elementEvents: [['img', 'load']], // array of tuples || null elementEvents: [['img', 'load']], // array of tuples || null
debounce: [0, 33], // number || number array || null debounce: [0, 33], // number || number array || null
@@ -107,10 +105,6 @@ export const defaultOptions: Options = {
x: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s x: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
y: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s y: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
}, },
nativeScrollbarsOverlaid: {
show: false, // true || false
initialize: false, // true || false
},
scrollbars: { scrollbars: {
theme: 'os-theme-dark', theme: 'os-theme-dark',
visibility: 'auto', // visible || hidden || auto || v || h || a visibility: 'auto', // visible || hidden || auto || v || h || a
@@ -129,11 +123,8 @@ export const defaultOptions: Options = {
*/ */
}; };
export const getOptionsDiff = <T>( export const getOptionsDiff = <T>(currOptions: T, newOptions: DeepPartial<T>): DeepPartial<T> => {
currOptions: T, const diff: DeepPartial<T> = {};
newOptions: PartialOptions<T>
): PartialOptions<T> => {
const diff: PartialOptions<T> = {};
const optionsKeys = keys(newOptions).concat(keys(currOptions)); const optionsKeys = keys(newOptions).concat(keys(currOptions));
each(optionsKeys, (optionKey) => { each(optionsKeys, (optionKey) => {
@@ -20,11 +20,12 @@ import {
OptionsValidationPluginInstance, OptionsValidationPluginInstance,
} from 'plugins'; } from 'plugins';
import { addInstance, getInstance, removeInstance } from 'instances'; import { addInstance, getInstance, removeInstance } from 'instances';
import type { PartialOptions, OverflowStyle } from 'typings'; import type { DeepPartial, OverflowStyle } from 'typings';
import type { import {
InitializationTarget, InitializationTarget,
InitializationTargetObject, InitializationTargetObject,
InitializationStrategy, DefaultInitialization,
cancelInitialization,
} from 'initialization'; } from 'initialization';
import type { import type {
InitialEventListeners as GeneralInitialEventListeners, InitialEventListeners as GeneralInitialEventListeners,
@@ -34,7 +35,7 @@ import type {
export interface OverlayScrollbarsStatic { export interface OverlayScrollbarsStatic {
( (
target: InitializationTarget | InitializationTargetObject, target: InitializationTarget | InitializationTargetObject,
options?: PartialOptions<Options>, options?: DeepPartial<Options>,
eventListeners?: GeneralInitialEventListeners<EventListenerMap> eventListeners?: GeneralInitialEventListeners<EventListenerMap>
): OverlayScrollbars; ): OverlayScrollbars;
@@ -49,13 +50,13 @@ export interface Environment {
rtlScrollBehavior: { n: boolean; i: boolean }; rtlScrollBehavior: { n: boolean; i: boolean };
flexboxGlue: boolean; flexboxGlue: boolean;
cssCustomProperties: boolean; cssCustomProperties: boolean;
defaultInitializationStrategy: InitializationStrategy; staticDefaultInitialization: DefaultInitialization;
defaultDefaultOptions: Options; staticDefaultOptions: Options;
getInitializationStrategy(): InitializationStrategy; getDefaultInitialization(): DefaultInitialization;
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void; setDefaultInitialization(newDefaultInitialization: DeepPartial<DefaultInitialization>): void;
getDefaultOptions(): Options; getDefaultOptions(): Options;
setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void; setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
} }
export interface State { export interface State {
@@ -87,7 +88,7 @@ export interface OnUpdatedEventListenerArgs {
hostMutation: boolean; hostMutation: boolean;
contentMutation: boolean; contentMutation: boolean;
}; };
changedOptions: PartialOptions<Options>; changedOptions: DeepPartial<Options>;
force: boolean; force: boolean;
} }
@@ -103,7 +104,7 @@ export type EventListenerMap = {
/** /**
* Triggered after all elements, observers and events are destroyed. * Triggered after all elements, observers and events are destroyed.
*/ */
destroyed: [instance: OverlayScrollbars, withdrawn: boolean]; destroyed: [instance: OverlayScrollbars, canceled: boolean];
}; };
export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>; export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>;
@@ -115,7 +116,7 @@ export type EventListener<Name extends keyof EventListenerMap> = GeneralEventLis
export interface OverlayScrollbars { export interface OverlayScrollbars {
options(): Options; options(): Options;
options(newOptions?: PartialOptions<Options>): Options; options(newOptions?: DeepPartial<Options>): Options;
update(force?: boolean): OverlayScrollbars; update(force?: boolean): OverlayScrollbars;
@@ -143,13 +144,10 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
eventListeners? eventListeners?
): OverlayScrollbars => { ): OverlayScrollbars => {
let destroyed = false; let destroyed = false;
const { const { _getDefaultOptions, _addListener: addEnvListener } = getEnvironment();
_getDefaultOptions,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_addListener: addEnvListener,
} = getEnvironment();
const plugins = getPlugins(); const plugins = getPlugins();
const instanceTarget = isHTMLElement(target) ? target : target.target; const targetIsElement = isHTMLElement(target);
const instanceTarget = targetIsElement ? target : target.target;
const potentialInstance = getInstance(instanceTarget); const potentialInstance = getInstance(instanceTarget);
if (potentialInstance) { if (potentialInstance) {
return potentialInstance; return potentialInstance;
@@ -158,7 +156,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
const optionsValidationPlugin = plugins[ const optionsValidationPlugin = plugins[
optionsValidationPluginName optionsValidationPluginName
] as OptionsValidationPluginInstance; ] as OptionsValidationPluginInstance;
const validateOptions = (newOptions?: PartialOptions<Options>) => { const validateOptions = (newOptions?: DeepPartial<Options>) => {
const opts = newOptions || {}; const opts = newOptions || {};
const validate = optionsValidationPlugin && optionsValidationPlugin._; const validate = optionsValidationPlugin && optionsValidationPlugin._;
return validate ? validate(opts, true) : opts; return validate ? validate(opts, true) : opts;
@@ -178,11 +176,11 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
currentOptions, currentOptions,
structureState structureState
); );
const update = (changedOptions: PartialOptions<Options>, force?: boolean) => { const update = (changedOptions: DeepPartial<Options>, force?: boolean) => {
updateStructure(changedOptions, !!force); updateStructure(changedOptions, !!force);
}; };
const removeEnvListener = addEnvListener(update.bind(0, {}, true)); const removeEnvListener = addEnvListener(update.bind(0, {}, true));
const destroy = (withdrawn?: boolean) => { const destroy = (canceled?: boolean) => {
removeInstance(instanceTarget); removeInstance(instanceTarget);
removeEnvListener(); removeEnvListener();
@@ -192,12 +190,12 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
destroyed = true; destroyed = true;
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
triggerEvent('destroyed', [instance, !!withdrawn]); triggerEvent('destroyed', [instance, !!canceled]);
removeEvent(); removeEvent();
}; };
const instance: OverlayScrollbars = { const instance: OverlayScrollbars = {
options(newOptions?: PartialOptions<Options>) { options(newOptions?: DeepPartial<Options>) {
if (newOptions) { if (newOptions) {
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions)); const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
@@ -265,11 +263,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
} }
}); });
if ( if (cancelInitialization(!targetIsElement && target.cancel, structureState._elements)) {
_nativeScrollbarIsOverlaid.x &&
_nativeScrollbarIsOverlaid.y &&
!currentOptions.nativeScrollbarsOverlaid.initialize
) {
destroy(true); destroy(true);
return instance; return instance;
} }
@@ -323,10 +317,10 @@ OverlayScrollbars.env = () => {
_rtlScrollBehavior, _rtlScrollBehavior,
_flexboxGlue, _flexboxGlue,
_cssCustomProperties, _cssCustomProperties,
_defaultInitializationStrategy, _staticDefaultInitialization,
_defaultDefaultOptions, _staticDefaultOptions,
_getInitializationStrategy, _getDefaultInitialization,
_setInitializationStrategy, _setDefaultInitialization,
_getDefaultOptions, _getDefaultOptions,
_setDefaultOptions, _setDefaultOptions,
} = getEnvironment(); } = getEnvironment();
@@ -339,11 +333,11 @@ OverlayScrollbars.env = () => {
rtlScrollBehavior: _rtlScrollBehavior, rtlScrollBehavior: _rtlScrollBehavior,
flexboxGlue: _flexboxGlue, flexboxGlue: _flexboxGlue,
cssCustomProperties: _cssCustomProperties, cssCustomProperties: _cssCustomProperties,
defaultInitializationStrategy: _defaultInitializationStrategy, staticDefaultInitialization: _staticDefaultInitialization,
defaultDefaultOptions: _defaultDefaultOptions, staticDefaultOptions: _staticDefaultOptions,
getInitializationStrategy: _getInitializationStrategy, getDefaultInitialization: _getDefaultInitialization,
setInitializationStrategy: _setInitializationStrategy, setDefaultInitialization: _setDefaultInitialization,
getDefaultOptions: _getDefaultOptions, getDefaultOptions: _getDefaultOptions,
setDefaultOptions: _setDefaultOptions, setDefaultOptions: _setDefaultOptions,
} }
@@ -10,7 +10,7 @@ import {
OptionsTemplateValue, OptionsTemplateValue,
optionsTemplateTypes as oTypes, optionsTemplateTypes as oTypes,
} from 'plugins/optionsValidationPlugin/validation'; } from 'plugins/optionsValidationPlugin/validation';
import type { PartialOptions } from 'typings'; import type { DeepPartial } from 'typings';
import type { Plugin } from 'plugins'; import type { Plugin } from 'plugins';
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number; const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
@@ -26,6 +26,7 @@ const scrollbarsAutoHideAllowedValues: OptionsTemplateValue<ScrollbarAutoHideBeh
const optionsTemplate: OptionsTemplate<Options> = { const optionsTemplate: OptionsTemplate<Options> = {
// resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b || // resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b ||
paddingAbsolute: booleanAllowedValues, // true || false paddingAbsolute: booleanAllowedValues, // true || false
showNativeOverlaidScrollbars: booleanAllowedValues, // true || false
updating: { updating: {
elementEvents: arrayNullValues, // array of tuples || null elementEvents: arrayNullValues, // array of tuples || null
attributes: arrayNullValues, attributes: arrayNullValues,
@@ -52,21 +53,17 @@ const optionsTemplate: OptionsTemplate<Options> = {
inheritedAttrs: stringArrayNullAllowedValues, // string || array || nul inheritedAttrs: stringArrayNullAllowedValues, // string || array || nul
}, },
*/ */
nativeScrollbarsOverlaid: {
show: booleanAllowedValues, // true || false
initialize: booleanAllowedValues, // true || false
},
}; };
export type OptionsValidationPluginInstance = { export type OptionsValidationPluginInstance = {
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => PartialOptions<Options>; _: (options: DeepPartial<Options>, doWriteErrors?: boolean) => DeepPartial<Options>;
}; };
export const optionsValidationPluginName = '__osOptionsValidationPlugin'; export const optionsValidationPluginName = '__osOptionsValidationPlugin';
export const optionsValidationPlugin: Plugin<OptionsValidationPluginInstance> = { export const optionsValidationPlugin: Plugin<OptionsValidationPluginInstance> = {
[optionsValidationPluginName]: { [optionsValidationPluginName]: {
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => { _: (options: DeepPartial<Options>, doWriteErrors?: boolean) => {
const [validated, foreign] = validateOptions(optionsTemplate, options, doWriteErrors); const [validated, foreign] = validateOptions(optionsTemplate, options, doWriteErrors);
return { ...foreign, ...validated }; return { ...foreign, ...validated };
}, },
@@ -1,6 +1,6 @@
import { each, hasOwnProperty, keys, push, isEmptyObject } from 'support/utils'; import { each, hasOwnProperty, keys, push, isEmptyObject } from 'support/utils';
import { type, isArray, isUndefined, isPlainObject, isString } from 'support/utils/types'; import { type, isArray, isUndefined, isPlainObject, isString } from 'support/utils/types';
import { PlainObject, PartialOptions } from 'typings'; import { PlainObject, DeepPartial } from 'typings';
export type OptionsObjectType = Record<string, unknown>; export type OptionsObjectType = Record<string, unknown>;
export type OptionsFunctionType = (this: any, ...args: any[]) => any; export type OptionsFunctionType = (this: any, ...args: any[]) => any;
@@ -26,7 +26,7 @@ export type OptionsTemplate<T> = {
}; };
export type OptionsValidationResult<T> = [ export type OptionsValidationResult<T> = [
PartialOptions<T>, // validated DeepPartial<T>, // validated
Record<string, unknown> // foreign Record<string, unknown> // foreign
]; ];
@@ -88,12 +88,12 @@ const optionsTemplateTypes: OptionsTemplateTypesDictionary = {
*/ */
const validateRecursive = <T extends PlainObject>( const validateRecursive = <T extends PlainObject>(
template: OptionsTemplate<T>, template: OptionsTemplate<T>,
options: PartialOptions<T>, options: DeepPartial<T>,
doWriteErrors?: boolean, doWriteErrors?: boolean,
propPath?: string propPath?: string
): OptionsValidationResult<T> => { ): OptionsValidationResult<T> => {
const validatedOptions: PartialOptions<T> = {}; const validatedOptions: DeepPartial<T> = {};
const optionsCopy: PartialOptions<T> = { ...options }; const optionsCopy: DeepPartial<T> = { ...options };
const props = keys(template).filter((prop) => hasOwnProperty(options, prop)); const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
each(props, (prop: Extract<keyof T, string>) => { each(props, (prop: Extract<keyof T, string>) => {
@@ -189,7 +189,7 @@ const validateRecursive = <T extends PlainObject>(
*/ */
const validateOptions = <T extends PlainObject>( const validateOptions = <T extends PlainObject>(
template: OptionsTemplate<T>, template: OptionsTemplate<T>,
options: PartialOptions<T>, options: DeepPartial<T>,
doWriteErrors?: boolean doWriteErrors?: boolean
): OptionsValidationResult<T> => validateRecursive<T>(template, options, doWriteErrors); ): OptionsValidationResult<T> => validateRecursive<T>(template, options, doWriteErrors);
@@ -82,15 +82,11 @@ export const scrollbarsHidingPluginName = '__osScrollbarsHidingPlugin';
export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = { export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
[scrollbarsHidingPluginName]: { [scrollbarsHidingPluginName]: {
_createUniqueViewportArrangeElement: (env: InternalEnvironment) => { _createUniqueViewportArrangeElement: (env: InternalEnvironment) => {
const { const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _cssCustomProperties } = env;
_nativeScrollbarsHiding: _nativeScrollbarStyling,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_cssCustomProperties,
} = env;
const create = const create =
!_cssCustomProperties && !_cssCustomProperties &&
!_nativeScrollbarStyling && !_nativeScrollbarsHiding &&
(_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y);
const result = create ? document.createElement('style') : false; const result = create ? document.createElement('style') : false;
if (result) { if (result) {
@@ -29,7 +29,7 @@ import type { InitializationTarget } from 'initialization';
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements'; import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
import type { import type {
ScrollbarsInitialization, ScrollbarsInitialization,
ScrollbarsInitializationStrategy, DefaultScrollbarsInitialization,
ScrollbarsDynamicInitializationElement, ScrollbarsDynamicInitializationElement,
} from 'setups/scrollbarsSetup/scrollbarsSetup.initialization'; } from 'setups/scrollbarsSetup/scrollbarsSetup.initialization';
import { StyleObject } from 'typings'; import { StyleObject } from 'typings';
@@ -85,19 +85,17 @@ export const createScrollbarsSetupElements = (
target: InitializationTarget, target: InitializationTarget,
structureSetupElements: StructureSetupElementsObj structureSetupElements: StructureSetupElementsObj
): ScrollbarsSetupElements => { ): ScrollbarsSetupElements => {
const { _getInitializationStrategy } = getEnvironment(); const { _getDefaultInitialization } = getEnvironment();
const { scrollbarsSlot: environmentScrollbarSlot } = const { scrollbarsSlot: defaultScrollbarSlot } =
_getInitializationStrategy() as ScrollbarsInitializationStrategy; _getDefaultInitialization() as DefaultScrollbarsInitialization;
const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements; const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements;
const initializationScrollbarSlot = _targetIsElm const scrollbarSlot = _targetIsElm ? null : (target as ScrollbarsInitialization).scrollbarsSlot;
? null
: (target as ScrollbarsInitialization).scrollbarsSlot;
const evaluatedScrollbarSlot = const evaluatedScrollbarSlot =
generalDynamicInitializationElement<ScrollbarsDynamicInitializationElement>( generalDynamicInitializationElement<ScrollbarsDynamicInitializationElement>(
[_target, _host, _viewport], [_target, _host, _viewport],
() => _host, () => _host,
environmentScrollbarSlot, defaultScrollbarSlot,
initializationScrollbarSlot scrollbarSlot
); );
const scrollbarsAddRemoveClass = ( const scrollbarsAddRemoveClass = (
scrollbarStructures: ScrollbarStructure[], scrollbarStructures: ScrollbarStructure[],
@@ -1,6 +1,6 @@
import type { import type {
InitializationTargetElement, InitializationTargetElement,
InitializtationElementStrategy, DefaultInitializtationElement,
DynamicInitializationElement, DynamicInitializationElement,
} from 'initialization'; } from 'initialization';
@@ -20,8 +20,6 @@ export interface ScrollbarsInitialization {
scrollbarsSlot?: ScrollbarsDynamicInitializationElement; scrollbarsSlot?: ScrollbarsDynamicInitializationElement;
} }
export type ScrollbarsInitializationStrategy = { export type DefaultScrollbarsInitialization = {
[K in keyof ScrollbarsInitialization]: InitializtationElementStrategy< [K in keyof ScrollbarsInitialization]: DefaultInitializtationElement<ScrollbarsInitialization[K]>;
ScrollbarsInitialization[K]
>;
}; };
@@ -1,11 +1,11 @@
import { assignDeep, hasOwnProperty } from 'support'; import { assignDeep, hasOwnProperty } from 'support';
import type { Options, ReadonlyOSOptions } from 'options'; import type { Options, ReadonlyOSOptions } from 'options';
import type { PartialOptions } from 'typings'; import type { DeepPartial } from 'typings';
export type SetupElements<T extends Record<string, any>> = [elements: T, destroy: () => void]; export type SetupElements<T extends Record<string, any>> = [elements: T, destroy: () => void];
export type SetupUpdate<T extends any[]> = ( export type SetupUpdate<T extends any[]> = (
changedOptions: PartialOptions<Options>, changedOptions: DeepPartial<Options>,
force: boolean, force: boolean,
...args: T ...args: T
) => void; ) => void;
@@ -37,7 +37,7 @@ const getPropByPath = <T>(obj: any, path: string): T =>
export const createOptionCheck = export const createOptionCheck =
( (
options: ReadonlyOSOptions, options: ReadonlyOSOptions,
changedOptions: PartialOptions<Options>, changedOptions: DeepPartial<Options>,
force?: boolean force?: boolean
): SetupUpdateCheckOption => ): SetupUpdateCheckOption =>
(path: string) => (path: string) =>
@@ -20,6 +20,8 @@ import {
attrClass, attrClass,
hasAttrClass, hasAttrClass,
ResizeObserverConstructor, ResizeObserverConstructor,
hasOwnProperty,
noop,
} from 'support'; } from 'support';
import { import {
dataAttributeHost, dataAttributeHost,
@@ -28,7 +30,7 @@ import {
classNamePadding, classNamePadding,
classNameViewport, classNameViewport,
classNameContent, classNameContent,
classNameViewportScrollbarStyling, classNameViewportScrollbarHidden,
} from 'classnames'; } from 'classnames';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { getPlugins, scrollbarsHidingPluginName } from 'plugins'; import { getPlugins, scrollbarsHidingPluginName } from 'plugins';
@@ -36,11 +38,11 @@ import type { ScrollbarsHidingPluginInstance } from 'plugins/scrollbarsHidingPlu
import { import {
staticInitializationElement as generalStaticInitializationElement, staticInitializationElement as generalStaticInitializationElement,
dynamicInitializationElement as generalDynamicInitializationElement, dynamicInitializationElement as generalDynamicInitializationElement,
InitializationTargetObject,
} from 'initialization'; } from 'initialization';
import type { InitializationTarget, InitializationTargetElement } from 'initialization'; import type { InitializationTarget, InitializationTargetElement } from 'initialization';
import type { import type {
StructureDynamicInitializationElement, StructureDynamicInitializationElement,
StructureInitialization,
StructureStaticInitializationElement, StructureStaticInitializationElement,
} from 'setups/structureSetup/structureSetup.initialization'; } from 'setups/structureSetup/structureSetup.initialization';
@@ -60,8 +62,6 @@ export interface StructureSetupElementsObj {
// ctx ---- // ctx ----
_isTextarea: boolean; _isTextarea: boolean;
_isBody: boolean; _isBody: boolean;
_htmlElm: HTMLHtmlElement;
_bodyElm: HTMLBodyElement;
_windowElm: Window; _windowElm: Window;
_documentElm: Document; _documentElm: Document;
_targetIsElm: boolean; _targetIsElm: boolean;
@@ -86,29 +86,35 @@ export const createStructureSetupElements = (
target: InitializationTarget target: InitializationTarget
): StructureSetupElements => { ): StructureSetupElements => {
const env = getEnvironment(); const env = getEnvironment();
const { _getInitializationStrategy, _nativeScrollbarsHiding } = env; const { _getDefaultInitialization, _nativeScrollbarsHiding } = env;
const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as
| ScrollbarsHidingPluginInstance | ScrollbarsHidingPluginInstance
| undefined; | undefined;
const createUniqueViewportArrangeElement = const createUniqueViewportArrangeElement =
scrollbarsHidingPlugin && scrollbarsHidingPlugin._createUniqueViewportArrangeElement; scrollbarsHidingPlugin && scrollbarsHidingPlugin._createUniqueViewportArrangeElement;
const { const {
host: hostInitializationStrategy, host: defaultHostInitializationStrategy,
viewport: viewportInitializationStrategy, viewport: defaultViewportInitializationStrategy,
padding: paddingInitializationStrategy, padding: defaultPaddingInitializationStrategy,
content: contentInitializationStrategy, content: defaultContentInitializationStrategy,
} = _getInitializationStrategy(); } = _getDefaultInitialization();
const targetIsElm = isHTMLElement(target); const targetIsElm = isHTMLElement(target);
const targetStructureInitialization = target as StructureInitialization; const targetStructureInitialization = (targetIsElm ? {} : target) as InitializationTargetObject;
const targetElement = targetIsElm const {
? (target as InitializationTargetElement) host: hostInitializationStrategy,
: targetStructureInitialization.target; padding: paddingInitializationStrategy,
viewport: viewportInitializationStrategy,
content: contentInitializationStrategy,
} = targetStructureInitialization;
const targetElement = targetIsElm ? target : targetStructureInitialization.target;
const isTextarea = is(targetElement, 'textarea'); const isTextarea = is(targetElement, 'textarea');
const isBody = !isTextarea && is(targetElement, 'body'); const ownerDocument = targetElement.ownerDocument;
const ownerDocument = targetElement!.ownerDocument; const isBody = targetElement === ownerDocument.body;
const bodyElm = ownerDocument.body as HTMLBodyElement;
const wnd = ownerDocument.defaultView as Window; const wnd = ownerDocument.defaultView as Window;
const singleElmSupport = !!ResizeObserverConstructor && !isTextarea && _nativeScrollbarsHiding; const singleElmSupport = isBody
? _nativeScrollbarsHiding
: !!ResizeObserverConstructor && !isTextarea && _nativeScrollbarsHiding;
const staticInitializationElement = const staticInitializationElement =
generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [ generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [
targetElement, targetElement,
@@ -120,13 +126,15 @@ export const createStructureSetupElements = (
const viewportElement = [ const viewportElement = [
staticInitializationElement( staticInitializationElement(
createNewDiv, createNewDiv,
viewportInitializationStrategy, defaultViewportInitializationStrategy,
targetStructureInitialization.viewport isBody && !hasOwnProperty(targetStructureInitialization, 'viewport')
? targetElement
: viewportInitializationStrategy
), ),
staticInitializationElement(createNewDiv, viewportInitializationStrategy), staticInitializationElement(createNewDiv, defaultViewportInitializationStrategy),
staticInitializationElement(createNewDiv), staticInitializationElement(createNewDiv),
].filter((potentialViewport) => ].filter((potentialViewport) =>
!singleElmSupport ? potentialViewport !== targetElement : true singleElmSupport ? true : potentialViewport !== targetElement
)[0]; )[0];
const viewportIsTarget = viewportElement === targetElement; const viewportIsTarget = viewportElement === targetElement;
const evaluatedTargetObj: StructureSetupElementsObj = { const evaluatedTargetObj: StructureSetupElementsObj = {
@@ -134,8 +142,8 @@ export const createStructureSetupElements = (
_host: isTextarea _host: isTextarea
? staticInitializationElement( ? staticInitializationElement(
createNewDiv, createNewDiv,
hostInitializationStrategy, defaultHostInitializationStrategy,
targetStructureInitialization.host hostInitializationStrategy
) )
: (targetElement as HTMLElement), : (targetElement as HTMLElement),
_viewport: viewportElement, _viewport: viewportElement,
@@ -143,15 +151,15 @@ export const createStructureSetupElements = (
!viewportIsTarget && !viewportIsTarget &&
dynamicInitializationElement( dynamicInitializationElement(
createNewDiv, createNewDiv,
paddingInitializationStrategy, defaultPaddingInitializationStrategy,
targetStructureInitialization.padding paddingInitializationStrategy
), ),
_content: _content:
!viewportIsTarget && !viewportIsTarget &&
dynamicInitializationElement( dynamicInitializationElement(
createNewDiv, createNewDiv,
contentInitializationStrategy, defaultContentInitializationStrategy,
targetStructureInitialization.content contentInitializationStrategy
), ),
_viewportArrange: _viewportArrange:
!viewportIsTarget && !viewportIsTarget &&
@@ -160,8 +168,6 @@ export const createStructureSetupElements = (
createUniqueViewportArrangeElement(env), createUniqueViewportArrangeElement(env),
_windowElm: wnd, _windowElm: wnd,
_documentElm: ownerDocument, _documentElm: ownerDocument,
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
_bodyElm: bodyElm,
_isTextarea: isTextarea, _isTextarea: isTextarea,
_isBody: isBody, _isBody: isBody,
_targetIsElm: targetIsElm, _targetIsElm: targetIsElm,
@@ -197,6 +203,9 @@ export const createStructureSetupElements = (
const removePaddingClass = addClass(_padding, classNamePadding); const removePaddingClass = addClass(_padding, classNamePadding);
const removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport); const removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport);
const removeContentClass = addClass(_content, classNameContent); const removeContentClass = addClass(_content, classNameContent);
const removeHtmlClass = isBody
? addClass(parent(targetElement), classNameViewportScrollbarHidden)
: noop;
// only insert host for textarea after target if it was generated // only insert host for textarea after target if it was generated
if (isTextareaHostGenerated) { if (isTextareaHostGenerated) {
@@ -214,6 +223,7 @@ export const createStructureSetupElements = (
appendChildren(_viewport, _content); appendChildren(_viewport, _content);
push(destroyFns, () => { push(destroyFns, () => {
removeHtmlClass();
removeHostDataAttr(); removeHostDataAttr();
removeAttr(_viewport, dataAttributeHostOverflowX); removeAttr(_viewport, dataAttributeHostOverflowX);
removeAttr(_viewport, dataAttributeHostOverflowY); removeAttr(_viewport, dataAttributeHostOverflowY);
@@ -233,7 +243,7 @@ export const createStructureSetupElements = (
}); });
if (_nativeScrollbarsHiding && !viewportIsTarget) { if (_nativeScrollbarsHiding && !viewportIsTarget) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling)); push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarHidden));
} }
if (_viewportArrange) { if (_viewportArrange) {
insertBefore(_viewport, _viewportArrange); insertBefore(_viewport, _viewportArrange);
@@ -2,7 +2,7 @@ import type {
InitializationTargetElement, InitializationTargetElement,
StaticInitializationElement, StaticInitializationElement,
DynamicInitializationElement, DynamicInitializationElement,
InitializtationElementStrategy, DefaultInitializtationElement,
} from 'initialization'; } from 'initialization';
export type StructureStaticInitializationElement = StaticInitializationElement< export type StructureStaticInitializationElement = StaticInitializationElement<
@@ -31,8 +31,8 @@ export interface StructureInitialization {
content?: StructureDynamicInitializationElement; content?: StructureDynamicInitializationElement;
} }
export type StructureInitializationStrategy = { export type DefaultStructureInitialization = {
[K in keyof Omit<StructureInitialization, 'target'>]: InitializtationElementStrategy< [K in keyof Omit<StructureInitialization, 'target'>]: DefaultInitializtationElement<
StructureInitialization[K] StructureInitialization[K]
>; >;
}; };
@@ -9,7 +9,7 @@ import type { TRBL, XY, EventListener } from 'support';
import type { Options, ReadonlyOSOptions } from 'options'; import type { Options, ReadonlyOSOptions } from 'options';
import type { Setup } from 'setups'; import type { Setup } from 'setups';
import type { InitializationTarget } from 'initialization'; import type { InitializationTarget } from 'initialization';
import type { PartialOptions, StyleObject, OverflowStyle } from 'typings'; import type { DeepPartial, StyleObject, OverflowStyle } from 'typings';
export interface StructureSetupState { export interface StructureSetupState {
_padding: TRBL; _padding: TRBL;
@@ -30,11 +30,7 @@ export interface StructureSetupStaticState {
} }
type StructureSetupEventMap = { type StructureSetupEventMap = {
u: [ u: [updateHints: StructureSetupUpdateHints, changedOptions: DeepPartial<Options>, force: boolean];
updateHints: StructureSetupUpdateHints,
changedOptions: PartialOptions<Options>,
force: boolean
];
}; };
const initialXYNumber = { x: 0, y: 0 }; const initialXYNumber = { x: 0, y: 0 };
@@ -57,13 +57,9 @@ export const createStructureSetupUpdate = (
state: SetupState<StructureSetupState> state: SetupState<StructureSetupState>
): StructureSetupUpdate => { ): StructureSetupUpdate => {
const { _viewport } = structureSetupElements; const { _viewport } = structureSetupElements;
const { const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _flexboxGlue } = getEnvironment();
_nativeScrollbarsHiding: _nativeScrollbarStyling,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_flexboxGlue,
} = getEnvironment();
const doViewportArrange = const doViewportArrange =
!_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y); !_nativeScrollbarsHiding && (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y);
const updateSegments: StructureSetupUpdateSegment[] = [ const updateSegments: StructureSetupUpdateSegment[] = [
createTrinsicUpdateSegment(structureSetupElements, state), createTrinsicUpdateSegment(structureSetupElements, state),
@@ -16,12 +16,12 @@ import {
} from 'support'; } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { import {
classNameViewportScrollbarStyling, classNameViewportScrollbarHidden,
classNameOverflowVisible, classNameOverflowVisible,
dataAttributeHost, dataAttributeHost,
dataAttributeHostOverflowX, dataAttributeHostOverflowX,
dataAttributeHostOverflowY, dataAttributeHostOverflowY,
dataValueHostViewportScrollbarStyling, dataValueHostScrollbarHidden,
dataValueHostOverflowVisible, dataValueHostOverflowVisible,
} from 'classnames'; } from 'classnames';
import { getPlugins, scrollbarsHidingPluginName } from 'plugins'; import { getPlugins, scrollbarsHidingPluginName } from 'plugins';
@@ -328,7 +328,7 @@ export const createOverflowUpdateSegment: CreateStructureUpdateSegment = (
} = updateHints; } = updateHints;
const { _heightIntrinsic, _directionIsRTL } = getState(); const { _heightIntrinsic, _directionIsRTL } = getState();
const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] = const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] =
checkOption<boolean>('nativeScrollbarsOverlaid.show'); checkOption<boolean>('showNativeOverlaidScrollbars');
const [overflow, overflowChanged] = checkOption<XY<OverflowBehavior>>('overflow'); const [overflow, overflowChanged] = checkOption<XY<OverflowBehavior>>('overflow');
const showNativeOverlaidScrollbars = const showNativeOverlaidScrollbars =
@@ -356,8 +356,8 @@ export const createOverflowUpdateSegment: CreateStructureUpdateSegment = (
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarsHiding) { if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarsHiding) {
_viewportAddRemoveClass( _viewportAddRemoveClass(
classNameViewportScrollbarStyling, classNameViewportScrollbarHidden,
dataValueHostViewportScrollbarStyling, dataValueHostScrollbarHidden,
!showNativeOverlaidScrollbars !showNativeOverlaidScrollbars
); );
} }
@@ -53,17 +53,20 @@
.os-viewport { .os-viewport {
-ms-overflow-style: scrollbar !important; -ms-overflow-style: scrollbar !important;
} }
[data-overlayscrollbars~='viewportStyled'], [data-overlayscrollbars~='scrollbarHidden'],
.os-viewport-scrollbar-styled.os-environment, html.os-viewport-scrollbar-hidden,
.os-viewport-scrollbar-styled.os-viewport { .os-viewport-scrollbar-hidden.os-environment,
.os-viewport-scrollbar-hidden.os-viewport {
scrollbar-width: none !important; scrollbar-width: none !important;
} }
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar, [data-overlayscrollbars~='scrollbarHidden']::-webkit-scrollbar,
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar-corner, [data-overlayscrollbars~='scrollbarHidden']::-webkit-scrollbar-corner,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar, html.os-viewport-scrollbar-hidden::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar, html.os-viewport-scrollbar-hidden::-webkit-scrollbar-corner,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar-corner, .os-viewport-scrollbar-hidden.os-environment::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar-corner { .os-viewport-scrollbar-hidden.os-environment::-webkit-scrollbar-corner,
.os-viewport-scrollbar-hidden.os-viewport::-webkit-scrollbar,
.os-viewport-scrollbar-hidden.os-viewport::-webkit-scrollbar-corner {
display: none !important; display: none !important;
width: 0px !important; width: 0px !important;
height: 0px !important; height: 0px !important;
+2 -381
View File
@@ -1,5 +1,5 @@
export type PartialOptions<T> = { export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Record<string, unknown> ? PartialOptions<T[P]> : T[P]; [P in keyof T]?: T[P] extends Record<string, unknown> ? DeepPartial<T[P]> : T[P];
}; };
export type ReadonlyOptions<T> = { export type ReadonlyOptions<T> = {
@@ -15,382 +15,3 @@ export type StyleObject<CustomCssProps = ''> = {
}; };
export type OverflowStyle = 'scroll' | 'hidden' | 'visible'; export type OverflowStyle = 'scroll' | 'hidden' | 'visible';
/*
export namespace OverlayScrollbars {
export type ResizeBehavior = 'none' | 'both' | 'horizontal' | 'vertical';
export type OverflowBehavior = 'hidden' | 'scroll' | 'visible-hidden' | 'visible-scroll';
export type VisibilityBehavior = 'visible' | 'hidden' | 'auto';
export type AutoHideBehavior = 'never' | 'scroll' | 'leave' | 'move';
export type ScrollBehavior = 'always' | 'ifneeded' | 'never';
export type BlockBehavior = 'begin' | 'end' | 'center' | 'nearest';
export type Easing = string | null | undefined;
export type Margin = number | boolean;
export type Position = number | string;
export type Extensions = string | ReadonlyArray<string> | { [extensionName: string]: {} };
export type BasicEventCallback = (this: OverlayScrollbars) => void;
export type ScrollEventCallback = (this: OverlayScrollbars, args?: UIEvent) => void;
export type OverflowChangedCallback = (this: OverlayScrollbars, args?: OverflowChangedArgs) => void;
export type OverflowAmountChangedCallback = (this: OverlayScrollbars, args?: OverflowAmountChangedArgs) => void;
export type DirectionChangedCallback = (this: OverlayScrollbars, args?: DirectionChangedArgs) => void;
export type SizeChangedCallback = (this: OverlayScrollbars, args?: SizeChangedArgs) => void;
export type UpdatedCallback = (this: OverlayScrollbars, args?: UpdatedArgs) => void;
export type Coordinates =
| { x?: Position; y?: Position }
| { l?: Position; t?: Position }
| { left?: Position; top?: Position }
| [Position, Position]
| Position
| HTMLElement
| {
el: HTMLElement;
scroll?: ScrollBehavior | { x?: ScrollBehavior; y?: ScrollBehavior } | [ScrollBehavior, ScrollBehavior];
block?: BlockBehavior | { x?: BlockBehavior; y?: BlockBehavior } | [BlockBehavior, BlockBehavior];
margin?:
| Margin
| {
top?: Margin;
right?: Margin;
bottom?: Margin;
left?: Margin;
}
| [Margin, Margin]
| [Margin, Margin, Margin, Margin];
};
export interface OverflowChangedArgs {
x: boolean;
y: boolean;
xScrollable: boolean;
yScrollable: boolean;
clipped: boolean;
}
export interface OverflowAmountChangedArgs {
x: number;
y: number;
}
export interface DirectionChangedArgs {
isRTL: number;
dir: string;
}
export interface SizeChangedArgs {
width: number;
height: number;
}
export interface UpdatedArgs {
forced: boolean;
}
export interface Options {
className?: string | null;
resize?: ResizeBehavior;
sizeAutoCapable?: boolean;
clipAlways?: boolean;
normalizeRTL?: boolean;
paddingAbsolute?: boolean;
autoUpdate?: boolean | null;
autoUpdateInterval?: number;
updateOnLoad?: string | ReadonlyArray<string> | null;
nativeScrollbarsOverlaid?: {
showNativeScrollbars?: boolean;
initialize?: boolean;
};
overflowBehavior?: {
x?: OverflowBehavior;
y?: OverflowBehavior;
};
scrollbars?: {
visibility?: VisibilityBehavior;
autoHide?: AutoHideBehavior;
autoHideDelay?: number;
dragScrolling?: boolean;
clickScrolling?: boolean;
touchSupport?: boolean;
snapHandle?: boolean;
};
textarea?: {
dynWidth?: boolean;
dynHeight?: boolean;
inheritedAttrs?: string | ReadonlyArray<string> | null;
};
callbacks?: {
onInitialized?: BasicEventCallback | null;
onInitializationWithdrawn?: BasicEventCallback | null;
onDestroyed?: BasicEventCallback | null;
onScrollStart?: ScrollEventCallback | null;
onScroll?: ScrollEventCallback | null;
onScrollStop?: ScrollEventCallback | null;
onOverflowChanged?: OverflowChangedCallback | null;
onOverflowAmountChanged?: OverflowAmountChangedCallback | null;
onDirectionChanged?: DirectionChangedCallback | null;
onContentSizeChanged?: SizeChangedCallback | null;
onHostSizeChanged?: SizeChangedCallback | null;
onUpdated?: UpdatedCallback | null;
};
}
export interface ScrollInfo {
position: {
x: number;
y: number;
};
ratio: {
x: number;
y: number;
};
max: {
x: number;
y: number;
};
handleOffset: {
x: number;
y: number;
};
handleLength: {
x: number;
y: number;
};
handleLengthRatio: {
x: number;
y: number;
};
trackLength: {
x: number;
y: number;
};
snappedHandleOffset: {
x: number;
y: number;
};
isRTL: boolean;
isRTLNormalized: boolean;
}
export interface Elements {
target: HTMLElement;
host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement;
content: HTMLElement;
scrollbarHorizontal: {
scrollbar: HTMLElement;
track: HTMLElement;
handle: HTMLElement;
};
scrollbarVertical: {
scrollbar: HTMLElement;
track: HTMLElement;
handle: HTMLElement;
};
scrollbarCorner: HTMLElement;
}
export interface State {
destroyed: boolean;
sleeping: boolean;
autoUpdate: boolean;
widthAuto: boolean;
heightAuto: boolean;
documentMixed: boolean;
padding: {
t: number;
r: number;
b: number;
l: number;
};
overflowAmount: {
x: number;
y: number;
};
hideOverflow: {
x: boolean;
y: boolean;
xs: boolean;
ys: boolean;
};
hasOverflow: {
x: boolean;
y: boolean;
};
contentScrollSize: {
width: number;
height: number;
};
viewportSize: {
width: number;
height: number;
};
hostSize: {
width: number;
height: number;
};
}
export interface Extension {
contract(global: any): boolean;
added(options?: {}): void;
removed(): void;
on(
callbackName: string,
callbackArgs?: UIEvent | OverflowChangedArgs | OverflowAmountChangedArgs | DirectionChangedArgs | SizeChangedArgs | UpdatedArgs,
): void;
}
export interface ExtensionInfo {
name: string;
extensionFactory: (this: OverlayScrollbars, defaultOptions: {}, compatibility: Compatibility, framework: any) => Extension;
defaultOptions?: {};
}
export interface Globals {
defaultOptions: {};
autoUpdateLoop: boolean;
autoUpdateRecommended: boolean;
supportMutationObserver: boolean;
supportResizeObserver: boolean;
supportPassiveEvents: boolean;
supportTransform: boolean;
supportTransition: boolean;
restrictedMeasuring: boolean;
nativeScrollbarStyling: boolean;
cssCalc: string | null;
nativeScrollbarSize: {
x: number;
y: number;
};
nativeScrollbarIsOverlaid: {
x: boolean;
y: boolean;
};
overlayScrollbarDummySize: {
x: number;
y: number;
};
rtlScrollBehavior: {
i: boolean;
n: boolean;
};
}
export interface Compatibility {
wW(): number;
wH(): number;
mO(): any;
rO(): any;
rAF(): (callback: (...args: any[]) => any) => number;
cAF(): (requestID: number) => void;
now(): number;
stpP(event: Event): void;
prvD(event: Event): void;
page(event: MouseEvent): { x: number; y: number };
mBtn(event: MouseEvent): number;
inA<T>(item: T, array: T[]): number;
isA(obj: any): boolean;
type(obj: any): string;
bind(func: (...args: any[]) => any, thisObj: any, ...args: any[]): any;
}
}
interface OverlayScrollbars {
options(): OverlayScrollbars.Options;
options(options: OverlayScrollbars.Options): void;
options(optionName: string): any;
options(optionName: string, optionValue: {} | null): void;
update(force?: boolean): void;
sleep(): void;
scroll(): OverlayScrollbars.ScrollInfo;
scroll(
coordinates: OverlayScrollbars.Coordinates,
duration?: number,
easing?:
| OverlayScrollbars.Easing
| { x?: OverlayScrollbars.Easing; y?: OverlayScrollbars.Easing }
| [OverlayScrollbars.Easing, OverlayScrollbars.Easing],
complete?: (...args: any[]) => any,
): void;
scroll(coordinates: OverlayScrollbars.Coordinates, options: {}): void;
scrollStop(): OverlayScrollbars;
getElements(): OverlayScrollbars.Elements;
getElements(elementName: string): any;
getState(): OverlayScrollbars.State;
getState(stateProperty: string): any;
destroy(): void;
ext(): {};
ext(extensionName: string): OverlayScrollbars.Extension;
addExt(extensionName: string, options: {}): OverlayScrollbars.Extension;
removeExt(extensionName: string): boolean;
}
interface OverlayScrollbarsStatic {
(element: HTMLElement | Element, options: OverlayScrollbars.Options, extensions?: OverlayScrollbars.Extensions): OverlayScrollbars;
(element: HTMLElement | Element | null): OverlayScrollbars | undefined;
(elements: NodeListOf<Element> | ReadonlyArray<Element>, options: OverlayScrollbars.Options, extensions?: OverlayScrollbars.Extensions):
| OverlayScrollbars
| OverlayScrollbars[]
| undefined;
(elements: NodeListOf<Element> | ReadonlyArray<Element>, filter?: string | ((element: Element, instance: OverlayScrollbars) => boolean)):
| OverlayScrollbars
| OverlayScrollbars[]
| undefined;
globals(): OverlayScrollbars.Globals;
defaultOptions(): OverlayScrollbars.Options;
defaultOptions(newDefaultOptions: OverlayScrollbars.Options): void;
extension(): {
[index: number]: OverlayScrollbars.ExtensionInfo;
length: number;
};
extension(extensionName: string): OverlayScrollbars.ExtensionInfo;
extension(
extensionName: string,
extensionFactory: (
this: OverlayScrollbars,
defaultOptions: {},
compatibility: OverlayScrollbars.Compatibility,
framework: any,
) => OverlayScrollbars.Extension,
defaultOptions?: {},
): void;
extension(extensionName: string, extensionFactory: null | undefined): void;
valid(osInstance: any): boolean;
}
*/
@@ -5,7 +5,7 @@ import {
createStructureSetupElements, createStructureSetupElements,
StructureSetupElementsObj, StructureSetupElementsObj,
} from 'setups/structureSetup/structureSetup.elements'; } from 'setups/structureSetup/structureSetup.elements';
import type { InitializationTarget, InitializtationElementStrategy } from 'initialization'; import type { InitializationTarget, DefaultInitializtationElement } from 'initialization';
import type { import type {
StructureInitialization, StructureInitialization,
StructureStaticInitializationElement, StructureStaticInitializationElement,
@@ -208,8 +208,8 @@ const assertCorrectSetupElements = (
inputStrategy: StructureStaticInitializationElement | StructureDynamicInitializationElement, inputStrategy: StructureStaticInitializationElement | StructureDynamicInitializationElement,
isStaticStrategy: boolean, isStaticStrategy: boolean,
strategy: strategy:
| InitializtationElementStrategy<StructureStaticInitializationElement> | DefaultInitializtationElement<StructureStaticInitializationElement>
| InitializtationElementStrategy<StructureDynamicInitializationElement>, | DefaultInitializtationElement<StructureDynamicInitializationElement>,
kind: 'padding' | 'viewport' | 'content' | 'host' kind: 'padding' | 'viewport' | 'content' | 'host'
) => { ) => {
const input = isFunction(inputStrategy) ? inputStrategy(target) : inputStrategy; const input = isFunction(inputStrategy) ? inputStrategy(target) : inputStrategy;
@@ -224,7 +224,7 @@ const assertCorrectSetupElements = (
if (input === undefined) { if (input === undefined) {
if (isStaticStrategy) { if (isStaticStrategy) {
strategy = strategy =
strategy as InitializtationElementStrategy<StructureStaticInitializationElement>; strategy as DefaultInitializtationElement<StructureStaticInitializationElement>;
if (typeof strategy === 'function') { if (typeof strategy === 'function') {
const result = strategy(target); const result = strategy(target);
if (_viewportIsTarget) { if (_viewportIsTarget) {
@@ -243,7 +243,7 @@ const assertCorrectSetupElements = (
} }
} else { } else {
strategy = strategy =
strategy as InitializtationElementStrategy<StructureDynamicInitializationElement>; strategy as DefaultInitializtationElement<StructureDynamicInitializationElement>;
if (typeof strategy === 'function') { if (typeof strategy === 'function') {
const result = strategy(target); const result = strategy(target);
@@ -2,67 +2,10 @@ import './index.scss';
import 'index.scss'; import 'index.scss';
import { OverlayScrollbars } from 'overlayscrollbars'; import { OverlayScrollbars } from 'overlayscrollbars';
OverlayScrollbars.env().setDefaultOptions({ // test with different cancel values for body
nativeScrollbarsOverlaid: { initialize: true },
OverlayScrollbars.env().setDefaultInitialization({
cancel: { nativeScrollbarsOverlaid: false },
}); });
// @ts-ignore
OverlayScrollbars.env().setInitializationStrategy({ viewport: (target) => target }); window.os = OverlayScrollbars({ target: document.body, cancel: { body: null } }, {});
window.os = OverlayScrollbars(document.body, {});
// test viewport inheritted attrs (tabindex) for multiple and single element init
// test appear & resize for multiple and single element init
// test children changing attr according to dom observer
/*
import { OverlayScrollbars } from 'overlayscrollbars';
import should from 'should';
import { resize } from '@/testing-browser/Resize';
import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { addClass, each, isArray, removeAttr, style } from 'support';
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
});
/*
const startBtn: HTMLButtonElement | null = document.querySelector('#start');
const target: HTMLElement | null = document.querySelector('#target');
const updatesSlot: HTMLElement | null = document.querySelector('#update');
let updateCount = 0;
const osInstance = OverlayScrollbars(
{ target: target! },
{
updating: {
ignoreMutation(mutation) {
console.log(mutation);
},
},
},
{
updated() {
updateCount++;
requestAnimationFrame(() => {
if (updatesSlot) {
updatesSlot.textContent = `${updateCount}`;
}
});
},
}
);
const start = async () => {
setTestResult(null);
setTestResult(true);
};
startBtn?.addEventListener('click', start);
*/
@@ -18,8 +18,8 @@ if (!OverlayScrollbars.env().scrollbarsHiding) {
// @ts-ignore // @ts-ignore
window.OverlayScrollbars = OverlayScrollbars; window.OverlayScrollbars = OverlayScrollbars;
OverlayScrollbars.env().setDefaultOptions({ OverlayScrollbars.env().setDefaultInitialization({
nativeScrollbarsOverlaid: { initialize: true }, cancel: { nativeScrollbarsOverlaid: false },
}); });
const startBtn: HTMLButtonElement | null = document.querySelector('#start'); const startBtn: HTMLButtonElement | null = document.querySelector('#start');
@@ -14,8 +14,8 @@ import { timeout } from '@/testing-browser/timeout';
import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult'; import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { addClass, each, isArray, removeAttr, style } from 'support'; import { addClass, each, isArray, removeAttr, style } from 'support';
OverlayScrollbars.env().setDefaultOptions({ OverlayScrollbars.env().setInitializationStrategy({
nativeScrollbarsOverlaid: { initialize: true }, cancel: { nativeScrollbarsOverlaid: false },
}); });
@@ -23,7 +23,7 @@ import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select'; import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout'; import { timeout } from '@/testing-browser/timeout';
import { Options } from 'options'; import { Options } from 'options';
import { PartialOptions } from 'typings'; import { DeepPartial } from 'typings';
import { addPlugin, scrollbarsHidingPlugin, sizeObserverPlugin } from 'plugins'; import { addPlugin, scrollbarsHidingPlugin, sizeObserverPlugin } from 'plugins';
if (!window.ResizeObserver) { if (!window.ResizeObserver) {
@@ -36,8 +36,8 @@ if (!OverlayScrollbars.env().scrollbarsHiding) {
// @ts-ignore // @ts-ignore
window.OverlayScrollbars = OverlayScrollbars; window.OverlayScrollbars = OverlayScrollbars;
OverlayScrollbars.env().setDefaultOptions({ OverlayScrollbars.env().setDefaultInitialization({
nativeScrollbarsOverlaid: { initialize: true }, cancel: { nativeScrollbarsOverlaid: false },
}); });
interface Metrics { interface Metrics {
@@ -587,7 +587,7 @@ const iterateMinMax = async (afterEach?: () => any) => {
await iterate(containerMinMaxSelect, afterEach); await iterate(containerMinMaxSelect, afterEach);
}; };
const overflowTest = async (osOptions?: PartialOptions<Options>) => { const overflowTest = async (osOptions?: DeepPartial<Options>) => {
const additiveOverflow = () => { const additiveOverflow = () => {
if (isFractionalPixelRatio()) { if (isFractionalPixelRatio()) {
return 1; return 1;
-297
View File
@@ -1,297 +0,0 @@
type CacheValues<T> = [
value: T,
changed: boolean,
previous?: T
];
type UpdateCache<Value> = (force?: boolean) => CacheValues<Value>;
interface WH<T> {
w: T;
h: T;
}
type PartialOptions<T> = {
[P in keyof T]?: T[P] extends Record<string, unknown> ? PartialOptions<T[P]> : T[P];
};
type StyleObject<CustomCssProps> = {
[Key in keyof CSSStyleDeclaration | (CustomCssProps extends string ? CustomCssProps : "")]?: string | number;
};
type OverflowStyle = "scroll" | "hidden" | "visible";
interface TRBL {
t: number;
r: number;
b: number;
l: number;
}
interface XY<T> {
x: T;
y: T;
}
type EventListener<EventMap extends Record<string, any[]>, Name extends keyof EventMap> = (...args: EventMap[Name]) => void;
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 ScrollbarVisibilityBehavior = "visible" | "hidden" | "auto";
type ScrollbarAutoHideBehavior = "never" | "scroll" | "leave" | "move";
interface Options {
paddingAbsolute: boolean;
updating: {
elementEvents: Array<[
elementSelector: string,
eventNames: string
]> | null;
attributes: string[] | null;
debounce: [
timeout: number,
maxWait: number
] | number | null; // (if tuple: [timeout: 0, maxWait: 33], if number: [timeout: number, maxWait: false]) debounce for content Changes
ignoreMutation: ((mutation: MutationRecord) => any) | null;
};
overflow: {
x: OverflowBehavior;
y: OverflowBehavior;
};
scrollbars: {
theme: string | null;
visibility: ScrollbarVisibilityBehavior;
autoHide: ScrollbarAutoHideBehavior;
autoHideDelay: number;
dragScroll: boolean;
clickScroll: boolean;
touch: boolean;
};
nativeScrollbarsOverlaid: {
show: boolean;
initialize: boolean;
};
}
type PluginInstance = Record<string, unknown> | ((staticObj: OverlayScrollbarsStatic, instanceObj: OverlayScrollbars) => void);
type Plugin<T extends PluginInstance> = {
[pluginName: string]: T;
};
type OptionsValidationPluginInstance = {
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => PartialOptions<Options>;
};
declare const optionsValidationPlugin: Plugin<OptionsValidationPluginInstance>;
type SizeObserverPluginInstance = {
_: (listenerElement: HTMLElement, onSizeChangedCallback: (appear: boolean) => any, observeAppearChange: boolean) => [
appearCallback: () => any,
offFns: (() => any)[]
];
};
declare const sizeObserverPlugin: Plugin<SizeObserverPluginInstance>;
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 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]: InitializtationElementStrategy<ScrollbarsInitialization[K]>;
};
interface StructureSetupState {
_padding: TRBL;
_paddingAbsolute: boolean;
_viewportPaddingStyle: StyleObject;
_overflowEdge: XY<number>;
_overflowAmount: XY<number>;
_overflowStyle: XY<OverflowStyle>;
_hasOverflow: XY<boolean>;
_heightIntrinsic: boolean;
_directionIsRTL: boolean;
}
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">]: InitializtationElementStrategy<StructureInitialization[K]>;
};
interface ViewportOverflowState {
_scrollbarsHideOffset: XY<number>;
_scrollbarsHideOffsetArrange: XY<boolean>;
_overflowScroll: XY<boolean>;
_overflowStyle: XY<OverflowStyle>;
}
type GetViewportOverflowState = (showNativeOverlaidScrollbars: boolean, viewportStyleObj?: StyleObject) => ViewportOverflowState;
type HideNativeScrollbars = (viewportOverflowState: ViewportOverflowState, directionIsRTL: boolean, viewportArrange: boolean, viewportStyleObj: StyleObject) => void;
type EnvironmentEventMap = {
_: [
];
};
interface InternalEnvironment {
readonly _nativeScrollbarsSize: XY;
readonly _nativeScrollbarsOverlaid: XY<boolean>;
readonly _nativeScrollbarsHiding: boolean;
readonly _rtlScrollBehavior: {
n: boolean;
i: boolean;
};
readonly _flexboxGlue: boolean;
readonly _cssCustomProperties: boolean;
readonly _defaultInitializationStrategy: InitializationStrategy;
readonly _defaultDefaultOptions: Options;
_addListener(listener: EventListener<EnvironmentEventMap, "_">): () => void;
_getInitializationStrategy(): InitializationStrategy;
_setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
_getDefaultOptions(): Options;
_setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
}
type ArrangeViewport = (viewportOverflowState: ViewportOverflowState, viewportScrollSize: WH<number>, sizeFraction: WH<number>, directionIsRTL: boolean) => boolean;
type UndoViewportArrangeResult = [
redoViewportArrange: () => void,
overflowState?: ViewportOverflowState
];
type UndoArrangeViewport = (showNativeOverlaidScrollbars: boolean, directionIsRTL: boolean, viewportOverflowState?: ViewportOverflowState) => UndoViewportArrangeResult;
type ScrollbarsHidingPluginInstance = {
_createUniqueViewportArrangeElement(env: InternalEnvironment): HTMLStyleElement | false;
_overflowUpdateSegment(doViewportArrange: boolean, flexboxGlue: boolean, viewport: HTMLElement, viewportArrange: HTMLStyleElement | false | null | undefined, getState: () => StructureSetupState, getViewportOverflowState: GetViewportOverflowState, hideNativeScrollbars: HideNativeScrollbars): [
ArrangeViewport,
UndoArrangeViewport
];
_envWindowZoom(): (envInstance: InternalEnvironment, updateNativeScrollbarSizeCache: UpdateCache<XY<number>>, triggerEvent: () => void) => void;
};
declare const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance>;
type GeneralInitialEventListeners = InitialEventListeners;
type GeneralEventListener = EventListener;
interface OverlayScrollbarsStatic {
(target: InitializationTarget | InitializationTargetObject, options?: PartialOptions<Options>, eventListeners?: GeneralInitialEventListeners<EventListenerMap>): OverlayScrollbars;
plugin(plugin: Plugin | Plugin[]): void;
env(): Environment;
}
interface Environment {
scrollbarsSize: XY<number>;
scrollbarsOverlaid: XY<boolean>;
scrollbarsHiding: 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;
overflowEdge: XY<number>;
overflowAmount: XY<number>;
overflowStyle: XY<OverflowStyle>;
hasOverflow: XY<boolean>;
destroyed: boolean;
}
interface Elements {
target: HTMLElement;
host: HTMLElement;
padding: HTMLElement;
viewport: HTMLElement;
content: HTMLElement;
}
interface OnUpdatedEventListenerArgs {
updateHints: {
sizeChanged: boolean;
directionChanged: boolean;
heightIntrinsicChanged: boolean;
overflowEdgeChanged: boolean;
overflowAmountChanged: boolean;
overflowStyleChanged: boolean;
hostMutation: boolean;
contentMutation: boolean;
};
changedOptions: PartialOptions<Options>;
force: boolean;
}
type EventListenerMap = {
/**
* Triggered after all elements are initialized and appended.
*/
initialized: [
instance: OverlayScrollbars
];
/**
* Triggered after an update.
*/
updated: [
instance: OverlayScrollbars,
onUpdatedArgs: OnUpdatedEventListenerArgs
];
/**
* Triggered after all elements, observers and events are destroyed.
*/
destroyed: [
instance: OverlayScrollbars,
withdrawn: boolean
];
};
type EventListener$0<Name extends keyof EventListenerMap> = GeneralEventListener<EventListenerMap, Name>;
interface OverlayScrollbars {
options(): Options;
options(newOptions?: PartialOptions<Options>): Options;
update(force?: boolean): OverlayScrollbars;
destroy(): void;
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:
* Height intrinsic detection use "content: true" init strategy - or open ticket for custom height intrinsic observer
*/
declare const OverlayScrollbars: OverlayScrollbarsStatic;
export { OverlayScrollbars, optionsValidationPlugin, scrollbarsHidingPlugin, sizeObserverPlugin };