@vaadin/context-menu 25.2.0-alpha1 → 25.2.0-alpha11

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.
@@ -29,7 +29,11 @@
29
29
  {
30
30
  "kind": "method",
31
31
  "name": "close",
32
- "description": "Closes the overlay."
32
+ "description": "Closes the overlay.",
33
+ "inheritedFrom": {
34
+ "name": "ItemsMixin",
35
+ "module": "src/vaadin-contextmenu-items-mixin.js"
36
+ }
33
37
  },
34
38
  {
35
39
  "kind": "field",
@@ -48,7 +52,7 @@
48
52
  "type": {
49
53
  "text": "!Array<!ContextMenuItem> | undefined"
50
54
  },
51
- "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```",
55
+ "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```",
52
56
  "attribute": "items",
53
57
  "inheritedFrom": {
54
58
  "name": "ItemsMixin",
@@ -230,13 +234,92 @@
230
234
  }
231
235
  ]
232
236
  },
237
+ {
238
+ "kind": "javascript-module",
239
+ "path": "src/vaadin-context-menu-tooltip-controller.js",
240
+ "declarations": [
241
+ {
242
+ "kind": "class",
243
+ "description": "Controller for the tooltip slotted into `<vaadin-context-menu>`. Configures\nthe tooltip in manual mode and drives its target, context, and position\nbased on the currently hovered or focused item.",
244
+ "name": "ContextMenuTooltipController",
245
+ "members": [
246
+ {
247
+ "kind": "method",
248
+ "name": "bringToFront"
249
+ },
250
+ {
251
+ "kind": "method",
252
+ "name": "close",
253
+ "parameters": [
254
+ {
255
+ "name": "immediate",
256
+ "type": {
257
+ "text": "boolean"
258
+ }
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ "kind": "method",
264
+ "name": "initCustomNode",
265
+ "parameters": [
266
+ {
267
+ "name": "tooltipNode"
268
+ }
269
+ ]
270
+ },
271
+ {
272
+ "kind": "method",
273
+ "name": "open",
274
+ "parameters": [
275
+ {
276
+ "name": "{ trigger }"
277
+ },
278
+ {
279
+ "name": "options",
280
+ "type": {
281
+ "text": "{ trigger: 'hover' | 'focus' }"
282
+ }
283
+ }
284
+ ]
285
+ },
286
+ {
287
+ "kind": "method",
288
+ "name": "setTarget",
289
+ "parameters": [
290
+ {
291
+ "name": "target",
292
+ "type": {
293
+ "text": "HTMLElement | null"
294
+ }
295
+ }
296
+ ]
297
+ }
298
+ ],
299
+ "superclass": {
300
+ "name": "SlotController",
301
+ "package": "@vaadin/component-base/src/slot-controller.js"
302
+ }
303
+ }
304
+ ],
305
+ "exports": [
306
+ {
307
+ "kind": "js",
308
+ "name": "ContextMenuTooltipController",
309
+ "declaration": {
310
+ "name": "ContextMenuTooltipController",
311
+ "module": "src/vaadin-context-menu-tooltip-controller.js"
312
+ }
313
+ }
314
+ ]
315
+ },
233
316
  {
234
317
  "kind": "javascript-module",
235
318
  "path": "src/vaadin-context-menu.js",
236
319
  "declarations": [
237
320
  {
238
321
  "kind": "class",
239
- "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>`](#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/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.",
322
+ "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#### Disabled menu items\n\nWhen disabled, menu items are rendered as \"dimmed\".\n\nBy default, disabled items are not focusable and don't react to hover.\nAs a result, they are hidden from assistive technologies, and it's not\npossible to show a tooltip to explain why they are disabled. This can\nbe addressed by enabling the feature flag `accessibleDisabledMenuItems`,\nwhich makes disabled items focusable and hoverable, while still\npreventing them from being activated:\n\n```js\n// Set before any context menu is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledMenuItems = true;\n```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```\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>`](#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](#/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.",
240
323
  "name": "ContextMenu",
241
324
  "members": [
242
325
  {
@@ -244,8 +327,8 @@
244
327
  "name": "close",
245
328
  "description": "Closes the overlay.",
246
329
  "inheritedFrom": {
247
- "name": "ContextMenuMixin",
248
- "module": "src/vaadin-context-menu-mixin.js"
330
+ "name": "ItemsMixin",
331
+ "module": "src/vaadin-contextmenu-items-mixin.js"
249
332
  }
250
333
  },
251
334
  {
@@ -269,7 +352,7 @@
269
352
  "type": {
270
353
  "text": "!Array<!ContextMenuItem> | undefined"
271
354
  },
272
- "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```",
355
+ "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```",
273
356
  "attribute": "items",
274
357
  "inheritedFrom": {
275
358
  "name": "ItemsMixin",
@@ -564,6 +647,10 @@
564
647
  "description": "",
565
648
  "name": "ItemsMixin",
566
649
  "members": [
650
+ {
651
+ "kind": "method",
652
+ "name": "close"
653
+ },
567
654
  {
568
655
  "kind": "field",
569
656
  "name": "items",
@@ -571,7 +658,7 @@
571
658
  "type": {
572
659
  "text": "!Array<!ContextMenuItem> | undefined"
573
660
  },
574
- "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```",
661
+ "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```",
575
662
  "attribute": "items"
576
663
  }
577
664
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/context-menu",
3
- "version": "25.2.0-alpha1",
3
+ "version": "25.2.0-alpha11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -37,21 +37,21 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@open-wc/dedupe-mixin": "^1.3.0",
40
- "@vaadin/a11y-base": "25.2.0-alpha1",
41
- "@vaadin/component-base": "25.2.0-alpha1",
42
- "@vaadin/item": "25.2.0-alpha1",
43
- "@vaadin/list-box": "25.2.0-alpha1",
44
- "@vaadin/lit-renderer": "25.2.0-alpha1",
45
- "@vaadin/overlay": "25.2.0-alpha1",
46
- "@vaadin/vaadin-themable-mixin": "25.2.0-alpha1",
40
+ "@vaadin/a11y-base": "25.2.0-alpha11",
41
+ "@vaadin/component-base": "25.2.0-alpha11",
42
+ "@vaadin/item": "25.2.0-alpha11",
43
+ "@vaadin/list-box": "25.2.0-alpha11",
44
+ "@vaadin/lit-renderer": "25.2.0-alpha11",
45
+ "@vaadin/overlay": "25.2.0-alpha11",
46
+ "@vaadin/vaadin-themable-mixin": "25.2.0-alpha11",
47
47
  "lit": "^3.0.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@vaadin/aura": "25.2.0-alpha1",
51
- "@vaadin/chai-plugins": "25.2.0-alpha1",
52
- "@vaadin/test-runner-commands": "25.2.0-alpha1",
50
+ "@vaadin/aura": "25.2.0-alpha11",
51
+ "@vaadin/chai-plugins": "25.2.0-alpha11",
52
+ "@vaadin/test-runner-commands": "25.2.0-alpha11",
53
53
  "@vaadin/testing-helpers": "^2.0.0",
54
- "@vaadin/vaadin-lumo-styles": "25.2.0-alpha1",
54
+ "@vaadin/vaadin-lumo-styles": "25.2.0-alpha11",
55
55
  "sinon": "^21.0.2"
56
56
  },
57
57
  "customElements": "custom-elements.json",
@@ -59,5 +59,5 @@
59
59
  "web-types.json",
60
60
  "web-types.lit.json"
61
61
  ],
62
- "gitHead": "866f813f89655a351cbd25328eba1fcb317e267d"
62
+ "gitHead": "fdc37e932709f95491a027aeb2090911cb7528c6"
63
63
  }
@@ -17,9 +17,6 @@ import { contextMenuItemStyles } from './styles/vaadin-context-menu-item-base-st
17
17
  *
18
18
  * @customElement vaadin-context-menu-item
19
19
  * @extends HTMLElement
20
- * @mixes DirMixin
21
- * @mixes ItemMixin
22
- * @mixes ThemableMixin
23
20
  * @protected
24
21
  */
25
22
  class ContextMenuItem extends ItemMixin(ThemableMixin(DirMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
@@ -47,6 +44,11 @@ class ContextMenuItem extends ItemMixin(ThemableMixin(DirMixin(PolylitMixin(Lumo
47
44
 
48
45
  this.setAttribute('role', 'menuitem');
49
46
  }
47
+
48
+ /** @override */
49
+ __shouldAllowFocusWhenDisabled() {
50
+ return window.Vaadin.featureFlags.accessibleDisabledMenuItems;
51
+ }
50
52
  }
51
53
 
52
54
  defineCustomElement(ContextMenuItem);
@@ -17,9 +17,6 @@ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mix
17
17
  *
18
18
  * @customElement vaadin-context-menu-list-box
19
19
  * @extends HTMLElement
20
- * @mixes DirMixin
21
- * @mixes ListMixin
22
- * @mixes ThemableMixin
23
20
  * @protected
24
21
  */
25
22
  class ContextMenuListBox extends ListMixin(ThemableMixin(DirMixin(PolylitMixin(LumoInjectionMixin(LitElement))))) {
@@ -7,12 +7,9 @@ import { isElementFocusable, isKeyboardActive } from '@vaadin/a11y-base/src/focu
7
7
  import { isAndroid, isIOS } from '@vaadin/component-base/src/browser-utils.js';
8
8
  import { addListener, deepTargetFind, gestures, removeListener } from '@vaadin/component-base/src/gestures.js';
9
9
  import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
10
+ import { ContextMenuTooltipController } from './vaadin-context-menu-tooltip-controller.js';
10
11
  import { ItemsMixin } from './vaadin-contextmenu-items-mixin.js';
11
12
 
12
- /**
13
- * @polymerMixin
14
- * @mixes ItemsMixin
15
- */
16
13
  export const ContextMenuMixin = (superClass) =>
17
14
  class ContextMenuMixinClass extends ItemsMixin(superClass) {
18
15
  static get properties() {
@@ -35,6 +32,7 @@ export const ContextMenuMixin = (superClass) =>
35
32
  value: false,
36
33
  notify: true,
37
34
  readOnly: true,
35
+ sync: true,
38
36
  },
39
37
 
40
38
  /**
@@ -134,7 +132,7 @@ export const ContextMenuMixin = (superClass) =>
134
132
  this._boundOpen = this.open.bind(this);
135
133
  this._boundClose = this.close.bind(this);
136
134
  this._boundPreventDefault = this._preventDefault.bind(this);
137
- this._boundOnGlobalContextMenu = this._onGlobalContextMenu.bind(this);
135
+ this.__onGlobalContextMenu = this.__onGlobalContextMenu.bind(this);
138
136
  }
139
137
 
140
138
  /** @protected */
@@ -143,6 +141,7 @@ export const ContextMenuMixin = (superClass) =>
143
141
 
144
142
  this.__boundOnScroll = this.__onScroll.bind(this);
145
143
  window.addEventListener('scroll', this.__boundOnScroll, true);
144
+ document.documentElement.addEventListener('contextmenu', this.__onGlobalContextMenu, true);
146
145
 
147
146
  // Restore opened state if overlay was opened when disconnecting
148
147
  if (this.__restoreOpened) {
@@ -155,10 +154,16 @@ export const ContextMenuMixin = (superClass) =>
155
154
  super.disconnectedCallback();
156
155
 
157
156
  window.removeEventListener('scroll', this.__boundOnScroll, true);
157
+ document.documentElement.removeEventListener('contextmenu', this.__onGlobalContextMenu, true);
158
158
 
159
- // Close overlay and memorize opened state
159
+ // Memorize opened state and defer close so that DOM moves (disconnect
160
+ // followed by reconnect within the same task) do not close the overlay.
160
161
  this.__restoreOpened = this.opened;
161
- this.close();
162
+ setTimeout(() => {
163
+ if (!this.isConnected) {
164
+ this.close();
165
+ }
166
+ });
162
167
  }
163
168
 
164
169
  /** @protected */
@@ -172,6 +177,15 @@ export const ContextMenuMixin = (superClass) =>
172
177
  this._fullscreen = matches;
173
178
  }),
174
179
  );
180
+
181
+ // Sub-menus inherit the tooltip controller from their parent menu
182
+ // (assigned before `firstUpdated` runs) to reuse the same slotted
183
+ // `<vaadin-tooltip>` across nesting levels. Only create a new one
184
+ // when none was inherited, i.e. on the outer host.
185
+ if (!this._tooltipController) {
186
+ this._tooltipController = new ContextMenuTooltipController(this);
187
+ this.addController(this._tooltipController);
188
+ }
175
189
  }
176
190
 
177
191
  /**
@@ -280,13 +294,7 @@ export const ContextMenuMixin = (superClass) =>
280
294
  }
281
295
 
282
296
  /** @private */
283
- _openedChanged(opened, oldOpened) {
284
- if (opened) {
285
- document.documentElement.addEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
286
- } else if (oldOpened) {
287
- document.documentElement.removeEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
288
- }
289
-
297
+ _openedChanged(opened) {
290
298
  this.__setListenOnUserSelect(opened);
291
299
  }
292
300
 
@@ -328,6 +336,8 @@ export const ContextMenuMixin = (superClass) =>
328
336
  * Closes the overlay.
329
337
  */
330
338
  close() {
339
+ super.close();
340
+
331
341
  this._setOpened(false);
332
342
  }
333
343
 
@@ -620,7 +630,10 @@ export const ContextMenuMixin = (superClass) =>
620
630
  }
621
631
 
622
632
  /** @private */
623
- _onGlobalContextMenu(e) {
633
+ __onGlobalContextMenu(e) {
634
+ if (!this.opened) {
635
+ return;
636
+ }
624
637
  if (!e.shiftKey) {
625
638
  const isTouchDevice = isAndroid || isIOS;
626
639
  if (!isTouchDevice) {
@@ -645,10 +658,4 @@ export const ContextMenuMixin = (superClass) =>
645
658
  this.close();
646
659
  }
647
660
  }
648
-
649
- /**
650
- * Fired when the context menu is closed.
651
- *
652
- * @event closed
653
- */
654
661
  };
@@ -18,10 +18,6 @@ import { MenuOverlayMixin } from './vaadin-menu-overlay-mixin.js';
18
18
  *
19
19
  * @customElement vaadin-context-menu-overlay
20
20
  * @extends HTMLElement
21
- * @mixes DirMixin
22
- * @mixes MenuOverlayMixin
23
- * @mixes OverlayMixin
24
- * @mixes ThemableMixin
25
21
  * @protected
26
22
  */
27
23
  export class ContextMenuOverlay extends MenuOverlayMixin(
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2016 - 2026 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
7
+
8
+ /**
9
+ * Controller for the tooltip slotted into `<vaadin-context-menu>`. Configures
10
+ * the tooltip in manual mode and drives its target, context, and position
11
+ * based on the currently hovered or focused item.
12
+ */
13
+ export class ContextMenuTooltipController extends SlotController {
14
+ constructor(host) {
15
+ super(host, 'tooltip');
16
+ }
17
+
18
+ /** @override */
19
+ initCustomNode(tooltipNode) {
20
+ tooltipNode.manual = true;
21
+ tooltipNode.generator ||= ({ item }) => item?.tooltip;
22
+ }
23
+
24
+ /** @protected */
25
+ _getItem(target) {
26
+ return target._item;
27
+ }
28
+
29
+ /** @protected */
30
+ _getDefaultPosition(target) {
31
+ const item = this._getItem(target);
32
+ return item.children?.length > 0 && !item.disabled ? 'start' : 'end';
33
+ }
34
+
35
+ /**
36
+ * @param {HTMLElement | null} target
37
+ */
38
+ setTarget(target) {
39
+ const tooltipNode = this.node;
40
+ if (!tooltipNode) {
41
+ return;
42
+ }
43
+
44
+ const item = target ? this._getItem(target) : null;
45
+ if (item?.tooltip) {
46
+ tooltipNode.target = target;
47
+ tooltipNode.context = { item };
48
+ tooltipNode._position = item.tooltipPosition || this._getDefaultPosition(target);
49
+ } else {
50
+ tooltipNode.target = null;
51
+ tooltipNode.context = { item: null };
52
+ this.close(true);
53
+ }
54
+ }
55
+
56
+ /**
57
+ * @param {{ trigger: 'hover' | 'focus' }} options
58
+ */
59
+ open({ trigger }) {
60
+ const tooltipNode = this.node;
61
+ if (tooltipNode?.isConnected && tooltipNode.target) {
62
+ tooltipNode._stateController.open({
63
+ hover: trigger === 'hover',
64
+ focus: trigger === 'focus',
65
+ });
66
+ }
67
+ }
68
+
69
+ bringToFront() {
70
+ const tooltipNode = this.node;
71
+ tooltipNode?._overlayElement?.bringToFront();
72
+ }
73
+
74
+ /**
75
+ * @param {boolean} immediate
76
+ */
77
+ close(immediate) {
78
+ const tooltipNode = this.node;
79
+ tooltipNode?._stateController.close(immediate);
80
+ }
81
+ }
@@ -106,6 +106,34 @@ export interface ContextMenuEventMap<TItem extends ContextMenuItem = ContextMenu
106
106
  *
107
107
  * **NOTE:** when the `items` array is defined, the renderer cannot be used.
108
108
  *
109
+ * #### Disabled menu items
110
+ *
111
+ * When disabled, menu items are rendered as "dimmed".
112
+ *
113
+ * By default, disabled items are not focusable and don't react to hover.
114
+ * As a result, they are hidden from assistive technologies, and it's not
115
+ * possible to show a tooltip to explain why they are disabled. This can
116
+ * be addressed by enabling the feature flag `accessibleDisabledMenuItems`,
117
+ * which makes disabled items focusable and hoverable, while still
118
+ * preventing them from being activated:
119
+ *
120
+ * ```js
121
+ * // Set before any context menu is attached to the DOM.
122
+ * window.Vaadin.featureFlags.accessibleDisabledMenuItems = true;
123
+ * ```
124
+ *
125
+ * #### Item tooltips
126
+ *
127
+ * Menu items can have tooltips that are shown on hover and keyboard
128
+ * focus. To enable them, add a slotted `<vaadin-tooltip>` element
129
+ * and set the `tooltip` property on each item that should have one:
130
+ *
131
+ * ```html
132
+ * <vaadin-context-menu>
133
+ * <vaadin-tooltip slot="tooltip"></vaadin-tooltip>
134
+ * </vaadin-context-menu>
135
+ * ```
136
+ *
109
137
  * ### Rendering
110
138
  *
111
139
  * The content of the menu can be populated by using the renderer callback function.
@@ -54,6 +54,34 @@ import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
54
54
  *
55
55
  * **NOTE:** when the `items` array is defined, the renderer cannot be used.
56
56
  *
57
+ * #### Disabled menu items
58
+ *
59
+ * When disabled, menu items are rendered as "dimmed".
60
+ *
61
+ * By default, disabled items are not focusable and don't react to hover.
62
+ * As a result, they are hidden from assistive technologies, and it's not
63
+ * possible to show a tooltip to explain why they are disabled. This can
64
+ * be addressed by enabling the feature flag `accessibleDisabledMenuItems`,
65
+ * which makes disabled items focusable and hoverable, while still
66
+ * preventing them from being activated:
67
+ *
68
+ * ```js
69
+ * // Set before any context menu is attached to the DOM.
70
+ * window.Vaadin.featureFlags.accessibleDisabledMenuItems = true;
71
+ * ```
72
+ *
73
+ * #### Item tooltips
74
+ *
75
+ * Menu items can have tooltips that are shown on hover and keyboard
76
+ * focus. To enable them, add a slotted `<vaadin-tooltip>` element
77
+ * and set the `tooltip` property on each item that should have one:
78
+ *
79
+ * ```html
80
+ * <vaadin-context-menu>
81
+ * <vaadin-tooltip slot="tooltip"></vaadin-tooltip>
82
+ * </vaadin-context-menu>
83
+ * ```
84
+ *
57
85
  * ### Rendering
58
86
  *
59
87
  * The content of the menu can be populated by using the renderer callback function.
@@ -218,9 +246,6 @@ import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
218
246
  *
219
247
  * @customElement vaadin-context-menu
220
248
  * @extends HTMLElement
221
- * @mixes ElementMixin
222
- * @mixes ContextMenuMixin
223
- * @mixes ThemePropertyMixin
224
249
  */
225
250
  class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(PolylitMixin(LitElement)))) {
226
251
  static get is() {
@@ -267,7 +292,7 @@ class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(Polyl
267
292
  .modeless="${this._modeless}"
268
293
  .renderer="${this.items ? this.__itemsRenderer : this.renderer}"
269
294
  .position="${position}"
270
- .positionTarget="${position ? context && context.target : this._positionTarget}"
295
+ .positionTarget="${position ? context?.target : this._positionTarget}"
271
296
  .horizontalAlign="${this.__computeHorizontalAlign(position)}"
272
297
  .verticalAlign="${this.__computeVerticalAlign(position)}"
273
298
  ?no-horizontal-overlap="${this.__computeNoHorizontalOverlap(position)}"
@@ -283,6 +308,8 @@ class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(Polyl
283
308
  <slot name="overlay"></slot>
284
309
  <slot name="submenu" slot="submenu"></slot>
285
310
  </vaadin-context-menu-overlay>
311
+
312
+ <slot name="tooltip"></slot>
286
313
  `;
287
314
  }
288
315
 
@@ -321,14 +348,6 @@ class ContextMenu extends ContextMenuMixin(ElementMixin(ThemePropertyMixin(Polyl
321
348
 
322
349
  return ['top-start', 'top-end', 'top', 'start-bottom', 'end-bottom'].includes(position) ? 'bottom' : 'top';
323
350
  }
324
-
325
- /**
326
- * Fired when an item is selected when the context menu is populated using the `items` API.
327
- *
328
- * @event item-selected
329
- * @param {Object} detail
330
- * @param {Object} detail.value the selected menu item
331
- */
332
351
  }
333
352
 
334
353
  defineCustomElement(ContextMenu);
@@ -120,7 +120,7 @@ register({
120
120
  ev.detail = { x, y, sourceEvent };
121
121
  target.dispatchEvent(ev);
122
122
  // Forward `preventDefault` in a clean way
123
- if (ev.defaultPrevented && sourceEvent && sourceEvent.preventDefault) {
123
+ if (ev.defaultPrevented && sourceEvent?.preventDefault) {
124
124
  sourceEvent.preventDefault();
125
125
  }
126
126
  },
@@ -9,6 +9,19 @@ import type { Constructor } from '@open-wc/dedupe-mixin';
9
9
 
10
10
  export type ContextMenuItem<TItemData extends object = object> = {
11
11
  text?: string;
12
+ /**
13
+ * Text to be set as the menu item's tooltip.
14
+ * Requires a `<vaadin-tooltip slot="tooltip">` element to be added inside the `<vaadin-context-menu>`.
15
+ */
16
+ tooltip?: string;
17
+ /**
18
+ * Position of the item's tooltip relative to the item
19
+ * (e.g. `end`, `top`, `bottom-start`). Items with a sub-menu default to `start` to
20
+ * avoid overlap with the opening sub-menu; all other items, including disabled ones
21
+ * (whose sub-menus cannot be opened), default to `end`. If the slotted
22
+ * `<vaadin-tooltip>` has its `position` property set, that value is used instead.
23
+ */
24
+ tooltipPosition?: string;
12
25
  component?: HTMLElement | string;
13
26
  disabled?: boolean;
14
27
  checked?: boolean;
@@ -48,6 +61,18 @@ export declare class ItemsMixinClass<TItem extends ContextMenuItem = ContextMenu
48
61
  * { text: 'Menu Item 3', disabled: true, className: 'last' }
49
62
  * ];
50
63
  * ```
64
+ *
65
+ * #### Item tooltips
66
+ *
67
+ * Menu items can have tooltips that are shown on hover and keyboard
68
+ * focus. To enable them, add a slotted `<vaadin-tooltip>` element
69
+ * and set the `tooltip` property on each item that should have one:
70
+ *
71
+ * ```html
72
+ * <vaadin-context-menu>
73
+ * <vaadin-tooltip slot="tooltip"></vaadin-tooltip>
74
+ * </vaadin-context-menu>
75
+ * ```
51
76
  */
52
77
  items: TItem[] | undefined;
53
78
 
@@ -3,11 +3,9 @@
3
3
  * Copyright (c) 2016 - 2026 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
6
7
  import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
7
8
 
8
- /**
9
- * @polymerMixin
10
- */
11
9
  export const ItemsMixin = (superClass) =>
12
10
  class ItemsMixin extends superClass {
13
11
  static get properties() {
@@ -16,6 +14,13 @@ export const ItemsMixin = (superClass) =>
16
14
  * @typedef ContextMenuItem
17
15
  * @type {object}
18
16
  * @property {string} text - Text to be set as the menu item component's textContent
17
+ * @property {string} tooltip - Text to be set as the menu item's tooltip.
18
+ * Requires a `<vaadin-tooltip slot="tooltip">` element to be added inside the `<vaadin-context-menu>`.
19
+ * @property {string} tooltipPosition - Position of the item's tooltip relative to the
20
+ * item (e.g. `end`, `top`, `bottom-start`). Items with a sub-menu default to `start`
21
+ * to avoid overlap with the opening sub-menu; all other items, including disabled
22
+ * ones (whose sub-menus cannot be opened), default to `end`. If the slotted
23
+ * `<vaadin-tooltip>` has its `position` property set, that value is used instead.
19
24
  * @property {string | HTMLElement} component - The component to represent the item.
20
25
  * Either a tagName or an element instance. Defaults to "vaadin-context-menu-item".
21
26
  * @property {boolean} disabled - If true, the item is disabled and cannot be selected
@@ -54,6 +59,18 @@ export const ItemsMixin = (superClass) =>
54
59
  * ];
55
60
  * ```
56
61
  *
62
+ * #### Item tooltips
63
+ *
64
+ * Menu items can have tooltips that are shown on hover and keyboard
65
+ * focus. To enable them, add a slotted `<vaadin-tooltip>` element
66
+ * and set the `tooltip` property on each item that should have one:
67
+ *
68
+ * ```html
69
+ * <vaadin-context-menu>
70
+ * <vaadin-tooltip slot="tooltip"></vaadin-tooltip>
71
+ * </vaadin-context-menu>
72
+ * ```
73
+ *
57
74
  * @type {!Array<!ContextMenuItem> | undefined}
58
75
  */
59
76
  items: {
@@ -105,6 +122,7 @@ export const ItemsMixin = (superClass) =>
105
122
  disconnectedCallback() {
106
123
  super.disconnectedCallback();
107
124
  document.documentElement.removeEventListener('click', this.__itemsOutsideClickListener);
125
+ this._tooltipController.setTarget(null);
108
126
  }
109
127
 
110
128
  /**
@@ -126,7 +144,7 @@ export const ItemsMixin = (superClass) =>
126
144
  // If parent item is not focused, do not focus submenu
127
145
  if (overlay.parentOverlay) {
128
146
  const parent = overlay.parentOverlay._contentRoot.querySelector('[expanded]');
129
- if (parent && parent.hasAttribute('focused') && child) {
147
+ if (parent?.hasAttribute('focused') && child) {
130
148
  child.focus();
131
149
  } else {
132
150
  overlay.$.overlay.focus();
@@ -231,6 +249,26 @@ export const ItemsMixin = (superClass) =>
231
249
  listBox.setAttribute('theme', this._theme);
232
250
  }
233
251
 
252
+ listBox.addEventListener('mouseover', (event) => {
253
+ const itemElement = event.target.closest(`${this._tagNamePrefix}-item`);
254
+ this._tooltipController.setTarget(itemElement);
255
+ this._tooltipController.open({ trigger: 'hover' });
256
+ });
257
+
258
+ listBox.addEventListener('mouseleave', () => {
259
+ this._tooltipController.close(true);
260
+ });
261
+
262
+ listBox.addEventListener('focusin', (event) => {
263
+ if (!isKeyboardActive()) {
264
+ return;
265
+ }
266
+
267
+ const itemElement = event.target.closest(`${this._tagNamePrefix}-item`);
268
+ this._tooltipController.setTarget(itemElement);
269
+ this._tooltipController.open({ trigger: 'focus' });
270
+ });
271
+
234
272
  listBox.addEventListener('selected-changed', (event) => {
235
273
  const { value } = event.detail;
236
274
  if (typeof value === 'number') {
@@ -300,6 +338,12 @@ export const ItemsMixin = (superClass) =>
300
338
  __initSubMenu() {
301
339
  const subMenu = document.createElement(this.constructor.is);
302
340
 
341
+ // The slotted `<vaadin-tooltip>` lives on the outer `<vaadin-context-menu>`
342
+ // host. Its tooltip controller instance is shared across sub-menus to
343
+ // reuse the same tooltip element for items at every nesting level.
344
+ subMenu._tooltipController = this._tooltipController;
345
+ subMenu.__parentMenu = this;
346
+
303
347
  subMenu._modeless = true;
304
348
  subMenu.openOn = 'opensubmenu';
305
349
 
@@ -323,9 +367,7 @@ export const ItemsMixin = (superClass) =>
323
367
 
324
368
  // Listen to the forwarded event from sub-menu.
325
369
  this.addEventListener('close-all-menus', () => {
326
- // Call `close()` on the overlay to close synchronously,
327
- // as we can't have `sync: true` on `opened` property.
328
- this._overlayElement.close();
370
+ this.close();
329
371
  });
330
372
 
331
373
  // Listen to the forwarded event from sub-menu.
@@ -344,7 +386,10 @@ export const ItemsMixin = (superClass) =>
344
386
 
345
387
  // Mark parent item as collapsed when closing.
346
388
  subMenu.addEventListener('opened-changed', (event) => {
347
- if (!event.detail.value) {
389
+ const opened = event.detail.value;
390
+ if (opened) {
391
+ this._tooltipController.bringToFront();
392
+ } else {
348
393
  const expandedItem = this._listBox.querySelector('[expanded]');
349
394
  if (expandedItem) {
350
395
  this.__updateExpanded(expandedItem, false);
@@ -374,12 +419,12 @@ export const ItemsMixin = (superClass) =>
374
419
  const subMenu = this._subMenu;
375
420
  const expandedItem = this._listBox.querySelector('[expanded]');
376
421
 
377
- if (item && item !== expandedItem) {
422
+ if (item && item !== expandedItem && !item.disabled) {
378
423
  const { children } = item._item;
379
424
 
380
425
  // Check if the sub-menu was focused before closing it.
381
426
  const child = subMenu._overlayElement._contentRoot.firstElementChild;
382
- const isSubmenuFocused = child && child.focused;
427
+ const isSubmenuFocused = child?.focused;
383
428
 
384
429
  // Mark previously expanded item as collapsed
385
430
  if (expandedItem) {
@@ -395,7 +440,7 @@ export const ItemsMixin = (superClass) =>
395
440
  return;
396
441
  }
397
442
 
398
- if (children && children.length) {
443
+ if (children?.length) {
399
444
  // Open or update the submenu if the new item has children
400
445
  this.__updateExpanded(item, true);
401
446
  this.__openSubMenu(subMenu, item);
@@ -407,6 +452,12 @@ export const ItemsMixin = (superClass) =>
407
452
  this._overlayElement.$.overlay.focus();
408
453
  }
409
454
  }
455
+
456
+ // Only reachable with `accessibleDisabledMenuItems` enabled (disabled
457
+ // items otherwise have `pointer-events: none` and never receive mouseover).
458
+ if (item?.disabled) {
459
+ subMenu.close();
460
+ }
410
461
  }
411
462
 
412
463
  /** @protected */
@@ -494,4 +545,10 @@ export const ItemsMixin = (superClass) =>
494
545
  component.removeAttribute('theme');
495
546
  }
496
547
  }
548
+
549
+ close() {
550
+ if (!this.__parentMenu) {
551
+ this._tooltipController.close(true);
552
+ }
553
+ }
497
554
  };
@@ -6,9 +6,6 @@
6
6
  import { OverlayFocusMixin } from '@vaadin/overlay/src/vaadin-overlay-focus-mixin.js';
7
7
  import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js';
8
8
 
9
- /**
10
- * @polymerMixin
11
- */
12
9
  export const MenuOverlayMixin = (superClass) =>
13
10
  class MenuOverlayMixin extends OverlayFocusMixin(PositionMixin(superClass)) {
14
11
  static get properties() {
@@ -103,7 +100,7 @@ export const MenuOverlayMixin = (superClass) =>
103
100
 
104
101
  // Adjust constraints to ensure bottom-aligned applies to sub-menu.
105
102
  const parent = this.parentOverlay;
106
- if (parent && parent.hasAttribute('bottom-aligned')) {
103
+ if (parent?.hasAttribute('bottom-aligned')) {
107
104
  const parentStyle = getComputedStyle(parent);
108
105
  yMax = yMax - parseFloat(parentStyle.bottom) - parseFloat(parentStyle.height);
109
106
  }
package/web-types.json CHANGED
@@ -1,23 +1,21 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/context-menu",
4
- "version": "25.2.0-alpha1",
4
+ "version": "25.2.0-alpha11",
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\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.2.0-alpha1/#/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.2.0-alpha1/#/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#### Disabled menu items\n\nWhen disabled, menu items are rendered as \"dimmed\".\n\nBy default, disabled items are not focusable and don't react to hover.\nAs a result, they are hidden from assistive technologies, and it's not\npossible to show a tooltip to explain why they are disabled. This can\nbe addressed by enabling the feature flag `accessibleDisabledMenuItems`,\nwhich makes disabled items focusable and hoverable, while still\npreventing them from being activated:\n\n```js\n// Set before any context menu is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledMenuItems = true;\n```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```\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.2.0-alpha11/#/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.2.0-alpha11/#/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": "close-on",
15
15
  "description": "Event name to listen for closing the context menu.",
16
16
  "value": {
17
17
  "type": [
18
- "string",
19
- "null",
20
- "undefined"
18
+ "string"
21
19
  ]
22
20
  }
23
21
  },
@@ -26,9 +24,7 @@
26
24
  "description": "Event name to listen for opening the context menu.",
27
25
  "value": {
28
26
  "type": [
29
- "string",
30
- "null",
31
- "undefined"
27
+ "string"
32
28
  ]
33
29
  }
34
30
  },
@@ -37,9 +33,7 @@
37
33
  "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`.",
38
34
  "value": {
39
35
  "type": [
40
- "string",
41
- "null",
42
- "undefined"
36
+ "string"
43
37
  ]
44
38
  }
45
39
  },
@@ -48,9 +42,7 @@
48
42
  "description": "CSS selector that can be used to target any child element\nof the context menu to listen for `openOn` events.",
49
43
  "value": {
50
44
  "type": [
51
- "string",
52
- "null",
53
- "undefined"
45
+ "string"
54
46
  ]
55
47
  }
56
48
  },
@@ -73,18 +65,16 @@
73
65
  "description": "Event name to listen for closing the context menu.",
74
66
  "value": {
75
67
  "type": [
76
- "string",
77
- "null",
78
- "undefined"
68
+ "string"
79
69
  ]
80
70
  }
81
71
  },
82
72
  {
83
73
  "name": "items",
84
- "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```",
74
+ "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```",
85
75
  "value": {
86
76
  "type": [
87
- "Array.<ContextMenuItem>",
77
+ "Array<ContextMenuItem>",
88
78
  "undefined"
89
79
  ]
90
80
  }
@@ -103,9 +93,7 @@
103
93
  "description": "Event name to listen for opening the context menu.",
104
94
  "value": {
105
95
  "type": [
106
- "string",
107
- "null",
108
- "undefined"
96
+ "string"
109
97
  ]
110
98
  }
111
99
  },
@@ -114,9 +102,7 @@
114
102
  "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`.",
115
103
  "value": {
116
104
  "type": [
117
- "string",
118
- "null",
119
- "undefined"
105
+ "string"
120
106
  ]
121
107
  }
122
108
  },
@@ -135,14 +121,16 @@
135
121
  "description": "CSS selector that can be used to target any child element\nof the context menu to listen for `openOn` events.",
136
122
  "value": {
137
123
  "type": [
138
- "string",
139
- "null",
140
- "undefined"
124
+ "string"
141
125
  ]
142
126
  }
143
127
  }
144
128
  ],
145
129
  "events": [
130
+ {
131
+ "name": "close-all-menus",
132
+ "description": "Fired when all menus should close, e.g., after pressing Tab or on submenu close."
133
+ },
146
134
  {
147
135
  "name": "closed",
148
136
  "description": "Fired when the context menu is closed."
@@ -151,6 +139,10 @@
151
139
  "name": "item-selected",
152
140
  "description": "Fired when an item is selected when the context menu is populated using the `items` API."
153
141
  },
142
+ {
143
+ "name": "items-outside-click",
144
+ "description": "Fired when a click happens outside any open sub-menus."
145
+ },
154
146
  {
155
147
  "name": "opened-changed",
156
148
  "description": "Fired when the `opened` property changes."
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/context-menu",
4
- "version": "25.2.0-alpha1",
4
+ "version": "25.2.0-alpha11",
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\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.2.0-alpha1/#/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.2.0-alpha1/#/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#### Disabled menu items\n\nWhen disabled, menu items are rendered as \"dimmed\".\n\nBy default, disabled items are not focusable and don't react to hover.\nAs a result, they are hidden from assistive technologies, and it's not\npossible to show a tooltip to explain why they are disabled. This can\nbe addressed by enabling the feature flag `accessibleDisabledMenuItems`,\nwhich makes disabled items focusable and hoverable, while still\npreventing them from being activated:\n\n```js\n// Set before any context menu is attached to the DOM.\nwindow.Vaadin.featureFlags.accessibleDisabledMenuItems = true;\n```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```\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.2.0-alpha11/#/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.2.0-alpha11/#/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
  {
@@ -28,7 +28,7 @@
28
28
  },
29
29
  {
30
30
  "name": ".items",
31
- "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```",
31
+ "description": "Defines 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\nThe items API can't be used together with a renderer!\n\n#### Example\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```\n\n#### Item tooltips\n\nMenu items can have tooltips that are shown on hover and keyboard\nfocus. To enable them, add a slotted `<vaadin-tooltip>` element\nand set the `tooltip` property on each item that should have one:\n\n```html\n<vaadin-context-menu>\n <vaadin-tooltip slot=\"tooltip\"></vaadin-tooltip>\n</vaadin-context-menu>\n```",
32
32
  "value": {
33
33
  "kind": "expression"
34
34
  }
@@ -68,6 +68,13 @@
68
68
  "kind": "expression"
69
69
  }
70
70
  },
71
+ {
72
+ "name": "@close-all-menus",
73
+ "description": "Fired when all menus should close, e.g., after pressing Tab or on submenu close.",
74
+ "value": {
75
+ "kind": "expression"
76
+ }
77
+ },
71
78
  {
72
79
  "name": "@closed",
73
80
  "description": "Fired when the context menu is closed.",
@@ -82,6 +89,13 @@
82
89
  "kind": "expression"
83
90
  }
84
91
  },
92
+ {
93
+ "name": "@items-outside-click",
94
+ "description": "Fired when a click happens outside any open sub-menus.",
95
+ "value": {
96
+ "kind": "expression"
97
+ }
98
+ },
85
99
  {
86
100
  "name": "@opened-changed",
87
101
  "description": "Fired when the `opened` property changes.",