@alepha/ui 0.13.6 → 0.13.8

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 (157) hide show
  1. package/dist/admin/AdminAudits-CwvH8e8c.js +215 -0
  2. package/dist/admin/AdminAudits-CwvH8e8c.js.map +1 -0
  3. package/dist/admin/AdminAudits-Dv8Vk_6r.js +3 -0
  4. package/dist/admin/AdminFiles-5CPA3lQk.js +3 -0
  5. package/dist/admin/{AdminFiles-B_jfB_Py.js → AdminFiles-C_w1tb_x.js} +4 -3
  6. package/dist/admin/AdminFiles-C_w1tb_x.js.map +1 -0
  7. package/dist/admin/AdminLayout-BnSmtA4x.js +3 -0
  8. package/dist/admin/AdminLayout-XiSivwWH.js +39 -0
  9. package/dist/admin/AdminLayout-XiSivwWH.js.map +1 -0
  10. package/dist/admin/AdminNotifications-DLjmZWtf.js +3 -0
  11. package/dist/admin/{AdminNotifications-BFEjqpqx.js → AdminNotifications-DuYy74AN.js} +3 -3
  12. package/dist/admin/AdminNotifications-DuYy74AN.js.map +1 -0
  13. package/dist/admin/AdminParameters-DYg48Jwe.js +3 -0
  14. package/dist/admin/AdminParameters-YagqWTG3.js +575 -0
  15. package/dist/admin/AdminParameters-YagqWTG3.js.map +1 -0
  16. package/dist/admin/{AdminSessions-D7DESfWK.js → AdminSessions-BCjgJ-93.js} +4 -4
  17. package/dist/admin/AdminSessions-BCjgJ-93.js.map +1 -0
  18. package/dist/admin/AdminSessions-DEh2uN-4.js +3 -0
  19. package/dist/admin/AdminUserAudits-B_PUXCKC.js +177 -0
  20. package/dist/admin/AdminUserAudits-B_PUXCKC.js.map +1 -0
  21. package/dist/admin/AdminUserAudits-D7cTcElL.js +3 -0
  22. package/dist/admin/{AdminUserCreate-Bhxsn92l.js → AdminUserCreate-DzfRbGZ4.js} +4 -4
  23. package/dist/admin/AdminUserCreate-DzfRbGZ4.js.map +1 -0
  24. package/dist/admin/{AdminUserCreate-CYI_xW5T.js → AdminUserCreate-oUA1KDIl.js} +1 -1
  25. package/dist/admin/{AdminUserDetails-C2y1Ig4n.js → AdminUserDetails-DeTrJm-t.js} +5 -5
  26. package/dist/admin/AdminUserDetails-DeTrJm-t.js.map +1 -0
  27. package/dist/admin/{AdminUserDetails-Cmzx9HxH.js → AdminUserDetails-y1H5DW8Y.js} +1 -1
  28. package/dist/admin/{AdminUserLayout-sW6cjZL0.js → AdminUserLayout-CsfrrZkD.js} +4 -7
  29. package/dist/admin/AdminUserLayout-CsfrrZkD.js.map +1 -0
  30. package/dist/admin/{AdminUserLayout-DGSf612u.js → AdminUserLayout-Dejnz13m.js} +1 -1
  31. package/dist/admin/AdminUserSessions-Bbhcpz4k.js +3 -0
  32. package/dist/admin/{AdminUserSessions-CvN15wPe.js → AdminUserSessions-DO9H85O-.js} +4 -4
  33. package/dist/admin/AdminUserSessions-DO9H85O-.js.map +1 -0
  34. package/dist/admin/{AdminUserSettings-DvaaxgcV.js → AdminUserSettings-B3jA8g3p.js} +4 -4
  35. package/dist/admin/AdminUserSettings-B3jA8g3p.js.map +1 -0
  36. package/dist/admin/AdminUserSettings-CE0xpbQc.js +3 -0
  37. package/dist/admin/AdminUsers-CegGZDhW.js +3 -0
  38. package/dist/admin/{AdminUsers-BR3C-jrg.js → AdminUsers-ebbrJBT0.js} +13 -17
  39. package/dist/admin/AdminUsers-ebbrJBT0.js.map +1 -0
  40. package/dist/admin/index.d.ts +2700 -1178
  41. package/dist/admin/index.js +65 -62
  42. package/dist/admin/index.js.map +1 -1
  43. package/dist/auth/AuthLayout-BAZJHzDG.js +23 -0
  44. package/dist/auth/AuthLayout-BAZJHzDG.js.map +1 -0
  45. package/dist/auth/{Login-7HlBjDeV.js → Login-CeNZZjrr.js} +80 -44
  46. package/dist/auth/Login-CeNZZjrr.js.map +1 -0
  47. package/dist/auth/Login-hQcu1nlu.js +4 -0
  48. package/dist/auth/Register-B6HBNVHS.js +4 -0
  49. package/dist/auth/{Register-CuQr3kgi.js → Register-s4ENeyiE.js} +131 -91
  50. package/dist/auth/Register-s4ENeyiE.js.map +1 -0
  51. package/dist/auth/ResetPassword-Cjd-W-Nu.js +3 -0
  52. package/dist/auth/ResetPassword-GLIFkJT7.js +278 -0
  53. package/dist/auth/ResetPassword-GLIFkJT7.js.map +1 -0
  54. package/dist/auth/index.d.ts +605 -532
  55. package/dist/auth/index.js +26 -18
  56. package/dist/auth/index.js.map +1 -1
  57. package/dist/core/index.d.ts +425 -155
  58. package/dist/core/index.js +1751 -1369
  59. package/dist/core/index.js.map +1 -1
  60. package/package.json +23 -20
  61. package/src/admin/AdminRouter.ts +70 -16
  62. package/src/admin/components/AdminLayout.tsx +41 -61
  63. package/src/admin/components/audits/AdminAudits.tsx +240 -0
  64. package/src/admin/components/{AdminFiles.tsx → files/AdminFiles.tsx} +1 -1
  65. package/src/admin/components/{AdminJobs.tsx → jobs/AdminJobs.tsx} +1 -1
  66. package/src/admin/components/parameters/AdminParameters.tsx +137 -0
  67. package/src/admin/components/parameters/ParameterDetails.tsx +228 -0
  68. package/src/admin/components/parameters/ParameterHistory.tsx +146 -0
  69. package/src/admin/components/parameters/ParameterTree.tsx +146 -0
  70. package/src/admin/components/parameters/types.ts +35 -0
  71. package/src/admin/components/{AdminSessions.tsx → sessions/AdminSessions.tsx} +1 -1
  72. package/src/admin/components/users/AdminUserAudits.tsx +183 -0
  73. package/src/admin/components/{AdminUserCreate.tsx → users/AdminUserCreate.tsx} +1 -1
  74. package/src/admin/components/{AdminUserLayout.tsx → users/AdminUserLayout.tsx} +1 -4
  75. package/src/admin/components/{AdminUserSettings.tsx → users/AdminUserSettings.tsx} +1 -1
  76. package/src/admin/components/{AdminUsers.tsx → users/AdminUsers.tsx} +10 -12
  77. package/src/admin/index.ts +24 -16
  78. package/src/auth/AuthRouter.ts +23 -17
  79. package/src/auth/components/AuthLayout.tsx +6 -3
  80. package/src/auth/components/Login.tsx +109 -47
  81. package/src/auth/components/Register.tsx +158 -94
  82. package/src/auth/components/ResetPassword.tsx +51 -5
  83. package/src/auth/components/buttons/UserButton.tsx +2 -0
  84. package/src/core/atoms/alephaThemeAtom.ts +13 -0
  85. package/src/core/atoms/alephaThemeListAtom.ts +10 -0
  86. package/src/core/atoms/themes/default.ts +6 -0
  87. package/src/core/{themes → atoms/themes}/midnight.ts +3 -5
  88. package/src/core/components/buttons/ActionButton.tsx +33 -26
  89. package/src/core/components/buttons/DarkModeButton.tsx +0 -1
  90. package/src/core/components/buttons/ThemeButton.tsx +10 -7
  91. package/src/core/components/buttons/ToggleSidebarButton.tsx +19 -16
  92. package/src/core/components/data/ErrorViewer.tsx +171 -0
  93. package/src/core/components/data/JsonViewer.tsx +147 -138
  94. package/src/core/components/form/Control.tsx +95 -18
  95. package/src/core/components/form/ControlArray.tsx +377 -0
  96. package/src/core/components/form/ControlObject.tsx +127 -0
  97. package/src/core/components/form/TypeForm.tsx +99 -37
  98. package/src/core/components/layout/AdminShell.tsx +14 -1
  99. package/src/core/components/layout/AlephaMantineProvider.tsx +7 -3
  100. package/src/core/components/layout/Omnibar.tsx +1 -1
  101. package/src/core/components/layout/Sidebar.tsx +47 -14
  102. package/src/core/components/table/ColumnPicker.tsx +126 -0
  103. package/src/core/components/table/DataTable.tsx +354 -181
  104. package/src/core/components/table/DataTableFilters.tsx +64 -0
  105. package/src/core/components/table/DataTablePagination.tsx +59 -0
  106. package/src/core/components/table/DataTableToolbar.tsx +126 -0
  107. package/src/core/components/table/FilterPicker.tsx +138 -0
  108. package/src/core/components/table/types.ts +199 -0
  109. package/src/core/helpers/isComponentType.ts +9 -0
  110. package/src/core/helpers/renderIcon.tsx +13 -0
  111. package/src/core/hooks/useTheme.ts +24 -18
  112. package/src/core/index.ts +24 -3
  113. package/src/core/interfaces/AlephaTheme.ts +8 -0
  114. package/src/core/providers/ThemeProvider.ts +44 -62
  115. package/src/core/services/DialogService.tsx +24 -0
  116. package/src/core/utils/parseInput.ts +2 -2
  117. package/styles.css +1 -1
  118. package/dist/admin/AdminFiles-B-0UcHVV.js +0 -3
  119. package/dist/admin/AdminFiles-B_jfB_Py.js.map +0 -1
  120. package/dist/admin/AdminLayout-BMtiXAzS.js +0 -396
  121. package/dist/admin/AdminLayout-BMtiXAzS.js.map +0 -1
  122. package/dist/admin/AdminLayout-BNo3GoHR.js +0 -3
  123. package/dist/admin/AdminNotifications-BFEjqpqx.js.map +0 -1
  124. package/dist/admin/AdminNotifications-DJs2ZjNj.js +0 -3
  125. package/dist/admin/AdminSessions-D7DESfWK.js.map +0 -1
  126. package/dist/admin/AdminSessions-PS2M8iXi.js +0 -3
  127. package/dist/admin/AdminUserCreate-Bhxsn92l.js.map +0 -1
  128. package/dist/admin/AdminUserDetails-C2y1Ig4n.js.map +0 -1
  129. package/dist/admin/AdminUserLayout-sW6cjZL0.js.map +0 -1
  130. package/dist/admin/AdminUserSessions-CvN15wPe.js.map +0 -1
  131. package/dist/admin/AdminUserSessions-D-aOcZgV.js +0 -3
  132. package/dist/admin/AdminUserSettings-CEMhIYrI.js +0 -3
  133. package/dist/admin/AdminUserSettings-DvaaxgcV.js.map +0 -1
  134. package/dist/admin/AdminUsers-BR3C-jrg.js.map +0 -1
  135. package/dist/admin/AdminUsers-CMW9vN09.js +0 -3
  136. package/dist/auth/AuthLayout-CzwUKD9y.js +0 -19
  137. package/dist/auth/AuthLayout-CzwUKD9y.js.map +0 -1
  138. package/dist/auth/Login-7HlBjDeV.js.map +0 -1
  139. package/dist/auth/Login-C-e27DGb.js +0 -4
  140. package/dist/auth/Register-CuQr3kgi.js.map +0 -1
  141. package/dist/auth/Register-DbvXwgbG.js +0 -4
  142. package/dist/auth/ResetPassword-BzU-cdd4.js +0 -243
  143. package/dist/auth/ResetPassword-BzU-cdd4.js.map +0 -1
  144. package/dist/auth/ResetPassword-DSvrdpaA.js +0 -3
  145. package/src/admin/AdminSidebar.ts +0 -31
  146. package/src/admin/components/AdminParameters.tsx +0 -24
  147. package/src/core/themes/aurora.ts +0 -107
  148. package/src/core/themes/crystal.ts +0 -107
  149. package/src/core/themes/default.ts +0 -7
  150. package/src/core/themes/ember.ts +0 -107
  151. package/src/core/themes/index.ts +0 -7
  152. package/src/core/themes/remoraid.ts +0 -278
  153. package/src/core/themes/slate.ts +0 -81
  154. /package/src/admin/components/{AdminNotifications.tsx → notifications/AdminNotifications.tsx} +0 -0
  155. /package/src/admin/components/{AdminUserDetails.tsx → users/AdminUserDetails.tsx} +0 -0
  156. /package/src/admin/components/{AdminUserSessions.tsx → users/AdminUserSessions.tsx} +0 -0
  157. /package/src/admin/components/{AdminVerifications.tsx → verifications/AdminVerifications.tsx} +0 -0
@@ -1,426 +1,43 @@
1
1
  import { AlephaReactForm, FormValidationError, useForm, useFormState } from "@alepha/react/form";
2
2
  import { $head, AlephaReactHead } from "@alepha/react/head";
3
3
  import { AlephaReactI18n, useI18n } from "@alepha/react/i18n";
4
- import { $atom, $inject, $module, Alepha, TypeBoxError, t } from "alepha";
4
+ import { $atom, $inject, $module, Alepha, AlephaError, TypeBoxError, t } from "alepha";
5
5
  import { $cookie } from "alepha/server/cookies";
6
6
  import { $page, NestedView, useAction, useActive, useEvents, useInject, useRouter, useStore } from "@alepha/react";
7
- import { ActionIcon, Anchor, AppShell, Autocomplete, Badge, Box, Burger, Button, Card, Collapse, ColorInput, ColorSchemeScript, CopyButton, Divider, FileInput, Flex, Flex as Flex$1, Grid, Group, Input, Kbd, MantineProvider, Menu, MultiSelect, NumberInput, Pagination, PasswordInput, Popover, SegmentedControl, Select, Slider, Stack, Switch, Table, TagsInput, Text, Text as Text$1, TextInput, Textarea, ThemeIcon, Tooltip, useComputedColorScheme, useMantineColorScheme } from "@mantine/core";
7
+ import { ActionIcon, Anchor, AppShell, Autocomplete, Badge, Box, Burger, Button, Card, Checkbox, Collapse, ColorInput, ColorSchemeScript, CopyButton, Divider, Fieldset, FileInput, Flex, Flex as Flex$1, Grid, Group, Input, Kbd, MantineProvider, Menu, MultiSelect, NumberInput, Pagination, PasswordInput, Popover, ScrollArea, SegmentedControl, Select, Slider, Stack, Switch, Table, TagsInput, Text, Text as Text$1, TextInput, Textarea, ThemeIcon, Tooltip, UnstyledButton, useComputedColorScheme, useMantineColorScheme, useMantineTheme } from "@mantine/core";
8
8
  import { ModalsProvider, modals } from "@mantine/modals";
9
9
  import { Notifications, notifications } from "@mantine/notifications";
10
10
  import { NavigationProgress, nprogress } from "@mantine/nprogress";
11
- import { IconAlertTriangle, IconAt, IconCalendar, IconCheck, IconChevronDown, IconChevronRight, IconClock, IconColorPicker, IconCopy, IconFile, IconFilter, IconHash, IconInfoCircle, IconInfoTriangle, IconKey, IconLanguage, IconLetterCase, IconLink, IconList, IconMail, IconMoon, IconPalette, IconPhone, IconSearch, IconSelector, IconSquareRounded, IconSun, IconToggleLeft, IconX } from "@tabler/icons-react";
11
+ import { IconAlertTriangle, IconArrowDown, IconArrowUp, IconArrowsSort, IconAt, IconCalendar, IconCheck, IconChevronDown, IconChevronRight, IconClock, IconColorPicker, IconColumns, IconCopy, IconFile, IconFilter, IconGripVertical, IconHash, IconInfoCircle, IconInfoTriangle, IconKey, IconLanguage, IconLayoutSidebarLeftCollapse, IconLayoutSidebarRightCollapse, IconLetterCase, IconLink, IconList, IconMail, IconMoon, IconPalette, IconPhone, IconPlus, IconRefresh, IconSearch, IconSelector, IconSquareRounded, IconSun, IconToggleLeft, IconTrash, IconX } from "@tabler/icons-react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
13
  import { Spotlight, spotlight } from "@mantine/spotlight";
14
14
  import { Children, createElement, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
15
+ import { ui as ui$1 } from "@alepha/ui";
15
16
  import { DateInput, DateTimePicker, TimeInput } from "@mantine/dates";
16
17
  import { parseQueryString } from "alepha/orm";
17
18
  import { useDebouncedCallback } from "@mantine/hooks";
18
19
  import { DateTimeProvider } from "alepha/datetime";
19
20
 
20
- //#region ../../src/core/themes/aurora.ts
21
- const auroraTheme = {
22
- id: "aurora",
23
- label: "Aurora",
24
- description: "Vibrant, playful with gradients and hover effects",
25
- primaryColor: "violet",
26
- primaryShade: {
27
- light: 5,
28
- dark: 4
29
- },
30
- fontFamily: "\"Nunito\", \"Poppins\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif",
31
- fontFamilyMonospace: "\"Fira Code\", \"JetBrains Mono\", ui-monospace, Consolas, monospace",
32
- headings: {
33
- fontFamily: "\"Nunito\", \"Poppins\", -apple-system, BlinkMacSystemFont, sans-serif",
34
- fontWeight: "700",
35
- textWrap: "wrap",
36
- sizes: {
37
- h1: {
38
- fontSize: "2.25rem",
39
- lineHeight: "1.3"
40
- },
41
- h2: {
42
- fontSize: "1.75rem",
43
- lineHeight: "1.35"
44
- },
45
- h3: {
46
- fontSize: "1.375rem",
47
- lineHeight: "1.4"
48
- },
49
- h4: {
50
- fontSize: "1.125rem",
51
- lineHeight: "1.45"
52
- },
53
- h5: {
54
- fontSize: "1rem",
55
- lineHeight: "1.5"
56
- },
57
- h6: {
58
- fontSize: "0.875rem",
59
- lineHeight: "1.5"
60
- }
61
- }
62
- },
63
- fontSizes: {
64
- xs: "0.8rem",
65
- sm: "0.9rem",
66
- md: "1rem",
67
- lg: "1.125rem",
68
- xl: "1.3rem"
69
- },
70
- lineHeights: {
71
- xs: "1.5",
72
- sm: "1.55",
73
- md: "1.6",
74
- lg: "1.65",
75
- xl: "1.7"
76
- },
77
- radius: {
78
- xs: "6px",
79
- sm: "8px",
80
- md: "12px",
81
- lg: "16px",
82
- xl: "24px"
83
- },
84
- defaultRadius: "md",
85
- shadows: {
86
- xs: "0 2px 4px rgba(139, 92, 246, 0.08)",
87
- sm: "0 4px 8px rgba(139, 92, 246, 0.1)",
88
- md: "0 8px 16px rgba(139, 92, 246, 0.12)",
89
- lg: "0 16px 32px rgba(139, 92, 246, 0.15)",
90
- xl: "0 24px 48px rgba(139, 92, 246, 0.18)"
91
- },
92
- defaultGradient: {
93
- from: "violet",
94
- to: "pink",
95
- deg: 135
96
- },
97
- colors: {
98
- dark: [
99
- "#d4d0dc",
100
- "#a8a3b3",
101
- "#7c7689",
102
- "#5c5568",
103
- "#454050",
104
- "#302c38",
105
- "#252129",
106
- "#1e1b24",
107
- "#16141a",
108
- "#0d0c10"
109
- ],
110
- gray: [
111
- "#faf9fb",
112
- "#f3f1f5",
113
- "#e8e5ed",
114
- "#d4d0dc",
115
- "#a8a3b3",
116
- "#7c7689",
117
- "#5c5568",
118
- "#454050",
119
- "#302c38",
120
- "#1e1b24"
121
- ],
122
- violet: [
123
- "#f5f3ff",
124
- "#ede9fe",
125
- "#ddd6fe",
126
- "#c4b5fd",
127
- "#a78bfa",
128
- "#8b5cf6",
129
- "#7c3aed",
130
- "#6d28d9",
131
- "#5b21b6",
132
- "#4c1d95"
133
- ],
134
- pink: [
135
- "#fdf2f8",
136
- "#fce7f3",
137
- "#fbcfe8",
138
- "#f9a8d4",
139
- "#f472b6",
140
- "#ec4899",
141
- "#db2777",
142
- "#be185d",
143
- "#9d174d",
144
- "#831843"
145
- ]
146
- }
147
- };
148
-
149
- //#endregion
150
- //#region ../../src/core/themes/crystal.ts
151
- const crystalTheme = {
152
- id: "crystal",
153
- label: "Crystal",
154
- description: "Glass-morphism with backdrop blur effects",
155
- primaryColor: "blue",
156
- primaryShade: {
157
- light: 5,
158
- dark: 4
159
- },
160
- fontFamily: "\"SF Pro Display\", \"Helvetica Neue\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif",
161
- fontFamilyMonospace: "\"SF Mono\", \"Fira Code\", ui-monospace, Menlo, Consolas, monospace",
162
- headings: {
163
- fontFamily: "\"SF Pro Display\", \"Helvetica Neue\", -apple-system, BlinkMacSystemFont, sans-serif",
164
- fontWeight: "600",
165
- textWrap: "wrap",
166
- sizes: {
167
- h1: {
168
- fontSize: "2rem",
169
- lineHeight: "1.25"
170
- },
171
- h2: {
172
- fontSize: "1.5rem",
173
- lineHeight: "1.3"
174
- },
175
- h3: {
176
- fontSize: "1.25rem",
177
- lineHeight: "1.35"
178
- },
179
- h4: {
180
- fontSize: "1.0625rem",
181
- lineHeight: "1.4"
182
- },
183
- h5: {
184
- fontSize: "0.9375rem",
185
- lineHeight: "1.45"
186
- },
187
- h6: {
188
- fontSize: "0.8125rem",
189
- lineHeight: "1.5"
190
- }
191
- }
192
- },
193
- fontSizes: {
194
- xs: "0.75rem",
195
- sm: "0.8125rem",
196
- md: "0.9375rem",
197
- lg: "1.0625rem",
198
- xl: "1.25rem"
199
- },
200
- lineHeights: {
201
- xs: "1.45",
202
- sm: "1.5",
203
- md: "1.55",
204
- lg: "1.6",
205
- xl: "1.65"
206
- },
207
- radius: {
208
- xs: "4px",
209
- sm: "8px",
210
- md: "10px",
211
- lg: "14px",
212
- xl: "20px"
213
- },
214
- defaultRadius: "md",
215
- shadows: {
216
- xs: "0 2px 8px rgba(59, 130, 246, 0.04)",
217
- sm: "0 4px 12px rgba(59, 130, 246, 0.06)",
218
- md: "0 8px 20px rgba(59, 130, 246, 0.08)",
219
- lg: "0 16px 32px rgba(59, 130, 246, 0.1)",
220
- xl: "0 24px 48px rgba(59, 130, 246, 0.12)"
221
- },
222
- defaultGradient: {
223
- from: "cyan",
224
- to: "blue",
225
- deg: 135
226
- },
227
- colors: {
228
- dark: [
229
- "#cbd5e1",
230
- "#94a3b8",
231
- "#64748b",
232
- "#475569",
233
- "#334155",
234
- "#1e293b",
235
- "#0f172a",
236
- "#0c1322",
237
- "#080e1a",
238
- "#040711"
239
- ],
240
- gray: [
241
- "#f8fafc",
242
- "#f1f5f9",
243
- "#e2e8f0",
244
- "#cbd5e1",
245
- "#94a3b8",
246
- "#64748b",
247
- "#475569",
248
- "#334155",
249
- "#1e293b",
250
- "#0f172a"
251
- ],
252
- blue: [
253
- "#f0f9ff",
254
- "#e0f2fe",
255
- "#bae6fd",
256
- "#7dd3fc",
257
- "#38bdf8",
258
- "#0ea5e9",
259
- "#0284c7",
260
- "#0369a1",
261
- "#075985",
262
- "#0c4a6e"
263
- ],
264
- cyan: [
265
- "#ecfeff",
266
- "#cffafe",
267
- "#a5f3fc",
268
- "#67e8f9",
269
- "#22d3ee",
270
- "#06b6d4",
271
- "#0891b2",
272
- "#0e7490",
273
- "#155e75",
274
- "#164e63"
275
- ]
276
- }
277
- };
21
+ //#region ../../src/core/atoms/alephaThemeAtom.ts
22
+ const alephaThemeAtom = $atom({
23
+ name: "alepha.ui.theme",
24
+ schema: t.object({ index: t.integer() }),
25
+ default: { index: 0 }
26
+ });
278
27
 
279
28
  //#endregion
280
- //#region ../../src/core/themes/default.ts
29
+ //#region ../../src/core/atoms/themes/default.ts
281
30
  const defaultTheme = {
282
- id: "default",
283
- label: "Default",
284
- description: "Mantine defaults with no customization"
285
- };
286
-
287
- //#endregion
288
- //#region ../../src/core/themes/ember.ts
289
- const emberTheme = {
290
- id: "ember",
291
- label: "Ember",
292
- description: "Warm, cozy with stone tints",
293
- primaryColor: "orange",
294
- primaryShade: {
295
- light: 5,
296
- dark: 4
297
- },
298
- fontFamily: "\"Source Sans Pro\", \"Open Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif",
299
- fontFamilyMonospace: "\"Source Code Pro\", \"Fira Code\", ui-monospace, Menlo, Consolas, monospace",
300
- headings: {
301
- fontFamily: "\"Source Sans Pro\", \"Open Sans\", -apple-system, BlinkMacSystemFont, sans-serif",
302
- fontWeight: "600",
303
- textWrap: "wrap",
304
- sizes: {
305
- h1: {
306
- fontSize: "2rem",
307
- lineHeight: "1.3"
308
- },
309
- h2: {
310
- fontSize: "1.5rem",
311
- lineHeight: "1.35"
312
- },
313
- h3: {
314
- fontSize: "1.25rem",
315
- lineHeight: "1.4"
316
- },
317
- h4: {
318
- fontSize: "1.125rem",
319
- lineHeight: "1.45"
320
- },
321
- h5: {
322
- fontSize: "1rem",
323
- lineHeight: "1.5"
324
- },
325
- h6: {
326
- fontSize: "0.875rem",
327
- lineHeight: "1.5"
328
- }
329
- }
330
- },
331
- fontSizes: {
332
- xs: "0.75rem",
333
- sm: "0.875rem",
334
- md: "1rem",
335
- lg: "1.125rem",
336
- xl: "1.25rem"
337
- },
338
- lineHeights: {
339
- xs: "1.5",
340
- sm: "1.55",
341
- md: "1.65",
342
- lg: "1.7",
343
- xl: "1.75"
344
- },
345
- radius: {
346
- xs: "4px",
347
- sm: "6px",
348
- md: "8px",
349
- lg: "12px",
350
- xl: "16px"
351
- },
352
- defaultRadius: "md",
353
- shadows: {
354
- xs: "0 1px 3px rgba(194, 65, 12, 0.06)",
355
- sm: "0 2px 6px rgba(194, 65, 12, 0.08)",
356
- md: "0 4px 12px rgba(194, 65, 12, 0.1)",
357
- lg: "0 8px 24px rgba(194, 65, 12, 0.12)",
358
- xl: "0 16px 40px rgba(194, 65, 12, 0.14)"
359
- },
360
- defaultGradient: {
361
- from: "orange",
362
- to: "yellow",
363
- deg: 180
364
- },
365
- colors: {
366
- dark: [
367
- "#d6d3d1",
368
- "#a8a29e",
369
- "#78716c",
370
- "#57534e",
371
- "#44403c",
372
- "#292524",
373
- "#1c1917",
374
- "#171412",
375
- "#120f0d",
376
- "#0c0a09"
377
- ],
378
- gray: [
379
- "#fafaf9",
380
- "#f5f5f4",
381
- "#e7e5e4",
382
- "#d6d3d1",
383
- "#a8a29e",
384
- "#78716c",
385
- "#57534e",
386
- "#44403c",
387
- "#292524",
388
- "#1c1917"
389
- ],
390
- orange: [
391
- "#fff7ed",
392
- "#ffedd5",
393
- "#fed7aa",
394
- "#fdba74",
395
- "#fb923c",
396
- "#f97316",
397
- "#ea580c",
398
- "#c2410c",
399
- "#9a3412",
400
- "#7c2d12"
401
- ],
402
- yellow: [
403
- "#fffbeb",
404
- "#fef3c7",
405
- "#fde68a",
406
- "#fcd34d",
407
- "#fbbf24",
408
- "#f59e0b",
409
- "#d97706",
410
- "#b45309",
411
- "#92400e",
412
- "#78350f"
413
- ]
414
- }
31
+ name: "Default",
32
+ description: "Default Alepha Theme"
415
33
  };
416
34
 
417
35
  //#endregion
418
- //#region ../../src/core/themes/midnight.ts
36
+ //#region ../../src/core/atoms/themes/midnight.ts
419
37
  const midnightTheme = {
420
- id: "midnight",
421
- label: "Midnight",
38
+ name: "Midnight",
422
39
  description: "Clean, developer-focused design",
423
- primaryColor: "gray",
40
+ primaryColor: "pink",
424
41
  primaryShade: {
425
42
  light: 7,
426
43
  dark: 8
@@ -531,432 +148,69 @@ const midnightTheme = {
531
148
  };
532
149
 
533
150
  //#endregion
534
- //#region ../../src/core/themes/remoraid.ts
535
- const remoraidTheme = {
536
- id: "remoraid",
537
- label: "Remoraid",
538
- description: "Soft, nature-inspired with green tones",
539
- colors: {
540
- dark: [
541
- "#fafcff",
542
- "#cad5e8",
543
- "#8697b5",
544
- "#4c5d7d",
545
- "#222833",
546
- "#222938",
547
- "#0b0f14",
548
- "#0b0f14",
549
- "#030405",
550
- "#000000"
551
- ],
552
- gray: [
553
- "#e3e7f1",
554
- "#d8ddeb",
555
- "#ced4e5",
556
- "#c3cadf",
557
- "#b8c1d9",
558
- "#b8c1d9",
559
- "#7b8cb8",
560
- "#4b5c8b",
561
- "#2a334d",
562
- "#090b10"
563
- ],
564
- blue: [
565
- "#ddf4ff",
566
- "#b6e3ff",
567
- "#80ccff",
568
- "#54aeff",
569
- "#218bff",
570
- "#0969da",
571
- "#0550ae",
572
- "#033d8b",
573
- "#0a3069",
574
- "#002155"
575
- ],
576
- green: [
577
- "#dafbe1",
578
- "#aceebb",
579
- "#6fdd8b",
580
- "#4ac26b",
581
- "#2da44e",
582
- "#1a7f37",
583
- "#116329",
584
- "#044f1e",
585
- "#003d16",
586
- "#002d11"
587
- ],
588
- yellow: [
589
- "#fff8c5",
590
- "#fae17d",
591
- "#eac54f",
592
- "#d4a72c",
593
- "#bf8700",
594
- "#9a6700",
595
- "#7d4e00",
596
- "#633c01",
597
- "#4d2d00",
598
- "#3b2300"
599
- ],
600
- orange: [
601
- "#fff1e5",
602
- "#ffd8b5",
603
- "#ffb77c",
604
- "#fb8f44",
605
- "#e16f24",
606
- "#bc4c00",
607
- "#953800",
608
- "#762c00",
609
- "#5c2200",
610
- "#471700"
611
- ],
612
- red: [
613
- "#fff5f5",
614
- "#ffe3e3",
615
- "#ffc9c9",
616
- "#ffa8a8",
617
- "#ff8787",
618
- "#ff6b6b",
619
- "#fa5252",
620
- "#f03e3e",
621
- "#e03131",
622
- "#c92a2a"
623
- ],
624
- pink: [
625
- "#fff0f6",
626
- "#ffdeeb",
627
- "#fcc2d7",
628
- "#faa2c1",
629
- "#f783ac",
630
- "#f06595",
631
- "#e64980",
632
- "#d6336c",
633
- "#c2255c",
634
- "#a61e4d"
635
- ],
636
- grape: [
637
- "#f8f0fc",
638
- "#f3d9fa",
639
- "#eebefa",
640
- "#e599f7",
641
- "#da77f2",
642
- "#cc5de8",
643
- "#be4bdb",
644
- "#ae3ec9",
645
- "#9c36b5",
646
- "#862e9c"
647
- ],
648
- violet: [
649
- "#f3f0ff",
650
- "#e5dbff",
651
- "#d0bfff",
652
- "#b197fc",
653
- "#9775fa",
654
- "#845ef7",
655
- "#7950f2",
656
- "#7048e8",
657
- "#6741d9",
658
- "#5f3dc4"
659
- ],
660
- indigo: [
661
- "#edf2ff",
662
- "#dbe4ff",
663
- "#bac8ff",
664
- "#91a7ff",
665
- "#748ffc",
666
- "#5c7cfa",
667
- "#4c6ef5",
668
- "#4263eb",
669
- "#3b5bdb",
670
- "#364fc7"
671
- ],
672
- cyan: [
673
- "#e3fafc",
674
- "#c5f6fa",
675
- "#99e9f2",
676
- "#66d9e8",
677
- "#3bc9db",
678
- "#22b8cf",
679
- "#15aabf",
680
- "#1098ad",
681
- "#0c8599",
682
- "#0b7285"
683
- ],
684
- teal: [
685
- "#e6fcf5",
686
- "#c3fae8",
687
- "#96f2d7",
688
- "#63e6be",
689
- "#38d9a9",
690
- "#20c997",
691
- "#12b886",
692
- "#0ca678",
693
- "#099268",
694
- "#087f5b"
695
- ],
696
- lime: [
697
- "#f4fce3",
698
- "#e9fac8",
699
- "#d8f5a2",
700
- "#c0eb75",
701
- "#a9e34b",
702
- "#94d82d",
703
- "#82c91e",
704
- "#74b816",
705
- "#66a80f",
706
- "#5c940d"
707
- ],
708
- Remoraid: [
709
- "#dcf2de",
710
- "#c7eccc",
711
- "#b2e6b9",
712
- "#9de1a6",
713
- "#88db93",
714
- "#88db93",
715
- "#5fc26d",
716
- "#479454",
717
- "#296133",
718
- "#19361d"
719
- ]
720
- },
721
- primaryColor: "Remoraid",
722
- primaryShade: {
723
- light: 6,
724
- dark: 7
725
- },
726
- white: "#ffffff",
727
- black: "#24292f",
728
- autoContrast: true,
729
- luminanceThreshold: .3,
730
- fontFamily: "Open Sans",
731
- fontFamilyMonospace: "Roboto Mono",
732
- headings: {
733
- fontFamily: "Open Sans",
734
- fontWeight: "500",
735
- sizes: {
736
- h1: {
737
- fontSize: "3.125rem",
738
- lineHeight: "1.3",
739
- fontWeight: "700"
740
- },
741
- h2: {
742
- fontSize: "1.625rem",
743
- lineHeight: "1.35",
744
- fontWeight: "0"
745
- },
746
- h3: {
747
- fontSize: "1.375rem",
748
- lineHeight: "1.4",
749
- fontWeight: "0"
750
- },
751
- h4: {
752
- fontSize: "1.125rem",
753
- lineHeight: "1.45",
754
- fontWeight: "0"
755
- },
756
- h5: {
757
- fontSize: "1rem",
758
- lineHeight: "1.5",
759
- fontWeight: "0"
760
- },
761
- h6: {
762
- fontSize: "0.875rem",
763
- lineHeight: "1.5",
764
- fontWeight: "0"
765
- }
766
- }
767
- },
768
- scale: 1,
769
- radius: {
770
- xs: "0.325rem",
771
- sm: "0.75rem",
772
- md: "0.7rem",
773
- lg: "1.2rem",
774
- xl: "2.4rem"
775
- },
776
- spacing: {
777
- xs: "0.525rem",
778
- sm: "0.65rem",
779
- md: "0.9rem",
780
- lg: "1.35rem",
781
- xl: "2.2rem"
782
- },
783
- defaultRadius: "md",
784
- breakpoints: {
785
- xs: "36em",
786
- sm: "48em",
787
- md: "62em",
788
- lg: "75em",
789
- xl: "88em"
790
- },
791
- fontSmoothing: true,
792
- respectReducedMotion: false,
793
- focusRing: "auto",
794
- cursorType: "default",
795
- components: {
796
- Input: {
797
- defaultProps: {
798
- variant: "default",
799
- radius: "xl"
800
- },
801
- styles: {}
802
- },
803
- Card: {
804
- defaultProps: { withBorder: true },
805
- styles: {}
806
- }
807
- }
808
- };
809
-
810
- //#endregion
811
- //#region ../../src/core/themes/slate.ts
812
- const slateTheme = {
813
- id: "slate",
814
- label: "Slate",
815
- description: "Professional, minimal zinc palette",
816
- primaryColor: "dark",
817
- primaryShade: {
818
- light: 9,
819
- dark: 7
820
- },
821
- fontFamily: "\"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif",
822
- fontFamilyMonospace: "\"JetBrains Mono\", \"Fira Code\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
823
- headings: {
824
- fontFamily: "\"Inter\", -apple-system, BlinkMacSystemFont, sans-serif",
825
- fontWeight: "600",
826
- textWrap: "wrap",
827
- sizes: {
828
- h1: {
829
- fontSize: "2rem",
830
- lineHeight: "1.2"
831
- },
832
- h2: {
833
- fontSize: "1.5rem",
834
- lineHeight: "1.25"
835
- },
836
- h3: {
837
- fontSize: "1.25rem",
838
- lineHeight: "1.3"
839
- },
840
- h4: {
841
- fontSize: "1rem",
842
- lineHeight: "1.4"
843
- },
844
- h5: {
845
- fontSize: "0.875rem",
846
- lineHeight: "1.5"
847
- },
848
- h6: {
849
- fontSize: "0.75rem",
850
- lineHeight: "1.5"
851
- }
852
- }
853
- },
854
- fontSizes: {
855
- xs: "0.75rem",
856
- sm: "0.875rem",
857
- md: "0.875rem",
858
- lg: "1rem",
859
- xl: "1.125rem"
860
- },
861
- lineHeights: {
862
- xs: "1.4",
863
- sm: "1.45",
864
- md: "1.5",
865
- lg: "1.55",
866
- xl: "1.6"
867
- },
868
- radius: {
869
- xs: "0.125rem",
870
- sm: "0.25rem",
871
- md: "0.375rem",
872
- lg: "0.5rem",
873
- xl: "0.75rem"
874
- },
875
- defaultRadius: "md",
876
- shadows: {
877
- xs: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
878
- sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)",
879
- md: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
880
- lg: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)",
881
- xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
882
- },
883
- colors: {
884
- dark: [
885
- "#f4f4f5",
886
- "#a1a1aa",
887
- "#71717a",
888
- "#52525b",
889
- "#3f3f46",
890
- "#27272a",
891
- "#18181b",
892
- "#0f0f10",
893
- "#09090b",
894
- "#000000"
895
- ],
896
- gray: [
897
- "#fafafa",
898
- "#f4f4f5",
899
- "#e4e4e7",
900
- "#d4d4d8",
901
- "#a1a1aa",
902
- "#71717a",
903
- "#52525b",
904
- "#3f3f46",
905
- "#27272a",
906
- "#18181b"
907
- ]
908
- }
909
- };
151
+ //#region ../../src/core/atoms/alephaThemeListAtom.ts
152
+ const alephaThemeListAtom = $atom({
153
+ name: "alepha.ui.themeList",
154
+ schema: t.array(t.json()),
155
+ default: [defaultTheme, midnightTheme]
156
+ });
910
157
 
911
158
  //#endregion
912
159
  //#region ../../src/core/providers/ThemeProvider.ts
913
- const mantineThemeAtom = $atom({
914
- name: "alepha.ui.theme",
915
- schema: t.object({ id: t.string() }),
916
- default: { id: "default" }
917
- });
918
160
  var ThemeProvider = class {
919
161
  alepha = $inject(Alepha);
920
- themeCookie = $cookie(mantineThemeAtom);
921
- themes = [
922
- defaultTheme,
923
- remoraidTheme,
924
- midnightTheme,
925
- slateTheme,
926
- auroraTheme,
927
- emberTheme,
928
- crystalTheme
929
- ];
930
- themeHead = $head(() => {
931
- return { htmlAttributes: { "data-theme": this.getTheme().id } };
162
+ cookie = $cookie({
163
+ name: "theme",
164
+ schema: alephaThemeAtom.schema,
165
+ ttl: [1, "year"]
166
+ });
167
+ head = $head(() => {
168
+ const theme = this.getTheme();
169
+ if (!theme || !theme.name) return {};
170
+ return { htmlAttributes: { "data-theme": theme.name } };
932
171
  });
933
- setTheme(theme) {
934
- this.themeCookie.set(theme);
935
- this.alepha.store.set(mantineThemeAtom, theme);
172
+ setTheme(index) {
173
+ const newTheme = this.alepha.store.get(alephaThemeListAtom)[index];
174
+ if (!newTheme) throw new AlephaError(`Theme with index ${index} not found`);
175
+ this.cookie.set({ index });
176
+ this.alepha.store.set(alephaThemeAtom, { index });
936
177
  if (typeof document === "undefined") return;
937
178
  document.documentElement.removeAttribute("data-theme");
938
- if (theme.id !== "default") document.documentElement.setAttribute("data-theme", theme.id);
179
+ if (newTheme.name) document.documentElement.setAttribute("data-theme", newTheme.name);
939
180
  }
940
181
  getTheme() {
182
+ const index = this.getThemeIndex();
183
+ const list = this.alepha.store.get(alephaThemeListAtom);
184
+ return list[index] || list[0] || defaultTheme;
185
+ }
186
+ getThemeIndex() {
941
187
  try {
942
- return this.themeCookie.get() ?? this.alepha.store.get(mantineThemeAtom) ?? mantineThemeAtom.options.default;
188
+ return this.cookie.get()?.index ?? this.alepha.store.get(alephaThemeAtom)?.index ?? 0;
943
189
  } catch {
944
- return this.alepha.store.get(mantineThemeAtom) ?? mantineThemeAtom.options.default;
190
+ return this.alepha.store.get(alephaThemeAtom)?.index ?? 0;
945
191
  }
946
192
  }
947
193
  };
948
194
 
949
195
  //#endregion
950
196
  //#region ../../src/core/hooks/useTheme.ts
951
- const useTheme = () => {
952
- useStore(mantineThemeAtom);
953
- const themeService = useInject(ThemeProvider);
954
- const currentTheme = themeService.getTheme();
955
- const fullTheme = themeService.themes.find((t$1) => t$1.id === currentTheme.id) ?? themeService.themes[0];
956
- const applyTheme = (theme) => {
957
- themeService.setTheme({ id: theme.id });
197
+ /**
198
+ * Hook to get and set the current theme.
199
+ *
200
+ * Returns a tuple with the current theme and a function to set the theme.
201
+ *
202
+ * ```tsx
203
+ * const [theme, setTheme] = useTheme();
204
+ * ```
205
+ */
206
+ const useTheme = () => {
207
+ useStore(alephaThemeAtom);
208
+ const themeProvider = useInject(ThemeProvider);
209
+ const theme = themeProvider.getTheme();
210
+ const setTheme = (theme$1) => {
211
+ themeProvider.setTheme(theme$1.index);
958
212
  };
959
- return [fullTheme, applyTheme];
213
+ return [theme, setTheme];
960
214
  };
961
215
 
962
216
  //#endregion
@@ -1054,282 +308,18 @@ const ui = {
1054
308
  };
1055
309
 
1056
310
  //#endregion
1057
- //#region ../../src/core/components/buttons/ActionButton.tsx
1058
- const ActionMenuItem = (props) => {
1059
- const { item, index } = props;
1060
- const router = useRouter();
1061
- const action = useAction({ handler: async (e) => {
1062
- await item.onClick?.();
1063
- } }, [item.onClick]);
1064
- if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
1065
- if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
1066
- if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
1067
- trigger: "hover",
1068
- position: "right-start",
1069
- offset: 2,
1070
- children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
1071
- leftSection: item.icon,
1072
- rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
1073
- children: item.label
1074
- }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx(ActionMenuItem, {
1075
- item: child,
1076
- index: childIndex
1077
- }, childIndex)) })]
1078
- }, index);
1079
- const menuItemProps = {};
1080
- if (props.item.onClick) menuItemProps.onClick = action.run;
1081
- else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
1082
- return /* @__PURE__ */ jsx(Menu.Item, {
1083
- leftSection: item.icon,
1084
- onClick: item.onClick,
1085
- color: item.color,
1086
- rightSection: item.active ? /* @__PURE__ */ jsx(ThemeIcon, {
1087
- size: "xs",
1088
- variant: "transparent",
1089
- children: /* @__PURE__ */ jsx(IconCheck, {})
1090
- }) : void 0,
1091
- ...menuItemProps,
1092
- children: item.label
1093
- }, index);
1094
- };
1095
- const ActionButton = (_props) => {
1096
- const props = {
1097
- variant: "subtle",
1098
- ..._props
1099
- };
1100
- const { tooltip, menu, icon, ...restProps } = props;
1101
- restProps.color ??= "gray";
1102
- restProps.c ??= "var(--mantine-color-text)";
1103
- if (props.icon) {
1104
- const icon$1 = isComponentType(props.icon) ? /* @__PURE__ */ jsx(props.icon, { size: ui.sizes.icon.md }) : /* @__PURE__ */ jsx(ThemeIcon, {
1105
- w: 24,
1106
- variant: "transparent",
1107
- size: "sm",
1108
- c: "var(--mantine-color-text)",
1109
- ...props.themeIconProps,
1110
- children: props.icon
1111
- });
1112
- if (!props.children) {
1113
- restProps.children = Children.only(icon$1);
1114
- restProps.px ??= "xs";
1115
- } else restProps.leftSection = icon$1;
1116
- }
1117
- if (props.leftSection && !props.children) restProps.px ??= "xs";
1118
- if (props.textVisibleFrom) {
1119
- const { children, textVisibleFrom, leftSection, ...rest } = restProps;
1120
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex$1, {
1121
- w: "100%",
1122
- visibleFrom: textVisibleFrom,
1123
- children: /* @__PURE__ */ jsx(ActionButton, {
1124
- flex: 1,
1125
- ...rest,
1126
- leftSection,
1127
- tooltip,
1128
- menu,
1129
- children
1130
- })
1131
- }), /* @__PURE__ */ jsx(Flex$1, {
1132
- w: "100%",
1133
- hiddenFrom: textVisibleFrom,
1134
- children: /* @__PURE__ */ jsx(ActionButton, {
1135
- px: "xs",
1136
- ...rest,
1137
- tooltip,
1138
- menu,
1139
- children: leftSection
1140
- })
1141
- })] });
1142
- }
1143
- const renderAction = () => {
1144
- if ("href" in restProps && restProps.href) {
1145
- if (restProps.href.startsWith("http") || restProps.target) return /* @__PURE__ */ jsx(ActionHrefButton, {
1146
- ...restProps,
1147
- href: restProps.href,
1148
- children: restProps.children
1149
- });
1150
- return /* @__PURE__ */ jsx(ActionNavigationButton, {
1151
- ...restProps,
1152
- href: restProps.href,
1153
- children: restProps.children
1154
- });
1155
- }
1156
- delete restProps.classNameActive;
1157
- delete restProps.variantActive;
1158
- if ("action" in restProps && restProps.action) return /* @__PURE__ */ jsx(ActionHookButton, {
1159
- ...restProps,
1160
- action: restProps.action,
1161
- children: restProps.children
1162
- });
1163
- if ("onClick" in restProps && restProps.onClick) return /* @__PURE__ */ jsx(ActionClickButton, {
1164
- ...restProps,
1165
- onClick: restProps.onClick,
1166
- children: restProps.children
1167
- });
1168
- if ("form" in restProps && restProps.form) {
1169
- if (restProps.type === "reset") return /* @__PURE__ */ jsx(ActionResetButton, {
1170
- ...restProps,
1171
- form: restProps.form,
1172
- children: restProps.children
1173
- });
1174
- return /* @__PURE__ */ jsx(ActionSubmitButton, {
1175
- ...restProps,
1176
- form: restProps.form,
1177
- children: restProps.children
1178
- });
1179
- }
1180
- return /* @__PURE__ */ jsx(Button, {
1181
- ...restProps,
1182
- children: restProps.children
1183
- });
1184
- };
1185
- let actionElement = renderAction();
1186
- if (menu) actionElement = /* @__PURE__ */ jsxs(Menu, {
1187
- position: menu.position || "bottom-start",
1188
- width: menu.width || 200,
1189
- shadow: menu.shadow || "md",
1190
- trigger: menu.on === "hover" ? "hover" : "click",
1191
- ...menu.menuProps,
1192
- children: [/* @__PURE__ */ jsx(Menu.Target, {
1193
- ...menu.targetProps,
1194
- children: actionElement
1195
- }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: menu.items.map((item, index) => /* @__PURE__ */ jsx(ActionMenuItem, {
1196
- item,
1197
- index
1198
- }, index)) })]
1199
- });
1200
- if (tooltip) {
1201
- const defaultTooltipProps = { openDelay: 1e3 };
1202
- return /* @__PURE__ */ jsx(Tooltip, { ...typeof tooltip === "string" ? {
1203
- ...defaultTooltipProps,
1204
- label: tooltip,
1205
- children: actionElement
1206
- } : {
1207
- ...defaultTooltipProps,
1208
- ...tooltip,
1209
- children: actionElement
1210
- } });
1211
- }
1212
- return actionElement;
1213
- };
1214
- var ActionButton_default = ActionButton;
1215
- /**
1216
- * Action button that submits a form with loading and disabled state handling.
1217
- */
1218
- const ActionSubmitButton = (props) => {
1219
- const { form, ...buttonProps } = props;
1220
- const state = useFormState(form);
1221
- return /* @__PURE__ */ jsx(Button, {
1222
- ...buttonProps,
1223
- loading: state.loading,
1224
- disabled: state.loading,
1225
- type: "submit",
1226
- children: props.children
1227
- });
1228
- };
1229
- const ActionResetButton = (props) => {
1230
- const { form, ...buttonProps } = props;
1231
- const state = useFormState(form);
1232
- return /* @__PURE__ */ jsx(Button, {
1233
- ...buttonProps,
1234
- disabled: state.loading,
1235
- type: "reset",
1236
- children: props.children
1237
- });
1238
- };
1239
- /**
1240
- * Action button that integrates with useAction hook return value.
1241
- * Automatically handles loading state and executes the action on click.
1242
- *
1243
- * @example
1244
- * ```tsx
1245
- * const saveAction = useAction({
1246
- * handler: async (data) => {
1247
- * await api.save(data);
1248
- * }
1249
- * }, []);
1250
- *
1251
- * <ActionButton action={saveAction}>
1252
- * Save
1253
- * </ActionButton>
1254
- * ```
1255
- */
1256
- const ActionHookButton = (props) => {
1257
- const { action, ...buttonProps } = props;
1258
- return /* @__PURE__ */ jsx(Button, {
1259
- ...buttonProps,
1260
- disabled: action.loading || props.disabled,
1261
- loading: action.loading,
1262
- onClick: () => action.run(),
1263
- children: props.children
1264
- });
1265
- };
1266
- /**
1267
- * Basic action button that handles click events with loading and error handling.
1268
- *
1269
- * @example
1270
- * ```tsx
1271
- * <ActionButton onClick={() => api.doSomething()}>
1272
- * Do Something
1273
- * </ActionButton>
1274
- * ```
1275
- */
1276
- const ActionClickButton = (props) => {
1277
- const action = useAction({ handler: async (e) => {
1278
- await props.onClick(e);
1279
- } }, [props.onClick]);
1280
- return /* @__PURE__ */ jsx(Button, {
1281
- ...props,
1282
- disabled: action.loading || props.disabled,
1283
- loading: action.loading,
1284
- onClick: action.run,
1285
- children: props.children
1286
- });
1287
- };
1288
- /**
1289
- * Action for navigation with active state support.
1290
- */
1291
- const ActionNavigationButton = (props) => {
1292
- const { active: options, classNameActive, variantActive, routerGoOptions, ...buttonProps } = props;
1293
- const router = useRouter();
1294
- const { isPending, isActive } = useActive(options ? {
1295
- href: props.href,
1296
- ...options
1297
- } : { href: props.href });
1298
- const anchorProps = router.anchor(props.href, routerGoOptions);
1299
- const className = buttonProps.className || "";
1300
- if (isActive && options !== false && classNameActive) buttonProps.className = `${className} ${classNameActive}`.trim();
1301
- if (props.anchorProps) return /* @__PURE__ */ jsx(Anchor, {
1302
- component: "a",
1303
- ...anchorProps,
1304
- ...props.anchorProps,
1305
- children: props.children
1306
- });
1307
- return /* @__PURE__ */ jsx(Button, {
1308
- component: "a",
1309
- loading: isPending,
1310
- ...buttonProps,
1311
- ...anchorProps,
1312
- variant: isActive && options !== false ? variantActive ?? "filled" : buttonProps.variant ?? "subtle",
1313
- children: props.children
1314
- });
1315
- };
1316
- const ActionHrefButton = (props) => {
1317
- const { active: options, classNameActive, variantActive, routerGoOptions, target, ...buttonProps } = props;
1318
- return /* @__PURE__ */ jsx(Button, {
1319
- component: "a",
1320
- target,
1321
- ...buttonProps,
1322
- children: props.children
1323
- });
1324
- };
311
+ //#region ../../src/core/helpers/isComponentType.ts
1325
312
  function isComponentType(param) {
1326
313
  if (isValidElement(param)) return false;
1327
314
  return typeof param === "function" || typeof param === "object" && param !== null && "$$typeof" in param;
1328
315
  }
316
+
317
+ //#endregion
318
+ //#region ../../src/core/helpers/renderIcon.tsx
1329
319
  const renderIcon = (icon) => {
1330
320
  if (!icon) return null;
1331
321
  if (isValidElement(icon)) return icon;
1332
- if (isComponentType(icon)) return /* @__PURE__ */ jsx(icon, { size: ui.sizes.icon.md });
322
+ if (isComponentType(icon)) return /* @__PURE__ */ jsx(icon, { size: ui$1.sizes.icon.md });
1333
323
  return icon;
1334
324
  };
1335
325
 
@@ -1386,11 +376,13 @@ const AlephaMantineProvider = (props) => {
1386
376
  });
1387
377
  }
1388
378
  }, []);
379
+ const defaultColorScheme = props.mantine?.defaultColorScheme ?? theme.defaultColorScheme;
1389
380
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ColorSchemeScript, {
1390
- defaultColorScheme: props.mantine?.defaultColorScheme,
381
+ defaultColorScheme,
1391
382
  ...props.colorSchemeScript
1392
383
  }), /* @__PURE__ */ jsxs(MantineProvider, {
1393
384
  ...props.mantine,
385
+ defaultColorScheme,
1394
386
  theme: {
1395
387
  ...theme,
1396
388
  ...props.mantine?.theme
@@ -1400,7 +392,7 @@ const AlephaMantineProvider = (props) => {
1400
392
  /* @__PURE__ */ jsx(NavigationProgress, { ...props.navigationProgress }),
1401
393
  /* @__PURE__ */ jsxs(ModalsProvider, {
1402
394
  ...props.modals,
1403
- children: [/* @__PURE__ */ jsx(Omnibar_default, { ...props.omnibar }), props.children ?? /* @__PURE__ */ jsx(NestedView, {})]
395
+ children: [props.omnibar !== false && /* @__PURE__ */ jsx(Omnibar_default, { ...props.omnibar }), props.children ?? /* @__PURE__ */ jsx(NestedView, {})]
1404
396
  })
1405
397
  ]
1406
398
  })] });
@@ -1416,6 +408,146 @@ var RootRouter = class {
1416
408
  });
1417
409
  };
1418
410
 
411
+ //#endregion
412
+ //#region ../../src/core/components/data/ErrorViewer.tsx
413
+ const getSizeConfig$1 = (size = "sm") => {
414
+ const configs = {
415
+ xs: {
416
+ text: "xs",
417
+ icon: 12,
418
+ gap: 2
419
+ },
420
+ sm: {
421
+ text: "sm",
422
+ icon: 14,
423
+ gap: 4
424
+ },
425
+ md: {
426
+ text: "md",
427
+ icon: 16,
428
+ gap: 6
429
+ },
430
+ lg: {
431
+ text: "lg",
432
+ icon: 18,
433
+ gap: 8
434
+ },
435
+ xl: {
436
+ text: "xl",
437
+ icon: 20,
438
+ gap: 10
439
+ }
440
+ };
441
+ return configs[size] || configs.sm;
442
+ };
443
+ const parseStackTrace = (stack) => {
444
+ return stack.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
445
+ };
446
+ const ErrorViewer = ({ error, showStack = true, copyable = true, size = "sm" }) => {
447
+ const [stackExpanded, setStackExpanded] = useState(false);
448
+ const sizeConfig = getSizeConfig$1(size);
449
+ const copyIconSize = sizeConfig.icon + 2;
450
+ const isError = error instanceof Error;
451
+ const errorName = isError ? error.name : "Error";
452
+ const errorMessage = isError ? error.message : String(error);
453
+ const errorStack = isError ? error.stack : void 0;
454
+ const stackLines = errorStack ? parseStackTrace(errorStack) : [];
455
+ const getCopyContent = () => {
456
+ if (isError) return `${errorName}: ${errorMessage}${errorStack ? `\n\n${errorStack}` : ""}`;
457
+ return String(error);
458
+ };
459
+ return /* @__PURE__ */ jsxs(Box, {
460
+ pos: "relative",
461
+ w: "100%",
462
+ children: [copyable && /* @__PURE__ */ jsx(Box, {
463
+ pos: "absolute",
464
+ top: 0,
465
+ right: 0,
466
+ style: { zIndex: 1 },
467
+ children: /* @__PURE__ */ jsx(CopyButton, {
468
+ value: getCopyContent(),
469
+ children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, {
470
+ label: copied ? "Copied" : "Copy Error",
471
+ children: /* @__PURE__ */ jsx(ActionIcon, {
472
+ color: copied ? "teal" : "gray",
473
+ variant: "subtle",
474
+ onClick: copy,
475
+ size,
476
+ children: copied ? /* @__PURE__ */ jsx(IconCheck, { size: copyIconSize }) : /* @__PURE__ */ jsx(IconCopy, { size: copyIconSize })
477
+ })
478
+ })
479
+ })
480
+ }), /* @__PURE__ */ jsxs(Box, {
481
+ pt: copyable ? 30 : 0,
482
+ children: [/* @__PURE__ */ jsxs(Box, {
483
+ style: {
484
+ display: "flex",
485
+ alignItems: "flex-start",
486
+ gap: sizeConfig.gap
487
+ },
488
+ children: [/* @__PURE__ */ jsxs(Text$1, {
489
+ component: "span",
490
+ c: "red",
491
+ ff: "monospace",
492
+ fw: 600,
493
+ size: sizeConfig.text,
494
+ children: [errorName, ":"]
495
+ }), /* @__PURE__ */ jsx(Text$1, {
496
+ component: "span",
497
+ ff: "monospace",
498
+ size: sizeConfig.text,
499
+ style: { wordBreak: "break-word" },
500
+ children: errorMessage
501
+ })]
502
+ }), showStack && stackLines.length > 1 && /* @__PURE__ */ jsxs(Box, {
503
+ mt: "sm",
504
+ children: [/* @__PURE__ */ jsxs(Box, {
505
+ style: {
506
+ display: "flex",
507
+ alignItems: "center",
508
+ gap: sizeConfig.gap,
509
+ cursor: "pointer"
510
+ },
511
+ onClick: () => setStackExpanded(!stackExpanded),
512
+ children: [/* @__PURE__ */ jsx(ActionIcon, {
513
+ size: "xs",
514
+ variant: "transparent",
515
+ c: "dimmed",
516
+ children: stackExpanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: sizeConfig.icon }) : /* @__PURE__ */ jsx(IconChevronRight, { size: sizeConfig.icon })
517
+ }), /* @__PURE__ */ jsxs(Text$1, {
518
+ c: "dimmed",
519
+ size: sizeConfig.text,
520
+ fw: 500,
521
+ children: [
522
+ "Stack Trace (",
523
+ stackLines.length - 1,
524
+ " frames)"
525
+ ]
526
+ })]
527
+ }), /* @__PURE__ */ jsx(Collapse, {
528
+ in: stackExpanded,
529
+ children: /* @__PURE__ */ jsx(Box, {
530
+ mt: "xs",
531
+ pl: "md",
532
+ style: { borderLeft: "1px solid var(--mantine-color-default-border)" },
533
+ children: stackLines.slice(1).map((line, index) => /* @__PURE__ */ jsx(Text$1, {
534
+ ff: "monospace",
535
+ size: "xs",
536
+ c: "dimmed",
537
+ style: {
538
+ whiteSpace: "pre-wrap",
539
+ wordBreak: "break-all"
540
+ },
541
+ children: line
542
+ }, index))
543
+ })
544
+ })]
545
+ })]
546
+ })]
547
+ });
548
+ };
549
+ var ErrorViewer_default = ErrorViewer;
550
+
1419
551
  //#endregion
1420
552
  //#region ../../src/core/components/data/JsonViewer.tsx
1421
553
  const getSizeConfig = (size = "sm") => {
@@ -1424,36 +556,41 @@ const getSizeConfig = (size = "sm") => {
1424
556
  text: "xs",
1425
557
  icon: 12,
1426
558
  indent: 16,
1427
- gap: 2
559
+ gap: 4,
560
+ iconWidth: 18
1428
561
  },
1429
562
  sm: {
1430
563
  text: "sm",
1431
564
  icon: 14,
1432
565
  indent: 20,
1433
- gap: 4
566
+ gap: 6,
567
+ iconWidth: 20
1434
568
  },
1435
569
  md: {
1436
570
  text: "md",
1437
571
  icon: 16,
1438
572
  indent: 24,
1439
- gap: 6
573
+ gap: 8,
574
+ iconWidth: 22
1440
575
  },
1441
576
  lg: {
1442
577
  text: "lg",
1443
578
  icon: 18,
1444
579
  indent: 28,
1445
- gap: 8
580
+ gap: 10,
581
+ iconWidth: 24
1446
582
  },
1447
583
  xl: {
1448
584
  text: "xl",
1449
585
  icon: 20,
1450
586
  indent: 32,
1451
- gap: 10
587
+ gap: 12,
588
+ iconWidth: 26
1452
589
  }
1453
590
  };
1454
591
  return configs[size] || configs.sm;
1455
592
  };
1456
- const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem = false, size = "sm" }) => {
593
+ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem = false, size = "sm", iconWidth }) => {
1457
594
  const [expanded, setExpanded] = useState(depth < 2);
1458
595
  const sizeConfig = getSizeConfig(size);
1459
596
  const getValueType = (val) => {
@@ -1464,13 +601,16 @@ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem =
1464
601
  };
1465
602
  const valueType = getValueType(value);
1466
603
  const renderPrimitive = (val) => {
1467
- switch (getValueType(val)) {
604
+ const type = getValueType(val);
605
+ const textProps = {
606
+ component: "span",
607
+ ff: "monospace",
608
+ size: sizeConfig.text
609
+ };
610
+ switch (type) {
1468
611
  case "string": return /* @__PURE__ */ jsxs(Text$1, {
1469
- component: "span",
612
+ ...textProps,
1470
613
  c: "teal",
1471
- ff: "monospace",
1472
- size: sizeConfig.text,
1473
- style: { whiteSpace: "nowrap" },
1474
614
  children: [
1475
615
  "\"",
1476
616
  val,
@@ -1478,57 +618,49 @@ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem =
1478
618
  ]
1479
619
  });
1480
620
  case "number": return /* @__PURE__ */ jsx(Text$1, {
1481
- component: "span",
621
+ ...textProps,
1482
622
  c: "blue",
1483
- ff: "monospace",
1484
- size: sizeConfig.text,
1485
- style: { whiteSpace: "nowrap" },
1486
623
  children: val
1487
624
  });
1488
625
  case "boolean": return /* @__PURE__ */ jsx(Text$1, {
1489
- component: "span",
626
+ ...textProps,
1490
627
  c: "violet",
1491
- ff: "monospace",
1492
- size: sizeConfig.text,
1493
- style: { whiteSpace: "nowrap" },
1494
628
  children: String(val)
1495
629
  });
1496
630
  case "null": return /* @__PURE__ */ jsx(Text$1, {
1497
- component: "span",
631
+ ...textProps,
1498
632
  c: "dimmed",
1499
- ff: "monospace",
1500
- size: sizeConfig.text,
1501
- style: { whiteSpace: "nowrap" },
1502
633
  children: "null"
1503
634
  });
1504
635
  case "undefined": return /* @__PURE__ */ jsx(Text$1, {
1505
- component: "span",
636
+ ...textProps,
1506
637
  c: "dimmed",
1507
- ff: "monospace",
1508
- size: sizeConfig.text,
1509
- style: { whiteSpace: "nowrap" },
1510
638
  children: "undefined"
1511
639
  });
1512
640
  default: return /* @__PURE__ */ jsx(Text$1, {
1513
- component: "span",
1514
- ff: "monospace",
1515
- size: sizeConfig.text,
1516
- style: { whiteSpace: "nowrap" },
641
+ ...textProps,
1517
642
  children: String(val)
1518
643
  });
1519
644
  }
1520
645
  };
1521
646
  const renderKey = () => {
1522
- if (!name) return null;
1523
- return /* @__PURE__ */ jsxs(Text$1, {
647
+ if (name === void 0) return null;
648
+ return /* @__PURE__ */ jsx(Text$1, {
1524
649
  component: "span",
1525
650
  c: "cyan",
1526
651
  ff: "monospace",
1527
652
  fw: 500,
1528
653
  size: sizeConfig.text,
1529
- children: [isArrayItem ? `[${name}]` : `"${name}"`, ":"]
654
+ children: isArrayItem ? `[${name}]` : `"${name}"`
1530
655
  });
1531
656
  };
657
+ const comma = !isLast && /* @__PURE__ */ jsx(Text$1, {
658
+ component: "span",
659
+ c: "dimmed",
660
+ ff: "monospace",
661
+ size: sizeConfig.text,
662
+ children: ","
663
+ });
1532
664
  if (valueType === "object" || valueType === "array") {
1533
665
  const isObject = valueType === "object";
1534
666
  const entries = isObject ? Object.entries(value) : value.map((v, i) => [i, v]);
@@ -1538,77 +670,79 @@ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem =
1538
670
  const brackets = isObject ? ["{", "}"] : ["[", "]"];
1539
671
  return /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Box, {
1540
672
  style: {
1541
- display: "flex",
1542
- alignItems: "center",
1543
- gap: sizeConfig.gap,
1544
- minWidth: "max-content"
673
+ display: "grid",
674
+ gridTemplateColumns: `${iconWidth}px auto`,
675
+ alignItems: "center"
1545
676
  },
1546
- children: [
1547
- canExpand && /* @__PURE__ */ jsx(ActionIcon, {
677
+ children: [/* @__PURE__ */ jsx(Box, {
678
+ style: {
679
+ display: "flex",
680
+ justifyContent: "center",
681
+ alignItems: "center"
682
+ },
683
+ children: canExpand && /* @__PURE__ */ jsx(ActionIcon, {
1548
684
  size: "xs",
1549
685
  variant: "transparent",
1550
686
  c: "dimmed",
1551
687
  onClick: () => setExpanded(!expanded),
1552
- style: {
1553
- cursor: "pointer",
1554
- flexShrink: 0
1555
- },
688
+ style: { cursor: "pointer" },
1556
689
  children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: sizeConfig.icon }) : /* @__PURE__ */ jsx(IconChevronRight, { size: sizeConfig.icon })
1557
- }),
1558
- !canExpand && /* @__PURE__ */ jsx(Box, {
1559
- w: sizeConfig.icon + 6,
1560
- style: { flexShrink: 0 }
1561
- }),
1562
- /* @__PURE__ */ jsx(Box, {
1563
- style: { flexShrink: 0 },
1564
- children: renderKey()
1565
- }),
1566
- " ",
1567
- /* @__PURE__ */ jsx(Text$1, {
1568
- component: "span",
1569
- c: "dimmed",
1570
- ff: "monospace",
1571
- size: sizeConfig.text,
1572
- style: { flexShrink: 0 },
1573
- children: brackets[0]
1574
- }),
1575
- !expanded && !isEmpty && /* @__PURE__ */ jsx(Text$1, {
1576
- component: "span",
1577
- c: "dimmed",
1578
- ff: "monospace",
1579
- fs: "italic",
1580
- size: sizeConfig.text,
1581
- style: { flexShrink: 0 },
1582
- children: preview
1583
- }),
1584
- (isEmpty || !expanded) && /* @__PURE__ */ jsx(Text$1, {
1585
- component: "span",
1586
- c: "dimmed",
1587
- ff: "monospace",
1588
- size: sizeConfig.text,
1589
- style: { flexShrink: 0 },
1590
- children: brackets[1]
1591
- }),
1592
- !isEmpty && !expanded && /* @__PURE__ */ jsxs(Text$1, {
1593
- component: "span",
1594
- c: "dimmed",
1595
- size: sizeConfig.text,
1596
- style: { flexShrink: 0 },
1597
- children: [
1598
- entries.length,
1599
- " ",
1600
- entries.length === 1 ? "item" : "items"
1601
- ]
1602
690
  })
1603
- ]
691
+ }), /* @__PURE__ */ jsxs(Box, {
692
+ style: {
693
+ display: "flex",
694
+ alignItems: "center",
695
+ gap: sizeConfig.gap
696
+ },
697
+ children: [
698
+ renderKey(),
699
+ name !== void 0 && /* @__PURE__ */ jsx(Text$1, {
700
+ component: "span",
701
+ c: "dimmed",
702
+ ff: "monospace",
703
+ size: sizeConfig.text,
704
+ children: ":"
705
+ }),
706
+ /* @__PURE__ */ jsx(Text$1, {
707
+ component: "span",
708
+ c: "dimmed",
709
+ ff: "monospace",
710
+ size: sizeConfig.text,
711
+ children: brackets[0]
712
+ }),
713
+ !expanded && !isEmpty && /* @__PURE__ */ jsx(Text$1, {
714
+ component: "span",
715
+ c: "dimmed",
716
+ ff: "monospace",
717
+ fs: "italic",
718
+ size: sizeConfig.text,
719
+ children: preview
720
+ }),
721
+ (isEmpty || !expanded) && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text$1, {
722
+ component: "span",
723
+ c: "dimmed",
724
+ ff: "monospace",
725
+ size: sizeConfig.text,
726
+ children: brackets[1]
727
+ }), comma] }),
728
+ !isEmpty && !expanded && /* @__PURE__ */ jsxs(Text$1, {
729
+ component: "span",
730
+ c: "dimmed",
731
+ size: sizeConfig.text,
732
+ children: [
733
+ entries.length,
734
+ " ",
735
+ entries.length === 1 ? "item" : "items"
736
+ ]
737
+ })
738
+ ]
739
+ })]
1604
740
  }), /* @__PURE__ */ jsxs(Collapse, {
1605
741
  in: expanded && canExpand,
1606
742
  children: [/* @__PURE__ */ jsx(Box, {
1607
743
  pl: sizeConfig.indent,
1608
- style: {
1609
- borderLeft: "1px solid var(--mantine-color-default-border)",
1610
- marginLeft: Math.floor((sizeConfig.icon + 6) / 2)
1611
- },
744
+ ml: iconWidth / 2,
745
+ style: { borderLeft: "1px solid var(--mantine-color-default-border)" },
1612
746
  children: entries.map(([key, val], index) => /* @__PURE__ */ jsx(JsonNode, {
1613
747
  name: String(key),
1614
748
  value: val,
@@ -1616,59 +750,59 @@ const JsonNode = ({ name, value, depth, maxDepth, isLast = false, isArrayItem =
1616
750
  maxDepth,
1617
751
  isLast: index === entries.length - 1,
1618
752
  isArrayItem: !isObject,
1619
- size
753
+ size,
754
+ iconWidth
1620
755
  }, String(key)))
1621
756
  }), /* @__PURE__ */ jsxs(Box, {
1622
757
  style: {
1623
- display: "flex",
1624
- minWidth: "max-content"
758
+ display: "grid",
759
+ gridTemplateColumns: `${iconWidth}px auto`
1625
760
  },
1626
- children: [/* @__PURE__ */ jsx(Box, {
1627
- w: sizeConfig.icon + 6,
1628
- style: { flexShrink: 0 }
1629
- }), /* @__PURE__ */ jsx(Text$1, {
1630
- c: "dimmed",
1631
- ff: "monospace",
1632
- size: sizeConfig.text,
1633
- style: { flexShrink: 0 },
1634
- children: brackets[1]
761
+ children: [/* @__PURE__ */ jsx(Box, {}), /* @__PURE__ */ jsxs(Box, {
762
+ style: {
763
+ display: "flex",
764
+ gap: sizeConfig.gap
765
+ },
766
+ children: [/* @__PURE__ */ jsx(Text$1, {
767
+ c: "dimmed",
768
+ ff: "monospace",
769
+ size: sizeConfig.text,
770
+ children: brackets[1]
771
+ }), comma]
1635
772
  })]
1636
773
  })]
1637
774
  })] });
1638
775
  }
1639
776
  return /* @__PURE__ */ jsxs(Box, {
1640
777
  style: {
1641
- display: "flex",
1642
- alignItems: "center",
1643
- gap: sizeConfig.gap,
1644
- minWidth: "max-content"
778
+ display: "grid",
779
+ gridTemplateColumns: `${iconWidth}px auto`,
780
+ alignItems: "center"
1645
781
  },
1646
- children: [
1647
- /* @__PURE__ */ jsx(Box, {
1648
- w: sizeConfig.icon + 6,
1649
- style: { flexShrink: 0 }
1650
- }),
1651
- /* @__PURE__ */ jsx(Box, {
1652
- style: { flexShrink: 0 },
1653
- children: renderKey()
1654
- }),
1655
- /* @__PURE__ */ jsx(Box, {
1656
- style: { flexShrink: 0 },
1657
- children: renderPrimitive(value)
1658
- }),
1659
- !isLast && /* @__PURE__ */ jsx(Text$1, {
1660
- component: "span",
1661
- c: "dimmed",
1662
- ff: "monospace",
1663
- size: sizeConfig.text,
1664
- style: { flexShrink: 0 },
1665
- children: ","
1666
- })
1667
- ]
782
+ children: [/* @__PURE__ */ jsx(Box, {}), /* @__PURE__ */ jsxs(Box, {
783
+ style: {
784
+ display: "flex",
785
+ alignItems: "center",
786
+ gap: sizeConfig.gap
787
+ },
788
+ children: [
789
+ renderKey(),
790
+ name !== void 0 && /* @__PURE__ */ jsx(Text$1, {
791
+ component: "span",
792
+ c: "dimmed",
793
+ ff: "monospace",
794
+ size: sizeConfig.text,
795
+ children: ":"
796
+ }),
797
+ renderPrimitive(value),
798
+ comma
799
+ ]
800
+ })]
1668
801
  });
1669
802
  };
1670
803
  const JsonViewer = ({ data, defaultExpanded = true, maxDepth = 10, copyable = true, size = "sm" }) => {
1671
- const copyIconSize = getSizeConfig(size).icon + 2;
804
+ const sizeConfig = getSizeConfig(size);
805
+ const copyIconSize = sizeConfig.icon + 2;
1672
806
  return /* @__PURE__ */ jsxs(Box, {
1673
807
  pos: "relative",
1674
808
  w: "100%",
@@ -1697,7 +831,8 @@ const JsonViewer = ({ data, defaultExpanded = true, maxDepth = 10, copyable = tr
1697
831
  value: data,
1698
832
  depth: 0,
1699
833
  maxDepth,
1700
- size
834
+ size,
835
+ iconWidth: sizeConfig.iconWidth
1701
836
  })
1702
837
  })]
1703
838
  });
@@ -1894,20 +1029,333 @@ var DialogService = class {
1894
1029
  })
1895
1030
  });
1896
1031
  }
1897
- /**
1898
- * Show a form dialog for structured input
1899
- */
1900
- form(options) {
1901
- return Promise.resolve(null);
1902
- }
1903
- /**
1904
- * Show a loading/progress dialog with optional progress percentage
1905
- */
1906
- loading(options) {}
1907
- /**
1908
- * Show an image viewer/gallery dialog
1909
- */
1910
- image(src, options) {}
1032
+ /**
1033
+ * Show an error viewer dialog
1034
+ */
1035
+ error(error, options) {
1036
+ this.open({
1037
+ size: "lg",
1038
+ title: options?.title || "Error",
1039
+ ...options,
1040
+ content: /* @__PURE__ */ jsx(Flex$1, {
1041
+ bdrs: "md",
1042
+ w: "100%",
1043
+ flex: 1,
1044
+ p: "sm",
1045
+ bg: ui.colors.surface,
1046
+ children: /* @__PURE__ */ jsx(ErrorViewer_default, {
1047
+ size: "xs",
1048
+ error,
1049
+ showStack: options?.showStack ?? true
1050
+ })
1051
+ })
1052
+ });
1053
+ }
1054
+ /**
1055
+ * Show a form dialog for structured input
1056
+ */
1057
+ form(options) {
1058
+ return Promise.resolve(null);
1059
+ }
1060
+ /**
1061
+ * Show a loading/progress dialog with optional progress percentage
1062
+ */
1063
+ loading(options) {}
1064
+ /**
1065
+ * Show an image viewer/gallery dialog
1066
+ */
1067
+ image(src, options) {}
1068
+ };
1069
+
1070
+ //#endregion
1071
+ //#region ../../src/core/components/buttons/ActionButton.tsx
1072
+ const ActionMenuItem = (props) => {
1073
+ const { item, index } = props;
1074
+ const router = useRouter();
1075
+ const action = useAction({ handler: async (e) => {
1076
+ await item.onClick?.();
1077
+ } }, [item.onClick]);
1078
+ if (item.type === "divider") return /* @__PURE__ */ jsx(Menu.Divider, {}, index);
1079
+ if (item.type === "label") return /* @__PURE__ */ jsx(Menu.Label, { children: item.label }, index);
1080
+ if (item.children && item.children.length > 0) return /* @__PURE__ */ jsxs(Menu, {
1081
+ trigger: "hover",
1082
+ position: "right-start",
1083
+ offset: 2,
1084
+ children: [/* @__PURE__ */ jsx(Menu.Target, { children: /* @__PURE__ */ jsx(Menu.Item, {
1085
+ leftSection: item.icon,
1086
+ rightSection: /* @__PURE__ */ jsx(IconChevronRight, { size: 14 }),
1087
+ children: item.label
1088
+ }) }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: item.children.map((child, childIndex) => /* @__PURE__ */ jsx(ActionMenuItem, {
1089
+ item: child,
1090
+ index: childIndex
1091
+ }, childIndex)) })]
1092
+ }, index);
1093
+ const menuItemProps = {};
1094
+ if (props.item.onClick) menuItemProps.onClick = action.run;
1095
+ else if (props.item.href) Object.assign(menuItemProps, router.anchor(props.item.href));
1096
+ return /* @__PURE__ */ jsx(Menu.Item, {
1097
+ leftSection: item.icon,
1098
+ onClick: item.onClick,
1099
+ color: item.color,
1100
+ rightSection: item.active ? /* @__PURE__ */ jsx(ThemeIcon, {
1101
+ size: "xs",
1102
+ variant: "transparent",
1103
+ children: /* @__PURE__ */ jsx(IconCheck, {})
1104
+ }) : void 0,
1105
+ ...menuItemProps,
1106
+ children: item.label
1107
+ }, index);
1108
+ };
1109
+ const ActionButton = (_props) => {
1110
+ const theme = useMantineTheme();
1111
+ const props = { ..._props };
1112
+ const { tooltip, menu, icon, ...restProps } = props;
1113
+ if (props.variant === "subtle") {
1114
+ restProps.c ??= "var(--mantine-color-text)";
1115
+ restProps.color ??= "gray";
1116
+ }
1117
+ if (props.intent) {
1118
+ if (props.intent === "none") {
1119
+ restProps.c ??= "var(--mantine-color-text)";
1120
+ restProps.color ??= "gray";
1121
+ } else if (props.intent === "primary") {
1122
+ restProps.c ??= "white";
1123
+ restProps.color ??= theme.primaryColor;
1124
+ } else if (props.intent === "success") {
1125
+ restProps.c ??= "white";
1126
+ restProps.color ??= "green";
1127
+ } else if (props.intent === "danger") {
1128
+ restProps.c ??= "white";
1129
+ restProps.color ??= "red";
1130
+ } else if (props.intent === "warning") {
1131
+ restProps.c ??= "var(--mantine-color-text)";
1132
+ restProps.color ??= "yellow";
1133
+ } else if (props.intent === "info") {
1134
+ restProps.c ??= "white";
1135
+ restProps.color ??= "blue";
1136
+ }
1137
+ }
1138
+ if (props.icon) {
1139
+ const icon$1 = isComponentType(props.icon) ? /* @__PURE__ */ jsx(props.icon, { size: ui.sizes.icon.md }) : /* @__PURE__ */ jsx(ThemeIcon, {
1140
+ w: 24,
1141
+ variant: "transparent",
1142
+ size: "sm",
1143
+ c: "var(--mantine-color-text)",
1144
+ ...props.themeIconProps,
1145
+ children: props.icon
1146
+ });
1147
+ if (!props.children) {
1148
+ restProps.children = Children.only(icon$1);
1149
+ restProps.px ??= "xs";
1150
+ } else restProps.leftSection = icon$1;
1151
+ }
1152
+ if (props.leftSection && !props.children) restProps.px ??= "xs";
1153
+ if (props.textVisibleFrom) {
1154
+ const { children, textVisibleFrom, leftSection, ...rest } = restProps;
1155
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Flex$1, {
1156
+ w: "100%",
1157
+ visibleFrom: textVisibleFrom,
1158
+ children: /* @__PURE__ */ jsx(ActionButton, {
1159
+ flex: 1,
1160
+ ...rest,
1161
+ leftSection,
1162
+ tooltip,
1163
+ menu,
1164
+ children
1165
+ })
1166
+ }), /* @__PURE__ */ jsx(Flex$1, {
1167
+ w: "100%",
1168
+ hiddenFrom: textVisibleFrom,
1169
+ children: /* @__PURE__ */ jsx(ActionButton, {
1170
+ px: "xs",
1171
+ ...rest,
1172
+ tooltip,
1173
+ menu,
1174
+ children: leftSection
1175
+ })
1176
+ })] });
1177
+ }
1178
+ const renderAction = () => {
1179
+ if ("href" in restProps && restProps.href) {
1180
+ if (restProps.href.startsWith("http") || restProps.target) return /* @__PURE__ */ jsx(ActionHrefButton, {
1181
+ ...restProps,
1182
+ href: restProps.href,
1183
+ children: restProps.children
1184
+ });
1185
+ return /* @__PURE__ */ jsx(ActionNavigationButton, {
1186
+ ...restProps,
1187
+ href: restProps.href,
1188
+ children: restProps.children
1189
+ });
1190
+ }
1191
+ delete restProps.classNameActive;
1192
+ delete restProps.variantActive;
1193
+ if ("action" in restProps && restProps.action) return /* @__PURE__ */ jsx(ActionHookButton, {
1194
+ ...restProps,
1195
+ action: restProps.action,
1196
+ children: restProps.children
1197
+ });
1198
+ if ("onClick" in restProps && restProps.onClick) return /* @__PURE__ */ jsx(ActionClickButton, {
1199
+ ...restProps,
1200
+ onClick: restProps.onClick,
1201
+ children: restProps.children
1202
+ });
1203
+ if ("form" in restProps && restProps.form) {
1204
+ if (restProps.type === "reset") return /* @__PURE__ */ jsx(ActionResetButton, {
1205
+ ...restProps,
1206
+ form: restProps.form,
1207
+ children: restProps.children
1208
+ });
1209
+ return /* @__PURE__ */ jsx(ActionSubmitButton, {
1210
+ ...restProps,
1211
+ form: restProps.form,
1212
+ children: restProps.children
1213
+ });
1214
+ }
1215
+ return /* @__PURE__ */ jsx(Button, {
1216
+ ...restProps,
1217
+ children: restProps.children
1218
+ });
1219
+ };
1220
+ let actionElement = renderAction();
1221
+ if (menu) actionElement = /* @__PURE__ */ jsxs(Menu, {
1222
+ position: menu.position || "bottom-start",
1223
+ width: menu.width || 200,
1224
+ shadow: menu.shadow || "md",
1225
+ trigger: menu.on === "hover" ? "hover" : "click",
1226
+ ...menu.menuProps,
1227
+ children: [/* @__PURE__ */ jsx(Menu.Target, {
1228
+ ...menu.targetProps,
1229
+ children: actionElement
1230
+ }), /* @__PURE__ */ jsx(Menu.Dropdown, { children: menu.items.map((item, index) => /* @__PURE__ */ jsx(ActionMenuItem, {
1231
+ item,
1232
+ index
1233
+ }, index)) })]
1234
+ });
1235
+ if (tooltip) {
1236
+ const defaultTooltipProps = { openDelay: 1e3 };
1237
+ return /* @__PURE__ */ jsx(Tooltip, { ...typeof tooltip === "string" || typeof tooltip === "number" ? {
1238
+ ...defaultTooltipProps,
1239
+ label: tooltip,
1240
+ children: actionElement
1241
+ } : {
1242
+ ...defaultTooltipProps,
1243
+ ...tooltip,
1244
+ children: actionElement
1245
+ } });
1246
+ }
1247
+ return actionElement;
1248
+ };
1249
+ var ActionButton_default = ActionButton;
1250
+ /**
1251
+ * Action button that submits a form with loading and disabled state handling.
1252
+ */
1253
+ const ActionSubmitButton = (props) => {
1254
+ const { form, ...buttonProps } = props;
1255
+ const state = useFormState(form);
1256
+ return /* @__PURE__ */ jsx(Button, {
1257
+ ...buttonProps,
1258
+ loading: state.loading,
1259
+ disabled: state.loading,
1260
+ type: "submit",
1261
+ children: props.children
1262
+ });
1263
+ };
1264
+ const ActionResetButton = (props) => {
1265
+ const { form, ...buttonProps } = props;
1266
+ const state = useFormState(form);
1267
+ return /* @__PURE__ */ jsx(Button, {
1268
+ ...buttonProps,
1269
+ disabled: state.loading,
1270
+ type: "reset",
1271
+ children: props.children
1272
+ });
1273
+ };
1274
+ /**
1275
+ * Action button that integrates with useAction hook return value.
1276
+ * Automatically handles loading state and executes the action on click.
1277
+ *
1278
+ * @example
1279
+ * ```tsx
1280
+ * const saveAction = useAction({
1281
+ * handler: async (data) => {
1282
+ * await api.save(data);
1283
+ * }
1284
+ * }, []);
1285
+ *
1286
+ * <ActionButton action={saveAction}>
1287
+ * Save
1288
+ * </ActionButton>
1289
+ * ```
1290
+ */
1291
+ const ActionHookButton = (props) => {
1292
+ const { action, ...buttonProps } = props;
1293
+ return /* @__PURE__ */ jsx(Button, {
1294
+ ...buttonProps,
1295
+ disabled: action.loading || props.disabled,
1296
+ loading: action.loading,
1297
+ onClick: () => action.run(),
1298
+ children: props.children
1299
+ });
1300
+ };
1301
+ /**
1302
+ * Basic action button that handles click events with loading and error handling.
1303
+ *
1304
+ * @example
1305
+ * ```tsx
1306
+ * <ActionButton onClick={() => api.doSomething()}>
1307
+ * Do Something
1308
+ * </ActionButton>
1309
+ * ```
1310
+ */
1311
+ const ActionClickButton = (props) => {
1312
+ const action = useAction({ handler: async (e) => {
1313
+ await props.onClick(e);
1314
+ } }, [props.onClick]);
1315
+ return /* @__PURE__ */ jsx(Button, {
1316
+ ...props,
1317
+ disabled: action.loading || props.disabled,
1318
+ loading: action.loading,
1319
+ onClick: action.run,
1320
+ children: props.children
1321
+ });
1322
+ };
1323
+ /**
1324
+ * Action for navigation with active state support.
1325
+ */
1326
+ const ActionNavigationButton = (props) => {
1327
+ const { active: options, classNameActive, variantActive, routerGoOptions, ...buttonProps } = props;
1328
+ const router = useRouter();
1329
+ const { isPending, isActive } = useActive(options ? {
1330
+ href: props.href,
1331
+ ...options
1332
+ } : { href: props.href });
1333
+ const anchorProps = router.anchor(props.href, routerGoOptions);
1334
+ const className = buttonProps.className || "";
1335
+ if (isActive && options !== false && classNameActive) buttonProps.className = `${className} ${classNameActive}`.trim();
1336
+ if (props.anchorProps) return /* @__PURE__ */ jsx(Anchor, {
1337
+ component: "a",
1338
+ ...anchorProps,
1339
+ ...props.anchorProps,
1340
+ children: props.children
1341
+ });
1342
+ return /* @__PURE__ */ jsx(Button, {
1343
+ component: "a",
1344
+ loading: isPending,
1345
+ ...buttonProps,
1346
+ ...anchorProps,
1347
+ variant: isActive && options !== false ? variantActive ?? "filled" : buttonProps.variant ?? "subtle",
1348
+ children: props.children
1349
+ });
1350
+ };
1351
+ const ActionHrefButton = (props) => {
1352
+ const { active: options, classNameActive, variantActive, routerGoOptions, target, ...buttonProps } = props;
1353
+ return /* @__PURE__ */ jsx(Button, {
1354
+ component: "a",
1355
+ target,
1356
+ ...buttonProps,
1357
+ children: props.children
1358
+ });
1911
1359
  };
1912
1360
 
1913
1361
  //#endregion
@@ -1988,7 +1436,6 @@ const DarkModeButton = (props) => {
1988
1436
  size: props.size ?? "sm",
1989
1437
  "aria-label": "Toggle color scheme",
1990
1438
  px: "xs",
1991
- c: colorScheme !== "default" ? void 0 : "transparent",
1992
1439
  fullWidth: props.fullWidth ?? false,
1993
1440
  icon: colorScheme === "dark" ? IconSun : colorScheme === "light" ? IconMoon : IconSun,
1994
1441
  ...props.actionProps
@@ -2051,14 +1498,14 @@ var OmnibarButton_default = OmnibarButton;
2051
1498
  //#region ../../src/core/components/buttons/ThemeButton.tsx
2052
1499
  const ThemeButton = (props) => {
2053
1500
  const [theme, setTheme] = useTheme();
2054
- const themes = useInject(ThemeProvider).themes;
1501
+ const themeList = useStore(alephaThemeListAtom)[0];
2055
1502
  return /* @__PURE__ */ jsx(ActionButton_default, {
2056
1503
  variant: "subtle",
2057
1504
  icon: IconPalette,
2058
- menu: { items: themes.map((it) => ({
2059
- label: it.label,
2060
- onClick: () => setTheme(it),
2061
- active: theme.id === it.id
1505
+ menu: { items: themeList.map((it, index) => ({
1506
+ label: it.name,
1507
+ onClick: () => setTheme({ index }),
1508
+ active: theme.name === it.name
2062
1509
  })) },
2063
1510
  ...props.actionProps
2064
1511
  });
@@ -2168,6 +1615,256 @@ const parseInput = (props, form) => {
2168
1615
  };
2169
1616
  };
2170
1617
 
1618
+ //#endregion
1619
+ //#region ../../src/core/components/form/ControlArray.tsx
1620
+ /**
1621
+ * ControlArray component for editing arrays of schema items.
1622
+ *
1623
+ * Features:
1624
+ * - Dynamic add/remove of items
1625
+ * - Supports arrays of objects with nested fields
1626
+ * - Supports arrays of primitives
1627
+ * - Grid layout for object items
1628
+ * - Min/max constraints
1629
+ *
1630
+ * @example
1631
+ * ```tsx
1632
+ * // For a schema like:
1633
+ * // t.object({
1634
+ * // contacts: t.array(t.object({
1635
+ * // name: t.text(),
1636
+ * // email: t.text({ format: "email" }),
1637
+ * // }))
1638
+ * // })
1639
+ *
1640
+ * <ControlArray
1641
+ * input={form.input.contacts}
1642
+ * columns={2}
1643
+ * addLabel="Add contact"
1644
+ * controlProps={{
1645
+ * email: { text: { placeholder: "email@example.com" } }
1646
+ * }}
1647
+ * />
1648
+ * ```
1649
+ */
1650
+ const ControlArray = (props) => {
1651
+ const { inputProps } = parseInput(props, {});
1652
+ const idCounter = useRef(0);
1653
+ const [items, setItems] = useState(() => {
1654
+ const defaultValue = props.input?.props?.defaultValue;
1655
+ if (Array.isArray(defaultValue)) return defaultValue.map((value) => ({
1656
+ key: idCounter.current++,
1657
+ value
1658
+ }));
1659
+ return [];
1660
+ });
1661
+ useEvents({ "form:reset": (event) => {
1662
+ if (event.id === props.input?.form?.id) {
1663
+ const defaultValue = props.input?.props?.defaultValue;
1664
+ if (Array.isArray(defaultValue)) {
1665
+ idCounter.current = 0;
1666
+ setItems(defaultValue.map((value) => ({
1667
+ key: idCounter.current++,
1668
+ value
1669
+ })));
1670
+ } else setItems([]);
1671
+ }
1672
+ } }, [props.input]);
1673
+ if (!props.input?.props) return null;
1674
+ const schema = props.input.schema;
1675
+ if (!schema || !("items" in schema)) return null;
1676
+ const itemSchema = schema.items;
1677
+ const isObjectItem = itemSchema && "properties" in itemSchema;
1678
+ const { min = 0, max = Number.POSITIVE_INFINITY, columns = 1 } = props;
1679
+ const updateFormValue = (newItems) => {
1680
+ props.input.set(newItems.map((item) => item.value));
1681
+ };
1682
+ const handleAdd = () => {
1683
+ if (items.length >= max) return;
1684
+ let newValue;
1685
+ if (isObjectItem) {
1686
+ newValue = {};
1687
+ const objSchema = itemSchema;
1688
+ for (const [key, propSchema] of Object.entries(objSchema.properties)) if ("default" in propSchema) newValue[key] = propSchema.default;
1689
+ } else newValue = "";
1690
+ const newItems = [...items, {
1691
+ key: idCounter.current++,
1692
+ value: newValue
1693
+ }];
1694
+ setItems(newItems);
1695
+ updateFormValue(newItems);
1696
+ };
1697
+ const handleRemove = (index) => {
1698
+ if (items.length <= min) return;
1699
+ const newItems = items.filter((_, i) => i !== index);
1700
+ setItems(newItems);
1701
+ updateFormValue(newItems);
1702
+ };
1703
+ const handleItemChange = (index, value) => {
1704
+ const newItems = [...items];
1705
+ newItems[index] = {
1706
+ ...newItems[index],
1707
+ value
1708
+ };
1709
+ setItems(newItems);
1710
+ updateFormValue(newItems);
1711
+ };
1712
+ const handleFieldChange = (index, field, value) => {
1713
+ const newItems = [...items];
1714
+ newItems[index] = {
1715
+ ...newItems[index],
1716
+ value: {
1717
+ ...newItems[index].value,
1718
+ [field]: value
1719
+ }
1720
+ };
1721
+ setItems(newItems);
1722
+ updateFormValue(newItems);
1723
+ };
1724
+ const colSpan = 12 / columns;
1725
+ const fieldNames = isObjectItem ? Object.keys(itemSchema.properties) : [];
1726
+ const renderItems = () => /* @__PURE__ */ jsxs(Stack, {
1727
+ gap: "sm",
1728
+ children: [items.map((item, index) => /* @__PURE__ */ jsxs(Flex$1, {
1729
+ gap: "sm",
1730
+ align: "flex-start",
1731
+ p: "xs",
1732
+ bg: ui.colors.surface,
1733
+ style: { borderRadius: "var(--mantine-radius-sm)" },
1734
+ children: [
1735
+ props.sortable && /* @__PURE__ */ jsx(ActionIcon, {
1736
+ variant: "subtle",
1737
+ color: "gray",
1738
+ style: { cursor: "grab" },
1739
+ children: /* @__PURE__ */ jsx(IconGripVertical, { size: 16 })
1740
+ }),
1741
+ isObjectItem ? /* @__PURE__ */ jsx(Grid, {
1742
+ style: { flex: 1 },
1743
+ gutter: "sm",
1744
+ children: fieldNames.map((fieldName) => {
1745
+ const fieldSchema = itemSchema.properties[fieldName];
1746
+ const fieldControlProps = props.controlProps?.[fieldName] ?? {};
1747
+ const virtualInput = {
1748
+ schema: fieldSchema,
1749
+ props: {
1750
+ id: `${props.input.props.id}-${item.key}-${fieldName}`,
1751
+ name: `${props.input.props.name}[${index}].${fieldName}`,
1752
+ defaultValue: item.value?.[fieldName]
1753
+ },
1754
+ path: `${props.input.path}/${index}/${fieldName}`,
1755
+ required: itemSchema.required?.includes(fieldName) ?? false,
1756
+ form: props.input.form,
1757
+ set: (value) => handleFieldChange(index, fieldName, value)
1758
+ };
1759
+ return /* @__PURE__ */ jsx(Grid.Col, {
1760
+ span: colSpan,
1761
+ children: /* @__PURE__ */ jsx(Control_default, {
1762
+ input: virtualInput,
1763
+ ...fieldControlProps
1764
+ })
1765
+ }, fieldName);
1766
+ })
1767
+ }) : /* @__PURE__ */ jsx(Flex$1, {
1768
+ style: { flex: 1 },
1769
+ children: /* @__PURE__ */ jsx(Control_default, {
1770
+ input: {
1771
+ schema: itemSchema,
1772
+ props: {
1773
+ id: `${props.input.props.id}-${item.key}`,
1774
+ name: `${props.input.props.name}[${index}]`,
1775
+ defaultValue: item.value
1776
+ },
1777
+ path: `${props.input.path}/${index}`,
1778
+ required: false,
1779
+ form: props.input.form,
1780
+ set: (value) => handleItemChange(index, value)
1781
+ },
1782
+ ...props.itemControlProps
1783
+ })
1784
+ }),
1785
+ /* @__PURE__ */ jsx(ActionIcon, {
1786
+ variant: "subtle",
1787
+ color: "red",
1788
+ onClick: () => handleRemove(index),
1789
+ disabled: items.length <= min,
1790
+ children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
1791
+ })
1792
+ ]
1793
+ }, item.key)), /* @__PURE__ */ jsxs(UnstyledButton, {
1794
+ onClick: handleAdd,
1795
+ disabled: items.length >= max,
1796
+ style: {
1797
+ display: "flex",
1798
+ alignItems: "center",
1799
+ justifyContent: "center",
1800
+ gap: 6,
1801
+ padding: "8px 12px",
1802
+ borderRadius: "var(--mantine-radius-sm)",
1803
+ border: "1px dashed var(--mantine-color-dimmed)",
1804
+ color: "var(--mantine-color-dimmed)",
1805
+ fontSize: "var(--mantine-font-size-sm)",
1806
+ cursor: items.length >= max ? "not-allowed" : "pointer",
1807
+ opacity: items.length >= max ? .5 : 1,
1808
+ transition: "all 150ms ease"
1809
+ },
1810
+ onMouseEnter: (e) => {
1811
+ if (items.length < max) {
1812
+ e.currentTarget.style.borderColor = "var(--mantine-color-blue-filled)";
1813
+ e.currentTarget.style.color = "var(--mantine-color-blue-filled)";
1814
+ e.currentTarget.style.background = "var(--mantine-color-blue-light)";
1815
+ }
1816
+ },
1817
+ onMouseLeave: (e) => {
1818
+ e.currentTarget.style.borderColor = "var(--mantine-color-dimmed)";
1819
+ e.currentTarget.style.color = "var(--mantine-color-dimmed)";
1820
+ e.currentTarget.style.background = "transparent";
1821
+ },
1822
+ children: [/* @__PURE__ */ jsx(IconPlus, { size: 14 }), props.addLabel ?? "Add"]
1823
+ })]
1824
+ });
1825
+ if (props.variant === "plain") return /* @__PURE__ */ jsxs(Stack, {
1826
+ gap: "xs",
1827
+ children: [
1828
+ inputProps.label && /* @__PURE__ */ jsx(Text$1, {
1829
+ size: "sm",
1830
+ fw: 500,
1831
+ children: inputProps.label
1832
+ }),
1833
+ inputProps.description && /* @__PURE__ */ jsx(Text$1, {
1834
+ size: "sm",
1835
+ c: "dimmed",
1836
+ children: inputProps.description
1837
+ }),
1838
+ renderItems(),
1839
+ inputProps.error && /* @__PURE__ */ jsx(Text$1, {
1840
+ size: "sm",
1841
+ c: "red",
1842
+ children: inputProps.error
1843
+ })
1844
+ ]
1845
+ });
1846
+ return /* @__PURE__ */ jsx(Fieldset, {
1847
+ legend: inputProps.label,
1848
+ children: /* @__PURE__ */ jsxs(Stack, {
1849
+ gap: "xs",
1850
+ children: [
1851
+ inputProps.description && /* @__PURE__ */ jsx(Text$1, {
1852
+ size: "sm",
1853
+ c: "dimmed",
1854
+ children: inputProps.description
1855
+ }),
1856
+ renderItems(),
1857
+ inputProps.error && /* @__PURE__ */ jsx(Text$1, {
1858
+ size: "sm",
1859
+ c: "red",
1860
+ children: inputProps.error
1861
+ })
1862
+ ]
1863
+ })
1864
+ });
1865
+ };
1866
+ var ControlArray_default = ControlArray;
1867
+
2171
1868
  //#endregion
2172
1869
  //#region ../../src/core/components/form/ControlDate.tsx
2173
1870
  /**
@@ -2261,22 +1958,99 @@ const ControlNumber = (props) => {
2261
1958
  })
2262
1959
  })
2263
1960
  });
2264
- return /* @__PURE__ */ jsx(NumberInput, {
2265
- ...inputProps,
2266
- ref,
2267
- id,
2268
- leftSection: icon,
2269
- ...inputPropsWithoutType,
2270
- ...props.numberInputProps,
2271
- value: value ?? "",
2272
- onChange: (val) => {
2273
- const newValue = val !== null ? Number(val) : void 0;
2274
- setValue(newValue);
2275
- props.input.set(newValue);
2276
- }
2277
- });
1961
+ return /* @__PURE__ */ jsx(NumberInput, {
1962
+ ...inputProps,
1963
+ ref,
1964
+ id,
1965
+ leftSection: icon,
1966
+ ...inputPropsWithoutType,
1967
+ ...props.numberInputProps,
1968
+ value: value ?? "",
1969
+ onChange: (val) => {
1970
+ const newValue = val !== null ? Number(val) : void 0;
1971
+ setValue(newValue);
1972
+ props.input.set(newValue);
1973
+ }
1974
+ });
1975
+ };
1976
+ var ControlNumber_default = ControlNumber;
1977
+
1978
+ //#endregion
1979
+ //#region ../../src/core/components/form/ControlObject.tsx
1980
+ /**
1981
+ * ControlObject component for editing nested object schemas.
1982
+ *
1983
+ * Features:
1984
+ * - Renders all properties of an object schema
1985
+ * - Supports grid layout with configurable columns
1986
+ * - Per-field customization via controlProps
1987
+ * - Recursive support for deeply nested objects
1988
+ *
1989
+ * The form system provides nested InputFields under the `.items` property.
1990
+ * For example: form.input.address.items.street
1991
+ *
1992
+ * @example
1993
+ * ```tsx
1994
+ * // For a schema like:
1995
+ * // t.object({
1996
+ * // address: t.object({
1997
+ * // street: t.text(),
1998
+ * // city: t.text(),
1999
+ * // zip: t.text(),
2000
+ * // })
2001
+ * // })
2002
+ *
2003
+ * <ControlObject
2004
+ * input={form.input.address}
2005
+ * columns={2}
2006
+ * controlProps={{
2007
+ * zip: { text: { maxLength: 10 } }
2008
+ * }}
2009
+ * />
2010
+ * ```
2011
+ */
2012
+ const ControlObject = (props) => {
2013
+ const { inputProps } = parseInput(props, {});
2014
+ if (!props.input?.props) return null;
2015
+ const schema = props.input.schema;
2016
+ if (!schema?.properties) return null;
2017
+ const fieldNames = Object.keys(schema.properties);
2018
+ const colSpan = 12 / (props.columns ?? 1);
2019
+ const nestedItems = props.input.items;
2020
+ const renderFields = () => /* @__PURE__ */ jsx(Grid, { children: fieldNames.map((fieldName) => {
2021
+ const fieldControlProps = props.controlProps?.[fieldName] ?? {};
2022
+ const field = nestedItems?.[fieldName];
2023
+ if (!field) return null;
2024
+ return /* @__PURE__ */ jsx(Grid.Col, {
2025
+ span: colSpan,
2026
+ children: /* @__PURE__ */ jsx(Control_default, {
2027
+ input: field,
2028
+ ...fieldControlProps
2029
+ })
2030
+ }, fieldName);
2031
+ }) });
2032
+ if (props.variant === "plain") return renderFields();
2033
+ return /* @__PURE__ */ jsx(Fieldset, {
2034
+ legend: inputProps.label,
2035
+ children: /* @__PURE__ */ jsxs(Stack, {
2036
+ gap: "xs",
2037
+ children: [
2038
+ inputProps.description && /* @__PURE__ */ jsx(Text$1, {
2039
+ size: "sm",
2040
+ c: "dimmed",
2041
+ children: inputProps.description
2042
+ }),
2043
+ renderFields(),
2044
+ inputProps.error && /* @__PURE__ */ jsx(Text$1, {
2045
+ size: "sm",
2046
+ c: "red",
2047
+ children: inputProps.error
2048
+ })
2049
+ ]
2050
+ })
2051
+ });
2278
2052
  };
2279
- var ControlNumber_default = ControlNumber;
2053
+ var ControlObject_default = ControlObject;
2280
2054
 
2281
2055
  //#endregion
2282
2056
  //#region ../../src/core/utils/extractSchemaFields.ts
@@ -2763,13 +2537,16 @@ var ControlSelect_default = ControlSelect;
2763
2537
  * - DateTimePicker (for date-time format)
2764
2538
  * - TimeInput (for time format)
2765
2539
  * - QueryBuilder (for building type-safe queries with autocomplete)
2540
+ * - ControlObject (for nested object schemas)
2541
+ * - ControlArray (for arrays of objects)
2766
2542
  * - Custom component
2767
2543
  *
2768
2544
  * Automatically handles labels, descriptions, error messages, required state, and default icons.
2769
2545
  */
2770
2546
  const Control = (_props) => {
2771
- const { inputProps, id, icon, format, schema } = parseInput(_props, useFormState(_props.input, ["error"]));
2547
+ const form = useFormState(_props.input, ["error"]);
2772
2548
  if (!_props.input?.props) return null;
2549
+ const { inputProps, id, icon, format, schema } = parseInput(_props, form);
2773
2550
  const props = {
2774
2551
  ..._props,
2775
2552
  ...schema.$control
@@ -2799,6 +2576,27 @@ const Control = (_props) => {
2799
2576
  })
2800
2577
  });
2801
2578
  }
2579
+ const isObject = props.input.schema && "type" in props.input.schema && props.input.schema.type === "object" && "properties" in props.input.schema;
2580
+ if (props.object || isObject) {
2581
+ const controlObjectProps = typeof props.object === "object" ? props.object : {};
2582
+ return /* @__PURE__ */ jsx(ControlObject_default, {
2583
+ input: props.input,
2584
+ title: props.title,
2585
+ description: props.description,
2586
+ ...controlObjectProps
2587
+ });
2588
+ }
2589
+ const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
2590
+ const isArrayOfObjects = isArray && "items" in props.input.schema && props.input.schema.items && typeof props.input.schema.items === "object" && "properties" in props.input.schema.items;
2591
+ if (props.array || isArrayOfObjects) {
2592
+ const controlArrayProps = typeof props.array === "object" ? props.array : {};
2593
+ return /* @__PURE__ */ jsx(ControlArray_default, {
2594
+ input: props.input,
2595
+ title: props.title,
2596
+ description: props.description,
2597
+ ...controlArrayProps
2598
+ });
2599
+ }
2802
2600
  if (props.number || props.input.schema && "type" in props.input.schema && (props.input.schema.type === "number" || props.input.schema.type === "integer")) {
2803
2601
  const controlNumberProps = typeof props.number === "object" ? props.number : {};
2804
2602
  return /* @__PURE__ */ jsx(ControlNumber_default, {
@@ -2831,9 +2629,7 @@ const Control = (_props) => {
2831
2629
  ...colorInputProps
2832
2630
  });
2833
2631
  }
2834
- const isEnum = props.input.schema && "enum" in props.input.schema && props.input.schema.enum;
2835
- const isArray = props.input.schema && "type" in props.input.schema && props.input.schema.type === "array";
2836
- if (isEnum || isArray || props.select) {
2632
+ if (props.input.schema && "enum" in props.input.schema && props.input.schema.enum || isArray && !isArrayOfObjects || props.select) {
2837
2633
  const opts = typeof props.select === "object" ? props.select : {};
2838
2634
  return /* @__PURE__ */ jsx(ControlSelect_default, {
2839
2635
  input: props.input,
@@ -2843,15 +2639,41 @@ const Control = (_props) => {
2843
2639
  ...opts
2844
2640
  });
2845
2641
  }
2846
- if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean" || props.switch) {
2847
- const switchProps = typeof props.switch === "object" ? props.switch : {};
2848
- return /* @__PURE__ */ jsx(Switch, {
2849
- ...inputProps,
2850
- id,
2851
- color: "blue",
2852
- defaultChecked: props.input.props.defaultValue,
2853
- ...props.input.props,
2854
- ...switchProps
2642
+ if (props.input.schema && "type" in props.input.schema && props.input.schema.type === "boolean") {
2643
+ if (props.switch) {
2644
+ const switchProps = typeof props.switch === "object" ? props.switch : {};
2645
+ return /* @__PURE__ */ jsx(Switch, {
2646
+ ...inputProps,
2647
+ id,
2648
+ color: "blue",
2649
+ defaultChecked: props.input.props.defaultValue,
2650
+ ...props.input.props,
2651
+ ...switchProps
2652
+ });
2653
+ }
2654
+ const selectProps = {
2655
+ loader: async () => [
2656
+ {
2657
+ value: "true",
2658
+ label: "Yes"
2659
+ },
2660
+ {
2661
+ value: "false",
2662
+ label: "No"
2663
+ },
2664
+ {
2665
+ value: "",
2666
+ label: ""
2667
+ }
2668
+ ],
2669
+ ...props.input.props
2670
+ };
2671
+ return /* @__PURE__ */ jsx(ControlSelect_default, {
2672
+ input: props.input,
2673
+ title: props.title,
2674
+ description: props.description,
2675
+ icon,
2676
+ ...selectProps
2855
2677
  });
2856
2678
  }
2857
2679
  if (props.password || props.input.props.name?.includes("password")) {
@@ -2917,6 +2739,13 @@ var Control_default = Control;
2917
2739
  * TypeForm component that automatically renders all form inputs based on schema.
2918
2740
  * Uses the Control component to render individual fields and Mantine Grid for responsive layout.
2919
2741
  *
2742
+ * Supports all field types including:
2743
+ * - Primitive types (string, number, boolean, etc.)
2744
+ * - Enum types (rendered as Select)
2745
+ * - Arrays of primitives (rendered as MultiSelect/TagsInput)
2746
+ * - Arrays of objects (rendered as ControlArray)
2747
+ * - Nested objects (rendered as ControlObject)
2748
+ *
2920
2749
  * @example
2921
2750
  * ```tsx
2922
2751
  * import { t } from "alepha";
@@ -2929,6 +2758,15 @@ var Control_default = Control;
2929
2758
  * email: t.text(),
2930
2759
  * age: t.integer(),
2931
2760
  * subscribe: t.boolean(),
2761
+ * address: t.object({
2762
+ * street: t.text(),
2763
+ * city: t.text(),
2764
+ * }),
2765
+ * tags: t.array(t.text()),
2766
+ * contacts: t.array(t.object({
2767
+ * name: t.text(),
2768
+ * email: t.text(),
2769
+ * })),
2932
2770
  * }),
2933
2771
  * handler: (values) => {
2934
2772
  * console.log(values);
@@ -2939,19 +2777,10 @@ var Control_default = Control;
2939
2777
  * ```
2940
2778
  */
2941
2779
  const TypeForm = (props) => {
2942
- const { form, columns = 3, children, controlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
2780
+ const { form, columns = 3, children, controlProps, fieldControlProps, skipFormElement = false, skipSubmitButton = false, submitButtonProps } = props;
2943
2781
  const schema = props.schema || form.options.schema;
2944
2782
  if (!schema?.properties) return null;
2945
- const supportedFields = Object.keys(schema.properties).filter((fieldName) => {
2946
- const field = form.input[fieldName];
2947
- if (!field || typeof field !== "object" || !("schema" in field)) return false;
2948
- const schema$1 = field.schema;
2949
- if ("type" in schema$1) {
2950
- if (schema$1.type === "object") return false;
2951
- }
2952
- if ("properties" in schema$1 && schema$1.properties) return false;
2953
- return true;
2954
- });
2783
+ const supportedFields = Object.keys(schema.properties);
2955
2784
  const colSpan = typeof columns === "number" ? {
2956
2785
  xs: 12,
2957
2786
  sm: 6,
@@ -2968,12 +2797,20 @@ const TypeForm = (props) => {
2968
2797
  if (children) return /* @__PURE__ */ jsx(Fragment, { children: children(form.input) });
2969
2798
  return /* @__PURE__ */ jsx(Grid, { children: supportedFields.map((fieldName) => {
2970
2799
  const field = form.input[fieldName];
2971
- if (!field || typeof field !== "object" || !("schema" in field)) return null;
2800
+ const fieldSchema = schema.properties[fieldName];
2801
+ if (!field || !fieldSchema) return null;
2802
+ const isObject = fieldSchema && "type" in fieldSchema && fieldSchema.type === "object";
2803
+ const isArrayOfObjects = fieldSchema && "type" in fieldSchema && fieldSchema.type === "array" && "items" in fieldSchema && fieldSchema.items && "properties" in fieldSchema.items;
2804
+ const span = isObject || isArrayOfObjects ? 12 : colSpan;
2805
+ const mergedControlProps = {
2806
+ ...controlProps,
2807
+ ...fieldControlProps?.[fieldName]
2808
+ };
2972
2809
  return /* @__PURE__ */ jsx(Grid.Col, {
2973
- span: colSpan,
2810
+ span,
2974
2811
  children: /* @__PURE__ */ jsx(Control_default, {
2975
2812
  input: field,
2976
- ...controlProps
2813
+ ...mergedControlProps
2977
2814
  })
2978
2815
  }, fieldName);
2979
2816
  }) });
@@ -2981,21 +2818,45 @@ const TypeForm = (props) => {
2981
2818
  const content = /* @__PURE__ */ jsxs(Flex$1, {
2982
2819
  direction: "column",
2983
2820
  gap: "sm",
2984
- children: [renderFields(), !skipSubmitButton && /* @__PURE__ */ jsxs(Flex$1, {
2821
+ flex: props.fill ? 1 : void 0,
2822
+ ...props.flexProps,
2823
+ children: [/* @__PURE__ */ jsx(Flex$1, {
2824
+ direction: "column",
2985
2825
  gap: "sm",
2986
- children: [/* @__PURE__ */ jsx(ActionButton_default, {
2987
- form,
2988
- ...submitButtonProps,
2989
- children: submitButtonProps?.children ?? "Submit"
2990
- }), /* @__PURE__ */ jsx(ActionButton_default, {
2991
- type: "reset",
2992
- children: "Reset"
2993
- })]
2826
+ flex: 1,
2827
+ children: renderFields()
2828
+ }), !skipSubmitButton && /* @__PURE__ */ jsx(Card, {
2829
+ w: "100%",
2830
+ withBorder: true,
2831
+ children: /* @__PURE__ */ jsxs(Flex$1, {
2832
+ gap: "sm",
2833
+ flex: 1,
2834
+ children: [
2835
+ /* @__PURE__ */ jsx(Flex$1, {}),
2836
+ /* @__PURE__ */ jsx(Flex$1, { flex: 1 }),
2837
+ /* @__PURE__ */ jsxs(Flex$1, {
2838
+ gap: "sm",
2839
+ children: [/* @__PURE__ */ jsx(ActionButton_default, {
2840
+ variant: "subtle",
2841
+ type: "reset",
2842
+ children: "Reset"
2843
+ }), /* @__PURE__ */ jsx(ActionButton_default, {
2844
+ intent: "primary",
2845
+ form,
2846
+ ...submitButtonProps,
2847
+ children: submitButtonProps?.children ?? "Submit"
2848
+ })]
2849
+ })
2850
+ ]
2851
+ })
2994
2852
  })]
2995
2853
  });
2996
2854
  if (skipFormElement) return content;
2997
- return /* @__PURE__ */ jsx("form", {
2855
+ return /* @__PURE__ */ jsx(Flex$1, {
2856
+ component: "form",
2857
+ flex: props.fill ? 1 : void 0,
2998
2858
  ...form.props,
2859
+ ...props.flexProps,
2999
2860
  children: content
3000
2861
  });
3001
2862
  };
@@ -3055,6 +2916,23 @@ const AppBar = (props) => {
3055
2916
  };
3056
2917
  var AppBar_default = AppBar;
3057
2918
 
2919
+ //#endregion
2920
+ //#region ../../src/core/components/buttons/ToggleSidebarButton.tsx
2921
+ const ToggleSidebarButton = () => {
2922
+ const [collapsed, setCollapsed] = useStore("alepha.ui.sidebar.collapsed");
2923
+ return /* @__PURE__ */ jsx(Flex$1, { children: /* @__PURE__ */ jsx(ActionButton_default, {
2924
+ icon: collapsed ? /* @__PURE__ */ jsx(IconLayoutSidebarRightCollapse, {}) : /* @__PURE__ */ jsx(IconLayoutSidebarLeftCollapse, {}),
2925
+ variant: "subtle",
2926
+ size: "md",
2927
+ onClick: () => setCollapsed(!collapsed),
2928
+ tooltip: {
2929
+ position: "right",
2930
+ label: collapsed ? "Show sidebar" : "Hide sidebar"
2931
+ }
2932
+ }) });
2933
+ };
2934
+ var ToggleSidebarButton_default = ToggleSidebarButton;
2935
+
3058
2936
  //#endregion
3059
2937
  //#region ../../src/core/components/layout/Sidebar.tsx
3060
2938
  const Sidebar = (props) => {
@@ -3070,6 +2948,7 @@ const Sidebar = (props) => {
3070
2948
  mx: "sm"
3071
2949
  }, key);
3072
2950
  if (item.type === "search") return /* @__PURE__ */ jsx(OmnibarButton_default, { collapsed: props.collapsed }, key);
2951
+ if (item.type === "toggle") return /* @__PURE__ */ jsx(ToggleSidebarButton_default, {}, key);
3073
2952
  if (item.type === "section") {
3074
2953
  if (props.collapsed) return;
3075
2954
  return /* @__PURE__ */ jsxs(Flex$1, {
@@ -3102,14 +2981,25 @@ const Sidebar = (props) => {
3102
2981
  theme: props.theme ?? {}
3103
2982
  }, key);
3104
2983
  };
2984
+ const getSidebarNodes = () => {
2985
+ if (props.menu) return props.menu;
2986
+ if (props.autoPopulateMenu) {
2987
+ const items = router.concretePages.map((page) => ({
2988
+ label: page.label ?? page.name,
2989
+ description: page.description?.slice(0, 32),
2990
+ icon: renderIcon(page.icon),
2991
+ href: router.path(page.name)
2992
+ }));
2993
+ if (typeof props.autoPopulateMenu === "object" && props.autoPopulateMenu.startsWith) {
2994
+ const startsWith = props.autoPopulateMenu.startsWith;
2995
+ return items.filter((item) => item.href?.startsWith(startsWith));
2996
+ }
2997
+ }
2998
+ return [];
2999
+ };
3105
3000
  const padding = "md";
3106
- const gap = props.gap;
3107
- const menu = props.menu ?? router.concretePages.map((page) => ({
3108
- label: page.label ?? page.name,
3109
- description: page.description,
3110
- icon: renderIcon(page.icon),
3111
- href: router.path(page.name)
3112
- }));
3001
+ const gap = props.menu ? props.gap : "xs";
3002
+ const menu = useMemo(() => getSidebarNodes(), []);
3113
3003
  return /* @__PURE__ */ jsxs(Flex$1, {
3114
3004
  flex: 1,
3115
3005
  py: padding,
@@ -3177,6 +3067,8 @@ const SidebarItem = (props) => {
3177
3067
  href: props.item.href,
3178
3068
  target: props.item.target,
3179
3069
  size: props.item.theme?.size ?? props.theme.button?.size ?? (level === 0 ? "sm" : "xs"),
3070
+ c: "var(--mantine-color-text)",
3071
+ color: "gray",
3180
3072
  variant: "subtle",
3181
3073
  variantActive: "default",
3182
3074
  radius: props.item.theme?.radius ?? props.theme.button?.radius ?? "md",
@@ -3290,11 +3182,15 @@ const AdminShell = (props) => {
3290
3182
  type: "burger"
3291
3183
  }];
3292
3184
  const hasSidebar = showSidebar && props.sidebarProps !== void 0;
3185
+ const hasAppBar = hasSidebar || props.appBarProps || props.header;
3186
+ const headerHeight = hasAppBar ? 60 : 0;
3187
+ const footerHeight = props.footer ? 24 : 0;
3188
+ const sidebarWidth = hasSidebar ? collapsed ? 78 : 300 : 0;
3293
3189
  return /* @__PURE__ */ jsxs(AppShell, {
3294
3190
  w: "100%",
3295
3191
  flex: 1,
3296
3192
  padding: "md",
3297
- header: hasSidebar || props.appBarProps || props.header ? { height: 60 } : void 0,
3193
+ header: hasAppBar ? { height: 60 } : void 0,
3298
3194
  navbar: hasSidebar ? {
3299
3195
  width: collapsed ? { base: 78 } : { base: 300 },
3300
3196
  breakpoint: "sm",
@@ -3320,8 +3216,13 @@ const AdminShell = (props) => {
3320
3216
  })
3321
3217
  }),
3322
3218
  /* @__PURE__ */ jsx(AppShell.Main, {
3219
+ pl: sidebarWidth,
3220
+ pt: headerHeight,
3221
+ pb: footerHeight,
3222
+ pr: 0,
3323
3223
  display: "flex",
3324
3224
  flex: 1,
3225
+ style: { flexDirection: "column" },
3325
3226
  ...props.appShellMainProps,
3326
3227
  children: props.children ?? /* @__PURE__ */ jsx(NestedView, {})
3327
3228
  }),
@@ -3335,8 +3236,377 @@ const AdminShell = (props) => {
3335
3236
  };
3336
3237
  var AdminShell_default = AdminShell;
3337
3238
 
3239
+ //#endregion
3240
+ //#region ../../src/core/components/table/DataTableFilters.tsx
3241
+ const DataTableFilters = ({ schema, form, typeFormProps, filterVisibility }) => {
3242
+ const visibleSchema = useMemo(() => {
3243
+ const visibleKeys = Object.keys(schema.properties).filter((key) => filterVisibility[key] !== false);
3244
+ if (visibleKeys.length === 0) return null;
3245
+ const visibleProps = visibleKeys.reduce((acc, key) => {
3246
+ acc[key] = schema.properties[key];
3247
+ return acc;
3248
+ }, {});
3249
+ return t.object(visibleProps);
3250
+ }, [schema, filterVisibility]);
3251
+ if (!visibleSchema) return null;
3252
+ return /* @__PURE__ */ jsx(Flex$1, {
3253
+ w: "100%",
3254
+ p: "xs",
3255
+ bg: ui.colors.surface,
3256
+ style: { borderBottom: "1px solid var(--alepha-border)" },
3257
+ children: /* @__PURE__ */ jsx(TypeForm_default, {
3258
+ ...typeFormProps,
3259
+ skipSubmitButton: true,
3260
+ fill: true,
3261
+ form,
3262
+ schema: visibleSchema
3263
+ })
3264
+ });
3265
+ };
3266
+ var DataTableFilters_default = DataTableFilters;
3267
+
3268
+ //#endregion
3269
+ //#region ../../src/core/components/table/DataTablePagination.tsx
3270
+ const DataTablePagination = ({ page, size, totalPages, onPageChange, onSizeChange }) => {
3271
+ return /* @__PURE__ */ jsxs(Flex$1, {
3272
+ align: "center",
3273
+ justify: "end",
3274
+ gap: "md",
3275
+ p: "xs",
3276
+ style: { borderTop: "1px solid var(--alepha-border)" },
3277
+ children: [/* @__PURE__ */ jsx(Flex$1, { children: /* @__PURE__ */ jsx(Select, {
3278
+ w: 96,
3279
+ variant: "default",
3280
+ value: size,
3281
+ onChange: (value) => {
3282
+ if (value) onSizeChange(Number(value));
3283
+ },
3284
+ data: [
3285
+ {
3286
+ value: "5",
3287
+ label: "5"
3288
+ },
3289
+ {
3290
+ value: "10",
3291
+ label: "10"
3292
+ },
3293
+ {
3294
+ value: "25",
3295
+ label: "25"
3296
+ },
3297
+ {
3298
+ value: "50",
3299
+ label: "50"
3300
+ },
3301
+ {
3302
+ value: "100",
3303
+ label: "100"
3304
+ }
3305
+ ]
3306
+ }) }), /* @__PURE__ */ jsx(Flex$1, { children: /* @__PURE__ */ jsx(Pagination, {
3307
+ withEdges: true,
3308
+ total: totalPages,
3309
+ value: page,
3310
+ onChange: onPageChange
3311
+ }) })]
3312
+ });
3313
+ };
3314
+ var DataTablePagination_default = DataTablePagination;
3315
+
3316
+ //#endregion
3317
+ //#region ../../src/core/components/table/ColumnPicker.tsx
3318
+ const ColumnPicker = ({ columns, visibility, onVisibilityChange }) => {
3319
+ const [opened, setOpened] = useState(false);
3320
+ const columnEntries = Object.entries(columns);
3321
+ const handleShowAll = () => {
3322
+ onVisibilityChange(columnEntries.reduce((acc, [key]) => ({
3323
+ ...acc,
3324
+ [key]: true
3325
+ }), {}));
3326
+ };
3327
+ const handleHideAll = () => {
3328
+ onVisibilityChange(columnEntries.reduce((acc, [key]) => ({
3329
+ ...acc,
3330
+ [key]: false
3331
+ }), {}));
3332
+ };
3333
+ const handleToggle = (key, checked) => {
3334
+ onVisibilityChange({
3335
+ ...visibility,
3336
+ [key]: checked
3337
+ });
3338
+ };
3339
+ const visibleCount = columnEntries.filter(([key]) => visibility[key] !== false).length;
3340
+ return /* @__PURE__ */ jsxs(Popover, {
3341
+ width: 280,
3342
+ position: "bottom-start",
3343
+ shadow: "md",
3344
+ opened,
3345
+ onChange: setOpened,
3346
+ closeOnClickOutside: true,
3347
+ closeOnEscape: true,
3348
+ transitionProps: {
3349
+ transition: "fade-up",
3350
+ duration: 200,
3351
+ timingFunction: "ease"
3352
+ },
3353
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionButton_default, {
3354
+ variant: "subtle",
3355
+ icon: IconColumns
3356
+ }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3357
+ bg: "transparent",
3358
+ p: "xs",
3359
+ bd: `1px solid ${ui.colors.border}`,
3360
+ style: { backdropFilter: "blur(20px)" },
3361
+ children: /* @__PURE__ */ jsxs(Stack, {
3362
+ gap: "xs",
3363
+ bg: ui.colors.surface,
3364
+ p: "sm",
3365
+ bdrs: "sm",
3366
+ children: [/* @__PURE__ */ jsxs(Group, {
3367
+ justify: "space-between",
3368
+ children: [/* @__PURE__ */ jsxs(Text$1, {
3369
+ size: "sm",
3370
+ fw: 500,
3371
+ children: [
3372
+ "Columns (",
3373
+ visibleCount,
3374
+ "/",
3375
+ columnEntries.length,
3376
+ ")"
3377
+ ]
3378
+ }), /* @__PURE__ */ jsxs(Group, {
3379
+ gap: 4,
3380
+ children: [/* @__PURE__ */ jsx(Button, {
3381
+ size: "compact-xs",
3382
+ variant: "subtle",
3383
+ onClick: handleShowAll,
3384
+ children: "All"
3385
+ }), /* @__PURE__ */ jsx(Button, {
3386
+ size: "compact-xs",
3387
+ variant: "subtle",
3388
+ onClick: handleHideAll,
3389
+ children: "None"
3390
+ })]
3391
+ })]
3392
+ }), /* @__PURE__ */ jsx(ScrollArea.Autosize, {
3393
+ mah: 300,
3394
+ children: /* @__PURE__ */ jsx(Stack, {
3395
+ gap: 4,
3396
+ children: columnEntries.map(([key, col]) => /* @__PURE__ */ jsx(Checkbox, {
3397
+ label: col.label,
3398
+ checked: visibility[key] !== false,
3399
+ onChange: (e) => handleToggle(key, e.currentTarget.checked),
3400
+ size: "sm"
3401
+ }, key))
3402
+ })
3403
+ })]
3404
+ })
3405
+ })]
3406
+ });
3407
+ };
3408
+ var ColumnPicker_default = ColumnPicker;
3409
+
3410
+ //#endregion
3411
+ //#region ../../src/core/components/table/FilterPicker.tsx
3412
+ const getFieldLabel = (schema, key) => {
3413
+ const prop = schema.properties[key];
3414
+ if (prop && typeof prop === "object" && "title" in prop && prop.title) return prop.title;
3415
+ return key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
3416
+ };
3417
+ const FilterPicker = ({ schema, visibility, onVisibilityChange }) => {
3418
+ const [opened, setOpened] = useState(false);
3419
+ const filterKeys = Object.keys(schema.properties);
3420
+ const handleShowAll = () => {
3421
+ onVisibilityChange(filterKeys.reduce((acc, key) => ({
3422
+ ...acc,
3423
+ [key]: true
3424
+ }), {}));
3425
+ };
3426
+ const handleHideAll = () => {
3427
+ onVisibilityChange(filterKeys.reduce((acc, key) => ({
3428
+ ...acc,
3429
+ [key]: false
3430
+ }), {}));
3431
+ };
3432
+ const handleToggle = (key, checked) => {
3433
+ onVisibilityChange({
3434
+ ...visibility,
3435
+ [key]: checked
3436
+ });
3437
+ };
3438
+ const visibleCount = filterKeys.filter((key) => visibility[key] !== false).length;
3439
+ return /* @__PURE__ */ jsxs(Popover, {
3440
+ width: 280,
3441
+ position: "bottom-start",
3442
+ shadow: "md",
3443
+ opened,
3444
+ onChange: setOpened,
3445
+ closeOnClickOutside: true,
3446
+ closeOnEscape: true,
3447
+ transitionProps: {
3448
+ transition: "fade-up",
3449
+ duration: 200,
3450
+ timingFunction: "ease"
3451
+ },
3452
+ children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionButton_default, {
3453
+ variant: "subtle",
3454
+ icon: IconFilter
3455
+ }) }), /* @__PURE__ */ jsx(Popover.Dropdown, {
3456
+ bg: "transparent",
3457
+ p: "xs",
3458
+ bd: `1px solid ${ui.colors.border}`,
3459
+ style: { backdropFilter: "blur(20px)" },
3460
+ children: /* @__PURE__ */ jsxs(Stack, {
3461
+ gap: "xs",
3462
+ bg: ui.colors.surface,
3463
+ p: "sm",
3464
+ bdrs: "sm",
3465
+ children: [/* @__PURE__ */ jsxs(Group, {
3466
+ justify: "space-between",
3467
+ children: [/* @__PURE__ */ jsxs(Text$1, {
3468
+ size: "sm",
3469
+ fw: 500,
3470
+ children: [
3471
+ "Filters (",
3472
+ visibleCount,
3473
+ "/",
3474
+ filterKeys.length,
3475
+ ")"
3476
+ ]
3477
+ }), /* @__PURE__ */ jsxs(Group, {
3478
+ gap: 4,
3479
+ children: [/* @__PURE__ */ jsx(Button, {
3480
+ size: "compact-xs",
3481
+ variant: "subtle",
3482
+ onClick: handleShowAll,
3483
+ children: "All"
3484
+ }), /* @__PURE__ */ jsx(Button, {
3485
+ size: "compact-xs",
3486
+ variant: "subtle",
3487
+ onClick: handleHideAll,
3488
+ children: "None"
3489
+ })]
3490
+ })]
3491
+ }), /* @__PURE__ */ jsx(ScrollArea.Autosize, {
3492
+ mah: 300,
3493
+ children: /* @__PURE__ */ jsx(Stack, {
3494
+ gap: 4,
3495
+ children: filterKeys.map((key) => /* @__PURE__ */ jsx(Checkbox, {
3496
+ label: getFieldLabel(schema, key),
3497
+ checked: visibility[key] !== false,
3498
+ onChange: (e) => handleToggle(key, e.currentTarget.checked),
3499
+ size: "sm"
3500
+ }, key))
3501
+ })
3502
+ })]
3503
+ })
3504
+ })]
3505
+ });
3506
+ };
3507
+ var FilterPicker_default = FilterPicker;
3508
+
3509
+ //#endregion
3510
+ //#region ../../src/core/components/table/DataTableToolbar.tsx
3511
+ const DataTableToolbar = ({ columns, filters, columnVisibility, filterVisibility, onColumnVisibilityChange, onFilterVisibilityChange, actions, onRefresh, selectedItems = [], checkboxActions, onClearSelection }) => {
3512
+ const hasSelection = selectedItems.length > 0;
3513
+ const handleCheckboxAction = async (action) => {
3514
+ const ctx = {
3515
+ selectedItems,
3516
+ clearSelection: onClearSelection || (() => {})
3517
+ };
3518
+ await action.onClick(ctx);
3519
+ };
3520
+ return /* @__PURE__ */ jsxs(Flex$1, {
3521
+ p: "xs",
3522
+ style: { borderBottom: "1px solid var(--alepha-border)" },
3523
+ children: [
3524
+ /* @__PURE__ */ jsxs(Flex$1, {
3525
+ gap: 4,
3526
+ align: "center",
3527
+ children: [
3528
+ filters && /* @__PURE__ */ jsx(FilterPicker_default, {
3529
+ schema: filters,
3530
+ visibility: filterVisibility,
3531
+ onVisibilityChange: onFilterVisibilityChange
3532
+ }),
3533
+ /* @__PURE__ */ jsx(ColumnPicker_default, {
3534
+ columns,
3535
+ visibility: columnVisibility,
3536
+ onVisibilityChange: onColumnVisibilityChange
3537
+ }),
3538
+ hasSelection && /* @__PURE__ */ jsxs(Fragment, { children: [
3539
+ /* @__PURE__ */ jsx(Divider, {
3540
+ orientation: "vertical",
3541
+ mx: "xs"
3542
+ }),
3543
+ /* @__PURE__ */ jsxs(Badge, {
3544
+ variant: "light",
3545
+ size: "lg",
3546
+ children: [selectedItems.length, " selected"]
3547
+ }),
3548
+ /* @__PURE__ */ jsx(ActionButton_default, {
3549
+ variant: "subtle",
3550
+ size: "compact-sm",
3551
+ icon: IconX,
3552
+ onClick: onClearSelection,
3553
+ children: "Clear"
3554
+ }),
3555
+ checkboxActions?.map((action, index) => /* @__PURE__ */ jsx(ActionButton_default, {
3556
+ variant: "light",
3557
+ size: "compact-sm",
3558
+ intent: action.intent,
3559
+ icon: action.icon && isComponentType(action.icon) ? action.icon : void 0,
3560
+ onClick: () => handleCheckboxAction(action),
3561
+ children: action.label
3562
+ }, index))
3563
+ ] })
3564
+ ]
3565
+ }),
3566
+ /* @__PURE__ */ jsx(Flex$1, { flex: 1 }),
3567
+ /* @__PURE__ */ jsxs(Flex$1, {
3568
+ gap: "xs",
3569
+ children: [actions?.map((props, index) => !isValidElement(props) ? /* @__PURE__ */ jsx(ActionButton_default, {
3570
+ ...props,
3571
+ children: props.label
3572
+ }, index) : props), /* @__PURE__ */ jsx(ActionButton_default, {
3573
+ icon: IconRefresh,
3574
+ onClick: onRefresh,
3575
+ children: "Refresh"
3576
+ })]
3577
+ })
3578
+ ]
3579
+ });
3580
+ };
3581
+ var DataTableToolbar_default = DataTableToolbar;
3582
+
3338
3583
  //#endregion
3339
3584
  //#region ../../src/core/components/table/DataTable.tsx
3585
+ const DEFAULT_VISIBLE_COLUMN_COUNT = 10;
3586
+ /**
3587
+ * Parse the sort string to get direction for a specific field.
3588
+ * Alepha convention: 'field' = ASC, '-field' = DESC
3589
+ */
3590
+ const getSortDirection = (sortString, field) => {
3591
+ if (!sortString) return null;
3592
+ const parts = sortString.split(",").map((s) => s.trim());
3593
+ for (const part of parts) {
3594
+ if (part === field) return "asc";
3595
+ if (part === `-${field}`) return "desc";
3596
+ }
3597
+ return null;
3598
+ };
3599
+ /**
3600
+ * Toggle sort for a field in the sort string.
3601
+ * Cycles: null -> asc -> desc -> null
3602
+ */
3603
+ const toggleSort = (sortString, field) => {
3604
+ const current = getSortDirection(sortString, field);
3605
+ const parts = (sortString || "").split(",").map((s) => s.trim()).filter((s) => s && s !== field && s !== `-${field}`);
3606
+ if (current === null) parts.unshift(field);
3607
+ else if (current === "asc") parts.unshift(`-${field}`);
3608
+ return parts.length > 0 ? parts.join(",") : void 0;
3609
+ };
3340
3610
  const DataTable = (props) => {
3341
3611
  const [items, setItems] = useState(typeof props.items === "function" ? { content: [] } : props.items);
3342
3612
  const defaultSize = props.infinityScroll ? 100 : props.defaultSize || 10;
@@ -3344,6 +3614,100 @@ const DataTable = (props) => {
3344
3614
  const [size, setSize] = useState(String(defaultSize));
3345
3615
  const [currentPage, setCurrentPage] = useState(0);
3346
3616
  const alepha = useInject(Alepha);
3617
+ const [columnVisibility, setColumnVisibility] = useState(() => {
3618
+ if (props.defaultColumnVisibility) return props.defaultColumnVisibility;
3619
+ const columnKeys = Object.keys(props.columns);
3620
+ const maxVisible = props.defaultVisibleColumnCount ?? DEFAULT_VISIBLE_COLUMN_COUNT;
3621
+ return columnKeys.reduce((acc, key, index) => ({
3622
+ ...acc,
3623
+ [key]: index < maxVisible
3624
+ }), {});
3625
+ });
3626
+ const [filterVisibility, setFilterVisibility] = useState(() => {
3627
+ if (props.defaultFilterVisibility) return props.defaultFilterVisibility;
3628
+ if (!props.filters?.properties) return {};
3629
+ return Object.keys(props.filters.properties).reduce((acc, key) => ({
3630
+ ...acc,
3631
+ [key]: true
3632
+ }), {});
3633
+ });
3634
+ const handleColumnVisibilityChange = (visibility) => {
3635
+ setColumnVisibility(visibility);
3636
+ props.onColumnVisibilityChange?.(visibility);
3637
+ };
3638
+ const handleFilterVisibilityChange = (visibility) => {
3639
+ setFilterVisibility(visibility);
3640
+ props.onFilterVisibilityChange?.(visibility);
3641
+ };
3642
+ const visibleColumns = useMemo(() => {
3643
+ return Object.entries(props.columns).filter(([key]) => columnVisibility[key] !== false);
3644
+ }, [props.columns, columnVisibility]);
3645
+ const [sortString, setSortString] = useState(void 0);
3646
+ const handleSortClick = (columnKey, sortKey) => {
3647
+ const newSort = toggleSort(sortString, sortKey || columnKey);
3648
+ setSortString(newSort);
3649
+ form.input.sort.set(newSort);
3650
+ form.input.page.set(0);
3651
+ };
3652
+ const [selectedKeys, setSelectedKeys] = useState(/* @__PURE__ */ new Set());
3653
+ const getItemKey = useCallback((item) => {
3654
+ if (props.getItemKey) return props.getItemKey(item);
3655
+ return JSON.stringify(item);
3656
+ }, [props.getItemKey]);
3657
+ const selectedItems = useMemo(() => {
3658
+ if (!props.withCheckbox) return [];
3659
+ return items.content.filter((item) => selectedKeys.has(getItemKey(item)));
3660
+ }, [
3661
+ items.content,
3662
+ selectedKeys,
3663
+ getItemKey,
3664
+ props.withCheckbox
3665
+ ]);
3666
+ const allSelected = useMemo(() => {
3667
+ if (items.content.length === 0) return false;
3668
+ return items.content.every((item) => selectedKeys.has(getItemKey(item)));
3669
+ }, [
3670
+ items.content,
3671
+ selectedKeys,
3672
+ getItemKey
3673
+ ]);
3674
+ const someSelected = useMemo(() => {
3675
+ if (items.content.length === 0) return false;
3676
+ const selectedCount = items.content.filter((item) => selectedKeys.has(getItemKey(item))).length;
3677
+ return selectedCount > 0 && selectedCount < items.content.length;
3678
+ }, [
3679
+ items.content,
3680
+ selectedKeys,
3681
+ getItemKey
3682
+ ]);
3683
+ const toggleItemSelection = useCallback((item) => {
3684
+ const key = getItemKey(item);
3685
+ setSelectedKeys((prev) => {
3686
+ const next = new Set(prev);
3687
+ if (next.has(key)) next.delete(key);
3688
+ else next.add(key);
3689
+ return next;
3690
+ });
3691
+ }, [getItemKey]);
3692
+ const toggleAllSelection = useCallback(() => {
3693
+ if (allSelected) setSelectedKeys((prev) => {
3694
+ const next = new Set(prev);
3695
+ for (const item of items.content) next.delete(getItemKey(item));
3696
+ return next;
3697
+ });
3698
+ else setSelectedKeys((prev) => {
3699
+ const next = new Set(prev);
3700
+ for (const item of items.content) next.add(getItemKey(item));
3701
+ return next;
3702
+ });
3703
+ }, [
3704
+ allSelected,
3705
+ items.content,
3706
+ getItemKey
3707
+ ]);
3708
+ const clearSelection = useCallback(() => {
3709
+ setSelectedKeys(/* @__PURE__ */ new Set());
3710
+ }, []);
3347
3711
  const form = useForm({
3348
3712
  schema: t.object({
3349
3713
  ...props.filters ? props.filters.properties : {},
@@ -3351,7 +3715,7 @@ const DataTable = (props) => {
3351
3715
  size: t.number({ default: defaultSize }),
3352
3716
  sort: t.optional(t.string())
3353
3717
  }),
3354
- handler: async (values, args) => {
3718
+ handler: async (values) => {
3355
3719
  if (typeof props.items === "function") {
3356
3720
  const response = await props.items(values, { items: items.content });
3357
3721
  if (props.infinityScroll && values.page > 0) setItems((prev) => ({
@@ -3416,100 +3780,118 @@ const DataTable = (props) => {
3416
3780
  currentPage,
3417
3781
  form
3418
3782
  ]);
3419
- const head = Object.entries(props.columns).map(([key, col]) => /* @__PURE__ */ jsx(Table.Th, {
3420
- style: { ...col.fit ? {
3421
- width: "1%",
3422
- whiteSpace: "nowrap"
3423
- } : {} },
3424
- children: /* @__PURE__ */ jsx(ActionButton_default, {
3425
- justify: "space-between",
3426
- radius: 0,
3427
- fullWidth: true,
3428
- size: "xs",
3429
- children: col.label
3783
+ const checkboxHeader = props.withCheckbox ? /* @__PURE__ */ jsx(Table.Th, {
3784
+ style: { width: 40 },
3785
+ children: /* @__PURE__ */ jsx(Checkbox, {
3786
+ checked: allSelected,
3787
+ indeterminate: someSelected,
3788
+ onChange: toggleAllSelection,
3789
+ "aria-label": "Select all"
3430
3790
  })
3431
- }, key));
3791
+ }) : null;
3792
+ const head = visibleColumns.map(([key, col]) => {
3793
+ const sortField = col.sortKey || key;
3794
+ const sortDir = col.sortable ? getSortDirection(sortString, sortField) : null;
3795
+ const headerContent = /* @__PURE__ */ jsxs(Flex$1, {
3796
+ align: "center",
3797
+ gap: 4,
3798
+ children: [/* @__PURE__ */ jsx(Text$1, {
3799
+ size: "xs",
3800
+ children: col.label
3801
+ }), col.sortable && /* @__PURE__ */ jsxs(Flex$1, {
3802
+ c: "dimmed",
3803
+ children: [
3804
+ sortDir === "asc" && /* @__PURE__ */ jsx(IconArrowUp, { size: ui.sizes.icon.sm }),
3805
+ sortDir === "desc" && /* @__PURE__ */ jsx(IconArrowDown, { size: ui.sizes.icon.sm }),
3806
+ sortDir === null && /* @__PURE__ */ jsx(IconArrowsSort, { size: ui.sizes.icon.sm })
3807
+ ]
3808
+ })]
3809
+ });
3810
+ return /* @__PURE__ */ jsx(Table.Th, {
3811
+ style: {
3812
+ ...col.fit ? {} : {},
3813
+ ...col.sortable ? { cursor: "pointer" } : {}
3814
+ },
3815
+ children: col.sortable ? /* @__PURE__ */ jsx(UnstyledButton, {
3816
+ onClick: () => handleSortClick(key, col.sortKey),
3817
+ children: headerContent
3818
+ }) : headerContent
3819
+ }, key);
3820
+ });
3432
3821
  const rows = items.content.map((item, index) => {
3433
3822
  const trProps = props.tableTrProps ? props.tableTrProps(item) : {};
3434
- return /* @__PURE__ */ jsx(Table.Tr, {
3823
+ const itemKey = getItemKey(item);
3824
+ const isSelected = selectedKeys.has(itemKey);
3825
+ return /* @__PURE__ */ jsxs(Table.Tr, {
3435
3826
  ...trProps,
3436
- children: Object.entries(props.columns).map(([key, col]) => /* @__PURE__ */ jsx(Table.Td, { children: col.value(item, {
3827
+ children: [props.withCheckbox && /* @__PURE__ */ jsx(Table.Td, {
3828
+ style: { width: 40 },
3829
+ children: /* @__PURE__ */ jsx(Checkbox, {
3830
+ checked: isSelected,
3831
+ onChange: () => toggleItemSelection(item),
3832
+ "aria-label": "Select row"
3833
+ })
3834
+ }), visibleColumns.map(([key, col]) => /* @__PURE__ */ jsx(Table.Td, { children: col.value(item, {
3437
3835
  index,
3438
3836
  form,
3439
3837
  alepha
3440
- }) }, key))
3441
- }, JSON.stringify(item));
3838
+ }) }, key))]
3839
+ }, itemKey);
3442
3840
  });
3443
- const schema = t.omit(form.options.schema, [
3444
- "page",
3445
- "size",
3446
- "sort"
3447
- ]);
3841
+ const filterSchema = useMemo(() => {
3842
+ if (!props.filters) return null;
3843
+ return t.omit(form.options.schema, [
3844
+ "page",
3845
+ "size",
3846
+ "sort"
3847
+ ]);
3848
+ }, [props.filters, form.options.schema]);
3448
3849
  return /* @__PURE__ */ jsxs(Flex$1, {
3449
- direction: "column",
3450
- gap: "sm",
3451
3850
  flex: 1,
3851
+ p: 0,
3852
+ bg: "var(--alepha-elevated)",
3853
+ bdrs: "sm",
3854
+ bd: "1px solid var(--alepha-border)",
3855
+ direction: "column",
3452
3856
  children: [
3453
- props.filters ? /* @__PURE__ */ jsx(Card, {
3454
- withBorder: true,
3455
- p: "lg",
3456
- bg: ui.colors.elevated,
3457
- children: /* @__PURE__ */ jsx(TypeForm_default, {
3458
- ...props.typeFormProps,
3459
- form,
3460
- schema
3461
- })
3462
- }) : null,
3857
+ /* @__PURE__ */ jsx(DataTableToolbar_default, {
3858
+ columns: props.columns,
3859
+ filters: props.filters,
3860
+ columnVisibility,
3861
+ filterVisibility,
3862
+ onColumnVisibilityChange: handleColumnVisibilityChange,
3863
+ onFilterVisibilityChange: handleFilterVisibilityChange,
3864
+ actions: props.actions,
3865
+ onRefresh: () => form.submit(),
3866
+ selectedItems,
3867
+ checkboxActions: props.checkboxActions,
3868
+ onClearSelection: clearSelection
3869
+ }),
3870
+ filterSchema && props.filters && /* @__PURE__ */ jsx(DataTableFilters_default, {
3871
+ schema: filterSchema,
3872
+ form,
3873
+ typeFormProps: props.typeFormProps,
3874
+ filterVisibility
3875
+ }),
3463
3876
  /* @__PURE__ */ jsx(Flex$1, {
3464
3877
  className: "overflow-auto",
3465
3878
  children: /* @__PURE__ */ jsxs(Table, {
3466
- striped: true,
3467
- withRowBorders: true,
3468
3879
  withColumnBorders: true,
3469
- withTableBorder: true,
3470
- stripedColor: "",
3880
+ withRowBorders: true,
3471
3881
  ...props.tableProps,
3472
- children: [/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsx(Table.Tr, { children: head }) }), /* @__PURE__ */ jsx(Table.Tbody, { children: rows })]
3882
+ children: [/* @__PURE__ */ jsx(Table.Thead, { children: /* @__PURE__ */ jsxs(Table.Tr, { children: [checkboxHeader, head] }) }), /* @__PURE__ */ jsx(Table.Tbody, { children: rows })]
3473
3883
  })
3474
3884
  }),
3475
- !props.infinityScroll && /* @__PURE__ */ jsxs(Flex$1, {
3476
- justify: "space-between",
3477
- align: "center",
3478
- children: [/* @__PURE__ */ jsx(Pagination, {
3479
- withEdges: true,
3480
- total: items.page?.totalPages ?? 1,
3481
- value: page,
3482
- onChange: (value) => {
3483
- form.input.page.set(value - 1);
3484
- }
3485
- }), /* @__PURE__ */ jsx(Flex$1, { children: /* @__PURE__ */ jsx(Select, {
3486
- value: size,
3487
- onChange: (value) => {
3488
- form.input.size.set(Number(value));
3489
- },
3490
- data: [
3491
- {
3492
- value: "5",
3493
- label: "5"
3494
- },
3495
- {
3496
- value: "10",
3497
- label: "10"
3498
- },
3499
- {
3500
- value: "25",
3501
- label: "25"
3502
- },
3503
- {
3504
- value: "50",
3505
- label: "50"
3506
- },
3507
- {
3508
- value: "100",
3509
- label: "100"
3510
- }
3511
- ]
3512
- }) })]
3885
+ !props.infinityScroll && /* @__PURE__ */ jsx(DataTablePagination_default, {
3886
+ page,
3887
+ size,
3888
+ totalPages: items.page?.totalPages ?? 1,
3889
+ onPageChange: (value) => {
3890
+ form.input.page.set(value - 1);
3891
+ },
3892
+ onSizeChange: (value) => {
3893
+ form.input.size.set(value);
3894
+ }
3513
3895
  })
3514
3896
  ]
3515
3897
  });
@@ -3559,5 +3941,5 @@ const AlephaUI = $module({
3559
3941
  });
3560
3942
 
3561
3943
  //#endregion
3562
- export { ActionButton_default as ActionButton, AdminShell_default as AdminShell, AlephaMantineProvider_default as AlephaMantineProvider, AlephaUI, AlertDialog_default as AlertDialog, AppBar_default as AppBar, BurgerButton_default as BurgerButton, ClipboardButton_default as ClipboardButton, ConfirmDialog_default as ConfirmDialog, Control_default as Control, ControlDate_default as ControlDate, ControlQueryBuilder_default as ControlQueryBuilder, ControlSelect_default as ControlSelect, DarkModeButton_default as DarkModeButton, DataTable_default as DataTable, DialogService, Flex, JsonViewer_default as JsonViewer, LanguageButton_default as LanguageButton, OPERATOR_INFO, Omnibar_default as Omnibar, OmnibarButton_default as OmnibarButton, PromptDialog_default as PromptDialog, RootRouter, Sidebar, Text, ThemeButton_default as ThemeButton, ThemeProvider, ToastService, TypeForm_default as TypeForm, capitalize, extractSchemaFields, getDefaultIcon, getOperatorsForField, mantineThemeAtom, prettyName, ui, useDialog, useToast };
3944
+ export { ActionButton_default as ActionButton, AdminShell_default as AdminShell, AlephaMantineProvider_default as AlephaMantineProvider, AlephaUI, AlertDialog_default as AlertDialog, AppBar_default as AppBar, BurgerButton_default as BurgerButton, ClipboardButton_default as ClipboardButton, ConfirmDialog_default as ConfirmDialog, Control_default as Control, ControlArray_default as ControlArray, ControlDate_default as ControlDate, ControlNumber_default as ControlNumber, ControlObject_default as ControlObject, ControlQueryBuilder_default as ControlQueryBuilder, ControlSelect_default as ControlSelect, DarkModeButton_default as DarkModeButton, DataTable_default as DataTable, DialogService, Flex, JsonViewer_default as JsonViewer, LanguageButton_default as LanguageButton, OPERATOR_INFO, Omnibar_default as Omnibar, OmnibarButton_default as OmnibarButton, PromptDialog_default as PromptDialog, RootRouter, Sidebar, Text, ThemeButton_default as ThemeButton, ThemeProvider, ToastService, TypeForm_default as TypeForm, alephaThemeAtom, alephaThemeListAtom, capitalize, defaultTheme, extractSchemaFields, getDefaultIcon, getOperatorsForField, midnightTheme, prettyName, ui, useDialog, useToast };
3563
3945
  //# sourceMappingURL=index.js.map