@radix-ng/primitives 0.51.0 → 1.0.0-beta.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 (186) hide show
  1. package/fesm2022/radix-ng-primitives-accordion.mjs +105 -38
  2. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +221 -129
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-arrow.mjs +20 -4
  6. package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-avatar.mjs +54 -61
  9. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-button.mjs +123 -0
  11. package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
  12. package/fesm2022/radix-ng-primitives-calendar.mjs +95 -83
  13. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-checkbox.mjs +378 -54
  15. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-collapsible.mjs +182 -81
  17. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-collection.mjs +40 -57
  19. package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-context-menu.mjs +140 -424
  22. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-core.mjs +845 -744
  24. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-cropper.mjs +288 -308
  26. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-date-field.mjs +104 -58
  28. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  29. package/fesm2022/radix-ng-primitives-dialog.mjs +655 -327
  30. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +70 -46
  32. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  33. package/fesm2022/radix-ng-primitives-drawer.mjs +960 -0
  34. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
  35. package/fesm2022/radix-ng-primitives-editable.mjs +304 -23
  36. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  37. package/fesm2022/radix-ng-primitives-field.mjs +363 -0
  38. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
  39. package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
  40. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
  41. package/fesm2022/radix-ng-primitives-focus-scope.mjs +23 -8
  42. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  43. package/fesm2022/radix-ng-primitives-input.mjs +172 -0
  44. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
  45. package/fesm2022/radix-ng-primitives-label.mjs +6 -6
  46. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  47. package/fesm2022/radix-ng-primitives-menu.mjs +1907 -363
  48. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  49. package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
  50. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  51. package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
  52. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
  53. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1052 -1553
  54. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  55. package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -367
  56. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  57. package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
  58. package/fesm2022/radix-ng-primitives-popover.mjs +978 -989
  59. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-popper.mjs +111 -44
  61. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-portal.mjs +34 -10
  63. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
  65. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  66. package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
  67. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
  68. package/fesm2022/radix-ng-primitives-progress.mjs +223 -84
  69. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  70. package/fesm2022/radix-ng-primitives-radio.mjs +191 -51
  71. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  72. package/fesm2022/radix-ng-primitives-roving-focus.mjs +96 -50
  73. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  74. package/fesm2022/radix-ng-primitives-scroll-area.mjs +923 -0
  75. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -0
  76. package/fesm2022/radix-ng-primitives-select.mjs +791 -509
  77. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  78. package/fesm2022/radix-ng-primitives-separator.mjs +12 -35
  79. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-slider.mjs +969 -717
  81. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  82. package/fesm2022/radix-ng-primitives-stepper.mjs +15 -19
  83. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  84. package/fesm2022/radix-ng-primitives-switch.mjs +125 -113
  85. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  86. package/fesm2022/radix-ng-primitives-tabs.mjs +390 -108
  87. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  88. package/fesm2022/radix-ng-primitives-time-field.mjs +55 -46
  89. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  90. package/fesm2022/radix-ng-primitives-toast.mjs +839 -0
  91. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -0
  92. package/fesm2022/radix-ng-primitives-toggle-group.mjs +121 -247
  93. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  94. package/fesm2022/radix-ng-primitives-toggle.mjs +98 -61
  95. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  96. package/fesm2022/radix-ng-primitives-toolbar.mjs +303 -92
  97. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  98. package/fesm2022/radix-ng-primitives-tooltip.mjs +699 -1072
  99. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  100. package/fesm2022/radix-ng-primitives-visually-hidden.mjs +25 -66
  101. package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
  102. package/meter/README.md +3 -0
  103. package/navigation-menu/README.md +2 -1
  104. package/package.json +39 -18
  105. package/portal/README.md +2 -0
  106. package/preview-card/README.md +3 -0
  107. package/schematics/collection.json +1 -0
  108. package/schematics/ng-add/index.d.ts +3 -2
  109. package/schematics/ng-add/index.js +62 -31
  110. package/schematics/ng-add/index.js.map +1 -1
  111. package/schematics/ng-add/package-config.d.ts +4 -2
  112. package/schematics/ng-add/package-config.js +10 -2
  113. package/schematics/ng-add/package-config.js.map +1 -1
  114. package/schematics/ng-add/schema.d.ts +3 -0
  115. package/schematics/ng-add/schema.js +3 -0
  116. package/schematics/ng-add/schema.js.map +1 -0
  117. package/schematics/ng-add/schema.json +14 -0
  118. package/select/README.md +2 -0
  119. package/types/radix-ng-primitives-accordion.d.ts +51 -16
  120. package/types/radix-ng-primitives-alert-dialog.d.ts +95 -38
  121. package/types/radix-ng-primitives-arrow.d.ts +1 -1
  122. package/types/radix-ng-primitives-aspect-ratio.d.ts +1 -1
  123. package/types/radix-ng-primitives-avatar.d.ts +7 -11
  124. package/types/radix-ng-primitives-button.d.ts +73 -0
  125. package/types/radix-ng-primitives-calendar.d.ts +39 -20
  126. package/types/radix-ng-primitives-checkbox.d.ts +204 -35
  127. package/types/radix-ng-primitives-collapsible.d.ts +114 -40
  128. package/types/radix-ng-primitives-collection.d.ts +38 -34
  129. package/types/radix-ng-primitives-config.d.ts +1 -1
  130. package/types/radix-ng-primitives-context-menu.d.ts +61 -116
  131. package/types/radix-ng-primitives-core.d.ts +345 -235
  132. package/types/radix-ng-primitives-cropper.d.ts +89 -56
  133. package/types/radix-ng-primitives-date-field.d.ts +49 -28
  134. package/types/radix-ng-primitives-dialog.d.ts +283 -165
  135. package/types/radix-ng-primitives-dismissable-layer.d.ts +15 -7
  136. package/types/radix-ng-primitives-drawer.d.ts +426 -0
  137. package/types/radix-ng-primitives-editable.d.ts +91 -14
  138. package/types/radix-ng-primitives-field.d.ts +374 -0
  139. package/types/radix-ng-primitives-fieldset.d.ts +49 -0
  140. package/types/radix-ng-primitives-focus-scope.d.ts +15 -6
  141. package/types/radix-ng-primitives-input.d.ts +87 -0
  142. package/types/radix-ng-primitives-label.d.ts +0 -1
  143. package/types/radix-ng-primitives-menu.d.ts +584 -99
  144. package/types/radix-ng-primitives-menubar.d.ts +61 -50
  145. package/types/radix-ng-primitives-meter.d.ts +194 -0
  146. package/types/radix-ng-primitives-navigation-menu.d.ts +422 -340
  147. package/types/radix-ng-primitives-number-field.d.ts +405 -145
  148. package/types/radix-ng-primitives-pagination.d.ts +2 -2
  149. package/types/radix-ng-primitives-popover.d.ts +366 -351
  150. package/types/radix-ng-primitives-popper.d.ts +68 -11
  151. package/types/radix-ng-primitives-portal.d.ts +14 -6
  152. package/types/radix-ng-primitives-presence.d.ts +28 -76
  153. package/types/radix-ng-primitives-preview-card.d.ts +359 -0
  154. package/types/radix-ng-primitives-progress.d.ts +175 -48
  155. package/types/radix-ng-primitives-radio.d.ts +55 -25
  156. package/types/radix-ng-primitives-roving-focus.d.ts +33 -23
  157. package/types/radix-ng-primitives-scroll-area.d.ts +253 -0
  158. package/types/radix-ng-primitives-select.d.ts +475 -177
  159. package/types/radix-ng-primitives-separator.d.ts +7 -32
  160. package/types/radix-ng-primitives-slider.d.ts +315 -201
  161. package/types/radix-ng-primitives-stepper.d.ts +5 -7
  162. package/types/radix-ng-primitives-switch.d.ts +86 -71
  163. package/types/radix-ng-primitives-tabs.d.ts +213 -79
  164. package/types/radix-ng-primitives-time-field.d.ts +42 -27
  165. package/types/radix-ng-primitives-toast.d.ts +378 -0
  166. package/types/radix-ng-primitives-toggle-group.d.ts +86 -164
  167. package/types/radix-ng-primitives-toggle.d.ts +43 -53
  168. package/types/radix-ng-primitives-toolbar.d.ts +164 -38
  169. package/types/radix-ng-primitives-tooltip.d.ts +348 -384
  170. package/types/radix-ng-primitives-visually-hidden.d.ts +19 -19
  171. package/dropdown-menu/README.md +0 -1
  172. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -581
  173. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
  174. package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1238
  175. package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
  176. package/fesm2022/radix-ng-primitives-select2.mjs +0 -897
  177. package/fesm2022/radix-ng-primitives-select2.mjs.map +0 -1
  178. package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -735
  179. package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
  180. package/hover-card/README.md +0 -3
  181. package/select2/README.md +0 -3
  182. package/tooltip2/README.md +0 -3
  183. package/types/radix-ng-primitives-dropdown-menu.d.ts +0 -171
  184. package/types/radix-ng-primitives-hover-card.d.ts +0 -471
  185. package/types/radix-ng-primitives-select2.d.ts +0 -511
  186. package/types/radix-ng-primitives-tooltip2.d.ts +0 -325
@@ -1,11 +1,219 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, model, input, booleanAttribute, computed, Directive, ElementRef, DestroyRef, afterNextRender, NgModule } from '@angular/core';
3
- import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
2
+ import { inject, model, input, booleanAttribute, output, signal, computed, effect, Directive, ElementRef, NgModule } from '@angular/core';
4
3
  import * as i1 from '@radix-ng/primitives/core';
5
- import { RdxControlValueAccessor, createContext } from '@radix-ng/primitives/core';
4
+ import { createContext, provideValueAccessor, RdxControlValueAccessor } from '@radix-ng/primitives/core';
5
+ import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
6
6
  import * as i1$1 from '@radix-ng/primitives/presence';
7
7
  import { provideRdxPresenceContext, RdxPresenceDirective } from '@radix-ng/primitives/presence';
8
8
 
9
+ const [injectCheckboxGroupContext, provideCheckboxGroupContext] = createContext('CheckboxGroupContext');
10
+ const groupContext = () => {
11
+ const group = inject(RdxCheckboxGroupDirective);
12
+ return {
13
+ value: group.value,
14
+ allValues: group.allValues,
15
+ disabled: group.disabledState,
16
+ parentState: group.parentState,
17
+ controlledIds: group.controlledIds,
18
+ controlId: (name) => group.controlId(name),
19
+ toggleValue: (name) => group.toggleValue(name),
20
+ toggleAll: () => group.toggleAll(),
21
+ registerChild: (name, disabled) => group.registerChild(name, disabled),
22
+ registerControl: (name, id) => group.registerControl(name, id)
23
+ };
24
+ };
25
+ let nextCheckboxGroupId = 0;
26
+ /**
27
+ * Groups a set of checkboxes that share a single array value (the names of the checked boxes).
28
+ *
29
+ * Each child `rdxCheckboxRoot` participates by its `name`. A child marked `parent` becomes a
30
+ * "select all" checkbox whose state is derived from `allValues`.
31
+ */
32
+ class RdxCheckboxGroupDirective {
33
+ constructor() {
34
+ /** The names of the currently checked checkboxes. Use with `onValueChange` or `[(value)]`. */
35
+ this.value = model([], ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
36
+ /** The names checked initially when the group is uncontrolled. */
37
+ this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
38
+ /** All checkbox names in the group. Required for a `parent` (select-all) checkbox. */
39
+ this.allValues = input([], ...(ngDevMode ? [{ debugName: "allValues" }] : /* istanbul ignore next */ []));
40
+ /** Whether the whole group is disabled. */
41
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
42
+ /** Emits the new array of checked names whenever the value changes. */
43
+ this.onValueChange = output();
44
+ this.disabledByCva = signal(false, ...(ngDevMode ? [{ debugName: "disabledByCva" }] : /* istanbul ignore next */ []));
45
+ this.disabledState = computed(() => this.disabledByCva() || this.disabled(), ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
46
+ /** Derived state for a `parent` checkbox: `true` (all), `false` (none) or `'indeterminate'`. */
47
+ this.parentState = computed(() => {
48
+ const total = this.allValues().length;
49
+ const count = this.value().length;
50
+ if (total > 0 && count === total) {
51
+ return true;
52
+ }
53
+ return count > 0 ? 'indeterminate' : false;
54
+ }, ...(ngDevMode ? [{ debugName: "parentState" }] : /* istanbul ignore next */ []));
55
+ /**
56
+ * The value as last set directly by a child (or the initial value) — the "remembered" partial
57
+ * selection that a `parent` checkbox cycles back to, mirroring Base UI's `uncontrolledStateRef`.
58
+ */
59
+ this.uncontrolledState = [];
60
+ this.seeded = false;
61
+ /** Where the parent is in its mixed → on → off cycle. Reset to `mixed` on any direct child change. */
62
+ this.parentStatus = 'mixed';
63
+ /** Per-name disabled signals, so the parent can preserve disabled-but-checked children. */
64
+ this.disabledByName = new Map();
65
+ /** Stable group id used to derive child control ids when the consumer sets none. */
66
+ this.elementId = `rdx-checkbox-group-${nextCheckboxGroupId++}`;
67
+ /** Registered control element ids, keyed by child name. */
68
+ this.controlIds = signal({}, ...(ngDevMode ? [{ debugName: "controlIds" }] : /* istanbul ignore next */ []));
69
+ /** The space-separated control ids in `allValues` order, for the parent's `aria-controls`. */
70
+ this.controlledIds = computed(() => {
71
+ const ids = this.controlIds();
72
+ const list = this.allValues()
73
+ .map((name) => ids[name])
74
+ .filter((id) => id !== undefined);
75
+ return list.length > 0 ? list.join(' ') : undefined;
76
+ }, ...(ngDevMode ? [{ debugName: "controlledIds" }] : /* istanbul ignore next */ []));
77
+ this.hasAppliedDefault = false;
78
+ this.onChange = () => {
79
+ /* Empty */
80
+ };
81
+ this.onTouched = () => {
82
+ /* Empty */
83
+ };
84
+ effect(() => {
85
+ const defaultValue = this.defaultValue();
86
+ if (!this.hasAppliedDefault && defaultValue !== undefined) {
87
+ this.hasAppliedDefault = true;
88
+ this.value.set(defaultValue);
89
+ }
90
+ });
91
+ }
92
+ /** @ignore Register a child's disabled signal keyed by its name. */
93
+ registerChild(name, disabled) {
94
+ this.disabledByName.set(name, disabled);
95
+ return () => {
96
+ if (this.disabledByName.get(name) === disabled) {
97
+ this.disabledByName.delete(name);
98
+ }
99
+ };
100
+ }
101
+ /** A stable control id for a child, derived from the group id and the child name. */
102
+ controlId(name) {
103
+ return `${this.elementId}-${name}`;
104
+ }
105
+ /** @ignore Register a child's control element id so the parent can list it in `aria-controls`. */
106
+ registerControl(name, id) {
107
+ this.controlIds.update((ids) => ({ ...ids, [name]: id }));
108
+ return () => {
109
+ this.controlIds.update((ids) => {
110
+ if (ids[name] !== id) {
111
+ return ids;
112
+ }
113
+ const next = { ...ids };
114
+ delete next[name];
115
+ return next;
116
+ });
117
+ };
118
+ }
119
+ /** Add/remove a single child name from the value (a direct child change). */
120
+ toggleValue(name) {
121
+ if (this.disabledState()) {
122
+ return;
123
+ }
124
+ const current = this.value();
125
+ const next = current.includes(name) ? current.filter((v) => v !== name) : [...current, name];
126
+ this.emit(next);
127
+ // A direct child change becomes the new "remembered" selection and resets the parent cycle.
128
+ this.seeded = true;
129
+ this.uncontrolledState = next;
130
+ this.parentStatus = 'mixed';
131
+ }
132
+ /**
133
+ * Toggle from the `parent` checkbox. Mirrors Base UI's `useCheckboxGroupParent`:
134
+ *
135
+ * - When the remembered selection is all/none, this is a plain check-all ↔ uncheck-all toggle.
136
+ * - When it is a partial selection, clicks cycle: partial → all → none → partial → …, so the
137
+ * user's original partial choice is restored rather than lost.
138
+ *
139
+ * Disabled-but-checked children are always preserved (they cannot be toggled programmatically).
140
+ */
141
+ toggleAll() {
142
+ if (this.disabledState()) {
143
+ return;
144
+ }
145
+ this.ensureSeeded();
146
+ const allValues = this.allValues();
147
+ const remembered = this.uncontrolledState;
148
+ // Disabled children that were checked stay checked through every transition.
149
+ const none = allValues.filter((name) => this.isNameDisabled(name) && remembered.includes(name));
150
+ const all = allValues.filter((name) => !this.isNameDisabled(name) || remembered.includes(name));
151
+ const rememberedIsAllOrNone = remembered.length === all.length || remembered.length === 0;
152
+ if (rememberedIsAllOrNone) {
153
+ this.emit(this.value().length === all.length ? none : all);
154
+ return;
155
+ }
156
+ let nextStatus = 'mixed';
157
+ let nextValue = remembered;
158
+ if (this.parentStatus === 'mixed') {
159
+ nextStatus = 'on';
160
+ nextValue = all;
161
+ }
162
+ else if (this.parentStatus === 'on') {
163
+ nextStatus = 'off';
164
+ nextValue = none;
165
+ }
166
+ this.emit(nextValue);
167
+ this.parentStatus = nextStatus;
168
+ }
169
+ isNameDisabled(name) {
170
+ return this.disabledByName.get(name)?.() ?? false;
171
+ }
172
+ /** Seed the remembered selection from the current value the first time the parent is used. */
173
+ ensureSeeded() {
174
+ if (!this.seeded) {
175
+ this.seeded = true;
176
+ this.uncontrolledState = this.value();
177
+ }
178
+ }
179
+ emit(next) {
180
+ this.value.set(next);
181
+ this.onValueChange.emit(next);
182
+ this.onChange(next);
183
+ this.onTouched();
184
+ }
185
+ /** @ignore */
186
+ writeValue(value) {
187
+ this.value.set(value ?? []);
188
+ }
189
+ /** @ignore */
190
+ registerOnChange(fn) {
191
+ this.onChange = fn;
192
+ }
193
+ /** @ignore */
194
+ registerOnTouched(fn) {
195
+ this.onTouched = fn;
196
+ }
197
+ /** @ignore */
198
+ setDisabledState(isDisabled) {
199
+ this.disabledByCva.set(isDisabled);
200
+ }
201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
202
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", 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: [provideValueAccessor(RdxCheckboxGroupDirective), provideCheckboxGroupContext(groupContext)], exportAs: ["rdxCheckboxGroup"], ngImport: i0 }); }
203
+ }
204
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxGroupDirective, decorators: [{
205
+ type: Directive,
206
+ args: [{
207
+ selector: '[rdxCheckboxGroup]',
208
+ exportAs: 'rdxCheckboxGroup',
209
+ providers: [provideValueAccessor(RdxCheckboxGroupDirective), provideCheckboxGroupContext(groupContext)],
210
+ host: {
211
+ role: 'group',
212
+ '[attr.data-disabled]': 'disabledState() ? "" : undefined'
213
+ }
214
+ }]
215
+ }], 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"] }] } });
216
+
9
217
  function isIndeterminate(checked) {
10
218
  return checked === 'indeterminate';
11
219
  }
@@ -14,13 +222,16 @@ function getState(checked) {
14
222
  }
15
223
  const rootContext = () => {
16
224
  const checkbox = inject(RdxCheckboxRootDirective);
17
- const controlValueAccessor = inject(RdxControlValueAccessor);
225
+ // `checked`/`disabled` come from the directive so they reflect group membership when the
226
+ // checkbox is inside a `rdxCheckboxGroup`; otherwise they fall back to the CVA.
18
227
  return {
19
- checked: controlValueAccessor.value,
20
- disabled: controlValueAccessor.disabled,
228
+ checked: checkbox.checkedState,
229
+ indeterminate: checkbox.indeterminateState,
230
+ disabled: checkbox.disabledState,
21
231
  required: checkbox.required,
22
- value: checkbox.value,
232
+ value: checkbox.submitValue,
23
233
  name: checkbox.name,
234
+ parent: checkbox.parent,
24
235
  form: checkbox.form,
25
236
  readonly: checkbox.readonly,
26
237
  state: checkbox.state,
@@ -36,16 +247,41 @@ const [injectCheckboxRootContext, provideCheckboxRootContext] = createContext('C
36
247
  class RdxCheckboxRootDirective {
37
248
  constructor() {
38
249
  this.controlValueAccessor = inject(RdxControlValueAccessor);
250
+ /** The group this checkbox belongs to, if it is rendered inside a `rdxCheckboxGroup`. */
251
+ this.group = injectCheckboxGroupContext(true);
252
+ /**
253
+ * @ignore
254
+ * Reflects the effective disabled state (CVA, covering reactive-forms `.disable()`, plus the
255
+ * group's disabled state), used for the `data-disabled` host attribute.
256
+ */
257
+ this.isDisabled = computed(() => this.disabledState(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
39
258
  /**
40
259
  * The controlled checked state of the checkbox. Must be used in conjunction with onCheckedChange.
260
+ *
261
+ * Mixed state is no longer expressed through `checked` — use the separate
262
+ * `indeterminate` input (Base UI shape). This `boolean` model is what
263
+ * `RdxFormCheckboxControl` / Angular Signal Forms bind to.
41
264
  * @group Props
42
265
  */
43
266
  this.checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
267
+ /**
268
+ * Whether the checkbox is in a mixed state: neither ticked nor unticked.
269
+ * Orthogonal to `checked` and not part of the submitted form value. A user
270
+ * click resolves the checkbox to `checked` and clears `indeterminate`.
271
+ * @group Props
272
+ */
273
+ this.indeterminate = model(false, ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
44
274
  /**
45
275
  * The value of the checkbox. This is what is submitted with the form when the checkbox is checked.
276
+ *
277
+ * Bound publicly as `[value]`; the TS member is named `submitValue` so the
278
+ * directive can satisfy `RdxFormCheckboxControl`, whose contract reserves a
279
+ * `value` member for `RdxFormValueControl` and forbids it on checkbox-style
280
+ * controls. (Checkbox is not yet marked `implements` — its `checked` is still
281
+ * `CheckedState`; see the `indeterminate` half of collision #1.)
46
282
  * @group Props
47
283
  */
48
- this.value = input('on', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
284
+ this.submitValue = input('on', { ...(ngDevMode ? { debugName: "submitValue" } : /* istanbul ignore next */ {}), alias: 'value' });
49
285
  /**
50
286
  * Whether or not the checkbox button is disabled. This prevents the user from interacting with it.
51
287
  * @group Props
@@ -62,10 +298,17 @@ class RdxCheckboxRootDirective {
62
298
  */
63
299
  this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
64
300
  /**
65
- * Name of the form control. Submitted with the form as part of a name/value pair.
301
+ * Name of the form control. Submitted with the form as part of a name/value pair. Inside a
302
+ * `rdxCheckboxGroup` this also identifies the checkbox in the group's value array.
66
303
  * @group Props
67
304
  */
68
305
  this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
306
+ /**
307
+ * When inside a `rdxCheckboxGroup`, marks this as the "select all" checkbox: its state is
308
+ * derived from the group's `allValues`, and toggling it checks or unchecks every child.
309
+ * @group Props
310
+ */
311
+ this.parent = input(false, { ...(ngDevMode ? { debugName: "parent" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
69
312
  /**
70
313
  * Associates the control with a form element.
71
314
  * @group Props
@@ -76,23 +319,74 @@ class RdxCheckboxRootDirective {
76
319
  * @group Emits
77
320
  */
78
321
  this.onCheckedChange = outputFromObservable(outputToObservable(this.controlValueAccessor.valueChange));
79
- this.state = computed(() => {
80
- const checked = this.controlValueAccessor.value();
81
- if (checked === 'indeterminate') {
82
- return checked;
322
+ /**
323
+ * @ignore
324
+ * The effective checked state as a `boolean`. Inside a `rdxCheckboxGroup` it is derived from the
325
+ * group (a `parent` checkbox is checked only when every child is; a child from whether its `name`
326
+ * is in the group value); standalone it reads the CVA value.
327
+ */
328
+ this.checkedState = computed(() => {
329
+ const group = this.group;
330
+ if (group) {
331
+ if (this.parent()) {
332
+ return group.parentState() === true;
333
+ }
334
+ const name = this.name();
335
+ if (name !== undefined) {
336
+ return group.value().includes(name);
337
+ }
338
+ }
339
+ return !!this.controlValueAccessor.value();
340
+ }, ...(ngDevMode ? [{ debugName: "checkedState" }] : /* istanbul ignore next */ []));
341
+ /**
342
+ * @ignore
343
+ * The effective mixed state. A `parent` checkbox is indeterminate when some — but not all —
344
+ * children are checked; otherwise it follows the `indeterminate` input.
345
+ */
346
+ this.indeterminateState = computed(() => {
347
+ const group = this.group;
348
+ if (group && this.parent()) {
349
+ return group.parentState() === 'indeterminate';
350
+ }
351
+ return this.indeterminate();
352
+ }, ...(ngDevMode ? [{ debugName: "indeterminateState" }] : /* istanbul ignore next */ []));
353
+ /** @ignore The effective disabled state, including the group. */
354
+ this.disabledState = computed(() => this.controlValueAccessor.disabled() || (this.group?.disabled() ?? false), ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
355
+ this.state = computed(() => this.indeterminateState() ? 'indeterminate' : this.checkedState() ? 'checked' : 'unchecked', ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
356
+ // Inside a group, register this child's name and its own disabled state so a `parent`
357
+ // checkbox can preserve disabled-but-checked children when selecting/deselecting all.
358
+ effect((onCleanup) => {
359
+ const group = this.group;
360
+ const name = this.name();
361
+ if (group && !this.parent() && name !== undefined) {
362
+ onCleanup(group.registerChild(name, this.controlValueAccessor.disabled));
83
363
  }
84
- return checked ? 'checked' : 'unchecked';
85
- }, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
364
+ });
86
365
  }
87
366
  toggle() {
88
- const checked = this.controlValueAccessor.value();
89
- if (checked === 'indeterminate') {
90
- this.controlValueAccessor.setValue(true);
367
+ const group = this.group;
368
+ if (group) {
369
+ if (this.parent()) {
370
+ group.toggleAll();
371
+ return;
372
+ }
373
+ const name = this.name();
374
+ if (name !== undefined) {
375
+ group.toggleValue(name);
376
+ return;
377
+ }
91
378
  }
92
- this.controlValueAccessor.setValue(!checked);
379
+ // From the indeterminate state a click resolves to checked (matching
380
+ // native + Base UI), otherwise it flips the boolean. A single setValue so
381
+ // onCheckedChange fires once; the `checked`/`indeterminate` models are
382
+ // kept in sync so `[(checked)]` / `[(indeterminate)]` reflect the change.
383
+ const next = this.indeterminateState() ? true : !this.checkedState();
384
+ this.indeterminate.set(false);
385
+ this.checked.set(next);
386
+ this.controlValueAccessor.setValue(next);
93
387
  }
94
388
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
95
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", 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 }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", onCheckedChange: "onCheckedChange" }, host: { properties: { "attr.data-state": "state()" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
389
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", 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 }, 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 }, 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", onCheckedChange: "onCheckedChange" }, host: { properties: { "attr.data-state": "state()", "attr.data-disabled": "isDisabled() ? \"\" : undefined", "attr.data-readonly": "readonly() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
96
390
  }
97
391
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxRootDirective, decorators: [{
98
392
  type: Directive,
@@ -106,10 +400,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
106
400
  }
107
401
  ],
108
402
  host: {
109
- '[attr.data-state]': 'state()'
403
+ '[attr.data-state]': 'state()',
404
+ '[attr.data-disabled]': 'isDisabled() ? "" : undefined',
405
+ '[attr.data-readonly]': 'readonly() ? "" : undefined',
406
+ '[attr.data-required]': 'required() ? "" : undefined'
110
407
  }
111
408
  }]
112
- }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", 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 }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], onCheckedChange: [{ type: i0.Output, args: ["onCheckedChange"] }] } });
409
+ }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], 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 }] }], 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 }] }], 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"] }] } });
113
410
 
114
411
  /**
115
412
  * Directive: rdxCheckboxButton
@@ -120,7 +417,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
120
417
  class RdxCheckboxButtonDirective {
121
418
  constructor() {
122
419
  this.rootContext = injectCheckboxRootContext();
420
+ this.group = injectCheckboxGroupContext(true);
123
421
  this.elementRef = inject(ElementRef);
422
+ /** A `parent` checkbox lists the ids of the children it controls. */
423
+ this.ariaControls = computed(() => this.group && this.rootContext.parent() ? this.group.controlledIds() : undefined, ...(ngDevMode ? [{ debugName: "ariaControls" }] : /* istanbul ignore next */ []));
424
+ // A child checkbox in a group exposes its control id so the parent can reference it via
425
+ // `aria-controls`. Use the consumer's id when present, otherwise derive a stable one.
426
+ effect((onCleanup) => {
427
+ const group = this.group;
428
+ const name = this.rootContext.name();
429
+ if (!group || this.rootContext.parent() || name === undefined) {
430
+ return;
431
+ }
432
+ const el = this.elementRef.nativeElement;
433
+ if (!el.id) {
434
+ el.id = group.controlId(name);
435
+ }
436
+ onCleanup(group.registerControl(name, el.id));
437
+ });
124
438
  }
125
439
  clicked(event) {
126
440
  if (event.defaultPrevented || this.rootContext.readonly()) {
@@ -135,7 +449,7 @@ class RdxCheckboxButtonDirective {
135
449
  }
136
450
  }
137
451
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
138
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxButtonDirective, isStandalone: true, selector: "button[rdxCheckboxButton]", host: { attributes: { "type": "button", "role": "checkbox" }, listeners: { "click": "clicked($event)", "keydown.enter": "$event.preventDefault()" }, properties: { "attr.aria-checked": "rootContext.checked() === \"indeterminate\" ? \"mixed\" : rootContext.checked()", "attr.aria-required": "rootContext.required() || undefined", "attr.aria-readonly": "rootContext.readonly() || undefined", "attr.data-state": "rootContext.state()", "attr.data-disabled": "rootContext.disabled() || undefined", "attr.data-readonly": "rootContext.readonly() || undefined", "attr.disabled": "rootContext.disabled() ? \"\" : undefined", "attr.value": "rootContext.value()" } }, ngImport: i0 }); }
452
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxButtonDirective, isStandalone: true, selector: "button[rdxCheckboxButton]", host: { attributes: { "type": "button", "role": "checkbox" }, listeners: { "click": "clicked($event)", "keydown.enter": "$event.preventDefault()" }, properties: { "attr.aria-checked": "rootContext.indeterminate() ? \"mixed\" : rootContext.checked()", "attr.aria-controls": "ariaControls()", "attr.aria-required": "rootContext.required() || undefined", "attr.aria-readonly": "rootContext.readonly() || undefined", "attr.data-state": "rootContext.state()", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.disabled": "rootContext.disabled() ? \"\" : undefined", "attr.value": "rootContext.value()" } }, ngImport: i0 }); }
139
453
  }
140
454
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxButtonDirective, decorators: [{
141
455
  type: Directive,
@@ -144,12 +458,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
144
458
  host: {
145
459
  type: 'button',
146
460
  role: 'checkbox',
147
- '[attr.aria-checked]': 'rootContext.checked() === "indeterminate" ? "mixed" : rootContext.checked()',
461
+ '[attr.aria-checked]': 'rootContext.indeterminate() ? "mixed" : rootContext.checked()',
462
+ '[attr.aria-controls]': 'ariaControls()',
148
463
  '[attr.aria-required]': 'rootContext.required() || undefined',
149
464
  '[attr.aria-readonly]': 'rootContext.readonly() || undefined',
150
465
  '[attr.data-state]': 'rootContext.state()',
151
- '[attr.data-disabled]': 'rootContext.disabled() || undefined',
152
- '[attr.data-readonly]': 'rootContext.readonly() || undefined',
466
+ '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
467
+ '[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
153
468
  '[attr.disabled]': 'rootContext.disabled() ? "" : undefined',
154
469
  '[attr.value]': 'rootContext.value()',
155
470
  '(click)': 'clicked($event)',
@@ -157,14 +472,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
157
472
  '(keydown.enter)': '$event.preventDefault()'
158
473
  }
159
474
  }]
160
- }] });
475
+ }], ctorParameters: () => [] });
161
476
 
162
477
  class RdxCheckboxIndicatorDirective {
163
478
  constructor() {
164
479
  this.rootContext = injectCheckboxRootContext();
165
480
  }
166
481
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
167
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxIndicatorDirective, isStandalone: true, selector: "[rdxCheckboxIndicator]", host: { properties: { "attr.data-state": "rootContext.state()", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "hidden": "!rootContext.checked()", "style.pointer-events": "\"none\"" } }, ngImport: i0 }); }
482
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxIndicatorDirective, isStandalone: true, selector: "[rdxCheckboxIndicator]", host: { properties: { "attr.data-state": "rootContext.state()", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "hidden": "!rootContext.checked() && !rootContext.indeterminate()", "style.pointer-events": "\"none\"" } }, ngImport: i0 }); }
168
483
  }
169
484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorDirective, decorators: [{
170
485
  type: Directive,
@@ -173,7 +488,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
173
488
  host: {
174
489
  '[attr.data-state]': 'rootContext.state()',
175
490
  '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
176
- '[hidden]': '!rootContext.checked()',
491
+ '[hidden]': '!rootContext.checked() && !rootContext.indeterminate()',
177
492
  '[style.pointer-events]': '"none"'
178
493
  }
179
494
  }]
@@ -185,7 +500,7 @@ class RdxCheckboxIndicatorPresenceDirective {
185
500
  provideRdxPresenceContext(() => {
186
501
  const rootContext = injectCheckboxRootContext();
187
502
  return {
188
- present: computed(() => !!rootContext.checked())
503
+ present: computed(() => rootContext.checked() || rootContext.indeterminate())
189
504
  };
190
505
  })
191
506
  ], hostDirectives: [{ directive: i1$1.RdxPresenceDirective }], ngImport: i0 }); }
@@ -198,7 +513,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
198
513
  provideRdxPresenceContext(() => {
199
514
  const rootContext = injectCheckboxRootContext();
200
515
  return {
201
- present: computed(() => !!rootContext.checked())
516
+ present: computed(() => rootContext.checked() || rootContext.indeterminate())
202
517
  };
203
518
  })
204
519
  ],
@@ -209,30 +524,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
209
524
  class RdxCheckboxInputDirective {
210
525
  constructor() {
211
526
  this.rootContext = injectCheckboxRootContext();
212
- this.elementRef = inject(ElementRef);
213
- this.destroyRef = inject(DestroyRef);
527
+ this.input = inject(ElementRef).nativeElement;
528
+ let isInitial = true;
214
529
  /**
215
- * Sets up an observer to bubble native input events when the underlying "checked" attribute changes.
530
+ * Keeps the hidden native input in sync so form submission, native
531
+ * validation and form events reflect the checkbox state.
216
532
  *
217
- * Why this exists
218
- * - The checkbox input in this directive is visually hidden and controlled by the Radix checkbox state.
219
- * - When Radix changes the checked state, it updates the input's DOM attribute ("checked").
220
- * - Some forms and frameworks rely on native events (click/change) from the input to sync value, validity and
221
- * form state. If the attribute changes programmatically without a user click, no event is fired by default.
533
+ * - `indeterminate` is a native property (not a submittable value), so we
534
+ * mirror it here rather than via an attribute.
535
+ * - On every change (but not the initial render) we emit bubbling
536
+ * `input`/`change` events so native form listeners react. We do NOT
537
+ * dispatch a `click`: a click on a checkbox runs the toggle activation
538
+ * behavior and would desync the input from the bound state.
222
539
  */
223
- afterNextRender(() => {
224
- const mutationObserver = new MutationObserver(() => {
225
- this.elementRef.nativeElement.dispatchEvent(new Event('click', { bubbles: true }));
226
- });
227
- mutationObserver.observe(this.elementRef.nativeElement, {
228
- attributes: true,
229
- attributeFilter: ['checked']
230
- });
231
- this.destroyRef.onDestroy(() => mutationObserver.disconnect());
540
+ effect(() => {
541
+ // Track both so the native input mirrors the checkbox and emits change
542
+ // events when either the checked or indeterminate state moves.
543
+ this.rootContext.checked();
544
+ this.input.indeterminate = this.rootContext.indeterminate();
545
+ if (isInitial) {
546
+ isInitial = false;
547
+ return;
548
+ }
549
+ this.input.dispatchEvent(new Event('input', { bubbles: true }));
550
+ this.input.dispatchEvent(new Event('change', { bubbles: true }));
232
551
  });
233
552
  }
234
553
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
235
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxInputDirective, isStandalone: true, selector: "input[rdxCheckboxInput]", host: { attributes: { "type": "checkbox", "tabindex": "-1", "aria-hidden": "true" }, properties: { "attr.name": "rootContext.name() || undefined", "attr.checked": "rootContext.checked()", "attr.form": "rootContext.form() || undefined", "attr.value": "rootContext.value()", "required": "rootContext.required() || undefined", "attr.disabled": "rootContext.disabled() ? \"\" : undefined", "style": "{\n position: 'absolute',\n pointerEvents: 'none',\n opacity: 0,\n margin: 0,\n inset: 0,\n transform: 'translateX(-100%)',\n }" } }, ngImport: i0 }); }
554
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxInputDirective, isStandalone: true, selector: "input[rdxCheckboxInput]", host: { attributes: { "type": "checkbox", "tabindex": "-1", "aria-hidden": "true" }, properties: { "attr.name": "rootContext.name() || undefined", "attr.checked": "rootContext.state() === \"checked\" ? \"\" : undefined", "attr.form": "rootContext.form() || undefined", "attr.value": "rootContext.value()", "required": "rootContext.required() || undefined", "attr.disabled": "rootContext.disabled() ? \"\" : undefined", "style": "{\n position: 'absolute',\n pointerEvents: 'none',\n opacity: 0,\n margin: 0,\n inset: 0,\n transform: 'translateX(-100%)',\n }" } }, ngImport: i0 }); }
236
555
  }
237
556
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxInputDirective, decorators: [{
238
557
  type: Directive,
@@ -243,7 +562,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
243
562
  tabindex: '-1',
244
563
  'aria-hidden': 'true',
245
564
  '[attr.name]': 'rootContext.name() || undefined',
246
- '[attr.checked]': 'rootContext.checked()',
565
+ // Only a truly checked box is submitted; `indeterminate` is a property
566
+ // (set below), never a submitted "checked" value.
567
+ '[attr.checked]': 'rootContext.state() === "checked" ? "" : undefined',
247
568
  '[attr.form]': 'rootContext.form() || undefined',
248
569
  '[attr.value]': 'rootContext.value()',
249
570
  '[required]': 'rootContext.required() || undefined',
@@ -265,7 +586,8 @@ const checkboxImports = [
265
586
  RdxCheckboxRootDirective,
266
587
  RdxCheckboxButtonDirective,
267
588
  RdxCheckboxIndicatorDirective,
268
- RdxCheckboxIndicatorPresenceDirective
589
+ RdxCheckboxIndicatorPresenceDirective,
590
+ RdxCheckboxGroupDirective
269
591
  ];
270
592
  class RdxCheckboxModule {
271
593
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
@@ -273,11 +595,13 @@ class RdxCheckboxModule {
273
595
  RdxCheckboxRootDirective,
274
596
  RdxCheckboxButtonDirective,
275
597
  RdxCheckboxIndicatorDirective,
276
- RdxCheckboxIndicatorPresenceDirective], exports: [RdxCheckboxInputDirective,
598
+ RdxCheckboxIndicatorPresenceDirective,
599
+ RdxCheckboxGroupDirective], exports: [RdxCheckboxInputDirective,
277
600
  RdxCheckboxRootDirective,
278
601
  RdxCheckboxButtonDirective,
279
602
  RdxCheckboxIndicatorDirective,
280
- RdxCheckboxIndicatorPresenceDirective] }); }
603
+ RdxCheckboxIndicatorPresenceDirective,
604
+ RdxCheckboxGroupDirective] }); }
281
605
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule }); }
282
606
  }
283
607
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule, decorators: [{
@@ -292,5 +616,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
292
616
  * Generated bundle index. Do not edit.
293
617
  */
294
618
 
295
- export { RdxCheckboxButtonDirective, RdxCheckboxIndicatorDirective, RdxCheckboxIndicatorPresenceDirective, RdxCheckboxInputDirective, RdxCheckboxModule, RdxCheckboxRootDirective, checkboxImports, getState, injectCheckboxRootContext, isIndeterminate, provideCheckboxRootContext };
619
+ export { RdxCheckboxButtonDirective, RdxCheckboxGroupDirective, RdxCheckboxIndicatorDirective, RdxCheckboxIndicatorPresenceDirective, RdxCheckboxInputDirective, RdxCheckboxModule, RdxCheckboxRootDirective, checkboxImports, getState, injectCheckboxGroupContext, injectCheckboxRootContext, isIndeterminate, provideCheckboxGroupContext, provideCheckboxRootContext };
296
620
  //# sourceMappingURL=radix-ng-primitives-checkbox.mjs.map