@alepha/ui 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/dist/admin/{AdminApiKeys-BJhIwfD6.js → AdminApiKeys-Dy_k-4Vd.js} +2 -2
  2. package/dist/admin/{AdminApiKeys-BJhIwfD6.js.map → AdminApiKeys-Dy_k-4Vd.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-DzD_4cDt.js → AdminAudits-CKiFMSSU.js} +2 -2
  4. package/dist/admin/{AdminAudits-DzD_4cDt.js.map → AdminAudits-CKiFMSSU.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-C92tIc6x.js → AdminDashboard-PhC_dZqo.js} +2 -2
  6. package/dist/admin/{AdminDashboard-C92tIc6x.js.map → AdminDashboard-PhC_dZqo.js.map} +1 -1
  7. package/dist/admin/{AdminFiles-DLpfhBkf.js → AdminFiles-DFTjijGp.js} +2 -2
  8. package/dist/admin/{AdminFiles-DLpfhBkf.js.map → AdminFiles-DFTjijGp.js.map} +1 -1
  9. package/dist/admin/{AdminJobDashboard-KIOkeMgE.js → AdminJobDashboard-BL8gGPDp.js} +2 -2
  10. package/dist/admin/{AdminJobDashboard-KIOkeMgE.js.map → AdminJobDashboard-BL8gGPDp.js.map} +1 -1
  11. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js → AdminJobExecutions-D9E-CS-U.js} +2 -2
  12. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js.map → AdminJobExecutions-D9E-CS-U.js.map} +1 -1
  13. package/dist/admin/{AdminJobRegistry-PFajqaGK.js → AdminJobRegistry-Ci9ue1zC.js} +2 -2
  14. package/dist/admin/{AdminJobRegistry-PFajqaGK.js.map → AdminJobRegistry-Ci9ue1zC.js.map} +1 -1
  15. package/dist/admin/{AdminLayout-B1DXZHDn.js → AdminLayout-I6TlUMPc.js} +2 -2
  16. package/dist/admin/{AdminLayout-B1DXZHDn.js.map → AdminLayout-I6TlUMPc.js.map} +1 -1
  17. package/dist/admin/AdminNotifications-ZPHCYrv7.js +542 -0
  18. package/dist/admin/AdminNotifications-ZPHCYrv7.js.map +1 -0
  19. package/dist/admin/{AdminParameters-BspPeqp_.js → AdminParameters-CqgvhRsb.js} +120 -105
  20. package/dist/admin/AdminParameters-CqgvhRsb.js.map +1 -0
  21. package/dist/admin/{AdminSessions-BnH5CZQl.js → AdminSessions-Bz5NRuoW.js} +2 -2
  22. package/dist/admin/{AdminSessions-BnH5CZQl.js.map → AdminSessions-Bz5NRuoW.js.map} +1 -1
  23. package/dist/admin/{AdminUserLayout-DUbC6-BI.js → AdminUserLayout-lXT6I0Qq.js} +14 -8
  24. package/dist/admin/AdminUserLayout-lXT6I0Qq.js.map +1 -0
  25. package/dist/admin/{AdminUserProfile-DuTUnjdG.js → AdminUserProfile-vFBLoJ3h.js} +3 -3
  26. package/dist/admin/{AdminUserProfile-DuTUnjdG.js.map → AdminUserProfile-vFBLoJ3h.js.map} +1 -1
  27. package/dist/admin/{AdminUserSessions-DvZdAGpL.js → AdminUserSessions-CT_YDim0.js} +2 -2
  28. package/dist/admin/{AdminUserSessions-DvZdAGpL.js.map → AdminUserSessions-CT_YDim0.js.map} +1 -1
  29. package/dist/admin/{AdminUsers-CR9z0g_5.js → AdminUsers-D1UfGya9.js} +2 -2
  30. package/dist/admin/{AdminUsers-CR9z0g_5.js.map → AdminUsers-D1UfGya9.js.map} +1 -1
  31. package/dist/admin/{AuthLayout-DsUfp9RG.js → AuthLayout-_frhdgOO.js} +2 -2
  32. package/dist/admin/{AuthLayout-DsUfp9RG.js.map → AuthLayout-_frhdgOO.js.map} +1 -1
  33. package/dist/admin/Login-xtNmQtGh.js +275 -0
  34. package/dist/admin/Login-xtNmQtGh.js.map +1 -0
  35. package/dist/admin/{Profile-B2EcIDB9.js → Profile-_AtPUwAP.js} +31 -27
  36. package/dist/admin/Profile-_AtPUwAP.js.map +1 -0
  37. package/dist/admin/{Register-Z3fxRbUF.js → Register-JcCjHUUn.js} +198 -142
  38. package/dist/admin/Register-JcCjHUUn.js.map +1 -0
  39. package/dist/admin/{ResetPassword-_Y1qTTKh.js → ResetPassword-CwGBPLJO.js} +7 -7
  40. package/dist/admin/ResetPassword-CwGBPLJO.js.map +1 -0
  41. package/dist/admin/{VerifyEmail-Bg22bwcC.js → VerifyEmail-hNxWejWf.js} +23 -8
  42. package/dist/admin/VerifyEmail-hNxWejWf.js.map +1 -0
  43. package/dist/admin/{core-BVO_TQxb.js → core-CYaRQ8O-.js} +709 -556
  44. package/dist/admin/core-CYaRQ8O-.js.map +1 -0
  45. package/dist/admin/index.d.ts +83 -44
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +58 -39
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/auth/{AuthLayout-C161NeF6.js → AuthLayout-AvLlcLjS.js} +2 -2
  50. package/dist/auth/{AuthLayout-C161NeF6.js.map → AuthLayout-AvLlcLjS.js.map} +1 -1
  51. package/dist/auth/Login-BA1E8IZl.js +275 -0
  52. package/dist/auth/Login-BA1E8IZl.js.map +1 -0
  53. package/dist/auth/{Profile-BMpXJ0oi.js → Profile-YcWdeuFz.js} +31 -27
  54. package/dist/auth/Profile-YcWdeuFz.js.map +1 -0
  55. package/dist/auth/{Register-2gx8qll-.js → Register-CPhEO5MG.js} +198 -142
  56. package/dist/auth/Register-CPhEO5MG.js.map +1 -0
  57. package/dist/{demo/ResetPassword-CAPj8MO3.js → auth/ResetPassword-DCtGcneA.js} +7 -7
  58. package/dist/auth/ResetPassword-DCtGcneA.js.map +1 -0
  59. package/dist/{demo/VerifyEmail-DFmdCdYs.js → auth/VerifyEmail-DkH7NBfn.js} +23 -8
  60. package/dist/auth/VerifyEmail-DkH7NBfn.js.map +1 -0
  61. package/dist/auth/{core-DyfeVr5c.js → core-D5jIAVF2.js} +386 -294
  62. package/dist/auth/core-D5jIAVF2.js.map +1 -0
  63. package/dist/auth/index.d.ts +93 -48
  64. package/dist/auth/index.d.ts.map +1 -1
  65. package/dist/auth/index.js +28 -24
  66. package/dist/auth/index.js.map +1 -1
  67. package/dist/core/index.d.ts +116 -61
  68. package/dist/core/index.d.ts.map +1 -1
  69. package/dist/core/index.js +873 -701
  70. package/dist/core/index.js.map +1 -1
  71. package/dist/demo/{AuthLayout-DN-ClJQk.js → AuthLayout-Brri4A-L.js} +2 -2
  72. package/dist/demo/{AuthLayout-DN-ClJQk.js.map → AuthLayout-Brri4A-L.js.map} +1 -1
  73. package/dist/demo/DemoButton-wiCxZZ_L.js +182 -0
  74. package/dist/demo/DemoButton-wiCxZZ_L.js.map +1 -0
  75. package/dist/demo/DemoControlSelect-D7ILObVg.js +305 -0
  76. package/dist/demo/DemoControlSelect-D7ILObVg.js.map +1 -0
  77. package/dist/demo/DemoDataTable-DZ5Y8pFX.js +362 -0
  78. package/dist/demo/DemoDataTable-DZ5Y8pFX.js.map +1 -0
  79. package/dist/demo/{DemoDialog-DW8QEvD1.js → DemoDialog-CUWdLHim.js} +2 -2
  80. package/dist/demo/{DemoDialog-DW8QEvD1.js.map → DemoDialog-CUWdLHim.js.map} +1 -1
  81. package/dist/demo/{DemoFlex-CAhLUanT.js → DemoFlex-a8OhMMvq.js} +3 -3
  82. package/dist/demo/{DemoFlex-CAhLUanT.js.map → DemoFlex-a8OhMMvq.js.map} +1 -1
  83. package/dist/demo/{DemoHeading-yIFmNjHB.js → DemoHeading-C13OVDfS.js} +3 -3
  84. package/dist/demo/{DemoHeading-yIFmNjHB.js.map → DemoHeading-C13OVDfS.js.map} +1 -1
  85. package/dist/demo/{DemoHome-BSGuBHus.js → DemoHome-D_De3UiT.js} +2 -2
  86. package/dist/demo/{DemoHome-BSGuBHus.js.map → DemoHome-D_De3UiT.js.map} +1 -1
  87. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js → DemoJsonViewer-B50s9aGM.js} +3 -3
  88. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js.map → DemoJsonViewer-B50s9aGM.js.map} +1 -1
  89. package/dist/demo/{DemoLayout-Cy6xjn6P.js → DemoLayout-CHU8WTwO.js} +14 -5
  90. package/dist/demo/DemoLayout-CHU8WTwO.js.map +1 -0
  91. package/dist/demo/{DemoLogin-vqxgTu4P.js → DemoLogin-BBlrWpml.js} +49 -32
  92. package/dist/demo/DemoLogin-BBlrWpml.js.map +1 -0
  93. package/dist/demo/{DemoRegister-YHPvPg77.js → DemoRegister-BuNE3_-f.js} +49 -50
  94. package/dist/demo/DemoRegister-BuNE3_-f.js.map +1 -0
  95. package/dist/demo/{DemoResetPassword-mOW18Zlm.js → DemoResetPassword-D_IjjjOJ.js} +12 -16
  96. package/dist/demo/DemoResetPassword-D_IjjjOJ.js.map +1 -0
  97. package/dist/demo/{DemoSidebar-od7aLjP_.js → DemoSidebar-Giy2HRBD.js} +3 -3
  98. package/dist/demo/{DemoSidebar-od7aLjP_.js.map → DemoSidebar-Giy2HRBD.js.map} +1 -1
  99. package/dist/demo/{DemoText-DU3JeRS0.js → DemoText-ubcw-vog.js} +3 -3
  100. package/dist/demo/{DemoText-DU3JeRS0.js.map → DemoText-ubcw-vog.js.map} +1 -1
  101. package/dist/demo/{DemoToast-CUJEiPRa.js → DemoToast-9die_dYT.js} +2 -2
  102. package/dist/demo/{DemoToast-CUJEiPRa.js.map → DemoToast-9die_dYT.js.map} +1 -1
  103. package/dist/demo/{DemoTypeForm-C1dNkahD.js → DemoTypeForm-D_d6OVKL.js} +8 -4
  104. package/dist/demo/DemoTypeForm-D_d6OVKL.js.map +1 -0
  105. package/dist/demo/DemoVerifyEmail-B43KlF4F.js +34 -0
  106. package/dist/demo/DemoVerifyEmail-B43KlF4F.js.map +1 -0
  107. package/dist/demo/Login-C12N4oGs.js +275 -0
  108. package/dist/demo/Login-C12N4oGs.js.map +1 -0
  109. package/dist/demo/{Profile-BE_Y3co2.js → Profile-DS5q4vOh.js} +31 -27
  110. package/dist/demo/Profile-DS5q4vOh.js.map +1 -0
  111. package/dist/demo/{Register-fXHmBpr3.js → Register-B4hLBeEv.js} +198 -142
  112. package/dist/demo/Register-B4hLBeEv.js.map +1 -0
  113. package/dist/{auth/ResetPassword-DBxt9hKk.js → demo/ResetPassword-D8g9ha1N.js} +7 -7
  114. package/dist/demo/ResetPassword-D8g9ha1N.js.map +1 -0
  115. package/dist/demo/{Showcase-BtEU0pY9.js → Showcase-D6Fxt4X4.js} +64 -65
  116. package/dist/demo/Showcase-D6Fxt4X4.js.map +1 -0
  117. package/dist/{auth/VerifyEmail-Z80Ubajk.js → demo/VerifyEmail-BjDo0cZA.js} +23 -8
  118. package/dist/demo/VerifyEmail-BjDo0cZA.js.map +1 -0
  119. package/dist/demo/{auth-Djd7SKiw.js → auth-ByVTreDl.js} +8 -8
  120. package/dist/demo/{auth-Djd7SKiw.js.map → auth-ByVTreDl.js.map} +1 -1
  121. package/dist/demo/{core-B7LNjM78.js → core-DFgB3yU4.js} +741 -573
  122. package/dist/demo/core-DFgB3yU4.js.map +1 -0
  123. package/dist/demo/index.d.ts +1 -0
  124. package/dist/demo/index.d.ts.map +1 -1
  125. package/dist/demo/index.js +24 -18
  126. package/dist/demo/index.js.map +1 -1
  127. package/package.json +7 -7
  128. package/src/admin/AdminRouter.tsx +24 -1
  129. package/src/admin/components/notifications/AdminNotifications.tsx +519 -0
  130. package/src/admin/components/parameters/ParameterDetails.tsx +12 -270
  131. package/src/admin/components/parameters/ParameterDetailsConfigForm.tsx +238 -0
  132. package/src/admin/components/parameters/ParameterDetailsLoading.tsx +24 -0
  133. package/src/admin/components/parameters/ParameterHistory.tsx +10 -11
  134. package/src/admin/components/parameters/ParameterTree.tsx +28 -184
  135. package/src/admin/components/parameters/ParameterTreeNode.tsx +151 -0
  136. package/src/admin/components/shared/AdminResourceHeader.tsx +2 -25
  137. package/src/admin/components/shared/AdminResourceHeaderMenuItem.tsx +37 -0
  138. package/src/admin/components/shared/AdminResourceTabs.tsx +2 -26
  139. package/src/admin/components/shared/AdminResourceTabsItem.tsx +36 -0
  140. package/src/auth/components/Login.tsx +188 -121
  141. package/src/auth/components/Profile.tsx +1 -22
  142. package/src/auth/components/ProfileField.tsx +39 -0
  143. package/src/auth/components/Register.tsx +215 -158
  144. package/src/auth/components/ResetPassword.tsx +7 -11
  145. package/src/auth/components/VerifyEmail.tsx +35 -10
  146. package/src/auth/components/buttons/UserButton.tsx +19 -21
  147. package/src/auth/index.ts +1 -0
  148. package/src/core/components/Flex.tsx +10 -0
  149. package/src/core/components/buttons/ActionButton.tsx +104 -78
  150. package/src/core/components/data/DetailDrawer.tsx +102 -96
  151. package/src/core/components/data/DetailList.tsx +2 -1
  152. package/src/core/components/layout/Breadcrumb.tsx +3 -6
  153. package/src/core/components/layout/DashboardShell.tsx +18 -4
  154. package/src/core/components/layout/Sidebar.tsx +16 -241
  155. package/src/core/components/layout/SidebarCollapsedItem.tsx +91 -0
  156. package/src/core/components/layout/SidebarItem.tsx +146 -0
  157. package/src/core/components/layout/index.ts +3 -1
  158. package/src/core/form/components/Control.tsx +31 -29
  159. package/src/core/form/components/ControlArray.tsx +13 -39
  160. package/src/core/form/components/ControlDate.tsx +10 -21
  161. package/src/core/form/components/ControlNumber.tsx +4 -33
  162. package/src/core/form/components/ControlQueryBuilder.tsx +12 -175
  163. package/src/core/form/components/ControlQueryBuilderHelp.tsx +165 -0
  164. package/src/core/form/components/ControlSelect.browser.spec.tsx +343 -0
  165. package/src/core/form/components/ControlSelect.tsx +294 -92
  166. package/src/core/form/components/TypeForm.browser.spec.tsx +3 -3
  167. package/src/core/form/components/TypeForm.tsx +5 -2
  168. package/src/core/form/index.ts +8 -1
  169. package/src/core/form/utils/parseInput.ts +7 -3
  170. package/src/core/index.ts +3 -1
  171. package/src/core/json/components/JsonViewer.tsx +103 -319
  172. package/src/core/json/components/JsonViewerCopyButton.tsx +46 -0
  173. package/src/core/json/components/JsonViewerRowNode.tsx +120 -0
  174. package/src/core/json/components/JsonViewerShared.ts +76 -0
  175. package/src/core/styles.css +12 -2
  176. package/src/core/table/components/ColumnPicker.tsx +3 -3
  177. package/src/core/table/components/DataTable.tsx +89 -29
  178. package/src/core/table/components/DataTableFilters.tsx +6 -11
  179. package/src/core/table/components/DataTablePagination.tsx +9 -3
  180. package/src/core/table/components/DataTableToolbar.tsx +7 -3
  181. package/src/core/table/components/FilterPicker.tsx +3 -3
  182. package/src/core/table/interfaces/types.ts +29 -0
  183. package/src/core/utils/icons.tsx +2 -2
  184. package/src/demo/DemoRouter.ts +8 -1
  185. package/src/demo/components/DemoLayout.tsx +12 -2
  186. package/src/demo/components/auth/DemoLogin.tsx +35 -28
  187. package/src/demo/components/auth/DemoRegister.tsx +35 -49
  188. package/src/demo/components/auth/DemoResetPassword.tsx +5 -9
  189. package/src/demo/components/auth/DemoVerifyEmail.tsx +7 -6
  190. package/src/demo/components/core/DemoButton.tsx +123 -103
  191. package/src/demo/components/core/DemoControlSelect.tsx +325 -0
  192. package/src/demo/components/core/DemoDataTable.tsx +255 -237
  193. package/src/demo/components/core/DemoTypeForm.tsx +7 -2
  194. package/src/demo/components/shared/MacWindow.tsx +5 -11
  195. package/src/demo/components/shared/Showcase.tsx +28 -42
  196. package/dist/admin/AdminParameters-BspPeqp_.js.map +0 -1
  197. package/dist/admin/AdminUserLayout-DUbC6-BI.js.map +0 -1
  198. package/dist/admin/Login-DHbYJKwg.js +0 -219
  199. package/dist/admin/Login-DHbYJKwg.js.map +0 -1
  200. package/dist/admin/Profile-B2EcIDB9.js.map +0 -1
  201. package/dist/admin/Register-Z3fxRbUF.js.map +0 -1
  202. package/dist/admin/ResetPassword-_Y1qTTKh.js.map +0 -1
  203. package/dist/admin/VerifyEmail-Bg22bwcC.js.map +0 -1
  204. package/dist/admin/core-BVO_TQxb.js.map +0 -1
  205. package/dist/auth/Login-C7jIqf00.js +0 -219
  206. package/dist/auth/Login-C7jIqf00.js.map +0 -1
  207. package/dist/auth/Profile-BMpXJ0oi.js.map +0 -1
  208. package/dist/auth/Register-2gx8qll-.js.map +0 -1
  209. package/dist/auth/ResetPassword-DBxt9hKk.js.map +0 -1
  210. package/dist/auth/VerifyEmail-Z80Ubajk.js.map +0 -1
  211. package/dist/auth/core-DyfeVr5c.js.map +0 -1
  212. package/dist/demo/DemoButton-CGUyR9eM.js +0 -178
  213. package/dist/demo/DemoButton-CGUyR9eM.js.map +0 -1
  214. package/dist/demo/DemoDataTable-QFG-xXSx.js +0 -358
  215. package/dist/demo/DemoDataTable-QFG-xXSx.js.map +0 -1
  216. package/dist/demo/DemoLayout-Cy6xjn6P.js.map +0 -1
  217. package/dist/demo/DemoLogin-vqxgTu4P.js.map +0 -1
  218. package/dist/demo/DemoRegister-YHPvPg77.js.map +0 -1
  219. package/dist/demo/DemoResetPassword-mOW18Zlm.js.map +0 -1
  220. package/dist/demo/DemoTypeForm-C1dNkahD.js.map +0 -1
  221. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js +0 -30
  222. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js.map +0 -1
  223. package/dist/demo/Login-CoYf_P_F.js +0 -219
  224. package/dist/demo/Login-CoYf_P_F.js.map +0 -1
  225. package/dist/demo/Profile-BE_Y3co2.js.map +0 -1
  226. package/dist/demo/Register-fXHmBpr3.js.map +0 -1
  227. package/dist/demo/ResetPassword-CAPj8MO3.js.map +0 -1
  228. package/dist/demo/Showcase-BtEU0pY9.js.map +0 -1
  229. package/dist/demo/VerifyEmail-DFmdCdYs.js.map +0 -1
  230. package/dist/demo/core-B7LNjM78.js.map +0 -1
  231. package/src/demo/styles.css +0 -0
@@ -1,5 +1,5 @@
1
1
  import { $atom, $context, $inject, $module, Alepha, AlephaError, TypeBoxError, t } from "alepha";
2
- import { AlephaReactForm, FormValidationError, useForm, useFormState } from "alepha/react/form";
2
+ import { AlephaReactForm, FormValidationError, useFieldValue, useForm, useFormState } from "alepha/react/form";
3
3
  import { $head, AlephaReactHead, BrowserHeadProvider } from "alepha/react/head";
4
4
  import { AlephaReactI18n, useI18n } from "alepha/react/i18n";
5
5
  import { $cookie } from "alepha/server/cookies";
@@ -1548,41 +1548,10 @@ function isComponentType(param) {
1548
1548
 
1549
1549
  //#endregion
1550
1550
  //#region ../../src/core/components/buttons/ActionButton.tsx
1551
- const ActionMenuItem = (props) => {
1552
- const { item, index } = props;
1553
- const router = useRouter();
1554
- const action = useAction({ handler: async (e) => {
1555
- await item.onClick?.();
1556
- } }, [item.onClick]);
1557
- if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
1558
- if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
1559
- if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
1560
- trigger: "hover",
1561
- position: "right-start",
1562
- offset: 2,
1563
- children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
1564
- leftSection: item.icon,
1565
- rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
1566
- children: item.label
1567
- }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx(ActionMenuItem, {
1568
- item: child,
1569
- index: childIndex
1570
- }, childIndex)) })]
1571
- }, index);
1572
- const menuItemProps = {};
1573
- if (props.item.onClick) menuItemProps.onClick = action.run;
1574
- else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
1575
- return /* @__PURE__ */ jsx(Menu.Item, {
1576
- leftSection: item.icon ?? (item.active ? /* @__PURE__ */ jsx(IconCheck, { size: ui.sizes.icon.sm }) : /* @__PURE__ */ jsx(Flex$1, { w: ui.sizes.icon.sm })),
1577
- onClick: item.onClick,
1578
- color: item.color,
1579
- ...menuItemProps,
1580
- children: item.label
1581
- }, index);
1582
- };
1583
1551
  const ActionButton = (_props) => {
1584
1552
  const theme = useMantineTheme();
1585
1553
  const props = { ..._props };
1554
+ if (props.variant === "minimal") {}
1586
1555
  const { tooltip, menu, icon, iconSize, ...restProps } = props;
1587
1556
  if (props.intent) {
1588
1557
  if (props.intent === "primary") restProps.color ??= theme.primaryColor;
@@ -1627,6 +1596,7 @@ const ActionButton = (_props) => {
1627
1596
  children: /* @__PURE__ */ jsx(ActionButton, {
1628
1597
  px: "xs",
1629
1598
  ...rest,
1599
+ "aria-label": typeof children === "string" ? children : void 0,
1630
1600
  tooltip,
1631
1601
  menu,
1632
1602
  children: leftSection
@@ -1783,7 +1753,7 @@ const ActionClickButton = ({ preventDefault, ...props }) => {
1783
1753
  * Action for navigation with active state support.
1784
1754
  */
1785
1755
  const ActionNavigationButton = (props) => {
1786
- const { active: options, classNameActive, variantActive, propsActive, routerGoOptions, onClick: propsOnClick, anchor, ...buttonProps } = props;
1756
+ const { active: options, classNameActive, variantActive, propsActive, routerGoOptions, onClick: propsOnClick, anchorProps: buttonAnchorProps, anchor, ...buttonProps } = props;
1787
1757
  const router = useRouter();
1788
1758
  const { isPending, isActive } = useActive(options ? {
1789
1759
  href: props.href,
@@ -1797,11 +1767,11 @@ const ActionNavigationButton = (props) => {
1797
1767
  };
1798
1768
  const className = buttonProps.className || "";
1799
1769
  if (isActive && options !== false && classNameActive) buttonProps.className = `${className} ${classNameActive}`.trim();
1800
- if (props.anchorProps || anchor) return /* @__PURE__ */ jsx(Anchor, {
1770
+ if (buttonAnchorProps || anchor) return /* @__PURE__ */ jsx(Anchor, {
1801
1771
  component: "a",
1802
1772
  ...anchorProps,
1803
1773
  ...buttonProps,
1804
- ...props.anchorProps,
1774
+ ...buttonAnchorProps,
1805
1775
  onClick: combinedOnClick,
1806
1776
  children: props.children
1807
1777
  });
@@ -1824,6 +1794,38 @@ const ActionHrefButton = (props) => {
1824
1794
  children: props.children
1825
1795
  });
1826
1796
  };
1797
+ const ActionMenuItem = (props) => {
1798
+ const { item, index } = props;
1799
+ const router = useRouter();
1800
+ const action = useAction({ handler: async (e) => {
1801
+ await item.onClick?.();
1802
+ } }, [item.onClick]);
1803
+ if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
1804
+ if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
1805
+ if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
1806
+ trigger: "hover",
1807
+ position: "right-start",
1808
+ offset: 2,
1809
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
1810
+ leftSection: item.icon,
1811
+ rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
1812
+ children: item.label
1813
+ }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx(ActionMenuItem, {
1814
+ item: child,
1815
+ index: childIndex
1816
+ }, childIndex)) })]
1817
+ }, index);
1818
+ const menuItemProps = {};
1819
+ if (props.item.onClick) menuItemProps.onClick = action.run;
1820
+ else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
1821
+ return /* @__PURE__ */ jsx(Menu.Item, {
1822
+ leftSection: item.icon ?? (item.active ? /* @__PURE__ */ jsx(IconCheck, { size: ui.sizes.icon.sm }) : /* @__PURE__ */ jsx(Flex$1, { w: ui.sizes.icon.sm })),
1823
+ onClick: item.onClick,
1824
+ color: item.color,
1825
+ ...menuItemProps,
1826
+ children: item.label
1827
+ }, index);
1828
+ };
1827
1829
 
1828
1830
  //#endregion
1829
1831
  //#region ../../src/core/components/buttons/BurgerButton.tsx
@@ -2234,99 +2236,103 @@ const ToggleSidebarButton = (props) => {
2234
2236
 
2235
2237
  //#endregion
2236
2238
  //#region ../../src/core/components/data/DetailDrawer.tsx
2237
- const DetailDrawer = ({ opened, onClose, title, subtitle, status, actions, tabs, children, loading, size = "xl", defaultTab }) => /* @__PURE__ */ jsxs(Drawer, {
2238
- opened,
2239
- onClose,
2240
- position: "right",
2241
- size,
2242
- withCloseButton: false,
2243
- padding: 0,
2244
- children: [/* @__PURE__ */ jsxs(Flex$1, {
2245
- p: "md",
2246
- justify: "space-between",
2247
- align: "flex-start",
2248
- style: { borderBottom: "1px solid var(--mantine-color-default-border)" },
2239
+ const DetailDrawer = (props) => {
2240
+ const { opened, onClose, title, subtitle, status, actions, tabs, children, loading, size = "xl", defaultTab } = props;
2241
+ return /* @__PURE__ */ jsxs(Drawer, {
2242
+ opened,
2243
+ onClose,
2244
+ position: "right",
2245
+ size,
2246
+ withCloseButton: false,
2247
+ padding: 0,
2249
2248
  children: [/* @__PURE__ */ jsxs(Flex$1, {
2250
- direction: "column",
2251
- gap: 2,
2252
- style: {
2253
- minWidth: 0,
2254
- flex: 1
2255
- },
2249
+ p: "md",
2250
+ justify: "space-between",
2251
+ align: "flex-start",
2252
+ style: { borderBottom: "1px solid var(--mantine-color-default-border)" },
2256
2253
  children: [/* @__PURE__ */ jsxs(Flex$1, {
2254
+ direction: "column",
2255
+ gap: 2,
2256
+ style: {
2257
+ minWidth: 0,
2258
+ flex: 1
2259
+ },
2260
+ children: [/* @__PURE__ */ jsxs(Flex$1, {
2261
+ gap: "xs",
2262
+ align: "center",
2263
+ children: [status && /* @__PURE__ */ jsx(Flex$1, {
2264
+ w: 8,
2265
+ h: 8,
2266
+ style: {
2267
+ borderRadius: "50%",
2268
+ backgroundColor: status.active ? "var(--mantine-color-green-6)" : "var(--mantine-color-red-6)",
2269
+ flexShrink: 0
2270
+ }
2271
+ }), /* @__PURE__ */ jsx(Text$1, {
2272
+ size: "lg",
2273
+ fw: 600,
2274
+ truncate: true,
2275
+ children: title
2276
+ })]
2277
+ }), subtitle && /* @__PURE__ */ jsx(Text$1, {
2278
+ size: "sm",
2279
+ c: "dimmed",
2280
+ truncate: true,
2281
+ children: subtitle
2282
+ })]
2283
+ }), /* @__PURE__ */ jsxs(Flex$1, {
2257
2284
  gap: "xs",
2258
2285
  align: "center",
2259
- children: [status && /* @__PURE__ */ jsx(Flex$1, {
2260
- w: 8,
2261
- h: 8,
2262
- style: {
2263
- borderRadius: "50%",
2264
- backgroundColor: status.active ? "var(--mantine-color-green-6)" : "var(--mantine-color-red-6)",
2265
- flexShrink: 0
2266
- }
2267
- }), /* @__PURE__ */ jsx(Text$1, {
2268
- size: "lg",
2269
- fw: 600,
2270
- truncate: true,
2271
- children: title
2286
+ style: { flexShrink: 0 },
2287
+ children: [actions && actions.length > 0 && /* @__PURE__ */ jsx(ActionButton, {
2288
+ variant: "default",
2289
+ size: "xs",
2290
+ menu: {
2291
+ items: actions,
2292
+ position: "bottom-end",
2293
+ width: 200
2294
+ },
2295
+ children: "Actions"
2296
+ }), /* @__PURE__ */ jsx(ActionButton, {
2297
+ variant: "subtle",
2298
+ size: "xs",
2299
+ c: "dimmed",
2300
+ onClick: onClose,
2301
+ children: "Close"
2272
2302
  })]
2273
- }), subtitle && /* @__PURE__ */ jsx(Text$1, {
2274
- size: "sm",
2275
- c: "dimmed",
2276
- truncate: true,
2277
- children: subtitle
2278
2303
  })]
2279
- }), /* @__PURE__ */ jsxs(Flex$1, {
2280
- gap: "xs",
2304
+ }), loading ? /* @__PURE__ */ jsx(Flex$1, {
2305
+ flex: 1,
2306
+ justify: "center",
2281
2307
  align: "center",
2282
- style: { flexShrink: 0 },
2283
- children: [actions && actions.length > 0 && /* @__PURE__ */ jsx(ActionButton, {
2284
- variant: "default",
2285
- size: "xs",
2286
- menu: {
2287
- items: actions,
2288
- position: "bottom-end",
2289
- width: 200
2290
- },
2291
- children: "Actions"
2292
- }), /* @__PURE__ */ jsx(ActionButton, {
2293
- variant: "subtle",
2294
- size: "xs",
2295
- c: "dimmed",
2296
- onClick: onClose,
2297
- children: "Close"
2298
- })]
2299
- })]
2300
- }), loading ? /* @__PURE__ */ jsx(Flex$1, {
2301
- flex: 1,
2302
- justify: "center",
2303
- align: "center",
2304
- py: "xl",
2305
- children: /* @__PURE__ */ jsx(Loader, {})
2306
- }) : tabs && tabs.length > 0 ? /* @__PURE__ */ jsxs(Tabs, {
2307
- defaultValue: defaultTab || tabs[0].value,
2308
- children: [/* @__PURE__ */ jsx(Tabs.List, {
2309
- px: "md",
2310
- children: tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Tab, {
2308
+ py: "xl",
2309
+ children: /* @__PURE__ */ jsx(Loader, {})
2310
+ }) : tabs && tabs.length > 0 ? /* @__PURE__ */ jsxs(Tabs, {
2311
+ defaultValue: defaultTab || tabs[0].value,
2312
+ children: [/* @__PURE__ */ jsx(Tabs.List, {
2313
+ px: "md",
2314
+ children: tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Tab, {
2315
+ value: tab.value,
2316
+ leftSection: tab.icon ? /* @__PURE__ */ jsx(tab.icon, { size: 14 }) : void 0,
2317
+ children: tab.label
2318
+ }, tab.value))
2319
+ }), tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Panel, {
2311
2320
  value: tab.value,
2312
- leftSection: tab.icon ? /* @__PURE__ */ jsx(tab.icon, { size: 14 }) : void 0,
2313
- children: tab.label
2314
- }, tab.value))
2315
- }), tabs.map((tab) => /* @__PURE__ */ jsx(Tabs.Panel, {
2316
- value: tab.value,
2321
+ p: "md",
2322
+ children: tab.content
2323
+ }, tab.value))]
2324
+ }) : /* @__PURE__ */ jsx(Flex$1, {
2325
+ direction: "column",
2317
2326
  p: "md",
2318
- children: tab.content
2319
- }, tab.value))]
2320
- }) : /* @__PURE__ */ jsx(Flex$1, {
2321
- direction: "column",
2322
- p: "md",
2323
- children
2324
- })]
2325
- });
2327
+ children
2328
+ })]
2329
+ });
2330
+ };
2326
2331
 
2327
2332
  //#endregion
2328
2333
  //#region ../../src/core/components/data/DetailList.tsx
2329
- const DetailList = ({ items, columns = 1 }) => {
2334
+ const DetailList = (props) => {
2335
+ const { items, columns = 1 } = props;
2330
2336
  return /* @__PURE__ */ jsx(Grid, {
2331
2337
  gutter: "xs",
2332
2338
  children: items.filter((item) => !item.hidden).map((item) => /* @__PURE__ */ jsx(Grid.Col, {
@@ -2405,7 +2411,7 @@ const StatCards = ({ items }) => /* @__PURE__ */ jsx(Flex$1, {
2405
2411
  //#endregion
2406
2412
  //#region ../../src/core/components/Flex.tsx
2407
2413
  const Flex = forwardRef((props, ref) => {
2408
- const { fill, center, centerX, centerY, col, ground, surface, elevated, rounded, bordered, borderedTop, borderedBottom, shadowed, ...rest } = props;
2414
+ const { fill, center, centerX, centerY, col, ground, surface, elevated, rounded, bordered, borderedTop, borderedBottom, shadowed, overflow, ...rest } = props;
2409
2415
  if (fill) rest.flex ??= 1;
2410
2416
  if (col) rest.direction ??= "column";
2411
2417
  if (center) {
@@ -2428,6 +2434,7 @@ const Flex = forwardRef((props, ref) => {
2428
2434
  ...rest.style ?? {}
2429
2435
  };
2430
2436
  if (shadowed) rest.className = `${rest.className ?? ""} shadow-${shadowed === true ? "md" : shadowed}`.trim();
2437
+ if (overflow) rest.className = `${rest.className ?? ""} overflow-auto`.trim();
2431
2438
  return /* @__PURE__ */ jsx(Flex$1, {
2432
2439
  ref,
2433
2440
  ...rest
@@ -2565,7 +2572,8 @@ const AppBar = (props) => {
2565
2572
  * Pages should define a `label` in their `$page()` options for best results.
2566
2573
  * Falls back to the page name converted to Title Case.
2567
2574
  */
2568
- const Breadcrumb = ({ home = "Home", separator, size = "sm", ...groupProps }) => {
2575
+ const Breadcrumb = (props) => {
2576
+ const { home = "Home", separator, size = "sm", ...groupProps } = props;
2569
2577
  const state = useRouterState();
2570
2578
  const router = useRouter();
2571
2579
  const crumbs = [];
@@ -2621,67 +2629,231 @@ const Container = forwardRef((props, ref) => {
2621
2629
  Container.displayName = "Container";
2622
2630
 
2623
2631
  //#endregion
2624
- //#region ../../src/core/components/layout/Sidebar.tsx
2625
- const Sidebar = (props) => {
2632
+ //#region ../../src/core/helpers/renderIcon.tsx
2633
+ const renderIcon = (icon, size) => {
2634
+ if (!icon) return null;
2635
+ if (isValidElement(icon)) return icon;
2636
+ if (isComponentType(icon)) return /* @__PURE__ */ jsx(icon, { size: size ?? ui.sizes.icon.md });
2637
+ return icon;
2638
+ };
2639
+
2640
+ //#endregion
2641
+ //#region ../../src/core/components/Text.tsx
2642
+ const INTENT_COLORS = {
2643
+ primary: "blue",
2644
+ info: "cyan",
2645
+ success: "green",
2646
+ warning: "yellow",
2647
+ danger: "red"
2648
+ };
2649
+ const Text = forwardRef((props, ref) => {
2650
+ const { intent, bold, italic, light, muted, small, uppercase, capitalize, center, monospace, title, ...rest } = props;
2651
+ if (intent) rest.c ??= INTENT_COLORS[intent];
2652
+ if (bold) rest.fw ??= 700;
2653
+ if (light) rest.fw ??= 300;
2654
+ if (italic) rest.fs ??= "italic";
2655
+ if (muted) rest.c ??= "dimmed";
2656
+ if (small) rest.size ??= "xs";
2657
+ if (uppercase) rest.tt ??= "uppercase";
2658
+ if (capitalize) rest.tt ??= "capitalize";
2659
+ if (center) rest.ta ??= "center";
2660
+ if (monospace) rest.ff ??= "monospace";
2661
+ if (title) rest.size ??= "xl";
2662
+ return /* @__PURE__ */ jsx(Text$1, {
2663
+ ref,
2664
+ ...rest
2665
+ });
2666
+ });
2667
+ Text.displayName = "Text";
2668
+
2669
+ //#endregion
2670
+ //#region ../../src/core/components/layout/SidebarCollapsedItem.tsx
2671
+ const SidebarCollapsedItem = (props) => {
2626
2672
  const router = useRouter();
2627
- const { onItemClick } = props;
2628
- const divider = (key, fill, collapsed) => {
2629
- return /* @__PURE__ */ jsx(Flex, {
2630
- h: 1,
2631
- bg: "var(--mantine-color-default-border)",
2632
- my: "xs",
2633
- mx: fill ? "calc(-1 * var(--mantine-spacing-md))" : collapsed ? 0 : "sm"
2634
- }, key);
2673
+ const handleItemClick = () => {
2674
+ props.onItemClick?.(props.item);
2675
+ props.item.onClick?.();
2635
2676
  };
2636
- const renderNode = (item, key, collapsed) => {
2637
- if ("type" in item) {
2638
- if (item.type === "spacer") {
2639
- if (collapsed) return null;
2640
- return /* @__PURE__ */ jsx(Flex, { h: 16 }, key);
2641
- }
2642
- if (item.type === "divider") return divider(key, item.fill, collapsed);
2643
- if (item.type === "search") return /* @__PURE__ */ jsx(Flex, {
2644
- mb: "xs",
2645
- w: "100%",
2646
- justify: "center",
2647
- pos: "relative",
2648
- children: /* @__PURE__ */ jsx(OmnibarButton, { collapsed })
2649
- }, key);
2650
- if (item.type === "toggle") return /* @__PURE__ */ jsx(ToggleSidebarButton, {}, key);
2651
- if (item.type === "section") {
2652
- if (item.children && item.children.length > 0) {
2653
- if (!item.children.some((child) => !("can" in child) || !child.can || child.can())) return null;
2654
- }
2655
- if (collapsed) return /* @__PURE__ */ jsx(Fragment$1, { children: item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed)) }, key);
2656
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Flex, {
2657
- mt: "md",
2658
- align: "center",
2659
- gap: "xs",
2660
- children: [renderIcon(item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Text, {
2661
- size: "xs",
2662
- c: "dimmed",
2663
- tt: "uppercase",
2664
- fw: "bold",
2665
- children: item.label
2666
- })]
2667
- }), item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed))] }, key);
2677
+ const hasChildren = props.item.children && props.item.children.length > 0;
2678
+ const menu = hasChildren ? {
2679
+ on: "hover",
2680
+ position: "right",
2681
+ menuProps: {
2682
+ arrowPosition: "center",
2683
+ arrowSize: 10,
2684
+ withArrow: true
2685
+ },
2686
+ items: [{
2687
+ type: "label",
2688
+ label: props.item.label
2689
+ }, ...props.item.children.filter((child) => !child.can || child.can()).map((child) => ({
2690
+ label: child.label,
2691
+ icon: renderIcon(child.icon, ui.sizes.icon.sm),
2692
+ href: child.href,
2693
+ active: child.href ? router.isActive(child.href, { startWith: child.activeStartsWith }) : void 0
2694
+ }))]
2695
+ } : void 0;
2696
+ return /* @__PURE__ */ jsx(Flex, {
2697
+ w: "100%",
2698
+ justify: "center",
2699
+ pos: "relative",
2700
+ children: /* @__PURE__ */ jsx(ActionButton, {
2701
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (props.level === 0 ? "sm" : "xs"),
2702
+ bd: 0,
2703
+ variant: "default",
2704
+ propsActive: { variant: "outline" },
2705
+ tooltip: hasChildren ? void 0 : {
2706
+ label: props.item.label,
2707
+ position: "right"
2708
+ },
2709
+ onClick: hasChildren ? void 0 : handleItemClick,
2710
+ icon: renderIcon(props.item.icon, ui.sizes.icon.sm) ?? /* @__PURE__ */ jsx(IconSquareRounded, { size: ui.sizes.icon.sm }),
2711
+ href: hasChildren ? void 0 : props.item.href,
2712
+ target: hasChildren ? void 0 : props.item.target,
2713
+ menu,
2714
+ ...props.item.actionProps
2715
+ })
2716
+ });
2717
+ };
2718
+
2719
+ //#endregion
2720
+ //#region ../../src/core/components/layout/SidebarItem.tsx
2721
+ const SidebarItem = (props) => {
2722
+ const maxLevel = 2;
2723
+ const router = useRouter();
2724
+ const isActive = useCallback((item) => {
2725
+ if (!item.children) return false;
2726
+ for (const child of item.children) {
2727
+ if (child.href) {
2728
+ if (router.isActive(child.href)) return true;
2668
2729
  }
2730
+ if (isActive(child)) return true;
2669
2731
  }
2670
- if ("element" in item) return /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, key);
2671
- if (item.can && !item.can()) return null;
2672
- if (item.children && item.children.length > 0) {
2673
- if (!item.children.some((child) => !child.can || child.can())) return null;
2674
- }
2675
- if (collapsed) return /* @__PURE__ */ jsx(SidebarCollapsedItem, {
2676
- item,
2732
+ return false;
2733
+ }, []);
2734
+ const [isOpen, setIsOpen] = useState(isActive(props.item));
2735
+ useEvents({ "react:transition:end": () => {
2736
+ if (isActive(props.item)) setIsOpen(true);
2737
+ } }, []);
2738
+ if (props.level > maxLevel) return null;
2739
+ const handleItemClick = (e) => {
2740
+ if (!props.item.target) e.preventDefault();
2741
+ if (props.item.children && props.item.children.length > 0) setIsOpen(!isOpen);
2742
+ else {
2743
+ props.onItemClick?.(props.item);
2744
+ props.item.onClick?.();
2745
+ }
2746
+ };
2747
+ return /* @__PURE__ */ jsxs(Flex, {
2748
+ direction: "column",
2749
+ ps: props.level === 0 ? 0 : 32,
2750
+ pos: "relative",
2751
+ children: [/* @__PURE__ */ jsx(ActionButton, {
2752
+ w: "100%",
2753
+ justify: "space-between",
2754
+ href: props.item.href,
2755
+ target: props.item.target,
2756
+ size: props.item.theme?.size ?? props.theme.button?.size ?? (props.level === 0 ? "sm" : "xs"),
2757
+ bd: 0,
2758
+ fw: "normal",
2759
+ variant: "default",
2760
+ propsActive: { variant: "outline" },
2761
+ radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
2762
+ onClick: handleItemClick,
2763
+ leftSection: /* @__PURE__ */ jsxs(Flex, {
2764
+ w: "100%",
2765
+ align: "center",
2766
+ gap: "sm",
2767
+ children: [renderIcon(props.item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Flex, {
2768
+ direction: "column",
2769
+ children: /* @__PURE__ */ jsx(Flex, { children: props.item.label })
2770
+ })]
2771
+ }),
2772
+ rightSection: props.item.children ? /* @__PURE__ */ jsx(Flex, { children: isOpen ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }) }) : props.item.rightSection,
2773
+ ...props.item.actionProps
2774
+ }), props.item.children && isOpen && /* @__PURE__ */ jsxs(Flex, {
2775
+ direction: "column",
2776
+ "data-parent-level": props.level,
2777
+ gap: 2,
2778
+ py: 2,
2779
+ children: [/* @__PURE__ */ jsx(Flex, { style: {
2780
+ position: "absolute",
2781
+ width: 1,
2782
+ background: "linear-gradient(to bottom, transparent, var(--mantine-color-default-border), transparent)",
2783
+ top: 48,
2784
+ left: 20 + 32 * props.level,
2785
+ bottom: 16
2786
+ } }), props.item.children.filter((child) => !child.can || child.can()).map((child, index) => /* @__PURE__ */ jsx(SidebarItem, {
2787
+ item: child,
2788
+ level: props.level + 1,
2789
+ onItemClick: props.onItemClick,
2790
+ theme: props.theme
2791
+ }, index))]
2792
+ })]
2793
+ });
2794
+ };
2795
+
2796
+ //#endregion
2797
+ //#region ../../src/core/components/layout/Sidebar.tsx
2798
+ const Sidebar = (props) => {
2799
+ const router = useRouter();
2800
+ const divider = (key, fill, collapsed) => {
2801
+ return /* @__PURE__ */ jsx(Flex, {
2802
+ h: 1,
2803
+ bg: "var(--mantine-color-default-border)",
2804
+ my: "xs",
2805
+ mx: fill ? "calc(-1 * var(--mantine-spacing-md))" : collapsed ? 0 : "sm"
2806
+ }, key);
2807
+ };
2808
+ const renderNode = (item, key, collapsed) => {
2809
+ if ("type" in item) {
2810
+ if (item.type === "spacer") {
2811
+ if (collapsed) return null;
2812
+ return /* @__PURE__ */ jsx(Flex, { h: 16 }, key);
2813
+ }
2814
+ if (item.type === "divider") return divider(key, item.fill, collapsed);
2815
+ if (item.type === "search") return /* @__PURE__ */ jsx(Flex, {
2816
+ mb: "xs",
2817
+ w: "100%",
2818
+ justify: "center",
2819
+ pos: "relative",
2820
+ children: /* @__PURE__ */ jsx(OmnibarButton, { collapsed })
2821
+ }, key);
2822
+ if (item.type === "toggle") return /* @__PURE__ */ jsx(ToggleSidebarButton, {}, key);
2823
+ if (item.type === "section") {
2824
+ if (item.children && item.children.length > 0) {
2825
+ if (!item.children.some((child) => !("can" in child) || !child.can || child.can())) return null;
2826
+ }
2827
+ if (collapsed) return /* @__PURE__ */ jsx(Fragment$1, { children: item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed)) }, key);
2828
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Flex, {
2829
+ mt: "md",
2830
+ align: "center",
2831
+ gap: "xs",
2832
+ children: [renderIcon(item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Text, {
2833
+ size: "xs",
2834
+ c: "dimmed",
2835
+ tt: "uppercase",
2836
+ fw: "bold",
2837
+ children: item.label
2838
+ })]
2839
+ }), item.children?.map((child, index) => renderNode(child, `s${key}-${index}`, collapsed))] }, key);
2840
+ }
2841
+ }
2842
+ if ("element" in item) return /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, key);
2843
+ if (item.can && !item.can()) return null;
2844
+ if (item.children && item.children.length > 0) {
2845
+ if (!item.children.some((child) => !child.can || child.can())) return null;
2846
+ }
2847
+ if (collapsed) return /* @__PURE__ */ jsx(SidebarCollapsedItem, {
2848
+ item,
2677
2849
  level: 0,
2678
- onItemClick,
2850
+ onItemClick: props.onItemClick,
2679
2851
  theme: props.theme ?? {}
2680
2852
  }, key);
2681
2853
  return /* @__PURE__ */ jsx(SidebarItem, {
2682
2854
  item,
2683
2855
  level: 0,
2684
- onItemClick,
2856
+ onItemClick: props.onItemClick,
2685
2857
  theme: props.theme ?? {}
2686
2858
  }, key);
2687
2859
  };
@@ -2744,129 +2916,6 @@ const Sidebar = (props) => {
2744
2916
  })] });
2745
2917
  return renderSidebar(false);
2746
2918
  };
2747
- const SidebarItem = (props) => {
2748
- const { item, level } = props;
2749
- const maxLevel = 2;
2750
- const router = useRouter();
2751
- const isActive = useCallback((item) => {
2752
- if (!item.children) return false;
2753
- for (const child of item.children) {
2754
- if (child.href) {
2755
- if (router.isActive(child.href)) return true;
2756
- }
2757
- if (isActive(child)) return true;
2758
- }
2759
- return false;
2760
- }, []);
2761
- const [isOpen, setIsOpen] = useState(isActive(item));
2762
- useEvents({ "react:transition:end": () => {
2763
- if (isActive(item)) setIsOpen(true);
2764
- } }, []);
2765
- if (level > maxLevel) return null;
2766
- const handleItemClick = (e) => {
2767
- if (!props.item.target) e.preventDefault();
2768
- if (item.children && item.children.length > 0) setIsOpen(!isOpen);
2769
- else {
2770
- props.onItemClick?.(item);
2771
- item.onClick?.();
2772
- }
2773
- };
2774
- return /* @__PURE__ */ jsxs(Flex, {
2775
- direction: "column",
2776
- ps: level === 0 ? 0 : 32,
2777
- pos: "relative",
2778
- children: [/* @__PURE__ */ jsx(ActionButton, {
2779
- w: "100%",
2780
- justify: "space-between",
2781
- href: props.item.href,
2782
- target: props.item.target,
2783
- size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
2784
- bd: 0,
2785
- fw: "normal",
2786
- variant: "default",
2787
- propsActive: { variant: "outline" },
2788
- radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
2789
- onClick: handleItemClick,
2790
- leftSection: /* @__PURE__ */ jsxs(Flex, {
2791
- w: "100%",
2792
- align: "center",
2793
- gap: "sm",
2794
- children: [renderIcon(item.icon, ui.sizes.icon.sm), /* @__PURE__ */ jsx(Flex, {
2795
- direction: "column",
2796
- children: /* @__PURE__ */ jsx(Flex, { children: item.label })
2797
- })]
2798
- }),
2799
- rightSection: item.children ? /* @__PURE__ */ jsx(Flex, { children: isOpen ? /* @__PURE__ */ jsx(IconChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }) }) : props.item.rightSection,
2800
- ...props.item.actionProps
2801
- }), item.children && isOpen && /* @__PURE__ */ jsxs(Flex, {
2802
- direction: "column",
2803
- "data-parent-level": level,
2804
- gap: 2,
2805
- py: 2,
2806
- children: [/* @__PURE__ */ jsx(Flex, { style: {
2807
- position: "absolute",
2808
- width: 1,
2809
- background: "linear-gradient(to bottom, transparent, var(--mantine-color-default-border), transparent)",
2810
- top: 48,
2811
- left: 20 + 32 * level,
2812
- bottom: 16
2813
- } }), item.children.filter((child) => !child.can || child.can()).map((child, index) => /* @__PURE__ */ jsx(SidebarItem, {
2814
- item: child,
2815
- level: level + 1,
2816
- onItemClick: props.onItemClick,
2817
- theme: props.theme
2818
- }, index))]
2819
- })]
2820
- });
2821
- };
2822
- const SidebarCollapsedItem = (props) => {
2823
- const { item, level } = props;
2824
- const router = useRouter();
2825
- const handleItemClick = () => {
2826
- props.onItemClick?.(item);
2827
- item.onClick?.();
2828
- };
2829
- const hasChildren = item.children && item.children.length > 0;
2830
- const menu = hasChildren ? {
2831
- on: "hover",
2832
- position: "right",
2833
- menuProps: {
2834
- arrowPosition: "center",
2835
- arrowSize: 10,
2836
- withArrow: true
2837
- },
2838
- items: [{
2839
- type: "label",
2840
- label: item.label
2841
- }, ...item.children.filter((child) => !child.can || child.can()).map((child) => ({
2842
- label: child.label,
2843
- icon: renderIcon(child.icon, ui.sizes.icon.sm),
2844
- href: child.href,
2845
- active: child.href ? router.isActive(child.href, { startWith: child.activeStartsWith }) : void 0
2846
- }))]
2847
- } : void 0;
2848
- return /* @__PURE__ */ jsx(Flex, {
2849
- w: "100%",
2850
- justify: "center",
2851
- pos: "relative",
2852
- children: /* @__PURE__ */ jsx(ActionButton, {
2853
- size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
2854
- bd: 0,
2855
- variant: "default",
2856
- propsActive: { variant: "outline" },
2857
- tooltip: hasChildren ? void 0 : {
2858
- label: item.label,
2859
- position: "right"
2860
- },
2861
- onClick: hasChildren ? void 0 : handleItemClick,
2862
- icon: renderIcon(item.icon, ui.sizes.icon.sm) ?? /* @__PURE__ */ jsx(IconSquareRounded, { size: ui.sizes.icon.sm }),
2863
- href: hasChildren ? void 0 : props.item.href,
2864
- target: hasChildren ? void 0 : props.item.target,
2865
- menu,
2866
- ...props.item.actionProps
2867
- })
2868
- });
2869
- };
2870
2919
 
2871
2920
  //#endregion
2872
2921
  //#region ../../src/core/components/layout/DashboardShell.tsx
@@ -2907,6 +2956,8 @@ const DashboardShell = (props) => {
2907
2956
  const fHeight = props.footerHeight ?? 24;
2908
2957
  const headerHeight = hasAppBar ? hHeight : 0;
2909
2958
  const footerHeight = footerElement ? fHeight : 0;
2959
+ const navbarWidth = collapsed ? collapsedWidth : expandedWidth;
2960
+ const mainContent = props.children ?? /* @__PURE__ */ jsx(NestedView, {});
2910
2961
  return /* @__PURE__ */ jsxs(AppShell, {
2911
2962
  layout: "alt",
2912
2963
  w: "100%",
@@ -2914,7 +2965,7 @@ const DashboardShell = (props) => {
2914
2965
  flex: 1,
2915
2966
  header: hasAppBar ? { height: hHeight } : void 0,
2916
2967
  navbar: hasSidebar ? {
2917
- width: { base: collapsed ? collapsedWidth : expandedWidth },
2968
+ width: { base: navbarWidth },
2918
2969
  breakpoint: "md",
2919
2970
  collapsed: { mobile: sidebar.closed }
2920
2971
  } : void 0,
@@ -2951,8 +3002,12 @@ const DashboardShell = (props) => {
2951
3002
  display: "flex",
2952
3003
  bg: "var(--alepha-ground)",
2953
3004
  pos: "relative",
3005
+ h: props.fill ? "100%" : "inherit",
2954
3006
  ...props.appShellMainProps,
2955
- children: props.children ?? /* @__PURE__ */ jsx(NestedView, {})
3007
+ children: props.container ? /* @__PURE__ */ jsx(Container, {
3008
+ ...typeof props.container === "boolean" ? {} : props.container,
3009
+ children: mainContent
3010
+ }) : mainContent
2956
3011
  }),
2957
3012
  footerElement && /* @__PURE__ */ jsx(AppShell.Footer, {
2958
3013
  ...props.appShellFooterProps,
@@ -2962,41 +3017,12 @@ const DashboardShell = (props) => {
2962
3017
  });
2963
3018
  };
2964
3019
 
2965
- //#endregion
2966
- //#region ../../src/core/components/Text.tsx
2967
- const INTENT_COLORS = {
2968
- primary: "blue",
2969
- info: "cyan",
2970
- success: "green",
2971
- warning: "yellow",
2972
- danger: "red"
2973
- };
2974
- const Text = forwardRef((props, ref) => {
2975
- const { intent, bold, italic, light, muted, small, uppercase, capitalize, center, monospace, title, ...rest } = props;
2976
- if (intent) rest.c ??= INTENT_COLORS[intent];
2977
- if (bold) rest.fw ??= 700;
2978
- if (light) rest.fw ??= 300;
2979
- if (italic) rest.fs ??= "italic";
2980
- if (muted) rest.c ??= "dimmed";
2981
- if (small) rest.size ??= "xs";
2982
- if (uppercase) rest.tt ??= "uppercase";
2983
- if (capitalize) rest.tt ??= "capitalize";
2984
- if (center) rest.ta ??= "center";
2985
- if (monospace) rest.ff ??= "monospace";
2986
- if (title) rest.size ??= "xl";
2987
- return /* @__PURE__ */ jsx(Text$1, {
2988
- ref,
2989
- ...rest
2990
- });
2991
- });
2992
- Text.displayName = "Text";
2993
-
2994
3020
  //#endregion
2995
3021
  //#region ../../src/core/form/utils/parseInput.ts
2996
3022
  const parseInput = (props, form) => {
2997
3023
  const disabled = false;
2998
3024
  const id = props.input.props.id;
2999
- 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);
3025
+ const label = props.label ?? ("title" in props.input.schema && typeof props.input.schema.title === "string" ? props.input.schema.title : void 0) ?? prettyName(props.input.path);
3000
3026
  const description = props.description ?? ("description" in props.input.schema && typeof props.input.schema.description === "string" ? props.input.schema.description : void 0);
3001
3027
  const error = form.error && form.error instanceof TypeBoxError ? form.error.value.message : void 0;
3002
3028
  const icon = !props.icon ? getDefaultIcon({
@@ -3004,17 +3030,20 @@ const parseInput = (props, form) => {
3004
3030
  format: props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0,
3005
3031
  name: props.input.props.name,
3006
3032
  isEnum: props.input.schema && "enum" in props.input.schema && Boolean(props.input.schema.enum),
3007
- isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array"
3008
- }) : isValidElement(props.icon) ? props.icon : createElement(props.icon, { size: ui.sizes.icon.md });
3033
+ isArray: props.input.schema && "type" in props.input.schema && props.input.schema.type === "array",
3034
+ size: props.size
3035
+ }) : isValidElement(props.icon) ? props.icon : createElement(props.icon, { size: ui.sizes.icon.sm });
3009
3036
  const format = props.input.schema && "format" in props.input.schema && typeof props.input.schema.format === "string" ? props.input.schema.format : void 0;
3010
3037
  const required = props.input.required;
3011
3038
  const schema = props.input.schema;
3039
+ const testId = props.input.props?.["data-testid"];
3012
3040
  const inputProps = {
3013
3041
  label,
3014
3042
  description,
3015
3043
  error,
3016
3044
  required,
3017
- disabled
3045
+ disabled,
3046
+ ...testId ? { "data-testid": testId } : {}
3018
3047
  };
3019
3048
  if ("minLength" in schema && typeof schema.minLength === "number") inputProps.minLength = schema.minLength;
3020
3049
  if ("maxLength" in schema && typeof schema.maxLength === "number") inputProps.maxLength = schema.maxLength;
@@ -3039,8 +3068,8 @@ const useArrayItems = (input) => {
3039
3068
  const alepha = useAlepha();
3040
3069
  const keyCounter = useRef(0);
3041
3070
  const [items, setItemsState] = useState(() => {
3042
- const defaultValue = input?.props?.defaultValue;
3043
- if (Array.isArray(defaultValue)) return defaultValue.map((value) => ({
3071
+ const initial = input?.initialValue;
3072
+ if (Array.isArray(initial)) return initial.map((value) => ({
3044
3073
  key: keyCounter.current++,
3045
3074
  value
3046
3075
  }));
@@ -3066,22 +3095,9 @@ const useArrayItems = (input) => {
3066
3095
  if (!input?.form) return;
3067
3096
  const formId = input.form.id;
3068
3097
  const fieldPath = input.path;
3069
- const listeners = [alepha.events.on("form:reset", (event) => {
3070
- if (event.id === formId) {
3071
- const defaultValue = input.props?.defaultValue;
3072
- keyCounter.current = 0;
3073
- if (Array.isArray(defaultValue)) setItemsState(defaultValue.map((value) => ({
3074
- key: keyCounter.current++,
3075
- value
3076
- })));
3077
- else setItemsState([]);
3078
- }
3079
- }), alepha.events.on("form:change", (event) => {
3098
+ return alepha.events.on("form:change", (event) => {
3080
3099
  if (event.id === formId && event.path === fieldPath) syncFromFormValue(event.value);
3081
- })];
3082
- return () => {
3083
- for (const unsub of listeners) unsub();
3084
- };
3100
+ });
3085
3101
  }, [
3086
3102
  alepha,
3087
3103
  input,
@@ -3106,10 +3122,10 @@ const createArrayItemInput = (parentInput, itemSchema, index, _itemKey, value, o
3106
3122
  path: `${parentInput.path}/${index}`,
3107
3123
  required: false,
3108
3124
  form: parentInput.form,
3125
+ initialValue: value,
3109
3126
  props: {
3110
3127
  id: `${parentInput.props.id}-${index}`,
3111
- name: `${parentInput.props.name}[${index}]`,
3112
- defaultValue: value
3128
+ name: `${parentInput.props.name}[${index}]`
3113
3129
  },
3114
3130
  set: onValueChange
3115
3131
  };
@@ -3124,10 +3140,10 @@ const createArrayItemFieldInput = (parentInput, itemSchema, fieldName, index, _i
3124
3140
  path: `${parentInput.path}/${index}/${fieldName}`,
3125
3141
  required: itemSchema.required?.includes(fieldName) ?? false,
3126
3142
  form: parentInput.form,
3143
+ initialValue: itemValue?.[fieldName],
3127
3144
  props: {
3128
3145
  id: `${parentInput.props.id}-${index}-${fieldName}`,
3129
- name: `${parentInput.props.name}[${index}].${fieldName}`,
3130
- defaultValue: itemValue?.[fieldName]
3146
+ name: `${parentInput.props.name}[${index}].${fieldName}`
3131
3147
  },
3132
3148
  set: (value) => onFieldChange(fieldName, value)
3133
3149
  };
@@ -3345,7 +3361,9 @@ const ControlArray = (props) => {
3345
3361
  * Automatically detects date formats from schema and renders appropriate picker.
3346
3362
  */
3347
3363
  const ControlDate = (props) => {
3348
- const { inputProps, id, icon, format } = parseInput(props, useFormState(props.input));
3364
+ const form = useFormState(props.input);
3365
+ const [value, setValue] = useFieldValue(props.input);
3366
+ const { inputProps, id, icon, format } = parseInput(props, form);
3349
3367
  if (!props.input?.props) return null;
3350
3368
  if (props.datetime || format === "date-time") {
3351
3369
  const dateTimePickerProps = typeof props.datetime === "object" ? props.datetime : {};
@@ -3353,10 +3371,8 @@ const ControlDate = (props) => {
3353
3371
  ...inputProps,
3354
3372
  id,
3355
3373
  leftSection: icon,
3356
- defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
3357
- onChange: (value) => {
3358
- props.input.set(value ? new Date(value).toISOString() : void 0);
3359
- },
3374
+ value: value ? new Date(value) : null,
3375
+ onChange: (val) => setValue(val ? new Date(val).toISOString() : void 0),
3360
3376
  ...dateTimePickerProps
3361
3377
  });
3362
3378
  }
@@ -3366,10 +3382,8 @@ const ControlDate = (props) => {
3366
3382
  ...inputProps,
3367
3383
  id,
3368
3384
  leftSection: icon,
3369
- defaultValue: props.input.props.defaultValue ? new Date(props.input.props.defaultValue) : void 0,
3370
- onChange: (value) => {
3371
- props.input.set(value ? new Date(value).toISOString().slice(0, 10) : void 0);
3372
- },
3385
+ value: value ? new Date(value) : null,
3386
+ onChange: (val) => setValue(val ? new Date(val).toISOString().slice(0, 10) : void 0),
3373
3387
  ...dateInputProps
3374
3388
  });
3375
3389
  }
@@ -3379,10 +3393,8 @@ const ControlDate = (props) => {
3379
3393
  ...inputProps,
3380
3394
  id,
3381
3395
  leftSection: icon,
3382
- defaultValue: props.input.props.defaultValue,
3383
- onChange: (event) => {
3384
- props.input.set(event.currentTarget.value);
3385
- },
3396
+ value: value ?? "",
3397
+ onChange: (event) => setValue(event.currentTarget.value),
3386
3398
  ...timeInputProps
3387
3399
  });
3388
3400
  }
@@ -3395,14 +3407,10 @@ const ControlDate = (props) => {
3395
3407
  *
3396
3408
  */
3397
3409
  const ControlNumber = (props) => {
3398
- const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
3399
- const ref = useRef(null);
3400
- const [value, setValue] = useState(props.input.props.defaultValue);
3401
- useEvents({ "form:reset": (event) => {
3402
- if (event.id === props.input?.form.id && ref.current) setValue(props.input.props.defaultValue);
3403
- } }, [props.input]);
3410
+ const form = useFormState(props.input);
3411
+ const [value, setValue] = useFieldValue(props.input);
3412
+ const { inputProps, id, icon } = parseInput(props, form);
3404
3413
  if (!props.input?.props) return null;
3405
- const { type, ...inputPropsWithoutType } = props.input.props;
3406
3414
  if (props.sliderProps) {
3407
3415
  const min = props.sliderProps.min ?? inputProps.minimum ?? 0;
3408
3416
  const max = props.sliderProps.max ?? inputProps.maximum ?? 100;
@@ -3415,34 +3423,25 @@ const ControlNumber = (props) => {
3415
3423
  },
3416
3424
  children: /* @__PURE__ */ jsx(Slider, {
3417
3425
  ...inputProps,
3418
- ref,
3419
3426
  id,
3420
- ...inputPropsWithoutType,
3421
3427
  ...props.sliderProps,
3422
- value,
3428
+ value: value ?? 0,
3423
3429
  min,
3424
3430
  max,
3425
3431
  label: () => value,
3426
- onChange: (val) => {
3427
- setValue(val);
3428
- props.input.set(val);
3429
- }
3432
+ onChange: (val) => setValue(val)
3430
3433
  })
3431
3434
  })
3432
3435
  });
3433
3436
  }
3434
3437
  return /* @__PURE__ */ jsx(NumberInput, {
3435
3438
  ...inputProps,
3436
- ref,
3437
3439
  id,
3438
3440
  leftSection: icon,
3439
- ...inputPropsWithoutType,
3440
3441
  ...props.numberInputProps,
3441
3442
  value: value ?? "",
3442
3443
  onChange: (val) => {
3443
- const newValue = val !== null ? Number(val) : void 0;
3444
- setValue(newValue);
3445
- props.input.set(newValue);
3444
+ setValue(val !== null ? Number(val) : void 0);
3446
3445
  }
3447
3446
  });
3448
3447
  };
@@ -3525,92 +3524,9 @@ const ControlObject = (props) => {
3525
3524
  };
3526
3525
 
3527
3526
  //#endregion
3528
- //#region ../../src/core/form/components/ControlQueryBuilder.tsx
3529
- /**
3530
- * Query builder with text input and help popover.
3531
- * Generates query strings for parseQueryString syntax.
3532
- */
3533
- const ControlQueryBuilder = ({ schema, value = "", onChange, placeholder = "Enter query or click for assistance...", ...textInputProps }) => {
3534
- const [helpOpened, setHelpOpened] = useState(false);
3535
- const [textValue, setTextValue] = useState(value);
3536
- const inputRef = useRef(null);
3537
- const fields = schema ? extractSchemaFields(schema) : [];
3538
- const [error, setError] = useState(null);
3539
- const isValid = (value) => {
3540
- try {
3541
- parseQueryString(value.trim());
3542
- } catch (e) {
3543
- setError(e.message);
3544
- return false;
3545
- }
3546
- setError(null);
3547
- return true;
3548
- };
3549
- const handleTextChange = (newValue) => {
3550
- setTextValue(newValue);
3551
- if (isValid(newValue)) onChange?.(newValue);
3552
- };
3553
- const handleClear = () => {
3554
- setTextValue("");
3555
- onChange?.("");
3556
- isValid("");
3557
- };
3558
- const handleInsert = (text) => {
3559
- const newValue = textValue ? `${textValue}${text} ` : `${text} `;
3560
- setTextValue(newValue);
3561
- if (isValid(newValue)) onChange?.(newValue);
3562
- setTimeout(() => {
3563
- inputRef.current?.focus();
3564
- const length = inputRef.current?.value.length || 0;
3565
- inputRef.current?.setSelectionRange(length, length);
3566
- }, 0);
3567
- };
3568
- useEvents({ "form:change": (event) => {
3569
- if (event.id === inputRef.current?.form?.id) {
3570
- if (event.path === textInputProps["data-path"]) setTextValue(event.value ?? "");
3571
- }
3572
- } }, []);
3573
- return /* @__PURE__ */ jsxs(Popover, {
3574
- width: 800,
3575
- position: "bottom-start",
3576
- shadow: "md",
3577
- opened: helpOpened,
3578
- onChange: setHelpOpened,
3579
- closeOnClickOutside: true,
3580
- closeOnEscape: true,
3581
- transitionProps: {
3582
- transition: "fade-up",
3583
- duration: 200,
3584
- timingFunction: "ease"
3585
- },
3586
- children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(TextInput, {
3587
- ref: inputRef,
3588
- placeholder,
3589
- value: textValue,
3590
- onChange: (e) => handleTextChange(e.currentTarget.value),
3591
- onFocus: () => setHelpOpened(true),
3592
- leftSection: error ? /* @__PURE__ */ jsx(IconInfoTriangle, { size: 16 }) : /* @__PURE__ */ jsx(IconFilter, { size: 16 }),
3593
- rightSection: textValue && /* @__PURE__ */ jsx(ActionIcon, {
3594
- size: "sm",
3595
- variant: "subtle",
3596
- color: "gray",
3597
- onClick: handleClear,
3598
- children: /* @__PURE__ */ jsx(IconX, { size: 14 })
3599
- }),
3600
- ...textInputProps
3601
- }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3602
- bg: "transparent",
3603
- p: "xs",
3604
- bd: `1px solid ${ui.colors.border}`,
3605
- style: { backdropFilter: "blur(20px)" },
3606
- children: /* @__PURE__ */ jsx(QueryHelp, {
3607
- fields,
3608
- onInsert: handleInsert
3609
- })
3610
- })]
3611
- });
3612
- };
3613
- function QueryHelp({ fields, onInsert }) {
3527
+ //#region ../../src/core/form/components/ControlQueryBuilderHelp.tsx
3528
+ const ControlQueryBuilderHelp = (props) => {
3529
+ const { fields, onInsert } = props;
3614
3530
  return /* @__PURE__ */ jsxs(Flex$1, {
3615
3531
  gap: "md",
3616
3532
  align: "flex-start",
@@ -3771,113 +3687,316 @@ function QueryHelp({ fields, onInsert }) {
3771
3687
  }, field.path))
3772
3688
  })]
3773
3689
  })
3774
- ]
3690
+ ]
3691
+ });
3692
+ };
3693
+
3694
+ //#endregion
3695
+ //#region ../../src/core/form/components/ControlQueryBuilder.tsx
3696
+ /**
3697
+ * Query builder with text input and help popover.
3698
+ * Generates query strings for parseQueryString syntax.
3699
+ */
3700
+ const ControlQueryBuilder = (props) => {
3701
+ const { schema, value = "", onChange, placeholder = "Enter query or click for assistance...", ...textInputProps } = props;
3702
+ const [helpOpened, setHelpOpened] = useState(false);
3703
+ const [textValue, setTextValue] = useState(value);
3704
+ const inputRef = useRef(null);
3705
+ const fields = schema ? extractSchemaFields(schema) : [];
3706
+ const [error, setError] = useState(null);
3707
+ const isValid = (value) => {
3708
+ try {
3709
+ parseQueryString(value.trim());
3710
+ } catch (e) {
3711
+ setError(e.message);
3712
+ return false;
3713
+ }
3714
+ setError(null);
3715
+ return true;
3716
+ };
3717
+ const handleTextChange = (newValue) => {
3718
+ setTextValue(newValue);
3719
+ if (isValid(newValue)) onChange?.(newValue);
3720
+ };
3721
+ const handleClear = () => {
3722
+ setTextValue("");
3723
+ onChange?.("");
3724
+ isValid("");
3725
+ };
3726
+ const handleInsert = (text) => {
3727
+ const newValue = textValue ? `${textValue}${text} ` : `${text} `;
3728
+ setTextValue(newValue);
3729
+ if (isValid(newValue)) onChange?.(newValue);
3730
+ setTimeout(() => {
3731
+ inputRef.current?.focus();
3732
+ const length = inputRef.current?.value.length || 0;
3733
+ inputRef.current?.setSelectionRange(length, length);
3734
+ }, 0);
3735
+ };
3736
+ useEvents({ "form:change": (event) => {
3737
+ if (event.id === inputRef.current?.form?.id) {
3738
+ if (event.path === textInputProps["data-path"]) setTextValue(event.value ?? "");
3739
+ }
3740
+ } }, []);
3741
+ return /* @__PURE__ */ jsxs(Popover, {
3742
+ width: 800,
3743
+ position: "bottom-start",
3744
+ shadow: "md",
3745
+ opened: helpOpened,
3746
+ onChange: setHelpOpened,
3747
+ closeOnClickOutside: true,
3748
+ closeOnEscape: true,
3749
+ transitionProps: {
3750
+ transition: "fade-up",
3751
+ duration: 200,
3752
+ timingFunction: "ease"
3753
+ },
3754
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(TextInput, {
3755
+ ref: inputRef,
3756
+ placeholder,
3757
+ value: textValue,
3758
+ onChange: (e) => handleTextChange(e.currentTarget.value),
3759
+ onFocus: () => setHelpOpened(true),
3760
+ leftSection: error ? /* @__PURE__ */ jsx(IconInfoTriangle, { size: 16 }) : /* @__PURE__ */ jsx(IconFilter, { size: 16 }),
3761
+ rightSection: textValue && /* @__PURE__ */ jsx(ActionIcon, {
3762
+ size: "sm",
3763
+ variant: "subtle",
3764
+ color: "gray",
3765
+ onClick: handleClear,
3766
+ children: /* @__PURE__ */ jsx(IconX, { size: 14 })
3767
+ }),
3768
+ ...textInputProps
3769
+ }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3770
+ bg: "transparent",
3771
+ p: "xs",
3772
+ bd: `1px solid ${ui.colors.border}`,
3773
+ style: { backdropFilter: "blur(20px)" },
3774
+ children: /* @__PURE__ */ jsx(ControlQueryBuilderHelp, {
3775
+ fields,
3776
+ onInsert: handleInsert
3777
+ })
3778
+ })]
3775
3779
  });
3776
- }
3780
+ };
3777
3781
 
3778
3782
  //#endregion
3779
3783
  //#region ../../src/core/form/components/ControlSelect.tsx
3780
3784
  /**
3781
- * ControlSelect component for handling Select, MultiSelect, and TagsInput.
3785
+ * ControlSelect component for handling Select, MultiSelect, Autocomplete, and TagsInput.
3782
3786
  *
3783
3787
  * Features:
3784
3788
  * - Basic Select with enum support
3785
3789
  * - MultiSelect for array of enums
3786
- * - TagsInput for array of strings (no enum)
3787
- * - Future: Lazy loading
3788
- * - Future: Searchable/filterable options
3789
- * - Future: Custom option rendering
3790
+ * - Autocomplete for creatable single values
3791
+ * - TagsInput for creatable array values
3792
+ * - Async lazy loading with auto short/long mode detection
3793
+ * - Short mode: client-side filtering with cached data
3794
+ * - Long mode: debounced server search
3790
3795
  *
3791
3796
  * Automatically detects enum values and array types from schema.
3792
3797
  */
3793
3798
  const ControlSelect = (props) => {
3794
- const { inputProps, id, icon } = parseInput(props, useFormState(props.input));
3799
+ const form = useFormState(props.input);
3800
+ const [value, setValue] = useFieldValue(props.input);
3801
+ const { inputProps, id, icon } = parseInput(props, form);
3795
3802
  const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
3796
- let itemsEnum;
3797
- if (isArray && "items" in props.input.schema && props.input.schema.items) {
3798
- const items = props.input.schema.items;
3799
- if ("enum" in items && Array.isArray(items.enum)) itemsEnum = items.enum;
3800
- }
3803
+ const isNumeric = props.input.schema && "type" in props.input.schema && (props.input.schema.type === "integer" || props.input.schema.type === "number");
3804
+ const isBoolean = props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean";
3801
3805
  const enumValues = props.input.schema && "enum" in props.input.schema && Array.isArray(props.input.schema.enum) ? props.input.schema.enum : [];
3802
- const [data, setData] = useState([]);
3806
+ const { data: asyncData, loading, mode, search } = useAsyncLoader(props.loader, props.loaderThreshold ?? 100, props.loaderDebounce ?? 300, props.input.initialValue);
3807
+ const [staticData, setStaticData] = useState([]);
3808
+ const enumKey = JSON.stringify(enumValues);
3803
3809
  useEffect(() => {
3804
- if (!props.input?.props) return;
3805
- if (props.loader) props.loader().then(setData);
3806
- else setData(enumValues);
3807
- }, [props.input, props.loader]);
3810
+ if (!props.input?.props || props.loader) return;
3811
+ if (isBoolean && enumValues.length === 0) setStaticData([{
3812
+ value: "true",
3813
+ label: "True"
3814
+ }, {
3815
+ value: "false",
3816
+ label: "False"
3817
+ }]);
3818
+ else setStaticData(enumValues);
3819
+ }, [
3820
+ props.input,
3821
+ props.loader,
3822
+ enumKey,
3823
+ isBoolean
3824
+ ]);
3825
+ const data = props.loader ? asyncData : staticData;
3808
3826
  if (!props.input?.props) return null;
3809
- if (props.segmented) {
3810
- const segmentedControlProps = typeof props.segmented === "object" ? props.segmented : {};
3827
+ /**
3828
+ * Coerce value for numeric schemas Select values are always strings.
3829
+ */
3830
+ const coerceValue = (val) => {
3831
+ if (val == null) return val;
3832
+ if (isNumeric) return Number(val);
3833
+ if (isBoolean) return val === "true";
3834
+ return val;
3835
+ };
3836
+ if (props.segmentedProps) {
3837
+ const segmentedControlProps = typeof props.segmentedProps === "object" ? props.segmentedProps : {};
3838
+ const segmentedData = segmentedControlProps.data ?? data.slice(0, 10);
3811
3839
  return /* @__PURE__ */ jsx(Input.Wrapper, {
3812
3840
  ...inputProps,
3813
- children: /* @__PURE__ */ jsx(Flex$1, { children: /* @__PURE__ */ jsx(SegmentedControl, {
3814
- disabled: inputProps.disabled,
3815
- defaultValue: String(props.input.props.defaultValue),
3816
- ...segmentedControlProps,
3817
- onChange: (value) => {
3818
- props.input.set(value);
3819
- },
3820
- data: data.slice(0, 10)
3821
- }) })
3841
+ children: /* @__PURE__ */ jsx(Flex$1, {
3842
+ my: "calc(var(--mantine-spacing-xs) / 2)",
3843
+ children: /* @__PURE__ */ jsx(SegmentedControl, {
3844
+ disabled: inputProps.disabled,
3845
+ value: value != null ? String(value) : "",
3846
+ ...segmentedControlProps,
3847
+ onChange: (val) => {
3848
+ setValue(coerceValue(val));
3849
+ },
3850
+ data: segmentedData
3851
+ })
3852
+ })
3822
3853
  });
3823
3854
  }
3824
- if (props.autocomplete) {
3825
- const autocompleteProps = typeof props.autocomplete === "object" ? props.autocomplete : {};
3826
- return /* @__PURE__ */ jsx(Autocomplete, {
3855
+ const sharedProps = {
3856
+ size: props.size,
3857
+ id,
3858
+ leftSection: loading ? /* @__PURE__ */ jsx(Loader, {
3859
+ color: "gray",
3860
+ size: 10
3861
+ }) : icon,
3862
+ data
3863
+ };
3864
+ const selectableProps = {
3865
+ ...sharedProps,
3866
+ searchable: true,
3867
+ rightSection: /* @__PURE__ */ jsx("span", {})
3868
+ };
3869
+ const longModeProps = mode === "long" ? {
3870
+ filter: ({ options }) => options,
3871
+ onSearchChange: search.run
3872
+ } : {};
3873
+ if (props.creatable && (isArray || props.tagsInputProps)) {
3874
+ const tagsInputExtraProps = props.tagsInputProps ?? {};
3875
+ return /* @__PURE__ */ jsx(TagsInput, {
3827
3876
  ...inputProps,
3828
- size: props.size,
3829
- id,
3830
- leftSection: icon,
3831
- data,
3832
- ...props.input.props,
3833
- ...autocompleteProps
3877
+ ...sharedProps,
3878
+ ...longModeProps,
3879
+ value: Array.isArray(value) ? value : [],
3880
+ onChange: (val) => {
3881
+ setValue(val);
3882
+ },
3883
+ ...tagsInputExtraProps
3834
3884
  });
3835
3885
  }
3836
- if (isArray && !itemsEnum || props.tags) {
3837
- const tagsInputProps = typeof props.tags === "object" ? props.tags : {};
3838
- return /* @__PURE__ */ jsx(TagsInput, {
3886
+ if (props.creatable) {
3887
+ const autocompleteExtraProps = props.autocompleteProps ?? {};
3888
+ return /* @__PURE__ */ jsx(Autocomplete, {
3839
3889
  ...inputProps,
3840
- size: props.size,
3841
- id,
3842
- leftSection: icon,
3843
- defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
3844
- onChange: (value) => {
3845
- props.input.set(value);
3890
+ ...sharedProps,
3891
+ ...longModeProps,
3892
+ value: value != null ? String(value) : "",
3893
+ onChange: (val) => {
3894
+ setValue(coerceValue(val));
3846
3895
  },
3847
- ...tagsInputProps
3896
+ ...autocompleteExtraProps
3848
3897
  });
3849
3898
  }
3850
- if (isArray && itemsEnum || props.multi) {
3851
- const data = itemsEnum?.map((value) => ({
3852
- value,
3853
- label: value
3854
- })) || [];
3855
- const multiSelectProps = typeof props.multi === "object" ? props.multi : {};
3899
+ if (isArray || props.multiSelectProps) {
3900
+ const multiSelectExtraProps = typeof props.multiSelectProps === "object" ? props.multiSelectProps : {};
3856
3901
  return /* @__PURE__ */ jsx(MultiSelect, {
3857
3902
  ...inputProps,
3858
- size: props.size,
3859
- id,
3860
- leftSection: icon,
3861
- data,
3862
- defaultValue: Array.isArray(props.input.props.defaultValue) ? props.input.props.defaultValue : [],
3863
- onChange: (value) => {
3864
- props.input.set(value);
3903
+ ...selectableProps,
3904
+ ...longModeProps,
3905
+ value: Array.isArray(value) ? value : [],
3906
+ onChange: (val) => {
3907
+ setValue(val);
3865
3908
  },
3866
- ...multiSelectProps
3909
+ ...multiSelectExtraProps
3867
3910
  });
3868
3911
  }
3869
- const selectProps = typeof props.select === "object" ? props.select : {};
3912
+ const selectExtraProps = typeof props.selectProps === "object" ? props.selectProps : {};
3913
+ if (mode === "static") return /* @__PURE__ */ jsx(Select, {
3914
+ ...inputProps,
3915
+ ...selectableProps,
3916
+ value: value != null ? String(value) : null,
3917
+ onChange: (val) => {
3918
+ setValue(coerceValue(val));
3919
+ },
3920
+ ...selectExtraProps
3921
+ });
3870
3922
  return /* @__PURE__ */ jsx(Select, {
3871
3923
  ...inputProps,
3872
- size: props.size,
3873
- id,
3874
- leftSection: icon,
3875
- rightSection: null,
3876
- data,
3877
- ...props.input.props,
3878
- ...selectProps
3924
+ ...selectableProps,
3925
+ ...longModeProps,
3926
+ value: value != null ? String(value) : null,
3927
+ onChange: (val) => {
3928
+ setValue(coerceValue(val));
3929
+ },
3930
+ ...selectExtraProps
3879
3931
  });
3880
3932
  };
3933
+ /**
3934
+ * Hook for async select data loading with auto short/long mode detection.
3935
+ */
3936
+ const useAsyncLoader = (loader, threshold, debounceMs, defaultValue) => {
3937
+ const [data, setData] = useState([]);
3938
+ const [loading, setLoading] = useState(false);
3939
+ const [mode, setMode] = useState("static");
3940
+ const cache = useRef(/* @__PURE__ */ new Map());
3941
+ useAction({
3942
+ name: "select:loader:init",
3943
+ runOnInit: true,
3944
+ handler: async () => {
3945
+ if (!loader) {
3946
+ setMode("static");
3947
+ return;
3948
+ }
3949
+ setLoading(true);
3950
+ try {
3951
+ const result = await loader("");
3952
+ const isShort = result.length <= threshold;
3953
+ setMode(isShort ? "short" : "long");
3954
+ cache.current.set("", result);
3955
+ setData(result);
3956
+ if (!isShort && defaultValue != null && String(defaultValue) !== "") {
3957
+ const resolved = await loader("", [String(defaultValue)]);
3958
+ if (resolved.length > 0) setData((prev) => {
3959
+ const existing = new Set(prev.map((d) => typeof d === "string" ? d : d.value));
3960
+ const newItems = resolved.filter((r) => {
3961
+ const val = typeof r === "string" ? r : r.value;
3962
+ return !existing.has(val);
3963
+ });
3964
+ return [...prev, ...newItems];
3965
+ });
3966
+ }
3967
+ } finally {
3968
+ setLoading(false);
3969
+ }
3970
+ }
3971
+ }, [loader, threshold]);
3972
+ return {
3973
+ data,
3974
+ loading,
3975
+ mode,
3976
+ search: useAction({
3977
+ debounce: debounceMs,
3978
+ handler: async (text) => {
3979
+ if (!loader || mode !== "long") return;
3980
+ if (cache.current.has(text)) {
3981
+ setData(cache.current.get(text));
3982
+ return;
3983
+ }
3984
+ setLoading(true);
3985
+ try {
3986
+ const result = await loader(text);
3987
+ cache.current.set(text, result);
3988
+ setData(result);
3989
+ } finally {
3990
+ setLoading(false);
3991
+ }
3992
+ }
3993
+ }, [
3994
+ loader,
3995
+ mode,
3996
+ debounceMs
3997
+ ])
3998
+ };
3999
+ };
3881
4000
 
3882
4001
  //#endregion
3883
4002
  //#region ../../src/core/form/components/Control.tsx
@@ -3907,6 +4026,7 @@ const ControlSelect = (props) => {
3907
4026
  */
3908
4027
  const Control = (_props) => {
3909
4028
  const form = useFormState(_props.input, ["error"]);
4029
+ const [value, setValue] = useFieldValue(_props.input);
3910
4030
  if (!_props.input?.props) return null;
3911
4031
  const { inputProps, id, icon, format, schema } = parseInput(_props, form);
3912
4032
  const props = {
@@ -3914,12 +4034,11 @@ const Control = (_props) => {
3914
4034
  ...schema.$control
3915
4035
  };
3916
4036
  if (props.query) return /* @__PURE__ */ jsx(ControlQueryBuilder, {
3917
- ...props.input.props,
3918
4037
  ...inputProps,
3919
4038
  schema: props.query,
3920
- value: props.input.props.value,
3921
- onChange: (value) => {
3922
- props.input.set(value);
4039
+ value,
4040
+ onChange: (val) => {
4041
+ setValue(val);
3923
4042
  }
3924
4043
  });
3925
4044
  if (props.custom) {
@@ -3930,9 +4049,9 @@ const Control = (_props) => {
3930
4049
  flex: 1,
3931
4050
  mt: "calc(var(--mantine-spacing-xs) / 2)",
3932
4051
  children: /* @__PURE__ */ jsx(Custom, {
3933
- defaultValue: props.input.props.defaultValue,
3934
- onChange: (value) => {
3935
- props.input.set(value);
4052
+ value,
4053
+ onChange: (val) => {
4054
+ setValue(val);
3936
4055
  }
3937
4056
  })
3938
4057
  })
@@ -3943,7 +4062,7 @@ const Control = (_props) => {
3943
4062
  const controlObjectProps = typeof props.object === "object" ? props.object : {};
3944
4063
  return /* @__PURE__ */ jsx(ControlObject, {
3945
4064
  input: props.input,
3946
- title: props.title,
4065
+ label: props.label,
3947
4066
  description: props.description,
3948
4067
  ...controlObjectProps
3949
4068
  });
@@ -3954,18 +4073,18 @@ const Control = (_props) => {
3954
4073
  const controlArrayProps = typeof props.array === "object" ? props.array : {};
3955
4074
  return /* @__PURE__ */ jsx(ControlArray, {
3956
4075
  input: props.input,
3957
- title: props.title,
4076
+ label: props.label,
3958
4077
  description: props.description,
3959
4078
  ...controlArrayProps
3960
4079
  });
3961
4080
  }
3962
- if (props.number || props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
4081
+ if (props.number || !props.select && props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
3963
4082
  const controlNumberProps = typeof props.number === "object" ? props.number : {};
3964
4083
  if (props.slider) controlNumberProps.sliderProps ??= {};
3965
4084
  return /* @__PURE__ */ jsx(ControlNumber, {
3966
4085
  size: props.size,
3967
4086
  input: props.input,
3968
- title: props.title,
4087
+ label: props.label,
3969
4088
  description: props.description,
3970
4089
  icon,
3971
4090
  ...controlNumberProps
@@ -3978,9 +4097,7 @@ const Control = (_props) => {
3978
4097
  size: props.size,
3979
4098
  id,
3980
4099
  leftSection: icon,
3981
- onChange: (file) => {
3982
- props.input.set(file);
3983
- },
4100
+ onChange: (file) => setValue(file),
3984
4101
  ...fileInputProps
3985
4102
  });
3986
4103
  }
@@ -3991,17 +4108,18 @@ const Control = (_props) => {
3991
4108
  size: props.size,
3992
4109
  id,
3993
4110
  leftSection: icon,
3994
- ...props.input.props,
4111
+ value: value ?? "",
4112
+ onChange: (val) => setValue(val),
3995
4113
  ...colorInputProps
3996
4114
  });
3997
4115
  }
3998
4116
  if (props.input.schema && "enum" in props.input.schema && props.input.schema.enum || isArray && !isArrayOfObjects || props.select) {
3999
4117
  const opts = typeof props.select === "object" ? props.select : {};
4000
- if (props.segmented) opts.segmented ??= {};
4118
+ if (props.segmented) opts.segmentedProps ??= {};
4001
4119
  return /* @__PURE__ */ jsx(ControlSelect, {
4002
4120
  size: props.size,
4003
4121
  input: props.input,
4004
- title: props.title,
4122
+ label: props.label,
4005
4123
  description: props.description,
4006
4124
  icon,
4007
4125
  ...opts
@@ -4015,16 +4133,16 @@ const Control = (_props) => {
4015
4133
  size: props.size,
4016
4134
  id,
4017
4135
  color: "blue",
4018
- defaultChecked: props.input.props.defaultValue,
4136
+ checked: Boolean(value),
4019
4137
  onChange: (event) => {
4020
- props.input.set(event.currentTarget.checked);
4138
+ setValue(event.currentTarget.checked);
4021
4139
  },
4022
4140
  ...switchProps
4023
4141
  });
4024
4142
  }
4025
4143
  const opts = {
4026
4144
  input: props.input,
4027
- select: { data: [{
4145
+ selectProps: { data: [{
4028
4146
  value: "true",
4029
4147
  label: "Yes"
4030
4148
  }, {
@@ -4034,7 +4152,7 @@ const Control = (_props) => {
4034
4152
  };
4035
4153
  return /* @__PURE__ */ jsx(ControlSelect, {
4036
4154
  size: props.size,
4037
- title: props.title,
4155
+ label: props.label,
4038
4156
  description: props.description,
4039
4157
  icon,
4040
4158
  ...opts
@@ -4047,7 +4165,8 @@ const Control = (_props) => {
4047
4165
  size: props.size,
4048
4166
  id,
4049
4167
  leftSection: icon,
4050
- ...props.input.props,
4168
+ value: value ?? "",
4169
+ onChange: (ev) => setValue(ev.target.value),
4051
4170
  ...passwordInputProps
4052
4171
  });
4053
4172
  }
@@ -4058,14 +4177,15 @@ const Control = (_props) => {
4058
4177
  size: props.size,
4059
4178
  id,
4060
4179
  leftSection: icon,
4061
- ...props.input.props,
4180
+ value: value ?? "",
4181
+ onChange: (ev) => setValue(ev.target.value),
4062
4182
  ...textAreaProps
4063
4183
  });
4064
4184
  }
4065
4185
  if (props.date || props.datetime || props.time || format === "date" || format === "date-time" || format === "time") return /* @__PURE__ */ jsx(ControlDate, {
4066
4186
  size: props.size,
4067
4187
  input: props.input,
4068
- title: props.title,
4188
+ label: props.label,
4069
4189
  description: props.description,
4070
4190
  icon,
4071
4191
  date: props.date,
@@ -4080,7 +4200,7 @@ const Control = (_props) => {
4080
4200
  case "uri": return "url";
4081
4201
  case "tel":
4082
4202
  case "phone": return "tel";
4083
- default: return;
4203
+ default: return props.input.props.type ?? "text";
4084
4204
  }
4085
4205
  };
4086
4206
  return /* @__PURE__ */ jsx(TextInput, {
@@ -4089,14 +4209,9 @@ const Control = (_props) => {
4089
4209
  id,
4090
4210
  leftSection: icon,
4091
4211
  type: getInputType(),
4092
- ...props.input.props,
4093
- ...textInputProps,
4094
- inputWrapperOrder: [
4095
- "label",
4096
- "input",
4097
- "description",
4098
- "error"
4099
- ]
4212
+ value: value ?? "",
4213
+ onChange: (ev) => setValue(ev.target.value),
4214
+ ...textInputProps
4100
4215
  });
4101
4216
  };
4102
4217
 
@@ -4145,6 +4260,7 @@ const Control = (_props) => {
4145
4260
  */
4146
4261
  const TypeForm = (props) => {
4147
4262
  const { form, columns = 3, children, controlProps, fieldControlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps, fill = true, size } = props;
4263
+ const { dirty } = useFormState(form, ["dirty"]);
4148
4264
  const schema = props.schema || form.options.schema;
4149
4265
  if (!schema?.properties) return null;
4150
4266
  const supportedFields = Object.keys(schema.properties);
@@ -4207,10 +4323,12 @@ const TypeForm = (props) => {
4207
4323
  children: [/* @__PURE__ */ jsx(ActionButton, {
4208
4324
  variant: "subtle",
4209
4325
  type: "reset",
4326
+ disabled: !dirty,
4210
4327
  children: "Reset"
4211
4328
  }), /* @__PURE__ */ jsx(ActionButton, {
4212
4329
  intent: "primary",
4213
4330
  form,
4331
+ disabled: !dirty,
4214
4332
  ...submitButtonProps,
4215
4333
  children: submitButtonProps?.children ?? "Submit"
4216
4334
  })]
@@ -4249,16 +4367,27 @@ const dialogForm = (form, options) => ({
4249
4367
  });
4250
4368
 
4251
4369
  //#endregion
4252
- //#region ../../src/core/helpers/renderIcon.tsx
4253
- const renderIcon = (icon, size) => {
4254
- if (!icon) return null;
4255
- if (isValidElement(icon)) return icon;
4256
- if (isComponentType(icon)) return /* @__PURE__ */ jsx(icon, { size: size ?? ui.sizes.icon.md });
4257
- return icon;
4370
+ //#region ../../src/core/json/components/JsonViewerCopyButton.tsx
4371
+ const JsonViewerCopyButton = (props) => {
4372
+ const [copied, setCopied] = useState(false);
4373
+ const handleCopy = useCallback((e) => {
4374
+ e.stopPropagation();
4375
+ navigator.clipboard.writeText(props.value);
4376
+ setCopied(true);
4377
+ setTimeout(() => setCopied(false), 1500);
4378
+ }, [props.value]);
4379
+ return /* @__PURE__ */ jsx(ActionIcon, {
4380
+ size: props.iconSize + 4,
4381
+ variant: "transparent",
4382
+ c: copied ? "green" : "dimmed",
4383
+ onClick: handleCopy,
4384
+ className: "alepha-json-viewer-copy",
4385
+ children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: props.iconSize }) : /* @__PURE__ */ jsx(IconCopy, { size: props.iconSize })
4386
+ });
4258
4387
  };
4259
4388
 
4260
4389
  //#endregion
4261
- //#region ../../src/core/json/components/JsonViewer.tsx
4390
+ //#region ../../src/core/json/components/JsonViewerShared.ts
4262
4391
  const SIZE_CONFIG = {
4263
4392
  xs: {
4264
4393
  icon: 14,
@@ -4307,77 +4436,21 @@ const getValueType = (val) => {
4307
4436
  if (Array.isArray(val)) return "array";
4308
4437
  return typeof val;
4309
4438
  };
4310
- function buildTreeNodes(data, path = [], key, isArrayItem = false, maxDepth = 10) {
4311
- const currentPath = key !== void 0 ? [...path, key] : path;
4312
- const nodeId = currentPath.length > 0 ? currentPath.join(".") : "root";
4313
- if (currentPath.length > maxDepth) return {
4314
- value: nodeId,
4315
- label: key ?? "",
4316
- nodeValue: data,
4317
- nodeKey: key,
4318
- path: currentPath,
4319
- isArrayItem
4320
- };
4321
- const type = getValueType(data);
4322
- if (type === "object" || type === "array") {
4323
- 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);
4324
- return {
4325
- value: nodeId,
4326
- label: key ?? "",
4327
- nodeValue: data,
4328
- nodeKey: key,
4329
- path: currentPath,
4330
- isArrayItem,
4331
- children: children.length > 0 ? children : void 0
4332
- };
4333
- }
4334
- return {
4335
- value: nodeId,
4336
- label: key ?? "",
4337
- nodeValue: data,
4338
- nodeKey: key,
4339
- path: currentPath,
4340
- isArrayItem
4341
- };
4342
- }
4343
- function getExpandedIds(nodes, targetDepth, currentDepth = 0) {
4344
- if (currentDepth >= targetDepth) return [];
4345
- const ids = [];
4346
- for (const node of nodes) if (node.children) {
4347
- ids.push(node.value);
4348
- ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));
4349
- }
4350
- return ids;
4351
- }
4352
- const CopyButton$1 = ({ value, iconSize }) => {
4353
- const [copied, setCopied] = useState(false);
4354
- const handleCopy = useCallback((e) => {
4355
- e.stopPropagation();
4356
- navigator.clipboard.writeText(value);
4357
- setCopied(true);
4358
- setTimeout(() => setCopied(false), 1500);
4359
- }, [value]);
4360
- return /* @__PURE__ */ jsx(ActionIcon, {
4361
- size: iconSize + 4,
4362
- variant: "transparent",
4363
- c: copied ? "green" : "dimmed",
4364
- onClick: handleCopy,
4365
- className: "alepha-json-viewer-copy",
4366
- children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: iconSize }) : /* @__PURE__ */ jsx(IconCopy, { size: iconSize })
4367
- });
4368
- };
4369
- const RowNode = ({ node, expanded, hasChildren, elementProps, size, config, showQuotes, showCopyButton, renderValue }) => {
4370
- const { nodeValue, nodeKey, path, isArrayItem, isRoot } = node;
4439
+
4440
+ //#endregion
4441
+ //#region ../../src/core/json/components/JsonViewerRowNode.tsx
4442
+ const JsonViewerRowNode = (props) => {
4443
+ const { nodeValue, nodeKey, path, isArrayItem } = props.node;
4371
4444
  const type = getValueType(nodeValue);
4372
4445
  const isExpandable = type === "object" || type === "array";
4373
4446
  const getPreview = () => {
4374
4447
  if (!isExpandable) return null;
4375
4448
  const count = (type === "array" ? nodeValue : Object.keys(nodeValue)).length;
4376
4449
  const label = type === "array" ? "item" : "key";
4377
- if (!expanded) return /* @__PURE__ */ jsx(Text$1, {
4450
+ if (!props.expanded) return /* @__PURE__ */ jsx(Text$1, {
4378
4451
  fs: "italic",
4379
4452
  component: "span",
4380
- size,
4453
+ size: props.size,
4381
4454
  style: STYLES.preview,
4382
4455
  children: count === 0 ? type === "array" ? "[]" : "{}" : type === "array" ? `[ ${count} ${count === 1 ? label : `${label}s`} ]` : `{ ${count} ${count === 1 ? label : `${label}s`} }`
4383
4456
  });
@@ -4387,25 +4460,25 @@ const RowNode = ({ node, expanded, hasChildren, elementProps, size, config, show
4387
4460
  return /* @__PURE__ */ jsxs(Flex$1, {
4388
4461
  gap: 6,
4389
4462
  wrap: "nowrap",
4390
- ...elementProps,
4391
- className: `alepha-json-viewer-row ${elementProps.className || ""}`,
4463
+ ...props.elementProps,
4464
+ className: `alepha-json-viewer-row ${props.elementProps.className || ""}`,
4392
4465
  children: [
4393
- hasChildren ? expanded ? /* @__PURE__ */ jsx(IconChevronDown, {
4394
- size: config.icon,
4466
+ props.hasChildren ? props.expanded ? /* @__PURE__ */ jsx(IconChevronDown, {
4467
+ size: props.config.icon,
4395
4468
  style: STYLES.chevron
4396
4469
  }) : /* @__PURE__ */ jsx(IconChevronRight, {
4397
- size: config.icon,
4470
+ size: props.config.icon,
4398
4471
  style: STYLES.chevron
4399
4472
  }) : /* @__PURE__ */ jsx("span", { style: {
4400
- width: config.icon,
4473
+ width: props.config.icon,
4401
4474
  flexShrink: 0
4402
4475
  } }),
4403
4476
  nodeKey !== void 0 && !isArrayItem && /* @__PURE__ */ jsxs(Text$1, {
4404
4477
  component: "span",
4405
- size,
4478
+ size: props.size,
4406
4479
  children: [/* @__PURE__ */ jsx("span", {
4407
4480
  style: STYLES.key,
4408
- children: showQuotes ? `"${nodeKey}"` : nodeKey
4481
+ children: props.showQuotes ? `"${nodeKey}"` : nodeKey
4409
4482
  }), /* @__PURE__ */ jsx("span", {
4410
4483
  style: STYLES.colon,
4411
4484
  children: ":"
@@ -4413,7 +4486,7 @@ const RowNode = ({ node, expanded, hasChildren, elementProps, size, config, show
4413
4486
  }),
4414
4487
  nodeKey !== void 0 && isArrayItem && /* @__PURE__ */ jsxs(Text$1, {
4415
4488
  component: "span",
4416
- size,
4489
+ size: props.size,
4417
4490
  children: [/* @__PURE__ */ jsx("span", {
4418
4491
  style: STYLES.key,
4419
4492
  children: nodeKey
@@ -4422,25 +4495,29 @@ const RowNode = ({ node, expanded, hasChildren, elementProps, size, config, show
4422
4495
  children: ":"
4423
4496
  })]
4424
4497
  }),
4425
- hasChildren ? getPreview() : isExpandable ? type === "array" ? /* @__PURE__ */ jsx(Text$1, {
4498
+ props.hasChildren ? getPreview() : isExpandable ? type === "array" ? /* @__PURE__ */ jsx(Text$1, {
4426
4499
  component: "span",
4427
- size,
4500
+ size: props.size,
4428
4501
  style: STYLES.preview,
4429
4502
  children: "[]"
4430
4503
  }) : /* @__PURE__ */ jsx(Text$1, {
4431
4504
  component: "span",
4432
- size,
4505
+ size: props.size,
4433
4506
  style: STYLES.preview,
4434
4507
  children: "{}"
4435
- }) : renderValue(nodeValue, nodeKey, path),
4436
- showCopyButton && /* @__PURE__ */ jsx(CopyButton$1, {
4508
+ }) : props.renderValue(nodeValue, nodeKey, path),
4509
+ props.showCopyButton && /* @__PURE__ */ jsx(JsonViewerCopyButton, {
4437
4510
  value: getCopyValue(),
4438
- iconSize: config.icon
4511
+ iconSize: props.config.icon
4439
4512
  })
4440
4513
  ]
4441
4514
  });
4442
4515
  };
4443
- const JsonViewer = ({ data, defaultExpandedDepth = 2, maxDepth = 10, size = "sm", showQuotes = false, showCopyButton = true, formatValue }) => {
4516
+
4517
+ //#endregion
4518
+ //#region ../../src/core/json/components/JsonViewer.tsx
4519
+ const JsonViewer = (props) => {
4520
+ const { data, defaultExpandedDepth = 2, maxDepth = 10, size = "sm", showQuotes = false, showCopyButton = true, formatValue } = props;
4444
4521
  const config = SIZE_CONFIG[size] || SIZE_CONFIG.sm;
4445
4522
  const treeData = useMemo(() => {
4446
4523
  const type = getValueType(data);
@@ -4520,7 +4597,7 @@ const JsonViewer = ({ data, defaultExpandedDepth = 2, maxDepth = 10, size = "sm"
4520
4597
  size
4521
4598
  ]);
4522
4599
  const renderNode = useCallback(({ node, expanded, hasChildren, elementProps }) => {
4523
- return /* @__PURE__ */ jsx(RowNode, {
4600
+ return /* @__PURE__ */ jsx(JsonViewerRowNode, {
4524
4601
  node,
4525
4602
  expanded,
4526
4603
  hasChildren,
@@ -4552,6 +4629,54 @@ const JsonViewer = ({ data, defaultExpandedDepth = 2, maxDepth = 10, size = "sm"
4552
4629
  styles: { root: STYLES.root }
4553
4630
  });
4554
4631
  };
4632
+ /**
4633
+ * Convert JSON to tree data structure.
4634
+ */
4635
+ const buildTreeNodes = (data, path = [], key, isArrayItem = false, maxDepth = 10) => {
4636
+ const currentPath = key !== void 0 ? [...path, key] : path;
4637
+ const nodeId = currentPath.length > 0 ? currentPath.join(".") : "root";
4638
+ if (currentPath.length > maxDepth) return {
4639
+ value: nodeId,
4640
+ label: key ?? "",
4641
+ nodeValue: data,
4642
+ nodeKey: key,
4643
+ path: currentPath,
4644
+ isArrayItem
4645
+ };
4646
+ const type = getValueType(data);
4647
+ if (type === "object" || type === "array") {
4648
+ 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);
4649
+ return {
4650
+ value: nodeId,
4651
+ label: key ?? "",
4652
+ nodeValue: data,
4653
+ nodeKey: key,
4654
+ path: currentPath,
4655
+ isArrayItem,
4656
+ children: children.length > 0 ? children : void 0
4657
+ };
4658
+ }
4659
+ return {
4660
+ value: nodeId,
4661
+ label: key ?? "",
4662
+ nodeValue: data,
4663
+ nodeKey: key,
4664
+ path: currentPath,
4665
+ isArrayItem
4666
+ };
4667
+ };
4668
+ /**
4669
+ * Get all expandable node IDs up to a certain depth.
4670
+ */
4671
+ const getExpandedIds = (nodes, targetDepth, currentDepth = 0) => {
4672
+ if (currentDepth >= targetDepth) return [];
4673
+ const ids = [];
4674
+ for (const node of nodes) if (node.children) {
4675
+ ids.push(node.value);
4676
+ ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));
4677
+ }
4678
+ return ids;
4679
+ };
4555
4680
 
4556
4681
  //#endregion
4557
4682
  //#region ../../src/core/json/factories/dialogJson.tsx
@@ -4595,7 +4720,8 @@ const DEFAULT_MAX_VISIBLE_COLUMNS = 8;
4595
4720
 
4596
4721
  //#endregion
4597
4722
  //#region ../../src/core/table/components/DataTableFilters.tsx
4598
- const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) => {
4723
+ const DataTableFilters = (props) => {
4724
+ const { schema, form, typeFormProps, filterVisibility } = props;
4599
4725
  const visibleSchema = useMemo(() => {
4600
4726
  const visibleKeys = Object.keys(schema.properties).filter((key) => filterVisibility[key] !== false);
4601
4727
  if (visibleKeys.length === 0) return null;
@@ -4606,14 +4732,14 @@ const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) =>
4606
4732
  return t.object(visibleProps);
4607
4733
  }, [schema, filterVisibility]);
4608
4734
  if (!visibleSchema) return null;
4609
- return /* @__PURE__ */ jsx(Flex$1, {
4610
- w: "100%",
4735
+ return /* @__PURE__ */ jsx(Flex, {
4736
+ surface: true,
4737
+ flex: 1,
4738
+ mt: -4,
4611
4739
  p: "xs",
4612
4740
  m: "xs",
4613
4741
  bdrs: "md",
4614
- bg: ui.colors.surface,
4615
4742
  children: /* @__PURE__ */ jsx(TypeForm, {
4616
- size: "xs",
4617
4743
  ...typeFormProps,
4618
4744
  skipSubmitButton: true,
4619
4745
  fill: true,
@@ -4624,7 +4750,7 @@ const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) =>
4624
4750
  sm: 2,
4625
4751
  md: 3,
4626
4752
  lg: 4,
4627
- xl: 6
4753
+ xl: 5
4628
4754
  }
4629
4755
  })
4630
4756
  });
@@ -4632,9 +4758,10 @@ const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) =>
4632
4758
 
4633
4759
  //#endregion
4634
4760
  //#region ../../src/core/table/components/DataTablePagination.tsx
4635
- const DataTablePagination = ({ page, size, totalPages, totalElements, offset, numberOfElements, onPageChange, onSizeChange }) => {
4761
+ const DataTablePagination = ({ page, size, totalPages, totalElements, isFirst, isLast, offset, numberOfElements, onPageChange, onSizeChange }) => {
4636
4762
  const from = numberOfElements > 0 ? offset + 1 : 0;
4637
4763
  const to = offset + numberOfElements;
4764
+ const hasTotal = totalPages != null;
4638
4765
  return /* @__PURE__ */ jsxs(Flex, {
4639
4766
  align: "center",
4640
4767
  justify: "space-between",
@@ -4682,8 +4809,9 @@ const DataTablePagination = ({ page, size, totalPages, totalElements, offset, nu
4682
4809
  ]
4683
4810
  }) }), /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Pagination, {
4684
4811
  size: "sm",
4685
- withEdges: true,
4686
- total: totalPages,
4812
+ withEdges: hasTotal,
4813
+ withPages: hasTotal,
4814
+ total: hasTotal ? totalPages : isLast !== false ? page : page + 1,
4687
4815
  value: page,
4688
4816
  onChange: onPageChange
4689
4817
  }) })]
@@ -4734,7 +4862,7 @@ const ColumnPicker = ({ columns, visibility, onVisibilityChange }) => {
4734
4862
  timingFunction: "ease"
4735
4863
  },
4736
4864
  children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(ActionButton, {
4737
- variant: "subtle",
4865
+ variant: "minimal",
4738
4866
  icon: IconColumns,
4739
4867
  onClick: () => setOpened((o) => !o)
4740
4868
  }) }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
@@ -4764,12 +4892,12 @@ const ColumnPicker = ({ columns, visibility, onVisibilityChange }) => {
4764
4892
  gap: 4,
4765
4893
  children: [/* @__PURE__ */ jsx(ActionButton, {
4766
4894
  size: "compact-xs",
4767
- variant: "subtle",
4895
+ variant: "minimal",
4768
4896
  onClick: handleShowAll,
4769
4897
  children: "All"
4770
4898
  }), /* @__PURE__ */ jsx(ActionButton, {
4771
4899
  size: "compact-xs",
4772
- variant: "subtle",
4900
+ variant: "minimal",
4773
4901
  onClick: handleDefault,
4774
4902
  children: "Default"
4775
4903
  })]
@@ -4835,7 +4963,7 @@ const FilterPicker = ({ schema, visibility, onVisibilityChange }) => {
4835
4963
  timingFunction: "ease"
4836
4964
  },
4837
4965
  children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(ActionButton, {
4838
- variant: "subtle",
4966
+ variant: "minimal",
4839
4967
  icon: IconFilter,
4840
4968
  onClick: () => setOpened((o) => !o)
4841
4969
  }) }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
@@ -4865,12 +4993,12 @@ const FilterPicker = ({ schema, visibility, onVisibilityChange }) => {
4865
4993
  gap: 4,
4866
4994
  children: [/* @__PURE__ */ jsx(ActionButton, {
4867
4995
  size: "compact-xs",
4868
- variant: "subtle",
4996
+ variant: "minimal",
4869
4997
  onClick: handleShowAll,
4870
4998
  children: "All"
4871
4999
  }), /* @__PURE__ */ jsx(ActionButton, {
4872
5000
  size: "compact-xs",
4873
- variant: "subtle",
5001
+ variant: "minimal",
4874
5002
  onClick: handleHideAll,
4875
5003
  children: "None"
4876
5004
  })]
@@ -4964,7 +5092,7 @@ const DataTableToolbar = ({ columns, filters, columnVisibility, filterVisibility
4964
5092
  onVisibilityChange: onColumnVisibilityChange
4965
5093
  }),
4966
5094
  withExport && /* @__PURE__ */ jsx(ActionButton, {
4967
- variant: "subtle",
5095
+ variant: "minimal",
4968
5096
  icon: IconDownload,
4969
5097
  menu: { items: [{
4970
5098
  label: "Export as CSV",
@@ -4987,7 +5115,7 @@ const DataTableToolbar = ({ columns, filters, columnVisibility, filterVisibility
4987
5115
  children: [selectedItems.length, " selected"]
4988
5116
  }),
4989
5117
  /* @__PURE__ */ jsx(ActionButton, {
4990
- variant: "subtle",
5118
+ variant: "minimal",
4991
5119
  size: "compact-sm",
4992
5120
  icon: IconX,
4993
5121
  onClick: onClearSelection,
@@ -5011,7 +5139,7 @@ const DataTableToolbar = ({ columns, filters, columnVisibility, filterVisibility
5011
5139
  ...props,
5012
5140
  children: props.label
5013
5141
  }, index) : props), /* @__PURE__ */ jsx(ActionButton, {
5014
- variant: "subtle",
5142
+ variant: "minimal",
5015
5143
  icon: IconRefresh,
5016
5144
  onClick: onRefresh
5017
5145
  })]
@@ -5120,12 +5248,16 @@ const FIT_STYLE = {
5120
5248
  };
5121
5249
  const DataTable = (props) => {
5122
5250
  const [items, setItems] = useState(typeof props.items === "function" ? { content: [] } : props.items);
5123
- const defaultSize = props.infinityScroll ? 100 : props.defaultSize || 10;
5251
+ const itemsRef = useRef(items);
5252
+ const [loaded, setLoaded] = useState(typeof props.items !== "function" || !props.submitOnInit);
5253
+ const defaultSize = props.defaultSize || (props.infinityScroll ? 100 : 10);
5124
5254
  const [page, setPage] = useState(1);
5125
5255
  const [size, setSize] = useState(String(defaultSize));
5126
5256
  const [currentPage, setCurrentPage] = useState(0);
5127
5257
  const alepha = useInject(Alepha);
5258
+ itemsRef.current = items;
5128
5259
  const sentinelRef = useRef(null);
5260
+ const debounceRef = useRef(null);
5129
5261
  const [columnVisibility, setColumnVisibility] = useState(() => {
5130
5262
  const entries = Object.entries(props.columns);
5131
5263
  let visibleCount = 0;
@@ -5198,13 +5330,14 @@ const DataTable = (props) => {
5198
5330
  }),
5199
5331
  handler: async (values) => {
5200
5332
  if (typeof props.items === "function") {
5201
- const response = await props.items(values, { items: items.content });
5333
+ const response = await props.items(values, { items: itemsRef.current.content });
5202
5334
  if (props.infinityScroll && values.page > 0) setItems((prev) => ({
5203
5335
  ...response,
5204
5336
  content: [...prev.content, ...response.content]
5205
5337
  }));
5206
5338
  else setItems(response);
5207
5339
  setCurrentPage(values.page);
5340
+ if (!loaded) setLoaded(true);
5208
5341
  }
5209
5342
  },
5210
5343
  onReset: async () => {
@@ -5224,9 +5357,23 @@ const DataTable = (props) => {
5224
5357
  return;
5225
5358
  }
5226
5359
  props.onFilterChange?.(key, value, form);
5360
+ if (props.skipSubmitOnChange) return;
5361
+ form.input.page.set(0);
5362
+ const delay = props.debounce ?? 300;
5363
+ if (delay > 0) {
5364
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5365
+ debounceRef.current = setTimeout(() => {
5366
+ form.submit();
5367
+ }, delay);
5368
+ } else await form.submit();
5227
5369
  }
5228
- }, [items]);
5370
+ }, []);
5229
5371
  const dt = useInject(DateTimeProvider);
5372
+ useEffect(() => {
5373
+ return () => {
5374
+ if (debounceRef.current) clearTimeout(debounceRef.current);
5375
+ };
5376
+ }, []);
5230
5377
  useEffect(() => {
5231
5378
  if (props.submitOnInit) form.submit();
5232
5379
  if (props.submitEvery) {
@@ -5291,9 +5438,9 @@ const DataTable = (props) => {
5291
5438
  }), col.sortable && /* @__PURE__ */ jsxs(Flex, {
5292
5439
  c: "dimmed",
5293
5440
  children: [
5294
- sortDir === "asc" && /* @__PURE__ */ jsx(IconArrowUp, { size: ui.sizes.icon.sm }),
5295
- sortDir === "desc" && /* @__PURE__ */ jsx(IconArrowDown, { size: ui.sizes.icon.sm }),
5296
- sortDir === null && /* @__PURE__ */ jsx(IconArrowsSort, { size: ui.sizes.icon.sm })
5441
+ sortDir === "asc" && /* @__PURE__ */ jsx(IconArrowUp, { size: ui.sizes.icon.xs }),
5442
+ sortDir === "desc" && /* @__PURE__ */ jsx(IconArrowDown, { size: ui.sizes.icon.xs }),
5443
+ sortDir === null && /* @__PURE__ */ jsx(IconArrowsSort, { size: ui.sizes.icon.xs })
5297
5444
  ]
5298
5445
  })]
5299
5446
  })
@@ -5353,9 +5500,15 @@ const DataTable = (props) => {
5353
5500
  form,
5354
5501
  alepha
5355
5502
  };
5503
+ const content = col.value?.(item, ctx);
5356
5504
  return /* @__PURE__ */ jsx(Table.Td, {
5357
5505
  style: col.fit ? FIT_STYLE : void 0,
5358
- children: col.value?.(item, ctx)
5506
+ children: col.action ? /* @__PURE__ */ jsx(ActionButton, {
5507
+ td: "inherit",
5508
+ unstyled: true,
5509
+ ...col.action(item),
5510
+ children: content
5511
+ }) : content
5359
5512
  }, key);
5360
5513
  }),
5361
5514
  props.rowActions && (() => {
@@ -5372,7 +5525,7 @@ const DataTable = (props) => {
5372
5525
  style: FIT_STYLE,
5373
5526
  onClick: (e) => e.stopPropagation(),
5374
5527
  children: /* @__PURE__ */ jsx(ActionButton, {
5375
- variant: "subtle",
5528
+ variant: "minimal",
5376
5529
  size: "xs",
5377
5530
  icon: IconDotsVertical,
5378
5531
  menu: { items: actions.map((action) => {
@@ -5380,7 +5533,10 @@ const DataTable = (props) => {
5380
5533
  return {
5381
5534
  label: action.label ?? (typeof action.tooltip === "string" ? action.tooltip : void 0),
5382
5535
  icon: Icon && isComponentType(Icon) ? /* @__PURE__ */ jsx(Icon, { size: 14 }) : Icon,
5383
- onClick: action.onClick,
5536
+ onClick: action.onClick ? async () => {
5537
+ await action.onClick();
5538
+ if (!action.skipRefresh) await form.submit();
5539
+ } : void 0,
5384
5540
  color: action.color
5385
5541
  };
5386
5542
  }) }
@@ -5442,19 +5598,26 @@ const DataTable = (props) => {
5442
5598
  bordered: true,
5443
5599
  elevated: true,
5444
5600
  shadowed: "xs",
5601
+ flex: 1,
5602
+ style: { minHeight: 0 },
5445
5603
  children: [
5446
5604
  /* @__PURE__ */ jsx(Flex, {
5447
5605
  className: "overflow-auto",
5606
+ flex: 1,
5607
+ style: { minHeight: 0 },
5608
+ col: true,
5448
5609
  children: /* @__PURE__ */ jsxs(Table, {
5449
5610
  "aria-label": "Data table",
5450
5611
  withRowBorders: true,
5451
5612
  highlightOnHover: true,
5452
5613
  ...props.tableProps,
5453
5614
  children: [/* @__PURE__ */ jsx(Table.Thead, {
5615
+ bdrs: "md",
5454
5616
  style: {
5455
5617
  position: "sticky",
5456
5618
  top: 0,
5457
- zIndex: 1
5619
+ zIndex: 1,
5620
+ backgroundColor: "var(--alepha-elevated)"
5458
5621
  },
5459
5622
  children: /* @__PURE__ */ jsxs(Table.Tr, { children: [
5460
5623
  panelConfig && /* @__PURE__ */ jsx(Table.Th, { style: { width: 36 } }),
@@ -5462,30 +5625,39 @@ const DataTable = (props) => {
5462
5625
  head,
5463
5626
  props.rowActions && /* @__PURE__ */ jsx(Table.Th, { style: FIT_STYLE })
5464
5627
  ] })
5465
- }), /* @__PURE__ */ jsxs(Table.Tbody, {
5466
- style: {
5467
- opacity: form.submitting ? .5 : 1,
5468
- transition: "opacity 150ms ease"
5469
- },
5470
- children: [rows, items.content.length === 0 && /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
5471
- colSpan: totalColumns || 1,
5472
- py: "xl",
5473
- style: { textAlign: "center" },
5628
+ }), /* @__PURE__ */ jsx(Table.Tbody, { children: !loaded || form.submitting ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
5629
+ colSpan: totalColumns || 1,
5630
+ py: "sm",
5631
+ children: /* @__PURE__ */ jsx(Flex, {
5632
+ justify: "center",
5633
+ p: "md",
5634
+ children: /* @__PURE__ */ jsx(Loader, {
5635
+ size: "sm",
5636
+ type: "dots"
5637
+ })
5638
+ })
5639
+ }) }) : rows.length === 0 ? /* @__PURE__ */ jsx(Table.Tr, { children: /* @__PURE__ */ jsx(Table.Td, {
5640
+ colSpan: totalColumns || 1,
5641
+ py: "xl",
5642
+ children: /* @__PURE__ */ jsx(Flex, {
5643
+ justify: "center",
5474
5644
  children: /* @__PURE__ */ jsx(Text, {
5475
5645
  c: "dimmed",
5476
5646
  size: "sm",
5477
- children: form.submitting ? "Loading…" : "No results"
5647
+ children: props.emptyLabel ?? "No results"
5478
5648
  })
5479
- }) })]
5480
- })]
5649
+ })
5650
+ }) }) : rows })]
5481
5651
  })
5482
5652
  }),
5483
5653
  props.infinityScroll && /* @__PURE__ */ jsx("div", { ref: sentinelRef }),
5484
5654
  !props.infinityScroll && /* @__PURE__ */ jsx(DataTablePagination, {
5485
5655
  page,
5486
5656
  size,
5487
- totalPages: items.page?.totalPages ?? 1,
5657
+ totalPages: items.page?.totalPages,
5488
5658
  totalElements: items.page?.totalElements,
5659
+ isFirst: items.page?.isFirst,
5660
+ isLast: items.page?.isLast,
5489
5661
  offset: items.page?.offset ?? 0,
5490
5662
  numberOfElements: items.content.length,
5491
5663
  onPageChange: (value) => {
@@ -5632,8 +5804,8 @@ const OPERATOR_INFO = {
5632
5804
  * Get the default icon for an input based on its type, format, or name.
5633
5805
  */
5634
5806
  const getDefaultIcon = (params) => {
5635
- const { type, format, name, isEnum, isArray, size = "sm" } = params;
5636
- const iconSize = ui.sizes.icon[size];
5807
+ const { type, format, name, isEnum, isArray, size = "xs" } = params;
5808
+ const iconSize = ui.sizes.icon[size] - 4;
5637
5809
  if (format) switch (format) {
5638
5810
  case "email": return /* @__PURE__ */ jsx(IconMail, { size: iconSize });
5639
5811
  case "url":
@@ -5746,5 +5918,5 @@ const AlephaUI = $module({
5746
5918
  });
5747
5919
 
5748
5920
  //#endregion
5749
- export { $ui, ActionButton, DashboardShell as AdminShell, DashboardShell, AlephaMantineProvider, AlephaUI, AlertDialog, AppBar, Breadcrumb as Breadcrumbs, BurgerButton, ClipboardButton, ConfirmDialog, Container, Control, ControlArray, ControlDate, ControlNumber, ControlObject, ControlQueryBuilder, ControlSelect, DarkModeButton, DataTable, DetailDrawer, DetailList, DialogService, Flex, Heading, JsonViewer, LanguageButton, OPERATOR_INFO, Omnibar, OmnibarButton, PromptDialog, Sidebar, ToggleSidebarButton as SidebarCollapseButton, StatCards, Text, ThemeButton, ThemeProvider, ToastService, TypeForm, UiRouter, alephaSidebarAtom, alephaThemeAtom, alephaThemeListAtom, alephaThemeOverridesAtom, capitalize, defaultTheme, dialogForm, dialogJson, extractSchemaFields, getDefaultIcon, getOperatorsForField, isComponentType, midnightTheme, prettyName, renderIcon, toTitleCase, ui, useDialog, useTheme, useToast };
5921
+ export { $ui, ActionButton, DashboardShell as AdminShell, DashboardShell, AlephaMantineProvider, AlephaUI, AlertDialog, AppBar, Breadcrumb as Breadcrumbs, BurgerButton, ClipboardButton, ConfirmDialog, Container, Control, ControlArray, ControlDate, ControlNumber, ControlObject, ControlQueryBuilder, ControlSelect, DarkModeButton, DataTable, DetailDrawer, DetailList, DialogService, Flex, Heading, JsonViewer, LanguageButton, OPERATOR_INFO, Omnibar, OmnibarButton, PromptDialog, Sidebar, ToggleSidebarButton as SidebarCollapseButton, SidebarCollapsedItem, SidebarItem, StatCards, Text, ThemeButton, ThemeProvider, ToastService, TypeForm, UiRouter, alephaSidebarAtom, alephaThemeAtom, alephaThemeListAtom, alephaThemeOverridesAtom, capitalize, defaultTheme, dialogForm, dialogJson, extractSchemaFields, getDefaultIcon, getOperatorsForField, isComponentType, midnightTheme, prettyName, renderIcon, toTitleCase, ui, useDialog, useTheme, useToast };
5750
5922
  //# sourceMappingURL=index.js.map