@radix-ng/primitives 0.50.0 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/collection/README.md +1 -0
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +134 -66
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +224 -132
  5. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-arrow.mjs +26 -10
  7. package/fesm2022/radix-ng-primitives-arrow.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs +6 -6
  9. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-avatar.mjs +68 -75
  11. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-button.mjs +123 -0
  13. package/fesm2022/radix-ng-primitives-button.mjs.map +1 -0
  14. package/fesm2022/radix-ng-primitives-calendar.mjs +104 -103
  15. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-checkbox.mjs +414 -80
  17. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-collapsible.mjs +193 -92
  19. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-collection.mjs +72 -0
  21. package/fesm2022/radix-ng-primitives-collection.mjs.map +1 -0
  22. package/fesm2022/radix-ng-primitives-config.mjs +5 -5
  23. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-context-menu.mjs +143 -427
  25. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-core.mjs +757 -757
  27. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-cropper.mjs +55 -53
  29. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-date-field.mjs +93 -86
  31. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-dialog.mjs +658 -330
  33. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +98 -76
  35. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-drawer.mjs +1059 -0
  37. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -0
  38. package/fesm2022/radix-ng-primitives-editable.mjs +20 -20
  39. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-field.mjs +363 -0
  41. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -0
  42. package/fesm2022/radix-ng-primitives-fieldset.mjs +79 -0
  43. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -0
  44. package/fesm2022/radix-ng-primitives-focus-guards.mjs +3 -3
  45. package/fesm2022/radix-ng-primitives-focus-guards.mjs.map +1 -1
  46. package/fesm2022/radix-ng-primitives-focus-scope.mjs +29 -14
  47. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  48. package/fesm2022/radix-ng-primitives-input.mjs +172 -0
  49. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -0
  50. package/fesm2022/radix-ng-primitives-label.mjs +11 -11
  51. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  52. package/fesm2022/radix-ng-primitives-menu.mjs +1484 -353
  53. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  54. package/fesm2022/radix-ng-primitives-menubar.mjs +290 -162
  55. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-meter.mjs +271 -0
  57. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -0
  58. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1060 -1553
  59. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-number-field.mjs +1102 -366
  61. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-pagination.mjs +51 -51
  63. package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-popover.mjs +980 -995
  65. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  66. package/fesm2022/radix-ng-primitives-popper.mjs +137 -82
  67. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  68. package/fesm2022/radix-ng-primitives-portal.mjs +40 -16
  69. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  70. package/fesm2022/radix-ng-primitives-presence.mjs +134 -246
  71. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  72. package/fesm2022/radix-ng-primitives-preview-card.mjs +997 -0
  73. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -0
  74. package/fesm2022/radix-ng-primitives-progress.mjs +231 -92
  75. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  76. package/fesm2022/radix-ng-primitives-radio.mjs +211 -70
  77. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  78. package/fesm2022/radix-ng-primitives-roving-focus.mjs +127 -77
  79. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-select.mjs +791 -511
  81. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  82. package/fesm2022/radix-ng-primitives-separator.mjs +16 -45
  83. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  84. package/fesm2022/radix-ng-primitives-slider.mjs +976 -720
  85. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  86. package/fesm2022/radix-ng-primitives-stepper.mjs +69 -71
  87. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  88. package/fesm2022/radix-ng-primitives-switch.mjs +128 -124
  89. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  90. package/fesm2022/radix-ng-primitives-tabs.mjs +388 -115
  91. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  92. package/fesm2022/radix-ng-primitives-time-field.mjs +111 -117
  93. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  94. package/fesm2022/radix-ng-primitives-toggle-group.mjs +122 -248
  95. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  96. package/fesm2022/radix-ng-primitives-toggle.mjs +99 -62
  97. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  98. package/fesm2022/radix-ng-primitives-toolbar.mjs +307 -94
  99. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  100. package/fesm2022/radix-ng-primitives-tooltip.mjs +690 -1079
  101. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  102. package/fesm2022/radix-ng-primitives-visually-hidden.mjs +46 -87
  103. package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
  104. package/fesm2022/radix-ng-primitives.mjs.map +1 -1
  105. package/meter/README.md +3 -0
  106. package/navigation-menu/README.md +2 -1
  107. package/package.json +85 -63
  108. package/portal/README.md +2 -0
  109. package/preview-card/README.md +3 -0
  110. package/schematics/collection.json +1 -0
  111. package/schematics/ng-add/index.d.ts +3 -2
  112. package/schematics/ng-add/index.js +62 -31
  113. package/schematics/ng-add/index.js.map +1 -1
  114. package/schematics/ng-add/package-config.d.ts +4 -2
  115. package/schematics/ng-add/package-config.js +10 -2
  116. package/schematics/ng-add/package-config.js.map +1 -1
  117. package/schematics/ng-add/schema.d.ts +3 -0
  118. package/schematics/ng-add/schema.js +3 -0
  119. package/schematics/ng-add/schema.js.map +1 -0
  120. package/schematics/ng-add/schema.json +14 -0
  121. package/select/README.md +2 -0
  122. package/{accordion/index.d.ts → types/radix-ng-primitives-accordion.d.ts} +102 -67
  123. package/types/radix-ng-primitives-alert-dialog.d.ts +114 -0
  124. package/{arrow/index.d.ts → types/radix-ng-primitives-arrow.d.ts} +1 -1
  125. package/{aspect-ratio/index.d.ts → types/radix-ng-primitives-aspect-ratio.d.ts} +1 -1
  126. package/{avatar/index.d.ts → types/radix-ng-primitives-avatar.d.ts} +7 -11
  127. package/types/radix-ng-primitives-button.d.ts +73 -0
  128. package/{calendar/index.d.ts → types/radix-ng-primitives-calendar.d.ts} +2 -3
  129. package/types/radix-ng-primitives-checkbox.d.ts +337 -0
  130. package/types/radix-ng-primitives-collapsible.d.ts +159 -0
  131. package/types/radix-ng-primitives-collection.d.ts +44 -0
  132. package/{config/index.d.ts → types/radix-ng-primitives-config.d.ts} +1 -1
  133. package/types/radix-ng-primitives-context-menu.d.ts +73 -0
  134. package/{core/index.d.ts → types/radix-ng-primitives-core.d.ts} +311 -236
  135. package/{cropper/index.d.ts → types/radix-ng-primitives-cropper.d.ts} +6 -5
  136. package/{date-field/index.d.ts → types/radix-ng-primitives-date-field.d.ts} +42 -27
  137. package/types/radix-ng-primitives-dialog.d.ts +323 -0
  138. package/{dismissable-layer/index.d.ts → types/radix-ng-primitives-dismissable-layer.d.ts} +15 -7
  139. package/types/radix-ng-primitives-drawer.d.ts +448 -0
  140. package/{editable/index.d.ts → types/radix-ng-primitives-editable.d.ts} +1 -1
  141. package/types/radix-ng-primitives-field.d.ts +373 -0
  142. package/types/radix-ng-primitives-fieldset.d.ts +48 -0
  143. package/{focus-scope/index.d.ts → types/radix-ng-primitives-focus-scope.d.ts} +13 -5
  144. package/types/radix-ng-primitives-input.d.ts +87 -0
  145. package/{label/index.d.ts → types/radix-ng-primitives-label.d.ts} +0 -1
  146. package/types/radix-ng-primitives-menu.d.ts +612 -0
  147. package/types/radix-ng-primitives-menubar.d.ts +66 -0
  148. package/types/radix-ng-primitives-meter.d.ts +193 -0
  149. package/types/radix-ng-primitives-navigation-menu.d.ts +488 -0
  150. package/types/radix-ng-primitives-number-field.d.ts +464 -0
  151. package/{pagination/index.d.ts → types/radix-ng-primitives-pagination.d.ts} +2 -2
  152. package/types/radix-ng-primitives-popover.d.ts +416 -0
  153. package/{popper/index.d.ts → types/radix-ng-primitives-popper.d.ts} +50 -9
  154. package/types/radix-ng-primitives-portal.d.ts +30 -0
  155. package/types/radix-ng-primitives-presence.d.ts +55 -0
  156. package/types/radix-ng-primitives-preview-card.d.ts +359 -0
  157. package/types/radix-ng-primitives-progress.d.ts +206 -0
  158. package/{radio/index.d.ts → types/radix-ng-primitives-radio.d.ts} +56 -26
  159. package/{roving-focus/index.d.ts → types/radix-ng-primitives-roving-focus.d.ts} +38 -27
  160. package/types/radix-ng-primitives-select.d.ts +512 -0
  161. package/types/radix-ng-primitives-separator.d.ts +38 -0
  162. package/types/radix-ng-primitives-slider.d.ts +377 -0
  163. package/{stepper/index.d.ts → types/radix-ng-primitives-stepper.d.ts} +21 -22
  164. package/types/radix-ng-primitives-switch.d.ts +121 -0
  165. package/types/radix-ng-primitives-tabs.d.ts +247 -0
  166. package/{time-field/index.d.ts → types/radix-ng-primitives-time-field.d.ts} +46 -31
  167. package/types/radix-ng-primitives-toggle-group.d.ts +116 -0
  168. package/types/radix-ng-primitives-toggle.d.ts +65 -0
  169. package/types/radix-ng-primitives-toolbar.d.ts +180 -0
  170. package/types/radix-ng-primitives-tooltip.d.ts +395 -0
  171. package/{visually-hidden/index.d.ts → types/radix-ng-primitives-visually-hidden.d.ts} +19 -19
  172. package/alert-dialog/index.d.ts +0 -57
  173. package/checkbox/index.d.ts +0 -164
  174. package/collapsible/index.d.ts +0 -85
  175. package/context-menu/index.d.ts +0 -129
  176. package/dialog/index.d.ts +0 -205
  177. package/dropdown-menu/README.md +0 -1
  178. package/dropdown-menu/index.d.ts +0 -171
  179. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs +0 -583
  180. package/fesm2022/radix-ng-primitives-dropdown-menu.mjs.map +0 -1
  181. package/fesm2022/radix-ng-primitives-hover-card.mjs +0 -1246
  182. package/fesm2022/radix-ng-primitives-hover-card.mjs.map +0 -1
  183. package/fesm2022/radix-ng-primitives-tooltip2.mjs +0 -740
  184. package/fesm2022/radix-ng-primitives-tooltip2.mjs.map +0 -1
  185. package/hover-card/README.md +0 -3
  186. package/hover-card/index.d.ts +0 -472
  187. package/menu/index.d.ts +0 -139
  188. package/menubar/index.d.ts +0 -56
  189. package/navigation-menu/index.d.ts +0 -405
  190. package/number-field/index.d.ts +0 -203
  191. package/popover/index.d.ts +0 -403
  192. package/portal/index.d.ts +0 -22
  193. package/presence/index.d.ts +0 -103
  194. package/progress/index.d.ts +0 -79
  195. package/select/index.d.ts +0 -214
  196. package/separator/index.d.ts +0 -63
  197. package/slider/index.d.ts +0 -263
  198. package/switch/index.d.ts +0 -105
  199. package/tabs/index.d.ts +0 -112
  200. package/toggle/index.d.ts +0 -75
  201. package/toggle-group/index.d.ts +0 -194
  202. package/toolbar/index.d.ts +0 -55
  203. package/tooltip/index.d.ts +0 -433
  204. package/tooltip2/README.md +0 -3
  205. package/tooltip2/index.d.ts +0 -325
  206. /package/{focus-guards/index.d.ts → types/radix-ng-primitives-focus-guards.d.ts} +0 -0
  207. /package/{index.d.ts → types/radix-ng-primitives.d.ts} +0 -0
@@ -1,29 +1,240 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, computed, model, input, booleanAttribute, 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
+
217
+ function isIndeterminate(checked) {
218
+ return checked === 'indeterminate';
219
+ }
220
+ function getState(checked) {
221
+ return isIndeterminate(checked) ? 'indeterminate' : checked ? 'checked' : 'unchecked';
222
+ }
9
223
  const rootContext = () => {
10
224
  const checkbox = inject(RdxCheckboxRootDirective);
11
- 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.
12
227
  return {
13
- checked: controlValueAccessor.value,
14
- disabled: controlValueAccessor.disabled,
228
+ checked: checkbox.checkedState,
229
+ indeterminate: checkbox.indeterminateState,
230
+ disabled: checkbox.disabledState,
15
231
  required: checkbox.required,
16
- value: checkbox.value,
232
+ value: checkbox.submitValue,
17
233
  name: checkbox.name,
234
+ parent: checkbox.parent,
18
235
  form: checkbox.form,
19
236
  readonly: checkbox.readonly,
20
- state: computed(() => {
21
- const checked = controlValueAccessor.value();
22
- if (checked === 'indeterminate') {
23
- return checked;
24
- }
25
- return checked ? 'checked' : 'unchecked';
26
- }),
237
+ state: checkbox.state,
27
238
  toggle() {
28
239
  checkbox.toggle();
29
240
  }
@@ -36,58 +247,148 @@ 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.
264
+ * @group Props
265
+ */
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`.
41
271
  * @group Props
42
272
  */
43
- this.checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
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" }] : []));
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
52
288
  */
53
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
289
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
54
290
  /**
55
291
  * Whether the user should be unable to tick or untick the checkbox.
56
292
  * @group Props
57
293
  */
58
- this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
294
+ this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
59
295
  /**
60
296
  * Whether or not the checkbox is required.
61
297
  * @group Props
62
298
  */
63
- this.required = input(false, ...(ngDevMode ? [{ debugName: "required", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
299
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
300
+ /**
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.
303
+ * @group Props
304
+ */
305
+ this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
64
306
  /**
65
- * Name of the form control. Submitted with the form as part of a name/value pair.
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.
66
309
  * @group Props
67
310
  */
68
- this.name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : []));
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
72
315
  */
73
- this.form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : []));
316
+ this.form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : /* istanbul ignore next */ []));
74
317
  /**
75
318
  * Event emitted when the checkbox checked state changes.
76
319
  * @group Emits
77
320
  */
78
321
  this.onCheckedChange = outputFromObservable(outputToObservable(this.controlValueAccessor.valueChange));
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));
363
+ }
364
+ });
79
365
  }
80
366
  toggle() {
81
- const checked = this.controlValueAccessor.value();
82
- if (checked === 'indeterminate') {
83
- 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
+ }
84
378
  }
85
- 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);
86
387
  }
87
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
88
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.3", 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" }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
388
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
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 }); }
89
390
  }
90
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxRootDirective, decorators: [{
391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxRootDirective, decorators: [{
91
392
  type: Directive,
92
393
  args: [{
93
394
  selector: '[rdxCheckboxRoot]',
@@ -97,9 +398,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
97
398
  directive: RdxControlValueAccessor,
98
399
  inputs: ['value:checked', 'disabled']
99
400
  }
100
- ]
401
+ ],
402
+ host: {
403
+ '[attr.data-state]': 'state()',
404
+ '[attr.data-disabled]': 'isDisabled() ? "" : undefined',
405
+ '[attr.data-readonly]': 'readonly() ? "" : undefined',
406
+ '[attr.data-required]': 'required() ? "" : undefined'
407
+ }
101
408
  }]
102
- }] });
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"] }] } });
103
410
 
104
411
  /**
105
412
  * Directive: rdxCheckboxButton
@@ -110,7 +417,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
110
417
  class RdxCheckboxButtonDirective {
111
418
  constructor() {
112
419
  this.rootContext = injectCheckboxRootContext();
420
+ this.group = injectCheckboxGroupContext(true);
113
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
+ });
114
438
  }
115
439
  clicked(event) {
116
440
  if (event.defaultPrevented || this.rootContext.readonly()) {
@@ -124,63 +448,64 @@ class RdxCheckboxButtonDirective {
124
448
  event.stopPropagation();
125
449
  }
126
450
  }
127
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
128
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.3", 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", "disabled": "rootContext.disabled()", "value": "rootContext.value()" } }, ngImport: i0 }); }
451
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
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 }); }
129
453
  }
130
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxButtonDirective, decorators: [{
454
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxButtonDirective, decorators: [{
131
455
  type: Directive,
132
456
  args: [{
133
457
  selector: 'button[rdxCheckboxButton]',
134
458
  host: {
135
459
  type: 'button',
136
460
  role: 'checkbox',
137
- '[attr.aria-checked]': 'rootContext.checked() === "indeterminate" ? "mixed" : rootContext.checked()',
461
+ '[attr.aria-checked]': 'rootContext.indeterminate() ? "mixed" : rootContext.checked()',
462
+ '[attr.aria-controls]': 'ariaControls()',
138
463
  '[attr.aria-required]': 'rootContext.required() || undefined',
139
464
  '[attr.aria-readonly]': 'rootContext.readonly() || undefined',
140
465
  '[attr.data-state]': 'rootContext.state()',
141
- '[attr.data-disabled]': 'rootContext.disabled() || undefined',
142
- '[attr.data-readonly]': 'rootContext.readonly() || undefined',
143
- '[disabled]': 'rootContext.disabled()',
144
- '[value]': 'rootContext.value()',
466
+ '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
467
+ '[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
468
+ '[attr.disabled]': 'rootContext.disabled() ? "" : undefined',
469
+ '[attr.value]': 'rootContext.value()',
145
470
  '(click)': 'clicked($event)',
146
471
  // According to WAI ARIA, Checkboxes don't activate on enter keypress
147
472
  '(keydown.enter)': '$event.preventDefault()'
148
473
  }
149
474
  }]
150
- }] });
475
+ }], ctorParameters: () => [] });
151
476
 
152
477
  class RdxCheckboxIndicatorDirective {
153
478
  constructor() {
154
479
  this.rootContext = injectCheckboxRootContext();
155
480
  }
156
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
157
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.3", 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 }); }
481
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
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 }); }
158
483
  }
159
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxIndicatorDirective, decorators: [{
484
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorDirective, decorators: [{
160
485
  type: Directive,
161
486
  args: [{
162
487
  selector: '[rdxCheckboxIndicator]',
163
488
  host: {
164
489
  '[attr.data-state]': 'rootContext.state()',
165
490
  '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
166
- '[hidden]': '!rootContext.checked()',
491
+ '[hidden]': '!rootContext.checked() && !rootContext.indeterminate()',
167
492
  '[style.pointer-events]': '"none"'
168
493
  }
169
494
  }]
170
495
  }] });
171
496
 
172
497
  class RdxCheckboxIndicatorPresenceDirective {
173
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxIndicatorPresenceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
174
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.3", type: RdxCheckboxIndicatorPresenceDirective, isStandalone: true, selector: "ng-template[rdxCheckboxIndicatorPresence]", providers: [
498
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorPresenceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
499
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxCheckboxIndicatorPresenceDirective, isStandalone: true, selector: "ng-template[rdxCheckboxIndicatorPresence]", providers: [
175
500
  provideRdxPresenceContext(() => {
176
501
  const rootContext = injectCheckboxRootContext();
177
502
  return {
178
- present: computed(() => !!rootContext.checked())
503
+ present: computed(() => rootContext.checked() || rootContext.indeterminate())
179
504
  };
180
505
  })
181
506
  ], hostDirectives: [{ directive: i1$1.RdxPresenceDirective }], ngImport: i0 }); }
182
507
  }
183
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxIndicatorPresenceDirective, decorators: [{
508
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxIndicatorPresenceDirective, decorators: [{
184
509
  type: Directive,
185
510
  args: [{
186
511
  selector: 'ng-template[rdxCheckboxIndicatorPresence]',
@@ -188,7 +513,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
188
513
  provideRdxPresenceContext(() => {
189
514
  const rootContext = injectCheckboxRootContext();
190
515
  return {
191
- present: computed(() => !!rootContext.checked())
516
+ present: computed(() => rootContext.checked() || rootContext.indeterminate())
192
517
  };
193
518
  })
194
519
  ],
@@ -199,32 +524,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
199
524
  class RdxCheckboxInputDirective {
200
525
  constructor() {
201
526
  this.rootContext = injectCheckboxRootContext();
202
- this.elementRef = inject(ElementRef);
203
- this.destroyRef = inject(DestroyRef);
527
+ this.input = inject(ElementRef).nativeElement;
528
+ let isInitial = true;
204
529
  /**
205
- * 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.
206
532
  *
207
- * Why this exists
208
- * - The checkbox input in this directive is visually hidden and controlled by the Radix checkbox state.
209
- * - When Radix changes the checked state, it updates the input's DOM attribute ("checked").
210
- * - Some forms and frameworks rely on native events (click/change) from the input to sync value, validity and
211
- * 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.
212
539
  */
213
- afterNextRender(() => {
214
- const mutationObserver = new MutationObserver(() => {
215
- this.elementRef.nativeElement.dispatchEvent(new Event('click', { bubbles: true }));
216
- });
217
- mutationObserver.observe(this.elementRef.nativeElement, {
218
- attributes: true,
219
- attributeFilter: ['checked']
220
- });
221
- 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 }));
222
551
  });
223
552
  }
224
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
225
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.3", 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", "value": "rootContext.value()", "required": "rootContext.required() || undefined", "disabled": "rootContext.disabled()", "style": "{\n position: 'absolute',\n pointerEvents: 'none',\n opacity: 0,\n margin: 0,\n inset: 0,\n transform: 'translateX(-100%)',\n }" } }, ngImport: i0 }); }
553
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxInputDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
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 }); }
226
555
  }
227
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxInputDirective, decorators: [{
556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxInputDirective, decorators: [{
228
557
  type: Directive,
229
558
  args: [{
230
559
  selector: 'input[rdxCheckboxInput]',
@@ -233,11 +562,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
233
562
  tabindex: '-1',
234
563
  'aria-hidden': 'true',
235
564
  '[attr.name]': 'rootContext.name() || undefined',
236
- '[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',
237
568
  '[attr.form]': 'rootContext.form() || undefined',
238
- '[value]': 'rootContext.value()',
569
+ '[attr.value]': 'rootContext.value()',
239
570
  '[required]': 'rootContext.required() || undefined',
240
- '[disabled]': 'rootContext.disabled()',
571
+ '[attr.disabled]': 'rootContext.disabled() ? "" : undefined',
241
572
  '[style]': `{
242
573
  position: 'absolute',
243
574
  pointerEvents: 'none',
@@ -255,22 +586,25 @@ const checkboxImports = [
255
586
  RdxCheckboxRootDirective,
256
587
  RdxCheckboxButtonDirective,
257
588
  RdxCheckboxIndicatorDirective,
258
- RdxCheckboxIndicatorPresenceDirective
589
+ RdxCheckboxIndicatorPresenceDirective,
590
+ RdxCheckboxGroupDirective
259
591
  ];
260
592
  class RdxCheckboxModule {
261
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
262
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxModule, imports: [RdxCheckboxInputDirective,
593
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
594
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule, imports: [RdxCheckboxInputDirective,
263
595
  RdxCheckboxRootDirective,
264
596
  RdxCheckboxButtonDirective,
265
597
  RdxCheckboxIndicatorDirective,
266
- RdxCheckboxIndicatorPresenceDirective], exports: [RdxCheckboxInputDirective,
598
+ RdxCheckboxIndicatorPresenceDirective,
599
+ RdxCheckboxGroupDirective], exports: [RdxCheckboxInputDirective,
267
600
  RdxCheckboxRootDirective,
268
601
  RdxCheckboxButtonDirective,
269
602
  RdxCheckboxIndicatorDirective,
270
- RdxCheckboxIndicatorPresenceDirective] }); }
271
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxModule }); }
603
+ RdxCheckboxIndicatorPresenceDirective,
604
+ RdxCheckboxGroupDirective] }); }
605
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule }); }
272
606
  }
273
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: RdxCheckboxModule, decorators: [{
607
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxCheckboxModule, decorators: [{
274
608
  type: NgModule,
275
609
  args: [{
276
610
  imports: [...checkboxImports],
@@ -282,5 +616,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImpor
282
616
  * Generated bundle index. Do not edit.
283
617
  */
284
618
 
285
- export { RdxCheckboxButtonDirective, RdxCheckboxIndicatorDirective, RdxCheckboxIndicatorPresenceDirective, RdxCheckboxInputDirective, RdxCheckboxModule, RdxCheckboxRootDirective, checkboxImports, injectCheckboxRootContext, provideCheckboxRootContext };
619
+ export { RdxCheckboxButtonDirective, RdxCheckboxGroupDirective, RdxCheckboxIndicatorDirective, RdxCheckboxIndicatorPresenceDirective, RdxCheckboxInputDirective, RdxCheckboxModule, RdxCheckboxRootDirective, checkboxImports, getState, injectCheckboxGroupContext, injectCheckboxRootContext, isIndeterminate, provideCheckboxGroupContext, provideCheckboxRootContext };
286
620
  //# sourceMappingURL=radix-ng-primitives-checkbox.mjs.map