@rivinia/modal 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -9
- package/fesm2022/rivinia-modal-ui.mjs +1001 -0
- package/fesm2022/rivinia-modal-ui.mjs.map +1 -0
- package/fesm2022/rivinia-modal.mjs +44 -20
- package/fesm2022/rivinia-modal.mjs.map +1 -1
- package/package.json +5 -1
- package/types/rivinia-modal-ui.d.ts +332 -0
- package/types/rivinia-modal.d.ts +22 -1
|
@@ -0,0 +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-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 Directive,\n DOCUMENT,\n ElementRef,\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 standalone: true,\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.aria-label]=\"computedLabel()\"\n [attr.aria-labelledby]=\"title() && !ariaLabel() ? titleId : null\"\n [attr.aria-describedby]=\"description() ? descriptionId : null\"\n (cancel)=\"onCancel($event)\"\n (click)=\"onBackdropClick($event)\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (hasCustomHeader()) {\n <div class=\"rv-modal-header rv-modal-header-custom\">\n <ng-content select=\"[rv-modal-header]\"></ng-content>\n </div>\n } @else if (title() || showCloseButton()) {\n <div class=\"rv-modal-header\">\n <div class=\"rv-modal-heading\">\n @if (title()) {\n <h2 class=\"rv-modal-title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (description()) {\n <p class=\"rv-modal-description\" [id]=\"descriptionId\">{{ description() }}</p>\n }\n </div>\n @if (showCloseButton()) {\n <button\n type=\"button\"\n class=\"rv-modal-close\"\n (click)=\"onCloseButton()\"\n aria-label=\"Close dialog\"\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n }\n </div>\n }\n <div class=\"rv-modal-body\">\n <ng-content></ng-content>\n </div>\n @if (hasCustomFooter()) {\n <div class=\"rv-modal-footer\">\n <ng-content select=\"[rv-modal-footer]\"></ng-content>\n </div>\n }\n </dialog>\n `,\n styles: [\n `\n .rv-modal {\n color-scheme: light dark;\n --rv-modal-bg: #ffffff;\n --rv-modal-fg: #111111;\n --rv-modal-muted: #555555;\n --rv-modal-border: #e0e0e0;\n --rv-modal-radius: 12px;\n --rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, 0.28);\n --rv-modal-backdrop: rgba(0, 0, 0, 0.5);\n --rv-modal-pad: 20px;\n --rv-modal-gap: 12px;\n --rv-modal-width-sm: 24rem;\n --rv-modal-width-md: 32rem;\n --rv-modal-width-lg: 48rem;\n --rv-modal-width-xl: 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: 20px;\n --rv-modal-width-panel: 24rem;\n padding: 0;\n border: 1px solid var(--rv-modal-border);\n border-radius: var(--rv-modal-radius);\n background: var(--rv-modal-bg);\n color: var(--rv-modal-fg);\n box-shadow: var(--rv-modal-shadow);\n max-height: var(--rv-modal-max-height);\n width: var(--rv-modal-width-md);\n max-width: calc(100dvw - 2rem);\n }\n @media (prefers-color-scheme: dark) {\n .rv-modal {\n --rv-modal-bg: #161616;\n --rv-modal-fg: #f2f2f2;\n --rv-modal-muted: #a8a8a8;\n --rv-modal-border: #333333;\n --rv-modal-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);\n }\n }\n .rv-modal::backdrop {\n background: var(--rv-modal-backdrop);\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: 4px;\n }\n .rv-modal-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--rv-modal-muted);\n }\n .rv-modal-close {\n margin-inline-start: auto;\n flex: none;\n border: 1px solid var(--rv-modal-border);\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font-size: 1.25rem;\n line-height: 1;\n padding: 4px 10px;\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 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","import { Component, Injector, computed, input } 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';\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 standalone: true,\n imports: [RiviniaModalDialog, NgComponentOutlet, NgTemplateOutlet],\n template: `\n @for (instance of visible(); track instance.id) {\n <rv-modal-dialog\n [open]=\"true\"\n [managed]=\"true\"\n [closing]=\"instance.status === 'closing'\"\n [title]=\"titleOfInstance(instance)\"\n [size]=\"size()\"\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 (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})\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 protected readonly visible = computed(() => this.modal().visible());\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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"}
|
|
@@ -26,6 +26,19 @@ var QueueStrategy = class {
|
|
|
26
26
|
};
|
|
27
27
|
const RIVINIA_MODAL_CONTEXT = new InjectionToken("RIVINIA_MODAL_CONTEXT");
|
|
28
28
|
const RIVINIA_MODAL_CONFIG = new InjectionToken("RIVINIA_MODAL_CONFIG");
|
|
29
|
+
function toPublicInstance(record) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
id: record.id,
|
|
32
|
+
ref: record.ref,
|
|
33
|
+
caller: record.caller,
|
|
34
|
+
parentId: record.parentId,
|
|
35
|
+
data: record.data,
|
|
36
|
+
content: record.content,
|
|
37
|
+
status: record.status,
|
|
38
|
+
dismiss: Object.freeze({ ...record.dismiss }),
|
|
39
|
+
metadata: Object.freeze({ ...record.metadata })
|
|
40
|
+
});
|
|
41
|
+
}
|
|
29
42
|
var RiviniaModal = class RiviniaModal {
|
|
30
43
|
config = inject(RIVINIA_MODAL_CONFIG, { optional: true });
|
|
31
44
|
_instances = signal([], ...ngDevMode ? [{ debugName: "_instances" }] : /* istanbul ignore next */ []);
|
|
@@ -35,8 +48,10 @@ var RiviniaModal = class RiviniaModal {
|
|
|
35
48
|
_seq = 0;
|
|
36
49
|
_diagSeq = 0;
|
|
37
50
|
_diagnostics = [];
|
|
38
|
-
active = computed(() => this._strategy.order(this._instances().filter((i) => i.status === "active")), ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
|
|
39
|
-
queued = computed(() => this._instances().filter((i) => i.status === "queued"), ...ngDevMode ? [{ debugName: "queued" }] : /* istanbul ignore next */ []);
|
|
51
|
+
active = computed(() => this._strategy.order(this._instances().filter((i) => i.status === "active")).map(toPublicInstance), ...ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []);
|
|
52
|
+
queued = computed(() => this._instances().filter((i) => i.status === "queued").map(toPublicInstance), ...ngDevMode ? [{ debugName: "queued" }] : /* istanbul ignore next */ []);
|
|
53
|
+
closing = computed(() => this._strategy.order(this._instances().filter((i) => i.status === "closing")).map(toPublicInstance), ...ngDevMode ? [{ debugName: "closing" }] : /* istanbul ignore next */ []);
|
|
54
|
+
visible = computed(() => this._strategy.order(this._instances().filter((i) => i.status === "active" || i.status === "closing")).map(toPublicInstance), ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
|
|
40
55
|
top = computed(() => {
|
|
41
56
|
const list = this.active();
|
|
42
57
|
return list.length > 0 ? list[list.length - 1] : null;
|
|
@@ -47,7 +62,7 @@ var RiviniaModal = class RiviniaModal {
|
|
|
47
62
|
this.remove(record.id);
|
|
48
63
|
this.settle(record.id, { status: "queued-cancelled" });
|
|
49
64
|
} else if (record.status !== "closed") {
|
|
50
|
-
this.clearClosingTimer(record);
|
|
65
|
+
this.clearClosingTimer(record.id);
|
|
51
66
|
this.remove(record.id);
|
|
52
67
|
this.settle(record.id, {
|
|
53
68
|
status: "dismissed",
|
|
@@ -214,7 +229,7 @@ var RiviniaModal = class RiviniaModal {
|
|
|
214
229
|
else if (record.status === "closing") this.finalize(record.id);
|
|
215
230
|
}
|
|
216
231
|
findByRef(ref) {
|
|
217
|
-
return this._instances().filter((i) => i.ref === ref && i.status !== "closed");
|
|
232
|
+
return this._instances().filter((i) => i.ref === ref && i.status !== "closed").map(toPublicInstance);
|
|
218
233
|
}
|
|
219
234
|
useStrategy(strategy) {
|
|
220
235
|
if (this._instances().some((i) => i.status !== "closed")) throw new Error("[rivinia/modal] useStrategy() rejected: active, queued or closing modals exist. Close everything first — live migration is not supported.");
|
|
@@ -309,9 +324,12 @@ var RiviniaModal = class RiviniaModal {
|
|
|
309
324
|
beginClosing(id, status, reason, value) {
|
|
310
325
|
const record = this.get(id);
|
|
311
326
|
if (!record || record.status !== "active") return;
|
|
312
|
-
record.generation
|
|
327
|
+
const generation = record.generation + 1;
|
|
313
328
|
this.closeDescendants(id, false);
|
|
314
|
-
this.patch(id, {
|
|
329
|
+
this.patch(id, {
|
|
330
|
+
status: "closing",
|
|
331
|
+
generation
|
|
332
|
+
});
|
|
315
333
|
if (status === "confirmed") this.settle(id, {
|
|
316
334
|
status,
|
|
317
335
|
value
|
|
@@ -323,12 +341,11 @@ var RiviniaModal = class RiviniaModal {
|
|
|
323
341
|
this.diagnose("closing", id, record.ref, reason);
|
|
324
342
|
const timeout = Math.max(0, this.config?.closingTimeout ?? 300);
|
|
325
343
|
if (timeout === 0) return;
|
|
326
|
-
const
|
|
327
|
-
record.closingTimer = setTimeout(() => {
|
|
344
|
+
const timer = setTimeout(() => {
|
|
328
345
|
const current = this.get(id);
|
|
329
346
|
if (current && current.status === "closing" && current.generation === generation) this.finalize(id);
|
|
330
347
|
}, timeout);
|
|
331
|
-
this.patch(id, { closingTimer:
|
|
348
|
+
this.patch(id, { closingTimer: timer });
|
|
332
349
|
}
|
|
333
350
|
closeDescendants(parentId, force) {
|
|
334
351
|
if (!force && (this.config?.descendants ?? "close-with-parent") !== "close-with-parent") return;
|
|
@@ -344,12 +361,17 @@ var RiviniaModal = class RiviniaModal {
|
|
|
344
361
|
finalize(id, promoteAfter = true) {
|
|
345
362
|
const record = this.get(id);
|
|
346
363
|
if (!record) return;
|
|
347
|
-
this.clearClosingTimer(
|
|
348
|
-
|
|
349
|
-
record.
|
|
364
|
+
this.clearClosingTimer(id);
|
|
365
|
+
const cleanup = record.ownerCleanup;
|
|
366
|
+
const ref = record.ref;
|
|
367
|
+
this.patch(id, {
|
|
368
|
+
status: "closed",
|
|
369
|
+
closingTimer: null
|
|
370
|
+
});
|
|
371
|
+
cleanup?.();
|
|
350
372
|
this._instances.update((list) => list.filter((i) => i.id !== id));
|
|
351
373
|
this._results.delete(id);
|
|
352
|
-
this.diagnose("closed", id,
|
|
374
|
+
this.diagnose("closed", id, ref);
|
|
353
375
|
if (promoteAfter) this.promote();
|
|
354
376
|
}
|
|
355
377
|
promote() {
|
|
@@ -363,14 +385,14 @@ var RiviniaModal = class RiviniaModal {
|
|
|
363
385
|
}
|
|
364
386
|
}
|
|
365
387
|
remove(id) {
|
|
366
|
-
|
|
367
|
-
if (record) this.clearClosingTimer(record);
|
|
388
|
+
this.clearClosingTimer(id);
|
|
368
389
|
this._instances.update((list) => list.filter((i) => i.id !== id));
|
|
369
390
|
}
|
|
370
|
-
clearClosingTimer(
|
|
371
|
-
|
|
391
|
+
clearClosingTimer(id) {
|
|
392
|
+
const record = this.get(id);
|
|
393
|
+
if (record?.closingTimer) {
|
|
372
394
|
clearTimeout(record.closingTimer);
|
|
373
|
-
|
|
395
|
+
this.patch(id, { closingTimer: null });
|
|
374
396
|
}
|
|
375
397
|
}
|
|
376
398
|
teardownOwned(id) {
|
|
@@ -383,9 +405,11 @@ var RiviniaModal = class RiviniaModal {
|
|
|
383
405
|
this.diagnose("dismissed", id, record.ref, "owner-destroyed");
|
|
384
406
|
return;
|
|
385
407
|
}
|
|
386
|
-
record.generation++;
|
|
387
408
|
this.closeDescendants(id, true);
|
|
388
|
-
this.patch(id, {
|
|
409
|
+
this.patch(id, {
|
|
410
|
+
status: "closing",
|
|
411
|
+
generation: record.generation + 1
|
|
412
|
+
});
|
|
389
413
|
this.settle(id, { status: "owner-destroyed" });
|
|
390
414
|
this.diagnose("closing", id, record.ref, "owner-destroyed");
|
|
391
415
|
this.finalize(id);
|
|
@@ -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 * 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.order(this._instances().filter((i) => i.status === 'active')),\n );\n /** Queued instances, FIFO admission order. */\n readonly queued = computed<readonly ModalInstance[]>(() =>\n this._instances().filter((i) => i.status === 'queued'),\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);\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 this._results.set(id, pending); 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 caller: options.caller ?? 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 metadata: { ...(options.metadata ?? {}) },\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(\n id: string,\n origin: ModalDismissOrigin,\n reason?: string,\n ): 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().filter((i) => i.ref === ref && i.status !== 'closed');\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 { closeOnEscape?: boolean; backdrop?: 'close' | 'static' | 'disabled' };\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) 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 record.generation++;\n this.closeDescendants(id, false);\n this.patch(id, { status: 'closing' });\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 generation = record.generation;\n record.closingTimer = setTimeout(() => {\n const current = this.get(id);\n if (current && current.status === 'closing' && current.generation === generation) {\n this.finalize(id);\n }\n }, timeout);\n // Patch the timer into the stored record (same object identity held above).\n this.patch(id, { closingTimer: record.closingTimer });\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(record);\n this.patch(id, { status: 'closed' });\n record.ownerCleanup?.();\n this._instances.update((list) => list.filter((i) => i.id !== id));\n this._results.delete(id);\n this.diagnose('closed', id, record.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 const record = this.get(id);\n if (record) this.clearClosingTimer(record);\n this._instances.update((list) => list.filter((i) => i.id !== id));\n }\n\n private clearClosingTimer(record: ModalRecord): void {\n if (record.closingTimer) {\n clearTimeout(record.closingTimer);\n record.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 record.generation++;\n this.closeDescendants(id, true);\n this.patch(id, { status: 'closing' });\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/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 caller: options.caller ?? 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 metadata: { ...(options.metadata ?? {}) },\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rivinia/modal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Headless modal manager for Angular: stack/queue strategies, typed results, guards, ownership.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
".": {
|
|
25
25
|
"types": "./types/rivinia-modal.d.ts",
|
|
26
26
|
"default": "./fesm2022/rivinia-modal.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./ui": {
|
|
29
|
+
"types": "./types/rivinia-modal-ui.d.ts",
|
|
30
|
+
"default": "./fesm2022/rivinia-modal-ui.mjs"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"type": "module",
|