@sonny-ui/core 0.1.0-alpha.1 → 0.1.0-alpha.11

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 (198) hide show
  1. package/README.md +101 -32
  2. package/fesm2022/sonny-ui-core.mjs +3031 -42
  3. package/fesm2022/sonny-ui-core.mjs.map +1 -1
  4. package/package.json +8 -5
  5. package/schematics/ng-add/schema.json +1 -1
  6. package/schematics/ng-generate/component/index.js +182 -1
  7. package/schematics/ng-generate/component/schema.json +2 -2
  8. package/src/lib/accordion/accordion.directives.spec.ts +173 -0
  9. package/src/lib/accordion/accordion.directives.ts +147 -0
  10. package/src/lib/accordion/index.ts +8 -0
  11. package/src/lib/alert/alert.directives.spec.ts +154 -0
  12. package/src/lib/alert/alert.directives.ts +70 -0
  13. package/src/lib/alert/alert.variants.ts +25 -0
  14. package/src/lib/alert/index.ts +6 -0
  15. package/src/lib/avatar/avatar.component.spec.ts +75 -0
  16. package/src/lib/avatar/avatar.component.ts +44 -0
  17. package/src/lib/avatar/avatar.variants.ts +26 -0
  18. package/src/lib/avatar/index.ts +2 -0
  19. package/src/lib/badge/badge.directive.spec.ts +74 -0
  20. package/src/lib/badge/badge.directive.ts +18 -0
  21. package/src/lib/badge/badge.variants.ts +29 -0
  22. package/src/lib/badge/index.ts +2 -0
  23. package/src/lib/breadcrumb/breadcrumb.directives.spec.ts +80 -0
  24. package/src/lib/breadcrumb/breadcrumb.directives.ts +84 -0
  25. package/src/lib/breadcrumb/index.ts +8 -0
  26. package/src/lib/button/button.directive.spec.ts +92 -0
  27. package/src/lib/button/button.directive.ts +29 -0
  28. package/src/lib/button/button.variants.ts +30 -0
  29. package/src/lib/button/index.ts +2 -0
  30. package/src/lib/button-group/button-group.directive.spec.ts +46 -0
  31. package/src/lib/button-group/button-group.directive.ts +20 -0
  32. package/src/lib/button-group/button-group.variants.ts +18 -0
  33. package/src/lib/button-group/index.ts +2 -0
  34. package/src/lib/calendar/calendar.component.spec.ts +105 -0
  35. package/src/lib/calendar/calendar.component.ts +231 -0
  36. package/src/lib/calendar/index.ts +1 -0
  37. package/src/lib/card/card.directives.spec.ts +104 -0
  38. package/src/lib/card/card.directives.ts +78 -0
  39. package/src/lib/card/card.variants.ts +28 -0
  40. package/src/lib/card/index.ts +9 -0
  41. package/src/lib/carousel/carousel.directives.spec.ts +85 -0
  42. package/src/lib/carousel/carousel.directives.ts +164 -0
  43. package/src/lib/carousel/index.ts +8 -0
  44. package/src/lib/chat-bubble/chat-bubble.directives.spec.ts +52 -0
  45. package/src/lib/chat-bubble/chat-bubble.directives.ts +102 -0
  46. package/src/lib/chat-bubble/index.ts +11 -0
  47. package/src/lib/checkbox/checkbox.directive.spec.ts +57 -0
  48. package/src/lib/checkbox/checkbox.directive.ts +17 -0
  49. package/src/lib/checkbox/checkbox.variants.ts +19 -0
  50. package/src/lib/checkbox/index.ts +2 -0
  51. package/src/lib/combobox/combobox.component.spec.ts +151 -0
  52. package/src/lib/combobox/combobox.component.ts +279 -0
  53. package/src/lib/combobox/combobox.variants.ts +19 -0
  54. package/src/lib/combobox/index.ts +2 -0
  55. package/src/lib/diff/diff.component.spec.ts +47 -0
  56. package/src/lib/diff/diff.component.ts +83 -0
  57. package/src/lib/diff/index.ts +1 -0
  58. package/src/lib/divider/divider.component.spec.ts +48 -0
  59. package/src/lib/divider/divider.component.ts +52 -0
  60. package/src/lib/divider/divider.variants.ts +22 -0
  61. package/src/lib/divider/index.ts +2 -0
  62. package/src/lib/dock/dock.directives.spec.ts +85 -0
  63. package/src/lib/dock/dock.directives.ts +83 -0
  64. package/src/lib/dock/index.ts +1 -0
  65. package/src/lib/drawer/drawer.directives.spec.ts +62 -0
  66. package/src/lib/drawer/drawer.directives.ts +83 -0
  67. package/src/lib/drawer/index.ts +8 -0
  68. package/src/lib/dropdown/dropdown.directives.spec.ts +106 -0
  69. package/src/lib/dropdown/dropdown.directives.ts +143 -0
  70. package/src/lib/dropdown/dropdown.variants.ts +27 -0
  71. package/src/lib/dropdown/index.ts +15 -0
  72. package/src/lib/fab/fab.directives.spec.ts +60 -0
  73. package/src/lib/fab/fab.directives.ts +80 -0
  74. package/src/lib/fab/index.ts +8 -0
  75. package/src/lib/fieldset/fieldset.directives.spec.ts +74 -0
  76. package/src/lib/fieldset/fieldset.directives.ts +52 -0
  77. package/src/lib/fieldset/fieldset.variants.ts +15 -0
  78. package/src/lib/fieldset/index.ts +6 -0
  79. package/src/lib/file-input/file-input.component.spec.ts +114 -0
  80. package/src/lib/file-input/file-input.component.ts +168 -0
  81. package/src/lib/file-input/file-input.variants.ts +25 -0
  82. package/src/lib/file-input/index.ts +6 -0
  83. package/src/lib/indicator/index.ts +6 -0
  84. package/src/lib/indicator/indicator.directives.spec.ts +64 -0
  85. package/src/lib/indicator/indicator.directives.ts +61 -0
  86. package/src/lib/input/index.ts +3 -0
  87. package/src/lib/input/input.directive.spec.ts +103 -0
  88. package/src/lib/input/input.directive.ts +26 -0
  89. package/src/lib/input/input.variants.ts +42 -0
  90. package/src/lib/input/label.directive.ts +17 -0
  91. package/src/lib/kbd/index.ts +2 -0
  92. package/src/lib/kbd/kbd.directive.spec.ts +42 -0
  93. package/src/lib/kbd/kbd.directive.ts +19 -0
  94. package/src/lib/kbd/kbd.variants.ts +19 -0
  95. package/src/lib/link/index.ts +2 -0
  96. package/src/lib/link/link.directive.spec.ts +41 -0
  97. package/src/lib/link/link.directive.ts +19 -0
  98. package/src/lib/link/link.variants.ts +20 -0
  99. package/src/lib/list/index.ts +8 -0
  100. package/src/lib/list/list.directives.spec.ts +65 -0
  101. package/src/lib/list/list.directives.ts +86 -0
  102. package/src/lib/loader/index.ts +2 -0
  103. package/src/lib/loader/loader.component.spec.ts +58 -0
  104. package/src/lib/loader/loader.component.ts +48 -0
  105. package/src/lib/loader/loader.variants.ts +21 -0
  106. package/src/lib/modal/dialog-ref.ts +19 -0
  107. package/src/lib/modal/dialog.directives.ts +90 -0
  108. package/src/lib/modal/dialog.service.spec.ts +52 -0
  109. package/src/lib/modal/dialog.service.ts +61 -0
  110. package/src/lib/modal/dialog.types.ts +16 -0
  111. package/src/lib/modal/index.ts +11 -0
  112. package/src/lib/navbar/index.ts +7 -0
  113. package/src/lib/navbar/navbar.directives.spec.ts +59 -0
  114. package/src/lib/navbar/navbar.directives.ts +61 -0
  115. package/src/lib/pagination/index.ts +6 -0
  116. package/src/lib/pagination/pagination.component.spec.ts +59 -0
  117. package/src/lib/pagination/pagination.component.ts +144 -0
  118. package/src/lib/pagination/pagination.variants.ts +31 -0
  119. package/src/lib/progress/index.ts +7 -0
  120. package/src/lib/progress/progress.component.spec.ts +117 -0
  121. package/src/lib/progress/progress.component.ts +65 -0
  122. package/src/lib/progress/progress.variants.ts +43 -0
  123. package/src/lib/radial-progress/index.ts +5 -0
  124. package/src/lib/radial-progress/radial-progress.component.spec.ts +41 -0
  125. package/src/lib/radial-progress/radial-progress.component.ts +71 -0
  126. package/src/lib/radio/index.ts +2 -0
  127. package/src/lib/radio/radio.directive.spec.ts +46 -0
  128. package/src/lib/radio/radio.directive.ts +17 -0
  129. package/src/lib/radio/radio.variants.ts +19 -0
  130. package/src/lib/rating/index.ts +2 -0
  131. package/src/lib/rating/rating.component.spec.ts +157 -0
  132. package/src/lib/rating/rating.component.ts +171 -0
  133. package/src/lib/rating/rating.variants.ts +20 -0
  134. package/src/lib/select/index.ts +2 -0
  135. package/src/lib/select/select.component.spec.ts +112 -0
  136. package/src/lib/select/select.component.ts +250 -0
  137. package/src/lib/select/select.variants.ts +19 -0
  138. package/src/lib/sheet/index.ts +10 -0
  139. package/src/lib/sheet/sheet-ref.ts +18 -0
  140. package/src/lib/sheet/sheet.component.spec.ts +67 -0
  141. package/src/lib/sheet/sheet.directives.ts +75 -0
  142. package/src/lib/sheet/sheet.service.ts +100 -0
  143. package/src/lib/sheet/sheet.types.ts +23 -0
  144. package/src/lib/skeleton/index.ts +2 -0
  145. package/src/lib/skeleton/skeleton.directive.spec.ts +63 -0
  146. package/src/lib/skeleton/skeleton.directive.ts +22 -0
  147. package/src/lib/skeleton/skeleton.variants.ts +27 -0
  148. package/src/lib/slider/index.ts +2 -0
  149. package/src/lib/slider/slider.component.spec.ts +104 -0
  150. package/src/lib/slider/slider.component.ts +188 -0
  151. package/src/lib/slider/slider.variants.ts +25 -0
  152. package/src/lib/stat/index.ts +8 -0
  153. package/src/lib/stat/stat.directives.spec.ts +60 -0
  154. package/src/lib/stat/stat.directives.ts +84 -0
  155. package/src/lib/status/index.ts +2 -0
  156. package/src/lib/status/status.directive.spec.ts +43 -0
  157. package/src/lib/status/status.directive.ts +38 -0
  158. package/src/lib/status/status.variants.ts +26 -0
  159. package/src/lib/steps/index.ts +8 -0
  160. package/src/lib/steps/steps.directives.spec.ts +52 -0
  161. package/src/lib/steps/steps.directives.ts +80 -0
  162. package/src/lib/switch/index.ts +2 -0
  163. package/src/lib/switch/switch.component.spec.ts +98 -0
  164. package/src/lib/switch/switch.component.ts +84 -0
  165. package/src/lib/switch/switch.variants.ts +31 -0
  166. package/src/lib/table/index.ts +12 -0
  167. package/src/lib/table/table.directives.spec.ts +111 -0
  168. package/src/lib/table/table.directives.ts +134 -0
  169. package/src/lib/table/table.variants.ts +36 -0
  170. package/src/lib/tabs/index.ts +8 -0
  171. package/src/lib/tabs/tabs.directives.spec.ts +136 -0
  172. package/src/lib/tabs/tabs.directives.ts +130 -0
  173. package/src/lib/tabs/tabs.variants.ts +17 -0
  174. package/src/lib/textarea/index.ts +7 -0
  175. package/src/lib/textarea/textarea.directive.spec.ts +84 -0
  176. package/src/lib/textarea/textarea.directive.ts +72 -0
  177. package/src/lib/textarea/textarea.variants.ts +34 -0
  178. package/src/lib/timeline/index.ts +11 -0
  179. package/src/lib/timeline/timeline.directives.spec.ts +55 -0
  180. package/src/lib/timeline/timeline.directives.ts +90 -0
  181. package/src/lib/toast/index.ts +3 -0
  182. package/src/lib/toast/toast.service.spec.ts +71 -0
  183. package/src/lib/toast/toast.service.ts +60 -0
  184. package/src/lib/toast/toast.variants.ts +38 -0
  185. package/src/lib/toast/toaster.component.spec.ts +38 -0
  186. package/src/lib/toast/toaster.component.ts +82 -0
  187. package/src/lib/toggle/index.ts +2 -0
  188. package/src/lib/toggle/toggle.directive.spec.ts +100 -0
  189. package/src/lib/toggle/toggle.directive.ts +73 -0
  190. package/src/lib/toggle/toggle.variants.ts +25 -0
  191. package/src/lib/tooltip/index.ts +2 -0
  192. package/src/lib/tooltip/tooltip.directive.spec.ts +113 -0
  193. package/src/lib/tooltip/tooltip.directive.ts +131 -0
  194. package/src/lib/tooltip/tooltip.variants.ts +20 -0
  195. package/src/lib/validator/index.ts +5 -0
  196. package/src/lib/validator/validator.directives.spec.ts +47 -0
  197. package/src/lib/validator/validator.directives.ts +52 -0
  198. package/types/sonny-ui-core.d.ts +878 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonny-ui/core",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.11",
4
4
  "description": "Angular UI component library inspired by shadcn/ui — signals, zoneless, Tailwind CSS v4",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -33,13 +33,14 @@
33
33
  "type": "git",
34
34
  "url": "https://github.com/coci-dev/sonny-ui.git"
35
35
  },
36
- "homepage": "https://github.com/coci-dev/sonny-ui#readme",
36
+ "homepage": "https://coci-dev.github.io/sonny-ui/",
37
37
  "bugs": {
38
38
  "url": "https://github.com/coci-dev/sonny-ui/issues"
39
39
  },
40
- "module": "fesm2022/sonny-ui-core.mjs",
41
- "typings": "types/sonny-ui-core.d.ts",
42
40
  "exports": {
41
+ "./styles/*": {
42
+ "default": "./src/styles/*"
43
+ },
43
44
  "./package.json": {
44
45
  "default": "./package.json"
45
46
  },
@@ -47,5 +48,7 @@
47
48
  "types": "./types/sonny-ui-core.d.ts",
48
49
  "default": "./fesm2022/sonny-ui-core.mjs"
49
50
  }
50
- }
51
+ },
52
+ "module": "fesm2022/sonny-ui-core.mjs",
53
+ "typings": "types/sonny-ui-core.d.ts"
51
54
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/schema",
3
- "id": "SonnyUIAddSchema",
3
+ "$id": "SonnyUIAddSchema",
4
4
  "title": "SonnyUI ng-add schematic",
5
5
  "type": "object",
6
6
  "properties": {
@@ -37,33 +37,66 @@ exports.generateComponent = generateComponent;
37
37
  const path = __importStar(require("path"));
38
38
  const AVAILABLE_COMPONENTS = [
39
39
  'accordion',
40
+ 'alert',
40
41
  'avatar',
41
42
  'badge',
42
43
  'breadcrumb',
43
44
  'button',
44
45
  'button-group',
46
+ 'calendar',
45
47
  'card',
48
+ 'carousel',
49
+ 'chat-bubble',
46
50
  'checkbox',
47
51
  'combobox',
52
+ 'diff',
53
+ 'divider',
54
+ 'dock',
55
+ 'drawer',
56
+ 'dropdown',
57
+ 'fab',
58
+ 'fieldset',
59
+ 'file-input',
60
+ 'indicator',
48
61
  'input',
62
+ 'kbd',
63
+ 'link',
64
+ 'list',
49
65
  'loader',
50
66
  'modal',
67
+ 'navbar',
68
+ 'pagination',
69
+ 'progress',
70
+ 'radial-progress',
51
71
  'radio',
72
+ 'rating',
52
73
  'select',
53
74
  'sheet',
54
75
  'skeleton',
55
76
  'slider',
77
+ 'stat',
78
+ 'status',
79
+ 'steps',
56
80
  'switch',
57
81
  'table',
58
82
  'tabs',
83
+ 'textarea',
84
+ 'timeline',
59
85
  'toast',
60
86
  'toggle',
87
+ 'tooltip',
88
+ 'validator',
61
89
  ];
62
90
  const COMPONENT_FILES = {
63
91
  accordion: [
64
92
  'accordion.directives.ts',
65
93
  'index.ts',
66
94
  ],
95
+ alert: [
96
+ 'alert.directives.ts',
97
+ 'alert.variants.ts',
98
+ 'index.ts',
99
+ ],
67
100
  avatar: [
68
101
  'avatar.component.ts',
69
102
  'avatar.variants.ts',
@@ -88,11 +121,23 @@ const COMPONENT_FILES = {
88
121
  'button-group.variants.ts',
89
122
  'index.ts',
90
123
  ],
124
+ calendar: [
125
+ 'calendar.component.ts',
126
+ 'index.ts',
127
+ ],
91
128
  card: [
92
129
  'card.directives.ts',
93
130
  'card.variants.ts',
94
131
  'index.ts',
95
132
  ],
133
+ carousel: [
134
+ 'carousel.directives.ts',
135
+ 'index.ts',
136
+ ],
137
+ 'chat-bubble': [
138
+ 'chat-bubble.directives.ts',
139
+ 'index.ts',
140
+ ],
96
141
  checkbox: [
97
142
  'checkbox.directive.ts',
98
143
  'checkbox.variants.ts',
@@ -103,12 +148,66 @@ const COMPONENT_FILES = {
103
148
  'combobox.variants.ts',
104
149
  'index.ts',
105
150
  ],
151
+ diff: [
152
+ 'diff.component.ts',
153
+ 'index.ts',
154
+ ],
155
+ divider: [
156
+ 'divider.component.ts',
157
+ 'divider.variants.ts',
158
+ 'index.ts',
159
+ ],
160
+ dock: [
161
+ 'dock.directives.ts',
162
+ 'index.ts',
163
+ ],
164
+ drawer: [
165
+ 'drawer.directives.ts',
166
+ 'index.ts',
167
+ ],
168
+ dropdown: [
169
+ 'dropdown.directives.ts',
170
+ 'dropdown.variants.ts',
171
+ 'index.ts',
172
+ ],
173
+ fab: [
174
+ 'fab.directives.ts',
175
+ 'index.ts',
176
+ ],
177
+ fieldset: [
178
+ 'fieldset.directives.ts',
179
+ 'fieldset.variants.ts',
180
+ 'index.ts',
181
+ ],
182
+ 'file-input': [
183
+ 'file-input.component.ts',
184
+ 'file-input.variants.ts',
185
+ 'index.ts',
186
+ ],
187
+ indicator: [
188
+ 'indicator.directives.ts',
189
+ 'index.ts',
190
+ ],
106
191
  input: [
107
192
  'input.directive.ts',
108
193
  'input.variants.ts',
109
194
  'label.directive.ts',
110
195
  'index.ts',
111
196
  ],
197
+ kbd: [
198
+ 'kbd.directive.ts',
199
+ 'kbd.variants.ts',
200
+ 'index.ts',
201
+ ],
202
+ link: [
203
+ 'link.directive.ts',
204
+ 'link.variants.ts',
205
+ 'index.ts',
206
+ ],
207
+ list: [
208
+ 'list.directives.ts',
209
+ 'index.ts',
210
+ ],
112
211
  loader: [
113
212
  'loader.component.ts',
114
213
  'loader.variants.ts',
@@ -121,11 +220,34 @@ const COMPONENT_FILES = {
121
220
  'dialog.directives.ts',
122
221
  'index.ts',
123
222
  ],
223
+ navbar: [
224
+ 'navbar.directives.ts',
225
+ 'index.ts',
226
+ ],
227
+ pagination: [
228
+ 'pagination.component.ts',
229
+ 'pagination.variants.ts',
230
+ 'index.ts',
231
+ ],
232
+ progress: [
233
+ 'progress.component.ts',
234
+ 'progress.variants.ts',
235
+ 'index.ts',
236
+ ],
237
+ 'radial-progress': [
238
+ 'radial-progress.component.ts',
239
+ 'index.ts',
240
+ ],
124
241
  radio: [
125
242
  'radio.directive.ts',
126
243
  'radio.variants.ts',
127
244
  'index.ts',
128
245
  ],
246
+ rating: [
247
+ 'rating.component.ts',
248
+ 'rating.variants.ts',
249
+ 'index.ts',
250
+ ],
129
251
  select: [
130
252
  'select.component.ts',
131
253
  'select.variants.ts',
@@ -148,6 +270,19 @@ const COMPONENT_FILES = {
148
270
  'slider.variants.ts',
149
271
  'index.ts',
150
272
  ],
273
+ stat: [
274
+ 'stat.directives.ts',
275
+ 'index.ts',
276
+ ],
277
+ status: [
278
+ 'status.directive.ts',
279
+ 'status.variants.ts',
280
+ 'index.ts',
281
+ ],
282
+ steps: [
283
+ 'steps.directives.ts',
284
+ 'index.ts',
285
+ ],
151
286
  switch: [
152
287
  'switch.component.ts',
153
288
  'switch.variants.ts',
@@ -163,6 +298,15 @@ const COMPONENT_FILES = {
163
298
  'tabs.variants.ts',
164
299
  'index.ts',
165
300
  ],
301
+ textarea: [
302
+ 'textarea.directive.ts',
303
+ 'textarea.variants.ts',
304
+ 'index.ts',
305
+ ],
306
+ timeline: [
307
+ 'timeline.directives.ts',
308
+ 'index.ts',
309
+ ],
166
310
  toast: [
167
311
  'toast.service.ts',
168
312
  'toast.variants.ts',
@@ -174,30 +318,67 @@ const COMPONENT_FILES = {
174
318
  'toggle.variants.ts',
175
319
  'index.ts',
176
320
  ],
321
+ tooltip: [
322
+ 'tooltip.directive.ts',
323
+ 'tooltip.variants.ts',
324
+ 'index.ts',
325
+ ],
326
+ validator: [
327
+ 'validator.directives.ts',
328
+ 'index.ts',
329
+ ],
177
330
  };
178
331
  const COMPONENT_SPEC_FILES = {
179
332
  accordion: ['accordion.directives.spec.ts'],
333
+ alert: ['alert.directives.spec.ts'],
180
334
  avatar: ['avatar.component.spec.ts'],
181
335
  badge: ['badge.directive.spec.ts'],
182
336
  breadcrumb: ['breadcrumb.directives.spec.ts'],
183
337
  button: ['button.directive.spec.ts'],
184
338
  'button-group': ['button-group.directive.spec.ts'],
339
+ calendar: ['calendar.component.spec.ts'],
185
340
  card: ['card.directives.spec.ts'],
341
+ carousel: ['carousel.directives.spec.ts'],
342
+ 'chat-bubble': ['chat-bubble.directives.spec.ts'],
186
343
  checkbox: ['checkbox.directive.spec.ts'],
187
344
  combobox: ['combobox.component.spec.ts'],
345
+ diff: ['diff.component.spec.ts'],
346
+ divider: ['divider.component.spec.ts'],
347
+ dock: ['dock.directives.spec.ts'],
348
+ drawer: ['drawer.directives.spec.ts'],
349
+ dropdown: ['dropdown.directives.spec.ts'],
350
+ fab: ['fab.directives.spec.ts'],
351
+ fieldset: ['fieldset.directives.spec.ts'],
352
+ 'file-input': ['file-input.component.spec.ts'],
353
+ indicator: ['indicator.directives.spec.ts'],
188
354
  input: ['input.directive.spec.ts'],
355
+ kbd: ['kbd.directive.spec.ts'],
356
+ link: ['link.directive.spec.ts'],
357
+ list: ['list.directives.spec.ts'],
189
358
  loader: ['loader.component.spec.ts'],
190
359
  modal: ['dialog.service.spec.ts'],
360
+ navbar: ['navbar.directives.spec.ts'],
361
+ pagination: ['pagination.component.spec.ts'],
362
+ progress: ['progress.component.spec.ts'],
363
+ 'radial-progress': ['radial-progress.component.spec.ts'],
191
364
  radio: ['radio.directive.spec.ts'],
365
+ rating: ['rating.component.spec.ts'],
192
366
  select: ['select.component.spec.ts'],
193
367
  sheet: ['sheet.component.spec.ts'],
194
368
  skeleton: ['skeleton.directive.spec.ts'],
195
369
  slider: ['slider.component.spec.ts'],
370
+ stat: ['stat.directives.spec.ts'],
371
+ status: ['status.directive.spec.ts'],
372
+ steps: ['steps.directives.spec.ts'],
196
373
  switch: ['switch.component.spec.ts'],
197
374
  table: ['table.directives.spec.ts'],
198
375
  tabs: ['tabs.directives.spec.ts'],
376
+ textarea: ['textarea.directive.spec.ts'],
377
+ timeline: ['timeline.directives.spec.ts'],
199
378
  toast: ['toast.service.spec.ts'],
200
379
  toggle: ['toggle.directive.spec.ts'],
380
+ tooltip: ['tooltip.directive.spec.ts'],
381
+ validator: ['validator.directives.spec.ts'],
201
382
  };
202
383
  function generateComponent(options) {
203
384
  return (tree, context) => {
@@ -236,7 +417,7 @@ export function cn(...inputs: ClassValue[]): string {
236
417
  const fs = require('fs');
237
418
  let content = fs.readFileSync(sourcePath, 'utf-8');
238
419
  // Rewrite imports to use local cn utility
239
- content = content.replace(/from ['"]\.\.\/core\/utils\/cn['"]/g, `from '../../utils/cn'`);
420
+ content = content.replace(/from ['"]\.\.\/core\/utils\/cn['"]/g, `from '../utils/cn'`);
240
421
  // Rewrite prefix if custom
241
422
  if (options.prefix && options.prefix !== 'sny') {
242
423
  content = content.replace(/sny/g, options.prefix);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/schema",
3
- "id": "SonnyUIGenerateComponentSchema",
3
+ "$id": "SonnyUIGenerateComponentSchema",
4
4
  "title": "SonnyUI component generator (copy-paste style)",
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "name": {
8
8
  "type": "string",
9
- "description": "The component to copy (accordion, avatar, badge, breadcrumb, button, button-group, card, checkbox, combobox, input, loader, modal, radio, select, sheet, skeleton, slider, switch, table, tabs, toast, toggle).",
9
+ "description": "The component to copy (accordion, alert, avatar, badge, breadcrumb, button, button-group, calendar, card, carousel, chat-bubble, checkbox, combobox, diff, divider, dock, drawer, dropdown, fab, fieldset, file-input, indicator, input, kbd, link, list, loader, modal, navbar, pagination, progress, radial-progress, radio, rating, select, sheet, skeleton, slider, stat, status, steps, switch, table, tabs, textarea, timeline, toast, toggle, tooltip, validator).",
10
10
  "$default": {
11
11
  "$source": "argv",
12
12
  "index": 0
@@ -0,0 +1,173 @@
1
+ import { Component, signal, viewChild } from '@angular/core';
2
+ import { TestBed, ComponentFixture } from '@angular/core/testing';
3
+ import {
4
+ SnyAccordionDirective,
5
+ SnyAccordionItemDirective,
6
+ SnyAccordionTriggerDirective,
7
+ SnyAccordionContentDirective,
8
+ } from './accordion.directives';
9
+
10
+ @Component({
11
+ standalone: true,
12
+ imports: [
13
+ SnyAccordionDirective,
14
+ SnyAccordionItemDirective,
15
+ SnyAccordionTriggerDirective,
16
+ SnyAccordionContentDirective,
17
+ ],
18
+ template: `
19
+ <div snyAccordion [multi]="multi()">
20
+ <div snyAccordionItem value="item-1">
21
+ <button snyAccordionTrigger>Item 1</button>
22
+ <div snyAccordionContent><div>Content 1</div></div>
23
+ </div>
24
+ <div snyAccordionItem value="item-2">
25
+ <button snyAccordionTrigger>Item 2</button>
26
+ <div snyAccordionContent><div>Content 2</div></div>
27
+ </div>
28
+ </div>
29
+ `,
30
+ })
31
+ class TestHostComponent {
32
+ multi = signal(false);
33
+ }
34
+
35
+ describe('Accordion Directives', () => {
36
+ let fixture: ComponentFixture<TestHostComponent>;
37
+
38
+ beforeEach(async () => {
39
+ await TestBed.configureTestingModule({
40
+ imports: [TestHostComponent],
41
+ }).compileComponents();
42
+
43
+ fixture = TestBed.createComponent(TestHostComponent);
44
+ fixture.detectChanges();
45
+ });
46
+
47
+ it('should render accordion items', () => {
48
+ const items = fixture.nativeElement.querySelectorAll('[snyAccordionItem]');
49
+ expect(items.length).toBe(2);
50
+ });
51
+
52
+ it('should toggle item on trigger click', () => {
53
+ const trigger = fixture.nativeElement.querySelector('[snyAccordionTrigger]');
54
+ trigger.click();
55
+ fixture.detectChanges();
56
+ expect(trigger.getAttribute('aria-expanded')).toBe('true');
57
+ });
58
+
59
+ it('should close previous item in single mode', () => {
60
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
61
+ triggers[0].click();
62
+ fixture.detectChanges();
63
+ expect(triggers[0].getAttribute('aria-expanded')).toBe('true');
64
+
65
+ triggers[1].click();
66
+ fixture.detectChanges();
67
+ expect(triggers[0].getAttribute('aria-expanded')).toBe('false');
68
+ expect(triggers[1].getAttribute('aria-expanded')).toBe('true');
69
+ });
70
+
71
+ it('should allow multiple open in multi mode', () => {
72
+ fixture.componentInstance.multi.set(true);
73
+ fixture.detectChanges();
74
+
75
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
76
+ triggers[0].click();
77
+ fixture.detectChanges();
78
+ triggers[1].click();
79
+ fixture.detectChanges();
80
+
81
+ expect(triggers[0].getAttribute('aria-expanded')).toBe('true');
82
+ expect(triggers[1].getAttribute('aria-expanded')).toBe('true');
83
+ });
84
+
85
+ it('should apply content visibility classes', () => {
86
+ const content = fixture.nativeElement.querySelector('[snyAccordionContent]');
87
+ expect(content.className).toContain('grid-rows-[0fr]');
88
+
89
+ const trigger = fixture.nativeElement.querySelector('[snyAccordionTrigger]');
90
+ trigger.click();
91
+ fixture.detectChanges();
92
+
93
+ expect(content.className).toContain('grid-rows-[1fr]');
94
+ });
95
+
96
+ it('should move focus with ArrowDown', () => {
97
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
98
+ (triggers[0] as HTMLElement).focus();
99
+ triggers[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }));
100
+ fixture.detectChanges();
101
+ const updated = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
102
+ expect(document.activeElement).toBe(updated[1]);
103
+ });
104
+
105
+ it('should move focus with ArrowUp', () => {
106
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
107
+ (triggers[1] as HTMLElement).focus();
108
+ triggers[1].dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true }));
109
+ fixture.detectChanges();
110
+ const updated = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
111
+ expect(document.activeElement).toBe(updated[0]);
112
+ });
113
+
114
+ it('should move focus to first with Home', () => {
115
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
116
+ (triggers[1] as HTMLElement).focus();
117
+ triggers[1].dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', bubbles: true }));
118
+ fixture.detectChanges();
119
+ const updated = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
120
+ expect(document.activeElement).toBe(updated[0]);
121
+ });
122
+
123
+ it('should move focus to last with End', () => {
124
+ const triggers = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
125
+ (triggers[0] as HTMLElement).focus();
126
+ triggers[0].dispatchEvent(new KeyboardEvent('keydown', { key: 'End', bubbles: true }));
127
+ fixture.detectChanges();
128
+ const updated = fixture.nativeElement.querySelectorAll('[snyAccordionTrigger]');
129
+ expect(document.activeElement).toBe(updated[1]);
130
+ });
131
+ });
132
+
133
+ @Component({
134
+ standalone: true,
135
+ imports: [
136
+ SnyAccordionDirective,
137
+ SnyAccordionItemDirective,
138
+ SnyAccordionTriggerDirective,
139
+ SnyAccordionContentDirective,
140
+ ],
141
+ template: `
142
+ <div snyAccordion #a="snyAccordion">
143
+ <div snyAccordionItem #i="snyAccordionItem" value="item-1">
144
+ <button snyAccordionTrigger>Item 1</button>
145
+ <div snyAccordionContent><div>Content 1</div></div>
146
+ </div>
147
+ </div>
148
+ `,
149
+ })
150
+ class ExportAsHost {
151
+ accordion = viewChild<SnyAccordionDirective>('a');
152
+ item = viewChild<SnyAccordionItemDirective>('i');
153
+ }
154
+
155
+ describe('Accordion exportAs', () => {
156
+ it('should expose snyAccordion via template ref', async () => {
157
+ await TestBed.configureTestingModule({ imports: [ExportAsHost] }).compileComponents();
158
+ const fixture = TestBed.createComponent(ExportAsHost);
159
+ fixture.detectChanges();
160
+ const ref = fixture.componentInstance.accordion();
161
+ expect(ref).toBeTruthy();
162
+ expect(ref!.multi()).toBe(false);
163
+ });
164
+
165
+ it('should expose snyAccordionItem via template ref', async () => {
166
+ await TestBed.configureTestingModule({ imports: [ExportAsHost] }).compileComponents();
167
+ const fixture = TestBed.createComponent(ExportAsHost);
168
+ fixture.detectChanges();
169
+ const ref = fixture.componentInstance.item();
170
+ expect(ref).toBeTruthy();
171
+ expect(ref!.isOpen()).toBe(false);
172
+ });
173
+ });
@@ -0,0 +1,147 @@
1
+ import { Directive, ElementRef, computed, inject, input, signal, InjectionToken } from '@angular/core';
2
+ import { cn } from '../core/utils/cn';
3
+
4
+ export const SNY_ACCORDION = new InjectionToken<SnyAccordionDirective>('SnyAccordion');
5
+ export const SNY_ACCORDION_ITEM = new InjectionToken<SnyAccordionItemDirective>('SnyAccordionItem');
6
+
7
+ @Directive({
8
+ selector: '[snyAccordion]',
9
+ standalone: true,
10
+ exportAs: 'snyAccordion',
11
+ providers: [{ provide: SNY_ACCORDION, useExisting: SnyAccordionDirective }],
12
+ host: {
13
+ '[class]': 'computedClass()',
14
+ '(keydown)': 'onKeydown($event)',
15
+ },
16
+ })
17
+ export class SnyAccordionDirective {
18
+ readonly multi = input(false);
19
+ readonly class = input<string>('');
20
+
21
+ private readonly elRef = inject(ElementRef);
22
+ private readonly _openItems = signal(new Set<string>());
23
+
24
+ protected readonly computedClass = computed(() =>
25
+ cn('divide-y divide-border', this.class())
26
+ );
27
+
28
+ isOpen(id: string): boolean {
29
+ return this._openItems().has(id);
30
+ }
31
+
32
+ toggle(id: string): void {
33
+ this._openItems.update(set => {
34
+ const next = new Set(set);
35
+ if (next.has(id)) {
36
+ next.delete(id);
37
+ } else {
38
+ if (!this.multi()) next.clear();
39
+ next.add(id);
40
+ }
41
+ return next;
42
+ });
43
+ }
44
+
45
+ onKeydown(event: KeyboardEvent): void {
46
+ const target = event.target as HTMLElement;
47
+ if (!target.hasAttribute('snyaccordiontrigger') && !target.closest('[snyAccordionTrigger]')) return;
48
+
49
+ const triggers = Array.from(
50
+ (this.elRef.nativeElement as HTMLElement).querySelectorAll<HTMLElement>('[snyAccordionTrigger]')
51
+ );
52
+ if (triggers.length === 0) return;
53
+
54
+ const currentIndex = triggers.indexOf(target.closest('[snyAccordionTrigger]') as HTMLElement || target);
55
+ if (currentIndex === -1) return;
56
+
57
+ let nextIndex: number | null = null;
58
+ switch (event.key) {
59
+ case 'ArrowDown':
60
+ event.preventDefault();
61
+ nextIndex = (currentIndex + 1) % triggers.length;
62
+ break;
63
+ case 'ArrowUp':
64
+ event.preventDefault();
65
+ nextIndex = (currentIndex - 1 + triggers.length) % triggers.length;
66
+ break;
67
+ case 'Home':
68
+ event.preventDefault();
69
+ nextIndex = 0;
70
+ break;
71
+ case 'End':
72
+ event.preventDefault();
73
+ nextIndex = triggers.length - 1;
74
+ break;
75
+ }
76
+ if (nextIndex !== null) {
77
+ triggers[nextIndex].focus();
78
+ }
79
+ }
80
+ }
81
+
82
+ @Directive({
83
+ selector: '[snyAccordionItem]',
84
+ standalone: true,
85
+ exportAs: 'snyAccordionItem',
86
+ providers: [{ provide: SNY_ACCORDION_ITEM, useExisting: SnyAccordionItemDirective }],
87
+ host: { '[class]': 'computedClass()' },
88
+ })
89
+ export class SnyAccordionItemDirective {
90
+ readonly value = input.required<string>();
91
+ readonly class = input<string>('');
92
+ private readonly accordion = inject(SNY_ACCORDION);
93
+
94
+ readonly isOpen = computed(() => this.accordion.isOpen(this.value()));
95
+
96
+ protected readonly computedClass = computed(() =>
97
+ cn('', this.class())
98
+ );
99
+
100
+ toggle(): void {
101
+ this.accordion.toggle(this.value());
102
+ }
103
+ }
104
+
105
+ @Directive({
106
+ selector: '[snyAccordionTrigger]',
107
+ standalone: true,
108
+ host: {
109
+ '[class]': 'computedClass()',
110
+ '[attr.aria-expanded]': 'item.isOpen()',
111
+ 'tabindex': '0',
112
+ '(click)': 'item.toggle()',
113
+ },
114
+ })
115
+ export class SnyAccordionTriggerDirective {
116
+ readonly class = input<string>('');
117
+ readonly item = inject(SNY_ACCORDION_ITEM);
118
+
119
+ protected readonly computedClass = computed(() =>
120
+ cn(
121
+ 'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline cursor-pointer [&>svg]:transition-transform',
122
+ this.item.isOpen() && '[&>svg]:rotate-180',
123
+ this.class()
124
+ )
125
+ );
126
+ }
127
+
128
+ @Directive({
129
+ selector: '[snyAccordionContent]',
130
+ standalone: true,
131
+ host: {
132
+ '[class]': 'computedClass()',
133
+ role: 'region',
134
+ },
135
+ })
136
+ export class SnyAccordionContentDirective {
137
+ readonly class = input<string>('');
138
+ readonly item = inject(SNY_ACCORDION_ITEM);
139
+
140
+ protected readonly computedClass = computed(() =>
141
+ cn(
142
+ 'grid transition-all duration-200',
143
+ this.item.isOpen() ? 'grid-rows-[1fr] opacity-100 pb-4' : 'grid-rows-[0fr] opacity-0 overflow-hidden',
144
+ this.class()
145
+ )
146
+ );
147
+ }
@@ -0,0 +1,8 @@
1
+ export {
2
+ SnyAccordionDirective,
3
+ SnyAccordionItemDirective,
4
+ SnyAccordionTriggerDirective,
5
+ SnyAccordionContentDirective,
6
+ SNY_ACCORDION,
7
+ SNY_ACCORDION_ITEM,
8
+ } from './accordion.directives';