@shadng/sng-ui 1.0.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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/cli/sng-ui.js +331 -0
  4. package/ng-package.json +29 -0
  5. package/package.json +64 -0
  6. package/registry.json +72 -0
  7. package/src/lib/accordion/cn.ts +6 -0
  8. package/src/lib/accordion/index.ts +18 -0
  9. package/src/lib/accordion/sng-accordion-content.ts +131 -0
  10. package/src/lib/accordion/sng-accordion-item.ts +299 -0
  11. package/src/lib/accordion/sng-accordion-trigger.ts +137 -0
  12. package/src/lib/accordion/sng-accordion.ts +118 -0
  13. package/src/lib/accordion/sng-accordion.types.ts +82 -0
  14. package/src/lib/alert/cn.ts +6 -0
  15. package/src/lib/alert/index.ts +3 -0
  16. package/src/lib/alert/sng-alert-description.ts +49 -0
  17. package/src/lib/alert/sng-alert-title.ts +46 -0
  18. package/src/lib/alert/sng-alert.ts +48 -0
  19. package/src/lib/avatar/cn.ts +6 -0
  20. package/src/lib/avatar/index.ts +3 -0
  21. package/src/lib/avatar/sng-avatar-fallback.ts +50 -0
  22. package/src/lib/avatar/sng-avatar-image.ts +73 -0
  23. package/src/lib/avatar/sng-avatar.ts +60 -0
  24. package/src/lib/badge/cn.ts +6 -0
  25. package/src/lib/badge/index.ts +1 -0
  26. package/src/lib/badge/sng-badge.ts +36 -0
  27. package/src/lib/breadcrumb/cn.ts +6 -0
  28. package/src/lib/breadcrumb/index.ts +7 -0
  29. package/src/lib/breadcrumb/sng-breadcrumb-ellipsis.ts +61 -0
  30. package/src/lib/breadcrumb/sng-breadcrumb-item.ts +47 -0
  31. package/src/lib/breadcrumb/sng-breadcrumb-link.ts +43 -0
  32. package/src/lib/breadcrumb/sng-breadcrumb-list.ts +42 -0
  33. package/src/lib/breadcrumb/sng-breadcrumb-page.ts +44 -0
  34. package/src/lib/breadcrumb/sng-breadcrumb-separator.ts +60 -0
  35. package/src/lib/breadcrumb/sng-breadcrumb.ts +52 -0
  36. package/src/lib/button/cn.ts +6 -0
  37. package/src/lib/button/index.ts +2 -0
  38. package/src/lib/button/sng-button.ts +264 -0
  39. package/src/lib/calendar/cn.ts +6 -0
  40. package/src/lib/calendar/index.ts +2 -0
  41. package/src/lib/calendar/sng-calendar.ts +753 -0
  42. package/src/lib/card/cn.ts +6 -0
  43. package/src/lib/card/index.ts +6 -0
  44. package/src/lib/card/sng-card-content.ts +36 -0
  45. package/src/lib/card/sng-card-description.ts +38 -0
  46. package/src/lib/card/sng-card-footer.ts +34 -0
  47. package/src/lib/card/sng-card-header.ts +34 -0
  48. package/src/lib/card/sng-card-title.ts +48 -0
  49. package/src/lib/card/sng-card.ts +43 -0
  50. package/src/lib/carousel/cn.ts +6 -0
  51. package/src/lib/carousel/index.ts +18 -0
  52. package/src/lib/carousel/sng-carousel.ts +526 -0
  53. package/src/lib/checkbox/cn.ts +6 -0
  54. package/src/lib/checkbox/index.ts +1 -0
  55. package/src/lib/checkbox/sng-checkbox.ts +154 -0
  56. package/src/lib/code-block/cn.ts +6 -0
  57. package/src/lib/code-block/index.ts +1 -0
  58. package/src/lib/code-block/sng-code-block.ts +296 -0
  59. package/src/lib/dialog/cn.ts +6 -0
  60. package/src/lib/dialog/index.ts +37 -0
  61. package/src/lib/dialog/sng-dialog-close.ts +76 -0
  62. package/src/lib/dialog/sng-dialog-content.ts +132 -0
  63. package/src/lib/dialog/sng-dialog-description.ts +36 -0
  64. package/src/lib/dialog/sng-dialog-footer.ts +39 -0
  65. package/src/lib/dialog/sng-dialog-header.ts +39 -0
  66. package/src/lib/dialog/sng-dialog-title.ts +52 -0
  67. package/src/lib/dialog/sng-dialog.service.ts +222 -0
  68. package/src/lib/dialog/sng-dialog.ts +224 -0
  69. package/src/lib/drawer/cn.ts +6 -0
  70. package/src/lib/drawer/index.ts +36 -0
  71. package/src/lib/drawer/sng-drawer-close.ts +28 -0
  72. package/src/lib/drawer/sng-drawer-content.ts +135 -0
  73. package/src/lib/drawer/sng-drawer-description.ts +29 -0
  74. package/src/lib/drawer/sng-drawer-footer.ts +34 -0
  75. package/src/lib/drawer/sng-drawer-handle.ts +30 -0
  76. package/src/lib/drawer/sng-drawer-header.ts +30 -0
  77. package/src/lib/drawer/sng-drawer-title.ts +27 -0
  78. package/src/lib/drawer/sng-drawer-trigger.ts +21 -0
  79. package/src/lib/drawer/sng-drawer-wrapper.ts +27 -0
  80. package/src/lib/drawer/sng-drawer.ts +166 -0
  81. package/src/lib/file-input/cn.ts +6 -0
  82. package/src/lib/file-input/index.ts +1 -0
  83. package/src/lib/file-input/sng-file-input.ts +288 -0
  84. package/src/lib/hover-card/cn.ts +6 -0
  85. package/src/lib/hover-card/index.ts +3 -0
  86. package/src/lib/hover-card/sng-hover-card-content.ts +100 -0
  87. package/src/lib/hover-card/sng-hover-card-trigger.ts +43 -0
  88. package/src/lib/hover-card/sng-hover-card.ts +246 -0
  89. package/src/lib/input/cn.ts +6 -0
  90. package/src/lib/input/index.ts +1 -0
  91. package/src/lib/input/sng-input.ts +160 -0
  92. package/src/lib/layout/cn.ts +6 -0
  93. package/src/lib/layout/index.ts +98 -0
  94. package/src/lib/layout/sng-layout-footer.ts +37 -0
  95. package/src/lib/layout/sng-layout-header.ts +38 -0
  96. package/src/lib/layout/sng-layout-sidebar-content.ts +149 -0
  97. package/src/lib/layout/sng-layout-sidebar-footer.ts +54 -0
  98. package/src/lib/layout/sng-layout-sidebar-group-action.ts +67 -0
  99. package/src/lib/layout/sng-layout-sidebar-group-content.ts +41 -0
  100. package/src/lib/layout/sng-layout-sidebar-group-label.ts +53 -0
  101. package/src/lib/layout/sng-layout-sidebar-group.ts +41 -0
  102. package/src/lib/layout/sng-layout-sidebar-header.ts +54 -0
  103. package/src/lib/layout/sng-layout-sidebar-input.ts +112 -0
  104. package/src/lib/layout/sng-layout-sidebar-inset.ts +45 -0
  105. package/src/lib/layout/sng-layout-sidebar-menu-action.ts +84 -0
  106. package/src/lib/layout/sng-layout-sidebar-menu-badge.ts +47 -0
  107. package/src/lib/layout/sng-layout-sidebar-menu-button.ts +160 -0
  108. package/src/lib/layout/sng-layout-sidebar-menu-item.ts +40 -0
  109. package/src/lib/layout/sng-layout-sidebar-menu-skeleton.ts +71 -0
  110. package/src/lib/layout/sng-layout-sidebar-menu-sub-button.ts +142 -0
  111. package/src/lib/layout/sng-layout-sidebar-menu-sub-item.ts +38 -0
  112. package/src/lib/layout/sng-layout-sidebar-menu-sub.ts +48 -0
  113. package/src/lib/layout/sng-layout-sidebar-menu.ts +41 -0
  114. package/src/lib/layout/sng-layout-sidebar-provider.ts +189 -0
  115. package/src/lib/layout/sng-layout-sidebar-rail.ts +60 -0
  116. package/src/lib/layout/sng-layout-sidebar-separator.ts +38 -0
  117. package/src/lib/layout/sng-layout-sidebar-trigger.ts +97 -0
  118. package/src/lib/layout/sng-layout-sidebar.ts +254 -0
  119. package/src/lib/menu/cn.ts +6 -0
  120. package/src/lib/menu/index.ts +21 -0
  121. package/src/lib/menu/sng-context-trigger.ts +128 -0
  122. package/src/lib/menu/sng-menu-checkbox-item.ts +91 -0
  123. package/src/lib/menu/sng-menu-item.ts +80 -0
  124. package/src/lib/menu/sng-menu-label.ts +47 -0
  125. package/src/lib/menu/sng-menu-radio-group.ts +38 -0
  126. package/src/lib/menu/sng-menu-radio-item.ts +94 -0
  127. package/src/lib/menu/sng-menu-separator.ts +27 -0
  128. package/src/lib/menu/sng-menu-shortcut.ts +25 -0
  129. package/src/lib/menu/sng-menu-sub-content.ts +267 -0
  130. package/src/lib/menu/sng-menu-sub-trigger.ts +68 -0
  131. package/src/lib/menu/sng-menu-sub.ts +124 -0
  132. package/src/lib/menu/sng-menu-tokens.ts +52 -0
  133. package/src/lib/menu/sng-menu-trigger.ts +266 -0
  134. package/src/lib/menu/sng-menu.ts +100 -0
  135. package/src/lib/nav-menu/cn.ts +6 -0
  136. package/src/lib/nav-menu/index.ts +6 -0
  137. package/src/lib/nav-menu/sng-nav-menu-content.ts +72 -0
  138. package/src/lib/nav-menu/sng-nav-menu-item.ts +109 -0
  139. package/src/lib/nav-menu/sng-nav-menu-link.ts +54 -0
  140. package/src/lib/nav-menu/sng-nav-menu-list.ts +43 -0
  141. package/src/lib/nav-menu/sng-nav-menu-trigger.ts +98 -0
  142. package/src/lib/nav-menu/sng-nav-menu.ts +99 -0
  143. package/src/lib/otp-input/cn.ts +6 -0
  144. package/src/lib/otp-input/index.ts +14 -0
  145. package/src/lib/otp-input/sng-otp-input-group.ts +38 -0
  146. package/src/lib/otp-input/sng-otp-input-separator.ts +43 -0
  147. package/src/lib/otp-input/sng-otp-input-slot.ts +128 -0
  148. package/src/lib/otp-input/sng-otp-input-tokens.ts +20 -0
  149. package/src/lib/otp-input/sng-otp-input.ts +301 -0
  150. package/src/lib/popover/cn.ts +6 -0
  151. package/src/lib/popover/index.ts +3 -0
  152. package/src/lib/popover/sng-popover-content.ts +66 -0
  153. package/src/lib/popover/sng-popover-trigger.ts +44 -0
  154. package/src/lib/popover/sng-popover.ts +218 -0
  155. package/src/lib/preview-box/cn.ts +6 -0
  156. package/src/lib/preview-box/index.ts +5 -0
  157. package/src/lib/preview-box/sng-code-block.ts +80 -0
  158. package/src/lib/preview-box/sng-html-block.ts +79 -0
  159. package/src/lib/preview-box/sng-preview-block.ts +47 -0
  160. package/src/lib/preview-box/sng-preview-box.ts +369 -0
  161. package/src/lib/preview-box/sng-style-block.ts +80 -0
  162. package/src/lib/progress/cn.ts +6 -0
  163. package/src/lib/progress/index.ts +1 -0
  164. package/src/lib/progress/sng-progress.ts +65 -0
  165. package/src/lib/radio/cn.ts +6 -0
  166. package/src/lib/radio/index.ts +5 -0
  167. package/src/lib/radio/sng-radio-item.ts +100 -0
  168. package/src/lib/radio/sng-radio.ts +54 -0
  169. package/src/lib/resizable/cn.ts +6 -0
  170. package/src/lib/resizable/index.ts +3 -0
  171. package/src/lib/resizable/sng-resizable-group.ts +188 -0
  172. package/src/lib/resizable/sng-resizable-handle.ts +236 -0
  173. package/src/lib/resizable/sng-resizable-panel.ts +71 -0
  174. package/src/lib/search-input/cn.ts +6 -0
  175. package/src/lib/search-input/index.ts +16 -0
  176. package/src/lib/search-input/sng-search-input-context.ts +24 -0
  177. package/src/lib/search-input/sng-search-input-empty.ts +42 -0
  178. package/src/lib/search-input/sng-search-input-group.ts +69 -0
  179. package/src/lib/search-input/sng-search-input-item.ts +164 -0
  180. package/src/lib/search-input/sng-search-input-list.ts +34 -0
  181. package/src/lib/search-input/sng-search-input-separator.ts +32 -0
  182. package/src/lib/search-input/sng-search-input-shortcut.ts +29 -0
  183. package/src/lib/search-input/sng-search-input.ts +368 -0
  184. package/src/lib/select/cn.ts +6 -0
  185. package/src/lib/select/index.ts +7 -0
  186. package/src/lib/select/sng-select-content.ts +27 -0
  187. package/src/lib/select/sng-select-empty.ts +48 -0
  188. package/src/lib/select/sng-select-group.ts +29 -0
  189. package/src/lib/select/sng-select-item.ts +140 -0
  190. package/src/lib/select/sng-select-label.ts +29 -0
  191. package/src/lib/select/sng-select-separator.ts +29 -0
  192. package/src/lib/select/sng-select.ts +326 -0
  193. package/src/lib/separator/cn.ts +6 -0
  194. package/src/lib/separator/index.ts +1 -0
  195. package/src/lib/separator/sng-separator.ts +40 -0
  196. package/src/lib/skeleton/cn.ts +6 -0
  197. package/src/lib/skeleton/index.ts +1 -0
  198. package/src/lib/skeleton/sng-skeleton.ts +49 -0
  199. package/src/lib/slider/cn.ts +6 -0
  200. package/src/lib/slider/index.ts +2 -0
  201. package/src/lib/slider/sng-slider.ts +137 -0
  202. package/src/lib/sng-table/cn.ts +6 -0
  203. package/src/lib/sng-table/flex-render.ts +222 -0
  204. package/src/lib/sng-table/index.ts +85 -0
  205. package/src/lib/sng-table/sng-table-body.ts +59 -0
  206. package/src/lib/sng-table/sng-table-caption.ts +49 -0
  207. package/src/lib/sng-table/sng-table-cell.ts +62 -0
  208. package/src/lib/sng-table/sng-table-footer.ts +60 -0
  209. package/src/lib/sng-table/sng-table-head.ts +66 -0
  210. package/src/lib/sng-table/sng-table-header.ts +48 -0
  211. package/src/lib/sng-table/sng-table-pagination.ts +265 -0
  212. package/src/lib/sng-table/sng-table-row.ts +65 -0
  213. package/src/lib/sng-table/sng-table.ts +67 -0
  214. package/src/lib/sng-table-core/core/create-cell.ts +117 -0
  215. package/src/lib/sng-table-core/core/create-column.ts +266 -0
  216. package/src/lib/sng-table-core/core/create-header.ts +271 -0
  217. package/src/lib/sng-table-core/core/create-row.ts +293 -0
  218. package/src/lib/sng-table-core/core/create-table.ts +534 -0
  219. package/src/lib/sng-table-core/core/types.ts +1197 -0
  220. package/src/lib/sng-table-core/core/utils.ts +307 -0
  221. package/src/lib/sng-table-core/features/column-filtering.ts +376 -0
  222. package/src/lib/sng-table-core/features/column-ordering.ts +159 -0
  223. package/src/lib/sng-table-core/features/column-pinning.ts +219 -0
  224. package/src/lib/sng-table-core/features/column-sizing.ts +268 -0
  225. package/src/lib/sng-table-core/features/column-visibility.ts +128 -0
  226. package/src/lib/sng-table-core/features/faceting.ts +279 -0
  227. package/src/lib/sng-table-core/features/fuzzy-filtering.ts +188 -0
  228. package/src/lib/sng-table-core/features/global-filtering.ts +128 -0
  229. package/src/lib/sng-table-core/features/pagination.ts +179 -0
  230. package/src/lib/sng-table-core/features/row-expanding.ts +181 -0
  231. package/src/lib/sng-table-core/features/row-grouping.ts +235 -0
  232. package/src/lib/sng-table-core/features/row-pinning.ts +196 -0
  233. package/src/lib/sng-table-core/features/row-selection.ts +298 -0
  234. package/src/lib/sng-table-core/features/sorting.ts +425 -0
  235. package/src/lib/sng-table-core/features/virtualization.ts +298 -0
  236. package/src/lib/sng-table-core/index.ts +235 -0
  237. package/src/lib/sng-table-core/row-models/core-row-model.ts +256 -0
  238. package/src/lib/sng-table-core/row-models/expanded-row-model.ts +175 -0
  239. package/src/lib/sng-table-core/row-models/filtered-row-model.ts +307 -0
  240. package/src/lib/sng-table-core/row-models/grouped-row-model.ts +290 -0
  241. package/src/lib/sng-table-core/row-models/paginated-row-model.ts +135 -0
  242. package/src/lib/sng-table-core/row-models/sorted-row-model.ts +197 -0
  243. package/src/lib/styles/sng-themes.css +164 -0
  244. package/src/lib/switch/cn.ts +6 -0
  245. package/src/lib/switch/index.ts +1 -0
  246. package/src/lib/switch/sng-switch.ts +137 -0
  247. package/src/lib/tabs/cn.ts +6 -0
  248. package/src/lib/tabs/index.ts +4 -0
  249. package/src/lib/tabs/sng-tabs-content.ts +66 -0
  250. package/src/lib/tabs/sng-tabs-list.ts +55 -0
  251. package/src/lib/tabs/sng-tabs-trigger.ts +86 -0
  252. package/src/lib/tabs/sng-tabs.ts +83 -0
  253. package/src/lib/toast/cn.ts +6 -0
  254. package/src/lib/toast/index.ts +3 -0
  255. package/src/lib/toast/sng-toast.service.ts +258 -0
  256. package/src/lib/toast/sng-toast.ts +101 -0
  257. package/src/lib/toast/sng-toaster.ts +67 -0
  258. package/src/lib/toggle/cn.ts +6 -0
  259. package/src/lib/toggle/index.ts +6 -0
  260. package/src/lib/toggle/sng-toggle-group-item.ts +89 -0
  261. package/src/lib/toggle/sng-toggle-group.ts +85 -0
  262. package/src/lib/toggle/sng-toggle.ts +78 -0
  263. package/src/lib/toggle-group/index.ts +6 -0
  264. package/src/lib/tooltip/cn.ts +6 -0
  265. package/src/lib/tooltip/index.ts +5 -0
  266. package/src/lib/tooltip/sng-tooltip-content.ts +64 -0
  267. package/src/lib/tooltip/sng-tooltip.ts +216 -0
  268. package/src/public-api.ts +207 -0
  269. package/tsconfig.json +24 -0
  270. package/tsconfig.lib.json +17 -0
  271. package/tsconfig.lib.prod.json +11 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShadNG Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # sng-ui
2
+
3
+ Native Angular UI components inspired by shadcn/ui Figma designs.
4
+
5
+ [![npm](https://img.shields.io/npm/v/sng-ui)](https://www.npmjs.com/package//sng-ui)
6
+ [![Angular](https://img.shields.io/badge/Angular-21-dd0031)](https://angular.dev)
7
+ [![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-4-38bdf8)](https://tailwindcss.com)
8
+ [![License](https://img.shields.io/badge/License-MIT-green)](LICENSE)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install sng-ui
14
+ ```
15
+
16
+ Peer dependencies:
17
+
18
+ ```json
19
+ {
20
+ "@angular/common": "^21.0.0",
21
+ "@angular/core": "^21.0.0",
22
+ "@angular/platform-browser": "^21.0.0",
23
+ "shiki": "^3.0.0"
24
+ }
25
+ ```
26
+
27
+ `shiki` is optional and only needed for `code-block` / `preview-box`.
28
+
29
+ ## CLI (Copy-Paste Model)
30
+
31
+ ```bash
32
+ npx sng-ui init
33
+ npx sng-ui add button
34
+ npx sng-ui add table menu
35
+ ```
36
+
37
+ The CLI copies editable component source into your app.
38
+
39
+ ## Usage
40
+
41
+ ```ts
42
+ import { Component } from '@angular/core';
43
+ import { SngButton } from 'sng-ui';
44
+
45
+ @Component({
46
+ selector: 'app-example',
47
+ imports: [SngButton],
48
+ template: `
49
+ <sng-button class="bg-primary text-primary-foreground shadow-xs hover:bg-primary/90">
50
+ Click me
51
+ </sng-button>
52
+ `,
53
+ })
54
+ export class ExampleComponent {}
55
+ ```
56
+
57
+ ## Theming
58
+
59
+ Theme tokens are defined in `src/lib/styles/sng-themes.css`.
60
+
61
+ ## Package Checks
62
+
63
+ ```bash
64
+ npm run test:cli
65
+ npm run pack:check
66
+ ```
67
+
68
+ ## Notes Before First Public Release
69
+
70
+ - Update `repository`, `homepage`, and `bugs.url` in `package.json` to your final GitHub repository.
71
+ - Keep tests and Storybook stories in this repo for contributors; npm publish excludes test/story artifacts via `.npmignore`.
72
+
73
+ ## License
74
+
75
+ MIT
package/cli/sng-ui.js ADDED
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const CONFIG_FILE = 'sng-ui.json';
7
+ const DEFAULT_COMPONENTS_DIR = 'src/lib/sng-ui';
8
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
9
+ const LIB_SOURCE_ROOT = path.join(PACKAGE_ROOT, 'src', 'lib');
10
+
11
+ const ALWAYS_AVAILABLE_IMPORTS = new Set([
12
+ '@angular/animations',
13
+ '@angular/common',
14
+ '@angular/core',
15
+ '@angular/forms',
16
+ '@angular/platform-browser',
17
+ '@angular/router',
18
+ 'rxjs',
19
+ 'tslib'
20
+ ]);
21
+
22
+ function printHelp() {
23
+ console.log(`sng-ui CLI
24
+
25
+ Usage:
26
+ npx sng-ui init [--path <dir>] [--force]
27
+ npx sng-ui add <component...> [--path <dir>] [--force] [--dry-run]
28
+
29
+ Examples:
30
+ npx sng-ui init
31
+ npx sng-ui add button
32
+ npx sng-ui add table menu
33
+ npx sng-ui add slider --path src/lib/ui
34
+ `);
35
+ }
36
+
37
+ function parseOptions(tokens) {
38
+ const options = { force: false, dryRun: false, path: null, values: [] };
39
+
40
+ for (let index = 0; index < tokens.length; index += 1) {
41
+ const token = tokens[index];
42
+ if (token === '--force') {
43
+ options.force = true;
44
+ continue;
45
+ }
46
+ if (token === '--dry-run') {
47
+ options.dryRun = true;
48
+ continue;
49
+ }
50
+ if (token === '--path') {
51
+ const next = tokens[index + 1];
52
+ if (!next || next.startsWith('--')) {
53
+ throw new Error('Missing value for --path.');
54
+ }
55
+ options.path = next;
56
+ index += 1;
57
+ continue;
58
+ }
59
+ options.values.push(token);
60
+ }
61
+
62
+ return options;
63
+ }
64
+
65
+ function readConfig(cwd) {
66
+ const configPath = path.join(cwd, CONFIG_FILE);
67
+ if (!fs.existsSync(configPath)) {
68
+ return { componentsDir: DEFAULT_COMPONENTS_DIR };
69
+ }
70
+ const raw = fs.readFileSync(configPath, 'utf8');
71
+ const parsed = JSON.parse(raw);
72
+ return {
73
+ componentsDir: typeof parsed.componentsDir === 'string' && parsed.componentsDir.trim()
74
+ ? parsed.componentsDir
75
+ : DEFAULT_COMPONENTS_DIR
76
+ };
77
+ }
78
+
79
+ function writeConfig(cwd, componentsDir, force) {
80
+ const configPath = path.join(cwd, CONFIG_FILE);
81
+ if (fs.existsSync(configPath) && !force) {
82
+ throw new Error(`Config already exists at ${CONFIG_FILE}. Use --force to overwrite.`);
83
+ }
84
+ const payload = {
85
+ componentsDir
86
+ };
87
+ fs.writeFileSync(configPath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
88
+ fs.mkdirSync(path.resolve(cwd, componentsDir), { recursive: true });
89
+ }
90
+
91
+ function getComponentFolderMap() {
92
+ const entries = fs.readdirSync(LIB_SOURCE_ROOT, { withFileTypes: true })
93
+ .filter((entry) => entry.isDirectory())
94
+ .map((entry) => entry.name)
95
+ .filter((name) => name !== 'styles');
96
+
97
+ const map = new Map();
98
+ for (const folder of entries) {
99
+ map.set(folder, folder);
100
+ if (folder.startsWith('sng-')) {
101
+ map.set(folder.slice(4), folder);
102
+ }
103
+ }
104
+ return map;
105
+ }
106
+
107
+ function resolveComponentFolder(componentName, folderMap) {
108
+ return folderMap.get(componentName) || null;
109
+ }
110
+
111
+ function shouldSkipFile(filePath) {
112
+ return filePath.endsWith('.spec.ts') || filePath.endsWith('.stories.ts');
113
+ }
114
+
115
+ function listFilesRecursively(directoryPath) {
116
+ const files = [];
117
+ const stack = [directoryPath];
118
+
119
+ while (stack.length) {
120
+ const current = stack.pop();
121
+ const entries = fs.readdirSync(current, { withFileTypes: true });
122
+ for (const entry of entries) {
123
+ const fullPath = path.join(current, entry.name);
124
+ if (entry.isDirectory()) {
125
+ stack.push(fullPath);
126
+ } else if (!shouldSkipFile(fullPath)) {
127
+ files.push(fullPath);
128
+ }
129
+ }
130
+ }
131
+
132
+ return files;
133
+ }
134
+
135
+ function resolveImportFile(fromFile, importPath) {
136
+ const fromDirectory = path.dirname(fromFile);
137
+ const base = path.resolve(fromDirectory, importPath);
138
+ const candidates = [
139
+ base,
140
+ `${base}.ts`,
141
+ `${base}.js`,
142
+ path.join(base, 'index.ts'),
143
+ path.join(base, 'index.js')
144
+ ];
145
+
146
+ for (const candidate of candidates) {
147
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
148
+ return candidate;
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+
154
+ function extractImports(filePath) {
155
+ const content = fs.readFileSync(filePath, 'utf8')
156
+ .replace(/\/\*[\s\S]*?\*\//g, '')
157
+ .replace(/^\s*\/\/.*$/gm, '');
158
+ const imports = [];
159
+ const pattern = /\bfrom\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g;
160
+ let match = pattern.exec(content);
161
+ while (match) {
162
+ imports.push(match[1] || match[2]);
163
+ match = pattern.exec(content);
164
+ }
165
+ return imports;
166
+ }
167
+
168
+ function toPackageName(importPath) {
169
+ if (importPath.startsWith('@')) {
170
+ const parts = importPath.split('/');
171
+ return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : importPath;
172
+ }
173
+ return importPath.split('/')[0];
174
+ }
175
+
176
+ function collectComponentFiles(componentFolder) {
177
+ const queue = [];
178
+ const visitedFiles = new Set();
179
+ const visitedFolders = new Set([componentFolder]);
180
+
181
+ const rootPath = path.join(LIB_SOURCE_ROOT, componentFolder);
182
+ for (const file of listFilesRecursively(rootPath)) {
183
+ queue.push(file);
184
+ }
185
+
186
+ const externalPackages = new Set();
187
+
188
+ while (queue.length) {
189
+ const currentFile = queue.shift();
190
+ if (visitedFiles.has(currentFile)) {
191
+ continue;
192
+ }
193
+ visitedFiles.add(currentFile);
194
+
195
+ const imports = extractImports(currentFile);
196
+ for (const importPath of imports) {
197
+ if (importPath.startsWith('.')) {
198
+ const resolved = resolveImportFile(currentFile, importPath);
199
+ if (!resolved || !resolved.startsWith(LIB_SOURCE_ROOT) || shouldSkipFile(resolved)) {
200
+ continue;
201
+ }
202
+ const relative = path.relative(LIB_SOURCE_ROOT, resolved);
203
+ const rootFolder = relative.split(path.sep)[0];
204
+ if (!visitedFolders.has(rootFolder)) {
205
+ visitedFolders.add(rootFolder);
206
+ for (const file of listFilesRecursively(path.join(LIB_SOURCE_ROOT, rootFolder))) {
207
+ queue.push(file);
208
+ }
209
+ } else {
210
+ queue.push(resolved);
211
+ }
212
+ } else if (!ALWAYS_AVAILABLE_IMPORTS.has(importPath) && !importPath.startsWith('node:')) {
213
+ const packageName = toPackageName(importPath);
214
+ if (!ALWAYS_AVAILABLE_IMPORTS.has(packageName) && packageName !== 'sng-ui') {
215
+ externalPackages.add(packageName);
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ return {
222
+ files: [...visitedFiles].sort(),
223
+ externalPackages: [...externalPackages].sort()
224
+ };
225
+ }
226
+
227
+ function copyFiles(files, destinationRoot, options) {
228
+ let copied = 0;
229
+ let skipped = 0;
230
+
231
+ for (const sourceFile of files) {
232
+ const relative = path.relative(LIB_SOURCE_ROOT, sourceFile);
233
+ const destination = path.join(destinationRoot, relative);
234
+ const destinationDirectory = path.dirname(destination);
235
+
236
+ if (!options.dryRun) {
237
+ fs.mkdirSync(destinationDirectory, { recursive: true });
238
+ }
239
+
240
+ if (fs.existsSync(destination) && !options.force) {
241
+ skipped += 1;
242
+ continue;
243
+ }
244
+
245
+ if (!options.dryRun) {
246
+ fs.copyFileSync(sourceFile, destination);
247
+ }
248
+ copied += 1;
249
+ }
250
+
251
+ return { copied, skipped };
252
+ }
253
+
254
+ function runInit(cwd, options) {
255
+ const componentsDir = options.path || DEFAULT_COMPONENTS_DIR;
256
+ writeConfig(cwd, componentsDir, options.force);
257
+ console.log(`Created ${CONFIG_FILE} with componentsDir="${componentsDir}".`);
258
+ }
259
+
260
+ function runAdd(cwd, options) {
261
+ if (!options.values.length) {
262
+ throw new Error('Missing component name. Example: npx sng-ui add button');
263
+ }
264
+
265
+ const config = readConfig(cwd);
266
+ const destinationRoot = path.resolve(cwd, options.path || config.componentsDir);
267
+ const folderMap = getComponentFolderMap();
268
+ const missing = [];
269
+
270
+ let totalCopied = 0;
271
+ let totalSkipped = 0;
272
+ const dependencySet = new Set();
273
+
274
+ for (const componentName of options.values) {
275
+ const folder = resolveComponentFolder(componentName, folderMap);
276
+ if (!folder) {
277
+ missing.push(componentName);
278
+ continue;
279
+ }
280
+
281
+ const { files, externalPackages } = collectComponentFiles(folder);
282
+ const result = copyFiles(files, destinationRoot, options);
283
+ totalCopied += result.copied;
284
+ totalSkipped += result.skipped;
285
+ for (const dependency of externalPackages) {
286
+ dependencySet.add(dependency);
287
+ }
288
+ console.log(`Added ${componentName}: ${result.copied} file(s), ${result.skipped} skipped.`);
289
+ }
290
+
291
+ if (missing.length) {
292
+ const available = [...new Set([...folderMap.keys()].filter((name) => !name.startsWith('sng-')))].sort();
293
+ throw new Error(`Unknown component(s): ${missing.join(', ')}.\nAvailable: ${available.join(', ')}`);
294
+ }
295
+
296
+ console.log(`Done. Copied ${totalCopied} file(s), skipped ${totalSkipped}.`);
297
+ if (dependencySet.size) {
298
+ console.log(`Install peer deps if missing:\n npm install ${[...dependencySet].join(' ')}`);
299
+ }
300
+ }
301
+
302
+ function main() {
303
+ const cwd = process.cwd();
304
+ const args = process.argv.slice(2);
305
+ const command = args.shift();
306
+
307
+ if (!command || command === '--help' || command === '-h') {
308
+ printHelp();
309
+ return;
310
+ }
311
+
312
+ const options = parseOptions(args);
313
+
314
+ if (command === 'init') {
315
+ runInit(cwd, options);
316
+ return;
317
+ }
318
+ if (command === 'add') {
319
+ runAdd(cwd, options);
320
+ return;
321
+ }
322
+
323
+ throw new Error(`Unknown command: ${command}`);
324
+ }
325
+
326
+ try {
327
+ main();
328
+ } catch (error) {
329
+ console.error(`[sng-ui] ${error.message}`);
330
+ process.exit(1);
331
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "dist/sng-ui",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ },
7
+ "assets": [
8
+ {
9
+ "input": "cli",
10
+ "glob": "**/*",
11
+ "ignore": [
12
+ "**/*.spec.cjs"
13
+ ],
14
+ "output": "cli"
15
+ },
16
+ "registry.json",
17
+ "LICENSE",
18
+ {
19
+ "input": "src/lib",
20
+ "glob": "**/*.ts",
21
+ "ignore": [
22
+ "**/*.spec.ts",
23
+ "**/*.stories.ts"
24
+ ],
25
+ "output": "src/lib"
26
+ },
27
+ "src/lib/styles/**/*.css"
28
+ ]
29
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@shadng/sng-ui",
3
+ "version": "1.0.0",
4
+ "description": "Native Angular UI components inspired by shadcn/ui Figma designs.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/alimjanablikim/sng-ui.git"
9
+ },
10
+ "homepage": "https://github.com/alimjanablikim/sng-ui",
11
+ "bugs": {
12
+ "url": "https://github.com/alimjanablikim/sng-ui/issues"
13
+ },
14
+ "keywords": [
15
+ "angular",
16
+ "ui",
17
+ "components",
18
+ "tailwindcss",
19
+ "shadcn"
20
+ ],
21
+ "bin": {
22
+ "sng-ui": "cli/sng-ui.js"
23
+ },
24
+ "engines": {
25
+ "node": ">=20.11.0",
26
+ "npm": ">=10.0.0"
27
+ },
28
+ "peerDependencies": {
29
+ "@angular/common": "^21.0.0",
30
+ "@angular/core": "^21.0.0",
31
+ "@angular/platform-browser": "^21.0.0",
32
+ "shiki": "^3.0.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "shiki": {
36
+ "optional": true
37
+ }
38
+ },
39
+ "dependencies": {
40
+ "tslib": "^2.3.0"
41
+ },
42
+ "sideEffects": false,
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "test:cli": "node --test cli/sng-ui.cli.spec.cjs",
48
+ "pack:check": "npm pack --dry-run"
49
+ },
50
+ "files": [
51
+ "src/",
52
+ "!src/**/*.spec.ts",
53
+ "!src/**/*.stories.ts",
54
+ "cli/",
55
+ "!cli/*.spec.cjs",
56
+ "registry.json",
57
+ "README.md",
58
+ "LICENSE",
59
+ "ng-package.json",
60
+ "tsconfig.json",
61
+ "tsconfig.lib.json",
62
+ "tsconfig.lib.prod.json"
63
+ ]
64
+ }
package/registry.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "$schema": "./registry-schema.json",
3
+ "name": "sng-ui",
4
+ "description": "ShadNG UI Component Registry",
5
+ "components": {
6
+ "cn": {
7
+ "name": "cn",
8
+ "description": "Utility for merging Tailwind CSS classes",
9
+ "files": ["lib/button/cn.ts"],
10
+ "dependencies": {
11
+ "npm": ["clsx", "tailwind-merge"]
12
+ }
13
+ },
14
+ "button": {
15
+ "name": "button",
16
+ "description": "Angular button component with class-based styling",
17
+ "files": ["lib/button/sng-button.ts"],
18
+ "dependencies": {
19
+ "internal": ["cn"]
20
+ }
21
+ },
22
+ "code-block": {
23
+ "name": "code-block",
24
+ "description": "Code block component with VS Code-accurate syntax highlighting powered by Shiki. Includes copy button and language header.",
25
+ "files": ["lib/code-block/sng-code-block.ts", "lib/code-block/index.ts"],
26
+ "dependencies": {
27
+ "npm": ["shiki"],
28
+ "internal": ["cn"]
29
+ },
30
+ "note": "Requires 'shiki' npm package. Supports 200+ languages and all VS Code themes."
31
+ },
32
+ "preview-box": {
33
+ "name": "preview-box",
34
+ "description": "Tabbed preview component with Preview, Code, and Source tabs. Perfect for documentation sites.",
35
+ "files": ["lib/preview-box/sng-preview-box.ts"],
36
+ "dependencies": {
37
+ "internal": ["cn", "code-block"],
38
+ "npm": ["shiki"]
39
+ },
40
+ "note": "Uses code-block for syntax-highlighted code display in Code/Source tabs."
41
+ },
42
+ "accordion": {
43
+ "name": "accordion",
44
+ "description": "Angular accordion component for collapsible content sections. Built with Angular signals for accessibility.",
45
+ "files": [
46
+ "lib/accordion/sng-accordion.ts",
47
+ "lib/accordion/sng-accordion-item.ts",
48
+ "lib/accordion/sng-accordion-trigger.ts",
49
+ "lib/accordion/sng-accordion-content.ts",
50
+ "lib/accordion/index.ts"
51
+ ],
52
+ "dependencies": {
53
+ "internal": ["cn"]
54
+ },
55
+ "note": "Includes smooth expand/collapse animations. Supports single/multiple mode and collapsible options."
56
+ },
57
+ "alert": {
58
+ "name": "alert",
59
+ "description": "Angular alert component for displaying important messages, errors, and notifications. Supports icons and multiple variants via Tailwind classes.",
60
+ "files": [
61
+ "lib/alert/sng-alert.ts",
62
+ "lib/alert/sng-alert-title.ts",
63
+ "lib/alert/sng-alert-description.ts",
64
+ "lib/alert/index.ts"
65
+ ],
66
+ "dependencies": {
67
+ "internal": ["cn"]
68
+ },
69
+ "note": "Purely presentational component. No Angular CDK required. Use Alert Dialog for confirmation modals."
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,18 @@
1
+ // Components
2
+ export { SngAccordion } from './sng-accordion';
3
+ export { SngAccordionItem } from './sng-accordion-item';
4
+ export { SngAccordionTrigger } from './sng-accordion-trigger';
5
+ export { SngAccordionContent } from './sng-accordion-content';
6
+
7
+ // Types
8
+ export type {
9
+ SngAccordionType,
10
+ SngAccordionValue,
11
+ SngAccordionState,
12
+ SngAccordionOrientation,
13
+ SngAccordionLayout,
14
+ SngAccordionApi,
15
+ SngAccordionItemApi,
16
+ SngAccordionTriggerApi,
17
+ SngAccordionContentApi,
18
+ } from './sng-accordion.types';