Add a context menu to your react application with ease !
- Demo
- Installation
- Features
- How it works ?
- Add a context menu to a table
- Api
- Migration from v1 to v2
- Release Notes
Live demo here
$ yarn add react-contexify
or
$ npm install --save react-contexify
You also need to include the css file provided. Otherwise it wont work as expected.
import'react-contexify/dist/ReactContexify.min.css'<link rel="stylesheet" href="/dist/ReactContexify.min.css"/>
- The context menu never leave the visible screen. You can reduce the window to check the behavior
- The callback provide an access to the wrapped component
- One menu can be use for multiple targets
- Create as many contextual menu as you want, as long as the id is unique
- Easily customizable. Theme are split into different sass file. Fifteen line of code can be enough to create a theme
- Don't rely on
findDOMNode
import{ContextMenu,Item,Separator,IconFont}from'react-contexify';functiononClick(targetNode,ref,data){// targetNode refer to the html node on which the menu is triggeredconsole.log(targetNode);//ref will be the mounted instance of the wrapped component//If you wrap more than one component, ref will be an array of refconsole.log(ref);// Additionnal data props passed down to the `Item`console.log(data);}// create your menu firstconstMyAwesomeMenu=()=>(<ContextMenuid='menu_id'><ItemleftIcon={<IconFontclassName="fa fa-plus"/>}onClick={onClick}>
Add
</Item><ItemleftIcon={<IconFontclassName="material-icons">remove_circle</IconFont>}onClick={onClick}>
Remove
</Item><Item><IconFontclassName="fa fa-scissors"/>cut</Item><Separator/><Itemdisabled>
Paste
</Item></ContextMenu>);import{ContextMenuProvider,menuProvider}from'react-contexify';//wrap your component with the `ContextMenuProvider`constFoo=()=><ContextMenuProviderid="menu_id">bar</ContextMenuProvider>;constBar=()=><ContextMenuProviderid="menu_id">baz</ContextMenuProvider>;// or you can use the curried function to add the same menu for many components, it's up to youconstaddContextMenu=menuProvider('menu_id');constFoo=addContextMenu(YourComponent);constBar=addContextMenu(YourCompoenent);importReactfrom'react';importMyAwesomeMenufrom'./MyAwesomeMenu';importFoofrom'./Foo';importBarfrom'./Bar';constApp=()=>(<div><Foo/><Bar/><MyAwesomeMenu/></div>)ReactDOM.render(<App/>,document.getElementById('root'));import{ContextMenuProvider}from'react-contexify';//You need to use a tr as a render tag otherwise your browser console will bleed !constTr=(props)=>(<ContextMenuProviderid="menu_id"renderTag="tr"><td>{props.cel1}</td><td>{props.cel2}</td></ContextMenuProvider>);classTableextendsComponent{render(){return(<table><thead><tr><th>Cel 1</th><th>Cel 2</th></tr></thead><tbody><Trcel1="lorem"cel2="ipsum"/><Trcel1="foo"cel2="bar"/></tbody></table>)}}| Props | Type | Default | Required | Possible Value | Description |
|---|---|---|---|---|---|
| id | string|int | - | ✓ | - | Used to identify the corresponding menu |
| children | Item|null | - | ✓ | - | Menu item |
| theme | string | ✘ | light | dark | Theme is appended to react-contexify__theme--${given theme} | |
| animation | string | ✘ | fade | flip | pop | zoom | Animation is appended to .react-contexify__will-enter--${given animation} |
You can set built-in theme and animation using ContextMenu constant as follow :
<ContextMenu id="foo" theme={ContextMenu.THEME.dark} animation={ContextMenu.ANIMATION.pop}> ... </ContextMenu>
| Props | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node | - | ✓ | Any valid node to render(string, react component...) |
| leftIcon | node | ✘ | Any valid node to render(string, react component...) | |
| rightIcon | node | ✘ | Any valid node to render(string, react component...) | |
| disabled | bool | false | ✘ | Disable the item |
| onClick | function | ✘ | Callback when the item is clicked | |
| data | any | ✘ | Additional data that will be passed to the callback |
When an you select an Item your callback will receive 3 parameters: targetNode, refs, data.
- if you wrap a single react component refs will be the mounted instance of the wrapped component
- If you wrap more than one component refs will be an array of ref
ref will be an instance of the react component only if the component is declared as a class
If you use any flux store like redux or mobx stick with it.
For more details about ref please read this
Don't expect any props. It's just a separator xD.
<Separator />
| Props | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node | - | ✘ | Menu item |
| className | string | ✘ | Additional className | |
| style | string | ✘ | Additional style |
The icon font render a i tag.
//example with Font Awesome <IconFontclassName="fa fa-trash"/>//example with material icons<IconFontclassName="material-icons">remove_circle</IconFont>| Props | Type | Default | Required | Possible Value | Description |
|---|---|---|---|---|---|
| id | string or int | - | ✓ | - | Id used to map your component to a context menu |
| renderTag | node | div | ✘ | - | The render tag of the wrapper |
| event | string | onContextMenu | ✘ | Same as React Event (onClick, onContextMenu ...) | Event to trigger the context menu |
| className | string | ✘ | Additional className | ||
| style | object | ✘ | Additional style |
constFoo=()=><ContextMenuProviderid="menu_id"><MyComponent/></ContextMenuProvider>;| Args | Type | Default | Required | Possible Value | Description |
|---|---|---|---|---|---|
| id | string | - | ✓ | - | Id used to map your component to a context menu |
- Function returned by menuProvider expect :
| Args | Type | Default | Required | Possible Value | Description |
|---|---|---|---|---|---|
| targetComponent | React Component | - | ✓ | - | The component on which you want to add a context menu |
| renderTag | node | div | ✘ | - | The render tag of the wrapper |
| event | string | onContextMenu | ✘ | Same as React Event (onClick, onContextMenu ...) | Event to trigger the context menu |
| className | string | ✘ | Additional className | ||
| style | object | ✘ | Additional style |
Breaking changes are a pain for developers but sometimes we have too.
Item Component: You can render whatever you want now, which gives you more control.
//before<Itemlabel="foo"/>// now<Item>foo</Item>onClick callback: Having access to the instance of the react component can be sometimes really useful.
//beforeonClick(targetNode,data)// nowonClick(targetNode,refs,data)- Fixed issue 33
- Removed Proxy Container.
- Fixed for real ! issue 27
- Context menu is now rendered outside of the main react root component to avoid the fixed position being broken by the parent style. For more details please see issue 27
- Simplified implementation Pull Request #22
- Typo fix in documentation
- Pass props down to
menuProviderfunction
- Will now hide menu on mousedown.Relate to issue 19
- Added flag noIdents to cssnano to avoid animation name collision with other libs
- Fixed zoom animation
- Minor code cleanup
- Firefox trigger a click event also on context menu which was making the menu disappear. Relate to issue 16
- Fix issue #14
- conditional rendering was badly tested, shame on me !
- This version introduce breaking changes for the item component
- Upgrade to
prop-types - Tested with jest and enzyme
- Reviewed build system
- The
onClickcallback provide a ref to the wrapped component - Can now use condtionnal rendering for dynamic menu. Relate to issue 12
- Added
IconFontcomponent
- Fixed right click behavior. Relate to issue 11
- Added possibility to set the render tag for the wrapper
- Added a ContextMenuProvider component
- Added possibility to set className and style to ContextMenuProvider
- Removed ContextMenuProvider style.Was too unpredictable
- fixed incorrect PropTypes used
- dead code elimination
Any idea and suggestions are welcome.
Big thanks to Tobias Reich. This project is based on basicContext. A vanilla js context menu.
React Contexify is licensed under MIT.
