1.0.10 • Published 5 years ago

@burtonator/react-context-menu-wrapper v1.0.10

Weekly downloads
6
License
MIT
Repository
-
Last release
5 years ago

React Context Menu Wrapper

react-context-menu-wrapper version react-context-menu-wrapper downloads

This component provides a simple wrapper for your context menu content. It will handle the right click event (or long press on mobile) and menu positioning, but the rest is up to you.

Gif of the context menu library in action.

Unlike other React context menu packages, this one does not provide any styling or pre-made components - you'll have to style the context menu yourself. This is useful when your CSS framework provides dropdown styling - consider dropdowns from Bootstrap or from Bulma.

Features:

  • Supports global and component-local context menus.
  • Supports long-press for mobile devices.
  • Lets you trigger context menus programmatically.
  • Lets you define context menu precedence.
  • Correctly displays context menus on nested components.
  • Correctly displays context menus near the borders of the window.
  • Does not provide out-of-the-box styling.

Please create an issue if you find a bug or want to suggest a new feature.

Usage examples

Click here to view demos with example code.

Installation

Install the main package:

npm install --save react-context-menu-wrapper

The gist

Here's an example of a simple context menu. It doesn't showcase many features, but it should give you a basic idea of how this component works. This particular context menu doesn't have any buttons - don't worry, it's pretty easy to add them. Take a look at demos and examples to find out more.

Gif of the context menu library in action.

(The context menu in the gif is triggered using a right click and dismissed using a left click.)

// Import our packages, the usual way.
import React, {Component} from 'react';
import {ContextMenuWrapper, prepareContextMenuHandlers} from 'react-context-menu-wrapper';

// Define some styles - remember that `react-context-menu-wrapper` does not provide any styling out-of-the-box.
const contextMenuStyle = {backgroundColor: '#eec185', padding: '10px', boxShadow: '0 3px 5px rgba(0, 0, 0, 0.5)'};
const blueBoxStyle = {backgroundColor: '#3e48f9', color: '#fff', padding: '40px'};
const redBoxStyle = {backgroundColor: '#aa2d35', color: '#fff', padding: '40px'};

class ComponentWithAContextMenu extends Component {
    constructor(props) {
        super(props);

        // Set the initial phrase to some dummy value.
        this.state = {phrase: 'Nothing.'};

        // Create "triggers" for our context menu. Each "trigger" has some unique data associated with it.
        this.redBoxHandlers = prepareContextMenuHandlers({id: 'my-context-menu', data: 'Hello from Lavagirl!'});
        this.blueBoxHandlers = prepareContextMenuHandlers({id: 'my-context-menu', data: 'Hello from Sharkboy!'});
    }

    // Define the logic for when the context menu is shown
    handleContextMenuShow = (data, publicProps) => {
        // 'data' variable contains the phrase we initialised our handlers with.
        this.setState({phrase: data});
        
        // We also have access to the 'publicProps' variable, but we're not going to use it. This variable
        // contains the properties of the context menu. For example, we could find out:
        //   - The ID of the menu (via 'publicProps.id')
        //   - Whether the menu is global (via 'publicProps.global')
    };

    render() {
        return (
            <div style={{fontSize: '1.4rem'}}>
                {/* Render the boxes that will trigger the context menu */}
                <div {...this.blueBoxHandlers} style={blueBoxStyle}>Blue box</div>
                <div {...this.redBoxHandlers} style={redBoxStyle}>Red box</div>

                {/*
                Include the component for the context menu itself. Note that this component doesn't have to be on
                the same level as triggers. In fact, the context menu can even come from a different React tree!
                */}
                <ContextMenuWrapper id="my-context-menu" onShow={this.handleContextMenuShow}>
                    <div style={contextMenuStyle}>
                        <div>The box says: <strong>{this.state.phrase}</strong></div>
                    </div>
                </ContextMenuWrapper>
            </div>
        );
    }
}

ContextMenuWrapper component

ContextMenuWrapper is the component that handles showing and hiding your context menu content on various events, which are triggered either programmatically or through user input. This is the only React component provided by the library, the rest of the functionality is provided as helper functions.

Example usage:

import {ContextMenuWrapper} from 'react-context-menu-wrapper';

const MyComponent = () => (
    <ContextMenuWrapper global={true}
                        onShow={() => console.log('Context menu shown!')}
                        onHidden={() => console.log('Context menu shown!')}>
        <div style={{backgroundColor: 'black', color: 'white'}}>This is a context menu.</div>
    </ContextMenuWrapper>
);

Properties supported by ContextMenuWrapper:

NameTypeDefault valueDescription
idStringNoneA user-defined string that is used to reference this context menu component in helper functions. For example: file-entry-menu.
globalBooleanfalseDetermines whether the context menu can be opened by right clicking (or long pressing on mobile) anywhere on the page. Note that a context menu can have an ID and be global at the same time.
onShowContext Menu CallbackNoneA callback that is called right before this context menu is shown. (see below for Context Menu Callback description)
onHideContext Menu CallbackNoneA callback that is called immediately after this context menu is hidden. (see below for Context Menu Callback description)
hideOnScrollBooleantrueDetermines whether the menu should disappear when document (top level node) is scrolled.
hideOnWindowResizeBooleantrueDetermines whether the menu should disappear when the window is resized.
hideOnSelfClickBooleantrueDetermines whether the context menu should disappear after something inside it was clicked.
hideOnOutsideClickBooleantrueDetermines whether the context menu should disappear after the user has clicked anything outside of it.

ContextMenuCallback is a function of type (data, publicProps) => void. data is the value that was passed to the handlers of the context menu, if any (see prepareContextMenuHandlers(...) below). publicProps is an object containing the values of the properties listed above.

Helper functions

All of the helper functions and enums can be imported from the main package, e.g.:

import {ContextMenuEvent, addContextMenuEventListener} from 'react-context-menu-wrapper';

// Do something with the `prepareContextMenuHandlers(...)` and the others

prepareContextMenuHandlers(params)

Generates event handlers that can be attached to a trigger component (e.g. image thumbnail). Once attached, these handlers will only show the relevant context menu when the component is clicked. Argument types:

  • params: object with keys:
    • id: string. The ID of the context menu the handlers will trigger.
    • data: any value. data can be anything that you want to attach to the trigger. This can be a number, a string, an object, or anything else. The supplied data value will be sent to event listeners and callbacks (see below).

The returned object contains various event handlers and looks similar to this:

const handlers = {
    onContextMenu: /* some handler */,
    onTouchStart: /* some handler */,
    onTouchEnd: /* some handler */,
}

Thanks to this structure, the handlers can be attached to other components using an object spread:

<div {...handlers}>Right click me!</div>

addContextMenuEventListener(id, listener)

Registers a listener for context menu events such as hide and show. Note that this listener doesn't give you any control over these events, it just notifies you that they took place. Argument types:

  • id: string or null. When a string is provided, your listener will be attached to the context menu ID that you have specified. If null is provided, your listener will be attached to global context menus.
  • listener: function of type (eventName, data, publicProps) => void. This function is similar to ContextMenuCallback, except there's an extra argument called eventName. eventName is a value from the ContextMenuEvent enum (see below). data is the value that was passed to the handlers of the context menu, if any (see prepareContextMenuHandlers(...) above). publicProps is an object containing the values of the properties listed in the table in the first section.

Note that a single listener function can listen to multiple IDs, but if you'll try to attach a single listener to the same ID twice, the second call will be ignored and a warning will be printed to the console.

removeContextMenuEventListener(id, listener)

Removes a listener that was previously added. The arguments are identical to the function above.

If the listener you try to remove doesn't exist or was never registered, the operation will just silently succeed. Note that you have provide the exact same listener function that was used to add the listener.

ContextMenuEvent enum

This enum defines the names of the events that the context menu can emit. Available names are Show and Hide. Example usage:

const handleContextMenuEvent = (eventName, data, publicProps) => {
    if (eventName === ContextMenuEvent.Show) {
        doActionX(data, publicProps);
    } else if (eventName === ContextMenuEvent.Hide) {
        doActionY(data, publicProps);
    } else {
        // For forward-compatibility, in case we add new states in the future
        doActionFallback(data, publicProps);
    }
};

showContextMenu(data)

Used when you want to trigger a context menu programmatically. Argument types:

  • data: object with keys:
    • id: string (optional). When a string is provided, triggers the context menu ID you have specified. Otherwise, a global context menu is triggered.
    • data: any value (optional). Supplied data value will be passed to event listeners and callbacks.
    • event: input event (optional). An instance of click or contextmenu event. You can pass it in if you don't want to specify x and y coordinates manually.
    • x: number (optional). The x pixel coordinate at which the context menu will be shown.
    • y: number (optional). The y pixel coordinate at which the context menu will be shown.

Note that if both event and x, y are specified, the latter will be used.

hideAllContextMenus()

Hides all visible context menus.

cancelOtherContextMenus()

Cancel the show event on all menus that were about to be displayed when the function was invoked. This function is meant to be used to prevent context menus from appearing when a specific component is clicked. For example, imagine you have a global context menu that appears if you right click any element on the page. You also have a button component MyButton and you want to make sure no context menu appears when you right click this button. You would achieve this as follows:

<MyButton onContextMenu={cancelOtherContextMenus}>Button with no context menu.</MyButton>

Notes

Whenever it says null, you don't actually have to provide null as the value. As long as the value you provide is falsy, it will be ignored.