@radix-ng/primitives 1.0.10 → 1.1.1

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 +88 -21
  3. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs +199 -129
  5. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-combobox.mjs +203 -52
  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 +352 -24
  11. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-date-field.mjs +11 -5
  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-form.mjs +35 -4
  17. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-input.mjs +87 -15
  19. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-menu.mjs +34 -7
  21. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-number-field.mjs +38 -7
  23. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-popper.mjs +80 -11
  25. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-radio.mjs +49 -2
  27. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-scroll-area.mjs +6 -3
  29. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-select.mjs +70 -14
  31. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-signal-forms.mjs +27 -11
  33. package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-slider.mjs +17 -2
  35. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-switch.mjs +60 -7
  37. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-time-field.mjs +12 -6
  39. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-toggle-group.mjs +25 -3
  41. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  42. package/package.json +8 -3
  43. package/types/radix-ng-primitives-autocomplete.d.ts +16 -2
  44. package/types/radix-ng-primitives-checkbox.d.ts +66 -44
  45. package/types/radix-ng-primitives-combobox.d.ts +74 -3
  46. package/types/radix-ng-primitives-composite.d.ts +48 -2
  47. package/types/radix-ng-primitives-core.d.ts +212 -96
  48. package/types/radix-ng-primitives-date-field.d.ts +7 -4
  49. package/types/radix-ng-primitives-field.d.ts +55 -8
  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 +45 -4
  53. package/types/radix-ng-primitives-navigation-menu.d.ts +2 -2
  54. package/types/radix-ng-primitives-number-field.d.ts +12 -5
  55. package/types/radix-ng-primitives-popover.d.ts +2 -2
  56. package/types/radix-ng-primitives-popper.d.ts +60 -15
  57. package/types/radix-ng-primitives-preview-card.d.ts +2 -2
  58. package/types/radix-ng-primitives-radio.d.ts +6 -1
  59. package/types/radix-ng-primitives-select.d.ts +25 -4
  60. package/types/radix-ng-primitives-signal-forms.d.ts +22 -8
  61. package/types/radix-ng-primitives-slider.d.ts +2 -1
  62. package/types/radix-ng-primitives-switch.d.ts +19 -3
  63. package/types/radix-ng-primitives-time-field.d.ts +9 -6
  64. package/types/radix-ng-primitives-toggle-group.d.ts +5 -1
  65. package/types/radix-ng-primitives-tooltip.d.ts +2 -2
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DestroyRef, Directive, input, NgModule } from '@angular/core';
3
- import { FormField } from '@angular/forms/signals';
2
+ import { inject, DestroyRef, Directive, input, booleanAttribute, NgModule } from '@angular/core';
3
+ import { FormField, submit } from '@angular/forms/signals';
4
4
  import { injectFieldRootContext } from '@radix-ng/primitives/field';
5
5
  import { injectFormRootContext } from '@radix-ng/primitives/form';
6
6
 
@@ -19,9 +19,9 @@ import { injectFormRootContext } from '@radix-ng/primitives/form';
19
19
  *
20
20
  * It owns no model and runs no validation — Signal Forms remains the source of truth. It only registers
21
21
  * an {@link RdxFieldState} provider on the ancestor Field context so the field's `invalid` / `disabled` /
22
- * `required` / `dirty` / `touched` data-attributes and the error *content* (`rdxFieldError.messages()`)
23
- * read authoritative Signal Forms state. `filled` / `focused` stay on Field's DOM heuristic (partial
24
- * ownership — the seam supports it).
22
+ * `required` / `dirty` / `touched` data-attributes, tri-state pending validity, and the error *content*
23
+ * (`rdxFieldError.messages()`) read authoritative Signal Forms state. `filled` / `focused` stay on
24
+ * Field's DOM heuristic (partial ownership — the seam supports it).
25
25
  *
26
26
  * It reports the **actual** Signal Forms state only; the `Field` decides *when* to display it from its
27
27
  * `validationMode` (default `'onBlur'` — neutral until the field is touched or the form submitted). So an
@@ -42,6 +42,7 @@ class RdxSignalField {
42
42
  // adapter reports authoritative *actual* state; the Field gates the *display* by its validationMode.
43
43
  const state = {
44
44
  invalid: () => this.state().invalid(),
45
+ pending: () => this.state().pending(),
45
46
  disabled: () => this.state().disabled(),
46
47
  required: () => this.state().required(),
47
48
  dirty: () => this.state().dirty(),
@@ -81,9 +82,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
81
82
  *
82
83
  * Registers an {@link RdxFormState} provider so the Form's aggregate `data-invalid` / `data-dirty` /
83
84
  * `data-touched` / `data-submitting` attributes and the submit guard read authoritative Signal Forms
84
- * state. This adapter owns only client-side state and `name`-routing; server errors stay a separate
85
- * eager channel applied through `rdxFormRoot[errors]`, and Signal Forms' own `submit()` owns the
86
- * submit lifecycle.
85
+ * state. By default this adapter owns only client-side state and `name`-routing; server errors stay a
86
+ * separate eager channel applied through `rdxFormRoot[errors]`. Add `rdxSignalSubmit` to delegate native
87
+ * submission to Signal Forms' own `submit()` lifecycle instead of `rdxFormRoot.onFormSubmit`:
88
+ *
89
+ * ```html
90
+ * <form rdxFormRoot [rdxSignalForm]="loginForm" rdxSignalSubmit>…</form>
91
+ * ```
87
92
  *
88
93
  * `errorsFor(name)` routes a field's errors to a `rdxFieldRoot` by its `name`, walking the `FieldTree`
89
94
  * so dotted paths into nested object/array fields resolve too (`address.street`, `items.0.name`). A
@@ -92,7 +97,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
92
97
  * a field carries both, `rdxFieldError.messages()` deduplicates by text so the shared message is not
93
98
  * shown twice.
94
99
  *
95
- * See ADR 0018.
100
+ * See ADR 0018 and ADR 0020.
96
101
  *
97
102
  * @group Components
98
103
  */
@@ -100,12 +105,23 @@ class RdxSignalForm {
100
105
  constructor() {
101
106
  /** The Signal Forms root field (from `form(...)`) whose aggregate state drives the enclosing Form. */
102
107
  this.form = input.required({ ...(ngDevMode ? { debugName: "form" } : /* istanbul ignore next */ {}), alias: 'rdxSignalForm' });
108
+ /**
109
+ * Delegate native submission to Angular Signal Forms' `submit()` lifecycle. Opt-in so adding the
110
+ * state adapter to an existing 1.x form cannot silently replace its `(onFormSubmit)` side effects.
111
+ * The bound `form()` must define `submission.action`; Angular reports a descriptive error otherwise.
112
+ *
113
+ * @group Props
114
+ * @defaultValue false
115
+ */
116
+ this.signalSubmit = input(false, { ...(ngDevMode ? { debugName: "signalSubmit" } : /* istanbul ignore next */ {}), alias: 'rdxSignalSubmit',
117
+ transform: booleanAttribute });
103
118
  this.formContext = injectFormRootContext();
104
119
  const state = {
105
120
  invalid: () => this.state().invalid(),
106
121
  dirty: () => this.state().dirty(),
107
122
  touched: () => this.state().touched(),
108
123
  submitting: () => this.state().submitting(),
124
+ submit: () => (this.signalSubmit() ? submit(this.form()) : undefined),
109
125
  errorsFor: (name) => this.errorsFor(name),
110
126
  // Per-name interaction state so a name-routed field (bare `[formField]`, no `rdxSignalField`)
111
127
  // can reveal its error on blur/change under `validationMode="onBlur"`/`"onChange"`.
@@ -152,7 +168,7 @@ class RdxSignalForm {
152
168
  return this.form()();
153
169
  }
154
170
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSignalForm, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
155
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSignalForm, isStandalone: true, selector: "form[rdxFormRoot][rdxSignalForm]", inputs: { form: { classPropertyName: "form", publicName: "rdxSignalForm", isSignal: true, isRequired: true, transformFunction: null } }, exportAs: ["rdxSignalForm"], ngImport: i0 }); }
171
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSignalForm, isStandalone: true, selector: "form[rdxFormRoot][rdxSignalForm]", inputs: { form: { classPropertyName: "form", publicName: "rdxSignalForm", isSignal: true, isRequired: true, transformFunction: null }, signalSubmit: { classPropertyName: "signalSubmit", publicName: "rdxSignalSubmit", isSignal: true, isRequired: false, transformFunction: null } }, exportAs: ["rdxSignalForm"], ngImport: i0 }); }
156
172
  }
157
173
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSignalForm, decorators: [{
158
174
  type: Directive,
@@ -160,7 +176,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
160
176
  selector: 'form[rdxFormRoot][rdxSignalForm]',
161
177
  exportAs: 'rdxSignalForm'
162
178
  }]
163
- }], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "rdxSignalForm", required: true }] }] } });
179
+ }], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "rdxSignalForm", required: true }] }], signalSubmit: [{ type: i0.Input, args: [{ isSignal: true, alias: "rdxSignalSubmit", required: false }] }] } });
164
180
 
165
181
  const _importsSignalForms = [RdxSignalField, RdxSignalForm];
166
182
  class RdxSignalFormsModule {
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-signal-forms.mjs","sources":["../../../packages/primitives/signal-forms/src/signal-field.ts","../../../packages/primitives/signal-forms/src/signal-form.ts","../../../packages/primitives/signal-forms/index.ts","../../../packages/primitives/signal-forms/radix-ng-primitives-signal-forms.ts"],"sourcesContent":["import { DestroyRef, Directive, inject } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { injectFieldRootContext, RdxFieldState } from '@radix-ng/primitives/field';\n\n/**\n * Bridges an Angular **Signal Forms** field into an enclosing `rdxFieldRoot` — with **no duplicate\n * binding**. Place it on the control that already carries `[formField]`; it reads the bound field's\n * state from that directive, so the field expression is written exactly once:\n *\n * ```html\n * <div rdxFieldRoot>\n * <label rdxFieldLabel>Email</label>\n * <input rdxInput [formField]=\"loginForm.email\" rdxSignalField />\n * <p rdxFieldError>Email is required.</p>\n * </div>\n * ```\n *\n * It owns no model and runs no validation — Signal Forms remains the source of truth. It only registers\n * an {@link RdxFieldState} provider on the ancestor Field context so the field's `invalid` / `disabled` /\n * `required` / `dirty` / `touched` data-attributes and the error *content* (`rdxFieldError.messages()`)\n * read authoritative Signal Forms state. `filled` / `focused` stay on Field's DOM heuristic (partial\n * ownership — the seam supports it).\n *\n * It reports the **actual** Signal Forms state only; the `Field` decides *when* to display it from its\n * `validationMode` (default `'onBlur'` — neutral until the field is touched or the form submitted). So an\n * empty required field is neutral on load with no manual `[invalid]`/`[touched]` wiring, and a pristine\n * submit reveals the errors (the `Form` records the submit attempt before checking validity and blocks).\n *\n * See ADR 0018.\n *\n * @group Components\n */\n@Directive({\n selector: '[formField][rdxSignalField]',\n exportAs: 'rdxSignalField'\n})\nexport class RdxSignalField {\n /** The co-located `[formField]` directive — the single source of the bound field's state. */\n private readonly formField = inject(FormField);\n private readonly fieldContext = injectFieldRootContext();\n\n constructor() {\n // The accessors are evaluated lazily by Field's `*State` computeds during change detection (after\n // the `[formField]` binding resolves), so reading `state()` inside these closures is safe. The\n // adapter reports authoritative *actual* state; the Field gates the *display* by its validationMode.\n const state: RdxFieldState = {\n invalid: () => this.state().invalid(),\n disabled: () => this.state().disabled(),\n required: () => this.state().required(),\n dirty: () => this.state().dirty(),\n touched: () => this.state().touched(),\n errors: () =>\n this.state()\n .errors()\n .map((error) => ({ kind: error.kind, message: error.message }))\n };\n\n const previous = this.fieldContext.setStateProvider(state);\n // Identity-checked teardown: only roll back if our provider is still active, so a newer adapter\n // mounted during a view swap (create-before-destroy) is not clobbered by this destroy.\n inject(DestroyRef).onDestroy(() => this.fieldContext.clearStateProvider(state, previous));\n }\n\n /** Current Signal Forms field state, read from the co-located `[formField]` directive. */\n private state() {\n return this.formField.state();\n }\n}\n","import { DestroyRef, Directive, inject, input } from '@angular/core';\nimport type { FieldTree } from '@angular/forms/signals';\nimport { injectFormRootContext, RdxFormState } from '@radix-ng/primitives/form';\n\n/** A Signal Forms subfield read structurally — callable to its state (`errors()` / `touched()` / `dirty()`). */\ntype NamedField = () => {\n errors: () => readonly { kind: string; message?: string }[];\n touched: () => boolean;\n dirty: () => boolean;\n};\n\n/**\n * Bridges an Angular **Signal Forms** form into an enclosing `form[rdxFormRoot]`.\n *\n * ```html\n * <form rdxFormRoot [rdxSignalForm]=\"loginForm\">\n * <!-- fields bound with [formField] (+ rdxSignalField) -->\n * </form>\n * ```\n *\n * Registers an {@link RdxFormState} provider so the Form's aggregate `data-invalid` / `data-dirty` /\n * `data-touched` / `data-submitting` attributes and the submit guard read authoritative Signal Forms\n * state. This adapter owns only client-side state and `name`-routing; server errors stay a separate\n * eager channel applied through `rdxFormRoot[errors]`, and Signal Forms' own `submit()` owns the\n * submit lifecycle.\n *\n * `errorsFor(name)` routes a field's errors to a `rdxFieldRoot` by its `name`, walking the `FieldTree`\n * so dotted paths into nested object/array fields resolve too (`address.street`, `items.0.name`). A\n * field can surface messages from the form alone — no per-field `rdxSignalField` needed for errors.\n * These are the **two modes** (per-field adapter vs form-level name routing) — prefer one per field; if\n * a field carries both, `rdxFieldError.messages()` deduplicates by text so the shared message is not\n * shown twice.\n *\n * See ADR 0018.\n *\n * @group Components\n */\n@Directive({\n selector: 'form[rdxFormRoot][rdxSignalForm]',\n exportAs: 'rdxSignalForm'\n})\nexport class RdxSignalForm {\n /** The Signal Forms root field (from `form(...)`) whose aggregate state drives the enclosing Form. */\n readonly form = input.required<FieldTree<unknown>>({ alias: 'rdxSignalForm' });\n\n private readonly formContext = injectFormRootContext();\n\n constructor() {\n const state: RdxFormState = {\n invalid: () => this.state().invalid(),\n dirty: () => this.state().dirty(),\n touched: () => this.state().touched(),\n submitting: () => this.state().submitting(),\n errorsFor: (name) => this.errorsFor(name),\n // Per-name interaction state so a name-routed field (bare `[formField]`, no `rdxSignalField`)\n // can reveal its error on blur/change under `validationMode=\"onBlur\"`/`\"onChange\"`.\n touchedFor: (name) => this.fieldState(name)?.touched() ?? false,\n dirtyFor: (name) => this.fieldState(name)?.dirty() ?? false\n };\n\n const previous = this.formContext.setStateProvider(state);\n // Identity-checked teardown: only roll back if our provider is still active, so a newer adapter\n // mounted during a view swap (create-before-destroy) is not clobbered by this destroy.\n inject(DestroyRef).onDestroy(() => this.formContext.clearStateProvider(state, previous));\n }\n\n /**\n * Messages for the field at the dotted `name` path (`message ?? kind` per error). Walks the\n * `FieldTree` so nested object/array fields resolve too — e.g. `address.street`, `items.0.name`.\n */\n private errorsFor(name: string): string[] {\n return (\n this.fieldState(name)\n ?.errors()\n .map((error) => error.message ?? error.kind) ?? []\n );\n }\n\n /**\n * The Signal Forms field *state* at the dotted `name` path, or `null` if it doesn't resolve. Walks the\n * `FieldTree` so nested object/array fields work (`address.street`, `items.0.name`).\n */\n private fieldState(name: string): ReturnType<NamedField> | null {\n let node: unknown = this.form();\n for (const segment of name.split('.')) {\n if (node == null || (typeof node !== 'object' && typeof node !== 'function')) {\n return null;\n }\n // Only walk into own properties: a `FieldTree` exposes its subfields as own keys, so a\n // segment that is not one (`constructor`, `__proto__`, `toString`, …) is not a real field.\n // Reading it would resolve to an inherited member — and a function one would then be called\n // below — so reject it instead.\n if (!Object.hasOwn(node, segment)) {\n return null;\n }\n node = (node as Record<string, unknown>)[segment];\n }\n return typeof node === 'function' ? (node as NamedField)() : null;\n }\n\n /** The current Signal Forms root field state (a `FieldTree` is callable). */\n private state() {\n return this.form()();\n }\n}\n","import { NgModule } from '@angular/core';\nimport { RdxSignalField } from './src/signal-field';\nimport { RdxSignalForm } from './src/signal-form';\n\nexport * from './src/signal-field';\nexport * from './src/signal-form';\n\nexport const _importsSignalForms = [RdxSignalField, RdxSignalForm];\n\n@NgModule({\n imports: [..._importsSignalForms],\n exports: [..._importsSignalForms]\n})\nexport class RdxSignalFormsModule {}\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-signal-forms.mjs","sources":["../../../packages/primitives/signal-forms/src/signal-field.ts","../../../packages/primitives/signal-forms/src/signal-form.ts","../../../packages/primitives/signal-forms/index.ts","../../../packages/primitives/signal-forms/radix-ng-primitives-signal-forms.ts"],"sourcesContent":["import { DestroyRef, Directive, inject } from '@angular/core';\nimport { FormField } from '@angular/forms/signals';\nimport { injectFieldRootContext, RdxFieldState } from '@radix-ng/primitives/field';\n\n/**\n * Bridges an Angular **Signal Forms** field into an enclosing `rdxFieldRoot` — with **no duplicate\n * binding**. Place it on the control that already carries `[formField]`; it reads the bound field's\n * state from that directive, so the field expression is written exactly once:\n *\n * ```html\n * <div rdxFieldRoot>\n * <label rdxFieldLabel>Email</label>\n * <input rdxInput [formField]=\"loginForm.email\" rdxSignalField />\n * <p rdxFieldError>Email is required.</p>\n * </div>\n * ```\n *\n * It owns no model and runs no validation — Signal Forms remains the source of truth. It only registers\n * an {@link RdxFieldState} provider on the ancestor Field context so the field's `invalid` / `disabled` /\n * `required` / `dirty` / `touched` data-attributes, tri-state pending validity, and the error *content*\n * (`rdxFieldError.messages()`) read authoritative Signal Forms state. `filled` / `focused` stay on\n * Field's DOM heuristic (partial ownership — the seam supports it).\n *\n * It reports the **actual** Signal Forms state only; the `Field` decides *when* to display it from its\n * `validationMode` (default `'onBlur'` — neutral until the field is touched or the form submitted). So an\n * empty required field is neutral on load with no manual `[invalid]`/`[touched]` wiring, and a pristine\n * submit reveals the errors (the `Form` records the submit attempt before checking validity and blocks).\n *\n * See ADR 0018.\n *\n * @group Components\n */\n@Directive({\n selector: '[formField][rdxSignalField]',\n exportAs: 'rdxSignalField'\n})\nexport class RdxSignalField {\n /** The co-located `[formField]` directive — the single source of the bound field's state. */\n private readonly formField = inject(FormField);\n private readonly fieldContext = injectFieldRootContext();\n\n constructor() {\n // The accessors are evaluated lazily by Field's `*State` computeds during change detection (after\n // the `[formField]` binding resolves), so reading `state()` inside these closures is safe. The\n // adapter reports authoritative *actual* state; the Field gates the *display* by its validationMode.\n const state: RdxFieldState = {\n invalid: () => this.state().invalid(),\n pending: () => this.state().pending(),\n disabled: () => this.state().disabled(),\n required: () => this.state().required(),\n dirty: () => this.state().dirty(),\n touched: () => this.state().touched(),\n errors: () =>\n this.state()\n .errors()\n .map((error) => ({ kind: error.kind, message: error.message }))\n };\n\n const previous = this.fieldContext.setStateProvider(state);\n // Identity-checked teardown: only roll back if our provider is still active, so a newer adapter\n // mounted during a view swap (create-before-destroy) is not clobbered by this destroy.\n inject(DestroyRef).onDestroy(() => this.fieldContext.clearStateProvider(state, previous));\n }\n\n /** Current Signal Forms field state, read from the co-located `[formField]` directive. */\n private state() {\n return this.formField.state();\n }\n}\n","import { booleanAttribute, DestroyRef, Directive, inject, input } from '@angular/core';\nimport { submit, type FieldTree } from '@angular/forms/signals';\nimport { BooleanInput } from '@radix-ng/primitives/core';\nimport { injectFormRootContext, RdxFormState } from '@radix-ng/primitives/form';\n\n/** A Signal Forms subfield read structurally — callable to its state (`errors()` / `touched()` / `dirty()`). */\ntype NamedField = () => {\n errors: () => readonly { kind: string; message?: string }[];\n touched: () => boolean;\n dirty: () => boolean;\n};\n\n/**\n * Bridges an Angular **Signal Forms** form into an enclosing `form[rdxFormRoot]`.\n *\n * ```html\n * <form rdxFormRoot [rdxSignalForm]=\"loginForm\">\n * <!-- fields bound with [formField] (+ rdxSignalField) -->\n * </form>\n * ```\n *\n * Registers an {@link RdxFormState} provider so the Form's aggregate `data-invalid` / `data-dirty` /\n * `data-touched` / `data-submitting` attributes and the submit guard read authoritative Signal Forms\n * state. By default this adapter owns only client-side state and `name`-routing; server errors stay a\n * separate eager channel applied through `rdxFormRoot[errors]`. Add `rdxSignalSubmit` to delegate native\n * submission to Signal Forms' own `submit()` lifecycle instead of `rdxFormRoot.onFormSubmit`:\n *\n * ```html\n * <form rdxFormRoot [rdxSignalForm]=\"loginForm\" rdxSignalSubmit>…</form>\n * ```\n *\n * `errorsFor(name)` routes a field's errors to a `rdxFieldRoot` by its `name`, walking the `FieldTree`\n * so dotted paths into nested object/array fields resolve too (`address.street`, `items.0.name`). A\n * field can surface messages from the form alone — no per-field `rdxSignalField` needed for errors.\n * These are the **two modes** (per-field adapter vs form-level name routing) — prefer one per field; if\n * a field carries both, `rdxFieldError.messages()` deduplicates by text so the shared message is not\n * shown twice.\n *\n * See ADR 0018 and ADR 0020.\n *\n * @group Components\n */\n@Directive({\n selector: 'form[rdxFormRoot][rdxSignalForm]',\n exportAs: 'rdxSignalForm'\n})\nexport class RdxSignalForm {\n /** The Signal Forms root field (from `form(...)`) whose aggregate state drives the enclosing Form. */\n readonly form = input.required<FieldTree<unknown>>({ alias: 'rdxSignalForm' });\n\n /**\n * Delegate native submission to Angular Signal Forms' `submit()` lifecycle. Opt-in so adding the\n * state adapter to an existing 1.x form cannot silently replace its `(onFormSubmit)` side effects.\n * The bound `form()` must define `submission.action`; Angular reports a descriptive error otherwise.\n *\n * @group Props\n * @defaultValue false\n */\n readonly signalSubmit = input<boolean, BooleanInput>(false, {\n alias: 'rdxSignalSubmit',\n transform: booleanAttribute\n });\n\n private readonly formContext = injectFormRootContext();\n\n constructor() {\n const state: RdxFormState = {\n invalid: () => this.state().invalid(),\n dirty: () => this.state().dirty(),\n touched: () => this.state().touched(),\n submitting: () => this.state().submitting(),\n submit: () => (this.signalSubmit() ? submit(this.form()) : undefined),\n errorsFor: (name) => this.errorsFor(name),\n // Per-name interaction state so a name-routed field (bare `[formField]`, no `rdxSignalField`)\n // can reveal its error on blur/change under `validationMode=\"onBlur\"`/`\"onChange\"`.\n touchedFor: (name) => this.fieldState(name)?.touched() ?? false,\n dirtyFor: (name) => this.fieldState(name)?.dirty() ?? false\n };\n\n const previous = this.formContext.setStateProvider(state);\n // Identity-checked teardown: only roll back if our provider is still active, so a newer adapter\n // mounted during a view swap (create-before-destroy) is not clobbered by this destroy.\n inject(DestroyRef).onDestroy(() => this.formContext.clearStateProvider(state, previous));\n }\n\n /**\n * Messages for the field at the dotted `name` path (`message ?? kind` per error). Walks the\n * `FieldTree` so nested object/array fields resolve too — e.g. `address.street`, `items.0.name`.\n */\n private errorsFor(name: string): string[] {\n return (\n this.fieldState(name)\n ?.errors()\n .map((error) => error.message ?? error.kind) ?? []\n );\n }\n\n /**\n * The Signal Forms field *state* at the dotted `name` path, or `null` if it doesn't resolve. Walks the\n * `FieldTree` so nested object/array fields work (`address.street`, `items.0.name`).\n */\n private fieldState(name: string): ReturnType<NamedField> | null {\n let node: unknown = this.form();\n for (const segment of name.split('.')) {\n if (node == null || (typeof node !== 'object' && typeof node !== 'function')) {\n return null;\n }\n // Only walk into own properties: a `FieldTree` exposes its subfields as own keys, so a\n // segment that is not one (`constructor`, `__proto__`, `toString`, …) is not a real field.\n // Reading it would resolve to an inherited member — and a function one would then be called\n // below — so reject it instead.\n if (!Object.hasOwn(node, segment)) {\n return null;\n }\n node = (node as Record<string, unknown>)[segment];\n }\n return typeof node === 'function' ? (node as NamedField)() : null;\n }\n\n /** The current Signal Forms root field state (a `FieldTree` is callable). */\n private state() {\n return this.form()();\n }\n}\n","import { NgModule } from '@angular/core';\nimport { RdxSignalField } from './src/signal-field';\nimport { RdxSignalForm } from './src/signal-form';\n\nexport * from './src/signal-field';\nexport * from './src/signal-form';\n\nexport const _importsSignalForms = [RdxSignalField, RdxSignalForm];\n\n@NgModule({\n imports: [..._importsSignalForms],\n exports: [..._importsSignalForms]\n})\nexport class RdxSignalFormsModule {}\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, DestroyRef, Directive, computed, input, numberAttribute, booleanAttribute, model, output, signal, effect, afterNextRender, afterRenderEffect, untracked, NgModule } from '@angular/core';
3
3
  import { DOCUMENT } from '@angular/common';
4
4
  import * as i2 from '@radix-ng/primitives/core';
5
- import { createContext, clamp, RdxFormUiControlBase, injectControlValueAccessor, injectDocument, injectId, createCancelableChangeEventDetails, provideFormUiState, RdxFormUiStateHost, RdxControlValueAccessor } from '@radix-ng/primitives/core';
5
+ import { createContext, clamp, RdxFormUiControlBase, injectControlValueAccessor, injectDocument, injectId, useNativeFormControl, createCancelableChangeEventDetails, provideFormUiState, RdxFormUiStateHost, RdxControlValueAccessor } from '@radix-ng/primitives/core';
6
6
  export { clamp } from '@radix-ng/primitives/core';
7
7
  import * as i1 from '@radix-ng/primitives/composite';
8
8
  import { RdxCompositeList, RdxCompositeListItem } from '@radix-ng/primitives/composite';
@@ -787,7 +787,7 @@ class RdxSliderRoot extends RdxFormUiControlBase {
787
787
  /** Locale used for value formatting. */
788
788
  this.locale = input(/* @ts-ignore */
789
789
  ...(ngDevMode ? [undefined, { debugName: "locale" }] : /* istanbul ignore next */ []));
790
- /** Name of the hidden inputs rendered by each thumb, for form submission. */
790
+ /** Name of the native range input rendered by each thumb, for form submission. */
791
791
  this.name = input(/* @ts-ignore */
792
792
  ...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
793
793
  /** Id of the form the slider belongs to. */
@@ -837,6 +837,21 @@ class RdxSliderRoot extends RdxFormUiControlBase {
837
837
  /** @ignore */
838
838
  this.isDisabled = computed(() => !!this.cva.disabled(), /* @ts-ignore */
839
839
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
840
+ // Range thumbs already are native inputs and therefore serialize themselves. The shared layer
841
+ // owns only reset here, so the root model follows a native `<form>.reset()` as well.
842
+ this.nativeFormControl = useNativeFormControl({
843
+ name: this.name,
844
+ form: this.form,
845
+ disabled: this.isDisabled,
846
+ defaultValue: () => this.cva.value() ?? this.defaultValue() ?? this.min(),
847
+ onReset: (value) => {
848
+ this.value.set(value);
849
+ if (!this.resetNgControl(value)) {
850
+ this.cva.setValue(value);
851
+ }
852
+ this.formUi.resetInteractionState?.();
853
+ }
854
+ });
840
855
  /** @ignore */
841
856
  this.inset = computed(() => this.thumbAlignment() !== 'center', /* @ts-ignore */
842
857
  ...(ngDevMode ? [{ debugName: "inset" }] : /* istanbul ignore next */ []));