@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/sonner/README.md CHANGED
@@ -1,3 +1,347 @@
1
- # Sonner Subpath
1
+ # Sonner (`@egose/shadcn-theme-ng/sonner`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/sonner` or `@egose/shadcn-theme-ng-tw/sonner`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ Toast notifications for Angular, styled like [shadcn/ui Sonner](https://ui.shadcn.com/docs/components/sonner). This subpath ships a single standalone component, `HlmToaster`, which is a thin shadcn-styled wrapper around the `NgxSonnerToaster` component from [`ngx-sonner`](https://github.com/adamwathan/ngx-sonner) (the Angular port of [sonner](https://sonner.emilkowal.ski/)). Place one `<hlm-toaster />` at your app root, then fire toasts imperatively with the `toast()` helpers from `ngx-sonner`.
4
+
5
+ > **Ships as:** `@egose/shadcn-theme-ng/sonner` and `@egose/shadcn-theme-ng-tw/sonner` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
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
+ This library has no extra runtime install beyond the package itself — `ngx-sonner` is installed transitively. See the [package README](../../README.md) for the full peer-dependency table (`@angular/core`, `@angular/common`, `@spartan-ng/brain`, `rxjs`, etc.) and the Tailwind variant contract.
18
+
19
+ > You call `toast()`, `toast.success()`, etc. from the **`ngx-sonner`** package, not from this subpath. This subpath only provides the styled `<hlm-toaster />` outlet component.
20
+
21
+ ## Imports
22
+
23
+ All public symbols are re-exported from `projects/sonner/src/public-api.ts`:
24
+
25
+ ```ts
26
+ import { HlmToaster, HlmToasterImports, HlmToasterModule } from '@egose/shadcn-theme-ng/sonner';
27
+ // tw variant:
28
+ // import { HlmToaster, HlmToasterImports, HlmToasterModule } from '@egose/shadcn-theme-ng-tw/sonner';
29
+ ```
30
+
31
+ Standalone-component usage (preferred):
32
+
33
+ ```ts
34
+ import { Component } from '@angular/core';
35
+ import { HlmToasterImports } from '@egose/shadcn-theme-ng/sonner';
36
+
37
+ @Component({
38
+ selector: 'app-root',
39
+ standalone: true,
40
+ imports: [...HlmToasterImports],
41
+ template: `<hlm-toaster />`,
42
+ })
43
+ export class AppComponent {}
44
+ ```
45
+
46
+ NgModule usage:
47
+
48
+ ```ts
49
+ import { NgModule } from '@angular/core';
50
+ import { HlmToasterModule } from '@egose/shadcn-theme-ng/sonner';
51
+
52
+ @NgModule({ imports: [HlmToasterModule] })
53
+ export class AppModule {}
54
+ ```
55
+
56
+ | Symbol | Kind | Description |
57
+ | ------------------- | -------------------- | ------------------------------------------------ |
58
+ | `HlmToaster` | Standalone component | The `<hlm-toaster>` outlet. Renders toasts. |
59
+ | `HlmToasterImports` | `const` array | `[HlmToaster]` — spread into `imports: [...]`. |
60
+ | `HlmToasterModule` | NgModule | Imports + re-exports `HlmToaster` for NgModules. |
61
+
62
+ ## Anatomy / Structure
63
+
64
+ Render exactly one toaster outlet, typically in `AppComponent`:
65
+
66
+ ```html
67
+ <!-- app.component.html -->
68
+ <hlm-toaster />
69
+ <router-outlet />
70
+ ```
71
+
72
+ With common options:
73
+
74
+ ```html
75
+ <hlm-toaster
76
+ theme="system"
77
+ position="top-center"
78
+ [richColors]="true"
79
+ [expand]="true"
80
+ [closeButton]="true"
81
+ [duration]="5000"
82
+ [visibleToasts]="5"
83
+ />
84
+ ```
85
+
86
+ The component template forwards everything to `<ngx-sonner-toaster>` internally — you never write `<ngx-sonner-toaster>` yourself.
87
+
88
+ ## API reference
89
+
90
+ ### `HlmToaster` — selector `hlm-toaster` (component)
91
+
92
+ All inputs are Angular signal `input()`s mirroring `ToasterProps` from `ngx-sonner`.
93
+
94
+ | Input | Type | Default | Description |
95
+ | --------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
96
+ | `invert` | `boolean` (coerced with `booleanAttribute`) | `false` | Dark mode inversion. |
97
+ | `theme` | `ToasterProps['theme']` (`'light' \| 'dark' \| 'system'` approx.) | `'light'` | Color theme of the toasts. |
98
+ | `position` | `ToasterProps['position']` | `'bottom-right'` | Screen corner/edge: e.g. `top-left`, `top-center`, `top-right`, `bottom-left`, `bottom-center`, `bottom-right`. |
99
+ | `hotKey` | `ToasterProps['hotkey']` (`string[]`) | `['altKey', 'KeyT']` | Hotkey that focuses/expands toasts. |
100
+ | `richColors` | `boolean` (coerced) | `false` | Success/error color accents. |
101
+ | `expand` | `boolean` (coerced) | `false` | Expand all toasts by default (else stacked). |
102
+ | `duration` | `number` (coerced with `numberAttribute`) | `4000` | Default toast lifetime in ms. |
103
+ | `visibleToasts` | `number` (coerced) | `3` | Max simultaneously visible toasts. |
104
+ | `closeButton` | `boolean` (coerced) | `false` | Show an × close button on every toast. |
105
+ | `toastOptions` | `ToasterProps['toastOptions']` | `{ classes: { toast: 'group toast group-[.toaster]:bg-background …', description: 'group-[.toast]:text-muted-foreground', actionButton: 'group-[.toast]:bg-primary …', cancelButton: 'group-[.toast]:bg-muted …' } }` | Per-toast default options + shadcn class overrides. |
106
+ | `offset` | `ToasterProps['offset']` | `null` | Pixel offset from the viewport edge. |
107
+ | `dir` | `ToasterProps['dir']` | `'auto'` | Text direction (`auto` / `ltr` / `rtl`). |
108
+ | `class` | `ClassValue` (aliased input `userClass`) | `''` | Extra classes merged via `hlm('toaster group', …)` onto the outlet. |
109
+ | `style` | `Record<string, string>` (aliased input `userStyle`) | `{}` | Inline styles forwarded to `ngx-sonner-toaster`. |
110
+
111
+ No outputs, no methods. The host element gets computed class `toaster group` (+ your `class`).
112
+
113
+ > `toast()`, `toast.success()`, `toast.error()`, `toast.promise()`, `toast.dismiss()` etc. are imported from **`ngx-sonner`**, not from this subpath:
114
+ >
115
+ > ```ts
116
+ > import { toast } from 'ngx-sonner';
117
+ > ```
118
+
119
+ ## Examples
120
+
121
+ ### 1. Basic setup + plain toast
122
+
123
+ Add the outlet once at the app root.
124
+
125
+ ```ts
126
+ // app.component.ts
127
+ import { Component } from '@angular/core';
128
+ import { HlmToasterImports } from '@egose/shadcn-theme-ng/sonner';
129
+
130
+ @Component({
131
+ selector: 'app-root',
132
+ standalone: true,
133
+ imports: [...HlmToasterImports],
134
+ template: `
135
+ <hlm-toaster />
136
+ <router-outlet />
137
+ `,
138
+ })
139
+ export class AppComponent {}
140
+ ```
141
+
142
+ Fire a toast from anywhere:
143
+
144
+ ```ts
145
+ // demo.component.ts
146
+ import { Component } from '@angular/core';
147
+ import { toast } from 'ngx-sonner';
148
+ import { HlmButton } from '@egose/shadcn-theme-ng/button';
149
+
150
+ @Component({
151
+ selector: 'demo-basic',
152
+ standalone: true,
153
+ imports: [HlmButton],
154
+ template: `<button hlmBtn (click)="notify()">Show toast</button>`,
155
+ })
156
+ export class DemoBasic {
157
+ notify() {
158
+ toast('Event created', { description: 'Your event is now live.' });
159
+ }
160
+ }
161
+ ```
162
+
163
+ ### 2. Success / error / warning states
164
+
165
+ ```ts
166
+ // demo-states.component.ts
167
+ import { Component } from '@angular/core';
168
+ import { toast } from 'ngx-sonner';
169
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
170
+
171
+ @Component({
172
+ selector: 'demo-states',
173
+ standalone: true,
174
+ imports: [...HlmButtonImports],
175
+ template: `
176
+ <div class="flex gap-2">
177
+ <button hlmBtn variant="default" (click)="ok()">Success</button>
178
+ <button hlmBtn variant="destructive" (click)="fail()">Error</button>
179
+ <button hlmBtn variant="secondary" (click)="warn()">Warning</button>
180
+ <button hlmBtn variant="outline" (click)="info()">Info</button>
181
+ </div>
182
+ `,
183
+ })
184
+ export class DemoStates {
185
+ ok() {
186
+ toast.success('Profile saved', { description: 'Your changes are live.' });
187
+ }
188
+ fail() {
189
+ toast.error('Upload failed', { description: 'File exceeds 10 MB.' });
190
+ }
191
+ warn() {
192
+ toast.warning('Session expiring', { description: 'You will be logged out in 2 minutes.' });
193
+ }
194
+ info() {
195
+ toast.info('New version', { description: 'Refresh to get the latest UI.' });
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### 3. Positions, themes, rich colors
201
+
202
+ ```html
203
+ <!-- Top-center, system theme, colored toasts with close buttons -->
204
+ <hlm-toaster position="top-center" theme="system" [richColors]="true" [closeButton]="true" [expand]="true" />
205
+ ```
206
+
207
+ ```ts
208
+ import { Component, signal } from '@angular/core';
209
+ import { HlmToasterImports } from '@egose/shadcn-theme-ng/sonner';
210
+
211
+ @Component({
212
+ selector: 'demo-position',
213
+ standalone: true,
214
+ imports: [...HlmToasterImports],
215
+ template: ` <hlm-toaster [position]="position()" theme="dark" [visibleToasts]="5" /> `,
216
+ })
217
+ export class DemoPosition {
218
+ // 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
219
+ readonly position = signal<'top-center'>('top-center');
220
+ }
221
+ ```
222
+
223
+ ### 4. Actions, cancellation, and durations
224
+
225
+ ```ts
226
+ // demo-actions.component.ts
227
+ import { Component } from '@angular/core';
228
+ import { toast } from 'ngx-sonner';
229
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
230
+
231
+ @Component({
232
+ selector: 'demo-actions',
233
+ standalone: true,
234
+ imports: [...HlmButtonImports],
235
+ template: `
236
+ <div class="flex gap-2">
237
+ <button hlmBtn (click)="withAction()">Undoable delete</button>
238
+ <button hlmBtn variant="outline" (click)="sticky()">Sticky toast</button>
239
+ <button hlmBtn variant="ghost" (click)="dismissAll()">Dismiss all</button>
240
+ </div>
241
+ `,
242
+ })
243
+ export class DemoActions {
244
+ withAction() {
245
+ toast('Conversation archived', {
246
+ description: 'You can undo this within 10 seconds.',
247
+ duration: 10000,
248
+ action: { label: 'Undo', onClick: () => toast.success('Restored') },
249
+ cancel: { label: 'Dismiss', onClick: () => {} },
250
+ });
251
+ }
252
+ sticky() {
253
+ toast('Sync paused — offline', { duration: Infinity, closeButton: true });
254
+ }
255
+ dismissAll() {
256
+ toast.dismiss();
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### 5. Async / promise toasts (loading → success → error)
262
+
263
+ ```ts
264
+ // demo-promise.component.ts
265
+ import { Component, inject } from '@angular/core';
266
+ import { HttpClient } from '@angular/common/http';
267
+ import { toast } from 'ngx-sonner';
268
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
269
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
270
+
271
+ @Component({
272
+ selector: 'demo-promise',
273
+ standalone: true,
274
+ imports: [...HlmButtonImports, ...HlmSpinnerImports],
275
+ template: `<button hlmBtn (click)="save()">Save report</button>`,
276
+ })
277
+ export class DemoPromise {
278
+ private readonly http = inject(HttpClient);
279
+
280
+ save() {
281
+ const request = this.http.post('/api/reports', { title: 'Q3' }).toPromise();
282
+ toast.promise(request, {
283
+ loading: 'Saving report…',
284
+ success: 'Report saved',
285
+ error: 'Could not save report',
286
+ });
287
+ }
288
+ }
289
+ ```
290
+
291
+ Combine with a spinner while the promise is pending by driving `show` from a signal — see `@egose/shadcn-theme-ng/spinner`.
292
+
293
+ ### 6. Custom styling via `toastOptions`, `class`, and `style`
294
+
295
+ ```ts
296
+ // app.component.ts
297
+ import { Component } from '@angular/core';
298
+ import { HlmToasterImports } from '@egose/shadcn-theme-ng/sonner';
299
+
300
+ @Component({
301
+ selector: 'app-root',
302
+ standalone: true,
303
+ imports: [...HlmToasterImports],
304
+ template: ` <hlm-toaster class="my-toaster" [toastOptions]="toastOptions" [offset]="24" dir="ltr" /> `,
305
+ })
306
+ export class AppComponent {
307
+ readonly toastOptions = {
308
+ classes: {
309
+ toast: 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground',
310
+ description: 'group-[.toast]:text-muted-foreground',
311
+ actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
312
+ cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
313
+ },
314
+ // per-toast defaults can also set duration/style here
315
+ duration: 6000,
316
+ };
317
+ }
318
+ ```
319
+
320
+ Per-toast override (description + action button classes come from `toastOptions.classes` above unless overridden):
321
+
322
+ ```ts
323
+ toast('Custom toast', {
324
+ description: 'This toast uses the global toastOptions classes.',
325
+ className: 'border-primary',
326
+ style: { borderWidth: '2px' },
327
+ });
328
+ ```
329
+
330
+ ## Accessibility notes
331
+
332
+ - `ngx-sonner` renders toasts in an `aria-live` region so screen readers announce them; keep messages short and put detail in `description`.
333
+ - Always pair an icon-only trigger with visible text or an `aria-label` on the button that fires the toast.
334
+ - Do not use toasts as the only feedback for errors on form submit — also surface the error inline near the field (the `description` alone is not focusable).
335
+ - `duration: Infinity` toasts trap no focus but persist; always provide a `closeButton` or explicit dismiss action for sticky toasts.
336
+ - Hotkey defaults to `Alt+T` (`hotKey` input); document it if your app relies on keyboard flows, or override it to avoid conflicts.
337
+
338
+ ## Theming / CSS variables
339
+
340
+ The outlet merges `hlm('toaster group', userClass)` and ships `toastOptions.classes` mapped onto shadcn theme tokens (`bg-background`, `text-foreground`, `border-border`, `bg-primary`, `bg-muted`, …). It follows your shadcn CSS-variable theme automatically. Override via the `class` input (outlet position/layout) or `toastOptions.classes` (toast/description/action/cancel styling).
341
+
342
+ ## Related subpaths
343
+
344
+ - `@egose/shadcn-theme-ng/button` — trigger buttons for toast demos.
345
+ - `@egose/shadcn-theme-ng/spinner` — loading indicator to pair with `toast.promise()`.
346
+ - `@egose/shadcn-theme-ng/alert` / `@egose/shadcn-theme-ng/basic-alert` — persistent inline feedback (vs. transient toasts).
347
+ - `@egose/shadcn-theme-ng/utils` — `hlm()` merger used internally for outlet classes.
package/spinner/README.md CHANGED
@@ -1,3 +1,285 @@
1
- # Spinner Subpath
1
+ # Spinner (`@egose/shadcn-theme-ng/spinner`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/spinner` or `@egose/shadcn-theme-ng-tw/spinner`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
3
+ A loading spinner, equivalent to [shadcn/ui Spinner](https://ui.shadcn.com/docs/components/spinner). This subpath ships a single standalone component, `HlmSpinner`, that renders an animated [`@ng-icons`](https://www.ng-icons.dev/) `tablerLoader2` icon (spinning via `animate-spin`) inside a flex container with `role="status"`. There is no spartan-ng primitive underneath — it is a pure styled icon wrapper with `size`/`show` inputs.
4
+
5
+ > **Ships as:** `@egose/shadcn-theme-ng/spinner` and `@egose/shadcn-theme-ng-tw/spinner` (the `tw:`-prefixed Tailwind variant). See the [package README](../../README.md) for install steps, peer dependencies, Tailwind setup, and testing/release guidance. Do not publish this project directory independently.
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
+ No extra runtime install is needed — `@ng-icons/core` / `@ng-icons/tabler-icons` and `clsx`/`tailwind-merge` (via `utils`) arrive transitively. See the [package README](../../README.md) for the full peer-dependency table.
18
+
19
+ ## Imports
20
+
21
+ All public symbols are re-exported from `projects/spinner/src/public-api.ts`:
22
+
23
+ ```ts
24
+ import { HlmSpinner, HlmSpinnerImports, HlmSpinnerModule } from '@egose/shadcn-theme-ng/spinner';
25
+ // tw variant:
26
+ // import { HlmSpinner, HlmSpinnerImports, HlmSpinnerModule } from '@egose/shadcn-theme-ng-tw/spinner';
27
+ ```
28
+
29
+ Standalone-component usage (preferred):
30
+
31
+ ```ts
32
+ import { Component } from '@angular/core';
33
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
34
+
35
+ @Component({
36
+ selector: 'app-demo',
37
+ standalone: true,
38
+ imports: [...HlmSpinnerImports],
39
+ template: `<hlm-spinner />`,
40
+ })
41
+ export class DemoComponent {}
42
+ ```
43
+
44
+ NgModule usage:
45
+
46
+ ```ts
47
+ import { NgModule } from '@angular/core';
48
+ import { HlmSpinnerModule } from '@egose/shadcn-theme-ng/spinner';
49
+
50
+ @NgModule({ imports: [HlmSpinnerModule] })
51
+ export class FeatureModule {}
52
+ ```
53
+
54
+ | Symbol | Kind | Description |
55
+ | ------------------- | -------------------- | ---------------------------------------------- |
56
+ | `HlmSpinner` | Standalone component | The `<hlm-spinner>` loading indicator. |
57
+ | `HlmSpinnerImports` | `const` array | `[HlmSpinner]` — spread into `imports: [...]`. |
58
+ | `HlmSpinnerModule` | NgModule | Imports + re-exports `HlmSpinner`. |
59
+
60
+ ## Anatomy / Structure
61
+
62
+ ```html
63
+ <!-- host element: <hlm-spinner role="status" class="flex … | hidden"> -->
64
+ <hlm-spinner />
65
+ <hlm-spinner size="2rem" />
66
+ <hlm-spinner [show]="loading()" class="my-4" spinnerClass="text-primary" />
67
+ ```
68
+
69
+ The host carries `role="status"` and toggles `flex`/`hidden` from `show()`. The inner `<ng-icon>` always renders the fixed `tablerLoader2` glyph at `size()` with `animate-spin`.
70
+
71
+ ## API reference
72
+
73
+ ### `HlmSpinner` — selector `hlm-spinner` (component)
74
+
75
+ | Input | Type | Default | Description |
76
+ | -------------- | ------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
77
+ | `size` | `string` | `'1.5rem'` | Passed as `[size]` to the inner `<ng-icon>`; any CSS length (`'1rem'`, `'24px'`, …). |
78
+ | `show` | `boolean` | `true` | Toggles the host between `flex` (visible) and `hidden`. |
79
+ | `class` | `ClassValue` (aliased input `wrapperClass`) | `''` | Extra classes merged onto the **host container** via `hlm('flex-col items-center justify-center', show ? 'flex' : 'hidden', …)`. |
80
+ | `spinnerClass` | `ClassValue` | `''` | Extra classes merged onto the **inner icon** (`animate-spin`, primary color, inherit sizing). |
81
+
82
+ Public field:
83
+
84
+ | Member | Type | Description |
85
+ | ------ | --------------------- | --------------------------------------------------------------------------------------------------------- |
86
+ | `icon` | `tablerLoader2` glyph | Fixed icon data bound to the inner `<ng-icon [svg]>`. Not an input — the glyph cannot be swapped via API. |
87
+
88
+ No outputs, no methods. Host attributes: `role="status"`.
89
+
90
+ ## Examples
91
+
92
+ ### 1. Basic usage
93
+
94
+ ```ts
95
+ // demo-basic.component.ts
96
+ import { Component } from '@angular/core';
97
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
98
+
99
+ @Component({
100
+ selector: 'demo-basic',
101
+ standalone: true,
102
+ imports: [...HlmSpinnerImports],
103
+ template: `<hlm-spinner />`,
104
+ })
105
+ export class DemoBasic {}
106
+ ```
107
+
108
+ ```html
109
+ <hlm-spinner />
110
+ ```
111
+
112
+ ### 2. Sizes
113
+
114
+ `size` accepts any CSS length string passed straight to `<ng-icon [size]>`.
115
+
116
+ ```ts
117
+ // demo-sizes.component.ts
118
+ import { Component } from '@angular/core';
119
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
120
+
121
+ @Component({
122
+ selector: 'demo-sizes',
123
+ standalone: true,
124
+ imports: [...HlmSpinnerImports],
125
+ template: `
126
+ <div class="flex items-end gap-6">
127
+ <hlm-spinner size="1rem" />
128
+ <hlm-spinner size="1.5rem" />
129
+ <hlm-spinner size="2.5rem" />
130
+ <hlm-spinner size="48px" />
131
+ </div>
132
+ `,
133
+ })
134
+ export class DemoSizes {}
135
+ ```
136
+
137
+ ### 3. Async loading state with `show`
138
+
139
+ ```ts
140
+ // demo-async.component.ts
141
+ import { Component, inject, signal } from '@angular/core';
142
+ import { HttpClient } from '@angular/common/http';
143
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
144
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
145
+
146
+ @Component({
147
+ selector: 'demo-async',
148
+ standalone: true,
149
+ imports: [...HlmSpinnerImports, ...HlmButtonImports],
150
+ template: `
151
+ <button hlmBtn (click)="reload()" [disabled]="loading()">Reload</button>
152
+ <hlm-spinner [show]="loading()" class="my-4" />
153
+ @if (!loading()) {
154
+ <p hlmP>Data: {{ data() ?? '—' }}</p>
155
+ }
156
+ `,
157
+ })
158
+ export class DemoAsync {
159
+ private readonly http = inject(HttpClient);
160
+ readonly loading = signal(false);
161
+ readonly data = signal<string | null>(null);
162
+
163
+ reload() {
164
+ this.loading.set(true);
165
+ this.http.get('/api/status', { responseType: 'text' }).subscribe({
166
+ next: (v) => {
167
+ this.data.set(v);
168
+ this.loading.set(false);
169
+ },
170
+ error: () => {
171
+ this.data.set('error');
172
+ this.loading.set(false);
173
+ },
174
+ });
175
+ }
176
+ }
177
+ ```
178
+
179
+ > `show` hides with `display: none` (`hidden`) — the icon stays in the DOM. For conditional mounting instead, use `@if (loading()) { <hlm-spinner /> }`.
180
+
181
+ ### 4. Inside a button (inline submitting state)
182
+
183
+ The spinner inherits font sizing from `spinnerClass`; pair it with a disabled button.
184
+
185
+ ```ts
186
+ // demo-button.component.ts
187
+ import { Component, signal } from '@angular/core';
188
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
189
+ import { HlmButtonImports } from '@egose/shadcn-theme-ng/button';
190
+
191
+ @Component({
192
+ selector: 'demo-button',
193
+ standalone: true,
194
+ imports: [...HlmSpinnerImports, ...HlmButtonImports],
195
+ template: `
196
+ <button hlmBtn [disabled]="saving()">
197
+ @if (saving()) {
198
+ <hlm-spinner size="1rem" spinnerClass="[&>svg]:text-primary-foreground" />
199
+ }
200
+ {{ saving() ? 'Saving…' : 'Save changes' }}
201
+ </button>
202
+ `,
203
+ })
204
+ export class DemoButton {
205
+ readonly saving = signal(false);
206
+
207
+ save() {
208
+ this.saving.set(true);
209
+ setTimeout(() => this.saving.set(false), 1500);
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### 5. Custom colors and layout via `class` / `spinnerClass`
215
+
216
+ ```ts
217
+ // demo-styled.component.ts
218
+ import { Component } from '@angular/core';
219
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
220
+
221
+ @Component({
222
+ selector: 'demo-styled',
223
+ standalone: true,
224
+ imports: [...HlmSpinnerImports],
225
+ template: `
226
+ <!-- centered page loader -->
227
+ <hlm-spinner size="3rem" class="min-h-40 w-full" spinnerClass="[&>svg]:text-muted-foreground" />
228
+
229
+ <!-- destructive-tinted spinner -->
230
+ <hlm-spinner size="1.25rem" spinnerClass="[&>svg]:text-destructive" />
231
+ `,
232
+ })
233
+ export class DemoStyled {}
234
+ ```
235
+
236
+ `class` targets the outer flex container; `spinnerClass` targets the inner `<ng-icon>`/`<svg>`. Both merge through `hlm()` so Tailwind conflicts resolve.
237
+
238
+ ### 6. Overlay + empty-state composition
239
+
240
+ ```ts
241
+ // demo-overlay.component.ts
242
+ import { Component, signal } from '@angular/core';
243
+ import { HlmSpinnerImports } from '@egose/shadcn-theme-ng/spinner';
244
+ import { HlmCardImports } from '@egose/shadcn-theme-ng/card';
245
+
246
+ @Component({
247
+ selector: 'demo-overlay',
248
+ standalone: true,
249
+ imports: [...HlmSpinnerImports, ...HlmCardImports],
250
+ template: `
251
+ <section hlmCard class="relative min-h-48 p-6">
252
+ <h3 hlmH3>Dashboard</h3>
253
+ @if (loading()) {
254
+ <div class="absolute inset-0 flex items-center justify-center rounded-md bg-background/70 backdrop-blur-[1px]">
255
+ <hlm-spinner size="2rem" />
256
+ <span class="sr-only">Loading dashboard…</span>
257
+ </div>
258
+ } @else {
259
+ <p hlmP>Dashboard content goes here.</p>
260
+ }
261
+ </section>
262
+ `,
263
+ })
264
+ export class DemoOverlay {
265
+ readonly loading = signal(true);
266
+ }
267
+ ```
268
+
269
+ ## Accessibility notes
270
+
271
+ - The host has `role="status"` so assistive tech announces the loading state; add adjacent text (visible or `.sr-only`, e.g. “Loading…”) describing _what_ is loading — the icon alone conveys nothing to screen readers.
272
+ - Keep `show`-hidden spinners out of the tab order (they contain no focusable elements by default — do not add any).
273
+ - For actions that take >1s, disable the triggering control while `show` is true to prevent duplicate submissions.
274
+ - Avoid stacking multiple spinners in one view; a single `role="status"` region per async region is enough.
275
+
276
+ ## Theming / CSS variables
277
+
278
+ The icon inherits `text-primary` by default (`[&>svg]:text-primary`); override with `spinnerClass` (e.g. `spinnerClass="[&>svg]:text-muted-foreground"`). Sizing is fully controlled by `size`. No additional CSS variables.
279
+
280
+ ## Related subpaths
281
+
282
+ - `@egose/shadcn-theme-ng/button` — inline submitting states.
283
+ - `@egose/shadcn-theme-ng/skeleton` — content placeholders to pair with spinners.
284
+ - `@egose/shadcn-theme-ng/sonner` — notify on async completion (`toast.promise()`).
285
+ - `@egose/shadcn-theme-ng/card` — surfaces to overlay a spinner on.