improve initialization

This commit is contained in:
Rene Haas
2022-07-24 21:50:34 +02:00
parent 027ae348ef
commit 772e6782fe
16 changed files with 1866 additions and 184 deletions
@@ -7,6 +7,7 @@ export const dataAttributeHostOverflowX = `${dataAttributeHost}-overflow-x`;
export const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`; export const dataAttributeHostOverflowY = `${dataAttributeHost}-overflow-y`;
export const dataValueHostOverflowVisible = 'overflowVisible'; export const dataValueHostOverflowVisible = 'overflowVisible';
export const dataValueHostScrollbarHidden = 'scrollbarHidden'; export const dataValueHostScrollbarHidden = 'scrollbarHidden';
export const dataValueHostUpdating = 'updating';
export const classNamePadding = 'os-padding'; export const classNamePadding = 'os-padding';
export const classNameViewport = 'os-viewport'; export const classNameViewport = 'os-viewport';
export const classNameViewportArrange = `${classNameViewport}-arrange`; export const classNameViewportArrange = `${classNameViewport}-arrange`;
+17 -14
View File
@@ -28,7 +28,7 @@ import {
} from 'classnames'; } from 'classnames';
import { Options, defaultOptions } from 'options'; import { Options, defaultOptions } from 'options';
import { DeepPartial } from 'typings'; import { DeepPartial } from 'typings';
import { DefaultInitialization } from 'initialization'; import { Initialization } from 'initialization';
import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins'; import { getPlugins, ScrollbarsHidingPluginInstance, scrollbarsHidingPluginName } from 'plugins';
type EnvironmentEventMap = { type EnvironmentEventMap = {
@@ -42,11 +42,11 @@ export interface InternalEnvironment {
readonly _rtlScrollBehavior: { n: boolean; i: boolean }; readonly _rtlScrollBehavior: { n: boolean; i: boolean };
readonly _flexboxGlue: boolean; readonly _flexboxGlue: boolean;
readonly _cssCustomProperties: boolean; readonly _cssCustomProperties: boolean;
readonly _staticDefaultInitialization: DefaultInitialization; readonly _staticDefaultInitialization: Initialization;
readonly _staticDefaultOptions: Options; readonly _staticDefaultOptions: Options;
_addListener(listener: EventListener<EnvironmentEventMap, '_'>): () => void; _addListener(listener: EventListener<EnvironmentEventMap, '_'>): () => void;
_getDefaultInitialization(): DefaultInitialization; _getDefaultInitialization(): Initialization;
_setDefaultInitialization(newInitialization: DeepPartial<DefaultInitialization>): void; _setDefaultInitialization(newInitialization: DeepPartial<Initialization>): void;
_getDefaultOptions(): Options; _getDefaultOptions(): Options;
_setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void; _setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
} }
@@ -152,15 +152,18 @@ const createEnvironment = (): InternalEnvironment => {
x: nativeScrollbarsSize.x === 0, x: nativeScrollbarsSize.x === 0,
y: nativeScrollbarsSize.y === 0, y: nativeScrollbarsSize.y === 0,
}; };
const defaultInitialization = { const staticDefaultInitialization: Initialization = {
host: null,
padding: !nativeScrollbarsHiding, padding: !nativeScrollbarsHiding,
viewport: (target) => nativeScrollbarsHiding && target === target.ownerDocument.body && target,
content: false, content: false,
scrollbarsSlot: true,
cancel: { cancel: {
nativeScrollbarsOverlaid: true, nativeScrollbarsOverlaid: true,
body: null, body: null,
}, },
}; };
const defaultDefaultOptions = assignDeep({}, defaultOptions); const staticDefaultOptions = assignDeep({}, defaultOptions);
const env: InternalEnvironment = { const env: InternalEnvironment = {
_nativeScrollbarsSize: nativeScrollbarsSize, _nativeScrollbarsSize: nativeScrollbarsSize,
@@ -170,20 +173,20 @@ const createEnvironment = (): InternalEnvironment => {
_rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm), _rtlScrollBehavior: getRtlScrollBehavior(envElm, envChildElm),
_flexboxGlue: getFlexboxGlue(envElm, envChildElm), _flexboxGlue: getFlexboxGlue(envElm, envChildElm),
_addListener: (listener) => addEvent('_', listener), _addListener: (listener) => addEvent('_', listener),
_getDefaultInitialization: assignDeep<DefaultInitialization, DefaultInitialization>.bind( _getDefaultInitialization: assignDeep<Initialization, Initialization>.bind(
0, 0,
{} as DefaultInitialization, {} as Initialization,
defaultInitialization staticDefaultInitialization
), ),
_setDefaultInitialization(newInitializationStrategy) { _setDefaultInitialization(newInitializationStrategy) {
assignDeep(defaultInitialization, newInitializationStrategy); assignDeep(staticDefaultInitialization, newInitializationStrategy);
}, },
_getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, defaultDefaultOptions), _getDefaultOptions: assignDeep<Options, Options>.bind(0, {} as Options, staticDefaultOptions),
_setDefaultOptions(newDefaultOptions) { _setDefaultOptions(newDefaultOptions) {
assignDeep(defaultDefaultOptions, newDefaultOptions); assignDeep(staticDefaultOptions, newDefaultOptions);
}, },
_staticDefaultInitialization: assignDeep({}, defaultInitialization), _staticDefaultInitialization: assignDeep({}, staticDefaultInitialization),
_staticDefaultOptions: assignDeep({}, defaultDefaultOptions), _staticDefaultOptions: assignDeep({}, staticDefaultOptions),
}; };
removeAttr(envElm, 'style'); removeAttr(envElm, 'style');
@@ -1,41 +1,31 @@
import { isBoolean, isFunction, isNull, isUndefined } from 'support'; import { isFunction, isHTMLElement, isNull, isUndefined } from 'support';
import type { import type { StructureInitialization } from 'setups/structureSetup';
StructureInitialization, import type { ScrollbarsInitialization } from 'setups/scrollbarsSetup';
DefaultStructureInitialization,
} from 'setups/structureSetup';
import type {
ScrollbarsInitialization,
DefaultScrollbarsInitialization,
} from 'setups/scrollbarsSetup';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { DeepPartial } from 'typings'; import { DeepPartial } from 'typings';
import { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements'; import { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
type StaticInitialization = HTMLElement | null | undefined; type StaticInitialization = HTMLElement | false | null;
type DynamicInitialization = HTMLElement | boolean | null | undefined; type DynamicInitialization = HTMLElement | boolean | null;
export type CancelInitialization = {
cancel: {
nativeScrollbarsOverlaid: boolean | undefined;
body: boolean | null | undefined;
};
};
export type InitializationTargetElement = HTMLElement | HTMLTextAreaElement; export type InitializationTargetElement = HTMLElement | HTMLTextAreaElement;
export type InitializationTargetObject = StructureInitialization & export type Initialization = Omit<StructureInitialization, 'target'> &
ScrollbarsInitialization & ScrollbarsInitialization & {
DeepPartial<CancelInitialization>; cancel: {
nativeScrollbarsOverlaid: boolean;
body: boolean | null;
};
};
export type InitializationTargetObject = DeepPartial<Initialization> &
Pick<StructureInitialization, 'target'>;
export type InitializationTarget = InitializationTargetElement | InitializationTargetObject; export type InitializationTarget = InitializationTargetElement | InitializationTargetObject;
export type DefaultInitialization = DefaultStructureInitialization &
DefaultScrollbarsInitialization &
CancelInitialization;
/** /**
* Static elements MUST be present. * Static elements MUST be present.
* Null or undefined behave like if this element wasn't specified during initialization. * With false, null or undefined the element will be generated, otherwise the specified element is taken.
*/ */
export type StaticInitializationElement<Args extends any[]> = export type StaticInitializationElement<Args extends any[]> =
| ((...args: Args) => StaticInitialization) | ((...args: Args) => StaticInitialization)
@@ -43,17 +33,14 @@ export type StaticInitializationElement<Args extends any[]> =
/** /**
* Dynamic element CAN be present. * Dynamic element CAN be present.
* If its a element the element will be handled as the repsective element. * If its a element the element will be taken as the repsective element.
* True means that the respective dynamic element is forced to be generated. * With true the element will be generated.
* False means that the respective dynamic element is forced NOT to be generated. * With false, null or undefined the element won't be generated.
* Null or undefined behave like if this element wasn't specified during initialization.
*/ */
export type DynamicInitializationElement<Args extends any[]> = export type DynamicInitializationElement<Args extends any[]> =
| ((...args: Args) => DynamicInitialization) | ((...args: Args) => DynamicInitialization)
| DynamicInitialization; | DynamicInitialization;
export type DefaultInitializtationElement<InitElm> = Exclude<InitElm, HTMLElement>;
export type FallbackInitializtationElement< export type FallbackInitializtationElement<
InitElm extends StaticInitializationElement<any> | DynamicInitializationElement<any> InitElm extends StaticInitializationElement<any> | DynamicInitializationElement<any>
> = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any > = Extract<InitElm, (...args: any[]) => any> extends (...args: infer P) => any
@@ -66,40 +53,42 @@ const resolveInitialization = <T>(value: any, args: any): T =>
const staticInitializationElement = <T extends StaticInitializationElement<any>>( const staticInitializationElement = <T extends StaticInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>, args: Parameters<Extract<T, (...args: any[]) => any>>,
fallbackStaticInitializationElement: FallbackInitializtationElement<T>, fallbackStaticInitializationElement: FallbackInitializtationElement<T>,
defaultStaticInitializationElementStrategy?: DefaultInitializtationElement<T>, defaultStaticInitializationElementStrategy: T,
staticInitializationElementValue?: T | false staticInitializationElementValue?: T
): HTMLElement => ): HTMLElement => {
resolveInitialization<StaticInitialization>( const staticInitialization = isUndefined(staticInitializationElementValue)
staticInitializationElementValue || ? defaultStaticInitializationElementStrategy
resolveInitialization<StaticInitialization>(defaultStaticInitializationElementStrategy, args), : staticInitializationElementValue;
const resolvedInitialization = resolveInitialization<StaticInitialization>(
staticInitialization,
args args
) || fallbackStaticInitializationElement.apply(0, args); );
return resolvedInitialization || fallbackStaticInitializationElement();
};
const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>( const dynamicInitializationElement = <T extends DynamicInitializationElement<any>>(
args: Parameters<Extract<T, (...args: any[]) => any>>, args: Parameters<Extract<T, (...args: any[]) => any>>,
fallbackDynamicInitializationElement: FallbackInitializtationElement<T>, fallbackDynamicInitializationElement: FallbackInitializtationElement<T>,
defaultDynamicInitializationElementStrategy?: DefaultInitializtationElement<T>, defaultDynamicInitializationElementStrategy: T,
dynamicInitializationElementValue?: T | false dynamicInitializationElementValue?: T
): HTMLElement | false => { ): HTMLElement | false => {
let result = resolveInitialization<DynamicInitialization>( const dynamicInitialization = isUndefined(dynamicInitializationElementValue)
dynamicInitializationElementValue, ? defaultDynamicInitializationElementStrategy
: dynamicInitializationElementValue;
const resolvedInitialization = resolveInitialization<DynamicInitialization>(
dynamicInitialization,
args args
); );
return (
if (isNull(result) || isUndefined(result)) { !!resolvedInitialization &&
result = resolveInitialization<DynamicInitialization>( (isHTMLElement(resolvedInitialization)
defaultDynamicInitializationElementStrategy, ? resolvedInitialization
args : fallbackDynamicInitializationElement())
); );
}
return result === true || isNull(result) || isUndefined(result)
? fallbackDynamicInitializationElement.apply(0, args)
: result;
}; };
const cancelInitialization = ( const cancelInitialization = (
cancelInitializationValue: DeepPartial<CancelInitialization['cancel']> | false | null | undefined, cancelInitializationValue: DeepPartial<Initialization['cancel']> | false | null | undefined,
structureSetupElements: StructureSetupElementsObj structureSetupElements: StructureSetupElementsObj
): boolean => { ): boolean => {
const { nativeScrollbarsOverlaid, body } = cancelInitializationValue || {}; const { nativeScrollbarsOverlaid, body } = cancelInitializationValue || {};
@@ -110,7 +99,7 @@ const cancelInitialization = (
const resolvedNativeScrollbarsOverlaid = const resolvedNativeScrollbarsOverlaid =
nativeScrollbarsOverlaid ?? defaultNativeScrollbarsOverlaid; nativeScrollbarsOverlaid ?? defaultNativeScrollbarsOverlaid;
const resolvedDocumentScrollingElement = isBoolean(body) || isNull(body) ? body : defaultbody; const resolvedDocumentScrollingElement = isUndefined(body) ? defaultbody : body;
const finalNativeScrollbarsOverlaid = const finalNativeScrollbarsOverlaid =
(_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y) && (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y) &&
@@ -21,12 +21,7 @@ import {
} from 'plugins'; } from 'plugins';
import { addInstance, getInstance, removeInstance } from 'instances'; import { addInstance, getInstance, removeInstance } from 'instances';
import type { DeepPartial, OverflowStyle } from 'typings'; import type { DeepPartial, OverflowStyle } from 'typings';
import { import { InitializationTarget, Initialization, cancelInitialization } from 'initialization';
InitializationTarget,
InitializationTargetObject,
DefaultInitialization,
cancelInitialization,
} from 'initialization';
import type { import type {
InitialEventListeners as GeneralInitialEventListeners, InitialEventListeners as GeneralInitialEventListeners,
EventListener as GeneralEventListener, EventListener as GeneralEventListener,
@@ -34,7 +29,7 @@ import type {
export interface OverlayScrollbarsStatic { export interface OverlayScrollbarsStatic {
( (
target: InitializationTarget | InitializationTargetObject, target: InitializationTarget,
options?: DeepPartial<Options>, options?: DeepPartial<Options>,
eventListeners?: GeneralInitialEventListeners<EventListenerMap> eventListeners?: GeneralInitialEventListeners<EventListenerMap>
): OverlayScrollbars; ): OverlayScrollbars;
@@ -50,11 +45,11 @@ export interface Environment {
rtlScrollBehavior: { n: boolean; i: boolean }; rtlScrollBehavior: { n: boolean; i: boolean };
flexboxGlue: boolean; flexboxGlue: boolean;
cssCustomProperties: boolean; cssCustomProperties: boolean;
staticDefaultInitialization: DefaultInitialization; staticDefaultInitialization: Initialization;
staticDefaultOptions: Options; staticDefaultOptions: Options;
getDefaultInitialization(): DefaultInitialization; getDefaultInitialization(): Initialization;
setDefaultInitialization(newDefaultInitialization: DeepPartial<DefaultInitialization>): void; setDefaultInitialization(newDefaultInitialization: DeepPartial<Initialization>): void;
getDefaultOptions(): Options; getDefaultOptions(): Options;
setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void; setDefaultOptions(newDefaultOptions: DeepPartial<Options>): void;
} }
@@ -29,7 +29,6 @@ import type { InitializationTarget } from 'initialization';
import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements'; import type { StructureSetupElementsObj } from 'setups/structureSetup/structureSetup.elements';
import type { import type {
ScrollbarsInitialization, ScrollbarsInitialization,
DefaultScrollbarsInitialization,
ScrollbarsDynamicInitializationElement, ScrollbarsDynamicInitializationElement,
} from 'setups/scrollbarsSetup/scrollbarsSetup.initialization'; } from 'setups/scrollbarsSetup/scrollbarsSetup.initialization';
import { StyleObject } from 'typings'; import { StyleObject } from 'typings';
@@ -86,8 +85,7 @@ export const createScrollbarsSetupElements = (
structureSetupElements: StructureSetupElementsObj structureSetupElements: StructureSetupElementsObj
): ScrollbarsSetupElements => { ): ScrollbarsSetupElements => {
const { _getDefaultInitialization } = getEnvironment(); const { _getDefaultInitialization } = getEnvironment();
const { scrollbarsSlot: defaultScrollbarSlot } = const { scrollbarsSlot: defaultScrollbarSlot } = _getDefaultInitialization();
_getDefaultInitialization() as DefaultScrollbarsInitialization;
const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements; const { _documentElm, _target, _host, _viewport, _targetIsElm } = structureSetupElements;
const scrollbarSlot = _targetIsElm ? null : (target as ScrollbarsInitialization).scrollbarsSlot; const scrollbarSlot = _targetIsElm ? null : (target as ScrollbarsInitialization).scrollbarsSlot;
const evaluatedScrollbarSlot = const evaluatedScrollbarSlot =
@@ -1,8 +1,4 @@
import type { import type { InitializationTargetElement, DynamicInitializationElement } from 'initialization';
InitializationTargetElement,
DefaultInitializtationElement,
DynamicInitializationElement,
} from 'initialization';
export type ScrollbarsDynamicInitializationElement = DynamicInitializationElement< export type ScrollbarsDynamicInitializationElement = DynamicInitializationElement<
[target: InitializationTargetElement, host: HTMLElement, viewport: HTMLElement] [target: InitializationTargetElement, host: HTMLElement, viewport: HTMLElement]
@@ -17,9 +13,5 @@ export type ScrollbarsDynamicInitializationElement = DynamicInitializationElemen
* Null or Undefined means that the environment initialization strategy is used. * Null or Undefined means that the environment initialization strategy is used.
*/ */
export interface ScrollbarsInitialization { export interface ScrollbarsInitialization {
scrollbarsSlot?: ScrollbarsDynamicInitializationElement; scrollbarsSlot: ScrollbarsDynamicInitializationElement;
} }
export type DefaultScrollbarsInitialization = {
[K in keyof ScrollbarsInitialization]: DefaultInitializtationElement<ScrollbarsInitialization[K]>;
};
@@ -1,9 +1,21 @@
import { rAF, cAF, isFunction, on, runEachAndClear, setT, clearT } from 'support'; import {
rAF,
cAF,
isFunction,
on,
runEachAndClear,
setT,
clearT,
parent,
scrollLeft,
scrollTop,
} from 'support';
import { createState, createOptionCheck } from 'setups/setups'; import { createState, createOptionCheck } from 'setups/setups';
import { import {
createScrollbarsSetupElements, createScrollbarsSetupElements,
ScrollbarsSetupElement, ScrollbarsSetupElement,
ScrollbarsSetupElementsObj, ScrollbarsSetupElementsObj,
ScrollbarStructure,
} from 'setups/scrollbarsSetup/scrollbarsSetup.elements'; } from 'setups/scrollbarsSetup/scrollbarsSetup.elements';
import { import {
classNamesScrollbarVisible, classNamesScrollbarVisible,
@@ -18,7 +30,7 @@ import type {
} from 'options'; } from 'options';
import type { Setup, StructureSetupState, StructureSetupStaticState } from 'setups'; import type { Setup, StructureSetupState, StructureSetupStaticState } from 'setups';
import type { InitializationTarget } from 'initialization'; import type { InitializationTarget } from 'initialization';
import type { OverflowStyle } from 'typings'; import type { OverflowStyle, StyleObject } from 'typings';
// eslint-disable-next-line @typescript-eslint/no-empty-interface // eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ScrollbarsSetupState {} export interface ScrollbarsSetupState {}
@@ -121,10 +133,20 @@ export const createScrollbarsSetup = (
target, target,
structureSetupState._elements structureSetupState._elements
); );
const { _host, _viewport } = structureSetupState._elements; const { _host, _viewport, _viewportIsTarget, _isBody } = structureSetupState._elements;
const { _horizontal, _vertical } = elements; const { _horizontal, _vertical } = elements;
const { _addRemoveClass: addRemoveClassHorizontal, _handleStyle: styleHorizontal } = _horizontal; const { _addRemoveClass: addRemoveClassHorizontal, _handleStyle: styleHorizontal } = _horizontal;
const { _addRemoveClass: addRemoveClassVertical, _handleStyle: styleVertical } = _vertical; const { _addRemoveClass: addRemoveClassVertical, _handleStyle: styleVertical } = _vertical;
const styleScrollbarPosition = (structure: ScrollbarStructure) => {
const { _scrollbar } = structure;
const elm = _viewportIsTarget && _isBody && parent(_scrollbar) === _viewport && _scrollbar;
return [
elm,
{
transform: elm ? `translate(${scrollLeft(_viewport)}px, ${scrollTop(_viewport)}px)` : '',
},
] as [HTMLElement | false, StyleObject];
};
const manageScrollbarsAutoHide = (removeAutoHide: boolean, delayless?: boolean) => { const manageScrollbarsAutoHide = (removeAutoHide: boolean, delayless?: boolean) => {
clearAutoTimeout(); clearAutoTimeout();
if (removeAutoHide) { if (removeAutoHide) {
@@ -146,6 +168,7 @@ export const createScrollbarsSetup = (
mouseInHost = autoHideIsLeave; mouseInHost = autoHideIsLeave;
mouseInHost && manageScrollbarsAutoHide(true); mouseInHost && manageScrollbarsAutoHide(true);
}; };
const destroyFns: (() => void)[] = [ const destroyFns: (() => void)[] = [
clearScrollTimeout, clearScrollTimeout,
clearAutoTimeout, clearAutoTimeout,
@@ -181,6 +204,9 @@ export const createScrollbarsSetup = (
autoHideNotNever && !mouseInHost && manageScrollbarsAutoHide(false); autoHideNotNever && !mouseInHost && manageScrollbarsAutoHide(false);
}); });
}); });
_viewportIsTarget && styleHorizontal(styleScrollbarPosition);
_viewportIsTarget && styleVertical(styleScrollbarPosition);
}), }),
]; ];
const scrollbarsSetupState = getState.bind(0) as (() => ScrollbarsSetupState) & const scrollbarsSetupState = getState.bind(0) as (() => ScrollbarsSetupState) &
@@ -19,8 +19,6 @@ import {
removeAttr, removeAttr,
attrClass, attrClass,
hasAttrClass, hasAttrClass,
ResizeObserverConstructor,
hasOwnProperty,
noop, noop,
} from 'support'; } from 'support';
import { import {
@@ -38,9 +36,12 @@ import type { ScrollbarsHidingPluginInstance } from 'plugins/scrollbarsHidingPlu
import { import {
staticInitializationElement as generalStaticInitializationElement, staticInitializationElement as generalStaticInitializationElement,
dynamicInitializationElement as generalDynamicInitializationElement, dynamicInitializationElement as generalDynamicInitializationElement,
} from 'initialization';
import type {
InitializationTarget,
InitializationTargetElement,
InitializationTargetObject, InitializationTargetObject,
} from 'initialization'; } from 'initialization';
import type { InitializationTarget, InitializationTargetElement } from 'initialization';
import type { import type {
StructureDynamicInitializationElement, StructureDynamicInitializationElement,
StructureStaticInitializationElement, StructureStaticInitializationElement,
@@ -93,18 +94,18 @@ export const createStructureSetupElements = (
const createUniqueViewportArrangeElement = const createUniqueViewportArrangeElement =
scrollbarsHidingPlugin && scrollbarsHidingPlugin._createUniqueViewportArrangeElement; scrollbarsHidingPlugin && scrollbarsHidingPlugin._createUniqueViewportArrangeElement;
const { const {
host: defaultHostInitializationStrategy, host: defaultHostInitialization,
viewport: defaultViewportInitializationStrategy, viewport: defaultViewportInitialization,
padding: defaultPaddingInitializationStrategy, padding: defaultPaddingInitialization,
content: defaultContentInitializationStrategy, content: defaultContentInitialization,
} = _getDefaultInitialization(); } = _getDefaultInitialization();
const targetIsElm = isHTMLElement(target); const targetIsElm = isHTMLElement(target);
const targetStructureInitialization = (targetIsElm ? {} : target) as InitializationTargetObject; const targetStructureInitialization = (targetIsElm ? {} : target) as InitializationTargetObject;
const { const {
host: hostInitializationStrategy, host: hostInitialization,
padding: paddingInitializationStrategy, padding: paddingInitialization,
viewport: viewportInitializationStrategy, viewport: viewportInitialization,
content: contentInitializationStrategy, content: contentInitialization,
} = targetStructureInitialization; } = targetStructureInitialization;
const targetElement = targetIsElm ? target : targetStructureInitialization.target; const targetElement = targetIsElm ? target : targetStructureInitialization.target;
@@ -112,9 +113,6 @@ export const createStructureSetupElements = (
const ownerDocument = targetElement.ownerDocument; const ownerDocument = targetElement.ownerDocument;
const isBody = targetElement === ownerDocument.body; const isBody = targetElement === ownerDocument.body;
const wnd = ownerDocument.defaultView as Window; const wnd = ownerDocument.defaultView as Window;
const singleElmSupport = isBody
? _nativeScrollbarsHiding
: !!ResizeObserverConstructor && !isTextarea && _nativeScrollbarsHiding;
const staticInitializationElement = const staticInitializationElement =
generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [ generalStaticInitializationElement<StructureStaticInitializationElement>.bind(0, [
targetElement, targetElement,
@@ -123,43 +121,31 @@ export const createStructureSetupElements = (
generalDynamicInitializationElement<StructureDynamicInitializationElement>.bind(0, [ generalDynamicInitializationElement<StructureDynamicInitializationElement>.bind(0, [
targetElement, targetElement,
]); ]);
const viewportElement = [ const viewportElement = staticInitializationElement(
staticInitializationElement( createNewDiv,
createNewDiv, defaultViewportInitialization,
defaultViewportInitializationStrategy, viewportInitialization
isBody && !hasOwnProperty(targetStructureInitialization, 'viewport') );
? targetElement
: viewportInitializationStrategy
),
staticInitializationElement(createNewDiv, defaultViewportInitializationStrategy),
staticInitializationElement(createNewDiv),
].filter((potentialViewport) =>
singleElmSupport ? true : potentialViewport !== targetElement
)[0];
const viewportIsTarget = viewportElement === targetElement; const viewportIsTarget = viewportElement === targetElement;
const evaluatedTargetObj: StructureSetupElementsObj = { const evaluatedTargetObj: StructureSetupElementsObj = {
_target: targetElement, _target: targetElement,
_host: isTextarea _host: isTextarea
? staticInitializationElement( ? staticInitializationElement(createNewDiv, defaultHostInitialization, hostInitialization)
createNewDiv,
defaultHostInitializationStrategy,
hostInitializationStrategy
)
: (targetElement as HTMLElement), : (targetElement as HTMLElement),
_viewport: viewportElement, _viewport: viewportElement,
_padding: _padding:
!viewportIsTarget && !viewportIsTarget &&
dynamicInitializationElement( dynamicInitializationElement(
createNewDiv, createNewDiv,
defaultPaddingInitializationStrategy, defaultPaddingInitialization,
paddingInitializationStrategy paddingInitialization
), ),
_content: _content:
!viewportIsTarget && !viewportIsTarget &&
dynamicInitializationElement( dynamicInitializationElement(
createNewDiv, createNewDiv,
defaultContentInitializationStrategy, defaultContentInitialization,
contentInitializationStrategy contentInitialization
), ),
_viewportArrange: _viewportArrange:
!viewportIsTarget && !viewportIsTarget &&
@@ -2,7 +2,6 @@ import type {
InitializationTargetElement, InitializationTargetElement,
StaticInitializationElement, StaticInitializationElement,
DynamicInitializationElement, DynamicInitializationElement,
DefaultInitializtationElement,
} from 'initialization'; } from 'initialization';
export type StructureStaticInitializationElement = StaticInitializationElement< export type StructureStaticInitializationElement = StaticInitializationElement<
@@ -25,14 +24,8 @@ export type StructureDynamicInitializationElement = DynamicInitializationElement
*/ */
export interface StructureInitialization { export interface StructureInitialization {
target: InitializationTargetElement; target: InitializationTargetElement;
host?: StructureStaticInitializationElement; // only relevant for textarea host: StructureStaticInitializationElement; // only relevant for textarea
viewport?: StructureStaticInitializationElement; viewport: StructureStaticInitializationElement;
padding?: StructureDynamicInitializationElement; padding: StructureDynamicInitializationElement;
content?: StructureDynamicInitializationElement; content: StructureDynamicInitializationElement;
} }
export type DefaultStructureInitialization = {
[K in keyof Omit<StructureInitialization, 'target'>]: DefaultInitializtationElement<
StructureInitialization[K]
>;
};
@@ -27,6 +27,7 @@ import { getEnvironment } from 'environment';
import { import {
dataAttributeHost, dataAttributeHost,
dataValueHostOverflowVisible, dataValueHostOverflowVisible,
dataValueHostUpdating,
classNameViewport, classNameViewport,
classNameOverflowVisible, classNameOverflowVisible,
classNameScrollbar, classNameScrollbar,
@@ -100,6 +101,7 @@ export const createStructureSetupObservers = (
const scrollOffsetY = hasVpStyle && scrollTop(_viewport); const scrollOffsetY = hasVpStyle && scrollTop(_viewport);
_viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible); _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible);
_viewportAddRemoveClass(classNameViewportArrange, ''); _viewportAddRemoveClass(classNameViewportArrange, '');
_viewportAddRemoveClass('', dataValueHostUpdating, true);
const contentScroll = scrollSize(_content); const contentScroll = scrollSize(_content);
const viewportScroll = scrollSize(_viewport); const viewportScroll = scrollSize(_viewport);
@@ -107,6 +109,7 @@ export const createStructureSetupObservers = (
_viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, hasOver); _viewportAddRemoveClass(classNameOverflowVisible, dataValueHostOverflowVisible, hasOver);
_viewportAddRemoveClass(classNameViewportArrange, '', hasVpStyle); _viewportAddRemoveClass(classNameViewportArrange, '', hasVpStyle);
_viewportAddRemoveClass('', dataValueHostUpdating, false);
scrollLeft(_viewport, scrollOffsetX); scrollLeft(_viewport, scrollOffsetX);
scrollTop(_viewport, scrollOffsetY); scrollTop(_viewport, scrollOffsetY);
return { return {
@@ -224,7 +227,8 @@ export const createStructureSetupObservers = (
const viewportIsTargetResizeObserver = const viewportIsTargetResizeObserver =
_viewportIsTarget && _viewportIsTarget &&
new ResizeObserverConstructor!(onSizeChanged.bind(0, { _sizeChanged: true })); ResizeObserverConstructor &&
new ResizeObserverConstructor(onSizeChanged.bind(0, { _sizeChanged: true }));
viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.observe(_host); viewportIsTargetResizeObserver && viewportIsTargetResizeObserver.observe(_host);
updateViewportAttrsFromHost(); updateViewportAttrsFromHost();
@@ -1,5 +1,6 @@
import { each, scrollLeft, scrollTop, assignDeep, keys } from 'support'; import { each, scrollLeft, scrollTop, assignDeep, keys } from 'support';
import { getEnvironment } from 'environment'; import { getEnvironment } from 'environment';
import { dataValueHostUpdating } from 'classnames';
import { import {
createTrinsicUpdateSegment, createTrinsicUpdateSegment,
createPaddingUpdateSegment, createPaddingUpdateSegment,
@@ -56,7 +57,7 @@ export const createStructureSetupUpdate = (
structureSetupElements: StructureSetupElementsObj, structureSetupElements: StructureSetupElementsObj,
state: SetupState<StructureSetupState> state: SetupState<StructureSetupState>
): StructureSetupUpdate => { ): StructureSetupUpdate => {
const { _viewport } = structureSetupElements; const { _viewport, _viewportAddRemoveClass } = structureSetupElements;
const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _flexboxGlue } = getEnvironment(); const { _nativeScrollbarsHiding, _nativeScrollbarsOverlaid, _flexboxGlue } = getEnvironment();
const doViewportArrange = const doViewportArrange =
!_nativeScrollbarsHiding && (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y); !_nativeScrollbarsHiding && (_nativeScrollbarsOverlaid.x || _nativeScrollbarsOverlaid.y);
@@ -93,6 +94,7 @@ export const createStructureSetupUpdate = (
const adjustScrollOffset = doViewportArrange || !_flexboxGlue; const adjustScrollOffset = doViewportArrange || !_flexboxGlue;
const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport); const scrollOffsetX = adjustScrollOffset && scrollLeft(_viewport);
const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport); const scrollOffsetY = adjustScrollOffset && scrollTop(_viewport);
_viewportAddRemoveClass('', dataValueHostUpdating, true);
let adaptivedUpdateHints: Required<StructureSetupUpdateHints> = initialUpdateHints; let adaptivedUpdateHints: Required<StructureSetupUpdateHints> = initialUpdateHints;
each(updateSegments, (updateSegment) => { each(updateSegments, (updateSegment) => {
@@ -105,6 +107,7 @@ export const createStructureSetupUpdate = (
scrollLeft(_viewport, scrollOffsetX); scrollLeft(_viewport, scrollOffsetX);
scrollTop(_viewport, scrollOffsetY); scrollTop(_viewport, scrollOffsetY);
_viewportAddRemoveClass('', dataValueHostUpdating);
return adaptivedUpdateHints; return adaptivedUpdateHints;
}; };
@@ -1,3 +1,6 @@
[data-overlayscrollbars~='updating'] > .os-scrollbar {
display: none !important;
}
.os-scrollbar { .os-scrollbar {
contain: strict; contain: strict;
transition: opacity 0.3s, visibility 0.3s, top 0.3s, right 0.3s, bottom 0.3s, left 0.3s; transition: opacity 0.3s, visibility 0.3s, top 0.3s, right 0.3s, bottom 0.3s, left 0.3s;
@@ -42,19 +42,13 @@ const getCSSVal = (elm: HTMLElement, computedStyle: CSSStyleDeclaration, prop: s
? computedStyle[prop] || computedStyle.getPropertyValue(prop) ? computedStyle[prop] || computedStyle.getPropertyValue(prop)
: elm.style[prop]; : elm.style[prop];
const setCSSVal = ( const setCSSVal = (elm: HTMLElement, prop: string, val: string | number): void => {
elm: HTMLElement | false | null | undefined,
prop: string,
val: string | number
): void => {
try { try {
if (elm) { const { style: elmStyle } = elm;
const { style: elmStyle } = elm; if (!isUndefined(elmStyle[prop])) {
if (!isUndefined(elmStyle[prop])) { elmStyle[prop] = adaptCSSVal(prop, val);
elmStyle[prop] = adaptCSSVal(prop, val); } else {
} else { elmStyle.setProperty(prop, val as string);
elmStyle.setProperty(prop, val as string);
}
} }
} catch (e) {} } catch (e) {}
}; };
@@ -96,7 +90,7 @@ export function style<CustomCssProps>(
} }
return getStylesResult; return getStylesResult;
} }
each(keys(styles), (key) => setCSSVal(elm, key, styles[key])); elm && each(keys(styles), (key) => setCSSVal(elm, key, styles[key]));
} }
/** /**
@@ -5,7 +5,8 @@ import {
createStructureSetupElements, createStructureSetupElements,
StructureSetupElementsObj, StructureSetupElementsObj,
} from 'setups/structureSetup/structureSetup.elements'; } from 'setups/structureSetup/structureSetup.elements';
import type { InitializationTarget, DefaultInitializtationElement } from 'initialization'; import { addPlugin, scrollbarsHidingPlugin } from 'plugins';
import type { InitializationTarget } from 'initialization';
import type { import type {
StructureInitialization, StructureInitialization,
StructureStaticInitializationElement, StructureStaticInitializationElement,
@@ -25,6 +26,8 @@ jest.mock('support/compatibility/apis', () => {
}; };
}); });
addPlugin(scrollbarsHidingPlugin);
interface StructureSetupElementsProxy { interface StructureSetupElementsProxy {
input: InitializationTarget; input: InitializationTarget;
elements: StructureSetupElementsObj; elements: StructureSetupElementsObj;
@@ -178,38 +181,33 @@ const assertCorrectSetupElements = (
expect(_content).toBeFalsy(); expect(_content).toBeFalsy();
} }
const { _isTextarea, _isBody, _bodyElm, _htmlElm, _documentElm, _windowElm } = elements; const { _isTextarea, _isBody, _documentElm, _windowElm } = elements;
expect(_isTextarea).toBe(isTextarea); expect(_isTextarea).toBe(isTextarea);
expect(_isBody).toBe(isBody); expect(_isBody).toBe(isBody);
expect(_windowElm).toBe(document.defaultView); expect(_windowElm).toBe(document.defaultView);
expect(_documentElm).toBe(document); expect(_documentElm).toBe(document);
expect(_htmlElm).toBe(document.body.parentElement);
expect(_bodyElm).toBe(document.body);
expect(typeof destroy).toBe('function'); expect(typeof destroy).toBe('function');
const { _nativeScrollbarsHiding, _cssCustomProperties, _getDefaultInitialization } = environment;
const { const {
_nativeScrollbarsHiding: _nativeScrollbarStyling, host: hostInitStrategy,
_cssCustomProperties, viewport: viewportInitStrategy,
_getInitializationStrategy, padding: paddingInitStrategy,
} = environment; content: contentInitStrategy,
const { } = _getDefaultInitialization();
_host: hostInitStrategy,
_viewport: viewportInitStrategy,
_padding: paddingInitStrategy,
_content: contentInitStrategy,
} = _getInitializationStrategy();
const inputIsElement = isHTMLElement(input); const inputIsElement = isHTMLElement(input);
const inputAsObj = input as StructureInitialization; const inputAsObj = input as StructureInitialization;
const styleElm = document.querySelector('style'); const styleElm = document.querySelector('style');
const checkStrategyDependendElements = ( const checkStrategyDependendElements = (
elm: Element | null, elm: Element | null,
inputStrategy: StructureStaticInitializationElement | StructureDynamicInitializationElement, inputStrategy:
| StructureStaticInitializationElement
| StructureDynamicInitializationElement
| undefined,
isStaticStrategy: boolean, isStaticStrategy: boolean,
strategy: strategy: StructureStaticInitializationElement | StructureDynamicInitializationElement,
| DefaultInitializtationElement<StructureStaticInitializationElement>
| DefaultInitializtationElement<StructureDynamicInitializationElement>,
kind: 'padding' | 'viewport' | 'content' | 'host' kind: 'padding' | 'viewport' | 'content' | 'host'
) => { ) => {
const input = isFunction(inputStrategy) ? inputStrategy(target) : inputStrategy; const input = isFunction(inputStrategy) ? inputStrategy(target) : inputStrategy;
@@ -223,8 +221,7 @@ const assertCorrectSetupElements = (
} }
if (input === undefined) { if (input === undefined) {
if (isStaticStrategy) { if (isStaticStrategy) {
strategy = strategy = strategy as StructureStaticInitializationElement;
strategy as DefaultInitializtationElement<StructureStaticInitializationElement>;
if (typeof strategy === 'function') { if (typeof strategy === 'function') {
const result = strategy(target); const result = strategy(target);
if (_viewportIsTarget) { if (_viewportIsTarget) {
@@ -242,8 +239,7 @@ const assertCorrectSetupElements = (
expect(elm).toBeTruthy(); expect(elm).toBeTruthy();
} }
} else { } else {
strategy = strategy = strategy as StructureDynamicInitializationElement;
strategy as DefaultInitializtationElement<StructureDynamicInitializationElement>;
if (typeof strategy === 'function') { if (typeof strategy === 'function') {
const result = strategy(target); const result = strategy(target);
@@ -278,7 +274,7 @@ const assertCorrectSetupElements = (
} }
}; };
if (_nativeScrollbarStyling || _cssCustomProperties) { if (_nativeScrollbarsHiding || _cssCustomProperties) {
expect(styleElm).toBeFalsy(); expect(styleElm).toBeFalsy();
} else { } else {
expect(styleElm).toBeTruthy(); expect(styleElm).toBeTruthy();
@@ -298,11 +298,11 @@ body {
// disable native scrollbar styling detection // disable native scrollbar styling detection
.nss { .nss {
.os-viewport-scrollbar-styled.os-environment { .os-viewport-scrollbar-hidden.os-environment {
scrollbar-width: auto !important; scrollbar-width: auto !important;
} }
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar, .os-viewport-scrollbar-hidden.os-environment::-webkit-scrollbar,
.os-viewport-scrollbar-styled.os-environment::-webkit-scrollbar-corner { .os-viewport-scrollbar-hidden.os-environment::-webkit-scrollbar-corner {
display: block !important; display: block !important;
} }
} }