@radix-ng/primitives 1.1.0 → 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 (31) hide show
  1. package/fesm2022/radix-ng-primitives-autocomplete.mjs +66 -7
  2. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-checkbox.mjs +147 -125
  4. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-combobox.mjs +171 -37
  6. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-core.mjs +173 -5
  8. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-number-field.mjs +38 -7
  10. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  11. package/fesm2022/radix-ng-primitives-radio.mjs +49 -2
  12. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-select.mjs +35 -3
  14. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-slider.mjs +17 -2
  16. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-switch.mjs +60 -7
  18. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-toggle-group.mjs +25 -3
  20. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  21. package/package.json +1 -1
  22. package/types/radix-ng-primitives-autocomplete.d.ts +10 -1
  23. package/types/radix-ng-primitives-checkbox.d.ts +56 -43
  24. package/types/radix-ng-primitives-combobox.d.ts +46 -2
  25. package/types/radix-ng-primitives-core.d.ts +62 -4
  26. package/types/radix-ng-primitives-number-field.d.ts +12 -5
  27. package/types/radix-ng-primitives-radio.d.ts +6 -1
  28. package/types/radix-ng-primitives-select.d.ts +8 -1
  29. package/types/radix-ng-primitives-slider.d.ts +2 -1
  30. package/types/radix-ng-primitives-switch.d.ts +19 -3
  31. package/types/radix-ng-primitives-toggle-group.d.ts +5 -1
@@ -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, injectNgControlState, 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
  }
@@ -283,11 +311,10 @@ class RdxCheckboxRootDirective {
283
311
  constructor() {
284
312
  this.controlValueAccessor = inject(RdxControlValueAccessor);
285
313
  this.ngControlState = injectNgControlState();
286
- this.elementRef = inject(ElementRef);
287
- this.renderer = inject(Renderer2);
288
- this.uncheckedInputElement = null;
289
314
  /** The group this checkbox belongs to, if it is rendered inside a `rdxCheckboxGroup`. */
290
315
  this.group = injectCheckboxGroupContext(true);
316
+ this.nativeInput = signal(null, /* @ts-ignore */
317
+ ...(ngDevMode ? [{ debugName: "nativeInput" }] : /* istanbul ignore next */ []));
291
318
  /**
292
319
  * The controlled checked state of the checkbox. Must be used in conjunction with onCheckedChange.
293
320
  *
@@ -319,11 +346,12 @@ class RdxCheckboxRootDirective {
319
346
  * Bound publicly as `[value]`; the TS member is named `submitValue` so the
320
347
  * directive can satisfy `RdxFormCheckboxControl`, whose contract reserves a
321
348
  * `value` member for `RdxFormValueControl` and forbids it on checkbox-style
322
- * controls. (Checkbox is not yet marked `implements` — its `checked` is still
323
- * `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.
324
352
  * @group Props
325
353
  */
326
- 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' });
327
355
  /**
328
356
  * The value submitted with the form when the checkbox is unchecked.
329
357
  * By default, unchecked checkboxes do not submit any value, matching native checkbox behavior.
@@ -374,8 +402,8 @@ class RdxCheckboxRootDirective {
374
402
  this.errors = input([], /* @ts-ignore */
375
403
  ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
376
404
  /**
377
- * Name of the form control. Submitted with the form as part of a name/value pair. Inside a
378
- * `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.
379
407
  * @group Props
380
408
  */
381
409
  this.name = input(/* @ts-ignore */
@@ -392,6 +420,18 @@ class RdxCheckboxRootDirective {
392
420
  */
393
421
  this.form = input(/* @ts-ignore */
394
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 */ []));
395
435
  /**
396
436
  * Event emitted when the checkbox checked state changes.
397
437
  * @group Emits
@@ -405,8 +445,8 @@ class RdxCheckboxRootDirective {
405
445
  /**
406
446
  * @ignore
407
447
  * The effective checked state as a `boolean`. Inside a `rdxCheckboxGroup` it is derived from the
408
- * group (a `parent` checkbox is checked only when every child is; a child from whether its `name`
409
- * 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.
410
450
  */
411
451
  this.checkedState = computed(() => {
412
452
  const group = this.group;
@@ -414,9 +454,9 @@ class RdxCheckboxRootDirective {
414
454
  if (this.parent()) {
415
455
  return group.parentState() === true;
416
456
  }
417
- const name = this.name();
418
- if (name !== undefined) {
419
- return group.value().includes(name);
457
+ const value = this.itemValue();
458
+ if (value !== undefined) {
459
+ return group.value().includes(value);
420
460
  }
421
461
  }
422
462
  return !!this.controlValueAccessor.value();
@@ -489,13 +529,45 @@ class RdxCheckboxRootDirective {
489
529
  ...(ngDevMode ? [{ debugName: "dirtyState" }] : /* istanbul ignore next */ []));
490
530
  this.state = computed(() => this.indeterminateState() ? 'indeterminate' : this.checkedState() ? 'checked' : 'unchecked', /* @ts-ignore */
491
531
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
492
- // 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`
493
565
  // checkbox can preserve disabled-but-checked children when selecting/deselecting all.
494
566
  effect((onCleanup) => {
495
567
  const group = this.group;
496
- const name = this.name();
497
- if (group && !this.parent() && name !== undefined) {
498
- 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));
499
571
  }
500
572
  });
501
573
  let hasAppliedDefault = false;
@@ -507,9 +579,6 @@ class RdxCheckboxRootDirective {
507
579
  this.controlValueAccessor.setValue(defaultChecked);
508
580
  }
509
581
  });
510
- effect(() => {
511
- this.syncUncheckedInput();
512
- });
513
582
  // Reactive/template-driven forms own their NgControl interaction state. Signal Forms instead
514
583
  // writes the public touched/dirty members, so standalone controls retain the existing fallback.
515
584
  effect(() => {
@@ -524,9 +593,6 @@ class RdxCheckboxRootDirective {
524
593
  this.dirtyValue.set(false);
525
594
  }
526
595
  }, { debugName: 'RdxCheckboxRoot.syncInteractionState' });
527
- inject(DestroyRef).onDestroy(() => {
528
- this.removeUncheckedInput();
529
- });
530
596
  }
531
597
  toggle(event) {
532
598
  if (this.disabledState() || this.readOnlyState()) {
@@ -538,9 +604,9 @@ class RdxCheckboxRootDirective {
538
604
  group.toggleAll(event);
539
605
  return;
540
606
  }
541
- const name = this.name();
542
- if (name !== undefined) {
543
- group.toggleValue(name, event);
607
+ const value = this.itemValue();
608
+ if (value !== undefined) {
609
+ group.toggleValue(value, event);
544
610
  return;
545
611
  }
546
612
  }
@@ -571,60 +637,14 @@ class RdxCheckboxRootDirective {
571
637
  this.touched.set(false);
572
638
  this.dirtyValue.set(false);
573
639
  }
574
- syncUncheckedInput() {
575
- const name = this.name();
576
- const uncheckedValue = this.uncheckedValue();
577
- const shouldRender = !this.checkedState() && !this.group && !this.parent() && name !== undefined && uncheckedValue !== undefined;
578
- if (!shouldRender) {
579
- this.removeUncheckedInput();
580
- return;
581
- }
582
- const input = this.ensureUncheckedInput();
583
- this.renderer.setAttribute(input, 'type', 'hidden');
584
- this.renderer.setAttribute(input, 'name', name);
585
- this.renderer.setAttribute(input, 'value', uncheckedValue);
586
- this.setOptionalAttribute(input, 'form', this.form());
587
- this.setBooleanAttribute(input, 'disabled', this.disabledState());
588
- }
589
- ensureUncheckedInput() {
590
- if (this.uncheckedInputElement) {
591
- return this.uncheckedInputElement;
592
- }
593
- const host = this.elementRef.nativeElement;
594
- const parent = host.parentNode;
595
- const input = this.renderer.createElement('input');
596
- if (parent) {
597
- this.renderer.insertBefore(parent, input, host.nextSibling);
598
- }
599
- this.uncheckedInputElement = input;
600
- return input;
601
- }
602
- removeUncheckedInput() {
603
- const input = this.uncheckedInputElement;
604
- if (!input) {
605
- return;
606
- }
607
- const parent = input.parentNode;
608
- if (parent) {
609
- this.renderer.removeChild(parent, input);
610
- }
611
- this.uncheckedInputElement = null;
612
- }
613
- setOptionalAttribute(element, name, value) {
614
- if (value) {
615
- this.renderer.setAttribute(element, name, value);
616
- }
617
- else {
618
- this.renderer.removeAttribute(element, name);
619
- }
620
- }
621
- setBooleanAttribute(element, name, value) {
622
- if (value) {
623
- this.renderer.setAttribute(element, name, '');
624
- }
625
- else {
626
- this.renderer.removeAttribute(element, name);
627
- }
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
+ };
628
648
  }
629
649
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
630
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 }); }
@@ -673,15 +693,15 @@ class RdxCheckboxButtonDirective {
673
693
  // `aria-controls`. Use the consumer's id when present, otherwise derive a stable one.
674
694
  effect((onCleanup) => {
675
695
  const group = this.group;
676
- const name = this.rootContext.name();
677
- if (!group || this.rootContext.parent() || name === undefined) {
696
+ const value = this.rootContext.itemValue();
697
+ if (!group || this.rootContext.parent() || value === undefined) {
678
698
  return;
679
699
  }
680
700
  const el = this.elementRef.nativeElement;
681
701
  if (!el.id) {
682
- el.id = group.controlId(name);
702
+ el.id = group.controlId(value);
683
703
  }
684
- onCleanup(group.registerControl(name, el.id));
704
+ onCleanup(group.registerControl(value, el.id));
685
705
  });
686
706
  }
687
707
  clicked(event) {
@@ -763,6 +783,8 @@ class RdxCheckboxInputDirective {
763
783
  constructor() {
764
784
  this.rootContext = injectCheckboxRootContext();
765
785
  this.input = inject(ElementRef).nativeElement;
786
+ const unregister = this.rootContext.registerNativeInput(this.input);
787
+ inject(DestroyRef).onDestroy(unregister);
766
788
  let isInitial = true;
767
789
  /**
768
790
  * Keeps the hidden native input in sync so form submission, native