@alepha/ui 0.11.3 → 0.11.4

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 (44) hide show
  1. package/dist/AlephaMantineProvider-CtIV-8MV.mjs +150 -0
  2. package/dist/AlephaMantineProvider-CtIV-8MV.mjs.map +1 -0
  3. package/dist/AlephaMantineProvider-D-vu9aCD.mjs +3 -0
  4. package/dist/{index.d.ts → index.d.mts} +290 -226
  5. package/dist/index.d.mts.map +1 -0
  6. package/dist/{index.js → index.mjs} +651 -730
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +14 -12
  9. package/src/RootRouter.ts +1 -1
  10. package/src/components/buttons/ActionButton.tsx +542 -0
  11. package/src/components/buttons/BurgerButton.tsx +20 -0
  12. package/src/components/{DarkModeButton.tsx → buttons/DarkModeButton.tsx} +27 -14
  13. package/src/components/buttons/LanguageButton.tsx +28 -0
  14. package/src/components/buttons/OmnibarButton.tsx +32 -0
  15. package/src/components/buttons/ToggleSidebarButton.tsx +28 -0
  16. package/src/components/dialogs/AlertDialog.tsx +10 -10
  17. package/src/components/dialogs/ConfirmDialog.tsx +18 -18
  18. package/src/components/dialogs/PromptDialog.tsx +5 -3
  19. package/src/components/{Control.tsx → form/Control.tsx} +6 -3
  20. package/src/components/{ControlDate.tsx → form/ControlDate.tsx} +4 -1
  21. package/src/components/{ControlSelect.tsx → form/ControlSelect.tsx} +4 -1
  22. package/src/components/{TypeForm.tsx → form/TypeForm.tsx} +8 -6
  23. package/src/components/layout/AdminShell.tsx +97 -0
  24. package/src/components/{AlephaMantineProvider.tsx → layout/AlephaMantineProvider.tsx} +30 -10
  25. package/src/components/layout/AppBar.tsx +133 -0
  26. package/src/components/layout/Omnibar.tsx +43 -0
  27. package/src/components/layout/Sidebar.tsx +410 -0
  28. package/src/components/table/DataTable.tsx +63 -0
  29. package/src/constants/ui.ts +8 -0
  30. package/src/index.ts +89 -24
  31. package/src/services/DialogService.tsx +13 -32
  32. package/src/services/ToastService.tsx +16 -4
  33. package/src/utils/parseInput.ts +1 -1
  34. package/dist/AlephaMantineProvider-DDbIijPF.js +0 -96
  35. package/dist/AlephaMantineProvider-DDbIijPF.js.map +0 -1
  36. package/dist/AlephaMantineProvider-pOu8hOzK.js +0 -3
  37. package/dist/index.d.ts.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/src/components/Action.tsx +0 -345
  40. package/src/components/DataTable.css +0 -199
  41. package/src/components/DataTable.tsx +0 -724
  42. package/src/components/Omnibar.tsx +0 -77
  43. package/src/components/Sidebar.css +0 -217
  44. package/src/components/Sidebar.tsx +0 -255
@@ -1,345 +0,0 @@
1
- import {
2
- type RouterGoOptions,
3
- type UseActiveOptions,
4
- useActive,
5
- useAlepha,
6
- useRouter,
7
- } from "@alepha/react";
8
- import { type FormModel, useFormState } from "@alepha/react-form";
9
- import {
10
- Button,
11
- type ButtonProps,
12
- Flex,
13
- Menu,
14
- Tooltip,
15
- type TooltipProps,
16
- } from "@mantine/core";
17
- import { IconChevronRight } from "@tabler/icons-react";
18
- import { type ReactNode, useState } from "react";
19
-
20
- export interface ActionMenuItem {
21
- /**
22
- * Menu item type
23
- */
24
- type?: "item" | "divider" | "label";
25
-
26
- /**
27
- * Label text for the menu item
28
- */
29
- label?: string;
30
-
31
- /**
32
- * Icon element to display before the label
33
- */
34
- icon?: ReactNode;
35
-
36
- /**
37
- * Click handler for menu items
38
- */
39
- onClick?: () => void;
40
-
41
- /**
42
- * Color for the menu item (e.g., "red" for danger actions)
43
- */
44
- color?: string;
45
-
46
- /**
47
- * Nested submenu items
48
- */
49
- children?: ActionMenuItem[];
50
- }
51
-
52
- export interface ActionMenuConfig {
53
- /**
54
- * Array of menu items to display
55
- */
56
- items: ActionMenuItem[];
57
-
58
- /**
59
- * Menu position relative to the button
60
- */
61
- position?:
62
- | "bottom"
63
- | "bottom-start"
64
- | "bottom-end"
65
- | "top"
66
- | "top-start"
67
- | "top-end"
68
- | "left"
69
- | "right";
70
-
71
- /**
72
- * Menu width
73
- */
74
- width?: number | string;
75
-
76
- /**
77
- * Menu shadow
78
- */
79
- shadow?: "xs" | "sm" | "md" | "lg" | "xl";
80
- }
81
-
82
- export interface ActionCommonProps extends ButtonProps {
83
- children?: ReactNode;
84
- textVisibleFrom?: "xs" | "sm" | "md" | "lg" | "xl";
85
-
86
- /**
87
- * Tooltip to display on hover. Can be a string for simple tooltips
88
- * or a TooltipProps object for advanced configuration.
89
- */
90
- tooltip?: string | TooltipProps;
91
-
92
- /**
93
- * Menu configuration. When provided, the action will display a dropdown menu.
94
- */
95
- menu?: ActionMenuConfig;
96
-
97
- /**
98
- * If set, a confirmation dialog will be shown before performing the action.
99
- * If `true`, a default title and message will be used.
100
- * If a string, it will be used as the message with a default title.
101
- * If an object, it can contain `title` and `message` properties to customize the dialog.
102
- */
103
- confirm?: boolean | string | { title?: string; message: string };
104
- }
105
-
106
- export type ActionProps = ActionCommonProps &
107
- (ActiveHrefProps | ActionClickProps | ActionSubmitProps | {});
108
-
109
- // ---------------------------------------------------------------------------------------------------------------------
110
-
111
- // Helper function to render menu items recursively
112
- const renderMenuItem = (item: ActionMenuItem, index: number): ReactNode => {
113
- // Render divider
114
- if (item.type === "divider") {
115
- return <Menu.Divider key={index} />;
116
- }
117
-
118
- // Render label
119
- if (item.type === "label") {
120
- return <Menu.Label key={index}>{item.label}</Menu.Label>;
121
- }
122
-
123
- // Render submenu if has children
124
- if (item.children && item.children.length > 0) {
125
- return (
126
- <Menu key={index} trigger="hover" position="right-start" offset={2}>
127
- <Menu.Target>
128
- <Menu.Item
129
- leftSection={item.icon}
130
- rightSection={<IconChevronRight size={14} />}
131
- >
132
- {item.label}
133
- </Menu.Item>
134
- </Menu.Target>
135
- <Menu.Dropdown>
136
- {item.children.map((child, childIndex) =>
137
- renderMenuItem(child, childIndex),
138
- )}
139
- </Menu.Dropdown>
140
- </Menu>
141
- );
142
- }
143
-
144
- // Render regular menu item
145
- return (
146
- <Menu.Item
147
- key={index}
148
- leftSection={item.icon}
149
- onClick={item.onClick}
150
- color={item.color}
151
- >
152
- {item.label}
153
- </Menu.Item>
154
- );
155
- };
156
-
157
- const Action = (_props: ActionProps) => {
158
- const props = { variant: "subtle", ..._props };
159
- const { tooltip, menu, ...restProps } = props;
160
-
161
- if (props.leftSection && !props.children) {
162
- restProps.className ??= "mantine-Action-iconOnly";
163
- restProps.p ??= "xs";
164
- }
165
-
166
- if (props.textVisibleFrom) {
167
- const { children, textVisibleFrom, leftSection, ...rest } = restProps;
168
- return (
169
- <>
170
- <Flex w={"100%"} visibleFrom={textVisibleFrom}>
171
- <Action
172
- flex={1}
173
- {...rest}
174
- leftSection={leftSection}
175
- tooltip={tooltip}
176
- menu={menu}
177
- >
178
- {children}
179
- </Action>
180
- </Flex>
181
- <Flex w={"100%"} hiddenFrom={textVisibleFrom}>
182
- <Action px={"xs"} {...rest} tooltip={tooltip} menu={menu}>
183
- {leftSection}
184
- </Action>
185
- </Flex>
186
- </>
187
- );
188
- }
189
-
190
- const renderAction = () => {
191
- if ("href" in restProps && restProps.href) {
192
- return (
193
- <ActionHref {...restProps} href={restProps.href}>
194
- {restProps.children}
195
- </ActionHref>
196
- );
197
- }
198
-
199
- if ("onClick" in restProps && restProps.onClick) {
200
- return (
201
- <ActionClick {...restProps} onClick={restProps.onClick}>
202
- {restProps.children}
203
- </ActionClick>
204
- );
205
- }
206
-
207
- if ("form" in restProps && restProps.form) {
208
- return (
209
- <ActionSubmit {...restProps} form={restProps.form}>
210
- {restProps.children}
211
- </ActionSubmit>
212
- );
213
- }
214
-
215
- return <Button {...(restProps as any)}>{restProps.children}</Button>;
216
- };
217
-
218
- let actionElement = renderAction();
219
-
220
- // Wrap with Menu if provided
221
- if (menu) {
222
- actionElement = (
223
- <Menu
224
- position={menu.position || "bottom-start"}
225
- width={menu.width || 200}
226
- shadow={menu.shadow || "md"}
227
- >
228
- <Menu.Target>{actionElement}</Menu.Target>
229
- <Menu.Dropdown>
230
- {menu.items.map((item, index) => renderMenuItem(item, index))}
231
- </Menu.Dropdown>
232
- </Menu>
233
- );
234
- }
235
-
236
- // Wrap with Tooltip if provided
237
- if (tooltip) {
238
- const tooltipProps: TooltipProps =
239
- typeof tooltip === "string"
240
- ? { label: tooltip, children: actionElement }
241
- : { ...tooltip, children: actionElement };
242
-
243
- return <Tooltip {...tooltipProps} />;
244
- }
245
-
246
- return actionElement;
247
- };
248
-
249
- export default Action;
250
-
251
- // ---------------------------------------------------------------------------------------------------------------------
252
-
253
- export interface ActionSubmitProps extends ButtonProps {
254
- form: FormModel<any>;
255
- }
256
-
257
- /**
258
- * Action button that submits a form with loading and disabled state handling.
259
- */
260
- const ActionSubmit = (props: ActionSubmitProps) => {
261
- const { form, ...buttonProps } = props;
262
- const state = useFormState(form);
263
- return (
264
- <Button
265
- {...buttonProps}
266
- loading={state.loading}
267
- disabled={state.loading}
268
- type={"submit"}
269
- >
270
- {props.children}
271
- </Button>
272
- );
273
- };
274
-
275
- // ---------------------------------------------------------------------------------------------------------------------
276
-
277
- export interface ActionClickProps extends ButtonProps {
278
- onClick: (e: any) => any;
279
- }
280
-
281
- /**
282
- * Basic action button that handles click events with loading and error handling.
283
- */
284
- const ActionClick = (props: ActionClickProps) => {
285
- const [pending, setPending] = useState(false);
286
- const alepha = useAlepha();
287
-
288
- const onClick = async (e: any) => {
289
- setPending(true);
290
- try {
291
- await props.onClick(e);
292
- } catch (e) {
293
- console.error(e);
294
- await alepha.events.emit("form:submit:error", {
295
- id: "action",
296
- error: e as Error,
297
- });
298
- } finally {
299
- setPending(false);
300
- }
301
- };
302
-
303
- return (
304
- <Button
305
- {...props}
306
- disabled={pending || props.disabled}
307
- loading={pending}
308
- onClick={onClick}
309
- >
310
- {props.children}
311
- </Button>
312
- );
313
- };
314
-
315
- // ---------------------------------------------------------------------------------------------------------------------
316
-
317
- export interface ActiveHrefProps extends ButtonProps {
318
- href: string;
319
- active?: Partial<UseActiveOptions> | false;
320
- routerGoOptions?: RouterGoOptions;
321
- }
322
-
323
- /**
324
- * Action for navigation with active state support.
325
- */
326
- const ActionHref = (props: ActiveHrefProps) => {
327
- const { active: options, routerGoOptions, ...buttonProps } = props;
328
- const router = useRouter();
329
- const { isPending, isActive } = useActive(
330
- options ? { href: props.href, ...options } : { href: props.href },
331
- );
332
- const anchorProps = router.anchor(props.href, routerGoOptions);
333
-
334
- return (
335
- <Button
336
- component={"a"}
337
- loading={isPending}
338
- {...anchorProps}
339
- {...buttonProps}
340
- variant={isActive && options !== false ? "filled" : "subtle"}
341
- >
342
- {props.children}
343
- </Button>
344
- );
345
- };
@@ -1,199 +0,0 @@
1
- /* ------------------------------------------------------------------------------------------------------------------ */
2
- /* DataTable Component Styles */
3
- /* ------------------------------------------------------------------------------------------------------------------ */
4
-
5
- .alepha-datatable-container {
6
- display: flex;
7
- flex-direction: column;
8
- height: 100%;
9
- }
10
-
11
- .alepha-datatable-toolbar {
12
- border-radius: var(--mantine-radius-md);
13
- background: var(--mantine-color-body);
14
- }
15
-
16
- .alepha-datatable-search-input {
17
- min-width: 200px;
18
- max-width: 300px;
19
- }
20
-
21
- .alepha-datatable-page-size-select {
22
- width: 120px;
23
- }
24
-
25
- .alepha-datatable-table {
26
- position: relative;
27
- }
28
-
29
- .alepha-datatable-th {
30
- font-weight: 600;
31
- white-space: nowrap;
32
- user-select: none;
33
- transition: background-color 0.2s ease;
34
- }
35
-
36
- .alepha-datatable-th:hover {
37
- background-color: var(--mantine-color-gray-0);
38
- }
39
-
40
- [data-mantine-color-scheme="dark"] .alepha-datatable-th:hover {
41
- background-color: var(--mantine-color-dark-6);
42
- }
43
-
44
- .alepha-datatable-tr {
45
- transition: background-color 0.15s ease;
46
- }
47
-
48
- .alepha-datatable-tr.alepha-datatable-selected {
49
- background-color: var(--mantine-color-blue-0) !important;
50
- }
51
-
52
- [data-mantine-color-scheme="dark"]
53
- .alepha-datatable-tr.alepha-datatable-selected {
54
- background-color: rgba(34, 139, 230, 0.1) !important;
55
- }
56
-
57
- .alepha-datatable-checkbox-column {
58
- width: 40px;
59
- text-align: center;
60
- }
61
-
62
- .alepha-datatable-actions-column {
63
- width: 100px;
64
- text-align: center;
65
- }
66
-
67
- .alepha-datatable-sort-icon {
68
- color: var(--mantine-color-blue-6);
69
- transition: transform 0.2s ease;
70
- }
71
-
72
- .alepha-datatable-sort-icon-inactive {
73
- color: var(--mantine-color-gray-5);
74
- opacity: 0;
75
- transition: opacity 0.2s ease;
76
- }
77
-
78
- .alepha-datatable-th:hover .alepha-datatable-sort-icon-inactive {
79
- opacity: 0.5;
80
- }
81
-
82
- /* Loading state */
83
- .alepha-datatable-loading {
84
- position: relative;
85
- pointer-events: none;
86
- opacity: 0.5;
87
- }
88
-
89
- .alepha-datatable-loading-overlay {
90
- position: absolute;
91
- top: 0;
92
- left: 0;
93
- right: 0;
94
- bottom: 0;
95
- display: flex;
96
- align-items: center;
97
- justify-content: center;
98
- background: rgba(255, 255, 255, 0.8);
99
- z-index: 10;
100
- }
101
-
102
- [data-mantine-color-scheme="dark"] .alepha-datatable-loading-overlay {
103
- background: rgba(26, 27, 30, 0.8);
104
- }
105
-
106
- /* Sticky header styles */
107
- .alepha-datatable-sticky-header {
108
- position: sticky;
109
- top: 0;
110
- z-index: 5;
111
- background: var(--mantine-color-body);
112
- }
113
-
114
- /* Zebra striping adjustments */
115
- .alepha-datatable-striped tbody tr:nth-of-type(odd) {
116
- background-color: var(--mantine-color-gray-0);
117
- }
118
-
119
- [data-mantine-color-scheme="dark"]
120
- .alepha-datatable-striped
121
- tbody
122
- tr:nth-of-type(odd) {
123
- background-color: var(--mantine-color-dark-7);
124
- }
125
-
126
- /* Empty state */
127
- .alepha-datatable-empty-state {
128
- padding: 3rem 1rem;
129
- text-align: center;
130
- }
131
-
132
- .alepha-datatable-empty-icon {
133
- color: var(--mantine-color-gray-5);
134
- margin-bottom: 1rem;
135
- }
136
-
137
- /* Footer */
138
- .alepha-datatable-footer {
139
- border-top: 1px solid var(--mantine-color-gray-2);
140
- padding-top: var(--mantine-spacing-sm);
141
- margin-top: var(--mantine-spacing-xs);
142
- }
143
-
144
- [data-mantine-color-scheme="dark"] .alepha-datatable-footer {
145
- border-top-color: var(--mantine-color-dark-5);
146
- }
147
-
148
- /* Custom scrollbar for better UX */
149
- .alepha-datatable-container ::-webkit-scrollbar {
150
- width: 8px;
151
- height: 8px;
152
- }
153
-
154
- .alepha-datatable-container ::-webkit-scrollbar-track {
155
- background: var(--mantine-color-gray-1);
156
- border-radius: 4px;
157
- }
158
-
159
- [data-mantine-color-scheme="dark"]
160
- .alepha-datatable-container
161
- ::-webkit-scrollbar-track {
162
- background: var(--mantine-color-dark-6);
163
- }
164
-
165
- .alepha-datatable-container ::-webkit-scrollbar-thumb {
166
- background: var(--mantine-color-gray-4);
167
- border-radius: 4px;
168
- }
169
-
170
- [data-mantine-color-scheme="dark"]
171
- .alepha-datatable-container
172
- ::-webkit-scrollbar-thumb {
173
- background: var(--mantine-color-dark-4);
174
- }
175
-
176
- .alepha-datatable-container ::-webkit-scrollbar-thumb:hover {
177
- background: var(--mantine-color-gray-5);
178
- }
179
-
180
- [data-mantine-color-scheme="dark"]
181
- .alepha-datatable-container
182
- ::-webkit-scrollbar-thumb:hover {
183
- background: var(--mantine-color-dark-3);
184
- }
185
-
186
- /* Responsive */
187
- @media (max-width: 768px) {
188
- .alepha-datatable-toolbar {
189
- padding: var(--mantine-spacing-sm);
190
- }
191
-
192
- .alepha-datatable-search-input {
193
- min-width: 150px;
194
- }
195
-
196
- .alepha-datatable-page-size-select {
197
- width: 100px;
198
- }
199
- }