# All files / src usePageInactive.ts

98.34% Statements 119/121
90.9% Branches 10/11
100% Functions 3/3
98.34% Lines 119/121

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
    1221x
    1x
    1x
    1x
    32x
    32x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    319x
    319x
    319x
    319x
    319x
    319x
    319x
    291x
    41x
    41x
    41x
    250x
    250x
    5x
    5x
    1x
    5x

    5x
    5x
    5x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    250x
    319x
    319x
     "use client";
    import { useEffect } from "react";

    const noop = (): void => {
      // do nothing
    };

    /**
     * When this is set to `"focus"`, the change handler will be fired whenever the
     * window gains or loses focus.
     *
     * When this is set to `visibility`, the change handler will be fired when the
     * browser is no longer partially visible or becomes partially visible.
     *
     * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/visibilityState
     * @remarks \@since 6.0.0
     */
    export type PageInactiveType = "focus" | "visibility";

    /**
     * @remarks \@since 6.0.0
     */
    export interface PageInactiveOptions {
      /**
       * @see {@link PageInactiveType}
       * @defaultValue `"focus"`
       */
      type?: PageInactiveType;

      /**
       * @defaultValue `false`
       */
      disabled?: boolean;

      /**
       * This will be called whenever the page activity changes based on the
       * {@link PageInactiveType}.
       */
      onChange(active: boolean): void;

      /**
       * This will be fired whenever the {@link disabled} state is `true` which can
       * be useful for clearing pending timers or resetting state.
       *
       * Since this is passed to a `useEffect` as a dependency, you might have to
       * wrap this in a `useCallback` if unexpected re-rendering or errors occurs.
       *
       * @defaultValue `() => {}`
       */
      onDisabledCleanup?(): void;
    }

    /**
     * @example
     * ```ts
     * import { usePageInactive } from "@react-md/core";
     * import { useCallback, useEffect, useRef, useState } from "react";
     *
     * function Example(): null {
     *   const [visible, setVisible] = useState(false);
     *   const timeout = useRef<number | undefined>();
     *   const startTimeout = useCallback(() => {
     *     timeout.current = window.setTimeout(() => {
     *       setVisible(false);
     *     }, 10000);
     *   }, []);
     *
     *   usePageInactive({
     *     onChange(active) {
     *       if (!active) {
     *         window.clearTimeout(timeout.current);
     *         setVisible(false);
     *       } else {
     *         startTimeout();
     *       }
     *     }
     *   });
     *
     *   // pretend implementation
     *   return null;
     * }
     * ```
     * @remarks \@since 6.0.0
     */
    export function usePageInactive(options: PageInactiveOptions): void {
      const {
        type = "focus",
        disabled,
        onChange,
        onDisabledCleanup = noop,
      } = options;
      useEffect(() => {
        if (disabled) {
          onDisabledCleanup();
          return;
        }

        const callback = (event: Event): void => {
          let active = document.visibilityState === "visible";
          if (event.type === "blur") {
            active = false;
          } else if (event.type === "focus") {
            active = true;
          }

          onChange(active);
        };

        document.addEventListener("visibilitychange", callback);
        if (type === "focus") {
          window.addEventListener("blur", callback);
          window.addEventListener("focus", callback);
        }

        return () => {
          document.removeEventListener("visibilitychange", callback);
          window.removeEventListener("blur", callback);
          window.removeEventListener("focus", callback);
        };
      }, [disabled, onChange, onDisabledCleanup, type]);
    }