@radix-ng/primitives 1.0.6 → 1.0.8

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 (83) hide show
  1. package/aspect-ratio/README.md +1 -1
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +6 -1
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs +9 -34
  5. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-autocomplete.mjs +43 -9
  7. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-avatar.mjs +69 -18
  9. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-checkbox.mjs +93 -16
  11. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-collapsible.mjs +4 -3
  13. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-combobox.mjs +43 -9
  15. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-core.mjs +208 -2
  17. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-date-field.mjs +50 -9
  19. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-editable.mjs +31 -5
  21. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-field.mjs +276 -58
  23. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-form.mjs +111 -27
  25. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-input.mjs +16 -6
  27. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-label.mjs +5 -4
  29. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-number-field.mjs +39 -10
  31. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-progress.mjs +37 -22
  33. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-radio.mjs +18 -9
  35. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-scroll-area.mjs +4 -4
  37. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-select.mjs +26 -8
  39. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-separator.mjs +7 -13
  41. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  42. package/fesm2022/radix-ng-primitives-signal-forms.mjs +184 -0
  43. package/fesm2022/radix-ng-primitives-signal-forms.mjs.map +1 -0
  44. package/fesm2022/radix-ng-primitives-slider.mjs +25 -10
  45. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  46. package/fesm2022/radix-ng-primitives-switch.mjs +32 -8
  47. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  48. package/fesm2022/radix-ng-primitives-tabs.mjs +4 -4
  49. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  50. package/fesm2022/radix-ng-primitives-time-field.mjs +49 -9
  51. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  52. package/fesm2022/radix-ng-primitives-toggle-group.mjs +33 -16
  53. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  54. package/fesm2022/radix-ng-primitives-toggle.mjs +24 -29
  55. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-toolbar.mjs +15 -2
  57. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  58. package/package.json +11 -1
  59. package/types/radix-ng-primitives-accordion.d.ts +1 -0
  60. package/types/radix-ng-primitives-aspect-ratio.d.ts +10 -23
  61. package/types/radix-ng-primitives-autocomplete.d.ts +21 -2
  62. package/types/radix-ng-primitives-avatar.d.ts +51 -25
  63. package/types/radix-ng-primitives-checkbox.d.ts +78 -101
  64. package/types/radix-ng-primitives-combobox.d.ts +51 -2
  65. package/types/radix-ng-primitives-core.d.ts +206 -3
  66. package/types/radix-ng-primitives-date-field.d.ts +27 -2
  67. package/types/radix-ng-primitives-editable.d.ts +15 -2
  68. package/types/radix-ng-primitives-field.d.ts +165 -236
  69. package/types/radix-ng-primitives-form.d.ts +122 -37
  70. package/types/radix-ng-primitives-input.d.ts +8 -1
  71. package/types/radix-ng-primitives-label.d.ts +3 -2
  72. package/types/radix-ng-primitives-number-field.d.ts +15 -4
  73. package/types/radix-ng-primitives-progress.d.ts +59 -121
  74. package/types/radix-ng-primitives-radio.d.ts +7 -3
  75. package/types/radix-ng-primitives-select.d.ts +11 -3
  76. package/types/radix-ng-primitives-separator.d.ts +0 -7
  77. package/types/radix-ng-primitives-signal-forms.d.ts +97 -0
  78. package/types/radix-ng-primitives-slider.d.ts +9 -7
  79. package/types/radix-ng-primitives-switch.d.ts +15 -2
  80. package/types/radix-ng-primitives-time-field.d.ts +26 -2
  81. package/types/radix-ng-primitives-toggle-group.d.ts +13 -7
  82. package/types/radix-ng-primitives-toggle.d.ts +8 -8
  83. package/types/radix-ng-primitives-toolbar.d.ts +2 -0
@@ -1,12 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, input, signal, Directive, PLATFORM_ID, output, NgModule } from '@angular/core';
3
- import { watch, provideToken } from '@radix-ng/primitives/core';
3
+ import { createContext, watch } from '@radix-ng/primitives/core';
4
4
  import { isPlatformBrowser } from '@angular/common';
5
5
 
6
- const AVATAR_ROOT_CONTEXT = new InjectionToken('AVATAR_ROOT_CONTEXT');
7
- function injectAvatarRootContext() {
8
- return inject(AVATAR_ROOT_CONTEXT);
9
- }
6
+ const [injectAvatarRootContext, provideAvatarRootContext] = createContext('AvatarRootContext', 'components/avatar');
10
7
 
11
8
  const defaultAvatarConfig = {
12
9
  delayMs: 0
@@ -84,6 +81,28 @@ class RdxAvatarImageDirective {
84
81
  */
85
82
  this.src = input(/* @ts-ignore */
86
83
  ...(ngDevMode ? [undefined, { debugName: "src" }] : /* istanbul ignore next */ []));
84
+ /**
85
+ * A responsive `srcset` for the image. When set without `src`, an empty `src` no longer counts as
86
+ * an error.
87
+ *
88
+ * @group Props
89
+ */
90
+ this.srcSet = input(/* @ts-ignore */
91
+ ...(ngDevMode ? [undefined, { debugName: "srcSet" }] : /* istanbul ignore next */ []));
92
+ /**
93
+ * The `sizes` attribute paired with `srcSet`.
94
+ *
95
+ * @group Props
96
+ */
97
+ this.sizes = input(/* @ts-ignore */
98
+ ...(ngDevMode ? [undefined, { debugName: "sizes" }] : /* istanbul ignore next */ []));
99
+ /**
100
+ * The CORS mode used when fetching the image.
101
+ *
102
+ * @group Props
103
+ */
104
+ this.crossOrigin = input(/* @ts-ignore */
105
+ ...(ngDevMode ? [undefined, { debugName: "crossOrigin" }] : /* istanbul ignore next */ []));
87
106
  this.referrerPolicy = input(/* @ts-ignore */
88
107
  ...(ngDevMode ? [undefined, { debugName: "referrerPolicy" }] : /* istanbul ignore next */ []));
89
108
  /**
@@ -99,8 +118,8 @@ class RdxAvatarImageDirective {
99
118
  if (!isPlatformBrowser(this.platformId)) {
100
119
  return;
101
120
  }
102
- watch([this.src, this.referrerPolicy], ([src, referrer], onCleanup) => {
103
- if (!src) {
121
+ watch([this.src, this.srcSet, this.sizes, this.crossOrigin, this.referrerPolicy], ([src, srcSet, sizes, crossOrigin, referrer], onCleanup) => {
122
+ if (!src && !srcSet) {
104
123
  this.loadingStatus.set('error');
105
124
  return;
106
125
  }
@@ -108,11 +127,27 @@ class RdxAvatarImageDirective {
108
127
  this.loadingStatus.set('loading');
109
128
  image.onload = () => this.loadingStatus.set('loaded');
110
129
  image.onerror = () => this.loadingStatus.set('error');
111
- // Set referrerPolicy before src so it applies to the fetch.
130
+ // Set the fetch-affecting attributes before `src`/`srcset` so they apply to the request.
112
131
  if (referrer) {
113
132
  image.referrerPolicy = referrer;
114
133
  }
115
- image.src = src;
134
+ if (crossOrigin !== undefined) {
135
+ image.crossOrigin = crossOrigin;
136
+ }
137
+ if (srcSet) {
138
+ image.srcset = srcSet;
139
+ }
140
+ if (sizes) {
141
+ image.sizes = sizes;
142
+ }
143
+ if (src) {
144
+ image.src = src;
145
+ }
146
+ // A cached/already-decoded image is ready synchronously — resolve immediately so the
147
+ // fallback delay isn't started for an image that is already available.
148
+ if (image.complete && image.naturalWidth > 0) {
149
+ this.loadingStatus.set('loaded');
150
+ }
116
151
  // Drop handlers for a stale src (or on destroy) so a late load/error
117
152
  // can't overwrite the status for the current one.
118
153
  onCleanup(() => {
@@ -121,14 +156,16 @@ class RdxAvatarImageDirective {
121
156
  });
122
157
  });
123
158
  watch([this.loadingStatus], ([value]) => {
124
- this.onLoadingStatusChange.emit(value);
125
- if (value !== 'idle') {
126
- this.rootContext.imageLoadingStatus.set(value);
159
+ // Base UI fires the callback only once loading actually starts — skip the initial `idle`.
160
+ if (value === 'idle') {
161
+ return;
127
162
  }
163
+ this.onLoadingStatusChange.emit(value);
164
+ this.rootContext.imageLoadingStatus.set(value);
128
165
  });
129
166
  }
130
167
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAvatarImageDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
131
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxAvatarImageDirective, isStandalone: true, selector: "img[rdxAvatarImage]", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, referrerPolicy: { classPropertyName: "referrerPolicy", publicName: "referrerPolicy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onLoadingStatusChange: "onLoadingStatusChange" }, host: { attributes: { "role": "img" }, properties: { "attr.src": "src()", "attr.referrerpolicy": "referrerPolicy()", "style.display": "(rootContext.imageLoadingStatus() === \"loaded\") ? null : \"none\"" } }, exportAs: ["rdxAvatarImage"], ngImport: i0 }); }
168
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxAvatarImageDirective, isStandalone: true, selector: "img[rdxAvatarImage]", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, srcSet: { classPropertyName: "srcSet", publicName: "srcSet", isSignal: true, isRequired: false, transformFunction: null }, sizes: { classPropertyName: "sizes", publicName: "sizes", isSignal: true, isRequired: false, transformFunction: null }, crossOrigin: { classPropertyName: "crossOrigin", publicName: "crossOrigin", isSignal: true, isRequired: false, transformFunction: null }, referrerPolicy: { classPropertyName: "referrerPolicy", publicName: "referrerPolicy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onLoadingStatusChange: "onLoadingStatusChange" }, host: { properties: { "attr.src": "src()", "attr.srcset": "srcSet()", "attr.sizes": "sizes()", "attr.crossorigin": "crossOrigin()", "attr.referrerpolicy": "referrerPolicy()", "style.display": "(rootContext.imageLoadingStatus() === \"loaded\") ? null : \"none\"" } }, exportAs: ["rdxAvatarImage"], ngImport: i0 }); }
132
169
  }
133
170
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAvatarImageDirective, decorators: [{
134
171
  type: Directive,
@@ -136,28 +173,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
136
173
  selector: 'img[rdxAvatarImage]',
137
174
  exportAs: 'rdxAvatarImage',
138
175
  host: {
139
- role: 'img',
176
+ // The host is a native `<img>` (selector `img[rdxAvatarImage]`) — its implicit `img` role is
177
+ // already correct, and forcing `role="img"` here would override any consumer override (Base UI
178
+ // adds no role).
140
179
  '[attr.src]': 'src()',
180
+ '[attr.srcset]': 'srcSet()',
181
+ '[attr.sizes]': 'sizes()',
182
+ '[attr.crossorigin]': 'crossOrigin()',
141
183
  '[attr.referrerpolicy]': 'referrerPolicy()',
142
184
  '[style.display]': '(rootContext.imageLoadingStatus() === "loaded") ? null : "none"'
143
185
  }
144
186
  }]
145
- }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], referrerPolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrerPolicy", required: false }] }], onLoadingStatusChange: [{ type: i0.Output, args: ["onLoadingStatusChange"] }] } });
187
+ }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], srcSet: [{ type: i0.Input, args: [{ isSignal: true, alias: "srcSet", required: false }] }], sizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizes", required: false }] }], crossOrigin: [{ type: i0.Input, args: [{ isSignal: true, alias: "crossOrigin", required: false }] }], referrerPolicy: [{ type: i0.Input, args: [{ isSignal: true, alias: "referrerPolicy", required: false }] }], onLoadingStatusChange: [{ type: i0.Output, args: ["onLoadingStatusChange"] }] } });
146
188
 
189
+ const rootContext = () => {
190
+ const root = inject(RdxAvatarRootDirective);
191
+ return {
192
+ imageLoadingStatus: root.imageLoadingStatus
193
+ };
194
+ };
195
+ /**
196
+ * @group Components
197
+ */
147
198
  class RdxAvatarRootDirective {
148
199
  constructor() {
149
200
  this.imageLoadingStatus = signal('idle', /* @ts-ignore */
150
201
  ...(ngDevMode ? [{ debugName: "imageLoadingStatus" }] : /* istanbul ignore next */ []));
151
202
  }
152
203
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAvatarRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
153
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxAvatarRootDirective, isStandalone: true, selector: "span[rdxAvatarRoot]", providers: [provideToken(AVATAR_ROOT_CONTEXT, RdxAvatarRootDirective)], exportAs: ["rdxAvatarRoot"], ngImport: i0 }); }
204
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxAvatarRootDirective, isStandalone: true, selector: "span[rdxAvatarRoot]", providers: [provideAvatarRootContext(rootContext)], exportAs: ["rdxAvatarRoot"], ngImport: i0 }); }
154
205
  }
155
206
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxAvatarRootDirective, decorators: [{
156
207
  type: Directive,
157
208
  args: [{
158
209
  selector: 'span[rdxAvatarRoot]',
159
210
  exportAs: 'rdxAvatarRoot',
160
- providers: [provideToken(AVATAR_ROOT_CONTEXT, RdxAvatarRootDirective)]
211
+ providers: [provideAvatarRootContext(rootContext)]
161
212
  }]
162
213
  }] });
163
214
 
@@ -179,5 +230,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
179
230
  * Generated bundle index. Do not edit.
180
231
  */
181
232
 
182
- export { RdxAvatarConfigToken, RdxAvatarFallbackDirective, RdxAvatarImageDirective, RdxAvatarModule, RdxAvatarRootDirective, defaultAvatarConfig, injectAvatarConfig, provideRdxAvatarConfig };
233
+ export { RdxAvatarConfigToken, RdxAvatarFallbackDirective, RdxAvatarImageDirective, RdxAvatarModule, RdxAvatarRootDirective, defaultAvatarConfig, injectAvatarConfig, injectAvatarRootContext, provideAvatarRootContext, provideRdxAvatarConfig };
183
234
  //# sourceMappingURL=radix-ng-primitives-avatar.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"radix-ng-primitives-avatar.mjs","sources":["../../../packages/primitives/avatar/src/avatar-context.token.ts","../../../packages/primitives/avatar/src/avatar.config.ts","../../../packages/primitives/avatar/src/avatar-fallback.directive.ts","../../../packages/primitives/avatar/src/avatar-image.directive.ts","../../../packages/primitives/avatar/src/avatar-root.directive.ts","../../../packages/primitives/avatar/index.ts","../../../packages/primitives/avatar/radix-ng-primitives-avatar.ts"],"sourcesContent":["import { inject, InjectionToken, WritableSignal } from '@angular/core';\nimport { RdxImageLoadingStatus } from './types';\n\nexport interface AvatarContextToken {\n imageLoadingStatus: WritableSignal<RdxImageLoadingStatus>;\n}\n\nexport const AVATAR_ROOT_CONTEXT = new InjectionToken<AvatarContextToken>('AVATAR_ROOT_CONTEXT');\n\nexport function injectAvatarRootContext(): AvatarContextToken {\n return inject(AVATAR_ROOT_CONTEXT);\n}\n","import { inject, InjectionToken, Provider } from '@angular/core';\n\nexport interface RdxAvatarConfig {\n /**\n * Define a delay before the fallback is shown.\n * This is useful to only show the fallback for those with slower connections.\n * @default 0\n */\n delayMs: number;\n}\n\nexport const defaultAvatarConfig: RdxAvatarConfig = {\n delayMs: 0\n};\n\nexport const RdxAvatarConfigToken = new InjectionToken<RdxAvatarConfig>('RdxAvatarConfigToken');\n\nexport function provideRdxAvatarConfig(config: Partial<RdxAvatarConfig>): Provider[] {\n return [\n {\n provide: RdxAvatarConfigToken,\n useValue: { ...defaultAvatarConfig, ...config }\n }\n ];\n}\n\nexport function injectAvatarConfig(): RdxAvatarConfig {\n return inject(RdxAvatarConfigToken, { optional: true }) ?? defaultAvatarConfig;\n}\n","import { Directive, input, signal } from '@angular/core';\nimport { watch } from '@radix-ng/primitives/core';\nimport { injectAvatarRootContext } from './avatar-context.token';\nimport { injectAvatarConfig } from './avatar.config';\n\n/**\n * @group Components\n */\n@Directive({\n selector: 'span[rdxAvatarFallback]',\n exportAs: 'rdxAvatarFallback',\n host: {\n '[style.display]': 'canRender() && rootContext.imageLoadingStatus() !== \"loaded\" ? null : \"none\"'\n }\n})\nexport class RdxAvatarFallbackDirective {\n private readonly config = injectAvatarConfig();\n\n protected readonly rootContext = injectAvatarRootContext();\n\n /**\n * Useful for delaying rendering so it only appears for those with slower connections.\n *\n * @group Props\n * @defaultValue 0\n */\n readonly delayMs = input<number>(this.config.delayMs);\n\n protected readonly canRender = signal(false);\n\n constructor() {\n // Enable the fallback after an optional delay (so it only appears for\n // those on slower connections), independent of the image load status —\n // matches Radix/Base UI. The `display` binding then hides it once the\n // image has loaded.\n watch([this.delayMs], ([delayMs], onCleanup) => {\n this.canRender.set(false);\n\n if (delayMs) {\n const timeout = setTimeout(() => this.canRender.set(true), delayMs);\n // Cancel a pending delay if delayMs changes or the directive is\n // destroyed before it fires.\n onCleanup(() => clearTimeout(timeout));\n } else {\n this.canRender.set(true);\n }\n });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { Directive, inject, input, output, PLATFORM_ID, signal } from '@angular/core';\nimport { watch } from '@radix-ng/primitives/core';\nimport { injectAvatarRootContext } from './avatar-context.token';\nimport { HTMLAttributeReferrerPolicy, RdxImageLoadingStatus } from './types';\n\n/**\n * @group Components\n */\n@Directive({\n selector: 'img[rdxAvatarImage]',\n exportAs: 'rdxAvatarImage',\n host: {\n role: 'img',\n '[attr.src]': 'src()',\n '[attr.referrerpolicy]': 'referrerPolicy()',\n '[style.display]': '(rootContext.imageLoadingStatus() === \"loaded\") ? null : \"none\"'\n }\n})\nexport class RdxAvatarImageDirective {\n private readonly platformId = inject(PLATFORM_ID);\n\n protected readonly rootContext = injectAvatarRootContext();\n\n /**\n * @group Props\n */\n readonly src = input<string>();\n\n readonly referrerPolicy = input<HTMLAttributeReferrerPolicy>();\n\n /**\n * A callback providing information about the loading status of the image.\n * This is useful in case you want to control more precisely what to render as the image is loading.\n *\n * @group Emits\n */\n readonly onLoadingStatusChange = output<RdxImageLoadingStatus>();\n\n private readonly loadingStatus = signal<RdxImageLoadingStatus>('idle');\n\n constructor() {\n // Loading is browser-only; on the server the status stays 'idle'.\n if (!isPlatformBrowser(this.platformId)) {\n return;\n }\n\n watch([this.src, this.referrerPolicy], ([src, referrer], onCleanup) => {\n if (!src) {\n this.loadingStatus.set('error');\n return;\n }\n\n const image = new window.Image();\n this.loadingStatus.set('loading');\n image.onload = () => this.loadingStatus.set('loaded');\n image.onerror = () => this.loadingStatus.set('error');\n // Set referrerPolicy before src so it applies to the fetch.\n if (referrer) {\n image.referrerPolicy = referrer;\n }\n image.src = src;\n\n // Drop handlers for a stale src (or on destroy) so a late load/error\n // can't overwrite the status for the current one.\n onCleanup(() => {\n image.onload = null;\n image.onerror = null;\n });\n });\n\n watch([this.loadingStatus], ([value]) => {\n this.onLoadingStatusChange.emit(value);\n if (value !== 'idle') {\n this.rootContext.imageLoadingStatus.set(value);\n }\n });\n }\n}\n","import { Directive, signal } from '@angular/core';\nimport { provideToken } from '@radix-ng/primitives/core';\nimport { AVATAR_ROOT_CONTEXT, AvatarContextToken } from './avatar-context.token';\nimport { RdxImageLoadingStatus } from './types';\n\n@Directive({\n selector: 'span[rdxAvatarRoot]',\n exportAs: 'rdxAvatarRoot',\n providers: [provideToken(AVATAR_ROOT_CONTEXT, RdxAvatarRootDirective)]\n})\nexport class RdxAvatarRootDirective implements AvatarContextToken {\n readonly imageLoadingStatus = signal<RdxImageLoadingStatus>('idle');\n}\n","import { NgModule } from '@angular/core';\nimport { RdxAvatarFallbackDirective } from './src/avatar-fallback.directive';\nimport { RdxAvatarImageDirective } from './src/avatar-image.directive';\nimport { RdxAvatarRootDirective } from './src/avatar-root.directive';\n\nexport * from './src/avatar-fallback.directive';\nexport * from './src/avatar-image.directive';\nexport * from './src/avatar-root.directive';\nexport * from './src/avatar.config';\nexport type { RdxImageLoadingStatus } from './src/types';\n\nconst _imports = [RdxAvatarRootDirective, RdxAvatarFallbackDirective, RdxAvatarImageDirective];\n\n@NgModule({\n imports: [..._imports],\n exports: [..._imports]\n})\nexport class RdxAvatarModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAOO,MAAM,mBAAmB,GAAG,IAAI,cAAc,CAAqB,qBAAqB,CAAC;SAEhF,uBAAuB,GAAA;AACnC,IAAA,OAAO,MAAM,CAAC,mBAAmB,CAAC;AACtC;;ACAO,MAAM,mBAAmB,GAAoB;AAChD,IAAA,OAAO,EAAE;;MAGA,oBAAoB,GAAG,IAAI,cAAc,CAAkB,sBAAsB;AAExF,SAAU,sBAAsB,CAAC,MAAgC,EAAA;IACnE,OAAO;AACH,QAAA;AACI,YAAA,OAAO,EAAE,oBAAoB;AAC7B,YAAA,QAAQ,EAAE,EAAE,GAAG,mBAAmB,EAAE,GAAG,MAAM;AAChD;KACJ;AACL;SAEgB,kBAAkB,GAAA;AAC9B,IAAA,OAAO,MAAM,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,IAAI,mBAAmB;AAClF;;ACvBA;;AAEG;MAQU,0BAA0B,CAAA;AAenC,IAAA,WAAA,GAAA;QAdiB,IAAA,CAAA,MAAM,GAAG,kBAAkB,EAAE;QAE3B,IAAA,CAAA,WAAW,GAAG,uBAAuB,EAAE;AAE1D;;;;;AAKG;AACM,QAAA,IAAA,CAAA,OAAO,GAAG,KAAK,CAAS,IAAI,CAAC,MAAM,CAAC,OAAO;oFAAC;QAElC,IAAA,CAAA,SAAS,GAAG,MAAM,CAAC,KAAK;sFAAC;;;;;AAOxC,QAAA,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,SAAS,KAAI;AAC3C,YAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;YAEzB,IAAI,OAAO,EAAE;AACT,gBAAA,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;;;gBAGnE,SAAS,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;YAC1C;iBAAO;AACH,gBAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAC5B;AACJ,QAAA,CAAC,CAAC;IACN;8GAhCS,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAA1B,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,kFAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,mBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAA1B,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAPtC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACP,oBAAA,QAAQ,EAAE,yBAAyB;AACnC,oBAAA,QAAQ,EAAE,mBAAmB;AAC7B,oBAAA,IAAI,EAAE;AACF,wBAAA,iBAAiB,EAAE;AACtB;AACJ,iBAAA;;;ACRD;;AAEG;MAWU,uBAAuB,CAAA;AAsBhC,IAAA,WAAA,GAAA;AArBiB,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;QAE9B,IAAA,CAAA,WAAW,GAAG,uBAAuB,EAAE;AAE1D;;AAEG;AACM,QAAA,IAAA,CAAA,GAAG,GAAG,KAAK;2FAAU;AAErB,QAAA,IAAA,CAAA,cAAc,GAAG,KAAK;sGAA+B;AAE9D;;;;;AAKG;QACM,IAAA,CAAA,qBAAqB,GAAG,MAAM,EAAyB;QAE/C,IAAA,CAAA,aAAa,GAAG,MAAM,CAAwB,MAAM;0FAAC;;QAIlE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE;YACrC;QACJ;QAEA,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAI;YAClE,IAAI,CAAC,GAAG,EAAE;AACN,gBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;gBAC/B;YACJ;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,EAAE;AAChC,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC;AACjC,YAAA,KAAK,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC;AACrD,YAAA,KAAK,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC;;YAErD,IAAI,QAAQ,EAAE;AACV,gBAAA,KAAK,CAAC,cAAc,GAAG,QAAQ;YACnC;AACA,YAAA,KAAK,CAAC,GAAG,GAAG,GAAG;;;YAIf,SAAS,CAAC,MAAK;AACX,gBAAA,KAAK,CAAC,MAAM,GAAG,IAAI;AACnB,gBAAA,KAAK,CAAC,OAAO,GAAG,IAAI;AACxB,YAAA,CAAC,CAAC;AACN,QAAA,CAAC,CAAC;AAEF,QAAA,KAAK,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAI;AACpC,YAAA,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC;AACtC,YAAA,IAAI,KAAK,KAAK,MAAM,EAAE;gBAClB,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC;YAClD;AACJ,QAAA,CAAC,CAAC;IACN;8GA1DS,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAvB,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,qBAAA,EAAA,uBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,MAAA,EAAA,KAAA,EAAA,EAAA,UAAA,EAAA,EAAA,UAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,kBAAA,EAAA,eAAA,EAAA,qEAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAvB,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAVnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACP,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,QAAQ,EAAE,gBAAgB;AAC1B,oBAAA,IAAI,EAAE;AACF,wBAAA,IAAI,EAAE,KAAK;AACX,wBAAA,YAAY,EAAE,OAAO;AACrB,wBAAA,uBAAuB,EAAE,kBAAkB;AAC3C,wBAAA,iBAAiB,EAAE;AACtB;AACJ,iBAAA;;;MCRY,sBAAsB,CAAA;AALnC,IAAA,WAAA,GAAA;QAMa,IAAA,CAAA,kBAAkB,GAAG,MAAM,CAAwB,MAAM;+FAAC;AACtE,IAAA;8GAFY,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAtB,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,SAAA,EAFpB,CAAC,YAAY,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,CAAC,EAAA,QAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAE7D,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBALlC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACP,oBAAA,QAAQ,EAAE,qBAAqB;AAC/B,oBAAA,QAAQ,EAAE,eAAe;AACzB,oBAAA,SAAS,EAAE,CAAC,YAAY,CAAC,mBAAmB,yBAAyB;AACxE,iBAAA;;;ACED,MAAM,QAAQ,GAAG,CAAC,sBAAsB,EAAE,0BAA0B,EAAE,uBAAuB,CAAC;MAMjF,eAAe,CAAA;8GAAf,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,QAAA,EAAA,CAAA,CAAA;+GAAf,eAAe,EAAA,OAAA,EAAA,CANV,sBAAsB,EAAE,0BAA0B,EAAE,uBAAuB,CAAA,EAAA,OAAA,EAAA,CAA3E,sBAAsB,EAAE,0BAA0B,EAAE,uBAAuB,CAAA,EAAA,CAAA,CAAA;+GAMhF,eAAe,EAAA,CAAA,CAAA;;2FAAf,eAAe,EAAA,UAAA,EAAA,CAAA;kBAJ3B,QAAQ;AAAC,YAAA,IAAA,EAAA,CAAA;AACN,oBAAA,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC;AACtB,oBAAA,OAAO,EAAE,CAAC,GAAG,QAAQ;AACxB,iBAAA;;;AChBD;;AAEG;;;;"}
1
+ {"version":3,"file":"radix-ng-primitives-avatar.mjs","sources":["../../../packages/primitives/avatar/src/avatar-context.token.ts","../../../packages/primitives/avatar/src/avatar.config.ts","../../../packages/primitives/avatar/src/avatar-fallback.directive.ts","../../../packages/primitives/avatar/src/avatar-image.directive.ts","../../../packages/primitives/avatar/src/avatar-root.directive.ts","../../../packages/primitives/avatar/index.ts","../../../packages/primitives/avatar/radix-ng-primitives-avatar.ts"],"sourcesContent":["import { WritableSignal } from '@angular/core';\nimport { createContext } from '@radix-ng/primitives/core';\nimport { RdxImageLoadingStatus } from './types';\n\nexport interface AvatarRootContext {\n imageLoadingStatus: WritableSignal<RdxImageLoadingStatus>;\n}\n\nexport const [injectAvatarRootContext, provideAvatarRootContext] = createContext<AvatarRootContext>(\n 'AvatarRootContext',\n 'components/avatar'\n);\n","import { inject, InjectionToken, Provider } from '@angular/core';\n\nexport interface RdxAvatarConfig {\n /**\n * Define a delay before the fallback is shown.\n * This is useful to only show the fallback for those with slower connections.\n * @default 0\n */\n delayMs: number;\n}\n\nexport const defaultAvatarConfig: RdxAvatarConfig = {\n delayMs: 0\n};\n\nexport const RdxAvatarConfigToken = new InjectionToken<RdxAvatarConfig>('RdxAvatarConfigToken');\n\nexport function provideRdxAvatarConfig(config: Partial<RdxAvatarConfig>): Provider[] {\n return [\n {\n provide: RdxAvatarConfigToken,\n useValue: { ...defaultAvatarConfig, ...config }\n }\n ];\n}\n\nexport function injectAvatarConfig(): RdxAvatarConfig {\n return inject(RdxAvatarConfigToken, { optional: true }) ?? defaultAvatarConfig;\n}\n","import { Directive, input, signal } from '@angular/core';\nimport { watch } from '@radix-ng/primitives/core';\nimport { injectAvatarRootContext } from './avatar-context.token';\nimport { injectAvatarConfig } from './avatar.config';\n\n/**\n * @group Components\n */\n@Directive({\n selector: 'span[rdxAvatarFallback]',\n exportAs: 'rdxAvatarFallback',\n host: {\n '[style.display]': 'canRender() && rootContext.imageLoadingStatus() !== \"loaded\" ? null : \"none\"'\n }\n})\nexport class RdxAvatarFallbackDirective {\n private readonly config = injectAvatarConfig();\n\n protected readonly rootContext = injectAvatarRootContext();\n\n /**\n * Useful for delaying rendering so it only appears for those with slower connections.\n *\n * @group Props\n * @defaultValue 0\n */\n readonly delayMs = input<number>(this.config.delayMs);\n\n protected readonly canRender = signal(false);\n\n constructor() {\n // Enable the fallback after an optional delay (so it only appears for\n // those on slower connections), independent of the image load status —\n // matches Radix/Base UI. The `display` binding then hides it once the\n // image has loaded.\n watch([this.delayMs], ([delayMs], onCleanup) => {\n this.canRender.set(false);\n\n if (delayMs) {\n const timeout = setTimeout(() => this.canRender.set(true), delayMs);\n // Cancel a pending delay if delayMs changes or the directive is\n // destroyed before it fires.\n onCleanup(() => clearTimeout(timeout));\n } else {\n this.canRender.set(true);\n }\n });\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport { Directive, inject, input, output, PLATFORM_ID, signal } from '@angular/core';\nimport { watch } from '@radix-ng/primitives/core';\nimport { injectAvatarRootContext } from './avatar-context.token';\nimport { HTMLAttributeReferrerPolicy, RdxImageLoadingStatus } from './types';\n\n/**\n * @group Components\n */\n@Directive({\n selector: 'img[rdxAvatarImage]',\n exportAs: 'rdxAvatarImage',\n host: {\n // The host is a native `<img>` (selector `img[rdxAvatarImage]`) — its implicit `img` role is\n // already correct, and forcing `role=\"img\"` here would override any consumer override (Base UI\n // adds no role).\n '[attr.src]': 'src()',\n '[attr.srcset]': 'srcSet()',\n '[attr.sizes]': 'sizes()',\n '[attr.crossorigin]': 'crossOrigin()',\n '[attr.referrerpolicy]': 'referrerPolicy()',\n '[style.display]': '(rootContext.imageLoadingStatus() === \"loaded\") ? null : \"none\"'\n }\n})\nexport class RdxAvatarImageDirective {\n private readonly platformId = inject(PLATFORM_ID);\n\n protected readonly rootContext = injectAvatarRootContext();\n\n /**\n * @group Props\n */\n readonly src = input<string>();\n\n /**\n * A responsive `srcset` for the image. When set without `src`, an empty `src` no longer counts as\n * an error.\n *\n * @group Props\n */\n readonly srcSet = input<string>();\n\n /**\n * The `sizes` attribute paired with `srcSet`.\n *\n * @group Props\n */\n readonly sizes = input<string>();\n\n /**\n * The CORS mode used when fetching the image.\n *\n * @group Props\n */\n readonly crossOrigin = input<'anonymous' | 'use-credentials' | ''>();\n\n readonly referrerPolicy = input<HTMLAttributeReferrerPolicy>();\n\n /**\n * A callback providing information about the loading status of the image.\n * This is useful in case you want to control more precisely what to render as the image is loading.\n *\n * @group Emits\n */\n readonly onLoadingStatusChange = output<RdxImageLoadingStatus>();\n\n private readonly loadingStatus = signal<RdxImageLoadingStatus>('idle');\n\n constructor() {\n // Loading is browser-only; on the server the status stays 'idle'.\n if (!isPlatformBrowser(this.platformId)) {\n return;\n }\n\n watch(\n [this.src, this.srcSet, this.sizes, this.crossOrigin, this.referrerPolicy],\n ([src, srcSet, sizes, crossOrigin, referrer], onCleanup) => {\n if (!src && !srcSet) {\n this.loadingStatus.set('error');\n return;\n }\n\n const image = new window.Image();\n this.loadingStatus.set('loading');\n image.onload = () => this.loadingStatus.set('loaded');\n image.onerror = () => this.loadingStatus.set('error');\n // Set the fetch-affecting attributes before `src`/`srcset` so they apply to the request.\n if (referrer) {\n image.referrerPolicy = referrer;\n }\n if (crossOrigin !== undefined) {\n image.crossOrigin = crossOrigin;\n }\n if (srcSet) {\n image.srcset = srcSet;\n }\n if (sizes) {\n image.sizes = sizes;\n }\n if (src) {\n image.src = src;\n }\n\n // A cached/already-decoded image is ready synchronously — resolve immediately so the\n // fallback delay isn't started for an image that is already available.\n if (image.complete && image.naturalWidth > 0) {\n this.loadingStatus.set('loaded');\n }\n\n // Drop handlers for a stale src (or on destroy) so a late load/error\n // can't overwrite the status for the current one.\n onCleanup(() => {\n image.onload = null;\n image.onerror = null;\n });\n }\n );\n\n watch([this.loadingStatus], ([value]) => {\n // Base UI fires the callback only once loading actually starts — skip the initial `idle`.\n if (value === 'idle') {\n return;\n }\n this.onLoadingStatusChange.emit(value);\n this.rootContext.imageLoadingStatus.set(value);\n });\n }\n}\n","import { Directive, inject, signal } from '@angular/core';\nimport { AvatarRootContext, provideAvatarRootContext } from './avatar-context.token';\nimport { RdxImageLoadingStatus } from './types';\n\nconst rootContext = (): AvatarRootContext => {\n const root = inject(RdxAvatarRootDirective);\n return {\n imageLoadingStatus: root.imageLoadingStatus\n };\n};\n\n/**\n * @group Components\n */\n@Directive({\n selector: 'span[rdxAvatarRoot]',\n exportAs: 'rdxAvatarRoot',\n providers: [provideAvatarRootContext(rootContext)]\n})\nexport class RdxAvatarRootDirective {\n readonly imageLoadingStatus = signal<RdxImageLoadingStatus>('idle');\n}\n","import { NgModule } from '@angular/core';\nimport { RdxAvatarFallbackDirective } from './src/avatar-fallback.directive';\nimport { RdxAvatarImageDirective } from './src/avatar-image.directive';\nimport { RdxAvatarRootDirective } from './src/avatar-root.directive';\n\nexport * from './src/avatar-context.token';\nexport * from './src/avatar-fallback.directive';\nexport * from './src/avatar-image.directive';\nexport * from './src/avatar-root.directive';\nexport * from './src/avatar.config';\nexport type { HTMLAttributeReferrerPolicy, RdxImageLoadingStatus } from './src/types';\n\nconst _imports = [RdxAvatarRootDirective, RdxAvatarFallbackDirective, RdxAvatarImageDirective];\n\n@NgModule({\n imports: [..._imports],\n exports: [..._imports]\n})\nexport class RdxAvatarModule {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, model, input, booleanAttribute, output, signal, computed, effect, Directive, ElementRef, Renderer2, DestroyRef, NgModule } from '@angular/core';
3
3
  import * as i1 from '@radix-ng/primitives/core';
4
- import { createContext, createCancelableChangeEventDetails, provideValueAccessor, RdxControlValueAccessor } from '@radix-ng/primitives/core';
4
+ import { createContext, RdxFormUiControlBase, createCancelableChangeEventDetails, provideValueAccessor, provideFormUiState, RdxFormUiStateHost, RdxControlValueAccessor, RDX_FIELD_VALIDITY } from '@radix-ng/primitives/core';
5
5
 
6
6
  const [injectCheckboxGroupContext, provideCheckboxGroupContext] = createContext('CheckboxGroupContext', 'components/checkbox');
7
7
  const groupContext = () => {
@@ -26,8 +26,9 @@ let nextCheckboxGroupId = 0;
26
26
  * Each child `rdxCheckboxRoot` participates by its `name`. A child marked `parent` becomes a
27
27
  * "select all" checkbox whose state is derived from `allValues`.
28
28
  */
29
- class RdxCheckboxGroupDirective {
29
+ class RdxCheckboxGroupDirective extends RdxFormUiControlBase {
30
30
  constructor() {
31
+ super();
31
32
  /** The names of the currently checked checkboxes. Use with `onValueChange` or `[(value)]`. */
32
33
  this.value = model([], /* @ts-ignore */
33
34
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -94,6 +95,10 @@ class RdxCheckboxGroupDirective {
94
95
  }
95
96
  });
96
97
  }
98
+ /** @ignore Bridge the CVA `onTouched` so `markAsTouched()` also notifies Reactive/template forms. */
99
+ formUiTouchTarget() {
100
+ return { markAsTouched: () => this.onTouched() };
101
+ }
97
102
  /** @ignore Register a child's disabled signal keyed by its name. */
98
103
  registerChild(name, disabled) {
99
104
  this.disabledByName.set(name, disabled);
@@ -193,6 +198,7 @@ class RdxCheckboxGroupDirective {
193
198
  return false;
194
199
  }
195
200
  this.value.set(next);
201
+ this.formUi.markDirty();
196
202
  this.onChange(next);
197
203
  this.onTouched();
198
204
  return true;
@@ -214,14 +220,23 @@ class RdxCheckboxGroupDirective {
214
220
  this.disabledByCva.set(isDisabled);
215
221
  }
216
222
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
217
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxGroupDirective, isStandalone: true, selector: "[rdxCheckboxGroup]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, allValues: { classPropertyName: "allValues", publicName: "allValues", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [provideValueAccessor(RdxCheckboxGroupDirective), provideCheckboxGroupContext(groupContext)], exportAs: ["rdxCheckboxGroup"], ngImport: i0 }); }
223
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxGroupDirective, isStandalone: true, selector: "[rdxCheckboxGroup]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, allValues: { classPropertyName: "allValues", publicName: "allValues", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onValueChange: "onValueChange" }, host: { attributes: { "role": "group" }, properties: { "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [
224
+ provideValueAccessor(RdxCheckboxGroupDirective),
225
+ provideCheckboxGroupContext(groupContext),
226
+ provideFormUiState(() => inject(RdxCheckboxGroupDirective).formUi)
227
+ ], exportAs: ["rdxCheckboxGroup"], usesInheritance: true, hostDirectives: [{ directive: i1.RdxFormUiStateHost }], ngImport: i0 }); }
218
228
  }
219
229
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxGroupDirective, decorators: [{
220
230
  type: Directive,
221
231
  args: [{
222
232
  selector: '[rdxCheckboxGroup]',
223
233
  exportAs: 'rdxCheckboxGroup',
224
- providers: [provideValueAccessor(RdxCheckboxGroupDirective), provideCheckboxGroupContext(groupContext)],
234
+ hostDirectives: [RdxFormUiStateHost],
235
+ providers: [
236
+ provideValueAccessor(RdxCheckboxGroupDirective),
237
+ provideCheckboxGroupContext(groupContext),
238
+ provideFormUiState(() => inject(RdxCheckboxGroupDirective).formUi)
239
+ ],
225
240
  host: {
226
241
  role: 'group',
227
242
  '[attr.data-disabled]': 'disabledState() ? "" : undefined'
@@ -250,6 +265,10 @@ const rootContext = () => {
250
265
  form: checkbox.form,
251
266
  readonly: checkbox.readOnlyState,
252
267
  state: checkbox.state,
268
+ invalidState: checkbox.invalidState,
269
+ displayValid: checkbox.displayValid,
270
+ touchedState: checkbox.touchedState,
271
+ dirtyState: checkbox.dirtyState,
253
272
  uncheckedValue: checkbox.uncheckedValue,
254
273
  toggle(event) {
255
274
  checkbox.toggle(event);
@@ -268,13 +287,6 @@ class RdxCheckboxRootDirective {
268
287
  this.uncheckedInputElement = null;
269
288
  /** The group this checkbox belongs to, if it is rendered inside a `rdxCheckboxGroup`. */
270
289
  this.group = injectCheckboxGroupContext(true);
271
- /**
272
- * @ignore
273
- * Reflects the effective disabled state (CVA, covering reactive-forms `.disable()`, plus the
274
- * group's disabled state), used for the `data-disabled` host attribute.
275
- */
276
- this.isDisabled = computed(() => this.disabledState(), /* @ts-ignore */
277
- ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
278
290
  /**
279
291
  * The controlled checked state of the checkbox. Must be used in conjunction with onCheckedChange.
280
292
  *
@@ -335,6 +347,29 @@ class RdxCheckboxRootDirective {
335
347
  * @group Props
336
348
  */
337
349
  this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
350
+ /**
351
+ * Whether the checkbox is invalid. A non-empty {@link errors} list also marks it invalid.
352
+ * @group Props
353
+ */
354
+ this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
355
+ /**
356
+ * Whether the checkbox has been touched. A `model()` so Signal Forms can write it; the checkbox
357
+ * also sets it on toggle (and emits {@link touch}).
358
+ * @group Props
359
+ */
360
+ this.touched = model(false, /* @ts-ignore */
361
+ ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
362
+ /**
363
+ * Whether the checked state changed from its initial value. Merged with internal tracking.
364
+ * @group Props
365
+ */
366
+ this.dirty = input(false, { ...(ngDevMode ? { debugName: "dirty" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
367
+ /**
368
+ * Validation errors for the checkbox. A non-empty list marks it invalid.
369
+ * @group Props
370
+ */
371
+ this.errors = input([], /* @ts-ignore */
372
+ ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
338
373
  /**
339
374
  * Name of the form control. Submitted with the form as part of a name/value pair. Inside a
340
375
  * `rdxCheckboxGroup` this also identifies the checkbox in the group's value array.
@@ -359,6 +394,11 @@ class RdxCheckboxRootDirective {
359
394
  * @group Emits
360
395
  */
361
396
  this.onCheckedChange = output();
397
+ /**
398
+ * Emits on interaction, notifying Signal Forms the checkbox was touched.
399
+ * @group Emits
400
+ */
401
+ this.touch = output();
362
402
  /**
363
403
  * @ignore
364
404
  * The effective checked state as a `boolean`. Inside a `rdxCheckboxGroup` it is derived from the
@@ -397,6 +437,27 @@ class RdxCheckboxRootDirective {
397
437
  ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
398
438
  this.readOnlyState = computed(() => this.readonly(), /* @ts-ignore */
399
439
  ...(ngDevMode ? [{ debugName: "readOnlyState" }] : /* istanbul ignore next */ []));
440
+ this.dirtyValue = signal(false, /* @ts-ignore */
441
+ ...(ngDevMode ? [{ debugName: "dirtyValue" }] : /* istanbul ignore next */ []));
442
+ /** @ignore Invalid when the `invalid` input is set or the `errors` list is non-empty. */
443
+ this.invalidState = computed(() => this.invalid() || (this.errors()?.length ?? 0) > 0, /* @ts-ignore */
444
+ ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
445
+ /** Tri-state display validity exposed by an enclosing Field; absent when standalone. */
446
+ this.fieldValidity = inject(RDX_FIELD_VALIDITY, { optional: true });
447
+ /**
448
+ * @ignore Tri-state *displayed* validity. A **standalone** checkbox inside a `rdxFieldRoot` defers to
449
+ * the field's gated `validState` (so a field whose `validationMode` defers display (e.g. `onBlur`) stays neutral); inside a
450
+ * `rdxCheckboxGroup` the group owns the field relationship, so the item keeps its own binary validity
451
+ * (the field's invalid must not paint every checkbox red).
452
+ */
453
+ this.displayValid = computed(() => !this.group && this.fieldValidity ? this.fieldValidity() : this.invalidState() ? false : true, /* @ts-ignore */
454
+ ...(ngDevMode ? [{ debugName: "displayValid" }] : /* istanbul ignore next */ []));
455
+ /** @ignore */
456
+ this.touchedState = computed(() => this.touched(), /* @ts-ignore */
457
+ ...(ngDevMode ? [{ debugName: "touchedState" }] : /* istanbul ignore next */ []));
458
+ /** @ignore */
459
+ this.dirtyState = computed(() => this.dirty() || this.dirtyValue(), /* @ts-ignore */
460
+ ...(ngDevMode ? [{ debugName: "dirtyState" }] : /* istanbul ignore next */ []));
400
461
  this.state = computed(() => this.indeterminateState() ? 'indeterminate' : this.checkedState() ? 'checked' : 'unchecked', /* @ts-ignore */
401
462
  ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
402
463
  // Inside a group, register this child's name and its own disabled state so a `parent`
@@ -454,6 +515,13 @@ class RdxCheckboxRootDirective {
454
515
  this.indeterminate.set(false);
455
516
  this.checked.set(next);
456
517
  this.controlValueAccessor.setValue(next);
518
+ this.dirtyValue.set(true);
519
+ // Mark the form control touched on user interaction so `touched`-gated validation shows
520
+ // (the visible control is the button, not the hidden input, so blur alone is unreliable).
521
+ // Both channels: CVA for Reactive forms, the `touched` model + `touch` output for Signal Forms.
522
+ this.controlValueAccessor.markAsTouched();
523
+ this.touched.set(true);
524
+ this.touch.emit();
457
525
  }
458
526
  syncUncheckedInput() {
459
527
  const name = this.name();
@@ -511,7 +579,7 @@ class RdxCheckboxRootDirective {
511
579
  }
512
580
  }
513
581
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxRootDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
514
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, defaultChecked: { classPropertyName: "defaultChecked", publicName: "defaultChecked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, submitValue: { classPropertyName: "submitValue", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, uncheckedValue: { classPropertyName: "uncheckedValue", publicName: "uncheckedValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, 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-checked": "checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-unchecked": "!checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-indeterminate": "indeterminateState() ? \"\" : undefined", "attr.data-disabled": "isDisabled() ? \"\" : undefined", "attr.data-readonly": "readOnlyState() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
582
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxCheckboxRootDirective, isStandalone: true, selector: "[rdxCheckboxRoot]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, defaultChecked: { classPropertyName: "defaultChecked", publicName: "defaultChecked", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, submitValue: { classPropertyName: "submitValue", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, uncheckedValue: { classPropertyName: "uncheckedValue", publicName: "uncheckedValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readOnly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, dirty: { classPropertyName: "dirty", publicName: "dirty", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", indeterminate: "indeterminateChange", touched: "touchedChange", onCheckedChange: "onCheckedChange", touch: "touch" }, host: { properties: { "attr.data-checked": "checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-unchecked": "!checkedState() && !indeterminateState() ? \"\" : undefined", "attr.data-indeterminate": "indeterminateState() ? \"\" : undefined", "attr.data-disabled": "disabledState() ? \"\" : undefined", "attr.data-readonly": "readOnlyState() ? \"\" : undefined", "attr.data-required": "required() ? \"\" : undefined", "attr.data-invalid": "displayValid() === false ? \"\" : undefined", "attr.data-valid": "displayValid() === true ? \"\" : undefined", "attr.data-touched": "touchedState() ? \"\" : undefined", "attr.data-dirty": "dirtyState() ? \"\" : undefined" } }, providers: [provideCheckboxRootContext(rootContext)], hostDirectives: [{ directive: i1.RdxControlValueAccessor, inputs: ["value", "checked", "disabled", "disabled"] }], ngImport: i0 }); }
515
583
  }
516
584
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxRootDirective, decorators: [{
517
585
  type: Directive,
@@ -528,12 +596,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
528
596
  '[attr.data-checked]': 'checkedState() && !indeterminateState() ? "" : undefined',
529
597
  '[attr.data-unchecked]': '!checkedState() && !indeterminateState() ? "" : undefined',
530
598
  '[attr.data-indeterminate]': 'indeterminateState() ? "" : undefined',
531
- '[attr.data-disabled]': 'isDisabled() ? "" : undefined',
599
+ '[attr.data-disabled]': 'disabledState() ? "" : undefined',
532
600
  '[attr.data-readonly]': 'readOnlyState() ? "" : undefined',
533
- '[attr.data-required]': 'required() ? "" : undefined'
601
+ '[attr.data-required]': 'required() ? "" : undefined',
602
+ '[attr.data-invalid]': 'displayValid() === false ? "" : undefined',
603
+ '[attr.data-valid]': 'displayValid() === true ? "" : undefined',
604
+ '[attr.data-touched]': 'touchedState() ? "" : undefined',
605
+ '[attr.data-dirty]': 'dirtyState() ? "" : undefined'
534
606
  }
535
607
  }]
536
- }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], defaultChecked: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultChecked", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], submitValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], uncheckedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "uncheckedValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], 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"] }] } });
608
+ }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], defaultChecked: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultChecked", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }, { type: i0.Output, args: ["indeterminateChange"] }], submitValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], uncheckedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "uncheckedValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readOnly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], dirty: [{ type: i0.Input, args: [{ isSignal: true, alias: "dirty", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], parent: [{ type: i0.Input, args: [{ isSignal: true, alias: "parent", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], onCheckedChange: [{ type: i0.Output, args: ["onCheckedChange"] }], touch: [{ type: i0.Output, args: ["touch"] }] } });
537
609
 
538
610
  /**
539
611
  * Directive: rdxCheckboxButton
@@ -577,7 +649,7 @@ class RdxCheckboxButtonDirective {
577
649
  }
578
650
  }
579
651
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
580
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", 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-disabled": "rootContext.disabled() ? \"true\" : undefined", "attr.aria-required": "rootContext.required() || undefined", "attr.aria-readonly": "rootContext.readonly() || undefined", "attr.data-checked": "rootContext.checked() && !rootContext.indeterminate() ? \"\" : undefined", "attr.data-unchecked": "!rootContext.checked() && !rootContext.indeterminate() ? \"\" : undefined", "attr.data-indeterminate": "rootContext.indeterminate() ? \"\" : undefined", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.data-required": "rootContext.required() ? \"\" : undefined" } }, ngImport: i0 }); }
652
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", 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-disabled": "rootContext.disabled() ? \"true\" : undefined", "attr.aria-required": "rootContext.required() || undefined", "attr.aria-readonly": "rootContext.readonly() || undefined", "attr.aria-invalid": "rootContext.displayValid() === false ? \"true\" : undefined", "attr.data-checked": "rootContext.checked() && !rootContext.indeterminate() ? \"\" : undefined", "attr.data-unchecked": "!rootContext.checked() && !rootContext.indeterminate() ? \"\" : undefined", "attr.data-indeterminate": "rootContext.indeterminate() ? \"\" : undefined", "attr.data-disabled": "rootContext.disabled() ? \"\" : undefined", "attr.data-readonly": "rootContext.readonly() ? \"\" : undefined", "attr.data-required": "rootContext.required() ? \"\" : undefined", "attr.data-invalid": "rootContext.displayValid() === false ? \"\" : undefined", "attr.data-valid": "rootContext.displayValid() === true ? \"\" : undefined", "attr.data-touched": "rootContext.touchedState() ? \"\" : undefined", "attr.data-dirty": "rootContext.dirtyState() ? \"\" : undefined" } }, ngImport: i0 }); }
581
653
  }
582
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxCheckboxButtonDirective, decorators: [{
583
655
  type: Directive,
@@ -591,12 +663,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
591
663
  '[attr.aria-disabled]': 'rootContext.disabled() ? "true" : undefined',
592
664
  '[attr.aria-required]': 'rootContext.required() || undefined',
593
665
  '[attr.aria-readonly]': 'rootContext.readonly() || undefined',
666
+ '[attr.aria-invalid]': 'rootContext.displayValid() === false ? "true" : undefined',
594
667
  '[attr.data-checked]': 'rootContext.checked() && !rootContext.indeterminate() ? "" : undefined',
595
668
  '[attr.data-unchecked]': '!rootContext.checked() && !rootContext.indeterminate() ? "" : undefined',
596
669
  '[attr.data-indeterminate]': 'rootContext.indeterminate() ? "" : undefined',
597
670
  '[attr.data-disabled]': 'rootContext.disabled() ? "" : undefined',
598
671
  '[attr.data-readonly]': 'rootContext.readonly() ? "" : undefined',
599
672
  '[attr.data-required]': 'rootContext.required() ? "" : undefined',
673
+ '[attr.data-invalid]': 'rootContext.displayValid() === false ? "" : undefined',
674
+ '[attr.data-valid]': 'rootContext.displayValid() === true ? "" : undefined',
675
+ '[attr.data-touched]': 'rootContext.touchedState() ? "" : undefined',
676
+ '[attr.data-dirty]': 'rootContext.dirtyState() ? "" : undefined',
600
677
  '(click)': 'clicked($event)',
601
678
  // According to WAI ARIA, Checkboxes don't activate on enter keypress
602
679
  '(keydown.enter)': '$event.preventDefault()'