@alepha/ui 0.18.2 → 0.18.3

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 (231) hide show
  1. package/dist/admin/{AdminApiKeys-BJhIwfD6.js → AdminApiKeys-Dy_k-4Vd.js} +2 -2
  2. package/dist/admin/{AdminApiKeys-BJhIwfD6.js.map → AdminApiKeys-Dy_k-4Vd.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-DzD_4cDt.js → AdminAudits-CKiFMSSU.js} +2 -2
  4. package/dist/admin/{AdminAudits-DzD_4cDt.js.map → AdminAudits-CKiFMSSU.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-C92tIc6x.js → AdminDashboard-PhC_dZqo.js} +2 -2
  6. package/dist/admin/{AdminDashboard-C92tIc6x.js.map → AdminDashboard-PhC_dZqo.js.map} +1 -1
  7. package/dist/admin/{AdminFiles-DLpfhBkf.js → AdminFiles-DFTjijGp.js} +2 -2
  8. package/dist/admin/{AdminFiles-DLpfhBkf.js.map → AdminFiles-DFTjijGp.js.map} +1 -1
  9. package/dist/admin/{AdminJobDashboard-KIOkeMgE.js → AdminJobDashboard-BL8gGPDp.js} +2 -2
  10. package/dist/admin/{AdminJobDashboard-KIOkeMgE.js.map → AdminJobDashboard-BL8gGPDp.js.map} +1 -1
  11. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js → AdminJobExecutions-D9E-CS-U.js} +2 -2
  12. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js.map → AdminJobExecutions-D9E-CS-U.js.map} +1 -1
  13. package/dist/admin/{AdminJobRegistry-PFajqaGK.js → AdminJobRegistry-Ci9ue1zC.js} +2 -2
  14. package/dist/admin/{AdminJobRegistry-PFajqaGK.js.map → AdminJobRegistry-Ci9ue1zC.js.map} +1 -1
  15. package/dist/admin/{AdminLayout-B1DXZHDn.js → AdminLayout-I6TlUMPc.js} +2 -2
  16. package/dist/admin/{AdminLayout-B1DXZHDn.js.map → AdminLayout-I6TlUMPc.js.map} +1 -1
  17. package/dist/admin/AdminNotifications-ZPHCYrv7.js +542 -0
  18. package/dist/admin/AdminNotifications-ZPHCYrv7.js.map +1 -0
  19. package/dist/admin/{AdminParameters-BspPeqp_.js → AdminParameters-CqgvhRsb.js} +120 -105
  20. package/dist/admin/AdminParameters-CqgvhRsb.js.map +1 -0
  21. package/dist/admin/{AdminSessions-BnH5CZQl.js → AdminSessions-Bz5NRuoW.js} +2 -2
  22. package/dist/admin/{AdminSessions-BnH5CZQl.js.map → AdminSessions-Bz5NRuoW.js.map} +1 -1
  23. package/dist/admin/{AdminUserLayout-DUbC6-BI.js → AdminUserLayout-lXT6I0Qq.js} +14 -8
  24. package/dist/admin/AdminUserLayout-lXT6I0Qq.js.map +1 -0
  25. package/dist/admin/{AdminUserProfile-DuTUnjdG.js → AdminUserProfile-vFBLoJ3h.js} +3 -3
  26. package/dist/admin/{AdminUserProfile-DuTUnjdG.js.map → AdminUserProfile-vFBLoJ3h.js.map} +1 -1
  27. package/dist/admin/{AdminUserSessions-DvZdAGpL.js → AdminUserSessions-CT_YDim0.js} +2 -2
  28. package/dist/admin/{AdminUserSessions-DvZdAGpL.js.map → AdminUserSessions-CT_YDim0.js.map} +1 -1
  29. package/dist/admin/{AdminUsers-CR9z0g_5.js → AdminUsers-D1UfGya9.js} +2 -2
  30. package/dist/admin/{AdminUsers-CR9z0g_5.js.map → AdminUsers-D1UfGya9.js.map} +1 -1
  31. package/dist/admin/{AuthLayout-DsUfp9RG.js → AuthLayout-_frhdgOO.js} +2 -2
  32. package/dist/admin/{AuthLayout-DsUfp9RG.js.map → AuthLayout-_frhdgOO.js.map} +1 -1
  33. package/dist/admin/Login-xtNmQtGh.js +275 -0
  34. package/dist/admin/Login-xtNmQtGh.js.map +1 -0
  35. package/dist/admin/{Profile-B2EcIDB9.js → Profile-_AtPUwAP.js} +31 -27
  36. package/dist/admin/Profile-_AtPUwAP.js.map +1 -0
  37. package/dist/admin/{Register-Z3fxRbUF.js → Register-JcCjHUUn.js} +198 -142
  38. package/dist/admin/Register-JcCjHUUn.js.map +1 -0
  39. package/dist/admin/{ResetPassword-_Y1qTTKh.js → ResetPassword-CwGBPLJO.js} +7 -7
  40. package/dist/admin/ResetPassword-CwGBPLJO.js.map +1 -0
  41. package/dist/admin/{VerifyEmail-Bg22bwcC.js → VerifyEmail-hNxWejWf.js} +23 -8
  42. package/dist/admin/VerifyEmail-hNxWejWf.js.map +1 -0
  43. package/dist/admin/{core-BVO_TQxb.js → core-CYaRQ8O-.js} +709 -556
  44. package/dist/admin/core-CYaRQ8O-.js.map +1 -0
  45. package/dist/admin/index.d.ts +83 -44
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +58 -39
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/auth/{AuthLayout-C161NeF6.js → AuthLayout-AvLlcLjS.js} +2 -2
  50. package/dist/auth/{AuthLayout-C161NeF6.js.map → AuthLayout-AvLlcLjS.js.map} +1 -1
  51. package/dist/auth/Login-BA1E8IZl.js +275 -0
  52. package/dist/auth/Login-BA1E8IZl.js.map +1 -0
  53. package/dist/auth/{Profile-BMpXJ0oi.js → Profile-YcWdeuFz.js} +31 -27
  54. package/dist/auth/Profile-YcWdeuFz.js.map +1 -0
  55. package/dist/auth/{Register-2gx8qll-.js → Register-CPhEO5MG.js} +198 -142
  56. package/dist/auth/Register-CPhEO5MG.js.map +1 -0
  57. package/dist/{demo/ResetPassword-CAPj8MO3.js → auth/ResetPassword-DCtGcneA.js} +7 -7
  58. package/dist/auth/ResetPassword-DCtGcneA.js.map +1 -0
  59. package/dist/{demo/VerifyEmail-DFmdCdYs.js → auth/VerifyEmail-DkH7NBfn.js} +23 -8
  60. package/dist/auth/VerifyEmail-DkH7NBfn.js.map +1 -0
  61. package/dist/auth/{core-DyfeVr5c.js → core-D5jIAVF2.js} +386 -294
  62. package/dist/auth/core-D5jIAVF2.js.map +1 -0
  63. package/dist/auth/index.d.ts +93 -48
  64. package/dist/auth/index.d.ts.map +1 -1
  65. package/dist/auth/index.js +28 -24
  66. package/dist/auth/index.js.map +1 -1
  67. package/dist/core/index.d.ts +116 -61
  68. package/dist/core/index.d.ts.map +1 -1
  69. package/dist/core/index.js +873 -701
  70. package/dist/core/index.js.map +1 -1
  71. package/dist/demo/{AuthLayout-DN-ClJQk.js → AuthLayout-Brri4A-L.js} +2 -2
  72. package/dist/demo/{AuthLayout-DN-ClJQk.js.map → AuthLayout-Brri4A-L.js.map} +1 -1
  73. package/dist/demo/DemoButton-wiCxZZ_L.js +182 -0
  74. package/dist/demo/DemoButton-wiCxZZ_L.js.map +1 -0
  75. package/dist/demo/DemoControlSelect-D7ILObVg.js +305 -0
  76. package/dist/demo/DemoControlSelect-D7ILObVg.js.map +1 -0
  77. package/dist/demo/DemoDataTable-DZ5Y8pFX.js +362 -0
  78. package/dist/demo/DemoDataTable-DZ5Y8pFX.js.map +1 -0
  79. package/dist/demo/{DemoDialog-DW8QEvD1.js → DemoDialog-CUWdLHim.js} +2 -2
  80. package/dist/demo/{DemoDialog-DW8QEvD1.js.map → DemoDialog-CUWdLHim.js.map} +1 -1
  81. package/dist/demo/{DemoFlex-CAhLUanT.js → DemoFlex-a8OhMMvq.js} +3 -3
  82. package/dist/demo/{DemoFlex-CAhLUanT.js.map → DemoFlex-a8OhMMvq.js.map} +1 -1
  83. package/dist/demo/{DemoHeading-yIFmNjHB.js → DemoHeading-C13OVDfS.js} +3 -3
  84. package/dist/demo/{DemoHeading-yIFmNjHB.js.map → DemoHeading-C13OVDfS.js.map} +1 -1
  85. package/dist/demo/{DemoHome-BSGuBHus.js → DemoHome-D_De3UiT.js} +2 -2
  86. package/dist/demo/{DemoHome-BSGuBHus.js.map → DemoHome-D_De3UiT.js.map} +1 -1
  87. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js → DemoJsonViewer-B50s9aGM.js} +3 -3
  88. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js.map → DemoJsonViewer-B50s9aGM.js.map} +1 -1
  89. package/dist/demo/{DemoLayout-Cy6xjn6P.js → DemoLayout-CHU8WTwO.js} +14 -5
  90. package/dist/demo/DemoLayout-CHU8WTwO.js.map +1 -0
  91. package/dist/demo/{DemoLogin-vqxgTu4P.js → DemoLogin-BBlrWpml.js} +49 -32
  92. package/dist/demo/DemoLogin-BBlrWpml.js.map +1 -0
  93. package/dist/demo/{DemoRegister-YHPvPg77.js → DemoRegister-BuNE3_-f.js} +49 -50
  94. package/dist/demo/DemoRegister-BuNE3_-f.js.map +1 -0
  95. package/dist/demo/{DemoResetPassword-mOW18Zlm.js → DemoResetPassword-D_IjjjOJ.js} +12 -16
  96. package/dist/demo/DemoResetPassword-D_IjjjOJ.js.map +1 -0
  97. package/dist/demo/{DemoSidebar-od7aLjP_.js → DemoSidebar-Giy2HRBD.js} +3 -3
  98. package/dist/demo/{DemoSidebar-od7aLjP_.js.map → DemoSidebar-Giy2HRBD.js.map} +1 -1
  99. package/dist/demo/{DemoText-DU3JeRS0.js → DemoText-ubcw-vog.js} +3 -3
  100. package/dist/demo/{DemoText-DU3JeRS0.js.map → DemoText-ubcw-vog.js.map} +1 -1
  101. package/dist/demo/{DemoToast-CUJEiPRa.js → DemoToast-9die_dYT.js} +2 -2
  102. package/dist/demo/{DemoToast-CUJEiPRa.js.map → DemoToast-9die_dYT.js.map} +1 -1
  103. package/dist/demo/{DemoTypeForm-C1dNkahD.js → DemoTypeForm-D_d6OVKL.js} +8 -4
  104. package/dist/demo/DemoTypeForm-D_d6OVKL.js.map +1 -0
  105. package/dist/demo/DemoVerifyEmail-B43KlF4F.js +34 -0
  106. package/dist/demo/DemoVerifyEmail-B43KlF4F.js.map +1 -0
  107. package/dist/demo/Login-C12N4oGs.js +275 -0
  108. package/dist/demo/Login-C12N4oGs.js.map +1 -0
  109. package/dist/demo/{Profile-BE_Y3co2.js → Profile-DS5q4vOh.js} +31 -27
  110. package/dist/demo/Profile-DS5q4vOh.js.map +1 -0
  111. package/dist/demo/{Register-fXHmBpr3.js → Register-B4hLBeEv.js} +198 -142
  112. package/dist/demo/Register-B4hLBeEv.js.map +1 -0
  113. package/dist/{auth/ResetPassword-DBxt9hKk.js → demo/ResetPassword-D8g9ha1N.js} +7 -7
  114. package/dist/demo/ResetPassword-D8g9ha1N.js.map +1 -0
  115. package/dist/demo/{Showcase-BtEU0pY9.js → Showcase-D6Fxt4X4.js} +64 -65
  116. package/dist/demo/Showcase-D6Fxt4X4.js.map +1 -0
  117. package/dist/{auth/VerifyEmail-Z80Ubajk.js → demo/VerifyEmail-BjDo0cZA.js} +23 -8
  118. package/dist/demo/VerifyEmail-BjDo0cZA.js.map +1 -0
  119. package/dist/demo/{auth-Djd7SKiw.js → auth-ByVTreDl.js} +8 -8
  120. package/dist/demo/{auth-Djd7SKiw.js.map → auth-ByVTreDl.js.map} +1 -1
  121. package/dist/demo/{core-B7LNjM78.js → core-DFgB3yU4.js} +741 -573
  122. package/dist/demo/core-DFgB3yU4.js.map +1 -0
  123. package/dist/demo/index.d.ts +1 -0
  124. package/dist/demo/index.d.ts.map +1 -1
  125. package/dist/demo/index.js +24 -18
  126. package/dist/demo/index.js.map +1 -1
  127. package/package.json +7 -7
  128. package/src/admin/AdminRouter.tsx +24 -1
  129. package/src/admin/components/notifications/AdminNotifications.tsx +519 -0
  130. package/src/admin/components/parameters/ParameterDetails.tsx +12 -270
  131. package/src/admin/components/parameters/ParameterDetailsConfigForm.tsx +238 -0
  132. package/src/admin/components/parameters/ParameterDetailsLoading.tsx +24 -0
  133. package/src/admin/components/parameters/ParameterHistory.tsx +10 -11
  134. package/src/admin/components/parameters/ParameterTree.tsx +28 -184
  135. package/src/admin/components/parameters/ParameterTreeNode.tsx +151 -0
  136. package/src/admin/components/shared/AdminResourceHeader.tsx +2 -25
  137. package/src/admin/components/shared/AdminResourceHeaderMenuItem.tsx +37 -0
  138. package/src/admin/components/shared/AdminResourceTabs.tsx +2 -26
  139. package/src/admin/components/shared/AdminResourceTabsItem.tsx +36 -0
  140. package/src/auth/components/Login.tsx +188 -121
  141. package/src/auth/components/Profile.tsx +1 -22
  142. package/src/auth/components/ProfileField.tsx +39 -0
  143. package/src/auth/components/Register.tsx +215 -158
  144. package/src/auth/components/ResetPassword.tsx +7 -11
  145. package/src/auth/components/VerifyEmail.tsx +35 -10
  146. package/src/auth/components/buttons/UserButton.tsx +19 -21
  147. package/src/auth/index.ts +1 -0
  148. package/src/core/components/Flex.tsx +10 -0
  149. package/src/core/components/buttons/ActionButton.tsx +104 -78
  150. package/src/core/components/data/DetailDrawer.tsx +102 -96
  151. package/src/core/components/data/DetailList.tsx +2 -1
  152. package/src/core/components/layout/Breadcrumb.tsx +3 -6
  153. package/src/core/components/layout/DashboardShell.tsx +18 -4
  154. package/src/core/components/layout/Sidebar.tsx +16 -241
  155. package/src/core/components/layout/SidebarCollapsedItem.tsx +91 -0
  156. package/src/core/components/layout/SidebarItem.tsx +146 -0
  157. package/src/core/components/layout/index.ts +3 -1
  158. package/src/core/form/components/Control.tsx +31 -29
  159. package/src/core/form/components/ControlArray.tsx +13 -39
  160. package/src/core/form/components/ControlDate.tsx +10 -21
  161. package/src/core/form/components/ControlNumber.tsx +4 -33
  162. package/src/core/form/components/ControlQueryBuilder.tsx +12 -175
  163. package/src/core/form/components/ControlQueryBuilderHelp.tsx +165 -0
  164. package/src/core/form/components/ControlSelect.browser.spec.tsx +343 -0
  165. package/src/core/form/components/ControlSelect.tsx +294 -92
  166. package/src/core/form/components/TypeForm.browser.spec.tsx +3 -3
  167. package/src/core/form/components/TypeForm.tsx +5 -2
  168. package/src/core/form/index.ts +8 -1
  169. package/src/core/form/utils/parseInput.ts +7 -3
  170. package/src/core/index.ts +3 -1
  171. package/src/core/json/components/JsonViewer.tsx +103 -319
  172. package/src/core/json/components/JsonViewerCopyButton.tsx +46 -0
  173. package/src/core/json/components/JsonViewerRowNode.tsx +120 -0
  174. package/src/core/json/components/JsonViewerShared.ts +76 -0
  175. package/src/core/styles.css +12 -2
  176. package/src/core/table/components/ColumnPicker.tsx +3 -3
  177. package/src/core/table/components/DataTable.tsx +89 -29
  178. package/src/core/table/components/DataTableFilters.tsx +6 -11
  179. package/src/core/table/components/DataTablePagination.tsx +9 -3
  180. package/src/core/table/components/DataTableToolbar.tsx +7 -3
  181. package/src/core/table/components/FilterPicker.tsx +3 -3
  182. package/src/core/table/interfaces/types.ts +29 -0
  183. package/src/core/utils/icons.tsx +2 -2
  184. package/src/demo/DemoRouter.ts +8 -1
  185. package/src/demo/components/DemoLayout.tsx +12 -2
  186. package/src/demo/components/auth/DemoLogin.tsx +35 -28
  187. package/src/demo/components/auth/DemoRegister.tsx +35 -49
  188. package/src/demo/components/auth/DemoResetPassword.tsx +5 -9
  189. package/src/demo/components/auth/DemoVerifyEmail.tsx +7 -6
  190. package/src/demo/components/core/DemoButton.tsx +123 -103
  191. package/src/demo/components/core/DemoControlSelect.tsx +325 -0
  192. package/src/demo/components/core/DemoDataTable.tsx +255 -237
  193. package/src/demo/components/core/DemoTypeForm.tsx +7 -2
  194. package/src/demo/components/shared/MacWindow.tsx +5 -11
  195. package/src/demo/components/shared/Showcase.tsx +28 -42
  196. package/dist/admin/AdminParameters-BspPeqp_.js.map +0 -1
  197. package/dist/admin/AdminUserLayout-DUbC6-BI.js.map +0 -1
  198. package/dist/admin/Login-DHbYJKwg.js +0 -219
  199. package/dist/admin/Login-DHbYJKwg.js.map +0 -1
  200. package/dist/admin/Profile-B2EcIDB9.js.map +0 -1
  201. package/dist/admin/Register-Z3fxRbUF.js.map +0 -1
  202. package/dist/admin/ResetPassword-_Y1qTTKh.js.map +0 -1
  203. package/dist/admin/VerifyEmail-Bg22bwcC.js.map +0 -1
  204. package/dist/admin/core-BVO_TQxb.js.map +0 -1
  205. package/dist/auth/Login-C7jIqf00.js +0 -219
  206. package/dist/auth/Login-C7jIqf00.js.map +0 -1
  207. package/dist/auth/Profile-BMpXJ0oi.js.map +0 -1
  208. package/dist/auth/Register-2gx8qll-.js.map +0 -1
  209. package/dist/auth/ResetPassword-DBxt9hKk.js.map +0 -1
  210. package/dist/auth/VerifyEmail-Z80Ubajk.js.map +0 -1
  211. package/dist/auth/core-DyfeVr5c.js.map +0 -1
  212. package/dist/demo/DemoButton-CGUyR9eM.js +0 -178
  213. package/dist/demo/DemoButton-CGUyR9eM.js.map +0 -1
  214. package/dist/demo/DemoDataTable-QFG-xXSx.js +0 -358
  215. package/dist/demo/DemoDataTable-QFG-xXSx.js.map +0 -1
  216. package/dist/demo/DemoLayout-Cy6xjn6P.js.map +0 -1
  217. package/dist/demo/DemoLogin-vqxgTu4P.js.map +0 -1
  218. package/dist/demo/DemoRegister-YHPvPg77.js.map +0 -1
  219. package/dist/demo/DemoResetPassword-mOW18Zlm.js.map +0 -1
  220. package/dist/demo/DemoTypeForm-C1dNkahD.js.map +0 -1
  221. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js +0 -30
  222. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js.map +0 -1
  223. package/dist/demo/Login-CoYf_P_F.js +0 -219
  224. package/dist/demo/Login-CoYf_P_F.js.map +0 -1
  225. package/dist/demo/Profile-BE_Y3co2.js.map +0 -1
  226. package/dist/demo/Register-fXHmBpr3.js.map +0 -1
  227. package/dist/demo/ResetPassword-CAPj8MO3.js.map +0 -1
  228. package/dist/demo/Showcase-BtEU0pY9.js.map +0 -1
  229. package/dist/demo/VerifyEmail-DFmdCdYs.js.map +0 -1
  230. package/dist/demo/core-B7LNjM78.js.map +0 -1
  231. package/src/demo/styles.css +0 -0
@@ -1,31 +1,24 @@
1
1
  import {
2
- ActionIcon,
3
- Flex,
4
2
  getTreeExpandedState,
5
3
  type MantineSize,
6
4
  Text,
7
5
  Tree,
8
6
  useTree,
9
7
  } from "@mantine/core";
8
+ import { type ReactNode, useCallback, useMemo } from "react";
9
+ import { JsonViewerRowNode } from "./JsonViewerRowNode.tsx";
10
10
  import {
11
- IconCheck,
12
- IconChevronDown,
13
- IconChevronRight,
14
- IconCopy,
15
- } from "@tabler/icons-react";
16
- import {
17
- type CSSProperties,
18
- type ReactNode,
19
- useCallback,
20
- useMemo,
21
- useState,
22
- } from "react";
11
+ getValueType,
12
+ type JsonTreeNode,
13
+ SIZE_CONFIG,
14
+ STYLES,
15
+ } from "./JsonViewerShared.ts";
23
16
 
24
17
  // =============================================================================
25
- // TYPES
18
+ // PROPS
26
19
  // =============================================================================
27
20
 
28
- interface JsonViewerProps {
21
+ export interface JsonViewerProps {
29
22
  data: any;
30
23
  /**
31
24
  * Depth level to expand by default (0 = collapsed, Infinity = all expanded)
@@ -57,312 +50,21 @@ interface JsonViewerProps {
57
50
  ) => string | number | undefined;
58
51
  }
59
52
 
60
- interface JsonTreeNode {
61
- value: string;
62
- label: string;
63
- children?: JsonTreeNode[];
64
- // Custom properties
65
- nodeValue: any;
66
- nodeKey: string | undefined;
67
- path: string[];
68
- isArrayItem: boolean;
69
- isRoot?: boolean;
70
- }
71
-
72
- // =============================================================================
73
- // CONSTANTS
74
- // =============================================================================
75
-
76
- const SIZE_CONFIG: Record<MantineSize, { icon: number; levelOffset: number }> =
77
- {
78
- xs: { icon: 14, levelOffset: 16 },
79
- sm: { icon: 16, levelOffset: 20 },
80
- md: { icon: 18, levelOffset: 24 },
81
- lg: { icon: 20, levelOffset: 28 },
82
- xl: { icon: 22, levelOffset: 32 },
83
- };
84
-
85
- const STYLES = {
86
- root: {
87
- fontFamily: "var(--mantine-font-family-monospace)",
88
- } satisfies CSSProperties,
89
- chevron: {
90
- flexShrink: 0,
91
- color: "var(--mantine-color-dimmed)",
92
- } satisfies CSSProperties,
93
- key: {
94
- color: "var(--mantine-color-cyan-text)",
95
- fontWeight: 500,
96
- } satisfies CSSProperties,
97
- colon: {
98
- color: "var(--mantine-color-dimmed)",
99
- } satisfies CSSProperties,
100
- string: {
101
- color: "var(--mantine-color-teal-text)",
102
- } satisfies CSSProperties,
103
- number: {
104
- color: "var(--mantine-color-blue-text)",
105
- } satisfies CSSProperties,
106
- boolean: {
107
- color: "var(--mantine-color-violet-text)",
108
- } satisfies CSSProperties,
109
- null: {
110
- color: "var(--mantine-color-dimmed)",
111
- fontStyle: "italic",
112
- } satisfies CSSProperties,
113
- preview: {
114
- color: "var(--mantine-color-dimmed)",
115
- } satisfies CSSProperties,
116
- };
117
-
118
- // =============================================================================
119
- // HELPERS
120
- // =============================================================================
121
-
122
- const getValueType = (val: any): string => {
123
- if (val === null) return "null";
124
- if (val === undefined) return "undefined";
125
- if (Array.isArray(val)) return "array";
126
- return typeof val;
127
- };
128
-
129
- // Convert JSON to tree data structure
130
- function buildTreeNodes(
131
- data: any,
132
- path: string[] = [],
133
- key?: string,
134
- isArrayItem = false,
135
- maxDepth = 10,
136
- ): JsonTreeNode | null {
137
- const currentPath = key !== undefined ? [...path, key] : path;
138
- const nodeId = currentPath.length > 0 ? currentPath.join(".") : "root";
139
-
140
- if (currentPath.length > maxDepth) {
141
- return {
142
- value: nodeId,
143
- label: key ?? "",
144
- nodeValue: data,
145
- nodeKey: key,
146
- path: currentPath,
147
- isArrayItem,
148
- };
149
- }
150
-
151
- const type = getValueType(data);
152
-
153
- if (type === "object" || type === "array") {
154
- const entries =
155
- type === "array"
156
- ? (data as any[]).map((v, i) => [String(i), v] as const)
157
- : Object.entries(data);
158
-
159
- const children = entries
160
- .map(([k, v]) =>
161
- buildTreeNodes(v, currentPath, k, type === "array", maxDepth),
162
- )
163
- .filter((n): n is JsonTreeNode => n !== null);
164
-
165
- return {
166
- value: nodeId,
167
- label: key ?? "",
168
- nodeValue: data,
169
- nodeKey: key,
170
- path: currentPath,
171
- isArrayItem,
172
- children: children.length > 0 ? children : undefined,
173
- };
174
- }
175
-
176
- return {
177
- value: nodeId,
178
- label: key ?? "",
179
- nodeValue: data,
180
- nodeKey: key,
181
- path: currentPath,
182
- isArrayItem,
183
- };
184
- }
185
-
186
- // Get all expandable node IDs up to a certain depth
187
- function getExpandedIds(
188
- nodes: JsonTreeNode[],
189
- targetDepth: number,
190
- currentDepth = 0,
191
- ): string[] {
192
- if (currentDepth >= targetDepth) return [];
193
- const ids: string[] = [];
194
- for (const node of nodes) {
195
- if (node.children) {
196
- ids.push(node.value);
197
- ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));
198
- }
199
- }
200
- return ids;
201
- }
202
-
203
53
  // =============================================================================
204
- // COPY BUTTON COMPONENT
54
+ // COMPONENT
205
55
  // =============================================================================
206
56
 
207
- const CopyButton = ({
208
- value,
209
- iconSize,
210
- }: {
211
- value: string;
212
- iconSize: number;
213
- }) => {
214
- const [copied, setCopied] = useState(false);
215
-
216
- const handleCopy = useCallback(
217
- (e: React.MouseEvent) => {
218
- e.stopPropagation();
219
- navigator.clipboard.writeText(value);
220
- setCopied(true);
221
- setTimeout(() => setCopied(false), 1500);
222
- },
223
- [value],
224
- );
225
-
226
- return (
227
- <ActionIcon
228
- size={iconSize + 4}
229
- variant="transparent"
230
- c={copied ? "green" : "dimmed"}
231
- onClick={handleCopy}
232
- className="alepha-json-viewer-copy"
233
- >
234
- {copied ? <IconCheck size={iconSize} /> : <IconCopy size={iconSize} />}
235
- </ActionIcon>
236
- );
237
- };
238
-
239
- // =============================================================================
240
- // ROW NODE COMPONENT
241
- // =============================================================================
242
-
243
- interface RowNodeProps {
244
- node: JsonTreeNode;
245
- expanded: boolean;
246
- hasChildren: boolean;
247
- elementProps: any;
248
- size: MantineSize;
249
- config: { icon: number; levelOffset: number };
250
- showQuotes: boolean;
251
- showCopyButton: boolean;
252
- renderValue: (val: any, key: string | undefined, path: string[]) => ReactNode;
253
- }
57
+ export const JsonViewer = (props: JsonViewerProps) => {
58
+ const {
59
+ data,
60
+ defaultExpandedDepth = 2,
61
+ maxDepth = 10,
62
+ size = "sm",
63
+ showQuotes = false,
64
+ showCopyButton = true,
65
+ formatValue,
66
+ } = props;
254
67
 
255
- const RowNode = ({
256
- node,
257
- expanded,
258
- hasChildren,
259
- elementProps,
260
- size,
261
- config,
262
- showQuotes,
263
- showCopyButton,
264
- renderValue,
265
- }: RowNodeProps) => {
266
- const { nodeValue, nodeKey, path, isArrayItem, isRoot } = node;
267
- const type = getValueType(nodeValue);
268
- const isExpandable = type === "object" || type === "array";
269
-
270
- const getPreview = () => {
271
- if (!isExpandable) return null;
272
- const entries = type === "array" ? nodeValue : Object.keys(nodeValue);
273
- const count = entries.length;
274
- const label = type === "array" ? "item" : "key";
275
-
276
- // For root node or collapsed nodes, show the count
277
- if (!expanded) {
278
- return (
279
- <Text fs={"italic"} component="span" size={size} style={STYLES.preview}>
280
- {count === 0
281
- ? type === "array"
282
- ? "[]"
283
- : "{}"
284
- : type === "array"
285
- ? `[ ${count} ${count === 1 ? label : `${label}s`} ]`
286
- : `{ ${count} ${count === 1 ? label : `${label}s`} }`}
287
- </Text>
288
- );
289
- }
290
-
291
- return null;
292
- };
293
-
294
- const getCopyValue = () =>
295
- isExpandable ? JSON.stringify(nodeValue, null, 2) : String(nodeValue ?? "");
296
-
297
- return (
298
- <Flex
299
- gap={6}
300
- wrap="nowrap"
301
- {...elementProps}
302
- className={`alepha-json-viewer-row ${elementProps.className || ""}`}
303
- >
304
- {hasChildren ? (
305
- expanded ? (
306
- <IconChevronDown size={config.icon} style={STYLES.chevron} />
307
- ) : (
308
- <IconChevronRight size={config.icon} style={STYLES.chevron} />
309
- )
310
- ) : (
311
- <span style={{ width: config.icon, flexShrink: 0 }} />
312
- )}
313
-
314
- {nodeKey !== undefined && !isArrayItem && (
315
- <Text component="span" size={size}>
316
- <span style={STYLES.key}>
317
- {showQuotes ? `"${nodeKey}"` : nodeKey}
318
- </span>
319
- <span style={STYLES.colon}>:</span>
320
- </Text>
321
- )}
322
-
323
- {nodeKey !== undefined && isArrayItem && (
324
- <Text component="span" size={size}>
325
- <span style={STYLES.key}>{nodeKey}</span>
326
- <span style={STYLES.colon}>:</span>
327
- </Text>
328
- )}
329
-
330
- {hasChildren ? (
331
- getPreview()
332
- ) : isExpandable ? (
333
- type === "array" ? (
334
- <Text component="span" size={size} style={STYLES.preview}>
335
- []
336
- </Text>
337
- ) : (
338
- <Text component="span" size={size} style={STYLES.preview}>
339
- {"{}"}
340
- </Text>
341
- )
342
- ) : (
343
- renderValue(nodeValue, nodeKey, path)
344
- )}
345
-
346
- {showCopyButton && (
347
- <CopyButton value={getCopyValue()} iconSize={config.icon} />
348
- )}
349
- </Flex>
350
- );
351
- };
352
-
353
- // =============================================================================
354
- // MAIN COMPONENT
355
- // =============================================================================
356
-
357
- export const JsonViewer = ({
358
- data,
359
- defaultExpandedDepth = 2,
360
- maxDepth = 10,
361
- size = "sm",
362
- showQuotes = false,
363
- showCopyButton = true,
364
- formatValue,
365
- }: JsonViewerProps) => {
366
68
  const config = SIZE_CONFIG[size] || SIZE_CONFIG.sm;
367
69
 
368
70
  // Build tree data from JSON with root wrapper
@@ -487,7 +189,7 @@ export const JsonViewer = ({
487
189
  elementProps: any;
488
190
  }): ReactNode => {
489
191
  return (
490
- <RowNode
192
+ <JsonViewerRowNode
491
193
  node={node}
492
194
  expanded={expanded}
493
195
  hasChildren={hasChildren}
@@ -523,4 +225,86 @@ export const JsonViewer = ({
523
225
  );
524
226
  };
525
227
 
228
+ // =============================================================================
229
+ // HELPERS
230
+ // =============================================================================
231
+
232
+ /**
233
+ * Convert JSON to tree data structure.
234
+ */
235
+ const buildTreeNodes = (
236
+ data: any,
237
+ path: string[] = [],
238
+ key?: string,
239
+ isArrayItem = false,
240
+ maxDepth = 10,
241
+ ): JsonTreeNode | null => {
242
+ const currentPath = key !== undefined ? [...path, key] : path;
243
+ const nodeId = currentPath.length > 0 ? currentPath.join(".") : "root";
244
+
245
+ if (currentPath.length > maxDepth) {
246
+ return {
247
+ value: nodeId,
248
+ label: key ?? "",
249
+ nodeValue: data,
250
+ nodeKey: key,
251
+ path: currentPath,
252
+ isArrayItem,
253
+ };
254
+ }
255
+
256
+ const type = getValueType(data);
257
+
258
+ if (type === "object" || type === "array") {
259
+ const entries =
260
+ type === "array"
261
+ ? (data as any[]).map((v, i) => [String(i), v] as const)
262
+ : Object.entries(data);
263
+
264
+ const children = entries
265
+ .map(([k, v]) =>
266
+ buildTreeNodes(v, currentPath, k, type === "array", maxDepth),
267
+ )
268
+ .filter((n): n is JsonTreeNode => n !== null);
269
+
270
+ return {
271
+ value: nodeId,
272
+ label: key ?? "",
273
+ nodeValue: data,
274
+ nodeKey: key,
275
+ path: currentPath,
276
+ isArrayItem,
277
+ children: children.length > 0 ? children : undefined,
278
+ };
279
+ }
280
+
281
+ return {
282
+ value: nodeId,
283
+ label: key ?? "",
284
+ nodeValue: data,
285
+ nodeKey: key,
286
+ path: currentPath,
287
+ isArrayItem,
288
+ };
289
+ };
290
+
291
+ /**
292
+ * Get all expandable node IDs up to a certain depth.
293
+ */
294
+ const getExpandedIds = (
295
+ nodes: JsonTreeNode[],
296
+ targetDepth: number,
297
+ currentDepth = 0,
298
+ ): string[] => {
299
+ if (currentDepth >= targetDepth) return [];
300
+ const ids: string[] = [];
301
+ for (const node of nodes) {
302
+ if (node.children) {
303
+ ids.push(node.value);
304
+ ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));
305
+ }
306
+ }
307
+ return ids;
308
+ };
309
+
526
310
  export default JsonViewer;
@@ -0,0 +1,46 @@
1
+ import { ActionIcon } from "@mantine/core";
2
+ import { IconCheck, IconCopy } from "@tabler/icons-react";
3
+ import { useCallback, useState } from "react";
4
+
5
+ // =============================================================================
6
+ // PROPS
7
+ // =============================================================================
8
+
9
+ export interface JsonViewerCopyButtonProps {
10
+ value: string;
11
+ iconSize: number;
12
+ }
13
+
14
+ // =============================================================================
15
+ // COMPONENT
16
+ // =============================================================================
17
+
18
+ export const JsonViewerCopyButton = (props: JsonViewerCopyButtonProps) => {
19
+ const [copied, setCopied] = useState(false);
20
+
21
+ const handleCopy = useCallback(
22
+ (e: React.MouseEvent) => {
23
+ e.stopPropagation();
24
+ navigator.clipboard.writeText(props.value);
25
+ setCopied(true);
26
+ setTimeout(() => setCopied(false), 1500);
27
+ },
28
+ [props.value],
29
+ );
30
+
31
+ return (
32
+ <ActionIcon
33
+ size={props.iconSize + 4}
34
+ variant="transparent"
35
+ c={copied ? "green" : "dimmed"}
36
+ onClick={handleCopy}
37
+ className="alepha-json-viewer-copy"
38
+ >
39
+ {copied ? (
40
+ <IconCheck size={props.iconSize} />
41
+ ) : (
42
+ <IconCopy size={props.iconSize} />
43
+ )}
44
+ </ActionIcon>
45
+ );
46
+ };
@@ -0,0 +1,120 @@
1
+ import { Flex, type MantineSize, Text } from "@mantine/core";
2
+ import { IconChevronDown, IconChevronRight } from "@tabler/icons-react";
3
+ import type { ReactNode } from "react";
4
+ import { JsonViewerCopyButton } from "./JsonViewerCopyButton.tsx";
5
+ import { getValueType, type JsonTreeNode, STYLES } from "./JsonViewerShared.ts";
6
+
7
+ // =============================================================================
8
+ // PROPS
9
+ // =============================================================================
10
+
11
+ export interface JsonViewerRowNodeProps {
12
+ node: JsonTreeNode;
13
+ expanded: boolean;
14
+ hasChildren: boolean;
15
+ elementProps: any;
16
+ size: MantineSize;
17
+ config: { icon: number; levelOffset: number };
18
+ showQuotes: boolean;
19
+ showCopyButton: boolean;
20
+ renderValue: (val: any, key: string | undefined, path: string[]) => ReactNode;
21
+ }
22
+
23
+ // =============================================================================
24
+ // COMPONENT
25
+ // =============================================================================
26
+
27
+ export const JsonViewerRowNode = (props: JsonViewerRowNodeProps) => {
28
+ const { nodeValue, nodeKey, path, isArrayItem } = props.node;
29
+ const type = getValueType(nodeValue);
30
+ const isExpandable = type === "object" || type === "array";
31
+
32
+ const getPreview = () => {
33
+ if (!isExpandable) return null;
34
+ const entries = type === "array" ? nodeValue : Object.keys(nodeValue);
35
+ const count = entries.length;
36
+ const label = type === "array" ? "item" : "key";
37
+
38
+ if (!props.expanded) {
39
+ return (
40
+ <Text
41
+ fs={"italic"}
42
+ component="span"
43
+ size={props.size}
44
+ style={STYLES.preview}
45
+ >
46
+ {count === 0
47
+ ? type === "array"
48
+ ? "[]"
49
+ : "{}"
50
+ : type === "array"
51
+ ? `[ ${count} ${count === 1 ? label : `${label}s`} ]`
52
+ : `{ ${count} ${count === 1 ? label : `${label}s`} }`}
53
+ </Text>
54
+ );
55
+ }
56
+
57
+ return null;
58
+ };
59
+
60
+ const getCopyValue = () =>
61
+ isExpandable ? JSON.stringify(nodeValue, null, 2) : String(nodeValue ?? "");
62
+
63
+ return (
64
+ <Flex
65
+ gap={6}
66
+ wrap="nowrap"
67
+ {...props.elementProps}
68
+ className={`alepha-json-viewer-row ${props.elementProps.className || ""}`}
69
+ >
70
+ {props.hasChildren ? (
71
+ props.expanded ? (
72
+ <IconChevronDown size={props.config.icon} style={STYLES.chevron} />
73
+ ) : (
74
+ <IconChevronRight size={props.config.icon} style={STYLES.chevron} />
75
+ )
76
+ ) : (
77
+ <span style={{ width: props.config.icon, flexShrink: 0 }} />
78
+ )}
79
+
80
+ {nodeKey !== undefined && !isArrayItem && (
81
+ <Text component="span" size={props.size}>
82
+ <span style={STYLES.key}>
83
+ {props.showQuotes ? `"${nodeKey}"` : nodeKey}
84
+ </span>
85
+ <span style={STYLES.colon}>:</span>
86
+ </Text>
87
+ )}
88
+
89
+ {nodeKey !== undefined && isArrayItem && (
90
+ <Text component="span" size={props.size}>
91
+ <span style={STYLES.key}>{nodeKey}</span>
92
+ <span style={STYLES.colon}>:</span>
93
+ </Text>
94
+ )}
95
+
96
+ {props.hasChildren ? (
97
+ getPreview()
98
+ ) : isExpandable ? (
99
+ type === "array" ? (
100
+ <Text component="span" size={props.size} style={STYLES.preview}>
101
+ []
102
+ </Text>
103
+ ) : (
104
+ <Text component="span" size={props.size} style={STYLES.preview}>
105
+ {"{}"}
106
+ </Text>
107
+ )
108
+ ) : (
109
+ props.renderValue(nodeValue, nodeKey, path)
110
+ )}
111
+
112
+ {props.showCopyButton && (
113
+ <JsonViewerCopyButton
114
+ value={getCopyValue()}
115
+ iconSize={props.config.icon}
116
+ />
117
+ )}
118
+ </Flex>
119
+ );
120
+ };
@@ -0,0 +1,76 @@
1
+ import type { MantineSize } from "@mantine/core";
2
+ import type { CSSProperties } from "react";
3
+
4
+ // =============================================================================
5
+ // TYPES
6
+ // =============================================================================
7
+
8
+ export interface JsonTreeNode {
9
+ value: string;
10
+ label: string;
11
+ children?: JsonTreeNode[];
12
+ nodeValue: any;
13
+ nodeKey: string | undefined;
14
+ path: string[];
15
+ isArrayItem: boolean;
16
+ isRoot?: boolean;
17
+ }
18
+
19
+ // =============================================================================
20
+ // CONSTANTS
21
+ // =============================================================================
22
+
23
+ export const SIZE_CONFIG: Record<
24
+ MantineSize,
25
+ { icon: number; levelOffset: number }
26
+ > = {
27
+ xs: { icon: 14, levelOffset: 16 },
28
+ sm: { icon: 16, levelOffset: 20 },
29
+ md: { icon: 18, levelOffset: 24 },
30
+ lg: { icon: 20, levelOffset: 28 },
31
+ xl: { icon: 22, levelOffset: 32 },
32
+ };
33
+
34
+ export const STYLES = {
35
+ root: {
36
+ fontFamily: "var(--mantine-font-family-monospace)",
37
+ } satisfies CSSProperties,
38
+ chevron: {
39
+ flexShrink: 0,
40
+ color: "var(--mantine-color-dimmed)",
41
+ } satisfies CSSProperties,
42
+ key: {
43
+ color: "var(--mantine-color-cyan-text)",
44
+ fontWeight: 500,
45
+ } satisfies CSSProperties,
46
+ colon: {
47
+ color: "var(--mantine-color-dimmed)",
48
+ } satisfies CSSProperties,
49
+ string: {
50
+ color: "var(--mantine-color-teal-text)",
51
+ } satisfies CSSProperties,
52
+ number: {
53
+ color: "var(--mantine-color-blue-text)",
54
+ } satisfies CSSProperties,
55
+ boolean: {
56
+ color: "var(--mantine-color-violet-text)",
57
+ } satisfies CSSProperties,
58
+ null: {
59
+ color: "var(--mantine-color-dimmed)",
60
+ fontStyle: "italic",
61
+ } satisfies CSSProperties,
62
+ preview: {
63
+ color: "var(--mantine-color-dimmed)",
64
+ } satisfies CSSProperties,
65
+ };
66
+
67
+ // =============================================================================
68
+ // HELPERS
69
+ // =============================================================================
70
+
71
+ export const getValueType = (val: any): string => {
72
+ if (val === null) return "null";
73
+ if (val === undefined) return "undefined";
74
+ if (Array.isArray(val)) return "array";
75
+ return typeof val;
76
+ };