@egose/shadcn-theme-ng 0.1.0 → 0.3.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 (162) hide show
  1. package/README.md +2 -2
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  8. package/avatar/README.md +357 -5
  9. package/badge/README.md +318 -2
  10. package/basic-alert/README.md +353 -2
  11. package/breadcrumb/README.md +406 -5
  12. package/button/README.md +482 -2
  13. package/button/fesm2022/button.mjs +85 -107
  14. package/button/types/button.d.ts +5 -8
  15. package/button-group/README.md +318 -5
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/checkbox/fesm2022/checkbox.mjs +6 -7
  22. package/checkbox/types/checkbox.d.ts +1 -1
  23. package/collapsible/README.md +332 -5
  24. package/combobox/README.md +507 -5
  25. package/combobox/fesm2022/combobox.mjs +5 -2
  26. package/command/README.md +435 -5
  27. package/confirmation-dialog/README.md +301 -2
  28. package/context-menu/README.md +366 -5
  29. package/date-picker/README.md +469 -2
  30. package/date-picker/fesm2022/date-picker.mjs +150 -32
  31. package/date-picker/types/date-picker.d.ts +102 -9
  32. package/dialog/README.md +448 -2
  33. package/drawer/README.md +395 -5
  34. package/dropdown-menu/README.md +417 -5
  35. package/empty/README.md +329 -5
  36. package/field/README.md +385 -5
  37. package/form-autocomplete/README.md +177 -0
  38. package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
  39. package/form-autocomplete/package.json +24 -0
  40. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  41. package/form-checkbox/README.md +322 -2
  42. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  43. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  44. package/form-combobox/README.md +202 -0
  45. package/form-combobox/fesm2022/form-combobox.mjs +147 -0
  46. package/form-combobox/package.json +24 -0
  47. package/form-combobox/types/form-combobox.d.ts +73 -0
  48. package/form-date-picker/README.md +348 -2
  49. package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
  50. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  51. package/form-date-picker-multi/README.md +191 -0
  52. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
  53. package/form-date-picker-multi/package.json +24 -0
  54. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  55. package/form-date-range-picker/README.md +253 -0
  56. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
  57. package/form-date-range-picker/package.json +24 -0
  58. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  59. package/form-field/README.md +356 -2
  60. package/form-field-simple/README.md +340 -2
  61. package/form-input-otp/README.md +194 -0
  62. package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
  63. package/form-input-otp/package.json +24 -0
  64. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  65. package/form-month-year-picker/README.md +188 -0
  66. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
  67. package/form-month-year-picker/package.json +24 -0
  68. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  69. package/form-native-select/README.md +205 -0
  70. package/form-native-select/fesm2022/form-native-select.mjs +103 -0
  71. package/form-native-select/package.json +24 -0
  72. package/form-native-select/types/form-native-select.d.ts +57 -0
  73. package/form-phone-input/README.md +188 -0
  74. package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
  75. package/form-phone-input/package.json +24 -0
  76. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  77. package/form-radio-group/README.md +198 -0
  78. package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
  79. package/form-radio-group/package.json +24 -0
  80. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  81. package/form-searchable-multiselect/README.md +371 -2
  82. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  83. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  84. package/form-select/README.md +360 -2
  85. package/form-select/fesm2022/form-select.mjs +20 -11
  86. package/form-select/types/form-select.d.ts +18 -1
  87. package/form-slider/README.md +182 -0
  88. package/form-slider/fesm2022/form-slider.mjs +106 -0
  89. package/form-slider/package.json +24 -0
  90. package/form-slider/types/form-slider.d.ts +60 -0
  91. package/form-switch/README.md +173 -0
  92. package/form-switch/fesm2022/form-switch.mjs +100 -0
  93. package/form-switch/package.json +24 -0
  94. package/form-switch/types/form-switch.d.ts +50 -0
  95. package/form-text-input/README.md +381 -2
  96. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  97. package/form-text-input/types/form-text-input.d.ts +18 -1
  98. package/form-textarea/README.md +357 -2
  99. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  100. package/form-textarea/types/form-textarea.d.ts +18 -1
  101. package/form-toggle/README.md +186 -0
  102. package/form-toggle/fesm2022/form-toggle.mjs +159 -0
  103. package/form-toggle/package.json +24 -0
  104. package/form-toggle/types/form-toggle.d.ts +82 -0
  105. package/form-toggle-group/README.md +176 -0
  106. package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
  107. package/form-toggle-group/package.json +24 -0
  108. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  109. package/hover-card/README.md +256 -5
  110. package/icon/README.md +239 -2
  111. package/input/README.md +269 -2
  112. package/input-group/README.md +335 -5
  113. package/input-group/fesm2022/input-group.mjs +22 -12
  114. package/input-group/types/input-group.d.ts +4 -1
  115. package/input-otp/README.md +375 -5
  116. package/item/README.md +385 -5
  117. package/item/fesm2022/item.mjs +3 -3
  118. package/kbd/README.md +291 -5
  119. package/label/README.md +272 -2
  120. package/layout-simple/README.md +193 -2
  121. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  122. package/layout-simple/types/layout-simple.d.ts +174 -137
  123. package/menu/README.md +417 -2
  124. package/menubar/README.md +343 -5
  125. package/native-select/README.md +323 -5
  126. package/native-select/fesm2022/native-select.mjs +18 -6
  127. package/native-select/types/native-select.d.ts +7 -2
  128. package/navigation-menu/README.md +369 -5
  129. package/package.json +57 -1
  130. package/pagination/README.md +388 -5
  131. package/phone-input/README.md +114 -0
  132. package/phone-input/fesm2022/phone-input.mjs +191 -0
  133. package/phone-input/package.json +24 -0
  134. package/phone-input/types/phone-input.d.ts +67 -0
  135. package/popover/README.md +331 -2
  136. package/progress/README.md +311 -5
  137. package/radio-group/README.md +364 -2
  138. package/radio-group/fesm2022/radio-group.mjs +5 -1
  139. package/resizable/README.md +269 -5
  140. package/scroll-area/README.md +233 -5
  141. package/searchable-multiselect/README.md +323 -2
  142. package/select/README.md +437 -2
  143. package/separator/README.md +222 -2
  144. package/sheet/README.md +311 -2
  145. package/sidebar/README.md +457 -5
  146. package/skeleton/README.md +217 -5
  147. package/slider/README.md +273 -5
  148. package/slider/fesm2022/slider.mjs +3 -3
  149. package/sonner/README.md +346 -2
  150. package/spinner/README.md +284 -2
  151. package/switch/README.md +310 -2
  152. package/switch/fesm2022/switch.mjs +7 -5
  153. package/switch/types/switch.d.ts +2 -1
  154. package/table/README.md +423 -5
  155. package/tabs/README.md +411 -2
  156. package/tabs/fesm2022/tabs.mjs +2 -2
  157. package/textarea/README.md +282 -5
  158. package/toggle/README.md +270 -5
  159. package/toggle-group/README.md +340 -5
  160. package/tooltip/README.md +269 -2
  161. package/typography/README.md +271 -5
  162. package/utils/README.md +303 -2
@@ -1,3 +1,321 @@
1
- # Checkbox Subpath
1
+ # Checkbox (`@egose/shadcn-theme-ng/checkbox`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/checkbox` or `@egose/shadcn-theme-ng-tw/checkbox`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A shadcn/ui-style checkbox — the Angular port of shadcn/ui `Checkbox`. A single standalone `hlm-checkbox` component wraps `BrnCheckbox` from `@spartan-ng/brain/checkbox`, renders the `lucideCheck` icon when checked, and fully implements `ControlValueAccessor`, so it works with `[(checked)]`, `[(ngModel)]`, and reactive `FormControl`s. Supports the tri-state `boolean | 'indeterminate'` model, separate `disabled` (form-aware) vs `wrapperDisabled` (visual-only) locks, and a `changed` output.
4
+
5
+ Ships as `@egose/shadcn-theme-ng/checkbox` and `@egose/shadcn-theme-ng-tw/checkbox` (the `tw:`-prefixed variant). See the [package README](../../README.md) for install, peer dependencies, and Tailwind setup. Do not publish this project directory independently — it is consumed via the published subpath only.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Plain Tailwind (no prefix):
11
+ npm install @egose/shadcn-theme-ng
12
+
13
+ # Or the tw:-prefixed variant:
14
+ npm install @egose/shadcn-theme-ng-tw
15
+ ```
16
+
17
+ Peers are listed in the [package README](../../README.md). This subpath declares `@angular/common` and `@angular/core` as peers (see `projects/checkbox/package.json`); `FormsModule`/`ReactiveFormsModule` come from your app when you use form bindings.
18
+
19
+ ## Imports
20
+
21
+ ```ts
22
+ import {
23
+ HlmCheckbox,
24
+ HlmCheckboxImports,
25
+ HlmCheckboxModule,
26
+ HLM_CHECKBOX_VALUE_ACCESSOR,
27
+ } from '@egose/shadcn-theme-ng/checkbox';
28
+ // tw variant:
29
+ // import { ... } from '@egose/shadcn-theme-ng-tw/checkbox';
30
+ ```
31
+
32
+ Standalone:
33
+
34
+ ```ts
35
+ import { Component } from '@angular/core';
36
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
37
+
38
+ @Component({
39
+ selector: 'app-demo',
40
+ standalone: true,
41
+ imports: [...HlmCheckboxImports],
42
+ template: `<hlm-checkbox />`,
43
+ })
44
+ export class DemoComponent {}
45
+ ```
46
+
47
+ NgModule:
48
+
49
+ ```ts
50
+ import { NgModule } from '@angular/core';
51
+ import { HlmCheckboxModule } from '@egose/shadcn-theme-ng/checkbox';
52
+
53
+ @NgModule({ imports: [HlmCheckboxModule] })
54
+ export class DemoModule {}
55
+ ```
56
+
57
+ ## Anatomy / Structure
58
+
59
+ ```html
60
+ <!-- Two-way model binding -->
61
+ <hlm-checkbox [(checked)]="accept" aria-label="Accept terms" />
62
+
63
+ <!-- With visible label -->
64
+ <div class="flex items-center gap-2">
65
+ <hlm-checkbox id="marketing" [(checked)]="marketing" />
66
+ <label for="marketing">Email me product updates</label>
67
+ </div>
68
+
69
+ <!-- Reactive form -->
70
+ <hlm-checkbox formControlName="accept" />
71
+
72
+ <!-- Template-driven -->
73
+ <hlm-checkbox name="accept" [(ngModel)]="accept" />
74
+ ```
75
+
76
+ Real selector: `hlm-checkbox` (element only). The host renders `class="contents peer"` so sibling label/description selectors (`peer-*`) keep working; `id` / `aria-*` attributes are nulled on the host and forwarded to the inner `brn-checkbox` instead. Set a visible label with a native `<label for>` — the component does not render label text itself.
77
+
78
+ ## API reference
79
+
80
+ ### `HlmCheckbox` (`hlm-checkbox`)
81
+
82
+ | Input | Type | Default | Description |
83
+ | -------------------------------------- | ----------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | `checked` | `model<boolean \| 'indeterminate'>` | `false` | Checked state. Two-way bindable (`[(checked)]`). `'indeterminate'` renders the checked icon; clicking it resolves to `true`. |
85
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra classes merged into the box. |
86
+ | `id` | `string \| null` | `null` | Forwarded to inner `brn-checkbox`. Pair with `<label for>`. |
87
+ | `aria-label` (`ariaLabel`) | `string \| null` | `null` | Forwarded aria-label. |
88
+ | `aria-labelledby` (`ariaLabelledby`) | `string \| null` | `null` | Forwarded aria-labelledby. |
89
+ | `aria-describedby` (`ariaDescribedby`) | `string \| null` | `null` | Forwarded aria-describedby. |
90
+ | `name` | `string \| null` | `null` | Forwarded `name`. |
91
+ | `required` | `boolean` | `false` | Forwarded `required` (boolean-coerced). |
92
+ | `disabled` | `boolean` | `false` | Visual + interaction lock (boolean-coerced). Clicks are ignored. |
93
+ | `wrapperDisabled` | `boolean` | `false` | Additional visual/interaction lock that does **not** write to a reactive form control (unlike `setDisabledState`). Useful for preview-only states. |
94
+
95
+ | Signal | Type | Description |
96
+ | --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
97
+ | `disabledState` | `Signal<boolean>` | `disabled() \|\| wrapperDisabled() \|\| formDisabled()` — the effective lock. Host reflects `data-disabled`. |
98
+
99
+ | Output | Payload | Description |
100
+ | --------- | --------- | -------------------------------------------------------------------------------------------------------------- |
101
+ | `changed` | `boolean` | Emitted on user toggle with the new boolean (`!previous`). Note: toggling from `'indeterminate'` emits `true`. |
102
+
103
+ ControlValueAccessor: `writeValue(value)`, `registerOnChange`, `registerOnTouched`, `setDisabledState(isDisabled)` (writes `formDisabled`, hence `disabledState`). `HLM_CHECKBOX_VALUE_ACCESSOR` (`NG_VALUE_ACCESSOR`, `forwardRef(HlmCheckbox)`, `multi: true`) is provided on the component.
104
+
105
+ ## Examples
106
+
107
+ ### 1. Basic with label
108
+
109
+ ```ts
110
+ import { Component, signal } from '@angular/core';
111
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
112
+
113
+ @Component({
114
+ selector: 'app-basic-checkbox',
115
+ standalone: true,
116
+ imports: [...HlmCheckboxImports],
117
+ template: `
118
+ <div class="flex items-center gap-2">
119
+ <hlm-checkbox id="terms" [(checked)]="accept" />
120
+ <label for="terms" class="text-sm">Accept terms and conditions</label>
121
+ </div>
122
+ <p class="mt-2 text-sm">Value: {{ accept() }}</p>
123
+ `,
124
+ })
125
+ export class BasicCheckboxComponent {
126
+ readonly accept = signal<boolean | 'indeterminate'>(false);
127
+ }
128
+ ```
129
+
130
+ ### 2. Reactive form with validation
131
+
132
+ `hlm-checkbox` is a `ControlValueAccessor` — `formControlName` just works, including `disable()`.
133
+
134
+ ```ts
135
+ import { Component } from '@angular/core';
136
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
137
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
138
+
139
+ @Component({
140
+ selector: 'app-reactive-checkbox',
141
+ standalone: true,
142
+ imports: [ReactiveFormsModule, ...HlmCheckboxImports],
143
+ template: `
144
+ <form [formGroup]="form" (ngSubmit)="submit()" class="space-y-3">
145
+ <div class="flex items-center gap-2">
146
+ <hlm-checkbox id="accept" formControlName="accept" />
147
+ <label for="accept" class="text-sm">I agree to the privacy policy</label>
148
+ </div>
149
+ @if (form.controls.accept.touched && form.controls.accept.invalid) {
150
+ <p class="text-destructive text-sm">You must accept to continue.</p>
151
+ }
152
+ <button type="submit" [disabled]="form.invalid">Continue</button>
153
+ <button type="button" (click)="form.controls.accept.disable()">Disable via form</button>
154
+ </form>
155
+ `,
156
+ })
157
+ export class ReactiveCheckboxComponent {
158
+ readonly form = new FormGroup({
159
+ accept: new FormControl(false, { validators: Validators.requiredTrue, nonNullable: true }),
160
+ });
161
+ submit() {
162
+ console.log(this.form.getRawValue());
163
+ }
164
+ }
165
+ ```
166
+
167
+ ### 3. Template-driven (`ngModel`)
168
+
169
+ ```ts
170
+ import { Component } from '@angular/core';
171
+ import { FormsModule } from '@angular/forms';
172
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
173
+
174
+ @Component({
175
+ selector: 'app-ngmodel-checkbox',
176
+ standalone: true,
177
+ imports: [FormsModule, ...HlmCheckboxImports],
178
+ template: `
179
+ <div class="flex items-center gap-2">
180
+ <hlm-checkbox id="news" name="news" [(ngModel)]="subscribed" aria-label="Subscribe to newsletter" />
181
+ <label for="news" class="text-sm">Subscribe to newsletter</label>
182
+ </div>
183
+ `,
184
+ })
185
+ export class NgModelCheckboxComponent {
186
+ subscribed = true;
187
+ }
188
+ ```
189
+
190
+ ### 4. Indeterminate + select-all pattern
191
+
192
+ Parent shows `'indeterminate'` when some (not all) children are checked — the classic bulk-select header.
193
+
194
+ ```ts
195
+ import { Component, computed, signal } from '@angular/core';
196
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
197
+
198
+ @Component({
199
+ selector: 'app-select-all',
200
+ standalone: true,
201
+ imports: [...HlmCheckboxImports],
202
+ template: `
203
+ <div class="flex items-center gap-2 border-b pb-2">
204
+ <hlm-checkbox id="all" [checked]="parentState()" (changed)="toggleAll($event)" aria-label="Select all files" />
205
+ <label for="all" class="text-sm font-medium">Select all</label>
206
+ </div>
207
+ @for (f of files(); track f.id) {
208
+ <div class="flex items-center gap-2 py-1">
209
+ <hlm-checkbox [id]="f.id" [checked]="f.done" (changed)="toggleOne(f.id, $event)" />
210
+ <label [for]="f.id" class="text-sm">{{ f.name }}</label>
211
+ </div>
212
+ }
213
+ `,
214
+ })
215
+ export class SelectAllComponent {
216
+ readonly files = signal([
217
+ { id: 'f1', name: 'invoice.pdf', done: true },
218
+ { id: 'f2', name: 'photo.png', done: false },
219
+ { id: 'f3', name: 'notes.md', done: false },
220
+ ]);
221
+ readonly parentState = computed<boolean | 'indeterminate'>(() => {
222
+ const all = this.files();
223
+ if (all.every((f) => f.done)) return true;
224
+ if (all.some((f) => f.done)) return 'indeterminate';
225
+ return false;
226
+ });
227
+ toggleAll(next: boolean) {
228
+ this.files.update((fs) => fs.map((f) => ({ ...f, done: next })));
229
+ }
230
+ toggleOne(id: string, next: boolean) {
231
+ this.files.update((fs) => fs.map((f) => (f.id === id ? { ...f, done: next } : f)));
232
+ }
233
+ }
234
+ ```
235
+
236
+ ### 5. Disabled / wrapper-disabled / required states
237
+
238
+ ```ts
239
+ import { Component, signal } from '@angular/core';
240
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
241
+
242
+ @Component({
243
+ selector: 'app-states-checkbox',
244
+ standalone: true,
245
+ imports: [...HlmCheckboxImports],
246
+ template: `
247
+ <div class="space-y-2">
248
+ <div class="flex items-center gap-2">
249
+ <hlm-checkbox id="d1" [disabled]="true" aria-label="Disabled unchecked" />
250
+ <label for="d1" class="text-sm opacity-70">Disabled (unchecked)</label>
251
+ </div>
252
+ <div class="flex items-center gap-2">
253
+ <hlm-checkbox id="d2" [checked]="true" [disabled]="true" aria-label="Disabled checked" />
254
+ <label for="d2" class="text-sm opacity-70">Disabled (checked)</label>
255
+ </div>
256
+ <div class="flex items-center gap-2">
257
+ <hlm-checkbox id="w1" [wrapperDisabled]="preview()" aria-label="Preview lock" />
258
+ <label for="w1" class="text-sm">Preview lock (visual only — form value untouched)</label>
259
+ </div>
260
+ <div class="flex items-center gap-2">
261
+ <hlm-checkbox id="r1" [(checked)]="v" [required]="true" aria-label="Required" />
262
+ <label for="r1" class="text-sm">Required</label>
263
+ </div>
264
+ </div>
265
+ `,
266
+ })
267
+ export class StatesCheckboxComponent {
268
+ readonly preview = signal(true);
269
+ v = false;
270
+ }
271
+ ```
272
+
273
+ ### 6. Listening to `changed` + custom styling
274
+
275
+ ```ts
276
+ import { Component } from '@angular/core';
277
+ import { HlmCheckboxImports } from '@egose/shadcn-theme-ng/checkbox';
278
+
279
+ @Component({
280
+ selector: 'app-changed-checkbox',
281
+ standalone: true,
282
+ imports: [...HlmCheckboxImports],
283
+ template: `
284
+ <div class="flex items-center gap-2">
285
+ <hlm-checkbox
286
+ id="audit"
287
+ class="data-[state=checked]:border-emerald-600 data-[state=checked]:bg-emerald-600"
288
+ aria-label="Enable audit log"
289
+ (changed)="onChanged($event)"
290
+ />
291
+ <label for="audit" class="text-sm">Enable audit log</label>
292
+ </div>
293
+ <p class="mt-1 text-sm">Last event: {{ lastChanged === null ? 'none' : lastChanged }}</p>
294
+ `,
295
+ })
296
+ export class ChangedCheckboxComponent {
297
+ lastChanged: boolean | null = null;
298
+ onChanged(next: boolean) {
299
+ this.lastChanged = next;
300
+ console.log('checkbox changed →', next);
301
+ }
302
+ }
303
+ ```
304
+
305
+ ## Accessibility notes
306
+
307
+ - The component renders a native-checkbox-equivalent `brn-checkbox` with `aria-label`/`labelledby`/`describedby` forwarding — always provide one: visible `<label for>` preferred, `aria-label` for icon-only rows.
308
+ - `disabledState` adds `data-disabled`, `cursor-not-allowed`, and `opacity-50`; disabled boxes are skipped correctly because clicks early-return.
309
+ - The check glyph is decorative (`lucideCheck` icon, no text) — state is exposed via `data-[state]` / native semantics, not the icon.
310
+ - Tri-state: `'indeterminate'` is a visual/model state, not `aria-checked="mixed"` — if you need mixed semantics for a tree, add `aria-checked` handling at your own level and test with a screen reader.
311
+
312
+ ## Theming / CSS variables
313
+
314
+ No component-specific CSS variables. The box uses `border-input`, `data-[state=checked]:bg-primary` / `text-primary-foreground`, `focus-visible:ring-ring/50`, `aria-invalid:border-destructive`, `size-4 rounded-[4px]`. Invalid-form styling integrates via `aria-invalid` ring tokens. Pass `class` to extend (merged with `hlm()`).
315
+
316
+ ## Related subpaths
317
+
318
+ - `@egose/shadcn-theme-ng/form-checkbox` — labeled + described + validated field composition.
319
+ - `@egose/shadcn-theme-ng/label` — accessible labels for the `for`/`id` pair.
320
+ - `@egose/shadcn-theme-ng/field` — form layout rows that host checkboxes.
321
+ - `@egose/shadcn-theme-ng/icon` — `HlmIcon` sizing used by the inner check glyph.
@@ -44,13 +44,12 @@ class HlmCheckbox {
44
44
  changed = output();
45
45
  _onChange;
46
46
  _onTouched;
47
- _handleChange() {
47
+ _handleCheckedChange(checked) {
48
48
  if (this.disabledState())
49
49
  return;
50
- const previousChecked = this.checked();
51
- this.checked.set(previousChecked === 'indeterminate' ? true : !previousChecked);
52
- this._onChange?.(!previousChecked);
53
- this.changed.emit(!previousChecked);
50
+ this.checked.set(checked);
51
+ this._onChange?.(checked);
52
+ this.changed.emit(checked);
54
53
  }
55
54
  /** CONTROL VALUE ACCESSOR */
56
55
  writeValue(value) {
@@ -66,14 +65,14 @@ class HlmCheckbox {
66
65
  this.formDisabled.set(isDisabled);
67
66
  }
68
67
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
69
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmCheckbox, isStandalone: true, selector: "hlm-checkbox", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrapperDisabled: { classPropertyName: "wrapperDisabled", publicName: "wrapperDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, host: { properties: { "attr.id": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null", "attr.data-disabled": "disabledState() ? \"\" : null" }, classAttribute: "contents peer" }, providers: [HLM_CHECKBOX_VALUE_ACCESSOR], ngImport: i0, template: "\n <brn-checkbox\n [id]=\"id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked()\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (changed)=\"_handleChange()\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ", isInline: true, dependencies: [{ kind: "component", type: BrnCheckbox, selector: "brn-checkbox", inputs: ["checked", "indeterminate", "id", "name", "class", "hostStyles", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "forceInvalid"], outputs: ["checkedChange", "indeterminateChange", "touched"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }], viewProviders: [provideIcons({ lucideCheck })], changeDetection: i0.ChangeDetectionStrategy.OnPush });
68
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: HlmCheckbox, isStandalone: true, selector: "hlm-checkbox", inputs: { userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrapperDisabled: { classPropertyName: "wrapperDisabled", publicName: "wrapperDisabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, host: { properties: { "attr.id": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.aria-describedby": "null", "attr.data-disabled": "disabledState() ? \"\" : null" }, classAttribute: "contents peer" }, providers: [HLM_CHECKBOX_VALUE_ACCESSOR], ngImport: i0, template: "\n <brn-checkbox\n [id]=\"id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked()\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (checkedChange)=\"_handleCheckedChange($event)\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ", isInline: true, dependencies: [{ kind: "component", type: BrnCheckbox, selector: "brn-checkbox", inputs: ["checked", "indeterminate", "id", "name", "class", "hostStyles", "aria-label", "aria-labelledby", "aria-describedby", "required", "disabled", "forceInvalid"], outputs: ["checkedChange", "indeterminateChange", "touched"] }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: HlmIcon, selector: "ng-icon[hlm]", inputs: ["size"] }], viewProviders: [provideIcons({ lucideCheck })], changeDetection: i0.ChangeDetectionStrategy.OnPush });
70
69
  }
71
70
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmCheckbox, decorators: [{
72
71
  type: Component,
73
72
  args: [{
74
73
  selector: 'hlm-checkbox',
75
74
  imports: [BrnCheckbox, NgIcon, HlmIcon],
76
- template: "\n <brn-checkbox\n [id]=\"id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked()\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (changed)=\"_handleChange()\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ",
75
+ template: "\n <brn-checkbox\n [id]=\"id()\"\n [name]=\"name()\"\n [class]=\"_computedClass()\"\n [checked]=\"checked()\"\n [disabled]=\"disabledState()\"\n [required]=\"required()\"\n [aria-label]=\"ariaLabel()\"\n [aria-labelledby]=\"ariaLabelledby()\"\n [aria-describedby]=\"ariaDescribedby()\"\n (checkedChange)=\"_handleCheckedChange($event)\"\n (touched)=\"_onTouched?.()\"\n >\n @if (checked()) {\n <span class=\"flex items-center justify-center text-current transition-none\">\n <ng-icon hlm size=\"14px\" name=\"lucideCheck\" />\n </span>\n }\n </brn-checkbox>\n ",
77
76
  host: {
78
77
  class: 'contents peer',
79
78
  '[attr.id]': 'null',
@@ -35,7 +35,7 @@ declare class HlmCheckbox implements ControlValueAccessor {
35
35
  readonly changed: _angular_core.OutputEmitterRef<boolean>;
36
36
  protected _onChange?: ChangeFn<CheckboxValue>;
37
37
  protected _onTouched?: TouchFn;
38
- protected _handleChange(): void;
38
+ protected _handleCheckedChange(checked: boolean): void;
39
39
  /** CONTROL VALUE ACCESSOR */
40
40
  writeValue(value: CheckboxValue): void;
41
41
  registerOnChange(fn: ChangeFn<CheckboxValue>): void;