@alepha/ui 0.15.0 → 0.15.2

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 (207) hide show
  1. package/dist/admin/AdminAudits-BlGGKLof.js +3 -0
  2. package/dist/admin/{AdminAudits-DIrCCPk3.js → AdminAudits-C0DPYw0W.js} +4 -4
  3. package/dist/admin/AdminAudits-C0DPYw0W.js.map +1 -0
  4. package/dist/admin/AdminFiles-Bg9feLFH.js +3 -0
  5. package/dist/admin/{AdminFiles-RsL178Ta.js → AdminFiles-Cu8GHgQ3.js} +5 -5
  6. package/dist/admin/AdminFiles-Cu8GHgQ3.js.map +1 -0
  7. package/dist/admin/AdminLayout-BfeFXiul.js +3 -0
  8. package/dist/admin/{AdminLayout-XiSivwWH.js → AdminLayout-QJLIesuG.js} +1 -1
  9. package/dist/admin/{AdminLayout-XiSivwWH.js.map → AdminLayout-QJLIesuG.js.map} +1 -1
  10. package/dist/admin/{AdminNotifications-cIbywWKi.js → AdminNotifications-CgYkBuG_.js} +5 -5
  11. package/dist/admin/AdminNotifications-CgYkBuG_.js.map +1 -0
  12. package/dist/admin/AdminNotifications-DmfGPqHe.js +3 -0
  13. package/dist/admin/{AdminParameters-BKObzzpN.js → AdminParameters-Cl-R0nXt.js} +1 -1
  14. package/dist/admin/{AdminParameters-D-q3Qmhv.js → AdminParameters-hjNG_KXb.js} +4 -4
  15. package/dist/admin/AdminParameters-hjNG_KXb.js.map +1 -0
  16. package/dist/admin/{AdminSessions-vOgkrQ2U.js → AdminSessions-Bey9cuy1.js} +6 -6
  17. package/dist/admin/AdminSessions-Bey9cuy1.js.map +1 -0
  18. package/dist/admin/AdminSessions-Cn4_jB04.js +3 -0
  19. package/dist/admin/{AdminUserAudits-CSsN1fIC.js → AdminUserAudits-C7AN9jx7.js} +4 -4
  20. package/dist/admin/AdminUserAudits-C7AN9jx7.js.map +1 -0
  21. package/dist/admin/{AdminUserAudits-DmAnivo3.js → AdminUserAudits-Cp_ERd2g.js} +1 -1
  22. package/dist/admin/{AdminUserCreate-DpA13zwj.js → AdminUserCreate-Chr-7hLk.js} +1 -1
  23. package/dist/admin/{AdminUserCreate-B72nu-3W.js → AdminUserCreate-DiXi1EWB.js} +4 -4
  24. package/dist/admin/AdminUserCreate-DiXi1EWB.js.map +1 -0
  25. package/dist/admin/{AdminUserDetails-Zib_B6Al.js → AdminUserDetails-Dcn3OwMC.js} +1 -1
  26. package/dist/admin/{AdminUserDetails-CKM2IEMr.js → AdminUserDetails-yM4x8JE6.js} +5 -5
  27. package/dist/admin/AdminUserDetails-yM4x8JE6.js.map +1 -0
  28. package/dist/admin/{AdminUserLayout-BNBOEiAO.js → AdminUserLayout-CfeQHH6e.js} +1 -1
  29. package/dist/admin/{AdminUserLayout-D7En9UBq.js → AdminUserLayout-D9bqGt6T.js} +3 -3
  30. package/dist/admin/AdminUserLayout-D9bqGt6T.js.map +1 -0
  31. package/dist/admin/{AdminUserSessions-DEaGu6n6.js → AdminUserSessions-kmkXG-xf.js} +4 -4
  32. package/dist/admin/AdminUserSessions-kmkXG-xf.js.map +1 -0
  33. package/dist/admin/AdminUserSessions-rvA0ztxn.js +3 -0
  34. package/dist/admin/{AdminUserSettings-Di73D7g2.js → AdminUserSettings-BnzRAcqV.js} +4 -4
  35. package/dist/admin/AdminUserSettings-BnzRAcqV.js.map +1 -0
  36. package/dist/admin/AdminUserSettings-CXs-jtRv.js +3 -0
  37. package/dist/admin/{AdminUsers-BnGIRvmV.js → AdminUsers-CYkcUWCg.js} +6 -6
  38. package/dist/admin/AdminUsers-CYkcUWCg.js.map +1 -0
  39. package/dist/admin/AdminUsers-DdFXzrEn.js +3 -0
  40. package/dist/admin/index.d.ts +33 -18
  41. package/dist/admin/index.d.ts.map +1 -1
  42. package/dist/admin/index.js +48 -33
  43. package/dist/admin/index.js.map +1 -1
  44. package/dist/auth/{AuthLayout-B1sUB8fB.js → AuthLayout-Dj5K4SIN.js} +2 -2
  45. package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -0
  46. package/dist/{demo/IconGoogle-DvmFiEDB.js → auth/IconGoogle-DpSlPZ1u.js} +1 -1
  47. package/dist/auth/{IconGoogle-Cm5d8J3f.js.map → IconGoogle-DpSlPZ1u.js.map} +1 -1
  48. package/dist/auth/{Login-Cjxv3EDi.js → Login-BAFVcX_J.js} +6 -6
  49. package/dist/auth/Login-BAFVcX_J.js.map +1 -0
  50. package/dist/auth/Login-C5PUsp8I.js +4 -0
  51. package/dist/auth/{Register-CGlbQ50l.js → Register-CZRXEcWy.js} +7 -7
  52. package/dist/auth/Register-CZRXEcWy.js.map +1 -0
  53. package/dist/auth/Register-DMTs5ep_.js +4 -0
  54. package/dist/auth/ResetPassword-D-mhMtmx.js +3 -0
  55. package/dist/auth/{ResetPassword-DvqD_1SJ.js → ResetPassword-DTYNsBIj.js} +5 -5
  56. package/dist/auth/ResetPassword-DTYNsBIj.js.map +1 -0
  57. package/dist/auth/VerifyEmail-BsrCmncc.js +3 -0
  58. package/dist/auth/{VerifyEmail-VaBruOnO.js → VerifyEmail-DolENWGn.js} +4 -4
  59. package/dist/auth/VerifyEmail-DolENWGn.js.map +1 -0
  60. package/dist/auth/index.d.ts +34 -23
  61. package/dist/auth/index.d.ts.map +1 -1
  62. package/dist/auth/index.js +25 -14
  63. package/dist/auth/index.js.map +1 -1
  64. package/dist/core/index.d.ts +272 -209
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/index.js +314 -99
  67. package/dist/core/index.js.map +1 -1
  68. package/dist/demo/{DemoDataTable-2mzzf__a.js → DemoDataTable-CguplbR7.js} +2 -2
  69. package/dist/demo/{DemoDataTable-2mzzf__a.js.map → DemoDataTable-CguplbR7.js.map} +1 -1
  70. package/dist/demo/{DemoHome-CnuL5WV9.js → DemoHome-Cce2bWmg.js} +1 -1
  71. package/dist/demo/{DemoHome-CnuL5WV9.js.map → DemoHome-Cce2bWmg.js.map} +1 -1
  72. package/dist/demo/DemoHome-DC9qkMNe.js +3 -0
  73. package/dist/demo/DemoJsonViewer-DIssGVlJ.js +4 -0
  74. package/dist/demo/{DemoJsonViewer-NUGst5wW.js → DemoJsonViewer-Dgdk3Txb.js} +3 -3
  75. package/dist/demo/{DemoJsonViewer-NUGst5wW.js.map → DemoJsonViewer-Dgdk3Txb.js.map} +1 -1
  76. package/dist/demo/{DemoLayout-dvbeuBBf.js → DemoLayout-B20TEuhV.js} +2 -2
  77. package/dist/demo/DemoLayout-B20TEuhV.js.map +1 -0
  78. package/dist/demo/DemoLayout-DSRyf4qJ.js +3 -0
  79. package/dist/demo/{DemoLogin-DvltFTER.js → DemoLogin-mtkN6340.js} +7 -7
  80. package/dist/demo/DemoLogin-mtkN6340.js.map +1 -0
  81. package/dist/demo/{DemoRegister-Vu6ZPWib.js → DemoRegister-C0MW7anp.js} +8 -8
  82. package/dist/demo/DemoRegister-C0MW7anp.js.map +1 -0
  83. package/dist/demo/{DemoResetPassword-BFwmqwec.js → DemoResetPassword-CPTy88iK.js} +6 -6
  84. package/dist/demo/DemoResetPassword-CPTy88iK.js.map +1 -0
  85. package/dist/demo/{DemoSidebar-DWnjYHoP.js → DemoSidebar-MVmQKfMt.js} +2 -2
  86. package/dist/demo/{DemoSidebar-DWnjYHoP.js.map → DemoSidebar-MVmQKfMt.js.map} +1 -1
  87. package/dist/demo/{DemoTypeForm-P5_VInW2.js → DemoTypeForm-w-qtfRlC.js} +3 -3
  88. package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +1 -0
  89. package/dist/demo/{DemoVerifyEmail-C_ooC5u8.js → DemoVerifyEmail-C8FFJT5A.js} +5 -5
  90. package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +1 -0
  91. package/dist/{auth/IconGoogle-Cm5d8J3f.js → demo/IconGoogle-CbBF8Hqq.js} +1 -1
  92. package/dist/demo/{IconGoogle-DvmFiEDB.js.map → IconGoogle-CbBF8Hqq.js.map} +1 -1
  93. package/dist/demo/{Showcase-vemLuO2t.js → Showcase-CQrMWars.js} +4 -4
  94. package/dist/demo/Showcase-CQrMWars.js.map +1 -0
  95. package/dist/demo/index.d.ts +37 -27
  96. package/dist/demo/index.d.ts.map +1 -1
  97. package/dist/demo/index.js +26 -16
  98. package/dist/demo/index.js.map +1 -1
  99. package/dist/json/index.d.ts +17 -17
  100. package/dist/json/index.d.ts.map +1 -1
  101. package/package.json +13 -14
  102. package/src/admin/AdminRouter.ts +2 -2
  103. package/src/admin/MainRouter.ts +1 -1
  104. package/src/admin/components/audits/AdminAudits.tsx +3 -3
  105. package/src/admin/components/files/AdminFiles.tsx +2 -2
  106. package/src/admin/components/jobs/AdminJobs.tsx +2 -2
  107. package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
  108. package/src/admin/components/parameters/AdminParameters.tsx +1 -1
  109. package/src/admin/components/parameters/ParameterDetails.tsx +2 -2
  110. package/src/admin/components/parameters/ParameterHistory.tsx +1 -1
  111. package/src/admin/components/parameters/types.ts +9 -3
  112. package/src/admin/components/sessions/AdminSessions.tsx +3 -3
  113. package/src/admin/components/shared/AdminResourceHeader.tsx +1 -1
  114. package/src/admin/components/shared/AdminResourceTabs.tsx +1 -1
  115. package/src/admin/components/users/AdminUserAudits.tsx +3 -3
  116. package/src/admin/components/users/AdminUserCreate.tsx +3 -3
  117. package/src/admin/components/users/AdminUserDetails.tsx +4 -4
  118. package/src/admin/components/users/AdminUserLayout.tsx +2 -2
  119. package/src/admin/components/users/AdminUserSessions.tsx +3 -3
  120. package/src/admin/components/users/AdminUserSettings.tsx +3 -3
  121. package/src/admin/components/users/AdminUsers.tsx +3 -3
  122. package/src/admin/index.ts +16 -1
  123. package/src/auth/AuthI18n.ts +1 -1
  124. package/src/auth/AuthRouter.ts +2 -2
  125. package/src/auth/components/AuthLayout.tsx +1 -1
  126. package/src/auth/components/Login.tsx +4 -4
  127. package/src/auth/components/Register.tsx +5 -5
  128. package/src/auth/components/ResetPassword.tsx +4 -4
  129. package/src/auth/components/VerifyEmail.tsx +3 -3
  130. package/src/auth/components/buttons/UserButton.tsx +2 -2
  131. package/src/auth/index.ts +14 -3
  132. package/src/core/RootRouter.ts +1 -1
  133. package/src/core/atoms/alephaSidebarAtom.ts +57 -0
  134. package/src/core/components/buttons/ActionButton.tsx +9 -9
  135. package/src/core/components/buttons/BurgerButton.tsx +5 -4
  136. package/src/core/components/buttons/LanguageButton.tsx +1 -1
  137. package/src/core/components/buttons/OmnibarButton.tsx +20 -1
  138. package/src/core/components/buttons/ThemeButton.tsx +1 -1
  139. package/src/core/components/buttons/ToggleSidebarButton.tsx +33 -23
  140. package/src/core/components/form/Control.tsx +1 -1
  141. package/src/core/components/form/ControlArray.tsx +2 -2
  142. package/src/core/components/form/ControlDate.tsx +1 -1
  143. package/src/core/components/form/ControlNumber.tsx +2 -2
  144. package/src/core/components/form/ControlObject.tsx +1 -1
  145. package/src/core/components/form/ControlQueryBuilder.tsx +1 -1
  146. package/src/core/components/form/ControlSelect.tsx +1 -1
  147. package/src/core/components/form/TypeForm.tsx +2 -2
  148. package/src/core/components/layout/AdminShell.tsx +205 -27
  149. package/src/core/components/layout/AlephaMantineProvider.tsx +3 -3
  150. package/src/core/components/layout/Omnibar.tsx +2 -2
  151. package/src/core/components/layout/Sidebar.tsx +42 -77
  152. package/src/core/components/table/DataTable.tsx +2 -2
  153. package/src/core/components/table/DataTableFilters.tsx +1 -1
  154. package/src/core/components/table/types.ts +1 -1
  155. package/src/core/hooks/useDialog.ts +1 -1
  156. package/src/core/hooks/useTheme.ts +1 -1
  157. package/src/core/hooks/useToast.ts +1 -1
  158. package/src/core/index.ts +57 -6
  159. package/src/core/providers/ThemeProvider.ts +1 -1
  160. package/src/core/styles.css +58 -0
  161. package/src/core/utils/parseInput.ts +1 -1
  162. package/src/demo/DemoRouter.ts +1 -1
  163. package/src/demo/components/DemoLayout.tsx +1 -1
  164. package/src/demo/components/core/DemoTypeForm.tsx +1 -1
  165. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  166. package/src/demo/components/shared/Showcase.tsx +1 -1
  167. package/src/demo/index.ts +11 -1
  168. package/src/json/index.ts +13 -0
  169. package/dist/admin/AdminAudits-B3EhKhN7.js +0 -3
  170. package/dist/admin/AdminAudits-DIrCCPk3.js.map +0 -1
  171. package/dist/admin/AdminFiles-C8OG4dtD.js +0 -3
  172. package/dist/admin/AdminFiles-RsL178Ta.js.map +0 -1
  173. package/dist/admin/AdminLayout-BnSmtA4x.js +0 -3
  174. package/dist/admin/AdminNotifications-BSL4B2fQ.js +0 -3
  175. package/dist/admin/AdminNotifications-cIbywWKi.js.map +0 -1
  176. package/dist/admin/AdminParameters-D-q3Qmhv.js.map +0 -1
  177. package/dist/admin/AdminSessions-DHG9zPfr.js +0 -3
  178. package/dist/admin/AdminSessions-vOgkrQ2U.js.map +0 -1
  179. package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +0 -1
  180. package/dist/admin/AdminUserCreate-B72nu-3W.js.map +0 -1
  181. package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +0 -1
  182. package/dist/admin/AdminUserLayout-D7En9UBq.js.map +0 -1
  183. package/dist/admin/AdminUserSessions-D9X2_HMA.js +0 -3
  184. package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +0 -1
  185. package/dist/admin/AdminUserSettings-Di73D7g2.js.map +0 -1
  186. package/dist/admin/AdminUserSettings-yI-JECf5.js +0 -3
  187. package/dist/admin/AdminUsers-BnGIRvmV.js.map +0 -1
  188. package/dist/admin/AdminUsers-CG9-2Z8W.js +0 -3
  189. package/dist/auth/AuthLayout-B1sUB8fB.js.map +0 -1
  190. package/dist/auth/Login-BWi-pPbO.js +0 -4
  191. package/dist/auth/Login-Cjxv3EDi.js.map +0 -1
  192. package/dist/auth/Register-CGlbQ50l.js.map +0 -1
  193. package/dist/auth/Register-CWdkXWkc.js +0 -4
  194. package/dist/auth/ResetPassword-BUdM7T_R.js +0 -3
  195. package/dist/auth/ResetPassword-DvqD_1SJ.js.map +0 -1
  196. package/dist/auth/VerifyEmail-BYmtnkEl.js +0 -3
  197. package/dist/auth/VerifyEmail-VaBruOnO.js.map +0 -1
  198. package/dist/demo/DemoHome-D6Z7EE4V.js +0 -3
  199. package/dist/demo/DemoJsonViewer-CYUggLop.js +0 -4
  200. package/dist/demo/DemoLayout-ZFDzyvY3.js +0 -3
  201. package/dist/demo/DemoLayout-dvbeuBBf.js.map +0 -1
  202. package/dist/demo/DemoLogin-DvltFTER.js.map +0 -1
  203. package/dist/demo/DemoRegister-Vu6ZPWib.js.map +0 -1
  204. package/dist/demo/DemoResetPassword-BFwmqwec.js.map +0 -1
  205. package/dist/demo/DemoTypeForm-P5_VInW2.js.map +0 -1
  206. package/dist/demo/DemoVerifyEmail-C_ooC5u8.js.map +0 -1
  207. package/dist/demo/Showcase-vemLuO2t.js.map +0 -1
@@ -1,4 +1,3 @@
1
- import { useEvents } from "@alepha/react";
2
1
  import {
3
2
  ActionIcon,
4
3
  Badge,
@@ -14,6 +13,7 @@ import {
14
13
  import { IconFilter, IconInfoTriangle, IconX } from "@tabler/icons-react";
15
14
  import type { TObject } from "alepha";
16
15
  import { parseQueryString } from "alepha/orm";
16
+ import { useEvents } from "alepha/react";
17
17
  import { useRef, useState } from "react";
18
18
  import { ui } from "../../constants/ui.ts";
19
19
  import {
@@ -1,4 +1,3 @@
1
- import { useFormState } from "@alepha/react/form";
2
1
  import {
3
2
  Autocomplete,
4
3
  type AutocompleteProps,
@@ -13,6 +12,7 @@ import {
13
12
  TagsInput,
14
13
  type TagsInputProps,
15
14
  } from "@mantine/core";
15
+ import { useFormState } from "alepha/react/form";
16
16
  import { useEffect, useState } from "react";
17
17
  import {
18
18
  type GenericControlProps,
@@ -1,6 +1,6 @@
1
- import type { FormModel } from "@alepha/react/form";
2
1
  import { Card, Flex, type FlexProps, Grid } from "@mantine/core";
3
2
  import type { TObject } from "alepha";
3
+ import type { FormModel } from "alepha/react/form";
4
4
  import type { ReactNode } from "react";
5
5
  import ActionButton, {
6
6
  type ActionSubmitButtonProps,
@@ -55,7 +55,7 @@ export interface TypeFormProps<T extends TObject> {
55
55
  * @example
56
56
  * ```tsx
57
57
  * import { t } from "alepha";
58
- * import { useForm } from "@alepha/react/form";
58
+ * import { useForm } from "alepha/react/form";
59
59
  * import { TypeForm } from "@alepha/ui";
60
60
  *
61
61
  * const form = useForm({
@@ -1,5 +1,3 @@
1
- import { useEvents, useStore } from "@alepha/react";
2
- import { NestedView, useRouter } from "@alepha/react/router";
3
1
  import {
4
2
  AppShell,
5
3
  type AppShellFooterProps,
@@ -7,8 +5,18 @@ import {
7
5
  type AppShellMainProps,
8
6
  type AppShellNavbarProps,
9
7
  type AppShellProps,
8
+ Flex,
10
9
  } from "@mantine/core";
11
- import { type ReactNode, useState } from "react";
10
+ import { useEvents, useStore } from "alepha/react";
11
+ import { NestedView, useRouter } from "alepha/react/router";
12
+ import {
13
+ type ReactNode,
14
+ useCallback,
15
+ useEffect,
16
+ useRef,
17
+ useState,
18
+ } from "react";
19
+ import { alephaSidebarAtom } from "../../atoms/alephaSidebarAtom.ts";
12
20
  import { ui } from "../../constants/ui.ts";
13
21
  import AppBar, { type AppBarProps } from "./AppBar.tsx";
14
22
  import { Sidebar, type SidebarProps } from "./Sidebar.tsx";
@@ -25,6 +33,12 @@ export interface AdminShellProps {
25
33
  footer?: ReactNode;
26
34
  children?: ReactNode;
27
35
 
36
+ /**
37
+ * Enable drag-to-resize for the sidebar.
38
+ * Width and constraints are configured in alephaSidebarAtom.
39
+ */
40
+ resizable?: boolean;
41
+
28
42
  noSidebarWhen?: {
29
43
  /**
30
44
  * Paths where the sidebar should be hidden.
@@ -33,28 +47,143 @@ export interface AdminShellProps {
33
47
  };
34
48
  }
35
49
 
36
- declare module "alepha" {
37
- interface State {
38
- /**
39
- * Whether the sidebar is opened or closed.
40
- */
41
- "alepha.ui.sidebar.opened"?: boolean;
42
-
43
- /**
44
- * Whether the sidebar is collapsed (narrow) or expanded (wide).
45
- */
46
- "alepha.ui.sidebar.collapsed"?: boolean;
47
- }
48
- }
49
-
50
50
  const AdminShell = (props: AdminShellProps) => {
51
51
  const router = useRouter();
52
- const [opened, setOpened] = useStore("alepha.ui.sidebar.opened");
53
- const [collapsed] = useStore(
54
- "alepha.ui.sidebar.collapsed",
55
- props.sidebarProps?.collapsed,
52
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
53
+ const { opened, collapsed } = sidebar;
54
+
55
+ // Initialize collapsed state from props on mount
56
+ useEffect(() => {
57
+ if (props.sidebarProps?.collapsed !== undefined) {
58
+ setSidebar({ ...sidebar, collapsed: props.sidebarProps.collapsed });
59
+ }
60
+ }, []);
61
+
62
+ // Resize state
63
+ const [isResizing, setIsResizing] = useState(false);
64
+ const [isHovering, setIsHovering] = useState(false);
65
+ const [collapseEffect, setCollapseEffect] = useState({
66
+ offset: 0,
67
+ opacity: 1,
68
+ });
69
+ const resizeRef = useRef<{ startX: number; startWidth: number } | null>(null);
70
+
71
+ // Use atom values for constraints
72
+ const {
73
+ collapsedWidth,
74
+ collapseThreshold,
75
+ maxWidth,
76
+ hoverDelay,
77
+ defaultWidth,
78
+ } = sidebar;
79
+
80
+ const handleResizeStart = useCallback(
81
+ (e: React.MouseEvent) => {
82
+ if (!props.resizable) return;
83
+ e.preventDefault();
84
+
85
+ // If collapsed and hovering, un-collapse first and start from defaultWidth
86
+ if (collapsed) {
87
+ setSidebar({ ...sidebar, collapsed: false, width: defaultWidth });
88
+ setIsResizing(true);
89
+ resizeRef.current = {
90
+ startX: e.clientX,
91
+ startWidth: defaultWidth,
92
+ };
93
+ } else {
94
+ setIsResizing(true);
95
+ resizeRef.current = {
96
+ startX: e.clientX,
97
+ startWidth: sidebar.width,
98
+ };
99
+ }
100
+ },
101
+ [props.resizable, collapsed, sidebar, setSidebar, defaultWidth],
56
102
  );
57
103
 
104
+ useEffect(() => {
105
+ if (!isResizing) return;
106
+
107
+ const handleMouseMove = (e: MouseEvent) => {
108
+ if (!resizeRef.current) return;
109
+ const delta = e.clientX - resizeRef.current.startX;
110
+ const rawWidth = resizeRef.current.startWidth + delta;
111
+ const newWidth = Math.min(Math.max(rawWidth, collapsedWidth), maxWidth);
112
+
113
+ // Visual effect when below collapse threshold
114
+ if (rawWidth < collapseThreshold) {
115
+ const progress = Math.max(
116
+ 0,
117
+ (collapseThreshold - rawWidth) / collapseThreshold,
118
+ );
119
+ setCollapseEffect({
120
+ offset: -progress * collapsedWidth,
121
+ opacity: 1 - progress * 0.7,
122
+ });
123
+ setSidebar({ ...sidebar, width: collapseThreshold, collapsed: false });
124
+ } else {
125
+ setCollapseEffect({ offset: 0, opacity: 1 });
126
+ setSidebar({ ...sidebar, width: newWidth, collapsed: false });
127
+ }
128
+ };
129
+
130
+ const handleMouseUp = () => {
131
+ // If we released while in collapse zone, actually collapse
132
+ if (collapseEffect.offset < 0) {
133
+ setSidebar({ ...sidebar, collapsed: true });
134
+ }
135
+ setCollapseEffect({ offset: 0, opacity: 1 });
136
+ setIsResizing(false);
137
+ resizeRef.current = null;
138
+ };
139
+
140
+ document.addEventListener("mousemove", handleMouseMove);
141
+ document.addEventListener("mouseup", handleMouseUp);
142
+
143
+ return () => {
144
+ document.removeEventListener("mousemove", handleMouseMove);
145
+ document.removeEventListener("mouseup", handleMouseUp);
146
+ };
147
+ }, [
148
+ isResizing,
149
+ sidebar,
150
+ setSidebar,
151
+ collapsedWidth,
152
+ maxWidth,
153
+ collapseThreshold,
154
+ collapseEffect.offset,
155
+ ]);
156
+
157
+ // Hover to expand when collapsed (with delay)
158
+ const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
159
+
160
+ const handleNavbarMouseEnter = useCallback(() => {
161
+ if (collapsed) {
162
+ hoverTimeoutRef.current = setTimeout(() => {
163
+ setIsHovering(true);
164
+ }, hoverDelay);
165
+ }
166
+ }, [collapsed, hoverDelay]);
167
+
168
+ const handleNavbarMouseLeave = useCallback(() => {
169
+ if (hoverTimeoutRef.current) {
170
+ clearTimeout(hoverTimeoutRef.current);
171
+ hoverTimeoutRef.current = null;
172
+ }
173
+ setIsHovering(false);
174
+ }, []);
175
+
176
+ // Reset hover state when collapsed changes (e.g., when toggle button is clicked)
177
+ useEffect(() => {
178
+ if (collapsed) {
179
+ setIsHovering(false);
180
+ if (hoverTimeoutRef.current) {
181
+ clearTimeout(hoverTimeoutRef.current);
182
+ hoverTimeoutRef.current = null;
183
+ }
184
+ }
185
+ }, [collapsed]);
186
+
58
187
  const shouldShowSidebar = () => {
59
188
  if (props.noSidebarWhen?.paths) {
60
189
  for (const path of props.noSidebarWhen.paths) {
@@ -78,10 +207,10 @@ const AdminShell = (props: AdminShellProps) => {
78
207
  setShowSidebar(shouldShowSidebar());
79
208
  },
80
209
  "react:transition:begin": () => {
81
- setOpened(false);
210
+ setSidebar({ ...sidebar, opened: false });
82
211
  },
83
212
  },
84
- [],
213
+ [sidebar],
85
214
  );
86
215
 
87
216
  // Default AppBar items with burger button on the left
@@ -94,7 +223,20 @@ const AdminShell = (props: AdminShellProps) => {
94
223
 
95
224
  const headerHeight = hasAppBar ? 60 : 0;
96
225
  const footerHeight = props.footer ? 24 : 0;
97
- const sidebarWidth = hasSidebar ? (collapsed ? 78 : 300) : 0;
226
+ const expandedWidth = Math.max(sidebar.width, collapsedWidth);
227
+
228
+ // When collapsed but hovering, show defaultWidth (not current width)
229
+ const isExpandedByHover = collapsed && isHovering;
230
+ const effectiveCollapsed = collapsed && !isHovering;
231
+ const hoverWidth = Math.max(defaultWidth, collapsedWidth);
232
+ const sidebarWidth = hasSidebar
233
+ ? effectiveCollapsed
234
+ ? collapsedWidth
235
+ : isExpandedByHover
236
+ ? hoverWidth
237
+ : expandedWidth
238
+ : 0;
239
+ const canResize = props.resizable && !collapsed;
98
240
 
99
241
  return (
100
242
  <AppShell
@@ -105,7 +247,11 @@ const AdminShell = (props: AdminShellProps) => {
105
247
  navbar={
106
248
  hasSidebar
107
249
  ? {
108
- width: collapsed ? { base: 78 } : { base: 300 },
250
+ width: effectiveCollapsed
251
+ ? { base: collapsedWidth }
252
+ : isExpandedByHover
253
+ ? { base: hoverWidth }
254
+ : { base: expandedWidth },
109
255
  breakpoint: "sm",
110
256
  collapsed: { mobile: !opened },
111
257
  }
@@ -121,8 +267,38 @@ const AdminShell = (props: AdminShellProps) => {
121
267
  </AppShell.Header>
122
268
 
123
269
  {hasSidebar && (
124
- <AppShell.Navbar bg={ui.colors.surface} {...props.appShellNavbarProps}>
125
- <Sidebar collapsed={collapsed} {...(props.sidebarProps ?? {})} />
270
+ <AppShell.Navbar
271
+ bg={ui.colors.surface}
272
+ className="alepha-sidebar-navbar"
273
+ data-resizing={isResizing}
274
+ onMouseEnter={handleNavbarMouseEnter}
275
+ onMouseLeave={handleNavbarMouseLeave}
276
+ style={{
277
+ transform: collapseEffect.offset
278
+ ? `translateX(${collapseEffect.offset}px)`
279
+ : undefined,
280
+ opacity: collapseEffect.opacity,
281
+ }}
282
+ {...props.appShellNavbarProps}
283
+ >
284
+ <Sidebar
285
+ {...(props.sidebarProps ?? {})}
286
+ collapsed={effectiveCollapsed}
287
+ />
288
+ {(canResize || isExpandedByHover) && (
289
+ <Flex
290
+ pos="absolute"
291
+ right={-2}
292
+ top={0}
293
+ bottom={0}
294
+ w={4}
295
+ style={{
296
+ cursor: "col-resize",
297
+ userSelect: "none",
298
+ }}
299
+ onMouseDown={handleResizeStart}
300
+ />
301
+ )}
126
302
  </AppShell.Navbar>
127
303
  )}
128
304
 
@@ -134,6 +310,8 @@ const AdminShell = (props: AdminShellProps) => {
134
310
  display={"flex"}
135
311
  flex={1}
136
312
  style={{ flexDirection: "column" }}
313
+ className="alepha-sidebar-main"
314
+ data-resizing={isResizing}
137
315
  {...props.appShellMainProps}
138
316
  >
139
317
  {props.children ?? <NestedView />}
@@ -1,6 +1,3 @@
1
- import { useEvents } from "@alepha/react";
2
- import { FormValidationError } from "@alepha/react/form";
3
- import { NestedView } from "@alepha/react/router";
4
1
  import {
5
2
  ColorSchemeScript,
6
3
  type ColorSchemeScriptProps,
@@ -12,6 +9,9 @@ import { Notifications, type NotificationsProps } from "@mantine/notifications";
12
9
  import type { NavigationProgressProps } from "@mantine/nprogress";
13
10
  import { NavigationProgress, nprogress } from "@mantine/nprogress";
14
11
  import { TypeBoxError } from "alepha";
12
+ import { useEvents } from "alepha/react";
13
+ import { FormValidationError } from "alepha/react/form";
14
+ import { NestedView } from "alepha/react/router";
15
15
  import type { ReactNode } from "react";
16
16
  import { useTheme } from "../../hooks/useTheme.ts";
17
17
  import { useToast } from "../../hooks/useToast.ts";
@@ -1,7 +1,7 @@
1
- import { useStore } from "@alepha/react";
2
- import { useRouter } from "@alepha/react/router";
3
1
  import { Spotlight, type SpotlightActionData } from "@mantine/spotlight";
4
2
  import { IconSearch } from "@tabler/icons-react";
3
+ import { useStore } from "alepha/react";
4
+ import { useRouter } from "alepha/react/router";
5
5
  import { type ReactNode, useMemo } from "react";
6
6
  import { ui } from "../../constants/ui.ts";
7
7
  import { renderIcon } from "../../helpers/renderIcon.tsx";
@@ -1,5 +1,3 @@
1
- import { useEvents } from "@alepha/react";
2
- import { useRouter } from "@alepha/react/router";
3
1
  import {
4
2
  Flex,
5
3
  type FlexProps,
@@ -11,6 +9,8 @@ import {
11
9
  IconChevronRight,
12
10
  IconSquareRounded,
13
11
  } from "@tabler/icons-react";
12
+ import { useEvents } from "alepha/react";
13
+ import { useRouter } from "alepha/react/router";
14
14
  import {
15
15
  type ComponentType,
16
16
  type ReactNode,
@@ -49,44 +49,51 @@ export const Sidebar = (props: SidebarProps) => {
49
49
  const router = useRouter();
50
50
  const { onItemClick } = props;
51
51
 
52
+ const divider = (key: string | number) => {
53
+ return (
54
+ <Flex
55
+ key={key}
56
+ h={1}
57
+ bg={"var(--alepha-border)"}
58
+ my={"xs"}
59
+ mx={props.collapsed ? 0 : "sm"}
60
+ />
61
+ );
62
+ };
63
+
52
64
  const renderNode = (item: SidebarNode, key: number) => {
53
65
  if ("type" in item) {
66
+ // Hide spacers when collapsed
54
67
  if (item.type === "spacer") {
68
+ if (props.collapsed) return null;
55
69
  return <Flex key={key} h={16} />;
56
70
  }
57
71
 
58
72
  if (item.type === "divider") {
59
- return (
60
- <Flex
61
- key={key}
62
- h={1}
63
- bg={"var(--alepha-border)"}
64
- my={"md"}
65
- mx={"sm"}
66
- />
67
- );
73
+ return divider(key);
68
74
  }
69
75
 
70
76
  if (item.type === "search") {
71
- return <OmnibarButton collapsed={props.collapsed} key={key} />;
77
+ return (
78
+ <Flex key={key} mb="xs">
79
+ <OmnibarButton collapsed={props.collapsed} />
80
+ </Flex>
81
+ );
72
82
  }
73
83
 
74
84
  if (item.type === "toggle") {
75
85
  return <ToggleSidebarButton key={key} />;
76
86
  }
77
87
 
88
+ // Replace sections with dividers when collapsed
78
89
  if (item.type === "section") {
79
- if (props.collapsed) return;
90
+ if (props.collapsed) {
91
+ return divider(key);
92
+ }
80
93
  return (
81
- <Flex key={key} mt={"md"} mb={"xs"} align={"center"} gap={"xs"}>
94
+ <Flex key={key} mt={"md"} align={"center"} gap={"xs"}>
82
95
  {renderIcon(item.icon)}
83
- <Text
84
- key={key}
85
- size={"xs"}
86
- c={"dimmed"}
87
- tt={"uppercase"}
88
- fw={"bold"}
89
- >
96
+ <Text size={"xs"} c={"dimmed"} tt={"uppercase"} fw={"bold"}>
90
97
  {item.label}
91
98
  </Text>
92
99
  </Flex>
@@ -150,15 +157,18 @@ export const Sidebar = (props: SidebarProps) => {
150
157
  };
151
158
 
152
159
  const padding = "md";
153
- const gap = props.items ? props.gap : "xs";
154
- const menu = useMemo(() => getSidebarNodes(), []);
160
+ const gap = props.items ? (props.gap ?? 2) : "xs";
161
+ const menu = useMemo(
162
+ () => getSidebarNodes(),
163
+ [props.items, props.autoPopulateMenu],
164
+ );
155
165
 
156
166
  return (
157
167
  <Flex
158
168
  flex={1}
159
169
  py={padding}
160
170
  direction={"column"}
161
- className={"overflow-auto"}
171
+ className="alepha-sidebar-scroll"
162
172
  {...props.flexProps}
163
173
  >
164
174
  <Flex gap={gap} px={padding} direction={"column"}>
@@ -171,7 +181,7 @@ export const Sidebar = (props: SidebarProps) => {
171
181
  px={padding}
172
182
  direction={"column"}
173
183
  flex={1}
174
- className={"overflow-auto"}
184
+ className="alepha-sidebar-scroll"
175
185
  >
176
186
  {menu
177
187
  .filter((it) => !it.position)
@@ -332,34 +342,9 @@ export interface SidebarItemProps {
332
342
  const SidebarCollapsedItem = (props: SidebarItemProps) => {
333
343
  const { item, level } = props;
334
344
 
335
- const router = useRouter();
336
- const isActive = useCallback((item: SidebarMenuItem): boolean => {
337
- if (!item.children) return false;
338
- for (const child of item.children) {
339
- if (child.href) {
340
- if (router.isActive(child.href)) {
341
- return true;
342
- }
343
- }
344
- if (isActive(child)) {
345
- return true;
346
- }
347
- }
348
- return false;
349
- }, []);
350
-
351
- const [isOpen, setIsOpen] = useState<boolean>(isActive(item));
352
-
353
- const handleItemClick = (e: MouseEvent) => {
354
- if (!props.item.target) {
355
- e.preventDefault();
356
- }
357
- if (item.children && item.children.length > 0) {
358
- setIsOpen(!isOpen);
359
- } else {
360
- props.onItemClick?.(item);
361
- item.onClick?.();
362
- }
345
+ const handleItemClick = () => {
346
+ props.onItemClick?.(item);
347
+ item.onClick?.();
363
348
  };
364
349
 
365
350
  return (
@@ -371,35 +356,15 @@ const SidebarCollapsedItem = (props: SidebarItemProps) => {
371
356
  }
372
357
  variant={"subtle"}
373
358
  variantActive={"default"}
374
- tooltip={
375
- item.children
376
- ? undefined
377
- : {
378
- label: item.label,
379
- position: "right",
380
- }
381
- }
359
+ tooltip={{
360
+ label: item.label,
361
+ position: "right",
362
+ }}
382
363
  radius={props.item.theme?.radius ?? props.theme.button?.radius ?? "md"}
383
364
  onClick={handleItemClick}
384
365
  icon={renderIcon(item.icon) ?? <IconSquareRounded />}
385
366
  href={props.item.href as any}
386
367
  target={props.item.target}
387
- menu={
388
- item.children
389
- ? ({
390
- position: "right",
391
- on: "hover",
392
- items: item.children
393
- .filter((child) => !child.can || child.can())
394
- .map((child) => ({
395
- label: child.label,
396
- href: child.href,
397
- icon: renderIcon(child.icon),
398
- children: child.children?.filter((c) => !c.can || c.can()),
399
- })),
400
- } as any)
401
- : undefined
402
- }
403
368
  {...props.item.actionProps}
404
369
  />
405
370
  );
@@ -1,5 +1,3 @@
1
- import { useInject } from "@alepha/react";
2
- import { type FormModel, useForm } from "@alepha/react/form";
3
1
  import { Checkbox, Flex, Table, Text, UnstyledButton } from "@mantine/core";
4
2
  import { useDebouncedCallback } from "@mantine/hooks";
5
3
  import {
@@ -9,6 +7,8 @@ import {
9
7
  } from "@tabler/icons-react";
10
8
  import { Alepha, type Static, type TObject, t } from "alepha";
11
9
  import { DateTimeProvider } from "alepha/datetime";
10
+ import { useInject } from "alepha/react";
11
+ import { type FormModel, useForm } from "alepha/react/form";
12
12
  import { useCallback, useEffect, useMemo, useState } from "react";
13
13
  import { ui } from "../../constants/ui.ts";
14
14
  import DataTableFilters, {
@@ -1,6 +1,6 @@
1
- import type { FormModel } from "@alepha/react/form";
2
1
  import { Flex } from "@mantine/core";
3
2
  import { type TObject, t } from "alepha";
3
+ import type { FormModel } from "alepha/react/form";
4
4
  import { useMemo } from "react";
5
5
  import { ui } from "../../constants/ui.ts";
6
6
  import TypeForm, { type TypeFormProps } from "../form/TypeForm.tsx";
@@ -1,4 +1,3 @@
1
- import type { FormModel } from "@alepha/react/form";
2
1
  import type { TableProps, TableTrProps } from "@mantine/core";
3
2
  import type {
4
3
  Alepha,
@@ -9,6 +8,7 @@ import type {
9
8
  TObject,
10
9
  } from "alepha";
11
10
  import type { DurationLike } from "alepha/datetime";
11
+ import type { FormModel } from "alepha/react/form";
12
12
  import type { ReactNode } from "react";
13
13
  import type { ActionProps } from "../buttons/ActionButton.tsx";
14
14
  import type { TypeFormProps } from "../form/TypeForm.tsx";
@@ -1,4 +1,4 @@
1
- import { useInject } from "@alepha/react";
1
+ import { useInject } from "alepha/react";
2
2
  import { DialogService } from "../services/DialogService.tsx";
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { useInject, useStore } from "@alepha/react";
1
+ import { useInject, useStore } from "alepha/react";
2
2
  import {
3
3
  alephaThemeAtom,
4
4
  type CurrentAlephaTheme,
@@ -1,4 +1,4 @@
1
- import { useInject } from "@alepha/react";
1
+ import { useInject } from "alepha/react";
2
2
  import { ToastService } from "../services/ToastService.tsx";
3
3
 
4
4
  /**