@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
@@ -0,0 +1,80 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ input,
6
+ } from '@angular/core';
7
+ import { SngCodeBlock, type ShikiTheme } from '../code-block/sng-code-block';
8
+
9
+ /**
10
+ * SngPreviewCodeBlock - TypeScript/code snippet for Code tab in preview-box.
11
+ *
12
+ * Displays syntax-highlighted TypeScript or other code in the Code tab of preview-box.
13
+ * Wraps sng-code-block internally for consistent styling.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <sng-preview-box>
18
+ * <sng-preview-block>
19
+ * <sng-button>Click me</sng-button>
20
+ * </sng-preview-block>
21
+ * <sng-html-block [code]="htmlCode" />
22
+ * <sng-preview-code-block [code]="tsCode" [theme]="'github-dark'" />
23
+ * </sng-preview-box>
24
+ * ```
25
+ */
26
+ @Component({
27
+ selector: 'sng-preview-code-block',
28
+ standalone: true,
29
+ imports: [SngCodeBlock],
30
+ changeDetection: ChangeDetectionStrategy.OnPush,
31
+ encapsulation: ViewEncapsulation.None,
32
+ host: {},
33
+ styles: [`
34
+ sng-preview-code-block {
35
+ display: flex;
36
+ flex-direction: column;
37
+ height: 100%;
38
+ }
39
+ sng-preview-code-block sng-code-block {
40
+ display: flex;
41
+ flex-direction: column;
42
+ height: 100%;
43
+ }
44
+ sng-preview-code-block .code-container {
45
+ display: flex;
46
+ flex-direction: column;
47
+ height: 100%;
48
+ border: none;
49
+ border-radius: 0;
50
+ }
51
+ sng-preview-code-block .code-content {
52
+ flex: 1;
53
+ overflow: auto;
54
+ }
55
+ `],
56
+ template: `
57
+ <sng-code-block
58
+ [code]="code()"
59
+ [language]="language()"
60
+ [theme]="theme()"
61
+ [showHeader]="false"
62
+ />
63
+ `
64
+ })
65
+ export class SngPreviewCodeBlock {
66
+ /**
67
+ * The TypeScript or other code string to display with syntax highlighting.
68
+ */
69
+ code = input.required<string>();
70
+
71
+ /**
72
+ * The language for syntax highlighting.
73
+ */
74
+ language = input<string>('typescript');
75
+
76
+ /**
77
+ * The Shiki theme for syntax highlighting.
78
+ */
79
+ theme = input<ShikiTheme>('github-light');
80
+ }
@@ -0,0 +1,79 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ input,
6
+ } from '@angular/core';
7
+ import { SngCodeBlock, type ShikiTheme } from '../code-block/sng-code-block';
8
+
9
+ /**
10
+ * SngHtmlBlock - HTML code snippet for HTML tab in preview-box.
11
+ *
12
+ * Displays syntax-highlighted HTML code in the HTML tab of preview-box.
13
+ * Wraps sng-code-block internally for consistent styling.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <sng-preview-box>
18
+ * <sng-preview-block>
19
+ * <sng-button>Click me</sng-button>
20
+ * </sng-preview-block>
21
+ * <sng-html-block [code]="htmlSnippet" [theme]="'github-dark'" />
22
+ * </sng-preview-box>
23
+ * ```
24
+ */
25
+ @Component({
26
+ selector: 'sng-html-block',
27
+ standalone: true,
28
+ imports: [SngCodeBlock],
29
+ changeDetection: ChangeDetectionStrategy.OnPush,
30
+ encapsulation: ViewEncapsulation.None,
31
+ host: {},
32
+ styles: [`
33
+ sng-html-block {
34
+ display: flex;
35
+ flex-direction: column;
36
+ height: 100%;
37
+ }
38
+ sng-html-block sng-code-block {
39
+ display: flex;
40
+ flex-direction: column;
41
+ height: 100%;
42
+ }
43
+ sng-html-block .code-container {
44
+ display: flex;
45
+ flex-direction: column;
46
+ height: 100%;
47
+ border: none;
48
+ border-radius: 0;
49
+ }
50
+ sng-html-block .code-content {
51
+ flex: 1;
52
+ overflow: auto;
53
+ }
54
+ `],
55
+ template: `
56
+ <sng-code-block
57
+ [code]="code()"
58
+ [language]="language()"
59
+ [theme]="theme()"
60
+ [showHeader]="false"
61
+ />
62
+ `
63
+ })
64
+ export class SngHtmlBlock {
65
+ /**
66
+ * The HTML code string to display with syntax highlighting.
67
+ */
68
+ code = input.required<string>();
69
+
70
+ /**
71
+ * The language for syntax highlighting.
72
+ */
73
+ language = input<string>('html');
74
+
75
+ /**
76
+ * The Shiki theme for syntax highlighting.
77
+ */
78
+ theme = input<ShikiTheme>('github-light');
79
+ }
@@ -0,0 +1,47 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ input,
6
+ computed,
7
+ } from '@angular/core';
8
+ import { cn } from './cn';
9
+
10
+ /**
11
+ * Content wrapper for live demo in preview-box. Centers content with padding.
12
+ *
13
+ * @example
14
+ * ```html
15
+ * <sng-preview-block>
16
+ * <sng-button>Click me</sng-button>
17
+ * </sng-preview-block>
18
+ * ```
19
+ */
20
+ @Component({
21
+ selector: 'sng-preview-block',
22
+ standalone: true,
23
+ changeDetection: ChangeDetectionStrategy.OnPush,
24
+ encapsulation: ViewEncapsulation.None,
25
+ host: {
26
+ '[class]': 'hostClasses()',
27
+ },
28
+ styles: [`
29
+ sng-preview-block {
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 100%;
34
+ flex-shrink: 0;
35
+ }
36
+ `],
37
+ template: `<ng-content />`
38
+ })
39
+ export class SngPreviewBlock {
40
+ /** Custom CSS classes. */
41
+ class = input<string>('');
42
+
43
+ hostClasses = computed(() => cn(
44
+ 'flex items-center justify-center p-6',
45
+ this.class()
46
+ ));
47
+ }
@@ -0,0 +1,369 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ signal,
6
+ ElementRef,
7
+ viewChildren,
8
+ AfterViewInit,
9
+ effect,
10
+ inject,
11
+ Injector,
12
+ contentChild,
13
+ TemplateRef,
14
+ computed,
15
+ input,
16
+ afterNextRender,
17
+ } from '@angular/core';
18
+ import { NgTemplateOutlet } from '@angular/common';
19
+ import { SngHtmlBlock } from './sng-html-block';
20
+ import { SngCodeBlock } from '../code-block/sng-code-block';
21
+ import { SngPreviewCodeBlock } from './sng-code-block';
22
+ import { SngStyleBlock } from './sng-style-block';
23
+ import { cn } from './cn';
24
+
25
+ type TabType = 'preview' | 'html' | 'code' | 'style';
26
+
27
+ /**
28
+ * ShadNG Preview Box - Component showcase with code examples.
29
+ *
30
+ * A tabbed container for displaying live component previews alongside their
31
+ * HTML, TypeScript, and CSS code. Supports fullscreen mode and background toggling.
32
+ *
33
+ * Height control via Tailwind classes:
34
+ * - Default: h-[350px]
35
+ * - Custom: `<sng-preview-box class="h-[700px]">`
36
+ *
37
+ * @example
38
+ * ```html
39
+ * <sng-preview-box class="h-[500px]">
40
+ * <sng-preview-block>
41
+ * <sng-button>Click me</sng-button>
42
+ * </sng-preview-block>
43
+ * <sng-html-block [code]="htmlCode" />
44
+ * <sng-code-block [code]="tsCode" />
45
+ * <sng-style-block [code]="cssCode" />
46
+ * </sng-preview-box>
47
+ * ```
48
+ */
49
+ @Component({
50
+ selector: 'sng-preview-box',
51
+ standalone: true,
52
+ imports: [NgTemplateOutlet],
53
+ changeDetection: ChangeDetectionStrategy.OnPush,
54
+ encapsulation: ViewEncapsulation.None,
55
+ host: {
56
+ '[class]': 'hostClasses()'
57
+ },
58
+ styles: [`
59
+ .sng-preview-box-code-area > sng-html-block,
60
+ .sng-preview-box-code-area > sng-code-block,
61
+ .sng-preview-box-code-area > sng-preview-code-block,
62
+ .sng-preview-box-code-area > sng-style-block {
63
+ display: flex;
64
+ flex-direction: column;
65
+ height: 100%;
66
+ }
67
+ `],
68
+ template: `
69
+ <!-- Fullscreen backdrop (decorative) -->
70
+ @if (isFullscreen()) {
71
+ <div
72
+ aria-hidden="true"
73
+ class="fixed inset-0 z-[10000] bg-black/80"
74
+ (click)="toggleFullscreen()"
75
+ ></div>
76
+ }
77
+
78
+ <!-- Main container -->
79
+ <div [class]="containerClasses()">
80
+ <!-- Tab bar -->
81
+ <div class="flex items-center justify-between h-11 px-1 border-b border-border bg-muted">
82
+ <!-- Tabs group -->
83
+ <div class="inline-flex items-center relative">
84
+ <!-- Sliding indicator - no transition until initialized to prevent FOUC -->
85
+ <div
86
+ [class]="indicatorClasses()"
87
+ [style.left.px]="indicatorLeft()"
88
+ [style.width.px]="indicatorWidth()"
89
+ ></div>
90
+
91
+ <!-- Preview Tab -->
92
+ <button
93
+ #tabBtn
94
+ [class]="tabClasses(activeTab() === 'preview')"
95
+ (click)="setActiveTab('preview')"
96
+ >
97
+ <svg class="shrink-0" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
98
+ <path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/>
99
+ <circle cx="12" cy="12" r="3"/>
100
+ </svg>
101
+ Preview
102
+ </button>
103
+
104
+ <!-- HTML Tab -->
105
+ @if (hasHtml()) {
106
+ <button
107
+ #tabBtn
108
+ [class]="tabClasses(activeTab() === 'html')"
109
+ (click)="setActiveTab('html')"
110
+ >
111
+ <svg class="shrink-0" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
112
+ <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/>
113
+ <polyline points="14 2 14 8 20 8"/>
114
+ <path d="m10 13-2 2 2 2"/>
115
+ <path d="m14 17 2-2-2-2"/>
116
+ </svg>
117
+ HTML
118
+ </button>
119
+ }
120
+
121
+ <!-- Code Tab -->
122
+ @if (hasCode()) {
123
+ <button
124
+ #tabBtn
125
+ [class]="tabClasses(activeTab() === 'code')"
126
+ (click)="setActiveTab('code')"
127
+ >
128
+ <svg class="shrink-0" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
129
+ <polyline points="16 18 22 12 16 6"/>
130
+ <polyline points="8 6 2 12 8 18"/>
131
+ </svg>
132
+ Code
133
+ </button>
134
+ }
135
+
136
+ <!-- Style Tab -->
137
+ @if (hasStyle()) {
138
+ <button
139
+ #tabBtn
140
+ [class]="tabClasses(activeTab() === 'style')"
141
+ (click)="setActiveTab('style')"
142
+ >
143
+ <svg class="shrink-0" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
144
+ <path d="M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1"/>
145
+ <path d="M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1"/>
146
+ </svg>
147
+ Style
148
+ </button>
149
+ }
150
+ </div>
151
+
152
+ <!-- Actions group -->
153
+ <div class="inline-flex items-center gap-1">
154
+ <!-- Background Toggle -->
155
+ <button
156
+ class="flex items-center justify-center px-2 py-1 border-0 rounded-md bg-transparent cursor-pointer"
157
+ [title]="useThemeBg() ? 'Switch to muted background' : 'Switch to theme background'"
158
+ (click)="toggleBackground()"
159
+ >
160
+ <span [class]="toggleTrackClasses()">
161
+ <span [class]="toggleThumbClasses()"></span>
162
+ </span>
163
+ </button>
164
+
165
+ <!-- Fullscreen Button -->
166
+ <button
167
+ class="flex items-center justify-center w-8 h-8 border-0 rounded-md bg-transparent text-muted-foreground hover:text-foreground cursor-pointer transition-colors"
168
+ title="Fullscreen"
169
+ (click)="toggleFullscreen()"
170
+ >
171
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
172
+ <path d="M8 3H5a2 2 0 0 0-2 2v3"/>
173
+ <path d="M21 8V5a2 2 0 0 0-2-2h-3"/>
174
+ <path d="M3 16v3a2 2 0 0 0 2 2h3"/>
175
+ <path d="M16 21h3a2 2 0 0 0 2-2v-3"/>
176
+ </svg>
177
+ </button>
178
+ </div>
179
+ </div>
180
+
181
+ <!-- Preview Tab Content -->
182
+ <div [class]="contentAreaClasses()">
183
+ @if (previewTemplate()) {
184
+ <ng-container *ngTemplateOutlet="previewTemplate()" />
185
+ } @else {
186
+ <ng-content select="sng-preview-block" />
187
+ }
188
+ </div>
189
+
190
+ <!-- HTML Tab Content -->
191
+ <div [class]="codeAreaClasses(activeTab() === 'html')">
192
+ <ng-content select="sng-html-block" />
193
+ </div>
194
+
195
+ <!-- Code Tab Content -->
196
+ <div [class]="codeAreaClasses(activeTab() === 'code')">
197
+ <ng-content select="sng-code-block, sng-preview-code-block" />
198
+ </div>
199
+
200
+ <!-- Style Tab Content -->
201
+ <div [class]="codeAreaClasses(activeTab() === 'style')">
202
+ <ng-content select="sng-style-block" />
203
+ </div>
204
+ </div>
205
+ `
206
+ })
207
+ export class SngPreviewBoxComponent implements AfterViewInit {
208
+ private injector = inject(Injector);
209
+
210
+ tabButtons = viewChildren<ElementRef<HTMLButtonElement>>('tabBtn');
211
+
212
+ /** Custom CSS classes. */
213
+ class = input<string>('');
214
+
215
+ // Host classes: default h-[350px], user can override with class="h-[700px]"
216
+ hostClasses = computed(() => cn(
217
+ 'block h-[350px]',
218
+ this.class()
219
+ ));
220
+
221
+ // Container classes
222
+ containerClasses = computed(() => cn(
223
+ 'flex flex-col h-full border border-border rounded-lg overflow-hidden',
224
+ this.isFullscreen() && 'fixed inset-6 z-[10001] rounded-xl shadow-2xl bg-background'
225
+ ));
226
+
227
+ // Tab button classes
228
+ tabClasses(isActive: boolean) {
229
+ return cn(
230
+ 'inline-flex items-center gap-2 px-3.5 py-2 border-0 rounded-md bg-transparent text-sm text-muted-foreground cursor-pointer transition-colors relative z-[1]',
231
+ 'hover:text-foreground',
232
+ isActive && 'text-foreground font-medium'
233
+ );
234
+ }
235
+
236
+ // Toggle track classes - no transition to prevent FOUC
237
+ toggleTrackClasses = computed(() => cn(
238
+ 'block w-9 h-5 rounded-full relative border',
239
+ this.useThemeBg()
240
+ ? 'bg-muted border-border hover:border-muted-foreground'
241
+ : 'bg-primary border-primary'
242
+ ));
243
+
244
+ // Toggle thumb classes - no transition to prevent FOUC
245
+ toggleThumbClasses = computed(() => cn(
246
+ 'block w-3.5 h-3.5 rounded-full absolute top-[2px] left-[2px]',
247
+ this.useThemeBg()
248
+ ? 'bg-muted-foreground'
249
+ : 'bg-primary-foreground translate-x-4'
250
+ ));
251
+
252
+ // Content area classes (preview tab)
253
+ contentAreaClasses = computed(() => cn(
254
+ 'bg-background relative',
255
+ this.activeTab() === 'preview'
256
+ ? 'flex flex-col items-stretch justify-start flex-1 min-h-0 overflow-y-auto'
257
+ : 'hidden',
258
+ !this.useThemeBg() && 'bg-muted'
259
+ ));
260
+
261
+ // Code area classes (html/code/style tabs)
262
+ codeAreaClasses(isActive: boolean) {
263
+ return cn(
264
+ 'sng-preview-box-code-area p-0 bg-muted overflow-hidden',
265
+ isActive ? 'flex flex-col flex-1' : 'hidden',
266
+ );
267
+ }
268
+
269
+ // Content child for template-based preview
270
+ previewTemplate = contentChild<TemplateRef<unknown>>('preview');
271
+
272
+ // Content children to detect tab content
273
+ htmlBlock = contentChild(SngHtmlBlock);
274
+ codeBlock = contentChild(SngCodeBlock);
275
+ previewCodeBlock = contentChild(SngPreviewCodeBlock);
276
+ styleBlock = contentChild(SngStyleBlock);
277
+
278
+ // Computed signals to check if tabs have content
279
+ hasHtml = computed(() => {
280
+ const block = this.htmlBlock();
281
+ return block && block.code()?.trim().length > 0;
282
+ });
283
+
284
+ hasCode = computed(() => {
285
+ const previewCodeBlock = this.previewCodeBlock();
286
+ if (previewCodeBlock) {
287
+ return previewCodeBlock.code()?.trim().length > 0;
288
+ }
289
+ const block = this.codeBlock();
290
+ return block && block.code()?.trim().length > 0;
291
+ });
292
+
293
+ hasStyle = computed(() => {
294
+ const block = this.styleBlock();
295
+ return block && block.code()?.trim().length > 0;
296
+ });
297
+
298
+ activeTab = signal<TabType>('preview');
299
+ isFullscreen = signal(false);
300
+ useThemeBg = signal(true); // true = theme bg (default), false = muted grey
301
+ indicatorLeft = signal(0);
302
+ indicatorWidth = signal(0);
303
+ isIndicatorInitialized = signal(false);
304
+
305
+ // Indicator classes - only add transition after initial positioning to prevent FOUC
306
+ indicatorClasses = computed(() => cn(
307
+ 'absolute h-full top-0 bg-background border border-border rounded-md shadow-sm pointer-events-none',
308
+ this.isIndicatorInitialized() && 'transition-all duration-200 ease-out'
309
+ ));
310
+
311
+ constructor() {
312
+ // Update indicator when tab buttons are available
313
+ effect(() => {
314
+ const buttons = this.tabButtons();
315
+ const tab = this.activeTab();
316
+ if (buttons.length > 0) {
317
+ this.updateIndicator(tab, buttons);
318
+ }
319
+ });
320
+ }
321
+
322
+ ngAfterViewInit() {
323
+ // Initial indicator position
324
+ afterNextRender(() => {
325
+ const buttons = this.tabButtons();
326
+ if (buttons.length > 0) {
327
+ this.updateIndicator('preview', buttons);
328
+ // Enable transitions after initial positioning (next frame)
329
+ requestAnimationFrame(() => this.isIndicatorInitialized.set(true));
330
+ }
331
+ }, { injector: this.injector });
332
+ }
333
+
334
+ private updateIndicator(tab: TabType, buttons: readonly ElementRef<HTMLButtonElement>[]) {
335
+ // Calculate dynamic tab index based on visible tabs
336
+ let tabIndex = 0;
337
+ if (tab === 'preview') {
338
+ tabIndex = 0;
339
+ } else if (tab === 'html') {
340
+ tabIndex = 1;
341
+ } else if (tab === 'code') {
342
+ tabIndex = 1 + (this.hasHtml() ? 1 : 0);
343
+ } else if (tab === 'style') {
344
+ tabIndex = 1 + (this.hasHtml() ? 1 : 0) + (this.hasCode() ? 1 : 0);
345
+ }
346
+ const button = buttons[tabIndex]?.nativeElement;
347
+ if (button) {
348
+ const parent = button.parentElement;
349
+ if (parent) {
350
+ const parentRect = parent.getBoundingClientRect();
351
+ const buttonRect = button.getBoundingClientRect();
352
+ this.indicatorLeft.set(buttonRect.left - parentRect.left);
353
+ this.indicatorWidth.set(buttonRect.width);
354
+ }
355
+ }
356
+ }
357
+
358
+ setActiveTab(tab: TabType) {
359
+ this.activeTab.set(tab);
360
+ }
361
+
362
+ toggleBackground() {
363
+ this.useThemeBg.update(v => !v);
364
+ }
365
+
366
+ toggleFullscreen() {
367
+ this.isFullscreen.update(v => !v);
368
+ }
369
+ }
@@ -0,0 +1,80 @@
1
+ import {
2
+ Component,
3
+ ChangeDetectionStrategy,
4
+ ViewEncapsulation,
5
+ input,
6
+ } from '@angular/core';
7
+ import { SngCodeBlock, type ShikiTheme } from '../code-block/sng-code-block';
8
+
9
+ /**
10
+ * SngStyleBlock - CSS/Tailwind styles for Style tab in preview-box.
11
+ *
12
+ * Displays syntax-highlighted CSS or style code in the Style tab of preview-box.
13
+ * Wraps sng-code-block internally for consistent styling.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <sng-preview-box>
18
+ * <sng-preview-block>
19
+ * <div class="custom-card">Card content</div>
20
+ * </sng-preview-block>
21
+ * <sng-html-block [code]="htmlCode" />
22
+ * <sng-style-block [code]="cssStyles" [theme]="'github-dark'" />
23
+ * </sng-preview-box>
24
+ * ```
25
+ */
26
+ @Component({
27
+ selector: 'sng-style-block',
28
+ standalone: true,
29
+ imports: [SngCodeBlock],
30
+ changeDetection: ChangeDetectionStrategy.OnPush,
31
+ encapsulation: ViewEncapsulation.None,
32
+ host: {},
33
+ styles: [`
34
+ sng-style-block {
35
+ display: flex;
36
+ flex-direction: column;
37
+ height: 100%;
38
+ }
39
+ sng-style-block sng-code-block {
40
+ display: flex;
41
+ flex-direction: column;
42
+ height: 100%;
43
+ }
44
+ sng-style-block .code-container {
45
+ display: flex;
46
+ flex-direction: column;
47
+ height: 100%;
48
+ border: none;
49
+ border-radius: 0;
50
+ }
51
+ sng-style-block .code-content {
52
+ flex: 1;
53
+ overflow: auto;
54
+ }
55
+ `],
56
+ template: `
57
+ <sng-code-block
58
+ [code]="code()"
59
+ [language]="language()"
60
+ [theme]="theme()"
61
+ [showHeader]="false"
62
+ />
63
+ `
64
+ })
65
+ export class SngStyleBlock {
66
+ /**
67
+ * The CSS/style code string to display with syntax highlighting.
68
+ */
69
+ code = input.required<string>();
70
+
71
+ /**
72
+ * The language for syntax highlighting.
73
+ */
74
+ language = input<string>('css');
75
+
76
+ /**
77
+ * The Shiki theme for syntax highlighting.
78
+ */
79
+ theme = input<ShikiTheme>('github-light');
80
+ }
@@ -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 @@
1
+ export { SngProgress } from './sng-progress';