@radix-ng/primitives 1.0.8 → 1.0.10

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.
Files changed (39) hide show
  1. package/fesm2022/radix-ng-primitives-autocomplete.mjs +11 -3
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-combobox.mjs +11 -3
  4. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-core.mjs +9 -6
  6. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-date-field.mjs +18 -8
  8. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +9 -0
  10. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -1
  11. package/fesm2022/radix-ng-primitives-focus-scope.mjs +241 -95
  12. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-menu.mjs +97 -9
  14. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +17 -3
  16. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-popper.mjs +148 -23
  18. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-portal.mjs +16 -5
  20. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-presence.mjs +4 -2
  22. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-select.mjs +14 -6
  24. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-time-field.mjs +12 -4
  26. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  27. package/package.json +1 -5
  28. package/types/radix-ng-primitives-core.d.ts +15 -12
  29. package/types/radix-ng-primitives-date-field.d.ts +14 -4
  30. package/types/radix-ng-primitives-floating-focus-manager.d.ts +7 -0
  31. package/types/radix-ng-primitives-focus-scope.d.ts +61 -44
  32. package/types/radix-ng-primitives-menu.d.ts +14 -3
  33. package/types/radix-ng-primitives-popper.d.ts +136 -43
  34. package/types/radix-ng-primitives-portal.d.ts +18 -8
  35. package/types/radix-ng-primitives-time-field.d.ts +12 -4
  36. package/fesm2022/radix-ng-primitives-focus-guards.mjs +0 -53
  37. package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +0 -1
  38. package/focus-guards/README.md +0 -1
  39. package/types/radix-ng-primitives-focus-guards.d.ts +0 -15
@@ -1,7 +1,7 @@
1
1
  import { DOCUMENT, isPlatformBrowser } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { ElementRef, inject, PLATFORM_ID, DestroyRef, input, linkedSignal, computed, effect, Directive, ViewContainerRef, TemplateRef, signal, Injector } from '@angular/core';
4
- import { PresenceMachine, RDX_PRESENCE_CONTEXT } from '@radix-ng/primitives/presence';
3
+ import { ElementRef, inject, PLATFORM_ID, DestroyRef, input, linkedSignal, computed, effect, Directive, ViewContainerRef, TemplateRef, booleanAttribute, signal, Injector } from '@angular/core';
4
+ import { RDX_PRESENCE_CONTEXT, PresenceMachine } from '@radix-ng/primitives/presence';
5
5
 
6
6
  /**
7
7
  * Resolve a {@link RdxPortalContainer} to a concrete element. Returns `null` when nothing usable is
@@ -104,6 +104,17 @@ class RdxPortalPresence {
104
104
  */
105
105
  this.container = input(/* @ts-ignore */
106
106
  ...(ngDevMode ? [undefined, { debugName: "container" }] : /* istanbul ignore next */ []));
107
+ /**
108
+ * Keep the portal mounted (in the container) while the layer is closed. When `true` the content
109
+ * stays in the DOM regardless of the {@link RDX_PRESENCE_CONTEXT} `present` flag — the consumer is
110
+ * responsible for hiding it while closed (typically via the `data-closed` / `data-open` state on the
111
+ * positioner). Any `*rdxXxxPortal` wrapper opts in by mapping this input in its `hostDirectives`.
112
+ */
113
+ this.keepMounted = input(false, { ...(ngDevMode ? { debugName: "keepMounted" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
114
+ /** The layer's own open/present flag, OR'd with {@link keepMounted} to gate mount/unmount. */
115
+ this.contextPresent = inject(RDX_PRESENCE_CONTEXT).present;
116
+ this.present = computed(() => this.contextPresent() || this.keepMounted(), /* @ts-ignore */
117
+ ...(ngDevMode ? [{ debugName: "present" }] : /* istanbul ignore next */ []));
107
118
  this._computedContainer = linkedSignal(this.container, /* @ts-ignore */
108
119
  ...(ngDevMode ? [{ debugName: "_computedContainer" }] : /* istanbul ignore next */ []));
109
120
  this.elementContainer = computed(() => {
@@ -116,7 +127,7 @@ class RdxPortalPresence {
116
127
  ...(ngDevMode ? [{ debugName: "mountedNodes" }] : /* istanbul ignore next */ []));
117
128
  this.viewRef = null;
118
129
  const machine = new PresenceMachine({
119
- present: inject(RDX_PRESENCE_CONTEXT).present,
130
+ present: this.present,
120
131
  isBrowser: this.isBrowser,
121
132
  injector: inject(Injector),
122
133
  mountView: () => this.mountView(),
@@ -155,14 +166,14 @@ class RdxPortalPresence {
155
166
  this.mountedNodes.set([]);
156
167
  }
157
168
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxPortalPresence, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
158
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxPortalPresence, isStandalone: true, inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
169
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxPortalPresence, isStandalone: true, inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null }, keepMounted: { classPropertyName: "keepMounted", publicName: "keepMounted", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
159
170
  }
160
171
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxPortalPresence, decorators: [{
161
172
  type: Directive,
162
173
  args: [{
163
174
  standalone: true
164
175
  }]
165
- }], ctorParameters: () => [], propDecorators: { container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }] } });
176
+ }], ctorParameters: () => [], propDecorators: { container: [{ type: i0.Input, args: [{ isSignal: true, alias: "container", required: false }] }], keepMounted: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepMounted", required: false }] }] } });
166
177
 
167
178
  /**
168
179
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-portal.mjs","sources":["../../../packages/primitives/portal/src/resolve-container.ts","../../../packages/primitives/portal/src/portal.ts","../../../packages/primitives/portal/src/portal-presence.ts","../../../packages/primitives/portal/radix-ng-primitives-portal.ts"],"sourcesContent":["import { ElementRef } from '@angular/core';\n\n/**\n * A target container for a portal. Accepts an `ElementRef`, a native element, or a CSS selector\n * resolved against the document.\n */\nexport type RdxPortalContainer = ElementRef<HTMLElement> | HTMLElement | string;\n\n/**\n * Resolve a {@link RdxPortalContainer} to a concrete element. Returns `null` when nothing usable is\n * provided (a missing container, a selector matching nothing, or a non-element value), so callers can\n * fall back to `document.body`. Shared by `RdxPortal` and `RdxPortalPresence`.\n */\nexport function resolvePortalContainer(\n container: RdxPortalContainer | undefined,\n document: Document | null\n): HTMLElement | null {\n if (!container) {\n return null;\n }\n if (typeof container === 'string') {\n return document?.querySelector<HTMLElement>(container) ?? null;\n }\n if (container instanceof ElementRef) {\n return container.nativeElement ?? null;\n }\n // Anything that isn't a real element (e.g. a TemplateRef passed by mistake) falls back to the\n // default container so the content still leaves the flow instead of staying in place.\n return container instanceof HTMLElement ? container : null;\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n linkedSignal,\n PLATFORM_ID\n} from '@angular/core';\nimport { RdxPortalContainer, resolvePortalContainer } from './resolve-container';\n\n@Directive({\n selector: '[rdxPortal]',\n exportAs: 'rdxPortal'\n})\nexport class RdxPortal {\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly document = inject(DOCUMENT, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n\n /**\n * Specify a container to portal the content into. Can be an `ElementRef`, a native element, or a\n * CSS selector. Defaults to `document.body` when not set (or when a selector matches nothing).\n */\n readonly container = input<RdxPortalContainer>();\n\n private readonly _computedContainer = linkedSignal(this.container);\n readonly computedContainer = this._computedContainer.asReadonly();\n\n private readonly elementContainer = computed<HTMLElement | null>(() => {\n const provided = resolvePortalContainer(this.computedContainer(), this.document);\n const body = this.document?.body ?? null;\n return provided ?? body;\n });\n\n constructor() {\n const isBrowser = isPlatformBrowser(this.platformId);\n if (!isBrowser || !this.document) {\n return;\n }\n\n const element = this.elementRef.nativeElement;\n // Anchor the original DOM position with a comment node, so the element can be restored\n // exactly where it was when the directive is destroyed.\n const anchor = this.document.createComment('rdx-portal');\n element.parentNode?.insertBefore(anchor, element);\n\n // Move reactively: the effect runs after inputs are bound (so `container` is respected on\n // first render) and re-runs whenever the target container changes. `appendChild` relocates\n // the element, it does not clone it.\n effect(() => {\n this.elementContainer()?.appendChild(element);\n });\n\n this.destroyRef.onDestroy(() => {\n anchor.parentNode?.replaceChild(element, anchor);\n });\n }\n\n setContainer(container: RdxPortalContainer) {\n this._computedContainer.set(container);\n }\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n EmbeddedViewRef,\n inject,\n Injector,\n input,\n linkedSignal,\n PLATFORM_ID,\n signal,\n TemplateRef,\n ViewContainerRef\n} from '@angular/core';\nimport { PresenceMachine, RDX_PRESENCE_CONTEXT } from '@radix-ng/primitives/presence';\nimport { RdxPortalContainer, resolvePortalContainer } from './resolve-container';\n\n/**\n * Structural directive merging what `RdxPortal` + `RdxPresenceDirective` did as a pair: it mounts its\n * template while `present()` (from {@link RDX_PRESENCE_CONTEXT}) is `true`, **relocates every root\n * node into a portal container** (default `document.body`), and on close runs the presence exit state\n * machine — keeping the content mounted until every running CSS exit `@keyframes` on any root node\n * finishes.\n *\n * Unlike `RdxPortal`, it adds **no wrapper element**: the template's root nodes become direct children\n * of the container. Use the `*` microsyntax for the common single-root case\n * (`<div *rdxXxxPortal rdxXxxPositioner>`) or the explicit `<ng-template rdxXxxPortal>` form for a\n * custom container or multiple root nodes (e.g. a dialog backdrop + popup).\n *\n * SSR: on the server the view renders in place and is never relocated; after hydration the relocation\n * effect moves the nodes into the container (same browser-guarded split `RdxPortal` uses).\n */\n@Directive({\n standalone: true\n})\nexport class RdxPortalPresence {\n private readonly viewContainerRef = inject(ViewContainerRef);\n private readonly templateRef = inject(TemplateRef<void>);\n private readonly document = inject(DOCUMENT, { optional: true });\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /**\n * Container to portal the content into. Can be an `ElementRef`, a native element, or a CSS\n * selector. Defaults to `document.body` (or when a selector matches nothing).\n */\n readonly container = input<RdxPortalContainer>();\n\n private readonly _computedContainer = linkedSignal(this.container);\n\n private readonly elementContainer = computed<HTMLElement | null>(() => {\n const provided = resolvePortalContainer(this._computedContainer(), this.document);\n return provided ?? this.document?.body ?? null;\n });\n\n /** The live view's root nodes, exposed as a signal so the relocation effect re-runs on (re)mount. */\n private readonly mountedNodes = signal<Node[]>([]);\n private viewRef: EmbeddedViewRef<void> | null = null;\n\n constructor() {\n const machine = new PresenceMachine({\n present: inject(RDX_PRESENCE_CONTEXT).present,\n isBrowser: this.isBrowser,\n injector: inject(Injector),\n mountView: () => this.mountView(),\n destroyView: () => this.destroyView()\n });\n\n // Relocate reactively: re-runs whenever the resolved container changes or the view is\n // (re)mounted. On the server we never relocate — the nodes render in place.\n effect(() => {\n const container = this.elementContainer();\n const nodes = this.mountedNodes();\n if (!this.isBrowser || !container) {\n return;\n }\n for (const node of nodes) {\n container.appendChild(node);\n }\n });\n\n inject(DestroyRef).onDestroy(() => machine.dispose());\n }\n\n /** Imperatively override the portal container (parity with `RdxPortal.setContainer`). */\n setContainer(container: RdxPortalContainer): void {\n this._computedContainer.set(container);\n }\n\n private mountView(): HTMLElement[] {\n this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef);\n const rootNodes = this.viewRef.rootNodes as Node[];\n this.mountedNodes.set(rootNodes);\n // Watch every element root for exit animations (dialog returns backdrop + popup).\n return rootNodes.filter((node): node is HTMLElement => node instanceof HTMLElement);\n }\n\n private destroyView(): void {\n // Destroying the view removes the nodes from wherever they currently live (the container) —\n // no anchor-comment juggling, unlike `RdxPortal` which restores its host element.\n this.viewRef?.destroy();\n this.viewRef = null;\n this.mountedNodes.set([]);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"radix-ng-primitives-portal.mjs","sources":["../../../packages/primitives/portal/src/resolve-container.ts","../../../packages/primitives/portal/src/portal.ts","../../../packages/primitives/portal/src/portal-presence.ts","../../../packages/primitives/portal/radix-ng-primitives-portal.ts"],"sourcesContent":["import { ElementRef } from '@angular/core';\n\n/**\n * A target container for a portal. Accepts an `ElementRef`, a native element, or a CSS selector\n * resolved against the document.\n */\nexport type RdxPortalContainer = ElementRef<HTMLElement> | HTMLElement | string;\n\n/**\n * Resolve a {@link RdxPortalContainer} to a concrete element. Returns `null` when nothing usable is\n * provided (a missing container, a selector matching nothing, or a non-element value), so callers can\n * fall back to `document.body`. Shared by `RdxPortal` and `RdxPortalPresence`.\n */\nexport function resolvePortalContainer(\n container: RdxPortalContainer | undefined,\n document: Document | null\n): HTMLElement | null {\n if (!container) {\n return null;\n }\n if (typeof container === 'string') {\n return document?.querySelector<HTMLElement>(container) ?? null;\n }\n if (container instanceof ElementRef) {\n return container.nativeElement ?? null;\n }\n // Anything that isn't a real element (e.g. a TemplateRef passed by mistake) falls back to the\n // default container so the content still leaves the flow instead of staying in place.\n return container instanceof HTMLElement ? container : null;\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n linkedSignal,\n PLATFORM_ID\n} from '@angular/core';\nimport { RdxPortalContainer, resolvePortalContainer } from './resolve-container';\n\n@Directive({\n selector: '[rdxPortal]',\n exportAs: 'rdxPortal'\n})\nexport class RdxPortal {\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly document = inject(DOCUMENT, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n\n /**\n * Specify a container to portal the content into. Can be an `ElementRef`, a native element, or a\n * CSS selector. Defaults to `document.body` when not set (or when a selector matches nothing).\n */\n readonly container = input<RdxPortalContainer>();\n\n private readonly _computedContainer = linkedSignal(this.container);\n readonly computedContainer = this._computedContainer.asReadonly();\n\n private readonly elementContainer = computed<HTMLElement | null>(() => {\n const provided = resolvePortalContainer(this.computedContainer(), this.document);\n const body = this.document?.body ?? null;\n return provided ?? body;\n });\n\n constructor() {\n const isBrowser = isPlatformBrowser(this.platformId);\n if (!isBrowser || !this.document) {\n return;\n }\n\n const element = this.elementRef.nativeElement;\n // Anchor the original DOM position with a comment node, so the element can be restored\n // exactly where it was when the directive is destroyed.\n const anchor = this.document.createComment('rdx-portal');\n element.parentNode?.insertBefore(anchor, element);\n\n // Move reactively: the effect runs after inputs are bound (so `container` is respected on\n // first render) and re-runs whenever the target container changes. `appendChild` relocates\n // the element, it does not clone it.\n effect(() => {\n this.elementContainer()?.appendChild(element);\n });\n\n this.destroyRef.onDestroy(() => {\n anchor.parentNode?.replaceChild(element, anchor);\n });\n }\n\n setContainer(container: RdxPortalContainer) {\n this._computedContainer.set(container);\n }\n}\n","import { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n effect,\n EmbeddedViewRef,\n inject,\n Injector,\n input,\n linkedSignal,\n PLATFORM_ID,\n signal,\n TemplateRef,\n ViewContainerRef\n} from '@angular/core';\nimport { PresenceMachine, RDX_PRESENCE_CONTEXT } from '@radix-ng/primitives/presence';\nimport { RdxPortalContainer, resolvePortalContainer } from './resolve-container';\n\n/**\n * Structural directive merging what `RdxPortal` + `RdxPresenceDirective` did as a pair: it mounts its\n * template while `present()` (from {@link RDX_PRESENCE_CONTEXT}) is `true`, **relocates every root\n * node into a portal container** (default `document.body`), and on close runs the presence exit state\n * machine — keeping the content mounted until every running CSS exit `@keyframes` on any root node\n * finishes.\n *\n * Unlike `RdxPortal`, it adds **no wrapper element**: the template's root nodes become direct children\n * of the container. Use the `*` microsyntax for the common single-root case\n * (`<div *rdxXxxPortal rdxXxxPositioner>`) or the explicit `<ng-template rdxXxxPortal>` form for a\n * custom container or multiple root nodes (e.g. a dialog backdrop + popup).\n *\n * SSR: on the server the view renders in place and is never relocated; after hydration the relocation\n * effect moves the nodes into the container (same browser-guarded split `RdxPortal` uses).\n */\n@Directive({\n standalone: true\n})\nexport class RdxPortalPresence {\n private readonly viewContainerRef = inject(ViewContainerRef);\n private readonly templateRef = inject(TemplateRef<void>);\n private readonly document = inject(DOCUMENT, { optional: true });\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n /**\n * Container to portal the content into. Can be an `ElementRef`, a native element, or a CSS\n * selector. Defaults to `document.body` (or when a selector matches nothing).\n */\n readonly container = input<RdxPortalContainer>();\n\n /**\n * Keep the portal mounted (in the container) while the layer is closed. When `true` the content\n * stays in the DOM regardless of the {@link RDX_PRESENCE_CONTEXT} `present` flag — the consumer is\n * responsible for hiding it while closed (typically via the `data-closed` / `data-open` state on the\n * positioner). Any `*rdxXxxPortal` wrapper opts in by mapping this input in its `hostDirectives`.\n */\n readonly keepMounted = input(false, { transform: booleanAttribute });\n\n /** The layer's own open/present flag, OR'd with {@link keepMounted} to gate mount/unmount. */\n private readonly contextPresent = inject(RDX_PRESENCE_CONTEXT).present;\n private readonly present = computed(() => this.contextPresent() || this.keepMounted());\n\n private readonly _computedContainer = linkedSignal(this.container);\n\n private readonly elementContainer = computed<HTMLElement | null>(() => {\n const provided = resolvePortalContainer(this._computedContainer(), this.document);\n return provided ?? this.document?.body ?? null;\n });\n\n /** The live view's root nodes, exposed as a signal so the relocation effect re-runs on (re)mount. */\n private readonly mountedNodes = signal<Node[]>([]);\n private viewRef: EmbeddedViewRef<void> | null = null;\n\n constructor() {\n const machine = new PresenceMachine({\n present: this.present,\n isBrowser: this.isBrowser,\n injector: inject(Injector),\n mountView: () => this.mountView(),\n destroyView: () => this.destroyView()\n });\n\n // Relocate reactively: re-runs whenever the resolved container changes or the view is\n // (re)mounted. On the server we never relocate — the nodes render in place.\n effect(() => {\n const container = this.elementContainer();\n const nodes = this.mountedNodes();\n if (!this.isBrowser || !container) {\n return;\n }\n for (const node of nodes) {\n container.appendChild(node);\n }\n });\n\n inject(DestroyRef).onDestroy(() => machine.dispose());\n }\n\n /** Imperatively override the portal container (parity with `RdxPortal.setContainer`). */\n setContainer(container: RdxPortalContainer): void {\n this._computedContainer.set(container);\n }\n\n private mountView(): HTMLElement[] {\n this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef);\n const rootNodes = this.viewRef.rootNodes as Node[];\n this.mountedNodes.set(rootNodes);\n // Watch every element root for exit animations (dialog returns backdrop + popup).\n return rootNodes.filter((node): node is HTMLElement => node instanceof HTMLElement);\n }\n\n private destroyView(): void {\n // Destroying the view removes the nodes from wherever they currently live (the container) —\n // no anchor-comment juggling, unlike `RdxPortal` which restores its host element.\n this.viewRef?.destroy();\n this.viewRef = null;\n this.mountedNodes.set([]);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAQA;;;;AAIG;AACG,SAAU,sBAAsB,CAClC,SAAyC,EACzC,QAAyB,EAAA;IAEzB,IAAI,CAAC,SAAS,EAAE;AACZ,QAAA,OAAO,IAAI;IACf;AACA,IAAA,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;QAC/B,OAAO,QAAQ,EAAE,aAAa,CAAc,SAAS,CAAC,IAAI,IAAI;IAClE;AACA,IAAA,IAAI,SAAS,YAAY,UAAU,EAAE;AACjC,QAAA,OAAO,SAAS,CAAC,aAAa,IAAI,IAAI;IAC1C;;;IAGA,OAAO,SAAS,YAAY,WAAW,GAAG,SAAS,GAAG,IAAI;AAC9D;;MCXa,SAAS,CAAA;AAqBlB,IAAA,WAAA,GAAA;AApBiB,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAA0B,UAAU,CAAC;AACxD,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;QAChC,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC/C,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAEhD;;;AAGG;AACM,QAAA,IAAA,CAAA,SAAS,GAAG,KAAK;iGAAsB;AAE/B,QAAA,IAAA,CAAA,kBAAkB,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS;+FAAC;AACzD,QAAA,IAAA,CAAA,iBAAiB,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,EAAE;AAEhD,QAAA,IAAA,CAAA,gBAAgB,GAAG,QAAQ,CAAqB,MAAK;AAClE,YAAA,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;YAChF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI;YACxC,OAAO,QAAQ,IAAI,IAAI;QAC3B,CAAC;6FAAC;QAGE,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;QACpD,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAC9B;QACJ;AAEA,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa;;;QAG7C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC;QACxD,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC;;;;QAKjD,MAAM,CAAC,MAAK;YACR,IAAI,CAAC,gBAAgB,EAAE,EAAE,WAAW,CAAC,OAAO,CAAC;AACjD,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;YAC3B,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC;AACpD,QAAA,CAAC,CAAC;IACN;AAEA,IAAA,YAAY,CAAC,SAA6B,EAAA;AACtC,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC;IAC1C;8GA/CS,SAAS,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAT,SAAS,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAT,SAAS,EAAA,UAAA,EAAA,CAAA;kBAJrB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACP,oBAAA,QAAQ,EAAE,aAAa;AACvB,oBAAA,QAAQ,EAAE;AACb,iBAAA;;;ACGD;;;;;;;;;;;;;;AAcG;MAIU,iBAAiB,CAAA;AAmC1B,IAAA,WAAA,GAAA;AAlCiB,QAAA,IAAA,CAAA,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAC3C,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,EAAC,WAAiB,EAAC;QACvC,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/C,IAAA,CAAA,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAEnE;;;AAGG;AACM,QAAA,IAAA,CAAA,SAAS,GAAG,KAAK;iGAAsB;AAEhD;;;;;AAKG;QACM,IAAA,CAAA,WAAW,GAAG,KAAK,CAAC,KAAK,mFAAI,SAAS,EAAE,gBAAgB,EAAA,CAAG;;AAGnD,QAAA,IAAA,CAAA,cAAc,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC,OAAO;AACrD,QAAA,IAAA,CAAA,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE;oFAAC;AAErE,QAAA,IAAA,CAAA,kBAAkB,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS;+FAAC;AAEjD,QAAA,IAAA,CAAA,gBAAgB,GAAG,QAAQ,CAAqB,MAAK;AAClE,YAAA,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;YACjF,OAAO,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,IAAI;QAClD,CAAC;6FAAC;;QAGe,IAAA,CAAA,YAAY,GAAG,MAAM,CAAS,EAAE;yFAAC;QAC1C,IAAA,CAAA,OAAO,GAAiC,IAAI;AAGhD,QAAA,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC;YAChC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;AACzB,YAAA,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;AAC1B,YAAA,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE;AACjC,YAAA,WAAW,EAAE,MAAM,IAAI,CAAC,WAAW;AACtC,SAAA,CAAC;;;QAIF,MAAM,CAAC,MAAK;AACR,YAAA,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE;AACzC,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,EAAE;YACjC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE;gBAC/B;YACJ;AACA,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACtB,gBAAA,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC;YAC/B;AACJ,QAAA,CAAC,CAAC;AAEF,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACzD;;AAGA,IAAA,YAAY,CAAC,SAA6B,EAAA;AACtC,QAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC;IAC1C;IAEQ,SAAS,GAAA;AACb,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC;AACzE,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAmB;AAClD,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;;AAEhC,QAAA,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,KAA0B,IAAI,YAAY,WAAW,CAAC;IACvF;IAEQ,WAAW,GAAA;;;AAGf,QAAA,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE;AACvB,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B;8GA/ES,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAjB,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAH7B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACP,oBAAA,UAAU,EAAE;AACf,iBAAA;;;ACrCD;;AAEG;;;;"}
@@ -329,8 +329,10 @@ class RdxPresenceDirective {
329
329
  }
330
330
  mountView() {
331
331
  this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef);
332
- const node = this.viewRef.rootNodes.find((n) => n instanceof HTMLElement);
333
- return node ? [node] : [];
332
+ // Return *all* root elements (not just the first) so a multi-root template — e.g. a backdrop
333
+ // plus content has the exit animation on any root suspend the unmount. Mirrors
334
+ // `RdxPortalPresence.mountView`, which already watches every relocated root node.
335
+ return this.viewRef.rootNodes.filter((node) => node instanceof HTMLElement);
334
336
  }
335
337
  destroyView() {
336
338
  this.viewRef?.destroy();
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-presence.mjs","sources":["../../../packages/primitives/presence/src/presence-machine.ts","../../../packages/primitives/presence/src/presence.directive.ts","../../../packages/primitives/presence/radix-ng-primitives-presence.ts"],"sourcesContent":["import { afterNextRender, effect, Injector, Signal } from '@angular/core';\nimport { getMaxTransitionDuration } from '@radix-ng/primitives/core';\n\ntype PresenceState = 'mounted' | 'unmountSuspended' | 'unmounted';\ntype PresenceEvent = 'MOUNT' | 'UNMOUNT' | 'ANIMATION_OUT' | 'ANIMATION_END';\n\n/**\n * State machine mirroring `@radix-ui/react-presence`.\n *\n * - `mounted` — content rendered, `present` is `true`.\n * - `unmountSuspended` — `present` flipped to `false` but an exit animation is running;\n * the content stays in the DOM until the animation ends.\n * - `unmounted` — content removed.\n */\nconst MACHINE: Record<PresenceState, Partial<Record<PresenceEvent, PresenceState>>> = {\n mounted: { UNMOUNT: 'unmounted', ANIMATION_OUT: 'unmountSuspended' },\n unmountSuspended: { MOUNT: 'mounted', ANIMATION_END: 'unmounted' },\n unmounted: { MOUNT: 'mounted' }\n};\n\n/**\n * Grace period (ms) added to the longest declared exit duration before the safety-net timer\n * force-completes the exit. Only matters when a `finished` promise never settles (the engine\n * under-reports `getAnimations`, the animation is replaced without a cancel, reduced motion, …).\n * Mirrors `TRANSITION_FALLBACK_BUFFER` in `use-transition-status.ts`.\n */\nconst EXIT_FALLBACK_BUFFER = 50;\n\n/**\n * Operations the host directive supplies to {@link PresenceMachine}. The machine owns *when* the\n * view is mounted/unmounted (the state transitions and exit-animation suspension); the host owns\n * *how* — `RdxPresenceDirective` simply creates the embedded view in place, while `RdxPortalPresence`\n * additionally relocates its root nodes into a portal container.\n */\nexport interface PresenceMachineHost {\n /** Reactive `present` flag driving the machine. */\n readonly present: Signal<boolean>;\n /** Whether we are running in a browser (animation/computed-style logic is skipped on the server). */\n readonly isBrowser: boolean;\n /** Injection context for the internal `effect` / `afterNextRender`. */\n readonly injector: Injector;\n /**\n * Create (and, for the portal, relocate) the view. Returns the root `HTMLElement` nodes whose\n * exit animations should suspend the unmount. For a single-root template this is one element —\n * identical to the previous single-node behavior; dialog-shaped templates return backdrop + popup.\n */\n mountView(): HTMLElement[];\n /** Destroy the view (Angular removes the nodes from wherever they currently live). */\n destroyView(): void;\n}\n\n/**\n * Reusable presence state machine extracted from `RdxPresenceDirective`. It keeps content mounted\n * while a CSS exit animation runs anywhere inside the template — a `@keyframes` **or** a\n * `transition` (`data-ending-style`), on a watched root **or any of its descendants** — and unmounts\n * only once every exit animation started by the close has finished. With no exit animation it\n * unmounts immediately. For a single watched node and a root-level keyframe this reduces exactly to\n * the original `RdxPresenceDirective` behavior.\n *\n * Detection (ADR 0011) uses the Web Animations API: when `present()` flips `false` we snapshot a\n * close timestamp, and after the next render collect `node.getAnimations({ subtree: true })`, keeping\n * only animations that are running/pending and were *started by* the close (`startTime` null or\n * `>= closeTimestamp`). The view stays mounted until all of their `finished` promises settle, bounded\n * by a duration-based safety net. The legacy root-level computed-`animationName` check and the\n * `animationstart`/`animationend` listeners are kept as an additional acceptor — they drive the\n * zoneless jsdom suites (where `getAnimations` is absent) and cost nothing in a real browser.\n */\nexport class PresenceMachine {\n private state: PresenceState;\n private prevPresent: boolean;\n\n /** Root nodes currently watched for exit animations (set on mount). */\n private nodes: HTMLElement[] = [];\n /** Last-seen computed `animationName` per node, used to detect a *fresh* root exit animation. */\n private readonly prevAnimationNames = new WeakMap<HTMLElement, string>();\n /** Root nodes whose exit animation the event path is still waiting on (jsdom / root-keyframe). */\n private readonly pendingExits = new Set<HTMLElement>();\n private removeListeners: (() => void) | null = null;\n\n /**\n * Timeline time captured the moment `present` flipped `false`, on the same clock as\n * `Animation.startTime`. Animations started at or after it are the exit animations.\n */\n private closeTimestamp = 0;\n /**\n * Monotonic counter bumped on every mount/unmount transition. A suspended exit captures it and\n * its `finished`/safety-net resolution is ignored if it changed in the meantime (re-open or a\n * second close), so a stale promise can never tear down a freshly-reopened view.\n */\n private exitVersion = 0;\n /** True while a WAAPI `finished`-promise wait owns completion; gates the event path off. */\n private waapiPending = false;\n private safetyTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly host: PresenceMachineHost) {\n this.prevPresent = host.present();\n this.state = this.prevPresent ? 'mounted' : 'unmounted';\n\n if (this.prevPresent) {\n this.mount();\n }\n\n effect(\n () => {\n const present = host.present();\n\n if (present === this.prevPresent) {\n return;\n }\n this.prevPresent = present;\n\n if (present) {\n // Mount synchronously so the enter animation can start on this frame.\n this.send('MOUNT');\n } else if (host.isBrowser) {\n // Snapshot the close time *now* (before the render that applies the closed-state\n // styles) so the freshness filter can tell exit animations from pre-existing ones.\n this.closeTimestamp = this.now();\n // Defer the unmount decision until the next render, so the consumer's\n // `data-state` / `data-ending-style` (and therefore the exit styles) are applied\n // to the DOM before we read the running animations.\n afterNextRender(() => this.evaluateExit(), { injector: host.injector });\n } else {\n this.send('UNMOUNT');\n }\n },\n { injector: host.injector, debugName: 'PresenceMachine.present' }\n );\n }\n\n /** Tear the machine down — destroys the view. Call from the host's `DestroyRef`. */\n dispose(): void {\n this.unmount();\n }\n\n /** Decides whether to suspend the unmount for an exit animation (port of Radix' logic). */\n private evaluateExit(): void {\n // Re-opened before this callback ran — keep the content mounted.\n if (this.state !== 'mounted' || this.host.present()) {\n return;\n }\n\n this.pendingExits.clear();\n\n // Legacy acceptor: a watched root whose closed state starts a *different* `@keyframes`.\n // This is what the zoneless jsdom suite drives (via synthetic `animationstart`/`animationend`)\n // and what catches root keyframes in engines that do not expose `getAnimations`.\n for (const node of this.nodes) {\n const styles = getComputedStyle(node);\n const currentAnimationName = styles.animationName || 'none';\n const prevAnimationName = this.prevAnimationNames.get(node) ?? 'none';\n\n const isAnimating =\n currentAnimationName !== 'none' &&\n styles.display !== 'none' &&\n prevAnimationName !== currentAnimationName;\n\n if (isAnimating) {\n this.pendingExits.add(node);\n }\n }\n\n // WAAPI acceptor (ADR 0011): subtree-aware, transitions *or* keyframes, on any element in the\n // template. This is what makes popup-level exits work without a positioner decoy keyframe.\n const exitAnimations = this.collectExitAnimations();\n\n if (this.pendingExits.size === 0 && exitAnimations.length === 0) {\n // Nothing runs a fresh exit animation — unmount right away.\n this.send('UNMOUNT');\n return;\n }\n\n this.send('ANIMATION_OUT');\n\n if (exitAnimations.length > 0) {\n // WAAPI sees the whole subtree, so it supersedes the root-event path for this close:\n // wait for every fresh exit animation, version-guarded against re-open.\n this.waapiPending = true;\n const version = this.exitVersion;\n\n void Promise.all(exitAnimations.map((animation) => animation.finished.catch(() => undefined))).then(() =>\n this.finishExit(version)\n );\n\n this.armSafetyNet(version, exitAnimations);\n }\n // else: no WAAPI animations (jsdom, or an engine without `getAnimations`) → the root-event\n // path drains `pendingExits` through `onEnd`, exactly as before this ADR.\n }\n\n /**\n * Running/pending animations across the watched subtrees that were *started by* the close.\n * Pre-existing animations (an infinite spinner, a settled enter) have an earlier `startTime`\n * and must not delay the unmount.\n */\n private collectExitAnimations(): Animation[] {\n if (!this.host.isBrowser) {\n return [];\n }\n\n const result: Animation[] = [];\n\n for (const node of this.nodes) {\n if (typeof node.getAnimations !== 'function') {\n continue;\n }\n\n for (const animation of node.getAnimations({ subtree: true })) {\n const fresh = animation.startTime === null || Number(animation.startTime) >= this.closeTimestamp;\n\n // `animation.pending` is the WAAPI \"created but not yet started this frame\" flag — a\n // freshly triggered exit. `playState` itself never reports `'pending'`.\n if ((animation.playState === 'running' || animation.pending) && fresh) {\n result.push(animation);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Force-completes the exit shortly after the longest declared duration, in case a `finished`\n * promise never settles. Measures the animated targets (falling back to the roots).\n */\n private armSafetyNet(version: number, exitAnimations: Animation[]): void {\n const targets = new Set<HTMLElement>(this.nodes);\n\n for (const animation of exitAnimations) {\n const target = (animation.effect as KeyframeEffect | null)?.target;\n if (target instanceof HTMLElement) {\n targets.add(target);\n }\n }\n\n let maxDuration = 0;\n for (const element of targets) {\n maxDuration = Math.max(maxDuration, getMaxTransitionDuration(element));\n }\n\n this.clearSafetyNet();\n this.safetyTimer = setTimeout(() => this.finishExit(version), maxDuration + EXIT_FALLBACK_BUFFER);\n }\n\n /** Settle a WAAPI/safety-net exit wait, ignoring it if a newer mount/unmount superseded it. */\n private finishExit(version: number): void {\n if (version !== this.exitVersion) {\n return;\n }\n this.waapiPending = false;\n this.clearSafetyNet();\n this.send('ANIMATION_END');\n }\n\n private clearSafetyNet(): void {\n if (this.safetyTimer !== null) {\n clearTimeout(this.safetyTimer);\n this.safetyTimer = null;\n }\n }\n\n private send(event: PresenceEvent): void {\n const next = MACHINE[this.state][event];\n if (next === undefined || next === this.state) {\n return;\n }\n\n this.state = next;\n\n if (next === 'mounted') {\n // Bump the version so any in-flight exit wait from a prior close is ignored.\n this.exitVersion++;\n this.waapiPending = false;\n this.clearSafetyNet();\n\n if (this.nodes.length > 0) {\n // Re-opened while an exit animation was running — refresh the tracked animations and\n // drop any pending exits so a late `animationend` cannot tear down the live view.\n this.pendingExits.clear();\n for (const node of this.nodes) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n } else {\n this.mount();\n }\n } else if (next === 'unmounted') {\n this.exitVersion++;\n this.unmount();\n }\n // `unmountSuspended` keeps the existing view mounted until ANIMATION_END.\n }\n\n private mount(): void {\n this.nodes = this.host.mountView();\n\n if (this.host.isBrowser && this.nodes.length > 0) {\n for (const node of this.nodes) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n this.addAnimationListeners();\n }\n }\n\n private unmount(): void {\n this.removeListeners?.();\n this.removeListeners = null;\n this.clearSafetyNet();\n this.waapiPending = false;\n this.host.destroyView();\n this.nodes = [];\n this.pendingExits.clear();\n }\n\n private addAnimationListeners(): void {\n const onStart = (event: AnimationEvent) => {\n const node = event.target as HTMLElement;\n if (this.nodes.includes(node)) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n };\n const onEnd = (event: AnimationEvent) => {\n const node = event.target as HTMLElement;\n if (!this.nodes.includes(node)) {\n return;\n }\n // Ignore the end of an animation other than the one we are currently waiting on.\n if (!this.getAnimationName(node).includes(event.animationName)) {\n return;\n }\n\n this.pendingExits.delete(node);\n // While a WAAPI wait owns completion it is authoritative (it sees the full subtree); the\n // event path only drives the unmount when no WAAPI animations were detected.\n if (!this.waapiPending && this.pendingExits.size === 0) {\n this.finishExit(this.exitVersion);\n }\n };\n\n for (const node of this.nodes) {\n node.addEventListener('animationstart', onStart);\n node.addEventListener('animationcancel', onEnd);\n node.addEventListener('animationend', onEnd);\n }\n\n this.removeListeners = () => {\n for (const node of this.nodes) {\n node.removeEventListener('animationstart', onStart);\n node.removeEventListener('animationcancel', onEnd);\n node.removeEventListener('animationend', onEnd);\n }\n };\n }\n\n private getAnimationName(node: HTMLElement): string {\n return (this.host.isBrowser ? getComputedStyle(node).animationName : '') || 'none';\n }\n\n /** Current timeline time on the same clock as `Animation.startTime` (ms), or 0 if unavailable. */\n private now(): number {\n const time = typeof document !== 'undefined' ? document.timeline?.currentTime : null;\n return typeof time === 'number' ? time : Number(time ?? 0);\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n Directive,\n EmbeddedViewRef,\n inject,\n InjectionToken,\n Injector,\n PLATFORM_ID,\n Provider,\n Signal,\n TemplateRef,\n ViewContainerRef\n} from '@angular/core';\nimport { PresenceMachine } from './presence-machine';\n\n/**\n * Context interface for RdxPresence directive\n * Contains a Signal that indicates whether the content should be present in the DOM\n */\nexport type RdxPresenceContext = {\n present: Signal<boolean>;\n};\n\nexport const RDX_PRESENCE_CONTEXT = new InjectionToken<RdxPresenceContext>('RdxPresenceContext');\n\n/**\n * Factory provider helper.\n * In your parent component/directive you can write:\n *\n * providers: [\n * provideRdxPresenceContext(() => ({ present: myBooleanSignal }))\n * ]\n */\nexport function provideRdxPresenceContext(contextFactory: () => RdxPresenceContext): Provider {\n return { provide: RDX_PRESENCE_CONTEXT, useFactory: contextFactory };\n}\n\n/**\n * Headless structural directive that conditionally renders its template based on a reactive\n * `present` signal supplied through {@link RDX_PRESENCE_CONTEXT}.\n *\n * Unlike a plain `*ngIf`, it keeps the content mounted while a CSS exit animation\n * (`@keyframes` applied for the closed state) is running, and unmounts it only once that\n * animation finishes. If the content has no exit animation, it unmounts immediately.\n *\n * The mount/unmount-with-exit logic lives in the shared {@link PresenceMachine}; this directive just\n * creates the embedded view in place (`RdxPortalPresence` reuses the same machine and additionally\n * relocates the view into a portal container).\n */\n@Directive({\n standalone: true\n})\nexport class RdxPresenceDirective {\n private readonly viewContainerRef = inject(ViewContainerRef);\n private readonly templateRef = inject(TemplateRef<void>);\n\n private viewRef: EmbeddedViewRef<void> | null = null;\n\n constructor() {\n const machine = new PresenceMachine({\n present: inject(RDX_PRESENCE_CONTEXT).present,\n isBrowser: isPlatformBrowser(inject(PLATFORM_ID)),\n injector: inject(Injector),\n mountView: () => this.mountView(),\n destroyView: () => this.destroyView()\n });\n\n inject(DestroyRef).onDestroy(() => machine.dispose());\n }\n\n private mountView(): HTMLElement[] {\n this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef);\n const node = this.viewRef.rootNodes.find((n): n is HTMLElement => n instanceof HTMLElement);\n return node ? [node] : [];\n }\n\n private destroyView(): void {\n this.viewRef?.destroy();\n this.viewRef = null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"radix-ng-primitives-presence.mjs","sources":["../../../packages/primitives/presence/src/presence-machine.ts","../../../packages/primitives/presence/src/presence.directive.ts","../../../packages/primitives/presence/radix-ng-primitives-presence.ts"],"sourcesContent":["import { afterNextRender, effect, Injector, Signal } from '@angular/core';\nimport { getMaxTransitionDuration } from '@radix-ng/primitives/core';\n\ntype PresenceState = 'mounted' | 'unmountSuspended' | 'unmounted';\ntype PresenceEvent = 'MOUNT' | 'UNMOUNT' | 'ANIMATION_OUT' | 'ANIMATION_END';\n\n/**\n * State machine mirroring `@radix-ui/react-presence`.\n *\n * - `mounted` — content rendered, `present` is `true`.\n * - `unmountSuspended` — `present` flipped to `false` but an exit animation is running;\n * the content stays in the DOM until the animation ends.\n * - `unmounted` — content removed.\n */\nconst MACHINE: Record<PresenceState, Partial<Record<PresenceEvent, PresenceState>>> = {\n mounted: { UNMOUNT: 'unmounted', ANIMATION_OUT: 'unmountSuspended' },\n unmountSuspended: { MOUNT: 'mounted', ANIMATION_END: 'unmounted' },\n unmounted: { MOUNT: 'mounted' }\n};\n\n/**\n * Grace period (ms) added to the longest declared exit duration before the safety-net timer\n * force-completes the exit. Only matters when a `finished` promise never settles (the engine\n * under-reports `getAnimations`, the animation is replaced without a cancel, reduced motion, …).\n * Mirrors `TRANSITION_FALLBACK_BUFFER` in `use-transition-status.ts`.\n */\nconst EXIT_FALLBACK_BUFFER = 50;\n\n/**\n * Operations the host directive supplies to {@link PresenceMachine}. The machine owns *when* the\n * view is mounted/unmounted (the state transitions and exit-animation suspension); the host owns\n * *how* — `RdxPresenceDirective` simply creates the embedded view in place, while `RdxPortalPresence`\n * additionally relocates its root nodes into a portal container.\n */\nexport interface PresenceMachineHost {\n /** Reactive `present` flag driving the machine. */\n readonly present: Signal<boolean>;\n /** Whether we are running in a browser (animation/computed-style logic is skipped on the server). */\n readonly isBrowser: boolean;\n /** Injection context for the internal `effect` / `afterNextRender`. */\n readonly injector: Injector;\n /**\n * Create (and, for the portal, relocate) the view. Returns the root `HTMLElement` nodes whose\n * exit animations should suspend the unmount. For a single-root template this is one element —\n * identical to the previous single-node behavior; dialog-shaped templates return backdrop + popup.\n */\n mountView(): HTMLElement[];\n /** Destroy the view (Angular removes the nodes from wherever they currently live). */\n destroyView(): void;\n}\n\n/**\n * Reusable presence state machine extracted from `RdxPresenceDirective`. It keeps content mounted\n * while a CSS exit animation runs anywhere inside the template — a `@keyframes` **or** a\n * `transition` (`data-ending-style`), on a watched root **or any of its descendants** — and unmounts\n * only once every exit animation started by the close has finished. With no exit animation it\n * unmounts immediately. For a single watched node and a root-level keyframe this reduces exactly to\n * the original `RdxPresenceDirective` behavior.\n *\n * Detection (ADR 0011) uses the Web Animations API: when `present()` flips `false` we snapshot a\n * close timestamp, and after the next render collect `node.getAnimations({ subtree: true })`, keeping\n * only animations that are running/pending and were *started by* the close (`startTime` null or\n * `>= closeTimestamp`). The view stays mounted until all of their `finished` promises settle, bounded\n * by a duration-based safety net. The legacy root-level computed-`animationName` check and the\n * `animationstart`/`animationend` listeners are kept as an additional acceptor — they drive the\n * zoneless jsdom suites (where `getAnimations` is absent) and cost nothing in a real browser.\n */\nexport class PresenceMachine {\n private state: PresenceState;\n private prevPresent: boolean;\n\n /** Root nodes currently watched for exit animations (set on mount). */\n private nodes: HTMLElement[] = [];\n /** Last-seen computed `animationName` per node, used to detect a *fresh* root exit animation. */\n private readonly prevAnimationNames = new WeakMap<HTMLElement, string>();\n /** Root nodes whose exit animation the event path is still waiting on (jsdom / root-keyframe). */\n private readonly pendingExits = new Set<HTMLElement>();\n private removeListeners: (() => void) | null = null;\n\n /**\n * Timeline time captured the moment `present` flipped `false`, on the same clock as\n * `Animation.startTime`. Animations started at or after it are the exit animations.\n */\n private closeTimestamp = 0;\n /**\n * Monotonic counter bumped on every mount/unmount transition. A suspended exit captures it and\n * its `finished`/safety-net resolution is ignored if it changed in the meantime (re-open or a\n * second close), so a stale promise can never tear down a freshly-reopened view.\n */\n private exitVersion = 0;\n /** True while a WAAPI `finished`-promise wait owns completion; gates the event path off. */\n private waapiPending = false;\n private safetyTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly host: PresenceMachineHost) {\n this.prevPresent = host.present();\n this.state = this.prevPresent ? 'mounted' : 'unmounted';\n\n if (this.prevPresent) {\n this.mount();\n }\n\n effect(\n () => {\n const present = host.present();\n\n if (present === this.prevPresent) {\n return;\n }\n this.prevPresent = present;\n\n if (present) {\n // Mount synchronously so the enter animation can start on this frame.\n this.send('MOUNT');\n } else if (host.isBrowser) {\n // Snapshot the close time *now* (before the render that applies the closed-state\n // styles) so the freshness filter can tell exit animations from pre-existing ones.\n this.closeTimestamp = this.now();\n // Defer the unmount decision until the next render, so the consumer's\n // `data-state` / `data-ending-style` (and therefore the exit styles) are applied\n // to the DOM before we read the running animations.\n afterNextRender(() => this.evaluateExit(), { injector: host.injector });\n } else {\n this.send('UNMOUNT');\n }\n },\n { injector: host.injector, debugName: 'PresenceMachine.present' }\n );\n }\n\n /** Tear the machine down — destroys the view. Call from the host's `DestroyRef`. */\n dispose(): void {\n this.unmount();\n }\n\n /** Decides whether to suspend the unmount for an exit animation (port of Radix' logic). */\n private evaluateExit(): void {\n // Re-opened before this callback ran — keep the content mounted.\n if (this.state !== 'mounted' || this.host.present()) {\n return;\n }\n\n this.pendingExits.clear();\n\n // Legacy acceptor: a watched root whose closed state starts a *different* `@keyframes`.\n // This is what the zoneless jsdom suite drives (via synthetic `animationstart`/`animationend`)\n // and what catches root keyframes in engines that do not expose `getAnimations`.\n for (const node of this.nodes) {\n const styles = getComputedStyle(node);\n const currentAnimationName = styles.animationName || 'none';\n const prevAnimationName = this.prevAnimationNames.get(node) ?? 'none';\n\n const isAnimating =\n currentAnimationName !== 'none' &&\n styles.display !== 'none' &&\n prevAnimationName !== currentAnimationName;\n\n if (isAnimating) {\n this.pendingExits.add(node);\n }\n }\n\n // WAAPI acceptor (ADR 0011): subtree-aware, transitions *or* keyframes, on any element in the\n // template. This is what makes popup-level exits work without a positioner decoy keyframe.\n const exitAnimations = this.collectExitAnimations();\n\n if (this.pendingExits.size === 0 && exitAnimations.length === 0) {\n // Nothing runs a fresh exit animation — unmount right away.\n this.send('UNMOUNT');\n return;\n }\n\n this.send('ANIMATION_OUT');\n\n if (exitAnimations.length > 0) {\n // WAAPI sees the whole subtree, so it supersedes the root-event path for this close:\n // wait for every fresh exit animation, version-guarded against re-open.\n this.waapiPending = true;\n const version = this.exitVersion;\n\n void Promise.all(exitAnimations.map((animation) => animation.finished.catch(() => undefined))).then(() =>\n this.finishExit(version)\n );\n\n this.armSafetyNet(version, exitAnimations);\n }\n // else: no WAAPI animations (jsdom, or an engine without `getAnimations`) → the root-event\n // path drains `pendingExits` through `onEnd`, exactly as before this ADR.\n }\n\n /**\n * Running/pending animations across the watched subtrees that were *started by* the close.\n * Pre-existing animations (an infinite spinner, a settled enter) have an earlier `startTime`\n * and must not delay the unmount.\n */\n private collectExitAnimations(): Animation[] {\n if (!this.host.isBrowser) {\n return [];\n }\n\n const result: Animation[] = [];\n\n for (const node of this.nodes) {\n if (typeof node.getAnimations !== 'function') {\n continue;\n }\n\n for (const animation of node.getAnimations({ subtree: true })) {\n const fresh = animation.startTime === null || Number(animation.startTime) >= this.closeTimestamp;\n\n // `animation.pending` is the WAAPI \"created but not yet started this frame\" flag — a\n // freshly triggered exit. `playState` itself never reports `'pending'`.\n if ((animation.playState === 'running' || animation.pending) && fresh) {\n result.push(animation);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Force-completes the exit shortly after the longest declared duration, in case a `finished`\n * promise never settles. Measures the animated targets (falling back to the roots).\n */\n private armSafetyNet(version: number, exitAnimations: Animation[]): void {\n const targets = new Set<HTMLElement>(this.nodes);\n\n for (const animation of exitAnimations) {\n const target = (animation.effect as KeyframeEffect | null)?.target;\n if (target instanceof HTMLElement) {\n targets.add(target);\n }\n }\n\n let maxDuration = 0;\n for (const element of targets) {\n maxDuration = Math.max(maxDuration, getMaxTransitionDuration(element));\n }\n\n this.clearSafetyNet();\n this.safetyTimer = setTimeout(() => this.finishExit(version), maxDuration + EXIT_FALLBACK_BUFFER);\n }\n\n /** Settle a WAAPI/safety-net exit wait, ignoring it if a newer mount/unmount superseded it. */\n private finishExit(version: number): void {\n if (version !== this.exitVersion) {\n return;\n }\n this.waapiPending = false;\n this.clearSafetyNet();\n this.send('ANIMATION_END');\n }\n\n private clearSafetyNet(): void {\n if (this.safetyTimer !== null) {\n clearTimeout(this.safetyTimer);\n this.safetyTimer = null;\n }\n }\n\n private send(event: PresenceEvent): void {\n const next = MACHINE[this.state][event];\n if (next === undefined || next === this.state) {\n return;\n }\n\n this.state = next;\n\n if (next === 'mounted') {\n // Bump the version so any in-flight exit wait from a prior close is ignored.\n this.exitVersion++;\n this.waapiPending = false;\n this.clearSafetyNet();\n\n if (this.nodes.length > 0) {\n // Re-opened while an exit animation was running — refresh the tracked animations and\n // drop any pending exits so a late `animationend` cannot tear down the live view.\n this.pendingExits.clear();\n for (const node of this.nodes) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n } else {\n this.mount();\n }\n } else if (next === 'unmounted') {\n this.exitVersion++;\n this.unmount();\n }\n // `unmountSuspended` keeps the existing view mounted until ANIMATION_END.\n }\n\n private mount(): void {\n this.nodes = this.host.mountView();\n\n if (this.host.isBrowser && this.nodes.length > 0) {\n for (const node of this.nodes) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n this.addAnimationListeners();\n }\n }\n\n private unmount(): void {\n this.removeListeners?.();\n this.removeListeners = null;\n this.clearSafetyNet();\n this.waapiPending = false;\n this.host.destroyView();\n this.nodes = [];\n this.pendingExits.clear();\n }\n\n private addAnimationListeners(): void {\n const onStart = (event: AnimationEvent) => {\n const node = event.target as HTMLElement;\n if (this.nodes.includes(node)) {\n this.prevAnimationNames.set(node, this.getAnimationName(node));\n }\n };\n const onEnd = (event: AnimationEvent) => {\n const node = event.target as HTMLElement;\n if (!this.nodes.includes(node)) {\n return;\n }\n // Ignore the end of an animation other than the one we are currently waiting on.\n if (!this.getAnimationName(node).includes(event.animationName)) {\n return;\n }\n\n this.pendingExits.delete(node);\n // While a WAAPI wait owns completion it is authoritative (it sees the full subtree); the\n // event path only drives the unmount when no WAAPI animations were detected.\n if (!this.waapiPending && this.pendingExits.size === 0) {\n this.finishExit(this.exitVersion);\n }\n };\n\n for (const node of this.nodes) {\n node.addEventListener('animationstart', onStart);\n node.addEventListener('animationcancel', onEnd);\n node.addEventListener('animationend', onEnd);\n }\n\n this.removeListeners = () => {\n for (const node of this.nodes) {\n node.removeEventListener('animationstart', onStart);\n node.removeEventListener('animationcancel', onEnd);\n node.removeEventListener('animationend', onEnd);\n }\n };\n }\n\n private getAnimationName(node: HTMLElement): string {\n return (this.host.isBrowser ? getComputedStyle(node).animationName : '') || 'none';\n }\n\n /** Current timeline time on the same clock as `Animation.startTime` (ms), or 0 if unavailable. */\n private now(): number {\n const time = typeof document !== 'undefined' ? document.timeline?.currentTime : null;\n return typeof time === 'number' ? time : Number(time ?? 0);\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n DestroyRef,\n Directive,\n EmbeddedViewRef,\n inject,\n InjectionToken,\n Injector,\n PLATFORM_ID,\n Provider,\n Signal,\n TemplateRef,\n ViewContainerRef\n} from '@angular/core';\nimport { PresenceMachine } from './presence-machine';\n\n/**\n * Context interface for RdxPresence directive\n * Contains a Signal that indicates whether the content should be present in the DOM\n */\nexport type RdxPresenceContext = {\n present: Signal<boolean>;\n};\n\nexport const RDX_PRESENCE_CONTEXT = new InjectionToken<RdxPresenceContext>('RdxPresenceContext');\n\n/**\n * Factory provider helper.\n * In your parent component/directive you can write:\n *\n * providers: [\n * provideRdxPresenceContext(() => ({ present: myBooleanSignal }))\n * ]\n */\nexport function provideRdxPresenceContext(contextFactory: () => RdxPresenceContext): Provider {\n return { provide: RDX_PRESENCE_CONTEXT, useFactory: contextFactory };\n}\n\n/**\n * Headless structural directive that conditionally renders its template based on a reactive\n * `present` signal supplied through {@link RDX_PRESENCE_CONTEXT}.\n *\n * Unlike a plain `*ngIf`, it keeps the content mounted while a CSS exit animation\n * (`@keyframes` applied for the closed state) is running, and unmounts it only once that\n * animation finishes. If the content has no exit animation, it unmounts immediately.\n *\n * The mount/unmount-with-exit logic lives in the shared {@link PresenceMachine}; this directive just\n * creates the embedded view in place (`RdxPortalPresence` reuses the same machine and additionally\n * relocates the view into a portal container).\n */\n@Directive({\n standalone: true\n})\nexport class RdxPresenceDirective {\n private readonly viewContainerRef = inject(ViewContainerRef);\n private readonly templateRef = inject(TemplateRef<void>);\n\n private viewRef: EmbeddedViewRef<void> | null = null;\n\n constructor() {\n const machine = new PresenceMachine({\n present: inject(RDX_PRESENCE_CONTEXT).present,\n isBrowser: isPlatformBrowser(inject(PLATFORM_ID)),\n injector: inject(Injector),\n mountView: () => this.mountView(),\n destroyView: () => this.destroyView()\n });\n\n inject(DestroyRef).onDestroy(() => machine.dispose());\n }\n\n private mountView(): HTMLElement[] {\n this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef);\n // Return *all* root elements (not just the first) so a multi-root template — e.g. a backdrop\n // plus content — has the exit animation on any root suspend the unmount. Mirrors\n // `RdxPortalPresence.mountView`, which already watches every relocated root node.\n return (this.viewRef.rootNodes as Node[]).filter((node): node is HTMLElement => node instanceof HTMLElement);\n }\n\n private destroyView(): void {\n this.viewRef?.destroy();\n this.viewRef = null;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, model, signal, computed, ElementRef, input, booleanAttribute, output, effect, untracked, Directive, InjectionToken, Injector, DestroyRef, afterNextRender, linkedSignal, contentChild, forwardRef, isDevMode } from '@angular/core';
3
3
  import * as i3 from '@radix-ng/primitives/core';
4
- import { isEqual, getActiveElement, formUiStateContext, createContext, RdxFormUiControlBase, injectId, createFloatingRootContext, useTransitionStatus, isNullish, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useListHighlight, useAnchoredScrollLock, RdxFloatingNodeRegistration, handleAndDispatchCustomEvent, resizeEffect, clamp, rdxDevError, setupInternalBackdrop } from '@radix-ng/primitives/core';
4
+ import { isEqual, getActiveElement, formUiStateContext, createContext, RdxFormUiControlBase, injectId, createFloatingRootContext, useTransitionStatus, isNullish, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useListHighlight, useAnchoredScrollLock, RdxFloatingNodeRegistration, handleAndDispatchCustomEvent, resizeEffect, clamp, rdxDevError, setupInternalBackdrop, provideExistingToken } from '@radix-ng/primitives/core';
5
5
  import { injectDirection } from '@radix-ng/primitives/direction-provider';
6
6
  import * as i2 from '@radix-ng/primitives/floating-focus-manager';
7
7
  import { getInteractionTypeFromEvent, provideFloatingFocusManagerConfig, RdxFloatingFocusManager, createRdxTriggerInteraction } from '@radix-ng/primitives/floating-focus-manager';
8
8
  import * as i1 from '@radix-ng/primitives/popper';
9
- import { RdxPopper, RdxPopperContent, RdxPopperContentWrapper, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
9
+ import { RdxPopper, RdxPopperContent, RdxPopperContentWrapper, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, DROPDOWN_COLLISION_AVOIDANCE, RdxPopperAnchor } from '@radix-ng/primitives/popper';
10
10
  import * as i4 from '@radix-ng/primitives/composite';
11
11
  import { RdxCompositeList, RdxCompositeListItem } from '@radix-ng/primitives/composite';
12
12
  import { RdxDismiss } from '@radix-ng/primitives/dismissable-layer';
@@ -1315,8 +1315,12 @@ class RdxSelectPositioner extends RdxPopperContentWrapper {
1315
1315
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1316
1316
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectPositioner, isStandalone: true, selector: "[rdxSelectPositioner]", host: { properties: { "style": "positionerStyle" } }, providers: [
1317
1317
  ...provideRdxPopperContentWrapper(RdxSelectPositioner),
1318
- provideRdxPopperContentConfig({ align: 'start', updatePositionStrategy: 'always' }),
1319
- { provide: RDX_SELECT_POSITIONER_TOKEN, useExisting: forwardRef(() => RdxSelectPositioner) }
1318
+ provideRdxPopperContentConfig({
1319
+ align: 'start',
1320
+ collisionAvoidance: DROPDOWN_COLLISION_AVOIDANCE,
1321
+ updatePositionStrategy: 'always'
1322
+ }),
1323
+ provideExistingToken(RDX_SELECT_POSITIONER_TOKEN, RdxSelectPositioner)
1320
1324
  ], usesInheritance: true, ngImport: i0 }); }
1321
1325
  }
1322
1326
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPositioner, decorators: [{
@@ -1325,8 +1329,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1325
1329
  selector: '[rdxSelectPositioner]',
1326
1330
  providers: [
1327
1331
  ...provideRdxPopperContentWrapper(RdxSelectPositioner),
1328
- provideRdxPopperContentConfig({ align: 'start', updatePositionStrategy: 'always' }),
1329
- { provide: RDX_SELECT_POSITIONER_TOKEN, useExisting: forwardRef(() => RdxSelectPositioner) }
1332
+ provideRdxPopperContentConfig({
1333
+ align: 'start',
1334
+ collisionAvoidance: DROPDOWN_COLLISION_AVOIDANCE,
1335
+ updatePositionStrategy: 'always'
1336
+ }),
1337
+ provideExistingToken(RDX_SELECT_POSITIONER_TOKEN, RdxSelectPositioner)
1330
1338
  ],
1331
1339
  host: {
1332
1340
  // The unified vars + placement attrs come from the wrapper (ADR 0012); only `box-sizing` and