@radix-ng/primitives 1.0.7 → 1.0.9

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 (54) hide show
  1. package/fesm2022/radix-ng-primitives-autocomplete.mjs +43 -9
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-checkbox.mjs +89 -8
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-combobox.mjs +43 -9
  6. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-core.mjs +208 -2
  8. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-date-field.mjs +50 -9
  10. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  11. package/fesm2022/radix-ng-primitives-editable.mjs +31 -5
  12. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-field.mjs +265 -46
  14. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-form.mjs +110 -26
  16. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-input.mjs +16 -6
  18. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-number-field.mjs +32 -7
  20. package/fesm2022/radix-ng-primitives-number-field.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-radio.mjs +18 -4
  24. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-select.mjs +26 -8
  26. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-signal-forms.mjs +184 -0
  28. package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -0
  29. package/fesm2022/radix-ng-primitives-slider.mjs +24 -8
  30. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-switch.mjs +29 -5
  32. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  33. package/fesm2022/radix-ng-primitives-time-field.mjs +49 -9
  34. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  35. package/fesm2022/radix-ng-primitives-toggle-group.mjs +23 -12
  36. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  37. package/package.json +11 -1
  38. package/types/radix-ng-primitives-autocomplete.d.ts +21 -2
  39. package/types/radix-ng-primitives-checkbox.d.ts +53 -4
  40. package/types/radix-ng-primitives-combobox.d.ts +51 -2
  41. package/types/radix-ng-primitives-core.d.ts +206 -3
  42. package/types/radix-ng-primitives-date-field.d.ts +27 -2
  43. package/types/radix-ng-primitives-editable.d.ts +15 -2
  44. package/types/radix-ng-primitives-field.d.ts +136 -33
  45. package/types/radix-ng-primitives-form.d.ts +115 -21
  46. package/types/radix-ng-primitives-input.d.ts +8 -1
  47. package/types/radix-ng-primitives-number-field.d.ts +14 -3
  48. package/types/radix-ng-primitives-radio.d.ts +7 -2
  49. package/types/radix-ng-primitives-select.d.ts +11 -3
  50. package/types/radix-ng-primitives-signal-forms.d.ts +97 -0
  51. package/types/radix-ng-primitives-slider.d.ts +9 -7
  52. package/types/radix-ng-primitives-switch.d.ts +15 -2
  53. package/types/radix-ng-primitives-time-field.d.ts +26 -2
  54. package/types/radix-ng-primitives-toggle-group.d.ts +6 -4
@@ -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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"}
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { inject, ElementRef, model, input, booleanAttribute, output, signal, computed, effect, untracked, Directive } from '@angular/core';
3
3
  import * as i1 from '@radix-ng/primitives/composite';
4
4
  import { RdxCompositeRoot, RdxCompositeItem } from '@radix-ng/primitives/composite';
5
- import { createContext, createCancelableChangeEventDetails, provideValueAccessor } from '@radix-ng/primitives/core';
5
+ import { createContext, RdxFormUiControlBase, createCancelableChangeEventDetails, provideValueAccessor } from '@radix-ng/primitives/core';
6
6
 
7
7
  const rootContext = () => {
8
8
  const root = inject(RdxRadioGroupDirective);
@@ -19,8 +19,9 @@ const rootContext = () => {
19
19
  };
20
20
  };
21
21
  const [injectRadioRootContext, provideRadioRootContext] = createContext('RadioRootContext', 'components/radio');
22
- class RdxRadioGroupDirective {
22
+ class RdxRadioGroupDirective extends RdxFormUiControlBase {
23
23
  constructor() {
24
+ super();
24
25
  this.elementRef = inject(ElementRef);
25
26
  this.compositeRoot = inject(RdxCompositeRoot, { self: true });
26
27
  /**
@@ -51,6 +52,9 @@ class RdxRadioGroupDirective {
51
52
  ...(ngDevMode ? [{ debugName: "disable" }] : /* istanbul ignore next */ []));
52
53
  this.disabledState = computed(() => this.disable() || this.disabled(), /* @ts-ignore */
53
54
  ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
55
+ this.invalidState = this.formUi.invalidState;
56
+ this.touchedState = this.formUi.touchedState;
57
+ this.dirtyState = this.formUi.dirtyState;
54
58
  this.arrowNavigation = signal(false, /* @ts-ignore */
55
59
  ...(ngDevMode ? [{ debugName: "arrowNavigation" }] : /* istanbul ignore next */ []));
56
60
  this.itemMetadata = computed(() => Array.from(this.compositeRoot.itemMap().values()).filter(isRadioItemMetadata), /* @ts-ignore */
@@ -128,9 +132,14 @@ class RdxRadioGroupDirective {
128
132
  }
129
133
  }
130
134
  this.value.set(value);
135
+ this.formUi.markDirty();
131
136
  this.onChange?.(value);
132
137
  this.onTouched();
133
138
  }
139
+ /** @ignore Bridge the CVA `onTouched` so `markAsTouched()` also notifies Reactive/template forms. */
140
+ formUiTouchTarget() {
141
+ return { markAsTouched: () => this.onTouched() };
142
+ }
134
143
  /**
135
144
  * Update the value of the radio group.
136
145
  * @param value The new value of the radio group.
@@ -172,11 +181,11 @@ class RdxRadioGroupDirective {
172
181
  onFocusOut(event) {
173
182
  const next = event.relatedTarget;
174
183
  if (!this.elementRef.nativeElement.contains(next)) {
175
- this.onTouched();
184
+ this.formUi.markAsTouched();
176
185
  }
177
186
  }
178
187
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxRadioGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
179
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxRadioGroupDirective, isStandalone: true, selector: "[rdxRadioRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "radiogroup" }, listeners: { "focusout": "onFocusOut($event)" }, properties: { "attr.aria-required": "required() ? \"true\" : undefined", "attr.aria-disabled": "disabledState() ? \"true\" : undefined", "attr.aria-readonly": "readonly() ? \"true\" : undefined", "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [provideValueAccessor(RdxRadioGroupDirective), provideRadioRootContext(rootContext)], exportAs: ["rdxRadioRoot"], hostDirectives: [{ directive: i1.RdxCompositeRoot }], ngImport: i0 }); }
188
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxRadioGroupDirective, isStandalone: true, selector: "[rdxRadioRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "radiogroup" }, listeners: { "focusout": "onFocusOut($event)" }, properties: { "attr.aria-required": "required() ? \"true\" : undefined", "attr.aria-disabled": "disabledState() ? \"true\" : undefined", "attr.aria-readonly": "readonly() ? \"true\" : undefined", "attr.aria-invalid": "displayValid() === false ? \"true\" : undefined", "attr.data-disabled": "disabledState() ? \"\" : undefined", "attr.data-invalid": "displayValid() === false ? \"\" : undefined", "attr.data-valid": "displayValid() === true ? \"\" : undefined", "attr.data-touched": "touchedState() ? \"\" : undefined", "attr.data-dirty": "dirtyState() ? \"\" : undefined" } }, providers: [provideValueAccessor(RdxRadioGroupDirective), provideRadioRootContext(rootContext)], exportAs: ["rdxRadioRoot"], usesInheritance: true, hostDirectives: [{ directive: i1.RdxCompositeRoot }], ngImport: i0 }); }
180
189
  }
181
190
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxRadioGroupDirective, decorators: [{
182
191
  type: Directive,
@@ -190,7 +199,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
190
199
  '[attr.aria-required]': 'required() ? "true" : undefined',
191
200
  '[attr.aria-disabled]': 'disabledState() ? "true" : undefined',
192
201
  '[attr.aria-readonly]': 'readonly() ? "true" : undefined',
202
+ '[attr.aria-invalid]': 'displayValid() === false ? "true" : undefined',
193
203
  '[attr.data-disabled]': 'disabledState() ? "" : undefined',
204
+ '[attr.data-invalid]': 'displayValid() === false ? "" : undefined',
205
+ '[attr.data-valid]': 'displayValid() === true ? "" : undefined',
206
+ '[attr.data-touched]': 'touchedState() ? "" : undefined',
207
+ '[attr.data-dirty]': 'dirtyState() ? "" : undefined',
194
208
  '(focusout)': 'onFocusOut($event)'
195
209
  }
196
210
  }]
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-radio.mjs","sources":["../../../packages/primitives/radio/src/radio-root.directive.ts","../../../packages/primitives/radio/src/radio-item.directive.ts","../../../packages/primitives/radio/src/radio-indicator.directive.ts","../../../packages/primitives/radio/src/radio-item-input.directive.ts","../../../packages/primitives/radio/radix-ng-primitives-radio.ts"],"sourcesContent":["import {\n booleanAttribute,\n computed,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n model,\n output,\n signal,\n Signal,\n untracked\n} from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { RdxCompositeMetadata, RdxCompositeRoot } from '@radix-ng/primitives/composite';\nimport {\n BooleanInput,\n createCancelableChangeEventDetails,\n createContext,\n provideValueAccessor,\n RdxCancelableChangeEventDetails,\n RdxFormValueControl\n} from '@radix-ng/primitives/core';\nimport { RdxRadioValueChangeReason } from './radio-tokens';\n\nexport type { RdxRadioValueChangeReason } from './radio-tokens';\n\nexport type RdxRadioValueChangeEventDetails = RdxCancelableChangeEventDetails<RdxRadioValueChangeReason>;\n\nexport interface RdxRadioValueChangeEvent {\n value: string;\n eventDetails: RdxRadioValueChangeEventDetails;\n}\n\nexport interface RadioRootContext {\n value: Signal<string | null>;\n disabledState: Signal<boolean>;\n readonly: Signal<boolean>;\n required: Signal<boolean>;\n name: Signal<string | undefined>;\n form: Signal<string | undefined>;\n select(value: string | null, event?: Event, reason?: RdxRadioValueChangeReason): void;\n setArrowNavigation(value: boolean): void;\n isArrowNavigation(): boolean;\n}\n\nconst rootContext = (): RadioRootContext => {\n const root = inject(RdxRadioGroupDirective);\n\n return {\n value: root.value,\n disabledState: root.disabledState,\n readonly: root.readonly,\n required: root.required,\n name: root.name,\n form: root.form,\n select: (value, event, reason) => root.select(value, event, reason),\n setArrowNavigation: (value) => root.setArrowNavigation(value),\n isArrowNavigation: () => root.isArrowNavigation()\n };\n};\n\nexport const [injectRadioRootContext, provideRadioRootContext] = createContext<RadioRootContext>(\n 'RadioRootContext',\n 'components/radio'\n);\n\n@Directive({\n selector: '[rdxRadioRoot]',\n exportAs: 'rdxRadioRoot',\n providers: [provideValueAccessor(RdxRadioGroupDirective), provideRadioRootContext(rootContext)],\n hostDirectives: [RdxCompositeRoot],\n host: {\n role: 'radiogroup',\n '[attr.aria-required]': 'required() ? \"true\" : undefined',\n '[attr.aria-disabled]': 'disabledState() ? \"true\" : undefined',\n '[attr.aria-readonly]': 'readonly() ? \"true\" : undefined',\n '[attr.data-disabled]': 'disabledState() ? \"\" : undefined',\n '(focusout)': 'onFocusOut($event)'\n }\n})\nexport class RdxRadioGroupDirective implements ControlValueAccessor, RdxFormValueControl<string | null> {\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly compositeRoot = inject(RdxCompositeRoot, { self: true });\n\n /**\n * The selected value. Deliberately typed as `string` (not Base UI's generic `Value`):\n * a radio group maps onto native radio inputs whose form value is a string, so values are\n * the option identifiers. Use the string key of your option as the value.\n */\n readonly value = model<string | null>(null);\n\n readonly defaultValue = input<string>();\n\n readonly name = input<string>();\n\n readonly form = input<string>();\n\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Whether the user should be unable to select a different radio button. Bound in templates as\n * `readOnly` (Base UI spelling); the TS member stays `readonly` for cross-primitive consistency.\n */\n readonly readonly = input<boolean, BooleanInput>(false, { alias: 'readOnly', transform: booleanAttribute });\n\n readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Event handler called when the value changes.\n */\n readonly onValueChange = output<RdxRadioValueChangeEvent>();\n\n private readonly disable = signal<boolean>(false);\n readonly disabledState = computed(() => this.disable() || this.disabled());\n private readonly arrowNavigation = signal(false);\n private readonly itemMetadata = computed(() =>\n Array.from(this.compositeRoot.itemMap().values()).filter(isRadioItemMetadata)\n );\n private readonly disabledIndices = computed(() =>\n this.itemMetadata()\n .filter((metadata) => metadata.disabled)\n .map((metadata) => metadata.index)\n );\n private readonly activeIndex = computed(() => {\n const value = this.value();\n if (value === null) {\n return -1;\n }\n\n return this.itemMetadata().find((metadata) => metadata.value === value)?.index ?? -1;\n });\n\n /**\n * The callback function to call when the value of the radio group changes.\n */\n private onChange: (value: string | null) => void = () => {\n /* Empty */\n };\n\n /**\n * The callback function to call when the radio group is touched.\n * @ignore\n */\n onTouched: () => void = () => {\n /* Empty */\n };\n\n constructor() {\n let hasAppliedDefault = false;\n effect(() => {\n const defaultValue = this.defaultValue();\n if (hasAppliedDefault || defaultValue === undefined) {\n return;\n }\n\n hasAppliedDefault = true;\n if (untracked(this.value) === null) {\n this.value.set(defaultValue);\n }\n });\n\n effect(() => {\n this.compositeRoot.setEnableHomeAndEndKeys(false);\n this.compositeRoot.setModifierKeys(['Shift']);\n });\n\n effect(() => {\n this.compositeRoot.setDisabledIndices(this.disabledIndices());\n });\n\n effect(() => {\n const activeIndex = this.activeIndex();\n\n if (activeIndex === -1 || this.disabledIndices().includes(activeIndex)) {\n return;\n }\n\n const activeElement = this.elementRef.nativeElement.ownerDocument.activeElement;\n if (activeElement && this.elementRef.nativeElement.contains(activeElement)) {\n return;\n }\n\n this.compositeRoot.setHighlightedIndex(activeIndex);\n });\n }\n\n /**\n * Select a radio item.\n * @param value The value of the radio item to select.\n * @ignore\n */\n select(value: string | null, event?: Event, reason: RdxRadioValueChangeReason = 'none'): void {\n if (this.disabledState() || this.readonly() || this.value() === value) {\n return;\n }\n\n if (value !== null) {\n const trigger = event?.currentTarget instanceof HTMLElement ? event.currentTarget : undefined;\n const { eventDetails } = createCancelableChangeEventDetails(\n reason,\n event ?? new Event('radio.value-change'),\n trigger\n );\n this.onValueChange.emit({ value, eventDetails });\n if (eventDetails.isCanceled()) {\n return;\n }\n }\n\n this.value.set(value);\n this.onChange?.(value);\n this.onTouched();\n }\n\n /**\n * Update the value of the radio group.\n * @param value The new value of the radio group.\n * @ignore\n */\n writeValue(value: string | null): void {\n this.value.set(value);\n }\n\n /**\n * Register a callback function to call when the value of the radio group changes.\n * @param fn The callback function to call when the value of the radio group changes.\n * @ignore\n */\n registerOnChange(fn: (value: string | null) => void): void {\n this.onChange = fn;\n }\n\n /** @ignore */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /**\n * Set the disabled state of the radio group.\n * @param isDisabled Whether the radio group is disabled.\n * @ignore\n */\n setDisabledState(isDisabled: boolean): void {\n this.disable.set(isDisabled);\n }\n\n setArrowNavigation(value: boolean): void {\n this.arrowNavigation.set(value);\n }\n\n isArrowNavigation(): boolean {\n return this.arrowNavigation();\n }\n\n /**\n * Marks the control touched when focus leaves the whole group (Base UI `RadioGroup` parity and the\n * ADR 0004 CVA strategy) — moving focus between items stays inside, so `relatedTarget` is checked.\n */\n protected onFocusOut(event: FocusEvent): void {\n const next = event.relatedTarget as Node | null;\n if (!this.elementRef.nativeElement.contains(next)) {\n this.onTouched();\n }\n }\n}\n\ninterface RdxRadioItemMetadata {\n [key: string]: unknown;\n disabled: boolean;\n value: string;\n}\n\nfunction isRadioItemMetadata(metadata: RdxCompositeMetadata): metadata is RdxCompositeMetadata<RdxRadioItemMetadata> {\n return typeof metadata['disabled'] === 'boolean' && typeof metadata['value'] === 'string';\n}\n","import { booleanAttribute, computed, Directive, effect, ElementRef, inject, input, Signal } from '@angular/core';\nimport { RdxCompositeItem } from '@radix-ng/primitives/composite';\nimport { BooleanInput, createContext } from '@radix-ng/primitives/core';\nimport { injectRadioRootContext } from './radio-root.directive';\n\nexport interface RadioItemContext {\n value: Signal<string>;\n checkedState: Signal<boolean>;\n disabledState: Signal<boolean>;\n readonlyState: Signal<boolean>;\n requiredState: Signal<boolean>;\n}\n\nconst itemContext = (): RadioItemContext => {\n const item = inject(RdxRadioItemDirective);\n\n return {\n value: item.value,\n checkedState: item.checkedState,\n disabledState: item.disabledState,\n readonlyState: item.readonlyState,\n requiredState: item.requiredState\n };\n};\n\nexport const [injectRadioItemContext, provideRadioItemContext] = createContext<RadioItemContext>(\n 'RadioItemContext',\n 'components/radio'\n);\n\n@Directive({\n selector: '[rdxRadioItem]',\n exportAs: 'rdxRadioItem',\n providers: [provideRadioItemContext(itemContext)],\n hostDirectives: [RdxCompositeItem],\n\n host: {\n '[attr.type]': 'isNativeButton ? \"button\" : undefined',\n role: 'radio',\n '[attr.aria-checked]': 'checkedState()',\n '[attr.aria-disabled]': 'disabledState() ? \"true\" : undefined',\n '[attr.aria-readonly]': 'readonlyState() ? \"true\" : undefined',\n '[attr.aria-required]': 'requiredState() ? \"true\" : undefined',\n '[attr.data-composite-item-active]': 'checkedState() ? \"\" : undefined',\n '[attr.data-checked]': 'checkedState() ? \"\" : undefined',\n '[attr.data-unchecked]': '!checkedState() ? \"\" : undefined',\n '[attr.data-disabled]': 'disabledState() ? \"\" : undefined',\n '[attr.data-readonly]': 'readonlyState() ? \"\" : undefined',\n '[attr.data-required]': 'requiredState() ? \"\" : undefined',\n '[attr.disabled]': 'isNativeButton && disabledState() ? \"\" : undefined',\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeyDown($event)',\n '(keyup)': 'onKeyUp()',\n '(focus)': 'onFocus($event)'\n }\n})\nexport class RdxRadioItemDirective {\n private readonly rootContext = injectRadioRootContext();\n private readonly compositeItem = inject(RdxCompositeItem, { self: true });\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n\n readonly value = input.required<string>();\n\n readonly id = input<string>();\n\n readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Whether the user should be unable to select this radio button. Bound in templates as\n * `readOnly` (Base UI spelling); the TS member stays `readonly` for cross-primitive consistency.\n */\n readonly readonly = input<boolean, BooleanInput>(false, { alias: 'readOnly', transform: booleanAttribute });\n\n /**\n * Whether the host is a native `<button>`. Detected from the host tag (unlike Base UI's explicit\n * `nativeButton` prop, the rendered element is statically known in Angular templates), and used to\n * apply `type=\"button\"` and the native `disabled` attribute.\n */\n protected readonly isNativeButton = this.elementRef.nativeElement.tagName === 'BUTTON';\n\n readonly disabledState = computed(() => this.rootContext.disabledState() || this.disabled());\n\n readonly readonlyState = computed(() => this.rootContext.readonly() || this.readonly());\n\n readonly requiredState = computed(() => this.rootContext.required() || this.required());\n\n readonly checkedState = computed(() => this.rootContext.value() === this.value());\n\n private readonly ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'] as const;\n\n constructor() {\n effect(() => {\n this.compositeItem.setMetadata({\n disabled: this.disabledState(),\n value: this.value()\n });\n });\n }\n\n /** @ignore */\n onClick(event?: Event) {\n if (!this.disabledState() && !this.readonlyState()) {\n this.rootContext.select(this.value(), event);\n }\n }\n\n /** @ignore */\n onKeyDown(event: Event): void {\n const keyEvent = event as KeyboardEvent;\n if (keyEvent.key === ' ') {\n this.onClick(keyEvent);\n return;\n }\n\n if (keyEvent.key === 'Enter') {\n keyEvent.preventDefault();\n return;\n }\n\n if (this.isAllowedArrowKey(keyEvent.key)) {\n this.rootContext.setArrowNavigation(true);\n }\n }\n\n /** @ignore */\n onKeyUp() {\n this.rootContext.setArrowNavigation(false);\n }\n\n /** @ignore */\n onFocus(event?: FocusEvent) {\n queueMicrotask(() => {\n if (this.rootContext.isArrowNavigation()) {\n this.rootContext.select(this.value(), event);\n this.rootContext.setArrowNavigation(false);\n }\n });\n }\n\n private isAllowedArrowKey(key: string): boolean {\n return (this.ARROW_KEYS as readonly string[]).includes(key);\n }\n}\n","import { booleanAttribute, computed, Directive, input } from '@angular/core';\nimport { BooleanInput } from '@radix-ng/primitives/core';\nimport { injectRadioItemContext } from './radio-item.directive';\n\n@Directive({\n selector: '[rdxRadioIndicator]',\n exportAs: 'rdxRadioIndicator',\n host: {\n '[attr.data-checked]': 'itemContext.checkedState() ? \"\" : undefined',\n '[attr.data-unchecked]': '!itemContext.checkedState() ? \"\" : undefined',\n '[attr.data-disabled]': 'itemContext.disabledState() ? \"\" : undefined',\n '[attr.data-readonly]': 'itemContext.readonlyState() ? \"\" : undefined',\n '[attr.data-required]': 'itemContext.requiredState() ? \"\" : undefined',\n '[attr.data-starting-style]': 'isVisible() ? \"\" : undefined',\n '[attr.data-ending-style]': '!isVisible() ? \"\" : undefined',\n '[style.display]': '!keepMounted() && !isVisible() ? \"none\" : null',\n '[style.pointer-events]': '\"none\"'\n }\n})\nexport class RdxRadioIndicatorDirective {\n protected readonly itemContext = injectRadioItemContext();\n\n /** Keep the indicator in the DOM when unchecked so CSS exit animations can play. */\n readonly keepMounted = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n protected readonly isVisible = computed(() => this.itemContext.checkedState());\n}\n","import { computed, Directive, effect, ElementRef, inject } from '@angular/core';\nimport { injectRadioItemContext } from './radio-item.directive';\nimport { injectRadioRootContext } from './radio-root.directive';\n\n/**\n * The hidden native radio input that mirrors the item state for form submission, native validation,\n * and `<label>` activation. Place it inside an `rdxRadioItem`.\n *\n * @see https://base-ui.com/react/components/radio\n */\n@Directive({\n selector: 'input[rdxRadioItemInput]',\n exportAs: 'rdxRadioItemInput',\n host: {\n type: 'radio',\n tabindex: '-1',\n 'aria-hidden': 'true',\n '[attr.name]': 'name()',\n '[attr.form]': 'form()',\n '[attr.required]': 'required() ? \"\" : undefined',\n '[attr.disabled]': 'disabled() ? \"\" : undefined',\n '[attr.checked]': 'checked() ? \"\" : undefined',\n '[checked]': 'checked()',\n '[attr.value]': 'value()',\n '(change)': 'onInputChange($event)',\n style: 'transform: translateX(-100%); position: absolute; pointer-events: none; opacity: 0; margin: 0; inset: 0;'\n }\n})\nexport class RdxRadioItemInputDirective {\n private readonly rootContext = injectRadioRootContext();\n private readonly itemContext = injectRadioItemContext();\n private readonly input = inject<ElementRef<HTMLInputElement>>(ElementRef).nativeElement;\n\n readonly name = computed(() => this.rootContext.name());\n readonly form = computed(() => this.rootContext.form());\n readonly value = computed(() => this.itemContext.value() || undefined);\n readonly checked = computed(() => this.itemContext.checkedState());\n readonly required = computed(() => this.itemContext.requiredState());\n readonly disabled = computed(() => this.itemContext.disabledState());\n\n constructor() {\n let isInitial = true;\n\n effect(() => {\n const checked = this.checked();\n\n if (isInitial) {\n isInitial = false;\n return;\n }\n\n if (checked) {\n this.input.dispatchEvent(new Event('input', { bubbles: true }));\n this.input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n });\n }\n\n /**\n * Selects this item when the native input is checked — covers `<label>` activation,\n * where clicking the label toggles the hidden radio input rather than the visible item.\n * `select()` is a no-op when the value is already current, so the programmatic\n * `change` dispatched above does not re-trigger selection.\n * @ignore\n */\n protected onInputChange(event: Event): void {\n if (this.input.checked) {\n this.rootContext.select(this.itemContext.value(), event);\n }\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-radio.mjs","sources":["../../../packages/primitives/radio/src/radio-root.directive.ts","../../../packages/primitives/radio/src/radio-item.directive.ts","../../../packages/primitives/radio/src/radio-indicator.directive.ts","../../../packages/primitives/radio/src/radio-item-input.directive.ts","../../../packages/primitives/radio/radix-ng-primitives-radio.ts"],"sourcesContent":["import {\n booleanAttribute,\n computed,\n Directive,\n effect,\n ElementRef,\n inject,\n input,\n model,\n output,\n signal,\n Signal,\n untracked\n} from '@angular/core';\nimport { ControlValueAccessor } from '@angular/forms';\nimport { RdxCompositeMetadata, RdxCompositeRoot } from '@radix-ng/primitives/composite';\nimport {\n BooleanInput,\n createCancelableChangeEventDetails,\n createContext,\n provideValueAccessor,\n RdxCancelableChangeEventDetails,\n RdxFormUiControlBase,\n RdxFormUiTouchTarget,\n RdxFormValueControl\n} from '@radix-ng/primitives/core';\nimport { RdxRadioValueChangeReason } from './radio-tokens';\n\nexport type { RdxRadioValueChangeReason } from './radio-tokens';\n\nexport type RdxRadioValueChangeEventDetails = RdxCancelableChangeEventDetails<RdxRadioValueChangeReason>;\n\nexport interface RdxRadioValueChangeEvent {\n value: string;\n eventDetails: RdxRadioValueChangeEventDetails;\n}\n\nexport interface RadioRootContext {\n value: Signal<string | null>;\n disabledState: Signal<boolean>;\n readonly: Signal<boolean>;\n required: Signal<boolean>;\n name: Signal<string | undefined>;\n form: Signal<string | undefined>;\n select(value: string | null, event?: Event, reason?: RdxRadioValueChangeReason): void;\n setArrowNavigation(value: boolean): void;\n isArrowNavigation(): boolean;\n}\n\nconst rootContext = (): RadioRootContext => {\n const root = inject(RdxRadioGroupDirective);\n\n return {\n value: root.value,\n disabledState: root.disabledState,\n readonly: root.readonly,\n required: root.required,\n name: root.name,\n form: root.form,\n select: (value, event, reason) => root.select(value, event, reason),\n setArrowNavigation: (value) => root.setArrowNavigation(value),\n isArrowNavigation: () => root.isArrowNavigation()\n };\n};\n\nexport const [injectRadioRootContext, provideRadioRootContext] = createContext<RadioRootContext>(\n 'RadioRootContext',\n 'components/radio'\n);\n\n@Directive({\n selector: '[rdxRadioRoot]',\n exportAs: 'rdxRadioRoot',\n providers: [provideValueAccessor(RdxRadioGroupDirective), provideRadioRootContext(rootContext)],\n hostDirectives: [RdxCompositeRoot],\n host: {\n role: 'radiogroup',\n '[attr.aria-required]': 'required() ? \"true\" : undefined',\n '[attr.aria-disabled]': 'disabledState() ? \"true\" : undefined',\n '[attr.aria-readonly]': 'readonly() ? \"true\" : undefined',\n '[attr.aria-invalid]': 'displayValid() === false ? \"true\" : undefined',\n '[attr.data-disabled]': 'disabledState() ? \"\" : undefined',\n '[attr.data-invalid]': 'displayValid() === false ? \"\" : undefined',\n '[attr.data-valid]': 'displayValid() === true ? \"\" : undefined',\n '[attr.data-touched]': 'touchedState() ? \"\" : undefined',\n '[attr.data-dirty]': 'dirtyState() ? \"\" : undefined',\n '(focusout)': 'onFocusOut($event)'\n }\n})\nexport class RdxRadioGroupDirective\n extends RdxFormUiControlBase\n implements ControlValueAccessor, RdxFormValueControl<string | null>\n{\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly compositeRoot = inject(RdxCompositeRoot, { self: true });\n\n /**\n * The selected value. Deliberately typed as `string` (not Base UI's generic `Value`):\n * a radio group maps onto native radio inputs whose form value is a string, so values are\n * the option identifiers. Use the string key of your option as the value.\n */\n readonly value = model<string | null>(null);\n\n readonly defaultValue = input<string>();\n\n readonly name = input<string>();\n\n readonly form = input<string>();\n\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Whether the user should be unable to select a different radio button. Bound in templates as\n * `readOnly` (Base UI spelling); the TS member stays `readonly` for cross-primitive consistency.\n */\n readonly readonly = input<boolean, BooleanInput>(false, { alias: 'readOnly', transform: booleanAttribute });\n\n readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Event handler called when the value changes.\n */\n readonly onValueChange = output<RdxRadioValueChangeEvent>();\n\n private readonly disable = signal<boolean>(false);\n readonly disabledState = computed(() => this.disable() || this.disabled());\n readonly invalidState = this.formUi.invalidState;\n readonly touchedState = this.formUi.touchedState;\n readonly dirtyState = this.formUi.dirtyState;\n private readonly arrowNavigation = signal(false);\n private readonly itemMetadata = computed(() =>\n Array.from(this.compositeRoot.itemMap().values()).filter(isRadioItemMetadata)\n );\n private readonly disabledIndices = computed(() =>\n this.itemMetadata()\n .filter((metadata) => metadata.disabled)\n .map((metadata) => metadata.index)\n );\n private readonly activeIndex = computed(() => {\n const value = this.value();\n if (value === null) {\n return -1;\n }\n\n return this.itemMetadata().find((metadata) => metadata.value === value)?.index ?? -1;\n });\n\n /**\n * The callback function to call when the value of the radio group changes.\n */\n private onChange: (value: string | null) => void = () => {\n /* Empty */\n };\n\n /**\n * The callback function to call when the radio group is touched.\n * @ignore\n */\n onTouched: () => void = () => {\n /* Empty */\n };\n\n constructor() {\n super();\n\n let hasAppliedDefault = false;\n effect(() => {\n const defaultValue = this.defaultValue();\n if (hasAppliedDefault || defaultValue === undefined) {\n return;\n }\n\n hasAppliedDefault = true;\n if (untracked(this.value) === null) {\n this.value.set(defaultValue);\n }\n });\n\n effect(() => {\n this.compositeRoot.setEnableHomeAndEndKeys(false);\n this.compositeRoot.setModifierKeys(['Shift']);\n });\n\n effect(() => {\n this.compositeRoot.setDisabledIndices(this.disabledIndices());\n });\n\n effect(() => {\n const activeIndex = this.activeIndex();\n\n if (activeIndex === -1 || this.disabledIndices().includes(activeIndex)) {\n return;\n }\n\n const activeElement = this.elementRef.nativeElement.ownerDocument.activeElement;\n if (activeElement && this.elementRef.nativeElement.contains(activeElement)) {\n return;\n }\n\n this.compositeRoot.setHighlightedIndex(activeIndex);\n });\n }\n\n /**\n * Select a radio item.\n * @param value The value of the radio item to select.\n * @ignore\n */\n select(value: string | null, event?: Event, reason: RdxRadioValueChangeReason = 'none'): void {\n if (this.disabledState() || this.readonly() || this.value() === value) {\n return;\n }\n\n if (value !== null) {\n const trigger = event?.currentTarget instanceof HTMLElement ? event.currentTarget : undefined;\n const { eventDetails } = createCancelableChangeEventDetails(\n reason,\n event ?? new Event('radio.value-change'),\n trigger\n );\n this.onValueChange.emit({ value, eventDetails });\n if (eventDetails.isCanceled()) {\n return;\n }\n }\n\n this.value.set(value);\n this.formUi.markDirty();\n this.onChange?.(value);\n this.onTouched();\n }\n\n /** @ignore Bridge the CVA `onTouched` so `markAsTouched()` also notifies Reactive/template forms. */\n protected override formUiTouchTarget(): RdxFormUiTouchTarget {\n return { markAsTouched: () => this.onTouched() };\n }\n\n /**\n * Update the value of the radio group.\n * @param value The new value of the radio group.\n * @ignore\n */\n writeValue(value: string | null): void {\n this.value.set(value);\n }\n\n /**\n * Register a callback function to call when the value of the radio group changes.\n * @param fn The callback function to call when the value of the radio group changes.\n * @ignore\n */\n registerOnChange(fn: (value: string | null) => void): void {\n this.onChange = fn;\n }\n\n /** @ignore */\n registerOnTouched(fn: () => void): void {\n this.onTouched = fn;\n }\n\n /**\n * Set the disabled state of the radio group.\n * @param isDisabled Whether the radio group is disabled.\n * @ignore\n */\n setDisabledState(isDisabled: boolean): void {\n this.disable.set(isDisabled);\n }\n\n setArrowNavigation(value: boolean): void {\n this.arrowNavigation.set(value);\n }\n\n isArrowNavigation(): boolean {\n return this.arrowNavigation();\n }\n\n /**\n * Marks the control touched when focus leaves the whole group (Base UI `RadioGroup` parity and the\n * ADR 0004 CVA strategy) — moving focus between items stays inside, so `relatedTarget` is checked.\n */\n protected onFocusOut(event: FocusEvent): void {\n const next = event.relatedTarget as Node | null;\n if (!this.elementRef.nativeElement.contains(next)) {\n this.formUi.markAsTouched();\n }\n }\n}\n\ninterface RdxRadioItemMetadata {\n [key: string]: unknown;\n disabled: boolean;\n value: string;\n}\n\nfunction isRadioItemMetadata(metadata: RdxCompositeMetadata): metadata is RdxCompositeMetadata<RdxRadioItemMetadata> {\n return typeof metadata['disabled'] === 'boolean' && typeof metadata['value'] === 'string';\n}\n","import { booleanAttribute, computed, Directive, effect, ElementRef, inject, input, Signal } from '@angular/core';\nimport { RdxCompositeItem } from '@radix-ng/primitives/composite';\nimport { BooleanInput, createContext } from '@radix-ng/primitives/core';\nimport { injectRadioRootContext } from './radio-root.directive';\n\nexport interface RadioItemContext {\n value: Signal<string>;\n checkedState: Signal<boolean>;\n disabledState: Signal<boolean>;\n readonlyState: Signal<boolean>;\n requiredState: Signal<boolean>;\n}\n\nconst itemContext = (): RadioItemContext => {\n const item = inject(RdxRadioItemDirective);\n\n return {\n value: item.value,\n checkedState: item.checkedState,\n disabledState: item.disabledState,\n readonlyState: item.readonlyState,\n requiredState: item.requiredState\n };\n};\n\nexport const [injectRadioItemContext, provideRadioItemContext] = createContext<RadioItemContext>(\n 'RadioItemContext',\n 'components/radio'\n);\n\n@Directive({\n selector: '[rdxRadioItem]',\n exportAs: 'rdxRadioItem',\n providers: [provideRadioItemContext(itemContext)],\n hostDirectives: [RdxCompositeItem],\n\n host: {\n '[attr.type]': 'isNativeButton ? \"button\" : undefined',\n role: 'radio',\n '[attr.aria-checked]': 'checkedState()',\n '[attr.aria-disabled]': 'disabledState() ? \"true\" : undefined',\n '[attr.aria-readonly]': 'readonlyState() ? \"true\" : undefined',\n '[attr.aria-required]': 'requiredState() ? \"true\" : undefined',\n '[attr.data-composite-item-active]': 'checkedState() ? \"\" : undefined',\n '[attr.data-checked]': 'checkedState() ? \"\" : undefined',\n '[attr.data-unchecked]': '!checkedState() ? \"\" : undefined',\n '[attr.data-disabled]': 'disabledState() ? \"\" : undefined',\n '[attr.data-readonly]': 'readonlyState() ? \"\" : undefined',\n '[attr.data-required]': 'requiredState() ? \"\" : undefined',\n '[attr.disabled]': 'isNativeButton && disabledState() ? \"\" : undefined',\n '(click)': 'onClick($event)',\n '(keydown)': 'onKeyDown($event)',\n '(keyup)': 'onKeyUp()',\n '(focus)': 'onFocus($event)'\n }\n})\nexport class RdxRadioItemDirective {\n private readonly rootContext = injectRadioRootContext();\n private readonly compositeItem = inject(RdxCompositeItem, { self: true });\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n\n readonly value = input.required<string>();\n\n readonly id = input<string>();\n\n readonly required = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n readonly disabled = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n /**\n * Whether the user should be unable to select this radio button. Bound in templates as\n * `readOnly` (Base UI spelling); the TS member stays `readonly` for cross-primitive consistency.\n */\n readonly readonly = input<boolean, BooleanInput>(false, { alias: 'readOnly', transform: booleanAttribute });\n\n /**\n * Whether the host is a native `<button>`. Detected from the host tag (unlike Base UI's explicit\n * `nativeButton` prop, the rendered element is statically known in Angular templates), and used to\n * apply `type=\"button\"` and the native `disabled` attribute.\n */\n protected readonly isNativeButton = this.elementRef.nativeElement.tagName === 'BUTTON';\n\n readonly disabledState = computed(() => this.rootContext.disabledState() || this.disabled());\n\n readonly readonlyState = computed(() => this.rootContext.readonly() || this.readonly());\n\n readonly requiredState = computed(() => this.rootContext.required() || this.required());\n\n readonly checkedState = computed(() => this.rootContext.value() === this.value());\n\n private readonly ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'] as const;\n\n constructor() {\n effect(() => {\n this.compositeItem.setMetadata({\n disabled: this.disabledState(),\n value: this.value()\n });\n });\n }\n\n /** @ignore */\n onClick(event?: Event) {\n if (!this.disabledState() && !this.readonlyState()) {\n this.rootContext.select(this.value(), event);\n }\n }\n\n /** @ignore */\n onKeyDown(event: Event): void {\n const keyEvent = event as KeyboardEvent;\n if (keyEvent.key === ' ') {\n this.onClick(keyEvent);\n return;\n }\n\n if (keyEvent.key === 'Enter') {\n keyEvent.preventDefault();\n return;\n }\n\n if (this.isAllowedArrowKey(keyEvent.key)) {\n this.rootContext.setArrowNavigation(true);\n }\n }\n\n /** @ignore */\n onKeyUp() {\n this.rootContext.setArrowNavigation(false);\n }\n\n /** @ignore */\n onFocus(event?: FocusEvent) {\n queueMicrotask(() => {\n if (this.rootContext.isArrowNavigation()) {\n this.rootContext.select(this.value(), event);\n this.rootContext.setArrowNavigation(false);\n }\n });\n }\n\n private isAllowedArrowKey(key: string): boolean {\n return (this.ARROW_KEYS as readonly string[]).includes(key);\n }\n}\n","import { booleanAttribute, computed, Directive, input } from '@angular/core';\nimport { BooleanInput } from '@radix-ng/primitives/core';\nimport { injectRadioItemContext } from './radio-item.directive';\n\n@Directive({\n selector: '[rdxRadioIndicator]',\n exportAs: 'rdxRadioIndicator',\n host: {\n '[attr.data-checked]': 'itemContext.checkedState() ? \"\" : undefined',\n '[attr.data-unchecked]': '!itemContext.checkedState() ? \"\" : undefined',\n '[attr.data-disabled]': 'itemContext.disabledState() ? \"\" : undefined',\n '[attr.data-readonly]': 'itemContext.readonlyState() ? \"\" : undefined',\n '[attr.data-required]': 'itemContext.requiredState() ? \"\" : undefined',\n '[attr.data-starting-style]': 'isVisible() ? \"\" : undefined',\n '[attr.data-ending-style]': '!isVisible() ? \"\" : undefined',\n '[style.display]': '!keepMounted() && !isVisible() ? \"none\" : null',\n '[style.pointer-events]': '\"none\"'\n }\n})\nexport class RdxRadioIndicatorDirective {\n protected readonly itemContext = injectRadioItemContext();\n\n /** Keep the indicator in the DOM when unchecked so CSS exit animations can play. */\n readonly keepMounted = input<boolean, BooleanInput>(false, { transform: booleanAttribute });\n\n protected readonly isVisible = computed(() => this.itemContext.checkedState());\n}\n","import { computed, Directive, effect, ElementRef, inject } from '@angular/core';\nimport { injectRadioItemContext } from './radio-item.directive';\nimport { injectRadioRootContext } from './radio-root.directive';\n\n/**\n * The hidden native radio input that mirrors the item state for form submission, native validation,\n * and `<label>` activation. Place it inside an `rdxRadioItem`.\n *\n * @see https://base-ui.com/react/components/radio\n */\n@Directive({\n selector: 'input[rdxRadioItemInput]',\n exportAs: 'rdxRadioItemInput',\n host: {\n type: 'radio',\n tabindex: '-1',\n 'aria-hidden': 'true',\n '[attr.name]': 'name()',\n '[attr.form]': 'form()',\n '[attr.required]': 'required() ? \"\" : undefined',\n '[attr.disabled]': 'disabled() ? \"\" : undefined',\n '[attr.checked]': 'checked() ? \"\" : undefined',\n '[checked]': 'checked()',\n '[attr.value]': 'value()',\n '(change)': 'onInputChange($event)',\n style: 'transform: translateX(-100%); position: absolute; pointer-events: none; opacity: 0; margin: 0; inset: 0;'\n }\n})\nexport class RdxRadioItemInputDirective {\n private readonly rootContext = injectRadioRootContext();\n private readonly itemContext = injectRadioItemContext();\n private readonly input = inject<ElementRef<HTMLInputElement>>(ElementRef).nativeElement;\n\n readonly name = computed(() => this.rootContext.name());\n readonly form = computed(() => this.rootContext.form());\n readonly value = computed(() => this.itemContext.value() || undefined);\n readonly checked = computed(() => this.itemContext.checkedState());\n readonly required = computed(() => this.itemContext.requiredState());\n readonly disabled = computed(() => this.itemContext.disabledState());\n\n constructor() {\n let isInitial = true;\n\n effect(() => {\n const checked = this.checked();\n\n if (isInitial) {\n isInitial = false;\n return;\n }\n\n if (checked) {\n this.input.dispatchEvent(new Event('input', { bubbles: true }));\n this.input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n });\n }\n\n /**\n * Selects this item when the native input is checked — covers `<label>` activation,\n * where clicking the label toggles the hidden radio input rather than the visible item.\n * `select()` is a no-op when the value is already current, so the programmatic\n * `change` dispatched above does not re-trigger selection.\n * @ignore\n */\n protected onInputChange(event: Event): void {\n if (this.input.checked) {\n this.rootContext.select(this.itemContext.value(), event);\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
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, createContext, 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 } 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';
@@ -69,6 +69,7 @@ const context$2 = () => {
69
69
  openMethod: context.openMethod,
70
70
  openInteractionType: context.openInteractionType,
71
71
  closeInteractionType: context.closeInteractionType,
72
+ ...formUiStateContext(context.formUi),
72
73
  disabled: context.disabled,
73
74
  readOnly: context.readOnly,
74
75
  required: context.required,
@@ -101,8 +102,9 @@ const context$2 = () => {
101
102
  };
102
103
  };
103
104
  const [injectSelectRootContext, provideSelectRootContext] = createContext('RdxSelectRootContext', 'components/select');
104
- class RdxSelectRoot {
105
+ class RdxSelectRoot extends RdxFormUiControlBase {
105
106
  constructor() {
107
+ super();
106
108
  this.contentId = injectId('rdx-select-content-');
107
109
  this.open = model(false, /* @ts-ignore */
108
110
  ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
@@ -256,6 +258,7 @@ class RdxSelectRoot {
256
258
  return false;
257
259
  }
258
260
  this.value.set(nextValue);
261
+ this.formUi.markDirty();
259
262
  return true;
260
263
  }
261
264
  setOpen(open, reason = 'none', event) {
@@ -282,7 +285,7 @@ class RdxSelectRoot {
282
285
  // New floating foundation (ADR 0015/0017) — the dismissal capability reads this shared context.
283
286
  provideFloatingTree(),
284
287
  provideFloatingRootContext(() => inject(RdxSelectRoot).floatingContext)
285
- ], exportAs: ["rdxSelectRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
288
+ ], exportAs: ["rdxSelectRoot"], usesInheritance: true, hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
286
289
  }
287
290
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectRoot, decorators: [{
288
291
  type: Directive,
@@ -1503,12 +1506,23 @@ class RdxSelectTrigger {
1503
1506
  disabled: () => this.isDisabled(),
1504
1507
  contentId: () => this.rootContext.contentId
1505
1508
  });
1506
- this.invalidState = computed(() => Boolean(this.fieldRootContext?.invalidState()), /* @ts-ignore */
1509
+ this.invalidState = computed(() => this.rootContext.invalidState() || Boolean(this.fieldRootContext?.invalidState()), /* @ts-ignore */
1507
1510
  ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
1511
+ /**
1512
+ * Tri-state *displayed* validity: when inside a `rdxFieldRoot` the field's gated `validState` is the
1513
+ * single source (so the field's `validationMode` keeps the trigger neutral until revealed), otherwise
1514
+ * the trigger's own binary invalidity.
1515
+ */
1516
+ this.displayValid = computed(() => this.fieldRootContext ? this.fieldRootContext.validState() : this.invalidState() ? false : true, /* @ts-ignore */
1517
+ ...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
1508
1518
  this.requiredState = computed(() => this.rootContext.required() || Boolean(this.fieldRootContext?.requiredState()), /* @ts-ignore */
1509
1519
  ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
1510
1520
  this.readOnlyState = computed(() => this.rootContext.readOnly(), /* @ts-ignore */
1511
1521
  ...(ngDevMode ? [{ debugName: "readOnlyState" }] : /* istanbul ignore next */ []));
1522
+ this.touchedState = computed(() => this.rootContext.touchedState() || Boolean(this.fieldRootContext?.touchedState()), /* @ts-ignore */
1523
+ ...(ngDevMode ? [{ debugName: "touchedState" }] : /* istanbul ignore next */ []));
1524
+ this.dirtyState = computed(() => this.rootContext.dirtyState() || Boolean(this.fieldRootContext?.dirtyState()), /* @ts-ignore */
1525
+ ...(ngDevMode ? [{ debugName: "dirtyState" }] : /* istanbul ignore next */ []));
1512
1526
  this.filledState = computed(() => !this.rootContext.isEmptyModelValue() || Boolean(this.fieldRootContext?.filledState()), /* @ts-ignore */
1513
1527
  ...(ngDevMode ? [{ debugName: "filledState" }] : /* istanbul ignore next */ []));
1514
1528
  this.focusedState = computed(() => Boolean(this.fieldRootContext?.focusedState()), /* @ts-ignore */
@@ -1597,9 +1611,11 @@ class RdxSelectTrigger {
1597
1611
  onBlur() {
1598
1612
  this.fieldRootContext?.setFocused(false);
1599
1613
  this.fieldRootContext?.setTouched(true);
1614
+ // Notify Signal Forms (touched model + touch output) the control was touched.
1615
+ this.rootContext.markAsTouched();
1600
1616
  }
1601
1617
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1602
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectTrigger, isStandalone: true, selector: "button[rdxSelectTrigger]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "type": "button" }, listeners: { "click": "onClickHandler($event)", "pointerdown": "onPointerDown($event)", "pointerup": "onPointerUp($event)", "keydown": "onKeydown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.id": "id()", "attr.aria-describedby": "describedBy()", "attr.aria-controls": "triggerInteraction.ariaControls()", "attr.aria-invalid": "invalidState() ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-readonly": "readOnlyState() ? \"true\" : undefined", "attr.disabled": "isDisabled() ? \"\" : undefined", "dir": "rootContext.dir()", "attr.data-popup-open": "triggerInteraction.dataPopupOpen()", "attr.data-placeholder": "dataAttr(rootContext.isEmptyModelValue())", "attr.data-disabled": "dataAttr(isDisabled())", "attr.data-invalid": "dataAttr(invalidState())", "attr.data-valid": "dataAttr(!invalidState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-readonly": "dataAttr(readOnlyState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
1618
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectTrigger, isStandalone: true, selector: "button[rdxSelectTrigger]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "type": "button" }, listeners: { "click": "onClickHandler($event)", "pointerdown": "onPointerDown($event)", "pointerup": "onPointerUp($event)", "keydown": "onKeydown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.id": "id()", "attr.aria-describedby": "describedBy()", "attr.aria-controls": "triggerInteraction.ariaControls()", "attr.aria-invalid": "displayValid() === false ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-readonly": "readOnlyState() ? \"true\" : undefined", "attr.disabled": "isDisabled() ? \"\" : undefined", "dir": "rootContext.dir()", "attr.data-popup-open": "triggerInteraction.dataPopupOpen()", "attr.data-placeholder": "dataAttr(rootContext.isEmptyModelValue())", "attr.data-disabled": "dataAttr(isDisabled())", "attr.data-invalid": "dataAttr(displayValid() === false)", "attr.data-valid": "dataAttr(displayValid() === true)", "attr.data-required": "dataAttr(requiredState())", "attr.data-readonly": "dataAttr(readOnlyState())", "attr.data-touched": "dataAttr(touchedState())", "attr.data-dirty": "dataAttr(dirtyState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
1603
1619
  }
1604
1620
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectTrigger, decorators: [{
1605
1621
  type: Directive,
@@ -1612,7 +1628,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1612
1628
  '[attr.id]': 'id()',
1613
1629
  '[attr.aria-describedby]': 'describedBy()',
1614
1630
  '[attr.aria-controls]': 'triggerInteraction.ariaControls()',
1615
- '[attr.aria-invalid]': 'invalidState() ? "true" : undefined',
1631
+ '[attr.aria-invalid]': 'displayValid() === false ? "true" : undefined',
1616
1632
  '[attr.aria-required]': 'requiredState() ? "true" : undefined',
1617
1633
  '[attr.aria-readonly]': 'readOnlyState() ? "true" : undefined',
1618
1634
  '[attr.disabled]': 'isDisabled() ? "" : undefined',
@@ -1620,10 +1636,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
1620
1636
  '[attr.data-popup-open]': 'triggerInteraction.dataPopupOpen()',
1621
1637
  '[attr.data-placeholder]': 'dataAttr(rootContext.isEmptyModelValue())',
1622
1638
  '[attr.data-disabled]': 'dataAttr(isDisabled())',
1623
- '[attr.data-invalid]': 'dataAttr(invalidState())',
1624
- '[attr.data-valid]': 'dataAttr(!invalidState())',
1639
+ '[attr.data-invalid]': 'dataAttr(displayValid() === false)',
1640
+ '[attr.data-valid]': 'dataAttr(displayValid() === true)',
1625
1641
  '[attr.data-required]': 'dataAttr(requiredState())',
1626
1642
  '[attr.data-readonly]': 'dataAttr(readOnlyState())',
1643
+ '[attr.data-touched]': 'dataAttr(touchedState())',
1644
+ '[attr.data-dirty]': 'dataAttr(dirtyState())',
1627
1645
  '[attr.data-filled]': 'dataAttr(filledState())',
1628
1646
  '[attr.data-focused]': 'dataAttr(focusedState())',
1629
1647
  '(click)': 'onClickHandler($event)',