mirror of
https://github.com/tenrok/OverlayScrollbars.git
synced 2026-06-12 15:32:27 +03:00
improve initialization strategy and begin with scrollbars
This commit is contained in:
+1
-1
@@ -60,7 +60,7 @@
|
|||||||
"rollup-plugin-typescript2": "^0.27.1",
|
"rollup-plugin-typescript2": "^0.27.1",
|
||||||
"should": "^13.2.3",
|
"should": "^13.2.3",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.3.2",
|
||||||
"utf-8-validate": "^5.0.2"
|
"utf-8-validate": "^5.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -17,3 +17,9 @@ export const classNameSizeObserverListenerItem = `${classNameSizeObserverListene
|
|||||||
export const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`;
|
export const classNameSizeObserverListenerItemFinal = `${classNameSizeObserverListenerItem}-final`;
|
||||||
|
|
||||||
export const classNameTrinsicObserver = 'os-trinsic-observer';
|
export const classNameTrinsicObserver = 'os-trinsic-observer';
|
||||||
|
|
||||||
|
export const classNameScrollbar = 'os-scrollbar';
|
||||||
|
export const classNameScrollbarHorizontal = `${classNameScrollbar}-horizontal`;
|
||||||
|
export const classNameScrollbarVertical = `${classNameScrollbar}-vertical`;
|
||||||
|
export const classNameScrollbarTrack = 'os-scrollbar-track';
|
||||||
|
export const classNameScrollbarHandle = 'os-scrollbar-handle';
|
||||||
|
|||||||
@@ -25,12 +25,40 @@ import {
|
|||||||
classNameViewportScrollbarStyling,
|
classNameViewportScrollbarStyling,
|
||||||
} from 'classnames';
|
} from 'classnames';
|
||||||
import { OSOptions, defaultOptions } from 'options';
|
import { OSOptions, defaultOptions } from 'options';
|
||||||
|
import { OSTargetElement } from 'typings';
|
||||||
|
|
||||||
export interface InitializationStrategy {
|
type StructureInitializationElementFn<T> = ((target: OSTargetElement) => HTMLElement | T) | T;
|
||||||
_padding: boolean;
|
|
||||||
_content: boolean;
|
type ScrollbarsInitializationElementFn<T> = ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => HTMLElement | T) | T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Static element is an element which MUST be generated.
|
||||||
|
* If null (or the returned result is null), the initialization function is generatig the element, otherwise
|
||||||
|
* the element returned by the function acts as the generated element.
|
||||||
|
*/
|
||||||
|
export type StructureInitializationStaticElement = StructureInitializationElementFn<null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Dynamic element is an element which CAN be generated.
|
||||||
|
* If null (or the returned result is null), then the default behavior is used.
|
||||||
|
* If boolean (or the returned result is boolean), the generation of the element is forced (or not).
|
||||||
|
* If the function returns and element, the element returned by the function acts as the generated element.
|
||||||
|
*/
|
||||||
|
export type StructureInitializationDynamicElement = StructureInitializationElementFn<boolean | null>;
|
||||||
|
|
||||||
|
export interface StructureInitializationStrategy {
|
||||||
|
_host: StructureInitializationStaticElement;
|
||||||
|
_viewport: StructureInitializationStaticElement;
|
||||||
|
_padding: StructureInitializationDynamicElement;
|
||||||
|
_content: StructureInitializationDynamicElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScrollbarsInitializationStrategy {
|
||||||
|
_scrollbarsSlot: ScrollbarsInitializationElementFn<null | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InitializationStrategy extends StructureInitializationStrategy, ScrollbarsInitializationStrategy {}
|
||||||
|
|
||||||
export type OnEnvironmentChanged = (env: Environment) => void;
|
export type OnEnvironmentChanged = (env: Environment) => void;
|
||||||
export interface Environment {
|
export interface Environment {
|
||||||
_nativeScrollbarSize: XY;
|
_nativeScrollbarSize: XY;
|
||||||
@@ -132,9 +160,13 @@ const getWindowDPR = (): number => {
|
|||||||
return window.devicePixelRatio || dDPI / sDPI;
|
return window.devicePixelRatio || dDPI / sDPI;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// init function decides for all values
|
||||||
const getDefaultInitializationStrategy = (nativeScrollbarStyling: boolean): InitializationStrategy => ({
|
const getDefaultInitializationStrategy = (nativeScrollbarStyling: boolean): InitializationStrategy => ({
|
||||||
_padding: !nativeScrollbarStyling,
|
_host: null,
|
||||||
_content: false,
|
_viewport: null,
|
||||||
|
_padding: null,
|
||||||
|
_content: null,
|
||||||
|
_scrollbarsSlot: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createEnvironment = (): Environment => {
|
const createEnvironment = (): Environment => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { createTrinsicLifecycle } from 'lifecycles/trinsicLifecycle';
|
|||||||
import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle';
|
import { createPaddingLifecycle } from 'lifecycles/paddingLifecycle';
|
||||||
import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle';
|
import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle';
|
||||||
import { StyleObject } from 'typings';
|
import { StyleObject } from 'typings';
|
||||||
|
import { ScrollbarsSetup } from 'setups/scrollbarsSetup';
|
||||||
|
|
||||||
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
|
export type LifecycleCheckOption = <T>(path: string) => LifecycleOptionInfo<T>;
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ const lifecycleCommunicationFallback: LifecycleCommunication = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup): LifecycleHubInstance => {
|
export const createLifecycleHub = (options: OSOptions, structureSetup: StructureSetup, scrollbarsSetup: ScrollbarsSetup): LifecycleHubInstance => {
|
||||||
let lifecycleCommunication = lifecycleCommunicationFallback;
|
let lifecycleCommunication = lifecycleCommunicationFallback;
|
||||||
const { _viewport } = structureSetup._targetObj;
|
const { _viewport } = structureSetup._targetObj;
|
||||||
const {
|
const {
|
||||||
@@ -201,6 +202,9 @@ export const createLifecycleHub = (options: OSOptions, structureSetup: Structure
|
|||||||
_destroy() {
|
_destroy() {
|
||||||
destroyObservers();
|
destroyObservers();
|
||||||
removeEnvironmentListener(envUpdateListener);
|
removeEnvironmentListener(envUpdateListener);
|
||||||
|
|
||||||
|
structureSetup._destroy();
|
||||||
|
scrollbarsSetup._destroy();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { OSTarget, OSTargetObject } from 'typings';
|
import { OSTarget, OSInitializationObject } from 'typings';
|
||||||
import { PartialOptions, validateOptions, assignDeep, isEmptyObject } from 'support';
|
import { PartialOptions, validateOptions, assignDeep, isEmptyObject } from 'support';
|
||||||
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
import { createStructureSetup, StructureSetup } from 'setups/structureSetup';
|
||||||
|
import { createScrollbarsSetup, ScrollbarsSetup } from 'setups/scrollbarsSetup';
|
||||||
import { createLifecycleHub } from 'lifecycles/lifecycleHub';
|
import { createLifecycleHub } from 'lifecycles/lifecycleHub';
|
||||||
import { OSOptions, optionsTemplate } from 'options';
|
import { OSOptions, optionsTemplate } from 'options';
|
||||||
import { getEnvironment } from 'environment';
|
import { getEnvironment } from 'environment';
|
||||||
|
|
||||||
export interface OverlayScrollbarsStatic {
|
export interface OverlayScrollbarsStatic {
|
||||||
(target: OSTarget | OSTargetObject, options?: PartialOptions<OSOptions>, extensions?: any): OverlayScrollbars;
|
(target: OSTarget | OSInitializationObject, options?: PartialOptions<OSOptions>, extensions?: any): OverlayScrollbars;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OverlayScrollbars {
|
export interface OverlayScrollbars {
|
||||||
@@ -14,12 +15,13 @@ export interface OverlayScrollbars {
|
|||||||
options(newOptions?: PartialOptions<OSOptions>): OSOptions;
|
options(newOptions?: PartialOptions<OSOptions>): OSOptions;
|
||||||
|
|
||||||
update(force?: boolean): void;
|
update(force?: boolean): void;
|
||||||
|
destroy(): void;
|
||||||
|
|
||||||
state(): any;
|
state(): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
||||||
target: OSTarget | OSTargetObject,
|
target: OSTarget | OSInitializationObject,
|
||||||
options?: PartialOptions<OSOptions>,
|
options?: PartialOptions<OSOptions>,
|
||||||
extensions?: any
|
extensions?: any
|
||||||
): OverlayScrollbars => {
|
): OverlayScrollbars => {
|
||||||
@@ -30,7 +32,9 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
validateOptions(options || ({} as PartialOptions<OSOptions>), optionsTemplate, null, true)._validated
|
validateOptions(options || ({} as PartialOptions<OSOptions>), optionsTemplate, null, true)._validated
|
||||||
);
|
);
|
||||||
const structureSetup: StructureSetup = createStructureSetup(target);
|
const structureSetup: StructureSetup = createStructureSetup(target);
|
||||||
const lifecycleHub = createLifecycleHub(currentOptions, structureSetup);
|
const scrollbarsSetup: ScrollbarsSetup = createScrollbarsSetup(target, structureSetup);
|
||||||
|
const lifecycleHub = createLifecycleHub(currentOptions, structureSetup, scrollbarsSetup);
|
||||||
|
|
||||||
const instance: OverlayScrollbars = {
|
const instance: OverlayScrollbars = {
|
||||||
options(newOptions?: PartialOptions<OSOptions>) {
|
options(newOptions?: PartialOptions<OSOptions>) {
|
||||||
if (newOptions) {
|
if (newOptions) {
|
||||||
@@ -47,6 +51,7 @@ export const OverlayScrollbars: OverlayScrollbarsStatic = (
|
|||||||
update(force?: boolean) {
|
update(force?: boolean) {
|
||||||
lifecycleHub._update(null, force);
|
lifecycleHub._update(null, force);
|
||||||
},
|
},
|
||||||
|
destroy: () => lifecycleHub._destroy(),
|
||||||
};
|
};
|
||||||
|
|
||||||
instance.update(true);
|
instance.update(true);
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { appendChildren, createDiv, removeElements, isFunction } from 'support';
|
||||||
|
import {
|
||||||
|
classNameScrollbar,
|
||||||
|
classNameScrollbarHorizontal,
|
||||||
|
classNameScrollbarVertical,
|
||||||
|
classNameScrollbarTrack,
|
||||||
|
classNameScrollbarHandle,
|
||||||
|
} from 'classnames';
|
||||||
|
import { getEnvironment, ScrollbarsInitializationStrategy } from 'environment';
|
||||||
|
import { OSTarget, ScrollbarsInitialization } from 'typings';
|
||||||
|
import { StructureSetup } from 'setups/structureSetup';
|
||||||
|
|
||||||
|
export interface ScrollbarStructure {
|
||||||
|
_scrollbar: HTMLElement;
|
||||||
|
_track: HTMLElement;
|
||||||
|
_handle: HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScrollbarsSetup {
|
||||||
|
_horizontalScrollbarStructure: ScrollbarStructure;
|
||||||
|
_verticalScrollbarStructure: ScrollbarStructure;
|
||||||
|
_destroy: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateScrollbarDOM = (scrollbarClassName: string): ScrollbarStructure => {
|
||||||
|
const scrollbar = createDiv(`${classNameScrollbar} ${scrollbarClassName}`);
|
||||||
|
const track = createDiv(classNameScrollbarTrack);
|
||||||
|
const handle = createDiv(classNameScrollbarHandle);
|
||||||
|
|
||||||
|
appendChildren(scrollbar, track);
|
||||||
|
appendChildren(track, handle);
|
||||||
|
|
||||||
|
return {
|
||||||
|
_scrollbar: scrollbar,
|
||||||
|
_track: track,
|
||||||
|
_handle: handle,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createScrollbarsSetup = (target: OSTarget | ScrollbarsInitialization, structureSetup: StructureSetup): ScrollbarsSetup => {
|
||||||
|
const { _getInitializationStrategy } = getEnvironment();
|
||||||
|
const { _scrollbarsSlot: environmentScrollbarSlot } = _getInitializationStrategy() as ScrollbarsInitializationStrategy;
|
||||||
|
const { _targetObj, _targetCtx } = structureSetup;
|
||||||
|
const { _target, _host, _viewport } = _targetObj;
|
||||||
|
const initializationScrollbarSlot = !_targetCtx._targetIsElm && (target as ScrollbarsInitialization).scrollbarsSlot;
|
||||||
|
const initializationScrollbarSlotResult = isFunction(initializationScrollbarSlot)
|
||||||
|
? initializationScrollbarSlot(_target, _host, _viewport)
|
||||||
|
: initializationScrollbarSlot;
|
||||||
|
const evaluatedScrollbarSlot =
|
||||||
|
initializationScrollbarSlotResult ||
|
||||||
|
(isFunction(environmentScrollbarSlot) ? environmentScrollbarSlot(_target, _host, _viewport) : environmentScrollbarSlot) ||
|
||||||
|
_host;
|
||||||
|
|
||||||
|
const horizontalScrollbarStructure = generateScrollbarDOM(classNameScrollbarHorizontal);
|
||||||
|
const verticalScrollbarStructure = generateScrollbarDOM(classNameScrollbarVertical);
|
||||||
|
|
||||||
|
const { _scrollbar: horizontalScrollbar } = horizontalScrollbarStructure;
|
||||||
|
const { _scrollbar: verticalScrollbar } = verticalScrollbarStructure;
|
||||||
|
|
||||||
|
appendChildren(evaluatedScrollbarSlot, horizontalScrollbar);
|
||||||
|
appendChildren(evaluatedScrollbarSlot, verticalScrollbar);
|
||||||
|
|
||||||
|
return {
|
||||||
|
_horizontalScrollbarStructure: horizontalScrollbarStructure,
|
||||||
|
_verticalScrollbarStructure: verticalScrollbarStructure,
|
||||||
|
_destroy() {
|
||||||
|
removeElements([horizontalScrollbar, verticalScrollbar]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
insertAfter,
|
insertAfter,
|
||||||
addClass,
|
addClass,
|
||||||
parent,
|
parent,
|
||||||
isUndefined,
|
indexOf,
|
||||||
removeElements,
|
removeElements,
|
||||||
removeClass,
|
removeClass,
|
||||||
push,
|
push,
|
||||||
@@ -15,6 +15,8 @@ import {
|
|||||||
insertBefore,
|
insertBefore,
|
||||||
attr,
|
attr,
|
||||||
isBoolean,
|
isBoolean,
|
||||||
|
isFunction,
|
||||||
|
keys,
|
||||||
} from 'support';
|
} from 'support';
|
||||||
import {
|
import {
|
||||||
classNameHost,
|
classNameHost,
|
||||||
@@ -24,8 +26,13 @@ import {
|
|||||||
classNameContent,
|
classNameContent,
|
||||||
classNameViewportScrollbarStyling,
|
classNameViewportScrollbarStyling,
|
||||||
} from 'classnames';
|
} from 'classnames';
|
||||||
import { getEnvironment } from 'environment';
|
import {
|
||||||
import { OSTarget, OSTargetObject, OSTargetElement } from 'typings';
|
getEnvironment,
|
||||||
|
StructureInitializationStaticElement,
|
||||||
|
StructureInitializationDynamicElement,
|
||||||
|
StructureInitializationStrategy,
|
||||||
|
} from 'environment';
|
||||||
|
import { OSTarget, OSTargetElement, StructureInitialization } from 'typings';
|
||||||
|
|
||||||
export interface OSTargetContext {
|
export interface OSTargetContext {
|
||||||
_isTextarea: boolean;
|
_isTextarea: boolean;
|
||||||
@@ -34,15 +41,16 @@ export interface OSTargetContext {
|
|||||||
_bodyElm: HTMLBodyElement;
|
_bodyElm: HTMLBodyElement;
|
||||||
_windowElm: Window;
|
_windowElm: Window;
|
||||||
_documentElm: HTMLDocument;
|
_documentElm: HTMLDocument;
|
||||||
|
_targetIsElm: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreparedOSTargetObject {
|
export interface PreparedOSTargetObject {
|
||||||
_target: OSTargetElement;
|
_target: OSTargetElement;
|
||||||
_host: HTMLElement;
|
_host: HTMLElement;
|
||||||
_viewport: HTMLElement;
|
_viewport: HTMLElement;
|
||||||
_padding: HTMLElement | false | null;
|
_padding: HTMLElement | false;
|
||||||
_content: HTMLElement | false | null;
|
_content: HTMLElement | false;
|
||||||
_viewportArrange: HTMLStyleElement | false | null;
|
_viewportArrange: HTMLStyleElement | false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StructureSetup {
|
export interface StructureSetup {
|
||||||
@@ -51,60 +59,113 @@ export interface StructureSetup {
|
|||||||
_destroy: () => void;
|
_destroy: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let contentArrangeCounter = 0;
|
||||||
|
|
||||||
const unwrap = (elm: HTMLElement | false | null | undefined) => {
|
const unwrap = (elm: HTMLElement | false | null | undefined) => {
|
||||||
appendChildren(parent(elm), contents(elm));
|
appendChildren(parent(elm), contents(elm));
|
||||||
removeElements(elm);
|
removeElements(elm);
|
||||||
};
|
};
|
||||||
|
|
||||||
let contentArrangeCounter = 0;
|
const createUniqueViewportArrangeElement = (): HTMLStyleElement | false => {
|
||||||
const createUniqueViewportArrangeElement = (): HTMLStyleElement => {
|
const { _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment();
|
||||||
const elm = document.createElement('style');
|
/* istanbul ignore next */
|
||||||
|
const create = !_cssCustomProperties && !_nativeScrollbarStyling && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y);
|
||||||
|
const result = create ? document.createElement('style') : false;
|
||||||
|
|
||||||
attr(elm, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`);
|
if (result) {
|
||||||
contentArrangeCounter++;
|
attr(result, 'id', `${classNameViewportArrange}-${contentArrangeCounter}`);
|
||||||
|
contentArrangeCounter++;
|
||||||
return elm;
|
|
||||||
};
|
|
||||||
const evaluateCreationFromStrategy = (initializationValue: HTMLElement | boolean | undefined, strategy: boolean): HTMLElement | false | undefined => {
|
|
||||||
const isBooleanValue = isBoolean(initializationValue);
|
|
||||||
if (isBooleanValue || isUndefined(initializationValue)) {
|
|
||||||
return (isBooleanValue ? initializationValue : strategy) && undefined;
|
|
||||||
}
|
}
|
||||||
return initializationValue as HTMLElement;
|
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createStructureSetup = (target: OSTarget | OSTargetObject): StructureSetup => {
|
const staticCreationFromStrategy = (
|
||||||
const { _getInitializationStrategy, _nativeScrollbarStyling, _nativeScrollbarIsOverlaid, _cssCustomProperties } = getEnvironment();
|
target: OSTargetElement,
|
||||||
const { _padding: paddingNeeded, _content: contentNeeded } = _getInitializationStrategy();
|
initializationValue: HTMLElement | undefined,
|
||||||
|
strategy: StructureInitializationStaticElement,
|
||||||
|
elementClass: string
|
||||||
|
): HTMLElement => {
|
||||||
|
const result = initializationValue ? initializationValue : isFunction(strategy) ? strategy(target) : (strategy as null);
|
||||||
|
return result ? result : createDiv(elementClass);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dynamicCreationFromStrategy = (
|
||||||
|
target: OSTargetElement,
|
||||||
|
initializationValue: HTMLElement | boolean | undefined,
|
||||||
|
strategy: StructureInitializationDynamicElement,
|
||||||
|
elementClass: string,
|
||||||
|
defaultValue: boolean
|
||||||
|
): HTMLElement | false => {
|
||||||
|
const takeInitializationValue = isBoolean(initializationValue) || initializationValue;
|
||||||
|
const result = takeInitializationValue ? (initializationValue as boolean | HTMLElement) : isFunction(strategy) ? strategy(target) : strategy;
|
||||||
|
|
||||||
|
if (result === null) {
|
||||||
|
return defaultValue ? createDiv(elementClass) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result === true ? createDiv(elementClass) : result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createStructureSetup = (target: OSTarget | StructureInitialization): StructureSetup => {
|
||||||
|
const { _getInitializationStrategy, _nativeScrollbarStyling } = getEnvironment();
|
||||||
|
const {
|
||||||
|
_host: hostInitializationStrategy,
|
||||||
|
_viewport: viewportInitializationStrategy,
|
||||||
|
_padding: paddingInitializationStrategy,
|
||||||
|
_content: contentInitializationStrategy,
|
||||||
|
} = _getInitializationStrategy() as StructureInitializationStrategy;
|
||||||
const targetIsElm = isHTMLElement(target);
|
const targetIsElm = isHTMLElement(target);
|
||||||
const osTargetObj: Partial<PreparedOSTargetObject> = targetIsElm
|
const targetStructureInitialization = target as StructureInitialization;
|
||||||
? ({} as Partial<PreparedOSTargetObject>)
|
const targetElement = targetIsElm ? (target as OSTargetElement) : targetStructureInitialization.target;
|
||||||
: {
|
const isTextarea = is(targetElement, 'textarea');
|
||||||
_host: (target as OSTargetObject).host,
|
const isBody = !isTextarea && is(targetElement, 'body');
|
||||||
_target: (target as OSTargetObject).target,
|
const ownerDocument: HTMLDocument = targetElement!.ownerDocument;
|
||||||
_viewport: (target as OSTargetObject).viewport,
|
const bodyElm = ownerDocument.body as HTMLBodyElement;
|
||||||
_padding: evaluateCreationFromStrategy((target as OSTargetObject).padding, paddingNeeded),
|
const wnd = ownerDocument.defaultView as Window;
|
||||||
_content: evaluateCreationFromStrategy((target as OSTargetObject).content, contentNeeded),
|
const evaluatedTargetObj: PreparedOSTargetObject = {
|
||||||
};
|
_target: targetElement,
|
||||||
|
_host: isTextarea
|
||||||
if (targetIsElm) {
|
? staticCreationFromStrategy(targetElement, targetStructureInitialization.host, hostInitializationStrategy, classNameHost)
|
||||||
const viewport = createDiv(classNameViewport);
|
: (targetElement as HTMLElement),
|
||||||
const padding = paddingNeeded && createDiv(classNamePadding);
|
_viewport: staticCreationFromStrategy(targetElement, targetStructureInitialization.viewport, viewportInitializationStrategy, classNameViewport),
|
||||||
const content = contentNeeded && createDiv(classNameContent);
|
_padding: dynamicCreationFromStrategy(
|
||||||
|
targetElement,
|
||||||
osTargetObj._target = target as OSTargetElement;
|
targetStructureInitialization.padding,
|
||||||
osTargetObj._padding = padding;
|
paddingInitializationStrategy,
|
||||||
osTargetObj._viewport = viewport;
|
classNamePadding,
|
||||||
osTargetObj._content = content;
|
!_nativeScrollbarStyling // default value for padding
|
||||||
}
|
),
|
||||||
|
_content: dynamicCreationFromStrategy(
|
||||||
let { _target, _padding, _viewport, _content } = osTargetObj;
|
targetElement,
|
||||||
|
targetStructureInitialization.content,
|
||||||
|
contentInitializationStrategy,
|
||||||
|
classNameContent,
|
||||||
|
false // default value for content
|
||||||
|
),
|
||||||
|
_viewportArrange: createUniqueViewportArrangeElement(),
|
||||||
|
};
|
||||||
|
const ctx: OSTargetContext = {
|
||||||
|
_windowElm: wnd,
|
||||||
|
_documentElm: ownerDocument,
|
||||||
|
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
|
||||||
|
_bodyElm: bodyElm,
|
||||||
|
_isTextarea: isTextarea,
|
||||||
|
_isBody: isBody,
|
||||||
|
_targetIsElm: targetIsElm,
|
||||||
|
};
|
||||||
|
const generatedElements = keys(evaluatedTargetObj).reduce((arr, key: string) => {
|
||||||
|
const value = evaluatedTargetObj[key];
|
||||||
|
return push(arr, value && !parent(value) ? value : false);
|
||||||
|
}, [] as HTMLElement[]);
|
||||||
|
const elementIsGenerated = (elm: HTMLElement | false) => (elm ? indexOf(generatedElements, elm) > -1 : null);
|
||||||
|
const { _target, _host, _padding, _viewport, _content, _viewportArrange } = evaluatedTargetObj;
|
||||||
const destroyFns: (() => any)[] = [];
|
const destroyFns: (() => any)[] = [];
|
||||||
const isTextarea = is(_target, 'textarea');
|
const isTextareaHostGenerated = isTextarea && elementIsGenerated(_host);
|
||||||
const isBody = !isTextarea && is(_target, 'body');
|
const targetContents = isTextarea
|
||||||
const _host = (isTextarea ? osTargetObj._host || createDiv() : _target) as HTMLElement;
|
? _target
|
||||||
const getTargetContents = (contentSlot: HTMLElement) => (isTextarea ? (_target as HTMLTextAreaElement) : contents(contentSlot as HTMLElement));
|
: contents([_content, _viewport, _padding, _host, _target].find((elm) => elementIsGenerated(elm) === false));
|
||||||
const isTextareaHostGenerated = isTextarea && _host !== osTargetObj._host;
|
const contentSlot = _content || _viewport;
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -116,88 +177,48 @@ export const createStructureSetup = (target: OSTarget | OSTargetObject): Structu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetIsElm) {
|
appendChildren(contentSlot, targetContents);
|
||||||
const contentSlot = _content || _viewport;
|
appendChildren(_host, _padding);
|
||||||
appendChildren(contentSlot, getTargetContents(_target!));
|
appendChildren(_padding || _host, _viewport);
|
||||||
appendChildren(_host, _padding);
|
appendChildren(_viewport, _content);
|
||||||
appendChildren(_padding || _host, _viewport);
|
|
||||||
appendChildren(_viewport, _content);
|
|
||||||
|
|
||||||
push(destroyFns, () => {
|
|
||||||
appendChildren(_host, contents(contentSlot));
|
|
||||||
removeElements(_padding || _viewport);
|
|
||||||
removeClass(_host, classNameHost);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const contentContainingElm = _content || _viewport || _padding || _host;
|
|
||||||
const createPadding = isUndefined(_padding);
|
|
||||||
const createViewport = isUndefined(_viewport);
|
|
||||||
const createContent = isUndefined(_content);
|
|
||||||
const targetContents = getTargetContents(contentContainingElm);
|
|
||||||
|
|
||||||
_padding = osTargetObj._padding = createPadding ? createDiv() : _padding;
|
|
||||||
_viewport = osTargetObj._viewport = createViewport ? createDiv() : _viewport;
|
|
||||||
_content = osTargetObj._content = createContent ? createDiv() : _content;
|
|
||||||
|
|
||||||
appendChildren(_host, _padding);
|
|
||||||
appendChildren(_padding || _host, _viewport);
|
|
||||||
appendChildren(_viewport, _content);
|
|
||||||
|
|
||||||
const contentSlot = _content || _viewport;
|
|
||||||
appendChildren(contentSlot, targetContents);
|
|
||||||
|
|
||||||
push(destroyFns, () => {
|
|
||||||
if (createContent) {
|
|
||||||
unwrap(_content);
|
|
||||||
}
|
|
||||||
if (createViewport) {
|
|
||||||
unwrap(_viewport);
|
|
||||||
}
|
|
||||||
if (createPadding) {
|
|
||||||
unwrap(_padding);
|
|
||||||
}
|
|
||||||
removeClass(_host, classNameHost);
|
|
||||||
removeClass(_padding, classNamePadding);
|
|
||||||
removeClass(_viewport, classNameViewport);
|
|
||||||
removeClass(_content, classNameContent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
addClass(_host, classNameHost);
|
addClass(_host, classNameHost);
|
||||||
addClass(_padding, classNamePadding);
|
addClass(_padding, classNamePadding);
|
||||||
addClass(_viewport, classNameViewport);
|
addClass(_viewport, classNameViewport);
|
||||||
addClass(_content, classNameContent);
|
addClass(_content, classNameContent);
|
||||||
|
|
||||||
const ownerDocument: HTMLDocument = _target!.ownerDocument;
|
push(destroyFns, () => {
|
||||||
const bodyElm = ownerDocument.body as HTMLBodyElement;
|
if (targetIsElm) {
|
||||||
const wnd = ownerDocument.defaultView as Window;
|
appendChildren(_host, contents(contentSlot));
|
||||||
const ctx: OSTargetContext = {
|
removeElements(_padding || _viewport);
|
||||||
_windowElm: wnd,
|
removeClass(_host, classNameHost);
|
||||||
_documentElm: ownerDocument,
|
} else {
|
||||||
_htmlElm: parent(bodyElm) as HTMLHtmlElement,
|
if (elementIsGenerated(_content)) {
|
||||||
_bodyElm: bodyElm,
|
unwrap(_content);
|
||||||
_isTextarea: isTextarea,
|
}
|
||||||
_isBody: isBody,
|
if (elementIsGenerated(_viewport)) {
|
||||||
};
|
unwrap(_viewport);
|
||||||
// @ts-ignore
|
}
|
||||||
const obj: PreparedOSTargetObject = {
|
if (elementIsGenerated(_padding)) {
|
||||||
...osTargetObj,
|
unwrap(_padding);
|
||||||
_host,
|
}
|
||||||
};
|
removeClass(_host, classNameHost);
|
||||||
|
removeClass(_padding, classNamePadding);
|
||||||
|
removeClass(_viewport, classNameViewport);
|
||||||
|
removeClass(_content, classNameContent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (_nativeScrollbarStyling) {
|
if (_nativeScrollbarStyling) {
|
||||||
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
|
push(destroyFns, removeClass.bind(0, _viewport, classNameViewportScrollbarStyling));
|
||||||
} else if (!_cssCustomProperties && (_nativeScrollbarIsOverlaid.x || _nativeScrollbarIsOverlaid.y)) {
|
}
|
||||||
const viewportArrangeElm = createUniqueViewportArrangeElement();
|
if (_viewportArrange) {
|
||||||
|
insertBefore(_viewport, _viewportArrange);
|
||||||
insertBefore(_viewport, viewportArrangeElm);
|
push(destroyFns, removeElements.bind(0, _viewportArrange));
|
||||||
push(destroyFns, removeElements.bind(0, viewportArrangeElm));
|
|
||||||
|
|
||||||
obj._viewportArrange = viewportArrangeElm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_targetObj: obj,
|
_targetObj: evaluatedTargetObj,
|
||||||
_targetCtx: ctx,
|
_targetCtx: ctx,
|
||||||
_destroy: () => {
|
_destroy: () => {
|
||||||
runEach(destroyFns);
|
runEach(destroyFns);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@import './sizeobserver.scss';
|
@import './sizeobserver.scss';
|
||||||
@import './trinsicobserver.scss';
|
@import './trinsicobserver.scss';
|
||||||
|
@import './scrollbars.scss';
|
||||||
|
|
||||||
.os-environment {
|
.os-environment {
|
||||||
--os-custom-prop: -1;
|
--os-custom-prop: -1;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -32,7 +32,7 @@ export function isBoolean(obj: any): obj is boolean {
|
|||||||
return typeof obj === 'boolean';
|
return typeof obj === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFunction(obj: any): obj is (...args: Array<unknown>) => unknown {
|
export function isFunction(obj: any): obj is (...args: any[]) => any {
|
||||||
return typeof obj === 'function';
|
return typeof obj === 'function';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
export type PlainObject<T = any> = { [name: string]: T };
|
export type PlainObject<T = any> = { [name: string]: T };
|
||||||
|
|
||||||
export type StyleObject<CustomCssProps = ''> = {
|
export type StyleObject<CustomCssProps = ''> = {
|
||||||
@@ -10,6 +12,19 @@ export type InternalVersionOf<T> = {
|
|||||||
|
|
||||||
export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static elements MUST be present.
|
||||||
|
*/
|
||||||
|
type StructureInitializationStaticElement = HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic element CAN be present.
|
||||||
|
* If its a element the element will be handled as the repsective element.
|
||||||
|
* True means that the respective dynamic element is forced to be generated.
|
||||||
|
* False means that the respective dynamic element is forced NOT to be generated.
|
||||||
|
*/
|
||||||
|
type StructureInitializationDynamicElement = HTMLElement | boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object for special initialization.
|
* Object for special initialization.
|
||||||
*
|
*
|
||||||
@@ -18,19 +33,29 @@ export type OSTargetElement = HTMLElement | HTMLTextAreaElement;
|
|||||||
* If element is provided, the provided element takes all its responsibilities.
|
* 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.
|
* DOM hierarchy isn't checked in this case, its assumed that hieararchy is correct in such a case.
|
||||||
*
|
*
|
||||||
* Undefined means that the plugin decides whether the respective element needs to be added or can be savely omitted.
|
* Undefined means that the environment initialization strategy for the respective element is used.
|
||||||
* True means that even if the plugin would decide to not generate the element, the element is still generated.
|
|
||||||
* False means that event if the plugin would decide to generate the element, the element won't be generated.
|
|
||||||
*/
|
*/
|
||||||
export interface OSTargetObject {
|
export interface StructureInitialization {
|
||||||
target: OSTargetElement;
|
target: OSTargetElement;
|
||||||
host?: HTMLElement;
|
host?: StructureInitializationStaticElement; // only relevant for textarea
|
||||||
padding?: HTMLElement | boolean;
|
viewport?: StructureInitializationStaticElement;
|
||||||
viewport?: HTMLElement;
|
padding?: StructureInitializationDynamicElement;
|
||||||
content?: HTMLElement | boolean;
|
content?: StructureInitializationDynamicElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OSTarget = OSTargetElement | OSTargetObject;
|
/**
|
||||||
|
* Object for special initialization.
|
||||||
|
*
|
||||||
|
* scrollbarsSlot is the element to which the scrollbars are applied to. If null or undefined the plugin decides by itself whats the scrollbars slot.
|
||||||
|
*/
|
||||||
|
export interface ScrollbarsInitialization {
|
||||||
|
scrollbarsSlot?: null | HTMLElement | ((target: OSTargetElement, host: HTMLElement, viewport: HTMLElement) => null | HTMLElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OSInitializationObject extends StructureInitialization, ScrollbarsInitialization {
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OSTarget = OSTargetElement | OSInitializationObject;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
export namespace OverlayScrollbars {
|
export namespace OverlayScrollbars {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8790,10 +8790,10 @@ typescript@^3.9.3:
|
|||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||||
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||||
|
|
||||||
typescript@^4.2.4:
|
typescript@^4.3.2:
|
||||||
version "4.2.4"
|
version "4.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
||||||
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
|
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@^1.0.4:
|
unicode-canonical-property-names-ecmascript@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
|
|||||||
Reference in New Issue
Block a user