@radix-ng/primitives 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +26 -5
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs +35 -19
  3. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs +55 -7
  5. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-combobox.mjs +46 -21
  7. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-composite.mjs +221 -36
  9. package/fesm2022/radix-ng-primitives-composite.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-core.mjs +189 -26
  11. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-date-field.mjs +29 -13
  13. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-field.mjs +127 -23
  15. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +9 -0
  17. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-focus-scope.mjs +241 -95
  19. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-form.mjs +35 -4
  21. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-input.mjs +87 -15
  23. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-menu.mjs +131 -16
  25. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +17 -3
  27. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-popper.mjs +223 -29
  29. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-portal.mjs +16 -5
  31. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-scroll-area.mjs +6 -3
  33. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-select.mjs +49 -17
  35. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-signal-forms.mjs +27 -11
  37. package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-time-field.mjs +24 -10
  39. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  40. package/package.json +8 -7
  41. package/types/radix-ng-primitives-autocomplete.d.ts +7 -2
  42. package/types/radix-ng-primitives-checkbox.d.ts +10 -1
  43. package/types/radix-ng-primitives-combobox.d.ts +29 -2
  44. package/types/radix-ng-primitives-composite.d.ts +48 -2
  45. package/types/radix-ng-primitives-core.d.ts +165 -104
  46. package/types/radix-ng-primitives-date-field.d.ts +21 -8
  47. package/types/radix-ng-primitives-field.d.ts +55 -8
  48. package/types/radix-ng-primitives-floating-focus-manager.d.ts +7 -0
  49. package/types/radix-ng-primitives-focus-scope.d.ts +61 -44
  50. package/types/radix-ng-primitives-form.d.ts +9 -0
  51. package/types/radix-ng-primitives-input.d.ts +10 -2
  52. package/types/radix-ng-primitives-menu.d.ts +59 -7
  53. package/types/radix-ng-primitives-navigation-menu.d.ts +2 -2
  54. package/types/radix-ng-primitives-popover.d.ts +2 -2
  55. package/types/radix-ng-primitives-popper.d.ts +186 -48
  56. package/types/radix-ng-primitives-portal.d.ts +18 -8
  57. package/types/radix-ng-primitives-preview-card.d.ts +2 -2
  58. package/types/radix-ng-primitives-select.d.ts +17 -3
  59. package/types/radix-ng-primitives-signal-forms.d.ts +22 -8
  60. package/types/radix-ng-primitives-time-field.d.ts +21 -10
  61. package/types/radix-ng-primitives-tooltip.d.ts +2 -2
  62. package/fesm2022/radix-ng-primitives-focus-guards.mjs +0 -53
  63. package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +0 -1
  64. package/focus-guards/README.md +0 -1
  65. package/types/radix-ng-primitives-focus-guards.d.ts +0 -15
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Type, Provider, InputSignal, WritableSignal, ModelSignal, Signal, InjectionToken, InputSignalWithTransform, OutputRef, OutputEmitterRef, Injector, ElementRef, EffectRef, EffectCleanupRegisterFn, CreateEffectOptions } from '@angular/core';
2
+ import { InputSignal, InputSignalWithTransform, ModelSignal, OutputRef, Signal, Type, Provider, WritableSignal, ProviderToken, InjectionToken, OutputEmitterRef, Injector, ElementRef, EffectRef, EffectCleanupRegisterFn, CreateEffectOptions } from '@angular/core';
3
3
  import { ControlValueAccessor } from '@angular/forms';
4
4
  import { DateValue, Time, CalendarDateTime, ZonedDateTime } from '@internationalized/date';
5
5
  import * as _radix_ng_primitives_core from '@radix-ng/primitives/core';
@@ -101,6 +101,114 @@ declare class RdxControlValueAccessor<T> implements ControlValueAccessor {
101
101
  */
102
102
  declare function injectControlValueAccessor<T>(): RdxControlValueAccessor<T>;
103
103
 
104
+ /**
105
+ * Local mirror of Angular Signal Forms' control contracts
106
+ * (`@angular/forms/signals`, stable in Angular 22).
107
+ *
108
+ * These interfaces intentionally do **not** import from `@angular/forms/signals`,
109
+ * so control entries do not pull that optional integration into their public
110
+ * dependency graph. Primitives can still declare `implements RdxFormValueControl<T>` /
111
+ * `implements RdxFormCheckboxControl`; the mirror locks the public surface (the
112
+ * required `value` / `checked` signal) and catches naming regressions on CI — e.g.
113
+ * a rewrite renaming `value` → `modelValue` (as the slider once had) would no
114
+ * longer type-check.
115
+ *
116
+ * Optional state types are widened only where Radix NG controls legitimately
117
+ * differ from Angular's exact types (e.g. `input<string>()` produces
118
+ * `string | undefined`, and boolean inputs carry a coercion transform).
119
+ *
120
+ * The real Angular types stay isolated in `@radix-ng/primitives/signal-forms`.
121
+ * See ADR 0018 and `.claude/skills/project-knowledge/references/signal-forms-readiness.md`.
122
+ */
123
+ /** An optional control-state member exposed as an Angular input signal (with or without a coercion transform). */
124
+ type RdxFormStateInput<T> = InputSignal<T> | InputSignalWithTransform<T, any>;
125
+ /**
126
+ * Minimal stand-in for Angular's `ValidationError`. The real type is a tagged
127
+ * union (`RequiredValidationError`, `PatternValidationError`, …); this keeps a
128
+ * shared shape until the v22 type is available.
129
+ */
130
+ interface RdxValidationError {
131
+ readonly kind: string;
132
+ readonly message?: string;
133
+ }
134
+ /** Optional state shared by value and checkbox controls (mirror of Angular's `FormUiControl`). */
135
+ interface RdxFormUiControl {
136
+ readonly disabled?: RdxFormStateInput<boolean>;
137
+ readonly readonly?: RdxFormStateInput<boolean>;
138
+ readonly required?: RdxFormStateInput<boolean>;
139
+ readonly invalid?: RdxFormStateInput<boolean>;
140
+ readonly hidden?: RdxFormStateInput<boolean>;
141
+ readonly pending?: RdxFormStateInput<boolean>;
142
+ /**
143
+ * Touched status the form writes into the control.
144
+ *
145
+ * The two API generations disagree on the notification half: the 21.x
146
+ * experimental implementation listens to a `touched` **model**'s
147
+ * `touchedChange` output, while stable Angular 22 reverted to a plain
148
+ * `touched` input plus a separate {@link touch} output. A `model()` set on
149
+ * blur **plus** an emitted `touch` output satisfies both (`ModelSignal`
150
+ * extends `InputSignalWithTransform`, so it type-checks as the 22 input).
151
+ */
152
+ readonly touched?: ModelSignal<boolean> | RdxFormStateInput<boolean> | OutputRef<boolean>;
153
+ readonly dirty?: RdxFormStateInput<boolean>;
154
+ readonly name?: RdxFormStateInput<string | undefined>;
155
+ readonly errors?: RdxFormStateInput<readonly RdxValidationError[]>;
156
+ readonly minLength?: RdxFormStateInput<number | undefined>;
157
+ readonly maxLength?: RdxFormStateInput<number | undefined>;
158
+ readonly pattern?: RdxFormStateInput<readonly RegExp[]>;
159
+ /** Notifies the form the control was touched (stable Angular 22 contract; ignored by 21.x). */
160
+ readonly touch?: OutputRef<void>;
161
+ /** Resets the control's UI state (optional method added in stable Angular 22). */
162
+ reset?(): void;
163
+ }
164
+ /**
165
+ * Mirror of `FormValueControl<TValue>` — a control that edits a single value via
166
+ * `value = model<TValue>()`. It must **not** expose `checked`.
167
+ */
168
+ interface RdxFormValueControl<TValue> extends RdxFormUiControl {
169
+ readonly value: ModelSignal<TValue>;
170
+ readonly checked?: undefined;
171
+ readonly min?: RdxFormStateInput<NonNullable<TValue> | undefined>;
172
+ readonly max?: RdxFormStateInput<NonNullable<TValue> | undefined>;
173
+ }
174
+ /**
175
+ * Mirror of `FormCheckboxControl` — a control that toggles via
176
+ * `checked = model<boolean>()`. It must **not** expose `value`.
177
+ */
178
+ interface RdxFormCheckboxControl extends RdxFormUiControl {
179
+ readonly checked: ModelSignal<boolean>;
180
+ readonly value?: undefined;
181
+ }
182
+
183
+ /**
184
+ * Form state owned by an Angular Reactive Forms or template-driven `NgControl` on the same host
185
+ * element. `connected` stays false for standalone and Signal Forms controls.
186
+ *
187
+ * @ignore
188
+ */
189
+ interface RdxNgControlState {
190
+ readonly connected: Signal<boolean>;
191
+ readonly name: Signal<string | undefined>;
192
+ readonly value: Signal<unknown>;
193
+ readonly valid: Signal<boolean>;
194
+ readonly invalid: Signal<boolean>;
195
+ readonly pending: Signal<boolean>;
196
+ readonly disabled: Signal<boolean>;
197
+ readonly errors: Signal<readonly RdxValidationError[]>;
198
+ readonly dirty: Signal<boolean>;
199
+ readonly touched: Signal<boolean>;
200
+ }
201
+ /**
202
+ * Lazily connects to the same-host `NgControl` without creating a circular dependency while Angular
203
+ * is resolving its `ControlValueAccessor`. The unified `AbstractControl.events` stream mirrors
204
+ * programmatic value, status, validation, and interaction-state changes as well as user-driven ones.
205
+ *
206
+ * Must be called in an injection context.
207
+ *
208
+ * @ignore
209
+ */
210
+ declare function injectNgControlState(): RdxNgControlState;
211
+
104
212
  /**
105
213
  * Include in the providers section of a component which utilizes ControlValueAccessor to redundant code.
106
214
  *
@@ -453,21 +561,21 @@ declare function getSegmentElements(parentElement: HTMLElement): Element[];
453
561
  type UseDateFieldProps = {
454
562
  hasLeftFocus: WritableSignal<boolean>;
455
563
  lastKeyZero: WritableSignal<boolean>;
456
- placeholder: ModelSignal<DateValue> | WritableSignal<DateValue>;
457
- hourCycle: HourCycle;
564
+ placeholder: Signal<DateValue>;
565
+ hourCycle: HourCycle | undefined;
458
566
  formatter: Formatter;
459
567
  segmentValues: WritableSignal<SegmentValueObj>;
460
568
  step: Signal<DateStep>;
461
- disabled: InputSignal<boolean>;
462
- readonly: InputSignal<boolean>;
569
+ disabled: Signal<boolean>;
570
+ readonly: Signal<boolean>;
463
571
  part: SegmentPart;
464
- modelValue: ModelSignal<DateValue | undefined> | WritableSignal<DateValue | undefined>;
572
+ modelValue: ModelSignal<DateValue | null> | WritableSignal<DateValue | null>;
465
573
  focusNext: () => void;
466
574
  };
467
575
  type SegmentAttrProps = {
468
576
  disabled: boolean;
469
577
  segmentValues: SegmentValueObj;
470
- hourCycle: HourCycle;
578
+ hourCycle: HourCycle | undefined;
471
579
  placeholder: DateValue;
472
580
  formatter: Formatter;
473
581
  };
@@ -763,104 +871,28 @@ declare function isEqual(a: any, b: any): boolean;
763
871
  * of the specified class. This is especially useful when you want multiple
764
872
  * tokens (or interfaces) to resolve to the same directive/component instance.
765
873
  *
766
- * @template T - The type associated with the injection token.
767
- * @param token - The InjectionToken or abstract type you want to provide.
768
- * @param type - The class type whose existing instance will be used for this token.
874
+ * `Type<T>` ties the class to the token's contract, so a class that does not
875
+ * satisfy `T` is rejected at compile time instead of failing at runtime.
876
+ *
877
+ * @template T - The type associated with the provider token.
878
+ * @param token - The token (InjectionToken, class, or abstract class) to provide.
879
+ * @param type - The class whose existing instance resolves this token; must satisfy `T`.
769
880
  * @returns A Provider configuration object for Angular's DI system.
770
881
  *
771
882
  * @example
772
883
  *
773
884
  * @Directive({
774
885
  * providers: [
775
- * provideToken(RdxFooToken, RdxFooDirective),
886
+ * provideExistingToken(RdxFooToken, RdxFooDirective),
776
887
  * provideValueAccessor(RdxFooDirective)
777
888
  * ]
778
889
  * })
779
890
  * export class RdxFooDirective {}
780
891
  */
781
- declare function provideToken<T>(token: InjectionToken<T>, type: Type<unknown>): Provider;
892
+ declare function provideExistingToken<T>(token: ProviderToken<T>, type: Type<T>): Provider;
782
893
 
783
894
  /**
784
- * Local mirror of Angular Signal Forms' control contracts
785
- * (`@angular/forms/signals`, stable in Angular 22).
786
- *
787
- * These interfaces intentionally do **not** import from `@angular/forms/signals`
788
- * so primitives can declare `implements RdxFormValueControl<T>` /
789
- * `implements RdxFormCheckboxControl` while the library baseline is still on
790
- * Angular 21, where the real API is experimental. They mirror Angular's contract
791
- * closely enough to lock the public surface (the required `value` / `checked`
792
- * signal) and catch naming regressions on CI — e.g. a rewrite renaming
793
- * `value` → `modelValue` (as the slider once had) would no longer type-check.
794
- *
795
- * Optional state types are widened only where Radix NG controls legitimately
796
- * differ from Angular's exact types (e.g. `input<string>()` produces
797
- * `string | undefined`, and boolean inputs carry a coercion transform).
798
- *
799
- * Replace with the real imports once the baseline moves to Angular 22.
800
- * See `.claude/skills/project-knowledge/references/signal-forms-readiness.md`.
801
- */
802
- /** An optional control-state member exposed as an Angular input signal (with or without a coercion transform). */
803
- type RdxFormStateInput<T> = InputSignal<T> | InputSignalWithTransform<T, any>;
804
- /**
805
- * Minimal stand-in for Angular's `ValidationError`. The real type is a tagged
806
- * union (`RequiredValidationError`, `PatternValidationError`, …); this keeps a
807
- * shared shape until the v22 type is available.
808
- */
809
- interface RdxValidationError {
810
- readonly kind: string;
811
- readonly message?: string;
812
- }
813
- /** Optional state shared by value and checkbox controls (mirror of Angular's `FormUiControl`). */
814
- interface RdxFormUiControl {
815
- readonly disabled?: RdxFormStateInput<boolean>;
816
- readonly readonly?: RdxFormStateInput<boolean>;
817
- readonly required?: RdxFormStateInput<boolean>;
818
- readonly invalid?: RdxFormStateInput<boolean>;
819
- readonly hidden?: RdxFormStateInput<boolean>;
820
- readonly pending?: RdxFormStateInput<boolean>;
821
- /**
822
- * Touched status the form writes into the control.
823
- *
824
- * The two API generations disagree on the notification half: the 21.x
825
- * experimental implementation listens to a `touched` **model**'s
826
- * `touchedChange` output, while stable Angular 22 reverted to a plain
827
- * `touched` input plus a separate {@link touch} output. A `model()` set on
828
- * blur **plus** an emitted `touch` output satisfies both (`ModelSignal`
829
- * extends `InputSignalWithTransform`, so it type-checks as the 22 input).
830
- */
831
- readonly touched?: ModelSignal<boolean> | RdxFormStateInput<boolean> | OutputRef<boolean>;
832
- readonly dirty?: RdxFormStateInput<boolean>;
833
- readonly name?: RdxFormStateInput<string | undefined>;
834
- readonly errors?: RdxFormStateInput<readonly RdxValidationError[]>;
835
- readonly minLength?: RdxFormStateInput<number | undefined>;
836
- readonly maxLength?: RdxFormStateInput<number | undefined>;
837
- readonly pattern?: RdxFormStateInput<readonly RegExp[]>;
838
- /** Notifies the form the control was touched (stable Angular 22 contract; ignored by 21.x). */
839
- readonly touch?: OutputRef<void>;
840
- /** Resets the control's UI state (optional method added in stable Angular 22). */
841
- reset?(): void;
842
- }
843
- /**
844
- * Mirror of `FormValueControl<TValue>` — a control that edits a single value via
845
- * `value = model<TValue>()`. It must **not** expose `checked`.
846
- */
847
- interface RdxFormValueControl<TValue> extends RdxFormUiControl {
848
- readonly value: ModelSignal<TValue>;
849
- readonly checked?: undefined;
850
- readonly min?: RdxFormStateInput<NonNullable<TValue> | undefined>;
851
- readonly max?: RdxFormStateInput<NonNullable<TValue> | undefined>;
852
- }
853
- /**
854
- * Mirror of `FormCheckboxControl` — a control that toggles via
855
- * `checked = model<boolean>()`. It must **not** expose `value`.
856
- */
857
- interface RdxFormCheckboxControl extends RdxFormUiControl {
858
- readonly checked: ModelSignal<boolean>;
859
- readonly value?: undefined;
860
- }
861
-
862
- /**
863
- * Optional form-control UI state derived from a control's `invalid`/`errors`/`touched`/`dirty`
895
+ * Optional form-control UI state derived from a control's `invalid`/`pending`/`errors`/`touched`/`dirty`
864
896
  * inputs (the optional members of Signal Forms' `FormUiControl`), plus the two mutators every
865
897
  * control needs.
866
898
  *
@@ -869,6 +901,12 @@ interface RdxFormCheckboxControl extends RdxFormUiControl {
869
901
  interface RdxFormUiState {
870
902
  /** Invalid when the `invalid` input is set or the `errors` list is non-empty. */
871
903
  readonly invalidState: Signal<boolean>;
904
+ /** Whether asynchronous validation is still settling. Pending is neither valid nor invalid. */
905
+ readonly pendingState: Signal<boolean>;
906
+ /** Tri-state validity: valid / invalid / neutral while pending or disabled. */
907
+ readonly validState: Signal<boolean | null>;
908
+ /** Explicit control errors merged with same-host Angular Forms errors. */
909
+ readonly errorsState: Signal<readonly RdxValidationError[]>;
872
910
  /** Whether the control has been touched (reflects the `touched` model). */
873
911
  readonly touchedState: Signal<boolean>;
874
912
  /** Whether the value changed from its initial value — external `dirty` OR internal tracking. */
@@ -877,6 +915,10 @@ interface RdxFormUiState {
877
915
  markAsTouched(): void;
878
916
  /** Flag the control dirty after a value change (feeds {@link dirtyState}). */
879
917
  markDirty(): void;
918
+ /** Clear control-owned touched/dirty state when a form resets it. */
919
+ resetInteractionState?(): void;
920
+ /** Clear only control-owned dirty state when the form marks the field pristine. */
921
+ resetDirtyState?(): void;
880
922
  }
881
923
  /** Minimal `ControlValueAccessor` surface the form-UI state bridges for the dual (Reactive/template) path. */
882
924
  interface RdxFormUiTouchTarget {
@@ -885,20 +927,24 @@ interface RdxFormUiTouchTarget {
885
927
  /** Inputs a control passes to {@link createFormUiState} — its own `FormUiControl` signals. */
886
928
  interface RdxFormUiStateOptions {
887
929
  readonly invalid: Signal<boolean>;
930
+ /** Optional for backwards compatibility with custom controls authored before pending support. */
931
+ readonly pending?: Signal<boolean>;
888
932
  readonly errors: Signal<readonly RdxValidationError[]>;
889
933
  readonly touched: ModelSignal<boolean>;
890
934
  readonly touch: OutputEmitterRef<void>;
891
935
  readonly dirty: Signal<boolean>;
936
+ /** Same-host Reactive/template-driven form interaction state, when one is present. */
937
+ readonly ngControlState?: RdxNgControlState | null;
892
938
  /**
893
939
  * The control's `ControlValueAccessor`, if it has one (dual controls — switch, number-field, …).
894
- * Omit or pass `null` for Signal-Forms-only controls without a CVA (e.g. select).
940
+ * Omit or pass `null` for Signal-Forms-only controls without a CVA (e.g. date-field).
895
941
  */
896
942
  readonly cva?: RdxFormUiTouchTarget | null;
897
943
  }
898
944
  /**
899
945
  * Builds the shared form-UI state and its mutators from a control's input signals, removing the
900
- * per-control copy-paste of the `invalidState`/`touchedState`/`dirtyState` computeds and the
901
- * `markAsTouched`/`markDirty` logic.
946
+ * per-control copy-paste of the validation/interaction computeds and the `markAsTouched`/`markDirty`
947
+ * logic.
902
948
  *
903
949
  * **Why the inputs stay on the control (not in here):** Angular's compiler only discovers
904
950
  * `input()`/`model()` declared as field initializers, and Signal Forms binds form-written state
@@ -916,6 +962,8 @@ declare function createFormUiState(options: RdxFormUiStateOptions): RdxFormUiSta
916
962
  */
917
963
  interface RdxFormUiStateContext {
918
964
  invalidState: Signal<boolean>;
965
+ pendingState: Signal<boolean>;
966
+ validState: Signal<boolean | null>;
919
967
  touchedState: Signal<boolean>;
920
968
  dirtyState: Signal<boolean>;
921
969
  markAsTouched: () => void;
@@ -936,14 +984,14 @@ declare const RDX_FIELD_VALIDITY: InjectionToken<Signal<boolean | null>>;
936
984
  /**
937
985
  * Tri-state display validity: when inside a `Field` the field's gated `validState` is the **single
938
986
  * source** (the control reflects it, including its `validationMode` neutral state); standalone, the
939
- * control's own binary invalidity.
987
+ * control's own tri-state validity (pending is neutral).
940
988
  *
941
989
  * **Contract:** inside a `Field` a control's own `invalid` / `errors` inputs are **not** displayed — the
942
990
  * Field owns displayed validity. Drive validity through the Field instead: bind `rdxSignalField`
943
991
  * (Signal Forms), or set `[invalid]` on `rdxFieldRoot`. The control's own `invalid`/`errors` are for
944
992
  * standalone use.
945
993
  */
946
- declare function resolveDisplayValid(fieldValidity: Signal<boolean | null> | null, ownInvalid: Signal<boolean>): boolean | null;
994
+ declare function resolveDisplayValid(fieldValidity: Signal<boolean | null> | null, ownInvalid: Signal<boolean>, ownPending?: Signal<boolean>): boolean | null;
947
995
  /**
948
996
  * Provide a control's {@link RdxFormUiState} on its host element so {@link RdxFormUiStateHost} can
949
997
  * reflect it. Pair with `hostDirectives: [RdxFormUiStateHost]`:
@@ -982,7 +1030,7 @@ declare class RdxFormUiStateHost {
982
1030
  }
983
1031
  /**
984
1032
  * Abstract base that declares the optional `FormUiControl` state inputs
985
- * (`invalid`/`errors`/`touched`/`dirty` + the `touch` output) once and builds the control's
1033
+ * (`invalid`/`pending`/`errors`/`touched`/`dirty` + the `touch` output) once and builds the control's
986
1034
  * {@link RdxFormUiState} from them, so a control directive can inherit the whole surface with a
987
1035
  * single `extends` instead of re-declaring it.
988
1036
  *
@@ -999,6 +1047,8 @@ declare class RdxFormUiStateHost {
999
1047
  declare abstract class RdxFormUiControlBase {
1000
1048
  /** Whether the control is invalid. A non-empty {@link errors} list also marks it invalid. */
1001
1049
  readonly invalid: i0.InputSignalWithTransform<boolean, BooleanInput>;
1050
+ /** Whether async validation is pending. Pending controls publish neither `data-valid` nor `data-invalid`. */
1051
+ readonly pending: i0.InputSignalWithTransform<boolean, BooleanInput>;
1002
1052
  /** Whether the control has been touched. A `model()` so Signal Forms can write it; set on blur/focus-out. */
1003
1053
  readonly touched: ModelSignal<boolean>;
1004
1054
  /** Whether the value changed from its initial value. Merged with internal tracking. */
@@ -1007,8 +1057,12 @@ declare abstract class RdxFormUiControlBase {
1007
1057
  readonly errors: i0.InputSignal<readonly RdxValidationError[]>;
1008
1058
  /** Emits when the control is touched, notifying Signal Forms (stable Angular 22 contract). */
1009
1059
  readonly touch: OutputEmitterRef<void>;
1060
+ private readonly ngControlState;
1010
1061
  /** The shared form-UI state derived from the inputs above. Call `formUi.markDirty()` on value change. */
1011
1062
  readonly formUi: RdxFormUiState;
1063
+ /** Validation errors from the control inputs and a same-host Reactive/template-driven form control. */
1064
+ readonly validationErrors: Signal<readonly RdxValidationError[]>;
1065
+ constructor();
1012
1066
  /** The enclosing Field's tri-state display validity, if any. `protected` so a control whose own
1013
1067
  * invalidity is richer than `formUi.invalidState` (e.g. date/time-field add a parse check) can build
1014
1068
  * its own `displayValid` from it. */
@@ -1016,19 +1070,26 @@ declare abstract class RdxFormUiControlBase {
1016
1070
  /**
1017
1071
  * Tri-state *displayed* validity for controls that bind their own host attributes (radio, switch,
1018
1072
  * number-field): the enclosing Field's gated state when inside a `rdxFieldRoot`, else this control's
1019
- * own binary invalidity (`formUi.invalidState`). Bind `data-valid`/`data-invalid`/`aria-invalid` to
1073
+ * own validity (`formUi.pendingState` is neutral; otherwise `formUi.invalidState` is binary). Bind
1074
+ * `data-valid`/`data-invalid`/`aria-invalid` to
1020
1075
  * this so a neutral field shows neither. Controls whose `invalidState` is richer than
1021
- * `formUi.invalidState` override this with `resolveDisplayValid(this.fieldValidity, this.invalidState)`.
1076
+ * `formUi.invalidState` override this with
1077
+ * `resolveDisplayValid(this.fieldValidity, this.invalidState, this.formUi.pendingState)`.
1022
1078
  */
1023
1079
  readonly displayValid: Signal<boolean | null>;
1024
1080
  /**
1025
1081
  * Override to bridge the control's `ControlValueAccessor` into `markAsTouched` (dual controls —
1026
1082
  * return `injectControlValueAccessor()` or a `{ markAsTouched }` adapter). Default: no CVA
1027
- * (Signal-Forms-only controls such as select).
1083
+ * (Signal-Forms-only controls such as date-field).
1028
1084
  */
1029
1085
  protected formUiTouchTarget(): RdxFormUiTouchTarget | null;
1086
+ /**
1087
+ * Reset control-owned interaction state. Angular Signal Forms calls this optional custom-control
1088
+ * hook from `FieldState.reset()` after restoring the field value.
1089
+ */
1090
+ reset(): void;
1030
1091
  static ɵfac: i0.ɵɵFactoryDeclaration<RdxFormUiControlBase, never>;
1031
- static ɵdir: i0.ɵɵDirectiveDeclaration<RdxFormUiControlBase, never, never, { "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "dirty": { "alias": "dirty"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "touched": "touchedChange"; "touch": "touch"; }, never, never, true, never>;
1092
+ static ɵdir: i0.ɵɵDirectiveDeclaration<RdxFormUiControlBase, never, never, { "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "pending": { "alias": "pending"; "required": false; "isSignal": true; }; "touched": { "alias": "touched"; "required": false; "isSignal": true; }; "dirty": { "alias": "dirty"; "required": false; "isSignal": true; }; "errors": { "alias": "errors"; "required": false; "isSignal": true; }; }, { "touched": "touchedChange"; "touch": "touch"; }, never, never, true, never>;
1032
1093
  }
1033
1094
 
1034
1095
  /**
@@ -2053,5 +2114,5 @@ declare enum RdxPositionAlign {
2053
2114
  End = "end"
2054
2115
  }
2055
2116
 
2056
- export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, DOCS_BASE_URL, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_DEFAULT_VALIDATION_MODE, RDX_FIELD_VALIDITY, RDX_FLOATING_REGISTRATION, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_TREE, RDX_FORM_UI_STATE, RDX_INTERNAL_BACKDROP_ATTR, RDX_SCROLL_LOCKED_ATTR, RdxControlValueAccessor, RdxFloatingNode, RdxFloatingNodeRegistration, RdxFloatingRegistrationContext, RdxFloatingRootContext, RdxFloatingTree, RdxFormUiControlBase, RdxFormUiStateHost, RdxIdGenerator, RdxLiveAnnouncer, RdxPositionAlign, RdxPositionSide, RdxTriggerRegistry, SHIFT, SPACE, SPACE_CODE, TAB, TIME_GRANULARITIES, a, areAllDaysBetweenValid, clamp, createCancelableChangeEventDetails, createContent, createContext, createFloatingEvents, createFloatingRootContext, createFormUiState, createFormatter, createMonth, createMonths, docsUrl, elementSize, formUiStateContext, getActiveElement, getDaysBetween, getDaysInMonth, getDefaultDate, getDefaultTime, getLastFirstDayOfWeek, getMaxTransitionDuration, getNextLastDayOfWeek, getOptsByGranularity, getPlaceholder, getSegmentElements, getWeekNumber, handleAndDispatchCustomEvent, handleCalendarInitialFocus, hasTime, initializeSegmentValues, injectControlValueAccessor, injectDocument, injectFloatingRootContext, injectId, isAcceptableSegmentKey, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isEqual, isItemEqualToValue, isNullish, isNumberString, isSegmentNavigationKey, isValidationRevealed, isZonedDateTime, itemToStringLabel, itemToStringValue, j, k, n, normalizeDateStep, normalizeHour12, normalizeHourCycle, p, provideFloatingRegistration, provideFloatingRootContext, provideFloatingTree, provideFormUiState, provideToken, provideValueAccessor, rdxCheckLabelElement, rdxCheckTriggerElement, rdxDevError, rdxDevWarning, resetRdxDevWarnings, resizeEffect, resolveDisplayValid, resolveFloatingTree, roundToStepPrecision, segmentBuilders, setupInternalBackdrop, snapValueToStep, syncSegmentValues, syncTimeSegmentValues, toDate, useAnchoredScrollLock, useArrowNavigation, useDateField, useFilter, useGraceArea, useListHighlight, usePointerDrag, useScrollLock, useTransitionStatus, watch };
2057
- export type { AcceptableValue, AnyExceptLiteral, AriaLivePoliteness, BooleanInput, CreateMonthProps, DataOrientation, DateAndTimeSegmentObj, DateFormatterOptions, DateMatcher, DateRange, DateSegmentObj, DateSegmentPart, DateStep, DayPeriod, Direction, EditableSegmentPart, FilterPredicates, Formatter, Granularity, HourCycle, InjectContext, ItemValueComparator, ListHighlight, Month, NonEditableSegmentPart, Nullable, NumberInput, PlaceholderMap, RdxAnchoredScrollLockOptions, RdxCancelableChangeEventDetails, RdxCancelableChangeEventTransaction, RdxFloatingEventMap, RdxFloatingEvents, RdxFloatingLifecycle, RdxFloatingNodeInit, RdxFloatingParentOverride, RdxFloatingRegistrationReader, RdxFloatingRegistrationStatus, RdxFloatingRootContextEventMap, RdxFloatingRootContextInit, RdxFormCheckboxControl, RdxFormStateInput, RdxFormUiControl, RdxFormUiState, RdxFormUiStateContext, RdxFormUiStateOptions, RdxFormUiTouchTarget, RdxFormValueControl, RdxInternalBackdropOptions, RdxPointerDragHandlers, RdxScrollLockOptions, RdxTransitionStatus, RdxTransitionStatusRef, RdxValidationError, RdxValidationInteraction, RdxValidationMode, SafeFunction, SegmentContentObj, SegmentPart, SegmentValueObj, TimeGranularity, TimeSegmentObj, TimeSegmentPart, TimeValue, UseDateFieldProps, UseFilterOptions, UseListHighlightOptions };
2117
+ export { A, ALT, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, ASTERISK, BACKSPACE, CAPS_LOCK, CONTROL, CTRL, DELETE, DOCS_BASE_URL, END, ENTER, ESCAPE, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, HOME, META, P, PAGE_DOWN, PAGE_UP, RDX_DEFAULT_VALIDATION_MODE, RDX_FIELD_VALIDITY, RDX_FLOATING_REGISTRATION, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_TREE, RDX_FORM_UI_STATE, RDX_INTERNAL_BACKDROP_ATTR, RDX_SCROLL_LOCKED_ATTR, RdxControlValueAccessor, RdxFloatingNode, RdxFloatingNodeRegistration, RdxFloatingRegistrationContext, RdxFloatingRootContext, RdxFloatingTree, RdxFormUiControlBase, RdxFormUiStateHost, RdxIdGenerator, RdxLiveAnnouncer, RdxPositionAlign, RdxPositionSide, RdxTriggerRegistry, SHIFT, SPACE, SPACE_CODE, TAB, TIME_GRANULARITIES, a, areAllDaysBetweenValid, clamp, createCancelableChangeEventDetails, createContent, createContext, createFloatingEvents, createFloatingRootContext, createFormUiState, createFormatter, createMonth, createMonths, docsUrl, elementSize, formUiStateContext, getActiveElement, getDaysBetween, getDaysInMonth, getDefaultDate, getDefaultTime, getLastFirstDayOfWeek, getMaxTransitionDuration, getNextLastDayOfWeek, getOptsByGranularity, getPlaceholder, getSegmentElements, getWeekNumber, handleAndDispatchCustomEvent, handleCalendarInitialFocus, hasTime, initializeSegmentValues, injectControlValueAccessor, injectDocument, injectFloatingRootContext, injectId, injectNgControlState, isAcceptableSegmentKey, isAfter, isAfterOrSame, isBefore, isBeforeOrSame, isBetween, isBetweenInclusive, isCalendarDateTime, isEqual, isItemEqualToValue, isNullish, isNumberString, isSegmentNavigationKey, isValidationRevealed, isZonedDateTime, itemToStringLabel, itemToStringValue, j, k, n, normalizeDateStep, normalizeHour12, normalizeHourCycle, p, provideExistingToken, provideFloatingRegistration, provideFloatingRootContext, provideFloatingTree, provideFormUiState, provideValueAccessor, rdxCheckLabelElement, rdxCheckTriggerElement, rdxDevError, rdxDevWarning, resetRdxDevWarnings, resizeEffect, resolveDisplayValid, resolveFloatingTree, roundToStepPrecision, segmentBuilders, setupInternalBackdrop, snapValueToStep, syncSegmentValues, syncTimeSegmentValues, toDate, useAnchoredScrollLock, useArrowNavigation, useDateField, useFilter, useGraceArea, useListHighlight, usePointerDrag, useScrollLock, useTransitionStatus, watch };
2118
+ export type { AcceptableValue, AnyExceptLiteral, AriaLivePoliteness, BooleanInput, CreateMonthProps, DataOrientation, DateAndTimeSegmentObj, DateFormatterOptions, DateMatcher, DateRange, DateSegmentObj, DateSegmentPart, DateStep, DayPeriod, Direction, EditableSegmentPart, FilterPredicates, Formatter, Granularity, HourCycle, InjectContext, ItemValueComparator, ListHighlight, Month, NonEditableSegmentPart, Nullable, NumberInput, PlaceholderMap, RdxAnchoredScrollLockOptions, RdxCancelableChangeEventDetails, RdxCancelableChangeEventTransaction, RdxFloatingEventMap, RdxFloatingEvents, RdxFloatingLifecycle, RdxFloatingNodeInit, RdxFloatingParentOverride, RdxFloatingRegistrationReader, RdxFloatingRegistrationStatus, RdxFloatingRootContextEventMap, RdxFloatingRootContextInit, RdxFormCheckboxControl, RdxFormStateInput, RdxFormUiControl, RdxFormUiState, RdxFormUiStateContext, RdxFormUiStateOptions, RdxFormUiTouchTarget, RdxFormValueControl, RdxInternalBackdropOptions, RdxNgControlState, RdxPointerDragHandlers, RdxScrollLockOptions, RdxTransitionStatus, RdxTransitionStatusRef, RdxValidationError, RdxValidationInteraction, RdxValidationMode, SafeFunction, SegmentContentObj, SegmentPart, SegmentValueObj, TimeGranularity, TimeSegmentObj, TimeSegmentPart, TimeValue, UseDateFieldProps, UseFilterOptions, UseListHighlightOptions };
@@ -4,11 +4,12 @@ import * as _radix_ng_primitives_core from '@radix-ng/primitives/core';
4
4
  import { RdxFormUiControlBase, RdxFormValueControl, DateMatcher, HourCycle, Granularity, Direction, BooleanInput, DateStep, Formatter, SegmentValueObj, SegmentPart } from '@radix-ng/primitives/core';
5
5
  import { DateValue } from '@internationalized/date';
6
6
 
7
- declare class RdxDateFieldRootDirective extends RdxFormUiControlBase implements RdxFormValueControl<DateValue | undefined> {
7
+ declare class RdxDateFieldRootDirective extends RdxFormUiControlBase implements RdxFormValueControl<DateValue | null> {
8
8
  /**
9
- * The controlled value of the date field.
9
+ * The controlled value of the date field. `null` represents an empty field and keeps the value
10
+ * addressable as an Angular Signal Forms child field (`undefined` denotes an absent optional path).
10
11
  */
11
- readonly value: _angular_core.ModelSignal<DateValue | undefined>;
12
+ readonly value: _angular_core.ModelSignal<DateValue | null>;
12
13
  /**
13
14
  * A matcher that marks specific dates as unavailable; a matched value makes the field invalid.
14
15
  */
@@ -64,6 +65,13 @@ declare class RdxDateFieldRootDirective extends RdxFormUiControlBase implements
64
65
  * The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view
65
66
  */
66
67
  readonly placeholder: _angular_core.ModelSignal<DateValue | undefined>;
68
+ /**
69
+ * Always-defined placeholder used for segment math. A controlled `[placeholder]` can be reset to
70
+ * `undefined`; fall back to the default date so segment attributes and key handlers never
71
+ * dereference `undefined`.
72
+ * @ignore
73
+ */
74
+ readonly effectivePlaceholder: Signal<DateValue>;
67
75
  /**
68
76
  * Segment input parts, collected from the projected content in DOM order. This
69
77
  * stays in sync with `segmentContents()` (granularity / locale / value changes
@@ -115,6 +123,8 @@ declare class RdxDateFieldRootDirective extends RdxFormUiControlBase implements
115
123
  private readonly userInteracted;
116
124
  /** @ignore Mark the field touched (model + `touch` output) for Signal Forms. Called on segment blur. */
117
125
  markAsTouched(): void;
126
+ /** Reset segment interaction so the form-owned value write remains pristine. */
127
+ reset(): void;
118
128
  /**
119
129
  * @ignore
120
130
  */
@@ -228,16 +238,19 @@ declare class RdxDateFieldInputDirective {
228
238
 
229
239
  interface DateFieldContextToken {
230
240
  locale: InputSignal<string>;
231
- value: ModelSignal<DateValue | undefined>;
232
- disabled: InputSignal<boolean>;
233
- readonly: InputSignal<boolean>;
241
+ value: ModelSignal<DateValue | null>;
242
+ disabled: Signal<boolean>;
243
+ readonly: Signal<boolean>;
234
244
  isInvalid: Signal<boolean>;
235
245
  /** Effective invalid: the built-in range/availability check OR the form-driven invalid state. */
236
246
  invalidState: Signal<boolean>;
237
247
  /** Tri-state displayed validity (`true`/`false`/`null`): the field's gated state inside a Field, else own. */
238
248
  displayValid: Signal<boolean | null>;
239
- placeholder: ModelSignal<DateValue>;
240
- hourCycle: InputSignal<HourCycle>;
249
+ /** The controlled placeholder; may be `undefined`. Use `effectivePlaceholder` for segment math. */
250
+ placeholder: Signal<DateValue | undefined>;
251
+ /** Always-defined placeholder (falls back to the default date) for segment attributes/key handlers. */
252
+ effectivePlaceholder: Signal<DateValue>;
253
+ hourCycle: Signal<HourCycle | undefined>;
241
254
  step$: Signal<DateStep>;
242
255
  formatter: Signal<Formatter>;
243
256
  segmentValues: WritableSignal<SegmentValueObj>;
@@ -63,6 +63,15 @@ declare class RdxFieldError {
63
63
  * @group Props
64
64
  */
65
65
  readonly id: _angular_core.InputSignal<string>;
66
+ /**
67
+ * Shows this error only for a matching validation-error key. Bind `true` to keep it visible under
68
+ * external control; omit it for the field's default error behavior.
69
+ *
70
+ * @group Props
71
+ */
72
+ readonly match: _angular_core.InputSignal<string | boolean | undefined>;
73
+ /** Whether this error part is currently presented. */
74
+ readonly visible: _angular_core.Signal<boolean>;
66
75
  /**
67
76
  * The field's validation messages — client (provider / form name-routing, once `validationMode`
68
77
  * reveals them) then server (the Form's `errors` input, always); `[]` when none. Render them
@@ -73,12 +82,11 @@ declare class RdxFieldError {
73
82
  constructor();
74
83
  protected readonly dataAttr: (value: boolean) => "" | undefined;
75
84
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<RdxFieldError, never>;
76
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxFieldError, "[rdxFieldError]", ["rdxFieldError"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
85
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxFieldError, "[rdxFieldError]", ["rdxFieldError"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "match": { "alias": "match"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
77
86
  }
78
87
 
79
88
  /**
80
- * External owner of field state. An adapter (e.g. a future Signal Forms
81
- * `[rdxSignalField]` directive, or a Reactive Forms bridge) registers one via
89
+ * External owner of field state. An adapter (`rdxSignalField` or `rdxNgControlField`) registers one via
82
90
  * `setStateProvider` so Field reads authoritative form state instead of
83
91
  * self-computing it from the DOM. Each member is an optional signal-like
84
92
  * accessor; only the states the adapter owns need to be provided — the rest
@@ -86,15 +94,19 @@ declare class RdxFieldError {
86
94
  * `() => boolean` accessors keeps Field framework-agnostic (no dependency on
87
95
  * `@angular/forms/signals`).
88
96
  *
89
- * See ADR 0004 and `signal-forms-readiness.md` (prep #4).
97
+ * See ADR 0004, ADR 0021, and `signal-forms-readiness.md` (prep #4).
90
98
  */
91
99
  interface RdxFieldState {
100
+ /** Field name inferred by an adapter. An explicit `rdxFieldRoot[name]` always wins. */
101
+ name?: () => string | undefined;
92
102
  /**
93
103
  * The control's **actual** invalidity (ungated). The Field decides *when* to display it from its
94
104
  * `validationMode` (e.g. only after blur) — the adapter just reports the real state. A non-empty
95
105
  * `errors()` also counts as invalid.
96
106
  */
97
107
  invalid?: () => boolean;
108
+ /** Whether async validation is pending. Pending validity remains neutral, never falsely valid. */
109
+ pending?: () => boolean;
98
110
  disabled?: () => boolean;
99
111
  required?: () => boolean;
100
112
  dirty?: () => boolean;
@@ -108,7 +120,7 @@ interface RdxFieldState {
108
120
  * any enclosing Form's server messages. Uses `core`'s framework-free shim type so the seam stays free of
109
121
  * `@angular/forms/signals` (ADR 0004 amendment).
110
122
  */
111
- errors?: () => RdxValidationError[];
123
+ errors?: () => readonly RdxValidationError[];
112
124
  }
113
125
  interface RdxFieldRootContext {
114
126
  controlId: Signal<string>;
@@ -116,6 +128,8 @@ interface RdxFieldRootContext {
116
128
  descriptionIds: Signal<string[]>;
117
129
  errorIds: Signal<string[]>;
118
130
  messages: Signal<string[]>;
131
+ /** Whether the currently displayed client errors contain this validation kind. */
132
+ matchesError: (kind: string) => boolean;
119
133
  notifyEdited: () => void;
120
134
  /** Tri-state *displayed* validity (`true` valid / `false` invalid / `null` neutral), gated by the
121
135
  * field's `validationMode`. The source for `data-valid` / `data-invalid` on the field and its controls. */
@@ -229,6 +243,8 @@ declare class RdxFieldRoot {
229
243
  private readonly stateProvider;
230
244
  /** Whether an external adapter currently owns field state. */
231
245
  readonly hasStateProvider: Signal<boolean>;
246
+ /** Explicit field name wins; an adapter may infer one from its form-control binding. */
247
+ readonly effectiveName: Signal<string | undefined>;
232
248
  /** Error content from a registered state provider (e.g. a Signal Forms adapter). */
233
249
  private readonly providerErrors;
234
250
  /** **Client** validation errors routed by a form-level provider (`rdxSignalForm`) — gated by `validationMode`. */
@@ -246,6 +262,8 @@ declare class RdxFieldRoot {
246
262
  * field state), so deduping renders it once while still surfacing distinct messages from each source.
247
263
  */
248
264
  readonly messages: Signal<string[]>;
265
+ /** Whether a client validation error of `kind` is currently revealed by the field. */
266
+ matchesError(kind: string): boolean;
249
267
  /** Effective validation-display mode: this field's override → the enclosing Form's → the default. */
250
268
  readonly effectiveValidationMode: Signal<RdxValidationMode>;
251
269
  /** Whether client-side validity is revealed yet, per {@link effectiveValidationMode} + interaction. */
@@ -276,6 +294,7 @@ declare class RdxFieldRoot {
276
294
  * the provider/input `invalid`.
277
295
  */
278
296
  readonly actualInvalidState: Signal<boolean>;
297
+ readonly pendingState: Signal<boolean>;
279
298
  readonly disabledState: Signal<boolean>;
280
299
  readonly requiredState: Signal<boolean>;
281
300
  readonly dirtyState: Signal<boolean>;
@@ -310,8 +329,8 @@ declare class RdxFieldRoot {
310
329
  clearStateProvider(provider: RdxFieldState | null, previous: RdxFieldState | null): void;
311
330
  /**
312
331
  * Prefer the registered provider's value for `key` when it exposes one,
313
- * otherwise fall back to the root inputs / DOM-derived signals. `errors` (content) and `valid`
314
- * (tri-state `boolean | null`) are resolved separately, so they're excluded from this boolean key.
332
+ * otherwise fall back to the root inputs / DOM-derived signals. `name` and `errors` are resolved
333
+ * separately, so they're excluded from this boolean key.
315
334
  */
316
335
  private resolve;
317
336
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<RdxFieldRoot, never>;
@@ -369,5 +388,33 @@ declare class RdxFieldLabel {
369
388
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxFieldLabel, "[rdxFieldLabel]", ["rdxFieldLabel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
370
389
  }
371
390
 
372
- export { RdxFieldControl, RdxFieldDescription, RdxFieldError, RdxFieldItem, RdxFieldLabel, RdxFieldRoot, injectFieldRootContext, provideFieldRootContext };
391
+ /**
392
+ * Bridges a same-host Reactive Forms or template-driven `NgControl` into an enclosing `rdxFieldRoot`.
393
+ * Place it next to `formControl`, `formControlName`, or `ngModel`; Angular remains the source of value,
394
+ * validation, and interaction state while Field owns when that state is displayed.
395
+ *
396
+ * ```html
397
+ * <div rdxFieldRoot>
398
+ * <label rdxFieldLabel>Email</label>
399
+ * <input formControlName="email" rdxFieldControl rdxNgControlField />
400
+ * <p match="required" rdxFieldError>Email is required.</p>
401
+ * </div>
402
+ * ```
403
+ *
404
+ * The adapter also infers the Field name from `formControlName` / `ngModel` for Form-level server-error
405
+ * routing. An explicit `name` on `rdxFieldRoot` takes precedence (useful for nested or remapped keys).
406
+ *
407
+ * @group Components
408
+ */
409
+ declare class RdxNgControlField {
410
+ private readonly ngControlState;
411
+ private readonly fieldContext;
412
+ /** Normalized Angular validation errors (`{ kind, message? }[]`). */
413
+ readonly validationErrors: _angular_core.Signal<readonly _radix_ng_primitives_core.RdxValidationError[]>;
414
+ constructor();
415
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RdxNgControlField, never>;
416
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<RdxNgControlField, "[formControl][rdxNgControlField], [formControlName][rdxNgControlField], [ngModel][rdxNgControlField]", ["rdxNgControlField"], {}, {}, never, never, true, never>;
417
+ }
418
+
419
+ export { RdxFieldControl, RdxFieldDescription, RdxFieldError, RdxFieldItem, RdxFieldLabel, RdxFieldRoot, RdxNgControlField, injectFieldRootContext, provideFieldRootContext };
373
420
  export type { RdxFieldRootContext, RdxFieldState };
@@ -162,6 +162,13 @@ declare class RdxFloatingFocusManager {
162
162
  * - an element (direct or from a callback) → that element (returned **explicitly**, bypassing the
163
163
  * "focus moved elsewhere" guard).
164
164
  */
165
+ /**
166
+ * Public view of {@link resolveReturnFocusTarget} for a **keep-mounted** primitive to apply
167
+ * return-focus on *close* — the composed scope's unmount-driven return never runs while the popup
168
+ * stays in the DOM. Returns an explicit element, `false` (suppress), or `undefined` (default →
169
+ * caller restores the previously-focused element, e.g. the trigger).
170
+ */
171
+ resolveReturnTarget(): HTMLElement | false | undefined;
165
172
  private resolveReturnFocusTarget;
166
173
  /**
167
174
  * Base UI's `defaultInitialFocus`: on a **touch** open, focus the popup itself instead of its first