# All files / src/portal PortalContainerProvider.tsx

100% Statements 94/94
100% Branches 17/17
100% Functions 3/3
100% Lines 94/94

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
    951x
    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
    1137x
    1137x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    324x
    324x
    324x
    324x
    324x
    174x
    1x
    1x
    1x
    173x
    174x
    1x
    1x
    172x
    172x
    34x
    34x
    34x
    172x
    172x
    172x
    172x
    172x
    172x
    172x
    172x
    172x
    172x
    172x
    324x
    324x
    324x
    324x
    324x
    324x
     "use client";
    import {
      createContext,
      useContext,
      useEffect,
      useState,
      type ReactElement,
      type ReactNode,
      type RefObject,
    } from "react";

    /**
     * @internal
     * @remarks \@since 6.0.0
     */
    export type PortalContainer = Element | DocumentFragment | null;

    export const PORTAL_CONTAINER_ID = "rmd-portal-container";

    let portalContainer: PortalContainer = null;

    const getPortalContainer = (): PortalContainer =>
      typeof window === "undefined" ? null : document.body;

    const context = createContext<PortalContainer>(getPortalContainer());
    context.displayName = "PortalContainer";
    const { Provider } = context;

    /**
     * @internal
     * @remarks \@since 6.0.0
     */
    export function usePortalContainer(): PortalContainer {
      return useContext(context);
    }

    /** @remarks \@since 6.0.0 */
    export interface PortalContainerProviderProps {
      /**
       * An optional container element to use. When this is `undefined`, a
       * `<div id="rmd-portal-container"></div>` will be added as the last child to
       * the `document.body` and be used as the container element.
       */
      container?: PortalContainer | RefObject<PortalContainer>;
      children: ReactNode;
    }

    /**
     * **Client Component**
     *
     * This component allows for all child `Portal` components to render within the
     * same container element. If a custom `container` element is not provided, a
     * `<div id="rmd-portal-container"></div>` will be added as the last child to
     * the `document.body` and be used as the container element.
     *
     * @see {@link Portal}
     * @remarks \@since 6.0.0
     */
    export function PortalContainerProvider(
      props: PortalContainerProviderProps
    ): ReactElement {
      const { container, children } = props;
      const [value, setValue] = useState<PortalContainer>(portalContainer);
      useEffect(() => {
        if (container && "current" in container) {
          setValue(container.current);
          return;
        }

        if (typeof container !== "undefined") {
          return;
        }

        if (!portalContainer) {
          portalContainer = document.createElement("div");
          portalContainer.id = PORTAL_CONTAINER_ID;
        }
        if (!document.body.contains(portalContainer)) {
          document.body.appendChild(portalContainer);
        }

        setValue(portalContainer);

        return () => {
          if (portalContainer && document.body.contains(portalContainer)) {
            document.body.removeChild(portalContainer);
          }
        };
      }, [container]);

      const containerValue =
        (container && "current" in container) || !container ? value : container;
      return <Provider value={containerValue}>{children}</Provider>;
    }