All files / utils size.ts

88.46% Statements 46/52
63.15% Branches 12/19
100% Functions 10/10
86.66% Lines 39/45

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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156        94x 94x       94x 94x               94x 313x   313x 313x                                     94x       313x   313x 313x                         94x 16x             94x 18x           94x 20x               94x 24x   24x             94x 645x 645x     644x       644x               94x 94x   94x 94x   94x       626x 626x                             94x 94x 24x 12x 12x                     24x    
/**
 * @module Utils
 */
 
import * as MC from "@lisn/globals/minification-constants";
import * as MH from "@lisn/globals/minification-helpers";
 
import { Box, Dimension, Size } from "@lisn/globals/types";
 
import { waitForMeasureTime } from "@lisn/utils/dom-optimize";
import { createOverlay } from "@lisn/utils/overlays";
 
/**
 * Returns the content box size of the given
 * {@link https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry | ResizeObserverEntry}.
 *
 * @category Size measurements
 */
export const getEntryContentBox = (entry: ResizeObserverEntry): Size => {
  const size = entry.contentBoxSize;
 
  if (size) {
    return getSizeFromInlineBlock(size);
  }
 
  const rect = entry.contentRect;
  return { [MC.S_WIDTH]: rect[MC.S_WIDTH], [MC.S_HEIGHT]: rect[MC.S_HEIGHT] };
};
 
/**
 * Returns the border box size of the given
 * {@link https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry | ResizeObserverEntry}.
 *
 * @param fallbackToContent If the entry does not contain border box
 *                          measurements (depending on browser), then fall back
 *                          to using the content box size. Otherwise (by
 *                          default) will return `NaN` values for width and
 *                          height.
 *
 * @category Size measurements
 */
export const getEntryBorderBox = (
  entry: ResizeObserverEntry,
  fallbackToContent = false,
): Size => {
  const size = entry.borderBoxSize;
 
  if (size) {
    return getSizeFromInlineBlock(size);
  } else IEif (fallbackToContent) {
    return getEntryContentBox(entry);
  }
 
  return { [MC.S_WIDTH]: NaN, [MC.S_HEIGHT]: NaN };
};
 
/**
 * Returns true if the given string is a valid box type.
 *
 * @category Validation
 */
export const isValidBox = (box: string): box is Box =>
  MH.includes(ALL_BOXES, box);
 
/**
 * Returns true if the given string is a valid dimension.
 *
 * @category Validation
 */
export const isValidDimension = (dimension: string): dimension is Dimension =>
  MH.includes(ALL_DIMENSIONS, dimension);
 
/**
 * @ignore
 * @internal
 */
export const tryGetViewportOverlay = (): HTMLElement | null =>
  viewportOverlay ?? null;
 
/**
 * @ignore
 * @internal
 *
 * Exposed via SizeWatcher
 */
export const fetchViewportOverlay = async (): Promise<HTMLElement> => {
  await init();
 
  return viewportOverlay;
};
 
/**
 * @ignore
 * @internal
 */
export const fetchViewportSize = async (realtime = false) => {
  if (!realtime) {
    await waitForMeasureTime();
  }
 
  const root = MH.hasDOM()
    ? (MH.getDocScrollingElement() ?? MH.getBody())
    : null;
 
  return {
    [MC.S_WIDTH]: root?.clientWidth ?? 0,
    [MC.S_HEIGHT]: root?.clientHeight ?? 0,
  };
};
 
// ----------------------------------------
 
const S_INLINE_SIZE = "inlineSize";
const S_BLOCK_SIZE = "blockSize";
 
const ALL_BOXES: Box[] = ["content", "border"] as const;
const ALL_DIMENSIONS: Dimension[] = [MC.S_WIDTH, MC.S_HEIGHT] as const;
 
const getSizeFromInlineBlock = (
  size: ResizeObserverSize | ReadonlyArray<ResizeObserverSize>,
): Size => {
  /* istanbul ignore else */
  if (MH.isIterableObject(size)) {
    return {
      [MC.S_WIDTH]: size[0][S_INLINE_SIZE],
      [MC.S_HEIGHT]: size[0][S_BLOCK_SIZE],
    };
  }
  return {
    // in some browsers inlineSize and blockSize are scalars and nor Arrays
    [MC.S_WIDTH]: (size as { [S_INLINE_SIZE]: number })[S_INLINE_SIZE],
    [MC.S_HEIGHT]: (size as { [S_BLOCK_SIZE]: number })[S_BLOCK_SIZE],
  };
};
 
// ------------------------------
 
let viewportOverlay: HTMLElement;
let initPromise: Promise<void> | null = null;
const init = (): Promise<void> => {
  if (!initPromise) {
    initPromise = (async () => {
      viewportOverlay = await createOverlay({
        id: MH.prefixName("vp-ovrl"),
        style: {
          position: "fixed",
          [MC.S_WIDTH]: "100vw",
          [MC.S_HEIGHT]: "100vh",
        },
      });
    })();
  }
 
  return initPromise;
};