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 dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`;
export const dataValueHostOverflowVisible = 'overflowVisible';
export const dataValueHostViewportScrollbarStyling = 'viewportStyled';
export const dataValueHostScrollbarHidden = 'scrollbarHidden';
export const classNamePadding = 'os-padding';
export const classNameViewport = 'os-viewport';
export const classNameViewportArrange = `${classNameViewport}-arrange`;
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 classNameSizeObserver = 'os-size-observer';
+21 -17
View File
@@ -24,11 +24,11 @@ import {
classNameEnvironment,
classNameEnvironmentFlexboxGlue,
classNameEnvironmentFlexboxGlueMax,
classNameViewportScrollbarStyling,
classNameViewportScrollbarHidden,
} from 'classnames';
import { Options, defaultOptions } from 'options';
import { PartialOptions } from 'typings';
import { InitializationStrategy } from 'initialization';
import { DeepPartial } from 'typings';
import { DefaultInitialization } from 'initialization';
import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins';
type EnvironmentEventMap = {
@@ -42,13 +42,13 @@ export interface InternalEnvironment {
readonly _rtlScrollBehavior: { n: boolean; i: boolean };
readonly _flexboxGlue: boolean;
readonly _cssCustomProperties: boolean;
readonly _defaultInitializationStrategy: InitializationStrategy;
readonly _defaultDefaultOptions: Options;
readonly _staticDefaultInitialization: DefaultInitialization;
readonly _staticDefaultOptions: Options;
_addListener(listener: EventListener<EnvironmentEventMap, '_'>): () => void;
_getInitializationStrategy(): InitializationStrategy;
_setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
_getDefaultInitialization(): DefaultInitialization;
_setDefaultInitialization(newInitialization: DeepPartial<DefaultInitialization>): void;
_getDefaultOptions(): Options;
_setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
_setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
}
let environmentInstance: InternalEnvironment;
@@ -75,7 +75,7 @@ const getNativeScrollbarSize = (
const getNativeScrollbarsHiding = (testElm: HTMLElement): boolean => {
let result = false;
const revertClass = addClass(testElm, classNameViewportScrollbarStyling);
const revertClass = addClass(testElm, classNameViewportScrollbarHidden);
try {
result =
style(testElm, cssProperty('scrollbar-width')) === 'none' ||
@@ -152,9 +152,13 @@ const createEnvironment = (): InternalEnvironment => {
x: nativeScrollbarsSize.x === 0,
y: nativeScrollbarsSize.y === 0,
};
const initializationStrategy = {
const defaultInitialization = {
padding: !nativeScrollbarsHiding,
content: false,
cancel: {
nativeScrollbarsOverlaid: true,
body: null,
},
};
const defaultDefaultOptions = assignDeep({}, defaultOptions);
@@ -166,20 +170,20 @@ const createEnvironment = (): InternalEnvironment => {
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener: (listener) => addEvent('_', listener),
_getInitializationStrategy: assignDeep<InitializationStrategy, InitializationStrategy>.bind(
_getDefaultInitialization: assignDeep<DefaultInitialization, DefaultInitialization>.bind(
0,
{} as InitializationStrategy,
initializationStrategy
{} as DefaultInitialization,
defaultInitialization
),
_setInitializationStrategy(newInitializationStrategy) {
assignDeep(initializationStrategy, newInitializationStrategy);
_setDefaultInitialization(newInitializationStrategy) {
assignDeep(defaultInitialization, newInitializationStrategy);
},
_getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, defaultDefaultOptions),
_setDefaultOptions(newDefaultOptions) {
assignDeep(defaultDefaultOptions, newDefaultOptions);
},
_defaultInitializationStrategy: assignDeep({}, initializationStrategy),
_defaultDefaultOptions: assignDeep({}, defaultDefaultOptions),
_staticDefaultInitialization: assignDeep({}, defaultInitialization),
_staticDefaultOptions: assignDeep({}, defaultDefaultOptions),
};
removeAttr(envElm, 'style');
@@ -1,24 +1,37 @@
import { isFunction, isNull, isUndefined } from 'support';
import { isBoolean, isFunction, isNull, isUndefined } from 'support';
import type {
StructureInitialization,
StructureInitializationStrategy,
DefaultStructureInitialization,
} from 'setups/structureSetup';
import type {
ScrollbarsInitialization,
ScrollbarsInitializationStrategy,
DefaultScrollbarsInitialization,
} from 'setups/scrollbarsSetup';
import { getEnvironment } from 'environment';
import { DeepPartial } from 'typings';
import { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
type StaticInitialization = HTMLElement | 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 InitializationTargetObject = StructureInitialization & ScrollbarsInitialization;
export type InitializationTargetObject = StructureInitialization &
ScrollbarsInitialization &
DeepPartial<CancelInitialization>;
export type InitializationTarget = InitializationTargetElement | InitializationTargetObject;
export type InitializationStrategy = StructureInitializationStrategy &
ScrollbarsInitializationStrategy;
export type DefaultInitialization = DefaultStructureInitialization &
DefaultScrollbarsInitialization &
CancelInitialization;
/**
* Static elements MUST be present.
@@ -39,9 +52,9 @@ export type DynamicInitializationElement<Args extends any[]> =
| ((...args: Args) => 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>
> = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any
? (...args: P) => HTMLElement
@@ -52,20 +65,20 @@ const resolveInitialization = <T>(value: any, args: any): T =>
const staticInitializationElement = <T extends StaticInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>,
defaultStaticInitializationElement: DefaultInitializtationElementStrategy<T>,
staticInitializationElementStrategy?: InitializtationElementStrategy<T>,
fallbackStaticInitializationElement: FallbackInitializtationElement<T>,
defaultStaticInitializationElementStrategy?: DefaultInitializtationElement<T>,
staticInitializationElementValue?: T | false
): HTMLElement =>
resolveInitialization<StaticInitialization>(
staticInitializationElementValue ||
resolveInitialization<StaticInitialization>(staticInitializationElementStrategy, args),
resolveInitialization<StaticInitialization>(defaultStaticInitializationElementStrategy, args),
args
) || defaultStaticInitializationElement.apply(0, args);
) || fallbackStaticInitializationElement.apply(0, args);
const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>,
defaultDynamicInitializationElement: DefaultInitializtationElementStrategy<T>,
dynamicInitializationElementStrategy?: InitializtationElementStrategy<T>,
fallbackDynamicInitializationElement: FallbackInitializtationElement<T>,
defaultDynamicInitializationElementStrategy?: DefaultInitializtationElement<T>,
dynamicInitializationElementValue?: T | false
): HTMLElement | false => {
let result = resolveInitialization<DynamicInitialization>(
@@ -75,14 +88,40 @@ const dynamicInitializationElement = <T extends DynamicInitializationElement<any
if (isNull(result) || isUndefined(result)) {
result = resolveInitialization<DynamicInitialization>(
dynamicInitializationElementStrategy,
defaultDynamicInitializationElementStrategy,
args
);
}
return result === true || isNull(result) || isUndefined(result)
? defaultDynamicInitializationElement.apply(0, args)
? fallbackDynamicInitializationElement.apply(0, args)
: 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 { PartialOptions, ReadonlyOptions } from 'typings';
import { DeepPartial, ReadonlyOptions } from 'typings';
const opsStringify = (value: any) =>
JSON.stringify(value, (_, val) => {
@@ -40,6 +40,7 @@ export type UpdatedCallback = (this: any, args?: UpdatedArgs) => void;
export interface Options {
paddingAbsolute: boolean;
showNativeOverlaidScrollbars: boolean;
updating: {
elementEvents: Array<[elementSelector: string, eventNames: string]> | null;
attributes: string[] | null;
@@ -59,10 +60,6 @@ export interface Options {
clickScroll: boolean;
touch: boolean;
};
nativeScrollbarsOverlaid: {
show: boolean;
initialize: boolean;
};
}
export type ReadonlyOSOptions = ReadonlyOptions<Options>;
@@ -97,6 +94,7 @@ export interface UpdatedArgs {
export const defaultOptions: Options = {
// resize: 'none', // none || both || horizontal || vertical || n || b || h || v
paddingAbsolute: false, // true || false
showNativeOverlaidScrollbars: false, // true || false
updating: {
elementEvents: [['img', 'load']], // array of tuples || 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
y: 'scroll', // visible-hidden || visible-scroll || hidden || scroll || v-h || v-s || h || s
},
nativeScrollbarsOverlaid: {
show: false, // true || false
initialize: false, // true || false
},
scrollbars: {
theme: 'os-theme-dark',
visibility: 'auto', // visible || hidden || auto || v || h || a
@@ -129,11 +123,8 @@ export const defaultOptions: Options = {
*/
};
export const getOptionsDiff = <T>(
currOptions: T,
newOptions: PartialOptions<T>
): PartialOptions<T> => {
const diff: PartialOptions<T> = {};
export const getOptionsDiff = <T>(currOptions: T, newOptions: DeepPartial<T>): DeepPartial<T> => {
const diff: DeepPartial<T> = {};
const optionsKeys = keys(newOptions).concat(keys(currOptions));
each(optionsKeys, (optionKey) => {
@@ -20,11 +20,12 @@ import {
OptionsValidationPluginInstance,
} from 'plugins';
import { addInstance, getInstance, removeInstance } from 'instances';
import type { PartialOptions, OverflowStyle } from 'typings';
import type {
import type { DeepPartial, OverflowStyle } from 'typings';
import {
InitializationTarget,
InitializationTargetObject,
InitializationStrategy,
DefaultInitialization,
cancelInitialization,
} from 'initialization';
import type {
InitialEventListeners as GeneralInitialEventListeners,
@@ -34,7 +35,7 @@ import type {
export interface OverlayScrollbarsStatic {
(
target: InitializationTarget | InitializationTargetObject,
options?: PartialOptions<Options>,
options?: DeepPartial<Options>,
eventListeners?: GeneralInitialEventListeners<EventListenerMap>
): OverlayScrollbars;
@@ -49,13 +50,13 @@ export interface Environment {
rtlScrollBehavior: { n: boolean; i: boolean };
flexboxGlue: boolean;
cssCustomProperties: boolean;
defaultInitializationStrategy: InitializationStrategy;
defaultDefaultOptions: Options;
staticDefaultInitialization: DefaultInitialization;
staticDefaultOptions: Options;
getInitializationStrategy(): InitializationStrategy;
setInitializationStrategy(newInitializationStrategy: Partial<InitializationStrategy>): void;
getDefaultInitialization(): DefaultInitialization;
setDefaultInitialization(newDefaultInitialization: DeepPartial<DefaultInitialization>): void;
getDefaultOptions(): Options;
setDefaultOptions(newDefaultOptions: PartialOptions<Options>): void;
setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
}
export interface State {
@@ -87,7 +88,7 @@ export interface OnUpdatedEventListenerArgs {
hostMutation: boolean;
contentMutation: boolean;
};
changedOptions: PartialOptions<Options>;
changedOptions: DeepPartial<Options>;
force: boolean;
}
@@ -103,7 +104,7 @@ export type EventListenerMap = {
/**
* Triggered after all elements, observers and events are destroyed.
*/
destroyed: [instance: OverlayScrollbars, withdrawn: boolean];
destroyed: [instance: OverlayScrollbars, canceled: boolean];
};
export type InitialEventListeners = GeneralInitialEventListeners<EventListenerMap>;
@@ -115,7 +116,7 @@ export type EventListener<Name extends keyof EventListenerMap> = GeneralEventLis
export interface OverlayScrollbars {
options(): Options;
options(newOptions?: PartialOptions<Options>): Options;
options(newOptions?: DeepPartial<Options>): Options;
update(force?: boolean): OverlayScrollbars;
@@ -143,13 +144,10 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
eventListeners?
): OverlayScrollbars => {
let destroyed = false;
const {
_getDefaultOptions,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_addListener: addEnvListener,
} = getEnvironment();
const { _getDefaultOptions, _addListener: addEnvListener } = getEnvironment();
const plugins = getPlugins();
const instanceTarget = isHTMLElement(target) ? target : target.target;
const targetIsElement = isHTMLElement(target);
const instanceTarget = targetIsElement ? target : target.target;
const potentialInstance = getInstance(instanceTarget);
if (potentialInstance) {
return potentialInstance;
@@ -158,7 +156,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
const optionsValidationPlugin = plugins[
optionsValidationPluginName
] as OptionsValidationPluginInstance;
const validateOptions = (newOptions?: PartialOptions<Options>) => {
const validateOptions = (newOptions?: DeepPartial<Options>) => {
const opts = newOptions || {};
const validate = optionsValidationPlugin && optionsValidationPlugin._;
return validate ? validate(opts, true) : opts;
@@ -178,11 +176,11 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
currentOptions,
structureState
);
const update = (changedOptions: PartialOptions<Options>, force?: boolean) => {
const update = (changedOptions: DeepPartial<Options>, force?: boolean) => {
updateStructure(changedOptions, !!force);
};
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
const destroy = (withdrawn?: boolean) => {
const destroy = (canceled?: boolean) => {
removeInstance(instanceTarget);
removeEnvListener();
@@ -192,12 +190,12 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
destroyed = true;
// eslint-disable-next-line no-use-before-define
triggerEvent('destroyed', [instance, !!withdrawn]);
triggerEvent('destroyed', [instance, !!canceled]);
removeEvent();
};
const instance: OverlayScrollbars = {
options(newOptions?: PartialOptions<Options>) {
options(newOptions?: DeepPartial<Options>) {
if (newOptions) {
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
@@ -265,11 +263,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
}
});
if (
_nativeScrollbarIsOverlaid.x &&
_nativeScrollbarIsOverlaid.y &&
!currentOptions.nativeScrollbarsOverlaid.initialize
) {
if (cancelInitialization(!targetIsElement && target.cancel, structureState._elements)) {
destroy(true);
return instance;
}
@@ -323,10 +317,10 @@ OverlayScrollbars.env = () => {
_rtlScrollBehavior,
_flexboxGlue,
_cssCustomProperties,
_defaultInitializationStrategy,
_defaultDefaultOptions,
_getInitializationStrategy,
_setInitializationStrategy,
_staticDefaultInitialization,
_staticDefaultOptions,
_getDefaultInitialization,
_setDefaultInitialization,
_getDefaultOptions,
_setDefaultOptions,
} = getEnvironment();
@@ -339,11 +333,11 @@ OverlayScrollbars.env = () => {
rtlScrollBehavior: _rtlScrollBehavior,
flexboxGlue: _flexboxGlue,
cssCustomProperties: _cssCustomProperties,
defaultInitializationStrategy: _defaultInitializationStrategy,
defaultDefaultOptions: _defaultDefaultOptions,
staticDefaultInitialization: _staticDefaultInitialization,
staticDefaultOptions: _staticDefaultOptions,
getInitializationStrategy: _getInitializationStrategy,
setInitializationStrategy: _setInitializationStrategy,
getDefaultInitialization: _getDefaultInitialization,
setDefaultInitialization: _setDefaultInitialization,
getDefaultOptions: _getDefaultOptions,
setDefaultOptions: _setDefaultOptions,
}
@@ -10,7 +10,7 @@ import {
OptionsTemplateValue,
optionsTemplateTypes as oTypes,
} from 'plugins/optionsValidationPlugin/validation';
import type { PartialOptions } from 'typings';
import type { DeepPartial } from 'typings';
import type { Plugin } from 'plugins';
const numberAllowedValues: OptionsTemplateValue<number> = oTypes.number;
@@ -26,6 +26,7 @@ const scrollbarsAutoHideAllowedValues: OptionsTemplateValue<ScrollbarAutoHideBeh
const optionsTemplate: OptionsTemplate<Options> = {
// resize: resizeAllowedValues, // none || both || horizontal || vertical || n || b ||
paddingAbsolute: booleanAllowedValues, // true || false
showNativeOverlaidScrollbars: booleanAllowedValues, // true || false
updating: {
elementEvents: arrayNullValues, // array of tuples || null
attributes: arrayNullValues,
@@ -52,21 +53,17 @@ const optionsTemplate: OptionsTemplate<Options> = {
inheritedAttrs: stringArrayNullAllowedValues, // string || array || nul
},
*/
nativeScrollbarsOverlaid: {
show: booleanAllowedValues, // true || false
initialize: booleanAllowedValues, // true || false
},
};
export type OptionsValidationPluginInstance = {
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => PartialOptions<Options>;
_: (options: DeepPartial<Options>, doWriteErrors?: boolean) => DeepPartial<Options>;
};
export const optionsValidationPluginName = '__osOptionsValidationPlugin';
export const optionsValidationPlugin: Plugin<OptionsValidationPluginInstance> = {
[optionsValidationPluginName]: {
_: (options: PartialOptions<Options>, doWriteErrors?: boolean) => {
_: (options: DeepPartial<Options>, doWriteErrors?: boolean) => {
const [validated, foreign] = validateOptions(optionsTemplate, options, doWriteErrors);
return { ...foreign, ...validated };
},
@@ -1,6 +1,6 @@
import { each, hasOwnProperty, keys, push, isEmptyObject } from 'support/utils';
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 OptionsFunctionType = (this: any, ...args: any[]) => any;
@@ -26,7 +26,7 @@ export type OptionsTemplate<T> = {
};
export type OptionsValidationResult<T> = [
PartialOptions<T>, // validated
DeepPartial<T>, // validated
Record<string, unknown> // foreign
];
@@ -88,12 +88,12 @@ const optionsTemplateTypes: OptionsTemplateTypesDictionary = {
*/
const validateRecursive = <T extends PlainObject>(
template: OptionsTemplate<T>,
options: PartialOptions<T>,
options: DeepPartial<T>,
doWriteErrors?: boolean,
propPath?: string
): OptionsValidationResult<T> => {
const validatedOptions: PartialOptions<T> = {};
const optionsCopy: PartialOptions<T> = { ...options };
const validatedOptions: DeepPartial<T> = {};
const optionsCopy: DeepPartial<T> = { ...options };
const props = keys(template).filter((prop) => hasOwnProperty(options, prop));
each(props, (prop: Extract<keyof T, string>) => {
@@ -189,7 +189,7 @@ const validateRecursive = <T extends PlainObject>(
*/
const validateOptions = <T extends PlainObject>(
template: OptionsTemplate<T>,
options: PartialOptions<T>,
options: DeepPartial<T>,
doWriteErrors?: boolean
): OptionsValidationResult<T> => validateRecursive<T>(template, options, doWriteErrors);
@@ -82,15 +82,11 @@ export const scrollbarsHidingPluginName = '__osScrollbarsHidingPlugin';
export const scrollbarsHidingPlugin: Plugin<ScrollbarsHidingPluginInstance> = {
[scrollbarsHidingPluginName]: {
_createUniqueViewportArrangeElement: (env: InternalEnvironment) => {
const {
_nativeScrollbarsHiding: _nativeScrollbarStyling,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_cssCustomProperties,
} = env;
const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _cssCustomProperties } = env;
const create =
!_cssCustomProperties &&
!_nativeScrollbarStyling &&
(_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
!_nativeScrollbarsHiding &&
(_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y);
const result = create ? document.createElement('style') : false;
if (result) {
@@ -29,7 +29,7 @@ import type { InitializationTarget } from 'initialization';
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
import type {
ScrollbarsInitialization,
ScrollbarsInitializationStrategy,
DefaultScrollbarsInitialization,
ScrollbarsDynamicInitializationElement,
} from 'setups/scrollbarsSetup/scrollbarsSetup.initialization';
import { StyleObject } from 'typings';
@@ -85,19 +85,17 @@ export const createScrollbarsSetupElements = (
target: InitializationTarget,
structureSetupElements: StructureSetupElementsObj
): ScrollbarsSetupElements => {
const { _getInitializationStrategy } = getEnvironment();
const { scrollbarsSlot: environmentScrollbarSlot } =
_getInitializationStrategy() as ScrollbarsInitializationStrategy;
const { _getDefaultInitialization } = getEnvironment();
const { scrollbarsSlot: defaultScrollbarSlot } =
_getDefaultInitialization() as DefaultScrollbarsInitialization;
const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements;
const initializationScrollbarSlot = _targetIsElm
? null
: (target as ScrollbarsInitialization).scrollbarsSlot;
const scrollbarSlot = _targetIsElm ? null : (target as ScrollbarsInitialization).scrollbarsSlot;
const evaluatedScrollbarSlot =
generalDynamicInitializationElement<ScrollbarsDynamicInitializationElement>(
[_target, _host, _viewport],
() => _host,
environmentScrollbarSlot,
initializationScrollbarSlot
defaultScrollbarSlot,
scrollbarSlot
);
const scrollbarsAddRemoveClass = (
scrollbarStructures: ScrollbarStructure[],
@@ -1,6 +1,6 @@
import type {
InitializationTargetElement,
InitializtationElementStrategy,
DefaultInitializtationElement,
DynamicInitializationElement,
} from 'initialization';
@@ -20,8 +20,6 @@ export interface ScrollbarsInitialization {
scrollbarsSlot?: ScrollbarsDynamicInitializationElement;
}
export type ScrollbarsInitializationStrategy = {
[K in keyof ScrollbarsInitialization]: InitializtationElementStrategy<
ScrollbarsInitialization[K]
>;
export type DefaultScrollbarsInitialization = {
[K in keyof ScrollbarsInitialization]: DefaultInitializtationElement<ScrollbarsInitialization[K]>;
};
@@ -1,11 +1,11 @@
import { assignDeep, hasOwnProperty } from 'support';
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 SetupUpdate<T extends any[]> = (
changedOptions: PartialOptions<Options>,
changedOptions: DeepPartial<Options>,
force: boolean,
...args: T
) => void;
@@ -37,7 +37,7 @@ const getPropByPath = <T>(obj: any, path: string): T =>
export const createOptionCheck =
(
options: ReadonlyOSOptions,
changedOptions: PartialOptions<Options>,
changedOptions: DeepPartial<Options>,
force?: boolean
): SetupUpdateCheckOption =>
(path: string) =>
@@ -20,6 +20,8 @@ import {
attrClass,
hasAttrClass,
ResizeObserverConstructor,
hasOwnProperty,
noop,
} from 'support';
import {
dataAttributeHost,
@@ -28,7 +30,7 @@ import {
classNamePadding,
classNameViewport,
classNameContent,
classNameViewportScrollbarStyling,
classNameViewportScrollbarHidden,
} from 'classnames';
import { getEnvironment } from 'environment';
import { getPlugins, scrollbarsHidingPluginName } from 'plugins';
@@ -36,11 +38,11 @@ import type { ScrollbarsHidingPluginInstance } from 'plugins/scrollbarsHidingPlu
import {
staticInitializationElement as generalStaticInitializationElement,
dynamicInitializationElement as generalDynamicInitializationElement,
InitializationTargetObject,
} from 'initialization';
import type { InitializationTarget, InitializationTargetElement } from 'initialization';
import type {
StructureDynamicInitializationElement,
StructureInitialization,
StructureStaticInitializationElement,
} from 'setups/structureSetup/structureSetup.initialization';
@@ -60,8 +62,6 @@ export interface StructureSetupElementsObj {
// ctx ----
_isTextarea: boolean;
_isBody: boolean;
_htmlElm: HTMLHtmlElement;
_bodyElm: HTMLBodyElement;
_windowElm: Window;
_documentElm: Document;
_targetIsElm: boolean;
@@ -86,29 +86,35 @@ export const createStructureSetupElements = (
target: InitializationTarget
): StructureSetupElements => {
const env = getEnvironment();
const { _getInitializationStrategy, _nativeScrollbarsHiding } = env;
const { _getDefaultInitialization, _nativeScrollbarsHiding } = env;
const scrollbarsHidingPlugin = getPlugins()[scrollbarsHidingPluginName] as
| ScrollbarsHidingPluginInstance
| undefined;
const createUniqueViewportArrangeElement =
scrollbarsHidingPlugin && scrollbarsHidingPlugin._createUniqueViewportArrangeElement;
const {
host: hostInitializationStrategy,
viewport: viewportInitializationStrategy,
padding: paddingInitializationStrategy,
content: contentInitializationStrategy,
} = _getInitializationStrategy();
host: defaultHostInitializationStrategy,
viewport: defaultViewportInitializationStrategy,
padding: defaultPaddingInitializationStrategy,
content: defaultContentInitializationStrategy,
} = _getDefaultInitialization();
const targetIsElm = isHTMLElement(target);
const targetStructureInitialization = target as StructureInitialization;
const targetElement = targetIsElm
? (target as InitializationTargetElement)
: targetStructureInitialization.target;
const targetStructureInitialization = (targetIsElm ? {} : target) as InitializationTargetObject;
const {
host: hostInitializationStrategy,
padding: paddingInitializationStrategy,
viewport: viewportInitializationStrategy,
content: contentInitializationStrategy,
} = targetStructureInitialization;
const targetElement = targetIsElm ? target : targetStructureInitialization.target;
const isTextarea = is(targetElement, 'textarea');
const isBody = !isTextarea && is(targetElement, 'body');
const ownerDocument = targetElement!.ownerDocument;
const bodyElm = ownerDocument.body as HTMLBodyElement;
const ownerDocument = targetElement.ownerDocument;
const isBody = targetElement === ownerDocument.body;
const wnd = ownerDocument.defaultView as Window;
const singleElmSupport = !!ResizeObserverConstructor && !isTextarea && _nativeScrollbarsHiding;
const singleElmSupport = isBody
? _nativeScrollbarsHiding
: !!ResizeObserverConstructor && !isTextarea && _nativeScrollbarsHiding;
const staticInitializationElement =
generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [
targetElement,
@@ -120,13 +126,15 @@ export const createStructureSetupElements = (
const viewportElement = [
staticInitializationElement(
createNewDiv,
viewportInitializationStrategy,
targetStructureInitialization.viewport
defaultViewportInitializationStrategy,
isBody && !hasOwnProperty(targetStructureInitialization, 'viewport')
? targetElement
: viewportInitializationStrategy
),
staticInitializationElement(createNewDiv, viewportInitializationStrategy),
staticInitializationElement(createNewDiv, defaultViewportInitializationStrategy),
staticInitializationElement(createNewDiv),
].filter((potentialViewport) =>
!singleElmSupport ? potentialViewport !== targetElement : true
singleElmSupport ? true : potentialViewport !== targetElement
)[0];
const viewportIsTarget = viewportElement === targetElement;
const evaluatedTargetObj: StructureSetupElementsObj = {
@@ -134,8 +142,8 @@ export const createStructureSetupElements = (
_host: isTextarea
? staticInitializationElement(
createNewDiv,
hostInitializationStrategy,
targetStructureInitialization.host
defaultHostInitializationStrategy,
hostInitializationStrategy
)
: (targetElement as HTMLElement),
_viewport: viewportElement,
@@ -143,15 +151,15 @@ export const createStructureSetupElements = (
!viewportIsTarget &&
dynamicInitializationElement(
createNewDiv,
paddingInitializationStrategy,
targetStructureInitialization.padding
defaultPaddingInitializationStrategy,
paddingInitializationStrategy
),
_content:
!viewportIsTarget &&
dynamicInitializationElement(
createNewDiv,
contentInitializationStrategy,
targetStructureInitialization.content
defaultContentInitializationStrategy,
contentInitializationStrategy
),
_viewportArrange:
!viewportIsTarget &&
@@ -160,8 +168,6 @@ export const createStructureSetupElements = (
createUniqueViewportArrangeElement(env),
_windowElm: wnd,
_documentElm: ownerDocument,
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
_bodyElm: bodyElm,
_isTextarea: isTextarea,
_isBody: isBody,
_targetIsElm: targetIsElm,
@@ -197,6 +203,9 @@ export const createStructureSetupElements = (
const removePaddingClass = addClass(_padding, classNamePadding);
const removeViewportClass = addClass(_viewport, !viewportIsTarget && classNameViewport);
const removeContentClass = addClass(_content, classNameContent);
const removeHtmlClass = isBody
? addClass(parent(targetElement), classNameViewportScrollbarHidden)
: noop;
// only insert host for textarea after target if it was generated
if (isTextareaHostGenerated) {
@@ -214,6 +223,7 @@ export const createStructureSetupElements = (
appendChildren(_viewport, _content);
push(destroyFns, () => {
removeHtmlClass();
removeHostDataAttr();
removeAttr(_viewport, dataAttributeHostOverflowX);
removeAttr(_viewport, dataAttributeHostOverflowY);
@@ -233,7 +243,7 @@ export const createStructureSetupElements = (
});
if (_nativeScrollbarsHiding && !viewportIsTarget) {
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarHidden));
}
if (_viewportArrange) {
insertBefore(_viewport, _viewportArrange);
@@ -2,7 +2,7 @@ import type {
InitializationTargetElement,
StaticInitializationElement,
DynamicInitializationElement,
InitializtationElementStrategy,
DefaultInitializtationElement,
} from 'initialization';
export type StructureStaticInitializationElement = StaticInitializationElement<
@@ -31,8 +31,8 @@ export interface StructureInitialization {
content?: StructureDynamicInitializationElement;
}
export type StructureInitializationStrategy = {
[K in keyof Omit<StructureInitialization, 'target'>]: InitializtationElementStrategy<
export type DefaultStructureInitialization = {
[K in keyof Omit<StructureInitialization, 'target'>]: DefaultInitializtationElement<
StructureInitialization[K]
>;
};
@@ -9,7 +9,7 @@ import type { TRBL, XY, EventListener } from 'support';
import type { Options, ReadonlyOSOptions } from 'options';
import type { Setup } from 'setups';
import type { InitializationTarget } from 'initialization';
import type { PartialOptions, StyleObject, OverflowStyle } from 'typings';
import type { DeepPartial, StyleObject, OverflowStyle } from 'typings';
export interface StructureSetupState {
_padding: TRBL;
@@ -30,11 +30,7 @@ export interface StructureSetupStaticState {
}
type StructureSetupEventMap = {
u: [
updateHints: StructureSetupUpdateHints,
changedOptions: PartialOptions<Options>,
force: boolean
];
u: [updateHints: StructureSetupUpdateHints, changedOptions: DeepPartial<Options>, force: boolean];
};
const initialXYNumber = { x: 0, y: 0 };
@@ -57,13 +57,9 @@ export const createStructureSetupUpdate = (
state: SetupState<StructureSetupState>
): StructureSetupUpdate => {
const { _viewport } = structureSetupElements;
const {
_nativeScrollbarsHiding: _nativeScrollbarStyling,
_nativeScrollbarsOverlaid: _nativeScrollbarIsOverlaid,
_flexboxGlue,
} = getEnvironment();
const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _flexboxGlue } = getEnvironment();
const doViewportArrange =
!_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
!_nativeScrollbarsHiding && (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y);
const updateSegments: StructureSetupUpdateSegment[] = [
createTrinsicUpdateSegment(structureSetupElements, state),
@@ -16,12 +16,12 @@ import {
} from 'support';
import { getEnvironment } from 'environment';
import {
classNameViewportScrollbarStyling,
classNameViewportScrollbarHidden,
classNameOverflowVisible,
dataAttributeHost,
dataAttributeHostOverflowX,
dataAttributeHostOverflowY,
dataValueHostViewportScrollbarStyling,
dataValueHostScrollbarHidden,
dataValueHostOverflowVisible,
} from 'classnames';
import { getPlugins, scrollbarsHidingPluginName } from 'plugins';
@@ -328,7 +328,7 @@ export const createOverflowUpdateSegment: CreateStructureUpdateSegment = (
} = updateHints;
const { _heightIntrinsic, _directionIsRTL } = getState();
const [showNativeOverlaidScrollbarsOption, showNativeOverlaidScrollbarsChanged] =
checkOption<boolean>('nativeScrollbarsOverlaid.show');
checkOption<boolean>('showNativeOverlaidScrollbars');
const [overflow, overflowChanged] = checkOption<XY<OverflowBehavior>>('overflow');
const showNativeOverlaidScrollbars =
@@ -356,8 +356,8 @@ export const createOverflowUpdateSegment: CreateStructureUpdateSegment = (
if (showNativeOverlaidScrollbarsChanged && _nativeScrollbarsHiding) {
_viewportAddRemoveClass(
classNameViewportScrollbarStyling,
dataValueHostViewportScrollbarStyling,
classNameViewportScrollbarHidden,
dataValueHostScrollbarHidden,
!showNativeOverlaidScrollbars
);
}
@@ -53,17 +53,20 @@
.os-viewport {
-ms-overflow-style: scrollbar !important;
}
[data-overlayscrollbars~='viewportStyled'],
.os-viewport-scrollbar-styled.os-environment,
.os-viewport-scrollbar-styled.os-viewport {
[data-overlayscrollbars~='scrollbarHidden'],
html.os-viewport-scrollbar-hidden,
.os-viewport-scrollbar-hidden.os-environment,
.os-viewport-scrollbar-hidden.os-viewport {
scrollbar-width: none !important;
}
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar,
[data-overlayscrollbars~='viewportStyled']::-webkit-scrollbar-corner,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar-corner,
.os-viewport-scrollbar-styled.os-viewport::-webkit-scrollbar-corner {
[data-overlayscrollbars~='scrollbarHidden']::-webkit-scrollbar,
[data-overlayscrollbars~='scrollbarHidden']::-webkit-scrollbar-corner,
html.os-viewport-scrollbar-hidden::-webkit-scrollbar,
html.os-viewport-scrollbar-hidden::-webkit-scrollbar-corner,
.os-viewport-scrollbar-hidden.os-environment::-webkit-scrollbar,
.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;
width: 0px !important;
height: 0px !important;
+2 -381
View File
@@ -1,5 +1,5 @@
export type PartialOptions<T> = {
[P in keyof T]?: T[P] extends Record<string, unknown> ? PartialOptions<T[P]> : T[P];
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Record<string, unknown> ? DeepPartial<T[P]> : T[P];
};
export type ReadonlyOptions<T> = {
@@ -15,382 +15,3 @@ export type StyleObject<CustomCssProps = ''> = {
};
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,
StructureSetupElementsObj,
} from 'setups/structureSetup/structureSetup.elements';
import type { InitializationTarget, InitializtationElementStrategy } from 'initialization';
import type { InitializationTarget, DefaultInitializtationElement } from 'initialization';
import type {
StructureInitialization,
StructureStaticInitializationElement,
@@ -208,8 +208,8 @@ const assertCorrectSetupElements = (
inputStrategy: StructureStaticInitializationElement | StructureDynamicInitializationElement,
isStaticStrategy: boolean,
strategy:
| InitializtationElementStrategy<StructureStaticInitializationElement>
| InitializtationElementStrategy<StructureDynamicInitializationElement>,
| DefaultInitializtationElement<StructureStaticInitializationElement>
| DefaultInitializtationElement<StructureDynamicInitializationElement>,
kind: 'padding' | 'viewport' | 'content' | 'host'
) => {
const input = isFunction(inputStrategy) ? inputStrategy(target) : inputStrategy;
@@ -224,7 +224,7 @@ const assertCorrectSetupElements = (
if (input === undefined) {
if (isStaticStrategy) {
strategy =
strategy as InitializtationElementStrategy<StructureStaticInitializationElement>;
strategy as DefaultInitializtationElement<StructureStaticInitializationElement>;
if (typeof strategy === 'function') {
const result = strategy(target);
if (_viewportIsTarget) {
@@ -243,7 +243,7 @@ const assertCorrectSetupElements = (
}
} else {
strategy =
strategy as InitializtationElementStrategy<StructureDynamicInitializationElement>;
strategy as DefaultInitializtationElement<StructureDynamicInitializationElement>;
if (typeof strategy === 'function') {
const result = strategy(target);
@@ -2,67 +2,10 @@ import './index.scss';
import 'index.scss';
import { OverlayScrollbars } from 'overlayscrollbars';
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
// test with different cancel values for body
OverlayScrollbars.env().setDefaultInitialization({
cancel: { nativeScrollbarsOverlaid: false },
});
OverlayScrollbars.env().setInitializationStrategy({ viewport: (target) => target });
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);
*/
// @ts-ignore
window.os = OverlayScrollbars({ target: document.body, cancel: { body: null } }, {});
@@ -18,8 +18,8 @@ if (!OverlayScrollbars.env().scrollbarsHiding) {
// @ts-ignore
window.OverlayScrollbars = OverlayScrollbars;
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
OverlayScrollbars.env().setDefaultInitialization({
cancel: { nativeScrollbarsOverlaid: false },
});
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 { addClass, each, isArray, removeAttr, style } from 'support';
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
OverlayScrollbars.env().setInitializationStrategy({
cancel: { nativeScrollbarsOverlaid: false },
});
@@ -23,7 +23,7 @@ import { setTestResult, waitForOrFailTest } from '@/testing-browser/TestResult';
import { generateClassChangeSelectCallback, iterateSelect } from '@/testing-browser/Select';
import { timeout } from '@/testing-browser/timeout';
import { Options } from 'options';
import { PartialOptions } from 'typings';
import { DeepPartial } from 'typings';
import { addPlugin, scrollbarsHidingPlugin, sizeObserverPlugin } from 'plugins';
if (!window.ResizeObserver) {
@@ -36,8 +36,8 @@ if (!OverlayScrollbars.env().scrollbarsHiding) {
// @ts-ignore
window.OverlayScrollbars = OverlayScrollbars;
OverlayScrollbars.env().setDefaultOptions({
nativeScrollbarsOverlaid: { initialize: true },
OverlayScrollbars.env().setDefaultInitialization({
cancel: { nativeScrollbarsOverlaid: false },
});
interface Metrics {
@@ -587,7 +587,7 @@ const iterateMinMax = async (afterEach?: () => any) => {
await iterate(containerMinMaxSelect, afterEach);
};
const overflowTest = async (osOptions?: PartialOptions<Options>) => {
const overflowTest = async (osOptions?: DeepPartial<Options>) => {
const additiveOverflow = () => {
if (isFractionalPixelRatio()) {
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 };