New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@vaadin/vaadin-context-menu

Package Overview
Dependencies
Maintainers
19
Versions
275
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin/vaadin-context-menu - npm Package Compare versions

Comparing version 22.0.0-alpha1 to 22.0.0-alpha10

56

package.json
{
"name": "@vaadin/vaadin-context-menu",
"version": "22.0.0-alpha1",
"version": "22.0.0-alpha10",
"publishConfig": {
"access": "public"
},
"description": "Web Component for showing context dependent items for any element on the page",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/vaadin/web-components.git",
"directory": "packages/vaadin-context-menu"
},
"author": "Vaadin Ltd",
"homepage": "https://vaadin.com/components",
"bugs": {
"url": "https://github.com/vaadin/vaadin-context-menu/issues"
},
"main": "vaadin-context-menu.js",
"module": "vaadin-context-menu.js",
"repository": "vaadin/vaadin-context-menu",
"files": [
"src",
"theme",
"vaadin-*.d.ts",
"vaadin-*.js"
],
"keywords": [

@@ -15,35 +34,6 @@ "Vaadin",

],
"author": "Vaadin Ltd",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/vaadin/vaadin-context-menu/issues"
},
"homepage": "https://vaadin.com/components",
"files": [
"vaadin-*.d.ts",
"vaadin-*.js",
"src",
"theme"
],
"dependencies": {
"@polymer/iron-media-query": "^3.0.0",
"@polymer/polymer": "^3.0.0",
"@vaadin/vaadin-element-mixin": "^22.0.0-alpha1",
"@vaadin/vaadin-item": "^22.0.0-alpha1",
"@vaadin/vaadin-list-box": "^22.0.0-alpha1",
"@vaadin/vaadin-lumo-styles": "^22.0.0-alpha1",
"@vaadin/vaadin-material-styles": "^22.0.0-alpha1",
"@vaadin/vaadin-overlay": "^22.0.0-alpha1",
"@vaadin/vaadin-themable-mixin": "^22.0.0-alpha1"
"@vaadin/context-menu": "22.0.0-alpha10"
},
"devDependencies": {
"@esm-bundle/chai": "^4.1.5",
"@vaadin/testing-helpers": "^0.2.1",
"@vaadin/vaadin-template-renderer": "^22.0.0-alpha1",
"sinon": "^9.2.1"
},
"publishConfig": {
"access": "public"
},
"gitHead": "c9694d6549bff1f7fffb9ece26178e57fc228a51"
"gitHead": "6d3055383b9c3c8306ea34c6f2e2087e63c49348"
}

@@ -10,6 +10,3 @@ # <vaadin-context-menu>

[![npm version](https://badgen.net/npm/v/@vaadin/vaadin-context-menu)](https://www.npmjs.com/package/@vaadin/vaadin-context-menu)
[![Build Status](https://travis-ci.org/vaadin/vaadin-context-menu.svg?branch=master)](https://travis-ci.org/vaadin/vaadin-context-menu)
[![Published on webcomponents.org](https://img.shields.io/badge/webcomponents.org-published-blue.svg)](https://www.webcomponents.org/element/vaadin/vaadin-context-menu)
[![Published on Vaadin Directory](https://img.shields.io/badge/Vaadin%20Directory-published-00b4f0.svg)](https://vaadin.com/directory/component/vaadinvaadin-context-menu)
[![Stars on vaadin.com/directory](https://img.shields.io/vaadin-directory/star/vaadinvaadin-context-menu.svg)](https://vaadin.com/directory/component/vaadinvaadin-context-menu)
[![Discord](https://img.shields.io/discord/732335336448852018?label=discord)](https://discord.gg/PHmkCKC)

@@ -19,3 +16,3 @@

<vaadin-context-menu>
Open a context menu with <b>right click</b> or with <b>long touch.</b>
<span>Open a context menu with <b>right click</b> or with <b>long touch.</b></span>
</vaadin-context-menu>

@@ -25,3 +22,3 @@

const contextMenu = document.querySelector('vaadin-context-menu');
contextMenu.renderer = function(root) {
contextMenu.renderer = function (root) {
let listBox = root.firstElementChild;

@@ -36,3 +33,3 @@ // Check if there is a list-box generated with the previous renderer call to update its content instead of recreation

['First', 'Second', 'Third'].forEach(function(name) {
['First', 'Second', 'Third'].forEach(function (name) {
const item = document.createElement('vaadin-item');

@@ -84,44 +81,6 @@ item.textContent = name + ' menu item';

## Running API docs and tests in a browser
1. Fork the `vaadin-context-menu` repository and clone it locally.
1. Make sure you have [node.js](https://nodejs.org/) 12.x installed.
1. Make sure you have [npm](https://www.npmjs.com/) installed.
1. When in the `vaadin-context-menu` directory, run `npm install` to install dependencies.
1. Run `npm start`, browser will automatically open the component API documentation.
1. You can also open visual tests, for example:
- http://127.0.0.1:3000/test/visual/default.html
## Running tests from the command line
1. When in the `vaadin-context-menu` directory, run `npm test`
## Debugging tests in the browser
1. Run `npm run debug`, then choose manual mode (M) and open the link in browser.
## Following the coding style
We are using [ESLint](http://eslint.org/) for linting JavaScript code. You can check if your code is following our standards by running `npm run lint`, which will automatically lint all `.js` files.
## Big Thanks
Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com).
## Contributing
To contribute to the component, please read [the guideline](https://github.com/vaadin/vaadin-core/blob/master/CONTRIBUTING.md) first.
Read the [contributing guide](https://vaadin.com/docs/latest/guide/contributing/overview) to learn about our development process, how to propose bugfixes and improvements, and how to test your changes to Vaadin components.
## License

@@ -128,0 +87,0 @@

@@ -1,283 +0,18 @@

import { GestureEventListeners } from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
/**
* @license
* Copyright (c) 2021 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ContextMenu } from '@vaadin/context-menu/src/vaadin-context-menu.js';
import { ItemsMixin } from './vaadin-contextmenu-items-mixin.js';
/**
* @deprecated Import `ContextMenu` from `@vaadin/context-menu` instead.
*/
export type ContextMenuElement = ContextMenu;
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
import { ContextMenuEventMap, ContextMenuRenderer } from './interfaces';
/**
* `<vaadin-context-menu>` is a Web Component for creating context menus.
*
* ### Items
*
* Items is a higher level convenience API for defining a (hierarchical) menu structure for the component.
* If a menu item has a non-empty `children` set, a sub-menu with the child items is opened
* next to the parent menu on mouseover, tap or a right arrow keypress.
*
* When an item is selected, `<vaadin-context-menu>` dispatches an "item-selected" event
* with the selected item as `event.detail.value` property.
*
* ```javascript
* contextMenu.items = [
* {text: 'Menu Item 1', children:
* [
* {text: 'Menu Item 1-1', checked: true},
* {text: 'Menu Item 1-2'}
* ]
* },
* {component: 'hr'},
* {text: 'Menu Item 2', children:
* [
* {text: 'Menu Item 2-1'},
* {text: 'Menu Item 2-2', disabled: true}
* ]
* },
* {text: 'Menu Item 3', disabled: true}
* ];
*
* contextMenu.addEventListener('item-selected', e => {
* const item = e.detail.value;
* console.log(`${item.text} selected`);
* });
* ```
*
* **NOTE:** when the `items` array is defined, the renderer cannot be used.
*
* ### Rendering
*
* The content of the menu can be populated by using the renderer callback function.
*
* The renderer function provides `root`, `contextMenu`, `model` arguments when applicable.
* Generate DOM content by using `model` object properties if needed, append it to the `root`
* element and control the state of the host element by accessing `contextMenu`. Before generating
* new content, the renderer function should check if there is already content in `root` for reusing it.
*
* ```html
* <vaadin-context-menu id="contextMenu">
* <p>This paragraph has a context menu.</p>
* </vaadin-context-menu>
* ```
* ```js
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.renderer = (root, contextMenu, context) => {
* let listBox = root.firstElementChild;
* if (!listBox) {
* listBox = document.createElement('vaadin-list-box');
* root.appendChild(listBox);
* }
*
* let item = listBox.querySelector('vaadin-item');
* if (!item) {
* item = document.createElement('vaadin-item');
* listBox.appendChild(item);
* }
* item.textContent = 'Content of the selector: ' + context.target.textContent;
* };
* ```
*
* You can access the menu context inside the renderer using
* `context.target` and `context.detail`.
*
* Renderer is called on the opening of the context-menu and each time the related context is updated.
* DOM generated during the renderer call can be reused
* in the next renderer call and will be provided with the `root` argument.
* On first call it will be empty.
*
* ### “vaadin-contextmenu” Gesture Event
*
* `vaadin-contextmenu` is a gesture event (a custom event),
* which is dispatched after either `contextmenu` or long touch events.
* This enables support for both mouse and touch environments in a uniform way.
*
* `<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`
* event by default.
*
* ### Menu Listener
*
* By default, the `<vaadin-context-menu>` element listens for the menu opening
* event on itself. In case if you do not want to wrap the target, you can listen for
* events on an element outside the `<vaadin-context-menu>` by setting the
* `listenOn` property:
*
* ```html
* <vaadin-context-menu id="contextMenu"></vaadin-context-menu>
*
* <div id="menuListener">The element that listens for the contextmenu event.</div>
* ```
* ```javascript
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.listenOn = document.querySelector('#menuListener');
* ```
*
* ### Filtering Menu Targets
*
* By default, the listener element and all its descendants open the context
* menu. You can filter the menu targets to a smaller set of elements inside
* the listener element by setting the `selector` property.
*
* In the following example, only the elements matching `.has-menu` will open the context menu:
*
* ```html
* <vaadin-context-menu selector=".has-menu">
* <p class="has-menu">This paragraph opens the context menu</p>
* <p>This paragraph does not open the context menu</p>
* </vaadin-context-menu>
* ```
*
* ### Menu Context
*
* You can use the following properties in the renderer function:
*
* - `target` is the menu opening event target, which is the element that
* the user has called the context menu for
* - `detail` is the menu opening event detail
*
* In the following example, the menu item text is composed with the contents
* of the element that opened the menu:
*
* ```html
* <vaadin-context-menu selector="li" id="contextMenu">
* <ul>
* <li>Foo</li>
* <li>Bar</li>
* <li>Baz</li>
* </ul>
* </vaadin-context-menu>
* ```
* ```js
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.renderer = (root, contextMenu, context) => {
* let listBox = root.firstElementChild;
* if (!listBox) {
* listBox = document.createElement('vaadin-list-box');
* root.appendChild(listBox);
* }
*
* let item = listBox.querySelector('vaadin-item');
* if (!item) {
* item = document.createElement('vaadin-item');
* listBox.appendChild(item);
* }
* item.textContent = 'The menu target: ' + context.target.textContent;
* };
* ```
*
* ### Styling
*
* `<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal
* themable component as the actual visible context menu overlay.
*
* See [`<vaadin-overlay>`](#/elements/vaadin-overlay)
* documentation for `<vaadin-context-menu-overlay>` stylable parts.
*
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
*
* ### Internal components
*
* When using `items` API, in addition `<vaadin-context-menu-overlay>`, the following
* internal components are themable:
*
* - `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
* - `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
*
* Note: the `theme` attribute value set on `<vaadin-context-menu>` is
* propagated to the internal components listed above.
*
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
* @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
* @deprecated Import `ContextMenu` from `@vaadin/context-menu` instead.
*/
declare class ContextMenuElement extends ElementMixin(
ThemePropertyMixin(ItemsMixin(GestureEventListeners(HTMLElement)))
) {
/**
* CSS selector that can be used to target any child element
* of the context menu to listen for `openOn` events.
*/
selector: string | null | undefined;
export const ContextMenuElement: typeof ContextMenu;
/**
* True if the overlay is currently displayed.
*/
readonly opened: boolean;
/**
* Event name to listen for opening the context menu.
* @attr {string} open-on
*/
openOn: string;
/**
* The target element that's listened to for context menu opening events.
* By default the vaadin-context-menu listens to the target's `vaadin-contextmenu`
* events.
*/
listenOn: HTMLElement;
/**
* Event name to listen for closing the context menu.
* @attr {string} close-on
*/
closeOn: string;
/**
* Custom function for rendering the content of the menu overlay.
* Receives three arguments:
*
* - `root` The root container DOM element. Append your content to it.
* - `contextMenu` The reference to the `<vaadin-context-menu>` element.
* - `context` The object with the menu context, contains:
* - `context.target` the target of the menu opening event,
* - `context.detail` the menu opening event detail.
*/
renderer: ContextMenuRenderer | null | undefined;
/**
* Requests an update for the content of the menu overlay.
* While performing the update, it invokes the renderer passed in the `renderer` property.
*
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
*/
requestContentUpdate(): void;
/**
* Manually invoke existing renderer.
*
* @deprecated Since Vaadin 21, `render()` is deprecated. Please use `requestContentUpdate()` instead.
*/
render(): void;
/**
* Closes the overlay.
*/
close(): void;
/**
* Opens the overlay.
*
* @param e used as the context for the menu. Overlay coordinates are taken from this event.
*/
open(e: Event | undefined): void;
addEventListener<K extends keyof ContextMenuEventMap>(
type: K,
listener: (this: ContextMenuElement, ev: ContextMenuEventMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<K extends keyof ContextMenuEventMap>(
type: K,
listener: (this: ContextMenuElement, ev: ContextMenuEventMap[K]) => void,
options?: boolean | EventListenerOptions
): void;
}
declare global {
interface HTMLElementTagNameMap {
'vaadin-context-menu': ContextMenuElement;
}
}
export { ContextMenuElement };
export * from '@vaadin/context-menu/src/vaadin-context-menu.js';

@@ -6,733 +6,9 @@ /**

*/
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { gestures, addListener, removeListener } from '@polymer/polymer/lib/utils/gestures.js';
import { GestureEventListeners } from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
import { ItemsMixin } from './vaadin-contextmenu-items-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
import { processTemplates } from '@vaadin/vaadin-element-mixin/templates.js';
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
import './vaadin-contextmenu-event.js';
import './vaadin-device-detector.js';
import './vaadin-context-menu-overlay.js';
import { ContextMenu } from '@vaadin/context-menu/src/vaadin-context-menu.js';
/**
* `<vaadin-context-menu>` is a Web Component for creating context menus.
*
* ### Items
*
* Items is a higher level convenience API for defining a (hierarchical) menu structure for the component.
* If a menu item has a non-empty `children` set, a sub-menu with the child items is opened
* next to the parent menu on mouseover, tap or a right arrow keypress.
*
* When an item is selected, `<vaadin-context-menu>` dispatches an "item-selected" event
* with the selected item as `event.detail.value` property.
*
* ```javascript
* contextMenu.items = [
* {text: 'Menu Item 1', children:
* [
* {text: 'Menu Item 1-1', checked: true},
* {text: 'Menu Item 1-2'}
* ]
* },
* {component: 'hr'},
* {text: 'Menu Item 2', children:
* [
* {text: 'Menu Item 2-1'},
* {text: 'Menu Item 2-2', disabled: true}
* ]
* },
* {text: 'Menu Item 3', disabled: true}
* ];
*
* contextMenu.addEventListener('item-selected', e => {
* const item = e.detail.value;
* console.log(`${item.text} selected`);
* });
* ```
*
* **NOTE:** when the `items` array is defined, the renderer cannot be used.
*
* ### Rendering
*
* The content of the menu can be populated by using the renderer callback function.
*
* The renderer function provides `root`, `contextMenu`, `model` arguments when applicable.
* Generate DOM content by using `model` object properties if needed, append it to the `root`
* element and control the state of the host element by accessing `contextMenu`. Before generating
* new content, the renderer function should check if there is already content in `root` for reusing it.
*
* ```html
* <vaadin-context-menu id="contextMenu">
* <p>This paragraph has a context menu.</p>
* </vaadin-context-menu>
* ```
* ```js
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.renderer = (root, contextMenu, context) => {
* let listBox = root.firstElementChild;
* if (!listBox) {
* listBox = document.createElement('vaadin-list-box');
* root.appendChild(listBox);
* }
*
* let item = listBox.querySelector('vaadin-item');
* if (!item) {
* item = document.createElement('vaadin-item');
* listBox.appendChild(item);
* }
* item.textContent = 'Content of the selector: ' + context.target.textContent;
* };
* ```
*
* You can access the menu context inside the renderer using
* `context.target` and `context.detail`.
*
* Renderer is called on the opening of the context-menu and each time the related context is updated.
* DOM generated during the renderer call can be reused
* in the next renderer call and will be provided with the `root` argument.
* On first call it will be empty.
*
* ### “vaadin-contextmenu” Gesture Event
*
* `vaadin-contextmenu` is a gesture event (a custom event),
* which is dispatched after either `contextmenu` or long touch events.
* This enables support for both mouse and touch environments in a uniform way.
*
* `<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`
* event by default.
*
* ### Menu Listener
*
* By default, the `<vaadin-context-menu>` element listens for the menu opening
* event on itself. In case if you do not want to wrap the target, you can listen for
* events on an element outside the `<vaadin-context-menu>` by setting the
* `listenOn` property:
*
* ```html
* <vaadin-context-menu id="contextMenu"></vaadin-context-menu>
*
* <div id="menuListener">The element that listens for the contextmenu event.</div>
* ```
* ```javascript
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.listenOn = document.querySelector('#menuListener');
* ```
*
* ### Filtering Menu Targets
*
* By default, the listener element and all its descendants open the context
* menu. You can filter the menu targets to a smaller set of elements inside
* the listener element by setting the `selector` property.
*
* In the following example, only the elements matching `.has-menu` will open the context menu:
*
* ```html
* <vaadin-context-menu selector=".has-menu">
* <p class="has-menu">This paragraph opens the context menu</p>
* <p>This paragraph does not open the context menu</p>
* </vaadin-context-menu>
* ```
*
* ### Menu Context
*
* The following properties are available in the `context` argument:
*
* - `target` is the menu opening event target, which is the element that
* the user has called the context menu for
* - `detail` is the menu opening event detail
*
* In the following example, the menu item text is composed with the contents
* of the element that opened the menu:
*
* ```html
* <vaadin-context-menu selector="li" id="contextMenu">
* <ul>
* <li>Foo</li>
* <li>Bar</li>
* <li>Baz</li>
* </ul>
* </vaadin-context-menu>
* ```
* ```js
* const contextMenu = document.querySelector('#contextMenu');
* contextMenu.renderer = (root, contextMenu, context) => {
* let listBox = root.firstElementChild;
* if (!listBox) {
* listBox = document.createElement('vaadin-list-box');
* root.appendChild(listBox);
* }
*
* let item = listBox.querySelector('vaadin-item');
* if (!item) {
* item = document.createElement('vaadin-item');
* listBox.appendChild(item);
* }
* item.textContent = 'The menu target: ' + context.target.textContent;
* };
* ```
*
* ### Styling
*
* `<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal
* themable component as the actual visible context menu overlay.
*
* See [`<vaadin-overlay>`](#/elements/vaadin-overlay)
* documentation for `<vaadin-context-menu-overlay>` stylable parts.
*
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
*
* ### Internal components
*
* When using `items` API, in addition `<vaadin-context-menu-overlay>`, the following
* internal components are themable:
*
* - `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
* - `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
*
* Note: the `theme` attribute value set on `<vaadin-context-menu>` is
* propagated to the internal components listed above.
*
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
* @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
*
* @extends HTMLElement
* @mixes ElementMixin
* @mixes ThemePropertyMixin
* @mixes ItemsMixin
* @mixes GestureEventListeners
* @deprecated Import `ContextMenu` from `@vaadin/context-menu` instead.
*/
class ContextMenuElement extends ElementMixin(ThemePropertyMixin(ItemsMixin(GestureEventListeners(PolymerElement)))) {
static get template() {
return html`
<style>
:host {
display: block;
}
export const ContextMenuElement = ContextMenu;
:host([hidden]) {
display: none !important;
}
</style>
<slot id="slot"></slot>
<vaadin-device-detector phone="{{_phone}}" touch="{{_touch}}"></vaadin-device-detector>
<vaadin-context-menu-overlay
id="overlay"
on-opened-changed="_onOverlayOpened"
on-vaadin-overlay-open="_onVaadinOverlayOpen"
with-backdrop="[[_phone]]"
phone$="[[_phone]]"
model="[[_context]]"
theme$="[[theme]]"
>
</vaadin-context-menu-overlay>
`;
}
static get is() {
return 'vaadin-context-menu';
}
static get version() {
return '22.0.0-alpha1';
}
static get properties() {
return {
/**
* CSS selector that can be used to target any child element
* of the context menu to listen for `openOn` events.
*/
selector: {
type: String
},
/**
* True if the overlay is currently displayed.
* @type {boolean}
*/
opened: {
type: Boolean,
value: false,
notify: true,
readOnly: true
},
/**
* Event name to listen for opening the context menu.
* @attr {string} open-on
* @type {string}
*/
openOn: {
type: String,
value: 'vaadin-contextmenu'
},
/**
* The target element that's listened to for context menu opening events.
* By default the vaadin-context-menu listens to the target's `vaadin-contextmenu`
* events.
* @type {!HTMLElement}
* @default self
*/
listenOn: {
type: Object,
value: function () {
return this;
}
},
/**
* Event name to listen for closing the context menu.
* @attr {string} close-on
* @type {string}
*/
closeOn: {
type: String,
value: 'click',
observer: '_closeOnChanged'
},
/**
* Custom function for rendering the content of the menu overlay.
* Receives three arguments:
*
* - `root` The root container DOM element. Append your content to it.
* - `contextMenu` The reference to the `<vaadin-context-menu>` element.
* - `context` The object with the menu context, contains:
* - `context.target` the target of the menu opening event,
* - `context.detail` the menu opening event detail.
* @type {ContextMenuRenderer | undefined}
*/
renderer: {
type: Function
},
/** @private */
_context: Object,
/** @private */
_boundClose: Object,
/** @private */
_boundOpen: Object,
/** @private */
_touch: Boolean
};
}
static get observers() {
return ['_openedChanged(opened)', '_targetOrOpenOnChanged(listenOn, openOn)', '_rendererChanged(renderer, items)'];
}
constructor() {
super();
this._boundOpen = this.open.bind(this);
this._boundClose = this.close.bind(this);
this._boundOnGlobalContextMenu = this._onGlobalContextMenu.bind(this);
}
/** @protected */
connectedCallback() {
super.connectedCallback();
this.__boundOnScroll = this.__onScroll.bind(this);
window.addEventListener('scroll', this.__boundOnScroll, true);
}
/** @protected */
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('scroll', this.__boundOnScroll, true);
this.close();
}
/** @protected */
ready() {
super.ready();
processTemplates(this);
}
/**
* Runs before overlay is fully rendered
* @private
*/
_onOverlayOpened(e) {
this._setOpened(e.detail.value);
this.__alignOverlayPosition();
}
/**
* Runs after overlay is fully rendered
* @private
*/
_onVaadinOverlayOpen() {
this.__alignOverlayPosition();
this.$.overlay.style.opacity = '';
this.__forwardFocus();
}
/** @private */
_targetOrOpenOnChanged(listenOn, openOn) {
if (this._oldListenOn && this._oldOpenOn) {
this._unlisten(this._oldListenOn, this._oldOpenOn, this._boundOpen);
this._oldListenOn.style.webkitTouchCallout = '';
this._oldListenOn.style.webkitUserSelect = '';
this._oldListenOn.style.userSelect = '';
this._oldListenOn = null;
this._oldOpenOn = null;
}
if (listenOn && openOn) {
this._listen(listenOn, openOn, this._boundOpen);
this._oldListenOn = listenOn;
this._oldOpenOn = openOn;
}
}
/** @private */
_setListenOnUserSelect(value) {
// note: these styles don't seem to work in Firefox on iOS.
this.listenOn.style.webkitTouchCallout = value;
this.listenOn.style.webkitUserSelect = value; // Chrome, Safari, Firefox
this.listenOn.style.userSelect = value;
// note: because user-selection is disabled on the overlay
// before opening the menu the text could be already selected
// so we need to clear that selection
document.getSelection().removeAllRanges();
}
/** @private */
_closeOnChanged(closeOn, oldCloseOn) {
// Listen on this.$.overlay.root to workaround issue on
// ShadyDOM polyfill: https://github.com/webcomponents/shadydom/issues/159
// Outside click event from overlay
const evtOverlay = 'vaadin-overlay-outside-click';
if (oldCloseOn) {
this._unlisten(this.$.overlay, oldCloseOn, this._boundClose);
this._unlisten(this.$.overlay.root, oldCloseOn, this._boundClose);
}
if (closeOn) {
this._listen(this.$.overlay, closeOn, this._boundClose);
this._listen(this.$.overlay.root, closeOn, this._boundClose);
this._unlisten(this.$.overlay, evtOverlay, this._preventDefault);
} else {
this._listen(this.$.overlay, evtOverlay, this._preventDefault);
}
}
/** @private */
_preventDefault(e) {
e.preventDefault();
}
/** @private */
_openedChanged(opened) {
if (opened) {
document.documentElement.addEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
this._setListenOnUserSelect('none');
} else {
document.documentElement.removeEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
this._setListenOnUserSelect('');
}
// Has to be set after instance has been created
this.$.overlay.opened = opened;
}
/**
* Requests an update for the content of the menu overlay.
* While performing the update, it invokes the renderer passed in the `renderer` property.
*
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
*/
requestContentUpdate() {
this.$.overlay.requestContentUpdate();
}
/**
* Manually invoke existing renderer.
*
* @deprecated Since Vaadin 21, `render()` is deprecated. Please use `requestContentUpdate()` instead.
*/
render() {
console.warn('WARNING: Since Vaadin 21, render() is deprecated. Please use requestContentUpdate() instead.');
this.requestContentUpdate();
}
/** @private */
_rendererChanged(renderer, items) {
if (items) {
if (renderer) {
throw new Error('The items API cannot be used together with a renderer');
}
if (this.closeOn === 'click') {
this.closeOn = '';
}
renderer = this.__itemsRenderer;
}
this.$.overlay.setProperties({ owner: this, renderer });
}
/**
* Closes the overlay.
*/
close() {
this._setOpened(false);
}
/** @private */
_contextTarget(e) {
if (this.selector) {
const targets = this.listenOn.querySelectorAll(this.selector);
return Array.prototype.filter.call(targets, (el) => {
return e.composedPath().indexOf(el) > -1;
})[0];
} else {
return e.target;
}
}
/**
* Opens the overlay.
* @param {!Event | undefined} e used as the context for the menu. Overlay coordinates are taken from this event.
*/
open(e) {
if (e && !this.opened) {
this._context = {
detail: e.detail,
target: this._contextTarget(e)
};
if (this._context.target) {
this._preventDefault(e);
e.stopPropagation();
// Used in alignment which is delayed until overlay is rendered
this.__x = this._getEventCoordinate(e, 'x');
this.__pageXOffset = window.pageXOffset;
this.__y = this._getEventCoordinate(e, 'y');
this.__pageYOffset = window.pageYOffset;
this.$.overlay.style.opacity = '0';
this._setOpened(true);
}
}
}
/** @private */
__onScroll() {
if (!this.opened) {
return;
}
const yDiff = window.pageYOffset - this.__pageYOffset;
const xDiff = window.pageXOffset - this.__pageXOffset;
this.__adjustPosition('left', -xDiff);
this.__adjustPosition('right', xDiff);
this.__adjustPosition('top', -yDiff);
this.__adjustPosition('bottom', yDiff);
this.__pageYOffset += yDiff;
this.__pageXOffset += xDiff;
}
/** @private */
__adjustPosition(coord, diff) {
const overlay = this.$.overlay;
const style = overlay.style;
style[coord] = (parseInt(style[coord]) || 0) + diff + 'px';
}
/** @private */
__alignOverlayPosition() {
const overlay = this.$.overlay;
const style = overlay.style;
// Reset all properties before measuring
['top', 'right', 'bottom', 'left'].forEach((prop) => style.removeProperty(prop));
['right-aligned', 'end-aligned', 'bottom-aligned'].forEach((attr) => overlay.removeAttribute(attr));
// Maximum x and y values are imposed by content size and overlay limits.
const { xMax, xMin, yMax, left, right, top, width } = overlay.getBoundaries();
// Reuse saved x and y event values, in order to this method be used async
// in the `vaadin-overlay-change` which guarantees that overlay is ready
let x = this.__x || (!this.__isRTL ? left : right);
const y = this.__y || top;
// Select one overlay corner and move to the event x/y position.
// Then set styling attrs for flex-aligning the content appropriately.
const wdthVport = document.documentElement.clientWidth;
const hghtVport = document.documentElement.clientHeight;
// Align to the parent menu overlay, if any.
const parent = overlay.parentOverlay;
let alignedToParent = false;
let parentContentRect;
if (parent) {
parentContentRect = parent.$.overlay.getBoundingClientRect();
if (parent.hasAttribute('right-aligned') || parent.hasAttribute('end-aligned')) {
const parentStyle = getComputedStyle(parent);
const getPadding = (el, direction) => {
return parseFloat(getComputedStyle(el.$.content)['padding' + direction]);
};
const dimensionToSet = parseFloat(parentStyle[this.__isRTL ? 'left' : 'right']) + parentContentRect.width;
const padding = getPadding(parent, 'Left') + getPadding(overlay, 'Right');
// Preserve end-aligned, if possible.
if (wdthVport - (dimensionToSet - padding) > width) {
this._setEndAligned(overlay);
style[this.__isRTL ? 'left' : 'right'] = dimensionToSet + 'px';
alignedToParent = true;
}
} else if (x < parentContentRect.x) {
// Check if sub menu opens on the left side and the parent menu is not right aligned.
// If so, use actual width of the submenu content instead of the parent menu content.
x = x - (width - parentContentRect.width);
}
}
if (!alignedToParent) {
if (!this.__isRTL) {
// Sub-menu is displayed in the right side of root menu
if ((x < wdthVport / 2 || x < xMax) && !parent) {
style.left = x + 'px';
} else if (parent && wdthVport - parentContentRect.width - parentContentRect.left >= parentContentRect.width) {
// Sub-menu is displayed in the right side of root menu If it is nested menu
style.left = parentContentRect.left + parentContentRect.width + 'px';
} else if (parent) {
// Sub-menu is displayed in the left side of root menu If it is nested menu
style.right = 'auto';
style.left =
Math.max(
overlay.getBoundingClientRect().left,
parentContentRect.left - overlay.getBoundingClientRect().width
) + 'px';
this._setEndAligned(overlay);
} else {
// Sub-menu is displayed in the left side of root menu
style.right = Math.max(0, wdthVport - x) + 'px';
this._setEndAligned(overlay);
}
} else {
// Sub-menu is displayed in the left side of root menu
if ((x > wdthVport / 2 || x > xMin) && !parent) {
style.right = Math.max(0, wdthVport - x) + 'px';
} else if (parent && parentContentRect.left >= parentContentRect.width) {
// Sub-menu is displayed in the left side of root menu If it is nested menu
style.right = wdthVport - parentContentRect.right + parentContentRect.width + 'px';
} else if (parent) {
// Sub-menu is displayed in the right side of root menu If it is nested menu
style.right = 'auto';
style.left =
Math.max(
overlay.getBoundingClientRect().left - overlay.getBoundingClientRect().width,
parentContentRect.right
) + 'px';
this._setEndAligned(overlay);
} else {
// Sub-menu is displayed in the left side of root menu
style.left = x + 'px';
this._setEndAligned(overlay);
}
}
}
if (y < hghtVport / 2 || y < yMax) {
style.top = y + 'px';
} else {
style.bottom = Math.max(0, hghtVport - y) + 'px';
overlay.setAttribute('bottom-aligned', '');
}
}
/** @private */
_setEndAligned(element) {
element.setAttribute('end-aligned', '');
if (!this.__isRTL) {
element.setAttribute('right-aligned', '');
}
}
/** @private */
_getEventCoordinate(event, coord) {
if (event.detail instanceof Object) {
if (event.detail[coord]) {
// Polymer gesture events, get coordinate from detail
return event.detail[coord];
} else if (event.detail.sourceEvent) {
// Unwrap detailed event
return this._getEventCoordinate(event.detail.sourceEvent, coord);
}
} else {
const prop = 'client' + coord.toUpperCase();
const position = event.changedTouches ? event.changedTouches[0][prop] : event[prop];
if (position === 0) {
// Native keyboard event
const rect = event.target.getBoundingClientRect();
return coord === 'x' ? rect.left : rect.top + rect.height;
} else {
// Native mouse or touch event
return position;
}
}
}
/** @private */
_listen(node, evType, handler) {
if (gestures[evType]) {
addListener(node, evType, handler);
} else {
node.addEventListener(evType, handler);
}
}
/** @private */
_unlisten(node, evType, handler) {
if (gestures[evType]) {
removeListener(node, evType, handler);
} else {
node.removeEventListener(evType, handler);
}
}
/** @private */
_onGlobalContextMenu(e) {
if (!e.shiftKey) {
e.preventDefault();
this.close();
}
}
/**
* Fired when an item is selected when the context menu is populated using the `items` API.
*
* @event item-selected
* @param {Object} detail
* @param {Object} detail.value the selected menu item
*/
}
customElements.define(ContextMenuElement.is, ContextMenuElement);
export { ContextMenuElement };
export * from '@vaadin/context-menu/src/vaadin-context-menu.js';

@@ -1,4 +0,6 @@

import './vaadin-context-menu-styles.js';
import '@vaadin/vaadin-item/theme/lumo/vaadin-item.js';
import '@vaadin/vaadin-list-box/theme/lumo/vaadin-list-box.js';
import '../../src/vaadin-context-menu.js';
/**
* @license
* Copyright (c) 2021 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js';

@@ -1,4 +0,6 @@

import './vaadin-context-menu-styles.js';
import '@vaadin/vaadin-item/theme/material/vaadin-item.js';
import '@vaadin/vaadin-list-box/theme/material/vaadin-list-box.js';
import '../../src/vaadin-context-menu.js';
/**
* @license
* Copyright (c) 2021 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/context-menu/theme/material/vaadin-context-menu.js';
export * from './src/vaadin-context-menu.js';
export * from './src/interfaces';
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc