@radix-ng/primitives 1.0.0-beta.3 → 1.0.0-beta.5

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 (118) hide show
  1. package/README.md +1 -1
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +5 -3
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +3 -2
  5. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-autocomplete.mjs +617 -659
  7. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-calendar.mjs +5 -3
  9. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-checkbox.mjs +33 -18
  11. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-combobox.mjs +1305 -572
  13. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-config.mjs +13 -4
  15. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-context-menu.mjs +51 -10
  17. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-core.mjs +1352 -64
  19. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-date-field.mjs +5 -3
  21. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-dialog.mjs +290 -120
  23. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-direction-provider.mjs +70 -0
  25. package/fesm2022/radix-ng-primitives-direction-provider.mjs.map +1 -0
  26. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +519 -184
  27. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-drawer.mjs +3 -3
  29. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-editable.mjs +12 -7
  31. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-field.mjs +3 -2
  33. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +803 -0
  35. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -0
  36. package/fesm2022/radix-ng-primitives-focus-scope.mjs +305 -70
  37. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-menu.mjs +893 -289
  39. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-menubar.mjs +32 -4
  41. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  42. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +144 -159
  43. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  44. package/fesm2022/radix-ng-primitives-number-field.mjs +7 -2
  45. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  46. package/fesm2022/radix-ng-primitives-popover.mjs +284 -212
  47. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  48. package/fesm2022/radix-ng-primitives-popper.mjs +94 -51
  49. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  50. package/fesm2022/radix-ng-primitives-presence.mjs +1 -1
  51. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  52. package/fesm2022/radix-ng-primitives-preview-card.mjs +141 -173
  53. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
  54. package/fesm2022/radix-ng-primitives-radio.mjs +19 -14
  55. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-roving-focus.mjs +4 -2
  57. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  58. package/fesm2022/radix-ng-primitives-scroll-area.mjs +5 -4
  59. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-select.mjs +241 -164
  61. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-slider.mjs +262 -29
  63. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-stepper.mjs +16 -10
  65. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  66. package/fesm2022/radix-ng-primitives-switch.mjs +10 -5
  67. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  68. package/fesm2022/radix-ng-primitives-tabs.mjs +15 -10
  69. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  70. package/fesm2022/radix-ng-primitives-time-field.mjs +5 -3
  71. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  72. package/fesm2022/radix-ng-primitives-toast.mjs +15 -36
  73. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -1
  74. package/fesm2022/radix-ng-primitives-toggle-group.mjs +14 -7
  75. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  76. package/fesm2022/radix-ng-primitives-toggle.mjs +12 -6
  77. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  78. package/fesm2022/radix-ng-primitives-toolbar.mjs +5 -3
  79. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-tooltip.mjs +251 -143
  81. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  82. package/package.json +10 -1
  83. package/types/radix-ng-primitives-accordion.d.ts +4 -3
  84. package/types/radix-ng-primitives-autocomplete.d.ts +217 -152
  85. package/types/radix-ng-primitives-calendar.d.ts +5 -3
  86. package/types/radix-ng-primitives-checkbox.d.ts +27 -15
  87. package/types/radix-ng-primitives-combobox.d.ts +672 -283
  88. package/types/radix-ng-primitives-config.d.ts +1 -1
  89. package/types/radix-ng-primitives-context-menu.d.ts +15 -5
  90. package/types/radix-ng-primitives-core.d.ts +764 -14
  91. package/types/radix-ng-primitives-date-field.d.ts +3 -2
  92. package/types/radix-ng-primitives-dialog.d.ts +88 -32
  93. package/types/radix-ng-primitives-direction-provider.d.ts +41 -0
  94. package/types/radix-ng-primitives-dismissable-layer.d.ts +147 -99
  95. package/types/radix-ng-primitives-editable.d.ts +11 -5
  96. package/types/radix-ng-primitives-field.d.ts +1 -0
  97. package/types/radix-ng-primitives-floating-focus-manager.d.ts +272 -0
  98. package/types/radix-ng-primitives-focus-scope.d.ts +132 -1
  99. package/types/radix-ng-primitives-menu.d.ts +192 -103
  100. package/types/radix-ng-primitives-navigation-menu.d.ts +37 -75
  101. package/types/radix-ng-primitives-number-field.d.ts +8 -3
  102. package/types/radix-ng-primitives-popover.d.ts +71 -92
  103. package/types/radix-ng-primitives-popper.d.ts +39 -9
  104. package/types/radix-ng-primitives-preview-card.d.ts +39 -72
  105. package/types/radix-ng-primitives-radio.d.ts +13 -6
  106. package/types/radix-ng-primitives-roving-focus.d.ts +7 -6
  107. package/types/radix-ng-primitives-scroll-area.d.ts +2 -2
  108. package/types/radix-ng-primitives-select.d.ts +142 -109
  109. package/types/radix-ng-primitives-slider.d.ts +64 -12
  110. package/types/radix-ng-primitives-stepper.d.ts +15 -7
  111. package/types/radix-ng-primitives-switch.d.ts +10 -4
  112. package/types/radix-ng-primitives-tabs.d.ts +12 -6
  113. package/types/radix-ng-primitives-time-field.d.ts +3 -2
  114. package/types/radix-ng-primitives-toast.d.ts +7 -7
  115. package/types/radix-ng-primitives-toggle-group.d.ts +15 -8
  116. package/types/radix-ng-primitives-toggle.d.ts +10 -3
  117. package/types/radix-ng-primitives-toolbar.d.ts +3 -2
  118. package/types/radix-ng-primitives-tooltip.d.ts +61 -80
@@ -0,0 +1,272 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { InjectionToken, Provider, Signal } from '@angular/core';
3
+ import { RdxFloatingTree, BooleanInput } from '@radix-ng/primitives/core';
4
+ import * as i1 from '@radix-ng/primitives/focus-scope';
5
+
6
+ /**
7
+ * How a popup was opened / closed (Base UI `InteractionType`). `null` = a **programmatic** open (prefer
8
+ * the previously-focused element); `''` = an **unknown** interaction. The two are deliberately distinct
9
+ * (Base UI keys `preferPreviousFocus = openInteractionType == null` off exactly this).
10
+ */
11
+ type RdxInteractionType = 'mouse' | 'touch' | 'pen' | 'keyboard' | '' | null;
12
+ /** A focus target: an element, a getter, `null`, or `undefined` from a policy callback. */
13
+ type RdxFocusTarget = HTMLElement | (() => HTMLElement | null) | null | undefined;
14
+ /** `initialFocus` policy (ADR 0017 §2) — a target, `false`, or an open-interaction callback. */
15
+ type RdxInitialFocus = RdxFocusTarget | false | ((openInteractionType: RdxInteractionType) => RdxFocusTarget | false);
16
+ /** `returnFocus` policy (ADR 0017 §2) — a target/boolean or a callback receiving the **close** interaction type. */
17
+ type RdxReturnFocus = RdxFocusTarget | boolean | ((closeInteractionType: RdxInteractionType) => RdxFocusTarget | boolean | undefined);
18
+ /** Normalizes a DOM event into Base UI-like interaction intent for focus policy decisions. */
19
+ declare function getInteractionTypeFromEvent(event?: Event): RdxInteractionType;
20
+ /** Resolves an {@link RdxFocusTarget} (element | getter | null) to a concrete element. */
21
+ declare function resolveFocusTarget(target: RdxFocusTarget): HTMLElement | null | undefined;
22
+ /**
23
+ * Resolves an {@link RdxInitialFocus} policy against how the popup opened.
24
+ */
25
+ declare function resolveInitialFocus(policy: RdxInitialFocus, openInteractionType: RdxInteractionType): HTMLElement | null | false | undefined;
26
+ /**
27
+ * Resolves an {@link RdxReturnFocus} policy against how the popup closed. `false` = do not return focus;
28
+ * `true` = the default (return to the previously-focused element); an element = return there.
29
+ */
30
+ declare function resolveReturnFocus(policy: RdxReturnFocus, closeInteractionType: RdxInteractionType): HTMLElement | boolean | null | undefined;
31
+ /**
32
+ * DI seam for a **composing primitive** (Dialog / Popover / Menu) to drive the manager's gates from its
33
+ * own root context instead of template-bound inputs (which a primitive cannot set). Each field falls
34
+ * back to the manager's input, then to the documented default. Mirrors {@link RdxFocusScopeConfig}.
35
+ */
36
+ interface RdxFloatingFocusManagerConfig {
37
+ modal?: () => boolean;
38
+ inert?: () => boolean;
39
+ enabled?: () => boolean;
40
+ closeOnFocusOut?: () => boolean;
41
+ initialFocus?: () => RdxInitialFocus;
42
+ returnFocus?: () => RdxReturnFocus;
43
+ restoreFocus?: () => boolean | 'popup';
44
+ previousFocusableElement?: () => RdxFocusTarget;
45
+ nextFocusableElement?: () => RdxFocusTarget;
46
+ beforeContentFocusGuardRef?: () => RdxFocusGuardRef | null | undefined;
47
+ externalTree?: () => RdxFloatingTree | null | undefined;
48
+ getInsideElements?: () => Array<Element | null | undefined>;
49
+ openInteractionType?: () => RdxInteractionType;
50
+ closeInteractionType?: () => RdxInteractionType;
51
+ }
52
+ type RdxFocusGuardRef = ((element: HTMLSpanElement | null) => void) | {
53
+ set: (element: HTMLSpanElement | null) => void;
54
+ };
55
+ declare const RDX_FLOATING_FOCUS_MANAGER_CONFIG: InjectionToken<RdxFloatingFocusManagerConfig>;
56
+ /** Provides a {@link RdxFloatingFocusManagerConfig} for an enclosing primitive's focus manager. */
57
+ declare function provideFloatingFocusManagerConfig(factory: () => RdxFloatingFocusManagerConfig): Provider;
58
+ /**
59
+ * `RdxFloatingFocusManager` (ADR 0017 Phase 1b skeleton) — the Angular counterpart of Base UI's
60
+ * `FloatingFocusManager`. It is a **coordinator** that composes three low-level focus parts (it never
61
+ * inherits them, which would re-fuse trap + popup policy): the **reworked {@link RdxFocusScope}** (the
62
+ * trap, via `hostDirectives`), the portal-focus bridge, and owner-`Document` guards. Per ADR 0017 §1/§2
63
+ * its policies are **independent**, none derived from `modal`.
64
+ *
65
+ * **This skeleton wires the composition + lifecycle gates:**
66
+ * - `enabled` — the manager's active-ness (`mounted && !hover-open`). When off, **no trap** (and, later,
67
+ * no aria-hidden / no marker — Phase 2).
68
+ * - `modal` → `RdxFocusScope.trapped`: the effective trap is `enabled() && modal()`, pushed into the
69
+ * composed focus scope through its config token (the composition seam).
70
+ * - `loop` is forwarded to `RdxFocusScope`.
71
+ * - `initialFocus` / `returnFocus` are **orchestrated** here (the §2 policy contract, incl. the
72
+ * interaction-type callback forms): `initialFocus` via the scope's `mountAutoFocus` hook, `returnFocus`
73
+ * via the scope's `returnFocus` config seam (resolved at the scope's queued post-unmount frame).
74
+ */
75
+ declare class RdxFloatingFocusManager {
76
+ /** Manager active-ness (ADR 0017 §2): the popup is mounted **and** not hover-opened. */
77
+ readonly enabled: _angular_core.InputSignalWithTransform<boolean | undefined, BooleanInput>;
78
+ /** Modal popup → focus trap. Combined with `enabled` to drive the composed `RdxFocusScope`. */
79
+ readonly modal: _angular_core.InputSignalWithTransform<boolean | undefined, BooleanInput>;
80
+ /**
81
+ * Whether outside elements receive the real `inert` attribute. Defaults to the effective `modal` value,
82
+ * but primitives can split focus trapping from pointer/AT isolation (Base UI `modal="trap-focus"`).
83
+ */
84
+ readonly inert: _angular_core.InputSignalWithTransform<boolean | undefined, BooleanInput>;
85
+ /** Where focus goes when the popup opens (ADR 0017 §2). */
86
+ readonly initialFocus: _angular_core.InputSignal<RdxInitialFocus>;
87
+ /** Where focus returns when the popup closes (ADR 0017 §2). */
88
+ readonly returnFocus: _angular_core.InputSignal<RdxReturnFocus>;
89
+ /**
90
+ * Restores focus inside the floating tree when the currently focused inside element is removed and
91
+ * the browser drops focus onto `<body>` (Base UI `restoreFocus`). `true` restores to the last
92
+ * available tabbable candidate, `'popup'` restores to the popup container.
93
+ */
94
+ readonly restoreFocus: _angular_core.InputSignal<boolean | "popup" | undefined>;
95
+ /** Overrides where backward tabbing out of a non-modal portaled popup lands. */
96
+ readonly previousFocusableElement: _angular_core.InputSignal<RdxFocusTarget>;
97
+ /** Overrides where forward tabbing out of a non-modal portaled popup lands. */
98
+ readonly nextFocusableElement: _angular_core.InputSignal<RdxFocusTarget>;
99
+ /** Optional callback/signal that receives the leading content focus guard. */
100
+ readonly beforeContentFocusGuardRef: _angular_core.InputSignal<RdxFocusGuardRef | null | undefined>;
101
+ /** Explicit floating tree for detached sibling composition (Base UI `externalTree`). */
102
+ readonly externalTree: _angular_core.InputSignal<RdxFloatingTree | null | undefined>;
103
+ /** Additional elements treated as inside the floating subtree (Base UI `getInsideElements`). */
104
+ readonly getInsideElements: _angular_core.InputSignal<(() => Array<Element | null | undefined>) | undefined>;
105
+ /**
106
+ * Whether a **non-modal** popup closes when focus leaves to an unrelated node (Base UI
107
+ * `closeOnFocusOut`, default `true`; Dialog sets it to `!disablePointerDismissal`). Modal popups
108
+ * never close on focus-out (the trap keeps focus in).
109
+ */
110
+ readonly closeOnFocusOut: _angular_core.InputSignalWithTransform<boolean | undefined, BooleanInput>;
111
+ /**
112
+ * Emitted when focus leaves a non-modal popup to a node **unrelated** to the floating tree (ADR 0017
113
+ * §3) — the consumer should close the popup. This is the focus-manager's focus-out close (it reads
114
+ * the shared tree), replacing the dismissal capability's focus-out at the ADR 0015 Phase-4 cutover.
115
+ */
116
+ readonly focusOut: _angular_core.OutputEmitterRef<FocusEvent>;
117
+ /** Optional DI config a composing primitive provides to drive the gates (input wins over config). */
118
+ private readonly config;
119
+ /** Effective gates: `input ?? config ?? default`. */
120
+ readonly effectiveEnabled: _angular_core.Signal<boolean>;
121
+ readonly effectiveModal: _angular_core.Signal<boolean>;
122
+ readonly effectiveInert: _angular_core.Signal<boolean>;
123
+ readonly effectiveCloseOnFocusOut: _angular_core.Signal<boolean>;
124
+ readonly effectiveInitialFocus: _angular_core.Signal<false | HTMLElement | ((openInteractionType: RdxInteractionType) => RdxFocusTarget | false) | null>;
125
+ readonly effectiveReturnFocus: _angular_core.Signal<boolean | HTMLElement | ((closeInteractionType: RdxInteractionType) => RdxFocusTarget | boolean | undefined)>;
126
+ readonly effectiveRestoreFocus: _angular_core.Signal<boolean | "popup">;
127
+ readonly effectivePreviousFocusableElement: _angular_core.Signal<RdxFocusTarget>;
128
+ readonly effectiveNextFocusableElement: _angular_core.Signal<RdxFocusTarget>;
129
+ readonly effectiveBeforeContentFocusGuardRef: _angular_core.Signal<RdxFocusGuardRef | null | undefined>;
130
+ readonly effectiveExternalTree: _angular_core.Signal<RdxFloatingTree | null>;
131
+ readonly insideElements: _angular_core.Signal<Element[]>;
132
+ readonly effectiveOpenInteractionType: _angular_core.Signal<RdxInteractionType>;
133
+ readonly effectiveCloseInteractionType: _angular_core.Signal<RdxInteractionType>;
134
+ /** The effective trap state the composed `RdxFocusScope` reads via its config token. */
135
+ readonly trapped: _angular_core.Signal<boolean>;
136
+ private readonly focusScopeConfig;
137
+ private readonly host;
138
+ private readonly isBrowser;
139
+ /** The shared per-popup context (open / triggers / elements), if a primitive root provides one. */
140
+ private readonly rootContext;
141
+ /** The registration handle for this node, used to read the shared tree (ancestors / descendants). */
142
+ private readonly registration;
143
+ private readonly _interactionType;
144
+ /** How the popup was most recently interacted with — fed to the initial/return focus policy callbacks. */
145
+ readonly interactionType: _angular_core.Signal<RdxInteractionType>;
146
+ constructor();
147
+ /** Records the most recent open/close interaction (pointer type or keyboard) for the focus policies. */
148
+ private trackInteractionType;
149
+ /**
150
+ * Initial-focus orchestration (ADR 0017 §2). The manager owns the focus *policy*; it intercepts the
151
+ * composed {@link RdxFocusScope}'s preventable `mountAutoFocus` (its designed extension point) and
152
+ * applies the `initialFocus` policy, falling back to the scope's first-tabbable default when the
153
+ * policy is `null`. (`returnFocus` is orchestrated separately via the config seam — see
154
+ * {@link resolveReturnFocusTarget} — because it must run during the scope's queued *post-unmount* frame.)
155
+ */
156
+ private wireFocusOrchestration;
157
+ /**
158
+ * Resolves the {@link returnFocus} policy against the **close** interaction type for the composed
159
+ * focus scope to apply at unmount (ADR 0017 §2). Mirrors Base UI's `getReturnElement`:
160
+ * - `false` → `false` (the scope suppresses return-focus);
161
+ * - `true` / `null` → `undefined` (the scope's default — return to the element focused before mount);
162
+ * - an element (direct or from a callback) → that element (returned **explicitly**, bypassing the
163
+ * "focus moved elsewhere" guard).
164
+ */
165
+ private resolveReturnFocusTarget;
166
+ /**
167
+ * Base UI's `defaultInitialFocus`: on a **touch** open, focus the popup itself instead of its first
168
+ * tabbable control, so a soft keyboard (Android) does not pop up over the popup. Any other interaction
169
+ * returns `null`, keeping the focus scope's first-tabbable default. The popup is made programmatically
170
+ * focusable (`tabindex="-1"`) if it isn't already.
171
+ */
172
+ private defaultInitialFocus;
173
+ /**
174
+ * Manager-owned post-open fallback. Some popup types open from a trigger event that fired before the
175
+ * manager existed, and some policies resolve their final target only after a couple of renders. Re-run
176
+ * the initial-focus policy for a few animation frames while the popup is open so the target can settle.
177
+ */
178
+ private wireInitialFocusFallback;
179
+ private scheduleInitialFocusFallback;
180
+ private applyInitialFocusFallback;
181
+ /**
182
+ * Close-on-focus-out (ADR 0017 §3): a **non-modal** active popup closes when focus moves to a node
183
+ * unrelated to the floating tree — not the popup, its trigger(s), a focus guard, or an ancestor /
184
+ * descendant popup — and not during a pointer press (a drag must not close it). Mirrors Base UI's
185
+ * `FloatingFocusManager` `!modal` branch (`movedToUnrelatedNode`).
186
+ */
187
+ private wireCloseOnFocusOut;
188
+ /**
189
+ * Restore focus when an inside focused element disappears and the document focus falls back to
190
+ * `<body>`. This mirrors the practical Base UI `restoreFocus` path used by Select while keeping the
191
+ * broader portal-guard navigation separate.
192
+ */
193
+ private wireRestoreFocus;
194
+ /**
195
+ * Portal focus bridge (Base UI inside guards): when focus reaches the hidden guards around portaled
196
+ * content, redirect it either back inside the popup or to the configured neighboring tabbable node.
197
+ */
198
+ private wirePortalFocusBridge;
199
+ /**
200
+ * The marker keep-set is intentionally narrow: the popup/focus host only. Own sibling roots such as a
201
+ * user backdrop are DOM-footprint bookkeeping, not marker keep-set members.
202
+ */
203
+ private avoidElements;
204
+ private isFloatingOpen;
205
+ /** Whether `relatedTarget` is inside the popup, its trigger(s), or an ancestor / descendant popup. */
206
+ private isRelatedTargetInside;
207
+ private contextContains;
208
+ private currentFloatingNode;
209
+ private getTabbableContent;
210
+ private portalFocusContainer;
211
+ private defaultPreviousFocusable;
212
+ private defaultNextFocusable;
213
+ private setBeforeContentFocusGuardRef;
214
+ private isFromOutsideFocusGuard;
215
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RdxFloatingFocusManager, never>;
216
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxFloatingFocusManager, "[rdxFloatingFocusManager]", ["rdxFloatingFocusManager"], { "enabled": { "alias": "enabled"; "required": false; "isSignal": true; }; "modal": { "alias": "modal"; "required": false; "isSignal": true; }; "inert": { "alias": "inert"; "required": false; "isSignal": true; }; "initialFocus": { "alias": "initialFocus"; "required": false; "isSignal": true; }; "returnFocus": { "alias": "returnFocus"; "required": false; "isSignal": true; }; "restoreFocus": { "alias": "restoreFocus"; "required": false; "isSignal": true; }; "previousFocusableElement": { "alias": "previousFocusableElement"; "required": false; "isSignal": true; }; "nextFocusableElement": { "alias": "nextFocusableElement"; "required": false; "isSignal": true; }; "beforeContentFocusGuardRef": { "alias": "beforeContentFocusGuardRef"; "required": false; "isSignal": true; }; "externalTree": { "alias": "externalTree"; "required": false; "isSignal": true; }; "getInsideElements": { "alias": "getInsideElements"; "required": false; "isSignal": true; }; "closeOnFocusOut": { "alias": "closeOnFocusOut"; "required": false; "isSignal": true; }; }, { "focusOut": "focusOut"; }, never, never, true, [{ directive: typeof i1.RdxFocusScope; inputs: { "loop": "loop"; }; outputs: {}; }]>;
217
+ }
218
+
219
+ /** The neutral "outside the active floating layer" marker (Base UI `data-base-ui-inert`). */
220
+ declare const RDX_FLOATING_MARKER = "data-rdx-floating-inert";
221
+
222
+ interface RdxTriggerInteractionOptions {
223
+ trigger: () => HTMLElement;
224
+ open: () => boolean;
225
+ activeTrigger?: () => HTMLElement | null | undefined;
226
+ disabled?: () => boolean;
227
+ contentId?: () => string | null | undefined;
228
+ }
229
+ interface RdxTriggerInteraction {
230
+ readonly lastPointerType: Signal<string>;
231
+ readonly isActive: Signal<boolean>;
232
+ readonly isInactive: Signal<boolean>;
233
+ readonly dataState: Signal<'open' | 'closed'>;
234
+ readonly dataPopupOpen: Signal<'' | undefined>;
235
+ readonly ariaControls: Signal<string | undefined>;
236
+ readonly ariaExpanded: Signal<boolean>;
237
+ readonly disabled: Signal<boolean>;
238
+ recordPointerDown: (event: PointerEvent) => void;
239
+ clickInteractionType: (event: MouseEvent) => RdxInteractionType;
240
+ }
241
+ interface RdxTriggerFocusGuardOptions {
242
+ trigger: () => HTMLElement;
243
+ close: (event: FocusEvent) => void;
244
+ beforeContentFocusGuard?: () => HTMLElement | null | undefined;
245
+ contentId?: () => string | null | undefined;
246
+ enabled?: () => boolean;
247
+ popupElement?: () => HTMLElement | null | undefined;
248
+ }
249
+ interface RdxTriggerFocusGuardAnchorOptions {
250
+ trigger: () => HTMLElement;
251
+ contentId: () => string | null | undefined;
252
+ enabled?: () => boolean;
253
+ }
254
+ /**
255
+ * Shared trigger state for floating primitives. It intentionally stays small: each primitive still owns
256
+ * its open/close business rules, while this layer normalizes active-trigger state and the open method
257
+ * signal that must be captured before the popup/focus manager mounts.
258
+ */
259
+ declare function createRdxTriggerInteraction(options: RdxTriggerInteractionOptions): RdxTriggerInteraction;
260
+ /**
261
+ * Adds Base UI's trigger-side focus guards for portaled content. The content guards remain owned by
262
+ * `RdxFloatingFocusManager`; these guards bridge tab order from the trigger into the portal and close the
263
+ * popup when focus leaves past either trigger-side boundary.
264
+ */
265
+ declare function useTriggerFocusGuards(options: RdxTriggerFocusGuardOptions): void;
266
+ /**
267
+ * Backwards-compatible aria-owns-only bridge for modal popups that do not need trigger-side tab guards.
268
+ */
269
+ declare function useTriggerFocusGuardAnchor(options: RdxTriggerFocusGuardAnchorOptions): void;
270
+
271
+ export { RDX_FLOATING_FOCUS_MANAGER_CONFIG, RDX_FLOATING_MARKER, RdxFloatingFocusManager, createRdxTriggerInteraction, getInteractionTypeFromEvent, provideFloatingFocusManagerConfig, resolveFocusTarget, resolveInitialFocus, resolveReturnFocus, useTriggerFocusGuardAnchor, useTriggerFocusGuards };
272
+ export type { RdxFloatingFocusManagerConfig, RdxFocusGuardRef, RdxFocusTarget, RdxInitialFocus, RdxInteractionType, RdxReturnFocus, RdxTriggerFocusGuardAnchorOptions, RdxTriggerFocusGuardOptions, RdxTriggerInteraction, RdxTriggerInteractionOptions };
@@ -3,6 +3,49 @@ import { WritableSignal, Signal, InjectionToken, Provider } from '@angular/core'
3
3
  import * as _radix_ng_primitives_core from '@radix-ng/primitives/core';
4
4
  import { BooleanInput } from '@radix-ng/primitives/core';
5
5
 
6
+ /** Marks the leading / trailing focus-guard spans (Base UI `data-base-ui-focus-guard`). */
7
+ declare const FOCUS_GUARD_ATTR = "data-rdx-focus-guard";
8
+ /** Visually-hidden, off-flow style for a focus guard / `aria-owns` anchor (Base UI `visuallyHidden`). */
9
+ declare const FOCUS_GUARD_STYLE: Partial<CSSStyleDeclaration>;
10
+ /**
11
+ * Creates a visually-hidden, **tabbable** focus-guard `<span>` — the Angular counterpart of Base UI's
12
+ * `FocusGuard`. The portal-focus bridge places one before and one after the portal content so a Tab into
13
+ * (or out of) the portal lands on a guard, which then redirects focus to the right boundary.
14
+ */
15
+ declare function createFocusGuard(ownerDocument: Document): HTMLSpanElement;
16
+ /**
17
+ * Creates a visually-hidden `<span aria-owns="…">` that links the portal node into the trigger's tab /
18
+ * AT order (Base UI's single `aria-owns` anchor). The manager places it next to the trigger.
19
+ */
20
+ declare function createAriaOwnsAnchor(ownerDocument: Document, portalId: string): HTMLSpanElement;
21
+ /**
22
+ * Makes every tabbable descendant of `container` **non-tabbable** (`tabindex="-1"`), saving each one's
23
+ * original tabindex so {@link enableFocusInside} can restore it. Base UI `disableFocusInside`: a
24
+ * non-modal portal keeps its content untabbable until focus is actually inside it, so a Tab from the
25
+ * trigger steps onto the guard instead of jumping into the content.
26
+ */
27
+ declare function disableFocusInside(container: HTMLElement): void;
28
+ /** Restores the tabbability that {@link disableFocusInside} suspended. Base UI `enableFocusInside`. */
29
+ declare function enableFocusInside(container: HTMLElement): void;
30
+ /**
31
+ * Whether a focus event crossed the `container` boundary — its `relatedTarget` (the other side of the
32
+ * focus move) is `null` or outside `container` (Base UI `isOutsideEvent`). Shadow-DOM-aware via
33
+ * {@link composedContains}.
34
+ */
35
+ declare function isOutsideEvent(event: FocusEvent, container: Element): boolean;
36
+ /**
37
+ * The portal-focus bridge's **tabbability toggle** (Base UI `FloatingPortal` capture-phase `onFocus`).
38
+ * While `portalNode` is mounted (and `enabled`), it makes the portal content tabbable **only when focus
39
+ * is inside it**: focus entering from outside re-enables tabbability, focus leaving to outside disables
40
+ * it again. Listens on the **capture** phase so it settles before the focus manager's guards react.
41
+ *
42
+ * Must be called in an injection context. The initial disable-on-mount and the guard-span placement are
43
+ * the manager's responsibility (Phase 1b); this owns only the dynamic in/out toggle.
44
+ */
45
+ declare function useFocusGuardsTabbability(portalNode: () => HTMLElement | null, options?: {
46
+ enabled?: () => boolean;
47
+ }): void;
48
+
6
49
  interface FocusScopeAPI {
7
50
  paused: WritableSignal<boolean>;
8
51
  pause(): void;
@@ -23,6 +66,8 @@ declare class RdxFocusScope {
23
66
  private readonly destroyRef;
24
67
  private readonly elementRef;
25
68
  private readonly config;
69
+ /** The host's owner `Document` — all focus listeners / reads are scoped here, never global `document`. */
70
+ private readonly ownerDocument;
26
71
  /**
27
72
  * When `true`, tabbing from last item will focus first tabbable
28
73
  * and shift+tab from first item will focus last tababble.
@@ -59,6 +104,15 @@ declare class RdxFocusScope {
59
104
  readonly focusScope: FocusScopeAPI;
60
105
  private alive;
61
106
  constructor();
107
+ /**
108
+ * Whether the interaction that unmounted this scope already moved focus to a legitimate element
109
+ * **outside** it — e.g. an outside press onto an interactive control in a non-modal layer (ADR 0017
110
+ * §2, finding #3). Returning focus to the previously-focused element would then *steal* it back from
111
+ * what the user just acted on. Focus that fell to `<body>` / `null` (a backdrop press, Escape, or the
112
+ * focused element being removed) is **not** "moved" — return focus normally so keyboard users land
113
+ * back on the trigger. The page never scroll-jumps either way: {@link focus} uses `preventScroll`.
114
+ */
115
+ private shouldPreserveMovedFocus;
62
116
  handleKeyDown(event: KeyboardEvent): void;
63
117
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<RdxFocusScope, never>;
64
118
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxFocusScope, "[rdxFocusScope]", never, { "loop": { "alias": "loop"; "required": false; "isSignal": true; }; "trapped": { "alias": "trapped"; "required": false; "isSignal": true; }; }, { "mountAutoFocus": "mountAutoFocus"; "unmountAutoFocus": "unmountAutoFocus"; }, never, never, true, never>;
@@ -66,9 +120,86 @@ declare class RdxFocusScope {
66
120
 
67
121
  type RdxFocusScopeConfig = {
68
122
  trapped: Signal<boolean>;
123
+ /**
124
+ * Optional return-focus policy override (ADR 0017 `returnFocus`), resolved by an enclosing
125
+ * {@link RdxFloatingFocusManager} at **unmount time**. The focus scope owns the *timing* (its queued
126
+ * post-unmount focus); this lets the manager own the *target*:
127
+ * - `false` → do **not** return focus;
128
+ * - an `HTMLElement` → return focus there **explicitly** (bypasses the moved-focus guard, matching
129
+ * Base UI's `hasExplicitReturnFocus`);
130
+ * - `undefined` → default behavior (return to the element focused before mount, with the guard).
131
+ */
132
+ returnFocus?: () => HTMLElement | false | undefined;
69
133
  };
70
134
  declare const RdxFocusScopeConfigToken: InjectionToken<RdxFocusScopeConfig>;
71
135
  declare function provideRdxFocusScopeConfig(factory: () => RdxFocusScopeConfig): Provider;
72
136
 
73
- export { RdxFocusScope, RdxFocusScopeConfigToken, injectFocusScopeContext, provideFocusScopeContext, provideRdxFocusScopeConfig };
137
+ declare const AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
138
+ declare const AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
139
+ declare const EVENT_OPTIONS: {
140
+ bubbles: boolean;
141
+ cancelable: boolean;
142
+ };
143
+ type FocusableTarget = HTMLElement | {
144
+ focus: () => void;
145
+ };
146
+ /**
147
+ * The real target of a (possibly retargeted) event, piercing shadow boundaries via `composedPath()`.
148
+ * Falls back to `event.target` when `composedPath` is unavailable.
149
+ */
150
+ declare function getEventTarget(event: Event): EventTarget | null;
151
+ /**
152
+ * Shadow-DOM-aware containment: whether `node` is `container` or lives inside it, crossing shadow roots
153
+ * via their `host` (unlike `Node.contains`, which stops at a shadow boundary).
154
+ */
155
+ declare function composedContains(container: Node, node: Node | null): boolean;
156
+ /**
157
+ * Attempts focusing the first element in a list of candidates.
158
+ * Stops when focus has actually moved.
159
+ */
160
+ declare function focusFirst(candidates: HTMLElement[], { select }?: {
161
+ select?: boolean | undefined;
162
+ }): true | undefined;
163
+ /**
164
+ * Returns a list of potential tabbable candidates.
165
+ *
166
+ * NOTE: This is only a close approximation. For example it doesn't take into account cases like when
167
+ * elements are not visible. This cannot be worked out easily by just reading a property, but rather
168
+ * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.
169
+ *
170
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
171
+ * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1
172
+ */
173
+ declare function getTabbableCandidates(container: HTMLElement): HTMLElement[];
174
+ declare function isHidden(node: HTMLElement, { upTo }: {
175
+ upTo?: HTMLElement;
176
+ }): boolean;
177
+ /**
178
+ * Returns the first visible element in a list.
179
+ * NOTE: Only checks visibility up to the `container`.
180
+ */
181
+ declare function findVisible(elements: HTMLElement[], container: HTMLElement): HTMLElement | undefined;
182
+ /**
183
+ * Returns the first and last tabbable elements inside a container.
184
+ */
185
+ declare function getTabbableEdges(container: HTMLElement): readonly [HTMLElement | undefined, HTMLElement | undefined];
186
+ /**
187
+ * The next tabbable in the document after the current focus (Base UI `getNextTabbable`) — used by the
188
+ * portal-focus bridge's trailing guard to step focus past the popup. Falls back to `reference`.
189
+ */
190
+ declare function getNextTabbable(reference: Element | null): HTMLElement | null;
191
+ /** The previous tabbable in the document before the current focus (Base UI `getPreviousTabbable`). */
192
+ declare function getPreviousTabbable(reference: Element | null): HTMLElement | null;
193
+ /** The tabbable immediately after `reference` in the document, wrapping (Base UI `getTabbableAfterElement`). */
194
+ declare function getTabbableAfterElement(reference: Element | null): HTMLElement | null;
195
+ /** The tabbable immediately before `reference` in the document, wrapping (Base UI `getTabbableBeforeElement`). */
196
+ declare function getTabbableBeforeElement(reference: Element | null): HTMLElement | null;
197
+ declare function isSelectableInput(element: any): element is FocusableTarget & {
198
+ select: () => void;
199
+ };
200
+ declare function focus(element?: FocusableTarget | null, { select }?: {
201
+ select?: boolean | undefined;
202
+ }): void;
203
+
204
+ export { AUTOFOCUS_ON_MOUNT, AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS, FOCUS_GUARD_ATTR, FOCUS_GUARD_STYLE, RdxFocusScope, RdxFocusScopeConfigToken, composedContains, createAriaOwnsAnchor, createFocusGuard, disableFocusInside, enableFocusInside, findVisible, focus, focusFirst, getEventTarget, getNextTabbable, getPreviousTabbable, getTabbableAfterElement, getTabbableBeforeElement, getTabbableCandidates, getTabbableEdges, injectFocusScopeContext, isHidden, isOutsideEvent, isSelectableInput, provideFocusScopeContext, provideRdxFocusScopeConfig, useFocusGuardsTabbability };
74
205
  export type { FocusScopeContext, RdxFocusScopeConfig };