diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts index 473d54d..cc4bcdb 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHub.ts @@ -9,6 +9,8 @@ import { scrollLeft, scrollTop, assignDeep, + keys, + isBoolean, } from 'support'; import { OSOptions } from 'options'; import { getEnvironment } from 'environment'; @@ -20,8 +22,6 @@ import { createOverflowLifecycle } from 'lifecycles/overflowLifecycle'; import { StyleObject, PartialOptions } from 'typings'; import { ScrollbarsSetup } from 'setups/scrollbarsSetup'; import { TriggerEventListener } from 'eventListeners'; -import { SizeObserver } from 'observers/sizeObserver'; -import { TrinsicObserver } from 'observers/trinsicObserver'; export type LifecycleCheckOption = (path: string) => LifecycleOptionInfo; @@ -29,7 +29,7 @@ export type Lifecycle = ( updateHints: LifecycleUpdateHints, checkOption: LifecycleCheckOption, force: boolean -) => Partial | void; +) => Partial | void; export type LifecycleOptionInfo = [T, boolean]; @@ -43,14 +43,11 @@ export interface LifecycleCommunication { _viewportOverflowAmount: WH; } -export interface LifecycleAdaptiveUpdateHints { +export interface LifecycleUpdateHints { _sizeChanged: boolean; _hostMutation: boolean; _contentMutation: boolean; _paddingStyleChanged: boolean; -} - -export interface LifecycleUpdateHints extends LifecycleAdaptiveUpdateHints { _directionIsRTL: CacheValues; _heightIntrinsic: CacheValues; } @@ -79,6 +76,11 @@ const getPropByPath = (obj: any, path: string): T => ? path.split('.').reduce((o, prop) => (o && hasOwnProperty(o, prop) ? o[prop] : undefined), obj) : undefined; +const applyForceToCache = (cacheValues: CacheValues, force?: boolean): CacheValues => [ + cacheValues[0], + force || cacheValues[1], + cacheValues[2], +]; const booleanCacheValuesFallback: CacheValues = [false, false, false]; const lifecycleCommunicationFallback: LifecycleCommunication = { _paddingInfo: { @@ -109,6 +111,26 @@ const lifecycleCommunicationFallback: LifecycleCommunication = { }, }; +const prepareUpdateHints = ( + leading: Required, + adaptive?: Partial, + force?: boolean +): Required => { + const result = {}; + const finalAdaptive = adaptive || {}; + const objKeys = keys(leading).concat(keys(finalAdaptive)); + + each(objKeys, (key) => { + const leadingValue = leading[key]; + const adaptiveValue = finalAdaptive[key]; + result[key] = isBoolean(leadingValue) + ? !!force || !!leadingValue || !!adaptiveValue + : applyForceToCache(leadingValue || booleanCacheValuesFallback, force); + }); + + return result as Required; +}; + export const createLifecycleHub = ( options: OSOptions, triggerListener: TriggerEventListener, @@ -116,8 +138,6 @@ export const createLifecycleHub = ( scrollbarsSetup: ScrollbarsSetup ): LifecycleHubInstance => { let lifecycleCommunication = lifecycleCommunicationFallback; - let sizeObserver: SizeObserver; - let trinsicObserver: false | TrinsicObserver; let updateObserverOptions: UpdateObserverOptions; let destroyObservers: () => void; const { _viewport } = structureSetup._targetObj; @@ -150,27 +170,21 @@ export const createLifecycleHub = ( changedOptions?: Partial, force?: boolean ) => { - let { - // eslint-disable-next-line prefer-const - _directionIsRTL, - // eslint-disable-next-line prefer-const - _heightIntrinsic, - _sizeChanged = force || false, - _hostMutation = force || false, - _contentMutation = force || false, - _paddingStyleChanged = force || false, - } = updateHints || {}; - - const finalDirectionIsRTL = - _directionIsRTL || - (sizeObserver - ? sizeObserver._getCurrentCacheValues(force)._directionIsRTL - : booleanCacheValuesFallback); - const finalHeightIntrinsic = - _heightIntrinsic || - (trinsicObserver - ? trinsicObserver._getCurrentCacheValues(force)._heightIntrinsic - : booleanCacheValuesFallback); + const initialUpdateHints = prepareUpdateHints( + assignDeep( + { + _sizeChanged: false, + _hostMutation: false, + _contentMutation: false, + _paddingStyleChanged: false, + _directionIsRTL: booleanCacheValuesFallback, + _heightIntrinsic: booleanCacheValuesFallback, + }, + updateHints + ), + {}, + force + ); const checkOption: LifecycleCheckOption = (path) => [ getPropByPath(options, path), force || getPropByPath(changedOptions, path) !== undefined, @@ -184,29 +198,13 @@ export const createLifecycleHub = ( updateObserverOptions(checkOption); } + let adaptivedUpdateHints: Required = initialUpdateHints; each(lifecycles, (lifecycle) => { - const { - _sizeChanged: adaptiveSizeChanged, - _hostMutation: adaptiveHostMutation, - _contentMutation: adaptiveContentMutation, - _paddingStyleChanged: adaptivePaddingStyleChanged, - } = lifecycle( - { - _directionIsRTL: finalDirectionIsRTL, - _heightIntrinsic: finalHeightIntrinsic, - _sizeChanged, - _hostMutation, - _contentMutation, - _paddingStyleChanged, - }, - checkOption, - !!force - ) || {}; - - _sizeChanged = adaptiveSizeChanged || _sizeChanged; - _hostMutation = adaptiveHostMutation || _hostMutation; - _contentMutation = adaptiveContentMutation || _contentMutation; - _paddingStyleChanged = adaptivePaddingStyleChanged || _paddingStyleChanged; + adaptivedUpdateHints = prepareUpdateHints( + adaptivedUpdateHints, + lifecycle(adaptivedUpdateHints, checkOption, !!force) || {}, + force + ); }); if (isNumber(scrollOffsetX)) { @@ -218,29 +216,24 @@ export const createLifecycleHub = ( triggerListener('updated', { updateHints: { - sizeChanged: _sizeChanged, - contentMutation: _contentMutation, - hostMutation: _hostMutation, - directionChanged: finalDirectionIsRTL[1], - heightIntrinsicChanged: finalHeightIntrinsic[1], + sizeChanged: adaptivedUpdateHints._sizeChanged, + contentMutation: adaptivedUpdateHints._contentMutation, + hostMutation: adaptivedUpdateHints._hostMutation, + directionChanged: adaptivedUpdateHints._directionIsRTL[1], + heightIntrinsicChanged: adaptivedUpdateHints._heightIntrinsic[1], }, changedOptions: changedOptions || {}, force: !!force, }); }; // eslint-disable-next-line prefer-const - [sizeObserver, trinsicObserver, updateObserverOptions, destroyObservers] = lifecycleHubOservers( - instance, - updateLifecycles - ); + [updateObserverOptions, destroyObservers] = lifecycleHubOservers(instance, updateLifecycles); const update = (changedOptions: Partial, force?: boolean) => updateLifecycles({}, changedOptions, force); const envUpdateListener = update.bind(0, {}, true); addEnvironmentListener(envUpdateListener); - console.log(getEnvironment()); - return { _update: update, _state: () => ({ diff --git a/packages/overlayscrollbars/src/lifecycles/lifecycleHubObservers.ts b/packages/overlayscrollbars/src/lifecycles/lifecycleHubObservers.ts index 9bb803a..2236ecb 100644 --- a/packages/overlayscrollbars/src/lifecycles/lifecycleHubObservers.ts +++ b/packages/overlayscrollbars/src/lifecycles/lifecycleHubObservers.ts @@ -11,23 +11,14 @@ import { CacheValues, } from 'support'; import { getEnvironment } from 'environment'; -import { - createSizeObserver, - SizeObserver, - SizeObserverCallbackParams, -} from 'observers/sizeObserver'; -import { createTrinsicObserver, TrinsicObserver } from 'observers/trinsicObserver'; +import { createSizeObserver, SizeObserverCallbackParams } from 'observers/sizeObserver'; +import { createTrinsicObserver } from 'observers/trinsicObserver'; import { createDOMObserver, DOMObserver } from 'observers/domObserver'; import { LifecycleHub, LifecycleCheckOption, LifecycleUpdateHints } from 'lifecycles/lifecycleHub'; export type UpdateObserverOptions = (checkOption: LifecycleCheckOption) => void; -export type LifecycleHubObservers = [ - SizeObserver, - TrinsicObserver | false, - UpdateObserverOptions, - () => void -]; +export type LifecycleHubObservers = [UpdateObserverOptions, () => void]; // const hostSelector = `.${classNameHost}`; @@ -122,6 +113,7 @@ export const lifecycleHubOservers = ( !_sizeChanged || _appear ? updateLifecycles : updateLifecyclesWithDebouncedAdaptiveUpdateHints; + updateFn({ _sizeChanged, _directionIsRTL: _directionIsRTLCache, @@ -146,9 +138,9 @@ export const lifecycleHubOservers = ( } }; - const trinsicObserver = + const destroyTrinsicObserver = (_content || !_flexboxGlue) && createTrinsicObserver(_host, onTrinsicChanged); - const sizeObserver = createSizeObserver(_host, onSizeChanged, { + const destroySizeObserver = createSizeObserver(_host, onSizeChanged, { _appear: true, _direction: !_nativeScrollbarStyling, }); @@ -212,13 +204,11 @@ export const lifecycleHubOservers = ( updateViewportAttrsFromHost(); return [ - sizeObserver, - trinsicObserver, updateOptions, () => { contentMutationObserver && contentMutationObserver._destroy(); - trinsicObserver && trinsicObserver._destroy(); - sizeObserver._destroy(); + destroyTrinsicObserver && destroyTrinsicObserver(); + destroySizeObserver(); hostMutationObserver._destroy(); }, ]; diff --git a/packages/overlayscrollbars/src/observers/sizeObserver.ts b/packages/overlayscrollbars/src/observers/sizeObserver.ts index 73d7ff3..114ae6a 100644 --- a/packages/overlayscrollbars/src/observers/sizeObserver.ts +++ b/packages/overlayscrollbars/src/observers/sizeObserver.ts @@ -45,12 +45,7 @@ export interface SizeObserverCallbackParams { _appear?: boolean; } -export interface SizeObserver { - _destroy(): void; - _getCurrentCacheValues(force?: boolean): { - _directionIsRTL: CacheValues; - }; -} +export type DestroySizeObserver = () => void; const animationStartEventName = 'animationstart'; const scrollEventName = 'scroll'; @@ -69,7 +64,7 @@ export const createSizeObserver = ( target: HTMLElement, onSizeChangedCallback: (params: SizeObserverCallbackParams) => any, options?: SizeObserverOptions -): SizeObserver => { +): DestroySizeObserver => { const { _direction: observeDirectionChange = false, _appear: observeAppearChange = false } = options || {}; const { _rtlScrollBehavior: rtlScrollBehavior } = getEnvironment(); @@ -271,17 +266,8 @@ export const createSizeObserver = ( prependChildren(target, sizeObserver); - return { - _destroy() { - runEach(offListeners); - removeElements(sizeObserver); - }, - _getCurrentCacheValues(force?: boolean) { - return { - _directionIsRTL: directionIsRTLCache - ? directionIsRTLCache[1](force) // get current cache values - : [false, false, false], - }; - }, + return () => { + runEach(offListeners); + removeElements(sizeObserver); }; }; diff --git a/packages/overlayscrollbars/src/observers/trinsicObserver.ts b/packages/overlayscrollbars/src/observers/trinsicObserver.ts index ea5e9e0..6a135bc 100644 --- a/packages/overlayscrollbars/src/observers/trinsicObserver.ts +++ b/packages/overlayscrollbars/src/observers/trinsicObserver.ts @@ -13,12 +13,7 @@ import { import { createSizeObserver } from 'observers/sizeObserver'; import { classNameTrinsicObserver } from 'classnames'; -export interface TrinsicObserver { - _destroy(): void; - _getCurrentCacheValues(force?: boolean): { - _heightIntrinsic: CacheValues; - }; -} +export type DestroyTrinsicObserver = () => void; const isHeightIntrinsic = (ioEntryOrSize: IntersectionObserverEntry | WH): boolean => (ioEntryOrSize as WH).h === 0 || @@ -34,10 +29,10 @@ const isHeightIntrinsic = (ioEntryOrSize: IntersectionObserverEntry | WH export const createTrinsicObserver = ( target: HTMLElement, onTrinsicChangedCallback: (heightIntrinsic: CacheValues) => any -): TrinsicObserver => { +): DestroyTrinsicObserver => { const trinsicObserver = createDiv(classNameTrinsicObserver); const offListeners: (() => void)[] = []; - const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache({ + const [updateHeightIntrinsicCache] = createCache({ _initialValue: false, }); @@ -72,21 +67,14 @@ export const createTrinsicObserver = ( const newSize = offsetSize(trinsicObserver); triggerOnTrinsicChangedCallback(newSize); }; - push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy); + push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)); onSizeChanged(); } prependChildren(target, trinsicObserver); - return { - _destroy() { - runEach(offListeners); - removeElements(trinsicObserver); - }, - _getCurrentCacheValues(force?: boolean) { - return { - _heightIntrinsic: getCurrentHeightIntrinsicCache(force), - }; - }, + return () => { + runEach(offListeners); + removeElements(trinsicObserver); }; }; diff --git a/packages/overlayscrollbars/tests/playwright/observers/sizeObserver/index.browser.ts b/packages/overlayscrollbars/tests/playwright/observers/sizeObserver/index.browser.ts index 3f210f7..5dde808 100644 --- a/packages/overlayscrollbars/tests/playwright/observers/sizeObserver/index.browser.ts +++ b/packages/overlayscrollbars/tests/playwright/observers/sizeObserver/index.browser.ts @@ -42,7 +42,7 @@ const startBtn: HTMLButtonElement | null = document.querySelector('#start'); const resizesSlot: HTMLButtonElement | null = document.querySelector('#resizes'); const preInitChildren = targetElm?.children.length; -const sizeObserver = createSizeObserver( +const destroySizeObserver = createSizeObserver( targetElm as HTMLElement, ({ _directionIsRTLCache, _sizeChanged }) => { if (_sizeChanged) { @@ -129,17 +129,19 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) if (dirChanged) { await waitForOrFailTest(() => { - const expectedCacheValue = newDir === 'rtl'; + // const expectedCacheValue = newDir === 'rtl'; should.equal( directionIterations, currDirectionIterations + 1, 'Direction change was detected correctly.' ); + /* should.equal( sizeObserver._getCurrentCacheValues()._directionIsRTL[0], expectedCacheValue, 'Direction cache value is correct.' ); + */ }); } @@ -261,7 +263,7 @@ const start = async () => { }); await cleanBoxSizingChange(); - sizeObserver._destroy(); + destroySizeObserver(); should.equal( targetElm?.children.length, preInitChildren, diff --git a/packages/overlayscrollbars/tests/playwright/observers/trinsicObserver/index.browser.ts b/packages/overlayscrollbars/tests/playwright/observers/trinsicObserver/index.browser.ts index e8c6b7e..5682c86 100644 --- a/packages/overlayscrollbars/tests/playwright/observers/trinsicObserver/index.browser.ts +++ b/packages/overlayscrollbars/tests/playwright/observers/trinsicObserver/index.browser.ts @@ -24,18 +24,21 @@ const startBtn: HTMLButtonElement | null = document.querySelector('#start'); const changesSlot: HTMLButtonElement | null = document.querySelector('#changes'); const preInitChildren = targetElm?.children.length; -const trinsicObserver = createTrinsicObserver(targetElm as HTMLElement, (heightIntrinsicCache) => { - const [currentHeightIntrinsic, currentHeightIntrinsicChanged] = heightIntrinsicCache; - if (currentHeightIntrinsicChanged) { - heightIterations += 1; - heightIntrinsic = currentHeightIntrinsic; - } - requestAnimationFrame(() => { - if (changesSlot) { - changesSlot.textContent = heightIterations.toString(); +const destroyTrinsicObserver = createTrinsicObserver( + targetElm as HTMLElement, + (heightIntrinsicCache) => { + const [currentHeightIntrinsic, currentHeightIntrinsicChanged] = heightIntrinsicCache; + if (currentHeightIntrinsicChanged) { + heightIterations += 1; + heightIntrinsic = currentHeightIntrinsic; } - }); -}); + requestAnimationFrame(() => { + if (changesSlot) { + changesSlot.textContent = heightIterations.toString(); + } + }); + } +); const envElmSelectCallback = generateClassChangeSelectCallback(envElm as HTMLElement); const targetElmSelectCallback = generateClassChangeSelectCallback(targetElm as HTMLElement); @@ -75,11 +78,13 @@ const iterate = async (select: HTMLSelectElement | null, afterEach?: () => any) 'Height intrinsic change has been detected correctly.' ); } + /* should.equal( trinsicObserver._getCurrentCacheValues()._heightIntrinsic[0], newHeightIntrinsic, 'Height intrinsic cache value is correct.' ); + */ }); }, afterEach, @@ -148,7 +153,7 @@ const start = async () => { }); await changeWhileHidden(); - trinsicObserver._destroy(); + destroyTrinsicObserver(); should.equal( targetElm?.children.length, preInitChildren,