react-contexify

repository·main·Indexed 22 days ago

https://github.com/fkhadra/react-contexify

A lightweight, customizable context menu library for React (v6.0.0) that supports submenus, keyboard navigation, dark mode, and custom positioning. It provides a set of components including Menu, Item, Separator, and Submenu, along with the useContextMenu hook for programmatically triggering menus.

Tokens
4.4K
Snippets
10
Records
26
Agent score
77%

What's inside react-contexify

  1. Implement a context menu with useContextMenu

    main

    To use react-contexify, you need to define a unique MENU_ID, import the necessary components and the CSS file, and use the useContextMenu hook to trigger the menu.

    1. Define a MENU_ID constant.
    2. Use useContextMenu({ id: MENU_ID }) to get the show function.
    3. Call show({ event, props }) inside your element's onContextMenu handler.
    4. Define the menu structure using <Menu>, <Item>, <Separator>, and <Submenu> components, ensuring the <Menu> has the same id used in the hook.
    import { Menu, Item, Separator, Submenu, useContextMenu } from 'react-contexify';
    import 'react-contexify/ReactContexify.css';
    
    const MENU_ID = 'blahblah';
    
    function App() {
      const { show } = useContextMenu({
        id: MENU_ID,
      });
    
      function handleContextMenu(event){
          show({
            event,
            props: {
                key: 'value'
            }
          })
      }
    
      const handleItemClick = ({ id, event, props }) => {
        switch (id) {
          case "copy":
            console.log(event, props)
            break;
          case "cut":
            console.log(event, props);
            break;
        }
      }
    
      return (
        <div>
        <p onContextMenu={handleContextMenu}>lorem ipsum...</p>  
        <Menu id={MENU_ID}>
          <Item id="copy" onClick={handleItemClick}>Copy</Item>
          <Item id="cut" onClick={handleItemClick}>Cut</Item>
          <Separator />
          <Item disabled>Disabled</Item>
          <Separator />
          <Submenu label="Foobar">
            <Item id="reload" onClick={handleItemClick}>Reload</Item>
            <Item id="something" onClick={handleItemClick}>Do something else</Item>
          </Submenu>
        </Menu>
        </div>
      );
    }
  2. Use Menu components: Item, Separator, and Submenu

    main

    The following components are used to build the menu structure inside a <Menu> component:

    • <Item>: A clickable menu entry. It accepts an id, an onClick handler, and a disabled prop. The onClick handler receives an object containing { id, event, props }.
    • <Separator />: A visual divider between items.
    • <Submenu>: A nested menu that requires a label prop. It contains other <Item> or <Submenu> components.
  3. Configure Menu animations

    main

    The animation prop on the Menu component allows you to control how the menu enters and leaves the DOM.

    • String value: Uses a single animation name for both entry and exit (e.g., animation="fade"). This applies classes .contexify_willEnter-fade and .contexify_willLeave-fade.
    • Object value: Allows specifying different animations for entering and exiting (e.g., animation={{ enter: 'slideIn', exit: 'fadeOut' }}).
    • Disable animations: Pass false to disable all animations, or provide an object with false for a specific phase (e.g., { enter: false, exit: 'fade' }).
  4. Handle Item clicks and access context data

    main

    The onClick callback provides an ItemParams object. This object contains the item's id, the triggerEvent (the event that opened the menu), the event (the click event itself), the props passed from the trigger, and the custom data object defined on the Item.

    function handleItemClick({ id, triggerEvent, event, props, data }: ItemParams<type of props, type of data>){
       // retrieve the id of the Item
       console.log(id) // item-id
    
       // access any other dom attribute
       console.log(event.currentTarget.dataset.foo) // 123
    
       // access the props and the data
       console.log(props, data);
    
       // access the coordinate of the mouse when the menu has been displayed
       const {  clientX, clientY } = triggerEvent;
    }
    
    <Item id="item-id" onClick={handleItemClick} data={{key: 'value'}} data-foo={123} >Something</Item>
  5. Use the react-contexify components

    main

    The react-contexify library provides a set of components to build custom context menus. You can import the following components to construct your menu structure:

    • Menu: The main container for the context menu.
    • Item: An individual clickable action within a menu.
    • Separator: A visual divider between groups of items.
    • Submenu: A menu item that opens a nested menu.
    • RightSlot: A specialized slot for positioning content on the right side of the menu.
  6. Implement keyboard shortcuts for an Item

    main

    Use the keyMatcher prop to implement keyboard shortcuts. The provided callback receives a KeyboardEvent. If the callback returns true, the onClick handler is triggered, and the event is intercepted to prevent default behavior.

    function handleShortcut(e: React.KeyboardEvent<HTMLElement>){
      // let's say we want to match ⌘ + c
      return e.metaKey && e.key === "c";
    }
    
    <Item onClick={doSomething}>Copy <RightSlot>⌘ C</RightSlot></Item>
  7. Configure the Menu component

    main

    The Menu component is the main container for a context menu. It is triggered via the eventManager using a unique id. It supports theming, animations, and boundary checking to ensure the menu stays within the viewport.

    Props

    PropTypeDefaultDescription
    idMenuIdRequiredA unique identifier used to trigger this specific menu via the eventManager.
    childrenReactNodeRequiredThe menu items to be rendered inside the menu.
    themeThemeundefinedThe visual theme. Built-in themes are 'light' and 'dark'. Appends to .contexify_theme-${theme}.
    animationMenuAnimation'fade'Controls entry/exit animations. Can be a string (e.g., 'fade') or an object specifying enter and exit animations. Passing false disables all animations.
    disableBoundariesCheckbooleanfalseIf true, the menu will not automatically reposition itself if it would be rendered outside the screen boundaries.
    preventDefaultOnKeydownbooleantrueIf true, prevents default browser behavior when navigating the menu with the keyboard.
    onVisibilityChange(isVisible: boolean) => voidundefinedCallback function triggered when the menu becomes visible or hidden.
  8. Use ItemTrackerProvider to share item tracking state

    main
    The ItemTrackerProvider component is used to provide an ItemTracker instance to a component tree via React Context. This allows child components to access the item interaction state through the useItemTrackerContext hook. To use it, wrap your component tree with ItemTrackerProvider and pass an ItemTracker instance as the value prop.
  9. Handle item click events with `ItemParams`

    main

    The onClick callback of an Item receives ItemParams. This object provides access to the item's id, the original DOM event, the props passed when the menu was triggered, the item's data, and the triggerEvent (the event that caused the menu to appear).

    function handleItemClick({ id, triggerEvent, event, props, data }: ItemParams<type of props, type of data>){
       // retrieve the id of the item
       console.log(id) // item-id
    
       // access any other dom attribute
       console.log(event.currentTarget.dataset.foo) // 123
    
       // access the props and the data
       console.log(props, data);
    
       // access the coordinate of the mouse when the menu has been displayed
       const {  clientX, clientY } = triggerEvent;
    }
    
    <Item id="item-id" onClick={handleItemClick} data={{key: 'value'}} data-foo={123} >Something</Item>
  10. Use predicates for `disabled` and `hidden` props

    main

    You can pass a boolean or a predicate function to the disabled or hidden props of an Item. The predicate function receives PredicateParams, which includes the props passed during the show() call, the item's data, and the trigger event.

    function isItemDisabled({ triggerEvent, props, data }: PredicateParams<type of props, type of data>): boolean
    <Item disabled={isItemDisabled} data={data}>content</Item>