@egose/shadcn-theme-ng-tw 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 (165) 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/fesm2022/alert.mjs +1 -1
  5. package/alert-dialog/README.md +471 -5
  6. package/aspect-ratio/README.md +272 -5
  7. package/autocomplete/README.md +502 -2
  8. package/autocomplete/fesm2022/autocomplete.mjs +1 -1
  9. package/avatar/README.md +357 -5
  10. package/badge/README.md +318 -2
  11. package/basic-alert/README.md +353 -2
  12. package/breadcrumb/README.md +406 -5
  13. package/button/README.md +482 -2
  14. package/button/fesm2022/button.mjs +85 -107
  15. package/button/types/button.d.ts +5 -8
  16. package/button-group/README.md +318 -5
  17. package/button-group/fesm2022/button-group.mjs +1 -1
  18. package/calendar/README.md +357 -2
  19. package/card/README.md +331 -5
  20. package/carousel/README.md +333 -5
  21. package/carousel/fesm2022/carousel.mjs +4 -1
  22. package/checkbox/README.md +320 -2
  23. package/checkbox/fesm2022/checkbox.mjs +6 -7
  24. package/checkbox/types/checkbox.d.ts +1 -1
  25. package/collapsible/README.md +332 -5
  26. package/combobox/README.md +507 -5
  27. package/combobox/fesm2022/combobox.mjs +5 -2
  28. package/command/README.md +435 -5
  29. package/confirmation-dialog/README.md +301 -2
  30. package/context-menu/README.md +366 -5
  31. package/date-picker/README.md +469 -2
  32. package/date-picker/fesm2022/date-picker.mjs +150 -32
  33. package/date-picker/types/date-picker.d.ts +102 -9
  34. package/dialog/README.md +448 -2
  35. package/drawer/README.md +395 -5
  36. package/dropdown-menu/README.md +417 -5
  37. package/empty/README.md +329 -5
  38. package/field/README.md +385 -5
  39. package/form-autocomplete/README.md +177 -0
  40. package/form-autocomplete/fesm2022/form-autocomplete.mjs +211 -0
  41. package/form-autocomplete/package.json +24 -0
  42. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  43. package/form-checkbox/README.md +322 -2
  44. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  45. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  46. package/form-combobox/README.md +202 -0
  47. package/form-combobox/fesm2022/form-combobox.mjs +273 -0
  48. package/form-combobox/package.json +24 -0
  49. package/form-combobox/types/form-combobox.d.ts +73 -0
  50. package/form-date-picker/README.md +348 -2
  51. package/form-date-picker/fesm2022/form-date-picker.mjs +60 -16
  52. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  53. package/form-date-picker-multi/README.md +191 -0
  54. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +239 -0
  55. package/form-date-picker-multi/package.json +24 -0
  56. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  57. package/form-date-range-picker/README.md +253 -0
  58. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +236 -0
  59. package/form-date-range-picker/package.json +24 -0
  60. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  61. package/form-field/README.md +356 -2
  62. package/form-field-simple/README.md +340 -2
  63. package/form-input-otp/README.md +194 -0
  64. package/form-input-otp/fesm2022/form-input-otp.mjs +194 -0
  65. package/form-input-otp/package.json +24 -0
  66. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  67. package/form-month-year-picker/README.md +188 -0
  68. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +229 -0
  69. package/form-month-year-picker/package.json +24 -0
  70. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  71. package/form-native-select/README.md +205 -0
  72. package/form-native-select/fesm2022/form-native-select.mjs +187 -0
  73. package/form-native-select/package.json +24 -0
  74. package/form-native-select/types/form-native-select.d.ts +57 -0
  75. package/form-phone-input/README.md +188 -0
  76. package/form-phone-input/fesm2022/form-phone-input.mjs +203 -0
  77. package/form-phone-input/package.json +24 -0
  78. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  79. package/form-radio-group/README.md +198 -0
  80. package/form-radio-group/fesm2022/form-radio-group.mjs +211 -0
  81. package/form-radio-group/package.json +24 -0
  82. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  83. package/form-searchable-multiselect/README.md +371 -2
  84. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  85. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  86. package/form-select/README.md +360 -2
  87. package/form-select/fesm2022/form-select.mjs +24 -15
  88. package/form-select/types/form-select.d.ts +18 -1
  89. package/form-slider/README.md +182 -0
  90. package/form-slider/fesm2022/form-slider.mjs +186 -0
  91. package/form-slider/package.json +24 -0
  92. package/form-slider/types/form-slider.d.ts +60 -0
  93. package/form-switch/README.md +173 -0
  94. package/form-switch/fesm2022/form-switch.mjs +178 -0
  95. package/form-switch/package.json +24 -0
  96. package/form-switch/types/form-switch.d.ts +50 -0
  97. package/form-text-input/README.md +381 -2
  98. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  99. package/form-text-input/types/form-text-input.d.ts +18 -1
  100. package/form-textarea/README.md +357 -2
  101. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  102. package/form-textarea/types/form-textarea.d.ts +18 -1
  103. package/form-toggle/README.md +186 -0
  104. package/form-toggle/fesm2022/form-toggle.mjs +241 -0
  105. package/form-toggle/package.json +24 -0
  106. package/form-toggle/types/form-toggle.d.ts +82 -0
  107. package/form-toggle-group/README.md +176 -0
  108. package/form-toggle-group/fesm2022/form-toggle-group.mjs +216 -0
  109. package/form-toggle-group/package.json +24 -0
  110. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  111. package/hover-card/README.md +256 -5
  112. package/icon/README.md +239 -2
  113. package/input/README.md +269 -2
  114. package/input-group/README.md +335 -5
  115. package/input-group/fesm2022/input-group.mjs +23 -13
  116. package/input-group/types/input-group.d.ts +4 -1
  117. package/input-otp/README.md +375 -5
  118. package/item/README.md +385 -5
  119. package/item/fesm2022/item.mjs +3 -3
  120. package/kbd/README.md +291 -5
  121. package/label/README.md +272 -2
  122. package/layout-simple/README.md +193 -2
  123. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  124. package/layout-simple/types/layout-simple.d.ts +174 -137
  125. package/menu/README.md +417 -2
  126. package/menubar/README.md +343 -5
  127. package/native-select/README.md +323 -5
  128. package/native-select/fesm2022/native-select.mjs +18 -6
  129. package/native-select/types/native-select.d.ts +7 -2
  130. package/navigation-menu/README.md +369 -5
  131. package/package.json +57 -1
  132. package/pagination/README.md +388 -5
  133. package/phone-input/README.md +114 -0
  134. package/phone-input/fesm2022/phone-input.mjs +191 -0
  135. package/phone-input/package.json +24 -0
  136. package/phone-input/types/phone-input.d.ts +67 -0
  137. package/popover/README.md +331 -2
  138. package/progress/README.md +311 -5
  139. package/radio-group/README.md +364 -2
  140. package/radio-group/fesm2022/radio-group.mjs +5 -1
  141. package/resizable/README.md +269 -5
  142. package/scroll-area/README.md +233 -5
  143. package/searchable-multiselect/README.md +323 -2
  144. package/select/README.md +437 -2
  145. package/separator/README.md +222 -2
  146. package/sheet/README.md +311 -2
  147. package/sheet/fesm2022/sheet.mjs +1 -1
  148. package/sidebar/README.md +457 -5
  149. package/skeleton/README.md +217 -5
  150. package/slider/README.md +273 -5
  151. package/slider/fesm2022/slider.mjs +17 -13
  152. package/sonner/README.md +346 -2
  153. package/spinner/README.md +284 -2
  154. package/switch/README.md +310 -2
  155. package/switch/fesm2022/switch.mjs +7 -5
  156. package/switch/types/switch.d.ts +2 -1
  157. package/table/README.md +423 -5
  158. package/tabs/README.md +411 -2
  159. package/tabs/fesm2022/tabs.mjs +12 -2
  160. package/textarea/README.md +282 -5
  161. package/toggle/README.md +270 -5
  162. package/toggle-group/README.md +340 -5
  163. package/tooltip/README.md +269 -2
  164. package/typography/README.md +271 -5
  165. package/utils/README.md +303 -2
package/avatar/README.md CHANGED
@@ -1,11 +1,363 @@
1
- # Avatar
1
+ # Avatar (`@egose/shadcn-theme-ng/avatar`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
3
+ A user identity image in the shadcn/ui Avatar style: a rounded photo that degrades gracefully to
4
+ initials (or any fallback) while the image loads or when it fails, with optional status badges and
5
+ overlapping group stacks.
4
6
 
5
- ## Building
7
+ The implementation styles the headless `BrnAvatar*` primitives from
8
+ `@spartan-ng/brain/avatar`. `HlmAvatar` is a component extending `BrnAvatar` that projects
9
+ `[hlmAvatarImage]` when the image `canShow()` and `[hlmAvatarFallback]` otherwise;
10
+ `HlmAvatarImage` / `HlmAvatarFallback` add brain image/fallback behavior plus shadcn sizing.
11
+ `HlmAvatarBadge`, `HlmAvatarGroup`, and `HlmAvatarGroupCount` are pure layout directives with no
12
+ brain dependency.
6
13
 
7
- To build the library, run:
14
+ > **Ships as:** `@egose/shadcn-theme-ng/avatar` and `@egose/shadcn-theme-ng-tw/avatar`
15
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
16
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
17
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
18
+
19
+ ## Installation
8
20
 
9
21
  ```bash
10
- ng build avatar
22
+ # Plain Tailwind (no prefix)
23
+ npm install @egose/shadcn-theme-ng
24
+
25
+ # tw:-prefixed Tailwind variant
26
+ npm install @egose/shadcn-theme-ng-tw
27
+ ```
28
+
29
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
30
+ [package README](../../README.md#peer-dependencies). Runtime styling helper:
31
+ `@egose/shadcn-theme-ng/utils` (`classes()`).
32
+
33
+ ## Imports
34
+
35
+ All symbols are exported from the subpath root (`projects/avatar/src/public-api.ts`):
36
+
37
+ ```ts
38
+ import {
39
+ HlmAvatar,
40
+ HlmAvatarImage,
41
+ HlmAvatarFallback,
42
+ HlmAvatarBadge,
43
+ HlmAvatarGroup,
44
+ HlmAvatarGroupCount,
45
+ HlmAvatarImports,
46
+ HlmAvatarModule,
47
+ } from '@egose/shadcn-theme-ng/avatar';
48
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/avatar'
49
+ ```
50
+
51
+ Standalone component — spread the `*Imports` array:
52
+
53
+ ```ts
54
+ import { Component } from '@angular/core';
55
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
56
+
57
+ @Component({
58
+ selector: 'app-demo',
59
+ standalone: true,
60
+ imports: [...HlmAvatarImports],
61
+ template: `...`,
62
+ })
63
+ export class DemoComponent {}
64
+ ```
65
+
66
+ NgModule-based consumer — import the module:
67
+
68
+ ```ts
69
+ import { NgModule } from '@angular/core';
70
+ import { HlmAvatarModule } from '@egose/shadcn-theme-ng/avatar';
71
+
72
+ @NgModule({ imports: [HlmAvatarModule] })
73
+ export class DemoModule {}
74
+ ```
75
+
76
+ ## Anatomy / Structure
77
+
78
+ ```html
79
+ <hlm-avatar size="default">
80
+ <img hlmAvatarImage src="user.jpg" alt="Ada Lovelace" />
81
+ <span hlmAvatarFallback>AL</span>
82
+ <span hlm-avatar-badge></span>
83
+ </hlm-avatar>
84
+
85
+ <div hlmAvatarGroup>
86
+ <hlm-avatar>…</hlm-avatar>
87
+ <hlm-avatar>…</hlm-avatar>
88
+ <span hlmAvatarGroupCount>+3</span>
89
+ </div>
11
90
  ```
91
+
92
+ Real selectors (from source):
93
+
94
+ | Class | Selector(s) | Kind |
95
+ | --------------------- | ----------------------------------------------------- | --------- |
96
+ | `HlmAvatar` | `hlm-avatar` | Component |
97
+ | `HlmAvatarImage` | `img[hlmAvatarImage]` (`exportAs: hlmAvatarImage`) | Directive |
98
+ | `HlmAvatarFallback` | `[hlmAvatarFallback]` (`exportAs: hlmAvatarFallback`) | Directive |
99
+ | `HlmAvatarBadge` | `[hlmAvatarBadge], hlm-avatar-badge` | Directive |
100
+ | `HlmAvatarGroup` | `[hlmAvatarGroup], hlm-avatar-group` | Directive |
101
+ | `HlmAvatarGroupCount` | `[hlmAvatarGroupCount], hlm-avatar-group-count` | Directive |
102
+
103
+ Projection rules inside `HlmAvatar` (from its template): the image slot only renders while
104
+ `_image()?.canShow()` is true and selects `[hlmAvatarImage],[brnAvatarImage]`; otherwise the
105
+ fallback slot (`[hlmAvatarFallback],[brnAvatarFallback]`) renders. Untagged `<ng-content />`
106
+ (such as the badge) always renders.
107
+
108
+ ## API reference
109
+
110
+ ### `HlmAvatar` — `hlm-avatar`
111
+
112
+ Component extending `BrnAvatar` (full brain surface inherited).
113
+
114
+ | Input | Type | Default | Description |
115
+ | ------ | --------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------- |
116
+ | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | `default` → `size-8`, `sm` → `size-6`, `lg` → `size-10`; reflected as `data-size`, which badge styles key off. |
117
+
118
+ ### `HlmAvatarImage` — `img[hlmAvatarImage]`
119
+
120
+ Directive on a native `<img>` wrapping `BrnAvatarImage` (load/error tracking inherited).
121
+
122
+ | Member | Kind | Description |
123
+ | --------- | ---------------------------------------------------- | --------------------------------------------- |
124
+ | `canShow` | `Signal<boolean>` (re-exposed from `BrnAvatarImage`) | Whether the image has loaded and may display. |
125
+
126
+ No own inputs — `src`/`alt` are the native img attributes.
127
+
128
+ ### `HlmAvatarFallback` — `[hlmAvatarFallback]`
129
+
130
+ Thin wrapper around `BrnAvatarFallback`. No own inputs; renders initials/text centered in the
131
+ muted circle.
132
+
133
+ ### `HlmAvatarBadge` — `[hlmAvatarBadge], hlm-avatar-badge`
134
+
135
+ Status dot pinned to the avatar's bottom-right (`absolute right-0 bottom-0`, ring-separated).
136
+ Sizing follows the parent avatar's `data-size` (`size-2` on `sm`, `size-2.5` on `default`,
137
+ `size-3` on `lg`). No inputs — put an `ng-icon` or plain dot inside.
138
+
139
+ ### `HlmAvatarGroup` — `[hlmAvatarGroup], hlm-avatar-group`
140
+
141
+ Overlapping stack (`flex -space-x-2`, each child avatar ringed). No inputs.
142
+
143
+ ### `HlmAvatarGroupCount` — `[hlmAvatarGroupCount], hlm-avatar-group-count`
144
+
145
+ The "+N" overflow pill. Sizing follows the parent group's `data-size` when present. No inputs.
146
+
147
+ ## Examples
148
+
149
+ ### 1. Basic avatar with image + initials fallback
150
+
151
+ ```ts
152
+ import { Component } from '@angular/core';
153
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
154
+
155
+ @Component({
156
+ selector: 'app-avatar-basic',
157
+ standalone: true,
158
+ imports: [...HlmAvatarImports],
159
+ template: `
160
+ <hlm-avatar>
161
+ <img hlmAvatarImage src="https://github.com/shadcn.png" alt="shadcn" />
162
+ <span hlmAvatarFallback>CN</span>
163
+ </hlm-avatar>
164
+ `,
165
+ })
166
+ export class AvatarBasicComponent {}
167
+ ```
168
+
169
+ ```html
170
+ <app-avatar-basic />
171
+ ```
172
+
173
+ ### 2. All sizes
174
+
175
+ ```ts
176
+ import { Component } from '@angular/core';
177
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
178
+
179
+ @Component({
180
+ selector: 'app-avatar-sizes',
181
+ standalone: true,
182
+ imports: [...HlmAvatarImports],
183
+ template: `
184
+ <div class="tw:flex tw:items-center tw:gap-4">
185
+ <hlm-avatar size="sm">
186
+ <img hlmAvatarImage src="https://github.com/shadcn.png" alt="Small" />
187
+ <span hlmAvatarFallback>SM</span>
188
+ </hlm-avatar>
189
+ <hlm-avatar size="default">
190
+ <img hlmAvatarImage src="https://github.com/shadcn.png" alt="Default" />
191
+ <span hlmAvatarFallback>MD</span>
192
+ </hlm-avatar>
193
+ <hlm-avatar size="lg">
194
+ <img hlmAvatarImage src="https://github.com/shadcn.png" alt="Large" />
195
+ <span hlmAvatarFallback>LG</span>
196
+ </hlm-avatar>
197
+ </div>
198
+ `,
199
+ })
200
+ export class AvatarSizesComponent {}
201
+ ```
202
+
203
+ ### 3. Fallback-only (no image) and broken-image degradation
204
+
205
+ When no `<img>` succeeds, the fallback shows automatically — useful for new users without photos:
206
+
207
+ ```ts
208
+ import { Component } from '@angular/core';
209
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
210
+
211
+ @Component({
212
+ selector: 'app-avatar-fallback',
213
+ standalone: true,
214
+ imports: [...HlmAvatarImports],
215
+ template: `
216
+ <div class="tw:flex tw:items-center tw:gap-4">
217
+ <!-- initials only -->
218
+ <hlm-avatar>
219
+ <span hlmAvatarFallback>JD</span>
220
+ </hlm-avatar>
221
+ <!-- broken src degrades to fallback via canShow() -->
222
+ <hlm-avatar>
223
+ <img hlmAvatarImage src="/does-not-exist.png" alt="Missing photo" />
224
+ <span hlmAvatarFallback>ER</span>
225
+ </hlm-avatar>
226
+ </div>
227
+ `,
228
+ })
229
+ export class AvatarFallbackComponent {}
230
+ ```
231
+
232
+ ### 4. Status badge (online/offline dot)
233
+
234
+ ```ts
235
+ import { Component } from '@angular/core';
236
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
237
+
238
+ @Component({
239
+ selector: 'app-avatar-badge',
240
+ standalone: true,
241
+ imports: [...HlmAvatarImports],
242
+ template: `
243
+ <div class="tw:flex tw:items-center tw:gap-4">
244
+ <hlm-avatar>
245
+ <img hlmAvatarImage src="https://github.com/shadcn.png" alt="Online user" />
246
+ <span hlmAvatarFallback>ON</span>
247
+ <span hlm-avatar-badge class="tw:bg-success"></span>
248
+ </hlm-avatar>
249
+ <hlm-avatar size="lg">
250
+ <span hlmAvatarFallback>OF</span>
251
+ <span hlmAvatarBadge class="tw:bg-muted-foreground"></span>
252
+ </hlm-avatar>
253
+ </div>
254
+ `,
255
+ })
256
+ export class AvatarBadgeComponent {}
257
+ ```
258
+
259
+ ### 5. Avatar group with overflow count
260
+
261
+ ```ts
262
+ import { Component, signal } from '@angular/core';
263
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
264
+
265
+ @Component({
266
+ selector: 'app-avatar-group',
267
+ standalone: true,
268
+ imports: [...HlmAvatarImports],
269
+ template: `
270
+ <div hlmAvatarGroup>
271
+ @for (m of visible(); track m.initials) {
272
+ <hlm-avatar>
273
+ @if (m.src) {
274
+ <img hlmAvatarImage [src]="m.src" [alt]="m.name" />
275
+ }
276
+ <span hlmAvatarFallback>{{ m.initials }}</span>
277
+ </hlm-avatar>
278
+ }
279
+ @if (overflow() > 0) {
280
+ <span hlmAvatarGroupCount>+{{ overflow() }}</span>
281
+ }
282
+ </div>
283
+ `,
284
+ })
285
+ export class AvatarGroupComponent {
286
+ private readonly members = signal([
287
+ { name: 'Ada Lovelace', initials: 'AL', src: '' },
288
+ { name: 'Grace Hopper', initials: 'GH', src: '' },
289
+ { name: 'Katherine Johnson', initials: 'KJ', src: '' },
290
+ { name: 'Radia Perlman', initials: 'RP', src: '' },
291
+ { name: 'Lynn Conway', initials: 'LC', src: '' },
292
+ ]);
293
+ readonly visible = () => this.members().slice(0, 3);
294
+ readonly overflow = () => this.members().length - 3;
295
+ }
296
+ ```
297
+
298
+ ### 6. Data-driven directory with badge icons
299
+
300
+ Badges can host an `ng-icon` (sized automatically per avatar size):
301
+
302
+ ```ts
303
+ import { Component, signal } from '@angular/core';
304
+ import { NgIcon, provideIcons } from '@ng-icons/core';
305
+ import { lucideCheck } from '@ng-icons/lucide';
306
+ import { HlmAvatarImports } from '@egose/shadcn-theme-ng/avatar';
307
+
308
+ @Component({
309
+ selector: 'app-avatar-directory',
310
+ standalone: true,
311
+ imports: [...HlmAvatarImports, NgIcon],
312
+ providers: [provideIcons({ lucideCheck })],
313
+ template: `
314
+ <ul class="tw:flex tw:flex-col tw:gap-3">
315
+ @for (u of users(); track u.email) {
316
+ <li class="tw:flex tw:items-center tw:gap-3">
317
+ <hlm-avatar size="sm">
318
+ @if (u.src) {
319
+ <img hlmAvatarImage [src]="u.src" [alt]="u.name" />
320
+ }
321
+ <span hlmAvatarFallback>{{ u.initials }}</span>
322
+ @if (u.verified) {
323
+ <span hlm-avatar-badge><ng-icon name="lucideCheck" /></span>
324
+ }
325
+ </hlm-avatar>
326
+ <div>
327
+ <p class="tw:text-sm tw:font-medium">{{ u.name }}</p>
328
+ <p class="tw:text-xs tw:text-muted-foreground">{{ u.email }}</p>
329
+ </div>
330
+ </li>
331
+ }
332
+ </ul>
333
+ `,
334
+ })
335
+ export class AvatarDirectoryComponent {
336
+ readonly users = signal([
337
+ { name: 'Ada Lovelace', email: 'ada@example.com', initials: 'AL', src: '', verified: true },
338
+ { name: 'Alan Turing', email: 'alan@example.com', initials: 'AT', src: '', verified: false },
339
+ ]);
340
+ }
341
+ ```
342
+
343
+ ## Accessibility notes
344
+
345
+ - The `<img>` **must** carry a meaningful `alt` (person's name). When only initials show, keep the
346
+ fallback text itself meaningful (`AL`, not `?`) — it is what screen readers announce.
347
+ - Badges are decorative status dots: hide icon-only badges from assistive technology or mirror the
348
+ status in adjacent text ("Online"), since color alone is not perceivable by everyone.
349
+ - Group overflow (`+3`) is plain text — ensure the full member list is reachable nearby (tooltip,
350
+ popover, or link) so keyboard/screen-reader users are not locked out of hidden members.
351
+
352
+ ## Theming / CSS variables
353
+
354
+ Class-based styling (muted fallback, primary badge, background ring tokens); no
355
+ component-specific CSS variables. Avatar and group `data-size` attributes drive the badge/count
356
+ sizing, so custom sizes compose by targeting those attributes in your own CSS.
357
+
358
+ ## Related subpaths
359
+
360
+ - `@egose/shadcn-theme-ng/badge` — textual status chips to pair with avatars
361
+ - `@egose/shadcn-theme-ng/tooltip` — full member names / status on hover
362
+ - `@egose/shadcn-theme-ng/hover-card` — profile previews anchored to avatars
363
+ - `@egose/shadcn-theme-ng/skeleton` — avatar-shaped loading placeholders
package/badge/README.md CHANGED
@@ -1,3 +1,319 @@
1
- # Badge Subpath
1
+ # Badge (`@egose/shadcn-theme-ng/badge`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/badge` or `@egose/shadcn-theme-ng-tw/badge`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A compact status/count chip in the shadcn/ui Badge style: a small pill for labels like "New",
4
+ "Beta", unread counts, or role tags. Use it inline in headings, list rows, tabs, and buttons where
5
+ a full [Alert](https://ui.shadcn.com/docs/components/alert) would be too heavy.
6
+
7
+ This is a **standalone component with no brain primitive and no `*Imports`/`*Module` wrapper** —
8
+ import `HlmBadge` itself. Variants come from a `cva` table (`badgeVariants`), merged with
9
+ `hlm()`; the `outline` / `outline-filled` appearances add per-tone border/hover classes computed
10
+ by private helpers. An imperative `setClass()` escape hatch appends extra classes.
11
+
12
+ > **Ships as:** `@egose/shadcn-theme-ng/badge` and `@egose/shadcn-theme-ng-tw/badge`
13
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
14
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
15
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # Plain Tailwind (no prefix)
21
+ npm install @egose/shadcn-theme-ng
22
+
23
+ # tw:-prefixed Tailwind variant
24
+ npm install @egose/shadcn-theme-ng-tw
25
+ ```
26
+
27
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
28
+ [package README](../../README.md#peer-dependencies). Runtime styling helper:
29
+ `@egose/shadcn-theme-ng/utils` (`hlm()`).
30
+
31
+ ## Imports
32
+
33
+ `HlmBadge` is exported from the subpath root (`projects/badge/src/public-api.ts`). Note there is
34
+ **no** `HlmBadgeImports` array and **no** `HlmBadgeModule` — import the component directly:
35
+
36
+ ```ts
37
+ import {
38
+ HlmBadge,
39
+ badgeVariants,
40
+ type BadgeVariants,
41
+ type BadgeVariantType,
42
+ type BadgeSizeType,
43
+ type BadgeAppearanceType,
44
+ } from '@egose/shadcn-theme-ng/badge';
45
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/badge'
46
+ ```
47
+
48
+ Standalone component usage:
49
+
50
+ ```ts
51
+ import { Component } from '@angular/core';
52
+ import { HlmBadge } from '@egose/shadcn-theme-ng/badge';
53
+
54
+ @Component({
55
+ selector: 'app-demo',
56
+ standalone: true,
57
+ imports: [HlmBadge],
58
+ template: `<span hlmBadge>New</span>`,
59
+ })
60
+ export class DemoComponent {}
61
+ ```
62
+
63
+ NgModule-based consumer — list the component in the module imports (it is standalone):
64
+
65
+ ```ts
66
+ import { NgModule } from '@angular/core';
67
+ import { HlmBadge } from '@egose/shadcn-theme-ng/badge';
68
+
69
+ @NgModule({ imports: [HlmBadge] })
70
+ export class DemoModule {}
71
+ ```
72
+
73
+ ## Anatomy / Structure
74
+
75
+ ```html
76
+ <span hlmBadge variant="success" size="default" appearance="solid">Active</span>
77
+ <a hlmBadge variant="link" href="/changelog">v2.4.1</a>
78
+ ```
79
+
80
+ Real selector (from source):
81
+
82
+ | Class | Selector(s) | Kind |
83
+ | ---------- | ----------------------------- | --------- |
84
+ | `HlmBadge` | `span[hlmBadge], a[hlmBadge]` | Component |
85
+
86
+ Only `<span>` and `<a>` hosts match — the component projects its content (`<ng-content>`) and
87
+ computes its host class from `variant` + `size` + `appearance` + `userClass`.
88
+
89
+ ## API reference
90
+
91
+ ### `HlmBadge` — `span[hlmBadge], a[hlmBadge]`
92
+
93
+ | Input | Type | Default | Description |
94
+ | ------------ | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
95
+ | `variant` | `BadgeVariantType` | `'primary'` | Color tone (13 values, see below). |
96
+ | `size` | `BadgeSizeType` | `'default'` | `sm` (`h-5 px-2 text-xs`), `default` (`h-6 px-3 text-sm`), `lg` (`h-7 px-4 text-base`). |
97
+ | `appearance` | `BadgeAppearanceType` | `'solid'` | `solid` (filled), `outline` (white bg + tone border/text), `outline-filled` (outline + fills with the tone on hover). |
98
+ | `userClass` | `ClassValue` | `''` | Extra classes merged into the `cva` call. **Note:** unlike most `hlm*` pieces this input has **no `class` alias** — bind `[userClass]`, not `[class]`. |
99
+
100
+ | Method | Signature | Description |
101
+ | ---------- | --------------------------------- | -------------------------------------------------------------------------------- |
102
+ | `setClass` | `setClass(classes: string): void` | Imperatively appends extra classes (stored in a private signal and merged last). |
103
+
104
+ `variant` values: `primary` · `secondary` · `success` · `warning` · `danger` · `info` ·
105
+ `light` · `dark` · `accent` · `destructive` · `muted` · `link` · `ghost`.
106
+
107
+ Exported helpers: `badgeVariants` (the `cva` table — reuse it for custom badge-like hosts),
108
+ `BadgeVariants`, `BadgeVariantType`, `BadgeSizeType`, `BadgeAppearanceType`.
109
+
110
+ ## Examples
111
+
112
+ ### 1. Basic badge
113
+
114
+ ```ts
115
+ import { Component } from '@angular/core';
116
+ import { HlmBadge } from '@egose/shadcn-theme-ng/badge';
117
+
118
+ @Component({
119
+ selector: 'app-badge-basic',
120
+ standalone: true,
121
+ imports: [HlmBadge],
122
+ template: `<span hlmBadge>New</span>`,
123
+ })
124
+ export class BadgeBasicComponent {}
125
+ ```
126
+
127
+ ```html
128
+ <app-badge-basic />
129
+ ```
130
+
131
+ ### 2. All variants × solid
132
+
133
+ ```ts
134
+ import { Component } from '@angular/core';
135
+ import { HlmBadge, type BadgeVariantType } from '@egose/shadcn-theme-ng/badge';
136
+
137
+ @Component({
138
+ selector: 'app-badge-variants',
139
+ standalone: true,
140
+ imports: [HlmBadge],
141
+ template: `
142
+ <div class="tw:flex tw:flex-wrap tw:gap-2">
143
+ @for (v of variants; track v) {
144
+ <span hlmBadge [variant]="v">{{ v }}</span>
145
+ }
146
+ </div>
147
+ `,
148
+ })
149
+ export class BadgeVariantsComponent {
150
+ readonly variants: BadgeVariantType[] = [
151
+ 'primary',
152
+ 'secondary',
153
+ 'success',
154
+ 'warning',
155
+ 'danger',
156
+ 'info',
157
+ 'light',
158
+ 'dark',
159
+ 'accent',
160
+ 'destructive',
161
+ 'muted',
162
+ 'link',
163
+ 'ghost',
164
+ ];
165
+ }
166
+ ```
167
+
168
+ ### 3. Sizes and outline appearances
169
+
170
+ ```ts
171
+ import { Component } from '@angular/core';
172
+ import { HlmBadge } from '@egose/shadcn-theme-ng/badge';
173
+
174
+ @Component({
175
+ selector: 'app-badge-sizes',
176
+ standalone: true,
177
+ imports: [HlmBadge],
178
+ template: `
179
+ <div class="tw:flex tw:items-center tw:gap-2">
180
+ <span hlmBadge size="sm">small</span>
181
+ <span hlmBadge size="default">default</span>
182
+ <span hlmBadge size="lg">large</span>
183
+ </div>
184
+ <div class="tw:mt-3 tw:flex tw:items-center tw:gap-2">
185
+ <span hlmBadge variant="success" appearance="outline">outline</span>
186
+ <span hlmBadge variant="destructive" appearance="outline-filled">outline-filled (hover me)</span>
187
+ </div>
188
+ `,
189
+ })
190
+ export class BadgeSizesComponent {}
191
+ ```
192
+
193
+ ### 4. Link badge + count badge composed with other components
194
+
195
+ `a[hlmBadge]` is a real link host; badges also sit naturally inside buttons and headings:
196
+
197
+ ```ts
198
+ import { Component, signal } from '@angular/core';
199
+ import { HlmBadge } from '@egose/shadcn-theme-ng/badge';
200
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
201
+
202
+ @Component({
203
+ selector: 'app-badge-composed',
204
+ standalone: true,
205
+ imports: [HlmBadge, ...HlmButtonImports],
206
+ template: `
207
+ <h2 class="tw:flex tw:items-center tw:gap-2 tw:text-lg tw:font-semibold">
208
+ Inbox <span hlmBadge variant="secondary" size="sm">{{ unread() }}</span>
209
+ </h2>
210
+ <p class="tw:mt-2">
211
+ <a hlmBadge variant="link" href="/changelog">v2.4.1 — see what's new</a>
212
+ </p>
213
+ <button hlmButton variant="outline" size="sm" class="tw:mt-3" (click)="unread.set(0)">
214
+ Mark all read <span hlmBadge variant="muted" size="sm">{{ unread() }}</span>
215
+ </button>
216
+ `,
217
+ })
218
+ export class BadgeComposedComponent {
219
+ readonly unread = signal(7);
220
+ }
221
+ ```
222
+
223
+ ### 5. Status mapping from domain state (signal-driven)
224
+
225
+ ```ts
226
+ import { Component, computed, signal } from '@angular/core';
227
+ import { HlmBadge, type BadgeVariantType } from '@egose/shadcn-theme-ng/badge';
228
+
229
+ type OrderState = 'pending' | 'shipped' | 'delivered' | 'cancelled';
230
+
231
+ const tone: Record<OrderState, BadgeVariantType> = {
232
+ pending: 'warning',
233
+ shipped: 'info',
234
+ delivered: 'success',
235
+ cancelled: 'destructive',
236
+ };
237
+
238
+ @Component({
239
+ selector: 'app-badge-status',
240
+ standalone: true,
241
+ imports: [HlmBadge],
242
+ template: `
243
+ <ul class="tw:flex tw:flex-col tw:gap-2">
244
+ @for (o of orders(); track o.id) {
245
+ <li class="tw:flex tw:items-center tw:gap-2 tw:text-sm">
246
+ Order #{{ o.id }}
247
+ <span hlmBadge [variant]="toneFor(o.state)" [userClass]="'tw:capitalize'">
248
+ {{ o.state }}
249
+ </span>
250
+ </li>
251
+ }
252
+ </ul>
253
+ `,
254
+ })
255
+ export class BadgeStatusComponent {
256
+ readonly orders = signal<{ id: number; state: OrderState }[]>([
257
+ { id: 101, state: 'pending' },
258
+ { id: 102, state: 'shipped' },
259
+ { id: 103, state: 'delivered' },
260
+ { id: 104, state: 'cancelled' },
261
+ ]);
262
+ toneFor = (s: OrderState) => tone[s];
263
+ }
264
+ ```
265
+
266
+ ### 6. Advanced: `badgeVariants()` for custom hosts + imperative `setClass`
267
+
268
+ Reuse the exported `cva` table for elements that cannot host the directive, and use `setClass`
269
+ for runtime class injection (e.g. from a directive or test harness):
270
+
271
+ ```ts
272
+ import { Component, viewChild } from '@angular/core';
273
+ import { HlmBadge, badgeVariants } from '@egose/shadcn-theme-ng/badge';
274
+
275
+ @Component({
276
+ selector: 'app-badge-advanced',
277
+ standalone: true,
278
+ imports: [HlmBadge],
279
+ template: `
280
+ <!-- custom host styled identically without the directive -->
281
+ <mark [class]="markClass">review</mark>
282
+ <!-- imperative escape hatch -->
283
+ <span hlmBadge #imp variant="info">runtime-tinted</span>
284
+ <button (click)="tint()">Tint via setClass()</button>
285
+ `,
286
+ })
287
+ export class BadgeAdvancedComponent {
288
+ readonly markClass = badgeVariants({ variant: 'warning', size: 'sm' });
289
+ private readonly badge = viewChild<HlmBadge>('imp');
290
+
291
+ tint() {
292
+ this.badge()?.setClass('tw:tracking-widest tw:uppercase');
293
+ }
294
+ }
295
+ ```
296
+
297
+ ## Accessibility notes
298
+
299
+ - Badges are visual labels, not live regions: dynamic counts ("7 unread") should live next to
300
+ properly labelled content (e.g. an `aria-label="Inbox, 7 unread"` heading) rather than relying
301
+ on the badge text alone.
302
+ - Never convey state by color only — the examples above pair each tone with explicit text
303
+ ("delivered", "cancelled"). The `light`/`ghost` tones are low-contrast by design; reserve them
304
+ for large/bold text or decorative contexts.
305
+ - `a[hlmBadge]` must have a real `href` (or be replaced by a `<button>`); do not use a link badge
306
+ as a click-handler span — keyboard users lose focusability.
307
+
308
+ ## Theming / CSS variables
309
+
310
+ Class-based `cva` theming over shadcn tokens (`bg-*/text-*`); no component-specific CSS variables.
311
+ Extend per-instance with `[userClass]` (note: no `class` alias on this component) or reuse
312
+ `badgeVariants()` for custom hosts.
313
+
314
+ ## Related subpaths
315
+
316
+ - `@egose/shadcn-theme-ng/alert` — full banner alerts for non-inline feedback
317
+ - `@egose/shadcn-theme-ng/basic-alert` — ready-made icon + title + description alert
318
+ - `@egose/shadcn-theme-ng/button` — action buttons badges decorate
319
+ - `@egose/shadcn-theme-ng/avatar` — identity visuals that pair with role/status badges