@alepha/ui 0.15.1 → 0.15.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 (199) hide show
  1. package/dist/admin/AdminAudits-BU-p1g7A.js +3 -0
  2. package/dist/admin/{AdminAudits-DClGEVBj.js → AdminAudits-Oh7iAfQa.js} +5 -5
  3. package/dist/admin/AdminAudits-Oh7iAfQa.js.map +1 -0
  4. package/dist/admin/AdminFiles-Bg9feLFH.js +3 -0
  5. package/dist/admin/{AdminFiles-C76r1_Xz.js → AdminFiles-Cu8GHgQ3.js} +3 -3
  6. package/dist/admin/AdminFiles-Cu8GHgQ3.js.map +1 -0
  7. package/dist/admin/{AdminNotifications-Bsalygm5.js → AdminNotifications-CgYkBuG_.js} +3 -3
  8. package/dist/admin/AdminNotifications-CgYkBuG_.js.map +1 -0
  9. package/dist/admin/AdminNotifications-DmfGPqHe.js +3 -0
  10. package/dist/admin/{AdminParameters-CpmAWwqN.js → AdminParameters-Cl-R0nXt.js} +1 -1
  11. package/dist/admin/{AdminParameters-Bmxtnpv-.js → AdminParameters-hjNG_KXb.js} +4 -4
  12. package/dist/admin/AdminParameters-hjNG_KXb.js.map +1 -0
  13. package/dist/admin/{AdminSessions-DmK3R6pP.js → AdminSessions-Bey9cuy1.js} +4 -4
  14. package/dist/admin/AdminSessions-Bey9cuy1.js.map +1 -0
  15. package/dist/admin/AdminSessions-Cn4_jB04.js +3 -0
  16. package/dist/admin/{AdminUserAudits-BPMP1Qd2.js → AdminUserAudits-C7AN9jx7.js} +4 -4
  17. package/dist/admin/AdminUserAudits-C7AN9jx7.js.map +1 -0
  18. package/dist/admin/{AdminUserAudits-Brcenss9.js → AdminUserAudits-Cp_ERd2g.js} +1 -1
  19. package/dist/admin/{AdminUserCreate-CZjB6NKc.js → AdminUserCreate-BVIm4JdN.js} +5 -5
  20. package/dist/admin/AdminUserCreate-BVIm4JdN.js.map +1 -0
  21. package/dist/admin/{AdminUserCreate-Cx8bkYC2.js → AdminUserCreate-C1aInRDk.js} +1 -1
  22. package/dist/admin/{AdminUserDetails-8TYsqQBy.js → AdminUserDetails-Dcn3OwMC.js} +1 -1
  23. package/dist/admin/{AdminUserDetails-DuqCOBJK.js → AdminUserDetails-yM4x8JE6.js} +5 -5
  24. package/dist/admin/AdminUserDetails-yM4x8JE6.js.map +1 -0
  25. package/dist/admin/{AdminUserLayout-Dgk8s7Cd.js → AdminUserLayout-BnfBC1gD.js} +4 -4
  26. package/dist/admin/AdminUserLayout-BnfBC1gD.js.map +1 -0
  27. package/dist/admin/{AdminUserLayout-Bz2u_zQ4.js → AdminUserLayout-gb-nbggz.js} +1 -1
  28. package/dist/admin/{AdminUserSessions-DCpe8_T6.js → AdminUserSessions-kmkXG-xf.js} +4 -4
  29. package/dist/admin/AdminUserSessions-kmkXG-xf.js.map +1 -0
  30. package/dist/admin/AdminUserSessions-rvA0ztxn.js +3 -0
  31. package/dist/admin/{AdminUserSettings-qxDfowqh.js → AdminUserSettings-DZ9iWhJW.js} +5 -5
  32. package/dist/admin/AdminUserSettings-DZ9iWhJW.js.map +1 -0
  33. package/dist/admin/AdminUserSettings-Dg-wTRzN.js +3 -0
  34. package/dist/admin/{AdminUsers-ZlPsDz0T.js → AdminUsers-D6Y5K8Am.js} +5 -5
  35. package/dist/admin/AdminUsers-D6Y5K8Am.js.map +1 -0
  36. package/dist/admin/AdminUsers-RCaxccEW.js +3 -0
  37. package/dist/admin/index.d.ts +55 -36
  38. package/dist/admin/index.d.ts.map +1 -1
  39. package/dist/admin/index.js +54 -32
  40. package/dist/admin/index.js.map +1 -1
  41. package/dist/auth/{AuthLayout-CWzQ8rCe.js → AuthLayout-Dj5K4SIN.js} +2 -2
  42. package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -0
  43. package/dist/auth/{Login-CyvKwy5e.js → Login-BBqTosqZ.js} +6 -6
  44. package/dist/auth/Login-BBqTosqZ.js.map +1 -0
  45. package/dist/auth/Login-CoU63mMR.js +4 -0
  46. package/dist/auth/Profile-Bxj8Nwom.js +150 -0
  47. package/dist/auth/Profile-Bxj8Nwom.js.map +1 -0
  48. package/dist/auth/Register-BV_oa_AK.js +4 -0
  49. package/dist/auth/{Register-C7Zp09Ks.js → Register-Ce675Crg.js} +8 -8
  50. package/dist/auth/Register-Ce675Crg.js.map +1 -0
  51. package/dist/auth/ResetPassword-D5wC8GAA.js +3 -0
  52. package/dist/auth/{ResetPassword-DYJSUC6B.js → ResetPassword-DWdt7c40.js} +5 -5
  53. package/dist/auth/ResetPassword-DWdt7c40.js.map +1 -0
  54. package/dist/auth/{VerifyEmail-CNXFIwWW.js → VerifyEmail-CI4JwByV.js} +4 -4
  55. package/dist/auth/VerifyEmail-CI4JwByV.js.map +1 -0
  56. package/dist/auth/VerifyEmail-DAfqVm5s.js +3 -0
  57. package/dist/auth/index.d.ts +36 -20
  58. package/dist/auth/index.d.ts.map +1 -1
  59. package/dist/auth/index.js +62 -17
  60. package/dist/auth/index.js.map +1 -1
  61. package/dist/core/index.d.ts +216 -71
  62. package/dist/core/index.d.ts.map +1 -1
  63. package/dist/core/index.js +733 -445
  64. package/dist/core/index.js.map +1 -1
  65. package/dist/demo/{DemoDataTable-DYbDYbs5.js → DemoDataTable-CguplbR7.js} +2 -2
  66. package/dist/demo/{DemoDataTable-DYbDYbs5.js.map → DemoDataTable-CguplbR7.js.map} +1 -1
  67. package/dist/demo/DemoJsonViewer-DIssGVlJ.js +4 -0
  68. package/dist/demo/{DemoJsonViewer-D_Hff1Q2.js → DemoJsonViewer-Dgdk3Txb.js} +3 -3
  69. package/dist/demo/{DemoJsonViewer-D_Hff1Q2.js.map → DemoJsonViewer-Dgdk3Txb.js.map} +1 -1
  70. package/dist/demo/{DemoLayout-DjIDm93B.js → DemoLayout-B20TEuhV.js} +2 -2
  71. package/dist/demo/DemoLayout-B20TEuhV.js.map +1 -0
  72. package/dist/demo/DemoLayout-DSRyf4qJ.js +3 -0
  73. package/dist/demo/{DemoLogin-BA_HiIRZ.js → DemoLogin-S-b15cmE.js} +7 -7
  74. package/dist/demo/DemoLogin-S-b15cmE.js.map +1 -0
  75. package/dist/demo/{DemoRegister-B6syaxP9.js → DemoRegister-B29MdAaZ.js} +9 -9
  76. package/dist/demo/DemoRegister-B29MdAaZ.js.map +1 -0
  77. package/dist/demo/{DemoResetPassword-BOcLG4GF.js → DemoResetPassword-CPTy88iK.js} +6 -6
  78. package/dist/demo/DemoResetPassword-CPTy88iK.js.map +1 -0
  79. package/dist/demo/{DemoSidebar-DpZXf7GO.js → DemoSidebar-MVmQKfMt.js} +2 -2
  80. package/dist/demo/{DemoSidebar-DpZXf7GO.js.map → DemoSidebar-MVmQKfMt.js.map} +1 -1
  81. package/dist/demo/{DemoTypeForm-BlLAcQqZ.js → DemoTypeForm-w-qtfRlC.js} +3 -3
  82. package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +1 -0
  83. package/dist/demo/{DemoVerifyEmail-C-J7bXUQ.js → DemoVerifyEmail-C8FFJT5A.js} +5 -5
  84. package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +1 -0
  85. package/dist/demo/{Showcase-HchhcsHV.js → Showcase-CQrMWars.js} +2 -2
  86. package/dist/demo/Showcase-CQrMWars.js.map +1 -0
  87. package/dist/demo/index.d.ts +25 -15
  88. package/dist/demo/index.d.ts.map +1 -1
  89. package/dist/demo/index.js +24 -14
  90. package/dist/demo/index.js.map +1 -1
  91. package/package.json +6 -6
  92. package/src/admin/AdminRouter.ts +4 -4
  93. package/src/admin/MainRouter.ts +1 -1
  94. package/src/admin/components/audits/AdminAudits.tsx +4 -4
  95. package/src/admin/components/files/AdminFiles.tsx +2 -2
  96. package/src/admin/components/jobs/AdminJobs.tsx +2 -2
  97. package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
  98. package/src/admin/components/parameters/AdminParameters.tsx +1 -1
  99. package/src/admin/components/parameters/ParameterDetails.tsx +2 -2
  100. package/src/admin/components/parameters/ParameterHistory.tsx +1 -1
  101. package/src/admin/components/parameters/types.ts +9 -3
  102. package/src/admin/components/sessions/AdminSessions.tsx +3 -3
  103. package/src/admin/components/shared/AdminResourceHeader.tsx +1 -1
  104. package/src/admin/components/shared/AdminResourceTabs.tsx +1 -1
  105. package/src/admin/components/users/AdminUserAudits.tsx +3 -3
  106. package/src/admin/components/users/AdminUserCreate.tsx +4 -4
  107. package/src/admin/components/users/AdminUserDetails.tsx +4 -4
  108. package/src/admin/components/users/AdminUserLayout.tsx +3 -3
  109. package/src/admin/components/users/AdminUserSessions.tsx +3 -3
  110. package/src/admin/components/users/AdminUserSettings.tsx +4 -4
  111. package/src/admin/components/users/AdminUsers.tsx +4 -4
  112. package/src/admin/index.ts +27 -2
  113. package/src/auth/AuthI18n.ts +1 -1
  114. package/src/auth/AuthRouter.ts +14 -2
  115. package/src/auth/components/AuthLayout.tsx +1 -1
  116. package/src/auth/components/Login.tsx +5 -5
  117. package/src/auth/components/Profile.tsx +157 -0
  118. package/src/auth/components/Register.tsx +7 -7
  119. package/src/auth/components/ResetPassword.tsx +4 -4
  120. package/src/auth/components/VerifyEmail.tsx +3 -3
  121. package/src/auth/components/buttons/UserButton.tsx +35 -3
  122. package/src/auth/index.ts +25 -4
  123. package/src/core/UiRouter.ts +15 -0
  124. package/src/core/atoms/alephaSidebarAtom.ts +57 -0
  125. package/src/core/atoms/alephaThemeListAtom.ts +3 -1
  126. package/src/core/components/buttons/ActionButton.tsx +10 -10
  127. package/src/core/components/buttons/BurgerButton.tsx +5 -4
  128. package/src/core/components/buttons/LanguageButton.tsx +1 -1
  129. package/src/core/components/buttons/OmnibarButton.tsx +20 -1
  130. package/src/core/components/buttons/ThemeButton.tsx +1 -1
  131. package/src/core/components/buttons/ToggleSidebarButton.tsx +33 -23
  132. package/src/core/components/form/Control.tsx +1 -1
  133. package/src/core/components/form/ControlArray.tsx +2 -2
  134. package/src/core/components/form/ControlDate.tsx +1 -1
  135. package/src/core/components/form/ControlNumber.tsx +2 -2
  136. package/src/core/components/form/ControlObject.tsx +1 -1
  137. package/src/core/components/form/ControlQueryBuilder.tsx +1 -1
  138. package/src/core/components/form/ControlSelect.tsx +1 -1
  139. package/src/core/components/form/TypeForm.tsx +2 -2
  140. package/src/core/components/layout/AdminShell.tsx +236 -30
  141. package/src/core/components/layout/AlephaMantineProvider.tsx +3 -3
  142. package/src/core/components/layout/AppBar.tsx +235 -18
  143. package/src/core/components/layout/Omnibar.tsx +4 -4
  144. package/src/core/components/layout/Sidebar.tsx +43 -82
  145. package/src/core/components/table/DataTable.tsx +2 -2
  146. package/src/core/components/table/DataTableFilters.tsx +1 -1
  147. package/src/core/components/table/types.ts +1 -1
  148. package/src/core/hooks/useDialog.ts +1 -1
  149. package/src/core/hooks/useTheme.ts +1 -1
  150. package/src/core/hooks/useToast.ts +1 -1
  151. package/src/core/index.ts +46 -9
  152. package/src/core/providers/ThemeProvider.ts +1 -1
  153. package/src/core/styles.css +58 -0
  154. package/src/core/utils/parseInput.ts +1 -1
  155. package/src/demo/DemoRouter.ts +1 -1
  156. package/src/demo/components/DemoLayout.tsx +1 -1
  157. package/src/demo/components/core/DemoTypeForm.tsx +1 -1
  158. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  159. package/src/demo/components/shared/Showcase.tsx +1 -1
  160. package/src/demo/index.ts +11 -1
  161. package/src/json/index.ts +13 -0
  162. package/dist/admin/AdminAudits-ColpiP4T.js +0 -3
  163. package/dist/admin/AdminAudits-DClGEVBj.js.map +0 -1
  164. package/dist/admin/AdminFiles-C5pqXN5B.js +0 -3
  165. package/dist/admin/AdminFiles-C76r1_Xz.js.map +0 -1
  166. package/dist/admin/AdminNotifications-BXixCBu9.js +0 -3
  167. package/dist/admin/AdminNotifications-Bsalygm5.js.map +0 -1
  168. package/dist/admin/AdminParameters-Bmxtnpv-.js.map +0 -1
  169. package/dist/admin/AdminSessions-CrkRvey3.js +0 -3
  170. package/dist/admin/AdminSessions-DmK3R6pP.js.map +0 -1
  171. package/dist/admin/AdminUserAudits-BPMP1Qd2.js.map +0 -1
  172. package/dist/admin/AdminUserCreate-CZjB6NKc.js.map +0 -1
  173. package/dist/admin/AdminUserDetails-DuqCOBJK.js.map +0 -1
  174. package/dist/admin/AdminUserLayout-Dgk8s7Cd.js.map +0 -1
  175. package/dist/admin/AdminUserSessions-DCpe8_T6.js.map +0 -1
  176. package/dist/admin/AdminUserSessions-beiJqY2D.js +0 -3
  177. package/dist/admin/AdminUserSettings-CxlInVnu.js +0 -3
  178. package/dist/admin/AdminUserSettings-qxDfowqh.js.map +0 -1
  179. package/dist/admin/AdminUsers-Bd0wMP8v.js +0 -3
  180. package/dist/admin/AdminUsers-ZlPsDz0T.js.map +0 -1
  181. package/dist/auth/AuthLayout-CWzQ8rCe.js.map +0 -1
  182. package/dist/auth/Login-CxOPyNFP.js +0 -4
  183. package/dist/auth/Login-CyvKwy5e.js.map +0 -1
  184. package/dist/auth/Register-C7Zp09Ks.js.map +0 -1
  185. package/dist/auth/Register-Cacr7YbA.js +0 -4
  186. package/dist/auth/ResetPassword-CMkx8Ibf.js +0 -3
  187. package/dist/auth/ResetPassword-DYJSUC6B.js.map +0 -1
  188. package/dist/auth/VerifyEmail-CNXFIwWW.js.map +0 -1
  189. package/dist/auth/VerifyEmail-DKyDlz96.js +0 -3
  190. package/dist/demo/DemoJsonViewer-DbWVDdz_.js +0 -4
  191. package/dist/demo/DemoLayout-DjIDm93B.js.map +0 -1
  192. package/dist/demo/DemoLayout-nNMajP_9.js +0 -3
  193. package/dist/demo/DemoLogin-BA_HiIRZ.js.map +0 -1
  194. package/dist/demo/DemoRegister-B6syaxP9.js.map +0 -1
  195. package/dist/demo/DemoResetPassword-BOcLG4GF.js.map +0 -1
  196. package/dist/demo/DemoTypeForm-BlLAcQqZ.js.map +0 -1
  197. package/dist/demo/DemoVerifyEmail-C-J7bXUQ.js.map +0 -1
  198. package/dist/demo/Showcase-HchhcsHV.js.map +0 -1
  199. package/src/core/RootRouter.ts +0 -9
@@ -1,15 +1,16 @@
1
- import { useStore } from "@alepha/react";
2
1
  import { Burger, type BurgerProps } from "@mantine/core";
2
+ import { useStore } from "alepha/react";
3
+ import { alephaSidebarAtom } from "../../atoms/alephaSidebarAtom.ts";
3
4
 
4
5
  export interface BurgerButtonProps extends BurgerProps {}
5
6
 
6
7
  const BurgerButton = (props: BurgerButtonProps) => {
7
- const [opened, setOpened] = useStore("alepha.ui.sidebar.opened");
8
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
8
9
 
9
10
  return (
10
11
  <Burger
11
- opened={opened}
12
- onClick={() => setOpened(!opened)}
12
+ opened={sidebar.opened}
13
+ onClick={() => setSidebar({ ...sidebar, opened: !sidebar.opened })}
13
14
  hiddenFrom="sm"
14
15
  size="sm"
15
16
  {...props}
@@ -1,5 +1,5 @@
1
- import { useI18n } from "@alepha/react/i18n";
2
1
  import { IconLanguage } from "@tabler/icons-react";
2
+ import { useI18n } from "alepha/react/i18n";
3
3
  import ActionButton, { type ActionProps } from "./ActionButton.tsx";
4
4
 
5
5
  export interface LanguageButtonProps {
@@ -1,6 +1,8 @@
1
1
  import { Flex, Kbd, Text } from "@mantine/core";
2
+ import { useOs } from "@mantine/hooks";
2
3
  import { spotlight } from "@mantine/spotlight";
3
4
  import { IconSearch } from "@tabler/icons-react";
5
+ import { ClientOnly } from "alepha/react";
4
6
  import ActionButton, { type ActionProps } from "./ActionButton.tsx";
5
7
 
6
8
  export interface OmnibarButtonProps {
@@ -9,6 +11,23 @@ export interface OmnibarButtonProps {
9
11
  }
10
12
 
11
13
  const OmnibarButton = (props: OmnibarButtonProps) => {
14
+ const os = useOs();
15
+ const isMac = os === "macos" || os === "ios";
16
+ const shortcut = isMac ? "⌘" : "Ctrl";
17
+
18
+ if (props.collapsed) {
19
+ return (
20
+ <ActionButton
21
+ variant={"subtle"}
22
+ onClick={spotlight.open}
23
+ radius={"md"}
24
+ icon={<IconSearch size={16} />}
25
+ tooltip={{ label: "Search", position: "right" }}
26
+ {...props.actionProps}
27
+ />
28
+ );
29
+ }
30
+
12
31
  return (
13
32
  <ActionButton
14
33
  variant={"default"}
@@ -16,7 +35,7 @@ const OmnibarButton = (props: OmnibarButtonProps) => {
16
35
  justify={"space-between"}
17
36
  rightSection={
18
37
  <Kbd visibleFrom={"sm"} size={"sm"}>
19
- ⌘+K
38
+ <ClientOnly>{shortcut}</ClientOnly>+K
20
39
  </Kbd>
21
40
  }
22
41
  radius={"md"}
@@ -1,5 +1,5 @@
1
- import { useStore } from "@alepha/react";
2
1
  import { IconPalette } from "@tabler/icons-react";
2
+ import { useStore } from "alepha/react";
3
3
  import { alephaThemeListAtom } from "../../atoms/alephaThemeListAtom.ts";
4
4
  import { useTheme } from "../../hooks/useTheme.ts";
5
5
  import ActionButton, { type ActionProps } from "./ActionButton.tsx";
@@ -1,33 +1,43 @@
1
- import { useStore } from "@alepha/react";
2
- import { Flex } from "@mantine/core";
3
1
  import {
4
2
  IconLayoutSidebarLeftCollapse,
5
3
  IconLayoutSidebarRightCollapse,
6
4
  } from "@tabler/icons-react";
7
- import ActionButton from "./ActionButton.tsx";
5
+ import { useStore } from "alepha/react";
6
+ import { alephaSidebarAtom } from "../../atoms/alephaSidebarAtom.ts";
7
+ import ActionButton, { type ActionProps } from "./ActionButton.tsx";
8
8
 
9
- const ToggleSidebarButton = () => {
10
- const [collapsed, setCollapsed] = useStore("alepha.ui.sidebar.collapsed");
9
+ type Props = ActionProps;
10
+
11
+ const ToggleSidebarButton = (props: Props) => {
12
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
11
13
 
12
14
  return (
13
- <Flex>
14
- <ActionButton
15
- icon={
16
- collapsed ? (
17
- <IconLayoutSidebarRightCollapse />
18
- ) : (
19
- <IconLayoutSidebarLeftCollapse />
20
- )
21
- }
22
- variant={"subtle"}
23
- size={"md"}
24
- onClick={() => setCollapsed(!collapsed)}
25
- tooltip={{
26
- position: "right",
27
- label: collapsed ? "Show sidebar" : "Hide sidebar",
28
- }}
29
- />
30
- </Flex>
15
+ <ActionButton
16
+ icon={
17
+ sidebar.collapsed ? (
18
+ <IconLayoutSidebarRightCollapse />
19
+ ) : (
20
+ <IconLayoutSidebarLeftCollapse />
21
+ )
22
+ }
23
+ visibleFrom={"sm"}
24
+ variant={"subtle"}
25
+ size={"md"}
26
+ onClick={() => {
27
+ const expanding = sidebar.collapsed;
28
+ setSidebar({
29
+ ...sidebar,
30
+ collapsed: !sidebar.collapsed,
31
+ // Reset width to defaultWidth when expanding
32
+ width: expanding ? sidebar.defaultWidth : sidebar.width,
33
+ });
34
+ }}
35
+ tooltip={{
36
+ position: "right",
37
+ label: sidebar.collapsed ? "Show sidebar" : "Hide sidebar",
38
+ }}
39
+ {...props}
40
+ />
31
41
  );
32
42
  };
33
43
 
@@ -1,4 +1,3 @@
1
- import { useFormState } from "@alepha/react/form";
2
1
  import {
3
2
  ColorInput,
4
3
  type ColorInputProps,
@@ -20,6 +19,7 @@ import type {
20
19
  DateTimePickerProps,
21
20
  TimeInputProps,
22
21
  } from "@mantine/dates";
22
+ import { useFormState } from "alepha/react/form";
23
23
  import type { ComponentType } from "react";
24
24
  import {
25
25
  type GenericControlProps,
@@ -1,5 +1,3 @@
1
- import { useEvents } from "@alepha/react";
2
- import type { BaseInputField } from "@alepha/react/form";
3
1
  import {
4
2
  ActionIcon,
5
3
  Fieldset,
@@ -11,6 +9,8 @@ import {
11
9
  } from "@mantine/core";
12
10
  import { IconGripVertical, IconPlus, IconTrash } from "@tabler/icons-react";
13
11
  import type { TObject, TSchema } from "alepha";
12
+ import { useEvents } from "alepha/react";
13
+ import type { BaseInputField } from "alepha/react/form";
14
14
  import { useRef, useState } from "react";
15
15
  import { ui } from "../../constants/ui.ts";
16
16
  import {
@@ -1,4 +1,3 @@
1
- import { useFormState } from "@alepha/react/form";
2
1
  import {
3
2
  DateInput,
4
3
  type DateInputProps,
@@ -7,6 +6,7 @@ import {
7
6
  TimeInput,
8
7
  type TimeInputProps,
9
8
  } from "@mantine/dates";
9
+ import { useFormState } from "alepha/react/form";
10
10
  import {
11
11
  type GenericControlProps,
12
12
  parseInput,
@@ -1,5 +1,3 @@
1
- import { useEvents } from "@alepha/react";
2
- import { useFormState } from "@alepha/react/form";
3
1
  import {
4
2
  Input,
5
3
  NumberInput,
@@ -7,6 +5,8 @@ import {
7
5
  Slider,
8
6
  type SliderProps,
9
7
  } from "@mantine/core";
8
+ import { useEvents } from "alepha/react";
9
+ import { useFormState } from "alepha/react/form";
10
10
  import { useRef, useState } from "react";
11
11
  import {
12
12
  type GenericControlProps,
@@ -1,6 +1,6 @@
1
- import type { BaseInputField, ObjectInputField } from "@alepha/react/form";
2
1
  import { Fieldset, Grid, Stack, Text } from "@mantine/core";
3
2
  import type { TObject } from "alepha";
3
+ import type { BaseInputField, ObjectInputField } from "alepha/react/form";
4
4
  import {
5
5
  type GenericControlProps,
6
6
  parseInput,
@@ -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,20 @@ import {
7
5
  type AppShellMainProps,
8
6
  type AppShellNavbarProps,
9
7
  type AppShellProps,
8
+ Container,
9
+ type ContainerProps,
10
+ Flex,
10
11
  } from "@mantine/core";
11
- import { type ReactNode, useState } from "react";
12
+ import { useEvents, useStore } from "alepha/react";
13
+ import { NestedView, useRouter } from "alepha/react/router";
14
+ import {
15
+ type ReactNode,
16
+ useCallback,
17
+ useEffect,
18
+ useRef,
19
+ useState,
20
+ } from "react";
21
+ import { alephaSidebarAtom } from "../../atoms/alephaSidebarAtom.ts";
12
22
  import { ui } from "../../constants/ui.ts";
13
23
  import AppBar, { type AppBarProps } from "./AppBar.tsx";
14
24
  import { Sidebar, type SidebarProps } from "./Sidebar.tsx";
@@ -25,36 +35,163 @@ export interface AdminShellProps {
25
35
  footer?: ReactNode;
26
36
  children?: ReactNode;
27
37
 
38
+ /**
39
+ * Enable drag-to-resize for the sidebar.
40
+ * Width and constraints are configured in alephaSidebarAtom.
41
+ */
42
+ sidebarResizable?: boolean;
43
+
28
44
  noSidebarWhen?: {
29
45
  /**
30
46
  * Paths where the sidebar should be hidden.
31
47
  */
32
48
  paths?: string[];
33
49
  };
34
- }
35
50
 
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
- }
51
+ /**
52
+ * Wrap AppBar and main content in a Mantine Container.
53
+ * Pass `true` for default Container, or ContainerProps to customize.
54
+ */
55
+ container?: boolean | ContainerProps;
48
56
  }
49
57
 
50
58
  const AdminShell = (props: AdminShellProps) => {
51
59
  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,
60
+ const [sidebar, setSidebar] = useStore(alephaSidebarAtom);
61
+ const { opened, collapsed } = sidebar;
62
+
63
+ // Initialize collapsed state from props on mount
64
+ useEffect(() => {
65
+ if (props.sidebarProps?.collapsed !== undefined) {
66
+ setSidebar({ ...sidebar, collapsed: props.sidebarProps.collapsed });
67
+ }
68
+ }, []);
69
+
70
+ // Resize state
71
+ const [isResizing, setIsResizing] = useState(false);
72
+ const [isHovering, setIsHovering] = useState(false);
73
+ const [collapseEffect, setCollapseEffect] = useState({
74
+ offset: 0,
75
+ opacity: 1,
76
+ });
77
+ const resizeRef = useRef<{ startX: number; startWidth: number } | null>(null);
78
+
79
+ // Use atom values for constraints
80
+ const {
81
+ collapsedWidth,
82
+ collapseThreshold,
83
+ maxWidth,
84
+ hoverDelay,
85
+ defaultWidth,
86
+ } = sidebar;
87
+
88
+ const handleResizeStart = useCallback(
89
+ (e: React.MouseEvent) => {
90
+ if (!props.sidebarResizable) return;
91
+ e.preventDefault();
92
+
93
+ // If collapsed and hovering, un-collapse first and start from defaultWidth
94
+ if (collapsed) {
95
+ setSidebar({ ...sidebar, collapsed: false, width: defaultWidth });
96
+ setIsResizing(true);
97
+ resizeRef.current = {
98
+ startX: e.clientX,
99
+ startWidth: defaultWidth,
100
+ };
101
+ } else {
102
+ setIsResizing(true);
103
+ resizeRef.current = {
104
+ startX: e.clientX,
105
+ startWidth: sidebar.width,
106
+ };
107
+ }
108
+ },
109
+ [props.sidebarResizable, collapsed, sidebar, setSidebar, defaultWidth],
56
110
  );
57
111
 
112
+ useEffect(() => {
113
+ if (!isResizing) return;
114
+
115
+ const handleMouseMove = (e: MouseEvent) => {
116
+ if (!resizeRef.current) return;
117
+ const delta = e.clientX - resizeRef.current.startX;
118
+ const rawWidth = resizeRef.current.startWidth + delta;
119
+ const newWidth = Math.min(Math.max(rawWidth, collapsedWidth), maxWidth);
120
+
121
+ // Visual effect when below collapse threshold
122
+ if (rawWidth < collapseThreshold) {
123
+ const progress = Math.max(
124
+ 0,
125
+ (collapseThreshold - rawWidth) / collapseThreshold,
126
+ );
127
+ setCollapseEffect({
128
+ offset: -progress * collapsedWidth,
129
+ opacity: 1 - progress * 0.7,
130
+ });
131
+ setSidebar({ ...sidebar, width: collapseThreshold, collapsed: false });
132
+ } else {
133
+ setCollapseEffect({ offset: 0, opacity: 1 });
134
+ setSidebar({ ...sidebar, width: newWidth, collapsed: false });
135
+ }
136
+ };
137
+
138
+ const handleMouseUp = () => {
139
+ // If we released while in collapse zone, actually collapse
140
+ if (collapseEffect.offset < 0) {
141
+ setSidebar({ ...sidebar, collapsed: true });
142
+ }
143
+ setCollapseEffect({ offset: 0, opacity: 1 });
144
+ setIsResizing(false);
145
+ resizeRef.current = null;
146
+ };
147
+
148
+ document.addEventListener("mousemove", handleMouseMove);
149
+ document.addEventListener("mouseup", handleMouseUp);
150
+
151
+ return () => {
152
+ document.removeEventListener("mousemove", handleMouseMove);
153
+ document.removeEventListener("mouseup", handleMouseUp);
154
+ };
155
+ }, [
156
+ isResizing,
157
+ sidebar,
158
+ setSidebar,
159
+ collapsedWidth,
160
+ maxWidth,
161
+ collapseThreshold,
162
+ collapseEffect.offset,
163
+ ]);
164
+
165
+ // Hover to expand when collapsed (with delay)
166
+ const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
167
+
168
+ const handleNavbarMouseEnter = useCallback(() => {
169
+ if (collapsed) {
170
+ hoverTimeoutRef.current = setTimeout(() => {
171
+ setIsHovering(true);
172
+ }, hoverDelay);
173
+ }
174
+ }, [collapsed, hoverDelay]);
175
+
176
+ const handleNavbarMouseLeave = useCallback(() => {
177
+ if (hoverTimeoutRef.current) {
178
+ clearTimeout(hoverTimeoutRef.current);
179
+ hoverTimeoutRef.current = null;
180
+ }
181
+ setIsHovering(false);
182
+ }, []);
183
+
184
+ // Reset hover state when collapsed changes (e.g., when toggle button is clicked)
185
+ useEffect(() => {
186
+ if (collapsed) {
187
+ setIsHovering(false);
188
+ if (hoverTimeoutRef.current) {
189
+ clearTimeout(hoverTimeoutRef.current);
190
+ hoverTimeoutRef.current = null;
191
+ }
192
+ }
193
+ }, [collapsed]);
194
+
58
195
  const shouldShowSidebar = () => {
59
196
  if (props.noSidebarWhen?.paths) {
60
197
  for (const path of props.noSidebarWhen.paths) {
@@ -78,10 +215,10 @@ const AdminShell = (props: AdminShellProps) => {
78
215
  setShowSidebar(shouldShowSidebar());
79
216
  },
80
217
  "react:transition:begin": () => {
81
- setOpened(false);
218
+ setSidebar({ ...sidebar, opened: false });
82
219
  },
83
220
  },
84
- [],
221
+ [sidebar],
85
222
  );
86
223
 
87
224
  // Default AppBar items with burger button on the left
@@ -89,12 +226,28 @@ const AdminShell = (props: AdminShellProps) => {
89
226
  { position: "left" as const, type: "burger" as const },
90
227
  ];
91
228
 
229
+ // Forward container to appBarProps if not already set
230
+ const appBarProps = { ...props.appBarProps };
231
+ appBarProps.container ??= props.container;
232
+
92
233
  const hasSidebar = showSidebar && props.sidebarProps !== undefined;
93
234
  const hasAppBar = hasSidebar || props.appBarProps || props.header;
94
235
 
95
236
  const headerHeight = hasAppBar ? 60 : 0;
96
237
  const footerHeight = props.footer ? 24 : 0;
97
- const sidebarWidth = hasSidebar ? (collapsed ? 78 : 300) : 0;
238
+ const expandedWidth = Math.max(sidebar.width, collapsedWidth);
239
+
240
+ // When collapsed but hovering, show defaultWidth (not current width)
241
+ const isExpandedByHover = collapsed && isHovering;
242
+ const effectiveCollapsed = collapsed && !isHovering;
243
+ const hoverWidth = Math.max(defaultWidth, collapsedWidth);
244
+ // When hovering, keep main content at collapsed width (sidebar overlays)
245
+ const sidebarWidth = hasSidebar
246
+ ? effectiveCollapsed || isExpandedByHover
247
+ ? collapsedWidth
248
+ : expandedWidth
249
+ : 0;
250
+ const canResize = props.sidebarResizable && !collapsed;
98
251
 
99
252
  return (
100
253
  <AppShell
@@ -105,7 +258,11 @@ const AdminShell = (props: AdminShellProps) => {
105
258
  navbar={
106
259
  hasSidebar
107
260
  ? {
108
- width: collapsed ? { base: 78 } : { base: 300 },
261
+ // When hovering, keep collapsed width to avoid pushing content
262
+ width:
263
+ effectiveCollapsed || isExpandedByHover
264
+ ? { base: collapsedWidth }
265
+ : { base: expandedWidth },
109
266
  breakpoint: "sm",
110
267
  collapsed: { mobile: !opened },
111
268
  }
@@ -115,14 +272,49 @@ const AdminShell = (props: AdminShellProps) => {
115
272
  {...props.appShellProps}
116
273
  >
117
274
  <AppShell.Header bg={ui.colors.surface} {...props.appShellHeaderProps}>
118
- {props.header ?? (
119
- <AppBar items={defaultAppBarItems} {...props.appBarProps} />
120
- )}
275
+ {props.header ?? <AppBar items={defaultAppBarItems} {...appBarProps} />}
121
276
  </AppShell.Header>
122
277
 
123
278
  {hasSidebar && (
124
- <AppShell.Navbar bg={ui.colors.surface} {...props.appShellNavbarProps}>
125
- <Sidebar collapsed={collapsed} {...(props.sidebarProps ?? {})} />
279
+ <AppShell.Navbar
280
+ bg={ui.colors.surface}
281
+ className="alepha-sidebar-navbar"
282
+ data-resizing={isResizing}
283
+ data-hover-expanded={isExpandedByHover}
284
+ onMouseEnter={handleNavbarMouseEnter}
285
+ onMouseLeave={handleNavbarMouseLeave}
286
+ style={{
287
+ transform: collapseEffect.offset
288
+ ? `translateX(${collapseEffect.offset}px)`
289
+ : undefined,
290
+ opacity: collapseEffect.opacity,
291
+ // When hovering, expand width visually as overlay
292
+ ...(isExpandedByHover && {
293
+ width: hoverWidth,
294
+ zIndex: 200,
295
+ boxShadow: "var(--mantine-shadow-xl)",
296
+ }),
297
+ }}
298
+ {...props.appShellNavbarProps}
299
+ >
300
+ <Sidebar
301
+ {...(props.sidebarProps ?? {})}
302
+ collapsed={effectiveCollapsed}
303
+ />
304
+ {(canResize || isExpandedByHover) && (
305
+ <Flex
306
+ pos="absolute"
307
+ right={-6}
308
+ top={0}
309
+ bottom={0}
310
+ w={12}
311
+ style={{
312
+ cursor: "col-resize",
313
+ userSelect: "none",
314
+ }}
315
+ onMouseDown={handleResizeStart}
316
+ />
317
+ )}
126
318
  </AppShell.Navbar>
127
319
  )}
128
320
 
@@ -134,9 +326,23 @@ const AdminShell = (props: AdminShellProps) => {
134
326
  display={"flex"}
135
327
  flex={1}
136
328
  style={{ flexDirection: "column" }}
329
+ className="alepha-sidebar-main"
330
+ data-resizing={isResizing}
137
331
  {...props.appShellMainProps}
138
332
  >
139
- {props.children ?? <NestedView />}
333
+ {props.container ? (
334
+ <Container
335
+ w={"100%"}
336
+ flex={1}
337
+ display="flex"
338
+ style={{ flexDirection: "column" }}
339
+ {...(typeof props.container === "boolean" ? {} : props.container)}
340
+ >
341
+ {props.children ?? <NestedView />}
342
+ </Container>
343
+ ) : (
344
+ (props.children ?? <NestedView />)
345
+ )}
140
346
  </AppShell.Main>
141
347
 
142
348
  {props.footer && (
@@ -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";