@vaadin/popover 24.5.0-alpha1

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.
@@ -0,0 +1,739 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2024 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import './vaadin-popover-overlay.js';
7
+ import { html, LitElement } from 'lit';
8
+ import { ifDefined } from 'lit/directives/if-defined.js';
9
+ import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
10
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
11
+ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
12
+ import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
13
+ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
14
+ import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
15
+ import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
16
+ import { PopoverPositionMixin } from './vaadin-popover-position-mixin.js';
17
+ import { PopoverTargetMixin } from './vaadin-popover-target-mixin.js';
18
+
19
+ /**
20
+ * Controller for handling popover opened state.
21
+ */
22
+ class PopoverOpenedStateController {
23
+ constructor(host) {
24
+ this.host = host;
25
+ }
26
+
27
+ /**
28
+ * Whether closing is currently in progress.
29
+ * @return {boolean}
30
+ */
31
+ get isClosing() {
32
+ return this.__closeTimeout != null;
33
+ }
34
+
35
+ /** @private */
36
+ get __focusDelay() {
37
+ return this.host.focusDelay || 0;
38
+ }
39
+
40
+ /** @private */
41
+ get __hoverDelay() {
42
+ return this.host.hoverDelay || 0;
43
+ }
44
+
45
+ /** @private */
46
+ get __hideDelay() {
47
+ return this.host.hideDelay || 0;
48
+ }
49
+
50
+ /**
51
+ * Schedule opening the popover.
52
+ * @param {Object} options
53
+ */
54
+ open(options = { immediate: false }) {
55
+ const { immediate, trigger } = options;
56
+ const shouldDelayHover = trigger === 'hover' && this.__hoverDelay > 0;
57
+ const shouldDelayFocus = trigger === 'focus' && this.__focusDelay > 0;
58
+
59
+ if (!immediate && (shouldDelayHover || shouldDelayFocus) && !this.__closeTimeout) {
60
+ this.__scheduleOpen(trigger);
61
+ } else {
62
+ this.__showPopover();
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Schedule closing the popover.
68
+ * @param {boolean} immediate
69
+ */
70
+ close(immediate) {
71
+ if (!immediate && this.__hideDelay > 0) {
72
+ this.__scheduleClose();
73
+ } else {
74
+ this.__abortClose();
75
+ this.__setOpened(false);
76
+ }
77
+ }
78
+
79
+ /** @private */
80
+ __setOpened(opened) {
81
+ this.host.opened = opened;
82
+ }
83
+
84
+ /** @private */
85
+ __showPopover() {
86
+ this.__abortClose();
87
+ this.__setOpened(true);
88
+ }
89
+
90
+ /** @private */
91
+ __abortClose() {
92
+ if (this.__closeTimeout) {
93
+ clearTimeout(this.__closeTimeout);
94
+ this.__closeTimeout = null;
95
+ }
96
+ }
97
+
98
+ /** @private */
99
+ __abortOpen() {
100
+ if (this.__openTimeout) {
101
+ clearTimeout(this.__openTimeout);
102
+ this.__openTimeout = null;
103
+ }
104
+ }
105
+
106
+ /** @private */
107
+ __scheduleClose() {
108
+ this.__closeTimeout = setTimeout(() => {
109
+ this.__closeTimeout = null;
110
+ this.__setOpened(false);
111
+ }, this.__hideDelay);
112
+ }
113
+
114
+ /** @private */
115
+ __scheduleOpen(trigger) {
116
+ this.__abortOpen();
117
+
118
+ const delay = trigger === 'focus' ? this.__focusDelay : this.__hoverDelay;
119
+ this.__openTimeout = setTimeout(() => {
120
+ this.__openTimeout = null;
121
+ this.__showPopover();
122
+ }, delay);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * `<vaadin-popover>` is a Web Component for creating overlays
128
+ * that are positioned next to specified DOM element (target).
129
+ *
130
+ * Unlike `<vaadin-tooltip>`, the popover supports rich content
131
+ * that can be provided by using `renderer` function.
132
+ *
133
+ * @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
134
+ *
135
+ * @customElement
136
+ * @extends HTMLElement
137
+ * @mixes ElementMixin
138
+ * @mixes ElementMixin
139
+ * @mixes PopoverPositionMixin
140
+ * @mixes PopoverTargetMixin
141
+ * @mixes ThemePropertyMixin
142
+ */
143
+ class Popover extends PopoverPositionMixin(
144
+ PopoverTargetMixin(OverlayClassMixin(ThemePropertyMixin(ElementMixin(PolylitMixin(LitElement))))),
145
+ ) {
146
+ static get is() {
147
+ return 'vaadin-popover';
148
+ }
149
+
150
+ static get properties() {
151
+ return {
152
+ /**
153
+ * String used to label the overlay to screen reader users.
154
+ *
155
+ * @attr {string} accessible-name
156
+ */
157
+ accessibleName: {
158
+ type: String,
159
+ },
160
+
161
+ /**
162
+ * Id of the element used as label of the overlay to screen reader users.
163
+ *
164
+ * @attr {string} accessible-name-ref
165
+ */
166
+ accessibleNameRef: {
167
+ type: String,
168
+ },
169
+
170
+ /**
171
+ * Height to be set on the overlay content.
172
+ *
173
+ * @attr {string} content-height
174
+ */
175
+ contentHeight: {
176
+ type: String,
177
+ },
178
+
179
+ /**
180
+ * Width to be set on the overlay content.
181
+ *
182
+ * @attr {string} content-width
183
+ */
184
+ contentWidth: {
185
+ type: String,
186
+ },
187
+
188
+ /**
189
+ * The delay in milliseconds before the popover is opened
190
+ * on focus when the corresponding trigger is used.
191
+ * @attr {number} focus-delay
192
+ */
193
+ focusDelay: {
194
+ type: Number,
195
+ },
196
+
197
+ /**
198
+ * The delay in milliseconds before the popover is closed
199
+ * on losing hover, when the corresponding trigger is used.
200
+ * On blur, the popover is closed immediately.
201
+ * @attr {number} hide-delay
202
+ */
203
+ hideDelay: {
204
+ type: Number,
205
+ },
206
+
207
+ /**
208
+ * The delay in milliseconds before the popover is opened
209
+ * on hover when the corresponding trigger is used.
210
+ * @attr {number} hover-delay
211
+ */
212
+ hoverDelay: {
213
+ type: Number,
214
+ },
215
+
216
+ /**
217
+ * True if the popover overlay is opened, false otherwise.
218
+ */
219
+ opened: {
220
+ type: Boolean,
221
+ value: false,
222
+ notify: true,
223
+ observer: '__openedChanged',
224
+ },
225
+
226
+ /**
227
+ * The `role` attribute value to be set on the overlay.
228
+ *
229
+ * @attr {string} overlay-role
230
+ */
231
+ overlayRole: {
232
+ type: String,
233
+ value: 'dialog',
234
+ },
235
+
236
+ /**
237
+ * Custom function for rendering the content of the overlay.
238
+ * Receives two arguments:
239
+ *
240
+ * - `root` The root container DOM element. Append your content to it.
241
+ * - `popover` The reference to the `vaadin-popover` element (overlay host).
242
+ */
243
+ renderer: {
244
+ type: Object,
245
+ },
246
+
247
+ /**
248
+ * When true, the popover prevents interacting with background elements
249
+ * by setting `pointer-events` style on the document body to `none`.
250
+ * This also enables trapping focus inside the overlay.
251
+ */
252
+ modal: {
253
+ type: Boolean,
254
+ value: false,
255
+ },
256
+
257
+ /**
258
+ * Set to true to disable closing popover overlay on outside click.
259
+ *
260
+ * @attr {boolean} no-close-on-outside-click
261
+ */
262
+ noCloseOnOutsideClick: {
263
+ type: Boolean,
264
+ value: false,
265
+ },
266
+
267
+ /**
268
+ * Set to true to disable closing popover overlay on Escape press.
269
+ * When the popover is modal, pressing Escape anywhere in the
270
+ * document closes the overlay. Otherwise, only Escape press
271
+ * from the popover itself or its target closes the overlay.
272
+ *
273
+ * @attr {boolean} no-close-on-esc
274
+ */
275
+ noCloseOnEsc: {
276
+ type: Boolean,
277
+ value: false,
278
+ },
279
+
280
+ /**
281
+ * Popover trigger mode, used to configure how the overlay is opened or closed.
282
+ * Could be set to multiple by providing an array, e.g. `trigger = ['hover', 'focus']`.
283
+ *
284
+ * Supported values:
285
+ * - `click` (default) - opens and closes on target click.
286
+ * - `hover` - opens on target mouseenter, closes on target mouseleave. Moving mouse
287
+ * to the popover overlay content keeps the overlay opened.
288
+ * - `focus` - opens on target focus, closes on target blur. Moving focus to the
289
+ * popover overlay content keeps the overlay opened.
290
+ *
291
+ * In addition to the behavior specified by `trigger`, the popover can be closed by:
292
+ * - pressing Escape key (unless `noCloseOnEsc` property is true)
293
+ * - outside click (unless `noCloseOnOutsideClick` property is true)
294
+ *
295
+ * When setting `trigger` property to `null`, `undefined` or empty array, the popover
296
+ * can be only opened or closed programmatically by changing `opened` property.
297
+ */
298
+ trigger: {
299
+ type: Array,
300
+ value: () => ['click'],
301
+ },
302
+
303
+ /**
304
+ * When true, the overlay has a backdrop (modality curtain) on top of the
305
+ * underlying page content, covering the whole viewport.
306
+ *
307
+ * @attr {boolean} with-backdrop
308
+ */
309
+ withBackdrop: {
310
+ type: Boolean,
311
+ value: false,
312
+ },
313
+
314
+ /** @private */
315
+ __shouldRestoreFocus: {
316
+ type: Boolean,
317
+ value: false,
318
+ sync: true,
319
+ },
320
+
321
+ /** @private */
322
+ __overlayId: {
323
+ type: String,
324
+ },
325
+ };
326
+ }
327
+
328
+ static get observers() {
329
+ return [
330
+ '__updateContentHeight(contentHeight, _overlayElement)',
331
+ '__updateContentWidth(contentWidth, _overlayElement)',
332
+ '__openedOrTargetChanged(opened, target)',
333
+ '__overlayRoleOrTargetChanged(overlayRole, target)',
334
+ ];
335
+ }
336
+
337
+ constructor() {
338
+ super();
339
+
340
+ this.__overlayId = `vaadin-popover-${generateUniqueId()}`;
341
+
342
+ this.__onGlobalClick = this.__onGlobalClick.bind(this);
343
+ this.__onGlobalKeyDown = this.__onGlobalKeyDown.bind(this);
344
+ this.__onTargetClick = this.__onTargetClick.bind(this);
345
+ this.__onTargetKeydown = this.__onTargetKeydown.bind(this);
346
+ this.__onTargetFocusIn = this.__onTargetFocusIn.bind(this);
347
+ this.__onTargetFocusOut = this.__onTargetFocusOut.bind(this);
348
+ this.__onTargetMouseEnter = this.__onTargetMouseEnter.bind(this);
349
+ this.__onTargetMouseLeave = this.__onTargetMouseLeave.bind(this);
350
+
351
+ this._openedStateController = new PopoverOpenedStateController(this);
352
+ }
353
+
354
+ /** @protected */
355
+ render() {
356
+ const effectivePosition = this.__effectivePosition;
357
+
358
+ return html`
359
+ <vaadin-popover-overlay
360
+ id="${this.__overlayId}"
361
+ role="${this.overlayRole}"
362
+ aria-label="${ifDefined(this.accessibleName)}"
363
+ aria-labelledby="${ifDefined(this.accessibleNameRef)}"
364
+ .renderer="${this.renderer}"
365
+ .owner="${this}"
366
+ theme="${ifDefined(this._theme)}"
367
+ .positionTarget="${this.target}"
368
+ .position="${effectivePosition}"
369
+ .opened="${this.opened}"
370
+ .modeless="${!this.modal}"
371
+ .focusTrap="${this.modal}"
372
+ .withBackdrop="${this.withBackdrop}"
373
+ ?no-horizontal-overlap="${this.__computeNoHorizontalOverlap(effectivePosition)}"
374
+ ?no-vertical-overlap="${this.__computeNoVerticalOverlap(effectivePosition)}"
375
+ .horizontalAlign="${this.__computeHorizontalAlign(effectivePosition)}"
376
+ .verticalAlign="${this.__computeVerticalAlign(effectivePosition)}"
377
+ @mouseenter="${this.__onOverlayMouseEnter}"
378
+ @mouseleave="${this.__onOverlayMouseLeave}"
379
+ @focusin="${this.__onOverlayFocusIn}"
380
+ @focusout="${this.__onOverlayFocusOut}"
381
+ @opened-changed="${this.__onOpenedChanged}"
382
+ .restoreFocusOnClose="${this.__shouldRestoreFocus}"
383
+ .restoreFocusNode="${this.target}"
384
+ @vaadin-overlay-escape-press="${this.__onEscapePress}"
385
+ @vaadin-overlay-outside-click="${this.__onOutsideClick}"
386
+ @vaadin-overlay-closed="${this.__onOverlayClosed}"
387
+ ></vaadin-popover-overlay>
388
+ `;
389
+ }
390
+
391
+ /**
392
+ * Requests an update for the content of the popover.
393
+ * While performing the update, it invokes the renderer passed in the `renderer` property.
394
+ *
395
+ * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
396
+ */
397
+ requestContentUpdate() {
398
+ if (!this.renderer || !this._overlayElement) {
399
+ return;
400
+ }
401
+
402
+ this._overlayElement.requestContentUpdate();
403
+ }
404
+
405
+ /** @protected */
406
+ ready() {
407
+ super.ready();
408
+
409
+ this._overlayElement = this.shadowRoot.querySelector('vaadin-popover-overlay');
410
+ }
411
+
412
+ /** @protected */
413
+ connectedCallback() {
414
+ super.connectedCallback();
415
+
416
+ document.addEventListener('click', this.__onGlobalClick, true);
417
+ }
418
+
419
+ /** @protected */
420
+ disconnectedCallback() {
421
+ super.disconnectedCallback();
422
+
423
+ document.removeEventListener('click', this.__onGlobalClick, true);
424
+
425
+ this._openedStateController.close(true);
426
+ }
427
+
428
+ /**
429
+ * @param {HTMLElement} target
430
+ * @protected
431
+ * @override
432
+ */
433
+ _addTargetListeners(target) {
434
+ target.addEventListener('click', this.__onTargetClick);
435
+ target.addEventListener('keydown', this.__onTargetKeydown);
436
+ target.addEventListener('mouseenter', this.__onTargetMouseEnter);
437
+ target.addEventListener('mouseleave', this.__onTargetMouseLeave);
438
+ target.addEventListener('focusin', this.__onTargetFocusIn);
439
+ target.addEventListener('focusout', this.__onTargetFocusOut);
440
+ }
441
+
442
+ /**
443
+ * @param {HTMLElement} target
444
+ * @protected
445
+ * @override
446
+ */
447
+ _removeTargetListeners(target) {
448
+ target.removeEventListener('click', this.__onTargetClick);
449
+ target.removeEventListener('keydown', this.__onTargetKeydown);
450
+ target.removeEventListener('mouseenter', this.__onTargetMouseEnter);
451
+ target.removeEventListener('mouseleave', this.__onTargetMouseLeave);
452
+ target.removeEventListener('focusin', this.__onTargetFocusIn);
453
+ target.removeEventListener('focusout', this.__onTargetFocusOut);
454
+ }
455
+
456
+ /** @private */
457
+ __openedChanged(opened, oldOpened) {
458
+ if (opened) {
459
+ document.addEventListener('keydown', this.__onGlobalKeyDown, true);
460
+ } else if (oldOpened) {
461
+ document.removeEventListener('keydown', this.__onGlobalKeyDown, true);
462
+ }
463
+ }
464
+
465
+ /** @private */
466
+ __openedOrTargetChanged(opened, target) {
467
+ if (target) {
468
+ target.setAttribute('aria-expanded', opened ? 'true' : 'false');
469
+
470
+ if (opened) {
471
+ target.setAttribute('aria-controls', this.__overlayId);
472
+ } else {
473
+ target.removeAttribute('aria-controls');
474
+ }
475
+ }
476
+ }
477
+
478
+ /** @private */
479
+ __overlayRoleOrTargetChanged(overlayRole, target) {
480
+ if (this.__oldTarget) {
481
+ this.__oldTarget.removeAttribute('aria-haspopup');
482
+ }
483
+
484
+ if (target) {
485
+ const isDialog = overlayRole === 'dialog' || overlayRole === 'alertdialog';
486
+ target.setAttribute('aria-haspopup', isDialog ? 'dialog' : 'true');
487
+
488
+ this.__oldTarget = target;
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Overlay's global outside click listener doesn't work when
494
+ * the overlay is modeless, so we use a separate listener.
495
+ * @private
496
+ */
497
+ __onGlobalClick(event) {
498
+ if (
499
+ this.opened &&
500
+ !this.__isManual &&
501
+ !this.modal &&
502
+ !event.composedPath().some((el) => el === this._overlayElement || el === this.target) &&
503
+ !this.noCloseOnOutsideClick
504
+ ) {
505
+ this._openedStateController.close(true);
506
+ }
507
+ }
508
+
509
+ /** @private */
510
+ __onTargetClick() {
511
+ if (this.__hasTrigger('click')) {
512
+ if (!this.opened) {
513
+ this.__shouldRestoreFocus = true;
514
+ }
515
+ if (this.opened) {
516
+ this._openedStateController.close(true);
517
+ } else {
518
+ this._openedStateController.open({ immediate: true });
519
+ }
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Overlay's global Escape press listener doesn't work when
525
+ * the overlay is modeless, so we use a separate listener.
526
+ * @private
527
+ */
528
+ __onGlobalKeyDown(event) {
529
+ if (event.key === 'Escape' && !this.modal && !this.noCloseOnEsc && this.opened && !this.__isManual) {
530
+ // Prevent closing parent overlay (e.g. dialog)
531
+ event.stopPropagation();
532
+ this._openedStateController.close(true);
533
+ }
534
+ }
535
+
536
+ /** @private */
537
+ __onTargetKeydown(event) {
538
+ // Prevent restoring focus after target blur on Tab key
539
+ if (event.key === 'Tab' && this.__shouldRestoreFocus) {
540
+ this.__shouldRestoreFocus = false;
541
+ }
542
+ }
543
+
544
+ /** @private */
545
+ __onTargetFocusIn() {
546
+ this.__focusInside = true;
547
+
548
+ if (this.__hasTrigger('focus')) {
549
+ // When trigger is set to both focus and click, only open on
550
+ // keyboard focus, to prevent issue when immediately closing
551
+ // on click which occurs after the focus caused by mousedown.
552
+ if (this.__hasTrigger('click') && !isKeyboardActive()) {
553
+ return;
554
+ }
555
+
556
+ // Prevent overlay re-opening when restoring focus on close.
557
+ if (!this.__shouldRestoreFocus) {
558
+ this.__shouldRestoreFocus = true;
559
+ this._openedStateController.open({ trigger: 'focus' });
560
+ }
561
+ }
562
+ }
563
+
564
+ /** @private */
565
+ __onTargetFocusOut(event) {
566
+ if (this._overlayElement.contains(event.relatedTarget)) {
567
+ return;
568
+ }
569
+
570
+ this.__handleFocusout();
571
+ }
572
+
573
+ /** @private */
574
+ __onTargetMouseEnter() {
575
+ this.__hoverInside = true;
576
+
577
+ if (this.__hasTrigger('hover') && !this.opened) {
578
+ // Prevent closing due to `pointer-events: none` set on body.
579
+ if (this.modal) {
580
+ this.target.style.pointerEvents = 'auto';
581
+ }
582
+ this._openedStateController.open({ trigger: 'hover' });
583
+ }
584
+ }
585
+
586
+ /** @private */
587
+ __onTargetMouseLeave(event) {
588
+ if (this._overlayElement.contains(event.relatedTarget)) {
589
+ return;
590
+ }
591
+
592
+ this.__handleMouseLeave();
593
+ }
594
+
595
+ /** @private */
596
+ __onOverlayFocusIn() {
597
+ this.__focusInside = true;
598
+
599
+ // When using Tab to move focus, restoring focus is reset. However, if pressing Tab
600
+ // causes focus to be moved inside the overlay, we should restore focus on close.
601
+ if (this.__hasTrigger('focus') || this.__hasTrigger('click')) {
602
+ this.__shouldRestoreFocus = true;
603
+ }
604
+ }
605
+
606
+ /** @private */
607
+ __onOverlayFocusOut(event) {
608
+ if (event.relatedTarget === this.target || this._overlayElement.contains(event.relatedTarget)) {
609
+ return;
610
+ }
611
+
612
+ this.__handleFocusout();
613
+ }
614
+
615
+ /** @private */
616
+ __onOverlayMouseEnter() {
617
+ this.__hoverInside = true;
618
+
619
+ // Prevent closing if cursor moves to the overlay during hide delay.
620
+ if (this.__hasTrigger('hover') && this._openedStateController.isClosing) {
621
+ this._openedStateController.open({ immediate: true });
622
+ }
623
+ }
624
+
625
+ /** @private */
626
+ __onOverlayMouseLeave(event) {
627
+ if (event.relatedTarget === this.target) {
628
+ return;
629
+ }
630
+
631
+ this.__handleMouseLeave();
632
+ }
633
+
634
+ /** @private */
635
+ __handleFocusout() {
636
+ this.__focusInside = false;
637
+
638
+ if (this.__hasTrigger('hover') && this.__hoverInside) {
639
+ return;
640
+ }
641
+
642
+ if (this.__hasTrigger('focus')) {
643
+ this._openedStateController.close(true);
644
+ }
645
+ }
646
+
647
+ /** @private */
648
+ __handleMouseLeave() {
649
+ this.__hoverInside = false;
650
+
651
+ if (this.__hasTrigger('focus') && this.__focusInside) {
652
+ return;
653
+ }
654
+
655
+ if (this.__hasTrigger('hover')) {
656
+ this._openedStateController.close();
657
+ }
658
+ }
659
+
660
+ /** @private */
661
+ __onOpenedChanged(event) {
662
+ this.opened = event.detail.value;
663
+ }
664
+
665
+ /** @private */
666
+ __onOverlayClosed() {
667
+ // Reset restoring focus state after a timeout to make sure focus was restored
668
+ // and then allow re-opening overlay on re-focusing target with focus trigger.
669
+ if (this.__shouldRestoreFocus) {
670
+ setTimeout(() => {
671
+ this.__shouldRestoreFocus = false;
672
+ });
673
+ }
674
+
675
+ // Restore pointer-events set when opening on hover.
676
+ if (this.modal && this.target.style.pointerEvents) {
677
+ this.target.style.pointerEvents = '';
678
+ }
679
+ }
680
+
681
+ /**
682
+ * Close the popover if `noCloseOnEsc` isn't set to true.
683
+ * @private
684
+ */
685
+ __onEscapePress(e) {
686
+ if (this.noCloseOnEsc || this.__isManual) {
687
+ e.preventDefault();
688
+ }
689
+ }
690
+
691
+ /**
692
+ * Close the popover if `noCloseOnOutsideClick` isn't set to true.
693
+ * @private
694
+ */
695
+ __onOutsideClick(e) {
696
+ if (this.noCloseOnOutsideClick || this.__isManual) {
697
+ e.preventDefault();
698
+ }
699
+ }
700
+
701
+ /** @private */
702
+ __hasTrigger(trigger) {
703
+ return Array.isArray(this.trigger) && this.trigger.includes(trigger);
704
+ }
705
+
706
+ /** @private */
707
+ get __isManual() {
708
+ return this.trigger == null || (Array.isArray(this.trigger) && this.trigger.length === 0);
709
+ }
710
+
711
+ /** @private */
712
+ __updateDimension(overlay, dimension, value) {
713
+ const prop = `--_vaadin-popover-content-${dimension}`;
714
+
715
+ if (value) {
716
+ overlay.style.setProperty(prop, value);
717
+ } else {
718
+ overlay.style.removeProperty(prop);
719
+ }
720
+ }
721
+
722
+ /** @private */
723
+ __updateContentHeight(height, overlay) {
724
+ if (overlay) {
725
+ this.__updateDimension(overlay, 'height', height);
726
+ }
727
+ }
728
+
729
+ /** @private */
730
+ __updateContentWidth(width, overlay) {
731
+ if (overlay) {
732
+ this.__updateDimension(overlay, 'width', width);
733
+ }
734
+ }
735
+ }
736
+
737
+ defineCustomElement(Popover);
738
+
739
+ export { Popover };