@pattern-stack/frontend-patterns 0.0.3 → 0.0.5

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 (406) hide show
  1. package/dist/frontend-patterns.css +1 -1
  2. package/dist/index.es.js +1918 -3
  3. package/dist/index.es.js.map +1 -1
  4. package/dist/index.js +1917 -1
  5. package/dist/index.js.map +1 -1
  6. package/package.json +10 -5
  7. package/src/App.css +42 -0
  8. package/src/App.tsx +64 -0
  9. package/src/__tests__/README.md +221 -0
  10. package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
  11. package/src/__tests__/atoms/ui/button.test.tsx +68 -0
  12. package/src/__tests__/atoms/utils/simple.test.ts +18 -0
  13. package/src/__tests__/atoms/utils/utils.test.ts +77 -0
  14. package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
  15. package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
  16. package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
  17. package/src/__tests__/setup.ts +51 -0
  18. package/src/__tests__/utils.tsx +123 -0
  19. package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
  20. package/{dist/atoms/composed/Accordion/index.d.ts → src/atoms/composed/Accordion/index.ts} +1 -2
  21. package/src/atoms/composed/Alert/Alert.tsx +132 -0
  22. package/src/atoms/composed/Alert/index.ts +1 -0
  23. package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
  24. package/src/atoms/composed/Breadcrumb/index.ts +1 -0
  25. package/src/atoms/composed/Chart/Chart.tsx +425 -0
  26. package/{dist/atoms/composed/Chart/index.d.ts → src/atoms/composed/Chart/index.ts} +1 -2
  27. package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
  28. package/{dist/atoms/composed/ColorSwatch/index.d.ts → src/atoms/composed/ColorSwatch/index.ts} +1 -2
  29. package/src/atoms/composed/DarkModeToggle.tsx +66 -0
  30. package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
  31. package/src/atoms/composed/DataBadge/index.ts +1 -0
  32. package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
  33. package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
  34. package/src/atoms/composed/DataTable/index.ts +2 -0
  35. package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
  36. package/src/atoms/composed/DateTimePicker/index.ts +2 -0
  37. package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
  38. package/src/atoms/composed/DetailedCard/index.ts +2 -0
  39. package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
  40. package/src/atoms/composed/EmptyState/index.ts +1 -0
  41. package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
  42. package/{dist/atoms/composed/FileUpload/index.d.ts → src/atoms/composed/FileUpload/index.ts} +1 -2
  43. package/src/atoms/composed/FormField/FormField.tsx +92 -0
  44. package/src/atoms/composed/FormField/index.ts +1 -0
  45. package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
  46. package/src/atoms/composed/GlobalSearch/index.ts +1 -0
  47. package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
  48. package/src/atoms/composed/IconBadge/index.ts +2 -0
  49. package/src/atoms/composed/Modal/Modal.tsx +223 -0
  50. package/src/atoms/composed/Modal/index.ts +2 -0
  51. package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
  52. package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
  53. package/{dist/atoms/composed/ProgressBar/index.d.ts → src/atoms/composed/ProgressBar/index.ts} +1 -2
  54. package/src/atoms/composed/SalesPanel/SalesPanel.tsx +116 -0
  55. package/src/atoms/composed/SalesPanel/index.ts +1 -0
  56. package/src/atoms/composed/SalesPanel/mockSalesData.ts +151 -0
  57. package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
  58. package/src/atoms/composed/StatCard/index.ts +1 -0
  59. package/src/atoms/composed/StyleGuide.tsx +717 -0
  60. package/src/atoms/composed/Toast/Toast.tsx +219 -0
  61. package/{dist/atoms/composed/Toast/index.d.ts → src/atoms/composed/Toast/index.ts} +1 -2
  62. package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
  63. package/src/atoms/composed/Tooltip/index.ts +1 -0
  64. package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
  65. package/src/atoms/composed/UserAvatar/index.ts +1 -0
  66. package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
  67. package/src/atoms/composed/UserMenu/index.ts +1 -0
  68. package/{dist/atoms/composed/index.d.ts → src/atoms/composed/index.ts} +7 -2
  69. package/src/atoms/hooks/useApi.ts +80 -0
  70. package/src/atoms/hooks/useHealth.ts +17 -0
  71. package/{dist/atoms/index.d.ts → src/atoms/index.ts} +6 -2
  72. package/src/atoms/services/api/client.ts +134 -0
  73. package/src/atoms/services/auth-service.ts +248 -0
  74. package/src/atoms/services/health.ts +15 -0
  75. package/{dist/atoms/services/index.d.ts → src/atoms/services/index.ts} +1 -2
  76. package/src/atoms/shared/config/constants.ts +17 -0
  77. package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
  78. package/src/atoms/shared/config/environment.ts +10 -0
  79. package/src/atoms/shared/index.ts +4 -0
  80. package/src/atoms/shared/styles/color-palettes.css +566 -0
  81. package/src/atoms/types/auth.ts +62 -0
  82. package/src/atoms/types/entity-config.ts +127 -0
  83. package/{dist/atoms/types/generated.d.ts → src/atoms/types/generated.ts} +1 -1
  84. package/{dist/atoms/types/index.d.ts → src/atoms/types/index.ts} +2 -1
  85. package/{dist/atoms/types/loading.d.ts → src/atoms/types/loading.ts} +10 -8
  86. package/src/atoms/ui/Badge.tsx +30 -0
  87. package/src/atoms/ui/ErrorBoundary.tsx +59 -0
  88. package/src/atoms/ui/Select.tsx +53 -0
  89. package/src/atoms/ui/Switch.tsx +42 -0
  90. package/src/atoms/ui/Tabs.tsx +118 -0
  91. package/src/atoms/ui/avatar.tsx +48 -0
  92. package/src/atoms/ui/button.tsx +70 -0
  93. package/src/atoms/ui/card.tsx +76 -0
  94. package/src/atoms/ui/dropdown-menu.tsx +199 -0
  95. package/{dist/atoms/ui/index.d.ts → src/atoms/ui/index.ts} +27 -3
  96. package/src/atoms/ui/input.tsx +23 -0
  97. package/src/atoms/ui/label.tsx +23 -0
  98. package/src/atoms/ui/skeleton.tsx +13 -0
  99. package/src/atoms/ui/spinner.tsx +49 -0
  100. package/src/atoms/ui/table.tsx +116 -0
  101. package/src/atoms/utils/animations.ts +135 -0
  102. package/src/atoms/utils/metric-engine.ts +236 -0
  103. package/src/atoms/utils/tooltip-helpers.ts +140 -0
  104. package/src/atoms/utils/utils.ts +10 -0
  105. package/src/features/auth/components/LoginForm.tsx +168 -0
  106. package/src/features/auth/components/LogoutButton.tsx +19 -0
  107. package/src/features/auth/components/ProtectedRoute.tsx +60 -0
  108. package/{dist/features/auth/components/index.d.ts → src/features/auth/components/index.ts} +1 -1
  109. package/src/features/auth/hooks/index.ts +2 -0
  110. package/src/features/auth/hooks/useAuth.tsx +205 -0
  111. package/src/features/auth/hooks/usePermissions.ts +35 -0
  112. package/src/features/auth/index.ts +2 -0
  113. package/src/features/index.ts +2 -0
  114. package/src/index.css +704 -0
  115. package/{dist/index.d.ts → src/index.ts} +5 -2
  116. package/src/main.tsx +48 -0
  117. package/src/molecules/.gitkeep +0 -0
  118. package/src/molecules/forms/FormGroup.tsx +75 -0
  119. package/src/molecules/forms/SearchInput.tsx +259 -0
  120. package/src/molecules/forms/index.ts +4 -0
  121. package/src/molecules/index.ts +4 -0
  122. package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
  123. package/src/molecules/layout/AppHeader/index.ts +1 -0
  124. package/src/molecules/layout/AppLayout.tsx +29 -0
  125. package/src/molecules/layout/DashboardWithSidePanel/DashboardWithSidePanel.tsx +42 -0
  126. package/src/molecules/layout/DashboardWithSidePanel/index.ts +1 -0
  127. package/src/molecules/layout/PageTemplate.tsx +87 -0
  128. package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
  129. package/{dist/molecules/layout/SectionHeader/index.d.ts → src/molecules/layout/SectionHeader/index.ts} +1 -2
  130. package/src/molecules/layout/ShowcaseSection.tsx +57 -0
  131. package/src/molecules/layout/Sidebar.tsx +152 -0
  132. package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
  133. package/src/molecules/layout/SidebarButton/index.ts +1 -0
  134. package/src/molecules/layout/SidebarContext.tsx +31 -0
  135. package/{dist/molecules/layout/index.d.ts → src/molecules/layout/index.ts} +2 -2
  136. package/src/molecules/navigation/NavMenu.tsx +188 -0
  137. package/src/molecules/navigation/Pagination.tsx +172 -0
  138. package/src/molecules/navigation/index.ts +4 -0
  139. package/src/organisms/entity/CategoryBreakdownPanel.tsx +427 -0
  140. package/src/organisms/entity/EntityListPanel.tsx +339 -0
  141. package/src/organisms/entity/MetricsOverviewPanel.tsx +236 -0
  142. package/src/organisms/entity/TrendAnalysisPanel.tsx +337 -0
  143. package/src/organisms/entity/index.ts +4 -0
  144. package/src/organisms/index.ts +8 -0
  145. package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
  146. package/src/organisms/showcase/index.ts +1 -0
  147. package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
  148. package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +173 -0
  149. package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
  150. package/src/pages/AdminShowcase/SalesPerformanceDashboard.tsx +158 -0
  151. package/src/pages/AdminShowcase/index.tsx +4 -0
  152. package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
  153. package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
  154. package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
  155. package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
  156. package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
  157. package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
  158. package/src/pages/ComponentShowcase/index.tsx +188 -0
  159. package/src/pages/EntityShowcase/EntityManagementShowcase.tsx +137 -0
  160. package/src/pages/EntityShowcase/EntityPerformanceShowcase.tsx +117 -0
  161. package/src/pages/EntityShowcase/index.ts +2 -0
  162. package/src/pages/EntityTemplateExample.tsx +229 -0
  163. package/src/pages/TestEntityTemplate.tsx +40 -0
  164. package/src/pages/index.ts +3 -0
  165. package/src/templates/AuthTemplate.tsx +216 -0
  166. package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
  167. package/src/templates/DashboardTemplate.tsx +232 -0
  168. package/src/templates/DataTemplate.tsx +319 -0
  169. package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
  170. package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
  171. package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
  172. package/src/templates/admin/index.ts +29 -0
  173. package/src/templates/entity/EntityManagementTemplate.tsx +430 -0
  174. package/src/templates/entity/EntityPerformanceDashboardTemplate.tsx +277 -0
  175. package/src/templates/entity/configs/financial-config.ts +141 -0
  176. package/src/templates/entity/configs/index.ts +1 -0
  177. package/src/templates/entity/index.ts +3 -0
  178. package/src/templates/factory.tsx +169 -0
  179. package/src/templates/financial/FinancialDashboardTemplate.tsx +326 -0
  180. package/src/templates/index.ts +40 -0
  181. package/src/vite-env.d.ts +1 -0
  182. package/dist/atoms/composed/Accordion/Accordion.d.ts +0 -20
  183. package/dist/atoms/composed/Accordion/Accordion.d.ts.map +0 -1
  184. package/dist/atoms/composed/Accordion/index.d.ts.map +0 -1
  185. package/dist/atoms/composed/Alert/Alert.d.ts +0 -25
  186. package/dist/atoms/composed/Alert/Alert.d.ts.map +0 -1
  187. package/dist/atoms/composed/Alert/index.d.ts +0 -2
  188. package/dist/atoms/composed/Alert/index.d.ts.map +0 -1
  189. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +0 -17
  190. package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +0 -1
  191. package/dist/atoms/composed/Breadcrumb/index.d.ts +0 -2
  192. package/dist/atoms/composed/Breadcrumb/index.d.ts.map +0 -1
  193. package/dist/atoms/composed/Chart/Chart.d.ts +0 -37
  194. package/dist/atoms/composed/Chart/Chart.d.ts.map +0 -1
  195. package/dist/atoms/composed/Chart/index.d.ts.map +0 -1
  196. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +0 -19
  197. package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +0 -1
  198. package/dist/atoms/composed/ColorSwatch/index.d.ts.map +0 -1
  199. package/dist/atoms/composed/DarkModeToggle.d.ts +0 -4
  200. package/dist/atoms/composed/DarkModeToggle.d.ts.map +0 -1
  201. package/dist/atoms/composed/DataBadge/DataBadge.d.ts +0 -13
  202. package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +0 -1
  203. package/dist/atoms/composed/DataBadge/index.d.ts +0 -2
  204. package/dist/atoms/composed/DataBadge/index.d.ts.map +0 -1
  205. package/dist/atoms/composed/DataTable/DataTable.d.ts +0 -28
  206. package/dist/atoms/composed/DataTable/DataTable.d.ts.map +0 -1
  207. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +0 -10
  208. package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +0 -1
  209. package/dist/atoms/composed/DataTable/index.d.ts +0 -3
  210. package/dist/atoms/composed/DataTable/index.d.ts.map +0 -1
  211. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +0 -45
  212. package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +0 -1
  213. package/dist/atoms/composed/DateTimePicker/index.d.ts +0 -3
  214. package/dist/atoms/composed/DateTimePicker/index.d.ts.map +0 -1
  215. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +0 -30
  216. package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +0 -1
  217. package/dist/atoms/composed/DetailedCard/index.d.ts +0 -3
  218. package/dist/atoms/composed/DetailedCard/index.d.ts.map +0 -1
  219. package/dist/atoms/composed/EmptyState/EmptyState.d.ts +0 -18
  220. package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +0 -1
  221. package/dist/atoms/composed/EmptyState/index.d.ts +0 -2
  222. package/dist/atoms/composed/EmptyState/index.d.ts.map +0 -1
  223. package/dist/atoms/composed/FileUpload/FileUpload.d.ts +0 -46
  224. package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +0 -1
  225. package/dist/atoms/composed/FileUpload/index.d.ts.map +0 -1
  226. package/dist/atoms/composed/FormField/FormField.d.ts +0 -23
  227. package/dist/atoms/composed/FormField/FormField.d.ts.map +0 -1
  228. package/dist/atoms/composed/FormField/index.d.ts +0 -2
  229. package/dist/atoms/composed/FormField/index.d.ts.map +0 -1
  230. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +0 -8
  231. package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +0 -1
  232. package/dist/atoms/composed/GlobalSearch/index.d.ts +0 -2
  233. package/dist/atoms/composed/GlobalSearch/index.d.ts.map +0 -1
  234. package/dist/atoms/composed/IconBadge/IconBadge.d.ts +0 -16
  235. package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +0 -1
  236. package/dist/atoms/composed/IconBadge/index.d.ts +0 -3
  237. package/dist/atoms/composed/IconBadge/index.d.ts.map +0 -1
  238. package/dist/atoms/composed/Modal/Modal.d.ts +0 -18
  239. package/dist/atoms/composed/Modal/Modal.d.ts.map +0 -1
  240. package/dist/atoms/composed/Modal/index.d.ts +0 -3
  241. package/dist/atoms/composed/Modal/index.d.ts.map +0 -1
  242. package/dist/atoms/composed/PaletteSwitcher.d.ts +0 -7
  243. package/dist/atoms/composed/PaletteSwitcher.d.ts.map +0 -1
  244. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +0 -25
  245. package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +0 -1
  246. package/dist/atoms/composed/ProgressBar/index.d.ts.map +0 -1
  247. package/dist/atoms/composed/StatCard/StatCard.d.ts +0 -21
  248. package/dist/atoms/composed/StatCard/StatCard.d.ts.map +0 -1
  249. package/dist/atoms/composed/StatCard/index.d.ts +0 -2
  250. package/dist/atoms/composed/StatCard/index.d.ts.map +0 -1
  251. package/dist/atoms/composed/StyleGuide.d.ts +0 -3
  252. package/dist/atoms/composed/StyleGuide.d.ts.map +0 -1
  253. package/dist/atoms/composed/Toast/Toast.d.ts +0 -40
  254. package/dist/atoms/composed/Toast/Toast.d.ts.map +0 -1
  255. package/dist/atoms/composed/Toast/index.d.ts.map +0 -1
  256. package/dist/atoms/composed/Tooltip/Tooltip.d.ts +0 -16
  257. package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +0 -1
  258. package/dist/atoms/composed/Tooltip/index.d.ts +0 -2
  259. package/dist/atoms/composed/Tooltip/index.d.ts.map +0 -1
  260. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +0 -8
  261. package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +0 -1
  262. package/dist/atoms/composed/UserAvatar/index.d.ts +0 -2
  263. package/dist/atoms/composed/UserAvatar/index.d.ts.map +0 -1
  264. package/dist/atoms/composed/UserMenu/UserMenu.d.ts +0 -8
  265. package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +0 -1
  266. package/dist/atoms/composed/UserMenu/index.d.ts +0 -2
  267. package/dist/atoms/composed/UserMenu/index.d.ts.map +0 -1
  268. package/dist/atoms/composed/index.d.ts.map +0 -1
  269. package/dist/atoms/hooks/useApi.d.ts +0 -25
  270. package/dist/atoms/hooks/useApi.d.ts.map +0 -1
  271. package/dist/atoms/hooks/useHealth.d.ts +0 -19
  272. package/dist/atoms/hooks/useHealth.d.ts.map +0 -1
  273. package/dist/atoms/index.d.ts.map +0 -1
  274. package/dist/atoms/services/api/client.d.ts +0 -20
  275. package/dist/atoms/services/api/client.d.ts.map +0 -1
  276. package/dist/atoms/services/auth-service.d.ts +0 -24
  277. package/dist/atoms/services/auth-service.d.ts.map +0 -1
  278. package/dist/atoms/services/health.d.ts +0 -7
  279. package/dist/atoms/services/health.d.ts.map +0 -1
  280. package/dist/atoms/services/index.d.ts.map +0 -1
  281. package/dist/atoms/shared/config/constants.d.ts +0 -15
  282. package/dist/atoms/shared/config/constants.d.ts.map +0 -1
  283. package/dist/atoms/shared/config/dashboard-sizes.d.ts +0 -83
  284. package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +0 -1
  285. package/dist/atoms/shared/config/environment.d.ts +0 -10
  286. package/dist/atoms/shared/config/environment.d.ts.map +0 -1
  287. package/dist/atoms/shared/index.d.ts +0 -4
  288. package/dist/atoms/shared/index.d.ts.map +0 -1
  289. package/dist/atoms/types/auth.d.ts +0 -56
  290. package/dist/atoms/types/auth.d.ts.map +0 -1
  291. package/dist/atoms/types/generated.d.ts.map +0 -1
  292. package/dist/atoms/types/index.d.ts.map +0 -1
  293. package/dist/atoms/types/loading.d.ts.map +0 -1
  294. package/dist/atoms/ui/Badge.d.ts +0 -10
  295. package/dist/atoms/ui/Badge.d.ts.map +0 -1
  296. package/dist/atoms/ui/ErrorBoundary.d.ts +0 -18
  297. package/dist/atoms/ui/ErrorBoundary.d.ts.map +0 -1
  298. package/dist/atoms/ui/Select.d.ts +0 -28
  299. package/dist/atoms/ui/Select.d.ts.map +0 -1
  300. package/dist/atoms/ui/Switch.d.ts +0 -9
  301. package/dist/atoms/ui/Switch.d.ts.map +0 -1
  302. package/dist/atoms/ui/Tabs.d.ts +0 -30
  303. package/dist/atoms/ui/Tabs.d.ts.map +0 -1
  304. package/dist/atoms/ui/avatar.d.ts +0 -7
  305. package/dist/atoms/ui/avatar.d.ts.map +0 -1
  306. package/dist/atoms/ui/button.d.ts +0 -14
  307. package/dist/atoms/ui/button.d.ts.map +0 -1
  308. package/dist/atoms/ui/card.d.ts +0 -12
  309. package/dist/atoms/ui/card.d.ts.map +0 -1
  310. package/dist/atoms/ui/dropdown-menu.d.ts +0 -28
  311. package/dist/atoms/ui/dropdown-menu.d.ts.map +0 -1
  312. package/dist/atoms/ui/index.d.ts.map +0 -1
  313. package/dist/atoms/ui/input.d.ts +0 -5
  314. package/dist/atoms/ui/input.d.ts.map +0 -1
  315. package/dist/atoms/ui/label.d.ts +0 -6
  316. package/dist/atoms/ui/label.d.ts.map +0 -1
  317. package/dist/atoms/ui/skeleton.d.ts +0 -3
  318. package/dist/atoms/ui/skeleton.d.ts.map +0 -1
  319. package/dist/atoms/ui/spinner.d.ts +0 -14
  320. package/dist/atoms/ui/spinner.d.ts.map +0 -1
  321. package/dist/atoms/ui/table.d.ts +0 -11
  322. package/dist/atoms/ui/table.d.ts.map +0 -1
  323. package/dist/atoms/utils/animations.d.ts +0 -65
  324. package/dist/atoms/utils/animations.d.ts.map +0 -1
  325. package/dist/atoms/utils/tooltip-helpers.d.ts +0 -71
  326. package/dist/atoms/utils/tooltip-helpers.d.ts.map +0 -1
  327. package/dist/atoms/utils/utils.d.ts +0 -4
  328. package/dist/atoms/utils/utils.d.ts.map +0 -1
  329. package/dist/features/auth/components/LoginForm.d.ts +0 -2
  330. package/dist/features/auth/components/LoginForm.d.ts.map +0 -1
  331. package/dist/features/auth/components/LogoutButton.d.ts +0 -2
  332. package/dist/features/auth/components/LogoutButton.d.ts.map +0 -1
  333. package/dist/features/auth/components/ProtectedRoute.d.ts +0 -10
  334. package/dist/features/auth/components/ProtectedRoute.d.ts.map +0 -1
  335. package/dist/features/auth/components/index.d.ts.map +0 -1
  336. package/dist/features/auth/hooks/index.d.ts +0 -3
  337. package/dist/features/auth/hooks/index.d.ts.map +0 -1
  338. package/dist/features/auth/hooks/useAuth.d.ts +0 -10
  339. package/dist/features/auth/hooks/useAuth.d.ts.map +0 -1
  340. package/dist/features/auth/hooks/usePermissions.d.ts +0 -13
  341. package/dist/features/auth/hooks/usePermissions.d.ts.map +0 -1
  342. package/dist/features/auth/index.d.ts +0 -3
  343. package/dist/features/auth/index.d.ts.map +0 -1
  344. package/dist/features/index.d.ts +0 -2
  345. package/dist/features/index.d.ts.map +0 -1
  346. package/dist/index.d.ts.map +0 -1
  347. package/dist/molecules/forms/FormGroup.d.ts +0 -17
  348. package/dist/molecules/forms/FormGroup.d.ts.map +0 -1
  349. package/dist/molecules/forms/SearchInput.d.ts +0 -36
  350. package/dist/molecules/forms/SearchInput.d.ts.map +0 -1
  351. package/dist/molecules/forms/index.d.ts +0 -3
  352. package/dist/molecules/forms/index.d.ts.map +0 -1
  353. package/dist/molecules/index.d.ts +0 -4
  354. package/dist/molecules/index.d.ts.map +0 -1
  355. package/dist/molecules/layout/AppHeader/AppHeader.d.ts +0 -7
  356. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +0 -1
  357. package/dist/molecules/layout/AppHeader/index.d.ts +0 -2
  358. package/dist/molecules/layout/AppHeader/index.d.ts.map +0 -1
  359. package/dist/molecules/layout/AppLayout.d.ts +0 -2
  360. package/dist/molecules/layout/AppLayout.d.ts.map +0 -1
  361. package/dist/molecules/layout/PageTemplate.d.ts +0 -19
  362. package/dist/molecules/layout/PageTemplate.d.ts.map +0 -1
  363. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +0 -24
  364. package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +0 -1
  365. package/dist/molecules/layout/SectionHeader/index.d.ts.map +0 -1
  366. package/dist/molecules/layout/ShowcaseSection.d.ts +0 -22
  367. package/dist/molecules/layout/ShowcaseSection.d.ts.map +0 -1
  368. package/dist/molecules/layout/Sidebar.d.ts +0 -6
  369. package/dist/molecules/layout/Sidebar.d.ts.map +0 -1
  370. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +0 -13
  371. package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +0 -1
  372. package/dist/molecules/layout/SidebarButton/index.d.ts +0 -2
  373. package/dist/molecules/layout/SidebarButton/index.d.ts.map +0 -1
  374. package/dist/molecules/layout/SidebarContext.d.ts +0 -12
  375. package/dist/molecules/layout/SidebarContext.d.ts.map +0 -1
  376. package/dist/molecules/layout/index.d.ts.map +0 -1
  377. package/dist/molecules/navigation/NavMenu.d.ts +0 -20
  378. package/dist/molecules/navigation/NavMenu.d.ts.map +0 -1
  379. package/dist/molecules/navigation/Pagination.d.ts +0 -14
  380. package/dist/molecules/navigation/Pagination.d.ts.map +0 -1
  381. package/dist/molecules/navigation/index.d.ts +0 -3
  382. package/dist/molecules/navigation/index.d.ts.map +0 -1
  383. package/dist/organisms/index.d.ts +0 -2
  384. package/dist/organisms/index.d.ts.map +0 -1
  385. package/dist/organisms/showcase/ComponentShowcasePage.d.ts +0 -3
  386. package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +0 -1
  387. package/dist/templates/AuthTemplate.d.ts +0 -68
  388. package/dist/templates/AuthTemplate.d.ts.map +0 -1
  389. package/dist/templates/ComponentShowcaseTemplate.d.ts +0 -53
  390. package/dist/templates/ComponentShowcaseTemplate.d.ts.map +0 -1
  391. package/dist/templates/DashboardTemplate.d.ts +0 -62
  392. package/dist/templates/DashboardTemplate.d.ts.map +0 -1
  393. package/dist/templates/DataTemplate.d.ts +0 -78
  394. package/dist/templates/DataTemplate.d.ts.map +0 -1
  395. package/dist/templates/admin/AdminCRUDTemplate.d.ts +0 -105
  396. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +0 -1
  397. package/dist/templates/admin/AdminDashboardTemplate.d.ts +0 -89
  398. package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +0 -1
  399. package/dist/templates/admin/AdminDetailTemplate.d.ts +0 -132
  400. package/dist/templates/admin/AdminDetailTemplate.d.ts.map +0 -1
  401. package/dist/templates/admin/index.d.ts +0 -4
  402. package/dist/templates/admin/index.d.ts.map +0 -1
  403. package/dist/templates/factory.d.ts +0 -28
  404. package/dist/templates/factory.d.ts.map +0 -1
  405. package/dist/templates/index.d.ts +0 -7
  406. package/dist/templates/index.d.ts.map +0 -1
@@ -0,0 +1,81 @@
1
+ import React from 'react'
2
+ import { describe, it, expect } from 'vitest'
3
+ import { render, screen } from '../../utils'
4
+
5
+ // Simple layout component for testing
6
+ const SimpleHeader = ({ title, showNav = true }: { title: string; showNav?: boolean }) => (
7
+ <header role="banner" className="bg-background border-b border-border">
8
+ <div className="flex items-center justify-between p-4">
9
+ <h1 className="text-xl font-semibold text-foreground">{title}</h1>
10
+ {showNav && (
11
+ <nav aria-label="Main navigation">
12
+ <ul className="flex space-x-4">
13
+ <li>
14
+ <a href="/" className="text-primary hover:text-primary-hover">Home</a>
15
+ </li>
16
+ <li>
17
+ <a href="/about" className="text-primary hover:text-primary-hover">About</a>
18
+ </li>
19
+ </ul>
20
+ </nav>
21
+ )}
22
+ </div>
23
+ </header>
24
+ )
25
+
26
+ describe('Layout Components (Molecule)', () => {
27
+ it('renders header with title', () => {
28
+ render(<SimpleHeader title="Test App" />)
29
+
30
+ const header = screen.getByRole('banner')
31
+ expect(header).toBeInTheDocument()
32
+
33
+ const title = screen.getByRole('heading', { level: 1 })
34
+ expect(title).toHaveTextContent('Test App')
35
+ })
36
+
37
+ it('renders navigation when showNav is true', () => {
38
+ render(<SimpleHeader title="Test App" showNav={true} />)
39
+
40
+ const nav = screen.getByRole('navigation', { name: /main navigation/i })
41
+ expect(nav).toBeInTheDocument()
42
+
43
+ expect(screen.getByRole('link', { name: /home/i })).toBeInTheDocument()
44
+ expect(screen.getByRole('link', { name: /about/i })).toBeInTheDocument()
45
+ })
46
+
47
+ it('hides navigation when showNav is false', () => {
48
+ render(<SimpleHeader title="Test App" showNav={false} />)
49
+
50
+ const nav = screen.queryByRole('navigation')
51
+ expect(nav).not.toBeInTheDocument()
52
+ })
53
+
54
+ it('uses design system classes', () => {
55
+ render(<SimpleHeader title="Test App" />)
56
+
57
+ const header = screen.getByRole('banner')
58
+
59
+ // Should use design system classes
60
+ expect(header).toHaveClass('bg-background')
61
+ expect(header).toHaveClass('border-border')
62
+
63
+ // Should not use hardcoded color classes
64
+ expect(header.className).not.toMatch(/bg-(blue|red|green)-\d+/)
65
+ })
66
+
67
+ it('has proper accessibility attributes', () => {
68
+ render(<SimpleHeader title="Test App" />)
69
+
70
+ const header = screen.getByRole('banner')
71
+ expect(header).toBeInTheDocument()
72
+
73
+ const nav = screen.getByRole('navigation')
74
+ expect(nav).toHaveAttribute('aria-label', 'Main navigation')
75
+
76
+ const links = screen.getAllByRole('link')
77
+ links.forEach(link => {
78
+ expect(link).toHaveAttribute('href')
79
+ })
80
+ })
81
+ })
@@ -0,0 +1,167 @@
1
+ import React from 'react'
2
+ import { describe, it, expect } from 'vitest'
3
+ import { render, screen, userEvent } from '../../utils'
4
+ import { cn } from '../../../atoms/utils/utils'
5
+
6
+ // Simple showcase component for testing
7
+ const SimpleShowcase = () => {
8
+ const [activeTab, setActiveTab] = React.useState('components')
9
+
10
+ return (
11
+ <main className="p-6">
12
+ <h1 className="text-3xl font-bold text-foreground mb-6">Component Showcase</h1>
13
+
14
+ <div className="mb-6">
15
+ <nav aria-label="Showcase navigation">
16
+ <div role="tablist" className="flex space-x-4 border-b border-border">
17
+ <button
18
+ role="tab"
19
+ aria-selected={activeTab === 'components'}
20
+ className={cn(
21
+ 'px-4 py-2 font-medium',
22
+ activeTab === 'components'
23
+ ? 'text-primary border-b-2 border-primary'
24
+ : 'text-muted-foreground hover:text-foreground'
25
+ )}
26
+ onClick={() => setActiveTab('components')}
27
+ >
28
+ Components
29
+ </button>
30
+ <button
31
+ role="tab"
32
+ aria-selected={activeTab === 'colors'}
33
+ className={cn(
34
+ 'px-4 py-2 font-medium',
35
+ activeTab === 'colors'
36
+ ? 'text-primary border-b-2 border-primary'
37
+ : 'text-muted-foreground hover:text-foreground'
38
+ )}
39
+ onClick={() => setActiveTab('colors')}
40
+ >
41
+ Colors
42
+ </button>
43
+ </div>
44
+ </nav>
45
+ </div>
46
+
47
+ <div role="tabpanel">
48
+ {activeTab === 'components' && (
49
+ <div data-testid="components-panel">
50
+ <h2 className="text-xl font-semibold mb-4">UI Components</h2>
51
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
52
+ <button className="bg-primary text-primary-foreground px-4 py-2 rounded">
53
+ Primary Button
54
+ </button>
55
+ <button className="bg-secondary text-secondary-foreground px-4 py-2 rounded">
56
+ Secondary Button
57
+ </button>
58
+ </div>
59
+ </div>
60
+ )}
61
+
62
+ {activeTab === 'colors' && (
63
+ <div data-testid="colors-panel">
64
+ <h2 className="text-xl font-semibold mb-4">Color Palette</h2>
65
+ <div className="grid grid-cols-4 gap-2">
66
+ <div className="bg-primary h-12 rounded flex items-center justify-center text-primary-foreground text-xs">
67
+ Primary
68
+ </div>
69
+ <div className="bg-secondary h-12 rounded flex items-center justify-center text-secondary-foreground text-xs">
70
+ Secondary
71
+ </div>
72
+ </div>
73
+ </div>
74
+ )}
75
+ </div>
76
+ </main>
77
+ )
78
+ }
79
+
80
+ describe('Showcase Page (Organism)', () => {
81
+ it('renders main showcase structure', () => {
82
+ render(<SimpleShowcase />)
83
+
84
+ const main = screen.getByRole('main')
85
+ expect(main).toBeInTheDocument()
86
+
87
+ const heading = screen.getByRole('heading', { level: 1 })
88
+ expect(heading).toHaveTextContent('Component Showcase')
89
+ })
90
+
91
+ it('has accessible tab navigation', () => {
92
+ render(<SimpleShowcase />)
93
+
94
+ const tablist = screen.getByRole('tablist')
95
+ expect(tablist).toBeInTheDocument()
96
+
97
+ const componentsTab = screen.getByRole('tab', { name: /components/i })
98
+ const colorsTab = screen.getByRole('tab', { name: /colors/i })
99
+
100
+ expect(componentsTab).toHaveAttribute('aria-selected', 'true')
101
+ expect(colorsTab).toHaveAttribute('aria-selected', 'false')
102
+ })
103
+
104
+ it('switches between tabs correctly', async () => {
105
+ const user = userEvent.setup()
106
+ render(<SimpleShowcase />)
107
+
108
+ // Initially shows components panel
109
+ expect(screen.getByTestId('components-panel')).toBeInTheDocument()
110
+ expect(screen.queryByTestId('colors-panel')).not.toBeInTheDocument()
111
+
112
+ // Click colors tab
113
+ const colorsTab = screen.getByRole('tab', { name: /colors/i })
114
+ await user.click(colorsTab)
115
+
116
+ // Should now show colors panel
117
+ expect(screen.getByTestId('colors-panel')).toBeInTheDocument()
118
+ expect(screen.queryByTestId('components-panel')).not.toBeInTheDocument()
119
+
120
+ // Tab should be selected
121
+ expect(colorsTab).toHaveAttribute('aria-selected', 'true')
122
+ })
123
+
124
+ it('displays interactive components', () => {
125
+ render(<SimpleShowcase />)
126
+
127
+ // Should have interactive buttons in showcase
128
+ const buttons = screen.getAllByRole('button')
129
+ expect(buttons.length).toBeGreaterThanOrEqual(2) // Tabs + showcase buttons
130
+
131
+ const primaryButton = screen.getByText('Primary Button')
132
+ expect(primaryButton).toBeInTheDocument()
133
+ expect(primaryButton).toHaveClass('bg-primary')
134
+ })
135
+
136
+ it('uses design system classes consistently', () => {
137
+ render(<SimpleShowcase />)
138
+
139
+ const main = screen.getByRole('main')
140
+
141
+ // Should use design system spacing and colors
142
+ expect(main).toHaveClass('p-6')
143
+
144
+ const heading = screen.getByRole('heading', { level: 1 })
145
+ expect(heading).toHaveClass('text-foreground')
146
+
147
+ // Check that components use design system colors
148
+ const primaryButton = screen.getByText('Primary Button')
149
+ expect(primaryButton).toHaveClass('bg-primary', 'text-primary-foreground')
150
+ })
151
+
152
+ it('is responsive and accessible', () => {
153
+ render(<SimpleShowcase />)
154
+
155
+ // Should have proper heading hierarchy
156
+ const h1 = screen.getByRole('heading', { level: 1 })
157
+ const h2s = screen.getAllByRole('heading', { level: 2 })
158
+
159
+ expect(h1).toBeInTheDocument()
160
+ expect(h2s.length).toBeGreaterThan(0)
161
+
162
+ // Should have responsive grid classes
163
+ const gridElements = screen.getByTestId('components-panel').querySelector('.grid')
164
+ expect(gridElements?.className).toMatch(/grid-cols-/)
165
+ expect(gridElements?.className).toMatch(/md:grid-cols-/)
166
+ })
167
+ })
@@ -0,0 +1,51 @@
1
+ import '@testing-library/jest-dom'
2
+ import { beforeAll, afterEach, afterAll } from 'vitest'
3
+ import { cleanup } from '@testing-library/react'
4
+
5
+ // Global test setup (equivalent to conftest.py)
6
+
7
+ // Clean up after each test
8
+ afterEach(() => {
9
+ cleanup()
10
+ })
11
+
12
+ // Mock environment variables for testing
13
+ beforeAll(() => {
14
+ // Mock environment variables
15
+ process.env.VITE_API_URL = 'http://localhost:3001/api/v1'
16
+ process.env.VITE_APP_NAME = 'Test App'
17
+
18
+ // Mock window.matchMedia (required for responsive components)
19
+ Object.defineProperty(window, 'matchMedia', {
20
+ writable: true,
21
+ value: (query: string) => ({
22
+ matches: false,
23
+ media: query,
24
+ onchange: null,
25
+ addListener: () => {},
26
+ removeListener: () => {},
27
+ addEventListener: () => {},
28
+ removeEventListener: () => {},
29
+ dispatchEvent: () => {},
30
+ }),
31
+ })
32
+
33
+ // Mock ResizeObserver (required for some components)
34
+ global.ResizeObserver = class ResizeObserver {
35
+ observe() {}
36
+ unobserve() {}
37
+ disconnect() {}
38
+ }
39
+
40
+ // Mock IntersectionObserver (required for some components)
41
+ global.IntersectionObserver = class IntersectionObserver {
42
+ constructor() {}
43
+ observe() {}
44
+ unobserve() {}
45
+ disconnect() {}
46
+ }
47
+ })
48
+
49
+ afterAll(() => {
50
+ // Clean up any global mocks if needed
51
+ })
@@ -0,0 +1,123 @@
1
+ import React from 'react'
2
+ import { render, RenderOptions } from '@testing-library/react'
3
+ import { BrowserRouter } from 'react-router-dom'
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
5
+
6
+ // Test utilities (equivalent to pytest fixtures)
7
+
8
+ /**
9
+ * Custom render function that wraps components with necessary providers
10
+ * Similar to how conftest.py provides fixtures for database, auth, etc.
11
+ */
12
+ interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
13
+ // Add custom options here
14
+ initialRoute?: string
15
+ queryClient?: QueryClient
16
+ }
17
+
18
+ const AllTheProviders = ({
19
+ children,
20
+ initialRoute = '/',
21
+ queryClient
22
+ }: {
23
+ children: React.ReactNode
24
+ initialRoute?: string
25
+ queryClient?: QueryClient
26
+ }) => {
27
+ // Create a fresh QueryClient for each test to avoid test pollution
28
+ const testQueryClient = queryClient || new QueryClient({
29
+ defaultOptions: {
30
+ queries: {
31
+ retry: false, // Don't retry in tests
32
+ gcTime: 0, // Disable garbage collection
33
+ },
34
+ mutations: {
35
+ retry: false,
36
+ },
37
+ },
38
+ })
39
+
40
+ // Set initial route for testing
41
+ if (initialRoute !== '/') {
42
+ window.history.pushState({}, 'Test page', initialRoute)
43
+ }
44
+
45
+ return (
46
+ <QueryClientProvider client={testQueryClient}>
47
+ <BrowserRouter>
48
+ {children}
49
+ </BrowserRouter>
50
+ </QueryClientProvider>
51
+ )
52
+ }
53
+
54
+ const customRender = (
55
+ ui: React.ReactElement,
56
+ options?: CustomRenderOptions
57
+ ) => {
58
+ const { initialRoute, queryClient, ...renderOptions } = options || {}
59
+
60
+ return render(ui, {
61
+ wrapper: (props) => (
62
+ <AllTheProviders
63
+ {...props}
64
+ initialRoute={initialRoute}
65
+ queryClient={queryClient}
66
+ />
67
+ ),
68
+ ...renderOptions,
69
+ })
70
+ }
71
+
72
+ /**
73
+ * Create a mock API client for testing
74
+ * Similar to how you might mock database connections in backend tests
75
+ */
76
+ export const createMockApiClient = () => ({
77
+ get: vi.fn(),
78
+ post: vi.fn(),
79
+ put: vi.fn(),
80
+ delete: vi.fn(),
81
+ patch: vi.fn(),
82
+ })
83
+
84
+ /**
85
+ * Mock user data factory
86
+ * Similar to factory patterns in backend testing
87
+ */
88
+ export const createMockUser = (overrides = {}) => ({
89
+ id: '1',
90
+ name: 'Test User',
91
+ email: 'test@example.com',
92
+ role: 'user',
93
+ ...overrides,
94
+ })
95
+
96
+ /**
97
+ * Mock auth context for testing
98
+ */
99
+ export const createMockAuthContext = (overrides = {}) => ({
100
+ user: createMockUser(),
101
+ isAuthenticated: true,
102
+ login: vi.fn(),
103
+ logout: vi.fn(),
104
+ loading: false,
105
+ ...overrides,
106
+ })
107
+
108
+ /**
109
+ * Helper to test error boundaries
110
+ */
111
+ export const ThrowError = ({ shouldThrow }: { shouldThrow?: boolean }) => {
112
+ if (shouldThrow) {
113
+ throw new Error('Test error')
114
+ }
115
+ return <div>No error</div>
116
+ }
117
+
118
+ // Re-export everything from testing-library
119
+ export * from '@testing-library/react'
120
+ export { default as userEvent } from '@testing-library/user-event'
121
+
122
+ // Export our custom render as the default render
123
+ export { customRender as render }
@@ -0,0 +1,271 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { cn } from '../../utils/utils';
3
+ import { getAnimationClasses } from '../../utils/animations';
4
+ import { ChevronDown, ChevronRight } from 'lucide-react';
5
+
6
+ export interface AccordionItem {
7
+ id: string;
8
+ title: string;
9
+ content: React.ReactNode;
10
+ disabled?: boolean;
11
+ icon?: React.ReactNode;
12
+ }
13
+
14
+ export interface AccordionProps {
15
+ items: AccordionItem[];
16
+ variant?: 'default' | 'bordered' | 'filled';
17
+ allowMultiple?: boolean;
18
+ defaultOpen?: string | string[];
19
+ collapsible?: boolean;
20
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
21
+ className?: string;
22
+ onItemToggle?: (itemId: string, isOpen: boolean) => void;
23
+ }
24
+
25
+ export const Accordion = ({
26
+ items,
27
+ variant = 'default',
28
+ allowMultiple = false,
29
+ defaultOpen = [],
30
+ collapsible = true,
31
+ category,
32
+ className,
33
+ onItemToggle
34
+ }: AccordionProps) => {
35
+ // Initialize openItems state based on defaultOpen
36
+ const [openItems, setOpenItems] = useState<Set<string>>(() => {
37
+ if (Array.isArray(defaultOpen)) {
38
+ return new Set(defaultOpen);
39
+ } else if (defaultOpen) {
40
+ return new Set([defaultOpen]);
41
+ }
42
+ return new Set();
43
+ });
44
+
45
+ const handleItemToggle = (itemId: string) => {
46
+ const isCurrentlyOpen = openItems.has(itemId);
47
+
48
+ if (!allowMultiple) {
49
+ // Single mode: close others, toggle current
50
+ if (isCurrentlyOpen && collapsible) {
51
+ setOpenItems(new Set());
52
+ onItemToggle?.(itemId, false);
53
+ } else if (!isCurrentlyOpen) {
54
+ setOpenItems(new Set([itemId]));
55
+ onItemToggle?.(itemId, true);
56
+ }
57
+ } else {
58
+ // Multiple mode: toggle current
59
+ const newOpenItems = new Set(openItems);
60
+ if (isCurrentlyOpen && collapsible) {
61
+ newOpenItems.delete(itemId);
62
+ onItemToggle?.(itemId, false);
63
+ } else if (!isCurrentlyOpen) {
64
+ newOpenItems.add(itemId);
65
+ onItemToggle?.(itemId, true);
66
+ }
67
+ setOpenItems(newOpenItems);
68
+ }
69
+ };
70
+
71
+ const getVariantClasses = () => {
72
+ switch (variant) {
73
+ case 'bordered':
74
+ return 'border border-border rounded-lg overflow-hidden';
75
+ case 'filled':
76
+ return 'bg-muted/30 rounded-lg overflow-hidden';
77
+ default:
78
+ return 'space-y-2';
79
+ }
80
+ };
81
+
82
+ const getItemClasses = (isOpen: boolean) => {
83
+ const baseClasses = variant === 'default'
84
+ ? 'border border-border rounded-lg overflow-hidden'
85
+ : variant === 'bordered'
86
+ ? 'border-b border-border last:border-b-0'
87
+ : '';
88
+
89
+ return cn(
90
+ baseClasses,
91
+ isOpen && category && variant !== 'filled' && `shadow-category-${category}/20 shadow-lg`,
92
+ 'transition-all duration-200'
93
+ );
94
+ };
95
+
96
+ const getHeaderClasses = (item: AccordionItem, isOpen: boolean) => {
97
+ return cn(
98
+ 'w-full flex items-center justify-between p-4 text-left',
99
+ 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
100
+ 'transition-all duration-200',
101
+ getAnimationClasses({ type: 'subtle', timing: 'normal' }),
102
+
103
+ // Variant-specific styling
104
+ variant === 'filled' && 'bg-muted/50',
105
+ variant === 'default' && 'bg-card hover:bg-muted/30',
106
+ variant === 'bordered' && 'bg-card hover:bg-muted/30',
107
+
108
+ // Category coloring when open
109
+ isOpen && category && `bg-category-${category}/5 border-category-${category}/20`,
110
+
111
+ // Disabled state
112
+ item.disabled && 'opacity-50 cursor-not-allowed',
113
+ !item.disabled && 'cursor-pointer',
114
+
115
+ // Focus and hover states
116
+ !item.disabled && 'hover:bg-accent/20',
117
+
118
+ 'group'
119
+ );
120
+ };
121
+
122
+ const getIconClasses = (isOpen: boolean) => {
123
+ return cn(
124
+ 'w-4 h-4 transition-transform duration-200 flex-shrink-0',
125
+ isOpen ? 'rotate-90' : 'rotate-0',
126
+ category ? `text-category-${category}` : 'text-muted-foreground',
127
+ 'group-hover:scale-110'
128
+ );
129
+ };
130
+
131
+ return (
132
+ <div
133
+ className={cn(
134
+ getVariantClasses(),
135
+ className
136
+ )}
137
+ data-component-name="Accordion"
138
+ data-variant={variant}
139
+ role="tablist"
140
+ >
141
+ {items.map((item) => {
142
+ const isOpen = openItems.has(item.id);
143
+
144
+ return (
145
+ <AccordionItem
146
+ key={item.id}
147
+ item={item}
148
+ isOpen={isOpen}
149
+ onToggle={() => handleItemToggle(item.id)}
150
+ variant={variant}
151
+ category={category}
152
+ itemClasses={getItemClasses(isOpen)}
153
+ headerClasses={getHeaderClasses(item, isOpen)}
154
+ iconClasses={getIconClasses(isOpen)}
155
+ />
156
+ );
157
+ })}
158
+ </div>
159
+ );
160
+ };
161
+
162
+ // Separate AccordionItem component for better performance and cleaner code
163
+ interface AccordionItemComponentProps {
164
+ item: AccordionItem;
165
+ isOpen: boolean;
166
+ onToggle: () => void;
167
+ variant: 'default' | 'bordered' | 'filled';
168
+ category?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
169
+ itemClasses: string;
170
+ headerClasses: string;
171
+ iconClasses: string;
172
+ }
173
+
174
+ const AccordionItem = ({
175
+ item,
176
+ isOpen,
177
+ onToggle,
178
+ variant,
179
+ category,
180
+ itemClasses,
181
+ headerClasses,
182
+ iconClasses
183
+ }: AccordionItemComponentProps) => {
184
+ const contentRef = useRef<HTMLDivElement>(null);
185
+ const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);
186
+
187
+ useEffect(() => {
188
+ if (contentRef.current) {
189
+ if (isOpen) {
190
+ setContentHeight(contentRef.current.scrollHeight);
191
+ } else {
192
+ setContentHeight(0);
193
+ }
194
+ }
195
+ }, [isOpen, item.content]);
196
+
197
+ return (
198
+ <div className={itemClasses}>
199
+ {/* Header */}
200
+ <button
201
+ className={headerClasses}
202
+ onClick={onToggle}
203
+ disabled={item.disabled}
204
+ aria-expanded={isOpen}
205
+ aria-controls={`accordion-content-${item.id}`}
206
+ id={`accordion-header-${item.id}`}
207
+ role="tab"
208
+ data-component-name="AccordionHeader"
209
+ >
210
+ <div className="flex items-center gap-3 flex-1 min-w-0">
211
+ {/* Custom icon or default chevron */}
212
+ {item.icon ? (
213
+ <div className={cn(
214
+ 'flex-shrink-0',
215
+ category ? `text-category-${category}` : 'text-muted-foreground'
216
+ )}>
217
+ {item.icon}
218
+ </div>
219
+ ) : (
220
+ <ChevronRight className={iconClasses} />
221
+ )}
222
+
223
+ {/* Title */}
224
+ <span className={cn(
225
+ 'font-medium text-sm truncate',
226
+ isOpen && category ? `text-category-${category}` : 'text-foreground',
227
+ 'group-hover:text-foreground transition-colors duration-200'
228
+ )}>
229
+ {item.title}
230
+ </span>
231
+ </div>
232
+
233
+ {/* Collapse indicator */}
234
+ <ChevronDown
235
+ className={cn(
236
+ 'w-4 h-4 transition-transform duration-200 flex-shrink-0 ml-2',
237
+ isOpen ? 'rotate-180' : 'rotate-0',
238
+ category && isOpen ? `text-category-${category}` : 'text-muted-foreground',
239
+ 'group-hover:scale-110'
240
+ )}
241
+ data-component-name="AccordionChevron"
242
+ />
243
+ </button>
244
+
245
+ {/* Content */}
246
+ <div
247
+ style={{
248
+ height: contentHeight,
249
+ overflow: 'hidden',
250
+ transition: 'height 250ms cubic-bezier(0.4, 0, 0.2, 1)'
251
+ }}
252
+ aria-labelledby={`accordion-header-${item.id}`}
253
+ id={`accordion-content-${item.id}`}
254
+ role="tabpanel"
255
+ data-component-name="AccordionContent"
256
+ >
257
+ <div
258
+ ref={contentRef}
259
+ className={cn(
260
+ 'p-4 pt-0',
261
+ variant === 'filled' && 'bg-background/50',
262
+ isOpen && category && variant !== 'filled' && `bg-category-${category}/2`,
263
+ 'text-muted-foreground text-sm leading-relaxed'
264
+ )}
265
+ >
266
+ {item.content}
267
+ </div>
268
+ </div>
269
+ </div>
270
+ );
271
+ };
@@ -1,2 +1 @@
1
- export { Accordion, type AccordionProps, type AccordionItem } from './Accordion';
2
- //# sourceMappingURL=index.d.ts.map
1
+ export { Accordion, type AccordionProps, type AccordionItem } from './Accordion';