React Context Menu Implementation
This challenge requires you to build a reusable context menu component in React. Context menus (also known as right-click menus) are a common UI pattern that provides users with a list of actions relevant to a specific element when right-clicked. Implementing this functionality will enhance user interaction by offering contextual options directly where they are needed.
Problem Description
Your task is to create a React component that displays a customizable context menu when an element is right-clicked. The context menu should be:
- Triggered by Right-Click: The menu should appear when a user right-clicks on a designated target element.
- Positioned Correctly: The menu should be positioned relative to the mouse cursor's position at the time of the right-click.
- Customizable Menu Items: The menu should accept an array of menu items, where each item can have a label and an associated action (a callback function).
- Closable: The menu should disappear when the user clicks anywhere outside of the menu or presses the Escape key.
- Reusable: The component should be designed to be easily integrated into different parts of an application, allowing various target elements to have their own context menus.
- Themed/Stylable: While basic styling will be provided, the component should be designed to allow for easy customization of its appearance.
Key Requirements:
ContextMenuComponent: Create a mainContextMenucomponent.MenuItemComponent (Optional but Recommended): Consider creating a separateMenuItemcomponent for clarity and reusability within the context menu.useContextMenuHook (Optional but Recommended): A custom hook can be beneficial for managing the state of the context menu (visibility, position, menu items).- Event Handling: Implement
onContextMenuevent handlers andonClickOutsidelogic. - Positioning Logic: Calculate the correct
topandleftCSS properties for the menu. - Keyboard Navigation: Handle the Escape key for closing the menu.
Expected Behavior:
- When a target element is right-clicked, the context menu appears near the cursor.
- Clicking on a menu item executes its associated action.
- Clicking outside the menu or pressing Escape closes the menu.
- If the menu is positioned near the edge of the viewport, it should ideally adjust its position to remain within view.
Edge Cases to Consider:
- Empty Menu Items: What happens if the
itemsprop is an empty array? - Menu Overflow: How to handle cases where the menu might extend beyond the viewport boundaries?
- Multiple Context Menus: Ensure that only one context menu is visible at a time if multiple target elements exist.
Examples
Example 1: Basic Usage
Input (Conceptual):
A div element with the class context-target.
ContextMenu component props:
items:[{ label: 'Copy', onClick: () => console.log('Copied!') }, { label: 'Paste', onClick: () => console.log('Pasted!') }]
Output (Visual):
On right-clicking the div, a menu appears with "Copy" and "Paste" options. Clicking "Copy" logs "Copied!" to the console and closes the menu. Clicking outside the menu closes it.
Example 2: Dynamic Menu Items
Input (Conceptual):
A UserCard component.
ContextMenu component props dynamically set based on user role:
items:user.isAdmin ? [{ label: 'Edit User', onClick: editUser }, { label: 'Delete User', onClick: deleteUser }] : [{ label: 'View Profile', onClick: viewProfile }]
Output (Visual):
If the user is an admin, the context menu shows "Edit User" and "Delete User". If not, it shows "View Profile".
Example 3: Menu Positioning near Edge
Input (Conceptual):
A target element at the bottom-right corner of the screen.
ContextMenu component props:
items:[...]
Output (Visual):
The context menu appears, and if its default position would cause it to go off-screen, it shifts upwards and/or leftwards to stay within the viewport.
Constraints
- The solution must be written in TypeScript.
- The solution must use React.
- The
ContextMenucomponent should be functional. - Avoid using external libraries specifically designed for context menus (e.g.,
react-contextmenu). You are to build the core logic yourself. - Basic CSS styling for the menu and menu items will be provided or you can define your own, but the component should be easily styleable via CSS classes.
Notes
- Consider using
useRefto manage DOM elements and event listeners. - The
window.addEventListenerforclickandkeydownwill be crucial for closing the menu. Remember to clean up these listeners when the component unmounts. - The
getBoundingClientRect()method will be useful for positioning. - Think about how to pass the
eventobject to youronClickhandlers if you want to access properties likeevent.preventDefault(). - You'll likely need to manage state for menu visibility and its position.
- Consider how to handle deeply nested menu items if you want to extend the functionality (though not required for the core challenge).