@vaadin/context-menu 25.0.0-alpha16 → 25.0.0-alpha18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/context-menu",
3
- "version": "25.0.0-alpha16",
3
+ "version": "25.0.0-alpha18",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -36,25 +36,25 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "@open-wc/dedupe-mixin": "^1.3.0",
39
- "@vaadin/a11y-base": "25.0.0-alpha16",
40
- "@vaadin/component-base": "25.0.0-alpha16",
41
- "@vaadin/item": "25.0.0-alpha16",
42
- "@vaadin/list-box": "25.0.0-alpha16",
43
- "@vaadin/lit-renderer": "25.0.0-alpha16",
44
- "@vaadin/overlay": "25.0.0-alpha16",
45
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha16",
39
+ "@vaadin/a11y-base": "25.0.0-alpha18",
40
+ "@vaadin/component-base": "25.0.0-alpha18",
41
+ "@vaadin/item": "25.0.0-alpha18",
42
+ "@vaadin/list-box": "25.0.0-alpha18",
43
+ "@vaadin/lit-renderer": "25.0.0-alpha18",
44
+ "@vaadin/overlay": "25.0.0-alpha18",
45
+ "@vaadin/vaadin-themable-mixin": "25.0.0-alpha18",
46
46
  "lit": "^3.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@vaadin/chai-plugins": "25.0.0-alpha16",
50
- "@vaadin/test-runner-commands": "25.0.0-alpha16",
49
+ "@vaadin/chai-plugins": "25.0.0-alpha18",
50
+ "@vaadin/test-runner-commands": "25.0.0-alpha18",
51
51
  "@vaadin/testing-helpers": "^2.0.0",
52
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha16",
53
- "sinon": "^18.0.0"
52
+ "@vaadin/vaadin-lumo-styles": "25.0.0-alpha18",
53
+ "sinon": "^21.0.0"
54
54
  },
55
55
  "web-types": [
56
56
  "web-types.json",
57
57
  "web-types.lit.json"
58
58
  ],
59
- "gitHead": "4b316158a4a4f702f032bc9940fc82f0faa840f4"
59
+ "gitHead": "cb5cafb5687a117ebead1b81e2116991cec13abe"
60
60
  }
@@ -3,7 +3,34 @@
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 { css } from 'lit';
6
7
  import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-base-styles.js';
7
8
  import { menuOverlayStyles } from './vaadin-menu-overlay-base-styles.js';
8
9
 
9
- export const contextMenuOverlayStyles = [overlayStyles, menuOverlayStyles];
10
+ const contextMenuOverlay = css`
11
+ :host {
12
+ --_default-offset: 4px;
13
+ }
14
+
15
+ :host([position^='top'][top-aligned]) [part='overlay'],
16
+ :host([position^='bottom'][top-aligned]) [part='overlay'] {
17
+ margin-top: var(--vaadin-context-menu-offset-top, var(--_default-offset));
18
+ }
19
+
20
+ :host([position^='top'][bottom-aligned]) [part='overlay'],
21
+ :host([position^='bottom'][bottom-aligned]) [part='overlay'] {
22
+ margin-bottom: var(--vaadin-context-menu-offset-bottom, var(--_default-offset));
23
+ }
24
+
25
+ :host([position^='start'][start-aligned]) [part='overlay'],
26
+ :host([position^='end'][start-aligned]) [part='overlay'] {
27
+ margin-inline-start: var(--vaadin-context-menu-offset-start, var(--_default-offset));
28
+ }
29
+
30
+ :host([position^='start'][end-aligned]) [part='overlay'],
31
+ :host([position^='end'][end-aligned]) [part='overlay'] {
32
+ margin-inline-end: var(--vaadin-context-menu-offset-end, var(--_default-offset));
33
+ }
34
+ `;
35
+
36
+ export const contextMenuOverlayStyles = [overlayStyles, menuOverlayStyles, contextMenuOverlay];
@@ -342,6 +342,9 @@ export const ContextMenuMixin = (superClass) =>
342
342
  return Array.prototype.filter.call(targets, (el) => {
343
343
  return e.composedPath().indexOf(el) > -1;
344
344
  })[0];
345
+ } else if (this.listenOn && this.listenOn !== this && this.position) {
346
+ // If listenOn has been set on a different element than the context menu root, then use listenOn as the target.
347
+ return this.listenOn;
345
348
  }
346
349
  return e.target;
347
350
  }
@@ -435,17 +438,13 @@ export const ContextMenuMixin = (superClass) =>
435
438
  /** @private */
436
439
  __focusItem(item) {
437
440
  if (item) {
438
- item.focus();
439
-
440
- if (isKeyboardActive()) {
441
- item.setAttribute('focus-ring', '');
442
- }
441
+ item.focus({ focusVisible: isKeyboardActive() });
443
442
  }
444
443
  }
445
444
 
446
445
  /** @private */
447
446
  __onScroll() {
448
- if (!this.opened) {
447
+ if (!this.opened || this.position) {
449
448
  return;
450
449
  }
451
450
 
@@ -31,10 +31,64 @@ export class ContextMenuOverlay extends MenuOverlayMixin(
31
31
  return 'vaadin-context-menu-overlay';
32
32
  }
33
33
 
34
+ static get properties() {
35
+ return {
36
+ /**
37
+ * Position of the overlay with respect to the target.
38
+ * Supported values: null, `top-start`, `top`, `top-end`,
39
+ * `bottom-start`, `bottom`, `bottom-end`, `start-top`,
40
+ * `start`, `start-bottom`, `end-top`, `end`, `end-bottom`.
41
+ */
42
+ position: {
43
+ type: String,
44
+ reflectToAttribute: true,
45
+ },
46
+ };
47
+ }
48
+
34
49
  static get styles() {
35
50
  return contextMenuOverlayStyles;
36
51
  }
37
52
 
53
+ /**
54
+ * @protected
55
+ * @override
56
+ */
57
+ _updatePosition() {
58
+ super._updatePosition();
59
+
60
+ if (this.parentOverlay == null && this.positionTarget && this.position && this.opened) {
61
+ if (this.position === 'bottom' || this.position === 'top') {
62
+ const targetRect = this.positionTarget.getBoundingClientRect();
63
+ const overlayRect = this.$.overlay.getBoundingClientRect();
64
+
65
+ const offset = targetRect.width / 2 - overlayRect.width / 2;
66
+
67
+ if (this.style.left) {
68
+ const left = overlayRect.left + offset;
69
+ if (left > 0) {
70
+ this.style.left = `${left}px`;
71
+ }
72
+ }
73
+
74
+ if (this.style.right) {
75
+ const right = parseFloat(this.style.right) + offset;
76
+ if (right > 0) {
77
+ this.style.right = `${right}px`;
78
+ }
79
+ }
80
+ }
81
+
82
+ if (this.position === 'start' || this.position === 'end') {
83
+ const targetRect = this.positionTarget.getBoundingClientRect();
84
+ const overlayRect = this.$.overlay.getBoundingClientRect();
85
+
86
+ const offset = targetRect.height / 2 - overlayRect.height / 2;
87
+ this.style.top = `${overlayRect.top + offset}px`;
88
+ }
89
+ }
90
+ }
91
+
38
92
  /** @protected */
39
93
  render() {
40
94
  return html`
@@ -10,6 +10,20 @@ import type { ContextMenuItem } from './vaadin-contextmenu-items-mixin.js';
10
10
 
11
11
  export { ContextMenuItem };
12
12
 
13
+ export type ContextMenuPosition =
14
+ | 'bottom-end'
15
+ | 'bottom-start'
16
+ | 'bottom'
17
+ | 'end-bottom'
18
+ | 'end-top'
19
+ | 'end'
20
+ | 'start-bottom'
21
+ | 'start-top'
22
+ | 'start'
23
+ | 'top-end'
24
+ | 'top-start'
25
+ | 'top';
26
+
13
27
  export interface ContextMenuRendererContext {
14
28
  target: HTMLElement;
15
29
  detail?: { sourceEvent: Event };
@@ -222,6 +236,17 @@ export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenu
222
236
  * `overlay` | The overlay container
223
237
  * `content` | The overlay content
224
238
  *
239
+ * ### Custom CSS Properties
240
+ *
241
+ * The following custom CSS properties are available for styling:
242
+ *
243
+ * Custom CSS property | Description
244
+ * --------------------------------------|-------------
245
+ * `--vaadin-context-menu-offset-top` | Used as an offset when using `position` and the context menu is aligned vertically below the target
246
+ * `--vaadin-context-menu-offset-bottom` | Used as an offset when using `position` and the context menu is aligned vertically above the target
247
+ * `--vaadin-context-menu-offset-start` | Used as an offset when using `position` and the context menu is aligned horizontally after the target
248
+ * `--vaadin-context-menu-offset-end` | Used as an offset when using `position` and the context menu is aligned horizontally before the target
249
+ *
225
250
  * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
226
251
  *
227
252
  * ### Internal components
@@ -243,6 +268,14 @@ export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenu
243
268
  * @fires {CustomEvent} closed - Fired when the context menu is closed.
244
269
  */
245
270
  declare class ContextMenu<TItem extends ContextMenuItem = ContextMenuItem> extends HTMLElement {
271
+ /**
272
+ * Position of the overlay with respect to the target.
273
+ * Supported values: null, `top-start`, `top`, `top-end`,
274
+ * `bottom-start`, `bottom`, `bottom-end`, `start-top`,
275
+ * `start`, `start-bottom`, `end-top`, `end`, `end-bottom`.
276
+ */
277
+ position: ContextMenuPosition | null | undefined;
278
+
246
279
  addEventListener<K extends keyof ContextMenuEventMap>(
247
280
  type: K,
248
281
  listener: (this: ContextMenu<TItem>, ev: ContextMenuEventMap<TItem>[K]) => void,
@@ -183,6 +183,17 @@ import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
183
183
  * `overlay` | The overlay container
184
184
  * `content` | The overlay content
185
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
196
+ *
186
197
  * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
187
198
  *
188
199
  * ### Internal components
@@ -226,17 +237,39 @@ class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(Polyl
226
237
  `;
227
238
  }
228
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
+
229
254
  /** @protected */
230
255
  render() {
256
+ const { _context: context, position } = this;
257
+
231
258
  return html`
232
259
  <slot id="slot"></slot>
233
260
  <vaadin-context-menu-overlay
234
261
  id="overlay"
235
262
  .owner="${this}"
236
263
  .opened="${this.opened}"
237
- .model="${this._context}"
264
+ .model="${context}"
238
265
  .modeless="${this._modeless}"
239
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)}"
240
273
  .withBackdrop="${this._phone}"
241
274
  ?phone="${this._phone}"
242
275
  theme="${ifDefined(this._theme)}"
@@ -251,6 +284,42 @@ class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(Polyl
251
284
  `;
252
285
  }
253
286
 
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';
321
+ }
322
+
254
323
  /**
255
324
  * Fired when an item is selected when the context menu is populated using the `items` API.
256
325
  *
@@ -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
 
@@ -137,7 +143,6 @@ export const ItemsMixin = (superClass) =>
137
143
  const parent = this._overlayElement;
138
144
 
139
145
  const subMenuOverlay = subMenu._overlayElement;
140
- subMenuOverlay.noHorizontalOverlap = true;
141
146
  // Store the reference parent overlay
142
147
  subMenuOverlay._setParentOverlay(parent);
143
148
 
@@ -164,7 +169,7 @@ export const ItemsMixin = (superClass) =>
164
169
  __updateSubMenuForItem(subMenu, itemElement) {
165
170
  subMenu.items = itemElement._item.children;
166
171
  subMenu.listenOn = itemElement;
167
- subMenu._overlayElement.positionTarget = itemElement;
172
+ subMenu._positionTarget = itemElement;
168
173
  }
169
174
 
170
175
  /**
@@ -113,7 +113,7 @@ export const MenuOverlayMixin = (superClass) =>
113
113
  _updatePosition() {
114
114
  super._updatePosition();
115
115
 
116
- if (this.positionTarget && this.parentOverlay) {
116
+ if (this.positionTarget && this.parentOverlay && this.opened) {
117
117
  // This overlay is positioned by a parent menu item,
118
118
  // adjust the position by the overlay content paddings
119
119
  const content = this.$.content;
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-alpha16",
4
+ "version": "25.0.0-alpha18",
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\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\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-alpha16/#/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-alpha16/#/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.",
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-alpha18/#/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-alpha18/#/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",
@@ -39,6 +39,17 @@
39
39
  ]
40
40
  }
41
41
  },
42
+ {
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
+ "value": {
46
+ "type": [
47
+ "string",
48
+ "null",
49
+ "undefined"
50
+ ]
51
+ }
52
+ },
42
53
  {
43
54
  "name": "theme",
44
55
  "description": "The theme variants to apply to the component.",
@@ -110,6 +121,17 @@
110
121
  "undefined"
111
122
  ]
112
123
  }
124
+ },
125
+ {
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
+ "value": {
129
+ "type": [
130
+ "string",
131
+ "null",
132
+ "undefined"
133
+ ]
134
+ }
113
135
  }
114
136
  ],
115
137
  "events": [
@@ -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-alpha16",
4
+ "version": "25.0.0-alpha18",
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\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\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-alpha16/#/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-alpha16/#/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.",
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-alpha18/#/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-alpha18/#/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
  {
@@ -61,6 +61,13 @@
61
61
  "kind": "expression"
62
62
  }
63
63
  },
64
+ {
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
+ },
64
71
  {
65
72
  "name": "@closed",
66
73
  "description": "Fired when the context menu is closed.",