# All files / src/media-queries AppSizeProvider.tsx

100% Statements 139/139
100% Branches 13/13
100% Functions 2/2
100% Lines 139/139

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
    1401x
    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
    835x
    835x
    4x
    4x
    830x
    830x
    830x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    4x
    4x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    318x
    324x
    324x
    324x
    324x
    324x
    324x
    324x
    193x
    193x
    193x
    193x
    193x
    193x
    193x
    324x
    324x
    324x
    324x
    324x
    2x
    2x
    2x
    2x
    2x
    318x
    318x
    318x
     "use client";
    import {
      createContext,
      useContext,
      useMemo,
      type ReactElement,
      type ReactNode,
    } from "react";
    import { useSsr } from "../SsrProvider.js";
    import { useOrientation } from "../useOrientation.js";
    import {
      DEFAULT_APP_SIZE,
      DEFAULT_DESKTOP_LARGE_MIN_WIDTH,
      DEFAULT_DESKTOP_MIN_WIDTH,
      DEFAULT_PHONE_MAX_WIDTH,
      DEFAULT_TABLET_MAX_WIDTH,
      DEFAULT_TABLET_MIN_WIDTH,
      type AppSize,
      type AppSizeQueries,
    } from "./appSize.js";
    import { useMediaQuery } from "./useMediaQuery.js";

    /** @internal */
    interface AppSizeContext extends AppSize {
      __root: boolean;
    }

    const context = createContext<AppSizeContext>({
      __root: false,
      isPhone: false,
      isTablet: false,
      isDesktop: true,
      isLargeDesktop: false,
      isLandscape: true,
    });
    context.displayName = "AppSize";
    const { Provider } = context;

    /**
     * **Note**: The app size object returned by this hook will **not** be equal
     * between renders.
     *
     * @example
     * Simple Example
     * ```tsx
     * import { useAppSize } from "@react-md/core";
     *
     * function Example() {
     *   const { isPhone, isTablet, isDesktop, isLargeDesktop, isLandscape } =
     *     useAppSize()
     *
     *   // do something based on the app size
     *   return null;
     * }
     * ```
     *
     * @returns the current app size.
     * @throws "The `AppSizeProvider` has not been mounted."
     */
    export function useAppSize(): Readonly<AppSize> {
      const { __root, ...appSize } = useContext(context);
      if (!__root) {
        throw new Error("The `AppSizeProvider` has not been mounted.");
      }

      return appSize;
    }

    export interface AppSizeProviderProps extends AppSizeQueries {
      /**
       * @defaultValue `DEFAULT_APP_SIZE`
       * @see {@link DEFAULT_APP_SIZE}
       */
      ssrSize?: Readonly<AppSize>;
      children: ReactNode;
    }

    /**
     * **Client Component**
     *
     * This component should be mounted near the root of your app to determine the
     * current app size based on different media queries.
     */
    export function AppSizeProvider(props: AppSizeProviderProps): ReactElement {
      const {
        ssrSize = DEFAULT_APP_SIZE,
        phoneMaxWidth = DEFAULT_PHONE_MAX_WIDTH,
        tabletMinWidth = DEFAULT_TABLET_MIN_WIDTH,
        tabletMaxWidth = DEFAULT_TABLET_MAX_WIDTH,
        desktopMinWidth = DEFAULT_DESKTOP_MIN_WIDTH,
        desktopLargeMinWidth = DEFAULT_DESKTOP_LARGE_MIN_WIDTH,
        children,
      } = props;
      const { __root } = useContext(context);
      if (__root) {
        throw new Error("The `AppSizeProvider` cannot be mounted multiple times.");
      }

      const ssr = useSsr();
      const matchesDesktop = useMediaQuery(
        `screen and (min-width: ${desktopMinWidth})`
      );
      const matchesLargeDesktop = useMediaQuery(
        `screen and (min-width: ${desktopLargeMinWidth})`
      );
      const matchesTablet = useMediaQuery(
        `screen and (min-width: ${tabletMinWidth}) and (max-width: ${tabletMaxWidth})`
      );
      const matchesPhone = useMediaQuery(
        `screen and (max-width: ${phoneMaxWidth})`
      );
      const isDesktop = matchesDesktop;
      const isTablet = !matchesDesktop && matchesTablet;
      const isPhone = !isTablet && !isDesktop && matchesPhone;
      const isLandscape = useOrientation().includes("landscape");
      const isLargeDesktop = matchesLargeDesktop;

      const appSize = useMemo<AppSizeContext>(
        () => ({
          __root: true,
          isPhone,
          isTablet,
          isDesktop,
          isLargeDesktop,
          isLandscape,
        }),
        [isDesktop, isLandscape, isLargeDesktop, isPhone, isTablet]
      );

      let value = appSize;
      if (ssr || typeof window === "undefined") {
        value = {
          __root: true,
          ...ssrSize,
        };
      }

      return <Provider value={value}>{children}</Provider>;
    }