Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/isaacplmann/ngx-contextmenu

An Angular component to show a context menu on an arbitrary component
https://github.com/isaacplmann/ngx-contextmenu

angular angular-library contextmenu ngx

Last synced: 4 days ago
JSON representation

An Angular component to show a context menu on an arbitrary component

Awesome Lists containing this project

README

        

# ngx-contextmenu

A context menu built with Angular (10) inspired by [ui.bootstrap.contextMenu](https://github.com/Templarian/ui.bootstrap.contextMenu). Bootstrap classes are included in the markup, but there is no explicit dependency on Bootstrap. [Demo](https://isaacplmann.github.io/ngx-contextmenu/) [Stackblitz example](https://stackblitz.com/edit/ngx-contextmenu-example)

## Installation

- `npm install ngx-contextmenu @angular/cdk`
- import ContextMenuModule.forRoot() into your app module
- Make sure to include `` at the top of your `index.html`

## Usage

### Angular 5 support

Please use [email protected] with Angular 5 projects.

### Template

```html


  • Right Click: {{item?.name}}


Say hi!



Bye, {{item?.name}}


Input something:

```

### Component Code

```js
@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo' },
{ name: 'Joe', otherProperty: 'Bar' }
];
@ViewChild(ContextMenuComponent) public basicMenu: ContextMenuComponent;
}
```

## Context Menu Items

- Each context menu item is a `` element with the `contextMenuItem` attribute directive applied.
- If the `item` object is used in the context menu item template, the `let-item` attribute must be applied to the `` element.
** Note: ** Make sure to use the `item?.property` syntax in the template rather than `item.property` as the item will be initially `undefined`.
- Every context menu item emits `execute` events. The `$event` object is of the form `{ event: MouseEvent, item: any }` where `event` is the mouse click event
that triggered the execution and `item` is the current item.
- The `divider` input parameter is optional. Items default to normal menu items. If `divider` is `true`, all the other inputs are ignored.
- The `passive` input parameter is optional. If `passive` is `true`, the menu item will not emit execute events or close
the context menu when clicked.
- The `enabled` input parameter is optional. Items are enabled by default.
This can be a boolean value or a function definition that takes an item and returns a boolean.
- The `visible` input parameter is optional. Items are visible by default. This property enables you to show certain context menu items based on what the data item is.
This can be a boolean value or a function definition that takes an item and returns a boolean.
- Within the template, you have access to any components and variables available in the outer context.

```html


Say hi, {{item?.name}}!
With access to the outside context: {{ outsideValue }}

```
```js
public outsideValue = "something";
public isMenuItemType1(item: any): boolean {
return item.type === 'type1';
}
```

## Sub-menus

You can specify sub-menus like this:

```html


  • Right Click: {{item?.name}}


Say...



...hi!


...hola!


...salut!




Bye, {{item?.name}}


Input something:

```

Notes:
1. The sub `` can not be placed inside the `` that references it.
2. Sub-menus may be nested as deeply as you wish.

## Upgrade from angular2-contextmenu 0.x

1. Change `package.json` to reference `ngx-contextmenu` instead of `angular2-contextmenu`
2. Upgrade to `@angular` 4.x
3. Use `` instead of ``
4. Update any styles that referenced `.angular2-contextmenu` to use `.ngx-contextmenu` instead

**Note:** The imperative way of declaring context menu items has been removed. i.e. You can't pass an `actions` property to `contextMenuService.show.next()`.

## Using `visible` and `enabled` functions

If you need access to properties in your component from within the `enabled` or `visible` functions, you can pass in an arrow function.

```html

```
```js
public outsideValue = "something";
public isMenuItemOutsideValue = (item: any): boolean => {
return item.type === this.outsideValue;
}
```

## Multiple Context Menus
You can use multiple context menus in the same component if you would like.

```html


  • {{item?.name}}

...


  • {{item?.name}}

...

```

```js
@ViewChild('basicMenu') public basicMenu: ContextMenuComponent;
@ViewChild('otherMenu') public otherMenu: ContextMenuComponent;
```

## Context Menu In a Different Component
If your `` component is in a different component from your list, you'll need to wire up the context menu event yourself.

```html


  • Right Click: {{item.name}}


```

```js
import { ContextMenuService } from 'ngx-contextmenu';

@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo' },
{ name: 'Joe', otherProperty: 'Bar' }
];

// Optional
@Input() contextMenu: ContextMenuComponent;

constructor(private contextMenuService: ContextMenuService) {}

public onContextMenu($event: MouseEvent, item: any): void {
this.contextMenuService.show.next({
// Optional - if unspecified, all context menu components will open
contextMenu: this.contextMenu,
event: $event,
item: item,
});
$event.preventDefault();
$event.stopPropagation();
}
}
```

## Triggering the Context Menu with a Different Event
The context menu can be triggered at any point using the method above. For instance, to trigger the context menu
with a left click instead of a right click, use this html:

```html


  • Left Click: {{item.name}}


```

This could be `(keydown)`, `(mouseover)`, or `(myCustomEvent)` as well.

## Positioning the Context Menu around an element
If you want to override the context menu positioning to be appended to an element instead of based on mouse position,
provide an `anchorElement` to the `contextMenuService`. This makes sense if you want to trigger the context menu with
a non-MouseEvent.

```ts
public onContextMenu($event: KeyboardEvent, item: any): void {
this.contextMenuService.show.next({
anchorElement: $event.target,
// Optional - if unspecified, all context menu components will open
contextMenu: this.contextMenu,
event: $event,
item: item,
});
$event.preventDefault();
$event.stopPropagation();
}
```

## Custom Styles

The html that is generated for the context menu looks like this:

```html


```

You can key off of the `ngx-contextmenu` class to create your own styles. Note that the `ul.dropdown-menu` will have inline styles applied for `position`, `display`, `left` and `top` so that it will be positioned at the cursor when you right-click.

```css
.ngx-contextmenu .dropdown-menu {
border: solid 1px chartreuse;
background-color: darkgreen;
padding: 0;
}
.ngx-contextmenu li {
display: block;
border-top: solid 1px chartreuse;
text-transform: uppercase;
text-align: center;
}
.ngx-contextmenu li:first-child {
border-top:none;
}
.ngx-contextmenu a {
color:chartreuse;
display: block;
padding: 0.5em 1em;
}
.ngx-contextmenu a:hover {
color:darkgreen;
background-color:chartreuse;
}
```

## Bootstrap 4

If you're using Bootstrap 4, you can specify a `useBootstrap4` property in the `forRoot` function of the `ContextMenuModule` in order to get the appropriate class names. Like this:

```js
@NgModule({
import: [
ContextMenuModule.forRoot({
useBootstrap4: true,
}),
],
})
export class AppModule {}
```

**Or, if you want to repeat yourself,** you can add a `useBootstrap4` attribute to each `context-menu` component. Like this:

```html

```

## Different styling on menus

If you want to style one menu differently than other menus, you can add a custom style to the menu.

```html

```

Please note that the style needs to be global to affect the menu, since the menu element is added to the page outside the component that triggers the menu.

## AutoFocus

You can optionally set focus on the context menu whenever it opens. This enables a user to easily tab through the context menu items and press enter to select them.

```js
@NgModule({
import: [
ContextMenuModule.forRoot({
autoFocus: true,
}),
],
})
export class AppModule {}
```

## Keyboard navigation

You can use the keyboard to manipulate the context menu. Note: Keyboard navigation should be used in conjunction with `autoFocus`, since key events are only captured when the context menu is focused.

| Key | Action |
|:--------------:|------------------------------------------------|
| ArrowDown | Move to next menu item (wrapping) |
| ArrowUp | Move to previous menu item (wrapping) |
| ArrowRight | Open submenu of current menu item if present |
| ArrowLeft | Close current menu unless already at root menu |
| Enter \| Space | Open submenu or execute current menu item |
| Esc | Close current menu |

## Disable Context Menu

If you need to disable the context menu, you can pass a `boolean` to the `[disabled]` input:

```html

```

## Close event emitter

There is a `(close)` output EventEmitter that you can subscribe to for notifications when the context menu closes (either by clicking outside or choosing a menu item).

```html

```

## Dynamic context menu

The items in the context menu are completely controlled by the `contextMenuActions` object.

```html


  • Right Click: {{item.name}}


{{ action.html($event.item) }}

```

```ts
@Component({
...
})
export class MyContextMenuClass {
public items = [
{ name: 'John', otherProperty: 'Foo', type: 'type1' },
{ name: 'Joe', otherProperty: 'Bar', type: 'type2' }
];
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
public contextMenuActions = [
{
html: (item) => `Say hi!`,
click: (item) => alert('Hi, ' + item.name),
enabled: (item) => true,
visible: (item) => item.type === 'type1',
},
{
divider: true,
visible: true,
},
{
html: (item) => `Something else`,
click: (item) => alert('Or not...'),
enabled: (item) => false,
visible: (item) => item.type === 'type1',
},
];
}
```