@nanahoshi/mona-ui 0.24.1 → 0.24.4
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/README.md +3 -0
- package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-button-group.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-button.mjs +122 -60
- package/fesm2022/nanahoshi-mona-ui-calendar.mjs +1 -2
- package/fesm2022/nanahoshi-mona-ui-check-box.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-chip.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +2 -2
- package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +4 -2
- package/fesm2022/nanahoshi-mona-ui-date-input.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +8 -4
- package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +8 -4
- package/fesm2022/nanahoshi-mona-ui-dialog.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-dropdown-button.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +4 -2
- package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +13 -3
- package/fesm2022/nanahoshi-mona-ui-editor.mjs +13 -13
- package/fesm2022/nanahoshi-mona-ui-filter.mjs +1 -1
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component-CnPhVIr4.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-DLOPRuFv.mjs} +2 -2
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-DxY6bfTX.mjs → nanahoshi-mona-ui-grid-grid-list.component-Dw5U9H0a.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-CaO5Lae6.mjs → nanahoshi-mona-ui-grid-grid-row.component-D1kyAzD8.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-jNYtqg5B.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-ECK1_smx.mjs} +4 -4
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-9FtfCutf.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-D4lUDbfx.mjs} +14 -18
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-internal-filter-input.mjs +2 -2
- package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-internal.mjs +15 -1
- package/fesm2022/nanahoshi-mona-ui-list-box.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-menubar.mjs +3 -3
- package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-notification.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +3 -3
- package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-pager.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +2 -2
- package/fesm2022/nanahoshi-mona-ui-popup.mjs +269 -182
- package/fesm2022/nanahoshi-mona-ui-sheet.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-split-button.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-splitter.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-switch.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-tabs.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-text-box.mjs +95 -58
- package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +7 -5
- package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +2 -2
- package/fesm2022/nanahoshi-mona-ui-window.mjs +1 -1
- package/package.json +1 -1
- package/types/nanahoshi-mona-ui-button.d.ts +44 -20
- package/types/nanahoshi-mona-ui-dropdowns.d.ts +6 -0
- package/types/nanahoshi-mona-ui-internal.d.ts +9 -1
- package/types/nanahoshi-mona-ui-popup.d.ts +97 -36
- package/types/nanahoshi-mona-ui-text-box.d.ts +62 -14
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, DestroyRef, DOCUMENT, viewChild, ViewContainerRef, computed, signal, TemplateRef, Component, Injector, ElementRef, Injectable, input, output, effect } from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, DestroyRef, DOCUMENT, viewChild, ViewContainerRef, computed, signal, TemplateRef, Component, Injector, ElementRef, Injectable, input, output, booleanAttribute, effect } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
|
-
import {
|
|
5
|
-
import { take, asapScheduler, Subject, ReplaySubject, asyncScheduler, fromEvent, takeUntil, Subscription, filter, tap, exhaustMap, merge } from 'rxjs';
|
|
4
|
+
import { take, asapScheduler, Subject, ReplaySubject, asyncScheduler, fromEvent, Subscription, merge, takeUntil } from 'rxjs';
|
|
6
5
|
import { PreventableEvent } from '@nanahoshi/mona-ui/common';
|
|
7
6
|
import { Directionality } from '@angular/cdk/bidi';
|
|
8
7
|
import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
|
|
9
8
|
import { ComponentPortal } from '@angular/cdk/portal';
|
|
10
9
|
import { ScrollDispatcher } from '@angular/cdk/scrolling';
|
|
11
10
|
import { MonaI18nService, resolveComponentDirection, observeComponentDirection } from '@nanahoshi/mona-ui/i18n';
|
|
11
|
+
import { focusCanReturn } from '@nanahoshi/mona-ui/internal';
|
|
12
12
|
import { NgTemplateOutlet } from '@angular/common';
|
|
13
13
|
|
|
14
14
|
class PopupCloseEvent extends PreventableEvent {
|
|
@@ -64,13 +64,17 @@ const slideFromBottomPopupAnimation = {
|
|
|
64
64
|
leave: "mona-popup-slide-to-bottom-leave"
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* The `data` passed in `PopupSettings`, available to the popup content.
|
|
69
|
+
*/
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- public API name, kept for compatibility.
|
|
71
|
+
const PopupDataInjectionToken = new InjectionToken("PopupDataInjectionToken");
|
|
72
|
+
const POPUP_SETTINGS = new InjectionToken("POPUP_SETTINGS");
|
|
69
73
|
|
|
70
74
|
/**
|
|
71
75
|
* @internal - used by the popup service. Do not use directly or export.
|
|
72
76
|
*/
|
|
73
|
-
const
|
|
77
|
+
const POPUP_REFERENCE = new InjectionToken("POPUP_REFERENCE");
|
|
74
78
|
|
|
75
79
|
class PopupWrapperComponent {
|
|
76
80
|
#destroyRef;
|
|
@@ -83,21 +87,21 @@ class PopupWrapperComponent {
|
|
|
83
87
|
constructor() {
|
|
84
88
|
this.#destroyRef = inject(DestroyRef);
|
|
85
89
|
this.#document = inject(DOCUMENT);
|
|
86
|
-
this.#popupReference = inject(
|
|
87
|
-
this.#popupSettings = inject(
|
|
90
|
+
this.#popupReference = inject(POPUP_REFERENCE);
|
|
91
|
+
this.#popupSettings = inject(POPUP_SETTINGS);
|
|
88
92
|
this.#closeEvent = null;
|
|
89
93
|
this.#fallbackTimer = null;
|
|
90
94
|
this.#leaveCompleted = false;
|
|
91
95
|
this.animationElement = viewChild.required("animationElement", /* @ts-ignore */
|
|
92
96
|
...(ngDevMode ? [{ debugName: "animationElement" }] : /* istanbul ignore next */ []));
|
|
93
97
|
this.componentOutlet = viewChild.required("componentOutlet", { ...(ngDevMode ? { debugName: "componentOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
|
|
98
|
+
this.enterAnimationClasses = computed(() => this.#getAnimationConfig()?.enter ?? "", /* @ts-ignore */
|
|
99
|
+
...(ngDevMode ? [{ debugName: "enterAnimationClasses" }] : /* istanbul ignore next */ []));
|
|
94
100
|
this.hostClasses = computed(() => {
|
|
95
101
|
const wrapperClass = this.wrapperClass();
|
|
96
102
|
return ["mona-popup-wrapper", "w-full", "h-full", wrapperClass].filter(Boolean).join(" ");
|
|
97
103
|
}, /* @ts-ignore */
|
|
98
104
|
...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
99
|
-
this.enterAnimationClasses = computed(() => this.#getAnimationConfig()?.enter ?? "", /* @ts-ignore */
|
|
100
|
-
...(ngDevMode ? [{ debugName: "enterAnimationClasses" }] : /* istanbul ignore next */ []));
|
|
101
105
|
this.leaveAnimationClasses = computed(() => this.#getAnimationConfig()?.leave ?? "", /* @ts-ignore */
|
|
102
106
|
...(ngDevMode ? [{ debugName: "leaveAnimationClasses" }] : /* istanbul ignore next */ []));
|
|
103
107
|
this.templateRef = signal(null, /* @ts-ignore */
|
|
@@ -120,7 +124,6 @@ class PopupWrapperComponent {
|
|
|
120
124
|
});
|
|
121
125
|
this.#destroyRef.onDestroy(() => this.#clearFallbackTimer());
|
|
122
126
|
}
|
|
123
|
-
ngOnInit() { }
|
|
124
127
|
attachContent(content, injector) {
|
|
125
128
|
if (content instanceof TemplateRef) {
|
|
126
129
|
this.templateRef.set(content);
|
|
@@ -227,8 +230,15 @@ class PopupRef {
|
|
|
227
230
|
constructor(options) {
|
|
228
231
|
this.#options = options;
|
|
229
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Requests the popup to close.
|
|
235
|
+
* @param result optional result passed to `beforeClose` and `closed` as the event's `result`
|
|
236
|
+
* @param delay optional delay in milliseconds before the popup is removed, instead of waiting for the
|
|
237
|
+
* leave animation
|
|
238
|
+
* @returns `false` when the close was prevented or the popup is already closing, `true` otherwise.
|
|
239
|
+
*/
|
|
230
240
|
close(result, delay) {
|
|
231
|
-
this.#options.close(result, delay);
|
|
241
|
+
return this.#options.close(result, delay);
|
|
232
242
|
}
|
|
233
243
|
/**
|
|
234
244
|
* Closes the popup after a delay.
|
|
@@ -317,13 +327,13 @@ class PopupReference {
|
|
|
317
327
|
constructor(overlayReference, preventClose) {
|
|
318
328
|
this.overlayReference = overlayReference;
|
|
319
329
|
this.preventClose = preventClose;
|
|
330
|
+
this.#closed = false;
|
|
331
|
+
this.#closing = false;
|
|
320
332
|
this.beforeClosed$ = new Subject();
|
|
321
333
|
this.closeStart$ = new Subject();
|
|
322
334
|
this.closed$ = new Subject();
|
|
323
335
|
this.opened$ = new ReplaySubject(1);
|
|
324
336
|
this.positionChanges$ = new Subject();
|
|
325
|
-
this.#closed = false;
|
|
326
|
-
this.#closing = false;
|
|
327
337
|
this.popupRef = new PopupRef(this);
|
|
328
338
|
}
|
|
329
339
|
/**
|
|
@@ -361,8 +371,20 @@ class PopupReference {
|
|
|
361
371
|
}
|
|
362
372
|
return true;
|
|
363
373
|
}
|
|
364
|
-
|
|
365
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Finishes a close that bypassed {@link close}: the overlay was detached or disposed directly. Nothing can
|
|
376
|
+
* prevent it any more, so `beforeClose` is skipped; `closeStart` and `closed` still fire, in that order.
|
|
377
|
+
*/
|
|
378
|
+
closeDetached() {
|
|
379
|
+
if (this.#closed) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const event = new PopupCloseEvent({ via: PopupCloseSource.Programmatic });
|
|
383
|
+
if (!this.#closing) {
|
|
384
|
+
this.#closing = true;
|
|
385
|
+
this.closeStart$.next(event);
|
|
386
|
+
}
|
|
387
|
+
this.completeClose(event);
|
|
366
388
|
}
|
|
367
389
|
completeClose(event) {
|
|
368
390
|
if (this.#closed) {
|
|
@@ -374,6 +396,9 @@ class PopupReference {
|
|
|
374
396
|
this.positionChanges$.complete();
|
|
375
397
|
this.overlayRef.dispose();
|
|
376
398
|
}
|
|
399
|
+
notifyOpen() {
|
|
400
|
+
this.opened$.next();
|
|
401
|
+
}
|
|
377
402
|
get beforeClose$() {
|
|
378
403
|
return this.beforeClosed$.asObservable();
|
|
379
404
|
}
|
|
@@ -516,38 +541,30 @@ const generateFallbackPositions = (primaryPosition) => {
|
|
|
516
541
|
else if (originX === "end" && originY === "top" && overlayX === "start" && overlayY === "bottom") {
|
|
517
542
|
fallbacks.push({ originX: "start", originY: "bottom", overlayX: "end", overlayY: "top" });
|
|
518
543
|
}
|
|
519
|
-
//6. Complete mirroring:
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
else if (originX === "start" && originY === "bottom") {
|
|
540
|
-
fallbacks.push({ originX: "end", originY: "top", overlayX: "end", overlayY: "top" });
|
|
541
|
-
}
|
|
542
|
-
else if (originX === "center" && originY === "bottom") {
|
|
543
|
-
fallbacks.push({ originX: "center", originY: "top", overlayX: "center", overlayY: "top" });
|
|
544
|
-
}
|
|
545
|
-
else if (originX === "end" && originY === "bottom") {
|
|
546
|
-
fallbacks.push({ originX: "start", originY: "top", overlayX: "start", overlayY: "top" });
|
|
547
|
-
}
|
|
544
|
+
// 6. Complete mirroring: flip both the anchor corner and the popup corner on both axes, so the popup lands on
|
|
545
|
+
// the opposite side and alignment of the anchor. Flipping only the anchor corner and copying it onto the popup
|
|
546
|
+
// corner, as this used to do, placed the popup over the anchor.
|
|
547
|
+
const mirrored = {
|
|
548
|
+
originX: flipX(originX),
|
|
549
|
+
originY: flipY(originY),
|
|
550
|
+
overlayX: flipX(overlayX),
|
|
551
|
+
overlayY: flipY(overlayY)
|
|
552
|
+
};
|
|
553
|
+
const isPrimary = mirrored.originX === originX &&
|
|
554
|
+
mirrored.originY === originY &&
|
|
555
|
+
mirrored.overlayX === overlayX &&
|
|
556
|
+
mirrored.overlayY === overlayY;
|
|
557
|
+
const isListed = fallbacks.some(f => f.originX === mirrored.originX &&
|
|
558
|
+
f.originY === mirrored.originY &&
|
|
559
|
+
f.overlayX === mirrored.overlayX &&
|
|
560
|
+
f.overlayY === mirrored.overlayY);
|
|
561
|
+
if (!isPrimary && !isListed) {
|
|
562
|
+
fallbacks.push(mirrored);
|
|
548
563
|
}
|
|
549
564
|
return fallbacks;
|
|
550
565
|
};
|
|
566
|
+
const flipX = (value) => (value === "start" ? "end" : value === "end" ? "start" : value);
|
|
567
|
+
const flipY = (value) => (value === "top" ? "bottom" : value === "bottom" ? "top" : value);
|
|
551
568
|
|
|
552
569
|
class PopupService {
|
|
553
570
|
#destroyRef = inject(DestroyRef);
|
|
@@ -555,10 +572,13 @@ class PopupService {
|
|
|
555
572
|
#document = inject(DOCUMENT);
|
|
556
573
|
#i18n = inject(MonaI18nService, { optional: true });
|
|
557
574
|
#injector = inject(Injector);
|
|
575
|
+
/** Open popups in the order they were opened; the last one is on top. */
|
|
576
|
+
#openPopups = [];
|
|
558
577
|
#outsideEventsToClose = ["click", "mousedown", "dblclick", "contextmenu", "auxclick"];
|
|
559
578
|
#overlay = inject(Overlay);
|
|
560
579
|
#scrollDispatcher = inject(ScrollDispatcher);
|
|
561
|
-
#
|
|
580
|
+
#escapeSubscription = null;
|
|
581
|
+
#escapeTarget = null;
|
|
562
582
|
create(settings) {
|
|
563
583
|
if (typeof settings.anchor === "string") {
|
|
564
584
|
return this.createMultiElementPopup(settings);
|
|
@@ -581,35 +601,33 @@ class PopupService {
|
|
|
581
601
|
}
|
|
582
602
|
return ["mona-popup-content"].concat(popupClass);
|
|
583
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* With `"auto"` (also the default), remembers the focused element when it is the anchor or inside it, such
|
|
606
|
+
* as the button inside a host component or the input inside a date picker, and returns focus to that element.
|
|
607
|
+
*/
|
|
584
608
|
captureOriginalFocus(settings) {
|
|
585
|
-
|
|
609
|
+
const restoreFocus = settings.restoreFocus ?? "auto";
|
|
610
|
+
if (typeof settings.anchor === "string" || restoreFocus === false) {
|
|
586
611
|
return null;
|
|
587
612
|
}
|
|
588
613
|
const activeElement = this.#document.activeElement;
|
|
589
614
|
if (!(activeElement instanceof HTMLElement)) {
|
|
590
615
|
return null;
|
|
591
616
|
}
|
|
592
|
-
if (
|
|
617
|
+
if (restoreFocus === "auto") {
|
|
593
618
|
const anchorElement = this.getAnchorElement(settings.anchor);
|
|
594
|
-
return activeElement
|
|
619
|
+
return anchorElement?.contains(activeElement) ? activeElement : null;
|
|
595
620
|
}
|
|
596
621
|
return this.getAnchorElement(settings.anchor);
|
|
597
622
|
}
|
|
598
|
-
cleanupEventDelegation(selector) {
|
|
599
|
-
const subscriptions = this.#selectorSubscriptions.get(selector);
|
|
600
|
-
if (subscriptions) {
|
|
601
|
-
subscriptions.forEach(sub => sub.unsubscribe());
|
|
602
|
-
this.#selectorSubscriptions.delete(selector);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
623
|
createInjector(settings, popupReference) {
|
|
606
624
|
return Injector.create({
|
|
607
|
-
parent: this.#injector,
|
|
625
|
+
parent: settings.injector ?? this.#injector,
|
|
608
626
|
providers: [
|
|
609
627
|
{ provide: PopupRef, useFactory: () => popupReference.popupRef },
|
|
610
628
|
{ provide: PopupDataInjectionToken, useValue: settings.data },
|
|
611
|
-
{ provide:
|
|
612
|
-
{ provide:
|
|
629
|
+
{ provide: POPUP_REFERENCE, useValue: popupReference },
|
|
630
|
+
{ provide: POPUP_SETTINGS, useValue: settings },
|
|
613
631
|
...(settings.providers ?? [])
|
|
614
632
|
]
|
|
615
633
|
});
|
|
@@ -619,12 +637,9 @@ class PopupService {
|
|
|
619
637
|
// Create a virtual PopupRef that manages multi-element interactions
|
|
620
638
|
const virtualOverlayRef = this.#overlay.create({ hasBackdrop: false });
|
|
621
639
|
const virtualPopupReference = new PopupReference(virtualOverlayRef);
|
|
622
|
-
//
|
|
623
|
-
this.setupInternalEventDelegation(selector, settings);
|
|
624
|
-
|
|
625
|
-
virtualPopupReference.popupRef.closed.subscribe(() => {
|
|
626
|
-
this.cleanupEventDelegation(selector);
|
|
627
|
-
});
|
|
640
|
+
// Closing the returned ref stops the delegation and closes the popup currently showing.
|
|
641
|
+
const delegation = this.setupInternalEventDelegation(selector, settings);
|
|
642
|
+
virtualPopupReference.popupRef.closed.pipe(take(1)).subscribe(() => delegation.unsubscribe());
|
|
628
643
|
return virtualPopupReference.popupRef;
|
|
629
644
|
}
|
|
630
645
|
createOverlay(settings) {
|
|
@@ -698,7 +713,8 @@ class PopupService {
|
|
|
698
713
|
this.setupScrollClosing(settings, popupReference, overlayRef);
|
|
699
714
|
}
|
|
700
715
|
this.setupScrollTracking(settings, overlayRef, popupReference);
|
|
701
|
-
this.
|
|
716
|
+
this.registerOpenPopup(settings, popupReference);
|
|
717
|
+
this.setupDetachTracking(overlayRef, popupReference);
|
|
702
718
|
this.setupPositionChangeTracking(settings, overlayRef, popupReference);
|
|
703
719
|
this.setupDirectionTracking(settings, overlayRef, popupReference);
|
|
704
720
|
return popupReference.popupRef;
|
|
@@ -745,18 +761,48 @@ class PopupService {
|
|
|
745
761
|
.forEach(scrollable => addScrollable(scrollable));
|
|
746
762
|
return Array.from(scrollablesByElement.values());
|
|
747
763
|
}
|
|
748
|
-
|
|
764
|
+
getTopmostOpenPopup() {
|
|
765
|
+
for (let index = this.#openPopups.length - 1; index >= 0; index--) {
|
|
766
|
+
const popup = this.#openPopups[index];
|
|
767
|
+
if (popup.reference.overlayRef.hasAttached()) {
|
|
768
|
+
return popup;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Escape goes to the topmost open popup only, so closing a dropdown inside a dialog leaves the dialog
|
|
775
|
+
* open. The topmost popup consumes the key even when it doesn't close on Escape, so a modal that can't
|
|
776
|
+
* be dismissed doesn't let it reach the popup underneath. An Escape that ends an IME composition is left
|
|
777
|
+
* alone. `preventDefault()` doesn't keep the popup open, because controls call it on every navigation key;
|
|
778
|
+
* a control that handles Escape itself stops its propagation instead.
|
|
779
|
+
*/
|
|
780
|
+
handleEscapeKey(event) {
|
|
781
|
+
const escapeTarget = this.#escapeTarget;
|
|
782
|
+
this.#escapeTarget = null;
|
|
783
|
+
if (event.key !== "Escape" || event.isComposing) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
// The popup on top when the key was pressed, before handlers further down could close it.
|
|
787
|
+
const popup = escapeTarget?.event === event ? escapeTarget.popup : this.getTopmostOpenPopup();
|
|
788
|
+
if (!popup?.closeOnEscape) {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
popup.reference.popupRef.close(new PopupCloseEvent({
|
|
792
|
+
event,
|
|
793
|
+
originalEvent: event,
|
|
794
|
+
via: PopupCloseSource.Escape
|
|
795
|
+
}));
|
|
796
|
+
}
|
|
797
|
+
handleFocusRestoration(settings, overlayRef, originallyFocusedElement) {
|
|
749
798
|
const restoreFocus = settings.restoreFocus ?? "auto";
|
|
750
799
|
if (restoreFocus === false) {
|
|
751
800
|
return;
|
|
752
801
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
}
|
|
757
|
-
return;
|
|
802
|
+
const target = restoreFocus === "auto" ? originallyFocusedElement : this.getAnchorElement(settings.anchor);
|
|
803
|
+
if (target && focusCanReturn(this.#document, target, overlayRef.overlayElement)) {
|
|
804
|
+
target.focus();
|
|
758
805
|
}
|
|
759
|
-
this.restoreFocusToAnchor(settings.anchor);
|
|
760
806
|
}
|
|
761
807
|
isScrollableElement(element) {
|
|
762
808
|
const defaultView = this.#document.defaultView;
|
|
@@ -773,6 +819,33 @@ class PopupService {
|
|
|
773
819
|
isScrollableOverflow(value) {
|
|
774
820
|
return value === "auto" || value === "scroll" || value === "overlay";
|
|
775
821
|
}
|
|
822
|
+
/**
|
|
823
|
+
* One pair of `document` listeners serves every open popup. The capturing one notes which popup is on
|
|
824
|
+
* top before the key reaches any control; the bubbling one closes it if nothing handled the key.
|
|
825
|
+
*/
|
|
826
|
+
listenForEscape() {
|
|
827
|
+
if (this.#escapeSubscription) {
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const subscription = new Subscription();
|
|
831
|
+
subscription.add(fromEvent(this.#document, "keydown", { capture: true }).subscribe(event => {
|
|
832
|
+
this.#escapeTarget = event.key === "Escape" ? { event, popup: this.getTopmostOpenPopup() } : null;
|
|
833
|
+
}));
|
|
834
|
+
subscription.add(fromEvent(this.#document, "keydown").subscribe(event => this.handleEscapeKey(event)));
|
|
835
|
+
this.#escapeSubscription = subscription;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* A popup leaves the stack as soon as it starts closing, so a second Escape during its leave animation
|
|
839
|
+
* goes to the popup underneath.
|
|
840
|
+
*/
|
|
841
|
+
registerOpenPopup(settings, popupReference) {
|
|
842
|
+
const popup = { closeOnEscape: settings.closeOnEscape ?? true, reference: popupReference };
|
|
843
|
+
this.#openPopups.push(popup);
|
|
844
|
+
this.listenForEscape();
|
|
845
|
+
merge(popupReference.closeStart, popupReference.closed)
|
|
846
|
+
.pipe(take(1))
|
|
847
|
+
.subscribe(() => this.unregisterOpenPopup(popup));
|
|
848
|
+
}
|
|
776
849
|
/**
|
|
777
850
|
* Resolves a PopupAnchor to a FlexibleConnectedPositionStrategyOrigin.
|
|
778
851
|
* This method should only receive actual elements or ElementRefs, not CSS selector strings.
|
|
@@ -787,16 +860,6 @@ class PopupService {
|
|
|
787
860
|
}
|
|
788
861
|
return anchor;
|
|
789
862
|
}
|
|
790
|
-
restoreFocusToAnchor(anchor) {
|
|
791
|
-
if (typeof anchor === "string") {
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
const resolvedAnchor = this.resolveAnchor(anchor);
|
|
795
|
-
const anchorElement = this.getAnchorElement(resolvedAnchor);
|
|
796
|
-
if (anchorElement) {
|
|
797
|
-
anchorElement.focus();
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
863
|
setupBackdropCloseSubscription(settings, popupReference, overlayRef) {
|
|
801
864
|
if (!(settings.closeOnBackdropClick ?? true)) {
|
|
802
865
|
return null;
|
|
@@ -831,7 +894,7 @@ class PopupService {
|
|
|
831
894
|
setupCleanupSubscription(popupReference, subscription, settings, originallyFocusedElement) {
|
|
832
895
|
popupReference.closed.pipe(take(1)).subscribe(() => {
|
|
833
896
|
subscription?.unsubscribe();
|
|
834
|
-
this.handleFocusRestoration(settings, originallyFocusedElement);
|
|
897
|
+
this.handleFocusRestoration(settings, popupReference.overlayRef, originallyFocusedElement);
|
|
835
898
|
});
|
|
836
899
|
}
|
|
837
900
|
setupCloseSubscriptions(settings, popupReference, overlayRef) {
|
|
@@ -861,46 +924,70 @@ class PopupService {
|
|
|
861
924
|
subscriptions.forEach(sub => combinedSubscription.add(sub));
|
|
862
925
|
return combinedSubscription;
|
|
863
926
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
927
|
+
/**
|
|
928
|
+
* `PopupRef.overlayRef` is public, so the overlay can be detached or disposed without going through
|
|
929
|
+
* `close()`. Finish the close then too, or `closed` never fires and focus, subscriptions and consumers
|
|
930
|
+
* waiting for it (the dropdowns won't reopen until it does) stay stuck.
|
|
931
|
+
*/
|
|
932
|
+
setupDetachTracking(overlayRef, popupReference) {
|
|
933
|
+
overlayRef
|
|
934
|
+
.detachments()
|
|
935
|
+
.pipe(take(1), takeUntil(popupReference.closed))
|
|
936
|
+
.subscribe(() => popupReference.closeDetached());
|
|
937
|
+
}
|
|
938
|
+
setupDirectionTracking(settings, overlayRef, popupReference) {
|
|
939
|
+
const resolvedAnchor = this.resolveAnchor(settings.anchor);
|
|
940
|
+
const anchorElement = this.getAnchorElement(resolvedAnchor);
|
|
941
|
+
const cleanup = observeComponentDirection(anchorElement, this.#directionality, nextDir => {
|
|
942
|
+
overlayRef.setDirection(nextDir);
|
|
943
|
+
overlayRef.updatePosition();
|
|
944
|
+
popupReference.componentRef?.changeDetectorRef.markForCheck();
|
|
945
|
+
});
|
|
946
|
+
popupReference.closed.pipe(take(1)).subscribe(() => {
|
|
947
|
+
cleanup();
|
|
877
948
|
});
|
|
878
949
|
}
|
|
950
|
+
/**
|
|
951
|
+
* Listens on the document rather than on the elements matched at creation time, so elements added later
|
|
952
|
+
* get a popup too. One popup shows at a time; it closes when the pointer leaves its element, and the
|
|
953
|
+
* returned subscription's teardown closes it as well.
|
|
954
|
+
*/
|
|
879
955
|
setupInternalEventDelegation(selector, settings) {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
956
|
+
let current = null;
|
|
957
|
+
const closeCurrent = () => {
|
|
958
|
+
const popup = current;
|
|
959
|
+
current = null;
|
|
960
|
+
popup?.popupRef.close();
|
|
961
|
+
};
|
|
962
|
+
const subscription = new Subscription(closeCurrent);
|
|
963
|
+
subscription.add(fromEvent(this.#document, "pointerover")
|
|
964
|
+
.pipe(takeUntilDestroyed(this.#destroyRef))
|
|
965
|
+
.subscribe(event => {
|
|
966
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
967
|
+
const element = target?.closest(selector);
|
|
968
|
+
if (!(element instanceof HTMLElement) || element === current?.element) {
|
|
886
969
|
return;
|
|
887
970
|
}
|
|
888
|
-
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}), exhaustMap(() => pointerLeave$.pipe(take(1))), tap(() => {
|
|
895
|
-
if (currentPopupRef) {
|
|
896
|
-
currentPopupRef.close();
|
|
897
|
-
currentPopupRef = null;
|
|
971
|
+
closeCurrent();
|
|
972
|
+
const popupRef = this.createSingleElementPopup({ ...settings, anchor: element });
|
|
973
|
+
current = { element, popupRef };
|
|
974
|
+
popupRef.closed.pipe(take(1)).subscribe(() => {
|
|
975
|
+
if (current?.popupRef === popupRef) {
|
|
976
|
+
current = null;
|
|
898
977
|
}
|
|
899
|
-
})
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
978
|
+
});
|
|
979
|
+
}));
|
|
980
|
+
subscription.add(fromEvent(this.#document, "pointerout")
|
|
981
|
+
.pipe(takeUntilDestroyed(this.#destroyRef))
|
|
982
|
+
.subscribe(event => {
|
|
983
|
+
const element = current?.element;
|
|
984
|
+
const target = event.target instanceof Node ? event.target : null;
|
|
985
|
+
const next = event.relatedTarget instanceof Node ? event.relatedTarget : null;
|
|
986
|
+
if (element && target && element.contains(target) && !(next && element.contains(next))) {
|
|
987
|
+
closeCurrent();
|
|
988
|
+
}
|
|
989
|
+
}));
|
|
990
|
+
return subscription;
|
|
904
991
|
}
|
|
905
992
|
setupMouseLeaveSubscription(settings, popupReference) {
|
|
906
993
|
if (typeof settings.anchor === "string") {
|
|
@@ -927,7 +1014,7 @@ class PopupService {
|
|
|
927
1014
|
.outsidePointerEvents()
|
|
928
1015
|
.pipe(takeUntil(popupReference.closed), takeUntilDestroyed(this.#destroyRef))
|
|
929
1016
|
.subscribe(event => {
|
|
930
|
-
if (this.shouldIgnoreOutsideClick(settings, event)) {
|
|
1017
|
+
if (this.shouldIgnoreOutsideClick(settings, event, overlayRef)) {
|
|
931
1018
|
return;
|
|
932
1019
|
}
|
|
933
1020
|
const closeEvent = new PopupCloseEvent({
|
|
@@ -949,18 +1036,6 @@ class PopupService {
|
|
|
949
1036
|
.subscribe(change => popupReference.positionChanges$.next(change.connectionPair));
|
|
950
1037
|
}
|
|
951
1038
|
}
|
|
952
|
-
setupDirectionTracking(settings, overlayRef, popupReference) {
|
|
953
|
-
const resolvedAnchor = this.resolveAnchor(settings.anchor);
|
|
954
|
-
const anchorElement = this.getAnchorElement(resolvedAnchor);
|
|
955
|
-
const cleanup = observeComponentDirection(anchorElement, this.#directionality, nextDir => {
|
|
956
|
-
overlayRef.setDirection(nextDir);
|
|
957
|
-
overlayRef.updatePosition();
|
|
958
|
-
popupReference.componentRef?.changeDetectorRef.markForCheck();
|
|
959
|
-
});
|
|
960
|
-
popupReference.closed.pipe(take(1)).subscribe(() => {
|
|
961
|
-
cleanup();
|
|
962
|
-
});
|
|
963
|
-
}
|
|
964
1039
|
setupScrollClosing(settings, popupReference, overlayRef) {
|
|
965
1040
|
if (!settings.closeOnScroll) {
|
|
966
1041
|
return;
|
|
@@ -978,22 +1053,26 @@ class PopupService {
|
|
|
978
1053
|
});
|
|
979
1054
|
popupReference.close(closeEvent, 0);
|
|
980
1055
|
};
|
|
1056
|
+
// The page scrolls the anchor away whether or not it also sits in a scrollable container.
|
|
1057
|
+
const win = this.#document.defaultView;
|
|
1058
|
+
const pageEvents = [
|
|
1059
|
+
fromEvent(win, "scroll", { passive: true }),
|
|
1060
|
+
fromEvent(this.#document, "scroll", { passive: true })
|
|
1061
|
+
];
|
|
981
1062
|
if (scrollableContainers.length === 0) {
|
|
982
|
-
|
|
983
|
-
const globalScrollSubscription = merge(fromEvent(win, "scroll", { passive: true }), fromEvent(win, "resize", { passive: true }), fromEvent(this.#document, "scroll", { passive: true }))
|
|
984
|
-
.pipe(takeUntil(popupReference.closed), takeUntilDestroyed(this.#destroyRef))
|
|
985
|
-
.subscribe(event => handleScrollClose(event));
|
|
986
|
-
subscriptions.push(globalScrollSubscription);
|
|
987
|
-
}
|
|
988
|
-
else {
|
|
989
|
-
scrollableContainers.forEach(scrollable => {
|
|
990
|
-
const containerScrollSubscription = scrollable
|
|
991
|
-
.elementScrolled()
|
|
992
|
-
.pipe(takeUntil(popupReference.closed), takeUntilDestroyed(this.#destroyRef))
|
|
993
|
-
.subscribe(() => handleScrollClose());
|
|
994
|
-
subscriptions.push(containerScrollSubscription);
|
|
995
|
-
});
|
|
1063
|
+
pageEvents.push(fromEvent(win, "resize", { passive: true }));
|
|
996
1064
|
}
|
|
1065
|
+
const pageScrollSubscription = merge(...pageEvents)
|
|
1066
|
+
.pipe(takeUntil(popupReference.closed), takeUntilDestroyed(this.#destroyRef))
|
|
1067
|
+
.subscribe(event => handleScrollClose(event));
|
|
1068
|
+
subscriptions.push(pageScrollSubscription);
|
|
1069
|
+
scrollableContainers.forEach(scrollable => {
|
|
1070
|
+
const containerScrollSubscription = scrollable
|
|
1071
|
+
.elementScrolled()
|
|
1072
|
+
.pipe(takeUntil(popupReference.closed), takeUntilDestroyed(this.#destroyRef))
|
|
1073
|
+
.subscribe(() => handleScrollClose());
|
|
1074
|
+
subscriptions.push(containerScrollSubscription);
|
|
1075
|
+
});
|
|
997
1076
|
popupReference.closed.pipe(take(1)).subscribe(() => {
|
|
998
1077
|
subscriptions.forEach(sub => sub.unsubscribe());
|
|
999
1078
|
});
|
|
@@ -1031,8 +1110,13 @@ class PopupService {
|
|
|
1031
1110
|
subscriptions.forEach(sub => sub.unsubscribe());
|
|
1032
1111
|
});
|
|
1033
1112
|
}
|
|
1034
|
-
shouldIgnoreOutsideClick(settings, event) {
|
|
1113
|
+
shouldIgnoreOutsideClick(settings, event, overlayRef) {
|
|
1035
1114
|
const eventTarget = event.target;
|
|
1115
|
+
// The backdrop lies outside the pane, so its clicks also arrive here. They belong to the backdrop
|
|
1116
|
+
// handler, which honours closeOnBackdropClick and reports them as BackdropClick.
|
|
1117
|
+
if (overlayRef.backdropElement?.contains(eventTarget)) {
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1036
1120
|
if (typeof settings.anchor === "string") {
|
|
1037
1121
|
return !this.#outsideEventsToClose.includes(event.type);
|
|
1038
1122
|
}
|
|
@@ -1042,6 +1126,17 @@ class PopupService {
|
|
|
1042
1126
|
const isRelevantEventType = this.#outsideEventsToClose.includes(event.type);
|
|
1043
1127
|
return isAnchorClick || !isRelevantEventType;
|
|
1044
1128
|
}
|
|
1129
|
+
unregisterOpenPopup(popup) {
|
|
1130
|
+
const index = this.#openPopups.indexOf(popup);
|
|
1131
|
+
if (index !== -1) {
|
|
1132
|
+
this.#openPopups.splice(index, 1);
|
|
1133
|
+
}
|
|
1134
|
+
if (this.#openPopups.length === 0) {
|
|
1135
|
+
this.#escapeSubscription?.unsubscribe();
|
|
1136
|
+
this.#escapeSubscription = null;
|
|
1137
|
+
this.#escapeTarget = null;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1045
1140
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1046
1141
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupService, providedIn: "root" }); }
|
|
1047
1142
|
}
|
|
@@ -1065,14 +1160,14 @@ class PopupComponent {
|
|
|
1065
1160
|
#destroyRef;
|
|
1066
1161
|
#document;
|
|
1067
1162
|
#popupService;
|
|
1068
|
-
#popupOpened;
|
|
1069
1163
|
#eventListenerCleanup$;
|
|
1164
|
+
#popupOpened;
|
|
1070
1165
|
constructor() {
|
|
1071
1166
|
this.#destroyRef = inject(DestroyRef);
|
|
1072
1167
|
this.#document = inject(DOCUMENT);
|
|
1073
1168
|
this.#popupService = inject(PopupService);
|
|
1074
|
-
this.#popupOpened = false;
|
|
1075
1169
|
this.#eventListenerCleanup$ = new Subject();
|
|
1170
|
+
this.#popupOpened = false;
|
|
1076
1171
|
this.contentTemplate = viewChild.required(TemplateRef, /* @ts-ignore */
|
|
1077
1172
|
...(ngDevMode ? [{ debugName: "contentTemplate" }] : /* istanbul ignore next */ []));
|
|
1078
1173
|
this.popupRef = null;
|
|
@@ -1099,28 +1194,30 @@ class PopupComponent {
|
|
|
1099
1194
|
/**
|
|
1100
1195
|
* @description Emits when the popup is closed.
|
|
1101
1196
|
*/
|
|
1197
|
+
// eslint-disable-next-line @angular-eslint/no-output-native -- public API name, kept for compatibility.
|
|
1102
1198
|
this.close = output();
|
|
1103
1199
|
/**
|
|
1104
1200
|
* @description Whether to close the popup when clicking on the backdrop.
|
|
1105
1201
|
* Only applies when the popup has a backdrop.
|
|
1106
1202
|
*/
|
|
1107
|
-
this.closeOnBackdropClick = input(true, /*
|
|
1108
|
-
...(ngDevMode ? [{ debugName: "closeOnBackdropClick" }] : /* istanbul ignore next */ []));
|
|
1203
|
+
this.closeOnBackdropClick = input(true, { ...(ngDevMode ? { debugName: "closeOnBackdropClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1109
1204
|
/**
|
|
1110
1205
|
* @description Whether to close the popup when the escape key is pressed.
|
|
1206
|
+
* Escape only reaches the topmost open popup.
|
|
1111
1207
|
*/
|
|
1112
|
-
this.closeOnEscape = input(true, /*
|
|
1113
|
-
...(ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []));
|
|
1208
|
+
this.closeOnEscape = input(true, { ...(ngDevMode ? { debugName: "closeOnEscape" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1114
1209
|
/**
|
|
1115
1210
|
* @description Whether to close the popup when the mouse leaves the anchor element.
|
|
1116
1211
|
*/
|
|
1117
|
-
this.closeOnMouseLeave = input(false, /*
|
|
1118
|
-
...(ngDevMode ? [{ debugName: "closeOnMouseLeave" }] : /* istanbul ignore next */ []));
|
|
1212
|
+
this.closeOnMouseLeave = input(false, { ...(ngDevMode ? { debugName: "closeOnMouseLeave" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1119
1213
|
/**
|
|
1120
1214
|
* @description Whether to close the popup when clicking outside of it.
|
|
1121
1215
|
*/
|
|
1122
|
-
this.closeOnOutsideClick = input(true, /*
|
|
1123
|
-
|
|
1216
|
+
this.closeOnOutsideClick = input(true, { ...(ngDevMode ? { debugName: "closeOnOutsideClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1217
|
+
/**
|
|
1218
|
+
* @description Whether to close the popup when the user scrolls the page or a scrollable container around the anchor.
|
|
1219
|
+
*/
|
|
1220
|
+
this.closeOnScroll = input(false, { ...(ngDevMode ? { debugName: "closeOnScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1124
1221
|
/**
|
|
1125
1222
|
* @description Arbitrary data to pass to the popup.
|
|
1126
1223
|
*/
|
|
@@ -1129,8 +1226,7 @@ class PopupComponent {
|
|
|
1129
1226
|
/**
|
|
1130
1227
|
* @description Whether the popup should have a backdrop.
|
|
1131
1228
|
*/
|
|
1132
|
-
this.hasBackdrop = input(false, /*
|
|
1133
|
-
...(ngDevMode ? [{ debugName: "hasBackdrop" }] : /* istanbul ignore next */ []));
|
|
1229
|
+
this.hasBackdrop = input(false, { ...(ngDevMode ? { debugName: "hasBackdrop" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1134
1230
|
/**
|
|
1135
1231
|
* @description The height of the popup.
|
|
1136
1232
|
*/
|
|
@@ -1205,6 +1301,15 @@ class PopupComponent {
|
|
|
1205
1301
|
*/
|
|
1206
1302
|
this.providers = input([], /* @ts-ignore */
|
|
1207
1303
|
...(ngDevMode ? [{ debugName: "providers" }] : /* istanbul ignore next */ []));
|
|
1304
|
+
/**
|
|
1305
|
+
* @description Controls focus restoration when the popup closes.
|
|
1306
|
+
* - `true`: always restore focus to the anchor element.
|
|
1307
|
+
* - `false`: never restore focus (recommended for tooltips).
|
|
1308
|
+
* - `"auto"`: restore focus only if the anchor, or an element inside it, was focused when the popup opened.
|
|
1309
|
+
*
|
|
1310
|
+
* Focus is left alone if the user moved it outside the popup and the anchor before it closed.
|
|
1311
|
+
*/
|
|
1312
|
+
this.restoreFocus = input("auto", { ...(ngDevMode ? { debugName: "restoreFocus" } : /* istanbul ignore next */ {}), transform: (value) => (value === "auto" ? "auto" : booleanAttribute(value)) });
|
|
1208
1313
|
/**
|
|
1209
1314
|
* @description The trigger event for opening the popup.
|
|
1210
1315
|
*/
|
|
@@ -1218,34 +1323,16 @@ class PopupComponent {
|
|
|
1218
1323
|
/**
|
|
1219
1324
|
* @description Whether to push the popup into the screen when it is opened.
|
|
1220
1325
|
*/
|
|
1221
|
-
this.withPush = input(true, /*
|
|
1222
|
-
...(ngDevMode ? [{ debugName: "withPush" }] : /* istanbul ignore next */ []));
|
|
1223
|
-
/**
|
|
1224
|
-
* @description Whether to close the popup when the user scrolls within a scrollable container.
|
|
1225
|
-
*/
|
|
1226
|
-
this.closeOnScroll = input(false, /* @ts-ignore */
|
|
1227
|
-
...(ngDevMode ? [{ debugName: "closeOnScroll" }] : /* istanbul ignore next */ []));
|
|
1228
|
-
/**
|
|
1229
|
-
* @description Controls focus restoration when the popup closes.
|
|
1230
|
-
* - `true`: always restore focus to the anchor element.
|
|
1231
|
-
* - `false`: never restore focus (recommended for tooltips).
|
|
1232
|
-
* - `"auto"`: restore focus only if the anchor was focused when the popup opened.
|
|
1233
|
-
*/
|
|
1234
|
-
this.restoreFocus = input("auto", /* @ts-ignore */
|
|
1235
|
-
...(ngDevMode ? [{ debugName: "restoreFocus" }] : /* istanbul ignore next */ []));
|
|
1326
|
+
this.withPush = input(true, { ...(ngDevMode ? { debugName: "withPush" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1236
1327
|
/**
|
|
1237
1328
|
* @description Whether the popup should track scroll events and reposition itself.
|
|
1238
1329
|
*/
|
|
1239
|
-
this.withScrollTracking = input(true, /*
|
|
1240
|
-
...(ngDevMode ? [{ debugName: "withScrollTracking" }] : /* istanbul ignore next */ []));
|
|
1330
|
+
this.withScrollTracking = input(true, { ...(ngDevMode ? { debugName: "withScrollTracking" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1241
1331
|
effect(() => this.setupEventListeners());
|
|
1242
1332
|
}
|
|
1243
1333
|
closePopup() {
|
|
1244
1334
|
this.popupRef?.close();
|
|
1245
1335
|
}
|
|
1246
|
-
ngAfterViewInit() {
|
|
1247
|
-
rxTimeout(this.#destroyRef, () => this.setupEventListeners());
|
|
1248
|
-
}
|
|
1249
1336
|
ngOnDestroy() {
|
|
1250
1337
|
this.popupRef?.close();
|
|
1251
1338
|
this.#eventListenerCleanup$.next();
|
|
@@ -1285,7 +1372,6 @@ class PopupComponent {
|
|
|
1285
1372
|
target = this.#document.body;
|
|
1286
1373
|
pointAnchor = true;
|
|
1287
1374
|
}
|
|
1288
|
-
const width = this.getPopupWidth();
|
|
1289
1375
|
fromEvent(target, trigger)
|
|
1290
1376
|
.pipe(takeUntil(this.#eventListenerCleanup$), takeUntilDestroyed(this.#destroyRef))
|
|
1291
1377
|
.subscribe(event => {
|
|
@@ -1325,7 +1411,8 @@ class PopupComponent {
|
|
|
1325
1411
|
preventClose: this.preventClose(),
|
|
1326
1412
|
providers: this.providers(),
|
|
1327
1413
|
restoreFocus: this.restoreFocus(),
|
|
1328
|
-
|
|
1414
|
+
// Measured now rather than when the listener was set up, so it follows the anchor's size.
|
|
1415
|
+
width: this.getPopupWidth(),
|
|
1329
1416
|
withPush: this.withPush(),
|
|
1330
1417
|
withScrollTracking: this.withScrollTracking()
|
|
1331
1418
|
};
|
|
@@ -1357,12 +1444,12 @@ class PopupComponent {
|
|
|
1357
1444
|
});
|
|
1358
1445
|
}
|
|
1359
1446
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1360
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.6", type: PopupComponent, isStandalone: true, selector: "mona-popup", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, anchorConnectionPoint: { classPropertyName: "anchorConnectionPoint", publicName: "anchorConnectionPoint", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, closeOnMouseLeave: { classPropertyName: "closeOnMouseLeave", publicName: "closeOnMouseLeave", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, popupClass: { classPropertyName: "popupClass", publicName: "popupClass", isSignal: true, isRequired: false, transformFunction: null }, popupConnectionPoint: { classPropertyName: "popupConnectionPoint", publicName: "popupConnectionPoint", isSignal: true, isRequired: false, transformFunction: null }, popupWrapperClass: { classPropertyName: "popupWrapperClass", publicName: "popupWrapperClass", isSignal: true, isRequired: false, transformFunction: null }, positionStrategy: { classPropertyName: "positionStrategy", publicName: "positionStrategy", isSignal: true, isRequired: false, transformFunction: null }, positions: { classPropertyName: "positions", publicName: "positions", isSignal: true, isRequired: false, transformFunction: null }, preventClose: { classPropertyName: "preventClose", publicName: "preventClose", isSignal: true, isRequired: false, transformFunction: null }, providers: { classPropertyName: "providers", publicName: "providers", isSignal: true, isRequired: false, transformFunction: null },
|
|
1447
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.6", type: PopupComponent, isStandalone: true, selector: "mona-popup", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, anchorConnectionPoint: { classPropertyName: "anchorConnectionPoint", publicName: "anchorConnectionPoint", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, backdropClass: { classPropertyName: "backdropClass", publicName: "backdropClass", isSignal: true, isRequired: false, transformFunction: null }, closeOnBackdropClick: { classPropertyName: "closeOnBackdropClick", publicName: "closeOnBackdropClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnEscape: { classPropertyName: "closeOnEscape", publicName: "closeOnEscape", isSignal: true, isRequired: false, transformFunction: null }, closeOnMouseLeave: { classPropertyName: "closeOnMouseLeave", publicName: "closeOnMouseLeave", isSignal: true, isRequired: false, transformFunction: null }, closeOnOutsideClick: { classPropertyName: "closeOnOutsideClick", publicName: "closeOnOutsideClick", isSignal: true, isRequired: false, transformFunction: null }, closeOnScroll: { classPropertyName: "closeOnScroll", publicName: "closeOnScroll", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, hasBackdrop: { classPropertyName: "hasBackdrop", publicName: "hasBackdrop", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, popupClass: { classPropertyName: "popupClass", publicName: "popupClass", isSignal: true, isRequired: false, transformFunction: null }, popupConnectionPoint: { classPropertyName: "popupConnectionPoint", publicName: "popupConnectionPoint", isSignal: true, isRequired: false, transformFunction: null }, popupWrapperClass: { classPropertyName: "popupWrapperClass", publicName: "popupWrapperClass", isSignal: true, isRequired: false, transformFunction: null }, positionStrategy: { classPropertyName: "positionStrategy", publicName: "positionStrategy", isSignal: true, isRequired: false, transformFunction: null }, positions: { classPropertyName: "positions", publicName: "positions", isSignal: true, isRequired: false, transformFunction: null }, preventClose: { classPropertyName: "preventClose", publicName: "preventClose", isSignal: true, isRequired: false, transformFunction: null }, providers: { classPropertyName: "providers", publicName: "providers", isSignal: true, isRequired: false, transformFunction: null }, restoreFocus: { classPropertyName: "restoreFocus", publicName: "restoreFocus", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, withPush: { classPropertyName: "withPush", publicName: "withPush", isSignal: true, isRequired: false, transformFunction: null }, withScrollTracking: { classPropertyName: "withScrollTracking", publicName: "withScrollTracking", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", open: "open" }, viewQueries: [{ propertyName: "contentTemplate", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }); }
|
|
1361
1448
|
}
|
|
1362
1449
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: PopupComponent, decorators: [{
|
|
1363
1450
|
type: Component,
|
|
1364
1451
|
args: [{ selector: "mona-popup", template: "<ng-template>\n <ng-content></ng-content>\n</ng-template>\n" }]
|
|
1365
|
-
}], ctorParameters: () => [], propDecorators: { contentTemplate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: true }] }], anchorConnectionPoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorConnectionPoint", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closeOnMouseLeave: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnMouseLeave", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], open: [{ type: i0.Output, args: ["open"] }], popupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupClass", required: false }] }], popupConnectionPoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupConnectionPoint", required: false }] }], popupWrapperClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupWrapperClass", required: false }] }], positionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "positionStrategy", required: false }] }], positions: [{ type: i0.Input, args: [{ isSignal: true, alias: "positions", required: false }] }], preventClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventClose", required: false }] }], providers: [{ type: i0.Input, args: [{ isSignal: true, alias: "providers", required: false }] }],
|
|
1452
|
+
}], ctorParameters: () => [], propDecorators: { contentTemplate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: true }] }], anchorConnectionPoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorConnectionPoint", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], backdropClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdropClass", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closeOnBackdropClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnBackdropClick", required: false }] }], closeOnEscape: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnEscape", required: false }] }], closeOnMouseLeave: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnMouseLeave", required: false }] }], closeOnOutsideClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnOutsideClick", required: false }] }], closeOnScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnScroll", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], hasBackdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasBackdrop", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], open: [{ type: i0.Output, args: ["open"] }], popupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupClass", required: false }] }], popupConnectionPoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupConnectionPoint", required: false }] }], popupWrapperClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupWrapperClass", required: false }] }], positionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "positionStrategy", required: false }] }], positions: [{ type: i0.Input, args: [{ isSignal: true, alias: "positions", required: false }] }], preventClose: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventClose", required: false }] }], providers: [{ type: i0.Input, args: [{ isSignal: true, alias: "providers", required: false }] }], restoreFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "restoreFocus", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "trigger", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], withPush: [{ type: i0.Input, args: [{ isSignal: true, alias: "withPush", required: false }] }], withScrollTracking: [{ type: i0.Input, args: [{ isSignal: true, alias: "withScrollTracking", required: false }] }] } });
|
|
1366
1453
|
|
|
1367
1454
|
/*
|
|
1368
1455
|
* Public API Surface of @nanahoshi/mona-ui/popup
|