@alepha/ui 0.17.2 → 0.18.1

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 (270) hide show
  1. package/dist/admin/{AdminApiKeys-CF_qOO3u.js → AdminApiKeys-C-6_Q-lH.js} +56 -192
  2. package/dist/admin/AdminApiKeys-C-6_Q-lH.js.map +1 -0
  3. package/dist/admin/{AdminAudits-BQno3hZG.js → AdminAudits-Bgbf04hO.js} +25 -61
  4. package/dist/admin/AdminAudits-Bgbf04hO.js.map +1 -0
  5. package/dist/admin/{AdminFiles-kvuUaASF.js → AdminFiles-B9a7G3cY.js} +6 -8
  6. package/dist/admin/AdminFiles-B9a7G3cY.js.map +1 -0
  7. package/dist/admin/{AdminJobDashboard-CrPxp0W1.js → AdminJobDashboard-DaTwf5OY.js} +55 -186
  8. package/dist/admin/AdminJobDashboard-DaTwf5OY.js.map +1 -0
  9. package/dist/admin/{AdminJobExecutions-D-b4Zt7W.js → AdminJobExecutions-B9cek5dl.js} +132 -168
  10. package/dist/admin/AdminJobExecutions-B9cek5dl.js.map +1 -0
  11. package/dist/admin/{AdminJobRegistry-CNX5cpDx.js → AdminJobRegistry-DFgV3oqx.js} +60 -83
  12. package/dist/admin/AdminJobRegistry-DFgV3oqx.js.map +1 -0
  13. package/dist/admin/AdminLayout-DHsvWxVB.js +70 -0
  14. package/dist/admin/AdminLayout-DHsvWxVB.js.map +1 -0
  15. package/dist/admin/{AdminParameters-DCGbpt2c.js → AdminParameters-DHw9ATgl.js} +53 -53
  16. package/dist/admin/AdminParameters-DHw9ATgl.js.map +1 -0
  17. package/dist/admin/{AdminSessions-DyhW6RZv.js → AdminSessions-BhGJPI3z.js} +11 -18
  18. package/dist/admin/AdminSessions-BhGJPI3z.js.map +1 -0
  19. package/dist/admin/{AdminUserLayout-CrBj4UuI.js → AdminUserLayout-BdC4Te8m.js} +112 -151
  20. package/dist/admin/AdminUserLayout-BdC4Te8m.js.map +1 -0
  21. package/dist/admin/AdminUserProfile-DAt23fqY.js +69 -0
  22. package/dist/admin/AdminUserProfile-DAt23fqY.js.map +1 -0
  23. package/dist/admin/AdminUserSessions-1uzcx02z.js +109 -0
  24. package/dist/admin/AdminUserSessions-1uzcx02z.js.map +1 -0
  25. package/dist/admin/AdminUsers-C85c3eiQ.js +121 -0
  26. package/dist/admin/AdminUsers-C85c3eiQ.js.map +1 -0
  27. package/dist/{auth/AuthLayout-CdJcrPs4.js → admin/AuthLayout-DFJvCvzw.js} +3 -3
  28. package/dist/{auth/AuthLayout-CdJcrPs4.js.map → admin/AuthLayout-DFJvCvzw.js.map} +1 -1
  29. package/dist/{auth/IconGoogle-Bm18QD2q.js → admin/IconGoogle-CSQLPYwX.js} +1 -1
  30. package/dist/{auth/IconGoogle-Bm18QD2q.js.map → admin/IconGoogle-CSQLPYwX.js.map} +1 -1
  31. package/dist/{demo/DemoLogin-DjJ9314c.js → admin/Login-BGheURrg.js} +15 -129
  32. package/dist/{auth/Login-BS_FYTy0.js.map → admin/Login-BGheURrg.js.map} +1 -1
  33. package/dist/{auth/Profile-CjDsW378.js → admin/Profile-B-c9pCPf.js} +5 -5
  34. package/dist/{auth/Profile-CjDsW378.js.map → admin/Profile-B-c9pCPf.js.map} +1 -1
  35. package/dist/{demo/DemoRegister-DzkJ5M83.js → admin/Register-Cs10l8vX.js} +20 -146
  36. package/dist/{auth/Register-C5eqzAaD.js.map → admin/Register-Cs10l8vX.js.map} +1 -1
  37. package/dist/{demo/DemoResetPassword-DWh4_BpQ.js → admin/ResetPassword-BwDdfkGH.js} +20 -82
  38. package/dist/{auth/ResetPassword-XifinVao.js.map → admin/ResetPassword-BwDdfkGH.js.map} +1 -1
  39. package/dist/{demo/DemoVerifyEmail-DbU_tCj8.js → admin/VerifyEmail-DfXHAiQl.js} +15 -32
  40. package/dist/{auth/VerifyEmail-DTgbeJOO.js.map → admin/VerifyEmail-DfXHAiQl.js.map} +1 -1
  41. package/dist/admin/auth-Dr0Cf8I7.js +319 -0
  42. package/dist/admin/auth-Dr0Cf8I7.js.map +1 -0
  43. package/dist/admin/core-2xoLiT0o.js +4031 -0
  44. package/dist/admin/core-2xoLiT0o.js.map +1 -0
  45. package/dist/admin/index.d.ts +739 -13
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +79 -111
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/admin/rolldown-runtime-CjeV3_4I.js +18 -0
  50. package/dist/auth/AuthLayout-CAE1pX9s.js +22 -0
  51. package/dist/auth/AuthLayout-CAE1pX9s.js.map +1 -0
  52. package/dist/auth/{Login-BS_FYTy0.js → Login-Denw_UGy.js} +8 -8
  53. package/dist/auth/Login-Denw_UGy.js.map +1 -0
  54. package/dist/auth/Profile-BMX_Ar_s.js +155 -0
  55. package/dist/auth/Profile-BMX_Ar_s.js.map +1 -0
  56. package/dist/auth/{Register-C5eqzAaD.js → Register-6hi_cpfF.js} +8 -8
  57. package/dist/auth/Register-6hi_cpfF.js.map +1 -0
  58. package/dist/auth/{ResetPassword-XifinVao.js → ResetPassword-CqfTk1FI.js} +6 -6
  59. package/dist/auth/ResetPassword-CqfTk1FI.js.map +1 -0
  60. package/dist/auth/{VerifyEmail-DTgbeJOO.js → VerifyEmail-nWiSTMjF.js} +5 -5
  61. package/dist/auth/VerifyEmail-nWiSTMjF.js.map +1 -0
  62. package/dist/auth/core-niW0sFLv.js +2264 -0
  63. package/dist/auth/core-niW0sFLv.js.map +1 -0
  64. package/dist/auth/index.d.ts +336 -8
  65. package/dist/auth/index.d.ts.map +1 -1
  66. package/dist/auth/index.js +18 -22
  67. package/dist/auth/index.js.map +1 -1
  68. package/dist/core/index.d.ts +1033 -843
  69. package/dist/core/index.d.ts.map +1 -1
  70. package/dist/core/index.js +1626 -1354
  71. package/dist/core/index.js.map +1 -1
  72. package/dist/demo/AuthLayout-jLa0aKsI.js +22 -0
  73. package/dist/demo/AuthLayout-jLa0aKsI.js.map +1 -0
  74. package/dist/demo/DemoButton-BmaWZVwf.js +178 -0
  75. package/dist/demo/DemoButton-BmaWZVwf.js.map +1 -0
  76. package/dist/demo/{DemoDataTable-lnBKWBf8.js → DemoDataTable-Z9xyV221.js} +18 -18
  77. package/dist/demo/DemoDataTable-Z9xyV221.js.map +1 -0
  78. package/dist/demo/DemoDialog-4ItHLf9t.js +101 -0
  79. package/dist/demo/DemoDialog-4ItHLf9t.js.map +1 -0
  80. package/dist/demo/DemoFlex-EtVq8QfX.js +105 -0
  81. package/dist/demo/DemoFlex-EtVq8QfX.js.map +1 -0
  82. package/dist/demo/DemoHeading-BS-vGfkI.js +18 -0
  83. package/dist/demo/DemoHeading-BS-vGfkI.js.map +1 -0
  84. package/dist/demo/{DemoHome-CUMZsYaH.js → DemoHome-Clbn8AmS.js} +9 -12
  85. package/dist/demo/DemoHome-Clbn8AmS.js.map +1 -0
  86. package/dist/demo/DemoJsonViewer-DkIX_ky2.js +109 -0
  87. package/dist/demo/DemoJsonViewer-DkIX_ky2.js.map +1 -0
  88. package/dist/demo/DemoLayout-C56xb5EE.js +73 -0
  89. package/dist/demo/DemoLayout-C56xb5EE.js.map +1 -0
  90. package/dist/demo/DemoLogin-BZwpicOS.js +128 -0
  91. package/dist/demo/DemoLogin-BZwpicOS.js.map +1 -0
  92. package/dist/demo/DemoRegister-C7_qc4MJ.js +140 -0
  93. package/dist/demo/DemoRegister-C7_qc4MJ.js.map +1 -0
  94. package/dist/demo/DemoResetPassword-BI1Ct4Dw.js +76 -0
  95. package/dist/demo/DemoResetPassword-BI1Ct4Dw.js.map +1 -0
  96. package/dist/demo/{DemoSidebar-C1csnGhX.js → DemoSidebar-CcBo4ltC.js} +6 -9
  97. package/dist/demo/DemoSidebar-CcBo4ltC.js.map +1 -0
  98. package/dist/demo/DemoText-CzXuUn3g.js +124 -0
  99. package/dist/demo/DemoText-CzXuUn3g.js.map +1 -0
  100. package/dist/demo/DemoToast-BgHDhWrX.js +95 -0
  101. package/dist/demo/DemoToast-BgHDhWrX.js.map +1 -0
  102. package/dist/demo/{DemoTypeForm-CWz6fJrJ.js → DemoTypeForm-DDzWoMSV.js} +4 -4
  103. package/dist/demo/{DemoTypeForm-CWz6fJrJ.js.map → DemoTypeForm-DDzWoMSV.js.map} +1 -1
  104. package/dist/demo/DemoVerifyEmail-C_Irdnov.js +30 -0
  105. package/dist/demo/DemoVerifyEmail-C_Irdnov.js.map +1 -0
  106. package/dist/demo/IconGoogle-CSQLPYwX.js +56 -0
  107. package/dist/demo/IconGoogle-CSQLPYwX.js.map +1 -0
  108. package/dist/demo/Login-hSOU3jZc.js +219 -0
  109. package/dist/demo/Login-hSOU3jZc.js.map +1 -0
  110. package/dist/demo/Profile-CWqti7FB.js +155 -0
  111. package/dist/demo/Profile-CWqti7FB.js.map +1 -0
  112. package/dist/demo/Register-a70LPgs2.js +375 -0
  113. package/dist/demo/Register-a70LPgs2.js.map +1 -0
  114. package/dist/demo/ResetPassword-DWN0lzr5.js +286 -0
  115. package/dist/demo/ResetPassword-DWN0lzr5.js.map +1 -0
  116. package/dist/demo/Showcase-Dq3MISpd.js +232 -0
  117. package/dist/demo/Showcase-Dq3MISpd.js.map +1 -0
  118. package/dist/demo/VerifyEmail-DZWL72K4.js +135 -0
  119. package/dist/demo/VerifyEmail-DZWL72K4.js.map +1 -0
  120. package/dist/demo/auth-d6n3xbug.js +257 -0
  121. package/dist/demo/auth-d6n3xbug.js.map +1 -0
  122. package/dist/demo/core-RCUw1Q-a.js +4217 -0
  123. package/dist/demo/core-RCUw1Q-a.js.map +1 -0
  124. package/dist/demo/index.d.ts +17 -6
  125. package/dist/demo/index.d.ts.map +1 -1
  126. package/dist/demo/index.js +92 -24
  127. package/dist/demo/index.js.map +1 -1
  128. package/dist/demo/rolldown-runtime-CjeV3_4I.js +18 -0
  129. package/package.json +16 -20
  130. package/src/admin/AdminRouter.ts +10 -39
  131. package/src/admin/components/AdminLayout.tsx +42 -10
  132. package/src/admin/components/audits/AdminAudits.tsx +10 -64
  133. package/src/admin/components/files/AdminFiles.tsx +2 -3
  134. package/src/admin/components/jobs/AdminJobDashboard.tsx +36 -142
  135. package/src/admin/components/jobs/AdminJobExecutions.tsx +117 -175
  136. package/src/admin/components/jobs/AdminJobRegistry.tsx +58 -73
  137. package/src/admin/components/keys/AdminApiKeys.tsx +21 -169
  138. package/src/admin/components/parameters/AdminParameters.tsx +4 -4
  139. package/src/admin/components/parameters/ParameterEmptyState.tsx +1 -2
  140. package/src/admin/components/parameters/ParameterHistory.tsx +3 -3
  141. package/src/admin/components/parameters/ParameterTree.tsx +2 -8
  142. package/src/admin/components/parameters/types.ts +3 -3
  143. package/src/admin/components/sessions/AdminSessions.tsx +8 -16
  144. package/src/admin/components/users/AdminUserLayout.tsx +113 -150
  145. package/src/admin/components/users/AdminUserProfile.tsx +50 -0
  146. package/src/admin/components/users/AdminUserSessions.tsx +106 -126
  147. package/src/admin/components/users/AdminUsers.tsx +46 -62
  148. package/src/admin/index.ts +0 -4
  149. package/src/auth/components/buttons/UserButton.tsx +1 -1
  150. package/src/auth/index.ts +0 -4
  151. package/src/core/UiRouter.ts +1 -1
  152. package/src/core/atoms/alephaSidebarAtom.ts +7 -31
  153. package/src/core/components/{layout/AlephaMantineProvider.tsx → AlephaMantineProvider.tsx} +3 -4
  154. package/src/core/components/Flex.tsx +63 -0
  155. package/src/core/components/Heading.tsx +19 -0
  156. package/src/core/components/Text.tsx +140 -0
  157. package/src/core/components/buttons/ActionButton.tsx +12 -1
  158. package/src/core/components/buttons/BurgerButton.tsx +3 -3
  159. package/src/core/components/buttons/LanguageButton.tsx +1 -1
  160. package/src/core/components/buttons/ToggleSidebarButton.tsx +1 -4
  161. package/src/core/components/data/DetailDrawer.tsx +144 -0
  162. package/src/core/components/data/DetailList.tsx +64 -0
  163. package/src/core/components/data/StatCards.tsx +50 -0
  164. package/src/core/components/layout/AppBar.tsx +11 -10
  165. package/src/core/components/layout/Breadcrumb.tsx +8 -8
  166. package/src/core/components/layout/Container.tsx +15 -0
  167. package/src/core/components/layout/DashboardShell.tsx +23 -238
  168. package/src/core/components/layout/Omnibar.tsx +1 -2
  169. package/src/core/components/layout/Sidebar.tsx +103 -71
  170. package/src/core/components/layout/index.ts +65 -0
  171. package/src/core/{components/form → form/components}/Control.tsx +32 -14
  172. package/src/core/{components/form → form/components}/ControlArray.tsx +2 -5
  173. package/src/core/{components/form → form/components}/ControlDate.tsx +1 -4
  174. package/src/core/{components/form → form/components}/ControlNumber.tsx +1 -4
  175. package/src/core/{components/form → form/components}/ControlObject.tsx +1 -4
  176. package/src/core/{components/form → form/components}/ControlQueryBuilder.tsx +7 -7
  177. package/src/core/{components/form → form/components}/ControlSelect.tsx +2 -4
  178. package/src/core/{components/form → form/components}/TypeForm.browser.spec.tsx +22 -64
  179. package/src/core/{components/form → form/components}/TypeForm.tsx +1 -3
  180. package/src/core/form/factories/dialogForm.tsx +31 -0
  181. package/src/core/form/index.ts +23 -0
  182. package/src/core/{utils → form/utils}/parseInput.ts +2 -4
  183. package/src/core/index.ts +43 -51
  184. package/src/core/interfaces/AlephaIntent.ts +6 -0
  185. package/src/core/interfaces/AlephaTheme.ts +0 -1
  186. package/src/core/json/factories/dialogJson.tsx +24 -0
  187. package/src/core/json/index.ts +2 -0
  188. package/src/core/primitives/$ui.ts +17 -0
  189. package/src/core/services/DialogService.tsx +1 -48
  190. package/src/core/styles.css +1 -8
  191. package/src/core/{components/table → table/components}/ColumnPicker.tsx +2 -3
  192. package/src/core/{components/table → table/components}/DataTable.tsx +8 -9
  193. package/src/core/{components/table → table/components}/DataTableFilters.tsx +6 -3
  194. package/src/core/{components/table → table/components}/DataTableToolbar.tsx +4 -5
  195. package/src/core/{components/table → table/components}/FilterPicker.tsx +2 -3
  196. package/src/core/table/index.ts +12 -0
  197. package/src/core/{components/table → table/interfaces}/types.ts +2 -2
  198. package/src/demo/DemoRouter.ts +87 -6
  199. package/src/demo/components/DemoHome.tsx +6 -10
  200. package/src/demo/components/DemoLayout.tsx +38 -8
  201. package/src/demo/components/auth/DemoLogin.tsx +1 -1
  202. package/src/demo/components/auth/DemoRegister.tsx +1 -1
  203. package/src/demo/components/auth/DemoResetPassword.tsx +1 -1
  204. package/src/demo/components/auth/DemoVerifyEmail.tsx +1 -1
  205. package/src/demo/components/core/DemoButton.tsx +160 -0
  206. package/src/demo/components/core/DemoFlex.tsx +101 -0
  207. package/src/demo/components/core/DemoHeading.tsx +13 -0
  208. package/src/demo/components/core/DemoText.tsx +110 -0
  209. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  210. package/src/demo/components/layout/DemoDialog.tsx +103 -0
  211. package/src/demo/components/{core → layout}/DemoSidebar.tsx +0 -1
  212. package/src/demo/components/layout/DemoToast.tsx +96 -0
  213. package/src/demo/components/shared/MacWindow.tsx +149 -74
  214. package/src/demo/components/shared/Showcase.tsx +4 -8
  215. package/src/demo/index.ts +1 -4
  216. package/src/demo/primitives/$uiDemo.ts +10 -0
  217. package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +0 -1
  218. package/dist/admin/AdminAudits-BQno3hZG.js.map +0 -1
  219. package/dist/admin/AdminFiles-kvuUaASF.js.map +0 -1
  220. package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +0 -1
  221. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +0 -1
  222. package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +0 -1
  223. package/dist/admin/AdminLayout-e-ZP5nWw.js +0 -37
  224. package/dist/admin/AdminLayout-e-ZP5nWw.js.map +0 -1
  225. package/dist/admin/AdminParameters-DCGbpt2c.js.map +0 -1
  226. package/dist/admin/AdminSessions-DyhW6RZv.js.map +0 -1
  227. package/dist/admin/AdminUserAudits-D1GcREEE.js +0 -177
  228. package/dist/admin/AdminUserAudits-D1GcREEE.js.map +0 -1
  229. package/dist/admin/AdminUserCreate-DR8LA0tv.js +0 -104
  230. package/dist/admin/AdminUserCreate-DR8LA0tv.js.map +0 -1
  231. package/dist/admin/AdminUserDetails-CDkZNHQD.js +0 -477
  232. package/dist/admin/AdminUserDetails-CDkZNHQD.js.map +0 -1
  233. package/dist/admin/AdminUserLayout-CrBj4UuI.js.map +0 -1
  234. package/dist/admin/AdminUserSessions-srgFHrqy.js +0 -129
  235. package/dist/admin/AdminUserSessions-srgFHrqy.js.map +0 -1
  236. package/dist/admin/AdminUserSettings-BFuxl-xT.js +0 -167
  237. package/dist/admin/AdminUserSettings-BFuxl-xT.js.map +0 -1
  238. package/dist/admin/AdminUsers-D1pDpiwK.js +0 -118
  239. package/dist/admin/AdminUsers-D1pDpiwK.js.map +0 -1
  240. package/dist/demo/DemoDataTable-lnBKWBf8.js.map +0 -1
  241. package/dist/demo/DemoHome-CUMZsYaH.js.map +0 -1
  242. package/dist/demo/DemoJsonViewer-_uokbGaW.js +0 -429
  243. package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +0 -1
  244. package/dist/demo/DemoLayout-DHVoacE6.js +0 -46
  245. package/dist/demo/DemoLayout-DHVoacE6.js.map +0 -1
  246. package/dist/demo/DemoLogin-DjJ9314c.js.map +0 -1
  247. package/dist/demo/DemoRegister-DzkJ5M83.js.map +0 -1
  248. package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +0 -1
  249. package/dist/demo/DemoSidebar-C1csnGhX.js.map +0 -1
  250. package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +0 -1
  251. package/dist/demo/Showcase-BzoXNlCn.js +0 -185
  252. package/dist/demo/Showcase-BzoXNlCn.js.map +0 -1
  253. package/dist/json/index.d.ts +0 -57
  254. package/dist/json/index.d.ts.map +0 -1
  255. package/dist/json/index.js +0 -325
  256. package/dist/json/index.js.map +0 -1
  257. package/src/admin/components/users/AdminUserAudits.tsx +0 -184
  258. package/src/admin/components/users/AdminUserCreate.tsx +0 -85
  259. package/src/admin/components/users/AdminUserDetails.tsx +0 -431
  260. package/src/admin/components/users/AdminUserSettings.tsx +0 -171
  261. package/src/core/components/data/ErrorViewer.tsx +0 -171
  262. package/src/json/extensions/DialogService.tsx +0 -31
  263. package/src/json/index.ts +0 -18
  264. package/src/json/styles.css +0 -1
  265. /package/dist/{demo → auth}/IconGoogle-Ch1m3Uzl.js +0 -0
  266. /package/dist/{demo → auth}/IconGoogle-Ch1m3Uzl.js.map +0 -0
  267. /package/src/{json → core/json}/components/JsonViewer.css +0 -0
  268. /package/src/{json → core/json}/components/JsonViewer.tsx +0 -0
  269. /package/src/core/{components/table → table/components}/DataTablePagination.tsx +0 -0
  270. /package/src/core/{components/table → table/components}/useTableSelection.ts +0 -0
@@ -0,0 +1,4217 @@
1
+ import { $atom, $context, $inject, $module, Alepha, AlephaError, TypeBoxError, t } from "alepha";
2
+ import { AlephaReactForm, FormValidationError, useForm, useFormState } from "alepha/react/form";
3
+ import { $head, AlephaReactHead } from "alepha/react/head";
4
+ import { AlephaReactI18n, useI18n } from "alepha/react/i18n";
5
+ import { $cookie } from "alepha/server/cookies";
6
+ import { ModalsProvider, modals } from "@mantine/modals";
7
+ import { ActionIcon, Anchor, AppShell, Autocomplete, Badge, Burger, Button, Card, Checkbox, ColorInput, ColorSchemeScript, Container, Divider, Drawer, Fieldset, FileInput, Flex, Grid, Image, Input, Kbd, Loader, MantineProvider, Menu, MultiSelect, NumberInput, Pagination, Paper, PasswordInput, Popover, ScrollArea, SegmentedControl, Select, Slider, Switch, Table, TagsInput, Text, TextInput, Textarea, ThemeIcon, Tooltip, Tree, UnstyledButton, getTreeExpandedState, useMantineColorScheme, useMantineTheme, useTree } from "@mantine/core";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { Children, Fragment as Fragment$1, createElement, forwardRef, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import { Notifications, notifications } from "@mantine/notifications";
11
+ import { IconAlertTriangle, IconArrowDown, IconArrowLeft, IconArrowUp, IconArrowsSort, IconAt, IconCalendar, IconCheck, IconChevronDown, IconChevronRight, IconClipboard, IconClock, IconColorPicker, IconColumns, IconCopy, IconDownload, IconFile, IconFilter, IconGripVertical, IconHash, IconInfoCircle, IconInfoTriangle, IconKey, IconLanguage, IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightCollapse, IconLetterCase, IconLink, IconList, IconMail, IconMoon, IconPalette, IconPhone, IconPlus, IconRefresh, IconSearch, IconSelector, IconSquareRounded, IconSun, IconToggleLeft, IconTrash, IconX } from "@tabler/icons-react";
12
+ import { $page, Link, NestedView, useActive, useRouter, useRouterState } from "alepha/react/router";
13
+ import { NavigationProgress, nprogress } from "@mantine/nprogress";
14
+ import { ClientOnly, useAction, useAlepha, useEvents, useInject, useStore } from "alepha/react";
15
+ import { Spotlight, spotlight } from "@mantine/spotlight";
16
+ import { currentUserAtom } from "alepha/security";
17
+ import { useOs } from "@mantine/hooks";
18
+ import { DateInput, DateTimePicker, TimeInput } from "@mantine/dates";
19
+ import { parseQueryString } from "alepha/orm";
20
+ import { DateTimeProvider } from "alepha/datetime";
21
+
22
+ //#region ../../src/core/atoms/alephaSidebarAtom.ts
23
+ const alephaSidebarAtom = $atom({
24
+ name: "alepha.ui.sidebar",
25
+ schema: t.object({
26
+ closed: t.boolean(),
27
+ collapsed: t.boolean(),
28
+ expandedWidth: t.number(),
29
+ collapsedWidth: t.number()
30
+ }),
31
+ default: {
32
+ closed: true,
33
+ collapsed: false,
34
+ expandedWidth: 300,
35
+ collapsedWidth: 78
36
+ }
37
+ });
38
+
39
+ //#endregion
40
+ //#region ../../src/core/atoms/alephaThemeAtom.ts
41
+ const alephaThemeAtom = $atom({
42
+ name: "alepha.ui.theme",
43
+ schema: t.object({ index: t.integer() }),
44
+ default: { index: 0 }
45
+ });
46
+
47
+ //#endregion
48
+ //#region ../../src/core/atoms/themes/default.ts
49
+ const defaultTheme = {
50
+ name: "Default",
51
+ description: "Default Alepha Theme"
52
+ };
53
+
54
+ //#endregion
55
+ //#region ../../src/core/atoms/themes/midnight.ts
56
+ const midnightTheme = {
57
+ name: "Midnight",
58
+ description: "Clean, developer-focused design",
59
+ primaryColor: "pink",
60
+ primaryShade: {
61
+ light: 7,
62
+ dark: 8
63
+ },
64
+ fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"",
65
+ fontFamilyMonospace: "ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace",
66
+ headings: {
67
+ fontFamily: "-apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica, Arial, sans-serif",
68
+ fontWeight: "600",
69
+ textWrap: "wrap",
70
+ sizes: {
71
+ h1: {
72
+ fontSize: "2rem",
73
+ lineHeight: "1.25"
74
+ },
75
+ h2: {
76
+ fontSize: "1.5rem",
77
+ lineHeight: "1.3"
78
+ },
79
+ h3: {
80
+ fontSize: "1.25rem",
81
+ lineHeight: "1.4"
82
+ },
83
+ h4: {
84
+ fontSize: "1rem",
85
+ lineHeight: "1.5"
86
+ },
87
+ h5: {
88
+ fontSize: "0.875rem",
89
+ lineHeight: "1.5"
90
+ },
91
+ h6: {
92
+ fontSize: "0.75rem",
93
+ lineHeight: "1.5"
94
+ }
95
+ }
96
+ },
97
+ radius: {
98
+ xs: "3px",
99
+ sm: "6px",
100
+ md: "6px",
101
+ lg: "8px",
102
+ xl: "12px"
103
+ },
104
+ defaultRadius: "sm",
105
+ colors: {
106
+ dark: [
107
+ "#d0d7de",
108
+ "#8b949e",
109
+ "#6e7681",
110
+ "#484f58",
111
+ "#30363d",
112
+ "#21262d",
113
+ "#161b22",
114
+ "#151b23",
115
+ "#0d1117",
116
+ "#010409"
117
+ ],
118
+ gray: [
119
+ "#f6f8fa",
120
+ "#eaeef2",
121
+ "#d0d7de",
122
+ "#afb8c1",
123
+ "#8c959f",
124
+ "#6e7781",
125
+ "#57606a",
126
+ "#424a53",
127
+ "#32383f",
128
+ "#24292f"
129
+ ],
130
+ blue: [
131
+ "#ddf4ff",
132
+ "#b6e3ff",
133
+ "#80ccff",
134
+ "#54aeff",
135
+ "#218bff",
136
+ "#0969da",
137
+ "#0550ae",
138
+ "#033d8b",
139
+ "#0a3069",
140
+ "#002155"
141
+ ],
142
+ green: [
143
+ "#dafbe1",
144
+ "#aceebb",
145
+ "#6fdd8b",
146
+ "#4ac26b",
147
+ "#2da44e",
148
+ "#1a7f37",
149
+ "#116329",
150
+ "#044f1e",
151
+ "#003d16",
152
+ "#002d11"
153
+ ],
154
+ red: [
155
+ "#ffebe9",
156
+ "#ffcecb",
157
+ "#ffaba8",
158
+ "#ff8182",
159
+ "#fa4549",
160
+ "#cf222e",
161
+ "#a40e26",
162
+ "#82071e",
163
+ "#660018",
164
+ "#4c0014"
165
+ ]
166
+ }
167
+ };
168
+
169
+ //#endregion
170
+ //#region ../../src/core/atoms/alephaThemeListAtom.ts
171
+ const alephaThemeListAtom = $atom({
172
+ name: "alepha.ui.themeList",
173
+ schema: t.array(t.json()),
174
+ default: [defaultTheme, midnightTheme]
175
+ });
176
+
177
+ //#endregion
178
+ //#region ../../src/core/providers/ThemeProvider.ts
179
+ var ThemeProvider = class {
180
+ alepha = $inject(Alepha);
181
+ cookie = $cookie({
182
+ name: "theme",
183
+ schema: alephaThemeAtom.schema,
184
+ ttl: [1, "year"]
185
+ });
186
+ head = $head(() => {
187
+ const theme = this.getTheme();
188
+ if (!theme || !theme.name) return {};
189
+ return { htmlAttributes: { "data-theme": theme.name } };
190
+ });
191
+ setTheme(index) {
192
+ const newTheme = this.alepha.store.get(alephaThemeListAtom)[index];
193
+ if (!newTheme) throw new AlephaError(`Theme with index ${index} not found`);
194
+ this.cookie.set({ index });
195
+ this.alepha.store.set(alephaThemeAtom, { index });
196
+ if (typeof document === "undefined") return;
197
+ document.documentElement.removeAttribute("data-theme");
198
+ if (newTheme.name) document.documentElement.setAttribute("data-theme", newTheme.name);
199
+ }
200
+ getTheme() {
201
+ const index = this.getThemeIndex();
202
+ const list = this.alepha.store.get(alephaThemeListAtom);
203
+ return list[index] || list[0] || defaultTheme;
204
+ }
205
+ getThemeIndex() {
206
+ try {
207
+ return this.cookie.get()?.index ?? this.alepha.store.get(alephaThemeAtom)?.index ?? 0;
208
+ } catch {
209
+ return this.alepha.store.get(alephaThemeAtom)?.index ?? 0;
210
+ }
211
+ }
212
+ };
213
+
214
+ //#endregion
215
+ //#region ../../src/core/components/dialogs/AlertDialog.tsx
216
+ const AlertDialog = ({ options, onClose }) => /* @__PURE__ */ jsxs(Fragment, { children: [options?.message && /* @__PURE__ */ jsx(Text, {
217
+ mb: "md",
218
+ children: options.message
219
+ }), /* @__PURE__ */ jsx(Flex, {
220
+ justify: "flex-end",
221
+ children: /* @__PURE__ */ jsx(Button, {
222
+ onClick: onClose,
223
+ children: options?.okLabel || "OK"
224
+ })
225
+ })] });
226
+
227
+ //#endregion
228
+ //#region ../../src/core/components/dialogs/ConfirmDialog.tsx
229
+ const ConfirmDialog = ({ options, onConfirm }) => /* @__PURE__ */ jsxs(Fragment, { children: [options?.message && /* @__PURE__ */ jsx(Text, {
230
+ mb: "md",
231
+ children: options.message
232
+ }), /* @__PURE__ */ jsxs(Flex, {
233
+ justify: "flex-end",
234
+ children: [/* @__PURE__ */ jsx(Button, {
235
+ variant: "subtle",
236
+ onClick: () => onConfirm(false),
237
+ children: options?.cancelLabel || "Cancel"
238
+ }), /* @__PURE__ */ jsx(Button, {
239
+ color: options?.confirmColor || "blue",
240
+ onClick: () => onConfirm(true),
241
+ children: options?.confirmLabel || "Confirm"
242
+ })]
243
+ })] });
244
+
245
+ //#endregion
246
+ //#region ../../src/core/components/dialogs/PromptDialog.tsx
247
+ const PromptDialog = ({ options, onSubmit }) => {
248
+ const [value, setValue] = useState(options?.defaultValue || "");
249
+ const inputRef = useRef(null);
250
+ useEffect(() => {
251
+ inputRef.current?.focus();
252
+ }, []);
253
+ const handleSubmit = () => {
254
+ if (!options?.required || value.trim()) onSubmit(value);
255
+ };
256
+ const handleKeyDown = (event) => {
257
+ if (event.key === "Enter") handleSubmit();
258
+ };
259
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
260
+ options?.message && /* @__PURE__ */ jsx(Text, {
261
+ mb: "md",
262
+ children: options.message
263
+ }),
264
+ /* @__PURE__ */ jsx(TextInput, {
265
+ ref: inputRef,
266
+ label: options?.label,
267
+ placeholder: options?.placeholder,
268
+ value,
269
+ onChange: (event) => setValue(event.currentTarget.value),
270
+ onKeyDown: handleKeyDown,
271
+ required: options?.required,
272
+ mb: "md"
273
+ }),
274
+ /* @__PURE__ */ jsxs(Flex, {
275
+ justify: "flex-end",
276
+ children: [/* @__PURE__ */ jsx(Button, {
277
+ variant: "subtle",
278
+ onClick: () => onSubmit(null),
279
+ children: options?.cancelLabel || "Cancel"
280
+ }), /* @__PURE__ */ jsx(Button, {
281
+ onClick: handleSubmit,
282
+ disabled: options?.required && !value.trim(),
283
+ children: options?.submitLabel || "OK"
284
+ })]
285
+ })
286
+ ] });
287
+ };
288
+
289
+ //#endregion
290
+ //#region ../../src/core/services/DialogService.tsx
291
+ var DialogService = class {
292
+ options = { default: {
293
+ centered: true,
294
+ withCloseButton: true,
295
+ size: "md",
296
+ overlayProps: {
297
+ backgroundOpacity: .55,
298
+ blur: 3
299
+ },
300
+ transitionProps: {
301
+ transition: "pop",
302
+ duration: 200
303
+ }
304
+ } };
305
+ /**
306
+ * Show an alert dialog with a message
307
+ */
308
+ alert(options) {
309
+ return new Promise((resolve) => {
310
+ const modalId = this.open({
311
+ ...options,
312
+ title: options?.title || "Alert",
313
+ content: /* @__PURE__ */ jsx(AlertDialog, {
314
+ options,
315
+ onClose: () => {
316
+ this.close(modalId);
317
+ resolve();
318
+ }
319
+ })
320
+ });
321
+ });
322
+ }
323
+ /**
324
+ * Show a confirmation dialog that returns a promise
325
+ */
326
+ confirm(options) {
327
+ return new Promise((resolve) => {
328
+ const modalId = this.open({
329
+ ...options,
330
+ title: options?.title || "Confirm",
331
+ closeOnClickOutside: false,
332
+ closeOnEscape: false,
333
+ content: /* @__PURE__ */ jsx(ConfirmDialog, {
334
+ options,
335
+ onConfirm: (confirmed) => {
336
+ this.close(modalId);
337
+ resolve(confirmed);
338
+ }
339
+ })
340
+ });
341
+ });
342
+ }
343
+ /**
344
+ * Show a prompt dialog to get user input
345
+ */
346
+ prompt(options) {
347
+ return new Promise((resolve) => {
348
+ const modalId = this.open({
349
+ ...options,
350
+ title: options?.title || "Input",
351
+ closeOnClickOutside: false,
352
+ closeOnEscape: false,
353
+ content: /* @__PURE__ */ jsx(PromptDialog, {
354
+ options,
355
+ onSubmit: (value) => {
356
+ this.close(modalId);
357
+ resolve(value);
358
+ }
359
+ })
360
+ });
361
+ });
362
+ }
363
+ /**
364
+ * Open a custom dialog with provided content
365
+ */
366
+ open(options) {
367
+ return modals.open({
368
+ ...this.options.default,
369
+ ...options,
370
+ children: options?.content || options?.message
371
+ });
372
+ }
373
+ /**
374
+ * Close the currently open dialog or a specific dialog by ID
375
+ */
376
+ close(modalId) {
377
+ if (modalId) modals.close(modalId);
378
+ else modals.closeAll();
379
+ }
380
+ };
381
+
382
+ //#endregion
383
+ //#region ../../src/core/services/ToastService.tsx
384
+ var ToastService = class {
385
+ raw = notifications;
386
+ options = { default: {
387
+ radius: "md",
388
+ withBorder: true,
389
+ withCloseButton: true,
390
+ autoClose: 5e3,
391
+ position: "top-center"
392
+ } };
393
+ show(options) {
394
+ notifications.show({
395
+ ...this.options.default,
396
+ ...options
397
+ });
398
+ }
399
+ info(options) {
400
+ if (typeof options === "string") options = { message: options };
401
+ this.show({
402
+ color: "blue",
403
+ icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: 20 }),
404
+ title: "Info",
405
+ message: "Information notification",
406
+ ...options
407
+ });
408
+ }
409
+ success(options) {
410
+ if (typeof options === "string") options = { message: options };
411
+ this.show({
412
+ color: "green",
413
+ icon: /* @__PURE__ */ jsx(IconCheck, { size: 16 }),
414
+ title: "Success",
415
+ message: "Operation completed successfully",
416
+ ...options
417
+ });
418
+ }
419
+ warning(options) {
420
+ if (typeof options === "string") options = { message: options };
421
+ this.show({
422
+ color: "yellow",
423
+ icon: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20 }),
424
+ title: "Warning",
425
+ message: "Please review this warning",
426
+ ...options
427
+ });
428
+ }
429
+ danger(options) {
430
+ if (typeof options === "string") options = { message: options };
431
+ this.show({
432
+ color: "red",
433
+ icon: /* @__PURE__ */ jsx(IconX, { size: 20 }),
434
+ title: "Error",
435
+ message: "An error occurred",
436
+ ...options
437
+ });
438
+ }
439
+ };
440
+
441
+ //#endregion
442
+ //#region ../../src/core/UiRouter.ts
443
+ /**
444
+ * UI Router defining the root page with AlephaMantineProvider.
445
+ *
446
+ * - Use UiRouter when you need Alepha's Mantine-based UI components and theming.
447
+ * - Prefer to use $ui() for convenience. (Custom Factory of UiRouter)
448
+ */
449
+ var UiRouter = class {
450
+ root = $page({
451
+ path: "/",
452
+ component: AlephaMantineProvider
453
+ });
454
+ };
455
+
456
+ //#endregion
457
+ //#region ../../src/core/hooks/useTheme.ts
458
+ /**
459
+ * Hook to get and set the current theme.
460
+ *
461
+ * Returns a tuple with the current theme and a function to set the theme.
462
+ *
463
+ * ```tsx
464
+ * const [theme, setTheme] = useTheme();
465
+ * ```
466
+ */
467
+ const useTheme = () => {
468
+ useStore(alephaThemeAtom);
469
+ const themeProvider = useInject(ThemeProvider);
470
+ const theme = themeProvider.getTheme();
471
+ const setTheme = (theme) => {
472
+ themeProvider.setTheme(theme.index);
473
+ };
474
+ return [theme, setTheme];
475
+ };
476
+
477
+ //#endregion
478
+ //#region ../../src/core/hooks/useToast.ts
479
+ /**
480
+ * Use this hook to access the Toast Service for showing notifications.
481
+ *
482
+ * @example
483
+ * ```tsx
484
+ * const toast = useToast();
485
+ * toast.success({ message: "Operation completed successfully!" });
486
+ * toast.error({ title: "Error", message: "Something went wrong" });
487
+ * ```
488
+ */
489
+ const useToast = () => {
490
+ return useInject(ToastService);
491
+ };
492
+
493
+ //#endregion
494
+ //#region ../../src/core/components/layout/Omnibar.tsx
495
+ const Omnibar = (props) => {
496
+ const shortcut = props.shortcut ?? "mod+K";
497
+ const searchPlaceholder = props.searchPlaceholder ?? "Search...";
498
+ const nothingFound = props.nothingFound ?? "Nothing found...";
499
+ const router = useRouter();
500
+ const [user] = useStore(currentUserAtom);
501
+ return /* @__PURE__ */ jsx(Spotlight, {
502
+ actions: useMemo(() => router.concretePages.filter((page) => {
503
+ if (page.can && !page.can()) return false;
504
+ return true;
505
+ }).map((page) => ({
506
+ id: page.name,
507
+ label: page.label ?? page.name,
508
+ description: page.description,
509
+ onClick: () => {
510
+ if (page.staticName) return router.push(page.staticName, { params: page.params });
511
+ return router.push(page.name);
512
+ },
513
+ leftSection: renderIcon(page.icon)
514
+ })), [user]),
515
+ shortcut,
516
+ limit: 10,
517
+ searchProps: {
518
+ leftSection: /* @__PURE__ */ jsx(IconSearch, { size: ui.sizes.icon.md }),
519
+ placeholder: searchPlaceholder
520
+ },
521
+ nothingFound
522
+ });
523
+ };
524
+
525
+ //#endregion
526
+ //#region ../../src/core/components/AlephaMantineProvider.tsx
527
+ const AlephaMantineProvider = (props) => {
528
+ const toast = useToast();
529
+ const [theme] = useTheme();
530
+ useEvents({
531
+ "react:transition:begin": () => {
532
+ nprogress.start();
533
+ },
534
+ "react:transition:end": () => {
535
+ nprogress.complete();
536
+ },
537
+ "react:action:error": ({ error, type }) => {
538
+ if (type === "transition" || error instanceof FormValidationError || error instanceof TypeBoxError) return;
539
+ toast.danger({
540
+ title: error.name || "Error",
541
+ message: error.message ?? "An error occurred while processing your action."
542
+ });
543
+ }
544
+ }, []);
545
+ const defaultColorScheme = props.mantine?.defaultColorScheme ?? theme.defaultColorScheme;
546
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ColorSchemeScript, {
547
+ defaultColorScheme,
548
+ ...props.colorSchemeScript
549
+ }), /* @__PURE__ */ jsxs(MantineProvider, {
550
+ ...props.mantine,
551
+ defaultColorScheme,
552
+ theme: {
553
+ ...theme,
554
+ ...props.mantine?.theme
555
+ },
556
+ children: [
557
+ /* @__PURE__ */ jsx(Notifications, { ...props.notifications }),
558
+ /* @__PURE__ */ jsx(NavigationProgress, { ...props.navigationProgress }),
559
+ /* @__PURE__ */ jsxs(ModalsProvider, {
560
+ ...props.modals,
561
+ children: [props.omnibar !== false && /* @__PURE__ */ jsx(Omnibar, { ...props.omnibar }), props.children ?? /* @__PURE__ */ jsx(NestedView, {})]
562
+ })
563
+ ]
564
+ })] });
565
+ };
566
+
567
+ //#endregion
568
+ //#region ../../src/core/constants/ui.ts
569
+ const ui = {
570
+ colors: {
571
+ transparent: "transparent",
572
+ background: "var(--alepha-background)",
573
+ surface: "var(--alepha-surface)",
574
+ elevated: "var(--alepha-elevated)",
575
+ border: "var(--alepha-border)"
576
+ },
577
+ sizes: { icon: {
578
+ xs: 14,
579
+ sm: 16,
580
+ md: 20,
581
+ lg: 24,
582
+ xl: 32
583
+ } }
584
+ };
585
+
586
+ //#endregion
587
+ //#region ../../src/core/helpers/isComponentType.ts
588
+ function isComponentType(param) {
589
+ if (isValidElement(param)) return false;
590
+ return typeof param === "function" || typeof param === "object" && param !== null && "$$typeof" in param;
591
+ }
592
+
593
+ //#endregion
594
+ //#region ../../src/core/components/buttons/ActionButton.tsx
595
+ const ActionMenuItem = (props) => {
596
+ const { item, index } = props;
597
+ const router = useRouter();
598
+ const action = useAction({ handler: async (e) => {
599
+ await item.onClick?.();
600
+ } }, [item.onClick]);
601
+ if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
602
+ if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
603
+ if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
604
+ trigger: "hover",
605
+ position: "right-start",
606
+ offset: 2,
607
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
608
+ leftSection: item.icon,
609
+ rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
610
+ children: item.label
611
+ }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx(ActionMenuItem, {
612
+ item: child,
613
+ index: childIndex
614
+ }, childIndex)) })]
615
+ }, index);
616
+ const menuItemProps = {};
617
+ if (props.item.onClick) menuItemProps.onClick = action.run;
618
+ else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
619
+ return /* @__PURE__ */ jsx(Menu.Item, {
620
+ leftSection: item.icon,
621
+ onClick: item.onClick,
622
+ color: item.color,
623
+ rightSection: item.active ? /* @__PURE__ */ jsx(ThemeIcon, {
624
+ size: "xs",
625
+ variant: "transparent",
626
+ children: /* @__PURE__ */ jsx(IconCheck, {})
627
+ }) : void 0,
628
+ ...menuItemProps,
629
+ children: item.label
630
+ }, index);
631
+ };
632
+ const ActionButton = (_props) => {
633
+ const theme = useMantineTheme();
634
+ const props = { ..._props };
635
+ const { tooltip, menu, icon, ...restProps } = props;
636
+ if (props.variant === "subtle" || props.variant === "outline") restProps.color ??= "gray";
637
+ if (props.intent) {
638
+ if (props.intent === "none") restProps.color ??= "gray";
639
+ else if (props.intent === "primary") restProps.color ??= theme.primaryColor;
640
+ else if (props.intent === "success") {
641
+ restProps.c ??= "white";
642
+ restProps.color ??= "green";
643
+ } else if (props.intent === "danger") {
644
+ restProps.c ??= "white";
645
+ restProps.color ??= "red";
646
+ } else if (props.intent === "warning") restProps.color ??= "yellow";
647
+ else if (props.intent === "info") {
648
+ restProps.c ??= "white";
649
+ restProps.color ??= "blue";
650
+ }
651
+ }
652
+ if (props.icon) {
653
+ const sizes = ui.sizes.icon;
654
+ const icon = isComponentType(props.icon) ? /* @__PURE__ */ jsx(props.icon, { size: sizes[props.size || "md"] }) : /* @__PURE__ */ jsx("span", { children: props.icon });
655
+ if (!props.children) {
656
+ restProps.children = Children.only(icon);
657
+ restProps.px ??= "xs";
658
+ } else restProps.leftSection = icon;
659
+ }
660
+ if (props.leftSection && !props.children) restProps.px ??= "xs";
661
+ if (props.textVisibleFrom) {
662
+ const { children, textVisibleFrom, leftSection, ...rest } = restProps;
663
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex, {
664
+ w: "100%",
665
+ visibleFrom: textVisibleFrom,
666
+ children: /* @__PURE__ */ jsx(ActionButton, {
667
+ flex: 1,
668
+ ...rest,
669
+ leftSection,
670
+ tooltip,
671
+ menu,
672
+ children
673
+ })
674
+ }), /* @__PURE__ */ jsx(Flex, {
675
+ w: "100%",
676
+ hiddenFrom: textVisibleFrom,
677
+ children: /* @__PURE__ */ jsx(ActionButton, {
678
+ px: "xs",
679
+ ...rest,
680
+ tooltip,
681
+ menu,
682
+ children: leftSection
683
+ })
684
+ })] });
685
+ }
686
+ const renderAction = () => {
687
+ if ("href" in restProps && restProps.href) {
688
+ if (restProps.href.startsWith("http") || restProps.target) return /* @__PURE__ */ jsx(ActionHrefButton, {
689
+ ...restProps,
690
+ href: restProps.href,
691
+ children: restProps.children
692
+ });
693
+ return /* @__PURE__ */ jsx(ActionNavigationButton, {
694
+ ...restProps,
695
+ href: restProps.href,
696
+ children: restProps.children
697
+ });
698
+ }
699
+ delete restProps.classNameActive;
700
+ delete restProps.variantActive;
701
+ delete restProps.propsActive;
702
+ if ("action" in restProps && restProps.action) return /* @__PURE__ */ jsx(ActionHookButton, {
703
+ ...restProps,
704
+ action: restProps.action,
705
+ children: restProps.children
706
+ });
707
+ if ("onClick" in restProps && restProps.onClick) return /* @__PURE__ */ jsx(ActionClickButton, {
708
+ ...restProps,
709
+ onClick: restProps.onClick,
710
+ children: restProps.children
711
+ });
712
+ if ("form" in restProps && restProps.form) {
713
+ if (restProps.type === "reset") return /* @__PURE__ */ jsx(ActionResetButton, {
714
+ ...restProps,
715
+ form: restProps.form,
716
+ children: restProps.children
717
+ });
718
+ return /* @__PURE__ */ jsx(ActionSubmitButton, {
719
+ ...restProps,
720
+ form: restProps.form,
721
+ children: restProps.children
722
+ });
723
+ }
724
+ return /* @__PURE__ */ jsx(Button, {
725
+ ...restProps,
726
+ children: restProps.children
727
+ });
728
+ };
729
+ let actionElement = renderAction();
730
+ if (menu) actionElement = /* @__PURE__ */ jsxs(Menu, {
731
+ position: menu.position || "bottom-start",
732
+ width: menu.width || 200,
733
+ shadow: menu.shadow || "md",
734
+ trigger: menu.on === "hover" ? "hover" : "click",
735
+ ...menu.menuProps,
736
+ children: [/* @__PURE__ */ jsx(Menu.Target, {
737
+ ...menu.targetProps,
738
+ children: actionElement
739
+ }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: menu.items.map((item, index) => /* @__PURE__ */ jsx(ActionMenuItem, {
740
+ item,
741
+ index
742
+ }, index)) })]
743
+ });
744
+ if (tooltip) {
745
+ const defaultTooltipProps = { openDelay: 1e3 };
746
+ return /* @__PURE__ */ jsx(Tooltip, { ...typeof tooltip === "string" || typeof tooltip === "number" ? {
747
+ ...defaultTooltipProps,
748
+ label: tooltip,
749
+ children: actionElement
750
+ } : {
751
+ ...defaultTooltipProps,
752
+ ...tooltip,
753
+ children: actionElement
754
+ } });
755
+ }
756
+ return actionElement;
757
+ };
758
+ /**
759
+ * Action button that submits a form with loading and disabled state handling.
760
+ */
761
+ const ActionSubmitButton = (props) => {
762
+ const { form, ...buttonProps } = props;
763
+ const state = useFormState(form);
764
+ return /* @__PURE__ */ jsx(Button, {
765
+ ...buttonProps,
766
+ loading: state.loading,
767
+ disabled: state.loading,
768
+ type: "submit",
769
+ children: props.children
770
+ });
771
+ };
772
+ const ActionResetButton = (props) => {
773
+ const { form, ...buttonProps } = props;
774
+ const state = useFormState(form);
775
+ return /* @__PURE__ */ jsx(Button, {
776
+ ...buttonProps,
777
+ disabled: state.loading,
778
+ type: "reset",
779
+ children: props.children
780
+ });
781
+ };
782
+ /**
783
+ * Action button that integrates with useAction hook return value.
784
+ * Automatically handles loading state and executes the action on click.
785
+ *
786
+ * @example
787
+ * ```tsx
788
+ * const saveAction = useAction({
789
+ * handler: async (data) => {
790
+ * await api.save(data);
791
+ * }
792
+ * }, []);
793
+ *
794
+ * <ActionButton action={saveAction}>
795
+ * Save
796
+ * </ActionButton>
797
+ * ```
798
+ */
799
+ const ActionHookButton = (props) => {
800
+ const { action, ...buttonProps } = props;
801
+ return /* @__PURE__ */ jsx(Button, {
802
+ ...buttonProps,
803
+ disabled: action.loading || props.disabled,
804
+ loading: action.loading,
805
+ onClick: () => action.run(),
806
+ children: props.children
807
+ });
808
+ };
809
+ /**
810
+ * Basic action button that handles click events with loading and error handling.
811
+ *
812
+ * @example
813
+ * ```tsx
814
+ * <ActionButton onClick={() => api.doSomething()}>
815
+ * Do Something
816
+ * </ActionButton>
817
+ * ```
818
+ */
819
+ const ActionClickButton = ({ preventDefault, ...props }) => {
820
+ const action = useAction({ handler: async (e) => {
821
+ if (preventDefault) e.preventDefault();
822
+ await props.onClick(e);
823
+ } }, [props.onClick, preventDefault]);
824
+ return /* @__PURE__ */ jsx(Button, {
825
+ ...props,
826
+ disabled: action.loading || props.disabled,
827
+ loading: action.loading,
828
+ onClick: action.run,
829
+ children: props.children
830
+ });
831
+ };
832
+ /**
833
+ * Action for navigation with active state support.
834
+ */
835
+ const ActionNavigationButton = (props) => {
836
+ const { active: options, classNameActive, variantActive, propsActive, routerGoOptions, onClick: propsOnClick, anchor, ...buttonProps } = props;
837
+ const router = useRouter();
838
+ const { isPending, isActive } = useActive(options ? {
839
+ href: props.href,
840
+ ...options
841
+ } : { href: props.href });
842
+ const anchorProps = router.anchor(props.href, routerGoOptions);
843
+ if (propsActive && isActive) Object.assign(buttonProps, propsActive);
844
+ const combinedOnClick = (e) => {
845
+ propsOnClick?.(e);
846
+ anchorProps.onClick?.(e);
847
+ };
848
+ const className = buttonProps.className || "";
849
+ if (isActive && options !== false && classNameActive) buttonProps.className = `${className} ${classNameActive}`.trim();
850
+ if (props.anchorProps || anchor) return /* @__PURE__ */ jsx(Anchor, {
851
+ component: "a",
852
+ ...anchorProps,
853
+ ...buttonProps,
854
+ ...props.anchorProps,
855
+ onClick: combinedOnClick,
856
+ children: props.children
857
+ });
858
+ return /* @__PURE__ */ jsx(Button, {
859
+ component: "a",
860
+ loading: isPending,
861
+ ...buttonProps,
862
+ ...anchorProps,
863
+ onClick: combinedOnClick,
864
+ variant: isActive && options !== false ? variantActive ?? "filled" : buttonProps.variant ?? "subtle",
865
+ children: props.children
866
+ });
867
+ };
868
+ const ActionHrefButton = (props) => {
869
+ const { active: options, classNameActive, variantActive, propsActive, routerGoOptions, target, ...buttonProps } = props;
870
+ return /* @__PURE__ */ jsx(Button, {
871
+ component: "a",
872
+ target,
873
+ ...buttonProps,
874
+ children: props.children
875
+ });
876
+ };
877
+
878
+ //#endregion
879
+ //#region ../../src/core/components/buttons/BurgerButton.tsx
880
+ const BurgerButton = (props) => {
881
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
882
+ return /* @__PURE__ */ jsx(Burger, {
883
+ opened: !sidebar.closed,
884
+ onClick: () => setSidebar({
885
+ ...sidebar,
886
+ closed: !sidebar.closed
887
+ }),
888
+ hiddenFrom: "md",
889
+ size: "sm",
890
+ ...props
891
+ });
892
+ };
893
+
894
+ //#endregion
895
+ //#region ../../src/core/components/buttons/DarkModeButton.tsx
896
+ /**
897
+ * SSR-safe dark mode toggle button.
898
+ *
899
+ * Uses CSS-based icon switching to avoid hydration mismatches.
900
+ * Both icons are rendered, CSS hides the wrong one based on
901
+ * `data-mantine-color-scheme` attribute.
902
+ */
903
+ const DarkModeButton = (props) => {
904
+ const { setColorScheme } = useMantineColorScheme();
905
+ const toggleColorScheme = () => {
906
+ setColorScheme((document.documentElement.getAttribute("data-mantine-color-scheme") ?? "light") === "dark" ? "light" : "dark");
907
+ };
908
+ const size = props.size ?? "md";
909
+ const iconSize = ui.sizes.icon[size] ?? ui.sizes.icon.md;
910
+ return /* @__PURE__ */ jsx(ActionButton, {
911
+ onClick: toggleColorScheme,
912
+ variant: props.variant ?? "subtle",
913
+ size,
914
+ "aria-label": "Toggle color scheme",
915
+ icon: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(IconSun, {
916
+ size: iconSize,
917
+ className: "alepha-light-hidden"
918
+ }), /* @__PURE__ */ jsx(IconMoon, {
919
+ size: iconSize,
920
+ className: "alepha-dark-hidden"
921
+ })] }),
922
+ ...props
923
+ });
924
+ };
925
+
926
+ //#endregion
927
+ //#region ../../src/core/components/buttons/LanguageButton.tsx
928
+ const LanguageButton = (props) => {
929
+ const i18n = useI18n();
930
+ return /* @__PURE__ */ jsx(ActionButton, {
931
+ variant: "subtle",
932
+ icon: IconLanguage,
933
+ menu: { items: i18n.languages.map((lang) => ({
934
+ label: i18n.tr(lang),
935
+ onClick: () => i18n.setLang(lang),
936
+ active: i18n.lang === lang
937
+ })) },
938
+ ...props
939
+ });
940
+ };
941
+
942
+ //#endregion
943
+ //#region ../../src/core/components/buttons/OmnibarButton.tsx
944
+ const OmnibarButton = (props) => {
945
+ const os = useOs();
946
+ const shortcut = os === "macos" || os === "ios" ? "⌘" : "Ctrl";
947
+ if (props.collapsed) return /* @__PURE__ */ jsx(ActionButton, {
948
+ variant: "subtle",
949
+ onClick: spotlight.open,
950
+ radius: "md",
951
+ icon: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
952
+ tooltip: {
953
+ label: "Search",
954
+ position: "right"
955
+ },
956
+ ...props.actionProps
957
+ });
958
+ return /* @__PURE__ */ jsx(ActionButton, {
959
+ variant: "default",
960
+ onClick: spotlight.open,
961
+ justify: "space-between",
962
+ rightSection: /* @__PURE__ */ jsxs(Kbd, {
963
+ visibleFrom: "sm",
964
+ size: "sm",
965
+ children: [/* @__PURE__ */ jsx(ClientOnly, { children: shortcut }), "+K"]
966
+ }),
967
+ radius: "md",
968
+ ...props.actionProps,
969
+ children: /* @__PURE__ */ jsxs(Flex, {
970
+ align: "center",
971
+ gap: "xs",
972
+ children: [/* @__PURE__ */ jsx(IconSearch, {
973
+ size: 16,
974
+ color: "gray"
975
+ }), /* @__PURE__ */ jsx(Flex, {
976
+ visibleFrom: "sm",
977
+ miw: 192,
978
+ children: /* @__PURE__ */ jsx(Text, {
979
+ size: "xs",
980
+ c: "dimmed",
981
+ children: "Search..."
982
+ })
983
+ })]
984
+ })
985
+ });
986
+ };
987
+
988
+ //#endregion
989
+ //#region ../../src/core/components/buttons/ToggleSidebarButton.tsx
990
+ const ToggleSidebarButton = (props) => {
991
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
992
+ return /* @__PURE__ */ jsx(ActionButton, {
993
+ icon: sidebar.collapsed ? IconLayoutSidebarRightCollapse : IconLayoutSidebarLeftCollapse,
994
+ visibleFrom: "md",
995
+ variant: "subtle",
996
+ size: "md",
997
+ onClick: () => {
998
+ setSidebar({
999
+ ...sidebar,
1000
+ collapsed: !sidebar.collapsed
1001
+ });
1002
+ },
1003
+ tooltip: {
1004
+ position: "right",
1005
+ label: sidebar.collapsed ? "Show sidebar" : "Hide sidebar"
1006
+ },
1007
+ ...props
1008
+ });
1009
+ };
1010
+
1011
+ //#endregion
1012
+ //#region ../../src/core/components/Flex.tsx
1013
+ const Flex$1 = forwardRef((props, ref) => {
1014
+ const { fill, center, centerX, centerY, col, ...rest } = props;
1015
+ if (fill) rest.flex ??= 1;
1016
+ if (col) rest.direction ??= "column";
1017
+ if (center) {
1018
+ rest.align ??= "center";
1019
+ rest.justify ??= "center";
1020
+ }
1021
+ if (centerX) rest.justify ??= "center";
1022
+ if (centerY) rest.align ??= "center";
1023
+ return /* @__PURE__ */ jsx(Flex, {
1024
+ ref,
1025
+ ...rest
1026
+ });
1027
+ });
1028
+ Flex$1.displayName = "Flex";
1029
+
1030
+ //#endregion
1031
+ //#region ../../src/core/components/Heading.tsx
1032
+ const Heading = (props) => {
1033
+ return /* @__PURE__ */ jsx("h1", { children: "Heading" });
1034
+ };
1035
+
1036
+ //#endregion
1037
+ //#region ../../src/core/components/layout/AppBar.tsx
1038
+ const AppBar = (props) => {
1039
+ const { items = [] } = props;
1040
+ const router = useRouter();
1041
+ const renderItem = (item, index) => {
1042
+ if (item.can && !item.can()) return null;
1043
+ if ("type" in item) {
1044
+ if (item.type === "burger") return /* @__PURE__ */ jsx(BurgerButton, {}, index);
1045
+ if (item.type === "dark") return /* @__PURE__ */ jsx(DarkModeButton, { ...item.props }, index);
1046
+ if (item.type === "search") return /* @__PURE__ */ jsx(OmnibarButton, { ...item.props }, index);
1047
+ if (item.type === "lang") return /* @__PURE__ */ jsx(LanguageButton, { ...item.props }, index);
1048
+ if (item.type === "spacer") return /* @__PURE__ */ jsx(Flex$1, { w: 16 }, index);
1049
+ if (item.type === "divider") return /* @__PURE__ */ jsx(Divider, { orientation: "vertical" }, index);
1050
+ if (item.type === "logo") return renderLogo(item, index);
1051
+ if (item.type === "back") return renderBack(item, index);
1052
+ }
1053
+ if ("element" in item) return item.element;
1054
+ return null;
1055
+ };
1056
+ const renderLogo = (item, index) => {
1057
+ const { src, text, icon, href, height = 32, width, fontWeight = 700, fontSize = "lg" } = item.props ?? {};
1058
+ const logoContent = src ? /* @__PURE__ */ jsx(Image, {
1059
+ src,
1060
+ h: height,
1061
+ w: width,
1062
+ fit: "contain"
1063
+ }) : icon ? typeof icon === "function" ? /* @__PURE__ */ jsx(icon, {}) : icon : text ? /* @__PURE__ */ jsx(Text$1, {
1064
+ fw: fontWeight,
1065
+ size: fontSize,
1066
+ children: text
1067
+ }) : null;
1068
+ if (href) return /* @__PURE__ */ jsx(Anchor, {
1069
+ component: Link,
1070
+ href,
1071
+ underline: "never",
1072
+ c: "inherit",
1073
+ children: logoContent
1074
+ }, index);
1075
+ return /* @__PURE__ */ jsx(Flex$1, { children: logoContent }, index);
1076
+ };
1077
+ const renderBack = (item, index) => {
1078
+ const { label = "Back", iconOnly = true, href, icon } = item.props ?? {};
1079
+ const renderIcon = () => {
1080
+ if (!icon) return /* @__PURE__ */ jsx(IconArrowLeft, { size: 18 });
1081
+ if (typeof icon === "function") return /* @__PURE__ */ jsx(icon, { size: 18 });
1082
+ return icon;
1083
+ };
1084
+ const iconElement = renderIcon();
1085
+ const handleClick = () => {
1086
+ if (href) router.push(href);
1087
+ else router.back();
1088
+ };
1089
+ if (iconOnly) return /* @__PURE__ */ jsx(ActionButton, {
1090
+ icon: iconElement,
1091
+ variant: "subtle",
1092
+ color: "gray",
1093
+ onClick: handleClick,
1094
+ tooltip: {
1095
+ label,
1096
+ position: "bottom"
1097
+ }
1098
+ }, index);
1099
+ return /* @__PURE__ */ jsx(ActionButton, {
1100
+ leftSection: iconElement,
1101
+ variant: "subtle",
1102
+ color: "gray",
1103
+ onClick: handleClick,
1104
+ children: label
1105
+ }, index);
1106
+ };
1107
+ const leftItems = items.filter((item) => item.position === "left");
1108
+ const centerItems = items.filter((item) => item.position === "center");
1109
+ const rightItems = items.filter((item) => item.position === "right");
1110
+ const content = /* @__PURE__ */ jsxs(Flex$1, {
1111
+ h: "100%",
1112
+ align: "center",
1113
+ px: props.container ? 0 : "md",
1114
+ justify: "space-between",
1115
+ ...props.flexProps,
1116
+ children: [
1117
+ /* @__PURE__ */ jsx(Flex$1, {
1118
+ flex: 1,
1119
+ children: leftItems.map((item, index) => /* @__PURE__ */ jsx(Flex$1, {
1120
+ ml: index === 0 ? 0 : "md",
1121
+ align: "center",
1122
+ children: renderItem(item, index)
1123
+ }, index))
1124
+ }),
1125
+ /* @__PURE__ */ jsx(Flex$1, { children: centerItems.map((item, index) => /* @__PURE__ */ jsx(Flex$1, {
1126
+ mx: "md",
1127
+ align: "center",
1128
+ children: renderItem(item, index)
1129
+ }, index)) }),
1130
+ /* @__PURE__ */ jsx(Flex$1, {
1131
+ flex: 1,
1132
+ align: "center",
1133
+ justify: "end",
1134
+ children: rightItems.map((item, index) => /* @__PURE__ */ jsx(Flex$1, {
1135
+ ml: index === 0 ? 0 : "md",
1136
+ align: "center",
1137
+ children: renderItem(item, index)
1138
+ }, index))
1139
+ })
1140
+ ]
1141
+ });
1142
+ if (props.container) return /* @__PURE__ */ jsx(Container, {
1143
+ h: "100%",
1144
+ ...typeof props.container === "boolean" ? {} : props.container,
1145
+ children: content
1146
+ });
1147
+ return content;
1148
+ };
1149
+
1150
+ //#endregion
1151
+ //#region ../../src/core/components/layout/Breadcrumb.tsx
1152
+ /**
1153
+ * Automatic breadcrumb component that reads the current route hierarchy
1154
+ * from the Alepha router's layer stack.
1155
+ *
1156
+ * Pages should define a `label` in their `$page()` options for best results.
1157
+ * Falls back to the page name converted to Title Case.
1158
+ */
1159
+ const Breadcrumb = ({ home = "Home", separator, size = "sm", ...groupProps }) => {
1160
+ const state = useRouterState();
1161
+ const router = useRouter();
1162
+ const crumbs = [];
1163
+ if (home !== false) crumbs.push({
1164
+ label: home,
1165
+ href: "/"
1166
+ });
1167
+ for (let i = 1; i < state.layers.length; i++) {
1168
+ const layer = state.layers[i];
1169
+ const route = layer.route;
1170
+ if (route?.path === "/" || route?.path === "") continue;
1171
+ const label = route?.label ?? toTitleCase(layer.name);
1172
+ const href = router.path(layer.name);
1173
+ crumbs.push({
1174
+ label,
1175
+ href
1176
+ });
1177
+ }
1178
+ if (crumbs.length === 0) return null;
1179
+ const sep = separator ?? /* @__PURE__ */ jsx(IconChevronRight, {
1180
+ size: 12,
1181
+ color: "#9ca3af"
1182
+ });
1183
+ return /* @__PURE__ */ jsx(Flex$1, {
1184
+ gap: "sm",
1185
+ ...groupProps,
1186
+ children: crumbs.map((crumb, i) => /* @__PURE__ */ jsxs(Flex$1, {
1187
+ align: "center",
1188
+ gap: "sm",
1189
+ children: [i > 0 && sep, i < crumbs.length - 1 ? /* @__PURE__ */ jsx(ActionButton, {
1190
+ anchor: true,
1191
+ href: crumb.href,
1192
+ size,
1193
+ c: "dimmed",
1194
+ children: crumb.label
1195
+ }) : /* @__PURE__ */ jsx(Text$1, {
1196
+ size,
1197
+ fw: 500,
1198
+ children: crumb.label
1199
+ })]
1200
+ }, crumb.href))
1201
+ });
1202
+ };
1203
+
1204
+ //#endregion
1205
+ //#region ../../src/core/components/layout/Container.tsx
1206
+ const Container$1 = forwardRef((props, ref) => {
1207
+ return /* @__PURE__ */ jsx(Container, {
1208
+ ref,
1209
+ ...props
1210
+ });
1211
+ });
1212
+ Container$1.displayName = "Container";
1213
+
1214
+ //#endregion
1215
+ //#region ../../src/core/components/layout/Sidebar.tsx
1216
+ const Sidebar = (props) => {
1217
+ const router = useRouter();
1218
+ const { onItemClick } = props;
1219
+ const divider = (key, fill, collapsed) => {
1220
+ return /* @__PURE__ */ jsx(Flex$1, {
1221
+ h: 1,
1222
+ bg: "var(--mantine-color-default-border)",
1223
+ my: "xs",
1224
+ mx: fill ? "calc(-1 * var(--mantine-spacing-md))" : collapsed ? 0 : "sm"
1225
+ }, key);
1226
+ };
1227
+ const renderNode = (item, key, collapsed) => {
1228
+ if ("type" in item) {
1229
+ if (item.type === "spacer") {
1230
+ if (collapsed) return null;
1231
+ return /* @__PURE__ */ jsx(Flex$1, { h: 16 }, key);
1232
+ }
1233
+ if (item.type === "divider") return divider(key, item.fill, collapsed);
1234
+ if (item.type === "search") return /* @__PURE__ */ jsx(Flex$1, {
1235
+ mb: "xs",
1236
+ children: /* @__PURE__ */ jsx(OmnibarButton, { collapsed })
1237
+ }, key);
1238
+ if (item.type === "toggle") return /* @__PURE__ */ jsx(ToggleSidebarButton, {}, key);
1239
+ if (item.type === "section") {
1240
+ if (item.children && item.children.length > 0) {
1241
+ if (!item.children.some((child) => !("can" in child) || !child.can || child.can())) return null;
1242
+ }
1243
+ if (collapsed) return /* @__PURE__ */ jsxs(Fragment$1, { children: [divider(`${key}-d`, void 0, collapsed), item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed))] }, key);
1244
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Flex$1, {
1245
+ mt: "md",
1246
+ align: "center",
1247
+ gap: "xs",
1248
+ children: [renderIcon(item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Text$1, {
1249
+ size: "xs",
1250
+ c: "dimmed",
1251
+ tt: "uppercase",
1252
+ fw: "bold",
1253
+ children: item.label
1254
+ })]
1255
+ }), item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed))] }, key);
1256
+ }
1257
+ }
1258
+ if ("element" in item) return /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, key);
1259
+ if (item.can && !item.can()) return null;
1260
+ if (item.children && item.children.length > 0) {
1261
+ if (!item.children.some((child) => !child.can || child.can())) return null;
1262
+ }
1263
+ if (collapsed) return /* @__PURE__ */ jsx(SidebarCollapsedItem, {
1264
+ item,
1265
+ level: 0,
1266
+ onItemClick,
1267
+ theme: props.theme ?? {}
1268
+ }, key);
1269
+ return /* @__PURE__ */ jsx(SidebarItem, {
1270
+ item,
1271
+ level: 0,
1272
+ onItemClick,
1273
+ theme: props.theme ?? {}
1274
+ }, key);
1275
+ };
1276
+ const getSidebarNodes = () => {
1277
+ if (props.items) return props.items;
1278
+ if (props.autoPopulateMenu) {
1279
+ const items = router.concretePages.filter((page) => !page.can || page.can()).map((page) => ({
1280
+ label: page.label ?? page.name,
1281
+ icon: renderIcon(page.icon),
1282
+ href: router.path(page.name)
1283
+ }));
1284
+ if (typeof props.autoPopulateMenu === "object" && props.autoPopulateMenu.startsWith) {
1285
+ const startsWith = props.autoPopulateMenu.startsWith;
1286
+ return items.filter((item) => item.href?.startsWith(startsWith));
1287
+ }
1288
+ return items;
1289
+ }
1290
+ return [];
1291
+ };
1292
+ const padding = "md";
1293
+ const gap = props.items ? props.gap ?? 4 : "xs";
1294
+ const menu = useMemo(() => getSidebarNodes(), [props.items, props.autoPopulateMenu]);
1295
+ const renderSidebar = (collapsed) => /* @__PURE__ */ jsxs(Flex$1, {
1296
+ flex: 1,
1297
+ py: padding,
1298
+ direction: "column",
1299
+ ...props.flexProps,
1300
+ children: [
1301
+ /* @__PURE__ */ jsx(Flex$1, {
1302
+ gap,
1303
+ px: padding,
1304
+ direction: "column",
1305
+ children: menu.filter((it) => it.position === "top").map((item, index) => renderNode(item, index, collapsed))
1306
+ }),
1307
+ /* @__PURE__ */ jsx(Flex$1, {
1308
+ gap,
1309
+ px: padding,
1310
+ direction: "column",
1311
+ flex: 1,
1312
+ children: menu.filter((it) => !it.position).map((item, index) => renderNode(item, index, collapsed))
1313
+ }),
1314
+ /* @__PURE__ */ jsx(Flex$1, {
1315
+ gap,
1316
+ px: padding,
1317
+ direction: "column",
1318
+ children: menu.filter((it) => it.position === "bottom").map((item, index) => renderNode(item, index, collapsed))
1319
+ })
1320
+ ]
1321
+ });
1322
+ if (props.collapsed) return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex$1, {
1323
+ flex: 1,
1324
+ direction: "column",
1325
+ visibleFrom: "md",
1326
+ children: renderSidebar(true)
1327
+ }), /* @__PURE__ */ jsx(Flex$1, {
1328
+ flex: 1,
1329
+ direction: "column",
1330
+ hiddenFrom: "md",
1331
+ children: renderSidebar(false)
1332
+ })] });
1333
+ return renderSidebar(false);
1334
+ };
1335
+ const SidebarItem = (props) => {
1336
+ const { item, level } = props;
1337
+ const maxLevel = 2;
1338
+ const router = useRouter();
1339
+ const isActive = useCallback((item) => {
1340
+ if (!item.children) return false;
1341
+ for (const child of item.children) {
1342
+ if (child.href) {
1343
+ if (router.isActive(child.href)) return true;
1344
+ }
1345
+ if (isActive(child)) return true;
1346
+ }
1347
+ return false;
1348
+ }, []);
1349
+ const [isOpen, setIsOpen] = useState(isActive(item));
1350
+ useEvents({ "react:transition:end": () => {
1351
+ if (isActive(item)) setIsOpen(true);
1352
+ } }, []);
1353
+ if (level > maxLevel) return null;
1354
+ const handleItemClick = (e) => {
1355
+ if (!props.item.target) e.preventDefault();
1356
+ if (item.children && item.children.length > 0) setIsOpen(!isOpen);
1357
+ else {
1358
+ props.onItemClick?.(item);
1359
+ item.onClick?.();
1360
+ }
1361
+ };
1362
+ return /* @__PURE__ */ jsxs(Flex$1, {
1363
+ direction: "column",
1364
+ ps: level === 0 ? 0 : 32,
1365
+ pos: "relative",
1366
+ children: [/* @__PURE__ */ jsx(ActionButton, {
1367
+ w: "100%",
1368
+ justify: "space-between",
1369
+ href: props.item.href,
1370
+ target: props.item.target,
1371
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
1372
+ bd: 0,
1373
+ fw: "normal",
1374
+ variant: "default",
1375
+ propsActive: {
1376
+ variant: "outline",
1377
+ fw: "bold"
1378
+ },
1379
+ radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
1380
+ onClick: handleItemClick,
1381
+ leftSection: /* @__PURE__ */ jsxs(Flex$1, {
1382
+ w: "100%",
1383
+ align: "center",
1384
+ gap: "sm",
1385
+ children: [renderIcon(item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Flex$1, {
1386
+ direction: "column",
1387
+ children: /* @__PURE__ */ jsx(Flex$1, { children: item.label })
1388
+ })]
1389
+ }),
1390
+ rightSection: item.children ? /* @__PURE__ */ jsx(Flex$1, { children: isOpen ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }) }) : props.item.rightSection,
1391
+ ...props.item.actionProps
1392
+ }), item.children && isOpen && /* @__PURE__ */ jsxs(Flex$1, {
1393
+ direction: "column",
1394
+ "data-parent-level": level,
1395
+ children: [/* @__PURE__ */ jsx(Flex$1, { style: {
1396
+ position: "absolute",
1397
+ width: 1,
1398
+ background: "linear-gradient(to bottom, transparent, var(--mantine-color-default-border), transparent)",
1399
+ top: 48,
1400
+ left: 20 + 32 * level,
1401
+ bottom: 16
1402
+ } }), item.children.filter((child) => !child.can || child.can()).map((child, index) => /* @__PURE__ */ jsx(SidebarItem, {
1403
+ item: child,
1404
+ level: level + 1,
1405
+ onItemClick: props.onItemClick,
1406
+ theme: props.theme
1407
+ }, index))]
1408
+ })]
1409
+ });
1410
+ };
1411
+ const SidebarCollapsedItem = (props) => {
1412
+ const { item, level } = props;
1413
+ const router = useRouter();
1414
+ const handleItemClick = () => {
1415
+ props.onItemClick?.(item);
1416
+ item.onClick?.();
1417
+ };
1418
+ const hasChildren = item.children && item.children.length > 0;
1419
+ const menu = hasChildren ? {
1420
+ on: "hover",
1421
+ position: "right",
1422
+ items: item.children.filter((child) => !child.can || child.can()).map((child) => ({
1423
+ label: child.label,
1424
+ icon: renderIcon(child.icon, ui.sizes.icon.sm),
1425
+ href: child.href,
1426
+ active: child.href ? router.isActive(child.href, { startWith: child.activeStartsWith }) : void 0
1427
+ }))
1428
+ } : void 0;
1429
+ return /* @__PURE__ */ jsx(ActionButton, {
1430
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
1431
+ variant: "subtle",
1432
+ variantActive: "default",
1433
+ tooltip: hasChildren ? void 0 : {
1434
+ label: item.label,
1435
+ position: "right"
1436
+ },
1437
+ radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
1438
+ onClick: hasChildren ? void 0 : handleItemClick,
1439
+ icon: renderIcon(item.icon, ui.sizes.icon.sm) ?? /* @__PURE__ */ jsx(IconSquareRounded, { size: ui.sizes.icon.sm }),
1440
+ href: hasChildren ? void 0 : props.item.href,
1441
+ target: hasChildren ? void 0 : props.item.target,
1442
+ menu,
1443
+ ...props.item.actionProps
1444
+ });
1445
+ };
1446
+
1447
+ //#endregion
1448
+ //#region ../../src/core/components/layout/DashboardShell.tsx
1449
+ const DashboardShell = (props) => {
1450
+ const router = useRouter();
1451
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
1452
+ const collapsed = props.sidebarProps?.collapsed !== void 0 ? props.sidebarProps.collapsed : sidebar.collapsed;
1453
+ const { collapsedWidth, expandedWidth } = sidebar;
1454
+ const shouldShowSidebar = () => {
1455
+ if (props.noSidebarWhen?.paths) {
1456
+ for (const path of props.noSidebarWhen.paths) if (router.isActive(path, { startWith: true })) return false;
1457
+ }
1458
+ return true;
1459
+ };
1460
+ const [showSidebar, setShowSidebar] = useState(shouldShowSidebar());
1461
+ useEvents({
1462
+ "react:transition:end": () => {
1463
+ setShowSidebar(shouldShowSidebar());
1464
+ },
1465
+ "react:transition:begin": () => {
1466
+ setSidebar({
1467
+ ...sidebar,
1468
+ closed: true
1469
+ });
1470
+ }
1471
+ }, [sidebar]);
1472
+ const defaultAppBarItems = [{
1473
+ position: "left",
1474
+ type: "burger"
1475
+ }];
1476
+ const appBarProps = { ...props.appBarProps };
1477
+ appBarProps.container ??= props.container;
1478
+ const hasSidebar = showSidebar && props.sidebarProps !== void 0;
1479
+ const hasAppBar = props.appBarProps || props.header;
1480
+ let footerElement = props.footer;
1481
+ if (props.footerHeight) footerElement ??= /* @__PURE__ */ jsx(Flex$1, { h: props.footerHeight });
1482
+ const hHeight = props.headerHeight ?? 60;
1483
+ const fHeight = props.footerHeight ?? 24;
1484
+ const headerHeight = hasAppBar ? hHeight : 0;
1485
+ const footerHeight = footerElement ? fHeight : 0;
1486
+ return /* @__PURE__ */ jsxs(AppShell, {
1487
+ layout: "alt",
1488
+ w: "100%",
1489
+ flex: 1,
1490
+ header: hasAppBar ? { height: hHeight } : void 0,
1491
+ navbar: hasSidebar ? {
1492
+ width: { base: collapsed ? collapsedWidth : expandedWidth },
1493
+ breakpoint: "md",
1494
+ collapsed: { mobile: sidebar.closed }
1495
+ } : void 0,
1496
+ footer: footerElement ? { height: fHeight } : void 0,
1497
+ ...props.appShellProps,
1498
+ children: [
1499
+ hasAppBar && /* @__PURE__ */ jsx(AppShell.Header, {
1500
+ ...props.appShellHeaderProps,
1501
+ children: props.header ?? /* @__PURE__ */ jsx(AppBar, {
1502
+ items: defaultAppBarItems,
1503
+ ...appBarProps
1504
+ })
1505
+ }),
1506
+ hasSidebar && /* @__PURE__ */ jsxs(AppShell.Navbar, {
1507
+ className: "alepha-sidebar-navbar",
1508
+ ...props.appShellNavbarProps,
1509
+ children: [
1510
+ props.navbarHeader ? /* @__PURE__ */ jsx(Flex$1, {
1511
+ style: { borderBottom: "1px solid var(--mantine-color-default-border)" },
1512
+ h: headerHeight,
1513
+ children: props.navbarHeader
1514
+ }) : null,
1515
+ /* @__PURE__ */ jsx(Sidebar, {
1516
+ ...props.sidebarProps ?? {},
1517
+ collapsed
1518
+ }),
1519
+ props.navbarFooter ? /* @__PURE__ */ jsx(Flex$1, {
1520
+ style: { borderTop: "1px solid var(--mantine-color-default-border)" },
1521
+ h: footerHeight,
1522
+ children: props.navbarFooter
1523
+ }) : null
1524
+ ]
1525
+ }),
1526
+ /* @__PURE__ */ jsx(AppShell.Main, {
1527
+ pos: "relative",
1528
+ ...props.appShellMainProps,
1529
+ children: props.children ?? /* @__PURE__ */ jsx(NestedView, {})
1530
+ }),
1531
+ footerElement && /* @__PURE__ */ jsx(AppShell.Footer, {
1532
+ ...props.appShellFooterProps,
1533
+ children: footerElement
1534
+ })
1535
+ ]
1536
+ });
1537
+ };
1538
+
1539
+ //#endregion
1540
+ //#region ../../src/core/components/Text.tsx
1541
+ const INTENT_COLORS = {
1542
+ primary: "blue",
1543
+ info: "cyan",
1544
+ success: "green",
1545
+ warning: "yellow",
1546
+ danger: "red"
1547
+ };
1548
+ const Text$1 = forwardRef((props, ref) => {
1549
+ const { intent, bold, italic, light, muted, small, uppercase, capitalize, center, monospace, title, ...rest } = props;
1550
+ if (intent) rest.c ??= INTENT_COLORS[intent];
1551
+ if (bold) rest.fw ??= 700;
1552
+ if (light) rest.fw ??= 300;
1553
+ if (italic) rest.fs ??= "italic";
1554
+ if (muted) rest.c ??= "dimmed";
1555
+ if (small) rest.size ??= "sm";
1556
+ if (uppercase) rest.tt ??= "uppercase";
1557
+ if (capitalize) rest.tt ??= "capitalize";
1558
+ if (center) rest.ta ??= "center";
1559
+ if (monospace) rest.ff ??= "monospace";
1560
+ if (title) rest.size ??= "xl";
1561
+ return /* @__PURE__ */ jsx(Text, {
1562
+ ref,
1563
+ ...rest
1564
+ });
1565
+ });
1566
+ Text$1.displayName = "Text";
1567
+
1568
+ //#endregion
1569
+ //#region ../../src/core/form/utils/parseInput.ts
1570
+ const parseInput = (props, form) => {
1571
+ const disabled = false;
1572
+ const id = props.input.props.id;
1573
+ const label = props.title ?? ("title" in props.input.schema && typeof props.input.schema.title === "string" ? props.input.schema.title : void 0) ?? prettyName(props.input.path);
1574
+ const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
1575
+ const error = form.error && form.error instanceof TypeBoxError ? form.error.value.message : void 0;
1576
+ const icon = !props.icon ? getDefaultIcon({
1577
+ type: props.input.schema && "type" in props.input.schema ? String(props.input.schema.type) : void 0,
1578
+ format: props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0,
1579
+ name: props.input.props.name,
1580
+ isEnum: props.input.schema && "enum" in props.input.schema && Boolean(props.input.schema.enum),
1581
+ isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array"
1582
+ }) : isValidElement(props.icon) ? props.icon : createElement(props.icon, { size: ui.sizes.icon.md });
1583
+ const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
1584
+ const required = props.input.required;
1585
+ const schema = props.input.schema;
1586
+ const inputProps = {
1587
+ label,
1588
+ description,
1589
+ error,
1590
+ required,
1591
+ disabled
1592
+ };
1593
+ if ("minLength" in schema && typeof schema.minLength === "number") inputProps.minLength = schema.minLength;
1594
+ if ("maxLength" in schema && typeof schema.maxLength === "number") inputProps.maxLength = schema.maxLength;
1595
+ if ("minimum" in schema && typeof schema.minimum === "number") inputProps.minimum = schema.minimum;
1596
+ if ("maximum" in schema && typeof schema.maximum === "number") inputProps.maximum = schema.maximum;
1597
+ return {
1598
+ id,
1599
+ icon,
1600
+ format,
1601
+ schema: props.input.schema,
1602
+ inputProps
1603
+ };
1604
+ };
1605
+
1606
+ //#endregion
1607
+ //#region ../../src/core/form/components/ControlArray.tsx
1608
+ /**
1609
+ * Custom hook to sync array items with form state.
1610
+ * Uses form events as the source of truth, eliminating dual-state issues.
1611
+ */
1612
+ const useArrayItems = (input) => {
1613
+ const alepha = useAlepha();
1614
+ const keyCounter = useRef(0);
1615
+ const [items, setItemsState] = useState(() => {
1616
+ const defaultValue = input?.props?.defaultValue;
1617
+ if (Array.isArray(defaultValue)) return defaultValue.map((value) => ({
1618
+ key: keyCounter.current++,
1619
+ value
1620
+ }));
1621
+ return [];
1622
+ });
1623
+ const syncFromFormValue = useCallback((formValue) => {
1624
+ if (!Array.isArray(formValue)) {
1625
+ setItemsState([]);
1626
+ return;
1627
+ }
1628
+ setItemsState((prevItems) => {
1629
+ if (prevItems.length === formValue.length) {
1630
+ if (prevItems.every((item, i) => item.value === formValue[i])) return prevItems;
1631
+ }
1632
+ keyCounter.current = 0;
1633
+ return formValue.map((value) => ({
1634
+ key: keyCounter.current++,
1635
+ value
1636
+ }));
1637
+ });
1638
+ }, []);
1639
+ useEffect(() => {
1640
+ if (!input?.form) return;
1641
+ const formId = input.form.id;
1642
+ const fieldPath = input.path;
1643
+ const listeners = [alepha.events.on("form:reset", (event) => {
1644
+ if (event.id === formId) {
1645
+ const defaultValue = input.props?.defaultValue;
1646
+ keyCounter.current = 0;
1647
+ if (Array.isArray(defaultValue)) setItemsState(defaultValue.map((value) => ({
1648
+ key: keyCounter.current++,
1649
+ value
1650
+ })));
1651
+ else setItemsState([]);
1652
+ }
1653
+ }), alepha.events.on("form:change", (event) => {
1654
+ if (event.id === formId && event.path === fieldPath) syncFromFormValue(event.value);
1655
+ })];
1656
+ return () => {
1657
+ for (const unsub of listeners) unsub();
1658
+ };
1659
+ }, [
1660
+ alepha,
1661
+ input,
1662
+ syncFromFormValue
1663
+ ]);
1664
+ return {
1665
+ items,
1666
+ setItems: useCallback((newItems) => {
1667
+ setItemsState(newItems);
1668
+ input?.set(newItems.map((item) => item.value));
1669
+ }, [input]),
1670
+ nextKey: useCallback(() => keyCounter.current++, [])
1671
+ };
1672
+ };
1673
+ /**
1674
+ * Creates a proper InputField for an array item that integrates with the form system.
1675
+ * Uses array index for test IDs to ensure predictable, testable element identifiers.
1676
+ */
1677
+ const createArrayItemInput = (parentInput, itemSchema, index, _itemKey, value, onValueChange) => {
1678
+ return {
1679
+ schema: itemSchema,
1680
+ path: `${parentInput.path}/${index}`,
1681
+ required: false,
1682
+ form: parentInput.form,
1683
+ props: {
1684
+ id: `${parentInput.props.id}-${index}`,
1685
+ name: `${parentInput.props.name}[${index}]`,
1686
+ defaultValue: value
1687
+ },
1688
+ set: onValueChange
1689
+ };
1690
+ };
1691
+ /**
1692
+ * Creates a proper InputField for a nested object field within an array item.
1693
+ * Uses array index for test IDs to ensure predictable, testable element identifiers.
1694
+ */
1695
+ const createArrayItemFieldInput = (parentInput, itemSchema, fieldName, index, _itemKey, itemValue, onFieldChange) => {
1696
+ return {
1697
+ schema: itemSchema.properties[fieldName],
1698
+ path: `${parentInput.path}/${index}/${fieldName}`,
1699
+ required: itemSchema.required?.includes(fieldName) ?? false,
1700
+ form: parentInput.form,
1701
+ props: {
1702
+ id: `${parentInput.props.id}-${index}-${fieldName}`,
1703
+ name: `${parentInput.props.name}[${index}].${fieldName}`,
1704
+ defaultValue: itemValue?.[fieldName]
1705
+ },
1706
+ set: (value) => onFieldChange(fieldName, value)
1707
+ };
1708
+ };
1709
+ /**
1710
+ * ControlArray component for editing arrays of schema items.
1711
+ *
1712
+ * Features:
1713
+ * - Dynamic add/remove of items
1714
+ * - Supports arrays of objects with nested fields
1715
+ * - Supports arrays of primitives
1716
+ * - Grid layout for object items
1717
+ * - Min/max constraints
1718
+ * - Syncs with form state (handles external updates and resets)
1719
+ *
1720
+ * @example
1721
+ * ```tsx
1722
+ * // For a schema like:
1723
+ * // t.object({
1724
+ * // contacts: t.array(t.object({
1725
+ * // name: t.text(),
1726
+ * // email: t.text({ format: "email" }),
1727
+ * // }))
1728
+ * // })
1729
+ *
1730
+ * <ControlArray
1731
+ * input={form.input.contacts}
1732
+ * columns={2}
1733
+ * addLabel="Add contact"
1734
+ * controlProps={{
1735
+ * email: { text: { placeholder: "email@example.com" } }
1736
+ * }}
1737
+ * />
1738
+ * ```
1739
+ */
1740
+ const ControlArray = (props) => {
1741
+ const { inputProps } = parseInput(props, {});
1742
+ const { items, setItems, nextKey } = useArrayItems(props.input);
1743
+ if (!props.input?.props) return null;
1744
+ const schema = props.input.schema;
1745
+ if (!schema || !("items" in schema)) return null;
1746
+ const itemSchema = schema.items;
1747
+ const isObjectItem = itemSchema && "properties" in itemSchema;
1748
+ const { min = 0, max = Number.POSITIVE_INFINITY, columns = 1 } = props;
1749
+ const handleAdd = () => {
1750
+ if (items.length >= max) return;
1751
+ let newValue;
1752
+ if (isObjectItem) {
1753
+ newValue = {};
1754
+ const objSchema = itemSchema;
1755
+ for (const [key, propSchema] of Object.entries(objSchema.properties)) if ("default" in propSchema) newValue[key] = propSchema.default;
1756
+ } else newValue = "";
1757
+ setItems([...items, {
1758
+ key: nextKey(),
1759
+ value: newValue
1760
+ }]);
1761
+ };
1762
+ const handleRemove = (index) => {
1763
+ if (items.length <= min) return;
1764
+ setItems(items.filter((_, i) => i !== index));
1765
+ };
1766
+ const handleItemChange = (index, value) => {
1767
+ const newItems = [...items];
1768
+ newItems[index] = {
1769
+ ...newItems[index],
1770
+ value
1771
+ };
1772
+ setItems(newItems);
1773
+ };
1774
+ const handleFieldChange = (index, field, value) => {
1775
+ const newItems = [...items];
1776
+ newItems[index] = {
1777
+ ...newItems[index],
1778
+ value: {
1779
+ ...newItems[index].value,
1780
+ [field]: value
1781
+ }
1782
+ };
1783
+ setItems(newItems);
1784
+ };
1785
+ const colSpan = 12 / columns;
1786
+ const objectItemSchema = isObjectItem ? itemSchema : null;
1787
+ const fieldNames = objectItemSchema ? Object.keys(objectItemSchema.properties) : [];
1788
+ const renderItems = () => /* @__PURE__ */ jsxs(Flex, {
1789
+ direction: "column",
1790
+ gap: "sm",
1791
+ children: [items.map((item, index) => /* @__PURE__ */ jsxs(Flex, {
1792
+ gap: "sm",
1793
+ align: "flex-start",
1794
+ p: "xs",
1795
+ bg: ui.colors.surface,
1796
+ style: { borderRadius: "var(--mantine-radius-sm)" },
1797
+ children: [
1798
+ props.sortable && /* @__PURE__ */ jsx(ActionIcon, {
1799
+ variant: "subtle",
1800
+ color: "gray",
1801
+ style: { cursor: "grab" },
1802
+ children: /* @__PURE__ */ jsx(IconGripVertical, { size: 16 })
1803
+ }),
1804
+ objectItemSchema ? /* @__PURE__ */ jsx(Grid, {
1805
+ style: { flex: 1 },
1806
+ gutter: "sm",
1807
+ children: fieldNames.map((fieldName) => {
1808
+ const fieldControlProps = props.controlProps?.[fieldName] ?? {};
1809
+ const fieldInput = createArrayItemFieldInput(props.input, objectItemSchema, fieldName, index, item.key, item.value, (field, value) => handleFieldChange(index, field, value));
1810
+ return /* @__PURE__ */ jsx(Grid.Col, {
1811
+ span: colSpan,
1812
+ children: /* @__PURE__ */ jsx(Control, {
1813
+ input: fieldInput,
1814
+ ...fieldControlProps
1815
+ })
1816
+ }, fieldName);
1817
+ })
1818
+ }) : /* @__PURE__ */ jsx(Flex, {
1819
+ style: { flex: 1 },
1820
+ children: /* @__PURE__ */ jsx(Control, {
1821
+ input: createArrayItemInput(props.input, itemSchema, index, item.key, item.value, (value) => handleItemChange(index, value)),
1822
+ ...props.itemControlProps
1823
+ })
1824
+ }),
1825
+ /* @__PURE__ */ jsx(ActionIcon, {
1826
+ variant: "subtle",
1827
+ color: "red",
1828
+ onClick: () => handleRemove(index),
1829
+ disabled: items.length <= min,
1830
+ children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
1831
+ })
1832
+ ]
1833
+ }, item.key)), /* @__PURE__ */ jsxs(UnstyledButton, {
1834
+ onClick: handleAdd,
1835
+ disabled: items.length >= max,
1836
+ style: {
1837
+ display: "flex",
1838
+ alignItems: "center",
1839
+ justifyContent: "center",
1840
+ gap: 6,
1841
+ padding: "8px 12px",
1842
+ borderRadius: "var(--mantine-radius-sm)",
1843
+ border: "1px dashed var(--mantine-color-dimmed)",
1844
+ color: "var(--mantine-color-dimmed)",
1845
+ fontSize: "var(--mantine-font-size-sm)",
1846
+ cursor: items.length >= max ? "not-allowed" : "pointer",
1847
+ opacity: items.length >= max ? .5 : 1,
1848
+ transition: "all 150ms ease"
1849
+ },
1850
+ onMouseEnter: (e) => {
1851
+ if (items.length < max) {
1852
+ e.currentTarget.style.borderColor = "var(--mantine-color-blue-filled)";
1853
+ e.currentTarget.style.color = "var(--mantine-color-blue-filled)";
1854
+ e.currentTarget.style.background = "var(--mantine-color-blue-light)";
1855
+ }
1856
+ },
1857
+ onMouseLeave: (e) => {
1858
+ e.currentTarget.style.borderColor = "var(--mantine-color-dimmed)";
1859
+ e.currentTarget.style.color = "var(--mantine-color-dimmed)";
1860
+ e.currentTarget.style.background = "transparent";
1861
+ },
1862
+ children: [/* @__PURE__ */ jsx(IconPlus, { size: 14 }), props.addLabel ?? "Add"]
1863
+ })]
1864
+ });
1865
+ if (props.variant === "plain") return /* @__PURE__ */ jsxs(Flex, {
1866
+ direction: "column",
1867
+ gap: "xs",
1868
+ children: [
1869
+ inputProps.label && /* @__PURE__ */ jsx(Text, {
1870
+ size: "sm",
1871
+ fw: 500,
1872
+ children: inputProps.label
1873
+ }),
1874
+ inputProps.description && /* @__PURE__ */ jsx(Text, {
1875
+ size: "sm",
1876
+ c: "dimmed",
1877
+ children: inputProps.description
1878
+ }),
1879
+ renderItems(),
1880
+ inputProps.error && /* @__PURE__ */ jsx(Text, {
1881
+ size: "sm",
1882
+ c: "red",
1883
+ children: inputProps.error
1884
+ })
1885
+ ]
1886
+ });
1887
+ return /* @__PURE__ */ jsx(Fieldset, {
1888
+ legend: inputProps.label,
1889
+ children: /* @__PURE__ */ jsxs(Flex, {
1890
+ direction: "column",
1891
+ gap: "xs",
1892
+ children: [
1893
+ inputProps.description && /* @__PURE__ */ jsx(Text, {
1894
+ size: "sm",
1895
+ c: "dimmed",
1896
+ children: inputProps.description
1897
+ }),
1898
+ renderItems(),
1899
+ inputProps.error && /* @__PURE__ */ jsx(Text, {
1900
+ size: "sm",
1901
+ c: "red",
1902
+ children: inputProps.error
1903
+ })
1904
+ ]
1905
+ })
1906
+ });
1907
+ };
1908
+
1909
+ //#endregion
1910
+ //#region ../../src/core/form/components/ControlDate.tsx
1911
+ /**
1912
+ * ControlDate component for handling date, datetime, and time inputs.
1913
+ *
1914
+ * Features:
1915
+ * - DateInput for date format
1916
+ * - DateTimePicker for date-time format
1917
+ * - TimeInput for time format
1918
+ *
1919
+ * Automatically detects date formats from schema and renders appropriate picker.
1920
+ */
1921
+ const ControlDate = (props) => {
1922
+ const { inputProps, id, icon, format } = parseInput(props, useFormState(props.input));
1923
+ if (!props.input?.props) return null;
1924
+ if (props.datetime || format === "date-time") {
1925
+ const dateTimePickerProps = typeof props.datetime === "object" ? props.datetime : {};
1926
+ return /* @__PURE__ */ jsx(DateTimePicker, {
1927
+ ...inputProps,
1928
+ id,
1929
+ leftSection: icon,
1930
+ defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
1931
+ onChange: (value) => {
1932
+ props.input.set(value ? new Date(value).toISOString() : void 0);
1933
+ },
1934
+ ...dateTimePickerProps
1935
+ });
1936
+ }
1937
+ if (props.date || format === "date") {
1938
+ const dateInputProps = typeof props.date === "object" ? props.date : {};
1939
+ return /* @__PURE__ */ jsx(DateInput, {
1940
+ ...inputProps,
1941
+ id,
1942
+ leftSection: icon,
1943
+ defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
1944
+ onChange: (value) => {
1945
+ props.input.set(value ? new Date(value).toISOString().slice(0, 10) : void 0);
1946
+ },
1947
+ ...dateInputProps
1948
+ });
1949
+ }
1950
+ if (props.time || format === "time") {
1951
+ const timeInputProps = typeof props.time === "object" ? props.time : {};
1952
+ return /* @__PURE__ */ jsx(TimeInput, {
1953
+ ...inputProps,
1954
+ id,
1955
+ leftSection: icon,
1956
+ defaultValue: props.input.props.defaultValue,
1957
+ onChange: (event) => {
1958
+ props.input.set(event.currentTarget.value);
1959
+ },
1960
+ ...timeInputProps
1961
+ });
1962
+ }
1963
+ return null;
1964
+ };
1965
+
1966
+ //#endregion
1967
+ //#region ../../src/core/form/components/ControlNumber.tsx
1968
+ /**
1969
+ *
1970
+ */
1971
+ const ControlNumber = (props) => {
1972
+ const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
1973
+ const ref = useRef(null);
1974
+ const [value, setValue] = useState(props.input.props.defaultValue);
1975
+ useEvents({ "form:reset": (event) => {
1976
+ if (event.id === props.input?.form.id && ref.current) setValue(props.input.props.defaultValue);
1977
+ } }, [props.input]);
1978
+ if (!props.input?.props) return null;
1979
+ const { type, ...inputPropsWithoutType } = props.input.props;
1980
+ if (props.sliderProps) {
1981
+ const min = props.sliderProps.min ?? inputProps.minimum ?? 0;
1982
+ const max = props.sliderProps.max ?? inputProps.maximum ?? 100;
1983
+ return /* @__PURE__ */ jsx(Input.Wrapper, {
1984
+ ...inputProps,
1985
+ children: /* @__PURE__ */ jsx("div", {
1986
+ style: {
1987
+ height: 32,
1988
+ padding: 8
1989
+ },
1990
+ children: /* @__PURE__ */ jsx(Slider, {
1991
+ ...inputProps,
1992
+ ref,
1993
+ id,
1994
+ ...inputPropsWithoutType,
1995
+ ...props.sliderProps,
1996
+ value,
1997
+ min,
1998
+ max,
1999
+ label: () => value,
2000
+ onChange: (val) => {
2001
+ setValue(val);
2002
+ props.input.set(val);
2003
+ }
2004
+ })
2005
+ })
2006
+ });
2007
+ }
2008
+ return /* @__PURE__ */ jsx(NumberInput, {
2009
+ ...inputProps,
2010
+ ref,
2011
+ id,
2012
+ leftSection: icon,
2013
+ ...inputPropsWithoutType,
2014
+ ...props.numberInputProps,
2015
+ value: value ?? "",
2016
+ onChange: (val) => {
2017
+ const newValue = val !== null ? Number(val) : void 0;
2018
+ setValue(newValue);
2019
+ props.input.set(newValue);
2020
+ }
2021
+ });
2022
+ };
2023
+
2024
+ //#endregion
2025
+ //#region ../../src/core/form/components/ControlObject.tsx
2026
+ /**
2027
+ * ControlObject component for editing nested object schemas.
2028
+ *
2029
+ * Features:
2030
+ * - Renders all properties of an object schema
2031
+ * - Supports grid layout with configurable columns
2032
+ * - Per-field customization via controlProps
2033
+ * - Recursive support for deeply nested objects
2034
+ *
2035
+ * The form system provides nested InputFields under the `.items` property.
2036
+ * For example: form.input.address.items.street
2037
+ *
2038
+ * @example
2039
+ * ```tsx
2040
+ * // For a schema like:
2041
+ * // t.object({
2042
+ * // address: t.object({
2043
+ * // street: t.text(),
2044
+ * // city: t.text(),
2045
+ * // zip: t.text(),
2046
+ * // })
2047
+ * // })
2048
+ *
2049
+ * <ControlObject
2050
+ * input={form.input.address}
2051
+ * columns={2}
2052
+ * controlProps={{
2053
+ * zip: { text: { maxLength: 10 } }
2054
+ * }}
2055
+ * />
2056
+ * ```
2057
+ */
2058
+ const ControlObject = (props) => {
2059
+ const { inputProps } = parseInput(props, {});
2060
+ if (!props.input?.props) return null;
2061
+ const schema = props.input.schema;
2062
+ if (!schema?.properties) return null;
2063
+ const fieldNames = Object.keys(schema.properties);
2064
+ const colSpan = 12 / (props.columns ?? 1);
2065
+ const nestedItems = props.input.items;
2066
+ const renderFields = () => /* @__PURE__ */ jsx(Grid, { children: fieldNames.map((fieldName) => {
2067
+ const fieldControlProps = props.controlProps?.[fieldName] ?? {};
2068
+ const field = nestedItems?.[fieldName];
2069
+ if (!field) return null;
2070
+ return /* @__PURE__ */ jsx(Grid.Col, {
2071
+ span: colSpan,
2072
+ children: /* @__PURE__ */ jsx(Control, {
2073
+ input: field,
2074
+ ...fieldControlProps
2075
+ })
2076
+ }, fieldName);
2077
+ }) });
2078
+ if (props.variant === "plain") return renderFields();
2079
+ return /* @__PURE__ */ jsx(Fieldset, {
2080
+ legend: inputProps.label,
2081
+ children: /* @__PURE__ */ jsxs(Flex, {
2082
+ direction: "column",
2083
+ gap: "xs",
2084
+ children: [
2085
+ inputProps.description && /* @__PURE__ */ jsx(Text, {
2086
+ size: "sm",
2087
+ c: "dimmed",
2088
+ children: inputProps.description
2089
+ }),
2090
+ renderFields(),
2091
+ inputProps.error && /* @__PURE__ */ jsx(Text, {
2092
+ size: "sm",
2093
+ c: "red",
2094
+ children: inputProps.error
2095
+ })
2096
+ ]
2097
+ })
2098
+ });
2099
+ };
2100
+
2101
+ //#endregion
2102
+ //#region ../../src/core/form/components/ControlQueryBuilder.tsx
2103
+ /**
2104
+ * Query builder with text input and help popover.
2105
+ * Generates query strings for parseQueryString syntax.
2106
+ */
2107
+ const ControlQueryBuilder = ({ schema, value = "", onChange, placeholder = "Enter query or click for assistance...", ...textInputProps }) => {
2108
+ const [helpOpened, setHelpOpened] = useState(false);
2109
+ const [textValue, setTextValue] = useState(value);
2110
+ const inputRef = useRef(null);
2111
+ const fields = schema ? extractSchemaFields(schema) : [];
2112
+ const [error, setError] = useState(null);
2113
+ const isValid = (value) => {
2114
+ try {
2115
+ parseQueryString(value.trim());
2116
+ } catch (e) {
2117
+ setError(e.message);
2118
+ return false;
2119
+ }
2120
+ setError(null);
2121
+ return true;
2122
+ };
2123
+ const handleTextChange = (newValue) => {
2124
+ setTextValue(newValue);
2125
+ if (isValid(newValue)) onChange?.(newValue);
2126
+ };
2127
+ const handleClear = () => {
2128
+ setTextValue("");
2129
+ onChange?.("");
2130
+ isValid("");
2131
+ };
2132
+ const handleInsert = (text) => {
2133
+ const newValue = textValue ? `${textValue}${text} ` : `${text} `;
2134
+ setTextValue(newValue);
2135
+ if (isValid(newValue)) onChange?.(newValue);
2136
+ setTimeout(() => {
2137
+ inputRef.current?.focus();
2138
+ const length = inputRef.current?.value.length || 0;
2139
+ inputRef.current?.setSelectionRange(length, length);
2140
+ }, 0);
2141
+ };
2142
+ useEvents({ "form:change": (event) => {
2143
+ if (event.id === inputRef.current?.form?.id) {
2144
+ if (event.path === textInputProps["data-path"]) setTextValue(event.value ?? "");
2145
+ }
2146
+ } }, []);
2147
+ return /* @__PURE__ */ jsxs(Popover, {
2148
+ width: 800,
2149
+ position: "bottom-start",
2150
+ shadow: "md",
2151
+ opened: helpOpened,
2152
+ onChange: setHelpOpened,
2153
+ closeOnClickOutside: true,
2154
+ closeOnEscape: true,
2155
+ transitionProps: {
2156
+ transition: "fade-up",
2157
+ duration: 200,
2158
+ timingFunction: "ease"
2159
+ },
2160
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(TextInput, {
2161
+ ref: inputRef,
2162
+ placeholder,
2163
+ value: textValue,
2164
+ onChange: (e) => handleTextChange(e.currentTarget.value),
2165
+ onFocus: () => setHelpOpened(true),
2166
+ leftSection: error ? /* @__PURE__ */ jsx(IconInfoTriangle, { size: 16 }) : /* @__PURE__ */ jsx(IconFilter, { size: 16 }),
2167
+ rightSection: textValue && /* @__PURE__ */ jsx(ActionIcon, {
2168
+ size: "sm",
2169
+ variant: "subtle",
2170
+ color: "gray",
2171
+ onClick: handleClear,
2172
+ children: /* @__PURE__ */ jsx(IconX, { size: 14 })
2173
+ }),
2174
+ ...textInputProps
2175
+ }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
2176
+ bg: "transparent",
2177
+ p: "xs",
2178
+ bd: `1px solid ${ui.colors.border}`,
2179
+ style: { backdropFilter: "blur(20px)" },
2180
+ children: /* @__PURE__ */ jsx(QueryHelp, {
2181
+ fields,
2182
+ onInsert: handleInsert
2183
+ })
2184
+ })]
2185
+ });
2186
+ };
2187
+ function QueryHelp({ fields, onInsert }) {
2188
+ return /* @__PURE__ */ jsxs(Flex, {
2189
+ gap: "md",
2190
+ align: "flex-start",
2191
+ wrap: "nowrap",
2192
+ bg: ui.colors.surface,
2193
+ p: "sm",
2194
+ bdrs: "sm",
2195
+ children: [
2196
+ /* @__PURE__ */ jsxs(Flex, {
2197
+ direction: "column",
2198
+ gap: "md",
2199
+ style: { flex: 1 },
2200
+ children: [
2201
+ /* @__PURE__ */ jsxs(Flex, {
2202
+ direction: "column",
2203
+ gap: "xs",
2204
+ children: [/* @__PURE__ */ jsx(Text, {
2205
+ size: "sm",
2206
+ fw: 600,
2207
+ children: "Operators"
2208
+ }), /* @__PURE__ */ jsx(Flex, {
2209
+ direction: "column",
2210
+ gap: 4,
2211
+ children: Object.entries(OPERATOR_INFO).map(([key, info]) => /* @__PURE__ */ jsxs(Flex, {
2212
+ gap: "xs",
2213
+ wrap: "nowrap",
2214
+ children: [/* @__PURE__ */ jsx(ActionButton, {
2215
+ px: "xs",
2216
+ size: "xs",
2217
+ h: 24,
2218
+ variant: "default",
2219
+ justify: "center",
2220
+ miw: 48,
2221
+ onClick: () => onInsert(info.symbol),
2222
+ children: info.symbol
2223
+ }), /* @__PURE__ */ jsx(Text, {
2224
+ size: "xs",
2225
+ c: "dimmed",
2226
+ style: { flex: 1 },
2227
+ children: info.label
2228
+ })]
2229
+ }, key))
2230
+ })]
2231
+ }),
2232
+ /* @__PURE__ */ jsx(Divider, {}),
2233
+ /* @__PURE__ */ jsxs(Flex, {
2234
+ direction: "column",
2235
+ gap: "xs",
2236
+ children: [/* @__PURE__ */ jsx(Text, {
2237
+ size: "sm",
2238
+ fw: 600,
2239
+ children: "Logic"
2240
+ }), /* @__PURE__ */ jsxs(Flex, {
2241
+ direction: "column",
2242
+ gap: 4,
2243
+ children: [/* @__PURE__ */ jsxs(Flex, {
2244
+ gap: "xs",
2245
+ wrap: "nowrap",
2246
+ children: [/* @__PURE__ */ jsx(ActionButton, {
2247
+ px: "xs",
2248
+ size: "xs",
2249
+ h: 24,
2250
+ variant: "default",
2251
+ justify: "center",
2252
+ miw: 48,
2253
+ onClick: () => onInsert("&"),
2254
+ children: "&"
2255
+ }), /* @__PURE__ */ jsx(Text, {
2256
+ size: "xs",
2257
+ c: "dimmed",
2258
+ children: "AND"
2259
+ })]
2260
+ }), /* @__PURE__ */ jsxs(Flex, {
2261
+ gap: "xs",
2262
+ wrap: "nowrap",
2263
+ children: [/* @__PURE__ */ jsx(ActionButton, {
2264
+ px: "xs",
2265
+ size: "xs",
2266
+ h: 24,
2267
+ variant: "default",
2268
+ justify: "center",
2269
+ miw: 48,
2270
+ onClick: () => onInsert("|"),
2271
+ children: "|"
2272
+ }), /* @__PURE__ */ jsx(Text, {
2273
+ size: "xs",
2274
+ c: "dimmed",
2275
+ children: "OR"
2276
+ })]
2277
+ })]
2278
+ })]
2279
+ })
2280
+ ]
2281
+ }),
2282
+ fields.length > 0 && /* @__PURE__ */ jsx(Divider, { orientation: "vertical" }),
2283
+ fields.length > 0 && /* @__PURE__ */ jsxs(Flex, {
2284
+ direction: "column",
2285
+ gap: "xs",
2286
+ style: { flex: 2 },
2287
+ children: [/* @__PURE__ */ jsx(Text, {
2288
+ size: "sm",
2289
+ fw: 600,
2290
+ children: "Fields"
2291
+ }), /* @__PURE__ */ jsx(Flex, {
2292
+ direction: "column",
2293
+ gap: 4,
2294
+ style: {
2295
+ maxHeight: 300,
2296
+ overflowY: "auto"
2297
+ },
2298
+ children: fields.map((field) => /* @__PURE__ */ jsxs(Flex, {
2299
+ gap: "xs",
2300
+ wrap: "nowrap",
2301
+ align: "flex-start",
2302
+ children: [
2303
+ /* @__PURE__ */ jsx(ActionButton, {
2304
+ px: "xs",
2305
+ size: "xs",
2306
+ h: 24,
2307
+ variant: "default",
2308
+ justify: "end",
2309
+ miw: 120,
2310
+ onClick: () => onInsert(field.path),
2311
+ children: field.path
2312
+ }),
2313
+ /* @__PURE__ */ jsxs(Flex, {
2314
+ mt: 3,
2315
+ direction: "column",
2316
+ gap: 2,
2317
+ style: {
2318
+ flex: 1,
2319
+ minWidth: 0
2320
+ },
2321
+ children: [/* @__PURE__ */ jsx(Text, {
2322
+ size: "xs",
2323
+ c: "dimmed",
2324
+ lineClamp: 1,
2325
+ children: field.description || field.type
2326
+ }), field.enum && /* @__PURE__ */ jsx(Flex, {
2327
+ gap: 0,
2328
+ wrap: "wrap",
2329
+ children: field.enum.map((enumValue) => /* @__PURE__ */ jsx(ActionButton, {
2330
+ px: "xs",
2331
+ size: "xs",
2332
+ h: 24,
2333
+ onClick: () => onInsert(enumValue),
2334
+ children: enumValue
2335
+ }, enumValue))
2336
+ })]
2337
+ }),
2338
+ /* @__PURE__ */ jsx(Badge, {
2339
+ size: "xs",
2340
+ variant: "light",
2341
+ style: { flexShrink: 0 },
2342
+ children: field.type
2343
+ })
2344
+ ]
2345
+ }, field.path))
2346
+ })]
2347
+ })
2348
+ ]
2349
+ });
2350
+ }
2351
+
2352
+ //#endregion
2353
+ //#region ../../src/core/form/components/ControlSelect.tsx
2354
+ /**
2355
+ * ControlSelect component for handling Select, MultiSelect, and TagsInput.
2356
+ *
2357
+ * Features:
2358
+ * - Basic Select with enum support
2359
+ * - MultiSelect for array of enums
2360
+ * - TagsInput for array of strings (no enum)
2361
+ * - Future: Lazy loading
2362
+ * - Future: Searchable/filterable options
2363
+ * - Future: Custom option rendering
2364
+ *
2365
+ * Automatically detects enum values and array types from schema.
2366
+ */
2367
+ const ControlSelect = (props) => {
2368
+ const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
2369
+ const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
2370
+ let itemsEnum;
2371
+ if (isArray && "items" in props.input.schema && props.input.schema.items) {
2372
+ const items = props.input.schema.items;
2373
+ if ("enum" in items && Array.isArray(items.enum)) itemsEnum = items.enum;
2374
+ }
2375
+ const enumValues = props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum : [];
2376
+ const [data, setData] = useState([]);
2377
+ useEffect(() => {
2378
+ if (!props.input?.props) return;
2379
+ if (props.loader) props.loader().then(setData);
2380
+ else setData(enumValues);
2381
+ }, [props.input, props.loader]);
2382
+ if (!props.input?.props) return null;
2383
+ if (props.segmented) {
2384
+ const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
2385
+ return /* @__PURE__ */ jsx(Input.Wrapper, {
2386
+ ...inputProps,
2387
+ children: /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(SegmentedControl, {
2388
+ disabled: inputProps.disabled,
2389
+ defaultValue: String(props.input.props.defaultValue),
2390
+ ...segmentedControlProps,
2391
+ onChange: (value) => {
2392
+ props.input.set(value);
2393
+ },
2394
+ data: data.slice(0, 10)
2395
+ }) })
2396
+ });
2397
+ }
2398
+ if (props.autocomplete) {
2399
+ const autocompleteProps = typeof props.autocomplete === "object" ? props.autocomplete : {};
2400
+ return /* @__PURE__ */ jsx(Autocomplete, {
2401
+ ...inputProps,
2402
+ size: props.size,
2403
+ id,
2404
+ leftSection: icon,
2405
+ data,
2406
+ ...props.input.props,
2407
+ ...autocompleteProps
2408
+ });
2409
+ }
2410
+ if (isArray && !itemsEnum || props.tags) {
2411
+ const tagsInputProps = typeof props.tags === "object" ? props.tags : {};
2412
+ return /* @__PURE__ */ jsx(TagsInput, {
2413
+ ...inputProps,
2414
+ size: props.size,
2415
+ id,
2416
+ leftSection: icon,
2417
+ defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
2418
+ onChange: (value) => {
2419
+ props.input.set(value);
2420
+ },
2421
+ ...tagsInputProps
2422
+ });
2423
+ }
2424
+ if (isArray && itemsEnum || props.multi) {
2425
+ const data = itemsEnum?.map((value) => ({
2426
+ value,
2427
+ label: value
2428
+ })) || [];
2429
+ const multiSelectProps = typeof props.multi === "object" ? props.multi : {};
2430
+ return /* @__PURE__ */ jsx(MultiSelect, {
2431
+ ...inputProps,
2432
+ size: props.size,
2433
+ id,
2434
+ leftSection: icon,
2435
+ data,
2436
+ defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
2437
+ onChange: (value) => {
2438
+ props.input.set(value);
2439
+ },
2440
+ ...multiSelectProps
2441
+ });
2442
+ }
2443
+ const selectProps = typeof props.select === "object" ? props.select : {};
2444
+ return /* @__PURE__ */ jsx(Select, {
2445
+ ...inputProps,
2446
+ size: props.size,
2447
+ id,
2448
+ leftSection: icon,
2449
+ rightSection: null,
2450
+ data,
2451
+ ...props.input.props,
2452
+ ...selectProps
2453
+ });
2454
+ };
2455
+
2456
+ //#endregion
2457
+ //#region ../../src/core/form/components/Control.tsx
2458
+ /**
2459
+ * Generic form control that renders the appropriate input based on the schema and props.
2460
+ *
2461
+ * Supports:
2462
+ * - TextInput (with format detection: email, url, tel)
2463
+ * - Textarea
2464
+ * - NumberInput (for number/integer types)
2465
+ * - FileInput
2466
+ * - ColorInput (for color format)
2467
+ * - Select (for enum types)
2468
+ * - Autocomplete
2469
+ * - PasswordInput
2470
+ * - Switch (for boolean types)
2471
+ * - SegmentedControl (for enum types)
2472
+ * - DateInput (for date format)
2473
+ * - DateTimePicker (for date-time format)
2474
+ * - TimeInput (for time format)
2475
+ * - QueryBuilder (for building type-safe queries with autocomplete)
2476
+ * - ControlObject (for nested object schemas)
2477
+ * - ControlArray (for arrays of objects)
2478
+ * - Custom component
2479
+ *
2480
+ * Automatically handles labels, descriptions, error messages, required state, and default icons.
2481
+ */
2482
+ const Control = (_props) => {
2483
+ const form = useFormState(_props.input, ["error"]);
2484
+ if (!_props.input?.props) return null;
2485
+ const { inputProps, id, icon, format, schema } = parseInput(_props, form);
2486
+ const props = {
2487
+ ..._props,
2488
+ ...schema.$control
2489
+ };
2490
+ if (props.query) return /* @__PURE__ */ jsx(ControlQueryBuilder, {
2491
+ ...props.input.props,
2492
+ ...inputProps,
2493
+ schema: props.query,
2494
+ value: props.input.props.value,
2495
+ onChange: (value) => {
2496
+ props.input.set(value);
2497
+ }
2498
+ });
2499
+ if (props.custom) {
2500
+ const Custom = props.custom;
2501
+ return /* @__PURE__ */ jsx(Input.Wrapper, {
2502
+ ...inputProps,
2503
+ children: /* @__PURE__ */ jsx(Flex, {
2504
+ flex: 1,
2505
+ mt: "calc(var(--mantine-spacing-xs) / 2)",
2506
+ children: /* @__PURE__ */ jsx(Custom, {
2507
+ defaultValue: props.input.props.defaultValue,
2508
+ onChange: (value) => {
2509
+ props.input.set(value);
2510
+ }
2511
+ })
2512
+ })
2513
+ });
2514
+ }
2515
+ const isObject = props.input.schema && "type" in props.input.schema && props.input.schema.type === "object" && "properties" in props.input.schema;
2516
+ if (props.object || isObject) {
2517
+ const controlObjectProps = typeof props.object === "object" ? props.object : {};
2518
+ return /* @__PURE__ */ jsx(ControlObject, {
2519
+ input: props.input,
2520
+ title: props.title,
2521
+ description: props.description,
2522
+ ...controlObjectProps
2523
+ });
2524
+ }
2525
+ const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
2526
+ const isArrayOfObjects = isArray && "items" in props.input.schema && props.input.schema.items && typeof props.input.schema.items === "object" && "properties" in props.input.schema.items;
2527
+ if (props.array || isArrayOfObjects) {
2528
+ const controlArrayProps = typeof props.array === "object" ? props.array : {};
2529
+ return /* @__PURE__ */ jsx(ControlArray, {
2530
+ input: props.input,
2531
+ title: props.title,
2532
+ description: props.description,
2533
+ ...controlArrayProps
2534
+ });
2535
+ }
2536
+ if (props.number || props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
2537
+ const controlNumberProps = typeof props.number === "object" ? props.number : {};
2538
+ if (props.slider) controlNumberProps.sliderProps ??= {};
2539
+ return /* @__PURE__ */ jsx(ControlNumber, {
2540
+ size: props.size,
2541
+ input: props.input,
2542
+ title: props.title,
2543
+ description: props.description,
2544
+ icon,
2545
+ ...controlNumberProps
2546
+ });
2547
+ }
2548
+ if (props.file) {
2549
+ const fileInputProps = typeof props.file === "object" ? props.file : {};
2550
+ return /* @__PURE__ */ jsx(FileInput, {
2551
+ ...inputProps,
2552
+ size: props.size,
2553
+ id,
2554
+ leftSection: icon,
2555
+ onChange: (file) => {
2556
+ props.input.set(file);
2557
+ },
2558
+ ...fileInputProps
2559
+ });
2560
+ }
2561
+ if (props.color || format === "color") {
2562
+ const colorInputProps = typeof props.color === "object" ? props.color : {};
2563
+ return /* @__PURE__ */ jsx(ColorInput, {
2564
+ ...inputProps,
2565
+ size: props.size,
2566
+ id,
2567
+ leftSection: icon,
2568
+ ...props.input.props,
2569
+ ...colorInputProps
2570
+ });
2571
+ }
2572
+ if (props.input.schema && "enum" in props.input.schema && props.input.schema.enum || isArray && !isArrayOfObjects || props.select) {
2573
+ const opts = typeof props.select === "object" ? props.select : {};
2574
+ if (props.segmented) opts.segmented ??= {};
2575
+ return /* @__PURE__ */ jsx(ControlSelect, {
2576
+ size: props.size,
2577
+ input: props.input,
2578
+ title: props.title,
2579
+ description: props.description,
2580
+ icon,
2581
+ ...opts
2582
+ });
2583
+ }
2584
+ if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean") {
2585
+ if (props.switch) {
2586
+ const switchProps = typeof props.switch === "object" ? props.switch : {};
2587
+ return /* @__PURE__ */ jsx(Switch, {
2588
+ ...inputProps,
2589
+ size: props.size,
2590
+ id,
2591
+ color: "blue",
2592
+ defaultChecked: props.input.props.defaultValue,
2593
+ onChange: (event) => {
2594
+ props.input.set(event.currentTarget.checked);
2595
+ },
2596
+ ...switchProps
2597
+ });
2598
+ }
2599
+ const opts = {
2600
+ input: props.input,
2601
+ select: { data: [{
2602
+ value: "true",
2603
+ label: "Yes"
2604
+ }, {
2605
+ value: "false",
2606
+ label: "No"
2607
+ }] }
2608
+ };
2609
+ return /* @__PURE__ */ jsx(ControlSelect, {
2610
+ size: props.size,
2611
+ title: props.title,
2612
+ description: props.description,
2613
+ icon,
2614
+ ...opts
2615
+ });
2616
+ }
2617
+ if (props.password || props.input.props.name?.includes("password")) {
2618
+ const passwordInputProps = typeof props.password === "object" ? props.password : {};
2619
+ return /* @__PURE__ */ jsx(PasswordInput, {
2620
+ ...inputProps,
2621
+ size: props.size,
2622
+ id,
2623
+ leftSection: icon,
2624
+ ...props.input.props,
2625
+ ...passwordInputProps
2626
+ });
2627
+ }
2628
+ if (props.area) {
2629
+ const textAreaProps = typeof props.area === "object" ? props.area : {};
2630
+ return /* @__PURE__ */ jsx(Textarea, {
2631
+ ...inputProps,
2632
+ size: props.size,
2633
+ id,
2634
+ leftSection: icon,
2635
+ ...props.input.props,
2636
+ ...textAreaProps
2637
+ });
2638
+ }
2639
+ if (props.date || props.datetime || props.time || format === "date" || format === "date-time" || format === "time") return /* @__PURE__ */ jsx(ControlDate, {
2640
+ size: props.size,
2641
+ input: props.input,
2642
+ title: props.title,
2643
+ description: props.description,
2644
+ icon,
2645
+ date: props.date,
2646
+ datetime: props.datetime,
2647
+ time: props.time
2648
+ });
2649
+ const textInputProps = typeof props.text === "object" ? props.text : {};
2650
+ const getInputType = () => {
2651
+ switch (format) {
2652
+ case "email": return "email";
2653
+ case "url":
2654
+ case "uri": return "url";
2655
+ case "tel":
2656
+ case "phone": return "tel";
2657
+ default: return;
2658
+ }
2659
+ };
2660
+ return /* @__PURE__ */ jsx(TextInput, {
2661
+ ...inputProps,
2662
+ size: props.size,
2663
+ id,
2664
+ leftSection: icon,
2665
+ type: getInputType(),
2666
+ ...props.input.props,
2667
+ ...textInputProps,
2668
+ inputWrapperOrder: [
2669
+ "label",
2670
+ "input",
2671
+ "description",
2672
+ "error"
2673
+ ]
2674
+ });
2675
+ };
2676
+
2677
+ //#endregion
2678
+ //#region ../../src/core/form/components/TypeForm.tsx
2679
+ /**
2680
+ * TypeForm component that automatically renders all form inputs based on schema.
2681
+ * Uses the Control component to render individual fields and Mantine Grid for responsive layout.
2682
+ *
2683
+ * Supports all field types including:
2684
+ * - Primitive types (string, number, boolean, etc.)
2685
+ * - Enum types (rendered as Select)
2686
+ * - Arrays of primitives (rendered as MultiSelect/TagsInput)
2687
+ * - Arrays of objects (rendered as ControlArray)
2688
+ * - Nested objects (rendered as ControlObject)
2689
+ *
2690
+ * @example
2691
+ * ```tsx
2692
+ * import { t } from "alepha";
2693
+ * import { useForm } from "alepha/react/form";
2694
+ * import { TypeForm } from "@alepha/ui";
2695
+ *
2696
+ * const form = useForm({
2697
+ * schema: t.object({
2698
+ * username: t.text(),
2699
+ * email: t.text(),
2700
+ * age: t.integer(),
2701
+ * subscribe: t.boolean(),
2702
+ * address: t.object({
2703
+ * street: t.text(),
2704
+ * city: t.text(),
2705
+ * }),
2706
+ * tags: t.array(t.text()),
2707
+ * contacts: t.array(t.object({
2708
+ * name: t.text(),
2709
+ * email: t.text(),
2710
+ * })),
2711
+ * }),
2712
+ * handler: (values) => {
2713
+ * console.log(values);
2714
+ * },
2715
+ * });
2716
+ *
2717
+ * return <TypeForm form={form} columns={2} />;
2718
+ * ```
2719
+ */
2720
+ const TypeForm = (props) => {
2721
+ const { form, columns = 3, children, controlProps, fieldControlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps, fill = true, size } = props;
2722
+ const schema = props.schema || form.options.schema;
2723
+ if (!schema?.properties) return null;
2724
+ const supportedFields = Object.keys(schema.properties);
2725
+ const colSpan = typeof columns === "number" ? {
2726
+ xs: 12,
2727
+ sm: 6,
2728
+ lg: 12 / columns
2729
+ } : {
2730
+ base: columns.base ? 12 / columns.base : void 0,
2731
+ xs: columns.xs ? 12 / columns.xs : 12,
2732
+ sm: columns.sm ? 12 / columns.sm : 6,
2733
+ md: columns.md ? 12 / columns.md : void 0,
2734
+ lg: columns.lg ? 12 / columns.lg : 4,
2735
+ xl: columns.xl ? 12 / columns.xl : void 0
2736
+ };
2737
+ const renderFields = () => {
2738
+ if (children) return /* @__PURE__ */ jsx(Fragment, { children: children(form.input) });
2739
+ return /* @__PURE__ */ jsx(Grid, { children: supportedFields.map((fieldName) => {
2740
+ const field = form.input[fieldName];
2741
+ const fieldSchema = schema.properties[fieldName];
2742
+ if (!field || !fieldSchema) return null;
2743
+ const isObject = fieldSchema && "type" in fieldSchema && fieldSchema.type === "object";
2744
+ const isArrayOfObjects = fieldSchema && "type" in fieldSchema && fieldSchema.type === "array" && "items" in fieldSchema && fieldSchema.items && "properties" in fieldSchema.items;
2745
+ const span = isObject || isArrayOfObjects ? 12 : colSpan;
2746
+ const mergedControlProps = {
2747
+ ...controlProps,
2748
+ ...fieldControlProps?.[fieldName]
2749
+ };
2750
+ if (size) mergedControlProps.size = size;
2751
+ return /* @__PURE__ */ jsx(Grid.Col, {
2752
+ span,
2753
+ children: /* @__PURE__ */ jsx(Control, {
2754
+ input: field,
2755
+ ...mergedControlProps
2756
+ })
2757
+ }, fieldName);
2758
+ }) });
2759
+ };
2760
+ const content = /* @__PURE__ */ jsxs(Flex, {
2761
+ direction: "column",
2762
+ gap: "sm",
2763
+ flex: fill ? 1 : void 0,
2764
+ ...props.flexProps,
2765
+ children: [/* @__PURE__ */ jsx(Flex, {
2766
+ direction: "column",
2767
+ gap: "sm",
2768
+ flex: 1,
2769
+ children: renderFields()
2770
+ }), !skipSubmitButton && /* @__PURE__ */ jsx(Card, {
2771
+ w: "100%",
2772
+ withBorder: true,
2773
+ children: /* @__PURE__ */ jsxs(Flex, {
2774
+ gap: "sm",
2775
+ flex: 1,
2776
+ children: [
2777
+ /* @__PURE__ */ jsx(Flex, {}),
2778
+ /* @__PURE__ */ jsx(Flex, { flex: 1 }),
2779
+ /* @__PURE__ */ jsxs(Flex, {
2780
+ gap: "sm",
2781
+ children: [/* @__PURE__ */ jsx(ActionButton, {
2782
+ variant: "subtle",
2783
+ type: "reset",
2784
+ children: "Reset"
2785
+ }), /* @__PURE__ */ jsx(ActionButton, {
2786
+ intent: "primary",
2787
+ form,
2788
+ ...submitButtonProps,
2789
+ children: submitButtonProps?.children ?? "Submit"
2790
+ })]
2791
+ })
2792
+ ]
2793
+ })
2794
+ })]
2795
+ });
2796
+ if (skipFormElement) return content;
2797
+ return /* @__PURE__ */ jsx(Flex, {
2798
+ component: "form",
2799
+ flex: fill ? 1 : void 0,
2800
+ ...form.props,
2801
+ ...props.flexProps,
2802
+ children: content
2803
+ });
2804
+ };
2805
+
2806
+ //#endregion
2807
+ //#region ../../src/core/helpers/renderIcon.tsx
2808
+ const renderIcon = (icon, size) => {
2809
+ if (!icon) return null;
2810
+ if (isValidElement(icon)) return icon;
2811
+ if (isComponentType(icon)) return /* @__PURE__ */ jsx(icon, { size: size ?? ui.sizes.icon.md });
2812
+ return icon;
2813
+ };
2814
+
2815
+ //#endregion
2816
+ //#region ../../src/core/hooks/useDialog.ts
2817
+ /**
2818
+ * Use this hook to access the Dialog Service for showing various dialog types.
2819
+ *
2820
+ * @example
2821
+ * ```tsx
2822
+ * const dialog = useDialog();
2823
+ * await dialog.alert({ title: "Alert", message: "This is an alert message" });
2824
+ * const confirmed = await dialog.confirm({ title: "Confirm", message: "Are you sure?" });
2825
+ * const input = await dialog.prompt({ title: "Input", message: "Enter your name:" });
2826
+ * ```
2827
+ */
2828
+ const useDialog = () => {
2829
+ return useInject(DialogService);
2830
+ };
2831
+
2832
+ //#endregion
2833
+ //#region ../../src/core/json/components/JsonViewer.tsx
2834
+ const SIZE_CONFIG = {
2835
+ xs: {
2836
+ icon: 14,
2837
+ levelOffset: 16
2838
+ },
2839
+ sm: {
2840
+ icon: 16,
2841
+ levelOffset: 20
2842
+ },
2843
+ md: {
2844
+ icon: 18,
2845
+ levelOffset: 24
2846
+ },
2847
+ lg: {
2848
+ icon: 20,
2849
+ levelOffset: 28
2850
+ },
2851
+ xl: {
2852
+ icon: 22,
2853
+ levelOffset: 32
2854
+ }
2855
+ };
2856
+ const STYLES = {
2857
+ root: { fontFamily: "var(--mantine-font-family-monospace)" },
2858
+ chevron: {
2859
+ flexShrink: 0,
2860
+ color: "var(--mantine-color-dimmed)"
2861
+ },
2862
+ key: {
2863
+ color: "var(--mantine-color-cyan-text)",
2864
+ fontWeight: 500
2865
+ },
2866
+ colon: { color: "var(--mantine-color-dimmed)" },
2867
+ string: { color: "var(--mantine-color-teal-text)" },
2868
+ number: { color: "var(--mantine-color-blue-text)" },
2869
+ boolean: { color: "var(--mantine-color-violet-text)" },
2870
+ null: {
2871
+ color: "var(--mantine-color-dimmed)",
2872
+ fontStyle: "italic"
2873
+ },
2874
+ preview: { color: "var(--mantine-color-dimmed)" }
2875
+ };
2876
+ const getValueType = (val) => {
2877
+ if (val === null) return "null";
2878
+ if (val === void 0) return "undefined";
2879
+ if (Array.isArray(val)) return "array";
2880
+ return typeof val;
2881
+ };
2882
+ function buildTreeNodes(data, path = [], key, isArrayItem = false, maxDepth = 10) {
2883
+ const currentPath = key !== void 0 ? [...path, key] : path;
2884
+ const nodeId = currentPath.length > 0 ? currentPath.join(".") : "root";
2885
+ if (currentPath.length > maxDepth) return {
2886
+ value: nodeId,
2887
+ label: key ?? "",
2888
+ nodeValue: data,
2889
+ nodeKey: key,
2890
+ path: currentPath,
2891
+ isArrayItem
2892
+ };
2893
+ const type = getValueType(data);
2894
+ if (type === "object" || type === "array") {
2895
+ const children = (type === "array" ? data.map((v, i) => [String(i), v]) : Object.entries(data)).map(([k, v]) => buildTreeNodes(v, currentPath, k, type === "array", maxDepth)).filter((n) => n !== null);
2896
+ return {
2897
+ value: nodeId,
2898
+ label: key ?? "",
2899
+ nodeValue: data,
2900
+ nodeKey: key,
2901
+ path: currentPath,
2902
+ isArrayItem,
2903
+ children: children.length > 0 ? children : void 0
2904
+ };
2905
+ }
2906
+ return {
2907
+ value: nodeId,
2908
+ label: key ?? "",
2909
+ nodeValue: data,
2910
+ nodeKey: key,
2911
+ path: currentPath,
2912
+ isArrayItem
2913
+ };
2914
+ }
2915
+ function getExpandedIds(nodes, targetDepth, currentDepth = 0) {
2916
+ if (currentDepth >= targetDepth) return [];
2917
+ const ids = [];
2918
+ for (const node of nodes) if (node.children) {
2919
+ ids.push(node.value);
2920
+ ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));
2921
+ }
2922
+ return ids;
2923
+ }
2924
+ const CopyButton = ({ value, iconSize }) => {
2925
+ const [copied, setCopied] = useState(false);
2926
+ const handleCopy = useCallback((e) => {
2927
+ e.stopPropagation();
2928
+ navigator.clipboard.writeText(value);
2929
+ setCopied(true);
2930
+ setTimeout(() => setCopied(false), 1500);
2931
+ }, [value]);
2932
+ return /* @__PURE__ */ jsx(ActionIcon, {
2933
+ size: iconSize + 4,
2934
+ variant: "transparent",
2935
+ c: copied ? "green" : "dimmed",
2936
+ onClick: handleCopy,
2937
+ className: "alepha-json-viewer-copy",
2938
+ children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: iconSize }) : /* @__PURE__ */ jsx(IconCopy, { size: iconSize })
2939
+ });
2940
+ };
2941
+ const RowNode = ({ node, expanded, hasChildren, elementProps, size, config, showQuotes, showCopyButton, renderValue }) => {
2942
+ const { nodeValue, nodeKey, path, isArrayItem, isRoot } = node;
2943
+ const type = getValueType(nodeValue);
2944
+ const isExpandable = type === "object" || type === "array";
2945
+ const getPreview = () => {
2946
+ if (!isExpandable) return null;
2947
+ const count = (type === "array" ? nodeValue : Object.keys(nodeValue)).length;
2948
+ const label = type === "array" ? "item" : "key";
2949
+ if (!expanded) return /* @__PURE__ */ jsx(Text, {
2950
+ fs: "italic",
2951
+ component: "span",
2952
+ size,
2953
+ style: STYLES.preview,
2954
+ children: count === 0 ? type === "array" ? "[]" : "{}" : type === "array" ? `[ ${count} ${count === 1 ? label : `${label}s`} ]` : `{ ${count} ${count === 1 ? label : `${label}s`} }`
2955
+ });
2956
+ return null;
2957
+ };
2958
+ const getCopyValue = () => isExpandable ? JSON.stringify(nodeValue, null, 2) : String(nodeValue ?? "");
2959
+ return /* @__PURE__ */ jsxs(Flex, {
2960
+ gap: 6,
2961
+ wrap: "nowrap",
2962
+ ...elementProps,
2963
+ className: `alepha-json-viewer-row ${elementProps.className || ""}`,
2964
+ children: [
2965
+ hasChildren ? expanded ? /* @__PURE__ */ jsx(IconChevronDown, {
2966
+ size: config.icon,
2967
+ style: STYLES.chevron
2968
+ }) : /* @__PURE__ */ jsx(IconChevronRight, {
2969
+ size: config.icon,
2970
+ style: STYLES.chevron
2971
+ }) : /* @__PURE__ */ jsx("span", { style: {
2972
+ width: config.icon,
2973
+ flexShrink: 0
2974
+ } }),
2975
+ nodeKey !== void 0 && !isArrayItem && /* @__PURE__ */ jsxs(Text, {
2976
+ component: "span",
2977
+ size,
2978
+ children: [/* @__PURE__ */ jsx("span", {
2979
+ style: STYLES.key,
2980
+ children: showQuotes ? `"${nodeKey}"` : nodeKey
2981
+ }), /* @__PURE__ */ jsx("span", {
2982
+ style: STYLES.colon,
2983
+ children: ":"
2984
+ })]
2985
+ }),
2986
+ nodeKey !== void 0 && isArrayItem && /* @__PURE__ */ jsxs(Text, {
2987
+ component: "span",
2988
+ size,
2989
+ children: [/* @__PURE__ */ jsx("span", {
2990
+ style: STYLES.key,
2991
+ children: nodeKey
2992
+ }), /* @__PURE__ */ jsx("span", {
2993
+ style: STYLES.colon,
2994
+ children: ":"
2995
+ })]
2996
+ }),
2997
+ hasChildren ? getPreview() : isExpandable ? type === "array" ? /* @__PURE__ */ jsx(Text, {
2998
+ component: "span",
2999
+ size,
3000
+ style: STYLES.preview,
3001
+ children: "[]"
3002
+ }) : /* @__PURE__ */ jsx(Text, {
3003
+ component: "span",
3004
+ size,
3005
+ style: STYLES.preview,
3006
+ children: "{}"
3007
+ }) : renderValue(nodeValue, nodeKey, path),
3008
+ showCopyButton && /* @__PURE__ */ jsx(CopyButton, {
3009
+ value: getCopyValue(),
3010
+ iconSize: config.icon
3011
+ })
3012
+ ]
3013
+ });
3014
+ };
3015
+ const JsonViewer = ({ data, defaultExpandedDepth = 2, maxDepth = 10, size = "sm", showQuotes = false, showCopyButton = true, formatValue }) => {
3016
+ const config = SIZE_CONFIG[size] || SIZE_CONFIG.sm;
3017
+ const treeData = useMemo(() => {
3018
+ const type = getValueType(data);
3019
+ if (type === "object" || type === "array") {
3020
+ const children = (type === "array" ? data.map((v, i) => [String(i), v]) : Object.entries(data)).map(([k, v]) => buildTreeNodes(v, [], k, type === "array", maxDepth)).filter((n) => n !== null);
3021
+ return [{
3022
+ value: "root",
3023
+ label: "",
3024
+ nodeValue: data,
3025
+ nodeKey: void 0,
3026
+ path: [],
3027
+ isArrayItem: false,
3028
+ isRoot: true,
3029
+ children: children.length > 0 ? children : void 0
3030
+ }];
3031
+ }
3032
+ const node = buildTreeNodes(data, [], void 0, false, maxDepth);
3033
+ return node ? [node] : [];
3034
+ }, [data, maxDepth]);
3035
+ const tree = useTree({ initialExpandedState: useMemo(() => {
3036
+ if (defaultExpandedDepth === 0) return {};
3037
+ if (defaultExpandedDepth === Infinity) return getTreeExpandedState(treeData, "*");
3038
+ return getTreeExpandedState(treeData, getExpandedIds(treeData, defaultExpandedDepth + 1));
3039
+ }, [treeData, defaultExpandedDepth]) });
3040
+ const renderValue = useCallback((val, key, path) => {
3041
+ const custom = formatValue?.(key, val, path);
3042
+ if (custom !== void 0) return /* @__PURE__ */ jsx(Text, {
3043
+ component: "span",
3044
+ size,
3045
+ style: STYLES.string,
3046
+ className: "alepha-json-viewer-value",
3047
+ title: String(val),
3048
+ children: custom
3049
+ });
3050
+ const type = getValueType(val);
3051
+ switch (type) {
3052
+ case "string": return /* @__PURE__ */ jsxs(Text, {
3053
+ style: STYLES.string,
3054
+ component: "span",
3055
+ size,
3056
+ className: "alepha-json-viewer-value",
3057
+ title: val,
3058
+ children: [
3059
+ "\"",
3060
+ val,
3061
+ "\""
3062
+ ]
3063
+ });
3064
+ case "number": return /* @__PURE__ */ jsx(Text, {
3065
+ component: "span",
3066
+ size,
3067
+ style: STYLES.number,
3068
+ children: val
3069
+ });
3070
+ case "boolean": return /* @__PURE__ */ jsx(Text, {
3071
+ component: "span",
3072
+ size,
3073
+ style: STYLES.boolean,
3074
+ children: String(val)
3075
+ });
3076
+ case "null":
3077
+ case "undefined": return /* @__PURE__ */ jsx(Text, {
3078
+ component: "span",
3079
+ size,
3080
+ style: STYLES.null,
3081
+ children: type
3082
+ });
3083
+ default: return /* @__PURE__ */ jsx(Text, {
3084
+ component: "span",
3085
+ size,
3086
+ children: String(val)
3087
+ });
3088
+ }
3089
+ }, [
3090
+ formatValue,
3091
+ showQuotes,
3092
+ size
3093
+ ]);
3094
+ const renderNode = useCallback(({ node, expanded, hasChildren, elementProps }) => {
3095
+ return /* @__PURE__ */ jsx(RowNode, {
3096
+ node,
3097
+ expanded,
3098
+ hasChildren,
3099
+ elementProps,
3100
+ size,
3101
+ config,
3102
+ showQuotes,
3103
+ showCopyButton,
3104
+ renderValue
3105
+ });
3106
+ }, [
3107
+ config,
3108
+ renderValue,
3109
+ showCopyButton,
3110
+ showQuotes,
3111
+ size
3112
+ ]);
3113
+ if (treeData.length === 0) return /* @__PURE__ */ jsx(Text, {
3114
+ size,
3115
+ style: STYLES.null,
3116
+ children: data === null ? "null" : data === void 0 ? "undefined" : "{}"
3117
+ });
3118
+ return /* @__PURE__ */ jsx(Tree, {
3119
+ data: treeData,
3120
+ tree,
3121
+ levelOffset: config.levelOffset,
3122
+ expandOnClick: true,
3123
+ renderNode,
3124
+ styles: { root: STYLES.root }
3125
+ });
3126
+ };
3127
+
3128
+ //#endregion
3129
+ //#region ../../src/core/table/interfaces/types.ts
3130
+ const DEFAULT_MAX_VISIBLE_COLUMNS = 8;
3131
+
3132
+ //#endregion
3133
+ //#region ../../src/core/table/components/DataTableFilters.tsx
3134
+ const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) => {
3135
+ const visibleSchema = useMemo(() => {
3136
+ const visibleKeys = Object.keys(schema.properties).filter((key) => filterVisibility[key] !== false);
3137
+ if (visibleKeys.length === 0) return null;
3138
+ const visibleProps = visibleKeys.reduce((acc, key) => {
3139
+ acc[key] = schema.properties[key];
3140
+ return acc;
3141
+ }, {});
3142
+ return t.object(visibleProps);
3143
+ }, [schema, filterVisibility]);
3144
+ if (!visibleSchema) return null;
3145
+ return /* @__PURE__ */ jsx(Flex, {
3146
+ w: "100%",
3147
+ p: "xs",
3148
+ bg: ui.colors.surface,
3149
+ style: { borderBottom: "1px solid var(--alepha-border)" },
3150
+ children: /* @__PURE__ */ jsx(TypeForm, {
3151
+ size: "xs",
3152
+ ...typeFormProps,
3153
+ skipSubmitButton: true,
3154
+ fill: true,
3155
+ form,
3156
+ schema: visibleSchema,
3157
+ columns: {
3158
+ base: 1,
3159
+ sm: 2,
3160
+ md: 3,
3161
+ lg: 4,
3162
+ xl: 6
3163
+ }
3164
+ })
3165
+ });
3166
+ };
3167
+
3168
+ //#endregion
3169
+ //#region ../../src/core/table/components/DataTablePagination.tsx
3170
+ const DataTablePagination = ({ page, size, totalPages, onPageChange, onSizeChange }) => {
3171
+ return /* @__PURE__ */ jsxs(Flex, {
3172
+ align: "center",
3173
+ justify: "end",
3174
+ gap: "md",
3175
+ p: "xs",
3176
+ style: { borderTop: "1px solid var(--alepha-border)" },
3177
+ children: [/* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Select, {
3178
+ w: 96,
3179
+ variant: "default",
3180
+ value: size,
3181
+ onChange: (value) => {
3182
+ if (value) onSizeChange(Number(value));
3183
+ },
3184
+ data: [
3185
+ {
3186
+ value: "5",
3187
+ label: "5"
3188
+ },
3189
+ {
3190
+ value: "10",
3191
+ label: "10"
3192
+ },
3193
+ {
3194
+ value: "25",
3195
+ label: "25"
3196
+ },
3197
+ {
3198
+ value: "50",
3199
+ label: "50"
3200
+ },
3201
+ {
3202
+ value: "100",
3203
+ label: "100"
3204
+ }
3205
+ ]
3206
+ }) }), /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Pagination, {
3207
+ withEdges: true,
3208
+ total: totalPages,
3209
+ value: page,
3210
+ onChange: onPageChange
3211
+ }) })]
3212
+ });
3213
+ };
3214
+
3215
+ //#endregion
3216
+ //#region ../../src/core/table/components/ColumnPicker.tsx
3217
+ const ColumnPicker = ({ columns, visibility, onVisibilityChange }) => {
3218
+ const [opened, setOpened] = useState(false);
3219
+ const columnEntries = Object.entries(columns);
3220
+ const handleShowAll = () => {
3221
+ onVisibilityChange(columnEntries.reduce((acc, [key]) => ({
3222
+ ...acc,
3223
+ [key]: true
3224
+ }), {}));
3225
+ };
3226
+ const handleDefault = () => {
3227
+ let count = 0;
3228
+ onVisibilityChange(columnEntries.reduce((acc, [key, col]) => {
3229
+ if (col.defaultHidden) acc[key] = false;
3230
+ else if (count < DEFAULT_MAX_VISIBLE_COLUMNS) {
3231
+ acc[key] = true;
3232
+ count++;
3233
+ } else acc[key] = false;
3234
+ return acc;
3235
+ }, {}));
3236
+ };
3237
+ const handleToggle = (key, checked) => {
3238
+ onVisibilityChange({
3239
+ ...visibility,
3240
+ [key]: checked
3241
+ });
3242
+ };
3243
+ const visibleCount = columnEntries.filter(([key]) => visibility[key] !== false).length;
3244
+ return /* @__PURE__ */ jsxs(Popover, {
3245
+ width: 280,
3246
+ position: "bottom-start",
3247
+ shadow: "md",
3248
+ opened,
3249
+ onChange: setOpened,
3250
+ closeOnClickOutside: true,
3251
+ closeOnEscape: true,
3252
+ transitionProps: {
3253
+ transition: "fade-up",
3254
+ duration: 200,
3255
+ timingFunction: "ease"
3256
+ },
3257
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(ActionButton, {
3258
+ variant: "subtle",
3259
+ icon: IconColumns,
3260
+ onClick: () => setOpened((o) => !o)
3261
+ }) }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3262
+ bg: "transparent",
3263
+ p: "xs",
3264
+ bd: `1px solid ${ui.colors.border}`,
3265
+ style: { backdropFilter: "blur(20px)" },
3266
+ children: /* @__PURE__ */ jsxs(Flex, {
3267
+ direction: "column",
3268
+ gap: "xs",
3269
+ bg: ui.colors.surface,
3270
+ p: "sm",
3271
+ bdrs: "sm",
3272
+ children: [/* @__PURE__ */ jsxs(Flex, {
3273
+ justify: "space-between",
3274
+ children: [/* @__PURE__ */ jsxs(Text, {
3275
+ size: "sm",
3276
+ fw: 500,
3277
+ children: [
3278
+ "Columns (",
3279
+ visibleCount,
3280
+ "/",
3281
+ columnEntries.length,
3282
+ ")"
3283
+ ]
3284
+ }), /* @__PURE__ */ jsxs(Flex, {
3285
+ gap: 4,
3286
+ children: [/* @__PURE__ */ jsx(ActionButton, {
3287
+ size: "compact-xs",
3288
+ variant: "subtle",
3289
+ onClick: handleShowAll,
3290
+ children: "All"
3291
+ }), /* @__PURE__ */ jsx(ActionButton, {
3292
+ size: "compact-xs",
3293
+ variant: "subtle",
3294
+ onClick: handleDefault,
3295
+ children: "Default"
3296
+ })]
3297
+ })]
3298
+ }), /* @__PURE__ */ jsx(ScrollArea.Autosize, {
3299
+ mah: 300,
3300
+ children: /* @__PURE__ */ jsx(Flex, {
3301
+ direction: "column",
3302
+ gap: 4,
3303
+ children: columnEntries.map(([key, col]) => /* @__PURE__ */ jsx(Checkbox, {
3304
+ label: col.label || key,
3305
+ checked: visibility[key] !== false,
3306
+ onChange: (e) => handleToggle(key, e.currentTarget.checked),
3307
+ size: "sm"
3308
+ }, key))
3309
+ })
3310
+ })]
3311
+ })
3312
+ })]
3313
+ });
3314
+ };
3315
+
3316
+ //#endregion
3317
+ //#region ../../src/core/table/components/FilterPicker.tsx
3318
+ const getFieldLabel = (schema, key) => {
3319
+ const prop = schema.properties[key];
3320
+ if (prop && typeof prop === "object" && "title" in prop && prop.title) return prop.title;
3321
+ return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
3322
+ };
3323
+ const FilterPicker = ({ schema, visibility, onVisibilityChange }) => {
3324
+ const [opened, setOpened] = useState(false);
3325
+ const filterKeys = Object.keys(schema.properties);
3326
+ const handleShowAll = () => {
3327
+ onVisibilityChange(filterKeys.reduce((acc, key) => ({
3328
+ ...acc,
3329
+ [key]: true
3330
+ }), {}));
3331
+ };
3332
+ const handleHideAll = () => {
3333
+ onVisibilityChange(filterKeys.reduce((acc, key) => ({
3334
+ ...acc,
3335
+ [key]: false
3336
+ }), {}));
3337
+ };
3338
+ const handleToggle = (key, checked) => {
3339
+ onVisibilityChange({
3340
+ ...visibility,
3341
+ [key]: checked
3342
+ });
3343
+ };
3344
+ const visibleCount = filterKeys.filter((key) => visibility[key]).length;
3345
+ return /* @__PURE__ */ jsxs(Popover, {
3346
+ width: 280,
3347
+ position: "bottom-start",
3348
+ shadow: "md",
3349
+ opened,
3350
+ onChange: setOpened,
3351
+ closeOnClickOutside: true,
3352
+ closeOnEscape: true,
3353
+ transitionProps: {
3354
+ transition: "fade-up",
3355
+ duration: 200,
3356
+ timingFunction: "ease"
3357
+ },
3358
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(ActionButton, {
3359
+ variant: "subtle",
3360
+ icon: IconFilter,
3361
+ onClick: () => setOpened((o) => !o)
3362
+ }) }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3363
+ bg: "transparent",
3364
+ p: "xs",
3365
+ bd: `1px solid ${ui.colors.border}`,
3366
+ style: { backdropFilter: "blur(20px)" },
3367
+ children: /* @__PURE__ */ jsxs(Flex, {
3368
+ direction: "column",
3369
+ gap: "xs",
3370
+ bg: ui.colors.surface,
3371
+ p: "sm",
3372
+ bdrs: "sm",
3373
+ children: [/* @__PURE__ */ jsxs(Flex, {
3374
+ justify: "space-between",
3375
+ children: [/* @__PURE__ */ jsxs(Text, {
3376
+ size: "sm",
3377
+ fw: 500,
3378
+ children: [
3379
+ "Filters (",
3380
+ visibleCount,
3381
+ "/",
3382
+ filterKeys.length,
3383
+ ")"
3384
+ ]
3385
+ }), /* @__PURE__ */ jsxs(Flex, {
3386
+ gap: 4,
3387
+ children: [/* @__PURE__ */ jsx(ActionButton, {
3388
+ size: "compact-xs",
3389
+ variant: "subtle",
3390
+ onClick: handleShowAll,
3391
+ children: "All"
3392
+ }), /* @__PURE__ */ jsx(ActionButton, {
3393
+ size: "compact-xs",
3394
+ variant: "subtle",
3395
+ onClick: handleHideAll,
3396
+ children: "None"
3397
+ })]
3398
+ })]
3399
+ }), /* @__PURE__ */ jsx(ScrollArea.Autosize, {
3400
+ mah: 300,
3401
+ children: /* @__PURE__ */ jsx(Flex, {
3402
+ direction: "column",
3403
+ gap: 4,
3404
+ children: filterKeys.map((key) => /* @__PURE__ */ jsx(Checkbox, {
3405
+ label: getFieldLabel(schema, key),
3406
+ checked: visibility[key] === true,
3407
+ onChange: (e) => handleToggle(key, e.currentTarget.checked),
3408
+ size: "sm"
3409
+ }, key))
3410
+ })
3411
+ })]
3412
+ })
3413
+ })]
3414
+ });
3415
+ };
3416
+
3417
+ //#endregion
3418
+ //#region ../../src/core/table/components/DataTableToolbar.tsx
3419
+ const escapeCsvField = (value) => {
3420
+ if (value.includes(",") || value.includes("\"") || value.includes("\n")) return `"${value.replace(/"/g, "\"\"")}"`;
3421
+ return value;
3422
+ };
3423
+ const extractText = (node) => {
3424
+ if (node == null || typeof node === "boolean") return "";
3425
+ if (typeof node === "string" || typeof node === "number") return String(node);
3426
+ if (Array.isArray(node)) return node.map(extractText).join("");
3427
+ if (typeof node === "object" && "props" in node) return extractText(node.props.children);
3428
+ return "";
3429
+ };
3430
+ const DataTableToolbar = ({ columns, filters, columnVisibility, filterVisibility, onColumnVisibilityChange, onFilterVisibilityChange, actions, onRefresh, items, withExport, selectedItems = [], checkboxActions, onClearSelection }) => {
3431
+ const hasSelection = selectedItems.length > 0;
3432
+ const exportableColumns = useCallback(() => {
3433
+ return Object.entries(columns).filter(([key, col]) => !col.actions && columnVisibility[key] !== false);
3434
+ }, [columns, columnVisibility]);
3435
+ const buildRows = useCallback(() => {
3436
+ const cols = exportableColumns();
3437
+ return items.map((item) => cols.map(([_key, col]) => {
3438
+ if (!col.value) return "";
3439
+ return extractText(col.value(item, {}));
3440
+ }));
3441
+ }, [items, exportableColumns]);
3442
+ const buildCsv = useCallback(() => {
3443
+ const header = exportableColumns().map(([_key, col]) => escapeCsvField(col.label));
3444
+ const rows = buildRows().map((row) => row.map(escapeCsvField));
3445
+ return [header.join(","), ...rows.map((r) => r.join(","))].join("\n");
3446
+ }, [exportableColumns, buildRows]);
3447
+ const exportCsv = useCallback(() => {
3448
+ const csv = buildCsv();
3449
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
3450
+ const url = URL.createObjectURL(blob);
3451
+ const a = document.createElement("a");
3452
+ a.href = url;
3453
+ a.download = "export.csv";
3454
+ a.click();
3455
+ URL.revokeObjectURL(url);
3456
+ }, [buildCsv]);
3457
+ const exportClipboard = useCallback(async () => {
3458
+ const header = exportableColumns().map(([_key, col]) => col.label);
3459
+ const rows = buildRows();
3460
+ const text = [header.join(" "), ...rows.map((r) => r.join(" "))].join("\n");
3461
+ await navigator.clipboard.writeText(text);
3462
+ }, [exportableColumns, buildRows]);
3463
+ const handleCheckboxAction = async (action) => {
3464
+ const ctx = {
3465
+ selectedItems,
3466
+ clearSelection: onClearSelection || (() => {})
3467
+ };
3468
+ await action.onClick(ctx);
3469
+ };
3470
+ return /* @__PURE__ */ jsxs(Flex, {
3471
+ p: "xs",
3472
+ style: { borderBottom: "1px solid var(--alepha-border)" },
3473
+ children: [
3474
+ /* @__PURE__ */ jsxs(Flex, {
3475
+ gap: 4,
3476
+ align: "center",
3477
+ children: [
3478
+ filters && /* @__PURE__ */ jsx(FilterPicker, {
3479
+ schema: filters,
3480
+ visibility: filterVisibility,
3481
+ onVisibilityChange: onFilterVisibilityChange
3482
+ }),
3483
+ /* @__PURE__ */ jsx(ColumnPicker, {
3484
+ columns,
3485
+ visibility: columnVisibility,
3486
+ onVisibilityChange: onColumnVisibilityChange
3487
+ }),
3488
+ withExport && /* @__PURE__ */ jsx(ActionButton, {
3489
+ variant: "subtle",
3490
+ icon: IconDownload,
3491
+ menu: { items: [{
3492
+ label: "Export as CSV",
3493
+ icon: /* @__PURE__ */ jsx(IconDownload, { size: 14 }),
3494
+ onClick: exportCsv
3495
+ }, {
3496
+ label: "Copy to clipboard",
3497
+ icon: /* @__PURE__ */ jsx(IconClipboard, { size: 14 }),
3498
+ onClick: exportClipboard
3499
+ }] }
3500
+ }),
3501
+ hasSelection && /* @__PURE__ */ jsxs(Fragment, { children: [
3502
+ /* @__PURE__ */ jsx(Divider, {
3503
+ orientation: "vertical",
3504
+ mx: "xs"
3505
+ }),
3506
+ /* @__PURE__ */ jsxs(Badge, {
3507
+ variant: "light",
3508
+ size: "lg",
3509
+ children: [selectedItems.length, " selected"]
3510
+ }),
3511
+ /* @__PURE__ */ jsx(ActionButton, {
3512
+ variant: "subtle",
3513
+ size: "compact-sm",
3514
+ icon: IconX,
3515
+ onClick: onClearSelection,
3516
+ children: "Clear"
3517
+ }),
3518
+ checkboxActions?.map((action, index) => /* @__PURE__ */ jsx(ActionButton, {
3519
+ variant: "light",
3520
+ size: "compact-sm",
3521
+ intent: action.intent,
3522
+ icon: action.icon && isComponentType(action.icon) ? action.icon : void 0,
3523
+ onClick: () => handleCheckboxAction(action),
3524
+ children: action.label
3525
+ }, index))
3526
+ ] })
3527
+ ]
3528
+ }),
3529
+ /* @__PURE__ */ jsx(Flex, { flex: 1 }),
3530
+ /* @__PURE__ */ jsxs(Flex, {
3531
+ gap: "xs",
3532
+ children: [actions?.map((props, index) => !isValidElement(props) ? /* @__PURE__ */ jsx(ActionButton, {
3533
+ ...props,
3534
+ children: props.label
3535
+ }, index) : props), /* @__PURE__ */ jsx(ActionButton, {
3536
+ variant: "subtle",
3537
+ icon: IconRefresh,
3538
+ onClick: onRefresh
3539
+ })]
3540
+ })
3541
+ ]
3542
+ });
3543
+ };
3544
+
3545
+ //#endregion
3546
+ //#region ../../src/core/table/components/useTableSelection.ts
3547
+ const useTableSelection = (items, getItemKey, enabled) => {
3548
+ const [selectedKeys, setSelectedKeys] = useState(/* @__PURE__ */ new Set());
3549
+ const selectedItems = useMemo(() => {
3550
+ if (!enabled) return [];
3551
+ return items.filter((item) => selectedKeys.has(getItemKey(item)));
3552
+ }, [
3553
+ items,
3554
+ selectedKeys,
3555
+ getItemKey,
3556
+ enabled
3557
+ ]);
3558
+ const allSelected = useMemo(() => {
3559
+ if (items.length === 0) return false;
3560
+ return items.every((item) => selectedKeys.has(getItemKey(item)));
3561
+ }, [
3562
+ items,
3563
+ selectedKeys,
3564
+ getItemKey
3565
+ ]);
3566
+ return {
3567
+ selectedItems,
3568
+ allSelected,
3569
+ someSelected: useMemo(() => {
3570
+ if (items.length === 0) return false;
3571
+ const count = items.filter((item) => selectedKeys.has(getItemKey(item))).length;
3572
+ return count > 0 && count < items.length;
3573
+ }, [
3574
+ items,
3575
+ selectedKeys,
3576
+ getItemKey
3577
+ ]),
3578
+ toggleItem: useCallback((item) => {
3579
+ const key = getItemKey(item);
3580
+ setSelectedKeys((prev) => {
3581
+ const next = new Set(prev);
3582
+ if (next.has(key)) next.delete(key);
3583
+ else next.add(key);
3584
+ return next;
3585
+ });
3586
+ }, [getItemKey]),
3587
+ toggleAll: useCallback(() => {
3588
+ if (allSelected) setSelectedKeys((prev) => {
3589
+ const next = new Set(prev);
3590
+ for (const item of items) next.delete(getItemKey(item));
3591
+ return next;
3592
+ });
3593
+ else setSelectedKeys((prev) => {
3594
+ const next = new Set(prev);
3595
+ for (const item of items) next.add(getItemKey(item));
3596
+ return next;
3597
+ });
3598
+ }, [
3599
+ allSelected,
3600
+ items,
3601
+ getItemKey
3602
+ ]),
3603
+ clear: useCallback(() => setSelectedKeys(/* @__PURE__ */ new Set()), []),
3604
+ isSelected: useCallback((item) => selectedKeys.has(getItemKey(item)), [selectedKeys, getItemKey])
3605
+ };
3606
+ };
3607
+
3608
+ //#endregion
3609
+ //#region ../../src/core/table/components/DataTable.tsx
3610
+ /**
3611
+ * Parse the sort string to get direction for a specific field.
3612
+ * Alepha convention: 'field' = ASC, '-field' = DESC
3613
+ */
3614
+ const getSortDirection = (sortString, field) => {
3615
+ if (!sortString) return null;
3616
+ const parts = sortString.split(",").map((s) => s.trim());
3617
+ for (const part of parts) {
3618
+ if (part === field) return "asc";
3619
+ if (part === `-${field}`) return "desc";
3620
+ }
3621
+ return null;
3622
+ };
3623
+ /**
3624
+ * Toggle sort for a field in the sort string.
3625
+ * Cycles: null -> asc -> desc -> null
3626
+ */
3627
+ const toggleSort = (sortString, field) => {
3628
+ const current = getSortDirection(sortString, field);
3629
+ const parts = (sortString || "").split(",").map((s) => s.trim()).filter((s) => s && s !== field && s !== `-${field}`);
3630
+ if (current === null) parts.unshift(field);
3631
+ else if (current === "asc") parts.unshift(`-${field}`);
3632
+ return parts.length > 0 ? parts.join(",") : void 0;
3633
+ };
3634
+ const toAriaSort = (dir) => {
3635
+ if (dir === "asc") return "ascending";
3636
+ if (dir === "desc") return "descending";
3637
+ return "none";
3638
+ };
3639
+ const FIT_STYLE = {
3640
+ width: 1,
3641
+ whiteSpace: "nowrap"
3642
+ };
3643
+ const DataTable = (props) => {
3644
+ const [items, setItems] = useState(typeof props.items === "function" ? { content: [] } : props.items);
3645
+ const defaultSize = props.infinityScroll ? 100 : props.defaultSize || 10;
3646
+ const [page, setPage] = useState(1);
3647
+ const [size, setSize] = useState(String(defaultSize));
3648
+ const [currentPage, setCurrentPage] = useState(0);
3649
+ const alepha = useInject(Alepha);
3650
+ const sentinelRef = useRef(null);
3651
+ const [columnVisibility, setColumnVisibility] = useState(() => {
3652
+ const entries = Object.entries(props.columns);
3653
+ let visibleCount = 0;
3654
+ return entries.reduce((acc, [key, col]) => {
3655
+ if (col.defaultHidden) acc[key] = false;
3656
+ else if (visibleCount < DEFAULT_MAX_VISIBLE_COLUMNS) {
3657
+ acc[key] = true;
3658
+ visibleCount++;
3659
+ } else acc[key] = false;
3660
+ return acc;
3661
+ }, {});
3662
+ });
3663
+ const [filterVisibility, setFilterVisibility] = useState(() => {
3664
+ if (!props.filters?.properties) return {};
3665
+ const defaults = new Set(props.defaultFilters ?? []);
3666
+ return Object.keys(props.filters.properties).reduce((acc, key) => ({
3667
+ ...acc,
3668
+ [key]: defaults.has(key)
3669
+ }), {});
3670
+ });
3671
+ const visibleColumns = useMemo(() => {
3672
+ return Object.entries(props.columns).filter(([key]) => columnVisibility[key] !== false);
3673
+ }, [props.columns, columnVisibility]);
3674
+ const [sortString, setSortString] = useState(void 0);
3675
+ const handleSortClick = (columnKey, sortKey) => {
3676
+ const newSort = toggleSort(sortString, sortKey || columnKey);
3677
+ setSortString(newSort);
3678
+ form.input.sort.set(newSort);
3679
+ form.input.page.set(0);
3680
+ };
3681
+ const getItemKey = useCallback((item) => {
3682
+ if (props.getItemKey) return props.getItemKey(item);
3683
+ if ("id" in item) return String(item.id);
3684
+ return JSON.stringify(item);
3685
+ }, [props.getItemKey]);
3686
+ const selection = useTableSelection(items.content, getItemKey, props.withCheckbox ?? false);
3687
+ const panelConfig = useMemo(() => {
3688
+ if (!props.panel) return null;
3689
+ if (typeof props.panel === "function") return {
3690
+ render: props.panel,
3691
+ can: void 0
3692
+ };
3693
+ return props.panel;
3694
+ }, [props.panel]);
3695
+ const [drawerItem, setDrawerItem] = useState(null);
3696
+ const drawerConfig = useMemo(() => {
3697
+ if (!props.drawer) return null;
3698
+ if (typeof props.drawer === "function") return {
3699
+ render: props.drawer,
3700
+ can: void 0,
3701
+ props: void 0
3702
+ };
3703
+ return props.drawer;
3704
+ }, [props.drawer]);
3705
+ const [expandedKeys, setExpandedKeys] = useState(/* @__PURE__ */ new Set());
3706
+ const toggleExpand = useCallback((key) => {
3707
+ setExpandedKeys((prev) => {
3708
+ const next = new Set(prev);
3709
+ if (next.has(key)) next.delete(key);
3710
+ else next.add(key);
3711
+ return next;
3712
+ });
3713
+ }, []);
3714
+ const form = useForm({
3715
+ schema: t.object({
3716
+ ...props.filters ? props.filters.properties : {},
3717
+ page: t.number({ default: 0 }),
3718
+ size: t.number({ default: defaultSize }),
3719
+ sort: t.optional(t.string())
3720
+ }),
3721
+ handler: async (values) => {
3722
+ if (typeof props.items === "function") {
3723
+ const response = await props.items(values, { items: items.content });
3724
+ if (props.infinityScroll && values.page > 0) setItems((prev) => ({
3725
+ ...response,
3726
+ content: [...prev.content, ...response.content]
3727
+ }));
3728
+ else setItems(response);
3729
+ setCurrentPage(values.page);
3730
+ }
3731
+ },
3732
+ onReset: async () => {
3733
+ setPage(1);
3734
+ setSize(String(defaultSize));
3735
+ await form.submit();
3736
+ },
3737
+ onChange: async (key, value) => {
3738
+ if (key === "page") {
3739
+ setPage(value + 1);
3740
+ await form.submit();
3741
+ return;
3742
+ }
3743
+ if (key === "size") {
3744
+ setSize(String(value));
3745
+ form.input.page.set(0);
3746
+ return;
3747
+ }
3748
+ props.onFilterChange?.(key, value, form);
3749
+ }
3750
+ }, [items]);
3751
+ const dt = useInject(DateTimeProvider);
3752
+ useEffect(() => {
3753
+ if (props.submitOnInit) form.submit();
3754
+ if (props.submitEvery) {
3755
+ const it = dt.createInterval(() => {
3756
+ form.submit();
3757
+ }, props.submitEvery);
3758
+ return () => dt.clearInterval(it);
3759
+ }
3760
+ }, []);
3761
+ useEffect(() => {
3762
+ if (typeof props.items !== "function") setItems(props.items);
3763
+ }, [props.items]);
3764
+ useEffect(() => {
3765
+ if (!props.infinityScroll || typeof props.items !== "function") return;
3766
+ const sentinel = sentinelRef.current;
3767
+ if (!sentinel) return;
3768
+ const observer = new IntersectionObserver((entries) => {
3769
+ if (!entries[0].isIntersecting || form.submitting) return;
3770
+ const totalPages = items.page?.totalPages ?? 1;
3771
+ if (currentPage + 1 < totalPages) form.input.page.set(currentPage + 1);
3772
+ }, { rootMargin: "300px" });
3773
+ observer.observe(sentinel);
3774
+ return () => observer.disconnect();
3775
+ }, [
3776
+ props.infinityScroll,
3777
+ form.submitting,
3778
+ items.page?.totalPages,
3779
+ currentPage,
3780
+ form
3781
+ ]);
3782
+ const totalColumns = visibleColumns.length + (panelConfig ? 1 : 0) + (props.withCheckbox ? 1 : 0);
3783
+ const checkboxHeader = props.withCheckbox ? /* @__PURE__ */ jsx(Table.Th, {
3784
+ style: { width: 40 },
3785
+ children: /* @__PURE__ */ jsx(Checkbox, {
3786
+ checked: selection.allSelected,
3787
+ indeterminate: selection.someSelected,
3788
+ onChange: selection.toggleAll,
3789
+ "aria-label": "Select all"
3790
+ })
3791
+ }) : null;
3792
+ const head = visibleColumns.map(([key, col]) => {
3793
+ const sortField = col.sortKey || key;
3794
+ const sortDir = col.sortable ? getSortDirection(sortString, sortField) : null;
3795
+ return /* @__PURE__ */ jsx(Table.Th, {
3796
+ onClick: col.sortable ? () => handleSortClick(key, col.sortKey) : void 0,
3797
+ "aria-sort": col.sortable ? toAriaSort(sortDir) : void 0,
3798
+ style: {
3799
+ ...col.fit ? FIT_STYLE : {},
3800
+ ...col.sortable ? {
3801
+ cursor: "pointer",
3802
+ userSelect: "none"
3803
+ } : {}
3804
+ },
3805
+ children: /* @__PURE__ */ jsxs(Flex, {
3806
+ align: "center",
3807
+ gap: 4,
3808
+ children: [/* @__PURE__ */ jsx(Text, {
3809
+ size: "xs",
3810
+ children: col.label
3811
+ }), col.sortable && /* @__PURE__ */ jsxs(Flex, {
3812
+ c: "dimmed",
3813
+ children: [
3814
+ sortDir === "asc" && /* @__PURE__ */ jsx(IconArrowUp, { size: ui.sizes.icon.sm }),
3815
+ sortDir === "desc" && /* @__PURE__ */ jsx(IconArrowDown, { size: ui.sizes.icon.sm }),
3816
+ sortDir === null && /* @__PURE__ */ jsx(IconArrowsSort, { size: ui.sizes.icon.sm })
3817
+ ]
3818
+ })]
3819
+ })
3820
+ }, key);
3821
+ });
3822
+ const rows = items.content.flatMap((item, index) => {
3823
+ const trProps = props.tableTrProps ? props.tableTrProps(item) : {};
3824
+ const itemKey = getItemKey(item);
3825
+ const isSelected = selection.isSelected(item);
3826
+ const showPanel = panelConfig && (panelConfig.can ? panelConfig.can(item) : true);
3827
+ const isExpanded = expandedKeys.has(itemKey);
3828
+ const canOpenDrawer = drawerConfig && (drawerConfig.can ? drawerConfig.can(item) : true);
3829
+ const elements = [/* @__PURE__ */ jsxs(Table.Tr, {
3830
+ ...trProps,
3831
+ style: {
3832
+ ...canOpenDrawer ? { cursor: "pointer" } : {},
3833
+ ...trProps.style ?? {}
3834
+ },
3835
+ onClick: (e) => {
3836
+ if (canOpenDrawer) setDrawerItem(item);
3837
+ trProps.onClick?.(e);
3838
+ },
3839
+ children: [
3840
+ panelConfig && /* @__PURE__ */ jsx(Table.Td, {
3841
+ style: {
3842
+ width: 36,
3843
+ textAlign: "center"
3844
+ },
3845
+ py: 2,
3846
+ px: 0,
3847
+ children: showPanel && /* @__PURE__ */ jsx(UnstyledButton, {
3848
+ onClick: (e) => {
3849
+ e.stopPropagation();
3850
+ toggleExpand(itemKey);
3851
+ },
3852
+ style: { display: "inline-flex" },
3853
+ children: /* @__PURE__ */ jsx(Flex, {
3854
+ c: "dimmed",
3855
+ align: "center",
3856
+ justify: "center",
3857
+ children: isExpanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: ui.sizes.icon.sm }) : /* @__PURE__ */ jsx(IconChevronRight, { size: ui.sizes.icon.sm })
3858
+ })
3859
+ })
3860
+ }),
3861
+ props.withCheckbox && /* @__PURE__ */ jsx(Table.Td, {
3862
+ style: { width: 40 },
3863
+ onClick: (e) => e.stopPropagation(),
3864
+ children: /* @__PURE__ */ jsx(Checkbox, {
3865
+ checked: isSelected,
3866
+ onChange: () => selection.toggleItem(item),
3867
+ "aria-label": "Select row"
3868
+ })
3869
+ }),
3870
+ visibleColumns.map(([key, col]) => {
3871
+ const ctx = {
3872
+ index,
3873
+ form,
3874
+ alepha
3875
+ };
3876
+ if (col.actions) {
3877
+ const rowActions = col.actions(item, ctx).filter((a) => a.visible !== false);
3878
+ return /* @__PURE__ */ jsx(Table.Td, {
3879
+ py: 2,
3880
+ px: 4,
3881
+ style: col.fit ? FIT_STYLE : void 0,
3882
+ onClick: (e) => e.stopPropagation(),
3883
+ children: /* @__PURE__ */ jsx(Flex, {
3884
+ gap: 4,
3885
+ children: rowActions.map(({ visible: _, ...actionProps }, i) => /* @__PURE__ */ jsx(ActionButton, {
3886
+ variant: "subtle",
3887
+ size: "xs",
3888
+ preventDefault: true,
3889
+ h: 20,
3890
+ ...actionProps
3891
+ }, i))
3892
+ })
3893
+ }, key);
3894
+ }
3895
+ return /* @__PURE__ */ jsx(Table.Td, {
3896
+ py: 2,
3897
+ px: 4,
3898
+ style: col.fit ? FIT_STYLE : void 0,
3899
+ children: col.value?.(item, ctx)
3900
+ }, key);
3901
+ })
3902
+ ]
3903
+ }, itemKey)];
3904
+ if (panelConfig && showPanel && isExpanded) elements.push(/* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
3905
+ colSpan: totalColumns,
3906
+ p: 0,
3907
+ children: panelConfig.render(item)
3908
+ }) }, `${itemKey}-panel`));
3909
+ return elements;
3910
+ });
3911
+ const filterSchema = useMemo(() => {
3912
+ if (!props.filters) return null;
3913
+ return t.omit(form.options.schema, [
3914
+ "page",
3915
+ "size",
3916
+ "sort"
3917
+ ]);
3918
+ }, [props.filters, form.options.schema]);
3919
+ return /* @__PURE__ */ jsxs(Flex, {
3920
+ flex: 1,
3921
+ p: 0,
3922
+ bdrs: "sm",
3923
+ direction: "column",
3924
+ children: [
3925
+ /* @__PURE__ */ jsx(DataTableToolbar, {
3926
+ columns: props.columns,
3927
+ filters: props.filters,
3928
+ columnVisibility,
3929
+ filterVisibility,
3930
+ onColumnVisibilityChange: setColumnVisibility,
3931
+ onFilterVisibilityChange: setFilterVisibility,
3932
+ actions: props.actions,
3933
+ onRefresh: () => form.submit(),
3934
+ items: items.content,
3935
+ withExport: props.withExport,
3936
+ selectedItems: selection.selectedItems,
3937
+ checkboxActions: props.checkboxActions,
3938
+ onClearSelection: selection.clear
3939
+ }),
3940
+ filterSchema && props.filters && /* @__PURE__ */ jsx(DataTableFilters, {
3941
+ schema: filterSchema,
3942
+ form,
3943
+ typeFormProps: props.typeFormProps,
3944
+ filterVisibility
3945
+ }),
3946
+ /* @__PURE__ */ jsx(Flex, {
3947
+ className: "overflow-auto",
3948
+ children: /* @__PURE__ */ jsxs(Table, {
3949
+ "aria-label": "Data table",
3950
+ withColumnBorders: true,
3951
+ withRowBorders: true,
3952
+ ...props.tableProps,
3953
+ children: [/* @__PURE__ */ jsx(Table.Thead, {
3954
+ style: {
3955
+ position: "sticky",
3956
+ top: 0,
3957
+ zIndex: 1,
3958
+ backgroundColor: "var(--mantine-color-body)"
3959
+ },
3960
+ children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
3961
+ panelConfig && /* @__PURE__ */ jsx(Table.Th, { style: { width: 36 } }),
3962
+ checkboxHeader,
3963
+ head
3964
+ ] })
3965
+ }), /* @__PURE__ */ jsxs(Table.Tbody, {
3966
+ style: {
3967
+ opacity: form.submitting ? .5 : 1,
3968
+ transition: "opacity 150ms ease"
3969
+ },
3970
+ children: [rows, items.content.length === 0 && /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
3971
+ colSpan: totalColumns || 1,
3972
+ py: "xl",
3973
+ style: { textAlign: "center" },
3974
+ children: /* @__PURE__ */ jsx(Text, {
3975
+ c: "dimmed",
3976
+ size: "sm",
3977
+ children: form.submitting ? "Loading…" : "No results"
3978
+ })
3979
+ }) })]
3980
+ })]
3981
+ })
3982
+ }),
3983
+ props.infinityScroll && /* @__PURE__ */ jsx("div", { ref: sentinelRef }),
3984
+ !props.infinityScroll && /* @__PURE__ */ jsx(DataTablePagination, {
3985
+ page,
3986
+ size,
3987
+ totalPages: items.page?.totalPages ?? 1,
3988
+ onPageChange: (value) => {
3989
+ form.input.page.set(value - 1);
3990
+ },
3991
+ onSizeChange: (value) => {
3992
+ form.input.size.set(value);
3993
+ }
3994
+ }),
3995
+ drawerConfig && /* @__PURE__ */ jsx(Drawer, {
3996
+ opened: drawerItem !== null,
3997
+ onClose: () => setDrawerItem(null),
3998
+ position: "right",
3999
+ size: "xl",
4000
+ ...drawerConfig.props,
4001
+ children: drawerItem && drawerConfig.render(drawerItem)
4002
+ })
4003
+ ]
4004
+ });
4005
+ };
4006
+
4007
+ //#endregion
4008
+ //#region ../../src/core/utils/extractSchemaFields.ts
4009
+ /**
4010
+ * Extract field information from a TypeBox schema for query building.
4011
+ * Supports nested objects and provides field metadata for autocomplete.
4012
+ */
4013
+ function extractSchemaFields(schema, prefix = "") {
4014
+ const fields = [];
4015
+ if (!schema || typeof schema !== "object") return fields;
4016
+ const properties = "properties" in schema ? schema.properties : schema;
4017
+ if (!properties || typeof properties !== "object") return fields;
4018
+ for (const [key, value] of Object.entries(properties)) {
4019
+ if (typeof value !== "object" || value === null) continue;
4020
+ const fieldSchema = value;
4021
+ const path = prefix ? `${prefix}.${key}` : key;
4022
+ const format = "format" in fieldSchema ? fieldSchema.format : void 0;
4023
+ let displayType = "type" in fieldSchema ? fieldSchema.type : "object";
4024
+ if (format === "date-time") displayType = "datetime";
4025
+ else if (format === "date") displayType = "date";
4026
+ else if (format === "time") displayType = "time";
4027
+ else if (format === "duration") displayType = "duration";
4028
+ const field = {
4029
+ name: key,
4030
+ path,
4031
+ type: displayType,
4032
+ format,
4033
+ description: "description" in fieldSchema ? fieldSchema.description : void 0
4034
+ };
4035
+ if ("enum" in fieldSchema && fieldSchema.enum) {
4036
+ field.enum = fieldSchema.enum;
4037
+ field.type = "enum";
4038
+ }
4039
+ if ("type" in fieldSchema && fieldSchema.type === "object" && "properties" in fieldSchema && typeof fieldSchema.properties === "object") field.nested = extractSchemaFields(fieldSchema.properties, path);
4040
+ fields.push(field);
4041
+ if (field.nested) fields.push(...field.nested);
4042
+ }
4043
+ return fields;
4044
+ }
4045
+ /**
4046
+ * Get operator symbol and description
4047
+ */
4048
+ const OPERATOR_INFO = {
4049
+ eq: {
4050
+ symbol: "=",
4051
+ label: "equals",
4052
+ example: "name=John"
4053
+ },
4054
+ ne: {
4055
+ symbol: "!=",
4056
+ label: "not equals",
4057
+ example: "status!=archived"
4058
+ },
4059
+ gt: {
4060
+ symbol: ">",
4061
+ label: "greater than",
4062
+ example: "age>18"
4063
+ },
4064
+ gte: {
4065
+ symbol: ">=",
4066
+ label: "greater or equal",
4067
+ example: "age>=18"
4068
+ },
4069
+ lt: {
4070
+ symbol: "<",
4071
+ label: "less than",
4072
+ example: "age<65"
4073
+ },
4074
+ lte: {
4075
+ symbol: "<=",
4076
+ label: "less or equal",
4077
+ example: "age<=65"
4078
+ },
4079
+ null: {
4080
+ symbol: "=null",
4081
+ label: "is null",
4082
+ example: "deletedAt=null"
4083
+ },
4084
+ notNull: {
4085
+ symbol: "!=null",
4086
+ label: "is not null",
4087
+ example: "email!=null"
4088
+ },
4089
+ in: {
4090
+ symbol: "[...]",
4091
+ label: "in array",
4092
+ example: "status=[active,pending]"
4093
+ }
4094
+ };
4095
+
4096
+ //#endregion
4097
+ //#region ../../src/core/utils/icons.tsx
4098
+ /**
4099
+ * Get the default icon for an input based on its type, format, or name.
4100
+ */
4101
+ const getDefaultIcon = (params) => {
4102
+ const { type, format, name, isEnum, isArray, size = "sm" } = params;
4103
+ const iconSize = ui.sizes.icon[size];
4104
+ if (format) switch (format) {
4105
+ case "email": return /* @__PURE__ */ jsx(IconMail, { size: iconSize });
4106
+ case "url":
4107
+ case "uri": return /* @__PURE__ */ jsx(IconLink, { size: iconSize });
4108
+ case "tel":
4109
+ case "phone": return /* @__PURE__ */ jsx(IconPhone, { size: iconSize });
4110
+ case "date": return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
4111
+ case "date-time": return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
4112
+ case "time": return /* @__PURE__ */ jsx(IconClock, { size: iconSize });
4113
+ case "color": return /* @__PURE__ */ jsx(IconColorPicker, { size: iconSize });
4114
+ case "uuid": return /* @__PURE__ */ jsx(IconKey, { size: iconSize });
4115
+ }
4116
+ if (name) {
4117
+ const nameLower = name.toLowerCase();
4118
+ if (nameLower.includes("password") || nameLower.includes("secret")) return /* @__PURE__ */ jsx(IconKey, { size: iconSize });
4119
+ if (nameLower.includes("email") || nameLower.includes("mail")) return /* @__PURE__ */ jsx(IconMail, { size: iconSize });
4120
+ if (nameLower.includes("url") || nameLower.includes("link")) return /* @__PURE__ */ jsx(IconLink, { size: iconSize });
4121
+ if (nameLower.includes("phone") || nameLower.includes("tel")) return /* @__PURE__ */ jsx(IconPhone, { size: iconSize });
4122
+ if (nameLower.includes("color")) return /* @__PURE__ */ jsx(IconPalette, { size: iconSize });
4123
+ if (nameLower.includes("file") || nameLower.includes("upload")) return /* @__PURE__ */ jsx(IconFile, { size: iconSize });
4124
+ if (nameLower.includes("date")) return /* @__PURE__ */ jsx(IconCalendar, { size: iconSize });
4125
+ if (nameLower.includes("time")) return /* @__PURE__ */ jsx(IconClock, { size: iconSize });
4126
+ }
4127
+ if (isEnum || isArray) return /* @__PURE__ */ jsx(IconSelector, { size: iconSize });
4128
+ if (type) switch (type) {
4129
+ case "boolean": return /* @__PURE__ */ jsx(IconToggleLeft, { size: iconSize });
4130
+ case "number":
4131
+ case "integer": return /* @__PURE__ */ jsx(IconHash, { size: iconSize });
4132
+ case "array": return /* @__PURE__ */ jsx(IconList, { size: iconSize });
4133
+ case "string": return /* @__PURE__ */ jsx(IconLetterCase, { size: iconSize });
4134
+ }
4135
+ return /* @__PURE__ */ jsx(IconAt, { size: iconSize });
4136
+ };
4137
+
4138
+ //#endregion
4139
+ //#region ../../src/core/utils/string.ts
4140
+ /**
4141
+ * Capitalizes the first letter of a string.
4142
+ *
4143
+ * @example
4144
+ * capitalize("hello") // "Hello"
4145
+ */
4146
+ const capitalize = (str) => {
4147
+ return str.charAt(0).toUpperCase() + str.slice(1);
4148
+ };
4149
+ /**
4150
+ * Converts camelCase or snake_case to Title Case with spaces.
4151
+ *
4152
+ * @example
4153
+ * toTitleCase("userName") // "User Name"
4154
+ * toTitleCase("first_name") // "First Name"
4155
+ * toTitleCase("email") // "Email"
4156
+ */
4157
+ const toTitleCase = (str) => {
4158
+ return str.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
4159
+ };
4160
+ /**
4161
+ * Converts a path or identifier string into a pretty display name.
4162
+ * For paths like "/contacts/0/name", extracts just the field name "Name".
4163
+ * Handles camelCase and snake_case conversion to Title Case.
4164
+ *
4165
+ * @example
4166
+ * prettyName("/userName") // "User Name"
4167
+ * prettyName("/contacts/0/email") // "Email"
4168
+ * prettyName("/address/streetName") // "Street Name"
4169
+ * prettyName("first_name") // "First Name"
4170
+ */
4171
+ const prettyName = (name) => {
4172
+ const segments = name.split("/").filter((s) => s && !/^\d+$/.test(s));
4173
+ return toTitleCase(segments[segments.length - 1] || name.replaceAll("/", ""));
4174
+ };
4175
+
4176
+ //#endregion
4177
+ //#region ../../src/core/index.ts
4178
+ /**
4179
+ * Core UI components based on Mantine UI v8.
4180
+ *
4181
+ * **Features:**
4182
+ * - Mantine integration with theme support
4183
+ * - ActionButton, BurgerButton, ClipboardButton, DarkModeButton, LanguageButton, ThemeButton
4184
+ * - AlertDialog, ConfirmDialog, PromptDialog
4185
+ * - Form controls: Control, ControlArray, ControlDate, ControlNumber, ControlObject, ControlSelect, ControlQueryBuilder
4186
+ * - TypeForm for automatic form generation from TypeBox schemas
4187
+ * - DashboardShell layout component
4188
+ * - AppBar with configurable elements
4189
+ * - Sidebar navigation with sections and menu items
4190
+ * - Omnibar for command palette / search
4191
+ * - DataTable with filtering, sorting, pagination
4192
+ * - Toast notifications
4193
+ * - Theme system with dark mode
4194
+ *
4195
+ * @module alepha.ui
4196
+ */
4197
+ const AlephaUI = $module({
4198
+ name: "alepha.ui",
4199
+ services: [
4200
+ DialogService,
4201
+ ToastService,
4202
+ ThemeProvider,
4203
+ UiRouter
4204
+ ],
4205
+ register: (alepha) => {
4206
+ alepha.with(AlephaReactI18n);
4207
+ alepha.with(AlephaReactHead);
4208
+ alepha.with(AlephaReactForm);
4209
+ alepha.with(ThemeProvider);
4210
+ alepha.with(DialogService);
4211
+ alepha.with(ToastService);
4212
+ }
4213
+ });
4214
+
4215
+ //#endregion
4216
+ export { AlephaMantineProvider as _, useDialog as a, Text$1 as c, Breadcrumb as d, Heading as f, ui as g, ActionButton as h, JsonViewer as i, DashboardShell as l, ToggleSidebarButton as m, capitalize as n, TypeForm as o, Flex$1 as p, DataTable as r, Control as s, AlephaUI as t, Sidebar as u, useToast as v };
4217
+ //# sourceMappingURL=core-RCUw1Q-a.js.map