@schukai/monster 4.120.1 → 4.121.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.121.0] - 2026-02-13
6
+
7
+ ### Add Features
8
+
9
+ - Enhance dismissal functionality for Popper and Select components
10
+
11
+
12
+
5
13
  ## [4.120.1] - 2026-02-12
6
14
 
7
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.120.1"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.121.0"}
@@ -18,7 +18,7 @@ import {
18
18
  assembleMethodSymbol,
19
19
  registerCustomElement,
20
20
  } from "../../dom/customelement.mjs";
21
- import { getDocument } from "../../dom/util.mjs";
21
+ import { findElementWithSelectorUpwards, getDocument } from "../../dom/util.mjs";
22
22
  import { isFunction } from "../../types/is.mjs";
23
23
  import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
24
24
  import { Popper } from "../layout/popper.mjs";
@@ -77,6 +77,20 @@ const popperElementSymbol = Symbol("popperElement");
77
77
  */
78
78
  const arrowElementSymbol = Symbol("arrowElement");
79
79
 
80
+ /**
81
+ * local symbol
82
+ * @private
83
+ * @type {symbol}
84
+ */
85
+ const hostElementSymbol = Symbol("hostElement");
86
+
87
+ /**
88
+ * local symbol
89
+ * @private
90
+ * @type {symbol}
91
+ */
92
+ const usesHostDismissSymbol = Symbol("usesHostDismiss");
93
+
80
94
  /**
81
95
  * A beautiful popper button that can make your life easier and also looks good.
82
96
  *
@@ -149,6 +163,10 @@ class PopperButton extends Popper {
149
163
  role: null,
150
164
  label: null,
151
165
  },
166
+
167
+ features: {
168
+ closeOnOutsideClick: true,
169
+ },
152
170
  });
153
171
  }
154
172
 
@@ -187,11 +205,24 @@ class PopperButton extends Popper {
187
205
  connectedCallback() {
188
206
  super.connectedCallback();
189
207
 
208
+ this[hostElementSymbol] = findElementWithSelectorUpwards(
209
+ this,
210
+ "monster-host",
211
+ );
212
+ this[usesHostDismissSymbol] =
213
+ this[hostElementSymbol] &&
214
+ typeof this[hostElementSymbol].registerDismissable === "function";
215
+
190
216
  const document = getDocument();
191
217
 
192
- for (const [, type] of Object.entries(["click", "touch"])) {
193
- // close on outside ui-events
194
- document.addEventListener(type, this[closeEventHandler]);
218
+ if (
219
+ this.getOption("features.closeOnOutsideClick", true) === true &&
220
+ !this[usesHostDismissSymbol]
221
+ ) {
222
+ for (const [, type] of Object.entries(["click", "touch"])) {
223
+ // close on outside ui-events
224
+ document.addEventListener(type, this[closeEventHandler]);
225
+ }
195
226
  }
196
227
 
197
228
  updatePopper.call(this);
@@ -204,9 +235,14 @@ class PopperButton extends Popper {
204
235
  disconnectedCallback() {
205
236
  super.disconnectedCallback();
206
237
 
207
- // close on outside ui-events
208
- for (const [, type] of Object.entries(["click", "touch"])) {
209
- document.removeEventListener(type, this[closeEventHandler]);
238
+ if (
239
+ this.getOption("features.closeOnOutsideClick", true) === true &&
240
+ !this[usesHostDismissSymbol]
241
+ ) {
242
+ // close on outside ui-events
243
+ for (const [, type] of Object.entries(["click", "touch"])) {
244
+ document.removeEventListener(type, this[closeEventHandler]);
245
+ }
210
246
  }
211
247
 
212
248
  disconnectResizeObserver.call(this);
@@ -38,7 +38,7 @@ import {
38
38
  fireEvent,
39
39
  } from "../../dom/events.mjs";
40
40
  import { getLocaleOfDocument } from "../../dom/locale.mjs";
41
- import { getDocument } from "../../dom/util.mjs";
41
+ import { findElementWithSelectorUpwards, getDocument } from "../../dom/util.mjs";
42
42
  import {
43
43
  getDocumentTranslations,
44
44
  Translations,
@@ -105,6 +105,9 @@ const isLoadingSymbol = Symbol("isLoading");
105
105
  * @type {Symbol}
106
106
  */
107
107
  const closeEventHandler = Symbol("closeEventHandler");
108
+ const hostElementSymbol = Symbol("hostElement");
109
+ const dismissRecordSymbol = Symbol("dismissRecord");
110
+ const usesHostDismissSymbol = Symbol("usesHostDismiss");
108
111
 
109
112
  /**
110
113
  * local symbol
@@ -841,11 +844,20 @@ class Select extends CustomControl {
841
844
  */
842
845
  connectedCallback() {
843
846
  super.connectedCallback();
844
- const document = getDocument();
845
-
846
- for (const [, type] of Object.entries(["click", "touch"])) {
847
- // close on outside ui-events
848
- document.addEventListener(type, this[closeEventHandler]);
847
+ this[hostElementSymbol] = findElementWithSelectorUpwards(
848
+ this,
849
+ "monster-host",
850
+ );
851
+ this[usesHostDismissSymbol] =
852
+ this[hostElementSymbol] &&
853
+ typeof this[hostElementSymbol].registerDismissable === "function";
854
+
855
+ if (!this[usesHostDismissSymbol]) {
856
+ const document = getDocument();
857
+ for (const [, type] of Object.entries(["click", "touch"])) {
858
+ // close on outside ui-events
859
+ document.addEventListener(type, this[closeEventHandler]);
860
+ }
849
861
  }
850
862
 
851
863
  parseSlotsToOptions.call(this);
@@ -873,13 +885,16 @@ class Select extends CustomControl {
873
885
  */
874
886
  disconnectedCallback() {
875
887
  super.disconnectedCallback();
876
- const document = getDocument();
877
-
878
- // close on outside ui-events
879
- for (const [, type] of Object.entries(["click", "touch"])) {
880
- document.removeEventListener(type, this[closeEventHandler]);
888
+ if (!this[usesHostDismissSymbol]) {
889
+ const document = getDocument();
890
+ // close on outside ui-events
891
+ for (const [, type] of Object.entries(["click", "touch"])) {
892
+ document.removeEventListener(type, this[closeEventHandler]);
893
+ }
881
894
  }
882
895
 
896
+ unregisterFromHost.call(this);
897
+
883
898
  disconnectResizeObserver.call(this);
884
899
  }
885
900
 
@@ -3703,6 +3718,7 @@ function hide() {
3703
3718
  this[popperElementSymbol].style.display = "none";
3704
3719
  setStatusOrRemoveBadges.call(this, "closed");
3705
3720
  removeAttributeToken(this[controlElementSymbol], "class", "open");
3721
+ unregisterFromHost.call(this);
3706
3722
  }
3707
3723
 
3708
3724
  /**
@@ -3772,6 +3788,7 @@ function show() {
3772
3788
  setStatusOrRemoveBadges.call(this, "open");
3773
3789
 
3774
3790
  addAttributeToken(this[controlElementSymbol], "class", "open");
3791
+ registerWithHost.call(this);
3775
3792
 
3776
3793
  new Processing(() => {
3777
3794
  if (!self?.[remoteFilterFirstOpendSymbol]) {
@@ -3798,6 +3815,56 @@ function show() {
3798
3815
  });
3799
3816
  }
3800
3817
 
3818
+ /**
3819
+ * @private
3820
+ */
3821
+ function registerWithHost() {
3822
+ if (!this[usesHostDismissSymbol]) {
3823
+ return;
3824
+ }
3825
+
3826
+ if (!(this[hostElementSymbol] instanceof HTMLElement)) {
3827
+ return;
3828
+ }
3829
+
3830
+ const record = this[hostElementSymbol].registerDismissable?.({
3831
+ element: this,
3832
+ owner: this,
3833
+ close: () => {
3834
+ hide.call(this);
3835
+ },
3836
+ priority: 20,
3837
+ options: {
3838
+ dismissOnOutside: true,
3839
+ },
3840
+ });
3841
+
3842
+ if (record) {
3843
+ this[dismissRecordSymbol] = record;
3844
+ }
3845
+ }
3846
+
3847
+ /**
3848
+ * @private
3849
+ */
3850
+ function unregisterFromHost() {
3851
+ if (!this[usesHostDismissSymbol]) {
3852
+ return;
3853
+ }
3854
+
3855
+ if (!(this[hostElementSymbol] instanceof HTMLElement)) {
3856
+ return;
3857
+ }
3858
+
3859
+ if (this[dismissRecordSymbol]) {
3860
+ this[hostElementSymbol].unregisterDismissable?.(this[dismissRecordSymbol]);
3861
+ this[dismissRecordSymbol] = null;
3862
+ return;
3863
+ }
3864
+
3865
+ this[hostElementSymbol].unregisterDismissable?.(this);
3866
+ }
3867
+
3801
3868
  function initDefaultOptionsFromUrl() {
3802
3869
  const url = this.getOption("filter.defaultOptionsUrl");
3803
3870
  if (!url) {
@@ -68,6 +68,48 @@ const focusManagerSymbol = Symbol("focusManager");
68
68
  */
69
69
  const resourceManagerSymbol = Symbol("resourceManager");
70
70
 
71
+ /**
72
+ * @private
73
+ * @type {symbol}
74
+ */
75
+ const dismissablesSymbol = Symbol("dismissables");
76
+
77
+ /**
78
+ * @private
79
+ * @type {symbol}
80
+ */
81
+ const dismissQueueSymbol = Symbol("dismissQueue");
82
+
83
+ /**
84
+ * @private
85
+ * @type {symbol}
86
+ */
87
+ const dismissVersionSymbol = Symbol("dismissVersion");
88
+
89
+ /**
90
+ * @private
91
+ * @type {symbol}
92
+ */
93
+ const dismissSequenceSymbol = Symbol("dismissSequence");
94
+
95
+ /**
96
+ * @private
97
+ * @type {symbol}
98
+ */
99
+ const dismissScheduledSymbol = Symbol("dismissScheduled");
100
+
101
+ /**
102
+ * @private
103
+ * @type {symbol}
104
+ */
105
+ const dismissHandlerSymbol = Symbol("dismissHandler");
106
+
107
+ /**
108
+ * @private
109
+ * @type {symbol}
110
+ */
111
+ const dismissListenersAttachedSymbol = Symbol("dismissListenersAttached");
112
+
71
113
  /**
72
114
  * The Host component is used to encapsulate the content of a web app.
73
115
  *
@@ -171,6 +213,8 @@ class Host extends CustomElement {
171
213
  document.body.classList.remove(classNames);
172
214
  }
173
215
 
216
+ attachDismissListeners.call(this);
217
+
174
218
  fireCustomEvent(this, "monster-host-connected");
175
219
  }
176
220
 
@@ -204,6 +248,8 @@ class Host extends CustomElement {
204
248
  );
205
249
 
206
250
  fireCustomEvent(this, "monster-host-disconnected");
251
+
252
+ detachDismissListeners.call(this);
207
253
  }
208
254
 
209
255
  /**
@@ -358,6 +404,24 @@ class Host extends CustomElement {
358
404
  this[notifyElementSymbol].push(message);
359
405
  return this;
360
406
  }
407
+
408
+ /**
409
+ * Register a dismissable overlay (popper/select/dialog) for outside click handling.
410
+ * @param {Object} entry
411
+ * @return {Object|null}
412
+ */
413
+ registerDismissable(entry) {
414
+ return registerDismissable.call(this, entry);
415
+ }
416
+
417
+ /**
418
+ * Unregister a dismissable overlay.
419
+ * @param {Object|HTMLElement} entry
420
+ * @return {boolean}
421
+ */
422
+ unregisterDismissable(entry) {
423
+ return unregisterDismissable.call(this, entry);
424
+ }
361
425
  }
362
426
 
363
427
  /**
@@ -392,9 +456,316 @@ function initTranslations() {
392
456
  * @private
393
457
  */
394
458
  function initEventHandler() {
459
+ initDismissManager.call(this);
395
460
  return this;
396
461
  }
397
462
 
463
+ /**
464
+ * @private
465
+ */
466
+ function initDismissManager() {
467
+ this[dismissablesSymbol] = new Map();
468
+ this[dismissQueueSymbol] = [];
469
+ this[dismissVersionSymbol] = 0;
470
+ this[dismissSequenceSymbol] = 0;
471
+ this[dismissScheduledSymbol] = false;
472
+ this[dismissListenersAttachedSymbol] = false;
473
+
474
+ this[dismissHandlerSymbol] = (event) => {
475
+ if (!this.isConnected) {
476
+ return;
477
+ }
478
+ queueDismissEvent.call(this, event);
479
+ };
480
+ }
481
+
482
+ /**
483
+ * @private
484
+ */
485
+ function attachDismissListeners() {
486
+ if (this[dismissListenersAttachedSymbol] === true) {
487
+ return;
488
+ }
489
+
490
+ const supportsPointer = typeof globalThis.PointerEvent === "function";
491
+ const eventTypes = supportsPointer
492
+ ? ["pointerdown"]
493
+ : ["mousedown", "touchstart"];
494
+
495
+ for (const type of eventTypes) {
496
+ this.addEventListener(type, this[dismissHandlerSymbol], {
497
+ capture: true,
498
+ });
499
+ }
500
+
501
+ this[dismissListenersAttachedSymbol] = true;
502
+ }
503
+
504
+ /**
505
+ * @private
506
+ */
507
+ function detachDismissListeners() {
508
+ if (this[dismissListenersAttachedSymbol] !== true) {
509
+ return;
510
+ }
511
+
512
+ const supportsPointer = typeof globalThis.PointerEvent === "function";
513
+ const eventTypes = supportsPointer
514
+ ? ["pointerdown"]
515
+ : ["mousedown", "touchstart"];
516
+
517
+ for (const type of eventTypes) {
518
+ this.removeEventListener(type, this[dismissHandlerSymbol], {
519
+ capture: true,
520
+ });
521
+ }
522
+
523
+ this[dismissListenersAttachedSymbol] = false;
524
+ }
525
+
526
+ /**
527
+ * @private
528
+ * @param {Event} event
529
+ */
530
+ function queueDismissEvent(event) {
531
+ if (!event) {
532
+ return;
533
+ }
534
+
535
+ const path = typeof event.composedPath === "function" ? event.composedPath() : [];
536
+ this[dismissQueueSymbol].push({
537
+ path,
538
+ target: event.target || null,
539
+ version: this[dismissVersionSymbol],
540
+ });
541
+
542
+ if (this[dismissScheduledSymbol] === true) {
543
+ return;
544
+ }
545
+
546
+ this[dismissScheduledSymbol] = true;
547
+ const schedule =
548
+ typeof queueMicrotask === "function"
549
+ ? queueMicrotask
550
+ : (cb) => Promise.resolve().then(cb);
551
+
552
+ schedule(() => {
553
+ this[dismissScheduledSymbol] = false;
554
+ processDismissQueue.call(this);
555
+ });
556
+ }
557
+
558
+ /**
559
+ * @private
560
+ */
561
+ function processDismissQueue() {
562
+ const queue = this[dismissQueueSymbol];
563
+ this[dismissQueueSymbol] = [];
564
+
565
+ if (!queue.length) {
566
+ return;
567
+ }
568
+
569
+ for (const entry of queue) {
570
+ if (entry.version !== this[dismissVersionSymbol]) {
571
+ continue;
572
+ }
573
+
574
+ const top = getTopDismissable.call(this);
575
+ if (!top) {
576
+ continue;
577
+ }
578
+
579
+ if (isEventInsideDismissable(entry, top)) {
580
+ continue;
581
+ }
582
+
583
+ if (top.options?.dismissOnOutside === false) {
584
+ continue;
585
+ }
586
+
587
+ if (typeof top.close === "function") {
588
+ top.close();
589
+ }
590
+
591
+ break;
592
+ }
593
+ }
594
+
595
+ /**
596
+ * @private
597
+ * @param {Object} entry
598
+ * @return {Object|null}
599
+ */
600
+ function registerDismissable(entry) {
601
+ if (this[dismissablesSymbol] instanceof Map === false) {
602
+ initDismissManager.call(this);
603
+ }
604
+
605
+ const normalized = normalizeDismissEntry(entry);
606
+ if (!normalized) {
607
+ return null;
608
+ }
609
+
610
+ const existing = this[dismissablesSymbol].get(normalized.element);
611
+ const record = existing || { element: normalized.element };
612
+
613
+ record.owner = normalized.owner || record.owner || null;
614
+ record.close = normalized.close || record.close || null;
615
+ record.priority = Number.isFinite(normalized.priority)
616
+ ? normalized.priority
617
+ : Number.isFinite(record.priority)
618
+ ? record.priority
619
+ : 0;
620
+ record.options = Object.assign({}, record.options || {}, normalized.options || {});
621
+ record.sequence = ++this[dismissSequenceSymbol];
622
+
623
+ this[dismissablesSymbol].set(record.element, record);
624
+ this[dismissVersionSymbol] += 1;
625
+
626
+ return record;
627
+ }
628
+
629
+ /**
630
+ * @private
631
+ * @param {Object|HTMLElement} entry
632
+ * @return {boolean}
633
+ */
634
+ function unregisterDismissable(entry) {
635
+ if (this[dismissablesSymbol] instanceof Map === false) {
636
+ return false;
637
+ }
638
+
639
+ const record = resolveDismissRecord.call(this, entry);
640
+ if (!record) {
641
+ return false;
642
+ }
643
+
644
+ this[dismissablesSymbol].delete(record.element);
645
+ this[dismissVersionSymbol] += 1;
646
+ return true;
647
+ }
648
+
649
+ /**
650
+ * @private
651
+ * @param {Object} entry
652
+ * @return {Object|null}
653
+ */
654
+ function normalizeDismissEntry(entry) {
655
+ if (!entry) {
656
+ return null;
657
+ }
658
+
659
+ const element = entry.element || entry.owner || entry;
660
+ if (!(element instanceof HTMLElement)) {
661
+ return null;
662
+ }
663
+
664
+ return {
665
+ element,
666
+ owner: entry.owner instanceof HTMLElement ? entry.owner : null,
667
+ close: typeof entry.close === "function" ? entry.close : null,
668
+ priority: entry.priority,
669
+ options: entry.options || {},
670
+ };
671
+ }
672
+
673
+ /**
674
+ * @private
675
+ * @param {Object|HTMLElement} entry
676
+ * @return {Object|null}
677
+ */
678
+ function resolveDismissRecord(entry) {
679
+ if (!entry) {
680
+ return null;
681
+ }
682
+
683
+ if (entry.element && this[dismissablesSymbol].has(entry.element)) {
684
+ return this[dismissablesSymbol].get(entry.element);
685
+ }
686
+
687
+ if (entry instanceof HTMLElement && this[dismissablesSymbol].has(entry)) {
688
+ return this[dismissablesSymbol].get(entry);
689
+ }
690
+
691
+ for (const record of this[dismissablesSymbol].values()) {
692
+ if (record === entry) {
693
+ return record;
694
+ }
695
+ if (record.owner && record.owner === entry) {
696
+ return record;
697
+ }
698
+ }
699
+
700
+ return null;
701
+ }
702
+
703
+ /**
704
+ * @private
705
+ * @return {Object|null}
706
+ */
707
+ function getTopDismissable() {
708
+ if (this[dismissablesSymbol] instanceof Map === false) {
709
+ return null;
710
+ }
711
+
712
+ let top = null;
713
+ for (const record of this[dismissablesSymbol].values()) {
714
+ if (!record) {
715
+ continue;
716
+ }
717
+ if (!top) {
718
+ top = record;
719
+ continue;
720
+ }
721
+ if ((record.priority || 0) > (top.priority || 0)) {
722
+ top = record;
723
+ continue;
724
+ }
725
+ if (
726
+ (record.priority || 0) === (top.priority || 0) &&
727
+ (record.sequence || 0) > (top.sequence || 0)
728
+ ) {
729
+ top = record;
730
+ }
731
+ }
732
+
733
+ return top;
734
+ }
735
+
736
+ /**
737
+ * @private
738
+ * @param {Object} eventEntry
739
+ * @param {Object} record
740
+ * @return {boolean}
741
+ */
742
+ function isEventInsideDismissable(eventEntry, record) {
743
+ if (!record) {
744
+ return false;
745
+ }
746
+
747
+ const path = eventEntry.path || [];
748
+ const target = eventEntry.target || null;
749
+
750
+ if (path.includes(record.element)) {
751
+ return true;
752
+ }
753
+
754
+ if (record.owner && path.includes(record.owner)) {
755
+ return true;
756
+ }
757
+
758
+ if (target && record.element?.contains?.(target)) {
759
+ return true;
760
+ }
761
+
762
+ if (target && record.owner?.contains?.(target)) {
763
+ return true;
764
+ }
765
+
766
+ return false;
767
+ }
768
+
398
769
  /**
399
770
  * @private
400
771
  * @return {string}
@@ -25,7 +25,7 @@ import {
25
25
  registerCustomElement,
26
26
  } from "../../dom/customelement.mjs";
27
27
  import { fireCustomEvent } from "../../dom/events.mjs";
28
- import { getDocument } from "../../dom/util.mjs";
28
+ import { findElementWithSelectorUpwards, getDocument } from "../../dom/util.mjs";
29
29
  import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
30
30
  import { STYLE_DISPLAY_MODE_BLOCK } from "../form/constants.mjs";
31
31
  import { positionPopper } from "../form/util/floating-ui.mjs";
@@ -83,6 +83,24 @@ const popperElementSymbol = Symbol("popperElement");
83
83
  */
84
84
  const arrowElementSymbol = Symbol("arrowElement");
85
85
 
86
+ /**
87
+ * @private
88
+ * @type {symbol}
89
+ */
90
+ const hostElementSymbol = Symbol("hostElement");
91
+
92
+ /**
93
+ * @private
94
+ * @type {symbol}
95
+ */
96
+ const dismissRecordSymbol = Symbol("dismissRecord");
97
+
98
+ /**
99
+ * @private
100
+ * @type {symbol}
101
+ */
102
+ const usesHostDismissSymbol = Symbol("usesHostDismiss");
103
+
86
104
  /**
87
105
  * Popper component for displaying floating UI elements
88
106
  *
@@ -177,10 +195,19 @@ class Popper extends CustomElement {
177
195
  connectedCallback() {
178
196
  super.connectedCallback();
179
197
 
180
- const document = getDocument();
181
-
182
- for (const [, type] of Object.entries(["click", "touch"])) {
183
- document.addEventListener(type, this[closeEventHandler]);
198
+ this[hostElementSymbol] = findElementWithSelectorUpwards(
199
+ this,
200
+ "monster-host",
201
+ );
202
+ this[usesHostDismissSymbol] =
203
+ this[hostElementSymbol] &&
204
+ typeof this[hostElementSymbol].registerDismissable === "function";
205
+
206
+ if (!this[usesHostDismissSymbol]) {
207
+ const document = getDocument();
208
+ for (const [, type] of Object.entries(["click", "touch"])) {
209
+ document.addEventListener(type, this[closeEventHandler]);
210
+ }
184
211
  }
185
212
 
186
213
  updatePopper.call(this);
@@ -194,10 +221,15 @@ class Popper extends CustomElement {
194
221
  disconnectedCallback() {
195
222
  super.disconnectedCallback();
196
223
 
197
- for (const [, type] of Object.entries(["click", "touch"])) {
198
- document.removeEventListener(type, this[closeEventHandler]);
224
+ if (!this[usesHostDismissSymbol]) {
225
+ const document = getDocument();
226
+ for (const [, type] of Object.entries(["click", "touch"])) {
227
+ document.removeEventListener(type, this[closeEventHandler]);
228
+ }
199
229
  }
200
230
 
231
+ unregisterFromHost.call(this);
232
+
201
233
  disconnectResizeObserver.call(this);
202
234
  }
203
235
 
@@ -399,6 +431,7 @@ function hide() {
399
431
 
400
432
  self[popperElementSymbol].style.display = "none";
401
433
  removeAttributeToken(self[controlElementSymbol], "class", "open");
434
+ unregisterFromHost.call(self);
402
435
 
403
436
  setTimeout(() => {
404
437
  fireCustomEvent(self, "monster-popper-hidden", {
@@ -430,6 +463,7 @@ function show() {
430
463
  self[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
431
464
 
432
465
  addAttributeToken(self[controlElementSymbol], "class", "open");
466
+ registerWithHost.call(self);
433
467
  updatePopper.call(self);
434
468
 
435
469
  setTimeout(() => {
@@ -460,6 +494,56 @@ function updatePopper() {
460
494
  );
461
495
  }
462
496
 
497
+ /**
498
+ * @private
499
+ */
500
+ function registerWithHost() {
501
+ if (!this[usesHostDismissSymbol]) {
502
+ return;
503
+ }
504
+
505
+ if (!(this[hostElementSymbol] instanceof HTMLElement)) {
506
+ return;
507
+ }
508
+
509
+ const record = this[hostElementSymbol].registerDismissable?.({
510
+ element: this,
511
+ owner: this,
512
+ close: () => {
513
+ this.hideDialog();
514
+ },
515
+ priority: 10,
516
+ options: {
517
+ dismissOnOutside: true,
518
+ },
519
+ });
520
+
521
+ if (record) {
522
+ this[dismissRecordSymbol] = record;
523
+ }
524
+ }
525
+
526
+ /**
527
+ * @private
528
+ */
529
+ function unregisterFromHost() {
530
+ if (!this[usesHostDismissSymbol]) {
531
+ return;
532
+ }
533
+
534
+ if (!(this[hostElementSymbol] instanceof HTMLElement)) {
535
+ return;
536
+ }
537
+
538
+ if (this[dismissRecordSymbol]) {
539
+ this[hostElementSymbol].unregisterDismissable?.(this[dismissRecordSymbol]);
540
+ this[dismissRecordSymbol] = null;
541
+ return;
542
+ }
543
+
544
+ this[hostElementSymbol].unregisterDismissable?.(this);
545
+ }
546
+
463
547
  /**
464
548
  * Initializes references to DOM elements
465
549
  * @private