addons/components/ViewportOverlay.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import * as stylex from '@stylexjs/stylex';
b69ab319import React, {useEffect, useRef} from 'react';
b69ab3110import ReactDOM from 'react-dom';
b69ab3111
b69ab3112const styles = stylex.create({
b69ab3113 root: {
b69ab3114 position: 'absolute',
b69ab3115 width: '100vw',
b69ab3116 height: '100vh',
b69ab3117 pointerEvents: 'none',
b69ab3118 zIndex: 1000,
b69ab3119 },
b69ab3120});
b69ab3121
b69ab3122/**
b69ab3123 * Render `children` as an overlay, in a container that uses absolute positioning.
b69ab3124 * Suitable for tooltips, menus, and dragging elements.
b69ab3125 */
b69ab3126export function ViewportOverlay(props: {
b69ab3127 children: React.ReactNode;
b69ab3128 key?: React.Key | null;
b69ab3129}): React.ReactPortal {
b69ab3130 const {key, children} = props;
b69ab3131 return ReactDOM.createPortal(
b69ab3132 children as Parameters<
b69ab3133 typeof ReactDOM.createPortal
b69ab3134 >[0] /** ReactDOM's understanding of ReactNode seems wrong here */,
b69ab3135 getRootContainer(),
b69ab3136 key == null ? null : `overlay-${key}`,
b69ab3137 ) as React.ReactPortal;
b69ab3138}
b69ab3139
b69ab3140let cachedRoot: HTMLElement | undefined;
b69ab3141const getRootContainer = (): HTMLElement => {
b69ab3142 if (cachedRoot) {
b69ab3143 // memoize since our root component won't change
b69ab3144 return cachedRoot;
b69ab3145 }
b69ab3146 throw new Error(
b69ab3147 'ViewportOverlayRoot not found. Make sure you render it at the root of the tree.',
b69ab3148 );
b69ab3149};
b69ab3150
b69ab3151export function ViewportOverlayRoot() {
b69ab3152 const rootRef = useRef<HTMLDivElement | null>(null);
b69ab3153 useEffect(() => {
b69ab3154 if (rootRef.current) {
b69ab3155 cachedRoot = rootRef.current;
b69ab3156 }
b69ab3157 return () => {
b69ab3158 cachedRoot = undefined;
b69ab3159 };
b69ab3160 }, []);
b69ab3161 return <div ref={rootRef} {...stylex.props(styles.root)} data-testid="viewport-overlay-root" />;
b69ab3162}