@vaadin/context-menu 25.0.0-alpha8 → 25.0.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -16
- package/src/styles/vaadin-context-menu-item-base-styles.js +3 -3
- package/src/styles/vaadin-context-menu-overlay-base-styles.js +28 -1
- package/src/vaadin-context-menu-item.js +1 -1
- package/src/vaadin-context-menu-list-box.js +1 -1
- package/src/vaadin-context-menu-mixin.js +48 -76
- package/src/vaadin-context-menu-overlay.js +56 -1
- package/src/vaadin-context-menu.d.ts +48 -11
- package/src/vaadin-context-menu.js +98 -23
- package/src/vaadin-contextmenu-items-mixin.js +44 -30
- package/src/vaadin-menu-overlay-mixin.d.ts +0 -5
- package/src/vaadin-menu-overlay-mixin.js +23 -28
- package/vaadin-context-menu.js +1 -1
- package/web-types.json +10 -6
- package/web-types.lit.json +11 -4
- package/src/styles/vaadin-context-menu-item-core-styles.d.ts +0 -8
- package/src/styles/vaadin-context-menu-item-core-styles.js +0 -8
- package/src/styles/vaadin-context-menu-overlay-core-styles.d.ts +0 -8
- package/src/styles/vaadin-context-menu-overlay-core-styles.js +0 -9
- package/src/styles/vaadin-menu-overlay-core-styles.d.ts +0 -8
- package/src/styles/vaadin-menu-overlay-core-styles.js +0 -32
- package/theme/lumo/vaadin-context-menu-item-styles.d.ts +0 -6
- package/theme/lumo/vaadin-context-menu-item-styles.js +0 -45
- package/theme/lumo/vaadin-context-menu-list-box-styles.d.ts +0 -5
- package/theme/lumo/vaadin-context-menu-list-box-styles.js +0 -47
- package/theme/lumo/vaadin-context-menu-overlay-styles.d.ts +0 -4
- package/theme/lumo/vaadin-context-menu-overlay-styles.js +0 -35
- package/theme/lumo/vaadin-context-menu.d.ts +0 -4
- package/theme/lumo/vaadin-context-menu.js +0 -4
|
@@ -8,9 +8,9 @@ import './vaadin-context-menu-item.js';
|
|
|
8
8
|
import './vaadin-context-menu-list-box.js';
|
|
9
9
|
import './vaadin-context-menu-overlay.js';
|
|
10
10
|
import { css, html, LitElement } from 'lit';
|
|
11
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
11
12
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
12
13
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
13
|
-
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
14
14
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
15
15
|
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
16
16
|
import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
|
|
@@ -175,18 +175,30 @@ import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
|
|
|
175
175
|
*
|
|
176
176
|
* ### Styling
|
|
177
177
|
*
|
|
178
|
-
*
|
|
179
|
-
* themable component as the actual visible context menu overlay.
|
|
178
|
+
* The following shadow DOM parts are available for styling:
|
|
180
179
|
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
180
|
+
* Part name | Description
|
|
181
|
+
* -----------------|-------------------------------------------
|
|
182
|
+
* `backdrop` | Backdrop of the overlay
|
|
183
|
+
* `overlay` | The overlay container
|
|
184
|
+
* `content` | The overlay content
|
|
185
|
+
*
|
|
186
|
+
* ### Custom CSS Properties
|
|
187
|
+
*
|
|
188
|
+
* The following custom CSS properties are available for styling:
|
|
189
|
+
*
|
|
190
|
+
* Custom CSS property | Description
|
|
191
|
+
* --------------------------------------|-------------
|
|
192
|
+
* `--vaadin-context-menu-offset-top` | Used as an offset when using `position` and the context menu is aligned vertically below the target
|
|
193
|
+
* `--vaadin-context-menu-offset-bottom` | Used as an offset when using `position` and the context menu is aligned vertically above the target
|
|
194
|
+
* `--vaadin-context-menu-offset-start` | Used as an offset when using `position` and the context menu is aligned horizontally after the target
|
|
195
|
+
* `--vaadin-context-menu-offset-end` | Used as an offset when using `position` and the context menu is aligned horizontally before the target
|
|
183
196
|
*
|
|
184
197
|
* See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
|
|
185
198
|
*
|
|
186
199
|
* ### Internal components
|
|
187
200
|
*
|
|
188
|
-
* When using `items` API
|
|
189
|
-
* internal components are themable:
|
|
201
|
+
* When using `items` API the following internal components are themable:
|
|
190
202
|
*
|
|
191
203
|
* - `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
|
|
192
204
|
* - `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
|
|
@@ -198,22 +210,17 @@ import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
|
|
|
198
210
|
* ---------- |-------------
|
|
199
211
|
* `expanded` | Expanded parent item.
|
|
200
212
|
*
|
|
201
|
-
* Note: the `theme` attribute value set on `<vaadin-context-menu>` is
|
|
202
|
-
* propagated to the internal components listed above.
|
|
203
|
-
*
|
|
204
213
|
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
205
214
|
* @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
|
|
215
|
+
* @fires {CustomEvent} closed - Fired when the context menu is closed.
|
|
206
216
|
*
|
|
207
217
|
* @customElement
|
|
208
218
|
* @extends HTMLElement
|
|
209
219
|
* @mixes ElementMixin
|
|
210
220
|
* @mixes ContextMenuMixin
|
|
211
|
-
* @mixes OverlayClassMixin
|
|
212
221
|
* @mixes ThemePropertyMixin
|
|
213
222
|
*/
|
|
214
|
-
class ContextMenu extends ContextMenuMixin(
|
|
215
|
-
OverlayClassMixin(ElementMixin(ThemePropertyMixin(PolylitMixin(LitElement)))),
|
|
216
|
-
) {
|
|
223
|
+
class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(PolylitMixin(LitElement)))) {
|
|
217
224
|
static get is() {
|
|
218
225
|
return 'vaadin-context-menu';
|
|
219
226
|
}
|
|
@@ -230,19 +237,87 @@ class ContextMenu extends ContextMenuMixin(
|
|
|
230
237
|
`;
|
|
231
238
|
}
|
|
232
239
|
|
|
240
|
+
static get properties() {
|
|
241
|
+
return {
|
|
242
|
+
/**
|
|
243
|
+
* Position of the overlay with respect to the target.
|
|
244
|
+
* Supported values: null, `top-start`, `top`, `top-end`,
|
|
245
|
+
* `bottom-start`, `bottom`, `bottom-end`, `start-top`,
|
|
246
|
+
* `start`, `start-bottom`, `end-top`, `end`, `end-bottom`.
|
|
247
|
+
*/
|
|
248
|
+
position: {
|
|
249
|
+
type: String,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
233
254
|
/** @protected */
|
|
234
255
|
render() {
|
|
235
|
-
|
|
256
|
+
const { _context: context, position } = this;
|
|
257
|
+
|
|
258
|
+
return html`
|
|
259
|
+
<slot id="slot"></slot>
|
|
260
|
+
<vaadin-context-menu-overlay
|
|
261
|
+
id="overlay"
|
|
262
|
+
.owner="${this}"
|
|
263
|
+
.opened="${this.opened}"
|
|
264
|
+
.model="${context}"
|
|
265
|
+
.modeless="${this._modeless}"
|
|
266
|
+
.renderer="${this.items ? this.__itemsRenderer : this.renderer}"
|
|
267
|
+
.position="${position}"
|
|
268
|
+
.positionTarget="${position ? context && context.target : this._positionTarget}"
|
|
269
|
+
.horizontalAlign="${this.__computeHorizontalAlign(position)}"
|
|
270
|
+
.verticalAlign="${this.__computeVerticalAlign(position)}"
|
|
271
|
+
?no-horizontal-overlap="${this.__computeNoHorizontalOverlap(position)}"
|
|
272
|
+
?no-vertical-overlap="${this.__computeNoVerticalOverlap(position)}"
|
|
273
|
+
.withBackdrop="${this._phone}"
|
|
274
|
+
?phone="${this._phone}"
|
|
275
|
+
theme="${ifDefined(this._theme)}"
|
|
276
|
+
exportparts="backdrop, overlay, content"
|
|
277
|
+
@opened-changed="${this._onOverlayOpened}"
|
|
278
|
+
@vaadin-overlay-open="${this._onVaadinOverlayOpen}"
|
|
279
|
+
@vaadin-overlay-closed="${this._onVaadinOverlayClosed}"
|
|
280
|
+
>
|
|
281
|
+
<slot name="overlay"></slot>
|
|
282
|
+
<slot name="submenu" slot="submenu"></slot>
|
|
283
|
+
</vaadin-context-menu-overlay>
|
|
284
|
+
`;
|
|
236
285
|
}
|
|
237
286
|
|
|
238
|
-
/**
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
287
|
+
/** @private */
|
|
288
|
+
__computeHorizontalAlign(position) {
|
|
289
|
+
if (!position) {
|
|
290
|
+
return 'start';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return ['top-end', 'bottom-end', 'start-top', 'start', 'start-bottom'].includes(position) ? 'end' : 'start';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** @private */
|
|
297
|
+
__computeNoHorizontalOverlap(position) {
|
|
298
|
+
if (!position) {
|
|
299
|
+
return !!this._positionTarget;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return ['start-top', 'start', 'start-bottom', 'end-top', 'end', 'end-bottom'].includes(position);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** @private */
|
|
306
|
+
__computeNoVerticalOverlap(position) {
|
|
307
|
+
if (!position) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return ['top-start', 'top-end', 'top', 'bottom-start', 'bottom', 'bottom-end'].includes(position);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** @private */
|
|
315
|
+
__computeVerticalAlign(position) {
|
|
316
|
+
if (!position) {
|
|
317
|
+
return 'top';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return ['top-start', 'top-end', 'top', 'start-bottom', 'end-bottom'].includes(position) ? 'bottom' : 'top';
|
|
246
321
|
}
|
|
247
322
|
|
|
248
323
|
/**
|
|
@@ -16,14 +16,14 @@ export const ItemsMixin = (superClass) =>
|
|
|
16
16
|
* @typedef ContextMenuItem
|
|
17
17
|
* @type {object}
|
|
18
18
|
* @property {string} text - Text to be set as the menu item component's textContent
|
|
19
|
-
* @property {
|
|
19
|
+
* @property {string | HTMLElement} component - The component to represent the item.
|
|
20
20
|
* Either a tagName or an element instance. Defaults to "vaadin-context-menu-item".
|
|
21
21
|
* @property {boolean} disabled - If true, the item is disabled and cannot be selected
|
|
22
22
|
* @property {boolean} checked - If true, the item shows a checkmark next to it
|
|
23
23
|
* @property {boolean} keepOpen - If true, the menu will not be closed on item selection
|
|
24
24
|
* @property {string} className - A space-delimited list of CSS class names to be set on the menu item component.
|
|
25
|
-
* @property {
|
|
26
|
-
* @property {
|
|
25
|
+
* @property {string | string[]} theme - If set, sets the given theme(s) as an attribute to the menu item component, overriding any theme set on the context menu.
|
|
26
|
+
* @property {ContextMenuItem[]} children - Array of child menu items
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -60,6 +60,12 @@ export const ItemsMixin = (superClass) =>
|
|
|
60
60
|
type: Array,
|
|
61
61
|
sync: true,
|
|
62
62
|
},
|
|
63
|
+
|
|
64
|
+
/** @protected */
|
|
65
|
+
_positionTarget: {
|
|
66
|
+
type: Object,
|
|
67
|
+
sync: true,
|
|
68
|
+
},
|
|
63
69
|
};
|
|
64
70
|
}
|
|
65
71
|
|
|
@@ -116,10 +122,10 @@ export const ItemsMixin = (superClass) =>
|
|
|
116
122
|
/** @protected */
|
|
117
123
|
__forwardFocus() {
|
|
118
124
|
const overlay = this._overlayElement;
|
|
119
|
-
const child = overlay.
|
|
125
|
+
const child = overlay._contentRoot.firstElementChild;
|
|
120
126
|
// If parent item is not focused, do not focus submenu
|
|
121
127
|
if (overlay.parentOverlay) {
|
|
122
|
-
const parent = overlay.parentOverlay.querySelector('[expanded]');
|
|
128
|
+
const parent = overlay.parentOverlay._contentRoot.querySelector('[expanded]');
|
|
123
129
|
if (parent && parent.hasAttribute('focused') && child) {
|
|
124
130
|
child.focus();
|
|
125
131
|
} else {
|
|
@@ -131,14 +137,12 @@ export const ItemsMixin = (superClass) =>
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
/** @private */
|
|
134
|
-
__openSubMenu(subMenu, itemElement
|
|
140
|
+
__openSubMenu(subMenu, itemElement) {
|
|
141
|
+
// Update sub-menu items and position target
|
|
135
142
|
this.__updateSubMenuForItem(subMenu, itemElement);
|
|
136
|
-
subMenu.overlayClass = overlayClass;
|
|
137
143
|
|
|
138
144
|
const parent = this._overlayElement;
|
|
139
|
-
|
|
140
145
|
const subMenuOverlay = subMenu._overlayElement;
|
|
141
|
-
subMenuOverlay.noHorizontalOverlap = true;
|
|
142
146
|
// Store the reference parent overlay
|
|
143
147
|
subMenuOverlay._setParentOverlay(parent);
|
|
144
148
|
|
|
@@ -165,7 +169,8 @@ export const ItemsMixin = (superClass) =>
|
|
|
165
169
|
__updateSubMenuForItem(subMenu, itemElement) {
|
|
166
170
|
subMenu.items = itemElement._item.children;
|
|
167
171
|
subMenu.listenOn = itemElement;
|
|
168
|
-
subMenu.
|
|
172
|
+
subMenu._positionTarget = itemElement;
|
|
173
|
+
subMenu._overlayElement.requestContentUpdate();
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
/**
|
|
@@ -253,10 +258,20 @@ export const ItemsMixin = (superClass) =>
|
|
|
253
258
|
// Open a submenu on click event when a touch device is used.
|
|
254
259
|
// On desktop, a submenu opens on hover.
|
|
255
260
|
overlay.addEventListener(isTouch ? 'click' : 'mouseover', (event) => {
|
|
261
|
+
// Ignore events from the submenus
|
|
262
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
this.__showSubMenu(event);
|
|
257
267
|
});
|
|
258
268
|
|
|
259
269
|
overlay.addEventListener('keydown', (event) => {
|
|
270
|
+
// Ignore events from the submenus
|
|
271
|
+
if (event.composedPath().includes(this._subMenu)) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
260
275
|
const { key } = event;
|
|
261
276
|
const isRTL = this.__isRTL;
|
|
262
277
|
|
|
@@ -288,10 +303,6 @@ export const ItemsMixin = (superClass) =>
|
|
|
288
303
|
subMenu._modeless = true;
|
|
289
304
|
subMenu.openOn = 'opensubmenu';
|
|
290
305
|
|
|
291
|
-
// Sub-menu doesn't have a target to wrap,
|
|
292
|
-
// so there is no need to keep it visible.
|
|
293
|
-
subMenu.setAttribute('hidden', '');
|
|
294
|
-
|
|
295
306
|
// Close sub-menu when the parent menu closes.
|
|
296
307
|
this.addEventListener('opened-changed', (event) => {
|
|
297
308
|
if (!event.detail.value) {
|
|
@@ -361,27 +372,33 @@ export const ItemsMixin = (superClass) =>
|
|
|
361
372
|
}
|
|
362
373
|
|
|
363
374
|
const subMenu = this._subMenu;
|
|
375
|
+
const expandedItem = this._listBox.querySelector('[expanded]');
|
|
364
376
|
|
|
365
|
-
if (item) {
|
|
377
|
+
if (item && item !== expandedItem) {
|
|
366
378
|
const { children } = item._item;
|
|
367
379
|
|
|
368
380
|
// Check if the sub-menu was focused before closing it.
|
|
369
|
-
const child = subMenu._overlayElement.
|
|
381
|
+
const child = subMenu._overlayElement._contentRoot.firstElementChild;
|
|
370
382
|
const isSubmenuFocused = child && child.focused;
|
|
371
383
|
|
|
372
|
-
|
|
384
|
+
// Mark previously expanded item as collapsed
|
|
385
|
+
if (expandedItem) {
|
|
386
|
+
this.__updateExpanded(expandedItem, false);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Close sub-menu if there are no children for the new item
|
|
390
|
+
if (!children || !children.length) {
|
|
373
391
|
subMenu.close();
|
|
374
392
|
}
|
|
393
|
+
|
|
375
394
|
if (!this.opened) {
|
|
376
395
|
return;
|
|
377
396
|
}
|
|
378
397
|
|
|
379
398
|
if (children && children.length) {
|
|
399
|
+
// Open or update the submenu if the new item has children
|
|
380
400
|
this.__updateExpanded(item, true);
|
|
381
|
-
|
|
382
|
-
// Forward parent overlay class
|
|
383
|
-
const { overlayClass } = this;
|
|
384
|
-
this.__openSubMenu(subMenu, item, overlayClass);
|
|
401
|
+
this.__openSubMenu(subMenu, item);
|
|
385
402
|
} else if (isSubmenuFocused) {
|
|
386
403
|
// If the sub-menu item was focused, focus its parent item.
|
|
387
404
|
subMenu.listenOn.focus();
|
|
@@ -394,27 +411,23 @@ export const ItemsMixin = (superClass) =>
|
|
|
394
411
|
|
|
395
412
|
/** @protected */
|
|
396
413
|
__getListBox() {
|
|
397
|
-
return this._overlayElement.querySelector(`${this._tagNamePrefix}-list-box`);
|
|
414
|
+
return this._overlayElement._contentRoot.querySelector(`${this._tagNamePrefix}-list-box`);
|
|
398
415
|
}
|
|
399
416
|
|
|
400
417
|
/**
|
|
401
418
|
* @param {!HTMLElement} root
|
|
402
419
|
* @param {!ContextMenu} menu
|
|
403
|
-
* @param {!ContextMenuRendererContext} context
|
|
404
420
|
* @protected
|
|
405
421
|
*/
|
|
406
422
|
__itemsRenderer(root, menu) {
|
|
407
423
|
this.__initMenu(root, menu);
|
|
408
424
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
const listBox = this.__getListBox();
|
|
413
|
-
listBox.innerHTML = '';
|
|
425
|
+
this._subMenu.closeOn = menu.closeOn;
|
|
426
|
+
this._listBox.innerHTML = '';
|
|
414
427
|
|
|
415
428
|
menu.items.forEach((item) => {
|
|
416
429
|
const component = this.__createComponent(item);
|
|
417
|
-
|
|
430
|
+
this._listBox.appendChild(component);
|
|
418
431
|
});
|
|
419
432
|
}
|
|
420
433
|
|
|
@@ -455,8 +468,9 @@ export const ItemsMixin = (superClass) =>
|
|
|
455
468
|
root.appendChild(listBox);
|
|
456
469
|
|
|
457
470
|
const subMenu = this.__initSubMenu();
|
|
471
|
+
subMenu.slot = 'submenu';
|
|
458
472
|
this._subMenu = subMenu;
|
|
459
|
-
|
|
473
|
+
this.appendChild(subMenu);
|
|
460
474
|
|
|
461
475
|
requestAnimationFrame(() => {
|
|
462
476
|
this.__openListenerActive = true;
|
|
@@ -18,9 +18,4 @@ export declare class MenuOverlayMixinClass {
|
|
|
18
18
|
* Returns the adjusted boundaries of the overlay.
|
|
19
19
|
*/
|
|
20
20
|
getBoundaries(): { xMax: number; xMin: number; yMax: number };
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Returns the first element in the overlay content.
|
|
24
|
-
*/
|
|
25
|
-
getFirstChild(): HTMLElement;
|
|
26
21
|
}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Copyright (c) 2016 - 2025 Vaadin Ltd.
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
|
-
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
|
|
7
6
|
import { OverlayFocusMixin } from '@vaadin/overlay/src/vaadin-overlay-focus-mixin.js';
|
|
8
7
|
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
|
|
9
8
|
|
|
@@ -37,6 +36,23 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
37
36
|
return ['_themeChanged(_theme)'];
|
|
38
37
|
}
|
|
39
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Override method from OverlayFocusMixin to use slotted div as the content root.
|
|
41
|
+
* @protected
|
|
42
|
+
* @override
|
|
43
|
+
*/
|
|
44
|
+
get _contentRoot() {
|
|
45
|
+
if (!this.__savedRoot) {
|
|
46
|
+
const root = document.createElement('div');
|
|
47
|
+
root.setAttribute('slot', 'overlay');
|
|
48
|
+
root.style.display = 'contents';
|
|
49
|
+
this.owner.appendChild(root);
|
|
50
|
+
this.__savedRoot = root;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return this.__savedRoot;
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
/** @protected */
|
|
41
57
|
ready() {
|
|
42
58
|
super.ready();
|
|
@@ -45,7 +61,7 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
45
61
|
|
|
46
62
|
this.addEventListener('keydown', (e) => {
|
|
47
63
|
if (!e.defaultPrevented && e.composedPath()[0] === this.$.overlay && [38, 40].indexOf(e.keyCode) > -1) {
|
|
48
|
-
const child = this.
|
|
64
|
+
const child = this._contentRoot.firstElementChild;
|
|
49
65
|
if (child && Array.isArray(child.items) && child.items.length) {
|
|
50
66
|
e.preventDefault();
|
|
51
67
|
if (e.keyCode === 38) {
|
|
@@ -58,15 +74,6 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
58
74
|
});
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
/**
|
|
62
|
-
* Returns the first element in the overlay content.
|
|
63
|
-
*
|
|
64
|
-
* @returns {HTMLElement}
|
|
65
|
-
*/
|
|
66
|
-
getFirstChild() {
|
|
67
|
-
return this.querySelector(':not(style):not(slot)');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
77
|
/** @private */
|
|
71
78
|
_themeChanged() {
|
|
72
79
|
this.close();
|
|
@@ -106,7 +113,7 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
106
113
|
_updatePosition() {
|
|
107
114
|
super._updatePosition();
|
|
108
115
|
|
|
109
|
-
if (this.positionTarget && this.parentOverlay) {
|
|
116
|
+
if (this.positionTarget && this.parentOverlay && this.opened) {
|
|
110
117
|
// This overlay is positioned by a parent menu item,
|
|
111
118
|
// adjust the position by the overlay content paddings
|
|
112
119
|
const content = this.$.content;
|
|
@@ -149,9 +156,9 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
/**
|
|
152
|
-
* Override method inherited from `OverlayFocusMixin` to
|
|
153
|
-
*
|
|
154
|
-
*
|
|
159
|
+
* Override method inherited from `OverlayFocusMixin` to check if the
|
|
160
|
+
* node is contained within the overlay's owner element (the menu),
|
|
161
|
+
* where all content (overlay content, sub-menus, etc.) is slotted.
|
|
155
162
|
*
|
|
156
163
|
* @protected
|
|
157
164
|
* @override
|
|
@@ -159,18 +166,6 @@ export const MenuOverlayMixin = (superClass) =>
|
|
|
159
166
|
* @return {boolean}
|
|
160
167
|
*/
|
|
161
168
|
_deepContains(node) {
|
|
162
|
-
|
|
163
|
-
let overlay = getClosestElement(this.localName, node);
|
|
164
|
-
while (overlay) {
|
|
165
|
-
if (overlay === this) {
|
|
166
|
-
// The node is inside a descendant menu overlay.
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Traverse the overlay hierarchy to check parent overlays.
|
|
171
|
-
overlay = overlay.parentOverlay;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return false;
|
|
169
|
+
return this.owner.contains(node);
|
|
175
170
|
}
|
|
176
171
|
};
|
package/vaadin-context-menu.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import './
|
|
1
|
+
import './src/vaadin-context-menu.js';
|
|
2
2
|
export * from './src/vaadin-context-menu.js';
|
package/web-types.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/context-menu",
|
|
4
|
-
"version": "25.0.0-
|
|
4
|
+
"version": "25.0.0-beta1",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
8
8
|
"elements": [
|
|
9
9
|
{
|
|
10
10
|
"name": "vaadin-context-menu",
|
|
11
|
-
"description": "`<vaadin-context-menu>` is a Web Component for creating context menus.\n\n### Items\n\nItems is a higher level convenience API for defining a (hierarchical) menu structure for the component.\nIf a menu item has a non-empty `children` set, a sub-menu with the child items is opened\nnext to the parent menu on mouseover, tap or a right arrow keypress.\n\nWhen an item is selected, `<vaadin-context-menu>` dispatches an \"item-selected\" event\nwith the selected item as `event.detail.value` property.\nIf item does not have `keepOpen` property the menu will be closed.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', className: 'first', children:\n [\n { text: 'Menu Item 1-1', checked: true, keepOpen: true },\n { text: 'Menu Item 1-2' }\n ]\n },\n { component: 'hr' },\n { text: 'Menu Item 2', children:\n [\n { text: 'Menu Item 2-1' },\n { text: 'Menu Item 2-2', disabled: true }\n ]\n },\n { text: 'Menu Item 3', disabled: true, className: 'last' }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n
|
|
11
|
+
"description": "`<vaadin-context-menu>` is a Web Component for creating context menus.\n\n### Items\n\nItems is a higher level convenience API for defining a (hierarchical) menu structure for the component.\nIf a menu item has a non-empty `children` set, a sub-menu with the child items is opened\nnext to the parent menu on mouseover, tap or a right arrow keypress.\n\nWhen an item is selected, `<vaadin-context-menu>` dispatches an \"item-selected\" event\nwith the selected item as `event.detail.value` property.\nIf item does not have `keepOpen` property the menu will be closed.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', className: 'first', children:\n [\n { text: 'Menu Item 1-1', checked: true, keepOpen: true },\n { text: 'Menu Item 1-2' }\n ]\n },\n { component: 'hr' },\n { text: 'Menu Item 2', children:\n [\n { text: 'Menu Item 2-1' },\n { text: 'Menu Item 2-2', disabled: true }\n ]\n },\n { text: 'Menu Item 3', disabled: true, className: 'last' }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------------------------------------\n`backdrop` | Backdrop of the overlay\n`overlay` | The overlay container\n`content` | The overlay content\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property | Description\n--------------------------------------|-------------\n`--vaadin-context-menu-offset-top` | Used as an offset when using `position` and the context menu is aligned vertically below the target\n`--vaadin-context-menu-offset-bottom` | Used as an offset when using `position` and the context menu is aligned vertically above the target\n`--vaadin-context-menu-offset-start` | Used as an offset when using `position` and the context menu is aligned horizontally after the target\n`--vaadin-context-menu-offset-end` | Used as an offset when using `position` and the context menu is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API the following internal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-list-box).\n\nThe `<vaadin-context-menu-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.",
|
|
12
12
|
"attributes": [
|
|
13
13
|
{
|
|
14
14
|
"name": "selector",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
|
-
"name": "
|
|
44
|
-
"description": "
|
|
43
|
+
"name": "position",
|
|
44
|
+
"description": "Position of the overlay with respect to the target.\nSupported values: null, `top-start`, `top`, `top-end`,\n`bottom-start`, `bottom`, `bottom-end`, `start-top`,\n`start`, `start-bottom`, `end-top`, `end`, `end-bottom`.",
|
|
45
45
|
"value": {
|
|
46
46
|
"type": [
|
|
47
47
|
"string",
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
}
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
|
-
"name": "
|
|
127
|
-
"description": "
|
|
126
|
+
"name": "position",
|
|
127
|
+
"description": "Position of the overlay with respect to the target.\nSupported values: null, `top-start`, `top`, `top-end`,\n`bottom-start`, `bottom`, `bottom-end`, `start-top`,\n`start`, `start-bottom`, `end-top`, `end`, `end-bottom`.",
|
|
128
128
|
"value": {
|
|
129
129
|
"type": [
|
|
130
130
|
"string",
|
|
@@ -135,6 +135,10 @@
|
|
|
135
135
|
}
|
|
136
136
|
],
|
|
137
137
|
"events": [
|
|
138
|
+
{
|
|
139
|
+
"name": "closed",
|
|
140
|
+
"description": "Fired when the context menu is closed."
|
|
141
|
+
},
|
|
138
142
|
{
|
|
139
143
|
"name": "item-selected",
|
|
140
144
|
"description": "Fired when an item is selected when the context menu is populated using the `items` API."
|
package/web-types.lit.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/context-menu",
|
|
4
|
-
"version": "25.0.0-
|
|
4
|
+
"version": "25.0.0-beta1",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"framework": "lit",
|
|
7
7
|
"framework-config": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"elements": [
|
|
17
17
|
{
|
|
18
18
|
"name": "vaadin-context-menu",
|
|
19
|
-
"description": "`<vaadin-context-menu>` is a Web Component for creating context menus.\n\n### Items\n\nItems is a higher level convenience API for defining a (hierarchical) menu structure for the component.\nIf a menu item has a non-empty `children` set, a sub-menu with the child items is opened\nnext to the parent menu on mouseover, tap or a right arrow keypress.\n\nWhen an item is selected, `<vaadin-context-menu>` dispatches an \"item-selected\" event\nwith the selected item as `event.detail.value` property.\nIf item does not have `keepOpen` property the menu will be closed.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', className: 'first', children:\n [\n { text: 'Menu Item 1-1', checked: true, keepOpen: true },\n { text: 'Menu Item 1-2' }\n ]\n },\n { component: 'hr' },\n { text: 'Menu Item 2', children:\n [\n { text: 'Menu Item 2-1' },\n { text: 'Menu Item 2-2', disabled: true }\n ]\n },\n { text: 'Menu Item 3', disabled: true, className: 'last' }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n
|
|
19
|
+
"description": "`<vaadin-context-menu>` is a Web Component for creating context menus.\n\n### Items\n\nItems is a higher level convenience API for defining a (hierarchical) menu structure for the component.\nIf a menu item has a non-empty `children` set, a sub-menu with the child items is opened\nnext to the parent menu on mouseover, tap or a right arrow keypress.\n\nWhen an item is selected, `<vaadin-context-menu>` dispatches an \"item-selected\" event\nwith the selected item as `event.detail.value` property.\nIf item does not have `keepOpen` property the menu will be closed.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', className: 'first', children:\n [\n { text: 'Menu Item 1-1', checked: true, keepOpen: true },\n { text: 'Menu Item 1-2' }\n ]\n },\n { component: 'hr' },\n { text: 'Menu Item 2', children:\n [\n { text: 'Menu Item 2-1' },\n { text: 'Menu Item 2-2', disabled: true }\n ]\n },\n { text: 'Menu Item 3', disabled: true, className: 'last' }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n-----------------|-------------------------------------------\n`backdrop` | Backdrop of the overlay\n`overlay` | The overlay container\n`content` | The overlay content\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property | Description\n--------------------------------------|-------------\n`--vaadin-context-menu-offset-top` | Used as an offset when using `position` and the context menu is aligned vertically below the target\n`--vaadin-context-menu-offset-bottom` | Used as an offset when using `position` and the context menu is aligned vertically above the target\n`--vaadin-context-menu-offset-start` | Used as an offset when using `position` and the context menu is aligned horizontally after the target\n`--vaadin-context-menu-offset-end` | Used as an offset when using `position` and the context menu is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API the following internal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/25.0.0-beta1/#/elements/vaadin-list-box).\n\nThe `<vaadin-context-menu-item>` sub-menu elements have the following additional state attributes\non top of the built-in `<vaadin-item>` state attributes:\n\nAttribute | Description\n---------- |-------------\n`expanded` | Expanded parent item.",
|
|
20
20
|
"extension": true,
|
|
21
21
|
"attributes": [
|
|
22
22
|
{
|
|
@@ -62,8 +62,15 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
"name": ".
|
|
66
|
-
"description": "
|
|
65
|
+
"name": ".position",
|
|
66
|
+
"description": "Position of the overlay with respect to the target.\nSupported values: null, `top-start`, `top`, `top-end`,\n`bottom-start`, `bottom`, `bottom-end`, `start-top`,\n`start`, `start-bottom`, `end-top`, `end`, `end-bottom`.",
|
|
67
|
+
"value": {
|
|
68
|
+
"kind": "expression"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "@closed",
|
|
73
|
+
"description": "Fired when the context menu is closed.",
|
|
67
74
|
"value": {
|
|
68
75
|
"kind": "expression"
|
|
69
76
|
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) 2016 - 2025 Vaadin Ltd.
|
|
4
|
-
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
-
*/
|
|
6
|
-
import { itemStyles } from '@vaadin/item/src/styles/vaadin-item-core-styles.js';
|
|
7
|
-
|
|
8
|
-
export const contextMenuItemStyles = [itemStyles];
|