@vaadin/vaadin-context-menu
Advanced tools
Comparing version 22.0.0-alpha1 to 22.0.0-alpha10
{ | ||
"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> | ||
[](https://www.npmjs.com/package/@vaadin/vaadin-context-menu) | ||
[](https://travis-ci.org/vaadin/vaadin-context-menu) | ||
[](https://www.webcomponents.org/element/vaadin/vaadin-context-menu) | ||
[](https://vaadin.com/directory/component/vaadinvaadin-context-menu) | ||
[](https://vaadin.com/directory/component/vaadinvaadin-context-menu) | ||
[](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'; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
1
0
0
16335
9
41
86
1
+ Added@vaadin/component-base@22.0.0-alpha10(transitive)
+ Added@vaadin/context-menu@22.0.0-alpha10(transitive)
+ Added@vaadin/icon@22.0.0-alpha10(transitive)
+ Added@vaadin/item@22.0.0-alpha10(transitive)
+ Added@vaadin/list-box@22.0.0-alpha10(transitive)
+ Added@vaadin/vaadin-list-mixin@22.0.0-alpha10(transitive)
+ Added@vaadin/vaadin-lumo-styles@22.0.0-alpha10(transitive)
+ Added@vaadin/vaadin-material-styles@22.0.0-alpha10(transitive)
+ Added@vaadin/vaadin-overlay@22.0.0-alpha10(transitive)
+ Added@vaadin/vaadin-themable-mixin@22.0.0-alpha10(transitive)
- Removed@polymer/iron-media-query@^3.0.0
- Removed@polymer/polymer@^3.0.0
- Removed@vaadin/vaadin-item@^22.0.0-alpha1
- Removed@open-wc/dedupe-mixin@1.4.0(transitive)
- Removed@vaadin/component-base@22.1.0(transitive)
- Removed@vaadin/icon@22.1.0(transitive)
- Removed@vaadin/item@22.1.0(transitive)
- Removed@vaadin/list-box@22.1.0(transitive)
- Removed@vaadin/vaadin-element-mixin@22.0.0-alpha6(transitive)
- Removed@vaadin/vaadin-item@22.1.0(transitive)
- Removed@vaadin/vaadin-list-box@22.1.0(transitive)
- Removed@vaadin/vaadin-list-mixin@22.1.0(transitive)
- Removed@vaadin/vaadin-lumo-styles@22.1.0(transitive)
- Removed@vaadin/vaadin-material-styles@22.1.0(transitive)
- Removed@vaadin/vaadin-overlay@22.1.0(transitive)
- Removed@vaadin/vaadin-themable-mixin@22.1.0(transitive)