@vaadin/context-menu 24.2.0-dev.f254716fe → 24.3.0-alpha2
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 +12 -12
- package/src/vaadin-context-menu-item.js +3 -1
- package/src/vaadin-context-menu-list-box.js +3 -1
- package/src/vaadin-context-menu-mixin.d.ts +81 -0
- package/src/vaadin-context-menu-mixin.js +494 -0
- package/src/vaadin-context-menu-overlay.js +3 -1
- package/src/vaadin-context-menu.d.ts +5 -70
- package/src/vaadin-context-menu.js +11 -481
- package/src/vaadin-contextmenu-items-mixin.d.ts +2 -1
- package/src/vaadin-contextmenu-items-mixin.js +34 -19
- package/web-types.json +152 -0
- package/web-types.lit.json +90 -0
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
7
7
|
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
8
8
|
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
9
|
-
import {
|
|
9
|
+
import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
|
|
10
|
+
import { ContextMenuItem } from './vaadin-contextmenu-items-mixin.js';
|
|
10
11
|
|
|
11
12
|
export { ContextMenuItem };
|
|
12
13
|
|
|
@@ -54,12 +55,13 @@ export interface ContextMenuEventMap extends HTMLElementEventMap, ContextMenuCus
|
|
|
54
55
|
*
|
|
55
56
|
* When an item is selected, `<vaadin-context-menu>` dispatches an "item-selected" event
|
|
56
57
|
* with the selected item as `event.detail.value` property.
|
|
58
|
+
* If item does not have `keepOpen` property the menu will be closed.
|
|
57
59
|
*
|
|
58
60
|
* ```javascript
|
|
59
61
|
* contextMenu.items = [
|
|
60
62
|
* { text: 'Menu Item 1', theme: 'primary', children:
|
|
61
63
|
* [
|
|
62
|
-
* { text: 'Menu Item 1-1', checked: true },
|
|
64
|
+
* { text: 'Menu Item 1-1', checked: true, keepOpen: true },
|
|
63
65
|
* { text: 'Menu Item 1-2' }
|
|
64
66
|
* ]
|
|
65
67
|
* },
|
|
@@ -224,74 +226,7 @@ export interface ContextMenuEventMap extends HTMLElementEventMap, ContextMenuCus
|
|
|
224
226
|
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
225
227
|
* @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
|
|
226
228
|
*/
|
|
227
|
-
declare class ContextMenu extends OverlayClassMixin(ElementMixin(ThemePropertyMixin(
|
|
228
|
-
/**
|
|
229
|
-
* CSS selector that can be used to target any child element
|
|
230
|
-
* of the context menu to listen for `openOn` events.
|
|
231
|
-
*/
|
|
232
|
-
selector: string | null | undefined;
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* True if the overlay is currently displayed.
|
|
236
|
-
*/
|
|
237
|
-
readonly opened: boolean;
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Event name to listen for opening the context menu.
|
|
241
|
-
* @attr {string} open-on
|
|
242
|
-
*/
|
|
243
|
-
openOn: string;
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* The target element that's listened to for context menu opening events.
|
|
247
|
-
* By default the vaadin-context-menu listens to the target's `vaadin-contextmenu`
|
|
248
|
-
* events.
|
|
249
|
-
*/
|
|
250
|
-
listenOn: HTMLElement;
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Event name to listen for closing the context menu.
|
|
254
|
-
* @attr {string} close-on
|
|
255
|
-
*/
|
|
256
|
-
closeOn: string;
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Custom function for rendering the content of the menu overlay.
|
|
260
|
-
* Receives three arguments:
|
|
261
|
-
*
|
|
262
|
-
* - `root` The root container DOM element. Append your content to it.
|
|
263
|
-
* - `contextMenu` The reference to the `<vaadin-context-menu>` element.
|
|
264
|
-
* - `context` The object with the menu context, contains:
|
|
265
|
-
* - `context.target` the target of the menu opening event,
|
|
266
|
-
* - `context.detail` the menu opening event detail.
|
|
267
|
-
*/
|
|
268
|
-
renderer: ContextMenuRenderer | null | undefined;
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* When true, the menu overlay is modeless.
|
|
272
|
-
*/
|
|
273
|
-
protected _modeless: boolean;
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Requests an update for the content of the menu overlay.
|
|
277
|
-
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
278
|
-
*
|
|
279
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
280
|
-
*/
|
|
281
|
-
requestContentUpdate(): void;
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Closes the overlay.
|
|
285
|
-
*/
|
|
286
|
-
close(): void;
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Opens the overlay.
|
|
290
|
-
*
|
|
291
|
-
* @param e used as the context for the menu. Overlay coordinates are taken from this event.
|
|
292
|
-
*/
|
|
293
|
-
open(e: Event | undefined): void;
|
|
294
|
-
|
|
229
|
+
declare class ContextMenu extends ContextMenuMixin(OverlayClassMixin(ElementMixin(ThemePropertyMixin(HTMLElement)))) {
|
|
295
230
|
addEventListener<K extends keyof ContextMenuEventMap>(
|
|
296
231
|
type: K,
|
|
297
232
|
listener: (this: ContextMenu, ev: ContextMenuEventMap[K]) => void,
|
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import './vaadin-contextmenu-event.js';
|
|
7
|
+
import './vaadin-context-menu-item.js';
|
|
8
|
+
import './vaadin-context-menu-list-box.js';
|
|
7
9
|
import './vaadin-context-menu-overlay.js';
|
|
8
10
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
9
|
-
import { isTouch } from '@vaadin/component-base/src/browser-utils.js';
|
|
10
11
|
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
12
|
+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
11
13
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
12
|
-
import { addListener, gestures, removeListener } from '@vaadin/component-base/src/gestures.js';
|
|
13
|
-
import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js';
|
|
14
14
|
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
15
15
|
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
16
16
|
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
17
|
-
import {
|
|
17
|
+
import { ContextMenuMixin } from './vaadin-context-menu-mixin.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* `<vaadin-context-menu>` is a Web Component for creating context menus.
|
|
@@ -27,12 +27,13 @@ import { ItemsMixin } from './vaadin-contextmenu-items-mixin.js';
|
|
|
27
27
|
*
|
|
28
28
|
* When an item is selected, `<vaadin-context-menu>` dispatches an "item-selected" event
|
|
29
29
|
* with the selected item as `event.detail.value` property.
|
|
30
|
+
* If item does not have `keepOpen` property the menu will be closed.
|
|
30
31
|
*
|
|
31
32
|
* ```javascript
|
|
32
33
|
* contextMenu.items = [
|
|
33
34
|
* { text: 'Menu Item 1', theme: 'primary', children:
|
|
34
35
|
* [
|
|
35
|
-
* { text: 'Menu Item 1-1', checked: true },
|
|
36
|
+
* { text: 'Menu Item 1-1', checked: true, keepOpen: true },
|
|
36
37
|
* { text: 'Menu Item 1-2' }
|
|
37
38
|
* ]
|
|
38
39
|
* },
|
|
@@ -197,15 +198,16 @@ import { ItemsMixin } from './vaadin-contextmenu-items-mixin.js';
|
|
|
197
198
|
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
198
199
|
* @fires {CustomEvent} item-selected - Fired when an item is selected when the context menu is populated using the `items` API.
|
|
199
200
|
*
|
|
201
|
+
* @customElement
|
|
200
202
|
* @extends HTMLElement
|
|
201
203
|
* @mixes ElementMixin
|
|
204
|
+
* @mixes ContextMenuMixin
|
|
202
205
|
* @mixes ControllerMixin
|
|
203
206
|
* @mixes OverlayClassMixin
|
|
204
207
|
* @mixes ThemePropertyMixin
|
|
205
|
-
* @mixes ItemsMixin
|
|
206
208
|
*/
|
|
207
|
-
class ContextMenu extends
|
|
208
|
-
ControllerMixin(ElementMixin(ThemePropertyMixin(
|
|
209
|
+
class ContextMenu extends ContextMenuMixin(
|
|
210
|
+
OverlayClassMixin(ControllerMixin(ElementMixin(ThemePropertyMixin(PolymerElement)))),
|
|
209
211
|
) {
|
|
210
212
|
static get template() {
|
|
211
213
|
return html`
|
|
@@ -238,485 +240,13 @@ class ContextMenu extends OverlayClassMixin(
|
|
|
238
240
|
return 'vaadin-context-menu';
|
|
239
241
|
}
|
|
240
242
|
|
|
241
|
-
static get properties() {
|
|
242
|
-
return {
|
|
243
|
-
/**
|
|
244
|
-
* CSS selector that can be used to target any child element
|
|
245
|
-
* of the context menu to listen for `openOn` events.
|
|
246
|
-
*/
|
|
247
|
-
selector: {
|
|
248
|
-
type: String,
|
|
249
|
-
},
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* True if the overlay is currently displayed.
|
|
253
|
-
* @type {boolean}
|
|
254
|
-
*/
|
|
255
|
-
opened: {
|
|
256
|
-
type: Boolean,
|
|
257
|
-
value: false,
|
|
258
|
-
notify: true,
|
|
259
|
-
readOnly: true,
|
|
260
|
-
},
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Event name to listen for opening the context menu.
|
|
264
|
-
* @attr {string} open-on
|
|
265
|
-
* @type {string}
|
|
266
|
-
*/
|
|
267
|
-
openOn: {
|
|
268
|
-
type: String,
|
|
269
|
-
value: 'vaadin-contextmenu',
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* The target element that's listened to for context menu opening events.
|
|
274
|
-
* By default the vaadin-context-menu listens to the target's `vaadin-contextmenu`
|
|
275
|
-
* events.
|
|
276
|
-
* @type {!HTMLElement}
|
|
277
|
-
* @default self
|
|
278
|
-
*/
|
|
279
|
-
listenOn: {
|
|
280
|
-
type: Object,
|
|
281
|
-
value() {
|
|
282
|
-
return this;
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Event name to listen for closing the context menu.
|
|
288
|
-
* @attr {string} close-on
|
|
289
|
-
* @type {string}
|
|
290
|
-
*/
|
|
291
|
-
closeOn: {
|
|
292
|
-
type: String,
|
|
293
|
-
value: 'click',
|
|
294
|
-
observer: '_closeOnChanged',
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Custom function for rendering the content of the menu overlay.
|
|
299
|
-
* Receives three arguments:
|
|
300
|
-
*
|
|
301
|
-
* - `root` The root container DOM element. Append your content to it.
|
|
302
|
-
* - `contextMenu` The reference to the `<vaadin-context-menu>` element.
|
|
303
|
-
* - `context` The object with the menu context, contains:
|
|
304
|
-
* - `context.target` the target of the menu opening event,
|
|
305
|
-
* - `context.detail` the menu opening event detail.
|
|
306
|
-
* @type {ContextMenuRenderer | undefined}
|
|
307
|
-
*/
|
|
308
|
-
renderer: {
|
|
309
|
-
type: Function,
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* When true, the menu overlay is modeless.
|
|
314
|
-
* @protected
|
|
315
|
-
*/
|
|
316
|
-
_modeless: {
|
|
317
|
-
type: Boolean,
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
/** @private */
|
|
321
|
-
_context: Object,
|
|
322
|
-
|
|
323
|
-
/** @private */
|
|
324
|
-
_phone: {
|
|
325
|
-
type: Boolean,
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
/** @private */
|
|
329
|
-
_touch: {
|
|
330
|
-
type: Boolean,
|
|
331
|
-
value: isTouch,
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
/** @private */
|
|
335
|
-
_wide: {
|
|
336
|
-
type: Boolean,
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
/** @private */
|
|
340
|
-
_wideMediaQuery: {
|
|
341
|
-
type: String,
|
|
342
|
-
value: '(min-device-width: 750px)',
|
|
343
|
-
},
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
static get observers() {
|
|
348
|
-
return [
|
|
349
|
-
'_openedChanged(opened)',
|
|
350
|
-
'_targetOrOpenOnChanged(listenOn, openOn)',
|
|
351
|
-
'_rendererChanged(renderer, items)',
|
|
352
|
-
'_touchOrWideChanged(_touch, _wide)',
|
|
353
|
-
];
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
constructor() {
|
|
357
|
-
super();
|
|
358
|
-
this._boundOpen = this.open.bind(this);
|
|
359
|
-
this._boundClose = this.close.bind(this);
|
|
360
|
-
this._boundPreventDefault = this._preventDefault.bind(this);
|
|
361
|
-
this._boundOnGlobalContextMenu = this._onGlobalContextMenu.bind(this);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/** @protected */
|
|
365
|
-
connectedCallback() {
|
|
366
|
-
super.connectedCallback();
|
|
367
|
-
|
|
368
|
-
this.__boundOnScroll = this.__onScroll.bind(this);
|
|
369
|
-
window.addEventListener('scroll', this.__boundOnScroll, true);
|
|
370
|
-
|
|
371
|
-
// Restore opened state if overlay was opened when disconnecting
|
|
372
|
-
if (this.__restoreOpened) {
|
|
373
|
-
this._setOpened(true);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/** @protected */
|
|
378
|
-
disconnectedCallback() {
|
|
379
|
-
super.disconnectedCallback();
|
|
380
|
-
|
|
381
|
-
window.removeEventListener('scroll', this.__boundOnScroll, true);
|
|
382
|
-
|
|
383
|
-
// Close overlay and memorize opened state
|
|
384
|
-
this.__restoreOpened = this.opened;
|
|
385
|
-
this.close();
|
|
386
|
-
}
|
|
387
|
-
|
|
388
243
|
/** @protected */
|
|
389
244
|
ready() {
|
|
390
245
|
super.ready();
|
|
391
246
|
|
|
392
|
-
this._overlayElement = this.$.overlay;
|
|
393
|
-
|
|
394
|
-
this.addController(
|
|
395
|
-
new MediaQueryController(this._wideMediaQuery, (matches) => {
|
|
396
|
-
this._wide = matches;
|
|
397
|
-
}),
|
|
398
|
-
);
|
|
399
|
-
|
|
400
247
|
processTemplates(this);
|
|
401
248
|
}
|
|
402
249
|
|
|
403
|
-
/**
|
|
404
|
-
* Runs before overlay is fully rendered
|
|
405
|
-
* @private
|
|
406
|
-
*/
|
|
407
|
-
_onOverlayOpened(e) {
|
|
408
|
-
this._setOpened(e.detail.value);
|
|
409
|
-
this.__alignOverlayPosition();
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Runs after overlay is fully rendered
|
|
414
|
-
* @private
|
|
415
|
-
*/
|
|
416
|
-
_onVaadinOverlayOpen() {
|
|
417
|
-
this.__alignOverlayPosition();
|
|
418
|
-
this.$.overlay.style.opacity = '';
|
|
419
|
-
this.__forwardFocus();
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/** @private */
|
|
423
|
-
_targetOrOpenOnChanged(listenOn, openOn) {
|
|
424
|
-
if (this._oldListenOn && this._oldOpenOn) {
|
|
425
|
-
this._unlisten(this._oldListenOn, this._oldOpenOn, this._boundOpen);
|
|
426
|
-
|
|
427
|
-
this._oldListenOn.style.webkitTouchCallout = '';
|
|
428
|
-
this._oldListenOn.style.webkitUserSelect = '';
|
|
429
|
-
this._oldListenOn.style.userSelect = '';
|
|
430
|
-
|
|
431
|
-
this._oldListenOn = null;
|
|
432
|
-
this._oldOpenOn = null;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (listenOn && openOn) {
|
|
436
|
-
this._listen(listenOn, openOn, this._boundOpen);
|
|
437
|
-
|
|
438
|
-
this._oldListenOn = listenOn;
|
|
439
|
-
this._oldOpenOn = openOn;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/** @private */
|
|
444
|
-
_touchOrWideChanged(touch, wide) {
|
|
445
|
-
this._phone = !wide && touch;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
/** @private */
|
|
449
|
-
_setListenOnUserSelect(value) {
|
|
450
|
-
// Note: these styles don't seem to work in Firefox on iOS.
|
|
451
|
-
this.listenOn.style.webkitTouchCallout = value;
|
|
452
|
-
this.listenOn.style.webkitUserSelect = value; // Chrome, Safari, Firefox
|
|
453
|
-
this.listenOn.style.userSelect = value;
|
|
454
|
-
|
|
455
|
-
// Note: because user-selection is disabled on the overlay
|
|
456
|
-
// before opening the menu the text could be already selected
|
|
457
|
-
// so we need to clear that selection
|
|
458
|
-
document.getSelection().removeAllRanges();
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
/** @private */
|
|
462
|
-
_closeOnChanged(closeOn, oldCloseOn) {
|
|
463
|
-
// Outside click event from overlay
|
|
464
|
-
const evtOverlay = 'vaadin-overlay-outside-click';
|
|
465
|
-
|
|
466
|
-
const overlay = this.$.overlay;
|
|
467
|
-
|
|
468
|
-
if (oldCloseOn) {
|
|
469
|
-
this._unlisten(overlay, oldCloseOn, this._boundClose);
|
|
470
|
-
}
|
|
471
|
-
if (closeOn) {
|
|
472
|
-
this._listen(overlay, closeOn, this._boundClose);
|
|
473
|
-
overlay.removeEventListener(evtOverlay, this._boundPreventDefault);
|
|
474
|
-
} else {
|
|
475
|
-
overlay.addEventListener(evtOverlay, this._boundPreventDefault);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/** @private */
|
|
480
|
-
_preventDefault(e) {
|
|
481
|
-
e.preventDefault();
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/** @private */
|
|
485
|
-
_openedChanged(opened) {
|
|
486
|
-
if (opened) {
|
|
487
|
-
document.documentElement.addEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
|
|
488
|
-
this._setListenOnUserSelect('none');
|
|
489
|
-
} else {
|
|
490
|
-
document.documentElement.removeEventListener('contextmenu', this._boundOnGlobalContextMenu, true);
|
|
491
|
-
this._setListenOnUserSelect('');
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Has to be set after instance has been created
|
|
495
|
-
this.$.overlay.opened = opened;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Requests an update for the content of the menu overlay.
|
|
500
|
-
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
501
|
-
*
|
|
502
|
-
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
503
|
-
*/
|
|
504
|
-
requestContentUpdate() {
|
|
505
|
-
if (!this._overlayElement || !this.renderer) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
this._overlayElement.requestContentUpdate();
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/** @private */
|
|
513
|
-
_rendererChanged(renderer, items) {
|
|
514
|
-
if (items) {
|
|
515
|
-
if (renderer) {
|
|
516
|
-
throw new Error('The items API cannot be used together with a renderer');
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
if (this.closeOn === 'click') {
|
|
520
|
-
this.closeOn = '';
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
renderer = this.__itemsRenderer;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
this.$.overlay.setProperties({ owner: this, renderer });
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Closes the overlay.
|
|
531
|
-
*/
|
|
532
|
-
close() {
|
|
533
|
-
this._setOpened(false);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
/** @private */
|
|
537
|
-
_contextTarget(e) {
|
|
538
|
-
if (this.selector) {
|
|
539
|
-
const targets = this.listenOn.querySelectorAll(this.selector);
|
|
540
|
-
|
|
541
|
-
return Array.prototype.filter.call(targets, (el) => {
|
|
542
|
-
return e.composedPath().indexOf(el) > -1;
|
|
543
|
-
})[0];
|
|
544
|
-
}
|
|
545
|
-
return e.target;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Opens the overlay.
|
|
550
|
-
* @param {!Event | undefined} e used as the context for the menu. Overlay coordinates are taken from this event.
|
|
551
|
-
*/
|
|
552
|
-
open(e) {
|
|
553
|
-
if (e && !this.opened) {
|
|
554
|
-
this._context = {
|
|
555
|
-
detail: e.detail,
|
|
556
|
-
target: this._contextTarget(e),
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
if (this._context.target) {
|
|
560
|
-
e.preventDefault();
|
|
561
|
-
e.stopPropagation();
|
|
562
|
-
|
|
563
|
-
// Used in alignment which is delayed until overlay is rendered
|
|
564
|
-
this.__x = this._getEventCoordinate(e, 'x');
|
|
565
|
-
this.__pageXOffset = window.pageXOffset;
|
|
566
|
-
|
|
567
|
-
this.__y = this._getEventCoordinate(e, 'y');
|
|
568
|
-
this.__pageYOffset = window.pageYOffset;
|
|
569
|
-
|
|
570
|
-
this.$.overlay.style.opacity = '0';
|
|
571
|
-
this._setOpened(true);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/** @private */
|
|
577
|
-
__onScroll() {
|
|
578
|
-
if (!this.opened) {
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const yDiff = window.pageYOffset - this.__pageYOffset;
|
|
583
|
-
const xDiff = window.pageXOffset - this.__pageXOffset;
|
|
584
|
-
|
|
585
|
-
this.__adjustPosition('left', -xDiff);
|
|
586
|
-
this.__adjustPosition('right', xDiff);
|
|
587
|
-
|
|
588
|
-
this.__adjustPosition('top', -yDiff);
|
|
589
|
-
this.__adjustPosition('bottom', yDiff);
|
|
590
|
-
|
|
591
|
-
this.__pageYOffset += yDiff;
|
|
592
|
-
this.__pageXOffset += xDiff;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/** @private */
|
|
596
|
-
__adjustPosition(coord, diff) {
|
|
597
|
-
const overlay = this.$.overlay;
|
|
598
|
-
const style = overlay.style;
|
|
599
|
-
|
|
600
|
-
style[coord] = `${(parseInt(style[coord]) || 0) + diff}px`;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/** @private */
|
|
604
|
-
__alignOverlayPosition() {
|
|
605
|
-
const overlay = this.$.overlay;
|
|
606
|
-
|
|
607
|
-
if (overlay.positionTarget) {
|
|
608
|
-
// The overlay is positioned relative to another node, for example, a
|
|
609
|
-
// menu item in a nested submenu structure where this overlay lists
|
|
610
|
-
// the items for another submenu.
|
|
611
|
-
// It means that the overlay positioning is controlled by
|
|
612
|
-
// vaadin-overlay-position-mixin so no manual alignment is needed.
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const style = overlay.style;
|
|
617
|
-
|
|
618
|
-
// Reset all properties before measuring
|
|
619
|
-
['top', 'right', 'bottom', 'left'].forEach((prop) => style.removeProperty(prop));
|
|
620
|
-
['right-aligned', 'end-aligned', 'bottom-aligned'].forEach((attr) => overlay.removeAttribute(attr));
|
|
621
|
-
|
|
622
|
-
// Maximum x and y values are imposed by content size and overlay limits.
|
|
623
|
-
const { xMax, xMin, yMax } = overlay.getBoundaries();
|
|
624
|
-
// Reuse saved x and y event values, in order to this method be used async
|
|
625
|
-
// in the `vaadin-overlay-change` which guarantees that overlay is ready.
|
|
626
|
-
// The valus represent an anchor position on the page where the contextmenu
|
|
627
|
-
// event took place.
|
|
628
|
-
const x = this.__x;
|
|
629
|
-
const y = this.__y;
|
|
630
|
-
|
|
631
|
-
// Select one overlay corner and move to the event x/y position.
|
|
632
|
-
// Then set styling attrs for flex-aligning the content appropriately.
|
|
633
|
-
const wdthVport = document.documentElement.clientWidth;
|
|
634
|
-
const hghtVport = document.documentElement.clientHeight;
|
|
635
|
-
|
|
636
|
-
if (!this.__isRTL) {
|
|
637
|
-
if (x < wdthVport / 2 || x < xMax) {
|
|
638
|
-
// Menu is displayed in the right side of the anchor
|
|
639
|
-
style.left = `${x}px`;
|
|
640
|
-
} else {
|
|
641
|
-
// Menu is displayed in the left side of the anchor
|
|
642
|
-
style.right = `${Math.max(0, wdthVport - x)}px`;
|
|
643
|
-
this._setEndAligned(overlay);
|
|
644
|
-
}
|
|
645
|
-
} else if (x > wdthVport / 2 || x > xMin) {
|
|
646
|
-
// Menu is displayed in the right side of the anchor
|
|
647
|
-
style.right = `${Math.max(0, wdthVport - x)}px`;
|
|
648
|
-
} else {
|
|
649
|
-
// Menu is displayed in the left side of the anchor
|
|
650
|
-
style.left = `${x}px`;
|
|
651
|
-
this._setEndAligned(overlay);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
if (y < hghtVport / 2 || y < yMax) {
|
|
655
|
-
style.top = `${y}px`;
|
|
656
|
-
} else {
|
|
657
|
-
style.bottom = `${Math.max(0, hghtVport - y)}px`;
|
|
658
|
-
overlay.setAttribute('bottom-aligned', '');
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** @private */
|
|
663
|
-
_setEndAligned(element) {
|
|
664
|
-
element.setAttribute('end-aligned', '');
|
|
665
|
-
if (!this.__isRTL) {
|
|
666
|
-
element.setAttribute('right-aligned', '');
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/** @private */
|
|
671
|
-
_getEventCoordinate(event, coord) {
|
|
672
|
-
if (event.detail instanceof Object) {
|
|
673
|
-
if (event.detail[coord]) {
|
|
674
|
-
// Polymer gesture events, get coordinate from detail
|
|
675
|
-
return event.detail[coord];
|
|
676
|
-
} else if (event.detail.sourceEvent) {
|
|
677
|
-
// Unwrap detailed event
|
|
678
|
-
return this._getEventCoordinate(event.detail.sourceEvent, coord);
|
|
679
|
-
}
|
|
680
|
-
} else {
|
|
681
|
-
const prop = `client${coord.toUpperCase()}`;
|
|
682
|
-
const position = event.changedTouches ? event.changedTouches[0][prop] : event[prop];
|
|
683
|
-
|
|
684
|
-
if (position === 0) {
|
|
685
|
-
// Native keyboard event
|
|
686
|
-
const rect = event.target.getBoundingClientRect();
|
|
687
|
-
return coord === 'x' ? rect.left : rect.top + rect.height;
|
|
688
|
-
}
|
|
689
|
-
// Native mouse or touch event
|
|
690
|
-
return position;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
/** @private */
|
|
695
|
-
_listen(node, evType, handler) {
|
|
696
|
-
if (gestures[evType]) {
|
|
697
|
-
addListener(node, evType, handler);
|
|
698
|
-
} else {
|
|
699
|
-
node.addEventListener(evType, handler);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/** @private */
|
|
704
|
-
_unlisten(node, evType, handler) {
|
|
705
|
-
if (gestures[evType]) {
|
|
706
|
-
removeListener(node, evType, handler);
|
|
707
|
-
} else {
|
|
708
|
-
node.removeEventListener(evType, handler);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/** @private */
|
|
713
|
-
_onGlobalContextMenu(e) {
|
|
714
|
-
if (!e.shiftKey) {
|
|
715
|
-
e.preventDefault();
|
|
716
|
-
this.close();
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
250
|
/**
|
|
721
251
|
* Fired when an item is selected when the context menu is populated using the `items` API.
|
|
722
252
|
*
|
|
@@ -726,5 +256,5 @@ class ContextMenu extends OverlayClassMixin(
|
|
|
726
256
|
*/
|
|
727
257
|
}
|
|
728
258
|
|
|
729
|
-
|
|
259
|
+
defineCustomElement(ContextMenu);
|
|
730
260
|
export { ContextMenu };
|
|
@@ -12,6 +12,7 @@ export interface ContextMenuItem {
|
|
|
12
12
|
component?: HTMLElement | string;
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
checked?: boolean;
|
|
15
|
+
keepOpen?: boolean;
|
|
15
16
|
theme?: string[] | string;
|
|
16
17
|
children?: ContextMenuItem[];
|
|
17
18
|
}
|
|
@@ -32,7 +33,7 @@ export declare class ItemsMixinClass {
|
|
|
32
33
|
* contextMenu.items = [
|
|
33
34
|
* { text: 'Menu Item 1', theme: 'primary', children:
|
|
34
35
|
* [
|
|
35
|
-
* { text: 'Menu Item 1-1', checked: true },
|
|
36
|
+
* { text: 'Menu Item 1-1', checked: true, keepOpen: true },
|
|
36
37
|
* { text: 'Menu Item 1-2' }
|
|
37
38
|
* ]
|
|
38
39
|
* },
|