@rivinia/modal 0.6.1 → 1.0.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.
@@ -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\">&times;</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\">&times;</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.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.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.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.6.1",
3
+ "version": "1.0.1",
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": {
@@ -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