@rivinia/modal 0.6.1 → 1.0.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 +9 -0
- package/LICENSE +21 -0
- package/README.md +9 -1
- package/fesm2022/rivinia-modal-ui.mjs +21 -219
- package/fesm2022/rivinia-modal-ui.mjs.map +1 -1
- package/fesm2022/rivinia-modal.mjs +3 -3
- package/fesm2022/rivinia-modal.mjs.map +1 -1
- package/package.json +13 -1
- package/types/rivinia-modal.d.ts +0 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
- First stable release of the modal lifecycle and native-dialog UI. Destroyed dialogs now release lifecycle resources safely.
|
|
6
|
+
|
|
7
|
+
## Unreleased
|
|
8
|
+
|
|
9
|
+
- Release scroll locks and exit timers when a modal dialog is destroyed while open or closing.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rivinia contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# @rivinia/modal
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/abdelfattahqandil21-oss/Revinia/modal/v1.0.0/assets/rivinia-abstract-mark-light.svg" alt="Rivinia" width="340" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
An Angular modal manager with typed results, stack/queue admission, dismissal
|
|
4
8
|
policies, async guards, and optional ready-made UI. The core package owns
|
|
5
9
|
modal state and lifecycle but does not render or style dialogs.
|
|
6
10
|
|
|
7
11
|
- Install: `npm install @rivinia/modal`
|
|
8
|
-
- Package version: **0.
|
|
12
|
+
- Package version: **1.0.0**
|
|
9
13
|
- Peer dependencies: `@angular/core ^22.0.0` and `@angular/common ^22.0.0`
|
|
10
14
|
- `@rivinia/modal` is the headless manager.
|
|
11
15
|
- `@rivinia/modal/ui` contains standalone dialog and host components.
|
|
@@ -593,3 +597,7 @@ The `@rivinia/modal/ui` entry point exports `RiviniaModalDialog`,
|
|
|
593
597
|
## License
|
|
594
598
|
|
|
595
599
|
MIT.
|
|
600
|
+
|
|
601
|
+
## Further reading
|
|
602
|
+
|
|
603
|
+
[Package design and usage notes](https://github.com/abdelfattahqandil21-oss/Revinia/blob/modal/v1.0.0/docs/decisions/D029-rivinia-modal-ui-on-native-dialog-no-cdk.md).
|
|
@@ -42,7 +42,7 @@ var RiviniaModalScrollLock = class RiviniaModalScrollLock {
|
|
|
42
42
|
}
|
|
43
43
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
44
44
|
minVersion: "12.0.0",
|
|
45
|
-
version: "22.2.
|
|
45
|
+
version: "22.2.3",
|
|
46
46
|
ngImport: i0,
|
|
47
47
|
type: RiviniaModalScrollLock,
|
|
48
48
|
deps: [],
|
|
@@ -50,14 +50,14 @@ var RiviniaModalScrollLock = class RiviniaModalScrollLock {
|
|
|
50
50
|
});
|
|
51
51
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
52
52
|
minVersion: "22.0.0",
|
|
53
|
-
version: "22.2.
|
|
53
|
+
version: "22.2.3",
|
|
54
54
|
ngImport: i0,
|
|
55
55
|
type: RiviniaModalScrollLock
|
|
56
56
|
});
|
|
57
57
|
};
|
|
58
58
|
i0.ɵɵngDeclareClassMetadata({
|
|
59
59
|
minVersion: "12.0.0",
|
|
60
|
-
version: "22.2.
|
|
60
|
+
version: "22.2.3",
|
|
61
61
|
ngImport: i0,
|
|
62
62
|
type: RiviniaModalScrollLock,
|
|
63
63
|
decorators: [{ type: Service }]
|
|
@@ -66,7 +66,7 @@ const EXIT_FALLBACK_MS = 200;
|
|
|
66
66
|
var RvModalHeader = class RvModalHeader {
|
|
67
67
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
68
68
|
minVersion: "12.0.0",
|
|
69
|
-
version: "22.2.
|
|
69
|
+
version: "22.2.3",
|
|
70
70
|
ngImport: i0,
|
|
71
71
|
type: RvModalHeader,
|
|
72
72
|
deps: [],
|
|
@@ -74,7 +74,7 @@ var RvModalHeader = class RvModalHeader {
|
|
|
74
74
|
});
|
|
75
75
|
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
76
76
|
minVersion: "14.0.0",
|
|
77
|
-
version: "22.2.
|
|
77
|
+
version: "22.2.3",
|
|
78
78
|
type: RvModalHeader,
|
|
79
79
|
isStandalone: true,
|
|
80
80
|
selector: "[rvModalHeader]",
|
|
@@ -83,7 +83,7 @@ var RvModalHeader = class RvModalHeader {
|
|
|
83
83
|
};
|
|
84
84
|
i0.ɵɵngDeclareClassMetadata({
|
|
85
85
|
minVersion: "12.0.0",
|
|
86
|
-
version: "22.2.
|
|
86
|
+
version: "22.2.3",
|
|
87
87
|
ngImport: i0,
|
|
88
88
|
type: RvModalHeader,
|
|
89
89
|
decorators: [{
|
|
@@ -97,7 +97,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
97
97
|
var RvModalFooter = class RvModalFooter {
|
|
98
98
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
99
99
|
minVersion: "12.0.0",
|
|
100
|
-
version: "22.2.
|
|
100
|
+
version: "22.2.3",
|
|
101
101
|
ngImport: i0,
|
|
102
102
|
type: RvModalFooter,
|
|
103
103
|
deps: [],
|
|
@@ -105,7 +105,7 @@ var RvModalFooter = class RvModalFooter {
|
|
|
105
105
|
});
|
|
106
106
|
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
107
107
|
minVersion: "14.0.0",
|
|
108
|
-
version: "22.2.
|
|
108
|
+
version: "22.2.3",
|
|
109
109
|
type: RvModalFooter,
|
|
110
110
|
isStandalone: true,
|
|
111
111
|
selector: "[rvModalFooter]",
|
|
@@ -114,7 +114,7 @@ var RvModalFooter = class RvModalFooter {
|
|
|
114
114
|
};
|
|
115
115
|
i0.ɵɵngDeclareClassMetadata({
|
|
116
116
|
minVersion: "12.0.0",
|
|
117
|
-
version: "22.2.
|
|
117
|
+
version: "22.2.3",
|
|
118
118
|
ngImport: i0,
|
|
119
119
|
type: RvModalFooter,
|
|
120
120
|
decorators: [{
|
|
@@ -160,6 +160,8 @@ var RiviniaModalDialog = class RiviniaModalDialog {
|
|
|
160
160
|
ngOnDestroy() {
|
|
161
161
|
this.stopResizeWatch();
|
|
162
162
|
if (this.settleTimer !== null) clearTimeout(this.settleTimer);
|
|
163
|
+
if (this.exitTimer !== null) clearTimeout(this.exitTimer);
|
|
164
|
+
if (this.phase() !== "closed") this.finalize(this.dialog()?.nativeElement ?? null);
|
|
163
165
|
}
|
|
164
166
|
showCloseButton = input(true, ...ngDevMode ? [{ debugName: "showCloseButton" }] : /* istanbul ignore next */ []);
|
|
165
167
|
managed = input(false, ...ngDevMode ? [{ debugName: "managed" }] : /* istanbul ignore next */ []);
|
|
@@ -351,7 +353,7 @@ var RiviniaModalDialog = class RiviniaModalDialog {
|
|
|
351
353
|
}
|
|
352
354
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
353
355
|
minVersion: "12.0.0",
|
|
354
|
-
version: "22.2.
|
|
356
|
+
version: "22.2.3",
|
|
355
357
|
ngImport: i0,
|
|
356
358
|
type: RiviniaModalDialog,
|
|
357
359
|
deps: [],
|
|
@@ -359,7 +361,7 @@ var RiviniaModalDialog = class RiviniaModalDialog {
|
|
|
359
361
|
});
|
|
360
362
|
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
361
363
|
minVersion: "17.0.0",
|
|
362
|
-
version: "22.2.
|
|
364
|
+
version: "22.2.3",
|
|
363
365
|
type: RiviniaModalDialog,
|
|
364
366
|
isStandalone: true,
|
|
365
367
|
selector: "rv-modal-dialog",
|
|
@@ -478,67 +480,14 @@ var RiviniaModalDialog = class RiviniaModalDialog {
|
|
|
478
480
|
isSignal: true
|
|
479
481
|
}],
|
|
480
482
|
ngImport: i0,
|
|
481
|
-
template:
|
|
482
|
-
<dialog
|
|
483
|
-
#dialog
|
|
484
|
-
tabindex="-1"
|
|
485
|
-
class="rv-modal"
|
|
486
|
-
[attr.data-size]="size()"
|
|
487
|
-
[attr.data-position]="position()"
|
|
488
|
-
[attr.data-animation]="animation()"
|
|
489
|
-
[attr.data-state]="state()"
|
|
490
|
-
[attr.data-resizing]="resizing()"
|
|
491
|
-
[attr.aria-label]="computedLabel()"
|
|
492
|
-
[attr.aria-labelledby]="title() && !ariaLabel() ? titleId : null"
|
|
493
|
-
[attr.aria-describedby]="description() ? descriptionId : null"
|
|
494
|
-
(cancel)="onCancel($event)"
|
|
495
|
-
(click)="onBackdropClick($event)"
|
|
496
|
-
(keydown)="onKeydown($event)"
|
|
497
|
-
>
|
|
498
|
-
@if (hasCustomHeader()) {
|
|
499
|
-
<div class="rv-modal-header rv-modal-header-custom">
|
|
500
|
-
<ng-content select="[rv-modal-header]"></ng-content>
|
|
501
|
-
</div>
|
|
502
|
-
} @else if (title() || showCloseButton()) {
|
|
503
|
-
<div class="rv-modal-header">
|
|
504
|
-
<div class="rv-modal-heading">
|
|
505
|
-
@if (title()) {
|
|
506
|
-
<h2 class="rv-modal-title" [id]="titleId">{{ title() }}</h2>
|
|
507
|
-
}
|
|
508
|
-
@if (description()) {
|
|
509
|
-
<p class="rv-modal-description" [id]="descriptionId">{{ description() }}</p>
|
|
510
|
-
}
|
|
511
|
-
</div>
|
|
512
|
-
@if (showCloseButton()) {
|
|
513
|
-
<button
|
|
514
|
-
type="button"
|
|
515
|
-
class="rv-modal-close"
|
|
516
|
-
(click)="onCloseButton()"
|
|
517
|
-
aria-label="Close dialog"
|
|
518
|
-
>
|
|
519
|
-
<span aria-hidden="true">×</span>
|
|
520
|
-
</button>
|
|
521
|
-
}
|
|
522
|
-
</div>
|
|
523
|
-
}
|
|
524
|
-
<div class="rv-modal-body">
|
|
525
|
-
<ng-content></ng-content>
|
|
526
|
-
</div>
|
|
527
|
-
@if (hasCustomFooter()) {
|
|
528
|
-
<div class="rv-modal-footer">
|
|
529
|
-
<ng-content select="[rv-modal-footer]"></ng-content>
|
|
530
|
-
</div>
|
|
531
|
-
}
|
|
532
|
-
</dialog>
|
|
533
|
-
`,
|
|
534
|
-
isInline: true,
|
|
483
|
+
template: "<dialog\n #dialog\n tabindex=\"-1\"\n class=\"rv-modal\"\n [attr.data-size]=\"size()\"\n [attr.data-position]=\"position()\"\n [attr.data-animation]=\"animation()\"\n [attr.data-state]=\"state()\"\n [attr.data-resizing]=\"resizing()\"\n [attr.aria-label]=\"computedLabel()\"\n [attr.aria-labelledby]=\"title() && !ariaLabel() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n (cancel)=\"onCancel($event)\"\n (click)=\"onBackdropClick($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n @if (hasCustomHeader()) {\n <div class=\"rv-modal-header rv-modal-header-custom\">\n <ng-content select=\"[rv-modal-header]\"></ng-content>\n </div>\n } @else if (title() || showCloseButton()) {\n <div class=\"rv-modal-header\">\n <div class=\"rv-modal-heading\">\n @if (title()) {\n <h2 class=\"rv-modal-title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (description()) {\n <p class=\"rv-modal-description\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"rv-modal-close\"\n (click)=\"onCloseButton()\"\n aria-label=\"Close dialog\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n }\n </div>\n }\n <div class=\"rv-modal-body\">\n <ng-content></ng-content>\n </div>\n @if (hasCustomFooter()) {\n <div class=\"rv-modal-footer\">\n <ng-content select=\"[rv-modal-footer]\"></ng-content>\n </div>\n }\n</dialog>\n",
|
|
535
484
|
styles: [".rv-modal{color-scheme:light dark;--rv-modal-pad: 20px;--rv-modal-gap: 12px;--rv-modal-width-sm: clamp(16rem, 40vw, 24rem);--rv-modal-width-md: clamp(20rem, 55vw, 32rem);--rv-modal-width-lg: clamp(24rem, 70vw, 48rem);--rv-modal-width-xl: clamp(28rem, 85vw, 64rem);--rv-modal-max-height: min(86dvh, 100%);--rv-modal-duration: .15s;--rv-modal-duration-in: var(--rv-modal-duration);--rv-modal-duration-out: .12s;--rv-modal-easing: ease-out;--rv-modal-easing-in: var(--rv-modal-easing);--rv-modal-easing-out: ease-in;--rv-modal-radius-sheet: 1.25rem;--rv-modal-width-panel: 24rem;padding:0;border:1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));border-radius:var(--rv-modal-radius, var(--rv-radius, 12px));background:var(--rv-modal-bg, var(--rv-bg, #ffffff));color:var(--rv-modal-fg, var(--rv-fg, #111111));box-shadow:var(--rv-modal-shadow, var(--rv-shadow, 0 24px 64px rgba(0, 0, 0, .28)));max-height:var(--rv-modal-max-height);width:var(--rv-modal-width-md);max-width:calc(100dvw - 2rem);transition:width var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),height var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),max-height var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),inset var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),margin var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out)}.rv-modal[data-resizing=true]{transition:none!important}.rv-modal-loading{display:flex;align-items:center;justify-content:center;gap:var(--rv-modal-loading-gap, .625rem);padding:var(--rv-modal-loading-padding, 2rem);min-height:var(--rv-modal-loading-min-height, 6rem);color:var(--rv-modal-muted, var(--rv-muted, #555555));font-size:var(--rv-modal-loading-size, .875rem)}.rv-modal-spinner{inline-size:var(--rv-modal-spinner-size, 1.125rem);block-size:var(--rv-modal-spinner-size, 1.125rem);border:2px solid currentColor;border-block-start-color:transparent;border-radius:50%;animation:rv-modal-spin var(--rv-modal-spinner-duration, .7s) linear infinite}@keyframes rv-modal-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.rv-modal-spinner{animation:none}}.rv-modal::backdrop{background:var(--rv-modal-backdrop, rgba(0, 0, 0, .5))}.rv-modal[data-size=sm]{width:var(--rv-modal-width-sm)}.rv-modal[data-size=lg]{width:var(--rv-modal-width-lg)}.rv-modal[data-size=xl]{width:var(--rv-modal-width-xl)}.rv-modal[data-size=full]{width:calc(100dvw - 2rem)}.rv-modal[data-size=auto]{width:auto}.rv-modal[data-position=top]{margin-block-start:8dvh}.rv-modal[data-position=bottom]{margin-block-start:auto;margin-block-end:8dvh}.rv-modal[data-position=top-start],.rv-modal[data-position=center-start]{margin-inline-start:1rem;margin-inline-end:auto}.rv-modal[data-position=top-end],.rv-modal[data-position=center-end]{margin-inline-start:auto;margin-inline-end:1rem}.rv-modal[data-position=bottom-start]{margin-block-start:auto;margin-inline-start:1rem}.rv-modal[data-position=bottom-end]{margin-block-start:auto;margin-inline-end:1rem}.rv-modal[data-position=fullscreen]{inset:0;width:100dvw;height:100dvh;max-width:none;max-height:none;border-radius:0}.rv-modal[data-position=side-start],.rv-modal[data-position=side-end]{height:100dvh;max-height:none;width:var(--rv-modal-width-panel);margin-block:0}.rv-modal[data-position=side-start]{margin-inline-start:0;margin-inline-end:auto;border-start-start-radius:0;border-end-start-radius:0}.rv-modal[data-position=side-end]{margin-inline-start:auto;margin-inline-end:0;border-start-end-radius:0;border-end-end-radius:0}@media(max-width:640px){.rv-modal[data-position=center],.rv-modal[data-position=top],.rv-modal[data-position=bottom],.rv-modal[data-position=bottom-start],.rv-modal[data-position=bottom-end]{margin-block-start:auto;margin-block-end:0;margin-inline:0;width:100dvw;max-width:none;border-end-end-radius:0;border-end-start-radius:0;border-start-start-radius:var(--rv-modal-radius-sheet);border-start-end-radius:var(--rv-modal-radius-sheet)}.rv-modal[data-position=center][data-state=open],.rv-modal[data-position=top][data-state=open],.rv-modal[data-position=bottom][data-state=open],.rv-modal[data-position=bottom-start][data-state=open],.rv-modal[data-position=bottom-end][data-state=open]{animation-name:rv-modal-in-bottom}}.rv-modal[data-animation]:not([data-animation=none])[data-state=open]{animation:var(--rv-modal-animation-in, rv-modal-in) var(--rv-modal-duration-in) var(--rv-modal-easing-in)}.rv-modal[data-animation=scale][data-state=open]{--rv-modal-animation-in: rv-modal-in-scale}.rv-modal[data-animation=slide-top][data-state=open]{--rv-modal-animation-in: rv-modal-in-top}.rv-modal[data-animation=slide-bottom][data-state=open]{--rv-modal-animation-in: rv-modal-in-bottom}.rv-modal[data-animation=slide-start][data-state=open]{--rv-modal-animation-in: rv-modal-in-start}.rv-modal[data-animation=slide-end][data-state=open],[dir=rtl] .rv-modal[data-animation=slide-start][data-state=open]{--rv-modal-animation-in: rv-modal-in-end}[dir=rtl] .rv-modal[data-animation=slide-end][data-state=open]{--rv-modal-animation-in: rv-modal-in-start}.rv-modal[data-state=closing]{animation:rv-modal-out var(--rv-modal-duration-out) var(--rv-modal-easing-out) forwards}@keyframes rv-modal-in{0%{opacity:0}}@keyframes rv-modal-in-scale{0%{opacity:0;transform:translateY(8px) scale(.98)}}@keyframes rv-modal-in-top{0%{opacity:0;transform:translateY(-24px)}}@keyframes rv-modal-in-bottom{0%{opacity:0;transform:translateY(24px)}}@keyframes rv-modal-in-start{0%{opacity:0;transform:translate(-24px)}}@keyframes rv-modal-in-end{0%{opacity:0;transform:translate(24px)}}@keyframes rv-modal-out{to{opacity:0;transform:translateY(8px) scale(.98)}}@media(prefers-reduced-motion:reduce){.rv-modal[data-animation]{animation:none}}.rv-modal-header{display:flex;align-items:flex-start;gap:var(--rv-modal-gap);padding:var(--rv-modal-pad) var(--rv-modal-pad) 0}.rv-modal-header-custom>:first-child{flex:1;min-width:0}.rv-modal-heading:empty{display:none}.rv-modal-title{margin:0;font-size:1.125rem;flex:1}.rv-modal-heading{flex:1;display:flex;flex-direction:column;gap:.25rem}.rv-modal-description{margin:0;font-size:.875rem;color:var(--rv-modal-muted, var(--rv-muted, #555555))}.rv-modal-close{margin-inline-start:auto;flex:none;border:1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));border-radius:.5rem;background:transparent;color:inherit;font-size:1.25rem;line-height:1;padding:.25rem .625rem;cursor:pointer}.rv-modal-close:focus-visible{outline:2px solid currentColor;outline-offset:2px}.rv-modal-body{padding:var(--rv-modal-pad);overflow:auto;max-height:inherit}.rv-modal-footer{display:flex;gap:var(--rv-modal-gap);justify-content:flex-end;padding:0 var(--rv-modal-pad) var(--rv-modal-pad)}.rv-modal-footer:empty{display:none}\n"],
|
|
536
485
|
encapsulation: i0.ViewEncapsulation.None
|
|
537
486
|
});
|
|
538
487
|
};
|
|
539
488
|
i0.ɵɵngDeclareClassMetadata({
|
|
540
489
|
minVersion: "12.0.0",
|
|
541
|
-
version: "22.2.
|
|
490
|
+
version: "22.2.3",
|
|
542
491
|
ngImport: i0,
|
|
543
492
|
type: RiviniaModalDialog,
|
|
544
493
|
decorators: [{
|
|
@@ -546,59 +495,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
546
495
|
args: [{
|
|
547
496
|
selector: "rv-modal-dialog",
|
|
548
497
|
encapsulation: ViewEncapsulation.None,
|
|
549
|
-
template:
|
|
550
|
-
<dialog
|
|
551
|
-
#dialog
|
|
552
|
-
tabindex="-1"
|
|
553
|
-
class="rv-modal"
|
|
554
|
-
[attr.data-size]="size()"
|
|
555
|
-
[attr.data-position]="position()"
|
|
556
|
-
[attr.data-animation]="animation()"
|
|
557
|
-
[attr.data-state]="state()"
|
|
558
|
-
[attr.data-resizing]="resizing()"
|
|
559
|
-
[attr.aria-label]="computedLabel()"
|
|
560
|
-
[attr.aria-labelledby]="title() && !ariaLabel() ? titleId : null"
|
|
561
|
-
[attr.aria-describedby]="description() ? descriptionId : null"
|
|
562
|
-
(cancel)="onCancel($event)"
|
|
563
|
-
(click)="onBackdropClick($event)"
|
|
564
|
-
(keydown)="onKeydown($event)"
|
|
565
|
-
>
|
|
566
|
-
@if (hasCustomHeader()) {
|
|
567
|
-
<div class="rv-modal-header rv-modal-header-custom">
|
|
568
|
-
<ng-content select="[rv-modal-header]"></ng-content>
|
|
569
|
-
</div>
|
|
570
|
-
} @else if (title() || showCloseButton()) {
|
|
571
|
-
<div class="rv-modal-header">
|
|
572
|
-
<div class="rv-modal-heading">
|
|
573
|
-
@if (title()) {
|
|
574
|
-
<h2 class="rv-modal-title" [id]="titleId">{{ title() }}</h2>
|
|
575
|
-
}
|
|
576
|
-
@if (description()) {
|
|
577
|
-
<p class="rv-modal-description" [id]="descriptionId">{{ description() }}</p>
|
|
578
|
-
}
|
|
579
|
-
</div>
|
|
580
|
-
@if (showCloseButton()) {
|
|
581
|
-
<button
|
|
582
|
-
type="button"
|
|
583
|
-
class="rv-modal-close"
|
|
584
|
-
(click)="onCloseButton()"
|
|
585
|
-
aria-label="Close dialog"
|
|
586
|
-
>
|
|
587
|
-
<span aria-hidden="true">×</span>
|
|
588
|
-
</button>
|
|
589
|
-
}
|
|
590
|
-
</div>
|
|
591
|
-
}
|
|
592
|
-
<div class="rv-modal-body">
|
|
593
|
-
<ng-content></ng-content>
|
|
594
|
-
</div>
|
|
595
|
-
@if (hasCustomFooter()) {
|
|
596
|
-
<div class="rv-modal-footer">
|
|
597
|
-
<ng-content select="[rv-modal-footer]"></ng-content>
|
|
598
|
-
</div>
|
|
599
|
-
}
|
|
600
|
-
</dialog>
|
|
601
|
-
`,
|
|
498
|
+
template: "<dialog\n #dialog\n tabindex=\"-1\"\n class=\"rv-modal\"\n [attr.data-size]=\"size()\"\n [attr.data-position]=\"position()\"\n [attr.data-animation]=\"animation()\"\n [attr.data-state]=\"state()\"\n [attr.data-resizing]=\"resizing()\"\n [attr.aria-label]=\"computedLabel()\"\n [attr.aria-labelledby]=\"title() && !ariaLabel() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n (cancel)=\"onCancel($event)\"\n (click)=\"onBackdropClick($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n @if (hasCustomHeader()) {\n <div class=\"rv-modal-header rv-modal-header-custom\">\n <ng-content select=\"[rv-modal-header]\"></ng-content>\n </div>\n } @else if (title() || showCloseButton()) {\n <div class=\"rv-modal-header\">\n <div class=\"rv-modal-heading\">\n @if (title()) {\n <h2 class=\"rv-modal-title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (description()) {\n <p class=\"rv-modal-description\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"rv-modal-close\"\n (click)=\"onCloseButton()\"\n aria-label=\"Close dialog\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n }\n </div>\n }\n <div class=\"rv-modal-body\">\n <ng-content></ng-content>\n </div>\n @if (hasCustomFooter()) {\n <div class=\"rv-modal-footer\">\n <ng-content select=\"[rv-modal-footer]\"></ng-content>\n </div>\n }\n</dialog>\n",
|
|
602
499
|
styles: [".rv-modal{color-scheme:light dark;--rv-modal-pad: 20px;--rv-modal-gap: 12px;--rv-modal-width-sm: clamp(16rem, 40vw, 24rem);--rv-modal-width-md: clamp(20rem, 55vw, 32rem);--rv-modal-width-lg: clamp(24rem, 70vw, 48rem);--rv-modal-width-xl: clamp(28rem, 85vw, 64rem);--rv-modal-max-height: min(86dvh, 100%);--rv-modal-duration: .15s;--rv-modal-duration-in: var(--rv-modal-duration);--rv-modal-duration-out: .12s;--rv-modal-easing: ease-out;--rv-modal-easing-in: var(--rv-modal-easing);--rv-modal-easing-out: ease-in;--rv-modal-radius-sheet: 1.25rem;--rv-modal-width-panel: 24rem;padding:0;border:1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));border-radius:var(--rv-modal-radius, var(--rv-radius, 12px));background:var(--rv-modal-bg, var(--rv-bg, #ffffff));color:var(--rv-modal-fg, var(--rv-fg, #111111));box-shadow:var(--rv-modal-shadow, var(--rv-shadow, 0 24px 64px rgba(0, 0, 0, .28)));max-height:var(--rv-modal-max-height);width:var(--rv-modal-width-md);max-width:calc(100dvw - 2rem);transition:width var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),height var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),max-height var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),inset var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out),margin var(--rv-modal-size-duration, .18s) var(--rv-modal-easing, ease-out)}.rv-modal[data-resizing=true]{transition:none!important}.rv-modal-loading{display:flex;align-items:center;justify-content:center;gap:var(--rv-modal-loading-gap, .625rem);padding:var(--rv-modal-loading-padding, 2rem);min-height:var(--rv-modal-loading-min-height, 6rem);color:var(--rv-modal-muted, var(--rv-muted, #555555));font-size:var(--rv-modal-loading-size, .875rem)}.rv-modal-spinner{inline-size:var(--rv-modal-spinner-size, 1.125rem);block-size:var(--rv-modal-spinner-size, 1.125rem);border:2px solid currentColor;border-block-start-color:transparent;border-radius:50%;animation:rv-modal-spin var(--rv-modal-spinner-duration, .7s) linear infinite}@keyframes rv-modal-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.rv-modal-spinner{animation:none}}.rv-modal::backdrop{background:var(--rv-modal-backdrop, rgba(0, 0, 0, .5))}.rv-modal[data-size=sm]{width:var(--rv-modal-width-sm)}.rv-modal[data-size=lg]{width:var(--rv-modal-width-lg)}.rv-modal[data-size=xl]{width:var(--rv-modal-width-xl)}.rv-modal[data-size=full]{width:calc(100dvw - 2rem)}.rv-modal[data-size=auto]{width:auto}.rv-modal[data-position=top]{margin-block-start:8dvh}.rv-modal[data-position=bottom]{margin-block-start:auto;margin-block-end:8dvh}.rv-modal[data-position=top-start],.rv-modal[data-position=center-start]{margin-inline-start:1rem;margin-inline-end:auto}.rv-modal[data-position=top-end],.rv-modal[data-position=center-end]{margin-inline-start:auto;margin-inline-end:1rem}.rv-modal[data-position=bottom-start]{margin-block-start:auto;margin-inline-start:1rem}.rv-modal[data-position=bottom-end]{margin-block-start:auto;margin-inline-end:1rem}.rv-modal[data-position=fullscreen]{inset:0;width:100dvw;height:100dvh;max-width:none;max-height:none;border-radius:0}.rv-modal[data-position=side-start],.rv-modal[data-position=side-end]{height:100dvh;max-height:none;width:var(--rv-modal-width-panel);margin-block:0}.rv-modal[data-position=side-start]{margin-inline-start:0;margin-inline-end:auto;border-start-start-radius:0;border-end-start-radius:0}.rv-modal[data-position=side-end]{margin-inline-start:auto;margin-inline-end:0;border-start-end-radius:0;border-end-end-radius:0}@media(max-width:640px){.rv-modal[data-position=center],.rv-modal[data-position=top],.rv-modal[data-position=bottom],.rv-modal[data-position=bottom-start],.rv-modal[data-position=bottom-end]{margin-block-start:auto;margin-block-end:0;margin-inline:0;width:100dvw;max-width:none;border-end-end-radius:0;border-end-start-radius:0;border-start-start-radius:var(--rv-modal-radius-sheet);border-start-end-radius:var(--rv-modal-radius-sheet)}.rv-modal[data-position=center][data-state=open],.rv-modal[data-position=top][data-state=open],.rv-modal[data-position=bottom][data-state=open],.rv-modal[data-position=bottom-start][data-state=open],.rv-modal[data-position=bottom-end][data-state=open]{animation-name:rv-modal-in-bottom}}.rv-modal[data-animation]:not([data-animation=none])[data-state=open]{animation:var(--rv-modal-animation-in, rv-modal-in) var(--rv-modal-duration-in) var(--rv-modal-easing-in)}.rv-modal[data-animation=scale][data-state=open]{--rv-modal-animation-in: rv-modal-in-scale}.rv-modal[data-animation=slide-top][data-state=open]{--rv-modal-animation-in: rv-modal-in-top}.rv-modal[data-animation=slide-bottom][data-state=open]{--rv-modal-animation-in: rv-modal-in-bottom}.rv-modal[data-animation=slide-start][data-state=open]{--rv-modal-animation-in: rv-modal-in-start}.rv-modal[data-animation=slide-end][data-state=open],[dir=rtl] .rv-modal[data-animation=slide-start][data-state=open]{--rv-modal-animation-in: rv-modal-in-end}[dir=rtl] .rv-modal[data-animation=slide-end][data-state=open]{--rv-modal-animation-in: rv-modal-in-start}.rv-modal[data-state=closing]{animation:rv-modal-out var(--rv-modal-duration-out) var(--rv-modal-easing-out) forwards}@keyframes rv-modal-in{0%{opacity:0}}@keyframes rv-modal-in-scale{0%{opacity:0;transform:translateY(8px) scale(.98)}}@keyframes rv-modal-in-top{0%{opacity:0;transform:translateY(-24px)}}@keyframes rv-modal-in-bottom{0%{opacity:0;transform:translateY(24px)}}@keyframes rv-modal-in-start{0%{opacity:0;transform:translate(-24px)}}@keyframes rv-modal-in-end{0%{opacity:0;transform:translate(24px)}}@keyframes rv-modal-out{to{opacity:0;transform:translateY(8px) scale(.98)}}@media(prefers-reduced-motion:reduce){.rv-modal[data-animation]{animation:none}}.rv-modal-header{display:flex;align-items:flex-start;gap:var(--rv-modal-gap);padding:var(--rv-modal-pad) var(--rv-modal-pad) 0}.rv-modal-header-custom>:first-child{flex:1;min-width:0}.rv-modal-heading:empty{display:none}.rv-modal-title{margin:0;font-size:1.125rem;flex:1}.rv-modal-heading{flex:1;display:flex;flex-direction:column;gap:.25rem}.rv-modal-description{margin:0;font-size:.875rem;color:var(--rv-modal-muted, var(--rv-muted, #555555))}.rv-modal-close{margin-inline-start:auto;flex:none;border:1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));border-radius:.5rem;background:transparent;color:inherit;font-size:1.25rem;line-height:1;padding:.25rem .625rem;cursor:pointer}.rv-modal-close:focus-visible{outline:2px solid currentColor;outline-offset:2px}.rv-modal-body{padding:var(--rv-modal-pad);overflow:auto;max-height:inherit}.rv-modal-footer{display:flex;gap:var(--rv-modal-gap);justify-content:flex-end;padding:0 var(--rv-modal-pad) var(--rv-modal-pad)}.rv-modal-footer:empty{display:none}\n"]
|
|
603
500
|
}]
|
|
604
501
|
}],
|
|
@@ -869,7 +766,7 @@ var RiviniaModalHost = class RiviniaModalHost {
|
|
|
869
766
|
}
|
|
870
767
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
871
768
|
minVersion: "12.0.0",
|
|
872
|
-
version: "22.2.
|
|
769
|
+
version: "22.2.3",
|
|
873
770
|
ngImport: i0,
|
|
874
771
|
type: RiviniaModalHost,
|
|
875
772
|
deps: [],
|
|
@@ -877,7 +774,7 @@ var RiviniaModalHost = class RiviniaModalHost {
|
|
|
877
774
|
});
|
|
878
775
|
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
879
776
|
minVersion: "17.0.0",
|
|
880
|
-
version: "22.2.
|
|
777
|
+
version: "22.2.3",
|
|
881
778
|
type: RiviniaModalHost,
|
|
882
779
|
isStandalone: true,
|
|
883
780
|
selector: "rv-modal-host",
|
|
@@ -926,55 +823,7 @@ var RiviniaModalHost = class RiviniaModalHost {
|
|
|
926
823
|
}
|
|
927
824
|
},
|
|
928
825
|
ngImport: i0,
|
|
929
|
-
template:
|
|
930
|
-
@for (instance of visible(); track instance.id) {
|
|
931
|
-
@if (isCovered(instance)) {
|
|
932
|
-
<!--
|
|
933
|
-
Hidden by a dialog above it that asked to cover the others. It stays
|
|
934
|
-
in the manager — only the rendering is skipped, so it comes back when
|
|
935
|
-
the cover closes.
|
|
936
|
-
-->
|
|
937
|
-
} @else {
|
|
938
|
-
<rv-modal-dialog
|
|
939
|
-
[open]="true"
|
|
940
|
-
[managed]="true"
|
|
941
|
-
[closing]="instance.status === 'closing'"
|
|
942
|
-
[title]="titleOfInstance(instance)"
|
|
943
|
-
[size]="sizeOf(instance)"
|
|
944
|
-
[attr.data-caller]="callerName(instance)"
|
|
945
|
-
[showCloseButton]="showCloseButton()"
|
|
946
|
-
[closeOnEscape]="instance.dismiss.closeOnEscape ?? true"
|
|
947
|
-
[backdrop]="instance.dismiss.backdrop ?? 'close'"
|
|
948
|
-
(dismissRequested)="onIntent(instance.id, $event)"
|
|
949
|
-
(exitDone)="onExitDone(instance.id)"
|
|
950
|
-
>
|
|
951
|
-
@if (isLoading(instance)) {
|
|
952
|
-
<!--
|
|
953
|
-
The data has not arrived yet. A loader says so; an empty dialog
|
|
954
|
-
looks broken.
|
|
955
|
-
-->
|
|
956
|
-
<div class="rv-modal-loading" role="status" aria-live="polite">
|
|
957
|
-
<span class="rv-modal-spinner" aria-hidden="true"></span>
|
|
958
|
-
<span>{{ loadingLabel() }}</span>
|
|
959
|
-
</div>
|
|
960
|
-
} @else if (instance.content.kind === 'template') {
|
|
961
|
-
<ng-container
|
|
962
|
-
*ngTemplateOutlet="instance.content.template; context: templateContext(instance)"
|
|
963
|
-
></ng-container>
|
|
964
|
-
} @else {
|
|
965
|
-
<ng-container
|
|
966
|
-
*ngComponentOutlet="
|
|
967
|
-
instance.content.component;
|
|
968
|
-
inputs: instance.content.inputs;
|
|
969
|
-
injector: contextInjector(instance)
|
|
970
|
-
"
|
|
971
|
-
></ng-container>
|
|
972
|
-
}
|
|
973
|
-
</rv-modal-dialog>
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
`,
|
|
977
|
-
isInline: true,
|
|
826
|
+
template: "@for (instance of visible(); track instance.id) {\n @if (isCovered(instance)) {\n <!--\n Hidden by a dialog above it that asked to cover the others. It stays\n in the manager — only the rendering is skipped, so it comes back when\n the cover closes.\n -->\n } @else {\n <rv-modal-dialog\n [open]=\"true\"\n [managed]=\"true\"\n [closing]=\"instance.status === 'closing'\"\n [title]=\"titleOfInstance(instance)\"\n [size]=\"sizeOf(instance)\"\n [attr.data-caller]=\"callerName(instance)\"\n [showCloseButton]=\"showCloseButton()\"\n [closeOnEscape]=\"instance.dismiss.closeOnEscape ?? true\"\n [backdrop]=\"instance.dismiss.backdrop ?? 'close'\"\n (dismissRequested)=\"onIntent(instance.id, $event)\"\n (exitDone)=\"onExitDone(instance.id)\"\n >\n @if (isLoading(instance)) {\n <!--\n The data has not arrived yet. A loader says so; an empty dialog\n looks broken.\n -->\n <div class=\"rv-modal-loading\" role=\"status\" aria-live=\"polite\">\n <span class=\"rv-modal-spinner\" aria-hidden=\"true\"></span>\n <span>{{ loadingLabel() }}</span>\n </div>\n } @else if (instance.content.kind === 'template') {\n <ng-container\n *ngTemplateOutlet=\"instance.content.template; context: templateContext(instance)\"\n ></ng-container>\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n instance.content.component;\n inputs: instance.content.inputs;\n injector: contextInjector(instance)\n \"\n ></ng-container>\n }\n </rv-modal-dialog>\n }\n}\n",
|
|
978
827
|
dependencies: [
|
|
979
828
|
{
|
|
980
829
|
kind: "component",
|
|
@@ -1032,7 +881,7 @@ var RiviniaModalHost = class RiviniaModalHost {
|
|
|
1032
881
|
};
|
|
1033
882
|
i0.ɵɵngDeclareClassMetadata({
|
|
1034
883
|
minVersion: "12.0.0",
|
|
1035
|
-
version: "22.2.
|
|
884
|
+
version: "22.2.3",
|
|
1036
885
|
ngImport: i0,
|
|
1037
886
|
type: RiviniaModalHost,
|
|
1038
887
|
decorators: [{
|
|
@@ -1044,54 +893,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1044
893
|
NgComponentOutlet,
|
|
1045
894
|
NgTemplateOutlet
|
|
1046
895
|
],
|
|
1047
|
-
template:
|
|
1048
|
-
@for (instance of visible(); track instance.id) {
|
|
1049
|
-
@if (isCovered(instance)) {
|
|
1050
|
-
<!--
|
|
1051
|
-
Hidden by a dialog above it that asked to cover the others. It stays
|
|
1052
|
-
in the manager — only the rendering is skipped, so it comes back when
|
|
1053
|
-
the cover closes.
|
|
1054
|
-
-->
|
|
1055
|
-
} @else {
|
|
1056
|
-
<rv-modal-dialog
|
|
1057
|
-
[open]="true"
|
|
1058
|
-
[managed]="true"
|
|
1059
|
-
[closing]="instance.status === 'closing'"
|
|
1060
|
-
[title]="titleOfInstance(instance)"
|
|
1061
|
-
[size]="sizeOf(instance)"
|
|
1062
|
-
[attr.data-caller]="callerName(instance)"
|
|
1063
|
-
[showCloseButton]="showCloseButton()"
|
|
1064
|
-
[closeOnEscape]="instance.dismiss.closeOnEscape ?? true"
|
|
1065
|
-
[backdrop]="instance.dismiss.backdrop ?? 'close'"
|
|
1066
|
-
(dismissRequested)="onIntent(instance.id, $event)"
|
|
1067
|
-
(exitDone)="onExitDone(instance.id)"
|
|
1068
|
-
>
|
|
1069
|
-
@if (isLoading(instance)) {
|
|
1070
|
-
<!--
|
|
1071
|
-
The data has not arrived yet. A loader says so; an empty dialog
|
|
1072
|
-
looks broken.
|
|
1073
|
-
-->
|
|
1074
|
-
<div class="rv-modal-loading" role="status" aria-live="polite">
|
|
1075
|
-
<span class="rv-modal-spinner" aria-hidden="true"></span>
|
|
1076
|
-
<span>{{ loadingLabel() }}</span>
|
|
1077
|
-
</div>
|
|
1078
|
-
} @else if (instance.content.kind === 'template') {
|
|
1079
|
-
<ng-container
|
|
1080
|
-
*ngTemplateOutlet="instance.content.template; context: templateContext(instance)"
|
|
1081
|
-
></ng-container>
|
|
1082
|
-
} @else {
|
|
1083
|
-
<ng-container
|
|
1084
|
-
*ngComponentOutlet="
|
|
1085
|
-
instance.content.component;
|
|
1086
|
-
inputs: instance.content.inputs;
|
|
1087
|
-
injector: contextInjector(instance)
|
|
1088
|
-
"
|
|
1089
|
-
></ng-container>
|
|
1090
|
-
}
|
|
1091
|
-
</rv-modal-dialog>
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
`
|
|
896
|
+
template: "@for (instance of visible(); track instance.id) {\n @if (isCovered(instance)) {\n <!--\n Hidden by a dialog above it that asked to cover the others. It stays\n in the manager — only the rendering is skipped, so it comes back when\n the cover closes.\n -->\n } @else {\n <rv-modal-dialog\n [open]=\"true\"\n [managed]=\"true\"\n [closing]=\"instance.status === 'closing'\"\n [title]=\"titleOfInstance(instance)\"\n [size]=\"sizeOf(instance)\"\n [attr.data-caller]=\"callerName(instance)\"\n [showCloseButton]=\"showCloseButton()\"\n [closeOnEscape]=\"instance.dismiss.closeOnEscape ?? true\"\n [backdrop]=\"instance.dismiss.backdrop ?? 'close'\"\n (dismissRequested)=\"onIntent(instance.id, $event)\"\n (exitDone)=\"onExitDone(instance.id)\"\n >\n @if (isLoading(instance)) {\n <!--\n The data has not arrived yet. A loader says so; an empty dialog\n looks broken.\n -->\n <div class=\"rv-modal-loading\" role=\"status\" aria-live=\"polite\">\n <span class=\"rv-modal-spinner\" aria-hidden=\"true\"></span>\n <span>{{ loadingLabel() }}</span>\n </div>\n } @else if (instance.content.kind === 'template') {\n <ng-container\n *ngTemplateOutlet=\"instance.content.template; context: templateContext(instance)\"\n ></ng-container>\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n instance.content.component;\n inputs: instance.content.inputs;\n injector: contextInjector(instance)\n \"\n ></ng-container>\n }\n </rv-modal-dialog>\n }\n}\n"
|
|
1095
897
|
}]
|
|
1096
898
|
}],
|
|
1097
899
|
propDecorators: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rivinia-modal-ui.mjs","names":[],"sources":["../../../projects/modal/ui/src/lib/modal-scroll-lock.ts","../../../projects/modal/ui/src/lib/modal-dialog.ts","../../../projects/modal/ui/src/lib/modal-covers.ts","../../../projects/modal/ui/src/lib/modal-host.ts"],"sourcesContent":["import { DOCUMENT, Service, inject } from '@angular/core';\n\n/**\n * Reference-counted background scroll lock. Multiple overlays share one lock:\n * closing one modal never unlocks the page while another still needs it.\n * Restores the exact previous `overflow` value (never blanks app-owned\n * styles). All DOM access happens inside methods — never at module\n * evaluation — and every method is a no-op without a document (SSR).\n */\n@Service()\nexport class RiviniaModalScrollLock {\n private readonly _doc = inject(DOCUMENT, { optional: true });\n private _locks = 0;\n private _previousOverflow: string | null = null;\n\n /** Number of active locks (diagnostics/tests). */\n get active(): number {\n return this._locks;\n }\n\n acquire(): void {\n const doc = this.document;\n if (!doc) return;\n this._locks += 1;\n if (this._locks > 1) return;\n const body = doc.body;\n this._previousOverflow = body.style.overflow || null;\n body.style.overflow = 'hidden';\n // Reserve the scrollbar gutter where supported so content does not jump.\n // Overlay scrollbars ignore it; that is documented, not promised.\n doc.documentElement.style.setProperty('scrollbar-gutter', 'stable');\n }\n\n release(): void {\n const doc = this.document;\n if (!doc || this._locks === 0) {\n this._locks = 0;\n return;\n }\n this._locks -= 1;\n if (this._locks > 0) return;\n const body = doc.body;\n if (this._previousOverflow === null) body.style.removeProperty('overflow');\n else body.style.overflow = this._previousOverflow;\n this._previousOverflow = null;\n doc.documentElement.style.removeProperty('scrollbar-gutter');\n }\n\n private get document(): Document | null {\n try {\n return this._doc ?? (typeof document === 'undefined' ? null : document);\n } catch {\n return null;\n }\n }\n}\n","import {\n Component,\n OnDestroy,\n Directive,\n DOCUMENT,\n ElementRef,\n ViewEncapsulation,\n computed,\n contentChild,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { RiviniaModalScrollLock } from './modal-scroll-lock';\nimport type {\n RiviniaModalAnimation,\n RiviniaModalBackdrop,\n RiviniaModalPosition,\n RiviniaModalSize,\n} from './ui-types';\n\nconst EXIT_FALLBACK_MS = 200;\n\n/**\n * Marker for custom header content. Put it on the element carrying\n * `rv-modal-header` — its presence (not just the attribute) tells the\n * dialog to hide the default title + close button:\n *\n * ```html\n * <div rv-modal-header rvModalHeader>My header</div>\n * ```\n */\n@Directive({ selector: '[rvModalHeader]', standalone: true })\nexport class RvModalHeader {}\n\n/**\n * Marker for custom footer content. Put it on the element carrying\n * `rv-modal-footer`. Without it the footer region never renders.\n */\n@Directive({ selector: '[rvModalFooter]', standalone: true })\nexport class RvModalFooter {}\n\n/**\n * Ready-made modal dialog on the native `<dialog>` element (top layer,\n * `::backdrop`, inert background, Escape `cancel` event — all platform\n * behavior, zero overlay dependencies).\n *\n * UI-only by design: it owns its own `open` state and never imports the\n * headless controller. Pair it with `@rivinia/modal` through\n * {@link RiviniaModalHost} (or your own adapter) when you want managed\n * lifecycle, guards and typed results.\n *\n * Projection: `[rv-modal-header]` replaces the default header (title +\n * close button); default `<ng-content>` is the body; `[rv-modal-footer]`\n * is optional and renders nothing when absent.\n */\n@Component({\n selector: 'rv-modal-dialog',\n // Unencapsulated like rv-menu: custom projected headers/footers belong to\n // the consumer's template, which emulated selectors cannot reach. Every\n // rule is scoped under `.rv-modal`, so nothing leaks.\n encapsulation: ViewEncapsulation.None,\n template: `\n <dialog\n #dialog\n tabindex=\"-1\"\n class=\"rv-modal\"\n [attr.data-size]=\"size()\"\n [attr.data-position]=\"position()\"\n [attr.data-animation]=\"animation()\"\n [attr.data-state]=\"state()\"\n [attr.data-resizing]=\"resizing()\"\n [attr.aria-label]=\"computedLabel()\"\n [attr.aria-labelledby]=\"title() && !ariaLabel() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n (cancel)=\"onCancel($event)\"\n (click)=\"onBackdropClick($event)\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (hasCustomHeader()) {\n <div class=\"rv-modal-header rv-modal-header-custom\">\n <ng-content select=\"[rv-modal-header]\"></ng-content>\n </div>\n } @else if (title() || showCloseButton()) {\n <div class=\"rv-modal-header\">\n <div class=\"rv-modal-heading\">\n @if (title()) {\n <h2 class=\"rv-modal-title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (description()) {\n <p class=\"rv-modal-description\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"rv-modal-close\"\n (click)=\"onCloseButton()\"\n aria-label=\"Close dialog\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n }\n </div>\n }\n <div class=\"rv-modal-body\">\n <ng-content></ng-content>\n </div>\n @if (hasCustomFooter()) {\n <div class=\"rv-modal-footer\">\n <ng-content select=\"[rv-modal-footer]\"></ng-content>\n </div>\n }\n </dialog>\n `,\n styles: [\n `\n .rv-modal {\n color-scheme: light dark;\n /*\n * The PALETTE tokens are NOT declared here.\n *\n * A declaration on the component element out-ranks any value an\n * ancestor sets, so declaring them here would make the shared\n * semantic layer unreachable. They are read as\n * var(--rv-modal-*, var(--rv-<semantic>, <default>)) at each read\n * site instead — so a consumer's --rv-bg reaches the dialog, and\n * --rv-modal-bg still wins for the dialog alone.\n *\n * The LAYOUT tokens (widths, padding, durations) stay here: they are\n * genuinely component-specific and have no semantic counterpart.\n */\n --rv-modal-pad: 20px;\n --rv-modal-gap: 12px;\n /*\n * Responsive widths.\n *\n * A fixed rem width does not survive a small screen: the dialog\n * either overflows or is capped by max-width and the PRESET loses its\n * meaning — an 'sm' dialog and an 'xl' dialog end up the same width.\n * clamp() scales the preset with the viewport between a floor and a\n * ceiling, so the presets stay distinct AND the dialog fits.\n *\n * Every one of them is a plain custom property, so a consumer\n * overrides the preset in CSS without touching the library.\n */\n --rv-modal-width-sm: clamp(16rem, 40vw, 24rem);\n --rv-modal-width-md: clamp(20rem, 55vw, 32rem);\n --rv-modal-width-lg: clamp(24rem, 70vw, 48rem);\n --rv-modal-width-xl: clamp(28rem, 85vw, 64rem);\n --rv-modal-max-height: min(86dvh, 100%);\n --rv-modal-duration: 150ms;\n --rv-modal-duration-in: var(--rv-modal-duration);\n --rv-modal-duration-out: 120ms;\n --rv-modal-easing: ease-out;\n --rv-modal-easing-in: var(--rv-modal-easing);\n --rv-modal-easing-out: ease-in;\n --rv-modal-radius-sheet: 1.25rem;\n --rv-modal-width-panel: 24rem;\n padding: 0;\n border: 1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));\n border-radius: var(--rv-modal-radius, var(--rv-radius, 12px));\n background: var(--rv-modal-bg, var(--rv-bg, #ffffff));\n color: var(--rv-modal-fg, var(--rv-fg, #111111));\n box-shadow: var(--rv-modal-shadow, var(--rv-shadow, 0 24px 64px rgba(0, 0, 0, 0.28)));\n max-height: var(--rv-modal-max-height);\n width: var(--rv-modal-width-md);\n max-width: calc(100dvw - 2rem);\n /*\n * Smooth when the dialog's box changes.\n *\n * A dialog that opens on top of another one, or that changes size,\n * used to JUMP: the box changed in a single frame while the content\n * reflowed, which reads as a flicker. Transitioning width, height and\n * the offsets makes the change a movement.\n *\n * Only the box is transitioned — never all, which would also\n * animate the content's own layout and produce lag.\n */\n transition:\n width var(--rv-modal-size-duration, 180ms) var(--rv-modal-easing, ease-out),\n height var(--rv-modal-size-duration, 180ms) var(--rv-modal-easing, ease-out),\n max-height var(--rv-modal-size-duration, 180ms) var(--rv-modal-easing, ease-out),\n inset var(--rv-modal-size-duration, 180ms) var(--rv-modal-easing, ease-out),\n margin var(--rv-modal-size-duration, 180ms) var(--rv-modal-easing, ease-out);\n }\n /*\n * While the viewport is being resized, the transition is OFF.\n *\n * The dialog's size is derived from the viewport, so during a resize\n * every frame is a new target. Animating towards a moving target lags\n * behind the pointer and looks worse than the jump it replaced. The\n * dialog snaps instead, which is what the eye expects from a resize.\n */\n .rv-modal[data-resizing='true'] {\n transition: none !important;\n }\n /*\n * The loader shown while a dialog's data is still in flight.\n *\n * role=\"status\" with a polite live region: the change is announced\n * without stealing focus from whatever the user was doing.\n */\n .rv-modal-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: var(--rv-modal-loading-gap, 0.625rem);\n padding: var(--rv-modal-loading-padding, 2rem);\n min-height: var(--rv-modal-loading-min-height, 6rem);\n color: var(--rv-modal-muted, var(--rv-muted, #555555));\n font-size: var(--rv-modal-loading-size, 0.875rem);\n }\n /*\n * A ring that spins. currentColor so it follows the theme, and\n * prefers-reduced-motion stops it — a spinner that will not stop is\n * exactly the kind of thing that makes a page unusable for some people.\n */\n .rv-modal-spinner {\n inline-size: var(--rv-modal-spinner-size, 1.125rem);\n block-size: var(--rv-modal-spinner-size, 1.125rem);\n border: 2px solid currentColor;\n border-block-start-color: transparent;\n border-radius: 50%;\n animation: rv-modal-spin var(--rv-modal-spinner-duration, 700ms) linear infinite;\n }\n @keyframes rv-modal-spin {\n to {\n transform: rotate(360deg);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .rv-modal-spinner {\n animation: none;\n }\n }\n .rv-modal::backdrop {\n background: var(--rv-modal-backdrop, rgba(0, 0, 0, 0.5));\n }\n .rv-modal[data-size='sm'] {\n width: var(--rv-modal-width-sm);\n }\n .rv-modal[data-size='lg'] {\n width: var(--rv-modal-width-lg);\n }\n .rv-modal[data-size='xl'] {\n width: var(--rv-modal-width-xl);\n }\n .rv-modal[data-size='full'] {\n width: calc(100dvw - 2rem);\n }\n .rv-modal[data-size='auto'] {\n width: auto;\n }\n .rv-modal[data-position='top'] {\n margin-block-start: 8dvh;\n }\n .rv-modal[data-position='bottom'] {\n margin-block-start: auto;\n margin-block-end: 8dvh;\n }\n .rv-modal[data-position='top-start'],\n .rv-modal[data-position='center-start'] {\n margin-inline-start: 1rem;\n margin-inline-end: auto;\n }\n .rv-modal[data-position='top-end'],\n .rv-modal[data-position='center-end'] {\n margin-inline-start: auto;\n margin-inline-end: 1rem;\n }\n .rv-modal[data-position='bottom-start'] {\n margin-block-start: auto;\n margin-inline-start: 1rem;\n }\n .rv-modal[data-position='bottom-end'] {\n margin-block-start: auto;\n margin-inline-end: 1rem;\n }\n .rv-modal[data-position='fullscreen'] {\n inset: 0;\n width: 100dvw;\n height: 100dvh;\n height: 100dvh;\n max-width: none;\n max-height: none;\n border-radius: 0;\n }\n .rv-modal[data-position='side-start'],\n .rv-modal[data-position='side-end'] {\n height: 100dvh;\n max-height: none;\n width: var(--rv-modal-width-panel);\n margin-block: 0;\n }\n .rv-modal[data-position='side-start'] {\n margin-inline-start: 0;\n margin-inline-end: auto;\n border-start-start-radius: 0;\n border-end-start-radius: 0;\n }\n .rv-modal[data-position='side-end'] {\n margin-inline-start: auto;\n margin-inline-end: 0;\n border-start-end-radius: 0;\n border-end-end-radius: 0;\n }\n @media (max-width: 640px) {\n .rv-modal[data-position='center'],\n .rv-modal[data-position='top'],\n .rv-modal[data-position='bottom'],\n .rv-modal[data-position='bottom-start'],\n .rv-modal[data-position='bottom-end'] {\n margin-block-start: auto;\n margin-block-end: 0;\n margin-inline: 0;\n width: 100dvw;\n max-width: none;\n border-end-end-radius: 0;\n border-end-start-radius: 0;\n border-start-start-radius: var(--rv-modal-radius-sheet);\n border-start-end-radius: var(--rv-modal-radius-sheet);\n }\n .rv-modal[data-position='center'][data-state='open'],\n .rv-modal[data-position='top'][data-state='open'],\n .rv-modal[data-position='bottom'][data-state='open'],\n .rv-modal[data-position='bottom-start'][data-state='open'],\n .rv-modal[data-position='bottom-end'][data-state='open'] {\n animation-name: rv-modal-in-bottom;\n }\n }\n .rv-modal[data-animation]:not([data-animation='none'])[data-state='open'] {\n animation: var(--rv-modal-animation-in, rv-modal-in) var(--rv-modal-duration-in)\n var(--rv-modal-easing-in);\n }\n .rv-modal[data-animation='scale'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-scale;\n }\n .rv-modal[data-animation='slide-top'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-top;\n }\n .rv-modal[data-animation='slide-bottom'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-bottom;\n }\n .rv-modal[data-animation='slide-start'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-start;\n }\n .rv-modal[data-animation='slide-end'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-end;\n }\n [dir='rtl'] .rv-modal[data-animation='slide-start'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-end;\n }\n [dir='rtl'] .rv-modal[data-animation='slide-end'][data-state='open'] {\n --rv-modal-animation-in: rv-modal-in-start;\n }\n .rv-modal[data-state='closing'] {\n animation: rv-modal-out var(--rv-modal-duration-out) var(--rv-modal-easing-out) forwards;\n }\n @keyframes rv-modal-in {\n from {\n opacity: 0;\n }\n }\n @keyframes rv-modal-in-scale {\n from {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n }\n @keyframes rv-modal-in-top {\n from {\n opacity: 0;\n transform: translateY(-24px);\n }\n }\n @keyframes rv-modal-in-bottom {\n from {\n opacity: 0;\n transform: translateY(24px);\n }\n }\n @keyframes rv-modal-in-start {\n from {\n opacity: 0;\n transform: translateX(-24px);\n }\n }\n @keyframes rv-modal-in-end {\n from {\n opacity: 0;\n transform: translateX(24px);\n }\n }\n @keyframes rv-modal-out {\n to {\n opacity: 0;\n transform: translateY(8px) scale(0.98);\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .rv-modal[data-animation] {\n animation: none;\n }\n }\n .rv-modal-header {\n display: flex;\n align-items: flex-start;\n gap: var(--rv-modal-gap);\n padding: var(--rv-modal-pad) var(--rv-modal-pad) 0;\n }\n .rv-modal-header-custom > :first-child {\n flex: 1;\n min-width: 0;\n }\n .rv-modal-heading:empty {\n display: none;\n }\n .rv-modal-title {\n margin: 0;\n font-size: 1.125rem;\n flex: 1;\n }\n .rv-modal-heading {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n }\n .rv-modal-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--rv-modal-muted, var(--rv-muted, #555555));\n }\n .rv-modal-close {\n margin-inline-start: auto;\n flex: none;\n border: 1px solid var(--rv-modal-border, var(--rv-border, #e0e0e0));\n border-radius: 0.5rem;\n background: transparent;\n color: inherit;\n font-size: 1.25rem;\n line-height: 1;\n padding: 0.25rem 0.625rem;\n cursor: pointer;\n }\n .rv-modal-close:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 2px;\n }\n .rv-modal-body {\n padding: var(--rv-modal-pad);\n overflow: auto;\n max-height: inherit;\n }\n .rv-modal-footer {\n display: flex;\n gap: var(--rv-modal-gap);\n justify-content: flex-end;\n padding: 0 var(--rv-modal-pad) var(--rv-modal-pad);\n }\n .rv-modal-footer:empty {\n display: none;\n }\n `,\n ],\n})\nexport class RiviniaModalDialog {\n /** Two-way open state. UI-only mode owns this; the host drives it in managed mode. */\n readonly open = model<boolean>(false);\n\n readonly title = input<string>('');\n /**\n * Supporting text under the title (shadcn-style description). Rendered in\n * the default header only, and wired as the dialog's `aria-describedby`\n * for free accessible naming. Empty = no description element at all.\n */\n readonly description = input<string>('');\n /** Explicit accessible name. When set, `aria-label` wins over the title reference. */\n readonly ariaLabel = input<string>('');\n readonly size = input<RiviniaModalSize>('md');\n readonly position = input<RiviniaModalPosition>('center');\n readonly animation = input<RiviniaModalAnimation>('scale');\n readonly closeOnEscape = input<boolean>(true);\n readonly backdrop = input<RiviniaModalBackdrop>('close');\n\n /**\n * True while the viewport is being resized.\n *\n * The dialog's size is derived from the viewport, so during a resize every\n * frame is a new target; animating towards a moving target lags behind the\n * pointer. The transition is switched off for the duration, and the dialog\n * snaps — which is what the eye expects from a resize.\n */\n protected readonly resizing = signal(false);\n\n /**\n * Track the viewport resize.\n *\n * matchMedia('(resolution: …)') is the reliable signal: a plain\n * resize listener fires for every scroll-driven layout change too, and\n * the resolution query only changes when the device pixel ratio or the\n * viewport size really changed.\n */\n private readonly viewportQuery = signal<MediaQueryList | null>(null);\n\n private readonly stopResizeWatch = (() => {\n if (typeof window === 'undefined') return () => undefined;\n let query: MediaQueryList | null = null;\n const onChange = () => {\n // A short settle window: the transition is switched off for the\n // duration of the gesture and switched back on once it stops.\n this.resizing.set(true);\n if (this.settleTimer !== null) clearTimeout(this.settleTimer);\n this.settleTimer = setTimeout(() => this.resizing.set(false), 120);\n };\n const attach = () => {\n query?.removeEventListener('change', onChange);\n // matchMedia is not everywhere (jsdom has no implementation), and the\n // dialog must not fail to construct because of a nicety.\n if (typeof window.matchMedia !== 'function') return;\n const ratio = window.devicePixelRatio ?? 1;\n query = window.matchMedia(`(resolution: ${ratio}dppx)`);\n query.addEventListener('change', onChange, { once: true });\n this.viewportQuery.set(query);\n };\n attach();\n return () => query?.removeEventListener('change', onChange);\n })();\n\n private settleTimer: ReturnType<typeof setTimeout> | null = null;\n\n ngOnDestroy(): void {\n // The viewport listener outlives the component otherwise.\n this.stopResizeWatch();\n if (this.settleTimer !== null) clearTimeout(this.settleTimer);\n }\n readonly showCloseButton = input<boolean>(true);\n /**\n * Managed mode (for {@link RiviniaModalHost}): UI intents (X button,\n * Escape, backdrop) emit `dismissRequested` INSTEAD of dismissing, so the\n * manager runs guards first and settles results. Drive the exit with the\n * `closing` input; acknowledge teardown with `exitDone`.\n * UI-only mode (`false`, default) dismisses locally.\n */\n readonly managed = input<boolean>(false);\n /**\n * Managed-mode exit trigger. Set `true` when the manager reports this\n * instance `closing`: the dialog plays its exit animation, then emits\n * `exitDone` (call `confirmClosed()` there). Ignored in UI-only mode.\n */\n readonly closing = input<boolean>(false);\n\n /** Settled with a value (primary/confirm actions call `close(value)`). */\n readonly confirmed = output<unknown>();\n /** Settled without a value (X button, backdrop, Escape, `dismiss()`). */\n readonly dismissed = output<string | undefined>();\n /** Explicit cancellation (`cancel()`). Kept distinct from dismissal. */\n readonly cancelled = output<string | undefined>();\n /**\n * Managed-mode intent (X button / Escape / backdrop). Emitted INSTEAD of\n * dismissing — the host routes it to the manager first. Never emitted in\n * UI-only mode.\n */\n readonly dismissRequested = output<'button' | 'escape' | 'backdrop'>();\n /** Managed-mode exit completion. Acknowledge with `confirmClosed()`. */\n readonly exitDone = output<void>();\n\n private readonly dialog = viewChild<ElementRef<HTMLDialogElement>>('dialog');\n private readonly headerSlot = contentChild(RvModalHeader);\n private readonly footerSlot = contentChild(RvModalFooter);\n private readonly doc = inject(DOCUMENT, { optional: true });\n private readonly scrollLock = inject(RiviniaModalScrollLock, { optional: true });\n private readonly titleId = `rv-modal-${++dialogSeq}`;\n private readonly descriptionId = `rv-modal-desc-${dialogSeq}`;\n private readonly phase = signal<'closed' | 'open' | 'closing'>('closed');\n private readonly invoker: { current: Element | null } = { current: null };\n private exitTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Template state for animation styling. Never rendered open on the server. */\n readonly state = computed(() => this.phase());\n readonly hasCustomHeader = computed(() => this.headerSlot() !== undefined);\n readonly hasCustomFooter = computed(() => this.footerSlot() !== undefined);\n readonly computedLabel = computed(() => this.ariaLabel() || null);\n\n constructor() {\n effect(() => {\n if (this.open()) this.show();\n // External close acts only while fully open: during 'closing' the\n // exit animation owns teardown (its timer finalizes). This guard is\n // what keeps `finish()` from cutting the animation via `open.set(false)`.\n else if (this.phase() === 'open') this.hide();\n });\n effect(() => {\n // Managed exit: the manager moved this instance to `closing`.\n if (this.managed() && this.closing() && this.phase() === 'open') {\n this.playExit(() => this.exitDone.emit());\n }\n });\n }\n\n /** Confirm with a value. No-op while closing/closed. */\n confirm(value?: unknown): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('confirmed', value);\n return true;\n }\n\n /** Backwards-compatible close: confirms when given a value, dismisses otherwise. */\n close(value?: unknown): boolean {\n return value === undefined ? this.dismiss() : this.confirm(value);\n }\n\n /** Dismiss without a value (X button, backdrop, Escape route here). */\n dismiss(reason?: string): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('dismissed', undefined, reason);\n return true;\n }\n\n /** Explicit cancellation. */\n cancel(reason?: string): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('cancelled', undefined, reason);\n return true;\n }\n\n protected onCloseButton(): void {\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('button');\n return;\n }\n this.dismiss('close-button');\n }\n\n protected onCancel(event: Event): void {\n if (!this.closeOnEscape()) {\n event.preventDefault();\n return;\n }\n // Native `cancel` already closed nothing yet — route through our pipeline\n // so scroll-lock, focus restore and outputs stay consistent.\n event.preventDefault();\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('escape');\n return;\n }\n this.dismiss('escape');\n }\n\n protected onBackdropClick(event: MouseEvent): void {\n const el = this.dialog()?.nativeElement;\n if (!el || event.target !== el) return; // inside clicks never count\n if (this.backdrop() !== 'close') return;\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('backdrop');\n return;\n }\n this.dismiss('backdrop');\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Tab') return;\n const el = this.dialog()?.nativeElement;\n if (!el) return;\n const items = focusables(el);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = (this.doc ?? document).activeElement;\n if (event.shiftKey && (active === first || !el.contains(active))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n\n private get live(): boolean {\n return typeof window !== 'undefined' && !!this.doc;\n }\n\n private show(): void {\n if (!this.live) return; // SSR: stay closed until client hydration opens us\n const el = this.dialog()?.nativeElement;\n if (!el || this.phase() !== 'closed') return;\n try {\n this.invoker.current = this.doc?.activeElement ?? null;\n } catch {\n this.invoker.current = null;\n }\n this.scrollLock?.acquire();\n this.phase.set('open');\n if (!el.open) {\n try {\n el.showModal();\n } catch {\n // Already open (e.g. double effect run): continue — state is ours.\n }\n }\n focusFirst(el, this.showCloseButton());\n }\n\n private hide(): void {\n if (this.phase() === 'closed') return;\n this.teardown(false);\n }\n\n private finish(\n kind: 'confirmed' | 'dismissed' | 'cancelled',\n value?: unknown,\n reason?: string,\n ): void {\n this.teardown(true);\n if (kind === 'confirmed') this.confirmed.emit(value);\n else if (kind === 'dismissed') this.dismissed.emit(reason);\n else this.cancelled.emit(reason);\n if (this.open()) this.open.set(false);\n }\n\n private teardown(animate: boolean): void {\n if (!animate) {\n if (this.exitTimer) {\n clearTimeout(this.exitTimer);\n this.exitTimer = null;\n }\n this.finalize(this.dialog()?.nativeElement ?? null);\n return;\n }\n this.playExit(() => {});\n }\n\n /** Plays the exit animation (when warranted), then runs `done`. Idempotent. */\n private playExit(done: () => void): void {\n if (this.phase() !== 'open') {\n done();\n return;\n }\n if (this.exitTimer) {\n clearTimeout(this.exitTimer);\n this.exitTimer = null;\n }\n const el = this.dialog()?.nativeElement ?? null;\n const animated = this.animation() !== 'none' && this.prefersMotion() && !!el?.open;\n this.phase.set(animated ? 'closing' : 'closed');\n if (!animated) {\n this.finalize(el);\n done();\n return;\n }\n // Honor consumer-overridden durations instead of a fixed fallback, so a\n // longer `--rv-modal-duration-out` is never cut short.\n this.exitTimer = setTimeout(() => {\n this.finalize(el);\n done();\n }, this.exitDurationMs(el));\n }\n\n private exitDurationMs(el: HTMLDialogElement | null): number {\n try {\n const raw = el ? getComputedStyle(el).animationDuration : '';\n const seconds = raw\n .split(',')\n .map((part) => parseFloat(part))\n .filter((n) => Number.isFinite(n));\n if (seconds.length > 0) return Math.max(0, Math.max(...seconds) * 1000) + 50;\n } catch {\n // Non-CSS environments (tests): fall through to the fallback.\n }\n return EXIT_FALLBACK_MS;\n }\n\n private finalize(el: HTMLDialogElement | null): void {\n this.exitTimer = null;\n try {\n el?.close();\n } catch {\n // Already closed natively — ours to reconcile, not to throw.\n }\n this.phase.set('closed');\n this.scrollLock?.release();\n const invoker = this.invoker.current as HTMLElement | null;\n this.invoker.current = null;\n try {\n invoker?.focus?.();\n } catch {\n // Invoker gone (navigated away) — nothing to restore to.\n }\n }\n\n private prefersMotion(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n !window.matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n } catch {\n return true;\n }\n }\n}\n\nlet dialogSeq = 0;\n\nfunction focusables(root: HTMLElement): HTMLElement[] {\n // No visibility filtering on purpose: jsdom reports zero rects (unit\n // tests), and `disabled` is already excluded by the selector. Hidden\n // focusables inside a visible dialog are a consumer edge, not ours.\n const selector =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), ' +\n 'textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(root.querySelectorAll<HTMLElement>(selector));\n}\n\nfunction focusFirst(dialog: HTMLDialogElement, closeVisible: boolean): void {\n try {\n const auto = dialog.querySelector<HTMLElement>('[autofocus]');\n if (auto) {\n auto.focus({ preventScroll: true });\n return;\n }\n // Atlassian rule: no close button → focus the title; otherwise the\n // dialog itself (its tabindex=\"-1\" makes it focusable).\n if (!closeVisible) {\n const title = dialog.querySelector<HTMLElement>('.rv-modal-title');\n if (title) {\n title.setAttribute('tabindex', '-1');\n title.focus({ preventScroll: true });\n return;\n }\n }\n dialog.focus({ preventScroll: true });\n } catch {\n // Focus is best-effort (e.g. during teardown races).\n }\n}\n","/**\n * Whether a dialog hides the ones below it.\n *\n * The flag is set when the dialog is OPENED, so it lives in the instance's\n * metadata — the same place the title lives, and the same place the host\n * already reads from.\n */\n\nimport type { ModalInstance } from '@rivinia/modal';\n\n/** The metadata key the flag is stored under. */\nexport const MODAL_COVERS = 'coversOthers';\n\n/**\n * Whether this dialog covers the ones below it.\n *\n * @param instance The instance to inspect.\n */\nexport function covers(instance: ModalInstance<unknown>): boolean {\n return instance.metadata?.[MODAL_COVERS] === true;\n}\n","import { Component, Injector, computed, effect, input, output, signal } from '@angular/core';\nimport type { Signal } from '@angular/core';\nimport { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport { RIVINIA_MODAL_CONTEXT } from '@rivinia/modal';\nimport type {\n ModalContext,\n ModalDismissOrigin,\n ModalInstance,\n ModalTemplateContext,\n} from '@rivinia/modal';\nimport { RiviniaModalDialog } from './modal-dialog';\nimport type { RiviniaModalSize } from './ui-types';\nimport { covers } from './modal-covers';\n\n/**\n * Managed adapter: renders every `visible()` instance of the headless\n * `RiviniaModal` inside a {@link RiviniaModalDialog} and routes all UI\n * intents through the manager first (guards run; results settle in Core —\n * the single source of truth). The dialog never settles anything itself in\n * this mode; listen to `ModalHandle.result`, not dialog outputs.\n *\n * Exit coordination: when Core reports an instance `closing`, the dialog\n * plays its exit animation and the host acknowledges with `confirmClosed()`\n * (idempotent — safe if `closingTimeout` auto-finalized first). Match\n * `closingTimeout` to the animation duration so exits stay visible.\n *\n * Title resolution: `titleOf` per instance (default: `metadata.title` →\n * `ref` → `''`). Content descriptors render as-is: templates via\n * `ngTemplateOutlet` with `{$implicit: data, modal}`, components via\n * `NgComponentOutlet` with `inputs` + a per-instance\n * `RIVINIA_MODAL_CONTEXT` injector (the lab-proven pattern).\n *\n * The manager arrives via `[modal]` input (D06 input-composition: the UI\n * entry never instantiates Core itself, so UI-only consumers pay nothing).\n * The input is typed as {@link RiviniaModalHostManager} — the structural\n * slice the host actually drives — so the full manager (or a test double)\n * satisfies it from any module realm:\n *\n * ```html\n * <rv-modal-host [modal]=\"injectRiviniaModal()\" />\n * ```\n */\nexport interface RiviniaModalHostManager {\n readonly visible: Signal<readonly ModalInstance<unknown>[]>;\n requestDismiss(id: string, origin: ModalDismissOrigin): Promise<boolean>;\n confirmClosed(id: string): boolean;\n close(id: string, value?: unknown): boolean;\n dismiss(id: string, reason?: string): boolean;\n}\n\n@Component({\n selector: 'rv-modal-host',\n imports: [RiviniaModalDialog, NgComponentOutlet, NgTemplateOutlet],\n template: `\n @for (instance of visible(); track instance.id) {\n @if (isCovered(instance)) {\n <!--\n Hidden by a dialog above it that asked to cover the others. It stays\n in the manager — only the rendering is skipped, so it comes back when\n the cover closes.\n -->\n } @else {\n <rv-modal-dialog\n [open]=\"true\"\n [managed]=\"true\"\n [closing]=\"instance.status === 'closing'\"\n [title]=\"titleOfInstance(instance)\"\n [size]=\"sizeOf(instance)\"\n [attr.data-caller]=\"callerName(instance)\"\n [showCloseButton]=\"showCloseButton()\"\n [closeOnEscape]=\"instance.dismiss.closeOnEscape ?? true\"\n [backdrop]=\"instance.dismiss.backdrop ?? 'close'\"\n (dismissRequested)=\"onIntent(instance.id, $event)\"\n (exitDone)=\"onExitDone(instance.id)\"\n >\n @if (isLoading(instance)) {\n <!--\n The data has not arrived yet. A loader says so; an empty dialog\n looks broken.\n -->\n <div class=\"rv-modal-loading\" role=\"status\" aria-live=\"polite\">\n <span class=\"rv-modal-spinner\" aria-hidden=\"true\"></span>\n <span>{{ loadingLabel() }}</span>\n </div>\n } @else if (instance.content.kind === 'template') {\n <ng-container\n *ngTemplateOutlet=\"instance.content.template; context: templateContext(instance)\"\n ></ng-container>\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n instance.content.component;\n inputs: instance.content.inputs;\n injector: contextInjector(instance)\n \"\n ></ng-container>\n }\n </rv-modal-dialog>\n }\n }\n `,\n})\nexport class RiviniaModalHost {\n /**\n * The headless manager to render. Typed as {@link RiviniaModalHostManager}\n * (structural) — pass `injectRiviniaModal()` from your app code.\n */\n readonly modal = input.required<RiviniaModalHostManager>();\n\n /** Dialog size for every managed instance. @default 'md' */\n readonly size = input<RiviniaModalSize>('md');\n /** Render the header close button on every managed instance. @default true */\n readonly showCloseButton = input<boolean>(true);\n /** Resolve a title per instance. Default: `metadata.title` → `ref` → `''`. */\n readonly titleOf = input<(instance: ModalInstance<unknown>) => string | undefined>();\n\n /**\n * Whether an instance is still loading.\n *\n * A dialog that opens before its data arrives has nothing to show yet, and\n * an empty box reads as a broken dialog. While this returns true for an\n * instance, the host renders a loader INSTEAD of the instance's content.\n *\n * It is per instance, because a host can have several dialogs open and\n * they load independently.\n *\n * ```html\n * <rv-modal-host\n * [modal]=\"modal\"\n * [loadingOf]=\"(instance) => instance.data?.loading === true\"\n * />\n * ```\n */\n readonly loadingOf = input<(instance: ModalInstance<unknown>) => boolean>();\n\n /** Text of the loader. @default 'Loading…' */\n readonly loadingLabel = input('Loading…');\n\n /**\n * The rendered stack.\n *\n * It is a signal the host owns rather than a computed over the manager, so\n * the update can be wrapped in a view transition: the browser then paints\n * the old stack and the new one as one continuous move instead of\n * flickering between them.\n */\n protected readonly visible = signal<readonly ModalInstance[]>([]);\n\n /** Push the manager's current stack into the rendered one. */\n private readonly syncStack = effect(() => {\n const next = this.modal().visible();\n this.withTransition(() => this.visible.set(next));\n });\n\n /**\n * Smooth the stack change with a view transition.\n *\n * Opening a dialog on top of another one used to FLICKER: the top layer\n * re-promotes the lower dialog, and the browser paints the change in\n * separate frames. A view transition makes it one continuous move.\n *\n * It is optional by design: where the API is missing, the change simply\n * happens without it — a missing nicety must not break the dialog.\n */\n private readonly transition = (() => {\n if (typeof document === 'undefined') return false;\n return typeof (document as Document & { startViewTransition?: unknown }).startViewTransition === 'function';\n })();\n\n /** The last stack signature, so a change can be detected. */\n private lastStackKey = '';\n\n /**\n * Run a view transition when the visible stack changed.\n */\n protected withTransition(change: () => void): void {\n const key = this.modal()\n .visible()\n .map((i) => `${i.id}:${i.status}`)\n .join('|');\n if (!this.transition || key === this.lastStackKey) {\n change();\n this.lastStackKey = key;\n return;\n }\n const doc = document as Document & { startViewTransition?: (cb: () => void) => unknown };\n try {\n doc.startViewTransition?.(() => {\n change();\n this.lastStackKey = key;\n });\n } catch {\n change();\n this.lastStackKey = key;\n }\n }\n\n /** Whether an instance hides the ones below it. */\n protected readonly covering = computed(() => {\n const list = this.visible();\n // The topmost covering instance hides everything under it.\n let coverId: string | null = null;\n for (const instance of list) {\n if (covers(instance)) coverId = instance.id;\n }\n return coverId;\n });\n\n /**\n * Whether this instance is loading.\n *\n * A throwing resolver must not break the dialog, so it falls back to \"not\n * loading\" — the content renders, which is the safe default.\n */\n protected isLoading(instance: ModalInstance<unknown>): boolean {\n try {\n return this.loadingOf()?.(instance) === true;\n } catch {\n return false;\n }\n }\n\n /**\n * The name of the component that opened this dialog.\n *\n * Rendered as `data-caller` on the dialog, so the DevTools inspector shows\n * WHICH component opened it — the thing you want when a dialog appears and\n * you need to find its source.\n */\n protected callerName(instance: ModalInstance<unknown>): string | null {\n return instance.caller ?? null;\n }\n\n /**\n * The size of an instance.\n *\n * The host's `size` is the default; an instance may override it, so a small\n * confirm can sit on top of a wide form without a second host.\n */\n protected sizeOf(instance: ModalInstance<unknown>): RiviniaModalSize {\n const own = (instance.metadata?.['size'] as RiviniaModalSize | undefined) ?? undefined;\n return own ?? this.size();\n }\n\n /**\n * Whether an instance hides the ones below it.\n *\n * A stack renders every dialog, so the ones below stay visible behind the\n * top one. When the top one covers the others, the ones below are not\n * rendered at all — and they come back when it closes.\n */\n protected isCovered(instance: ModalInstance<unknown>): boolean {\n return this.visible().some((other) => other.id !== instance.id && covers(other));\n }\n\n protected titleOfInstance(instance: ModalInstance<unknown>): string {\n try {\n const custom = this.titleOf()?.(instance);\n if (custom !== undefined) return custom;\n } catch {\n // A throwing resolver must never break rendering — fall through.\n }\n const meta = instance.metadata?.['title'];\n if (typeof meta === 'string' && meta.length > 0) return meta;\n return instance.ref ?? '';\n }\n\n protected templateContext(instance: ModalInstance<unknown>): ModalTemplateContext<unknown> {\n return { $implicit: instance.data, modal: this.modalContext(instance) };\n }\n\n protected contextInjector(instance: ModalInstance<unknown>): Injector {\n const modal = this.modal();\n return Injector.create({\n providers: [\n {\n provide: RIVINIA_MODAL_CONTEXT,\n useValue: {\n id: instance.id,\n data: instance.data,\n parentId: instance.parentId,\n close: (result?: unknown) => modal.close(instance.id, result),\n dismiss: (reason?: string) => modal.dismiss(instance.id, reason),\n } satisfies ModalContext<unknown>,\n },\n ],\n });\n }\n\n protected async onIntent(id: string, origin: 'button' | 'escape' | 'backdrop'): Promise<void> {\n try {\n await this.modal().requestDismiss(id, origin);\n } catch {\n // requestDismiss never rejects (deny-safe by contract) — defensive only.\n }\n // Granted → status flips to `closing` → dialog exit → `exitDone`.\n // Denied → nothing moves; the dialog never left `open`. No manual toggle.\n }\n\n protected onExitDone(id: string): void {\n this.modal().confirmClosed(id);\n }\n\n private modalContext(instance: ModalInstance<unknown>): ModalContext<unknown> {\n const modal = this.modal();\n const id = instance.id;\n return {\n id,\n data: instance.data,\n parentId: instance.parentId,\n close: (result?: unknown) => modal.close(id, result),\n dismiss: (reason?: string) => modal.dismiss(id, reason),\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rivinia-modal-ui.mjs","names":[],"sources":["../../../projects/modal/ui/src/lib/components/modal-scroll-lock/modal-scroll-lock.ts","../../../projects/modal/ui/src/lib/components/modal-dialog/modal-dialog.ts","../../../projects/modal/ui/src/lib/components/modal-dialog/modal-dialog.html","../../../projects/modal/ui/src/lib/utils/modal-covers.ts","../../../projects/modal/ui/src/lib/components/modal-host/modal-host.ts","../../../projects/modal/ui/src/lib/components/modal-host/modal-host.html"],"sourcesContent":["import { DOCUMENT, Service, inject } from '@angular/core';\n\n/**\n * Reference-counted background scroll lock. Multiple overlays share one lock:\n * closing one modal never unlocks the page while another still needs it.\n * Restores the exact previous `overflow` value (never blanks app-owned\n * styles). All DOM access happens inside methods — never at module\n * evaluation — and every method is a no-op without a document (SSR).\n */\n@Service()\nexport class RiviniaModalScrollLock {\n private readonly _doc = inject(DOCUMENT, { optional: true });\n private _locks = 0;\n private _previousOverflow: string | null = null;\n\n /** Number of active locks (diagnostics/tests). */\n get active(): number {\n return this._locks;\n }\n\n acquire(): void {\n const doc = this.document;\n if (!doc) return;\n this._locks += 1;\n if (this._locks > 1) return;\n const body = doc.body;\n this._previousOverflow = body.style.overflow || null;\n body.style.overflow = 'hidden';\n // Reserve the scrollbar gutter where supported so content does not jump.\n // Overlay scrollbars ignore it; that is documented, not promised.\n doc.documentElement.style.setProperty('scrollbar-gutter', 'stable');\n }\n\n release(): void {\n const doc = this.document;\n if (!doc || this._locks === 0) {\n this._locks = 0;\n return;\n }\n this._locks -= 1;\n if (this._locks > 0) return;\n const body = doc.body;\n if (this._previousOverflow === null) body.style.removeProperty('overflow');\n else body.style.overflow = this._previousOverflow;\n this._previousOverflow = null;\n doc.documentElement.style.removeProperty('scrollbar-gutter');\n }\n\n private get document(): Document | null {\n try {\n return this._doc ?? (typeof document === 'undefined' ? null : document);\n } catch {\n return null;\n }\n }\n}\n","import {\n Component,\n OnDestroy,\n Directive,\n DOCUMENT,\n ElementRef,\n ViewEncapsulation,\n computed,\n contentChild,\n effect,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { RiviniaModalScrollLock } from '../modal-scroll-lock/modal-scroll-lock';\nimport type {\n RiviniaModalAnimation,\n RiviniaModalBackdrop,\n RiviniaModalPosition,\n RiviniaModalSize,\n} from '../../models/ui-types';\n\nconst EXIT_FALLBACK_MS = 200;\n\n/**\n * Marker for custom header content. Put it on the element carrying\n * `rv-modal-header` — its presence (not just the attribute) tells the\n * dialog to hide the default title + close button:\n *\n * ```html\n * <div rv-modal-header rvModalHeader>My header</div>\n * ```\n */\n@Directive({ selector: '[rvModalHeader]', standalone: true })\nexport class RvModalHeader {}\n\n/**\n * Marker for custom footer content. Put it on the element carrying\n * `rv-modal-footer`. Without it the footer region never renders.\n */\n@Directive({ selector: '[rvModalFooter]', standalone: true })\nexport class RvModalFooter {}\n\n/**\n * Ready-made modal dialog on the native `<dialog>` element (top layer,\n * `::backdrop`, inert background, Escape `cancel` event — all platform\n * behavior, zero overlay dependencies).\n *\n * UI-only by design: it owns its own `open` state and never imports the\n * headless controller. Pair it with `@rivinia/modal` through\n * {@link RiviniaModalHost} (or your own adapter) when you want managed\n * lifecycle, guards and typed results.\n *\n * Projection: `[rv-modal-header]` replaces the default header (title +\n * close button); default `<ng-content>` is the body; `[rv-modal-footer]`\n * is optional and renders nothing when absent.\n */\n@Component({\n selector: 'rv-modal-dialog',\n // Unencapsulated like rv-menu: custom projected headers/footers belong to\n // the consumer's template, which emulated selectors cannot reach. Every\n // rule is scoped under `.rv-modal`, so nothing leaks.\n encapsulation: ViewEncapsulation.None,\n templateUrl: './modal-dialog.html',\n styleUrl: './modal-dialog.css',\n})\nexport class RiviniaModalDialog {\n /** Two-way open state. UI-only mode owns this; the host drives it in managed mode. */\n readonly open = model<boolean>(false);\n\n readonly title = input<string>('');\n /**\n * Supporting text under the title (shadcn-style description). Rendered in\n * the default header only, and wired as the dialog's `aria-describedby`\n * for free accessible naming. Empty = no description element at all.\n */\n readonly description = input<string>('');\n /** Explicit accessible name. When set, `aria-label` wins over the title reference. */\n readonly ariaLabel = input<string>('');\n readonly size = input<RiviniaModalSize>('md');\n readonly position = input<RiviniaModalPosition>('center');\n readonly animation = input<RiviniaModalAnimation>('scale');\n readonly closeOnEscape = input<boolean>(true);\n readonly backdrop = input<RiviniaModalBackdrop>('close');\n\n /**\n * True while the viewport is being resized.\n *\n * The dialog's size is derived from the viewport, so during a resize every\n * frame is a new target; animating towards a moving target lags behind the\n * pointer. The transition is switched off for the duration, and the dialog\n * snaps — which is what the eye expects from a resize.\n */\n protected readonly resizing = signal(false);\n\n /**\n * Track the viewport resize.\n *\n * matchMedia('(resolution: …)') is the reliable signal: a plain\n * resize listener fires for every scroll-driven layout change too, and\n * the resolution query only changes when the device pixel ratio or the\n * viewport size really changed.\n */\n private readonly viewportQuery = signal<MediaQueryList | null>(null);\n\n private readonly stopResizeWatch = (() => {\n if (typeof window === 'undefined') return () => undefined;\n let query: MediaQueryList | null = null;\n const onChange = () => {\n // A short settle window: the transition is switched off for the\n // duration of the gesture and switched back on once it stops.\n this.resizing.set(true);\n if (this.settleTimer !== null) clearTimeout(this.settleTimer);\n this.settleTimer = setTimeout(() => this.resizing.set(false), 120);\n };\n const attach = () => {\n query?.removeEventListener('change', onChange);\n // matchMedia is not everywhere (jsdom has no implementation), and the\n // dialog must not fail to construct because of a nicety.\n if (typeof window.matchMedia !== 'function') return;\n const ratio = window.devicePixelRatio ?? 1;\n query = window.matchMedia(`(resolution: ${ratio}dppx)`);\n query.addEventListener('change', onChange, { once: true });\n this.viewportQuery.set(query);\n };\n attach();\n return () => query?.removeEventListener('change', onChange);\n })();\n\n private settleTimer: ReturnType<typeof setTimeout> | null = null;\n\n ngOnDestroy(): void {\n // The viewport listener outlives the component otherwise.\n this.stopResizeWatch();\n if (this.settleTimer !== null) clearTimeout(this.settleTimer);\n if (this.exitTimer !== null) clearTimeout(this.exitTimer);\n if (this.phase() !== 'closed') this.finalize(this.dialog()?.nativeElement ?? null);\n }\n readonly showCloseButton = input<boolean>(true);\n /**\n * Managed mode (for {@link RiviniaModalHost}): UI intents (X button,\n * Escape, backdrop) emit `dismissRequested` INSTEAD of dismissing, so the\n * manager runs guards first and settles results. Drive the exit with the\n * `closing` input; acknowledge teardown with `exitDone`.\n * UI-only mode (`false`, default) dismisses locally.\n */\n readonly managed = input<boolean>(false);\n /**\n * Managed-mode exit trigger. Set `true` when the manager reports this\n * instance `closing`: the dialog plays its exit animation, then emits\n * `exitDone` (call `confirmClosed()` there). Ignored in UI-only mode.\n */\n readonly closing = input<boolean>(false);\n\n /** Settled with a value (primary/confirm actions call `close(value)`). */\n readonly confirmed = output<unknown>();\n /** Settled without a value (X button, backdrop, Escape, `dismiss()`). */\n readonly dismissed = output<string | undefined>();\n /** Explicit cancellation (`cancel()`). Kept distinct from dismissal. */\n readonly cancelled = output<string | undefined>();\n /**\n * Managed-mode intent (X button / Escape / backdrop). Emitted INSTEAD of\n * dismissing — the host routes it to the manager first. Never emitted in\n * UI-only mode.\n */\n readonly dismissRequested = output<'button' | 'escape' | 'backdrop'>();\n /** Managed-mode exit completion. Acknowledge with `confirmClosed()`. */\n readonly exitDone = output<void>();\n\n private readonly dialog = viewChild<ElementRef<HTMLDialogElement>>('dialog');\n private readonly headerSlot = contentChild(RvModalHeader);\n private readonly footerSlot = contentChild(RvModalFooter);\n private readonly doc = inject(DOCUMENT, { optional: true });\n private readonly scrollLock = inject(RiviniaModalScrollLock, { optional: true });\n private readonly titleId = `rv-modal-${++dialogSeq}`;\n private readonly descriptionId = `rv-modal-desc-${dialogSeq}`;\n private readonly phase = signal<'closed' | 'open' | 'closing'>('closed');\n private readonly invoker: { current: Element | null } = { current: null };\n private exitTimer: ReturnType<typeof setTimeout> | null = null;\n\n /** Template state for animation styling. Never rendered open on the server. */\n readonly state = computed(() => this.phase());\n readonly hasCustomHeader = computed(() => this.headerSlot() !== undefined);\n readonly hasCustomFooter = computed(() => this.footerSlot() !== undefined);\n readonly computedLabel = computed(() => this.ariaLabel() || null);\n\n constructor() {\n effect(() => {\n if (this.open()) this.show();\n // External close acts only while fully open: during 'closing' the\n // exit animation owns teardown (its timer finalizes). This guard is\n // what keeps `finish()` from cutting the animation via `open.set(false)`.\n else if (this.phase() === 'open') this.hide();\n });\n effect(() => {\n // Managed exit: the manager moved this instance to `closing`.\n if (this.managed() && this.closing() && this.phase() === 'open') {\n this.playExit(() => this.exitDone.emit());\n }\n });\n }\n\n /** Confirm with a value. No-op while closing/closed. */\n confirm(value?: unknown): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('confirmed', value);\n return true;\n }\n\n /** Backwards-compatible close: confirms when given a value, dismisses otherwise. */\n close(value?: unknown): boolean {\n return value === undefined ? this.dismiss() : this.confirm(value);\n }\n\n /** Dismiss without a value (X button, backdrop, Escape route here). */\n dismiss(reason?: string): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('dismissed', undefined, reason);\n return true;\n }\n\n /** Explicit cancellation. */\n cancel(reason?: string): boolean {\n if (this.phase() !== 'open') return false;\n this.finish('cancelled', undefined, reason);\n return true;\n }\n\n protected onCloseButton(): void {\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('button');\n return;\n }\n this.dismiss('close-button');\n }\n\n protected onCancel(event: Event): void {\n if (!this.closeOnEscape()) {\n event.preventDefault();\n return;\n }\n // Native `cancel` already closed nothing yet — route through our pipeline\n // so scroll-lock, focus restore and outputs stay consistent.\n event.preventDefault();\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('escape');\n return;\n }\n this.dismiss('escape');\n }\n\n protected onBackdropClick(event: MouseEvent): void {\n const el = this.dialog()?.nativeElement;\n if (!el || event.target !== el) return; // inside clicks never count\n if (this.backdrop() !== 'close') return;\n if (this.managed()) {\n if (this.phase() === 'open') this.dismissRequested.emit('backdrop');\n return;\n }\n this.dismiss('backdrop');\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Tab') return;\n const el = this.dialog()?.nativeElement;\n if (!el) return;\n const items = focusables(el);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const first = items[0];\n const last = items[items.length - 1];\n const active = (this.doc ?? document).activeElement;\n if (event.shiftKey && (active === first || !el.contains(active))) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n\n private get live(): boolean {\n return typeof window !== 'undefined' && !!this.doc;\n }\n\n private show(): void {\n if (!this.live) return; // SSR: stay closed until client hydration opens us\n const el = this.dialog()?.nativeElement;\n if (!el || this.phase() !== 'closed') return;\n try {\n this.invoker.current = this.doc?.activeElement ?? null;\n } catch {\n this.invoker.current = null;\n }\n this.scrollLock?.acquire();\n this.phase.set('open');\n if (!el.open) {\n try {\n el.showModal();\n } catch {\n // Already open (e.g. double effect run): continue — state is ours.\n }\n }\n focusFirst(el, this.showCloseButton());\n }\n\n private hide(): void {\n if (this.phase() === 'closed') return;\n this.teardown(false);\n }\n\n private finish(\n kind: 'confirmed' | 'dismissed' | 'cancelled',\n value?: unknown,\n reason?: string,\n ): void {\n this.teardown(true);\n if (kind === 'confirmed') this.confirmed.emit(value);\n else if (kind === 'dismissed') this.dismissed.emit(reason);\n else this.cancelled.emit(reason);\n if (this.open()) this.open.set(false);\n }\n\n private teardown(animate: boolean): void {\n if (!animate) {\n if (this.exitTimer) {\n clearTimeout(this.exitTimer);\n this.exitTimer = null;\n }\n this.finalize(this.dialog()?.nativeElement ?? null);\n return;\n }\n this.playExit(() => {});\n }\n\n /** Plays the exit animation (when warranted), then runs `done`. Idempotent. */\n private playExit(done: () => void): void {\n if (this.phase() !== 'open') {\n done();\n return;\n }\n if (this.exitTimer) {\n clearTimeout(this.exitTimer);\n this.exitTimer = null;\n }\n const el = this.dialog()?.nativeElement ?? null;\n const animated = this.animation() !== 'none' && this.prefersMotion() && !!el?.open;\n this.phase.set(animated ? 'closing' : 'closed');\n if (!animated) {\n this.finalize(el);\n done();\n return;\n }\n // Honor consumer-overridden durations instead of a fixed fallback, so a\n // longer `--rv-modal-duration-out` is never cut short.\n this.exitTimer = setTimeout(() => {\n this.finalize(el);\n done();\n }, this.exitDurationMs(el));\n }\n\n private exitDurationMs(el: HTMLDialogElement | null): number {\n try {\n const raw = el ? getComputedStyle(el).animationDuration : '';\n const seconds = raw\n .split(',')\n .map((part) => parseFloat(part))\n .filter((n) => Number.isFinite(n));\n if (seconds.length > 0) return Math.max(0, Math.max(...seconds) * 1000) + 50;\n } catch {\n // Non-CSS environments (tests): fall through to the fallback.\n }\n return EXIT_FALLBACK_MS;\n }\n\n private finalize(el: HTMLDialogElement | null): void {\n this.exitTimer = null;\n try {\n el?.close();\n } catch {\n // Already closed natively — ours to reconcile, not to throw.\n }\n this.phase.set('closed');\n this.scrollLock?.release();\n const invoker = this.invoker.current as HTMLElement | null;\n this.invoker.current = null;\n try {\n invoker?.focus?.();\n } catch {\n // Invoker gone (navigated away) — nothing to restore to.\n }\n }\n\n private prefersMotion(): boolean {\n try {\n return (\n typeof window !== 'undefined' &&\n !window.matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n } catch {\n return true;\n }\n }\n}\n\nlet dialogSeq = 0;\n\nfunction focusables(root: HTMLElement): HTMLElement[] {\n // No visibility filtering on purpose: jsdom reports zero rects (unit\n // tests), and `disabled` is already excluded by the selector. Hidden\n // focusables inside a visible dialog are a consumer edge, not ours.\n const selector =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), ' +\n 'textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(root.querySelectorAll<HTMLElement>(selector));\n}\n\nfunction focusFirst(dialog: HTMLDialogElement, closeVisible: boolean): void {\n try {\n const auto = dialog.querySelector<HTMLElement>('[autofocus]');\n if (auto) {\n auto.focus({ preventScroll: true });\n return;\n }\n // Atlassian rule: no close button → focus the title; otherwise the\n // dialog itself (its tabindex=\"-1\" makes it focusable).\n if (!closeVisible) {\n const title = dialog.querySelector<HTMLElement>('.rv-modal-title');\n if (title) {\n title.setAttribute('tabindex', '-1');\n title.focus({ preventScroll: true });\n return;\n }\n }\n dialog.focus({ preventScroll: true });\n } catch {\n // Focus is best-effort (e.g. during teardown races).\n }\n}\n","<dialog\n #dialog\n tabindex=\"-1\"\n class=\"rv-modal\"\n [attr.data-size]=\"size()\"\n [attr.data-position]=\"position()\"\n [attr.data-animation]=\"animation()\"\n [attr.data-state]=\"state()\"\n [attr.data-resizing]=\"resizing()\"\n [attr.aria-label]=\"computedLabel()\"\n [attr.aria-labelledby]=\"title() && !ariaLabel() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n (cancel)=\"onCancel($event)\"\n (click)=\"onBackdropClick($event)\"\n (keydown)=\"onKeydown($event)\"\n>\n @if (hasCustomHeader()) {\n <div class=\"rv-modal-header rv-modal-header-custom\">\n <ng-content select=\"[rv-modal-header]\"></ng-content>\n </div>\n } @else if (title() || showCloseButton()) {\n <div class=\"rv-modal-header\">\n <div class=\"rv-modal-heading\">\n @if (title()) {\n <h2 class=\"rv-modal-title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (description()) {\n <p class=\"rv-modal-description\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"rv-modal-close\"\n (click)=\"onCloseButton()\"\n aria-label=\"Close dialog\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n }\n </div>\n }\n <div class=\"rv-modal-body\">\n <ng-content></ng-content>\n </div>\n @if (hasCustomFooter()) {\n <div class=\"rv-modal-footer\">\n <ng-content select=\"[rv-modal-footer]\"></ng-content>\n </div>\n }\n</dialog>\n","/**\n * Whether a dialog hides the ones below it.\n *\n * The flag is set when the dialog is OPENED, so it lives in the instance's\n * metadata — the same place the title lives, and the same place the host\n * already reads from.\n */\n\nimport type { ModalInstance } from '@rivinia/modal';\n\n/** The metadata key the flag is stored under. */\nexport const MODAL_COVERS = 'coversOthers';\n\n/**\n * Whether this dialog covers the ones below it.\n *\n * @param instance The instance to inspect.\n */\nexport function covers(instance: ModalInstance<unknown>): boolean {\n return instance.metadata?.[MODAL_COVERS] === true;\n}\n","import { Component, Injector, computed, effect, input, output, signal } from '@angular/core';\nimport type { Signal } from '@angular/core';\nimport { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';\nimport { RIVINIA_MODAL_CONTEXT } from '@rivinia/modal';\nimport type {\n ModalContext,\n ModalDismissOrigin,\n ModalInstance,\n ModalTemplateContext,\n} from '@rivinia/modal';\nimport { RiviniaModalDialog } from '../modal-dialog/modal-dialog';\nimport type { RiviniaModalSize } from '../../models/ui-types';\nimport { covers } from '../../utils/modal-covers';\n\n/**\n * Managed adapter: renders every `visible()` instance of the headless\n * `RiviniaModal` inside a {@link RiviniaModalDialog} and routes all UI\n * intents through the manager first (guards run; results settle in Core —\n * the single source of truth). The dialog never settles anything itself in\n * this mode; listen to `ModalHandle.result`, not dialog outputs.\n *\n * Exit coordination: when Core reports an instance `closing`, the dialog\n * plays its exit animation and the host acknowledges with `confirmClosed()`\n * (idempotent — safe if `closingTimeout` auto-finalized first). Match\n * `closingTimeout` to the animation duration so exits stay visible.\n *\n * Title resolution: `titleOf` per instance (default: `metadata.title` →\n * `ref` → `''`). Content descriptors render as-is: templates via\n * `ngTemplateOutlet` with `{$implicit: data, modal}`, components via\n * `NgComponentOutlet` with `inputs` + a per-instance\n * `RIVINIA_MODAL_CONTEXT` injector (the lab-proven pattern).\n *\n * The manager arrives via `[modal]` input (D06 input-composition: the UI\n * entry never instantiates Core itself, so UI-only consumers pay nothing).\n * The input is typed as {@link RiviniaModalHostManager} — the structural\n * slice the host actually drives — so the full manager (or a test double)\n * satisfies it from any module realm:\n *\n * ```html\n * <rv-modal-host [modal]=\"injectRiviniaModal()\" />\n * ```\n */\nexport interface RiviniaModalHostManager {\n readonly visible: Signal<readonly ModalInstance<unknown>[]>;\n requestDismiss(id: string, origin: ModalDismissOrigin): Promise<boolean>;\n confirmClosed(id: string): boolean;\n close(id: string, value?: unknown): boolean;\n dismiss(id: string, reason?: string): boolean;\n}\n\n@Component({\n selector: 'rv-modal-host',\n imports: [RiviniaModalDialog, NgComponentOutlet, NgTemplateOutlet],\n templateUrl: './modal-host.html',\n})\nexport class RiviniaModalHost {\n /**\n * The headless manager to render. Typed as {@link RiviniaModalHostManager}\n * (structural) — pass `injectRiviniaModal()` from your app code.\n */\n readonly modal = input.required<RiviniaModalHostManager>();\n\n /** Dialog size for every managed instance. @default 'md' */\n readonly size = input<RiviniaModalSize>('md');\n /** Render the header close button on every managed instance. @default true */\n readonly showCloseButton = input<boolean>(true);\n /** Resolve a title per instance. Default: `metadata.title` → `ref` → `''`. */\n readonly titleOf = input<(instance: ModalInstance<unknown>) => string | undefined>();\n\n /**\n * Whether an instance is still loading.\n *\n * A dialog that opens before its data arrives has nothing to show yet, and\n * an empty box reads as a broken dialog. While this returns true for an\n * instance, the host renders a loader INSTEAD of the instance's content.\n *\n * It is per instance, because a host can have several dialogs open and\n * they load independently.\n *\n * ```html\n * <rv-modal-host\n * [modal]=\"modal\"\n * [loadingOf]=\"(instance) => instance.data?.loading === true\"\n * />\n * ```\n */\n readonly loadingOf = input<(instance: ModalInstance<unknown>) => boolean>();\n\n /** Text of the loader. @default 'Loading…' */\n readonly loadingLabel = input('Loading…');\n\n /**\n * The rendered stack.\n *\n * It is a signal the host owns rather than a computed over the manager, so\n * the update can be wrapped in a view transition: the browser then paints\n * the old stack and the new one as one continuous move instead of\n * flickering between them.\n */\n protected readonly visible = signal<readonly ModalInstance[]>([]);\n\n /** Push the manager's current stack into the rendered one. */\n private readonly syncStack = effect(() => {\n const next = this.modal().visible();\n this.withTransition(() => this.visible.set(next));\n });\n\n /**\n * Smooth the stack change with a view transition.\n *\n * Opening a dialog on top of another one used to FLICKER: the top layer\n * re-promotes the lower dialog, and the browser paints the change in\n * separate frames. A view transition makes it one continuous move.\n *\n * It is optional by design: where the API is missing, the change simply\n * happens without it — a missing nicety must not break the dialog.\n */\n private readonly transition = (() => {\n if (typeof document === 'undefined') return false;\n return typeof (document as Document & { startViewTransition?: unknown }).startViewTransition === 'function';\n })();\n\n /** The last stack signature, so a change can be detected. */\n private lastStackKey = '';\n\n /**\n * Run a view transition when the visible stack changed.\n */\n protected withTransition(change: () => void): void {\n const key = this.modal()\n .visible()\n .map((i) => `${i.id}:${i.status}`)\n .join('|');\n if (!this.transition || key === this.lastStackKey) {\n change();\n this.lastStackKey = key;\n return;\n }\n const doc = document as Document & { startViewTransition?: (cb: () => void) => unknown };\n try {\n doc.startViewTransition?.(() => {\n change();\n this.lastStackKey = key;\n });\n } catch {\n change();\n this.lastStackKey = key;\n }\n }\n\n /** Whether an instance hides the ones below it. */\n protected readonly covering = computed(() => {\n const list = this.visible();\n // The topmost covering instance hides everything under it.\n let coverId: string | null = null;\n for (const instance of list) {\n if (covers(instance)) coverId = instance.id;\n }\n return coverId;\n });\n\n /**\n * Whether this instance is loading.\n *\n * A throwing resolver must not break the dialog, so it falls back to \"not\n * loading\" — the content renders, which is the safe default.\n */\n protected isLoading(instance: ModalInstance<unknown>): boolean {\n try {\n return this.loadingOf()?.(instance) === true;\n } catch {\n return false;\n }\n }\n\n /**\n * The name of the component that opened this dialog.\n *\n * Rendered as `data-caller` on the dialog, so the DevTools inspector shows\n * WHICH component opened it — the thing you want when a dialog appears and\n * you need to find its source.\n */\n protected callerName(instance: ModalInstance<unknown>): string | null {\n return instance.caller ?? null;\n }\n\n /**\n * The size of an instance.\n *\n * The host's `size` is the default; an instance may override it, so a small\n * confirm can sit on top of a wide form without a second host.\n */\n protected sizeOf(instance: ModalInstance<unknown>): RiviniaModalSize {\n const own = (instance.metadata?.['size'] as RiviniaModalSize | undefined) ?? undefined;\n return own ?? this.size();\n }\n\n /**\n * Whether an instance hides the ones below it.\n *\n * A stack renders every dialog, so the ones below stay visible behind the\n * top one. When the top one covers the others, the ones below are not\n * rendered at all — and they come back when it closes.\n */\n protected isCovered(instance: ModalInstance<unknown>): boolean {\n return this.visible().some((other) => other.id !== instance.id && covers(other));\n }\n\n protected titleOfInstance(instance: ModalInstance<unknown>): string {\n try {\n const custom = this.titleOf()?.(instance);\n if (custom !== undefined) return custom;\n } catch {\n // A throwing resolver must never break rendering — fall through.\n }\n const meta = instance.metadata?.['title'];\n if (typeof meta === 'string' && meta.length > 0) return meta;\n return instance.ref ?? '';\n }\n\n protected templateContext(instance: ModalInstance<unknown>): ModalTemplateContext<unknown> {\n return { $implicit: instance.data, modal: this.modalContext(instance) };\n }\n\n protected contextInjector(instance: ModalInstance<unknown>): Injector {\n const modal = this.modal();\n return Injector.create({\n providers: [\n {\n provide: RIVINIA_MODAL_CONTEXT,\n useValue: {\n id: instance.id,\n data: instance.data,\n parentId: instance.parentId,\n close: (result?: unknown) => modal.close(instance.id, result),\n dismiss: (reason?: string) => modal.dismiss(instance.id, reason),\n } satisfies ModalContext<unknown>,\n },\n ],\n });\n }\n\n protected async onIntent(id: string, origin: 'button' | 'escape' | 'backdrop'): Promise<void> {\n try {\n await this.modal().requestDismiss(id, origin);\n } catch {\n // requestDismiss never rejects (deny-safe by contract) — defensive only.\n }\n // Granted → status flips to `closing` → dialog exit → `exitDone`.\n // Denied → nothing moves; the dialog never left `open`. No manual toggle.\n }\n\n protected onExitDone(id: string): void {\n this.modal().confirmClosed(id);\n }\n\n private modalContext(instance: ModalInstance<unknown>): ModalContext<unknown> {\n const modal = this.modal();\n const id = instance.id;\n return {\n id,\n data: instance.data,\n parentId: instance.parentId,\n close: (result?: unknown) => modal.close(id, result),\n dismiss: (reason?: string) => modal.dismiss(id, reason),\n };\n }\n}\n","@for (instance of visible(); track instance.id) {\n @if (isCovered(instance)) {\n <!--\n Hidden by a dialog above it that asked to cover the others. It stays\n in the manager — only the rendering is skipped, so it comes back when\n the cover closes.\n -->\n } @else {\n <rv-modal-dialog\n [open]=\"true\"\n [managed]=\"true\"\n [closing]=\"instance.status === 'closing'\"\n [title]=\"titleOfInstance(instance)\"\n [size]=\"sizeOf(instance)\"\n [attr.data-caller]=\"callerName(instance)\"\n [showCloseButton]=\"showCloseButton()\"\n [closeOnEscape]=\"instance.dismiss.closeOnEscape ?? true\"\n [backdrop]=\"instance.dismiss.backdrop ?? 'close'\"\n (dismissRequested)=\"onIntent(instance.id, $event)\"\n (exitDone)=\"onExitDone(instance.id)\"\n >\n @if (isLoading(instance)) {\n <!--\n The data has not arrived yet. A loader says so; an empty dialog\n looks broken.\n -->\n <div class=\"rv-modal-loading\" role=\"status\" aria-live=\"polite\">\n <span class=\"rv-modal-spinner\" aria-hidden=\"true\"></span>\n <span>{{ loadingLabel() }}</span>\n </div>\n } @else if (instance.content.kind === 'template') {\n <ng-container\n *ngTemplateOutlet=\"instance.content.template; context: templateContext(instance)\"\n ></ng-container>\n } @else {\n <ng-container\n *ngComponentOutlet=\"\n instance.content.component;\n inputs: instance.content.inputs;\n injector: contextInjector(instance)\n \"\n ></ng-container>\n }\n </rv-modal-dialog>\n }\n}\n"],"mappings":"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"}
|
|
@@ -461,7 +461,7 @@ var RiviniaModal = class RiviniaModal {
|
|
|
461
461
|
}
|
|
462
462
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
463
463
|
minVersion: "12.0.0",
|
|
464
|
-
version: "22.2.
|
|
464
|
+
version: "22.2.3",
|
|
465
465
|
ngImport: i0,
|
|
466
466
|
type: RiviniaModal,
|
|
467
467
|
deps: [],
|
|
@@ -469,14 +469,14 @@ var RiviniaModal = class RiviniaModal {
|
|
|
469
469
|
});
|
|
470
470
|
static ɵprov = i0.ɵɵngDeclareService({
|
|
471
471
|
minVersion: "22.0.0",
|
|
472
|
-
version: "22.2.
|
|
472
|
+
version: "22.2.3",
|
|
473
473
|
ngImport: i0,
|
|
474
474
|
type: RiviniaModal
|
|
475
475
|
});
|
|
476
476
|
};
|
|
477
477
|
i0.ɵɵngDeclareClassMetadata({
|
|
478
478
|
minVersion: "12.0.0",
|
|
479
|
-
version: "22.2.
|
|
479
|
+
version: "22.2.3",
|
|
480
480
|
ngImport: i0,
|
|
481
481
|
type: RiviniaModal,
|
|
482
482
|
decorators: [{ type: Service }],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rivinia-modal.mjs","names":[],"sources":["../../../projects/modal/src/lib/modal-strategy.ts","../../../projects/modal/src/lib/modal-context.ts","../../../projects/modal/src/lib/modal-config.ts","../../../projects/modal/src/lib/rivinia-modal.ts","../../../projects/modal/src/lib/provide-modal.ts"],"sourcesContent":["/**\n * Ordering/admission policy. Strategies are pure with respect to lifecycle:\n * the manager owns identity, results, timers and disposal; strategies own\n * ordering and activation width. No mode-dependent `if/else` in the manager.\n */\nexport interface ModalStrategy {\n readonly name: string;\n /** Maximum simultaneously active instances under this strategy. */\n maxActive(maxVisible: number): number;\n /**\n * Admission of a new request given current visible/queued sets.\n * Returns the action; the manager executes it (and settles results).\n */\n admit(visibleCount: number, queuedCount: number, maxVisible: number): 'activate' | 'queue';\n /** Display order of visible instances, bottom-first. */\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[];\n}\n\n/** Multiple concurrent modals, topmost (last opened) interactive. */\nexport class StackStrategy implements ModalStrategy {\n readonly name = 'stack';\n maxActive(maxVisible: number): number {\n return Math.max(1, Math.floor(maxVisible));\n }\n admit(visibleCount: number, _queuedCount: number, maxVisible: number): 'activate' | 'queue' {\n return visibleCount < Math.max(1, Math.floor(maxVisible)) ? 'activate' : 'queue';\n }\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[] {\n return [...visible].sort((a, b) => a.openedSeq - b.openedSeq);\n }\n}\n\n/** Strictly serial: one active modal, FIFO activation of the rest. */\nexport class QueueStrategy implements ModalStrategy {\n readonly name = 'queue';\n maxActive(_maxVisible: number): number {\n return 1;\n }\n admit(visibleCount: number, _queuedCount: number, _maxVisible: number): 'activate' | 'queue' {\n return visibleCount < 1 ? 'activate' : 'queue';\n }\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[] {\n return [...visible].sort((a, b) => a.openedSeq - b.openedSeq);\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport type { ModalContext } from './modal-types';\n\n/**\n * Injection token providing the `ModalContext` of the enclosing modal to\n * dynamically created components. Template content receives the same object\n * as `let-modal=\"modal\"`. Never global — one value per modal instance.\n */\nexport const RIVINIA_MODAL_CONTEXT = new InjectionToken<ModalContext<unknown>>(\n 'RIVINIA_MODAL_CONTEXT',\n);\n","import { InjectionToken } from '@angular/core';\nimport type {\n DescendantPolicy,\n ModalDismissPolicy,\n ModalOverflowPolicy,\n} from './modal-types';\n\n/** Named preset: a reusable dismissal shape (explicit options always win). */\nexport interface ModalPreset {\n readonly dismiss?: Partial<ModalDismissPolicy>;\n}\n\n/**\n * Global configuration for `@rivinia/modal`. Everything optional; per-open\n * options win. No styling, positions, icons, or translations here — ever.\n */\nexport interface RiviniaModalConfig {\n /**\n * Ordering/activation policy. `'stack'` = up to `maxVisible` concurrent,\n * topmost interactive. `'queue'` = strictly serial (one active, FIFO rest).\n * A custom `ModalStrategy` instance is also accepted.\n * @default 'stack'\n */\n readonly strategy?: 'stack' | 'queue' | import('./modal-strategy').ModalStrategy;\n /** Global dismissal defaults. @default { closeOnEscape: true, backdrop: 'close' } */\n readonly dismiss?: ModalDismissPolicy;\n /** Capacity of simultaneously active modals. Clamped to ≥ 1. @default 5 */\n readonly maxVisible?: number;\n /** Bound for queued requests (queue policy). @default Infinity */\n readonly maxQueue?: number;\n /** Overflow behavior when full. @default 'queue' (lossless — never drops input) */\n readonly overflow?: ModalOverflowPolicy;\n /** Parent closure with remaining descendants. @default 'close-with-parent' */\n readonly descendants?: DescendantPolicy;\n /** Named dismissal presets referenced by `openModal({ preset })`. */\n readonly presets?: Record<string, ModalPreset>;\n /**\n * Time the manager waits for UI teardown confirmation (`confirmClosed`)\n * before auto-finalizing a `closing` instance. `0` = manual only.\n * @default 300\n */\n readonly closingTimeout?: number;\n /**\n * Bounded transition log for debugging (ring buffer, no payloads).\n * Off by default to stay production-light.\n * @default false\n */\n readonly diagnostics?: boolean;\n}\n\n/**\n * Internal configuration token. Consumers must use `provideRiviniaModal()`.\n * @internal\n */\nexport const RIVINIA_MODAL_CONFIG = new InjectionToken<RiviniaModalConfig>(\n 'RIVINIA_MODAL_CONFIG',\n);\n\nexport const DEFAULT_MAX_VISIBLE = 5;\nexport const DEFAULT_CLOSING_TIMEOUT = 300;\nexport const DIAGNOSTIC_CAP = 100;\n","import { DestroyRef, TemplateRef, computed, inject, Service, signal } from '@angular/core';\nimport type { Type } from '@angular/core';\nimport {\n DEFAULT_CLOSING_TIMEOUT,\n DEFAULT_MAX_VISIBLE,\n DIAGNOSTIC_CAP,\n RIVINIA_MODAL_CONFIG,\n type RiviniaModalConfig,\n} from './modal-config';\nimport type {\n ModalContent,\n ModalDiagnostic,\n ModalDismissOrigin,\n ModalDismissPolicy,\n ModalGuard,\n ModalHandle,\n ModalInstance,\n ModalOpenOptions,\n ModalResult,\n ModalTemplateContext,\n ModalUpdate,\n} from './modal-types';\nimport { QueueStrategy, StackStrategy, type ModalStrategy } from './modal-strategy';\n\ninterface ModalRecord extends ModalInstance<unknown> {\n openedSeq: number;\n generation: number;\n guards: readonly ModalGuard[];\n closingTimer: ReturnType<typeof setTimeout> | null;\n ownerCleanup: (() => void) | null;\n}\n\ninterface PendingResult {\n promise: Promise<ModalResult<unknown>>;\n resolve: (value: ModalResult<unknown>) => void;\n settled: boolean;\n}\n\n/**\n * Public snapshot of an internal record. Strips lifecycle internals (`guards`,\n * `closingTimer`, `ownerCleanup`, `generation`, `openedSeq`) so consumers can\n * never observe or mutate manager state. Frozen: UI must use `update()`.\n */\nfunction toPublicInstance(record: ModalRecord): ModalInstance {\n return Object.freeze({\n id: record.id,\n ref: record.ref,\n caller: record.caller,\n parentId: record.parentId,\n data: record.data,\n content: record.content,\n status: record.status,\n dismiss: Object.freeze({ ...record.dismiss }),\n metadata: Object.freeze({ ...record.metadata }),\n }) as ModalInstance;\n}\n\n/**\n * Headless modal manager. Owns identity, lifecycle coordination, result\n * settlement and diagnostics. Ordering/admission policy is delegated to a\n * `ModalStrategy` (Stack/Queue/custom) — the manager never branches on mode.\n *\n * Settlement contract (each path settles exactly once, never hangs):\n * - `close()` on active → `confirmed` · `dismiss()` on active → `dismissed`\n * - `cancel()` on active → `cancelled` · on queued → `queued-cancelled`\n * - `dismiss()` on queued → `dismissed` with the reason (pre-activation)\n * - overflow/guard refusal → `rejected` · owner gone → `owner-destroyed`\n * - forced paths (owner, overflow eviction, `dismissAll`) bypass guards\n * - `close/dismiss/cancel` are guard-free primitives; `requestDismiss` runs\n * guards for user intents (escape/backdrop/button) or explicit review\n * - infrastructure failures reject the promise — never a result variant\n *\n * Results settle at dismissal initiation, not after UI teardown: animations\n * never delay callers. The `closing` → `closed` handshake (`confirmClosed`\n * or `closingTimeout` auto-finalize) exists only for exit animations.\n */\n@Service()\nexport class RiviniaModal {\n private readonly config = inject<RiviniaModalConfig>(RIVINIA_MODAL_CONFIG, {\n optional: true,\n });\n\n private readonly _instances = signal<readonly ModalRecord[]>([]);\n private readonly _results = new Map<string, PendingResult>();\n /** In-flight guarded dismissals: concurrent intents share one guard run. */\n private readonly _dismissInflight = new Map<string, Promise<boolean>>();\n private _strategy: ModalStrategy = this.resolveStrategy(this.config?.strategy);\n private _seq = 0;\n private _diagSeq = 0;\n private readonly _diagnostics: ModalDiagnostic[] = [];\n\n /** Active instances in strategy display order (bottom-first). */\n readonly active = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'active'))\n .map(toPublicInstance),\n );\n /** Queued instances, FIFO admission order. */\n readonly queued = computed<readonly ModalInstance[]>(() =>\n this._instances()\n .filter((i) => i.status === 'queued')\n .map(toPublicInstance),\n );\n /** Closing instances (exit animations), in strategy display order. */\n readonly closing = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'closing'))\n .map(toPublicInstance),\n );\n /** Everything rendered (active + closing), in strategy display order. */\n readonly visible = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'active' || i.status === 'closing'))\n .map(toPublicInstance),\n );\n /** Topmost active instance (the only Escape-eligible one), if any. */\n readonly top = computed<ModalInstance | null>(() => {\n const list = this.active();\n return list.length > 0 ? list[list.length - 1] : null;\n });\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n for (const record of [...this._instances()]) {\n if (record.status === 'queued') {\n this.remove(record.id);\n this.settle(record.id, { status: 'queued-cancelled' });\n } else if (record.status !== 'closed') {\n this.clearClosingTimer(record.id);\n this.remove(record.id);\n this.settle(record.id, { status: 'dismissed', reason: 'manager-destroyed' });\n }\n }\n });\n }\n\n openModal<TData = unknown, TResult = unknown>(\n template: TemplateRef<ModalTemplateContext<TData>>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TComponent, TData = unknown, TResult = unknown>(\n component: Type<TComponent>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TData = unknown, TResult = unknown>(\n content: ModalContent<TData>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TData = unknown, TResult = unknown>(\n content: ModalContent<TData> | TemplateRef<ModalTemplateContext<TData>> | Type<unknown>,\n options: ModalOpenOptions<TData> = {},\n ): ModalHandle<TResult> {\n const id = `modal-${++this._seq}`;\n const pending = this.deferred();\n // Type hole (internal, by design): the deferred promise is created as\n // `ModalResult<unknown>` and cast to `ModalResult<TResult>`. A `TResult`\n // mismatch is never validated at runtime — `close()` settles whatever it\n // receives. The handle's `close(result?: TResult)` signature IS checked\n // (see type tests); this cast only forwards that contract.\n this._results.set(id, pending);\n const handle: ModalHandle<TResult> = {\n id,\n result: pending.promise as Promise<ModalResult<TResult>>,\n close: (value?: TResult) => this.close(id, value),\n dismiss: (reason?: string) => this.dismiss(id, reason),\n cancel: (reason?: string) => this.cancel(id, reason),\n };\n\n const visibleCount = this._instances().filter((i) => i.status !== 'queued').length;\n const intent = this._strategy.admit(visibleCount, this.queuedCount(), this.maxVisible());\n let status: 'active' | 'queued' = intent === 'activate' ? 'active' : 'queued';\n if (intent === 'queue') {\n const outcome = this.admitQueued(id);\n if (outcome === 'active' || outcome === 'queued') {\n // 'dismiss-oldest' evicted room: the newcomer activates immediately.\n // 'queue' keeps it queued.\n status = outcome;\n } else {\n this.settle(id, { status: 'rejected', reason: outcome });\n this.diagnose('rejected', id, options.ref ?? null, outcome);\n return handle;\n }\n }\n\n const record: ModalRecord = {\n id,\n ref: options.ref ?? null,\n // A component type is stored by its name, so the UI can render it and\n // the DevTools inspector can show it.\n caller:\n typeof options.caller === 'string'\n ? options.caller\n : (options.caller?.name ?? null),\n parentId: this.knownParent(options.parentId) ? (options.parentId as string) : null,\n data: options.data as unknown,\n content: this.normalizeContent(content, options),\n status,\n // Precedence: built-in defaults < global config < named preset < per-call.\n // Layers merge field-by-field so an absent key never clobbers a default.\n dismiss: this.resolveDismiss(options.dismiss, options.preset),\n /*\n * `size` and `coversOthers` are options, but the UI reads them from the\n * instance — so they are recorded in the metadata, next to the title.\n * A caller who set the same key in `metadata` wins: an explicit\n * `metadata` entry is more specific than the option.\n */\n metadata: {\n ...(options.size !== undefined ? { size: options.size } : {}),\n ...(options.coversOthers !== undefined ? { coversOthers: options.coversOthers } : {}),\n ...(options.metadata ?? {}),\n },\n openedSeq: this._seq,\n generation: 0,\n guards: [...(options.guards ?? [])],\n closingTimer: null,\n ownerCleanup: null,\n };\n if (options.parentId !== undefined && record.parentId === null) {\n this.diagnose('rejected', id, record.ref, 'stale-parent');\n }\n this._instances.update((list) => [...list, record]);\n\n if (options.owner) {\n let ownerCleanup: (() => void) | null = null;\n try {\n ownerCleanup = options.owner.onDestroy(() => this.teardownOwned(id));\n } catch {\n // Owner already destroyed (NG0205): settle immediately and never add.\n this._instances.update((list) => list.filter((i) => i.id !== id));\n this.settle(id, { status: 'owner-destroyed' });\n this.diagnose('rejected', id, record.ref, 'owner-destroyed');\n return handle;\n }\n this.patch(id, { ownerCleanup });\n }\n this.diagnose(intent === 'activate' ? 'activated' : 'queued', id, record.ref);\n return handle;\n }\n\n /**\n * Confirms with a value. Active instances only; settles `confirmed`.\n * Blocked (returns `false`) by the prevent-parent-close policy.\n */\n close(id: string, value?: unknown): boolean {\n const record = this.get(id);\n if (!record || record.status !== 'active' || this.blockedByDescendants(id)) return false;\n this.beginClosing(id, 'confirmed', undefined, value);\n return true;\n }\n\n /**\n * Guard-free dismissal primitive. Active → `dismissed`; queued → removed as\n * `dismissed` with the reason. Blocked by prevent-parent-close.\n */\n dismiss(id: string, reason?: string): boolean {\n const record = this.get(id);\n if (!record || this.blockedByDescendants(id)) return false;\n if (record.status === 'active') {\n this.beginClosing(id, 'dismissed', reason);\n return true;\n }\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'dismissed', reason });\n this.diagnose('dismissed', id, record.ref, reason);\n return true;\n }\n return false;\n }\n\n /**\n * Cancels: active → `cancelled`, queued → removed as `queued-cancelled`.\n * Blocked by prevent-parent-close. Closing/closed/unknown → `false`.\n */\n cancel(id: string, reason?: string): boolean {\n const record = this.get(id);\n if (!record || this.blockedByDescendants(id)) return false;\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', id, record.ref, reason ?? 'cancelled');\n return true;\n }\n if (record.status !== 'active') return false;\n this.beginClosing(id, 'cancelled', reason);\n return true;\n }\n\n /**\n * Guarded dismissal for user intents (escape/backdrop/button) or explicit\n * programmatic review. Guards run sequentially; any `false` or throw denies\n * (stays open + diagnostic). Stale guard completions are ignored.\n */\n async requestDismiss(id: string, origin: ModalDismissOrigin, reason?: string): Promise<boolean> {\n const record = this.get(id);\n if (!record || record.status !== 'active' || this.blockedByDescendants(id)) return false;\n const inflight = this._dismissInflight.get(id);\n if (inflight) return inflight;\n const run = this.runGuards(id, origin, reason);\n this._dismissInflight.set(id, run);\n try {\n return await run;\n } finally {\n if (this._dismissInflight.get(id) === run) this._dismissInflight.delete(id);\n }\n }\n\n private async runGuards(\n id: string,\n origin: ModalDismissOrigin,\n reason?: string,\n ): Promise<boolean> {\n const record = this.get(id);\n if (!record) return false;\n const generation = record.generation;\n for (const guard of record.guards) {\n let allowed: boolean;\n try {\n allowed = await guard({ id, origin });\n } catch {\n allowed = false;\n }\n const current = this.get(id);\n if (!current || current.status !== 'active' || current.generation !== generation) {\n return false; // Resolved stale: something else already moved it.\n }\n if (!allowed) {\n this.diagnose('guard-denied', id, record.ref, origin);\n return false;\n }\n }\n return this.dismiss(id, reason ?? origin);\n }\n\n /** Merges data/metadata (new object identity so UI re-renders). */\n update<TData>(id: string, changes: import('./modal-types').ModalUpdate<TData>): boolean {\n const record = this.get(id);\n if (!record || (record.status !== 'active' && record.status !== 'queued')) return false;\n this._instances.update((list) =>\n list.map((t) =>\n t.id === id\n ? {\n ...t,\n data: (changes.data !== undefined ? changes.data : t.data) as unknown,\n metadata: changes.metadata !== undefined ? { ...changes.metadata } : t.metadata,\n }\n : t,\n ),\n );\n return true;\n }\n\n /** UI teardown confirmation after exit animations. Auto-finalizes via timeout. */\n confirmClosed(id: string): boolean {\n const record = this.get(id);\n if (!record || record.status !== 'closing') return false;\n this.finalize(id);\n return true;\n }\n\n /** Forced cleanup (bypasses guards): active → `dismissed`, queued → `queued-cancelled`. */\n dismissAll(reason = 'dismiss-all'): void {\n for (const record of [...this._instances()]) {\n if (record.status === 'queued') {\n this.remove(record.id);\n this.settle(record.id, { status: 'queued-cancelled' });\n } else if (record.status === 'active') {\n this.beginClosing(record.id, 'dismissed', reason);\n } else if (record.status === 'closing') {\n this.finalize(record.id);\n }\n }\n }\n\n /** Non-closed snapshots for a definition ref (e.g. avoid duplicate confirms). */\n findByRef(ref: string): ModalInstance[] {\n return this._instances()\n .filter((i) => i.ref === ref && i.status !== 'closed')\n .map(toPublicInstance);\n }\n\n /**\n * Swaps the strategy. Rejected with an explicit error while any instance is\n * not closed — silent live migration is never attempted.\n */\n useStrategy(strategy: ModalStrategy): void {\n if (this._instances().some((i) => i.status !== 'closed')) {\n throw new Error(\n '[rivinia/modal] useStrategy() rejected: active, queued or closing modals exist. ' +\n 'Close everything first — live migration is not supported.',\n );\n }\n this._strategy = strategy;\n }\n\n /** Introspection snapshot. Active means interactive; closing instances are\n * still rendered (exit animations) until `confirmClosed`/timeout finalizes. */\n debugState(): { strategy: string; active: string[]; closing: string[]; queued: string[] } {\n const all = this._instances();\n return {\n strategy: this._strategy.name,\n active: this.orderedActive()\n .filter((i) => i.status === 'active')\n .map((i) => i.id),\n closing: all.filter((i) => i.status === 'closing').map((i) => i.id),\n queued: all.filter((i) => i.status === 'queued').map((i) => i.id),\n };\n }\n\n /** Bounded transition log (only when `diagnostics: true`). */\n getDiagnostics(): readonly import('./modal-types').ModalDiagnostic[] {\n return [...this._diagnostics];\n }\n\n clearDiagnostics(): void {\n this._diagnostics.length = 0;\n }\n\n // ------------------------------------------------------------------ internals\n\n private get(id: string): ModalRecord | undefined {\n return this._instances().find((i) => i.id === id);\n }\n\n /** Accepts raw `TemplateRef`/`Type` or a full descriptor (README form). */\n private normalizeContent<TData>(\n content: ModalContent<TData> | TemplateRef<ModalTemplateContext<TData>> | Type<unknown>,\n options: ModalOpenOptions<TData>,\n ): ModalRecord['content'] {\n if (content instanceof TemplateRef) {\n return {\n kind: 'template',\n template: content as unknown as TemplateRef<ModalTemplateContext<unknown>>,\n };\n }\n if (typeof content === 'function') {\n return {\n kind: 'component',\n component: content as Type<unknown>,\n ...(options.inputs ? { inputs: { ...options.inputs } } : {}),\n };\n }\n return content as ModalRecord['content'];\n }\n\n private queuedCount(): number {\n return this._instances().filter((i) => i.status === 'queued').length;\n }\n\n private orderedActive(): ModalRecord[] {\n return [\n ...this._strategy.order(\n this._instances().filter((i) => i.status === 'active' || i.status === 'closing'),\n ),\n ];\n }\n\n private maxVisible(): number {\n return Math.max(1, Math.floor(this.config?.maxVisible ?? DEFAULT_MAX_VISIBLE));\n }\n\n private maxQueue(): number {\n const value = this.config?.maxQueue ?? Number.POSITIVE_INFINITY;\n return value < 0 ? 0 : value;\n }\n\n private presetDismiss(preset?: string): Record<string, unknown> {\n if (!preset) return {};\n return { ...(this.config?.presets?.[preset]?.dismiss ?? {}) };\n }\n\n private resolveDismiss(\n perCall?: Partial<ModalDismissPolicy>,\n preset?: string,\n ): ModalDismissPolicy {\n const merged: { closeOnEscape?: boolean; backdrop?: 'close' | 'static' | 'disabled' } = {\n closeOnEscape: true,\n backdrop: 'close',\n };\n const layers: Array<Partial<ModalDismissPolicy> | Record<string, unknown> | undefined> = [\n this.config?.dismiss,\n this.presetDismiss(preset),\n perCall,\n ];\n for (const layer of layers) {\n if (!layer) continue;\n const typed = layer as {\n closeOnEscape?: boolean;\n backdrop?: 'close' | 'static' | 'disabled';\n };\n if (typed.closeOnEscape !== undefined) merged.closeOnEscape = typed.closeOnEscape;\n if (typed.backdrop !== undefined) merged.backdrop = typed.backdrop;\n }\n return merged as import('./modal-types').ModalDismissPolicy;\n }\n\n private knownParent(parentId: string | undefined): parentId is string {\n if (parentId === undefined) return false;\n return this._instances().some((i) => i.id === parentId && i.status !== 'closed');\n }\n\n /** Overflow handling for queue-intent admissions. Returns rejection reason, or the resulting status. */\n private admitQueued(id: string): string | 'active' | 'queued' {\n switch (this.config?.overflow ?? 'queue') {\n case 'reject':\n return 'overflow';\n case 'dismiss-oldest': {\n // Preempt: evict oldest pure-active instance (system path, no guards).\n const oldest = this.orderedActive().find((i) => i.status === 'active');\n if (oldest) this.beginClosing(oldest.id, 'dismissed', 'overflow-evicted');\n return 'active';\n }\n case 'queue':\n default:\n return this.queuedCount() >= this.maxQueue() ? 'queue-full' : 'queued';\n }\n }\n\n private blockedByDescendants(id: string): boolean {\n if ((this.config?.descendants ?? 'close-with-parent') !== 'prevent-parent-close') {\n return false;\n }\n const blocked = this._instances().some(\n (i) => i.parentId === id && i.status !== 'closed' && i.status !== 'closing',\n );\n if (blocked)\n this.diagnose('strategy-rejected', id, this.get(id)?.ref ?? null, 'prevent-parent-close');\n return blocked;\n }\n\n private beginClosing(\n id: string,\n status: 'confirmed' | 'dismissed' | 'cancelled',\n reason?: string,\n value?: unknown,\n ): void {\n const record = this.get(id);\n if (!record || record.status !== 'active') return;\n const generation = record.generation + 1;\n this.closeDescendants(id, false);\n this.patch(id, { status: 'closing', generation });\n if (status === 'confirmed') {\n this.settle(id, { status, value } as ModalResult<unknown>);\n } else {\n this.settle(id, { status, reason } as ModalResult<unknown>);\n }\n this.diagnose('closing', id, record.ref, reason);\n const timeout = Math.max(0, this.config?.closingTimeout ?? DEFAULT_CLOSING_TIMEOUT);\n if (timeout === 0) return; // Manual teardown: UI must call confirmClosed().\n const timer = setTimeout(() => {\n const current = this.get(id);\n if (current && current.status === 'closing' && current.generation === generation) {\n this.finalize(id);\n }\n }, timeout);\n this.patch(id, { closingTimer: timer });\n }\n\n private closeDescendants(parentId: string, force: boolean): void {\n if (!force && (this.config?.descendants ?? 'close-with-parent') !== 'close-with-parent') {\n return;\n }\n for (const child of this._instances().filter(\n (i) => i.parentId === parentId && (i.status === 'active' || i.status === 'queued'),\n )) {\n if (child.status === 'queued') {\n this.remove(child.id);\n this.settle(child.id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', child.id, child.ref, 'parent-closed');\n } else {\n this.closeDescendants(child.id, force);\n this.beginClosing(child.id, 'dismissed', 'parent-closed');\n }\n }\n }\n\n private finalize(id: string, promoteAfter = true): void {\n const record = this.get(id);\n if (!record) return;\n this.clearClosingTimer(id);\n const cleanup = record.ownerCleanup;\n const ref = record.ref;\n this.patch(id, { status: 'closed', closingTimer: null });\n cleanup?.();\n this._instances.update((list) => list.filter((i) => i.id !== id));\n this._results.delete(id);\n this.diagnose('closed', id, ref);\n if (promoteAfter) this.promote();\n }\n\n private promote(): void {\n const width = this._strategy.maxActive(this.maxVisible());\n const visible = (): number =>\n this._instances().filter((i) => i.status === 'active' || i.status === 'closing').length;\n while (visible() < width) {\n const head = this._instances().find((i) => i.status === 'queued');\n if (!head) break;\n this.patch(head.id, { status: 'active' });\n this.diagnose('promoted', head.id, head.ref);\n }\n }\n\n private remove(id: string): void {\n this.clearClosingTimer(id);\n this._instances.update((list) => list.filter((i) => i.id !== id));\n }\n\n private clearClosingTimer(id: string): void {\n const record = this.get(id);\n if (record?.closingTimer) {\n clearTimeout(record.closingTimer);\n this.patch(id, { closingTimer: null });\n }\n }\n\n private teardownOwned(id: string): void {\n const record = this.get(id);\n if (!record || record.status === 'closed' || record.status === 'closing') return;\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', id, record.ref, 'owner-destroyed');\n return;\n }\n this.closeDescendants(id, true);\n this.patch(id, { status: 'closing', generation: record.generation + 1 });\n this.settle(id, { status: 'owner-destroyed' });\n this.diagnose('closing', id, record.ref, 'owner-destroyed');\n // Promote normally: freeing room must activate the queued head (whose own\n // owner is alive). A queued toast sharing the dead owner is torn down by\n // its own owner callback in the same pass, settling owner-destroyed.\n this.finalize(id);\n }\n\n private patch(id: string, changes: Partial<ModalRecord>): void {\n this._instances.update((list) => list.map((t) => (t.id === id ? { ...t, ...changes } : t)));\n }\n\n private deferred<TResult>(): PendingResult & { promise: Promise<ModalResult<TResult>> } {\n let resolve!: (value: ModalResult<unknown>) => void;\n const promise = new Promise<ModalResult<unknown>>((res) => {\n resolve = res;\n });\n return {\n promise: promise as Promise<ModalResult<TResult>>,\n resolve: (value) => {\n resolve(value);\n },\n settled: false,\n };\n }\n\n private settle(id: string, value: ModalResult<unknown>): void {\n const pending = this._results.get(id);\n if (!pending || pending.settled) return; // Exactly-once, even under reentrancy.\n pending.settled = true;\n // Delete on settle: entries must not outlive their promises. finalize()\n // keeps its own delete as a backstop for paths that bypass settle.\n this._results.delete(id);\n pending.resolve(value);\n }\n\n private diagnose(\n type: import('./modal-types').ModalDiagnostic['type'],\n id: string,\n ref: string | null,\n reason?: string,\n ): void {\n if (!this.config?.diagnostics) return;\n this._diagnostics.push({ seq: ++this._diagSeq, type, id, ref, reason });\n while (this._diagnostics.length > DIAGNOSTIC_CAP) this._diagnostics.shift();\n }\n\n private resolveStrategy(configured: RiviniaModalConfig['strategy']): ModalStrategy {\n if (!configured || configured === 'stack') return new StackStrategy();\n if (configured === 'queue') return new QueueStrategy();\n return configured;\n }\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders } from '@angular/core';\nimport { RIVINIA_MODAL_CONFIG, type RiviniaModalConfig } from './modal-config';\nimport { RiviniaModal } from './rivinia-modal';\n\n/**\n * Configures `@rivinia/modal`. The manager is a root singleton; call once at\n * bootstrap. Per-open options always win over these globals. No styling,\n * positions, or icons here — ever.\n *\n * @example\n * ```ts\n * provideRiviniaModal({ strategy: 'stack', maxVisible: 3 })\n * ```\n */\nexport function provideRiviniaModal(config: RiviniaModalConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: RIVINIA_MODAL_CONFIG, useValue: config }]);\n}\n\n/** Shorthand for `inject(RiviniaModal)`. */\nexport function injectRiviniaModal(): RiviniaModal {\n return inject(RiviniaModal);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rivinia-modal.mjs","names":[],"sources":["../../../projects/modal/src/lib/services/modal-strategy.ts","../../../projects/modal/src/lib/models/modal-context.ts","../../../projects/modal/src/lib/models/modal-config.ts","../../../projects/modal/src/lib/services/rivinia-modal.ts","../../../projects/modal/src/lib/providers/provide-modal.ts"],"sourcesContent":["/**\n * Ordering/admission policy. Strategies are pure with respect to lifecycle:\n * the manager owns identity, results, timers and disposal; strategies own\n * ordering and activation width. No mode-dependent `if/else` in the manager.\n */\nexport interface ModalStrategy {\n readonly name: string;\n /** Maximum simultaneously active instances under this strategy. */\n maxActive(maxVisible: number): number;\n /**\n * Admission of a new request given current visible/queued sets.\n * Returns the action; the manager executes it (and settles results).\n */\n admit(visibleCount: number, queuedCount: number, maxVisible: number): 'activate' | 'queue';\n /** Display order of visible instances, bottom-first. */\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[];\n}\n\n/** Multiple concurrent modals, topmost (last opened) interactive. */\nexport class StackStrategy implements ModalStrategy {\n readonly name = 'stack';\n maxActive(maxVisible: number): number {\n return Math.max(1, Math.floor(maxVisible));\n }\n admit(visibleCount: number, _queuedCount: number, maxVisible: number): 'activate' | 'queue' {\n return visibleCount < Math.max(1, Math.floor(maxVisible)) ? 'activate' : 'queue';\n }\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[] {\n return [...visible].sort((a, b) => a.openedSeq - b.openedSeq);\n }\n}\n\n/** Strictly serial: one active modal, FIFO activation of the rest. */\nexport class QueueStrategy implements ModalStrategy {\n readonly name = 'queue';\n maxActive(_maxVisible: number): number {\n return 1;\n }\n admit(visibleCount: number, _queuedCount: number, _maxVisible: number): 'activate' | 'queue' {\n return visibleCount < 1 ? 'activate' : 'queue';\n }\n order<T extends { readonly openedSeq: number }>(visible: readonly T[]): readonly T[] {\n return [...visible].sort((a, b) => a.openedSeq - b.openedSeq);\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport type { ModalContext } from './modal-types';\n\n/**\n * Injection token providing the `ModalContext` of the enclosing modal to\n * dynamically created components. Template content receives the same object\n * as `let-modal=\"modal\"`. Never global — one value per modal instance.\n */\nexport const RIVINIA_MODAL_CONTEXT = new InjectionToken<ModalContext<unknown>>(\n 'RIVINIA_MODAL_CONTEXT',\n);\n","import { InjectionToken } from '@angular/core';\nimport type {\n DescendantPolicy,\n ModalDismissPolicy,\n ModalOverflowPolicy,\n} from './modal-types';\n\n/** Named preset: a reusable dismissal shape (explicit options always win). */\nexport interface ModalPreset {\n readonly dismiss?: Partial<ModalDismissPolicy>;\n}\n\n/**\n * Global configuration for `@rivinia/modal`. Everything optional; per-open\n * options win. No styling, positions, icons, or translations here — ever.\n */\nexport interface RiviniaModalConfig {\n /**\n * Ordering/activation policy. `'stack'` = up to `maxVisible` concurrent,\n * topmost interactive. `'queue'` = strictly serial (one active, FIFO rest).\n * A custom `ModalStrategy` instance is also accepted.\n * @default 'stack'\n */\n readonly strategy?: 'stack' | 'queue' | import('../services/modal-strategy').ModalStrategy;\n /** Global dismissal defaults. @default { closeOnEscape: true, backdrop: 'close' } */\n readonly dismiss?: ModalDismissPolicy;\n /** Capacity of simultaneously active modals. Clamped to ≥ 1. @default 5 */\n readonly maxVisible?: number;\n /** Bound for queued requests (queue policy). @default Infinity */\n readonly maxQueue?: number;\n /** Overflow behavior when full. @default 'queue' (lossless — never drops input) */\n readonly overflow?: ModalOverflowPolicy;\n /** Parent closure with remaining descendants. @default 'close-with-parent' */\n readonly descendants?: DescendantPolicy;\n /** Named dismissal presets referenced by `openModal({ preset })`. */\n readonly presets?: Record<string, ModalPreset>;\n /**\n * Time the manager waits for UI teardown confirmation (`confirmClosed`)\n * before auto-finalizing a `closing` instance. `0` = manual only.\n * @default 300\n */\n readonly closingTimeout?: number;\n /**\n * Bounded transition log for debugging (ring buffer, no payloads).\n * Off by default to stay production-light.\n * @default false\n */\n readonly diagnostics?: boolean;\n}\n\n/**\n * Internal configuration token. Consumers must use `provideRiviniaModal()`.\n * @internal\n */\nexport const RIVINIA_MODAL_CONFIG = new InjectionToken<RiviniaModalConfig>(\n 'RIVINIA_MODAL_CONFIG',\n);\n\nexport const DEFAULT_MAX_VISIBLE = 5;\nexport const DEFAULT_CLOSING_TIMEOUT = 300;\nexport const DIAGNOSTIC_CAP = 100;\n","import { DestroyRef, TemplateRef, computed, inject, Service, signal } from '@angular/core';\nimport type { Type } from '@angular/core';\nimport {\n DEFAULT_CLOSING_TIMEOUT,\n DEFAULT_MAX_VISIBLE,\n DIAGNOSTIC_CAP,\n RIVINIA_MODAL_CONFIG,\n type RiviniaModalConfig,\n} from '../models/modal-config';\nimport type {\n ModalContent,\n ModalDiagnostic,\n ModalDismissOrigin,\n ModalDismissPolicy,\n ModalGuard,\n ModalHandle,\n ModalInstance,\n ModalOpenOptions,\n ModalResult,\n ModalTemplateContext,\n ModalUpdate,\n} from '../models/modal-types';\nimport { QueueStrategy, StackStrategy, type ModalStrategy } from './modal-strategy';\n\ninterface ModalRecord extends ModalInstance<unknown> {\n openedSeq: number;\n generation: number;\n guards: readonly ModalGuard[];\n closingTimer: ReturnType<typeof setTimeout> | null;\n ownerCleanup: (() => void) | null;\n}\n\ninterface PendingResult {\n promise: Promise<ModalResult<unknown>>;\n resolve: (value: ModalResult<unknown>) => void;\n settled: boolean;\n}\n\n/**\n * Public snapshot of an internal record. Strips lifecycle internals (`guards`,\n * `closingTimer`, `ownerCleanup`, `generation`, `openedSeq`) so consumers can\n * never observe or mutate manager state. Frozen: UI must use `update()`.\n */\nfunction toPublicInstance(record: ModalRecord): ModalInstance {\n return Object.freeze({\n id: record.id,\n ref: record.ref,\n caller: record.caller,\n parentId: record.parentId,\n data: record.data,\n content: record.content,\n status: record.status,\n dismiss: Object.freeze({ ...record.dismiss }),\n metadata: Object.freeze({ ...record.metadata }),\n }) as ModalInstance;\n}\n\n/**\n * Headless modal manager. Owns identity, lifecycle coordination, result\n * settlement and diagnostics. Ordering/admission policy is delegated to a\n * `ModalStrategy` (Stack/Queue/custom) — the manager never branches on mode.\n *\n * Settlement contract (each path settles exactly once, never hangs):\n * - `close()` on active → `confirmed` · `dismiss()` on active → `dismissed`\n * - `cancel()` on active → `cancelled` · on queued → `queued-cancelled`\n * - `dismiss()` on queued → `dismissed` with the reason (pre-activation)\n * - overflow/guard refusal → `rejected` · owner gone → `owner-destroyed`\n * - forced paths (owner, overflow eviction, `dismissAll`) bypass guards\n * - `close/dismiss/cancel` are guard-free primitives; `requestDismiss` runs\n * guards for user intents (escape/backdrop/button) or explicit review\n * - infrastructure failures reject the promise — never a result variant\n *\n * Results settle at dismissal initiation, not after UI teardown: animations\n * never delay callers. The `closing` → `closed` handshake (`confirmClosed`\n * or `closingTimeout` auto-finalize) exists only for exit animations.\n */\n@Service()\nexport class RiviniaModal {\n private readonly config = inject<RiviniaModalConfig>(RIVINIA_MODAL_CONFIG, {\n optional: true,\n });\n\n private readonly _instances = signal<readonly ModalRecord[]>([]);\n private readonly _results = new Map<string, PendingResult>();\n /** In-flight guarded dismissals: concurrent intents share one guard run. */\n private readonly _dismissInflight = new Map<string, Promise<boolean>>();\n private _strategy: ModalStrategy = this.resolveStrategy(this.config?.strategy);\n private _seq = 0;\n private _diagSeq = 0;\n private readonly _diagnostics: ModalDiagnostic[] = [];\n\n /** Active instances in strategy display order (bottom-first). */\n readonly active = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'active'))\n .map(toPublicInstance),\n );\n /** Queued instances, FIFO admission order. */\n readonly queued = computed<readonly ModalInstance[]>(() =>\n this._instances()\n .filter((i) => i.status === 'queued')\n .map(toPublicInstance),\n );\n /** Closing instances (exit animations), in strategy display order. */\n readonly closing = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'closing'))\n .map(toPublicInstance),\n );\n /** Everything rendered (active + closing), in strategy display order. */\n readonly visible = computed<readonly ModalInstance[]>(() =>\n this._strategy\n .order(this._instances().filter((i) => i.status === 'active' || i.status === 'closing'))\n .map(toPublicInstance),\n );\n /** Topmost active instance (the only Escape-eligible one), if any. */\n readonly top = computed<ModalInstance | null>(() => {\n const list = this.active();\n return list.length > 0 ? list[list.length - 1] : null;\n });\n\n constructor() {\n inject(DestroyRef).onDestroy(() => {\n for (const record of [...this._instances()]) {\n if (record.status === 'queued') {\n this.remove(record.id);\n this.settle(record.id, { status: 'queued-cancelled' });\n } else if (record.status !== 'closed') {\n this.clearClosingTimer(record.id);\n this.remove(record.id);\n this.settle(record.id, { status: 'dismissed', reason: 'manager-destroyed' });\n }\n }\n });\n }\n\n openModal<TData = unknown, TResult = unknown>(\n template: TemplateRef<ModalTemplateContext<TData>>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TComponent, TData = unknown, TResult = unknown>(\n component: Type<TComponent>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TData = unknown, TResult = unknown>(\n content: ModalContent<TData>,\n options?: ModalOpenOptions<TData>,\n ): ModalHandle<TResult>;\n openModal<TData = unknown, TResult = unknown>(\n content: ModalContent<TData> | TemplateRef<ModalTemplateContext<TData>> | Type<unknown>,\n options: ModalOpenOptions<TData> = {},\n ): ModalHandle<TResult> {\n const id = `modal-${++this._seq}`;\n const pending = this.deferred();\n // Type hole (internal, by design): the deferred promise is created as\n // `ModalResult<unknown>` and cast to `ModalResult<TResult>`. A `TResult`\n // mismatch is never validated at runtime — `close()` settles whatever it\n // receives. The handle's `close(result?: TResult)` signature IS checked\n // (see type tests); this cast only forwards that contract.\n this._results.set(id, pending);\n const handle: ModalHandle<TResult> = {\n id,\n result: pending.promise as Promise<ModalResult<TResult>>,\n close: (value?: TResult) => this.close(id, value),\n dismiss: (reason?: string) => this.dismiss(id, reason),\n cancel: (reason?: string) => this.cancel(id, reason),\n };\n\n const visibleCount = this._instances().filter((i) => i.status !== 'queued').length;\n const intent = this._strategy.admit(visibleCount, this.queuedCount(), this.maxVisible());\n let status: 'active' | 'queued' = intent === 'activate' ? 'active' : 'queued';\n if (intent === 'queue') {\n const outcome = this.admitQueued(id);\n if (outcome === 'active' || outcome === 'queued') {\n // 'dismiss-oldest' evicted room: the newcomer activates immediately.\n // 'queue' keeps it queued.\n status = outcome;\n } else {\n this.settle(id, { status: 'rejected', reason: outcome });\n this.diagnose('rejected', id, options.ref ?? null, outcome);\n return handle;\n }\n }\n\n const record: ModalRecord = {\n id,\n ref: options.ref ?? null,\n // A component type is stored by its name, so the UI can render it and\n // the DevTools inspector can show it.\n caller:\n typeof options.caller === 'string'\n ? options.caller\n : (options.caller?.name ?? null),\n parentId: this.knownParent(options.parentId) ? (options.parentId as string) : null,\n data: options.data as unknown,\n content: this.normalizeContent(content, options),\n status,\n // Precedence: built-in defaults < global config < named preset < per-call.\n // Layers merge field-by-field so an absent key never clobbers a default.\n dismiss: this.resolveDismiss(options.dismiss, options.preset),\n /*\n * `size` and `coversOthers` are options, but the UI reads them from the\n * instance — so they are recorded in the metadata, next to the title.\n * A caller who set the same key in `metadata` wins: an explicit\n * `metadata` entry is more specific than the option.\n */\n metadata: {\n ...(options.size !== undefined ? { size: options.size } : {}),\n ...(options.coversOthers !== undefined ? { coversOthers: options.coversOthers } : {}),\n ...(options.metadata ?? {}),\n },\n openedSeq: this._seq,\n generation: 0,\n guards: [...(options.guards ?? [])],\n closingTimer: null,\n ownerCleanup: null,\n };\n if (options.parentId !== undefined && record.parentId === null) {\n this.diagnose('rejected', id, record.ref, 'stale-parent');\n }\n this._instances.update((list) => [...list, record]);\n\n if (options.owner) {\n let ownerCleanup: (() => void) | null = null;\n try {\n ownerCleanup = options.owner.onDestroy(() => this.teardownOwned(id));\n } catch {\n // Owner already destroyed (NG0205): settle immediately and never add.\n this._instances.update((list) => list.filter((i) => i.id !== id));\n this.settle(id, { status: 'owner-destroyed' });\n this.diagnose('rejected', id, record.ref, 'owner-destroyed');\n return handle;\n }\n this.patch(id, { ownerCleanup });\n }\n this.diagnose(intent === 'activate' ? 'activated' : 'queued', id, record.ref);\n return handle;\n }\n\n /**\n * Confirms with a value. Active instances only; settles `confirmed`.\n * Blocked (returns `false`) by the prevent-parent-close policy.\n */\n close(id: string, value?: unknown): boolean {\n const record = this.get(id);\n if (!record || record.status !== 'active' || this.blockedByDescendants(id)) return false;\n this.beginClosing(id, 'confirmed', undefined, value);\n return true;\n }\n\n /**\n * Guard-free dismissal primitive. Active → `dismissed`; queued → removed as\n * `dismissed` with the reason. Blocked by prevent-parent-close.\n */\n dismiss(id: string, reason?: string): boolean {\n const record = this.get(id);\n if (!record || this.blockedByDescendants(id)) return false;\n if (record.status === 'active') {\n this.beginClosing(id, 'dismissed', reason);\n return true;\n }\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'dismissed', reason });\n this.diagnose('dismissed', id, record.ref, reason);\n return true;\n }\n return false;\n }\n\n /**\n * Cancels: active → `cancelled`, queued → removed as `queued-cancelled`.\n * Blocked by prevent-parent-close. Closing/closed/unknown → `false`.\n */\n cancel(id: string, reason?: string): boolean {\n const record = this.get(id);\n if (!record || this.blockedByDescendants(id)) return false;\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', id, record.ref, reason ?? 'cancelled');\n return true;\n }\n if (record.status !== 'active') return false;\n this.beginClosing(id, 'cancelled', reason);\n return true;\n }\n\n /**\n * Guarded dismissal for user intents (escape/backdrop/button) or explicit\n * programmatic review. Guards run sequentially; any `false` or throw denies\n * (stays open + diagnostic). Stale guard completions are ignored.\n */\n async requestDismiss(id: string, origin: ModalDismissOrigin, reason?: string): Promise<boolean> {\n const record = this.get(id);\n if (!record || record.status !== 'active' || this.blockedByDescendants(id)) return false;\n const inflight = this._dismissInflight.get(id);\n if (inflight) return inflight;\n const run = this.runGuards(id, origin, reason);\n this._dismissInflight.set(id, run);\n try {\n return await run;\n } finally {\n if (this._dismissInflight.get(id) === run) this._dismissInflight.delete(id);\n }\n }\n\n private async runGuards(\n id: string,\n origin: ModalDismissOrigin,\n reason?: string,\n ): Promise<boolean> {\n const record = this.get(id);\n if (!record) return false;\n const generation = record.generation;\n for (const guard of record.guards) {\n let allowed: boolean;\n try {\n allowed = await guard({ id, origin });\n } catch {\n allowed = false;\n }\n const current = this.get(id);\n if (!current || current.status !== 'active' || current.generation !== generation) {\n return false; // Resolved stale: something else already moved it.\n }\n if (!allowed) {\n this.diagnose('guard-denied', id, record.ref, origin);\n return false;\n }\n }\n return this.dismiss(id, reason ?? origin);\n }\n\n /** Merges data/metadata (new object identity so UI re-renders). */\n update<TData>(id: string, changes: import('../models/modal-types').ModalUpdate<TData>): boolean {\n const record = this.get(id);\n if (!record || (record.status !== 'active' && record.status !== 'queued')) return false;\n this._instances.update((list) =>\n list.map((t) =>\n t.id === id\n ? {\n ...t,\n data: (changes.data !== undefined ? changes.data : t.data) as unknown,\n metadata: changes.metadata !== undefined ? { ...changes.metadata } : t.metadata,\n }\n : t,\n ),\n );\n return true;\n }\n\n /** UI teardown confirmation after exit animations. Auto-finalizes via timeout. */\n confirmClosed(id: string): boolean {\n const record = this.get(id);\n if (!record || record.status !== 'closing') return false;\n this.finalize(id);\n return true;\n }\n\n /** Forced cleanup (bypasses guards): active → `dismissed`, queued → `queued-cancelled`. */\n dismissAll(reason = 'dismiss-all'): void {\n for (const record of [...this._instances()]) {\n if (record.status === 'queued') {\n this.remove(record.id);\n this.settle(record.id, { status: 'queued-cancelled' });\n } else if (record.status === 'active') {\n this.beginClosing(record.id, 'dismissed', reason);\n } else if (record.status === 'closing') {\n this.finalize(record.id);\n }\n }\n }\n\n /** Non-closed snapshots for a definition ref (e.g. avoid duplicate confirms). */\n findByRef(ref: string): ModalInstance[] {\n return this._instances()\n .filter((i) => i.ref === ref && i.status !== 'closed')\n .map(toPublicInstance);\n }\n\n /**\n * Swaps the strategy. Rejected with an explicit error while any instance is\n * not closed — silent live migration is never attempted.\n */\n useStrategy(strategy: ModalStrategy): void {\n if (this._instances().some((i) => i.status !== 'closed')) {\n throw new Error(\n '[rivinia/modal] useStrategy() rejected: active, queued or closing modals exist. ' +\n 'Close everything first — live migration is not supported.',\n );\n }\n this._strategy = strategy;\n }\n\n /** Introspection snapshot. Active means interactive; closing instances are\n * still rendered (exit animations) until `confirmClosed`/timeout finalizes. */\n debugState(): { strategy: string; active: string[]; closing: string[]; queued: string[] } {\n const all = this._instances();\n return {\n strategy: this._strategy.name,\n active: this.orderedActive()\n .filter((i) => i.status === 'active')\n .map((i) => i.id),\n closing: all.filter((i) => i.status === 'closing').map((i) => i.id),\n queued: all.filter((i) => i.status === 'queued').map((i) => i.id),\n };\n }\n\n /** Bounded transition log (only when `diagnostics: true`). */\n getDiagnostics(): readonly import('../models/modal-types').ModalDiagnostic[] {\n return [...this._diagnostics];\n }\n\n clearDiagnostics(): void {\n this._diagnostics.length = 0;\n }\n\n // ------------------------------------------------------------------ internals\n\n private get(id: string): ModalRecord | undefined {\n return this._instances().find((i) => i.id === id);\n }\n\n /** Accepts raw `TemplateRef`/`Type` or a full descriptor (README form). */\n private normalizeContent<TData>(\n content: ModalContent<TData> | TemplateRef<ModalTemplateContext<TData>> | Type<unknown>,\n options: ModalOpenOptions<TData>,\n ): ModalRecord['content'] {\n if (content instanceof TemplateRef) {\n return {\n kind: 'template',\n template: content as unknown as TemplateRef<ModalTemplateContext<unknown>>,\n };\n }\n if (typeof content === 'function') {\n return {\n kind: 'component',\n component: content as Type<unknown>,\n ...(options.inputs ? { inputs: { ...options.inputs } } : {}),\n };\n }\n return content as ModalRecord['content'];\n }\n\n private queuedCount(): number {\n return this._instances().filter((i) => i.status === 'queued').length;\n }\n\n private orderedActive(): ModalRecord[] {\n return [\n ...this._strategy.order(\n this._instances().filter((i) => i.status === 'active' || i.status === 'closing'),\n ),\n ];\n }\n\n private maxVisible(): number {\n return Math.max(1, Math.floor(this.config?.maxVisible ?? DEFAULT_MAX_VISIBLE));\n }\n\n private maxQueue(): number {\n const value = this.config?.maxQueue ?? Number.POSITIVE_INFINITY;\n return value < 0 ? 0 : value;\n }\n\n private presetDismiss(preset?: string): Record<string, unknown> {\n if (!preset) return {};\n return { ...(this.config?.presets?.[preset]?.dismiss ?? {}) };\n }\n\n private resolveDismiss(\n perCall?: Partial<ModalDismissPolicy>,\n preset?: string,\n ): ModalDismissPolicy {\n const merged: { closeOnEscape?: boolean; backdrop?: 'close' | 'static' | 'disabled' } = {\n closeOnEscape: true,\n backdrop: 'close',\n };\n const layers: Array<Partial<ModalDismissPolicy> | Record<string, unknown> | undefined> = [\n this.config?.dismiss,\n this.presetDismiss(preset),\n perCall,\n ];\n for (const layer of layers) {\n if (!layer) continue;\n const typed = layer as {\n closeOnEscape?: boolean;\n backdrop?: 'close' | 'static' | 'disabled';\n };\n if (typed.closeOnEscape !== undefined) merged.closeOnEscape = typed.closeOnEscape;\n if (typed.backdrop !== undefined) merged.backdrop = typed.backdrop;\n }\n return merged as import('../models/modal-types').ModalDismissPolicy;\n }\n\n private knownParent(parentId: string | undefined): parentId is string {\n if (parentId === undefined) return false;\n return this._instances().some((i) => i.id === parentId && i.status !== 'closed');\n }\n\n /** Overflow handling for queue-intent admissions. Returns rejection reason, or the resulting status. */\n private admitQueued(id: string): string | 'active' | 'queued' {\n switch (this.config?.overflow ?? 'queue') {\n case 'reject':\n return 'overflow';\n case 'dismiss-oldest': {\n // Preempt: evict oldest pure-active instance (system path, no guards).\n const oldest = this.orderedActive().find((i) => i.status === 'active');\n if (oldest) this.beginClosing(oldest.id, 'dismissed', 'overflow-evicted');\n return 'active';\n }\n case 'queue':\n default:\n return this.queuedCount() >= this.maxQueue() ? 'queue-full' : 'queued';\n }\n }\n\n private blockedByDescendants(id: string): boolean {\n if ((this.config?.descendants ?? 'close-with-parent') !== 'prevent-parent-close') {\n return false;\n }\n const blocked = this._instances().some(\n (i) => i.parentId === id && i.status !== 'closed' && i.status !== 'closing',\n );\n if (blocked)\n this.diagnose('strategy-rejected', id, this.get(id)?.ref ?? null, 'prevent-parent-close');\n return blocked;\n }\n\n private beginClosing(\n id: string,\n status: 'confirmed' | 'dismissed' | 'cancelled',\n reason?: string,\n value?: unknown,\n ): void {\n const record = this.get(id);\n if (!record || record.status !== 'active') return;\n const generation = record.generation + 1;\n this.closeDescendants(id, false);\n this.patch(id, { status: 'closing', generation });\n if (status === 'confirmed') {\n this.settle(id, { status, value } as ModalResult<unknown>);\n } else {\n this.settle(id, { status, reason } as ModalResult<unknown>);\n }\n this.diagnose('closing', id, record.ref, reason);\n const timeout = Math.max(0, this.config?.closingTimeout ?? DEFAULT_CLOSING_TIMEOUT);\n if (timeout === 0) return; // Manual teardown: UI must call confirmClosed().\n const timer = setTimeout(() => {\n const current = this.get(id);\n if (current && current.status === 'closing' && current.generation === generation) {\n this.finalize(id);\n }\n }, timeout);\n this.patch(id, { closingTimer: timer });\n }\n\n private closeDescendants(parentId: string, force: boolean): void {\n if (!force && (this.config?.descendants ?? 'close-with-parent') !== 'close-with-parent') {\n return;\n }\n for (const child of this._instances().filter(\n (i) => i.parentId === parentId && (i.status === 'active' || i.status === 'queued'),\n )) {\n if (child.status === 'queued') {\n this.remove(child.id);\n this.settle(child.id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', child.id, child.ref, 'parent-closed');\n } else {\n this.closeDescendants(child.id, force);\n this.beginClosing(child.id, 'dismissed', 'parent-closed');\n }\n }\n }\n\n private finalize(id: string, promoteAfter = true): void {\n const record = this.get(id);\n if (!record) return;\n this.clearClosingTimer(id);\n const cleanup = record.ownerCleanup;\n const ref = record.ref;\n this.patch(id, { status: 'closed', closingTimer: null });\n cleanup?.();\n this._instances.update((list) => list.filter((i) => i.id !== id));\n this._results.delete(id);\n this.diagnose('closed', id, ref);\n if (promoteAfter) this.promote();\n }\n\n private promote(): void {\n const width = this._strategy.maxActive(this.maxVisible());\n const visible = (): number =>\n this._instances().filter((i) => i.status === 'active' || i.status === 'closing').length;\n while (visible() < width) {\n const head = this._instances().find((i) => i.status === 'queued');\n if (!head) break;\n this.patch(head.id, { status: 'active' });\n this.diagnose('promoted', head.id, head.ref);\n }\n }\n\n private remove(id: string): void {\n this.clearClosingTimer(id);\n this._instances.update((list) => list.filter((i) => i.id !== id));\n }\n\n private clearClosingTimer(id: string): void {\n const record = this.get(id);\n if (record?.closingTimer) {\n clearTimeout(record.closingTimer);\n this.patch(id, { closingTimer: null });\n }\n }\n\n private teardownOwned(id: string): void {\n const record = this.get(id);\n if (!record || record.status === 'closed' || record.status === 'closing') return;\n if (record.status === 'queued') {\n this.closeDescendants(id, true);\n this.remove(id);\n this.settle(id, { status: 'queued-cancelled' });\n this.diagnose('dismissed', id, record.ref, 'owner-destroyed');\n return;\n }\n this.closeDescendants(id, true);\n this.patch(id, { status: 'closing', generation: record.generation + 1 });\n this.settle(id, { status: 'owner-destroyed' });\n this.diagnose('closing', id, record.ref, 'owner-destroyed');\n // Promote normally: freeing room must activate the queued head (whose own\n // owner is alive). A queued toast sharing the dead owner is torn down by\n // its own owner callback in the same pass, settling owner-destroyed.\n this.finalize(id);\n }\n\n private patch(id: string, changes: Partial<ModalRecord>): void {\n this._instances.update((list) => list.map((t) => (t.id === id ? { ...t, ...changes } : t)));\n }\n\n private deferred<TResult>(): PendingResult & { promise: Promise<ModalResult<TResult>> } {\n let resolve!: (value: ModalResult<unknown>) => void;\n const promise = new Promise<ModalResult<unknown>>((res) => {\n resolve = res;\n });\n return {\n promise: promise as Promise<ModalResult<TResult>>,\n resolve: (value) => {\n resolve(value);\n },\n settled: false,\n };\n }\n\n private settle(id: string, value: ModalResult<unknown>): void {\n const pending = this._results.get(id);\n if (!pending || pending.settled) return; // Exactly-once, even under reentrancy.\n pending.settled = true;\n // Delete on settle: entries must not outlive their promises. finalize()\n // keeps its own delete as a backstop for paths that bypass settle.\n this._results.delete(id);\n pending.resolve(value);\n }\n\n private diagnose(\n type: import('../models/modal-types').ModalDiagnostic['type'],\n id: string,\n ref: string | null,\n reason?: string,\n ): void {\n if (!this.config?.diagnostics) return;\n this._diagnostics.push({ seq: ++this._diagSeq, type, id, ref, reason });\n while (this._diagnostics.length > DIAGNOSTIC_CAP) this._diagnostics.shift();\n }\n\n private resolveStrategy(configured: RiviniaModalConfig['strategy']): ModalStrategy {\n if (!configured || configured === 'stack') return new StackStrategy();\n if (configured === 'queue') return new QueueStrategy();\n return configured;\n }\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders } from '@angular/core';\nimport { RIVINIA_MODAL_CONFIG, type RiviniaModalConfig } from '../models/modal-config';\nimport { RiviniaModal } from '../services/rivinia-modal';\n\n/**\n * Configures `@rivinia/modal`. The manager is a root singleton; call once at\n * bootstrap. Per-open options always win over these globals. No styling,\n * positions, or icons here — ever.\n *\n * @example\n * ```ts\n * provideRiviniaModal({ strategy: 'stack', maxVisible: 3 })\n * ```\n */\nexport function provideRiviniaModal(config: RiviniaModalConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: RIVINIA_MODAL_CONFIG, useValue: config }]);\n}\n\n/** Shorthand for `inject(RiviniaModal)`. */\nexport function injectRiviniaModal(): RiviniaModal {\n return inject(RiviniaModal);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivinia/modal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Headless modal manager for Angular: stack/queue strategies, typed results, guards, ownership.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -17,6 +17,18 @@
|
|
|
17
17
|
},
|
|
18
18
|
"sideEffects": false,
|
|
19
19
|
"schematics": "./schematics/collection.json",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/abdelfattahqandil21-oss/Revinia.git",
|
|
23
|
+
"directory": "projects/modal"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/abdelfattahqandil21-oss/Revinia/tree/modal/v1.0.0/projects/modal#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/abdelfattahqandil21-oss/Revinia/issues"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": "^22.22.3 || ^24.15.0 || >=26.0.0"
|
|
31
|
+
},
|
|
20
32
|
"module": "fesm2022/rivinia-modal.mjs",
|
|
21
33
|
"typings": "types/rivinia-modal.d.ts",
|
|
22
34
|
"exports": {
|
package/types/rivinia-modal.d.ts
CHANGED
|
@@ -270,14 +270,6 @@ interface RiviniaModalConfig {
|
|
|
270
270
|
*/
|
|
271
271
|
readonly diagnostics?: boolean;
|
|
272
272
|
}
|
|
273
|
-
/**
|
|
274
|
-
* Internal configuration token. Consumers must use `provideRiviniaModal()`.
|
|
275
|
-
* @internal
|
|
276
|
-
*/
|
|
277
|
-
declare const RIVINIA_MODAL_CONFIG: InjectionToken<RiviniaModalConfig>;
|
|
278
|
-
declare const DEFAULT_MAX_VISIBLE = 5;
|
|
279
|
-
declare const DEFAULT_CLOSING_TIMEOUT = 300;
|
|
280
|
-
declare const DIAGNOSTIC_CAP = 100;
|
|
281
273
|
/**
|
|
282
274
|
* Injection token providing the `ModalContext` of the enclosing modal to
|
|
283
275
|
* dynamically created components. Template content receives the same object
|