@alepha/ui 0.13.5 → 0.13.7
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.
- package/dist/admin/AdminAudits-CwvH8e8c.js +215 -0
- package/dist/admin/AdminAudits-CwvH8e8c.js.map +1 -0
- package/dist/admin/AdminAudits-Dv8Vk_6r.js +3 -0
- package/dist/admin/AdminFiles-5CPA3lQk.js +3 -0
- package/dist/admin/{AdminFiles-B_jfB_Py.js → AdminFiles-C_w1tb_x.js} +4 -3
- package/dist/admin/AdminFiles-C_w1tb_x.js.map +1 -0
- package/dist/admin/AdminLayout-BnSmtA4x.js +3 -0
- package/dist/admin/AdminLayout-XiSivwWH.js +39 -0
- package/dist/admin/AdminLayout-XiSivwWH.js.map +1 -0
- package/dist/admin/AdminNotifications-DLjmZWtf.js +3 -0
- package/dist/admin/{AdminNotifications-BFEjqpqx.js → AdminNotifications-DuYy74AN.js} +3 -3
- package/dist/admin/AdminNotifications-DuYy74AN.js.map +1 -0
- package/dist/admin/AdminParameters-DYg48Jwe.js +3 -0
- package/dist/admin/AdminParameters-YagqWTG3.js +575 -0
- package/dist/admin/AdminParameters-YagqWTG3.js.map +1 -0
- package/dist/admin/{AdminSessions-D7DESfWK.js → AdminSessions-BCjgJ-93.js} +4 -4
- package/dist/admin/AdminSessions-BCjgJ-93.js.map +1 -0
- package/dist/admin/AdminSessions-DEh2uN-4.js +3 -0
- package/dist/admin/AdminUserAudits-B_PUXCKC.js +177 -0
- package/dist/admin/AdminUserAudits-B_PUXCKC.js.map +1 -0
- package/dist/admin/AdminUserAudits-D7cTcElL.js +3 -0
- package/dist/admin/{AdminUserCreate-Bhxsn92l.js → AdminUserCreate-DzfRbGZ4.js} +4 -4
- package/dist/admin/AdminUserCreate-DzfRbGZ4.js.map +1 -0
- package/dist/admin/{AdminUserCreate-CYI_xW5T.js → AdminUserCreate-oUA1KDIl.js} +1 -1
- package/dist/admin/{AdminUserDetails-C2y1Ig4n.js → AdminUserDetails-DeTrJm-t.js} +5 -5
- package/dist/admin/AdminUserDetails-DeTrJm-t.js.map +1 -0
- package/dist/admin/{AdminUserDetails-Cmzx9HxH.js → AdminUserDetails-y1H5DW8Y.js} +1 -1
- package/dist/admin/{AdminUserLayout-sW6cjZL0.js → AdminUserLayout-CsfrrZkD.js} +4 -7
- package/dist/admin/AdminUserLayout-CsfrrZkD.js.map +1 -0
- package/dist/admin/{AdminUserLayout-DGSf612u.js → AdminUserLayout-Dejnz13m.js} +1 -1
- package/dist/admin/AdminUserSessions-Bbhcpz4k.js +3 -0
- package/dist/admin/{AdminUserSessions-CvN15wPe.js → AdminUserSessions-DO9H85O-.js} +4 -4
- package/dist/admin/AdminUserSessions-DO9H85O-.js.map +1 -0
- package/dist/admin/{AdminUserSettings-DvaaxgcV.js → AdminUserSettings-B3jA8g3p.js} +4 -4
- package/dist/admin/AdminUserSettings-B3jA8g3p.js.map +1 -0
- package/dist/admin/AdminUserSettings-CE0xpbQc.js +3 -0
- package/dist/admin/AdminUsers-CegGZDhW.js +3 -0
- package/dist/admin/{AdminUsers-BR3C-jrg.js → AdminUsers-ebbrJBT0.js} +13 -17
- package/dist/admin/AdminUsers-ebbrJBT0.js.map +1 -0
- package/dist/admin/index.d.ts +2044 -1044
- package/dist/admin/index.js +65 -62
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/AuthLayout-BAZJHzDG.js +23 -0
- package/dist/auth/AuthLayout-BAZJHzDG.js.map +1 -0
- package/dist/auth/{Login-7HlBjDeV.js → Login-CeNZZjrr.js} +80 -44
- package/dist/auth/Login-CeNZZjrr.js.map +1 -0
- package/dist/auth/Login-hQcu1nlu.js +4 -0
- package/dist/auth/Register-B6HBNVHS.js +4 -0
- package/dist/auth/{Register-CuQr3kgi.js → Register-s4ENeyiE.js} +131 -91
- package/dist/auth/Register-s4ENeyiE.js.map +1 -0
- package/dist/auth/ResetPassword-Cjd-W-Nu.js +3 -0
- package/dist/auth/ResetPassword-GLIFkJT7.js +278 -0
- package/dist/auth/ResetPassword-GLIFkJT7.js.map +1 -0
- package/dist/auth/index.d.ts +471 -426
- package/dist/auth/index.js +26 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +400 -130
- package/dist/core/index.js +1751 -1369
- package/dist/core/index.js.map +1 -1
- package/package.json +15 -11
- package/src/admin/AdminRouter.ts +70 -16
- package/src/admin/components/AdminLayout.tsx +41 -61
- package/src/admin/components/audits/AdminAudits.tsx +240 -0
- package/src/admin/components/{AdminFiles.tsx → files/AdminFiles.tsx} +1 -1
- package/src/admin/components/{AdminJobs.tsx → jobs/AdminJobs.tsx} +1 -1
- package/src/admin/components/parameters/AdminParameters.tsx +137 -0
- package/src/admin/components/parameters/ParameterDetails.tsx +228 -0
- package/src/admin/components/parameters/ParameterHistory.tsx +146 -0
- package/src/admin/components/parameters/ParameterTree.tsx +146 -0
- package/src/admin/components/parameters/types.ts +35 -0
- package/src/admin/components/{AdminSessions.tsx → sessions/AdminSessions.tsx} +1 -1
- package/src/admin/components/users/AdminUserAudits.tsx +183 -0
- package/src/admin/components/{AdminUserCreate.tsx → users/AdminUserCreate.tsx} +1 -1
- package/src/admin/components/{AdminUserLayout.tsx → users/AdminUserLayout.tsx} +1 -4
- package/src/admin/components/{AdminUserSettings.tsx → users/AdminUserSettings.tsx} +1 -1
- package/src/admin/components/{AdminUsers.tsx → users/AdminUsers.tsx} +10 -12
- package/src/admin/index.ts +24 -16
- package/src/auth/AuthRouter.ts +23 -17
- package/src/auth/components/AuthLayout.tsx +6 -3
- package/src/auth/components/Login.tsx +109 -47
- package/src/auth/components/Register.tsx +158 -94
- package/src/auth/components/ResetPassword.tsx +51 -5
- package/src/auth/components/buttons/UserButton.tsx +2 -0
- package/src/core/atoms/alephaThemeAtom.ts +13 -0
- package/src/core/atoms/alephaThemeListAtom.ts +10 -0
- package/src/core/atoms/themes/default.ts +6 -0
- package/src/core/{themes → atoms/themes}/midnight.ts +3 -5
- package/src/core/components/buttons/ActionButton.tsx +33 -26
- package/src/core/components/buttons/DarkModeButton.tsx +0 -1
- package/src/core/components/buttons/ThemeButton.tsx +10 -7
- package/src/core/components/buttons/ToggleSidebarButton.tsx +19 -16
- package/src/core/components/data/ErrorViewer.tsx +171 -0
- package/src/core/components/data/JsonViewer.tsx +147 -138
- package/src/core/components/form/Control.tsx +95 -18
- package/src/core/components/form/ControlArray.tsx +377 -0
- package/src/core/components/form/ControlObject.tsx +127 -0
- package/src/core/components/form/TypeForm.tsx +99 -37
- package/src/core/components/layout/AdminShell.tsx +14 -1
- package/src/core/components/layout/AlephaMantineProvider.tsx +7 -3
- package/src/core/components/layout/Omnibar.tsx +1 -1
- package/src/core/components/layout/Sidebar.tsx +47 -14
- package/src/core/components/table/ColumnPicker.tsx +126 -0
- package/src/core/components/table/DataTable.tsx +354 -181
- package/src/core/components/table/DataTableFilters.tsx +64 -0
- package/src/core/components/table/DataTablePagination.tsx +59 -0
- package/src/core/components/table/DataTableToolbar.tsx +126 -0
- package/src/core/components/table/FilterPicker.tsx +138 -0
- package/src/core/components/table/types.ts +199 -0
- package/src/core/helpers/isComponentType.ts +9 -0
- package/src/core/helpers/renderIcon.tsx +13 -0
- package/src/core/hooks/useTheme.ts +24 -18
- package/src/core/index.ts +24 -3
- package/src/core/interfaces/AlephaTheme.ts +8 -0
- package/src/core/providers/ThemeProvider.ts +44 -62
- package/src/core/services/DialogService.tsx +24 -0
- package/src/core/utils/parseInput.ts +2 -2
- package/styles.css +1 -1
- package/dist/admin/AdminFiles-B-0UcHVV.js +0 -3
- package/dist/admin/AdminFiles-B_jfB_Py.js.map +0 -1
- package/dist/admin/AdminLayout-BMtiXAzS.js +0 -396
- package/dist/admin/AdminLayout-BMtiXAzS.js.map +0 -1
- package/dist/admin/AdminLayout-BNo3GoHR.js +0 -3
- package/dist/admin/AdminNotifications-BFEjqpqx.js.map +0 -1
- package/dist/admin/AdminNotifications-DJs2ZjNj.js +0 -3
- package/dist/admin/AdminSessions-D7DESfWK.js.map +0 -1
- package/dist/admin/AdminSessions-PS2M8iXi.js +0 -3
- package/dist/admin/AdminUserCreate-Bhxsn92l.js.map +0 -1
- package/dist/admin/AdminUserDetails-C2y1Ig4n.js.map +0 -1
- package/dist/admin/AdminUserLayout-sW6cjZL0.js.map +0 -1
- package/dist/admin/AdminUserSessions-CvN15wPe.js.map +0 -1
- package/dist/admin/AdminUserSessions-D-aOcZgV.js +0 -3
- package/dist/admin/AdminUserSettings-CEMhIYrI.js +0 -3
- package/dist/admin/AdminUserSettings-DvaaxgcV.js.map +0 -1
- package/dist/admin/AdminUsers-BR3C-jrg.js.map +0 -1
- package/dist/admin/AdminUsers-CMW9vN09.js +0 -3
- package/dist/auth/AuthLayout-CzwUKD9y.js +0 -19
- package/dist/auth/AuthLayout-CzwUKD9y.js.map +0 -1
- package/dist/auth/Login-7HlBjDeV.js.map +0 -1
- package/dist/auth/Login-C-e27DGb.js +0 -4
- package/dist/auth/Register-CuQr3kgi.js.map +0 -1
- package/dist/auth/Register-DbvXwgbG.js +0 -4
- package/dist/auth/ResetPassword-BzU-cdd4.js +0 -243
- package/dist/auth/ResetPassword-BzU-cdd4.js.map +0 -1
- package/dist/auth/ResetPassword-DSvrdpaA.js +0 -3
- package/src/admin/AdminSidebar.ts +0 -31
- package/src/admin/components/AdminParameters.tsx +0 -24
- package/src/core/themes/aurora.ts +0 -107
- package/src/core/themes/crystal.ts +0 -107
- package/src/core/themes/default.ts +0 -7
- package/src/core/themes/ember.ts +0 -107
- package/src/core/themes/index.ts +0 -7
- package/src/core/themes/remoraid.ts +0 -278
- package/src/core/themes/slate.ts +0 -81
- /package/src/admin/components/{AdminNotifications.tsx → notifications/AdminNotifications.tsx} +0 -0
- /package/src/admin/components/{AdminUserDetails.tsx → users/AdminUserDetails.tsx} +0 -0
- /package/src/admin/components/{AdminUserSessions.tsx → users/AdminUserSessions.tsx} +0 -0
- /package/src/admin/components/{AdminVerifications.tsx → verifications/AdminVerifications.tsx} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Flex, Pagination, Select } from "@mantine/core";
|
|
2
|
+
|
|
3
|
+
export interface DataTablePaginationProps {
|
|
4
|
+
page: number;
|
|
5
|
+
size: string;
|
|
6
|
+
totalPages: number;
|
|
7
|
+
onPageChange: (page: number) => void;
|
|
8
|
+
onSizeChange: (size: number) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const DataTablePagination = ({
|
|
12
|
+
page,
|
|
13
|
+
size,
|
|
14
|
+
totalPages,
|
|
15
|
+
onPageChange,
|
|
16
|
+
onSizeChange,
|
|
17
|
+
}: DataTablePaginationProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<Flex
|
|
20
|
+
align="center"
|
|
21
|
+
justify="end"
|
|
22
|
+
gap="md"
|
|
23
|
+
p="xs"
|
|
24
|
+
style={{
|
|
25
|
+
borderTop: "1px solid var(--alepha-border)",
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
<Flex>
|
|
29
|
+
<Select
|
|
30
|
+
w={96}
|
|
31
|
+
variant="default"
|
|
32
|
+
value={size}
|
|
33
|
+
onChange={(value) => {
|
|
34
|
+
if (value) {
|
|
35
|
+
onSizeChange(Number(value));
|
|
36
|
+
}
|
|
37
|
+
}}
|
|
38
|
+
data={[
|
|
39
|
+
{ value: "5", label: "5" },
|
|
40
|
+
{ value: "10", label: "10" },
|
|
41
|
+
{ value: "25", label: "25" },
|
|
42
|
+
{ value: "50", label: "50" },
|
|
43
|
+
{ value: "100", label: "100" },
|
|
44
|
+
]}
|
|
45
|
+
/>
|
|
46
|
+
</Flex>
|
|
47
|
+
<Flex>
|
|
48
|
+
<Pagination
|
|
49
|
+
withEdges
|
|
50
|
+
total={totalPages}
|
|
51
|
+
value={page}
|
|
52
|
+
onChange={onPageChange}
|
|
53
|
+
/>
|
|
54
|
+
</Flex>
|
|
55
|
+
</Flex>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export default DataTablePagination;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Badge, Divider, Flex } from "@mantine/core";
|
|
2
|
+
import { IconRefresh, IconX } from "@tabler/icons-react";
|
|
3
|
+
import type { TObject } from "alepha";
|
|
4
|
+
import { isValidElement, type ReactNode } from "react";
|
|
5
|
+
import { isComponentType } from "../../helpers/isComponentType.ts";
|
|
6
|
+
import ActionButton, { type ActionProps } from "../buttons/ActionButton.tsx";
|
|
7
|
+
import ColumnPicker from "./ColumnPicker.tsx";
|
|
8
|
+
import FilterPicker from "./FilterPicker.tsx";
|
|
9
|
+
import type {
|
|
10
|
+
CheckboxAction,
|
|
11
|
+
CheckboxActionContext,
|
|
12
|
+
ColumnVisibility,
|
|
13
|
+
DataTableColumn,
|
|
14
|
+
FilterVisibility,
|
|
15
|
+
} from "./types.ts";
|
|
16
|
+
|
|
17
|
+
export interface DataTableToolbarProps<
|
|
18
|
+
T extends object,
|
|
19
|
+
Filters extends TObject,
|
|
20
|
+
> {
|
|
21
|
+
columns: { [key: string]: DataTableColumn<T, Filters> };
|
|
22
|
+
filters?: TObject;
|
|
23
|
+
columnVisibility: ColumnVisibility;
|
|
24
|
+
filterVisibility: FilterVisibility;
|
|
25
|
+
onColumnVisibilityChange: (visibility: ColumnVisibility) => void;
|
|
26
|
+
onFilterVisibilityChange: (visibility: FilterVisibility) => void;
|
|
27
|
+
actions?: Array<ActionProps & { label?: ReactNode }>;
|
|
28
|
+
onRefresh?: () => void;
|
|
29
|
+
// Checkbox-related props
|
|
30
|
+
selectedItems?: T[];
|
|
31
|
+
checkboxActions?: Array<CheckboxAction<T>>;
|
|
32
|
+
onClearSelection?: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DataTableToolbar = <T extends object, Filters extends TObject>({
|
|
36
|
+
columns,
|
|
37
|
+
filters,
|
|
38
|
+
columnVisibility,
|
|
39
|
+
filterVisibility,
|
|
40
|
+
onColumnVisibilityChange,
|
|
41
|
+
onFilterVisibilityChange,
|
|
42
|
+
actions,
|
|
43
|
+
onRefresh,
|
|
44
|
+
selectedItems = [],
|
|
45
|
+
checkboxActions,
|
|
46
|
+
onClearSelection,
|
|
47
|
+
}: DataTableToolbarProps<T, Filters>) => {
|
|
48
|
+
const hasSelection = selectedItems.length > 0;
|
|
49
|
+
|
|
50
|
+
const handleCheckboxAction = async (action: CheckboxAction<T>) => {
|
|
51
|
+
const ctx: CheckboxActionContext<T> = {
|
|
52
|
+
selectedItems,
|
|
53
|
+
clearSelection: onClearSelection || (() => {}),
|
|
54
|
+
};
|
|
55
|
+
await action.onClick(ctx);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Flex p="xs" style={{ borderBottom: "1px solid var(--alepha-border)" }}>
|
|
60
|
+
<Flex gap={4} align="center">
|
|
61
|
+
{filters && (
|
|
62
|
+
<FilterPicker
|
|
63
|
+
schema={filters}
|
|
64
|
+
visibility={filterVisibility}
|
|
65
|
+
onVisibilityChange={onFilterVisibilityChange}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
<ColumnPicker
|
|
69
|
+
columns={columns}
|
|
70
|
+
visibility={columnVisibility}
|
|
71
|
+
onVisibilityChange={onColumnVisibilityChange}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
{hasSelection && (
|
|
75
|
+
<>
|
|
76
|
+
<Divider orientation="vertical" mx="xs" />
|
|
77
|
+
<Badge variant="light" size="lg">
|
|
78
|
+
{selectedItems.length} selected
|
|
79
|
+
</Badge>
|
|
80
|
+
<ActionButton
|
|
81
|
+
variant="subtle"
|
|
82
|
+
size="compact-sm"
|
|
83
|
+
icon={IconX}
|
|
84
|
+
onClick={onClearSelection}
|
|
85
|
+
>
|
|
86
|
+
Clear
|
|
87
|
+
</ActionButton>
|
|
88
|
+
{checkboxActions?.map((action, index) => (
|
|
89
|
+
<ActionButton
|
|
90
|
+
key={index}
|
|
91
|
+
variant="light"
|
|
92
|
+
size="compact-sm"
|
|
93
|
+
intent={action.intent}
|
|
94
|
+
icon={
|
|
95
|
+
action.icon && isComponentType(action.icon)
|
|
96
|
+
? action.icon
|
|
97
|
+
: undefined
|
|
98
|
+
}
|
|
99
|
+
onClick={() => handleCheckboxAction(action)}
|
|
100
|
+
>
|
|
101
|
+
{action.label}
|
|
102
|
+
</ActionButton>
|
|
103
|
+
))}
|
|
104
|
+
</>
|
|
105
|
+
)}
|
|
106
|
+
</Flex>
|
|
107
|
+
<Flex flex={1} />
|
|
108
|
+
<Flex gap="xs">
|
|
109
|
+
{actions?.map((props, index) =>
|
|
110
|
+
!isValidElement(props) ? (
|
|
111
|
+
<ActionButton key={index} {...(props as ActionProps)}>
|
|
112
|
+
{(props as ActionProps & { label?: ReactNode }).label}
|
|
113
|
+
</ActionButton>
|
|
114
|
+
) : (
|
|
115
|
+
props
|
|
116
|
+
),
|
|
117
|
+
)}
|
|
118
|
+
<ActionButton icon={IconRefresh} onClick={onRefresh}>
|
|
119
|
+
Refresh
|
|
120
|
+
</ActionButton>
|
|
121
|
+
</Flex>
|
|
122
|
+
</Flex>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default DataTableToolbar;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Checkbox,
|
|
4
|
+
Group,
|
|
5
|
+
Popover,
|
|
6
|
+
ScrollArea,
|
|
7
|
+
Stack,
|
|
8
|
+
Text,
|
|
9
|
+
} from "@mantine/core";
|
|
10
|
+
import { IconFilter } from "@tabler/icons-react";
|
|
11
|
+
import type { TObject } from "alepha";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { ui } from "../../constants/ui.ts";
|
|
14
|
+
import ActionButton from "../buttons/ActionButton.tsx";
|
|
15
|
+
import type { FilterVisibility } from "./types.ts";
|
|
16
|
+
|
|
17
|
+
export interface FilterPickerProps {
|
|
18
|
+
schema: TObject;
|
|
19
|
+
visibility: FilterVisibility;
|
|
20
|
+
onVisibilityChange: (visibility: FilterVisibility) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getFieldLabel = (schema: TObject, key: string): string => {
|
|
24
|
+
const prop = schema.properties[key];
|
|
25
|
+
if (prop && typeof prop === "object" && "title" in prop && prop.title) {
|
|
26
|
+
return prop.title as string;
|
|
27
|
+
}
|
|
28
|
+
// Convert camelCase to Title Case
|
|
29
|
+
return key
|
|
30
|
+
.replace(/([A-Z])/g, " $1")
|
|
31
|
+
.replace(/^./, (str) => str.toUpperCase())
|
|
32
|
+
.trim();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const FilterPicker = ({
|
|
36
|
+
schema,
|
|
37
|
+
visibility,
|
|
38
|
+
onVisibilityChange,
|
|
39
|
+
}: FilterPickerProps) => {
|
|
40
|
+
const [opened, setOpened] = useState(false);
|
|
41
|
+
const filterKeys = Object.keys(schema.properties);
|
|
42
|
+
|
|
43
|
+
const handleShowAll = () => {
|
|
44
|
+
const newVisibility = filterKeys.reduce(
|
|
45
|
+
(acc, key) => ({ ...acc, [key]: true }),
|
|
46
|
+
{} as FilterVisibility,
|
|
47
|
+
);
|
|
48
|
+
onVisibilityChange(newVisibility);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const handleHideAll = () => {
|
|
52
|
+
const newVisibility = filterKeys.reduce(
|
|
53
|
+
(acc, key) => ({ ...acc, [key]: false }),
|
|
54
|
+
{} as FilterVisibility,
|
|
55
|
+
);
|
|
56
|
+
onVisibilityChange(newVisibility);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleToggle = (key: string, checked: boolean) => {
|
|
60
|
+
onVisibilityChange({
|
|
61
|
+
...visibility,
|
|
62
|
+
[key]: checked,
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const visibleCount = filterKeys.filter(
|
|
67
|
+
(key) => visibility[key] !== false,
|
|
68
|
+
).length;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Popover
|
|
72
|
+
width={280}
|
|
73
|
+
position="bottom-start"
|
|
74
|
+
shadow="md"
|
|
75
|
+
opened={opened}
|
|
76
|
+
onChange={setOpened}
|
|
77
|
+
closeOnClickOutside
|
|
78
|
+
closeOnEscape
|
|
79
|
+
transitionProps={{
|
|
80
|
+
transition: "fade-up",
|
|
81
|
+
duration: 200,
|
|
82
|
+
timingFunction: "ease",
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
<Popover.Target>
|
|
86
|
+
<ActionButton variant="subtle" icon={IconFilter} />
|
|
87
|
+
</Popover.Target>
|
|
88
|
+
<Popover.Dropdown
|
|
89
|
+
bg="transparent"
|
|
90
|
+
p="xs"
|
|
91
|
+
bd={`1px solid ${ui.colors.border}`}
|
|
92
|
+
style={{
|
|
93
|
+
backdropFilter: "blur(20px)",
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
<Stack gap="xs" bg={ui.colors.surface} p="sm" bdrs="sm">
|
|
97
|
+
<Group justify="space-between">
|
|
98
|
+
<Text size="sm" fw={500}>
|
|
99
|
+
Filters ({visibleCount}/{filterKeys.length})
|
|
100
|
+
</Text>
|
|
101
|
+
<Group gap={4}>
|
|
102
|
+
<Button
|
|
103
|
+
size="compact-xs"
|
|
104
|
+
variant="subtle"
|
|
105
|
+
onClick={handleShowAll}
|
|
106
|
+
>
|
|
107
|
+
All
|
|
108
|
+
</Button>
|
|
109
|
+
<Button
|
|
110
|
+
size="compact-xs"
|
|
111
|
+
variant="subtle"
|
|
112
|
+
onClick={handleHideAll}
|
|
113
|
+
>
|
|
114
|
+
None
|
|
115
|
+
</Button>
|
|
116
|
+
</Group>
|
|
117
|
+
</Group>
|
|
118
|
+
|
|
119
|
+
<ScrollArea.Autosize mah={300}>
|
|
120
|
+
<Stack gap={4}>
|
|
121
|
+
{filterKeys.map((key) => (
|
|
122
|
+
<Checkbox
|
|
123
|
+
key={key}
|
|
124
|
+
label={getFieldLabel(schema, key)}
|
|
125
|
+
checked={visibility[key] !== false}
|
|
126
|
+
onChange={(e) => handleToggle(key, e.currentTarget.checked)}
|
|
127
|
+
size="sm"
|
|
128
|
+
/>
|
|
129
|
+
))}
|
|
130
|
+
</Stack>
|
|
131
|
+
</ScrollArea.Autosize>
|
|
132
|
+
</Stack>
|
|
133
|
+
</Popover.Dropdown>
|
|
134
|
+
</Popover>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export default FilterPicker;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { FormModel } from "@alepha/react/form";
|
|
2
|
+
import type { TableProps, TableTrProps } from "@mantine/core";
|
|
3
|
+
import type {
|
|
4
|
+
Alepha,
|
|
5
|
+
Async,
|
|
6
|
+
Page,
|
|
7
|
+
PageMetadata,
|
|
8
|
+
Static,
|
|
9
|
+
TObject,
|
|
10
|
+
} from "alepha";
|
|
11
|
+
import type { DurationLike } from "alepha/datetime";
|
|
12
|
+
import type { ReactNode } from "react";
|
|
13
|
+
import type { ActionProps } from "../buttons/ActionButton.tsx";
|
|
14
|
+
import type { TypeFormProps } from "../form/TypeForm.tsx";
|
|
15
|
+
|
|
16
|
+
// -----------------------------------------------------------------------------
|
|
17
|
+
// Visibility Types
|
|
18
|
+
// -----------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export interface ColumnVisibility {
|
|
21
|
+
[key: string]: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface FilterVisibility {
|
|
25
|
+
[key: string]: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// -----------------------------------------------------------------------------
|
|
29
|
+
// Column Types
|
|
30
|
+
// -----------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
export interface DataTableColumnContext<Filters extends TObject> {
|
|
33
|
+
index: number;
|
|
34
|
+
form: FormModel<Filters>;
|
|
35
|
+
alepha: Alepha;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DataTableColumn<T extends object, Filters extends TObject> {
|
|
39
|
+
label: string;
|
|
40
|
+
value: (item: T, ctx: DataTableColumnContext<Filters>) => ReactNode;
|
|
41
|
+
fit?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Enable sorting for this column. When true, clicking the header will sort by this column.
|
|
44
|
+
*/
|
|
45
|
+
sortable?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The field name to use for sorting. If not provided, the column key is used.
|
|
48
|
+
* Follows Alepha sort convention: 'field' for ASC, '-field' for DESC.
|
|
49
|
+
*/
|
|
50
|
+
sortKey?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// -----------------------------------------------------------------------------
|
|
54
|
+
// Page Types
|
|
55
|
+
// -----------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
export type MaybePage<T> = Omit<Page<T>, "page"> & {
|
|
58
|
+
page?: Partial<PageMetadata>;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export interface DataTableSubmitContext<T extends object> {
|
|
62
|
+
items: T[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// -----------------------------------------------------------------------------
|
|
66
|
+
// Checkbox Types
|
|
67
|
+
// -----------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
export interface CheckboxActionContext<T extends object> {
|
|
70
|
+
selectedItems: T[];
|
|
71
|
+
clearSelection: () => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface CheckboxAction<T extends object> {
|
|
75
|
+
label: ReactNode;
|
|
76
|
+
icon?: ReactNode;
|
|
77
|
+
intent?: "primary" | "success" | "danger" | "warning" | "info" | "none";
|
|
78
|
+
onClick: (ctx: CheckboxActionContext<T>) => void | Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// -----------------------------------------------------------------------------
|
|
82
|
+
// Props Types
|
|
83
|
+
// -----------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
export interface DataTableProps<T extends object, Filters extends TObject> {
|
|
86
|
+
/**
|
|
87
|
+
* The items to display in the table. Can be a static page of items or a function that returns a promise resolving to a page of items.
|
|
88
|
+
*/
|
|
89
|
+
items:
|
|
90
|
+
| MaybePage<T>
|
|
91
|
+
| ((
|
|
92
|
+
filters: Static<Filters> & {
|
|
93
|
+
page: number;
|
|
94
|
+
size: number;
|
|
95
|
+
sort?: string;
|
|
96
|
+
},
|
|
97
|
+
ctx: DataTableSubmitContext<T>,
|
|
98
|
+
) => Async<MaybePage<T>>);
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The columns to display in the table. Each column is defined by a key and a DataTableColumn object.
|
|
102
|
+
*/
|
|
103
|
+
columns: {
|
|
104
|
+
[key: string]: DataTableColumn<T, Filters>;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
defaultSize?: number;
|
|
108
|
+
|
|
109
|
+
typeFormProps?: Partial<Omit<TypeFormProps<Filters>, "form">>;
|
|
110
|
+
|
|
111
|
+
onFilterChange?: (
|
|
112
|
+
key: string,
|
|
113
|
+
value: unknown,
|
|
114
|
+
form: FormModel<Filters>,
|
|
115
|
+
) => void;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Optional filters to apply to the data.
|
|
119
|
+
*/
|
|
120
|
+
filters?: TObject;
|
|
121
|
+
|
|
122
|
+
panel?: (item: T) => ReactNode;
|
|
123
|
+
canPanel?: (item: T) => boolean;
|
|
124
|
+
|
|
125
|
+
submitOnInit?: boolean;
|
|
126
|
+
submitEvery?: DurationLike;
|
|
127
|
+
|
|
128
|
+
withLineNumbers?: boolean;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Enable row selection with checkboxes. When true, a checkbox column is added as the first column.
|
|
132
|
+
*/
|
|
133
|
+
withCheckbox?: boolean;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Function to get a unique key for each item. Required when withCheckbox is true.
|
|
137
|
+
* Used to track selected items across pagination.
|
|
138
|
+
*/
|
|
139
|
+
getItemKey?: (item: T) => string;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Actions to display when items are selected. Each action receives the selected items.
|
|
143
|
+
*/
|
|
144
|
+
checkboxActions?: Array<CheckboxAction<T>>;
|
|
145
|
+
|
|
146
|
+
actions?: Array<ActionProps & { label?: ReactNode }>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Enable infinity scroll mode. When true, pagination controls are hidden and new items are loaded automatically when scrolling to the bottom.
|
|
150
|
+
*/
|
|
151
|
+
infinityScroll?: boolean;
|
|
152
|
+
|
|
153
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
154
|
+
// Column Visibility
|
|
155
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Initial column visibility state. By default, first 10 columns are visible.
|
|
159
|
+
*/
|
|
160
|
+
defaultColumnVisibility?: ColumnVisibility;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Maximum number of columns to show by default. Default is 10.
|
|
164
|
+
*/
|
|
165
|
+
defaultVisibleColumnCount?: number;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Callback when column visibility changes.
|
|
169
|
+
*/
|
|
170
|
+
onColumnVisibilityChange?: (visibility: ColumnVisibility) => void;
|
|
171
|
+
|
|
172
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
173
|
+
// Filter Visibility
|
|
174
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Initial filter visibility state. By default, all filters are visible.
|
|
178
|
+
*/
|
|
179
|
+
defaultFilterVisibility?: FilterVisibility;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Callback when filter visibility changes.
|
|
183
|
+
*/
|
|
184
|
+
onFilterVisibilityChange?: (visibility: FilterVisibility) => void;
|
|
185
|
+
|
|
186
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
187
|
+
// Mantine Props
|
|
188
|
+
// -------------------------------------------------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Props to pass to the Mantine Table component.
|
|
192
|
+
*/
|
|
193
|
+
tableProps?: TableProps;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Function to generate props for each table row based on the item.
|
|
197
|
+
*/
|
|
198
|
+
tableTrProps?: (item: T) => TableTrProps;
|
|
199
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComponentType, isValidElement } from "react";
|
|
2
|
+
|
|
3
|
+
export function isComponentType(param: any): param is ComponentType<any> {
|
|
4
|
+
if (isValidElement(param)) return false;
|
|
5
|
+
return (
|
|
6
|
+
typeof param === "function" ||
|
|
7
|
+
(typeof param === "object" && param !== null && "$$typeof" in param)
|
|
8
|
+
);
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ui } from "@alepha/ui";
|
|
2
|
+
import { type ComponentType, isValidElement, type ReactNode } from "react";
|
|
3
|
+
import { isComponentType } from "./isComponentType.ts";
|
|
4
|
+
|
|
5
|
+
export const renderIcon = (icon: ReactNode | ComponentType): ReactNode => {
|
|
6
|
+
if (!icon) return null;
|
|
7
|
+
if (isValidElement(icon)) return icon;
|
|
8
|
+
if (isComponentType(icon)) {
|
|
9
|
+
const IconComponent = icon;
|
|
10
|
+
return <IconComponent size={ui.sizes.icon.md} />;
|
|
11
|
+
}
|
|
12
|
+
return icon as ReactNode;
|
|
13
|
+
};
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { useInject, useStore } from "@alepha/react";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
} from "../providers/ThemeProvider.ts";
|
|
3
|
+
alephaThemeAtom,
|
|
4
|
+
type CurrentAlephaTheme,
|
|
5
|
+
} from "../atoms/alephaThemeAtom.ts";
|
|
6
|
+
import type { AlephaTheme } from "../interfaces/AlephaTheme.ts";
|
|
7
|
+
import { ThemeProvider } from "../providers/ThemeProvider.ts";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Hook to get and set the current theme.
|
|
11
|
+
*
|
|
12
|
+
* Returns a tuple with the current theme and a function to set the theme.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const [theme, setTheme] = useTheme();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export const useTheme = (): [
|
|
19
|
+
AlephaTheme,
|
|
20
|
+
(theme: CurrentAlephaTheme) => void,
|
|
21
|
+
] => {
|
|
22
|
+
useStore(alephaThemeAtom);
|
|
11
23
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const fullTheme =
|
|
17
|
-
themeService.themes.find((t) => t.id === currentTheme.id) ??
|
|
18
|
-
themeService.themes[0];
|
|
19
|
-
|
|
20
|
-
const applyTheme = (theme: Theme | AlephaTheme) => {
|
|
21
|
-
themeService.setTheme({ id: theme.id });
|
|
24
|
+
const themeProvider = useInject(ThemeProvider);
|
|
25
|
+
const theme = themeProvider.getTheme();
|
|
26
|
+
const setTheme = (theme: CurrentAlephaTheme) => {
|
|
27
|
+
themeProvider.setTheme(theme.index);
|
|
22
28
|
};
|
|
23
29
|
|
|
24
|
-
return [
|
|
30
|
+
return [theme, setTheme] as const;
|
|
25
31
|
};
|
package/src/core/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { AlephaReactForm } from "@alepha/react/form";
|
|
2
2
|
import { AlephaReactHead } from "@alepha/react/head";
|
|
3
3
|
import { AlephaReactI18n } from "@alepha/react/i18n";
|
|
4
|
-
import { $module } from "alepha";
|
|
4
|
+
import { $module, type Static } from "alepha";
|
|
5
5
|
import type { ComponentType, ReactNode } from "react";
|
|
6
|
+
import { alephaThemeAtom } from "./atoms/alephaThemeAtom.ts";
|
|
6
7
|
import type { ControlProps } from "./components/form/Control.tsx";
|
|
7
8
|
import { ThemeProvider } from "./providers/ThemeProvider.ts";
|
|
8
9
|
import { RootRouter } from "./RootRouter.ts";
|
|
@@ -12,6 +13,10 @@ import { ToastService } from "./services/ToastService.tsx";
|
|
|
12
13
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
13
14
|
|
|
14
15
|
export { Flex, Text } from "@mantine/core";
|
|
16
|
+
export * from "./atoms/alephaThemeAtom.ts";
|
|
17
|
+
export * from "./atoms/alephaThemeListAtom.ts";
|
|
18
|
+
export * from "./atoms/themes/default.ts";
|
|
19
|
+
export * from "./atoms/themes/midnight.ts";
|
|
15
20
|
export type {
|
|
16
21
|
ActionClickButtonProps,
|
|
17
22
|
ActionCommonProps,
|
|
@@ -35,7 +40,10 @@ export { default as AlertDialog } from "./components/dialogs/AlertDialog.tsx";
|
|
|
35
40
|
export { default as ConfirmDialog } from "./components/dialogs/ConfirmDialog.tsx";
|
|
36
41
|
export { default as PromptDialog } from "./components/dialogs/PromptDialog.tsx";
|
|
37
42
|
export { default as Control } from "./components/form/Control.tsx";
|
|
43
|
+
export { default as ControlArray } from "./components/form/ControlArray.tsx";
|
|
38
44
|
export { default as ControlDate } from "./components/form/ControlDate.tsx";
|
|
45
|
+
export { default as ControlNumber } from "./components/form/ControlNumber.tsx";
|
|
46
|
+
export { default as ControlObject } from "./components/form/ControlObject.tsx";
|
|
39
47
|
export { default as ControlQueryBuilder } from "./components/form/ControlQueryBuilder.tsx";
|
|
40
48
|
export { default as ControlSelect } from "./components/form/ControlSelect.tsx";
|
|
41
49
|
export { default as TypeForm } from "./components/form/TypeForm.tsx";
|
|
@@ -72,11 +80,18 @@ export type {
|
|
|
72
80
|
SidebarTheme,
|
|
73
81
|
} from "./components/layout/Sidebar.tsx";
|
|
74
82
|
export { Sidebar } from "./components/layout/Sidebar.tsx";
|
|
83
|
+
export { default as DataTable } from "./components/table/DataTable.tsx";
|
|
75
84
|
export type {
|
|
85
|
+
CheckboxAction,
|
|
86
|
+
CheckboxActionContext,
|
|
87
|
+
ColumnVisibility,
|
|
76
88
|
DataTableColumn,
|
|
89
|
+
DataTableColumnContext,
|
|
77
90
|
DataTableProps,
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
DataTableSubmitContext,
|
|
92
|
+
FilterVisibility,
|
|
93
|
+
MaybePage,
|
|
94
|
+
} from "./components/table/types.ts";
|
|
80
95
|
export * from "./constants/ui.ts";
|
|
81
96
|
export { useDialog } from "./hooks/useDialog.ts";
|
|
82
97
|
export { useToast } from "./hooks/useToast.ts";
|
|
@@ -105,6 +120,12 @@ declare module "typebox" {
|
|
|
105
120
|
}
|
|
106
121
|
}
|
|
107
122
|
|
|
123
|
+
declare module "alepha" {
|
|
124
|
+
interface State {
|
|
125
|
+
[alephaThemeAtom.key]?: Static<typeof alephaThemeAtom.schema>;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
108
129
|
declare module "@alepha/react" {
|
|
109
130
|
interface PagePrimitiveOptions {
|
|
110
131
|
/**
|