@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.
Files changed (29) hide show
  1. package/package.json +13 -16
  2. package/src/styles/vaadin-context-menu-item-base-styles.js +3 -3
  3. package/src/styles/vaadin-context-menu-overlay-base-styles.js +28 -1
  4. package/src/vaadin-context-menu-item.js +1 -1
  5. package/src/vaadin-context-menu-list-box.js +1 -1
  6. package/src/vaadin-context-menu-mixin.js +48 -76
  7. package/src/vaadin-context-menu-overlay.js +56 -1
  8. package/src/vaadin-context-menu.d.ts +48 -11
  9. package/src/vaadin-context-menu.js +98 -23
  10. package/src/vaadin-contextmenu-items-mixin.js +44 -30
  11. package/src/vaadin-menu-overlay-mixin.d.ts +0 -5
  12. package/src/vaadin-menu-overlay-mixin.js +23 -28
  13. package/vaadin-context-menu.js +1 -1
  14. package/web-types.json +10 -6
  15. package/web-types.lit.json +11 -4
  16. package/src/styles/vaadin-context-menu-item-core-styles.d.ts +0 -8
  17. package/src/styles/vaadin-context-menu-item-core-styles.js +0 -8
  18. package/src/styles/vaadin-context-menu-overlay-core-styles.d.ts +0 -8
  19. package/src/styles/vaadin-context-menu-overlay-core-styles.js +0 -9
  20. package/src/styles/vaadin-menu-overlay-core-styles.d.ts +0 -8
  21. package/src/styles/vaadin-menu-overlay-core-styles.js +0 -32
  22. package/theme/lumo/vaadin-context-menu-item-styles.d.ts +0 -6
  23. package/theme/lumo/vaadin-context-menu-item-styles.js +0 -45
  24. package/theme/lumo/vaadin-context-menu-list-box-styles.d.ts +0 -5
  25. package/theme/lumo/vaadin-context-menu-list-box-styles.js +0 -47
  26. package/theme/lumo/vaadin-context-menu-overlay-styles.d.ts +0 -4
  27. package/theme/lumo/vaadin-context-menu-overlay-styles.js +0 -35
  28. package/theme/lumo/vaadin-context-menu.d.ts +0 -4
  29. package/theme/lumo/vaadin-context-menu.js +0 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/context-menu",
3
- "version": "25.0.0-alpha8",
3
+ "version": "25.0.0-beta1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -23,9 +23,6 @@
23
23
  "lit.d.ts",
24
24
  "lit.js",
25
25
  "src",
26
- "!src/styles/*-base-styles.d.ts",
27
- "!src/styles/*-base-styles.js",
28
- "theme",
29
26
  "vaadin-*.d.ts",
30
27
  "vaadin-*.js",
31
28
  "web-types.json",
@@ -39,25 +36,25 @@
39
36
  ],
40
37
  "dependencies": {
41
38
  "@open-wc/dedupe-mixin": "^1.3.0",
42
- "@vaadin/a11y-base": "25.0.0-alpha8",
43
- "@vaadin/component-base": "25.0.0-alpha8",
44
- "@vaadin/item": "25.0.0-alpha8",
45
- "@vaadin/list-box": "25.0.0-alpha8",
46
- "@vaadin/lit-renderer": "25.0.0-alpha8",
47
- "@vaadin/overlay": "25.0.0-alpha8",
48
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha8",
49
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha8",
39
+ "@vaadin/a11y-base": "25.0.0-beta1",
40
+ "@vaadin/component-base": "25.0.0-beta1",
41
+ "@vaadin/item": "25.0.0-beta1",
42
+ "@vaadin/list-box": "25.0.0-beta1",
43
+ "@vaadin/lit-renderer": "25.0.0-beta1",
44
+ "@vaadin/overlay": "25.0.0-beta1",
45
+ "@vaadin/vaadin-themable-mixin": "25.0.0-beta1",
50
46
  "lit": "^3.0.0"
51
47
  },
52
48
  "devDependencies": {
53
- "@vaadin/chai-plugins": "25.0.0-alpha8",
54
- "@vaadin/test-runner-commands": "25.0.0-alpha8",
49
+ "@vaadin/chai-plugins": "25.0.0-beta1",
50
+ "@vaadin/test-runner-commands": "25.0.0-beta1",
55
51
  "@vaadin/testing-helpers": "^2.0.0",
56
- "sinon": "^18.0.0"
52
+ "@vaadin/vaadin-lumo-styles": "25.0.0-beta1",
53
+ "sinon": "^21.0.0"
57
54
  },
58
55
  "web-types": [
59
56
  "web-types.json",
60
57
  "web-types.lit.json"
61
58
  ],
62
- "gitHead": "ebf53673d5f639d2b1b6f2b31f640f530643ee2f"
59
+ "gitHead": "1d20cf54e582d1f2e209126d4586f8b4c01c50e0"
63
60
  }
@@ -3,17 +3,17 @@
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 '@vaadin/component-base/src/style-props.js';
6
+ import '@vaadin/component-base/src/styles/style-props.js';
7
7
  import { css } from 'lit';
8
8
  import { itemStyles } from '@vaadin/item/src/styles/vaadin-item-base-styles.js';
9
9
 
10
10
  const menuItemStyles = css`
11
11
  :host::after {
12
- background: var(--vaadin-color-subtle);
12
+ background: var(--vaadin-text-color-secondary);
13
13
  content: '';
14
14
  display: block;
15
15
  height: var(--vaadin-icon-size, 1lh);
16
- mask-image: var(--_vaadin-icon-chevron-down);
16
+ mask: var(--_vaadin-icon-chevron-down) 50% / var(--vaadin-icon-visual-size, 100%) no-repeat;
17
17
  rotate: -90deg;
18
18
  visibility: hidden;
19
19
  width: var(--vaadin-icon-size, 1lh);
@@ -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];
@@ -10,7 +10,7 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
10
10
  import { ItemMixin } from '@vaadin/item/src/vaadin-item-mixin.js';
11
11
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
12
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
13
- import { contextMenuItemStyles } from './styles/vaadin-context-menu-item-core-styles.js';
13
+ import { contextMenuItemStyles } from './styles/vaadin-context-menu-item-base-styles.js';
14
14
 
15
15
  /**
16
16
  * An element used internally by `<vaadin-context-menu>`. Not intended to be used separately.
@@ -8,7 +8,7 @@ import { ListMixin } from '@vaadin/a11y-base/src/list-mixin.js';
8
8
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
9
9
  import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
10
10
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
11
- import { listBoxStyles } from '@vaadin/list-box/src/styles/vaadin-list-box-core-styles.js';
11
+ import { listBoxStyles } from '@vaadin/list-box/src/styles/vaadin-list-box-base-styles.js';
12
12
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
13
13
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14
14
 
@@ -31,6 +31,8 @@ export const ContextMenuMixin = (superClass) =>
31
31
  */
32
32
  opened: {
33
33
  type: Boolean,
34
+ reflectToAttribute: true,
35
+ observer: '_openedChanged',
34
36
  value: false,
35
37
  notify: true,
36
38
  readOnly: true,
@@ -123,22 +125,15 @@ export const ContextMenuMixin = (superClass) =>
123
125
 
124
126
  static get observers() {
125
127
  return [
126
- '_openedChanged(opened)',
127
128
  '_targetOrOpenOnChanged(listenOn, openOn)',
128
129
  '_rendererChanged(renderer, items)',
129
130
  '_fullscreenChanged(_fullscreen)',
130
- '_overlayContextChanged(_overlayElement, _context)',
131
- '_overlayModelessChanged(_overlayElement, _modeless)',
132
- '_overlayPhoneChanged(_overlayElement, _phone)',
133
- '_overlayThemeChanged(_overlayElement, _theme)',
134
131
  ];
135
132
  }
136
133
 
137
134
  constructor() {
138
135
  super();
139
136
 
140
- this._createOverlay();
141
-
142
137
  this._boundOpen = this.open.bind(this);
143
138
  this._boundClose = this.close.bind(this);
144
139
  this._boundPreventDefault = this._preventDefault.bind(this);
@@ -170,8 +165,10 @@ export const ContextMenuMixin = (superClass) =>
170
165
  }
171
166
 
172
167
  /** @protected */
173
- ready() {
174
- super.ready();
168
+ firstUpdated() {
169
+ super.firstUpdated();
170
+
171
+ this._overlayElement = this.$.overlay;
175
172
 
176
173
  this.addController(
177
174
  new MediaQueryController(this._fullscreenMediaQuery, (matches) => {
@@ -180,29 +177,17 @@ export const ContextMenuMixin = (superClass) =>
180
177
  );
181
178
  }
182
179
 
183
- /** @private */
184
- _createOverlay() {
185
- // Create an overlay in the constructor to use in observers before `ready()`
186
- const overlay = document.createElement(`${this._tagNamePrefix}-overlay`);
187
- overlay.owner = this;
188
-
189
- overlay.addEventListener('opened-changed', (e) => {
190
- this._onOverlayOpened(e);
191
- });
192
-
193
- overlay.addEventListener('vaadin-overlay-open', (e) => {
194
- this._onVaadinOverlayOpen(e);
195
- });
196
-
197
- this._overlayElement = overlay;
198
- }
199
-
200
180
  /**
201
181
  * Runs before overlay is fully rendered
202
182
  * @private
203
183
  */
204
- _onOverlayOpened(e) {
205
- const opened = e.detail.value;
184
+ _onOverlayOpened(event) {
185
+ // Ignore events from submenus
186
+ if (event.target !== this._overlayElement) {
187
+ return;
188
+ }
189
+
190
+ const opened = event.detail.value;
206
191
  this._setOpened(opened);
207
192
  if (opened) {
208
193
  this.__alignOverlayPosition();
@@ -213,43 +198,23 @@ export const ContextMenuMixin = (superClass) =>
213
198
  * Runs after overlay is fully rendered
214
199
  * @private
215
200
  */
216
- _onVaadinOverlayOpen() {
201
+ _onVaadinOverlayOpen(event) {
202
+ // Ignore events from submenus
203
+ if (event.target !== this._overlayElement) {
204
+ return;
205
+ }
206
+
217
207
  this.__alignOverlayPosition();
218
208
  this._overlayElement.style.visibility = '';
219
209
  this.__forwardFocus();
220
210
  }
221
211
 
222
- /** @private */
223
- _overlayContextChanged(overlay, context) {
224
- if (overlay) {
225
- overlay.model = context;
226
- }
227
- }
228
-
229
- /** @private */
230
- _overlayModelessChanged(overlay, modeless) {
231
- if (overlay) {
232
- overlay.modeless = modeless;
233
- }
234
- }
235
-
236
- /** @private */
237
- _overlayPhoneChanged(overlay, phone) {
238
- if (overlay) {
239
- overlay.toggleAttribute('phone', phone);
240
- overlay.withBackdrop = phone;
241
- }
242
- }
243
-
244
- /** @private */
245
- _overlayThemeChanged(overlay, theme) {
246
- if (overlay) {
247
- if (theme) {
248
- overlay.setAttribute('theme', theme);
249
- } else {
250
- overlay.removeAttribute('theme');
251
- }
252
- }
212
+ /**
213
+ * Runs after overlay's closing animation is finished
214
+ * @private
215
+ */
216
+ _onVaadinOverlayClosed() {
217
+ this.dispatchEvent(new CustomEvent('closed'));
253
218
  }
254
219
 
255
220
  /** @private */
@@ -318,17 +283,14 @@ export const ContextMenuMixin = (superClass) =>
318
283
  }
319
284
 
320
285
  /** @private */
321
- _openedChanged(opened) {
286
+ _openedChanged(opened, oldOpened) {
322
287
  if (opened) {
323
288
  document.documentElement.addEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
324
- } else {
289
+ } else if (oldOpened) {
325
290
  document.documentElement.removeEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
326
291
  }
327
292
 
328
293
  this.__setListenOnUserSelect(opened);
329
-
330
- // Has to be set after instance has been created
331
- this._overlayElement.opened = opened;
332
294
  }
333
295
 
334
296
  /**
@@ -362,11 +324,7 @@ export const ContextMenuMixin = (superClass) =>
362
324
  if (this.closeOn === 'click') {
363
325
  this.closeOn = '';
364
326
  }
365
-
366
- renderer = this.__itemsRenderer;
367
327
  }
368
-
369
- this._overlayElement.renderer = renderer;
370
328
  }
371
329
 
372
330
  /**
@@ -384,6 +342,9 @@ export const ContextMenuMixin = (superClass) =>
384
342
  return Array.prototype.filter.call(targets, (el) => {
385
343
  return e.composedPath().indexOf(el) > -1;
386
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;
387
348
  }
388
349
  return e.target;
389
350
  }
@@ -393,6 +354,11 @@ export const ContextMenuMixin = (superClass) =>
393
354
  * @param {!Event | undefined} e used as the context for the menu. Overlay coordinates are taken from this event.
394
355
  */
395
356
  open(e) {
357
+ // Ignore events from the overlay
358
+ if (this._overlayElement && e.composedPath().includes(this._overlayElement)) {
359
+ return;
360
+ }
361
+
396
362
  if (e && !this.opened) {
397
363
  this._context = {
398
364
  detail: e.detail,
@@ -472,17 +438,13 @@ export const ContextMenuMixin = (superClass) =>
472
438
  /** @private */
473
439
  __focusItem(item) {
474
440
  if (item) {
475
- item.focus();
476
-
477
- if (isKeyboardActive()) {
478
- item.setAttribute('focus-ring', '');
479
- }
441
+ item.focus({ focusVisible: isKeyboardActive() });
480
442
  }
481
443
  }
482
444
 
483
445
  /** @private */
484
446
  __onScroll() {
485
- if (!this.opened) {
447
+ if (!this.opened || this.position) {
486
448
  return;
487
449
  }
488
450
 
@@ -671,7 +633,11 @@ export const ContextMenuMixin = (superClass) =>
671
633
  // Dispatch another contextmenu at the same coordinates after the overlay is closed
672
634
  this._overlayElement.addEventListener(
673
635
  'vaadin-overlay-closed',
674
- () => this.__contextMenuAt(e.clientX, e.clientY),
636
+ (closeEvent) => {
637
+ if (closeEvent.target === this._overlayElement) {
638
+ this.__contextMenuAt(e.clientX, e.clientY);
639
+ }
640
+ },
675
641
  {
676
642
  once: true,
677
643
  },
@@ -682,4 +648,10 @@ export const ContextMenuMixin = (superClass) =>
682
648
  this.close();
683
649
  }
684
650
  }
651
+
652
+ /**
653
+ * Fired when the context menu is closed.
654
+ *
655
+ * @event closed
656
+ */
685
657
  };
@@ -10,7 +10,7 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
10
10
  import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js';
11
11
  import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
12
12
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
13
- import { contextMenuOverlayStyles } from './styles/vaadin-context-menu-overlay-core-styles.js';
13
+ import { contextMenuOverlayStyles } from './styles/vaadin-context-menu-overlay-base-styles.js';
14
14
  import { MenuOverlayMixin } from './vaadin-menu-overlay-mixin.js';
15
15
 
16
16
  /**
@@ -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`
@@ -42,6 +96,7 @@ export class ContextMenuOverlay extends MenuOverlayMixin(
42
96
  <div part="overlay" id="overlay" tabindex="0">
43
97
  <div part="content" id="content">
44
98
  <slot></slot>
99
+ <slot name="submenu"></slot>
45
100
  </div>
46
101
  </div>
47
102
  `;
@@ -4,13 +4,26 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import type { ElementMixinClass } from '@vaadin/component-base/src/element-mixin.js';
7
- import type { OverlayClassMixinClass } from '@vaadin/component-base/src/overlay-class-mixin.js';
8
7
  import type { ThemePropertyMixinClass } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
9
8
  import type { ContextMenuMixinClass } from './vaadin-context-menu-mixin.js';
10
9
  import type { ContextMenuItem } from './vaadin-contextmenu-items-mixin.js';
11
10
 
12
11
  export { ContextMenuItem };
13
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
+
14
27
  export interface ContextMenuRendererContext {
15
28
  target: HTMLElement;
16
29
  detail?: { sourceEvent: Event };
@@ -34,6 +47,11 @@ export type ContextMenuItemSelectedEvent<TItem extends ContextMenuItem = Context
34
47
  value: TItem;
35
48
  }>;
36
49
 
50
+ /**
51
+ * Fired when the context menu is closed.
52
+ */
53
+ export type ContextMenuClosedEvent = CustomEvent;
54
+
37
55
  export interface ContextMenuCustomEventMap<TItem extends ContextMenuItem = ContextMenuItem> {
38
56
  'opened-changed': ContextMenuOpenedChangedEvent;
39
57
 
@@ -42,6 +60,8 @@ export interface ContextMenuCustomEventMap<TItem extends ContextMenuItem = Conte
42
60
  'close-all-menus': Event;
43
61
 
44
62
  'items-outside-click': Event;
63
+
64
+ closed: ContextMenuClosedEvent;
45
65
  }
46
66
 
47
67
  export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenuItem>
@@ -208,18 +228,30 @@ export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenu
208
228
  *
209
229
  * ### Styling
210
230
  *
211
- * `<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal
212
- * themable component as the actual visible context menu overlay.
231
+ * The following shadow DOM parts are available for styling:
213
232
  *
214
- * See [`<vaadin-overlay>`](#/elements/vaadin-overlay)
215
- * documentation for `<vaadin-context-menu-overlay>` stylable parts.
233
+ * Part name | Description
234
+ * -----------------|-------------------------------------------
235
+ * `backdrop` | Backdrop of the overlay
236
+ * `overlay` | The overlay container
237
+ * `content` | The overlay content
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
216
249
  *
217
250
  * See [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.
218
251
  *
219
252
  * ### Internal components
220
253
  *
221
- * When using `items` API, in addition `<vaadin-context-menu-overlay>`, the following
222
- * internal components are themable:
254
+ * When using `items` API the following internal components are themable:
223
255
  *
224
256
  * - `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](#/elements/vaadin-item).
225
257
  * - `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
@@ -231,13 +263,19 @@ export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenu
231
263
  * ---------- |-------------
232
264
  * `expanded` | Expanded parent item.
233
265
  *
234
- * Note: the `theme` attribute value set on `<vaadin-context-menu>` is
235
- * propagated to the internal components listed above.
236
- *
237
266
  * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
238
267
  * @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
268
+ * @fires {CustomEvent} closed - Fired when the context menu is closed.
239
269
  */
240
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
+
241
279
  addEventListener<K extends keyof ContextMenuEventMap>(
242
280
  type: K,
243
281
  listener: (this: ContextMenu<TItem>, ev: ContextMenuEventMap<TItem>[K]) => void,
@@ -253,7 +291,6 @@ declare class ContextMenu<TItem extends ContextMenuItem = ContextMenuItem> exten
253
291
 
254
292
  interface ContextMenu<TItem extends ContextMenuItem = ContextMenuItem>
255
293
  extends ContextMenuMixinClass<TItem>,
256
- OverlayClassMixinClass,
257
294
  ElementMixinClass,
258
295
  ThemePropertyMixinClass {}
259
296