@momentum-design/components 0.100.0 → 0.100.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/browser/index.js +1440 -1210
  2. package/dist/browser/index.js.map +4 -4
  3. package/dist/components/accordion/accordion.component.d.ts +74 -0
  4. package/dist/components/accordion/accordion.component.js +128 -0
  5. package/dist/components/accordion/accordion.constants.d.ts +2 -0
  6. package/dist/components/accordion/accordion.constants.js +3 -0
  7. package/dist/components/accordion/accordion.styles.d.ts +2 -0
  8. package/dist/components/accordion/accordion.styles.js +17 -0
  9. package/dist/components/accordion/index.d.ts +10 -0
  10. package/dist/components/accordion/index.js +7 -0
  11. package/dist/components/accordionbutton/accordionbutton.component.d.ts +119 -0
  12. package/dist/components/accordionbutton/accordionbutton.component.js +238 -0
  13. package/dist/components/accordionbutton/accordionbutton.constants.d.ts +17 -0
  14. package/dist/components/accordionbutton/accordionbutton.constants.js +19 -0
  15. package/dist/components/accordionbutton/accordionbutton.styles.d.ts +2 -0
  16. package/dist/components/accordionbutton/accordionbutton.styles.js +121 -0
  17. package/dist/components/accordionbutton/accordionbutton.types.d.ts +8 -0
  18. package/dist/components/accordionbutton/accordionbutton.types.js +1 -0
  19. package/dist/components/accordionbutton/index.d.ts +10 -0
  20. package/dist/components/accordionbutton/index.js +7 -0
  21. package/dist/components/accordiongroup/accordiongroup.component.d.ts +71 -0
  22. package/dist/components/accordiongroup/accordiongroup.component.js +132 -0
  23. package/dist/components/accordiongroup/accordiongroup.constants.d.ts +15 -0
  24. package/dist/components/accordiongroup/accordiongroup.constants.js +16 -0
  25. package/dist/components/accordiongroup/accordiongroup.styles.d.ts +2 -0
  26. package/dist/components/accordiongroup/accordiongroup.styles.js +48 -0
  27. package/dist/components/accordiongroup/accordiongroup.types.d.ts +5 -0
  28. package/dist/components/accordiongroup/accordiongroup.types.js +1 -0
  29. package/dist/components/accordiongroup/index.d.ts +7 -0
  30. package/dist/components/accordiongroup/index.js +4 -0
  31. package/dist/components/animation/animation.component.d.ts +1 -0
  32. package/dist/components/animation/animation.component.js +9 -0
  33. package/dist/components/cardcheckbox/cardcheckbox.component.js +3 -3
  34. package/dist/components/cardradio/cardradio.component.js +3 -3
  35. package/dist/components/dialog/dialog.component.js +5 -0
  36. package/dist/components/formfieldwrapper/formfieldwrapper.styles.js +1 -0
  37. package/dist/components/icon/icon.component.d.ts +2 -1
  38. package/dist/components/icon/icon.component.js +9 -1
  39. package/dist/components/linksimple/linksimple.component.js +11 -11
  40. package/dist/components/listitem/listitem.component.js +6 -6
  41. package/dist/components/listitem/listitem.events.js +3 -1
  42. package/dist/components/menubar/menubar.component.d.ts +9 -0
  43. package/dist/components/menubar/menubar.component.js +38 -4
  44. package/dist/components/menuitem/menuitem.component.js +1 -1
  45. package/dist/components/menuitemcheckbox/menuitemcheckbox.component.js +12 -12
  46. package/dist/components/menuitemradio/menuitemradio.component.js +13 -13
  47. package/dist/components/menupopover/menupopover.component.d.ts +30 -12
  48. package/dist/components/menupopover/menupopover.component.js +231 -179
  49. package/dist/components/menupopover/menupopover.utils.d.ts +2 -2
  50. package/dist/components/menupopover/menupopover.utils.js +2 -2
  51. package/dist/components/navmenuitem/navmenuitem.component.js +1 -1
  52. package/dist/components/popover/popover.component.d.ts +29 -9
  53. package/dist/components/popover/popover.component.js +277 -212
  54. package/dist/components/popover/popover.constants.d.ts +1 -0
  55. package/dist/components/popover/popover.constants.js +1 -0
  56. package/dist/components/popover/popover.types.d.ts +19 -6
  57. package/dist/components/popover/popover.utils.d.ts +7 -2
  58. package/dist/components/popover/popover.utils.js +19 -8
  59. package/dist/components/select/select.component.js +2 -0
  60. package/dist/components/select/select.styles.js +1 -1
  61. package/dist/components/select/select.types.d.ts +5 -1
  62. package/dist/components/sidenavigation/sidenavigation.component.js +1 -1
  63. package/dist/components/spinner/spinner.component.d.ts +1 -1
  64. package/dist/components/spinner/spinner.component.js +1 -1
  65. package/dist/components/tablist/tablist.component.d.ts +1 -0
  66. package/dist/components/tablist/tablist.component.js +284 -273
  67. package/dist/components/textarea/textarea.component.d.ts +2 -2
  68. package/dist/components/textarea/textarea.component.js +2 -2
  69. package/dist/components/tooltip/tooltip.component.d.ts +1 -1
  70. package/dist/components/tooltip/tooltip.component.js +14 -15
  71. package/dist/custom-elements.json +7364 -6056
  72. package/dist/index.d.ts +10 -7
  73. package/dist/index.js +10 -7
  74. package/dist/react/accordion/index.d.ts +53 -0
  75. package/dist/react/accordion/index.js +61 -0
  76. package/dist/react/accordionbutton/index.d.ts +50 -0
  77. package/dist/react/accordionbutton/index.js +58 -0
  78. package/dist/react/accordiongroup/index.d.ts +30 -0
  79. package/dist/react/accordiongroup/index.js +39 -0
  80. package/dist/react/coachmark/index.d.ts +12 -4
  81. package/dist/react/index.d.ts +9 -6
  82. package/dist/react/index.js +9 -6
  83. package/dist/react/menupopover/index.d.ts +12 -4
  84. package/dist/react/popover/index.d.ts +12 -4
  85. package/dist/react/toggletip/index.d.ts +12 -4
  86. package/dist/react/tooltip/index.d.ts +12 -4
  87. package/dist/utils/keys.d.ts +1 -0
  88. package/dist/utils/keys.js +1 -0
  89. package/dist/utils/roles.d.ts +2 -0
  90. package/dist/utils/roles.js +2 -0
  91. package/package.json +2 -2
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ /* eslint-disable no-restricted-syntax */
10
11
  import { arrow, autoUpdate, computePosition, flip, offset, shift, size } from '@floating-ui/dom';
11
12
  import { html, nothing } from 'lit';
12
13
  import { property } from 'lit/decorators.js';
@@ -69,6 +70,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
69
70
  * - **mouseenter**
70
71
  * - **focusin**
71
72
  * - **manual**
73
+ *
72
74
  * @default click
73
75
  */
74
76
  this.trigger = DEFAULTS.TRIGGER;
@@ -249,6 +251,13 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
249
251
  * @default false
250
252
  */
251
253
  this.disableAriaHasPopup = DEFAULTS.DISABLE_ARIA_HAS_POPUP;
254
+ /**
255
+ * If a tooltip is connected to the same trigger element,
256
+ * this property will keep the connected tooltip closed if this popover is open.
257
+ * This is useful when you want to show a popover with a tooltip
258
+ * but you don't want the tooltip to be shown at the same time.
259
+ */
260
+ this.keepConnectedTooltipClosed = DEFAULTS.KEEP_CONNECTED_TOOLTIP_CLOSED;
252
261
  this.arrowElement = null;
253
262
  this.triggerElement = null;
254
263
  /** @internal */
@@ -261,13 +270,107 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
261
270
  /** @internal */
262
271
  this.isHovered = false;
263
272
  /** @internal */
264
- this.isTriggerClicked = false;
265
- /** @internal */
266
273
  this.openDelay = 0;
267
274
  /** @internal */
268
275
  this.closeDelay = 0;
269
276
  /** @internal */
277
+ this.floatingUICleanupFunction = null;
278
+ /** @internal */
279
+ this.shouldSupressOpening = false;
280
+ /** @internal */
270
281
  this.backdropElement = null;
282
+ /** @internal */
283
+ this.connectedTooltip = null;
284
+ this.storeConnectedTooltip = () => {
285
+ const connectedTooltips = this.getRootNode().querySelectorAll(`mdc-tooltip[triggerID="${this.triggerID}"]`);
286
+ for (const tooltip of connectedTooltips) {
287
+ if (tooltip !== this) {
288
+ this.connectedTooltip = tooltip;
289
+ return;
290
+ }
291
+ }
292
+ this.connectedTooltip = null;
293
+ };
294
+ this.cleanupTrigger = () => {
295
+ var _a;
296
+ const triggers = ((_a = this.trigger) === null || _a === void 0 ? void 0 : _a.split(' ')) || [];
297
+ const validTriggers = triggers.filter(trigger => Object.values(TRIGGER).includes(trigger));
298
+ let newTrigger = validTriggers.length > 0 ? this.trigger : DEFAULTS.TRIGGER;
299
+ if (newTrigger === 'mouseenter') {
300
+ if (this.interactive) {
301
+ // if the popover is interactive, there is interactive content inside the popover
302
+ // so we can't use the focusin trigger, since after closing with escape key, the
303
+ // popover keeps opening. So we need to use the click trigger instead.
304
+ newTrigger = 'mouseenter click';
305
+ }
306
+ else {
307
+ // non-interactive popovers with trigger mouseenter (like a tooltip) should also open
308
+ // when focusing to the trigger element
309
+ newTrigger = 'mouseenter focusin';
310
+ }
311
+ }
312
+ this.trigger = newTrigger;
313
+ };
314
+ /**
315
+ * Sets up the trigger related event listeners, based on the trigger type.
316
+ * Includes fallback for mouseenter trigger to also handle focusin for non-interactive popovers.
317
+ */
318
+ this.setupTriggerListeners = () => {
319
+ if (!this.triggerElement)
320
+ return;
321
+ if (this.trigger.includes('click')) {
322
+ this.triggerElement.addEventListener('click', this.togglePopoverVisible);
323
+ }
324
+ if (this.trigger.includes('mouseenter')) {
325
+ const hoverBridge = this.renderRoot.querySelector('.popover-hover-bridge');
326
+ hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.addEventListener('mouseenter', this.show);
327
+ this.triggerElement.addEventListener('mouseenter', this.handleMouseEnter);
328
+ this.triggerElement.addEventListener('mouseleave', this.handleMouseLeave);
329
+ this.addEventListener('mouseenter', this.cancelCloseDelay);
330
+ this.addEventListener('mouseleave', this.startCloseDelay);
331
+ }
332
+ if (this.trigger.includes('focusin')) {
333
+ this.triggerElement.addEventListener('focusin', this.show);
334
+ if (!this.interactive) {
335
+ this.triggerElement.addEventListener('focusout', this.handleFocusOut);
336
+ }
337
+ }
338
+ };
339
+ /**
340
+ * Removes the trigger related event listeners.
341
+ */
342
+ this.removeTriggerListeners = () => {
343
+ var _a, _b, _c, _d, _e;
344
+ // click trigger
345
+ (_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.removeEventListener('click', this.togglePopoverVisible);
346
+ // mouseenter trigger
347
+ const hoverBridge = this.renderRoot.querySelector('.popover-hover-bridge');
348
+ hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.removeEventListener('mouseenter', this.show);
349
+ (_b = this.triggerElement) === null || _b === void 0 ? void 0 : _b.removeEventListener('mouseenter', this.handleMouseEnter);
350
+ (_c = this.triggerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('mouseleave', this.handleMouseLeave);
351
+ this.removeEventListener('mouseenter', this.cancelCloseDelay);
352
+ this.removeEventListener('mouseleave', this.startCloseDelay);
353
+ // focusin trigger
354
+ (_d = this.triggerElement) === null || _d === void 0 ? void 0 : _d.removeEventListener('focusin', this.show);
355
+ (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.removeEventListener('focusout', this.handleFocusOut);
356
+ };
357
+ /**
358
+ * Removes all event listeners related to the popover.
359
+ */
360
+ this.removeAllListeners = () => {
361
+ var _a;
362
+ this.removeTriggerListeners();
363
+ if (this.hideOnOutsideClick) {
364
+ document.removeEventListener('click', this.onOutsidePopoverClick);
365
+ }
366
+ if (this.hideOnEscape) {
367
+ this.removeEventListener('keydown', this.onEscapeKeydown);
368
+ (_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.removeEventListener('keydown', this.onEscapeKeydown);
369
+ }
370
+ if (this.hideOnBlur) {
371
+ this.removeEventListener('focusout', this.onPopoverFocusOut);
372
+ }
373
+ };
271
374
  /**
272
375
  * Handles the outside click event to close the popover.
273
376
  *
@@ -276,13 +379,11 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
276
379
  this.onOutsidePopoverClick = (event) => {
277
380
  if (popoverStack.peek() !== this)
278
381
  return;
279
- let insidePopoverClick = false;
280
382
  const path = event.composedPath();
281
- insidePopoverClick =
282
- this.contains(event.target) || path.includes(this.triggerElement) || path.includes(this);
383
+ const insidePopoverClick = this.contains(event.target) || path.includes(this.triggerElement) || path.includes(this);
283
384
  const clickedOnBackdrop = this.backdropElement ? path.includes(this.backdropElement) : false;
284
385
  if (!insidePopoverClick || clickedOnBackdrop) {
285
- this.hidePopover();
386
+ this.hide();
286
387
  PopoverEventManager.onClickOutside(this);
287
388
  }
288
389
  };
@@ -302,7 +403,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
302
403
  event.stopPropagation();
303
404
  }
304
405
  event.preventDefault();
305
- this.hidePopover();
406
+ this.hide();
306
407
  PopoverEventManager.onEscapeKeyPressed(this);
307
408
  };
308
409
  /**
@@ -312,7 +413,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
312
413
  */
313
414
  this.onPopoverFocusOut = (event) => {
314
415
  if (!this.contains(event.relatedTarget)) {
315
- this.hidePopover();
416
+ this.hide();
316
417
  }
317
418
  };
318
419
  /**
@@ -321,7 +422,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
321
422
  */
322
423
  this.handleMouseEnter = () => {
323
424
  this.isHovered = true;
324
- this.showPopover();
425
+ this.show();
325
426
  };
326
427
  /**
327
428
  * Handles mouse leave event on the trigger element.
@@ -339,7 +440,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
339
440
  */
340
441
  this.handleFocusOut = () => {
341
442
  if (!this.isHovered) {
342
- this.hidePopover();
443
+ this.hide();
343
444
  }
344
445
  };
345
446
  /**
@@ -348,11 +449,9 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
348
449
  */
349
450
  this.startCloseDelay = () => {
350
451
  if (!this.interactive) {
351
- this.hidePopover();
452
+ this.hide();
352
453
  }
353
454
  else {
354
- if (this.isTriggerClicked)
355
- return;
356
455
  this.hoverTimer = window.setTimeout(() => {
357
456
  this.visible = false;
358
457
  }, this.closeDelay);
@@ -363,163 +462,184 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
363
462
  */
364
463
  this.cancelCloseDelay = () => {
365
464
  if (this.hoverTimer) {
366
- clearTimeout(this.hoverTimer);
465
+ window.clearTimeout(this.hoverTimer);
367
466
  this.hoverTimer = null;
368
467
  }
369
468
  };
370
469
  /**
371
470
  * Shows the popover.
372
471
  */
373
- this.showPopover = () => {
472
+ this.show = () => {
473
+ if (this.visible || this.shouldSupressOpening) {
474
+ return;
475
+ }
374
476
  this.cancelCloseDelay();
375
- setTimeout(() => {
477
+ if (this.openDelay > 0) {
478
+ setTimeout(() => {
479
+ this.visible = true;
480
+ }, this.openDelay);
481
+ }
482
+ else {
376
483
  this.visible = true;
377
- }, this.openDelay);
484
+ }
378
485
  };
379
486
  /**
380
487
  * Hides the popover.
381
488
  */
382
- this.hidePopover = () => {
489
+ this.hide = () => {
383
490
  if (this.closeDelay) {
384
491
  setTimeout(() => {
385
492
  this.visible = false;
386
- this.isTriggerClicked = false;
387
493
  }, this.closeDelay);
388
494
  }
389
495
  else {
390
496
  this.visible = false;
391
- this.isTriggerClicked = false;
392
497
  }
393
498
  };
394
499
  /**
395
500
  * Toggles the popover visibility.
396
501
  */
397
502
  this.togglePopoverVisible = () => {
398
- if (this.isTriggerClicked) {
399
- this.hidePopover();
503
+ if (this.visible) {
504
+ this.hide();
400
505
  }
401
506
  else {
402
- this.showPopover();
403
- this.isTriggerClicked = true;
507
+ this.show();
508
+ }
509
+ };
510
+ /**
511
+ * Positions the popover based on the trigger element.
512
+ * It also handles the flip, size and arrow placement.
513
+ * It uses the floating-ui/dom library to calculate the position.
514
+ */
515
+ this.positionPopover = () => {
516
+ if (!this.triggerElement)
517
+ return;
518
+ const middleware = [
519
+ shift({
520
+ boundary: !this.boundary || this.boundary === 'clippingAncestors'
521
+ ? 'clippingAncestors'
522
+ : Array.from(document.querySelectorAll(this.boundary)),
523
+ rootBoundary: this.boundaryRoot,
524
+ padding: this.boundaryPadding,
525
+ }),
526
+ ];
527
+ let popoverOffset = this.offset;
528
+ if (this.flip) {
529
+ middleware.push(flip());
530
+ }
531
+ if (this.size) {
532
+ // expose a CSS variable for the available height
533
+ // so that it can be used in other components styles
534
+ const setInternalAvailableHeight = (availableHeight) => {
535
+ this.style.setProperty('--mdc-popover-internal-available-height', `${availableHeight}px`);
536
+ };
537
+ const popoverContent = this.renderRoot.querySelector('[part="popover-content"]');
538
+ middleware.push(size({
539
+ apply({ availableHeight }) {
540
+ if (!popoverContent)
541
+ return;
542
+ Object.assign(popoverContent.style, {
543
+ maxHeight: `${availableHeight}px`,
544
+ overflowY: 'auto',
545
+ });
546
+ setInternalAvailableHeight(availableHeight);
547
+ },
548
+ padding: 50,
549
+ }));
550
+ }
551
+ if (this.showArrow) {
552
+ this.arrowElement = this.renderRoot.querySelector('.popover-arrow');
553
+ if (this.arrowElement) {
554
+ const arrowLen = this.arrowElement.offsetHeight;
555
+ const arrowOffset = Math.sqrt(2 * arrowLen ** 2) / 2;
556
+ popoverOffset += arrowOffset;
557
+ middleware.push(arrow({ element: this.arrowElement, padding: 12 }));
558
+ }
559
+ }
560
+ middleware.push(offset(popoverOffset));
561
+ this.floatingUICleanupFunction = autoUpdate(this.triggerElement, this, async () => {
562
+ if (!this.triggerElement)
563
+ return;
564
+ const { x, y, middlewareData, placement } = await computePosition(this.triggerElement, this, {
565
+ placement: this.placement,
566
+ middleware,
567
+ });
568
+ this.utils.updatePopoverStyle(x, y);
569
+ if (middlewareData.arrow && this.arrowElement) {
570
+ this.utils.updateArrowStyle(middlewareData.arrow, placement);
571
+ }
572
+ if (this.trigger.includes('mouseenter') && this.interactive) {
573
+ this.utils.setupHoverBridge(placement);
574
+ }
575
+ });
576
+ };
577
+ /**
578
+ * Finds the closest popover to the passed element in the DOM tree.
579
+ *
580
+ * Useful when need to find the parent popover in a nested popover scenario.
581
+ *
582
+ * @param element - The element to start searching from.
583
+ */
584
+ this.findClosestPopover = (element) => {
585
+ let el = element;
586
+ while (el && !(el instanceof Popover)) {
587
+ el = el.parentElement;
404
588
  }
589
+ return el;
405
590
  };
406
591
  this.utils = new PopoverUtils(this);
407
592
  }
593
+ setupTriggerRelatedElement() {
594
+ this.triggerElement = this.getRootNode().querySelector(`[id="${this.triggerID}"]`);
595
+ this.storeConnectedTooltip();
596
+ }
408
597
  async firstUpdated(changedProperties) {
409
- var _a, _b;
410
598
  super.firstUpdated(changedProperties);
411
599
  this.utils.setupAppendTo();
412
600
  [this.openDelay, this.closeDelay] = this.utils.setupDelay();
413
- this.setupTriggerListener();
414
- this.utils.setupAccessibility();
601
+ this.setupTriggerRelatedElement();
602
+ this.cleanupTrigger();
603
+ this.setupTriggerListeners();
415
604
  this.style.zIndex = `${this.zIndex}`;
416
605
  PopoverEventManager.onCreatedPopover(this);
417
- if (this.visible) {
418
- this.positionPopover();
419
- this.activatePreventScroll();
420
- // If the popover is visible on first update and focustrap is enabled, we need to activate the focus trap
421
- if (this.interactive && this.focusTrap) {
422
- // Wait for the first update to complete before setting focusable elements
423
- await this.updateComplete;
424
- (_a = this.activateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
425
- (_b = this.setInitialFocus) === null || _b === void 0 ? void 0 : _b.call(this);
426
- }
427
- }
428
606
  }
429
607
  async disconnectedCallback() {
430
- var _a;
608
+ var _a, _b;
431
609
  super.disconnectedCallback();
432
- this.removeEventListeners();
610
+ this.removeAllListeners();
433
611
  (_a = this.deactivateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
434
612
  this.deactivatePreventScroll();
435
- PopoverEventManager.onDestroyedPopover(this);
436
- popoverStack.remove(this);
437
- }
438
- /**
439
- * Sets up the trigger event listeners based on the trigger type.
440
- */
441
- setupTriggerListener() {
442
- if (!this.triggerID)
443
- return;
444
- this.triggerElement = this.getRootNode().querySelector(`[id="${this.triggerID}"]`);
445
- if (!this.triggerElement)
446
- return;
447
- if (this.trigger === 'mouseenter') {
448
- if (this.interactive) {
449
- // if the popover is interactive, there is interactive content inside the popover
450
- // so we can't use the focusin trigger, since after closing with escape key, the
451
- // popover keeps opening. So we need to use the click trigger instead.
452
- this.trigger = 'mouseenter click';
613
+ this.utils.removeBackdrop();
614
+ (_b = this.floatingUICleanupFunction) === null || _b === void 0 ? void 0 : _b.call(this);
615
+ // clean timer if there is one set:
616
+ this.cancelCloseDelay();
617
+ if (this.keepConnectedTooltipClosed) {
618
+ if (this.connectedTooltip) {
619
+ this.connectedTooltip.shouldSupressOpening = false;
453
620
  }
454
- else {
455
- // non-interactive popovers with trigger mouseenter (like a tooltip) should also open
456
- // when focusing to the trigger element
457
- this.trigger = 'mouseenter focusin';
458
- }
459
- }
460
- if (this.trigger.includes('click')) {
461
- this.triggerElement.addEventListener('click', this.togglePopoverVisible);
462
- }
463
- if (this.trigger.includes('mouseenter')) {
464
- const hoverBridge = this.renderRoot.querySelector('.popover-hover-bridge');
465
- this.triggerElement.addEventListener('mouseenter', this.handleMouseEnter);
466
- this.triggerElement.addEventListener('mouseleave', this.handleMouseLeave);
467
- this.addEventListener('mouseenter', this.cancelCloseDelay);
468
- this.addEventListener('mouseleave', this.startCloseDelay);
469
- hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.addEventListener('mouseenter', this.showPopover);
470
621
  }
471
- if (this.trigger.includes('focusin')) {
472
- this.triggerElement.addEventListener('focusin', this.showPopover);
473
- if (!this.interactive) {
474
- this.triggerElement.addEventListener('focusout', this.handleFocusOut);
475
- }
476
- }
477
- this.addEventListener('focus-trap-exit', this.hidePopover);
478
- }
479
- /**
480
- * Removes the trigger event listeners.
481
- */
482
- removeEventListeners() {
483
- if (!this.triggerElement)
484
- return;
485
- const hoverBridge = this.renderRoot.querySelector('.popover-hover-bridge');
486
- this.triggerElement.removeEventListener('click', this.togglePopoverVisible);
487
- this.triggerElement.removeEventListener('mouseenter', this.handleMouseEnter);
488
- this.triggerElement.removeEventListener('mouseleave', this.handleMouseLeave);
489
- this.removeEventListener('mouseenter', this.cancelCloseDelay);
490
- this.removeEventListener('mouseleave', this.startCloseDelay);
491
- this.triggerElement.removeEventListener('focusin', this.showPopover);
492
- this.triggerElement.removeEventListener('focusout', this.handleFocusOut);
493
- hoverBridge === null || hoverBridge === void 0 ? void 0 : hoverBridge.removeEventListener('mouseenter', this.showPopover);
494
- this.removeEventListener('focus-trap-exit', this.hidePopover);
622
+ PopoverEventManager.onDestroyedPopover(this);
623
+ popoverStack.remove(this);
495
624
  }
496
625
  async updated(changedProperties) {
497
626
  super.updated(changedProperties);
498
- // If the role is changed to an empty string, set it to null
499
- // to avoid setting an invalid role on the popover element.
500
- if (changedProperties.has('role')) {
501
- if (this.role === '') {
502
- this.role = null;
503
- }
504
- }
505
627
  if (changedProperties.has('visible')) {
506
628
  const oldValue = changedProperties.get('visible') || false;
507
629
  await this.isOpenUpdated(oldValue, this.visible);
508
630
  this.utils.updateAriaExpandedAttribute();
509
631
  }
632
+ if (changedProperties.has('trigger')) {
633
+ this.cleanupTrigger();
634
+ this.removeTriggerListeners();
635
+ this.setupTriggerListeners();
636
+ }
510
637
  if (changedProperties.has('placement')) {
511
638
  this.setAttribute('placement', Object.values(POPOVER_PLACEMENT).includes(this.placement) ? this.placement : DEFAULTS.PLACEMENT);
512
639
  }
513
640
  if (changedProperties.has('delay')) {
514
641
  [this.openDelay, this.closeDelay] = this.utils.setupDelay();
515
642
  }
516
- if (changedProperties.has('trigger')) {
517
- const triggers = this.trigger.split(' ');
518
- const validTriggers = triggers.filter(trigger => Object.values(TRIGGER).includes(trigger));
519
- this.setAttribute('trigger', validTriggers.length > 0 ? this.trigger : DEFAULTS.TRIGGER);
520
- this.removeEventListeners();
521
- this.setupTriggerListener();
522
- }
523
643
  if (changedProperties.has('color')) {
524
644
  this.setAttribute('color', Object.values(COLOR).includes(this.color) ? this.color : DEFAULTS.COLOR);
525
645
  }
@@ -532,7 +652,15 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
532
652
  if (changedProperties.has('interactive') ||
533
653
  changedProperties.has('aria-label') ||
534
654
  changedProperties.has('aria-labelledby')) {
535
- this.utils.setupAccessibility();
655
+ this.utils.updateAriaLabels();
656
+ }
657
+ if (changedProperties.has('role')) {
658
+ // If the role is changed to an empty string, set it to null
659
+ // to avoid setting an invalid role on the popover element.
660
+ if (this.role === '') {
661
+ this.role = null;
662
+ }
663
+ this.utils.updateAriaModal();
536
664
  }
537
665
  if (changedProperties.has('disableAriaExpanded')) {
538
666
  this.utils.updateAriaExpandedAttribute();
@@ -564,14 +692,22 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
564
692
  * @param newValue - The new value of the visible property.
565
693
  */
566
694
  async isOpenUpdated(oldValue, newValue) {
567
- var _a, _b, _c, _d, _e, _f, _g;
695
+ var _a, _b, _c, _d, _e;
568
696
  if (oldValue === newValue || !this.triggerElement) {
569
697
  return;
570
698
  }
571
- if (newValue) {
699
+ if (newValue && !this.shouldSupressOpening) {
572
700
  if (popoverStack.peek() !== this) {
573
701
  popoverStack.push(this);
574
702
  }
703
+ if (this.keepConnectedTooltipClosed) {
704
+ // If this popover gets visible and keepConnectedTooltipsClosed is true,
705
+ // we need to close the connected tooltip.
706
+ if (this.connectedTooltip) {
707
+ this.connectedTooltip.visible = false;
708
+ this.connectedTooltip.shouldSupressOpening = true;
709
+ }
710
+ }
575
711
  if (this.backdrop) {
576
712
  this.utils.createBackdrop();
577
713
  this.triggerElementOriginalStyle = {
@@ -588,33 +724,34 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
588
724
  this.triggerElement.style.pointerEvents = 'none';
589
725
  }
590
726
  }
591
- if (this.hideOnOutsideClick) {
592
- document.addEventListener('click', this.onOutsidePopoverClick);
593
- }
594
727
  if (this.hideOnEscape) {
595
728
  this.addEventListener('keydown', this.onEscapeKeydown);
596
729
  (_a = this.triggerElement) === null || _a === void 0 ? void 0 : _a.addEventListener('keydown', this.onEscapeKeydown);
597
730
  }
598
731
  this.activatePreventScroll();
599
- if (this.interactive && this.focusTrap) {
600
- // Wait for the update to complete before setting focusable elements
601
- await this.updateComplete;
602
- (_b = this.activateFocusTrap) === null || _b === void 0 ? void 0 : _b.call(this);
603
- (_c = this.setInitialFocus) === null || _c === void 0 ? void 0 : _c.call(this);
732
+ if (this.hideOnOutsideClick) {
733
+ document.addEventListener('click', this.onOutsidePopoverClick);
604
734
  }
735
+ // make sure popover is fully rendered before activating focus trap
736
+ setTimeout(() => {
737
+ var _a, _b;
738
+ if (this.interactive && this.focusTrap) {
739
+ (_a = this.activateFocusTrap) === null || _a === void 0 ? void 0 : _a.call(this);
740
+ (_b = this.setInitialFocus) === null || _b === void 0 ? void 0 : _b.call(this);
741
+ }
742
+ }, 0);
605
743
  PopoverEventManager.onShowPopover(this);
606
744
  }
607
745
  else {
608
746
  if (popoverStack.peek() === this) {
609
747
  popoverStack.pop();
610
748
  }
749
+ // cleanup floating-ui on closing the popover
750
+ (_b = this.floatingUICleanupFunction) === null || _b === void 0 ? void 0 : _b.call(this);
611
751
  if (this.backdrop) {
612
752
  this.triggerElement.style.position = this.triggerElementOriginalStyle.position;
613
753
  this.triggerElement.style.zIndex = this.triggerElementOriginalStyle.zIndex;
614
- }
615
- if (this.backdropElement) {
616
- (_d = this.backdropElement) === null || _d === void 0 ? void 0 : _d.remove();
617
- this.backdropElement = null;
754
+ this.utils.removeBackdrop();
618
755
  }
619
756
  if (this.hideOnBlur) {
620
757
  this.removeEventListener('focusout', this.onPopoverFocusOut);
@@ -627,7 +764,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
627
764
  }
628
765
  if (this.hideOnEscape) {
629
766
  this.removeEventListener('keydown', this.onEscapeKeydown);
630
- (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.removeEventListener('keydown', this.onEscapeKeydown);
767
+ (_c = this.triggerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('keydown', this.onEscapeKeydown);
631
768
  }
632
769
  if (this.disableAriaExpanded) {
633
770
  this.triggerElement.removeAttribute('aria-expanded');
@@ -637,93 +774,17 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
637
774
  this.triggerElement.removeAttribute('aria-haspopup');
638
775
  }
639
776
  this.deactivatePreventScroll();
640
- (_f = this.deactivateFocusTrap) === null || _f === void 0 ? void 0 : _f.call(this);
777
+ (_d = this.deactivateFocusTrap) === null || _d === void 0 ? void 0 : _d.call(this);
641
778
  if (this.focusBackToTrigger) {
642
- (_g = this.triggerElement) === null || _g === void 0 ? void 0 : _g.focus();
779
+ (_e = this.triggerElement) === null || _e === void 0 ? void 0 : _e.focus();
643
780
  }
644
- PopoverEventManager.onHidePopover(this);
645
- }
646
- }
647
- /**
648
- * Positions the popover based on the trigger element.
649
- * It also handles the flip, size and arrow placement.
650
- * It uses the floating-ui/dom library to calculate the position.
651
- */
652
- positionPopover() {
653
- if (!this.triggerElement)
654
- return;
655
- const middleware = [
656
- shift({
657
- boundary: !this.boundary || this.boundary === 'clippingAncestors'
658
- ? 'clippingAncestors'
659
- : Array.from(document.querySelectorAll(this.boundary)),
660
- rootBoundary: this.boundaryRoot,
661
- padding: this.boundaryPadding,
662
- }),
663
- ];
664
- let popoverOffset = this.offset;
665
- if (this.flip) {
666
- middleware.push(flip());
667
- }
668
- if (this.size) {
669
- // expose a CSS variable for the available height
670
- // so that it can be used in other components styles
671
- const setInternalAvailableHeight = (availableHeight) => {
672
- this.style.setProperty('--mdc-popover-internal-available-height', `${availableHeight}px`);
673
- };
674
- const popoverContent = this.renderRoot.querySelector('[part="popover-content"]');
675
- middleware.push(size({
676
- apply({ availableHeight }) {
677
- if (!popoverContent)
678
- return;
679
- Object.assign(popoverContent.style, {
680
- maxHeight: `${availableHeight}px`,
681
- overflowY: 'auto',
682
- });
683
- setInternalAvailableHeight(availableHeight);
684
- },
685
- padding: 50,
686
- }));
687
- }
688
- if (this.showArrow) {
689
- this.arrowElement = this.renderRoot.querySelector('.popover-arrow');
690
- if (this.arrowElement) {
691
- const arrowLen = this.arrowElement.offsetHeight;
692
- const arrowOffset = Math.sqrt(2 * arrowLen ** 2) / 2;
693
- popoverOffset += arrowOffset;
694
- middleware.push(arrow({ element: this.arrowElement, padding: 12 }));
695
- }
696
- }
697
- middleware.push(offset(popoverOffset));
698
- autoUpdate(this.triggerElement, this, async () => {
699
- if (!this.triggerElement)
700
- return;
701
- const { x, y, middlewareData, placement } = await computePosition(this.triggerElement, this, {
702
- placement: this.placement,
703
- middleware,
704
- });
705
- this.utils.updatePopoverStyle(x, y);
706
- if (middlewareData.arrow && this.arrowElement) {
707
- this.utils.updateArrowStyle(middlewareData.arrow, placement);
708
- }
709
- if (this.trigger.includes('mouseenter') && this.interactive) {
710
- this.utils.setupHoverBridge(placement);
781
+ if (this.keepConnectedTooltipClosed) {
782
+ if (this.connectedTooltip) {
783
+ this.connectedTooltip.shouldSupressOpening = false;
784
+ }
711
785
  }
712
- });
713
- }
714
- /**
715
- * Finds the closest popover to the passed element in the DOM tree.
716
- *
717
- * Useful when need to find the parent popover in a nested popover scenario.
718
- *
719
- * @param element - The element to start searching from.
720
- */
721
- findClosestPopover(element) {
722
- let el = element;
723
- while (el && !(el instanceof Popover)) {
724
- el = el.parentElement;
786
+ PopoverEventManager.onHidePopover(this);
725
787
  }
726
- return el;
727
788
  }
728
789
  render() {
729
790
  return html `
@@ -735,7 +796,7 @@ class Popover extends PreventScrollMixin(FocusTrapMixin(Component)) {
735
796
  variant="tertiary"
736
797
  size="20"
737
798
  aria-label=${ifDefined(this.closeButtonAriaLabel) || ''}
738
- @click="${this.hidePopover}"
799
+ @click="${this.hide}"
739
800
  ></mdc-button>`
740
801
  : nothing}
741
802
  ${this.showArrow ? html `<div class="popover-arrow"></div>` : nothing}
@@ -874,4 +935,8 @@ __decorate([
874
935
  property({ type: Boolean, reflect: true, attribute: 'disable-aria-haspopup' }),
875
936
  __metadata("design:type", Boolean)
876
937
  ], Popover.prototype, "disableAriaHasPopup", void 0);
938
+ __decorate([
939
+ property({ type: Boolean, reflect: true, attribute: 'keep-connected-tooltip-closed' }),
940
+ __metadata("design:type", Boolean)
941
+ ], Popover.prototype, "keepConnectedTooltipClosed", void 0);
877
942
  export default Popover;