@vaadin/context-menu 24.0.0-beta1 → 24.0.0-beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/context-menu",
3
- "version": "24.0.0-beta1",
3
+ "version": "24.0.0-beta3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -39,14 +39,14 @@
39
39
  "dependencies": {
40
40
  "@open-wc/dedupe-mixin": "^1.3.0",
41
41
  "@polymer/polymer": "^3.0.0",
42
- "@vaadin/component-base": "24.0.0-beta1",
43
- "@vaadin/item": "24.0.0-beta1",
44
- "@vaadin/list-box": "24.0.0-beta1",
45
- "@vaadin/lit-renderer": "24.0.0-beta1",
46
- "@vaadin/overlay": "24.0.0-beta1",
47
- "@vaadin/vaadin-lumo-styles": "24.0.0-beta1",
48
- "@vaadin/vaadin-material-styles": "24.0.0-beta1",
49
- "@vaadin/vaadin-themable-mixin": "24.0.0-beta1"
42
+ "@vaadin/component-base": "24.0.0-beta3",
43
+ "@vaadin/item": "24.0.0-beta3",
44
+ "@vaadin/list-box": "24.0.0-beta3",
45
+ "@vaadin/lit-renderer": "24.0.0-beta3",
46
+ "@vaadin/overlay": "24.0.0-beta3",
47
+ "@vaadin/vaadin-lumo-styles": "24.0.0-beta3",
48
+ "@vaadin/vaadin-material-styles": "24.0.0-beta3",
49
+ "@vaadin/vaadin-themable-mixin": "24.0.0-beta3"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@esm-bundle/chai": "^4.3.4",
@@ -58,5 +58,5 @@
58
58
  "web-types.json",
59
59
  "web-types.lit.json"
60
60
  ],
61
- "gitHead": "c5b48921a62482746df8e46994b37e1490fec27e"
61
+ "gitHead": "4daeac63327393aacd9cfaa5abeb031ea86b14a5"
62
62
  }
@@ -267,6 +267,11 @@ declare class ContextMenu extends OverlayClassMixin(ElementMixin(ThemePropertyMi
267
267
  */
268
268
  renderer: ContextMenuRenderer | null | undefined;
269
269
 
270
+ /**
271
+ * When true, the menu overlay is modeless.
272
+ */
273
+ protected _modeless: boolean;
274
+
270
275
  /**
271
276
  * Requests an update for the content of the menu overlay.
272
277
  * While performing the update, it invokes the renderer passed in the `renderer` property.
@@ -225,12 +225,12 @@ class ContextMenu extends OverlayClassMixin(
225
225
  id="overlay"
226
226
  on-opened-changed="_onOverlayOpened"
227
227
  on-vaadin-overlay-open="_onVaadinOverlayOpen"
228
+ modeless="[[_modeless]]"
228
229
  with-backdrop="[[_phone]]"
229
230
  phone$="[[_phone]]"
230
231
  model="[[_context]]"
231
232
  theme$="[[_theme]]"
232
- >
233
- </vaadin-context-menu-overlay>
233
+ ></vaadin-context-menu-overlay>
234
234
  `;
235
235
  }
236
236
 
@@ -309,6 +309,14 @@ class ContextMenu extends OverlayClassMixin(
309
309
  type: Function,
310
310
  },
311
311
 
312
+ /**
313
+ * When true, the menu overlay is modeless.
314
+ * @protected
315
+ */
316
+ _modeless: {
317
+ type: Boolean,
318
+ },
319
+
312
320
  /** @private */
313
321
  _context: Object,
314
322
 
@@ -6,7 +6,6 @@
6
6
  import './vaadin-context-menu-item.js';
7
7
  import './vaadin-context-menu-list-box.js';
8
8
  import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
9
- import { Item } from '@vaadin/item/src/vaadin-item.js';
10
9
 
11
10
  /**
12
11
  * @polymerMixin
@@ -159,55 +158,216 @@ export const ItemsMixin = (superClass) =>
159
158
  }
160
159
 
161
160
  /**
162
- * @param {!HTMLElement} root
163
- * @param {!ContextMenu} menu
164
- * @param {!ContextMenuRendererContext} context
165
- * @protected
161
+ * @param {!ContextMenuItem} item
162
+ * @return {HTMLElement}
163
+ * @private
166
164
  */
167
- __itemsRenderer(root, menu, context) {
168
- this.__initMenu(root, menu);
165
+ __createComponent(item) {
166
+ let component;
169
167
 
170
- const subMenu = root.querySelector(this.constructor.is);
171
- subMenu.closeOn = menu.closeOn;
168
+ if (item.component instanceof HTMLElement) {
169
+ component = item.component;
170
+ } else {
171
+ component = document.createElement(item.component || `${this._tagNamePrefix}-item`);
172
+ }
172
173
 
173
- const listBox = root.querySelector(`${this._tagNamePrefix}-list-box`);
174
+ // Support menu-bar / context-menu item
175
+ if (component._hasVaadinItemMixin) {
176
+ component.setAttribute('role', 'menuitem');
177
+ }
174
178
 
175
- listBox.innerHTML = '';
179
+ if (component.localName === 'hr') {
180
+ component.setAttribute('role', 'separator');
181
+ } else {
182
+ // Accept not `menuitem` elements e.g. `<button>`
183
+ component.setAttribute('aria-haspopup', 'false');
184
+ }
176
185
 
177
- const items = Array.from(context.detail.children || menu.items);
186
+ this._setMenuItemTheme(component, item, this._theme);
178
187
 
179
- items.forEach((item) => {
180
- let component;
181
- if (item.component instanceof HTMLElement) {
182
- component = item.component;
183
- } else {
184
- component = document.createElement(item.component || `${this._tagNamePrefix}-item`);
188
+ component._item = item;
189
+
190
+ if (item.text) {
191
+ component.textContent = item.text;
192
+ }
193
+
194
+ this.__toggleMenuComponentAttribute(component, 'menu-item-checked', item.checked);
195
+ this.__toggleMenuComponentAttribute(component, 'disabled', item.disabled);
196
+
197
+ if (item.children && item.children.length) {
198
+ this.__updateExpanded(component, false);
199
+ component.setAttribute('aria-haspopup', 'true');
200
+ }
201
+
202
+ return component;
203
+ }
204
+
205
+ /** @private */
206
+ __initListBox() {
207
+ const listBox = document.createElement(`${this._tagNamePrefix}-list-box`);
208
+
209
+ if (this._theme) {
210
+ listBox.setAttribute('theme', this._theme);
211
+ }
212
+
213
+ listBox.addEventListener('selected-changed', (event) => {
214
+ const { value } = event.detail;
215
+ if (typeof value === 'number') {
216
+ const item = listBox.items[value]._item;
217
+ if (!item.children) {
218
+ this.dispatchEvent(new CustomEvent('item-selected', { detail: { value: item } }));
219
+ }
220
+ listBox.selected = null;
185
221
  }
222
+ });
223
+
224
+ return listBox;
225
+ }
186
226
 
187
- if (component instanceof Item) {
188
- component.setAttribute('role', 'menuitem');
189
- } else if (component.localName === 'hr') {
190
- component.setAttribute('role', 'separator');
227
+ /** @private */
228
+ __initOverlay() {
229
+ const overlay = this.$.overlay;
230
+
231
+ overlay.$.backdrop.addEventListener('click', () => {
232
+ this.close();
233
+ });
234
+
235
+ // Open a submenu on click event when a touch device is used.
236
+ // On desktop, a submenu opens on hover.
237
+ overlay.addEventListener(isTouch ? 'click' : 'mouseover', (event) => {
238
+ this.__showSubMenu(event);
239
+ });
240
+
241
+ overlay.addEventListener('keydown', (event) => {
242
+ const { key } = event;
243
+ const isRTL = this.__isRTL;
244
+
245
+ const isArrowRight = key === 'ArrowRight';
246
+ const isArrowLeft = key === 'ArrowLeft';
247
+
248
+ if ((!isRTL && isArrowRight) || (isRTL && isArrowLeft) || key === 'Enter' || key === ' ') {
249
+ // Open a sub-menu
250
+ this.__showSubMenu(event);
251
+ } else if ((!isRTL && isArrowLeft) || (isRTL && isArrowRight)) {
252
+ // Close the menu
253
+ this.close();
254
+ this.listenOn.focus();
255
+ } else if (key === 'Escape' || key === 'Tab') {
256
+ // Close all menus
257
+ this.dispatchEvent(new CustomEvent('close-all-menus'));
191
258
  }
259
+ });
260
+ }
261
+
262
+ /** @private */
263
+ __initSubMenu() {
264
+ const subMenu = document.createElement(this.constructor.is);
192
265
 
193
- this._setMenuItemTheme(component, item, this._theme);
266
+ subMenu._modeless = true;
267
+ subMenu.openOn = 'opensubmenu';
194
268
 
195
- component._item = item;
269
+ // Sub-menu doesn't have a target to wrap,
270
+ // so there is no need to keep it visible.
271
+ subMenu.setAttribute('hidden', '');
196
272
 
197
- if (item.text) {
198
- component.textContent = item.text;
273
+ // Close sub-menu when the parent menu closes.
274
+ this.addEventListener('opened-changed', (event) => {
275
+ if (!event.detail.value) {
276
+ this._subMenu.close();
199
277
  }
278
+ });
200
279
 
201
- this.__toggleMenuComponentAttribute(component, 'menu-item-checked', item.checked);
202
- this.__toggleMenuComponentAttribute(component, 'disabled', item.disabled);
280
+ // Forward event to the parent menu element.
281
+ subMenu.addEventListener('close-all-menus', () => {
282
+ this.dispatchEvent(new CustomEvent('close-all-menus'));
283
+ });
203
284
 
204
- component.setAttribute('aria-haspopup', 'false');
205
- if (item.children && item.children.length) {
206
- component.setAttribute('aria-haspopup', 'true');
207
- component.setAttribute('aria-expanded', 'false');
208
- component.removeAttribute('expanded');
285
+ // Forward event to the parent menu element.
286
+ subMenu.addEventListener('item-selected', (event) => {
287
+ const { detail } = event;
288
+ this.dispatchEvent(new CustomEvent('item-selected', { detail }));
289
+ });
290
+
291
+ // Listen to the forwarded event from sub-menu.
292
+ this.addEventListener('close-all-menus', () => {
293
+ this.close();
294
+ });
295
+
296
+ // Listen to the forwarded event from sub-menu.
297
+ this.addEventListener('item-selected', () => {
298
+ this.close();
299
+ });
300
+
301
+ // Mark parent item as collapsed when closing.
302
+ subMenu.addEventListener('opened-changed', (event) => {
303
+ if (!event.detail.value) {
304
+ const expandedItem = this._listBox.querySelector('[expanded]');
305
+ if (expandedItem) {
306
+ this.__updateExpanded(expandedItem, false);
307
+ }
209
308
  }
309
+ });
310
+
311
+ return subMenu;
312
+ }
313
+
314
+ /** @private */
315
+ __showSubMenu(event, item = event.composedPath().find((node) => node.localName === `${this._tagNamePrefix}-item`)) {
316
+ // Delay enabling the mouseover listener to avoid it from triggering on parent menu open
317
+ if (!this.__openListenerActive) {
318
+ return;
319
+ }
320
+
321
+ // Don't open sub-menus while the menu is still opening
322
+ if (this.$.overlay.hasAttribute('opening')) {
323
+ requestAnimationFrame(() => {
324
+ this.__showSubMenu(event, item);
325
+ });
326
+
327
+ return;
328
+ }
210
329
 
330
+ const subMenu = this._subMenu;
331
+
332
+ if (item) {
333
+ const { children } = item._item;
334
+
335
+ if (subMenu.items !== children) {
336
+ subMenu.close();
337
+ }
338
+ if (!this.opened) {
339
+ return;
340
+ }
341
+
342
+ if (children && children.length) {
343
+ this.__updateExpanded(item, true);
344
+
345
+ // Forward parent overlay class
346
+ const { overlayClass } = this;
347
+ this.__openSubMenu(subMenu, item, overlayClass);
348
+ } else {
349
+ subMenu.listenOn.focus();
350
+ }
351
+ }
352
+ }
353
+
354
+ /**
355
+ * @param {!HTMLElement} root
356
+ * @param {!ContextMenu} menu
357
+ * @param {!ContextMenuRendererContext} context
358
+ * @protected
359
+ */
360
+ __itemsRenderer(root, menu, { detail }) {
361
+ this.__initMenu(root, menu);
362
+
363
+ const subMenu = root.querySelector(this.constructor.is);
364
+ subMenu.closeOn = menu.closeOn;
365
+
366
+ const listBox = root.querySelector(`${this._tagNamePrefix}-list-box`);
367
+ listBox.innerHTML = '';
368
+
369
+ [...(detail.children || menu.items)].forEach((item) => {
370
+ const component = this.__createComponent(item);
211
371
  listBox.appendChild(component);
212
372
  });
213
373
  }
@@ -223,11 +383,7 @@ export const ItemsMixin = (superClass) =>
223
383
  theme = Array.isArray(item.theme) ? item.theme.join(' ') : item.theme;
224
384
  }
225
385
 
226
- if (theme) {
227
- component.setAttribute('theme', theme);
228
- } else {
229
- component.removeAttribute('theme');
230
- }
386
+ this.__updateTheme(component, theme);
231
387
  }
232
388
 
233
389
  /** @private */
@@ -243,122 +399,39 @@ export const ItemsMixin = (superClass) =>
243
399
 
244
400
  /** @private */
245
401
  __initMenu(root, menu) {
402
+ // NOTE: in this method, `menu` and `this` reference the same element,
403
+ // so we can use either of those. Original implementation used `menu`.
246
404
  if (!root.firstElementChild) {
247
- const listBox = document.createElement(`${this._tagNamePrefix}-list-box`);
248
- root.appendChild(listBox);
405
+ this.__initOverlay();
249
406
 
250
- if (this._theme) {
251
- listBox.setAttribute('theme', this._theme);
252
- }
253
- requestAnimationFrame(() => listBox.setAttribute('role', 'menu'));
407
+ const listBox = this.__initListBox();
408
+ this._listBox = listBox;
409
+ root.appendChild(listBox);
254
410
 
255
- const subMenu = document.createElement(this.constructor.is);
256
- subMenu.setAttribute('hidden', '');
411
+ const subMenu = this.__initSubMenu();
412
+ this._subMenu = subMenu;
257
413
  root.appendChild(subMenu);
258
- subMenu.$.overlay.modeless = true;
259
- subMenu.openOn = 'opensubmenu';
260
-
261
- menu.addEventListener('opened-changed', (e) => !e.detail.value && subMenu.close());
262
- subMenu.addEventListener('opened-changed', (e) => {
263
- if (!e.detail.value) {
264
- const expandedItem = listBox.querySelector('[expanded]');
265
- if (expandedItem) {
266
- expandedItem.setAttribute('aria-expanded', 'false');
267
- expandedItem.removeAttribute('expanded');
268
- }
269
- }
270
- });
271
-
272
- listBox.addEventListener('selected-changed', (e) => {
273
- if (typeof e.detail.value === 'number') {
274
- const item = e.target.items[e.detail.value]._item;
275
- if (!item.children) {
276
- const detail = { value: item };
277
- menu.dispatchEvent(new CustomEvent('item-selected', { detail }));
278
- }
279
- listBox.selected = null;
280
- }
281
- });
282
-
283
- subMenu.addEventListener('item-selected', (e) => {
284
- menu.dispatchEvent(new CustomEvent('item-selected', { detail: e.detail }));
285
- });
286
-
287
- subMenu.addEventListener('close-all-menus', () => {
288
- menu.dispatchEvent(new CustomEvent('close-all-menus'));
289
- });
290
- menu.addEventListener('close-all-menus', menu.close);
291
- menu.addEventListener('item-selected', menu.close);
292
- menu.$.overlay.$.backdrop.addEventListener('click', () => menu.close());
293
-
294
- menu.$.overlay.addEventListener('keydown', (e) => {
295
- const isRTL = this.__isRTL;
296
- if ((!isRTL && e.keyCode === 37) || (isRTL && e.keyCode === 39)) {
297
- menu.close();
298
- menu.listenOn.focus();
299
- } else if (e.key === 'Escape' || e.key === 'Tab') {
300
- menu.dispatchEvent(new CustomEvent('close-all-menus'));
301
- }
302
- });
303
414
 
304
415
  requestAnimationFrame(() => {
305
416
  this.__openListenerActive = true;
306
417
  });
307
- const openSubMenu = (
308
- e,
309
- itemElement = e.composedPath().find((e) => e.localName === `${this._tagNamePrefix}-item`),
310
- ) => {
311
- // Delay enabling the mouseover listener to avoid it from triggering on parent menu open
312
- if (!this.__openListenerActive) {
313
- return;
314
- }
315
-
316
- // Don't open sub-menus while the menu is still opening
317
- if (menu.$.overlay.hasAttribute('opening')) {
318
- requestAnimationFrame(() => openSubMenu(e, itemElement));
319
- return;
320
- }
321
-
322
- if (itemElement) {
323
- if (subMenu.items !== itemElement._item.children) {
324
- subMenu.close();
325
- }
326
- if (!menu.opened) {
327
- return;
328
- }
329
- if (itemElement._item.children && itemElement._item.children.length) {
330
- itemElement.setAttribute('aria-expanded', 'true');
331
- itemElement.setAttribute('expanded', '');
332
-
333
- // Forward parent overlay class
334
- const { overlayClass } = menu;
335
- this.__openSubMenu(subMenu, itemElement, overlayClass);
336
- } else {
337
- subMenu.listenOn.focus();
338
- }
339
- }
340
- };
341
-
342
- // Open a submenu on click event when a touch device is used.
343
- // On desktop, a submenu opens on hover.
344
- menu.$.overlay.addEventListener(isTouch ? 'click' : 'mouseover', openSubMenu);
418
+ } else {
419
+ this.__updateTheme(this._listBox, this._theme);
420
+ }
421
+ }
345
422
 
346
- menu.$.overlay.addEventListener('keydown', (e) => {
347
- const isRTL = this.__isRTL;
348
- const shouldOpenSubMenu =
349
- (!isRTL && e.keyCode === 39) || (isRTL && e.keyCode === 37) || e.keyCode === 13 || e.keyCode === 32;
423
+ /** @private */
424
+ __updateExpanded(component, expanded) {
425
+ component.setAttribute('aria-expanded', expanded.toString());
426
+ component.toggleAttribute('expanded', expanded);
427
+ }
350
428
 
351
- if (shouldOpenSubMenu) {
352
- openSubMenu(e);
353
- }
354
- });
429
+ /** @private */
430
+ __updateTheme(component, theme) {
431
+ if (theme) {
432
+ component.setAttribute('theme', theme);
355
433
  } else {
356
- const listBox = root.querySelector(`${this._tagNamePrefix}-list-box`);
357
- if (this._theme) {
358
- listBox.setAttribute('theme', this._theme);
359
- } else {
360
- listBox.removeAttribute('theme');
361
- }
434
+ component.removeAttribute('theme');
362
435
  }
363
436
  }
364
437
  };
package/web-types.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/context-menu",
4
- "version": "24.0.0-alpha12",
4
+ "version": "24.0.0-beta3",
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.\n\n```javascript\ncontextMenu.items = [\n {text: 'Menu Item 1', theme: 'primary', children:\n [\n {text: 'Menu Item 1-1', checked: 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}\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### vaadin-contextmenu Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n`<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal\nthemable component as the actual visible context menu overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-overlay)\ndocumentation for `<vaadin-context-menu-overlay>` stylable parts.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API, in addition `<vaadin-context-menu-overlay>`, the following\ninternal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-list-box).\n\nNote: the `theme` attribute value set on `<vaadin-context-menu>` is\npropagated to the internal components listed above.",
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.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', children:\n [\n { text: 'Menu Item 1-1', checked: 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 }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n`<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal\nthemable component as the actual visible context menu overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-overlay)\ndocumentation for `<vaadin-context-menu-overlay>` stylable parts.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API, in addition `<vaadin-context-menu-overlay>`, the following\ninternal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-list-box).\n\nNote: the `theme` attribute value set on `<vaadin-context-menu>` is\npropagated to the internal components listed above.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "overlay-class",
@@ -77,7 +77,7 @@
77
77
  },
78
78
  {
79
79
  "name": "items",
80
- "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', children:\n [\n {text: 'Menu Item 1-1', checked: 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}\n];\n```",
80
+ "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', children:\n [\n { text: 'Menu Item 1-1', checked: 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 }\n];\n```",
81
81
  "value": {
82
82
  "type": [
83
83
  "Array.<ContextMenuItem>",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/context-menu",
4
- "version": "24.0.0-alpha12",
4
+ "version": "24.0.0-beta3",
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.\n\n```javascript\ncontextMenu.items = [\n {text: 'Menu Item 1', theme: 'primary', children:\n [\n {text: 'Menu Item 1-1', checked: 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}\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### vaadin-contextmenu Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n`<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal\nthemable component as the actual visible context menu overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-overlay)\ndocumentation for `<vaadin-context-menu-overlay>` stylable parts.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API, in addition `<vaadin-context-menu-overlay>`, the following\ninternal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-alpha12/#/elements/vaadin-list-box).\n\nNote: the `theme` attribute value set on `<vaadin-context-menu>` is\npropagated to the internal components listed above.",
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.\n\n```javascript\ncontextMenu.items = [\n { text: 'Menu Item 1', theme: 'primary', children:\n [\n { text: 'Menu Item 1-1', checked: 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 }\n];\n\ncontextMenu.addEventListener('item-selected', e => {\n const item = e.detail.value;\n console.log(`${item.text} selected`);\n});\n```\n\n**NOTE:** when the `items` array is defined, the renderer cannot be used.\n\n### Rendering\n\nThe content of the menu can be populated by using the renderer callback function.\n\nThe renderer function provides `root`, `contextMenu`, `model` arguments when applicable.\nGenerate DOM content by using `model` object properties if needed, append it to the `root`\nelement and control the state of the host element by accessing `contextMenu`. Before generating\nnew content, the renderer function should check if there is already content in `root` for reusing it.\n\n```html\n<vaadin-context-menu id=\"contextMenu\">\n <p>This paragraph has a context menu.</p>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'Content of the selector: ' + context.target.textContent;\n};\n```\n\nYou can access the menu context inside the renderer using\n`context.target` and `context.detail`.\n\nRenderer is called on the opening of the context-menu and each time the related context is updated.\nDOM generated during the renderer call can be reused\nin the next renderer call and will be provided with the `root` argument.\nOn first call it will be empty.\n\n### `vaadin-contextmenu` Gesture Event\n\n`vaadin-contextmenu` is a gesture event (a custom event),\nwhich is dispatched after either `contextmenu` or long touch events.\nThis enables support for both mouse and touch environments in a uniform way.\n\n`<vaadin-context-menu>` opens the menu overlay on the `vaadin-contextmenu`\nevent by default.\n\n### Menu Listener\n\nBy default, the `<vaadin-context-menu>` element listens for the menu opening\nevent on itself. In case if you do not want to wrap the target, you can listen for\nevents on an element outside the `<vaadin-context-menu>` by setting the\n`listenOn` property:\n\n```html\n<vaadin-context-menu id=\"contextMenu\"></vaadin-context-menu>\n\n<div id=\"menuListener\">The element that listens for the contextmenu event.</div>\n```\n```javascript\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.listenOn = document.querySelector('#menuListener');\n```\n\n### Filtering Menu Targets\n\nBy default, the listener element and all its descendants open the context\nmenu. You can filter the menu targets to a smaller set of elements inside\nthe listener element by setting the `selector` property.\n\nIn the following example, only the elements matching `.has-menu` will open the context menu:\n\n```html\n<vaadin-context-menu selector=\".has-menu\">\n <p class=\"has-menu\">This paragraph opens the context menu</p>\n <p>This paragraph does not open the context menu</p>\n</vaadin-context-menu>\n```\n\n### Menu Context\n\nThe following properties are available in the `context` argument:\n\n- `target` is the menu opening event target, which is the element that\nthe user has called the context menu for\n- `detail` is the menu opening event detail\n\nIn the following example, the menu item text is composed with the contents\nof the element that opened the menu:\n\n```html\n<vaadin-context-menu selector=\"li\" id=\"contextMenu\">\n <ul>\n <li>Foo</li>\n <li>Bar</li>\n <li>Baz</li>\n </ul>\n</vaadin-context-menu>\n```\n```js\nconst contextMenu = document.querySelector('#contextMenu');\ncontextMenu.renderer = (root, contextMenu, context) => {\n let listBox = root.firstElementChild;\n if (!listBox) {\n listBox = document.createElement('vaadin-list-box');\n root.appendChild(listBox);\n }\n\n let item = listBox.querySelector('vaadin-item');\n if (!item) {\n item = document.createElement('vaadin-item');\n listBox.appendChild(item);\n }\n item.textContent = 'The menu target: ' + context.target.textContent;\n};\n```\n\n### Styling\n\n`<vaadin-context-menu>` uses `<vaadin-context-menu-overlay>` internal\nthemable component as the actual visible context menu overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-overlay)\ndocumentation for `<vaadin-context-menu-overlay>` stylable parts.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/custom-theme/styling-components) documentation.\n\n### Internal components\n\nWhen using `items` API, in addition `<vaadin-context-menu-overlay>`, the following\ninternal components are themable:\n\n- `<vaadin-context-menu-item>` - has the same API as [`<vaadin-item>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-item).\n- `<vaadin-context-menu-list-box>` - has the same API as [`<vaadin-list-box>`](https://cdn.vaadin.com/vaadin-web-components/24.0.0-beta3/#/elements/vaadin-list-box).\n\nNote: the `theme` attribute value set on `<vaadin-context-menu>` is\npropagated to the internal components listed above.",
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', children:\n [\n {text: 'Menu Item 1-1', checked: 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}\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', children:\n [\n { text: 'Menu Item 1-1', checked: 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 }\n];\n```",
32
32
  "value": {
33
33
  "kind": "expression"
34
34
  }