Home / File/ ContextMenu.js — react Source File

ContextMenu.js — react Source File

Architecture documentation for ContextMenu.js, a javascript file in the react codebase. 6 imports, 1 dependents.

File javascript BabelCompiler Validation 6 imports 1 dependents 3 functions

Entity Profile

Dependency Diagram

graph LR
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9["ContextMenu.js"]
  28f7134a_9071_560d_f1cb_c165426d8d0d["ContextMenuItem.js"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> 28f7134a_9071_560d_f1cb_c165426d8d0d
  ef1c4452_d855_c9fb_c5d9_5374ffee93d1["ContextMenuItem"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> ef1c4452_d855_c9fb_c5d9_5374ffee93d1
  f94c772c_e76f_6e89_bc9b_2d47148e561c["types.js"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> f94c772c_e76f_6e89_bc9b_2d47148e561c
  084ed530_083f_7c69_24af_564299a5e5a0["ContextMenu.css"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> 084ed530_083f_7c69_24af_564299a5e5a0
  ac587885_e294_a1e9_b13f_5e7b920fdb42["react"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> ac587885_e294_a1e9_b13f_5e7b920fdb42
  4d0c4ccc_5970_e7f9_458f_15f0290099b1["react-dom"]
  6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 --> 4d0c4ccc_5970_e7f9_458f_15f0290099b1
  7eedd21d_ae6a_7447_035b_f34143688483["ContextMenuContainer.js"]
  7eedd21d_ae6a_7447_035b_f34143688483 --> 6b15267e_6087_7e9b_ceab_28e7a8ab3cd9
  style 6b15267e_6087_7e9b_ceab_28e7a8ab3cd9 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import * as React from 'react';
import {useLayoutEffect, createRef} from 'react';
import {createPortal} from 'react-dom';

import ContextMenuItem from './ContextMenuItem';

import type {
  ContextMenuItem as ContextMenuItemType,
  ContextMenuPosition,
  ContextMenuRef,
} from './types';

import styles from './ContextMenu.css';

function repositionToFit(element: HTMLElement, x: number, y: number) {
  const ownerWindow = element.ownerDocument.defaultView;
  if (y + element.offsetHeight >= ownerWindow.innerHeight) {
    if (y - element.offsetHeight > 0) {
      element.style.top = `${y - element.offsetHeight}px`;
    } else {
      element.style.top = '0px';
    }
  } else {
    element.style.top = `${y}px`;
  }

  if (x + element.offsetWidth >= ownerWindow.innerWidth) {
    if (x - element.offsetWidth > 0) {
      element.style.left = `${x - element.offsetWidth}px`;
    } else {
      element.style.left = '0px';
    }
  } else {
    element.style.left = `${x}px`;
  }
}

type Props = {
  anchorElementRef: {current: React.ElementRef<any> | null},
  items: ContextMenuItemType[],
  position: ContextMenuPosition,
  hide: () => void,
  ref?: ContextMenuRef,
};

export default function ContextMenu({
  anchorElementRef,
  position,
  items,
  hide,
  ref = createRef(),
}: Props): React.Node {
  // This works on the assumption that ContextMenu component is only rendered when it should be shown
  const anchor = anchorElementRef.current;

  if (anchor == null) {
    throw new Error(
      'Attempted to open a context menu for an element, which is not mounted',
    );
  }

  const ownerDocument = anchor.ownerDocument;
  const portalContainer = ownerDocument.querySelector(
    '[data-react-devtools-portal-root]',
  );

  useLayoutEffect(() => {
    const menu = ((ref.current: any): HTMLElement);

    function hideUnlessContains(event: Event) {
      if (!menu.contains(((event.target: any): Node))) {
        hide();
      }
    }

    ownerDocument.addEventListener('mousedown', hideUnlessContains);
    ownerDocument.addEventListener('touchstart', hideUnlessContains);
    ownerDocument.addEventListener('keydown', hideUnlessContains);

    const ownerWindow = ownerDocument.defaultView;
    ownerWindow.addEventListener('resize', hide);

    repositionToFit(menu, position.x, position.y);

    return () => {
      ownerDocument.removeEventListener('mousedown', hideUnlessContains);
      ownerDocument.removeEventListener('touchstart', hideUnlessContains);
      ownerDocument.removeEventListener('keydown', hideUnlessContains);

      ownerWindow.removeEventListener('resize', hide);
    };
  }, []);

  if (portalContainer == null || items.length === 0) {
    return null;
  }

  return createPortal(
    <div className={styles.ContextMenu} ref={ref}>
      {items.map(({onClick, content}, index) => (
        <ContextMenuItem key={index} onClick={onClick} hide={hide}>
          {content}
        </ContextMenuItem>
      ))}
    </div>,
    portalContainer,
  );
}

Domain

Subdomains

Frequently Asked Questions

What does ContextMenu.js do?
ContextMenu.js is a source file in the react codebase, written in javascript. It belongs to the BabelCompiler domain, Validation subdomain.
What functions are defined in ContextMenu.js?
ContextMenu.js defines 3 function(s): ContextMenu, repositionToFit, type.hide.
What does ContextMenu.js depend on?
ContextMenu.js imports 6 module(s): ContextMenu.css, ContextMenuItem, ContextMenuItem.js, react, react-dom, types.js.
What files import ContextMenu.js?
ContextMenu.js is imported by 1 file(s): ContextMenuContainer.js.
Where is ContextMenu.js in the architecture?
ContextMenu.js is located at packages/react-devtools-shared/src/devtools/ContextMenu/ContextMenu.js (domain: BabelCompiler, subdomain: Validation, directory: packages/react-devtools-shared/src/devtools/ContextMenu).

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free