Dilog

Right click here for Menu
<DilogContener rightclick={() => console.log(' Menu 2 clicked!')} className={"border-gray-500/50 border p-2 w-full rounded-md "}> 
    <DilogContent> 
        <span>Right-click me for Menu</span> 
    </DilogContent> 
    <DilogMenuContent className=" w-96  " > 
        <DilogMenuGroup title="hello"> 
            <DilogMenuList >Item A</DilogMenuList> 
            <DilogMenuList>Item B</DilogMenuList> 
            <DilogMenuList >Item A</DilogMenuList> 
            <DilogMenuList>Item B</DilogMenuList> 
        </DilogMenuGroup> 
        <DilogMenuGroup> 
            <DilogMenuList >Item A</DilogMenuList> 
            <DilogMenuList>Item B</DilogMenuList> 
        </DilogMenuGroup> 
    </DilogMenuContent> 
</DilogContener>

/**
 * @file Provides a set of components for creating context menus (right-click menus).
 *
 * This module offers a way to easily implement context menus within your application.
 * It includes components for the container, menu content, menu groups, and individual menu items.
 *
 * @example
 * // Basic usage:
 * <DilogContener rightclick={() => console.log('Menu clicked!')}>
 *   <DilogContent>
 *     <span>Right-click me</span>
 *   </DilogContent>
 *   <DilogMenuContent>
 *     <DilogMenuGroup title="Actions">
 *       <DilogMenuList>Option 1</DilogMenuList>
 *       <DilogMenuList>Option 2</DilogMenuList>
 *     </DilogMenuGroup>
 *   </DilogMenuContent>
 * </DilogContener>
 */
import React, { useEffect, useRef, useState } from 'react';

/**
 * The main container component for the context menu.
 *
 * This component wraps the main content and the context menu content.
 * It handles the right-click event and displays the menu at the appropriate position.
 *
 * @component
 * @param {Object} props - The component's props.
 * @param {string} [props.className] - Additional CSS classes to apply to the container.
 * @param {function} [props.rightclick] - A callback function triggered when the container is right-clicked.
 * @param {React.ReactNode} props.children - The content of the container.
 *   This should be two children: the main content wrapped in `DilogContent` and 
 *   the context menu content wrapped in `DilogMenuContent`.
 */
export default function DilogContener({ className, rightclick, children }) {
    const [DilogIsOpen, setdilogisopen] = useState(false);
    const [dilogPosition, setDilogPosition] = useState({ x: 0, y: 0 });
    const menuRef = useRef(null);

    /**
     * Handles the right-click event on the container.
     *
     * @param {MouseEvent} e - The MouseEvent object.
     */
    const handleRightClick = (e) => {
        e.preventDefault(); // Prevent default right-click menu
        rightclick && rightclick(); // Execute the provided callback
        setdilogisopen(!DilogIsOpen); // Toggle menu visibility
        const rect = e.target.getBoundingClientRect(); // Get container's position

        // Set menu position relative to the container
        setDilogPosition({
            x: e.clientX - rect.left,
            y: e.clientY - rect.top
        });
    };

    /**
     * Handles clicks outside of the context menu.
     *
     * @param {MouseEvent} event - The MouseEvent object.
     */
    const handleClickOutside = (event) => {
        if (menuRef.current && !menuRef.current.contains(event.target)) {
            setdilogisopen(false); // Close the menu if clicked outside
        }
    };

    // Attach and detach the click outside event listener
    useEffect(() => {
        document.addEventListener('mousedown', handleClickOutside);
        return () => {
            document.removeEventListener('mousedown', handleClickOutside);
        };
    }, []);

    return (
        <div
            onContextMenu={handleRightClick}
            className={`relative ${className}`}
        >
            {children[0]} {/* Render the main content */}
            {DilogIsOpen && (
                <div
                    ref={menuRef}
                    style={{ top: `${dilogPosition.y}px`, left: `${dilogPosition.x}px` }}
                    className="absolute transform z-10 "
                >
                    {children[1]} {/* Render the context menu content */}
                </div>
            )}
        </div>
    );
}

/**
 * A wrapper component for the context menu content.
 *
 * This component provides basic styling for the menu.
 *
 * @component
 * @param {Object} props - The component's props.
 * @param {React.ReactNode} props.children - The content of the menu.
 * @param {string} [props.className] - Additional CSS classes to apply to the menu content.
 */
export const DilogMenuContent = ({ children, className }) => {
    return (
        <div className={`rounded-md border border-gray-500 bg-gray-900 ${className} `}>
            {children}
        </div>
    );
};

/**
 * A component to group menu items within the context menu.
 *
 * This component allows you to visually group related menu items and provide a title for the group.
 *
 * @component
 * @param {Object} props - The component's props.
 * @param {React.ReactNode} props.children - The menu items to group.
 * @param {string} [props.title] - The title for the group.
 * @param {string} [props.className] - Additional CSS classes to apply to the group.
 */
export const DilogMenuGroup = ({ children, title, className }) => {
    return (
        <div className={`w-full p-2 flex flex-col gap-1 mb-1 ${className}`}>
            <div className='flex items-center gap-1'>
                <span className='text-gray-500 my-2'>
                    {title}
                </span>
                <hr className='flex-grow border-t border-gray-500' />
            </div>
            {children}
        </div>
    );
};

/**
 * A component for individual menu items within the context menu.
 *
 * @component
 * @param {Object} props - The component's props.
 * @param {React.ReactNode} props.children - The content of the menu item.
 * @param {string} [props.className] - Additional CSS classes to apply to the menu item.
 */
export const DilogMenuList = ({ children, className }) => {
    return (
        <div className={`hover:bg-gray-800 w-full px-1 py-0.5 rounded-sm flex justify-between ${className}`}>
            {children}
        </div>
    );
};

/**
 * A wrapper component for the main content within the `DilogContener`.
 *
 * This component helps to separate the main content from the context menu content.
 *
 * @component
 * @param {Object} props - The component's props.
 * @param {React.ReactNode} props.children - The main content to display.
 * @param {string} [props.className] - Additional CSS classes to apply to the content.
 */
export const DilogContent = ({ children, className }) => {
    return (
        <div className={`w-full h-full ${className}`}>
            {children}
        </div>
    );
};