All files / src/observers trinsicObserver.ts

6.9% Statements 2/29
0% Branches 0/13
0% Functions 0/8
6.9% Lines 2/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95                                                1x                     1x                                                                                                                      
import {
  WH,
  CacheValues,
  createDiv,
  offsetSize,
  runEach,
  prependChildren,
  removeElements,
  createCache,
  push,
  IntersectionObserverConstructor,
} from 'support';
import { createSizeObserver } from 'observers/sizeObserver';
import { classNameTrinsicObserver } from 'classnames';
 
export interface TrinsicObserver {
  _destroy(): void;
  _getCurrentCacheValues(
    force?: boolean
  ): {
    _heightIntrinsic: CacheValues<boolean>;
  };
}
 
const isHeightIntrinsic = (ioEntryOrSize: IntersectionObserverEntry | WH<number>): boolean =>
  (ioEntryOrSize as WH<number>).h === 0 ||
  (ioEntryOrSize as IntersectionObserverEntry).isIntersecting ||
  (ioEntryOrSize as IntersectionObserverEntry).intersectionRatio > 0;
 
/**
 * Creates a trinsic observer which observes changes to intrinsic or extrinsic sizing for the height of the target element.
 * @param target The element which shall be observed.
 * @param onTrinsicChangedCallback The callback which gets called after a change was detected.
 * @returns A object which represents the instance of the trinsic observer.
 */
export const createTrinsicObserver = (
  target: HTMLElement,
  onTrinsicChangedCallback: (heightIntrinsic: CacheValues<boolean>) => any
): TrinsicObserver => {
  const trinsicObserver = createDiv(classNameTrinsicObserver);
  const offListeners: (() => void)[] = [];
  const [updateHeightIntrinsicCache, getCurrentHeightIntrinsicCache] = createCache({
    _initialValue: false,
  });
 
  const triggerOnTrinsicChangedCallback = (
    updateValue?: IntersectionObserverEntry | WH<number>
  ) => {
    if (updateValue) {
      const heightIntrinsic = updateHeightIntrinsicCache(isHeightIntrinsic(updateValue));
      const [, heightIntrinsicChanged] = heightIntrinsic;
 
      if (heightIntrinsicChanged) {
        onTrinsicChangedCallback(heightIntrinsic);
      }
    }
  };
 
  if (IntersectionObserverConstructor) {
    const intersectionObserverInstance: IntersectionObserver = new IntersectionObserverConstructor(
      (entries: IntersectionObserverEntry[]) => {
        if (entries && entries.length > 0) {
          triggerOnTrinsicChangedCallback(entries.pop());
        }
      },
      { root: target }
    );
    intersectionObserverInstance.observe(trinsicObserver);
    push(offListeners, () => {
      intersectionObserverInstance.disconnect();
    });
  } else {
    const onSizeChanged = () => {
      const newSize = offsetSize(trinsicObserver);
      triggerOnTrinsicChangedCallback(newSize);
    };
    push(offListeners, createSizeObserver(trinsicObserver, onSizeChanged)._destroy);
    onSizeChanged();
  }
 
  prependChildren(target, trinsicObserver);
 
  return {
    _destroy() {
      runEach(offListeners);
      removeElements(trinsicObserver);
    },
    _getCurrentCacheValues(force?: boolean) {
      return {
        _heightIntrinsic: getCurrentHeightIntrinsicCache(force),
      };
    },
  };
};