@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,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, model, input, booleanAttribute, output, signal, computed, effect, Directive, ElementRef, Renderer2, DestroyRef, NgModule } from '@angular/core';
2
+ import { inject, model, input, booleanAttribute, output, signal, computed, effect, Directive, ElementRef, DestroyRef, NgModule } from '@angular/core';
3
3
  import * as i1 from '@radix-ng/primitives/core';
4
- import { createContext, RdxFormUiControlBase, createCancelableChangeEventDetails, provideValueAccessor, provideFormUiState, RdxFormUiStateHost, RdxControlValueAccessor, RDX_FIELD_VALIDITY } from '@radix-ng/primitives/core';
4
+ import { createContext, RdxFormUiControlBase, useNativeFormControl, serializeNativeFormValue, createCancelableChangeEventDetails, provideValueAccessor, provideFormUiState, RdxFormUiStateHost, RdxControlValueAccessor, injectNgControlState, RDX_FIELD_VALIDITY } from '@radix-ng/primitives/core';
5
5
 
6
6
  const [injectCheckboxGroupContext, provideCheckboxGroupContext] = createContext('CheckboxGroupContext', 'components/checkbox');
7
7
  const groupContext = () => {
@@ -9,38 +9,45 @@ const groupContext = () => {
9
9
  return {
10
10
  value: group.value,
11
11
  allValues: group.allValues,
12
+ form: group.form,
12
13
  disabled: group.disabledState,
13
14
  parentState: group.parentState,
14
15
  controlledIds: group.controlledIds,
15
- controlId: (name) => group.controlId(name),
16
- toggleValue: (name, event) => group.toggleValue(name, event),
16
+ controlId: (value) => group.controlId(value),
17
+ toggleValue: (value, event) => group.toggleValue(value, event),
17
18
  toggleAll: (event) => group.toggleAll(event),
18
- registerChild: (name, disabled) => group.registerChild(name, disabled),
19
- registerControl: (name, id) => group.registerControl(name, id)
19
+ registerChild: (value, disabled) => group.registerChild(value, disabled),
20
+ registerControl: (value, id) => group.registerControl(value, id)
20
21
  };
21
22
  };
22
23
  let nextCheckboxGroupId = 0;
23
24
  /**
24
- * Groups a set of checkboxes that share a single array value (the names of the checked boxes).
25
+ * Groups a set of checkboxes that share a single array value (the values of the checked boxes).
25
26
  *
26
- * Each child `rdxCheckboxRoot` participates by its `name`. A child marked `parent` becomes a
27
- * "select all" checkbox whose state is derived from `allValues`.
27
+ * Each child `rdxCheckboxRoot` participates by its `value` (falling back to `name`). A child marked
28
+ * `parent` becomes a "select all" checkbox whose state is derived from `allValues`.
28
29
  */
29
30
  class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
30
31
  constructor() {
31
32
  super();
32
- /** The names of the currently checked checkboxes. Use with `onValueChange` or `[(value)]`. */
33
+ /** The values of the currently checked checkboxes. Use with `onValueChange` or `[(value)]`. */
33
34
  this.value = model([], /* @ts-ignore */
34
35
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
35
- /** The names checked initially when the group is uncontrolled. */
36
+ /** The values checked initially when the group is uncontrolled. */
36
37
  this.defaultValue = input(/* @ts-ignore */
37
38
  ...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
38
- /** All checkbox names in the group. Required for a `parent` (select-all) checkbox. */
39
+ /** All checkbox values in the group. Required for a `parent` (select-all) checkbox. */
39
40
  this.allValues = input([], /* @ts-ignore */
40
41
  ...(ngDevMode ? [{ debugName: "allValues" }] : /* istanbul ignore next */ []));
42
+ /** Native form field name. Selected values are submitted as repeated entries under this name. */
43
+ this.name = input(/* @ts-ignore */
44
+ ...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
45
+ /** Associates the group with an external form by id. */
46
+ this.form = input(/* @ts-ignore */
47
+ ...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
41
48
  /** Whether the whole group is disabled. */
42
49
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
43
- /** Emits the new array of checked names whenever the value changes. */
50
+ /** Emits the new array of checked values whenever the value changes. */
44
51
  this.onValueChange = output();
45
52
  this.disabledByCva = signal(false, /* @ts-ignore */
46
53
  ...(ngDevMode ? [{ debugName: "disabledByCva" }] : /* istanbul ignore next */ []));
@@ -64,18 +71,18 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
64
71
  this.seeded = false;
65
72
  /** Where the parent is in its mixed → on → off cycle. Reset to `mixed` on any direct child change. */
66
73
  this.parentStatus = 'mixed';
67
- /** Per-name disabled signals, so the parent can preserve disabled-but-checked children. */
68
- this.disabledByName = new Map();
74
+ /** Per-value disabled signals, so the parent can preserve disabled-but-checked children. */
75
+ this.disabledByValue = new Map();
69
76
  /** Stable group id used to derive child control ids when the consumer sets none. */
70
77
  this.elementId = `rdx-checkbox-group-${nextCheckboxGroupId++}`;
71
- /** Registered control element ids, keyed by child name. */
78
+ /** Registered control element ids, keyed by child value. */
72
79
  this.controlIds = signal({}, /* @ts-ignore */
73
80
  ...(ngDevMode ? [{ debugName: "controlIds" }] : /* istanbul ignore next */ []));
74
81
  /** The space-separated control ids in `allValues` order, for the parent's `aria-controls`. */
75
82
  this.controlledIds = computed(() => {
76
83
  const ids = this.controlIds();
77
84
  const list = this.allValues()
78
- .map((name) => ids[name])
85
+ .map((value) => ids[value])
79
86
  .filter((id) => id !== undefined);
80
87
  return list.length > 0 ? list.join(' ') : undefined;
81
88
  }, /* @ts-ignore */
@@ -87,6 +94,25 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
87
94
  this.onTouched = () => {
88
95
  /* Empty */
89
96
  };
97
+ useNativeFormControl({
98
+ name: this.name,
99
+ form: this.form,
100
+ disabled: this.disabledState,
101
+ value: this.value,
102
+ serialize: serializeNativeFormValue,
103
+ defaultValue: () => [...(this.defaultValue() ?? this.value())],
104
+ onReset: (value) => {
105
+ const next = [...value];
106
+ this.value.set(next);
107
+ if (!this.resetNgControl(next)) {
108
+ this.onChange(next);
109
+ }
110
+ this.seeded = false;
111
+ this.uncontrolledState = next;
112
+ this.parentStatus = 'mixed';
113
+ this.formUi.resetInteractionState?.();
114
+ }
115
+ });
90
116
  effect(() => {
91
117
  const defaultValue = this.defaultValue();
92
118
  if (!this.hasAppliedDefault && defaultValue !== undefined) {
@@ -99,40 +125,40 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
99
125
  formUiTouchTarget() {
100
126
  return { markAsTouched: () => this.onTouched() };
101
127
  }
102
- /** @ignore Register a child's disabled signal keyed by its name. */
103
- registerChild(name, disabled) {
104
- this.disabledByName.set(name, disabled);
128
+ /** @ignore Register a child's disabled signal keyed by its value. */
129
+ registerChild(value, disabled) {
130
+ this.disabledByValue.set(value, disabled);
105
131
  return () => {
106
- if (this.disabledByName.get(name) === disabled) {
107
- this.disabledByName.delete(name);
132
+ if (this.disabledByValue.get(value) === disabled) {
133
+ this.disabledByValue.delete(value);
108
134
  }
109
135
  };
110
136
  }
111
- /** A stable control id for a child, derived from the group id and the child name. */
112
- controlId(name) {
113
- return `${this.elementId}-${name}`;
137
+ /** A stable control id for a child, derived from the group id and item value. */
138
+ controlId(value) {
139
+ return `${this.elementId}-${value}`;
114
140
  }
115
141
  /** @ignore Register a child's control element id so the parent can list it in `aria-controls`. */
116
- registerControl(name, id) {
117
- this.controlIds.update((ids) => ({ ...ids, [name]: id }));
142
+ registerControl(value, id) {
143
+ this.controlIds.update((ids) => ({ ...ids, [value]: id }));
118
144
  return () => {
119
145
  this.controlIds.update((ids) => {
120
- if (ids[name] !== id) {
146
+ if (ids[value] !== id) {
121
147
  return ids;
122
148
  }
123
149
  const next = { ...ids };
124
- delete next[name];
150
+ delete next[value];
125
151
  return next;
126
152
  });
127
153
  };
128
154
  }
129
- /** Add/remove a single child name from the value (a direct child change). */
130
- toggleValue(name, event) {
155
+ /** Add/remove a single child value from the group value (a direct child change). */
156
+ toggleValue(value, event) {
131
157
  if (this.disabledState()) {
132
158
  return;
133
159
  }
134
160
  const current = this.value();
135
- const next = current.includes(name) ? current.filter((v) => v !== name) : [...current, name];
161
+ const next = current.includes(value) ? current.filter((item) => item !== value) : [...current, value];
136
162
  if (!this.emit(next, event)) {
137
163
  return;
138
164
  }
@@ -158,8 +184,8 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
158
184
  const allValues = this.allValues();
159
185
  const remembered = this.uncontrolledState;
160
186
  // Disabled children that were checked stay checked through every transition.
161
- const none = allValues.filter((name) => this.isNameDisabled(name) && remembered.includes(name));
162
- const all = allValues.filter((name) => !this.isNameDisabled(name) || remembered.includes(name));
187
+ const none = allValues.filter((value) => this.isValueDisabled(value) && remembered.includes(value));
188
+ const all = allValues.filter((value) => !this.isValueDisabled(value) || remembered.includes(value));
163
189
  const rememberedIsAllOrNone = remembered.length === all.length || remembered.length === 0;
164
190
  if (rememberedIsAllOrNone) {
165
191
  this.emit(this.value().length === all.length ? none : all, event);
@@ -180,8 +206,8 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
180
206
  }
181
207
  this.parentStatus = nextStatus;
182
208
  }
183
- isNameDisabled(name) {
184
- return this.disabledByName.get(name)?.() ?? false;
209
+ isValueDisabled(value) {
210
+ return this.disabledByValue.get(value)?.() ?? false;
185
211
  }
186
212
  /** Seed the remembered selection from the current value the first time the parent is used. */
187
213
  ensureSeeded() {
@@ -220,7 +246,7 @@ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
220
246
  this.disabledByCva.set(isDisabled);
221
247
  }
222
248
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
223
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxGroupDirective, isStandalone: true, selector: "[rdxCheckboxGroup]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, allValues: { classPropertyName: "allValues", publicName: "allValues", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [
249
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxGroupDirective, isStandalone: true, selector: "[rdxCheckboxGroup]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, allValues: { classPropertyName: "allValues", publicName: "allValues", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [
224
250
  provideValueAccessor(RdxCheckboxGroupDirective),
225
251
  provideCheckboxGroupContext(groupContext),
226
252
  provideFormUiState(() => inject(RdxCheckboxGroupDirective).formUi)
@@ -242,7 +268,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
242
268
  '[attr.data-disabled]': 'disabledState() ? "" : undefined'
243
269
  }
244
270
  }]
245
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], allValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "allValues", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
271
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], allValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "allValues", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }] } });
246
272
 
247
273
  function isIndeterminate(checked) {
248
274
  return checked === 'indeterminate';
@@ -259,10 +285,11 @@ const rootContext = () => {
259
285
  indeterminate: checkbox.indeterminateState,
260
286
  disabled: checkbox.disabledState,
261
287
  required: checkbox.required,
262
- value: checkbox.submitValue,
263
- name: checkbox.name,
288
+ value: checkbox.nativeValue,
289
+ name: checkbox.nativeName,
290
+ itemValue: checkbox.itemValue,
264
291
  parent: checkbox.parent,
265
- form: checkbox.form,
292
+ form: checkbox.nativeForm,
266
293
  readonly: checkbox.readOnlyState,
267
294
  state: checkbox.state,
268
295
  invalidState: checkbox.invalidState,
@@ -270,6 +297,7 @@ const rootContext = () => {
270
297
  touchedState: checkbox.touchedState,
271
298
  dirtyState: checkbox.dirtyState,
272
299
  uncheckedValue: checkbox.uncheckedValue,
300
+ registerNativeInput: (input) => checkbox.registerNativeInput(input),
273
301
  toggle(event) {
274
302
  checkbox.toggle(event);
275
303
  }
@@ -282,11 +310,11 @@ const [injectCheckboxRootContext, provideCheckboxRootContext] = createContext('C
282
310
  class RdxCheckboxRootDirective {
283
311
  constructor() {
284
312
  this.controlValueAccessor = inject(RdxControlValueAccessor);
285
- this.elementRef = inject(ElementRef);
286
- this.renderer = inject(Renderer2);
287
- this.uncheckedInputElement = null;
313
+ this.ngControlState = injectNgControlState();
288
314
  /** The group this checkbox belongs to, if it is rendered inside a `rdxCheckboxGroup`. */
289
315
  this.group = injectCheckboxGroupContext(true);
316
+ this.nativeInput = signal(null, /* @ts-ignore */
317
+ ...(ngDevMode ? [{ debugName: "nativeInput" }] : /* istanbul ignore next */ []));
290
318
  /**
291
319
  * The controlled checked state of the checkbox. Must be used in conjunction with onCheckedChange.
292
320
  *
@@ -318,11 +346,12 @@ class RdxCheckboxRootDirective {
318
346
  * Bound publicly as `[value]`; the TS member is named `submitValue` so the
319
347
  * directive can satisfy `RdxFormCheckboxControl`, whose contract reserves a
320
348
  * `value` member for `RdxFormValueControl` and forbids it on checkbox-style
321
- * controls. (Checkbox is not yet marked `implements` — its `checked` is still
322
- * `CheckedState`; see the `indeterminate` half of collision #1.)
349
+ * controls.
350
+ * When omitted, native submission uses the browser checkbox default (`on`). Inside a
351
+ * `rdxCheckboxGroup`, the child's `name` is used as a compatibility fallback.
323
352
  * @group Props
324
353
  */
325
- this.submitValue = input('on', { ...(ngDevMode ? { debugName: "submitValue" } : /* istanbul ignore next */ {}), alias: 'value' });
354
+ this.submitValue = input(undefined, { ...(ngDevMode ? { debugName: "submitValue" } : /* istanbul ignore next */ {}), alias: 'value' });
326
355
  /**
327
356
  * The value submitted with the form when the checkbox is unchecked.
328
357
  * By default, unchecked checkboxes do not submit any value, matching native checkbox behavior.
@@ -352,6 +381,8 @@ class RdxCheckboxRootDirective {
352
381
  * @group Props
353
382
  */
354
383
  this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
384
+ /** Whether async validation is pending. Pending checkboxes publish neither valid nor invalid state. */
385
+ this.pending = input(false, { ...(ngDevMode ? { debugName: "pending" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
355
386
  /**
356
387
  * Whether the checkbox has been touched. A `model()` so Signal Forms can write it; the checkbox
357
388
  * also sets it on toggle (and emits {@link touch}).
@@ -371,8 +402,8 @@ class RdxCheckboxRootDirective {
371
402
  this.errors = input([], /* @ts-ignore */
372
403
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
373
404
  /**
374
- * Name of the form control. Submitted with the form as part of a name/value pair. Inside a
375
- * `rdxCheckboxGroup` this also identifies the checkbox in the group's value array.
405
+ * Name of a standalone form control. Inside a `rdxCheckboxGroup`, prefer `[value]` to identify
406
+ * the item; `name` remains a compatibility fallback for existing group templates.
376
407
  * @group Props
377
408
  */
378
409
  this.name = input(/* @ts-ignore */
@@ -389,6 +420,18 @@ class RdxCheckboxRootDirective {
389
420
  */
390
421
  this.form = input(/* @ts-ignore */
391
422
  ...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
423
+ /** @ignore Item identity inside a checkbox group (`value`, falling back to the legacy child name). */
424
+ this.itemValue = computed(() => this.submitValue() ?? this.name(), /* @ts-ignore */
425
+ ...(ngDevMode ? [{ debugName: "itemValue" }] : /* istanbul ignore next */ []));
426
+ /** @ignore Native checkbox value; browsers default an omitted checkbox value to `on`. */
427
+ this.nativeValue = computed(() => this.submitValue() ?? 'on', /* @ts-ignore */
428
+ ...(ngDevMode ? [{ debugName: "nativeValue" }] : /* istanbul ignore next */ []));
429
+ /** @ignore A checkbox group owns the successful-control name and serializes its children centrally. */
430
+ this.nativeName = computed(() => (this.group || this.parent() ? undefined : this.name()), /* @ts-ignore */
431
+ ...(ngDevMode ? [{ debugName: "nativeName" }] : /* istanbul ignore next */ []));
432
+ /** @ignore Child native inputs inherit an external form association from their checkbox group. */
433
+ this.nativeForm = computed(() => this.group?.form() ?? this.form(), /* @ts-ignore */
434
+ ...(ngDevMode ? [{ debugName: "nativeForm" }] : /* istanbul ignore next */ []));
392
435
  /**
393
436
  * Event emitted when the checkbox checked state changes.
394
437
  * @group Emits
@@ -402,8 +445,8 @@ class RdxCheckboxRootDirective {
402
445
  /**
403
446
  * @ignore
404
447
  * The effective checked state as a `boolean`. Inside a `rdxCheckboxGroup` it is derived from the
405
- * group (a `parent` checkbox is checked only when every child is; a child from whether its `name`
406
- * is in the group value); standalone it reads the CVA value.
448
+ * group (a `parent` checkbox is checked only when every child is; a child from whether its item
449
+ * value is in the group value); standalone it reads the CVA value.
407
450
  */
408
451
  this.checkedState = computed(() => {
409
452
  const group = this.group;
@@ -411,9 +454,9 @@ class RdxCheckboxRootDirective {
411
454
  if (this.parent()) {
412
455
  return group.parentState() === true;
413
456
  }
414
- const name = this.name();
415
- if (name !== undefined) {
416
- return group.value().includes(name);
457
+ const value = this.itemValue();
458
+ if (value !== undefined) {
459
+ return group.value().includes(value);
417
460
  }
418
461
  }
419
462
  return !!this.controlValueAccessor.value();
@@ -439,9 +482,21 @@ class RdxCheckboxRootDirective {
439
482
  ...(ngDevMode ? [{ debugName: "readOnlyState" }] : /* istanbul ignore next */ []));
440
483
  this.dirtyValue = signal(false, /* @ts-ignore */
441
484
  ...(ngDevMode ? [{ debugName: "dirtyValue" }] : /* istanbul ignore next */ []));
485
+ /** Validation errors from the checkbox input and a same-host Reactive/template-driven form control. */
486
+ this.validationErrors = computed(() => {
487
+ const ownErrors = this.errors() ?? [];
488
+ const ngControlErrors = this.ngControlState.connected() ? this.ngControlState.errors() : [];
489
+ return ngControlErrors.length > 0 ? [...ownErrors, ...ngControlErrors] : ownErrors;
490
+ }, /* @ts-ignore */
491
+ ...(ngDevMode ? [{ debugName: "validationErrors" }] : /* istanbul ignore next */ []));
442
492
  /** @ignore Invalid when the `invalid` input is set or the `errors` list is non-empty. */
443
- this.invalidState = computed(() => this.invalid() || (this.errors()?.length ?? 0) > 0, /* @ts-ignore */
493
+ this.invalidState = computed(() => this.invalid() ||
494
+ this.validationErrors().length > 0 ||
495
+ Boolean(this.ngControlState.connected() && this.ngControlState.invalid()), /* @ts-ignore */
444
496
  ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
497
+ /** @ignore Whether explicit or Angular-owned async validation is pending. */
498
+ this.pendingState = computed(() => this.pending() || Boolean(this.ngControlState.connected() && this.ngControlState.pending()), /* @ts-ignore */
499
+ ...(ngDevMode ? [{ debugName: "pendingState" }] : /* istanbul ignore next */ []));
445
500
  /** Tri-state display validity exposed by an enclosing Field; absent when standalone. */
446
501
  this.fieldValidity = inject(RDX_FIELD_VALIDITY, { optional: true });
447
502
  /**
@@ -450,23 +505,69 @@ class RdxCheckboxRootDirective {
450
505
  * `rdxCheckboxGroup` the group owns the field relationship, so the item keeps its own binary validity
451
506
  * (the field's invalid must not paint every checkbox red).
452
507
  */
453
- this.displayValid = computed(() => !this.group && this.fieldValidity ? this.fieldValidity() : this.invalidState() ? false : true, /* @ts-ignore */
508
+ this.displayValid = computed(() => {
509
+ if (!this.group && this.fieldValidity) {
510
+ return this.fieldValidity();
511
+ }
512
+ if (this.pendingState()) {
513
+ return null;
514
+ }
515
+ if (this.invalidState()) {
516
+ return false;
517
+ }
518
+ if (this.ngControlState.connected() && this.ngControlState.disabled()) {
519
+ return null;
520
+ }
521
+ return true;
522
+ }, /* @ts-ignore */
454
523
  ...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
455
524
  /** @ignore */
456
- this.touchedState = computed(() => this.touched(), /* @ts-ignore */
525
+ this.touchedState = computed(() => this.ngControlState.connected() ? this.ngControlState.touched() : this.touched(), /* @ts-ignore */
457
526
  ...(ngDevMode ? [{ debugName: "touchedState" }] : /* istanbul ignore next */ []));
458
527
  /** @ignore */
459
- this.dirtyState = computed(() => this.dirty() || this.dirtyValue(), /* @ts-ignore */
528
+ this.dirtyState = computed(() => this.ngControlState.connected() ? this.ngControlState.dirty() : this.dirty() || this.dirtyValue(), /* @ts-ignore */
460
529
  ...(ngDevMode ? [{ debugName: "dirtyState" }] : /* istanbul ignore next */ []));
461
530
  this.state = computed(() => this.indeterminateState() ? 'indeterminate' : this.checkedState() ? 'checked' : 'unchecked', /* @ts-ignore */
462
531
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
463
- // Inside a group, register this child's name and its own disabled state so a `parent`
532
+ this.nativeInputOwnsValue = computed(() => this.nativeInput() !== null && (this.checkedState() || this.uncheckedValue() === undefined), /* @ts-ignore */
533
+ ...(ngDevMode ? [{ debugName: "nativeInputOwnsValue" }] : /* istanbul ignore next */ []));
534
+ useNativeFormControl({
535
+ name: this.nativeName,
536
+ form: this.nativeForm,
537
+ disabled: this.disabledState,
538
+ value: this.checkedState,
539
+ serialize: (checked) => {
540
+ const value = checked ? this.nativeValue() : this.uncheckedValue();
541
+ return value === undefined ? [] : [value];
542
+ },
543
+ hasNativeControl: this.nativeInputOwnsValue,
544
+ syncNativeControl: () => {
545
+ const input = this.nativeInput();
546
+ if (input) {
547
+ input.checked = this.checkedState();
548
+ input.indeterminate = this.indeterminateState();
549
+ }
550
+ },
551
+ defaultValue: () => this.defaultChecked() ?? this.checkedState(),
552
+ onReset: (value) => {
553
+ if (this.group || this.parent()) {
554
+ return;
555
+ }
556
+ this.indeterminate.set(false);
557
+ this.checked.set(value);
558
+ if (!this.ngControlState.reset(value)) {
559
+ this.controlValueAccessor.writeValue(value);
560
+ }
561
+ this.reset();
562
+ }
563
+ });
564
+ // Inside a group, register this child's item value and its own disabled state so a `parent`
464
565
  // checkbox can preserve disabled-but-checked children when selecting/deselecting all.
465
566
  effect((onCleanup) => {
466
567
  const group = this.group;
467
- const name = this.name();
468
- if (group && !this.parent() && name !== undefined) {
469
- onCleanup(group.registerChild(name, this.controlValueAccessor.disabled));
568
+ const value = this.itemValue();
569
+ if (group && !this.parent() && value !== undefined) {
570
+ onCleanup(group.registerChild(value, this.controlValueAccessor.disabled));
470
571
  }
471
572
  });
472
573
  let hasAppliedDefault = false;
@@ -478,12 +579,20 @@ class RdxCheckboxRootDirective {
478
579
  this.controlValueAccessor.setValue(defaultChecked);
479
580
  }
480
581
  });
582
+ // Reactive/template-driven forms own their NgControl interaction state. Signal Forms instead
583
+ // writes the public touched/dirty members, so standalone controls retain the existing fallback.
481
584
  effect(() => {
482
- this.syncUncheckedInput();
483
- });
484
- inject(DestroyRef).onDestroy(() => {
485
- this.removeUncheckedInput();
486
- });
585
+ if (this.ngControlState.connected()) {
586
+ this.touched.set(this.ngControlState.touched());
587
+ if (!this.ngControlState.dirty()) {
588
+ this.dirtyValue.set(false);
589
+ }
590
+ return;
591
+ }
592
+ if (!this.dirty()) {
593
+ this.dirtyValue.set(false);
594
+ }
595
+ }, { debugName: 'RdxCheckboxRoot.syncInteractionState' });
487
596
  }
488
597
  toggle(event) {
489
598
  if (this.disabledState() || this.readOnlyState()) {
@@ -495,9 +604,9 @@ class RdxCheckboxRootDirective {
495
604
  group.toggleAll(event);
496
605
  return;
497
606
  }
498
- const name = this.name();
499
- if (name !== undefined) {
500
- group.toggleValue(name, event);
607
+ const value = this.itemValue();
608
+ if (value !== undefined) {
609
+ group.toggleValue(value, event);
501
610
  return;
502
611
  }
503
612
  }
@@ -523,63 +632,22 @@ class RdxCheckboxRootDirective {
523
632
  this.touched.set(true);
524
633
  this.touch.emit();
525
634
  }
526
- syncUncheckedInput() {
527
- const name = this.name();
528
- const uncheckedValue = this.uncheckedValue();
529
- const shouldRender = !this.checkedState() && !this.group && !this.parent() && name !== undefined && uncheckedValue !== undefined;
530
- if (!shouldRender) {
531
- this.removeUncheckedInput();
532
- return;
533
- }
534
- const input = this.ensureUncheckedInput();
535
- this.renderer.setAttribute(input, 'type', 'hidden');
536
- this.renderer.setAttribute(input, 'name', name);
537
- this.renderer.setAttribute(input, 'value', uncheckedValue);
538
- this.setOptionalAttribute(input, 'form', this.form());
539
- this.setBooleanAttribute(input, 'disabled', this.disabledState());
635
+ /** Reset control-owned interaction state; Angular Signal Forms calls this from `FieldState.reset()`. */
636
+ reset() {
637
+ this.touched.set(false);
638
+ this.dirtyValue.set(false);
540
639
  }
541
- ensureUncheckedInput() {
542
- if (this.uncheckedInputElement) {
543
- return this.uncheckedInputElement;
544
- }
545
- const host = this.elementRef.nativeElement;
546
- const parent = host.parentNode;
547
- const input = this.renderer.createElement('input');
548
- if (parent) {
549
- this.renderer.insertBefore(parent, input, host.nextSibling);
550
- }
551
- this.uncheckedInputElement = input;
552
- return input;
553
- }
554
- removeUncheckedInput() {
555
- const input = this.uncheckedInputElement;
556
- if (!input) {
557
- return;
558
- }
559
- const parent = input.parentNode;
560
- if (parent) {
561
- this.renderer.removeChild(parent, input);
562
- }
563
- this.uncheckedInputElement = null;
564
- }
565
- setOptionalAttribute(element, name, value) {
566
- if (value) {
567
- this.renderer.setAttribute(element, name, value);
568
- }
569
- else {
570
- this.renderer.removeAttribute(element, name);
571
- }
572
- }
573
- setBooleanAttribute(element, name, value) {
574
- if (value) {
575
- this.renderer.setAttribute(element, name, '');
576
- }
577
- else {
578
- this.renderer.removeAttribute(element, name);
579
- }
640
+ /** @ignore Register the optional native checkbox so it owns checked-value serialization. */
641
+ registerNativeInput(input) {
642
+ this.nativeInput.set(input);
643
+ return () => {
644
+ if (this.nativeInput() === input) {
645
+ this.nativeInput.set(null);
646
+ }
647
+ };
580
648
  }
581
649
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
582
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, defaultChecked: { classPropertyName: "defaultChecked", publicName: "defaultChecked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, submitValue: { classPropertyName: "submitValue", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, uncheckedValue: { classPropertyName: "uncheckedValue", publicName: "uncheckedValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", indeterminate: "indeterminateChange", touched: "touchedChange", onCheckedChange: "onCheckedChange", touch: "touch" }, host: { properties: { "attr.data-checked": "checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-unchecked": "!checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-indeterminate": "indeterminateState() ? \"\" : undefined", "attr.data-disabled": "disabledState() ? \"\" : undefined", "attr.data-readonly": "readOnlyState() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined", "attr.data-invalid": "displayValid() === false ? \"\" : undefined", "attr.data-valid": "displayValid() === true ? \"\" : undefined", "attr.data-touched": "touchedState() ? \"\" : undefined", "attr.data-dirty": "dirtyState() ? \"\" : undefined" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
650
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, defaultChecked: { classPropertyName: "defaultChecked", publicName: "defaultChecked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, submitValue: { classPropertyName: "submitValue", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, uncheckedValue: { classPropertyName: "uncheckedValue", publicName: "uncheckedValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, pending: { classPropertyName: "pending", publicName: "pending", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", indeterminate: "indeterminateChange", touched: "touchedChange", onCheckedChange: "onCheckedChange", touch: "touch" }, host: { properties: { "attr.data-checked": "checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-unchecked": "!checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-indeterminate": "indeterminateState() ? \"\" : undefined", "attr.data-disabled": "disabledState() ? \"\" : undefined", "attr.data-readonly": "readOnlyState() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined", "attr.data-invalid": "displayValid() === false ? \"\" : undefined", "attr.data-valid": "displayValid() === true ? \"\" : undefined", "attr.data-touched": "touchedState() ? \"\" : undefined", "attr.data-dirty": "dirtyState() ? \"\" : undefined" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
583
651
  }
584
652
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxRootDirective, decorators: [{
585
653
  type: Directive,
@@ -605,7 +673,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
605
673
  '[attr.data-dirty]': 'dirtyState() ? "" : undefined'
606
674
  }
607
675
  }]
608
- }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], defaultChecked: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultChecked", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], submitValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], uncheckedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "uncheckedValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], parent: [{ type: i0.Input, args: [{ isSignal: true, alias: "parent", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], onCheckedChange: [{ type: i0.Output, args: ["onCheckedChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
676
+ }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], defaultChecked: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultChecked", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], submitValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], uncheckedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "uncheckedValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], pending: [{ type: i0.Input, args: [{ isSignal: true, alias: "pending", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], parent: [{ type: i0.Input, args: [{ isSignal: true, alias: "parent", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], onCheckedChange: [{ type: i0.Output, args: ["onCheckedChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
609
677
 
610
678
  /**
611
679
  * Directive: rdxCheckboxButton
@@ -625,15 +693,15 @@ class RdxCheckboxButtonDirective {
625
693
  // `aria-controls`. Use the consumer's id when present, otherwise derive a stable one.
626
694
  effect((onCleanup) => {
627
695
  const group = this.group;
628
- const name = this.rootContext.name();
629
- if (!group || this.rootContext.parent() || name === undefined) {
696
+ const value = this.rootContext.itemValue();
697
+ if (!group || this.rootContext.parent() || value === undefined) {
630
698
  return;
631
699
  }
632
700
  const el = this.elementRef.nativeElement;
633
701
  if (!el.id) {
634
- el.id = group.controlId(name);
702
+ el.id = group.controlId(value);
635
703
  }
636
- onCleanup(group.registerControl(name, el.id));
704
+ onCleanup(group.registerControl(value, el.id));
637
705
  });
638
706
  }
639
707
  clicked(event) {
@@ -715,6 +783,8 @@ class RdxCheckboxInputDirective {
715
783
  constructor() {
716
784
  this.rootContext = injectCheckboxRootContext();
717
785
  this.input = inject(ElementRef).nativeElement;
786
+ const unregister = this.rootContext.registerNativeInput(this.input);
787
+ inject(DestroyRef).onDestroy(unregister);
718
788
  let isInitial = true;
719
789
  /**
720
790
  * Keeps the hidden native input in sync so form submission, native