@rivinia/modal 0.5.3 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ policies, async guards, and optional ready-made UI. The core package owns
5
5
  modal state and lifecycle but does not render or style dialogs.
6
6
 
7
7
  - Install: `npm install @rivinia/modal`
8
- - Package version: **0.5.3**
8
+ - Package version: **0.6.0**
9
9
  - Peer dependencies: `@angular/core ^22.0.0` and `@angular/common ^22.0.0`
10
10
  - `@rivinia/modal` is the headless manager.
11
11
  - `@rivinia/modal/ui` contains standalone dialog and host components.
@@ -58,7 +58,6 @@ interface DeleteData {
58
58
  }
59
59
 
60
60
  @Component({
61
- standalone: true,
62
61
  imports: [RiviniaModalHost],
63
62
  templateUrl: './users-page.component.html',
64
63
  })
@@ -142,7 +141,6 @@ interface DeleteData {
142
141
  }
143
142
 
144
143
  @Component({
145
- standalone: true,
146
144
  imports: [RiviniaModalHost],
147
145
  templateUrl: './users-page.component.html',
148
146
  })
@@ -212,7 +210,6 @@ import { Component, inject } from '@angular/core';
212
210
  import { RIVINIA_MODAL_CONTEXT } from '@rivinia/modal';
213
211
 
214
212
  @Component({
215
- standalone: true,
216
213
  templateUrl: './delete-confirmation.component.html',
217
214
  })
218
215
  export class DeleteConfirmationComponent {
@@ -276,7 +273,6 @@ import { RiviniaToast } from '@rivinia/toast';
276
273
  import { RiviniaToastList } from '@rivinia/toast/ui';
277
274
 
278
275
  @Component({
279
- standalone: true,
280
276
  imports: [RiviniaModalHost, RiviniaToastList],
281
277
  templateUrl: './payment-page.component.html',
282
278
  })
@@ -399,7 +395,6 @@ import { Component } from '@angular/core';
399
395
  import { RiviniaModalDialog } from '@rivinia/modal/ui';
400
396
 
401
397
  @Component({
402
- standalone: true,
403
398
  imports: [RiviniaModalDialog],
404
399
  templateUrl: './preview-dialog.component.html',
405
400
  })
@@ -440,6 +435,135 @@ closed and opens on the client after hydration. Theme it with CSS custom
440
435
  properties such as `--rv-modal-bg`, `--rv-modal-fg`, `--rv-modal-border`,
441
436
  `--rv-modal-radius`, and `--rv-modal-width-md`.
442
437
 
438
+ ## Theming
439
+
440
+ Every component reads **its own token → the shared semantic layer → its
441
+ built-in default**:
442
+
443
+ ```css
444
+ --rv-modal-bg → --rv-bg → #ffffff
445
+ ```
446
+
447
+ | Level | Token | Scope |
448
+ | -------------- | --------------- | ----------------------------------- |
449
+ | 1. per package | `--rv-modal-bg` | that dialog only |
450
+ | 2. global | `--rv-bg` | every Rivinia component, everywhere |
451
+ | 3. default | — | what you get if you set nothing |
452
+
453
+ ```css
454
+ @import '@rivinia/theme';
455
+
456
+ :root {
457
+ --rv-bg: #ffffff;
458
+ --rv-fg: #111111;
459
+ --rv-border: #e0e0e0;
460
+ }
461
+ ```
462
+
463
+ Its defaults sit in `@layer rivinia-theme`, so your unlayered `:root` rules win
464
+ wherever they appear. **Map it on `:root`, never on a component element** — an
465
+ element-level declaration shadows the root value and the override silently
466
+ stops working.
467
+
468
+ ### The library's own tokens
469
+
470
+ | Token | Default | Applies to |
471
+ | --------------------- | ----------------------------- | -------------- |
472
+ | `--rv-modal-bg` | `#ffffff` | the dialog |
473
+ | `--rv-modal-fg` | `#111111` | the dialog |
474
+ | `--rv-modal-muted` | `#555555` | secondary text |
475
+ | `--rv-modal-border` | `#e0e0e0` | the dialog |
476
+ | `--rv-modal-radius` | `12px` | the dialog |
477
+ | `--rv-modal-shadow` | `0 24px 64px rgba(0,0,0,.28)` | the dialog |
478
+ | `--rv-modal-backdrop` | `rgba(0,0,0,.5)` | the scrim |
479
+ | `--rv-modal-pad` | `20px` | the dialog |
480
+ | `--rv-modal-width-md` | `32rem` | the dialog |
481
+
482
+ **No forced dark mode** — there is no `prefers-color-scheme` block in this
483
+ package, so a dialog never overrides your explicit choice.
484
+
485
+ ### Which styling stack?
486
+
487
+ | Stack | Import |
488
+ | --------------- | -------------------------------------------- |
489
+ | **Plain CSS** | `@import '@rivinia/theme';` |
490
+ | **Tailwind v4** | `@import '@rivinia/theme/tailwind.css';` |
491
+ | **Bootstrap** | `@import '@rivinia/theme/bootstrap.css';` |
492
+ | **Sass / SCSS** | `@use '@rivinia/theme/scss';` |
493
+ | **Less** | `@import (less) '@rivinia/theme/theme.css';` |
494
+
495
+ ## Sizing, stacking and who opened it
496
+
497
+ **A size per dialog.** The host's `size` is the default for every instance; a
498
+ dialog may override it when it is opened:
499
+
500
+ ```ts
501
+ this.modal.openModal(this.confirmTpl, { ref: 'confirm', size: 'sm' });
502
+ ```
503
+
504
+ ```html
505
+ <rv-modal-host [modal]="modal" size="lg" />
506
+ ```
507
+
508
+ The presets are **responsive** — each is a `clamp()`, so it scales with the
509
+ viewport between a floor and a ceiling instead of being a fixed width that
510
+ either overflows or gets capped:
511
+
512
+ | Preset | Width |
513
+ | ------ | --------------------------- |
514
+ | `sm` | `clamp(16rem, 40vw, 24rem)` |
515
+ | `md` | `clamp(20rem, 55vw, 32rem)` |
516
+ | `lg` | `clamp(24rem, 70vw, 48rem)` |
517
+ | `xl` | `clamp(28rem, 85vw, 64rem)` |
518
+ | `full` | `calc(100dvw - 2rem)` |
519
+ | `auto` | sized to its content |
520
+
521
+ Every one is a plain custom property, so the consumer overrides the preset in
522
+ CSS without touching the library:
523
+
524
+ ```css
525
+ my-scope {
526
+ --rv-modal-width-lg: clamp(30rem, 80vw, 60rem);
527
+ }
528
+ ```
529
+
530
+ **One dialog can cover the ones below it.** A stack shows every dialog, so the
531
+ ones below stay visible behind the top one — right for a wizard, wrong for a
532
+ flow where the detail view REPLACES the list view:
533
+
534
+ ```ts
535
+ this.modal.openModal(this.detailTpl, { ref: 'detail', coversOthers: true });
536
+ ```
537
+
538
+ The dialogs below are not rendered while the cover is up, and they come back
539
+ when it closes. They stay in the manager the whole time.
540
+
541
+ **The opener is attributable.** `openModal` is normally called from an event
542
+ handler, which is not an injection context, so the manager cannot discover the
543
+ caller. Naming it puts `data-caller` on the dialog, which the DevTools
544
+ inspector shows — so a dialog that appears unexpectedly points at its source:
545
+
546
+ ```ts
547
+ export class UsersScreen {
548
+ open() {
549
+ this.modal.openModal(this.detailTpl, { caller: UsersScreen });
550
+ }
551
+ }
552
+ ```
553
+
554
+ ```html
555
+ <dialog class="rv-modal" data-caller="UsersScreen">…</dialog>
556
+ ```
557
+
558
+ **Smoothness.** The dialog transitions its own box — width, height and the
559
+ offsets — so a size change or a dialog opening on top of another is a
560
+ movement rather than a jump. The stack change is additionally wrapped in a
561
+ **view transition** where the browser supports it, so the lower dialog is
562
+ painted continuously instead of flickering. During a viewport resize the
563
+ transition is switched off (`data-resizing="true"`): the dialog's size is
564
+ derived from the viewport, so animating towards a moving target lags behind
565
+ the pointer.
566
+
443
567
  ## Public core exports
444
568
 
445
569
  The core entry point exports:
@@ -135,6 +135,32 @@ var RiviniaModalDialog = class RiviniaModalDialog {
135
135
  animation = input("scale", ...ngDevMode ? [{ debugName: "animation" }] : /* istanbul ignore next */ []);
136
136
  closeOnEscape = input(true, ...ngDevMode ? [{ debugName: "closeOnEscape" }] : /* istanbul ignore next */ []);
137
137
  backdrop = input("close", ...ngDevMode ? [{ debugName: "backdrop" }] : /* istanbul ignore next */ []);
138
+ resizing = signal(false, ...ngDevMode ? [{ debugName: "resizing" }] : /* istanbul ignore next */ []);
139
+ viewportQuery = signal(null, ...ngDevMode ? [{ debugName: "viewportQuery" }] : /* istanbul ignore next */ []);
140
+ stopResizeWatch = (() => {
141
+ if (typeof window === "undefined") return () => void 0;
142
+ let query = null;
143
+ const onChange = () => {
144
+ this.resizing.set(true);
145
+ if (this.settleTimer !== null) clearTimeout(this.settleTimer);
146
+ this.settleTimer = setTimeout(() => this.resizing.set(false), 120);
147
+ };
148
+ const attach = () => {
149
+ query?.removeEventListener("change", onChange);
150
+ if (typeof window.matchMedia !== "function") return;
151
+ const ratio = window.devicePixelRatio ?? 1;
152
+ query = window.matchMedia(`(resolution: ${ratio}dppx)`);
153
+ query.addEventListener("change", onChange, { once: true });
154
+ this.viewportQuery.set(query);
155
+ };
156
+ attach();
157
+ return () => query?.removeEventListener("change", onChange);
158
+ })();
159
+ settleTimer = null;
160
+ ngOnDestroy() {
161
+ this.stopResizeWatch();
162
+ if (this.settleTimer !== null) clearTimeout(this.settleTimer);
163
+ }
138
164
  showCloseButton = input(true, ...ngDevMode ? [{ debugName: "showCloseButton" }] : /* istanbul ignore next */ []);
139
165
  managed = input(false, ...ngDevMode ? [{ debugName: "managed" }] : /* istanbul ignore next */ []);
140
166
  closing = input(false, ...ngDevMode ? [{ debugName: "closing" }] : /* istanbul ignore next */ []);
@@ -461,6 +487,7 @@ var RiviniaModalDialog = class RiviniaModalDialog {
461
487
  [attr.data-position]="position()"
462
488
  [attr.data-animation]="animation()"
463
489
  [attr.data-state]="state()"
490
+ [attr.data-resizing]="resizing()"
464
491
  [attr.aria-label]="computedLabel()"
465
492
  [attr.aria-labelledby]="title() && !ariaLabel() ? titleId : null"
466
493
  [attr.aria-describedby]="description() ? descriptionId : null"
@@ -505,7 +532,7 @@ var RiviniaModalDialog = class RiviniaModalDialog {
505
532
  </dialog>
506
533
  `,
507
534
  isInline: true,
508
- styles: [".rv-modal{color-scheme:light dark;--rv-modal-bg: #ffffff;--rv-modal-fg: #111111;--rv-modal-muted: #555555;--rv-modal-border: #e0e0e0;--rv-modal-radius: 12px;--rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, .28);--rv-modal-backdrop: rgba(0, 0, 0, .5);--rv-modal-pad: 20px;--rv-modal-gap: 12px;--rv-modal-width-sm: 24rem;--rv-modal-width-md: 32rem;--rv-modal-width-lg: 48rem;--rv-modal-width-xl: 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: 20px;--rv-modal-width-panel: 24rem;padding:0;border:1px solid var(--rv-modal-border);border-radius:var(--rv-modal-radius);background:var(--rv-modal-bg);color:var(--rv-modal-fg);box-shadow:var(--rv-modal-shadow);max-height:var(--rv-modal-max-height);width:var(--rv-modal-width-md);max-width:calc(100dvw - 2rem)}@media(prefers-color-scheme:dark){.rv-modal{--rv-modal-bg: #161616;--rv-modal-fg: #f2f2f2;--rv-modal-muted: #a8a8a8;--rv-modal-border: #333333;--rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, .6)}}.rv-modal::backdrop{background:var(--rv-modal-backdrop)}.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:4px}.rv-modal-description{margin:0;font-size:.875rem;color:var(--rv-modal-muted)}.rv-modal-close{margin-inline-start:auto;flex:none;border:1px solid var(--rv-modal-border);border-radius:8px;background:transparent;color:inherit;font-size:1.25rem;line-height:1;padding:4px 10px;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"],
535
+ 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"],
509
536
  encapsulation: i0.ViewEncapsulation.None
510
537
  });
511
538
  };
@@ -518,7 +545,6 @@ i0.ɵɵngDeclareClassMetadata({
518
545
  type: Component,
519
546
  args: [{
520
547
  selector: "rv-modal-dialog",
521
- standalone: true,
522
548
  encapsulation: ViewEncapsulation.None,
523
549
  template: `
524
550
  <dialog
@@ -529,6 +555,7 @@ i0.ɵɵngDeclareClassMetadata({
529
555
  [attr.data-position]="position()"
530
556
  [attr.data-animation]="animation()"
531
557
  [attr.data-state]="state()"
558
+ [attr.data-resizing]="resizing()"
532
559
  [attr.aria-label]="computedLabel()"
533
560
  [attr.aria-labelledby]="title() && !ariaLabel() ? titleId : null"
534
561
  [attr.aria-describedby]="description() ? descriptionId : null"
@@ -572,7 +599,7 @@ i0.ɵɵngDeclareClassMetadata({
572
599
  }
573
600
  </dialog>
574
601
  `,
575
- styles: [".rv-modal{color-scheme:light dark;--rv-modal-bg: #ffffff;--rv-modal-fg: #111111;--rv-modal-muted: #555555;--rv-modal-border: #e0e0e0;--rv-modal-radius: 12px;--rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, .28);--rv-modal-backdrop: rgba(0, 0, 0, .5);--rv-modal-pad: 20px;--rv-modal-gap: 12px;--rv-modal-width-sm: 24rem;--rv-modal-width-md: 32rem;--rv-modal-width-lg: 48rem;--rv-modal-width-xl: 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: 20px;--rv-modal-width-panel: 24rem;padding:0;border:1px solid var(--rv-modal-border);border-radius:var(--rv-modal-radius);background:var(--rv-modal-bg);color:var(--rv-modal-fg);box-shadow:var(--rv-modal-shadow);max-height:var(--rv-modal-max-height);width:var(--rv-modal-width-md);max-width:calc(100dvw - 2rem)}@media(prefers-color-scheme:dark){.rv-modal{--rv-modal-bg: #161616;--rv-modal-fg: #f2f2f2;--rv-modal-muted: #a8a8a8;--rv-modal-border: #333333;--rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, .6)}}.rv-modal::backdrop{background:var(--rv-modal-backdrop)}.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:4px}.rv-modal-description{margin:0;font-size:.875rem;color:var(--rv-modal-muted)}.rv-modal-close{margin-inline-start:auto;flex:none;border:1px solid var(--rv-modal-border);border-radius:8px;background:transparent;color:inherit;font-size:1.25rem;line-height:1;padding:4px 10px;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"]
602
+ 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"]
576
603
  }]
577
604
  }],
578
605
  ctorParameters: () => [],
@@ -732,12 +759,67 @@ function focusFirst(dialog, closeVisible) {
732
759
  dialog.focus({ preventScroll: true });
733
760
  } catch {}
734
761
  }
762
+ const MODAL_COVERS = "coversOthers";
763
+ function covers(instance) {
764
+ return instance.metadata?.[MODAL_COVERS] === true;
765
+ }
735
766
  var RiviniaModalHost = class RiviniaModalHost {
736
767
  modal = input.required(...ngDevMode ? [{ debugName: "modal" }] : /* istanbul ignore next */ []);
737
768
  size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
738
769
  showCloseButton = input(true, ...ngDevMode ? [{ debugName: "showCloseButton" }] : /* istanbul ignore next */ []);
739
770
  titleOf = input(...ngDevMode ? [void 0, { debugName: "titleOf" }] : /* istanbul ignore next */ []);
740
- visible = computed(() => this.modal().visible(), ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
771
+ loadingOf = input(...ngDevMode ? [void 0, { debugName: "loadingOf" }] : /* istanbul ignore next */ []);
772
+ loadingLabel = input("Loading…", ...ngDevMode ? [{ debugName: "loadingLabel" }] : /* istanbul ignore next */ []);
773
+ visible = signal([], ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
774
+ syncStack = effect(() => {
775
+ const next = this.modal().visible();
776
+ this.withTransition(() => this.visible.set(next));
777
+ }, ...ngDevMode ? [{ debugName: "syncStack" }] : /* istanbul ignore next */ []);
778
+ transition = (() => {
779
+ if (typeof document === "undefined") return false;
780
+ return typeof document.startViewTransition === "function";
781
+ })();
782
+ lastStackKey = "";
783
+ withTransition(change) {
784
+ const key = this.modal().visible().map((i) => `${i.id}:${i.status}`).join("|");
785
+ if (!this.transition || key === this.lastStackKey) {
786
+ change();
787
+ this.lastStackKey = key;
788
+ return;
789
+ }
790
+ const doc = document;
791
+ try {
792
+ doc.startViewTransition?.(() => {
793
+ change();
794
+ this.lastStackKey = key;
795
+ });
796
+ } catch {
797
+ change();
798
+ this.lastStackKey = key;
799
+ }
800
+ }
801
+ covering = computed(() => {
802
+ const list = this.visible();
803
+ let coverId = null;
804
+ for (const instance of list) if (covers(instance)) coverId = instance.id;
805
+ return coverId;
806
+ }, ...ngDevMode ? [{ debugName: "covering" }] : /* istanbul ignore next */ []);
807
+ isLoading(instance) {
808
+ try {
809
+ return this.loadingOf()?.(instance) === true;
810
+ } catch {
811
+ return false;
812
+ }
813
+ }
814
+ callerName(instance) {
815
+ return instance.caller ?? null;
816
+ }
817
+ sizeOf(instance) {
818
+ return instance.metadata?.["size"] ?? void 0 ?? this.size();
819
+ }
820
+ isCovered(instance) {
821
+ return this.visible().some((other) => other.id !== instance.id && covers(other));
822
+ }
741
823
  titleOfInstance(instance) {
742
824
  try {
743
825
  const custom = this.titleOf()?.(instance);
@@ -827,24 +909,55 @@ var RiviniaModalHost = class RiviniaModalHost {
827
909
  isSignal: true,
828
910
  isRequired: false,
829
911
  transformFunction: null
912
+ },
913
+ loadingOf: {
914
+ classPropertyName: "loadingOf",
915
+ publicName: "loadingOf",
916
+ isSignal: true,
917
+ isRequired: false,
918
+ transformFunction: null
919
+ },
920
+ loadingLabel: {
921
+ classPropertyName: "loadingLabel",
922
+ publicName: "loadingLabel",
923
+ isSignal: true,
924
+ isRequired: false,
925
+ transformFunction: null
830
926
  }
831
927
  },
832
928
  ngImport: i0,
833
929
  template: `
834
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 {
835
938
  <rv-modal-dialog
836
939
  [open]="true"
837
940
  [managed]="true"
838
941
  [closing]="instance.status === 'closing'"
839
942
  [title]="titleOfInstance(instance)"
840
- [size]="size()"
943
+ [size]="sizeOf(instance)"
944
+ [attr.data-caller]="callerName(instance)"
841
945
  [showCloseButton]="showCloseButton()"
842
946
  [closeOnEscape]="instance.dismiss.closeOnEscape ?? true"
843
947
  [backdrop]="instance.dismiss.backdrop ?? 'close'"
844
948
  (dismissRequested)="onIntent(instance.id, $event)"
845
949
  (exitDone)="onExitDone(instance.id)"
846
950
  >
847
- @if (instance.content.kind === 'template') {
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') {
848
961
  <ng-container
849
962
  *ngTemplateOutlet="instance.content.template; context: templateContext(instance)"
850
963
  ></ng-container>
@@ -858,6 +971,7 @@ var RiviniaModalHost = class RiviniaModalHost {
858
971
  ></ng-container>
859
972
  }
860
973
  </rv-modal-dialog>
974
+ }
861
975
  }
862
976
  `,
863
977
  isInline: true,
@@ -925,7 +1039,6 @@ i0.ɵɵngDeclareClassMetadata({
925
1039
  type: Component,
926
1040
  args: [{
927
1041
  selector: "rv-modal-host",
928
- standalone: true,
929
1042
  imports: [
930
1043
  RiviniaModalDialog,
931
1044
  NgComponentOutlet,
@@ -933,19 +1046,36 @@ i0.ɵɵngDeclareClassMetadata({
933
1046
  ],
934
1047
  template: `
935
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 {
936
1056
  <rv-modal-dialog
937
1057
  [open]="true"
938
1058
  [managed]="true"
939
1059
  [closing]="instance.status === 'closing'"
940
1060
  [title]="titleOfInstance(instance)"
941
- [size]="size()"
1061
+ [size]="sizeOf(instance)"
1062
+ [attr.data-caller]="callerName(instance)"
942
1063
  [showCloseButton]="showCloseButton()"
943
1064
  [closeOnEscape]="instance.dismiss.closeOnEscape ?? true"
944
1065
  [backdrop]="instance.dismiss.backdrop ?? 'close'"
945
1066
  (dismissRequested)="onIntent(instance.id, $event)"
946
1067
  (exitDone)="onExitDone(instance.id)"
947
1068
  >
948
- @if (instance.content.kind === 'template') {
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') {
949
1079
  <ng-container
950
1080
  *ngTemplateOutlet="instance.content.template; context: templateContext(instance)"
951
1081
  ></ng-container>
@@ -959,6 +1089,7 @@ i0.ɵɵngDeclareClassMetadata({
959
1089
  ></ng-container>
960
1090
  }
961
1091
  </rv-modal-dialog>
1092
+ }
962
1093
  }
963
1094
  `
964
1095
  }]
@@ -995,6 +1126,22 @@ i0.ɵɵngDeclareClassMetadata({
995
1126
  alias: "titleOf",
996
1127
  required: false
997
1128
  }]
1129
+ }],
1130
+ loadingOf: [{
1131
+ type: i0.Input,
1132
+ args: [{
1133
+ isSignal: true,
1134
+ alias: "loadingOf",
1135
+ required: false
1136
+ }]
1137
+ }],
1138
+ loadingLabel: [{
1139
+ type: i0.Input,
1140
+ args: [{
1141
+ isSignal: true,
1142
+ alias: "loadingLabel",
1143
+ required: false
1144
+ }]
998
1145
  }]
999
1146
  }
1000
1147
  });