@alepha/ui 0.18.1 → 0.18.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 (187) hide show
  1. package/dist/admin/{AdminApiKeys-C-6_Q-lH.js → AdminApiKeys-BJhIwfD6.js} +17 -38
  2. package/dist/admin/AdminApiKeys-BJhIwfD6.js.map +1 -0
  3. package/dist/admin/{AdminAudits-Bgbf04hO.js → AdminAudits-DzD_4cDt.js} +23 -19
  4. package/dist/admin/AdminAudits-DzD_4cDt.js.map +1 -0
  5. package/dist/admin/AdminDashboard-C92tIc6x.js +67 -0
  6. package/dist/admin/AdminDashboard-C92tIc6x.js.map +1 -0
  7. package/dist/admin/{AdminFiles-B9a7G3cY.js → AdminFiles-DLpfhBkf.js} +3 -7
  8. package/dist/admin/AdminFiles-DLpfhBkf.js.map +1 -0
  9. package/dist/admin/{AdminJobDashboard-DaTwf5OY.js → AdminJobDashboard-KIOkeMgE.js} +2 -2
  10. package/dist/admin/{AdminJobDashboard-DaTwf5OY.js.map → AdminJobDashboard-KIOkeMgE.js.map} +1 -1
  11. package/dist/admin/{AdminJobExecutions-B9cek5dl.js → AdminJobExecutions-D0Yo_PU0.js} +24 -36
  12. package/dist/admin/AdminJobExecutions-D0Yo_PU0.js.map +1 -0
  13. package/dist/admin/{AdminJobRegistry-DFgV3oqx.js → AdminJobRegistry-PFajqaGK.js} +10 -18
  14. package/dist/admin/AdminJobRegistry-PFajqaGK.js.map +1 -0
  15. package/dist/admin/AdminLayout-B1DXZHDn.js +61 -0
  16. package/dist/admin/AdminLayout-B1DXZHDn.js.map +1 -0
  17. package/dist/admin/{AdminParameters-DHw9ATgl.js → AdminParameters-BspPeqp_.js} +2 -2
  18. package/dist/admin/{AdminParameters-DHw9ATgl.js.map → AdminParameters-BspPeqp_.js.map} +1 -1
  19. package/dist/admin/{AdminSessions-BhGJPI3z.js → AdminSessions-BnH5CZQl.js} +48 -53
  20. package/dist/admin/AdminSessions-BnH5CZQl.js.map +1 -0
  21. package/dist/admin/{AdminUserLayout-BdC4Te8m.js → AdminUserLayout-DUbC6-BI.js} +2 -2
  22. package/dist/admin/{AdminUserLayout-BdC4Te8m.js.map → AdminUserLayout-DUbC6-BI.js.map} +1 -1
  23. package/dist/admin/{AdminUserProfile-DAt23fqY.js → AdminUserProfile-DuTUnjdG.js} +3 -3
  24. package/dist/admin/{AdminUserProfile-DAt23fqY.js.map → AdminUserProfile-DuTUnjdG.js.map} +1 -1
  25. package/dist/admin/{AdminUserSessions-1uzcx02z.js → AdminUserSessions-DvZdAGpL.js} +33 -35
  26. package/dist/admin/AdminUserSessions-DvZdAGpL.js.map +1 -0
  27. package/dist/admin/AdminUsers-CR9z0g_5.js +206 -0
  28. package/dist/admin/AdminUsers-CR9z0g_5.js.map +1 -0
  29. package/dist/admin/{AuthLayout-DFJvCvzw.js → AuthLayout-DsUfp9RG.js} +2 -2
  30. package/dist/admin/{AuthLayout-DFJvCvzw.js.map → AuthLayout-DsUfp9RG.js.map} +1 -1
  31. package/dist/admin/{IconGoogle-CSQLPYwX.js → IconGoogle-Ch1m3Uzl.js} +1 -1
  32. package/dist/admin/{IconGoogle-CSQLPYwX.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
  33. package/dist/admin/{Login-BGheURrg.js → Login-DHbYJKwg.js} +3 -3
  34. package/dist/{auth/Login-Denw_UGy.js.map → admin/Login-DHbYJKwg.js.map} +1 -1
  35. package/dist/{auth/Profile-BMX_Ar_s.js → admin/Profile-B2EcIDB9.js} +2 -2
  36. package/dist/{auth/Profile-BMX_Ar_s.js.map → admin/Profile-B2EcIDB9.js.map} +1 -1
  37. package/dist/admin/{Register-Cs10l8vX.js → Register-Z3fxRbUF.js} +3 -3
  38. package/dist/{demo/Register-a70LPgs2.js.map → admin/Register-Z3fxRbUF.js.map} +1 -1
  39. package/dist/admin/{ResetPassword-BwDdfkGH.js → ResetPassword-_Y1qTTKh.js} +2 -2
  40. package/dist/admin/{ResetPassword-BwDdfkGH.js.map → ResetPassword-_Y1qTTKh.js.map} +1 -1
  41. package/dist/admin/{VerifyEmail-DfXHAiQl.js → VerifyEmail-Bg22bwcC.js} +2 -2
  42. package/dist/admin/{VerifyEmail-DfXHAiQl.js.map → VerifyEmail-Bg22bwcC.js.map} +1 -1
  43. package/dist/admin/{core-2xoLiT0o.js → core-BVO_TQxb.js} +1474 -233
  44. package/dist/admin/core-BVO_TQxb.js.map +1 -0
  45. package/dist/admin/index.d.ts +29 -4
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +448 -69
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/auth/{AuthLayout-CAE1pX9s.js → AuthLayout-C161NeF6.js} +2 -2
  50. package/dist/auth/{AuthLayout-CAE1pX9s.js.map → AuthLayout-C161NeF6.js.map} +1 -1
  51. package/dist/auth/{Login-Denw_UGy.js → Login-C7jIqf00.js} +2 -2
  52. package/dist/{admin/Login-BGheURrg.js.map → auth/Login-C7jIqf00.js.map} +1 -1
  53. package/dist/{admin/Profile-B-c9pCPf.js → auth/Profile-BMpXJ0oi.js} +2 -2
  54. package/dist/{demo/Profile-CWqti7FB.js.map → auth/Profile-BMpXJ0oi.js.map} +1 -1
  55. package/dist/auth/{Register-6hi_cpfF.js → Register-2gx8qll-.js} +2 -2
  56. package/dist/auth/{Register-6hi_cpfF.js.map → Register-2gx8qll-.js.map} +1 -1
  57. package/dist/{demo/ResetPassword-DWN0lzr5.js → auth/ResetPassword-DBxt9hKk.js} +2 -2
  58. package/dist/auth/{ResetPassword-CqfTk1FI.js.map → ResetPassword-DBxt9hKk.js.map} +1 -1
  59. package/dist/{demo/VerifyEmail-DZWL72K4.js → auth/VerifyEmail-Z80Ubajk.js} +2 -2
  60. package/dist/auth/{VerifyEmail-nWiSTMjF.js.map → VerifyEmail-Z80Ubajk.js.map} +1 -1
  61. package/dist/auth/{core-niW0sFLv.js → core-DyfeVr5c.js} +1002 -38
  62. package/dist/auth/core-DyfeVr5c.js.map +1 -0
  63. package/dist/auth/index.d.ts +12 -1
  64. package/dist/auth/index.d.ts.map +1 -1
  65. package/dist/auth/index.js +12 -13
  66. package/dist/auth/index.js.map +1 -1
  67. package/dist/core/index.d.ts +95 -14
  68. package/dist/core/index.d.ts.map +1 -1
  69. package/dist/core/index.js +1473 -232
  70. package/dist/core/index.js.map +1 -1
  71. package/dist/demo/{AuthLayout-jLa0aKsI.js → AuthLayout-DN-ClJQk.js} +2 -2
  72. package/dist/demo/{AuthLayout-jLa0aKsI.js.map → AuthLayout-DN-ClJQk.js.map} +1 -1
  73. package/dist/demo/{DemoButton-BmaWZVwf.js → DemoButton-CGUyR9eM.js} +3 -3
  74. package/dist/demo/{DemoButton-BmaWZVwf.js.map → DemoButton-CGUyR9eM.js.map} +1 -1
  75. package/dist/demo/{DemoDataTable-Z9xyV221.js → DemoDataTable-QFG-xXSx.js} +15 -19
  76. package/dist/demo/DemoDataTable-QFG-xXSx.js.map +1 -0
  77. package/dist/demo/{DemoDialog-4ItHLf9t.js → DemoDialog-DW8QEvD1.js} +2 -2
  78. package/dist/demo/{DemoDialog-4ItHLf9t.js.map → DemoDialog-DW8QEvD1.js.map} +1 -1
  79. package/dist/demo/{DemoFlex-EtVq8QfX.js → DemoFlex-CAhLUanT.js} +3 -3
  80. package/dist/demo/{DemoFlex-EtVq8QfX.js.map → DemoFlex-CAhLUanT.js.map} +1 -1
  81. package/dist/demo/{DemoHeading-BS-vGfkI.js → DemoHeading-yIFmNjHB.js} +3 -3
  82. package/dist/demo/{DemoHeading-BS-vGfkI.js.map → DemoHeading-yIFmNjHB.js.map} +1 -1
  83. package/dist/demo/{DemoHome-Clbn8AmS.js → DemoHome-BSGuBHus.js} +2 -2
  84. package/dist/demo/{DemoHome-Clbn8AmS.js.map → DemoHome-BSGuBHus.js.map} +1 -1
  85. package/dist/demo/{DemoJsonViewer-DkIX_ky2.js → DemoJsonViewer-DsA2IpgV.js} +3 -3
  86. package/dist/demo/{DemoJsonViewer-DkIX_ky2.js.map → DemoJsonViewer-DsA2IpgV.js.map} +1 -1
  87. package/dist/demo/{DemoLayout-C56xb5EE.js → DemoLayout-Cy6xjn6P.js} +2 -2
  88. package/dist/demo/{DemoLayout-C56xb5EE.js.map → DemoLayout-Cy6xjn6P.js.map} +1 -1
  89. package/dist/demo/{DemoLogin-BZwpicOS.js → DemoLogin-vqxgTu4P.js} +8 -8
  90. package/dist/demo/{DemoLogin-BZwpicOS.js.map → DemoLogin-vqxgTu4P.js.map} +1 -1
  91. package/dist/demo/{DemoRegister-C7_qc4MJ.js → DemoRegister-YHPvPg77.js} +8 -8
  92. package/dist/demo/{DemoRegister-C7_qc4MJ.js.map → DemoRegister-YHPvPg77.js.map} +1 -1
  93. package/dist/demo/{DemoResetPassword-BI1Ct4Dw.js → DemoResetPassword-mOW18Zlm.js} +8 -8
  94. package/dist/demo/{DemoResetPassword-BI1Ct4Dw.js.map → DemoResetPassword-mOW18Zlm.js.map} +1 -1
  95. package/dist/demo/{DemoSidebar-CcBo4ltC.js → DemoSidebar-od7aLjP_.js} +3 -3
  96. package/dist/demo/{DemoSidebar-CcBo4ltC.js.map → DemoSidebar-od7aLjP_.js.map} +1 -1
  97. package/dist/demo/{DemoText-CzXuUn3g.js → DemoText-DU3JeRS0.js} +3 -3
  98. package/dist/demo/{DemoText-CzXuUn3g.js.map → DemoText-DU3JeRS0.js.map} +1 -1
  99. package/dist/demo/{DemoToast-BgHDhWrX.js → DemoToast-CUJEiPRa.js} +2 -2
  100. package/dist/demo/{DemoToast-BgHDhWrX.js.map → DemoToast-CUJEiPRa.js.map} +1 -1
  101. package/dist/demo/{DemoTypeForm-DDzWoMSV.js → DemoTypeForm-C1dNkahD.js} +3 -3
  102. package/dist/demo/{DemoTypeForm-DDzWoMSV.js.map → DemoTypeForm-C1dNkahD.js.map} +1 -1
  103. package/dist/demo/{DemoVerifyEmail-C_Irdnov.js → DemoVerifyEmail-D9EcXZ38.js} +8 -8
  104. package/dist/demo/{DemoVerifyEmail-C_Irdnov.js.map → DemoVerifyEmail-D9EcXZ38.js.map} +1 -1
  105. package/dist/demo/{Login-hSOU3jZc.js → Login-CoYf_P_F.js} +2 -2
  106. package/dist/demo/{Login-hSOU3jZc.js.map → Login-CoYf_P_F.js.map} +1 -1
  107. package/dist/demo/{Profile-CWqti7FB.js → Profile-BE_Y3co2.js} +2 -2
  108. package/dist/{admin/Profile-B-c9pCPf.js.map → demo/Profile-BE_Y3co2.js.map} +1 -1
  109. package/dist/demo/{Register-a70LPgs2.js → Register-fXHmBpr3.js} +2 -2
  110. package/dist/{admin/Register-Cs10l8vX.js.map → demo/Register-fXHmBpr3.js.map} +1 -1
  111. package/dist/{auth/ResetPassword-CqfTk1FI.js → demo/ResetPassword-CAPj8MO3.js} +2 -2
  112. package/dist/demo/{ResetPassword-DWN0lzr5.js.map → ResetPassword-CAPj8MO3.js.map} +1 -1
  113. package/dist/demo/{Showcase-Dq3MISpd.js → Showcase-BtEU0pY9.js} +2 -2
  114. package/dist/demo/{Showcase-Dq3MISpd.js.map → Showcase-BtEU0pY9.js.map} +1 -1
  115. package/dist/{auth/VerifyEmail-nWiSTMjF.js → demo/VerifyEmail-DFmdCdYs.js} +2 -2
  116. package/dist/demo/{VerifyEmail-DZWL72K4.js.map → VerifyEmail-DFmdCdYs.js.map} +1 -1
  117. package/dist/demo/{auth-d6n3xbug.js → auth-Djd7SKiw.js} +8 -8
  118. package/dist/demo/{auth-d6n3xbug.js.map → auth-Djd7SKiw.js.map} +1 -1
  119. package/dist/demo/{core-RCUw1Q-a.js → core-B7LNjM78.js} +1484 -226
  120. package/dist/demo/core-B7LNjM78.js.map +1 -0
  121. package/dist/demo/index.js +17 -17
  122. package/package.json +3 -3
  123. package/src/admin/{AdminRouter.ts → AdminRouter.tsx} +128 -19
  124. package/src/admin/components/AdminDashboard.tsx +52 -0
  125. package/src/admin/components/AdminLayout.tsx +32 -40
  126. package/src/admin/components/audits/AdminAudits.tsx +22 -16
  127. package/src/admin/components/files/AdminFiles.tsx +1 -6
  128. package/src/admin/components/jobs/AdminJobExecutions.tsx +33 -39
  129. package/src/admin/components/jobs/AdminJobRegistry.tsx +9 -18
  130. package/src/admin/components/keys/AdminApiKeys.tsx +23 -41
  131. package/src/admin/components/sessions/AdminSessions.tsx +71 -71
  132. package/src/admin/components/users/AdminUserSessions.tsx +33 -31
  133. package/src/admin/components/users/AdminUsers.tsx +184 -72
  134. package/src/admin/index.ts +2 -2
  135. package/src/admin/primitives/$uiAdmin.ts +1 -1
  136. package/src/auth/components/buttons/UserButton.tsx +1 -3
  137. package/src/core/atoms/alephaSidebarAtom.ts +1 -1
  138. package/src/core/atoms/alephaThemeListAtom.ts +14 -1
  139. package/src/core/atoms/alephaThemeOverridesAtom.ts +17 -0
  140. package/src/core/atoms/themes/editorial.ts +184 -0
  141. package/src/core/atoms/themes/monochrome.ts +197 -0
  142. package/src/core/atoms/themes/rosePine.ts +208 -0
  143. package/src/core/atoms/themes/softBrutalism.ts +221 -0
  144. package/src/core/atoms/themes/terminal.ts +186 -0
  145. package/src/core/components/Flex.tsx +91 -1
  146. package/src/core/components/Text.tsx +1 -1
  147. package/src/core/components/buttons/ActionButton.tsx +15 -19
  148. package/src/core/components/buttons/DarkModeButton.tsx +3 -3
  149. package/src/core/components/buttons/LanguageButton.tsx +1 -1
  150. package/src/core/components/buttons/OmnibarButton.tsx +1 -2
  151. package/src/core/components/buttons/ThemeButton.tsx +40 -11
  152. package/src/core/components/buttons/ThemeExpertModal.tsx +184 -0
  153. package/src/core/components/buttons/ToggleSidebarButton.tsx +1 -2
  154. package/src/core/components/layout/AppBar.tsx +10 -0
  155. package/src/core/components/layout/DashboardShell.tsx +10 -7
  156. package/src/core/components/layout/Sidebar.tsx +60 -52
  157. package/src/core/constants/ui.ts +5 -5
  158. package/src/core/hooks/useTheme.ts +26 -3
  159. package/src/core/index.ts +6 -1
  160. package/src/core/interfaces/AlephaTheme.ts +2 -0
  161. package/src/core/providers/ThemeProvider.ts +108 -8
  162. package/src/core/services/DialogService.tsx +24 -3
  163. package/src/core/styles.css +26 -23
  164. package/src/core/table/components/DataTable.tsx +167 -137
  165. package/src/core/table/components/DataTableFilters.tsx +1 -6
  166. package/src/core/table/components/DataTablePagination.tsx +51 -28
  167. package/src/core/table/components/DataTableToolbar.tsx +9 -4
  168. package/src/core/table/index.ts +1 -0
  169. package/src/core/table/interfaces/types.ts +13 -9
  170. package/src/demo/components/core/DemoDataTable.tsx +15 -19
  171. package/dist/admin/AdminApiKeys-C-6_Q-lH.js.map +0 -1
  172. package/dist/admin/AdminAudits-Bgbf04hO.js.map +0 -1
  173. package/dist/admin/AdminFiles-B9a7G3cY.js.map +0 -1
  174. package/dist/admin/AdminJobExecutions-B9cek5dl.js.map +0 -1
  175. package/dist/admin/AdminJobRegistry-DFgV3oqx.js.map +0 -1
  176. package/dist/admin/AdminLayout-DHsvWxVB.js +0 -70
  177. package/dist/admin/AdminLayout-DHsvWxVB.js.map +0 -1
  178. package/dist/admin/AdminSessions-BhGJPI3z.js.map +0 -1
  179. package/dist/admin/AdminUserSessions-1uzcx02z.js.map +0 -1
  180. package/dist/admin/AdminUsers-C85c3eiQ.js +0 -121
  181. package/dist/admin/AdminUsers-C85c3eiQ.js.map +0 -1
  182. package/dist/admin/auth-Dr0Cf8I7.js +0 -319
  183. package/dist/admin/auth-Dr0Cf8I7.js.map +0 -1
  184. package/dist/admin/core-2xoLiT0o.js.map +0 -1
  185. package/dist/auth/core-niW0sFLv.js.map +0 -1
  186. package/dist/demo/DemoDataTable-Z9xyV221.js.map +0 -1
  187. package/dist/demo/core-RCUw1Q-a.js.map +0 -1
@@ -1,8 +1,12 @@
1
1
  import { $inject, Alepha, AlephaError } from "alepha";
2
- import { $head } from "alepha/react/head";
2
+ import { $head, BrowserHeadProvider } from "alepha/react/head";
3
3
  import { $cookie } from "alepha/server/cookies";
4
4
  import { alephaThemeAtom } from "../atoms/alephaThemeAtom.ts";
5
5
  import { alephaThemeListAtom } from "../atoms/alephaThemeListAtom.ts";
6
+ import {
7
+ type AlephaThemeOverrides,
8
+ alephaThemeOverridesAtom,
9
+ } from "../atoms/alephaThemeOverridesAtom.ts";
6
10
  import { defaultTheme } from "../atoms/themes/default.ts";
7
11
  import type { AlephaTheme } from "../interfaces/AlephaTheme.ts";
8
12
 
@@ -14,6 +18,12 @@ export class ThemeProvider {
14
18
  ttl: [1, "year"],
15
19
  });
16
20
 
21
+ protected readonly overridesCookie = $cookie({
22
+ name: "themeOverrides",
23
+ schema: alephaThemeOverridesAtom.schema,
24
+ ttl: [1, "year"],
25
+ });
26
+
17
27
  protected readonly head = $head(() => {
18
28
  const theme = this.getTheme();
19
29
  if (!theme || !theme.name) {
@@ -21,8 +31,9 @@ export class ThemeProvider {
21
31
  }
22
32
  return {
23
33
  htmlAttributes: {
24
- "data-theme": theme.name,
34
+ "data-theme": this.slugify(theme.name),
25
35
  },
36
+ ...theme.head,
26
37
  };
27
38
  });
28
39
 
@@ -38,23 +49,112 @@ export class ThemeProvider {
38
49
  this.cookie.set({ index });
39
50
  this.alepha.store.set(alephaThemeAtom, { index });
40
51
 
41
- if (typeof document === "undefined") {
52
+ if (!this.alepha.isBrowser()) {
42
53
  return;
43
54
  }
44
55
 
45
- document.documentElement.removeAttribute("data-theme");
56
+ this.alepha.inject(BrowserHeadProvider).refreshGlobalHead();
57
+ }
46
58
 
47
- if (newTheme.name) {
48
- document.documentElement.setAttribute("data-theme", newTheme.name);
49
- }
59
+ protected slugify(name: string): string {
60
+ return name.toLowerCase().replace(/\s+/g, "-");
50
61
  }
51
62
 
63
+ protected static readonly FONT_SIZE_MULTIPLIERS: Record<string, number> = {
64
+ xs: 0.85,
65
+ sm: 0.925,
66
+ md: 1,
67
+ lg: 1.1,
68
+ xl: 1.25,
69
+ };
70
+
71
+ protected static readonly SCALE_VALUES: Record<string, number> = {
72
+ xs: 0.85,
73
+ sm: 0.925,
74
+ md: 1,
75
+ lg: 1.1,
76
+ xl: 1.25,
77
+ };
78
+
79
+ protected static readonly DEFAULT_FONT_SIZES: Record<string, string> = {
80
+ xs: "0.75rem",
81
+ sm: "0.875rem",
82
+ md: "1rem",
83
+ lg: "1.125rem",
84
+ xl: "1.25rem",
85
+ };
86
+
52
87
  public getTheme() {
53
88
  const index = this.getThemeIndex();
54
89
  const list = this.alepha.store.get(
55
90
  alephaThemeListAtom,
56
91
  ) as Array<AlephaTheme>;
57
- return list[index] || list[0] || defaultTheme;
92
+ const base = list[index] || list[0] || defaultTheme;
93
+ const overrides = this.getThemeOverrides();
94
+
95
+ if (
96
+ !overrides.primaryColor &&
97
+ !overrides.radius &&
98
+ !overrides.fontFamily &&
99
+ !overrides.fontSize &&
100
+ !overrides.scale
101
+ ) {
102
+ return base;
103
+ }
104
+
105
+ const merged = {
106
+ ...base,
107
+ ...(overrides.primaryColor && { primaryColor: overrides.primaryColor }),
108
+ ...(overrides.radius && { defaultRadius: overrides.radius }),
109
+ ...(overrides.fontFamily && { fontFamily: overrides.fontFamily }),
110
+ ...(overrides.scale &&
111
+ overrides.scale !== "md" && {
112
+ scale: ThemeProvider.SCALE_VALUES[overrides.scale] ?? 1,
113
+ }),
114
+ };
115
+
116
+ if (overrides.fontSize && overrides.fontSize !== "md") {
117
+ const multiplier =
118
+ ThemeProvider.FONT_SIZE_MULTIPLIERS[overrides.fontSize] ?? 1;
119
+ const baseSizes =
120
+ (base.fontSizes as Record<string, string> | undefined) ??
121
+ ThemeProvider.DEFAULT_FONT_SIZES;
122
+ merged.fontSizes = Object.fromEntries(
123
+ Object.entries(baseSizes).map(([key, val]) => [
124
+ key,
125
+ `${(Number.parseFloat(String(val)) * multiplier).toFixed(4)}rem`,
126
+ ]),
127
+ ) as typeof merged.fontSizes;
128
+ }
129
+
130
+ return merged;
131
+ }
132
+
133
+ public setThemeOverrides(overrides: AlephaThemeOverrides) {
134
+ this.overridesCookie.set(overrides);
135
+ this.alepha.store.set(alephaThemeOverridesAtom, overrides);
136
+
137
+ if (!this.alepha.isBrowser()) {
138
+ return;
139
+ }
140
+
141
+ this.alepha.inject(BrowserHeadProvider).refreshGlobalHead();
142
+ }
143
+
144
+ public getThemeOverrides(): AlephaThemeOverrides {
145
+ try {
146
+ return (
147
+ this.overridesCookie.get() ??
148
+ this.alepha.store.get(alephaThemeOverridesAtom) ??
149
+ {}
150
+ );
151
+ } catch {
152
+ return this.alepha.store.get(alephaThemeOverridesAtom) ?? {};
153
+ }
154
+ }
155
+
156
+ public resetThemeOverrides() {
157
+ this.setThemeOverrides({});
58
158
  }
59
159
 
60
160
  protected getThemeIndex() {
@@ -73,15 +73,22 @@ export class DialogService {
73
73
  */
74
74
  public alert(options?: AlertDialogOptions): Promise<void> {
75
75
  return new Promise((resolve) => {
76
+ let resolved = false;
77
+ const done = () => {
78
+ if (resolved) return;
79
+ resolved = true;
80
+ resolve();
81
+ };
76
82
  const modalId = this.open({
77
83
  ...options,
78
84
  title: options?.title || "Alert",
85
+ onClose: done,
79
86
  content: (
80
87
  <AlertDialog
81
88
  options={options}
82
89
  onClose={() => {
83
90
  this.close(modalId);
84
- resolve();
91
+ done();
85
92
  }}
86
93
  />
87
94
  ),
@@ -94,17 +101,24 @@ export class DialogService {
94
101
  */
95
102
  public confirm(options?: ConfirmDialogOptions): Promise<boolean> {
96
103
  return new Promise((resolve) => {
104
+ let resolved = false;
105
+ const done = (confirmed: boolean) => {
106
+ if (resolved) return;
107
+ resolved = true;
108
+ resolve(confirmed);
109
+ };
97
110
  const modalId = this.open({
98
111
  ...options,
99
112
  title: options?.title || "Confirm",
100
113
  closeOnClickOutside: false,
101
114
  closeOnEscape: false,
115
+ onClose: () => done(false),
102
116
  content: (
103
117
  <ConfirmDialog
104
118
  options={options}
105
119
  onConfirm={(confirmed) => {
106
120
  this.close(modalId);
107
- resolve(confirmed);
121
+ done(confirmed);
108
122
  }}
109
123
  />
110
124
  ),
@@ -117,17 +131,24 @@ export class DialogService {
117
131
  */
118
132
  public prompt(options?: PromptDialogOptions): Promise<string | null> {
119
133
  return new Promise((resolve) => {
134
+ let resolved = false;
135
+ const done = (value: string | null) => {
136
+ if (resolved) return;
137
+ resolved = true;
138
+ resolve(value);
139
+ };
120
140
  const modalId = this.open({
121
141
  ...options,
122
142
  title: options?.title || "Input",
123
143
  closeOnClickOutside: false,
124
144
  closeOnEscape: false,
145
+ onClose: () => done(null),
125
146
  content: (
126
147
  <PromptDialog
127
148
  options={options}
128
149
  onSubmit={(value) => {
129
150
  this.close(modalId);
130
- resolve(value);
151
+ done(value);
131
152
  }}
132
153
  />
133
154
  ),
@@ -8,9 +8,9 @@
8
8
  @import "./json/components/JsonViewer.css";
9
9
 
10
10
  :root {
11
- --alepha-background-light: var(--mantine-color-gray-1);
12
- --alepha-background-dark: var(--mantine-color-dark-9);
13
- --alepha-background: var(--alepha-background-light);
11
+ --alepha-ground-light: var(--mantine-color-gray-1);
12
+ --alepha-ground-dark: var(--mantine-color-dark-9);
13
+ --alepha-ground: var(--alepha-ground-light);
14
14
 
15
15
  --alepha-surface-light: var(--mantine-color-gray-0);
16
16
  --alepha-surface-dark: var(--mantine-color-dark-8);
@@ -31,32 +31,46 @@
31
31
  --alepha-text: var(--alepha-text-light);
32
32
  }
33
33
 
34
- /*#root {*/
35
- /* display: flex;*/
36
- /* background-color: var(--alepha-background);*/
37
- /* color: var(--alepha-text);*/
38
- /* min-height: 100dvh;*/
39
- /* flex-direction: column;*/
40
- /*}*/
41
-
42
34
  /* ------------------------------------------------------------------------------------------------------------------ */
43
35
 
44
36
  [data-mantine-color-scheme="dark"] {
45
37
  --alepha-surface: var(--alepha-surface-dark);
46
38
  --alepha-elevated: var(--alepha-elevated-dark);
47
39
  --alepha-elevated-hover: var(--alepha-elevated-hover-dark);
48
- --alepha-background: var(--alepha-background-dark);
40
+ --alepha-ground: var(--alepha-ground-dark);
49
41
  --alepha-text: var(--alepha-text-dark);
50
42
  }
51
43
 
52
44
  /* ------------------------------------------------------------------------------------------------------------------ */
53
45
 
46
+ .shadow-xs {
47
+ box-shadow: var(--mantine-shadow-xs);
48
+ }
49
+
50
+ .shadow-sm {
51
+ box-shadow: var(--mantine-shadow-sm);
52
+ }
53
+
54
+ .shadow-md {
55
+ box-shadow: var(--mantine-shadow-md);
56
+ }
57
+
58
+ .shadow-lg {
59
+ box-shadow: var(--mantine-shadow-lg);
60
+ }
61
+
62
+ .shadow-xl {
63
+ box-shadow: var(--mantine-shadow-xl);
64
+ }
65
+
54
66
  .overflow-auto {
55
67
  overflow: auto;
56
68
  scrollbar-color: var(--alepha-text-muted) transparent;
57
69
  scrollbar-width: thin;
58
70
  }
59
71
 
72
+ /* ------------------------------------------------------------------------------------------------------------------ */
73
+
60
74
  /* Sidebar scroll area - subtle scrollbar that appears on hover */
61
75
  .alepha-sidebar-scroll {
62
76
  overflow-y: auto;
@@ -96,17 +110,6 @@
96
110
  background: var(--mantine-color-dark-4);
97
111
  }
98
112
 
99
- /* ------------------------------------------------------------------------------------------------------------------ */
100
- /* Sidebar Transitions */
101
-
102
- .alepha-sidebar-navbar {
103
- transition: width 0.2s ease-in-out;
104
- }
105
-
106
- .alepha-sidebar-main {
107
- transition: padding-left 0.2s ease-in-out;
108
- }
109
-
110
113
  /* ------------------------------------------------------------------------------------------------------------------ */
111
114
  /* Modal Customizations */
112
115
 
@@ -1,24 +1,25 @@
1
- import { ActionButton, ui } from "@alepha/ui";
2
- import {
3
- Checkbox,
4
- Drawer,
5
- Flex,
6
- Table,
7
- Text,
8
- UnstyledButton,
9
- } from "@mantine/core";
1
+ import { ActionButton, Flex, isComponentType, Text, ui } from "@alepha/ui";
2
+ import { Checkbox, Drawer, Table, UnstyledButton } from "@mantine/core";
10
3
  import {
11
4
  IconArrowDown,
12
5
  IconArrowsSort,
13
6
  IconArrowUp,
14
7
  IconChevronDown,
15
8
  IconChevronRight,
9
+ IconDotsVertical,
16
10
  } from "@tabler/icons-react";
17
11
  import { Alepha, type Static, type TObject, t } from "alepha";
18
12
  import { DateTimeProvider } from "alepha/datetime";
19
13
  import { useInject } from "alepha/react";
20
14
  import { type FormModel, useForm } from "alepha/react/form";
21
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
15
+ import {
16
+ type ReactNode,
17
+ useCallback,
18
+ useEffect,
19
+ useMemo,
20
+ useRef,
21
+ useState,
22
+ } from "react";
22
23
  import type {
23
24
  ColumnVisibility,
24
25
  DataTableColumnContext,
@@ -318,7 +319,8 @@ const DataTable = <T extends object, Filters extends TObject>(
318
319
  const totalColumns =
319
320
  visibleColumns.length +
320
321
  (panelConfig ? 1 : 0) +
321
- (props.withCheckbox ? 1 : 0);
322
+ (props.withCheckbox ? 1 : 0) +
323
+ (props.rowActions ? 1 : 0);
322
324
 
323
325
  // Checkbox header column
324
326
  const checkboxHeader = props.withCheckbox ? (
@@ -351,7 +353,9 @@ const DataTable = <T extends object, Filters extends TObject>(
351
353
  }}
352
354
  >
353
355
  <Flex align="center" gap={4}>
354
- <Text size="xs">{col.label}</Text>
356
+ <Text bold muted size="xs">
357
+ {col.label}
358
+ </Text>
355
359
  {col.sortable && (
356
360
  <Flex c="dimmed">
357
361
  {sortDir === "asc" && <IconArrowUp size={ui.sizes.icon.sm} />}
@@ -424,45 +428,58 @@ const DataTable = <T extends object, Filters extends TObject>(
424
428
  alepha,
425
429
  } as DataTableColumnContext<Filters>;
426
430
 
427
- if (col.actions) {
428
- const rowActions = col
429
- .actions(item as T, ctx)
430
- .filter((a) => a.visible !== false);
431
+ return (
432
+ <Table.Td key={key} style={col.fit ? FIT_STYLE : undefined}>
433
+ {col.value?.(item as T, ctx)}
434
+ </Table.Td>
435
+ );
436
+ })}
437
+ {props.rowActions &&
438
+ (() => {
439
+ const ctx = {
440
+ index,
441
+ form: form as unknown as FormModel<Filters>,
442
+ alepha,
443
+ } as DataTableColumnContext<Filters>;
444
+ const actions = props.rowActions!(item as T, ctx).filter(
445
+ (a) => a.visible !== false,
446
+ );
447
+ if (actions.length === 0) return <Table.Td style={FIT_STYLE} />;
431
448
  return (
432
449
  <Table.Td
433
450
  py={2}
434
451
  px={4}
435
- key={key}
436
- style={col.fit ? FIT_STYLE : undefined}
452
+ style={FIT_STYLE}
437
453
  onClick={(e) => e.stopPropagation()}
438
454
  >
439
- <Flex gap={4}>
440
- {rowActions.map(({ visible: _, ...actionProps }, i) => (
441
- <ActionButton
442
- key={i}
443
- variant="subtle"
444
- size="xs"
445
- preventDefault
446
- h={20}
447
- {...actionProps}
448
- />
449
- ))}
450
- </Flex>
455
+ <ActionButton
456
+ variant="subtle"
457
+ size="xs"
458
+ icon={IconDotsVertical}
459
+ menu={{
460
+ items: actions.map((action) => {
461
+ const Icon = action.icon;
462
+ return {
463
+ label:
464
+ action.label ??
465
+ (typeof action.tooltip === "string"
466
+ ? action.tooltip
467
+ : undefined),
468
+ icon:
469
+ Icon && isComponentType(Icon) ? (
470
+ <Icon size={14} />
471
+ ) : (
472
+ (Icon as ReactNode)
473
+ ),
474
+ onClick: (action as any).onClick,
475
+ color: action.color,
476
+ };
477
+ }),
478
+ }}
479
+ />
451
480
  </Table.Td>
452
481
  );
453
- }
454
-
455
- return (
456
- <Table.Td
457
- py={2}
458
- px={4}
459
- key={key}
460
- style={col.fit ? FIT_STYLE : undefined}
461
- >
462
- {col.value?.(item as T, ctx)}
463
- </Table.Td>
464
- );
465
- })}
482
+ })()}
466
483
  </Table.Tr>,
467
484
  ];
468
485
 
@@ -485,106 +502,119 @@ const DataTable = <T extends object, Filters extends TObject>(
485
502
  }, [props.filters, form.options.schema]);
486
503
 
487
504
  return (
488
- <Flex flex={1} p={0} bdrs="sm" direction="column">
489
- <DataTableToolbar
490
- columns={props.columns}
491
- filters={props.filters}
492
- columnVisibility={columnVisibility}
493
- filterVisibility={filterVisibility}
494
- onColumnVisibilityChange={setColumnVisibility}
495
- onFilterVisibilityChange={setFilterVisibility}
496
- actions={props.actions}
497
- onRefresh={() => form.submit()}
498
- items={items.content as T[]}
499
- withExport={props.withExport}
500
- selectedItems={selection.selectedItems}
501
- checkboxActions={props.checkboxActions}
502
- onClearSelection={selection.clear}
503
- />
504
-
505
- {filterSchema && props.filters && (
506
- <DataTableFilters
507
- schema={filterSchema}
508
- form={form as unknown as FormModel<TObject>}
509
- typeFormProps={
510
- props.typeFormProps as DataTableFiltersProps["typeFormProps"]
511
- }
505
+ <Flex
506
+ gap={"xs"}
507
+ flex={1}
508
+ p={0}
509
+ direction="column"
510
+ style={{ overflow: "hidden" }}
511
+ >
512
+ <Flex rounded bordered elevated shadowed={"xs"} col>
513
+ <DataTableToolbar
514
+ columns={props.columns}
515
+ filters={props.filters}
516
+ columnVisibility={columnVisibility}
512
517
  filterVisibility={filterVisibility}
518
+ onColumnVisibilityChange={setColumnVisibility}
519
+ onFilterVisibilityChange={setFilterVisibility}
520
+ actions={props.actions}
521
+ onRefresh={() => form.submit()}
522
+ items={items.content as T[]}
523
+ withExport={props.withExport}
524
+ selectedItems={selection.selectedItems}
525
+ checkboxActions={props.checkboxActions}
526
+ onClearSelection={selection.clear}
513
527
  />
514
- )}
515
-
516
- <Flex className="overflow-auto">
517
- <Table
518
- aria-label="Data table"
519
- withColumnBorders
520
- withRowBorders
521
- {...props.tableProps}
522
- >
523
- <Table.Thead
524
- style={{
525
- position: "sticky",
526
- top: 0,
527
- zIndex: 1,
528
- backgroundColor: "var(--mantine-color-body)",
529
- }}
530
- >
531
- <Table.Tr>
532
- {panelConfig && <Table.Th style={{ width: 36 }} />}
533
- {checkboxHeader}
534
- {head}
535
- </Table.Tr>
536
- </Table.Thead>
537
- <Table.Tbody
538
- style={{
539
- opacity: form.submitting ? 0.5 : 1,
540
- transition: "opacity 150ms ease",
541
- }}
528
+
529
+ {filterSchema && props.filters && (
530
+ <DataTableFilters
531
+ schema={filterSchema}
532
+ form={form as unknown as FormModel<TObject>}
533
+ typeFormProps={
534
+ props.typeFormProps as DataTableFiltersProps["typeFormProps"]
535
+ }
536
+ filterVisibility={filterVisibility}
537
+ />
538
+ )}
539
+ </Flex>
540
+
541
+ <Flex col rounded bordered elevated shadowed={"xs"}>
542
+ <Flex className="overflow-auto">
543
+ <Table
544
+ aria-label="Data table"
545
+ withRowBorders
546
+ highlightOnHover
547
+ {...props.tableProps}
542
548
  >
543
- {rows}
544
- {items.content.length === 0 && (
549
+ <Table.Thead
550
+ style={{
551
+ position: "sticky",
552
+ top: 0,
553
+ zIndex: 1,
554
+ }}
555
+ >
545
556
  <Table.Tr>
546
- <Table.Td
547
- colSpan={totalColumns || 1}
548
- py="xl"
549
- style={{ textAlign: "center" }}
550
- >
551
- <Text c="dimmed" size="sm">
552
- {form.submitting ? "Loading…" : "No results"}
553
- </Text>
554
- </Table.Td>
557
+ {panelConfig && <Table.Th style={{ width: 36 }} />}
558
+ {checkboxHeader}
559
+ {head}
560
+ {props.rowActions && <Table.Th style={FIT_STYLE} />}
555
561
  </Table.Tr>
556
- )}
557
- </Table.Tbody>
558
- </Table>
559
- </Flex>
562
+ </Table.Thead>
563
+ <Table.Tbody
564
+ style={{
565
+ opacity: form.submitting ? 0.5 : 1,
566
+ transition: "opacity 150ms ease",
567
+ }}
568
+ >
569
+ {rows}
570
+ {items.content.length === 0 && (
571
+ <Table.Tr>
572
+ <Table.Td
573
+ colSpan={totalColumns || 1}
574
+ py="xl"
575
+ style={{ textAlign: "center" }}
576
+ >
577
+ <Text c="dimmed" size="sm">
578
+ {form.submitting ? "Loading…" : "No results"}
579
+ </Text>
580
+ </Table.Td>
581
+ </Table.Tr>
582
+ )}
583
+ </Table.Tbody>
584
+ </Table>
585
+ </Flex>
560
586
 
561
- {props.infinityScroll && <div ref={sentinelRef} />}
562
-
563
- {!props.infinityScroll && (
564
- <DataTablePagination
565
- page={page}
566
- size={size}
567
- totalPages={items.page?.totalPages ?? 1}
568
- onPageChange={(value) => {
569
- form.input.page.set(value - 1);
570
- }}
571
- onSizeChange={(value) => {
572
- form.input.size.set(value);
573
- }}
574
- />
575
- )}
576
-
577
- {drawerConfig && (
578
- <Drawer
579
- opened={drawerItem !== null}
580
- onClose={() => setDrawerItem(null)}
581
- position="right"
582
- size="xl"
583
- {...drawerConfig.props}
584
- >
585
- {drawerItem && drawerConfig.render(drawerItem)}
586
- </Drawer>
587
- )}
587
+ {props.infinityScroll && <div ref={sentinelRef} />}
588
+
589
+ {!props.infinityScroll && (
590
+ <DataTablePagination
591
+ page={page}
592
+ size={size}
593
+ totalPages={items.page?.totalPages ?? 1}
594
+ totalElements={items.page?.totalElements}
595
+ offset={items.page?.offset ?? 0}
596
+ numberOfElements={items.content.length}
597
+ onPageChange={(value) => {
598
+ form.input.page.set(value - 1);
599
+ }}
600
+ onSizeChange={(value) => {
601
+ form.input.size.set(value);
602
+ }}
603
+ />
604
+ )}
605
+
606
+ {drawerConfig && (
607
+ <Drawer
608
+ opened={drawerItem !== null}
609
+ onClose={() => setDrawerItem(null)}
610
+ position="right"
611
+ size="xl"
612
+ {...drawerConfig.props}
613
+ >
614
+ {drawerItem && drawerConfig.render(drawerItem)}
615
+ </Drawer>
616
+ )}
617
+ </Flex>
588
618
  </Flex>
589
619
  );
590
620
  };
@@ -47,12 +47,7 @@ const DataTableFilters = ({
47
47
  }
48
48
 
49
49
  return (
50
- <Flex
51
- w="100%"
52
- p="xs"
53
- bg={ui.colors.surface}
54
- style={{ borderBottom: "1px solid var(--alepha-border)" }}
55
- >
50
+ <Flex w="100%" p="xs" m="xs" bdrs="md" bg={ui.colors.surface}>
56
51
  <TypeForm
57
52
  size={"xs"}
58
53
  {...typeFormProps}