mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-19 22:40:37 +03:00
improve public api
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
XY,
|
XY,
|
||||||
TRBL,
|
TRBL,
|
||||||
createEventListenerHub,
|
createEventListenerHub,
|
||||||
|
isPlainObject,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import { createStructureSetup, createScrollbarsSetup } from 'setups';
|
import { createStructureSetup, createScrollbarsSetup } from 'setups';
|
||||||
import { getOptionsDiff, Options, ReadonlyOptions } from 'options';
|
import { getOptionsDiff, Options, ReadonlyOptions } from 'options';
|
||||||
@@ -31,14 +32,19 @@ import {
|
|||||||
ScrollbarStructure,
|
ScrollbarStructure,
|
||||||
} from 'setups/scrollbarsSetup/scrollbarsSetup.elements';
|
} from 'setups/scrollbarsSetup/scrollbarsSetup.elements';
|
||||||
|
|
||||||
|
// Notes:
|
||||||
|
// Height intrinsic detection use "content: true" init strategy - or open ticket for custom height intrinsic observer
|
||||||
|
|
||||||
export interface OverlayScrollbarsStatic {
|
export interface OverlayScrollbarsStatic {
|
||||||
|
(target: InitializationTarget): OverlayScrollbars | undefined;
|
||||||
(
|
(
|
||||||
target: InitializationTarget,
|
target: InitializationTarget,
|
||||||
options?: DeepPartial<Options>,
|
options: DeepPartial<Options>,
|
||||||
eventListeners?: GeneralInitialEventListeners<EventListenerMap>
|
eventListeners?: GeneralInitialEventListeners<EventListenerMap>
|
||||||
): OverlayScrollbars;
|
): OverlayScrollbars;
|
||||||
|
|
||||||
plugin(plugin: Plugin | Plugin[]): void;
|
plugin(plugin: Plugin | Plugin[]): void;
|
||||||
|
valid(osInstance: any): boolean;
|
||||||
env(): Environment;
|
env(): Environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,222 +153,221 @@ export interface OverlayScrollbars {
|
|||||||
off<Name extends keyof EventListenerMap>(name: Name, listener: EventListener<Name>[]): void;
|
off<Name extends keyof EventListenerMap>(name: Name, listener: EventListener<Name>[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notes:
|
|
||||||
* Height intrinsic detection use "content: true" init strategy - or open ticket for custom height intrinsic observer
|
|
||||||
*/
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||||
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||||
target,
|
target: InitializationTarget,
|
||||||
options?,
|
options?: DeepPartial<Options>,
|
||||||
eventListeners?
|
eventListeners?: GeneralInitialEventListeners<EventListenerMap>
|
||||||
): OverlayScrollbars => {
|
) => {
|
||||||
let destroyed = false;
|
|
||||||
const { _getDefaultOptions, _addListener: addEnvListener } = getEnvironment();
|
const { _getDefaultOptions, _addListener: addEnvListener } = getEnvironment();
|
||||||
const plugins = getPlugins();
|
const plugins = getPlugins();
|
||||||
const targetIsElement = isHTMLElement(target);
|
const targetIsElement = isHTMLElement(target);
|
||||||
const instanceTarget = targetIsElement ? target : target.target;
|
const instanceTarget = targetIsElement ? target : target.target;
|
||||||
const potentialInstance = getInstance(instanceTarget);
|
const potentialInstance = getInstance(instanceTarget);
|
||||||
if (potentialInstance) {
|
if (options && !potentialInstance) {
|
||||||
return potentialInstance;
|
let destroyed = false;
|
||||||
}
|
const optionsValidationPlugin = plugins[
|
||||||
|
optionsValidationPluginName
|
||||||
|
] as OptionsValidationPluginInstance;
|
||||||
|
const validateOptions = (newOptions?: DeepPartial<Options>) => {
|
||||||
|
const opts = newOptions || {};
|
||||||
|
const validate = optionsValidationPlugin && optionsValidationPlugin._;
|
||||||
|
return validate ? validate(opts, true) : opts;
|
||||||
|
};
|
||||||
|
const currentOptions: ReadonlyOptions = assignDeep(
|
||||||
|
{},
|
||||||
|
_getDefaultOptions(),
|
||||||
|
validateOptions(options)
|
||||||
|
);
|
||||||
|
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub(eventListeners);
|
||||||
|
const [updateStructure, structureState, destroyStructure] = createStructureSetup(
|
||||||
|
target,
|
||||||
|
currentOptions
|
||||||
|
);
|
||||||
|
const [updateScrollbars, scrollbarsState, destroyScrollbars] = createScrollbarsSetup(
|
||||||
|
target,
|
||||||
|
currentOptions,
|
||||||
|
structureState
|
||||||
|
);
|
||||||
|
const update = (changedOptions: DeepPartial<Options>, force?: boolean) => {
|
||||||
|
updateStructure(changedOptions, !!force);
|
||||||
|
};
|
||||||
|
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
|
||||||
|
const destroy = (canceled?: boolean) => {
|
||||||
|
removeInstance(instanceTarget);
|
||||||
|
removeEnvListener();
|
||||||
|
|
||||||
const optionsValidationPlugin = plugins[
|
destroyScrollbars();
|
||||||
optionsValidationPluginName
|
destroyStructure();
|
||||||
] as OptionsValidationPluginInstance;
|
|
||||||
const validateOptions = (newOptions?: DeepPartial<Options>) => {
|
|
||||||
const opts = newOptions || {};
|
|
||||||
const validate = optionsValidationPlugin && optionsValidationPlugin._;
|
|
||||||
return validate ? validate(opts, true) : opts;
|
|
||||||
};
|
|
||||||
const currentOptions: ReadonlyOptions = assignDeep(
|
|
||||||
{},
|
|
||||||
_getDefaultOptions(),
|
|
||||||
validateOptions(options)
|
|
||||||
);
|
|
||||||
const [addEvent, removeEvent, triggerEvent] = createEventListenerHub(eventListeners);
|
|
||||||
const [updateStructure, structureState, destroyStructure] = createStructureSetup(
|
|
||||||
target,
|
|
||||||
currentOptions
|
|
||||||
);
|
|
||||||
const [updateScrollbars, scrollbarsState, destroyScrollbars] = createScrollbarsSetup(
|
|
||||||
target,
|
|
||||||
currentOptions,
|
|
||||||
structureState
|
|
||||||
);
|
|
||||||
const update = (changedOptions: DeepPartial<Options>, force?: boolean) => {
|
|
||||||
updateStructure(changedOptions, !!force);
|
|
||||||
};
|
|
||||||
const removeEnvListener = addEnvListener(update.bind(0, {}, true));
|
|
||||||
const destroy = (canceled?: boolean) => {
|
|
||||||
removeInstance(instanceTarget);
|
|
||||||
removeEnvListener();
|
|
||||||
|
|
||||||
destroyScrollbars();
|
destroyed = true;
|
||||||
destroyStructure();
|
|
||||||
|
|
||||||
destroyed = true;
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
|
triggerEvent('destroyed', [instance, !!canceled]);
|
||||||
|
removeEvent();
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
const instance: OverlayScrollbars = {
|
||||||
triggerEvent('destroyed', [instance, !!canceled]);
|
options(newOptions?: DeepPartial<Options>) {
|
||||||
removeEvent();
|
if (newOptions) {
|
||||||
};
|
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
|
||||||
|
|
||||||
const instance: OverlayScrollbars = {
|
if (!isEmptyObject(changedOptions)) {
|
||||||
options(newOptions?: DeepPartial<Options>) {
|
assignDeep(currentOptions, changedOptions);
|
||||||
if (newOptions) {
|
update(changedOptions);
|
||||||
const changedOptions = getOptionsDiff(currentOptions, validateOptions(newOptions));
|
}
|
||||||
|
|
||||||
if (!isEmptyObject(changedOptions)) {
|
|
||||||
assignDeep(currentOptions, changedOptions);
|
|
||||||
update(changedOptions);
|
|
||||||
}
|
}
|
||||||
}
|
return assignDeep({}, currentOptions);
|
||||||
return assignDeep({}, currentOptions);
|
|
||||||
},
|
|
||||||
on: addEvent,
|
|
||||||
off: (name, listener) => {
|
|
||||||
name && listener && removeEvent(name, listener as any);
|
|
||||||
},
|
|
||||||
state() {
|
|
||||||
const {
|
|
||||||
_overflowEdge,
|
|
||||||
_overflowAmount,
|
|
||||||
_overflowStyle,
|
|
||||||
_hasOverflow,
|
|
||||||
_padding,
|
|
||||||
_paddingAbsolute,
|
|
||||||
_directionIsRTL,
|
|
||||||
} = structureState();
|
|
||||||
return assignDeep(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
overflowEdge: _overflowEdge,
|
|
||||||
overflowAmount: _overflowAmount,
|
|
||||||
overflowStyle: _overflowStyle,
|
|
||||||
hasOverflow: _hasOverflow,
|
|
||||||
padding: _padding,
|
|
||||||
paddingAbsolute: _paddingAbsolute,
|
|
||||||
directionRTL: _directionIsRTL,
|
|
||||||
destroyed,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
elements() {
|
|
||||||
const {
|
|
||||||
_target,
|
|
||||||
_host,
|
|
||||||
_padding,
|
|
||||||
_viewport,
|
|
||||||
_content,
|
|
||||||
_scrollOffsetElement,
|
|
||||||
_scrollEventElement,
|
|
||||||
} = structureState._elements;
|
|
||||||
const { _horizontal, _vertical } = scrollbarsState._elements;
|
|
||||||
const translateScrollbarStructure = (
|
|
||||||
scrollbarStructure: ScrollbarStructure
|
|
||||||
): ScrollbarElements => {
|
|
||||||
const { _handle, _track, _scrollbar } = scrollbarStructure;
|
|
||||||
return {
|
|
||||||
scrollbar: _scrollbar,
|
|
||||||
track: _track,
|
|
||||||
handle: _handle,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const translateScrollbarsSetupElement = (
|
|
||||||
scrollbarsSetupElement: ScrollbarsSetupElement
|
|
||||||
): CloneableScrollbarElements => {
|
|
||||||
const { _scrollbarStructures, _clone } = scrollbarsSetupElement;
|
|
||||||
const translatedStructure = translateScrollbarStructure(_scrollbarStructures[0]);
|
|
||||||
|
|
||||||
return assignDeep({}, translatedStructure, {
|
|
||||||
clone: () => {
|
|
||||||
const result = translateScrollbarStructure(_clone());
|
|
||||||
updateScrollbars({}, true, {});
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return assignDeep(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
target: _target,
|
|
||||||
host: _host,
|
|
||||||
padding: _padding || _viewport,
|
|
||||||
viewport: _viewport,
|
|
||||||
content: _content || _viewport,
|
|
||||||
scrollOffsetElement: _scrollOffsetElement,
|
|
||||||
scrollEventElement: _scrollEventElement,
|
|
||||||
scrollbarHorizontal: translateScrollbarsSetupElement(_horizontal),
|
|
||||||
scrollbarVertical: translateScrollbarsSetupElement(_vertical),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
update(force?: boolean) {
|
|
||||||
update({}, force);
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
destroy: destroy.bind(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
structureState._addOnUpdatedListener((updateHints, changedOptions, force: boolean) => {
|
|
||||||
updateScrollbars(changedOptions, force, updateHints);
|
|
||||||
});
|
|
||||||
|
|
||||||
each(keys(plugins), (pluginName) => {
|
|
||||||
const pluginInstance = plugins[pluginName];
|
|
||||||
if (isFunction(pluginInstance)) {
|
|
||||||
pluginInstance(OverlayScrollbars, instance);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (cancelInitialization(!targetIsElement && target.cancel, structureState._elements)) {
|
|
||||||
destroy(true);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
structureState._appendElements();
|
|
||||||
scrollbarsState._appendElements();
|
|
||||||
|
|
||||||
addInstance(instanceTarget, instance);
|
|
||||||
triggerEvent('initialized', [instance]);
|
|
||||||
|
|
||||||
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
|
|
||||||
const {
|
|
||||||
_sizeChanged,
|
|
||||||
_directionChanged,
|
|
||||||
_heightIntrinsicChanged,
|
|
||||||
_overflowEdgeChanged,
|
|
||||||
_overflowAmountChanged,
|
|
||||||
_overflowStyleChanged,
|
|
||||||
_contentMutation,
|
|
||||||
_hostMutation,
|
|
||||||
} = updateHints;
|
|
||||||
|
|
||||||
triggerEvent('updated', [
|
|
||||||
instance,
|
|
||||||
{
|
|
||||||
updateHints: {
|
|
||||||
sizeChanged: _sizeChanged,
|
|
||||||
directionChanged: _directionChanged,
|
|
||||||
heightIntrinsicChanged: _heightIntrinsicChanged,
|
|
||||||
overflowEdgeChanged: _overflowEdgeChanged,
|
|
||||||
overflowAmountChanged: _overflowAmountChanged,
|
|
||||||
overflowStyleChanged: _overflowStyleChanged,
|
|
||||||
contentMutation: _contentMutation,
|
|
||||||
hostMutation: _hostMutation,
|
|
||||||
},
|
|
||||||
changedOptions,
|
|
||||||
force,
|
|
||||||
},
|
},
|
||||||
]);
|
on: addEvent,
|
||||||
});
|
off: (name, listener) => {
|
||||||
|
name && listener && removeEvent(name, listener as any);
|
||||||
|
},
|
||||||
|
state() {
|
||||||
|
const {
|
||||||
|
_overflowEdge,
|
||||||
|
_overflowAmount,
|
||||||
|
_overflowStyle,
|
||||||
|
_hasOverflow,
|
||||||
|
_padding,
|
||||||
|
_paddingAbsolute,
|
||||||
|
_directionIsRTL,
|
||||||
|
} = structureState();
|
||||||
|
return assignDeep(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
overflowEdge: _overflowEdge,
|
||||||
|
overflowAmount: _overflowAmount,
|
||||||
|
overflowStyle: _overflowStyle,
|
||||||
|
hasOverflow: _hasOverflow,
|
||||||
|
padding: _padding,
|
||||||
|
paddingAbsolute: _paddingAbsolute,
|
||||||
|
directionRTL: _directionIsRTL,
|
||||||
|
destroyed,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
elements() {
|
||||||
|
const {
|
||||||
|
_target,
|
||||||
|
_host,
|
||||||
|
_padding,
|
||||||
|
_viewport,
|
||||||
|
_content,
|
||||||
|
_scrollOffsetElement,
|
||||||
|
_scrollEventElement,
|
||||||
|
} = structureState._elements;
|
||||||
|
const { _horizontal, _vertical } = scrollbarsState._elements;
|
||||||
|
const translateScrollbarStructure = (
|
||||||
|
scrollbarStructure: ScrollbarStructure
|
||||||
|
): ScrollbarElements => {
|
||||||
|
const { _handle, _track, _scrollbar } = scrollbarStructure;
|
||||||
|
return {
|
||||||
|
scrollbar: _scrollbar,
|
||||||
|
track: _track,
|
||||||
|
handle: _handle,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const translateScrollbarsSetupElement = (
|
||||||
|
scrollbarsSetupElement: ScrollbarsSetupElement
|
||||||
|
): CloneableScrollbarElements => {
|
||||||
|
const { _scrollbarStructures, _clone } = scrollbarsSetupElement;
|
||||||
|
const translatedStructure = translateScrollbarStructure(_scrollbarStructures[0]);
|
||||||
|
|
||||||
return instance.update(true);
|
return assignDeep({}, translatedStructure, {
|
||||||
|
clone: () => {
|
||||||
|
const result = translateScrollbarStructure(_clone());
|
||||||
|
updateScrollbars({}, true, {});
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return assignDeep(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
target: _target,
|
||||||
|
host: _host,
|
||||||
|
padding: _padding || _viewport,
|
||||||
|
viewport: _viewport,
|
||||||
|
content: _content || _viewport,
|
||||||
|
scrollOffsetElement: _scrollOffsetElement,
|
||||||
|
scrollEventElement: _scrollEventElement,
|
||||||
|
scrollbarHorizontal: translateScrollbarsSetupElement(_horizontal),
|
||||||
|
scrollbarVertical: translateScrollbarsSetupElement(_vertical),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
update(force?: boolean) {
|
||||||
|
update({}, force);
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
destroy: destroy.bind(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
structureState._addOnUpdatedListener((updateHints, changedOptions, force: boolean) => {
|
||||||
|
updateScrollbars(changedOptions, force, updateHints);
|
||||||
|
});
|
||||||
|
|
||||||
|
each(keys(plugins), (pluginName) => {
|
||||||
|
const pluginInstance = plugins[pluginName];
|
||||||
|
if (isFunction(pluginInstance)) {
|
||||||
|
pluginInstance(OverlayScrollbars, instance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (cancelInitialization(!targetIsElement && target.cancel, structureState._elements)) {
|
||||||
|
destroy(true);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
structureState._appendElements();
|
||||||
|
scrollbarsState._appendElements();
|
||||||
|
|
||||||
|
addInstance(instanceTarget, instance);
|
||||||
|
triggerEvent('initialized', [instance]);
|
||||||
|
|
||||||
|
structureState._addOnUpdatedListener((updateHints, changedOptions, force) => {
|
||||||
|
const {
|
||||||
|
_sizeChanged,
|
||||||
|
_directionChanged,
|
||||||
|
_heightIntrinsicChanged,
|
||||||
|
_overflowEdgeChanged,
|
||||||
|
_overflowAmountChanged,
|
||||||
|
_overflowStyleChanged,
|
||||||
|
_contentMutation,
|
||||||
|
_hostMutation,
|
||||||
|
} = updateHints;
|
||||||
|
|
||||||
|
triggerEvent('updated', [
|
||||||
|
instance,
|
||||||
|
{
|
||||||
|
updateHints: {
|
||||||
|
sizeChanged: _sizeChanged,
|
||||||
|
directionChanged: _directionChanged,
|
||||||
|
heightIntrinsicChanged: _heightIntrinsicChanged,
|
||||||
|
overflowEdgeChanged: _overflowEdgeChanged,
|
||||||
|
overflowAmountChanged: _overflowAmountChanged,
|
||||||
|
overflowStyleChanged: _overflowStyleChanged,
|
||||||
|
contentMutation: _contentMutation,
|
||||||
|
hostMutation: _hostMutation,
|
||||||
|
},
|
||||||
|
changedOptions,
|
||||||
|
force,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance.update(true);
|
||||||
|
}
|
||||||
|
return potentialInstance!;
|
||||||
};
|
};
|
||||||
|
|
||||||
OverlayScrollbars.plugin = addPlugin;
|
OverlayScrollbars.plugin = addPlugin;
|
||||||
|
OverlayScrollbars.valid = (osInstance: any) => {
|
||||||
|
const hasElmsFn = osInstance && (osInstance as OverlayScrollbars).elements;
|
||||||
|
const elements = isFunction(hasElmsFn) && hasElmsFn();
|
||||||
|
return isPlainObject(elements) && !!getInstance(elements.target);
|
||||||
|
};
|
||||||
OverlayScrollbars.env = () => {
|
OverlayScrollbars.env = () => {
|
||||||
const {
|
const {
|
||||||
_nativeScrollbarsSize,
|
_nativeScrollbarsSize,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const pluginRegistry: Record<string, PluginInstance> = {};
|
|||||||
|
|
||||||
export const getPlugins = () => pluginRegistry;
|
export const getPlugins = () => pluginRegistry;
|
||||||
|
|
||||||
export const addPlugin = (addedPlugin: Plugin | Plugin[]) => {
|
export const addPlugin = (addedPlugin: Plugin | Plugin[]): void => {
|
||||||
each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => {
|
each((isArray(addedPlugin) ? addedPlugin : [addedPlugin]) as Plugin[], (plugin) => {
|
||||||
const pluginName = keys(plugin)[0];
|
const pluginName = keys(plugin)[0];
|
||||||
pluginRegistry[pluginName] = plugin[pluginName];
|
pluginRegistry[pluginName] = plugin[pluginName];
|
||||||
|
|||||||
Reference in New Issue
Block a user