@alepha/ui 0.16.2 → 0.17.1
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/{AdminApiKeys-CoTOTfgU.js → AdminApiKeys-CF_qOO3u.js} +20 -20
- package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +1 -0
- package/dist/admin/{AdminAudits-BmsxFbDa.js → AdminAudits-BQno3hZG.js} +7 -8
- package/dist/admin/AdminAudits-BQno3hZG.js.map +1 -0
- package/dist/admin/{AdminFiles-BBB8knca.js → AdminFiles-kvuUaASF.js} +3 -5
- package/dist/admin/{AdminFiles-BBB8knca.js.map → AdminFiles-kvuUaASF.js.map} +1 -1
- package/dist/admin/AdminJobDashboard-CrPxp0W1.js +485 -0
- package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +1 -0
- package/dist/admin/AdminJobExecutions-D-b4Zt7W.js +678 -0
- package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +1 -0
- package/dist/admin/AdminJobRegistry-CNX5cpDx.js +301 -0
- package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +1 -0
- package/dist/admin/{AdminLayout-CsjvpeD1.js → AdminLayout-e-ZP5nWw.js} +1 -1
- package/dist/admin/{AdminLayout-CsjvpeD1.js.map → AdminLayout-e-ZP5nWw.js.map} +1 -1
- package/dist/admin/{AdminNotifications-LwR6RKrx.js → AdminNotifications-DeHJFf6W.js} +3 -5
- package/dist/admin/{AdminNotifications-LwR6RKrx.js.map → AdminNotifications-DeHJFf6W.js.map} +1 -1
- package/dist/admin/{AdminParameters-B_83Vie9.js → AdminParameters-iQE8o7a7.js} +43 -36
- package/dist/admin/AdminParameters-iQE8o7a7.js.map +1 -0
- package/dist/admin/{AdminSessions-CWnPosdd.js → AdminSessions-oKJCbd7w.js} +5 -7
- package/dist/admin/AdminSessions-oKJCbd7w.js.map +1 -0
- package/dist/admin/{AdminUserAudits-nHv636E_.js → AdminUserAudits-BNCEle_E.js} +6 -8
- package/dist/admin/AdminUserAudits-BNCEle_E.js.map +1 -0
- package/dist/admin/{AdminUserCreate-CjYD3Kjc.js → AdminUserCreate-CgqeFwCt.js} +6 -7
- package/dist/admin/AdminUserCreate-CgqeFwCt.js.map +1 -0
- package/dist/admin/{AdminUserDetails-Ccq-LsZ0.js → AdminUserDetails-DDe1A1GP.js} +30 -29
- package/dist/admin/AdminUserDetails-DDe1A1GP.js.map +1 -0
- package/dist/admin/{AdminUserLayout-7s41DiF_.js → AdminUserLayout-HAlobhWf.js} +18 -16
- package/dist/admin/AdminUserLayout-HAlobhWf.js.map +1 -0
- package/dist/admin/{AdminUserSessions-Ds3ODq_d.js → AdminUserSessions-Bq1LnVLf.js} +5 -7
- package/dist/admin/AdminUserSessions-Bq1LnVLf.js.map +1 -0
- package/dist/admin/{AdminUserSettings-CGh4gROo.js → AdminUserSettings-BRsBZoxV.js} +10 -10
- package/dist/admin/AdminUserSettings-BRsBZoxV.js.map +1 -0
- package/dist/admin/{AdminUsers-CvPiBzQK.js → AdminUsers-D71kIOSn.js} +6 -8
- package/dist/admin/AdminUsers-D71kIOSn.js.map +1 -0
- package/dist/admin/index.d.ts +7 -83
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +49 -70
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/{Login-DS_OqA0G.js → Login-BS_FYTy0.js} +13 -8
- package/dist/auth/Login-BS_FYTy0.js.map +1 -0
- package/dist/auth/{Profile-Di7N7HZL.js → Profile-CjDsW378.js} +16 -10
- package/dist/auth/Profile-CjDsW378.js.map +1 -0
- package/dist/auth/{Register-BRR2_gux.js → Register-C5eqzAaD.js} +21 -12
- package/dist/auth/Register-C5eqzAaD.js.map +1 -0
- package/dist/auth/{ResetPassword-oQu72lod.js → ResetPassword-XifinVao.js} +14 -8
- package/dist/auth/ResetPassword-XifinVao.js.map +1 -0
- package/dist/auth/{VerifyEmail-DC6HPZjd.js → VerifyEmail-DTgbeJOO.js} +6 -4
- package/dist/auth/VerifyEmail-DTgbeJOO.js.map +1 -0
- package/dist/auth/index.d.ts +4 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +15 -14
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +37 -26
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +444 -193
- package/dist/core/index.js.map +1 -1
- package/dist/demo/DemoDataTable-lnBKWBf8.js +362 -0
- package/dist/demo/DemoDataTable-lnBKWBf8.js.map +1 -0
- package/dist/demo/{DemoHome-DpRrPlBC.js → DemoHome-CUMZsYaH.js} +6 -7
- package/dist/demo/DemoHome-CUMZsYaH.js.map +1 -0
- package/dist/demo/{DemoJsonViewer-zeucGKHV.js → DemoJsonViewer-_uokbGaW.js} +17 -19
- package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +1 -0
- package/dist/demo/{DemoLayout-PhgbAAiQ.js → DemoLayout-DHVoacE6.js} +2 -4
- package/dist/demo/{DemoLayout-PhgbAAiQ.js.map → DemoLayout-DHVoacE6.js.map} +1 -1
- package/dist/demo/{DemoLogin-DSzP0Lkv.js → DemoLogin-DjJ9314c.js} +22 -17
- package/dist/demo/DemoLogin-DjJ9314c.js.map +1 -0
- package/dist/demo/{DemoRegister-DavFBsCz.js → DemoRegister-DzkJ5M83.js} +34 -25
- package/dist/demo/DemoRegister-DzkJ5M83.js.map +1 -0
- package/dist/demo/{DemoResetPassword-BS2rIAQK.js → DemoResetPassword-DWh4_BpQ.js} +27 -21
- package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +1 -0
- package/dist/demo/{DemoSidebar-zNkUmHRl.js → DemoSidebar-C1csnGhX.js} +2 -2
- package/dist/demo/{DemoSidebar-zNkUmHRl.js.map → DemoSidebar-C1csnGhX.js.map} +1 -1
- package/dist/demo/{DemoTypeForm-B9q7oT0b.js → DemoTypeForm-CWz6fJrJ.js} +2 -2
- package/dist/demo/{DemoTypeForm-B9q7oT0b.js.map → DemoTypeForm-CWz6fJrJ.js.map} +1 -1
- package/dist/demo/{DemoVerifyEmail-Bi4SdWz0.js → DemoVerifyEmail-DbU_tCj8.js} +13 -11
- package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +1 -0
- package/dist/demo/{IconGoogle-CTeZyrek.js → IconGoogle-Ch1m3Uzl.js} +1 -1
- package/dist/demo/{IconGoogle-CTeZyrek.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
- package/dist/demo/{Showcase-C9btr_SJ.js → Showcase-BzoXNlCn.js} +10 -10
- package/dist/demo/Showcase-BzoXNlCn.js.map +1 -0
- package/dist/demo/index.d.ts +1 -68
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +11 -15
- package/dist/demo/index.js.map +1 -1
- package/dist/json/index.js +2 -2
- package/dist/json/index.js.map +1 -1
- package/package.json +9 -5
- package/src/admin/AdminRouter.ts +36 -5
- package/src/admin/components/audits/AdminAudits.tsx +5 -5
- package/src/admin/components/jobs/AdminJobDashboard.tsx +455 -0
- package/src/admin/components/jobs/AdminJobExecutions.tsx +693 -0
- package/src/admin/components/jobs/AdminJobRegistry.tsx +325 -0
- package/src/admin/components/keys/AdminApiKeys.tsx +28 -31
- package/src/admin/components/parameters/AdminParameters.tsx +3 -3
- package/src/admin/components/parameters/ParameterDetails.tsx +34 -29
- package/src/admin/components/parameters/ParameterEmptyState.tsx +5 -5
- package/src/admin/components/parameters/ParameterHistory.tsx +11 -19
- package/src/admin/components/parameters/ParameterTree.tsx +16 -18
- package/src/admin/components/sessions/AdminSessions.tsx +3 -3
- package/src/admin/components/shared/AdminResourceHeader.tsx +20 -16
- package/src/admin/components/users/AdminUserAudits.tsx +5 -5
- package/src/admin/components/users/AdminUserCreate.tsx +3 -3
- package/src/admin/components/users/AdminUserDetails.tsx +51 -53
- package/src/admin/components/users/AdminUserLayout.tsx +7 -7
- package/src/admin/components/users/AdminUserSessions.tsx +3 -3
- package/src/admin/components/users/AdminUserSettings.tsx +9 -9
- package/src/admin/components/users/AdminUsers.tsx +5 -5
- package/src/admin/components/verifications/AdminVerifications.tsx +3 -3
- package/src/admin/index.ts +0 -24
- package/src/auth/components/Login.tsx +13 -13
- package/src/auth/components/Profile.tsx +17 -26
- package/src/auth/components/Register.tsx +21 -31
- package/src/auth/components/ResetPassword.tsx +13 -22
- package/src/auth/components/VerifyEmail.tsx +5 -5
- package/src/auth/components/buttons/UserButton.tsx +14 -4
- package/src/core/components/buttons/ActionButton.tsx +9 -2
- package/src/core/components/data/ErrorViewer.tsx +15 -15
- package/src/core/components/dialogs/AlertDialog.tsx +3 -3
- package/src/core/components/dialogs/ConfirmDialog.tsx +3 -3
- package/src/core/components/dialogs/PromptDialog.tsx +3 -3
- package/src/core/components/form/Control.tsx +9 -0
- package/src/core/components/form/ControlArray.tsx +6 -7
- package/src/core/components/form/ControlObject.tsx +3 -3
- package/src/core/components/form/ControlQueryBuilder.tsx +20 -22
- package/src/core/components/form/ControlSelect.tsx +4 -0
- package/src/core/components/form/TypeForm.tsx +7 -0
- package/src/core/components/layout/Breadcrumb.tsx +6 -6
- package/src/core/components/layout/Omnibar.tsx +2 -1
- package/src/core/components/layout/Sidebar.tsx +5 -1
- package/src/core/components/table/ColumnPicker.tsx +47 -31
- package/src/core/components/table/DataTable.tsx +277 -201
- package/src/core/components/table/DataTableFilters.tsx +8 -0
- package/src/core/components/table/DataTableToolbar.tsx +98 -5
- package/src/core/components/table/FilterPicker.tsx +28 -26
- package/src/core/components/table/types.ts +52 -37
- package/src/core/components/table/useTableSelection.ts +83 -0
- package/src/core/styles.css +1 -0
- package/src/core/utils/parseInput.ts +1 -0
- package/src/demo/components/DemoHome.tsx +5 -5
- package/src/demo/components/core/DemoDataTable.tsx +209 -5
- package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
- package/src/demo/components/shared/MacWindow.tsx +7 -7
- package/src/demo/components/shared/Showcase.tsx +3 -3
- package/src/demo/index.ts +0 -11
- package/src/json/components/JsonViewer.tsx +3 -3
- package/dist/admin/AdminApiKeys-CoTOTfgU.js.map +0 -1
- package/dist/admin/AdminAudits-BmsxFbDa.js.map +0 -1
- package/dist/admin/AdminJobs-C604joTz.js +0 -698
- package/dist/admin/AdminJobs-C604joTz.js.map +0 -1
- package/dist/admin/AdminParameters-B_83Vie9.js.map +0 -1
- package/dist/admin/AdminSessions-CWnPosdd.js.map +0 -1
- package/dist/admin/AdminUserAudits-nHv636E_.js.map +0 -1
- package/dist/admin/AdminUserCreate-CjYD3Kjc.js.map +0 -1
- package/dist/admin/AdminUserDetails-Ccq-LsZ0.js.map +0 -1
- package/dist/admin/AdminUserLayout-7s41DiF_.js.map +0 -1
- package/dist/admin/AdminUserSessions-Ds3ODq_d.js.map +0 -1
- package/dist/admin/AdminUserSettings-CGh4gROo.js.map +0 -1
- package/dist/admin/AdminUsers-CvPiBzQK.js.map +0 -1
- package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
- package/dist/auth/Login-DS_OqA0G.js.map +0 -1
- package/dist/auth/Profile-Di7N7HZL.js.map +0 -1
- package/dist/auth/Register-BRR2_gux.js.map +0 -1
- package/dist/auth/ResetPassword-oQu72lod.js.map +0 -1
- package/dist/auth/VerifyEmail-DC6HPZjd.js.map +0 -1
- package/dist/demo/DemoDataTable-DCsJq8v5.js +0 -149
- package/dist/demo/DemoDataTable-DCsJq8v5.js.map +0 -1
- package/dist/demo/DemoHome-DpRrPlBC.js.map +0 -1
- package/dist/demo/DemoJsonViewer-zeucGKHV.js.map +0 -1
- package/dist/demo/DemoLogin-DSzP0Lkv.js.map +0 -1
- package/dist/demo/DemoRegister-DavFBsCz.js.map +0 -1
- package/dist/demo/DemoResetPassword-BS2rIAQK.js.map +0 -1
- package/dist/demo/DemoVerifyEmail-Bi4SdWz0.js.map +0 -1
- package/dist/demo/Showcase-C9btr_SJ.js.map +0 -1
- package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
- package/src/admin/components/jobs/AdminJobs.tsx +0 -772
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Badge, Divider, Flex } from "@mantine/core";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IconClipboard,
|
|
4
|
+
IconDownload,
|
|
5
|
+
IconRefresh,
|
|
6
|
+
IconX,
|
|
7
|
+
} from "@tabler/icons-react";
|
|
3
8
|
import type { TObject } from "alepha";
|
|
4
|
-
import { isValidElement, type ReactNode } from "react";
|
|
9
|
+
import { isValidElement, type ReactNode, useCallback } from "react";
|
|
5
10
|
import { isComponentType } from "../../helpers/isComponentType.ts";
|
|
6
11
|
import ActionButton, { type ActionProps } from "../buttons/ActionButton.tsx";
|
|
7
12
|
import ColumnPicker from "./ColumnPicker.tsx";
|
|
@@ -14,6 +19,27 @@ import type {
|
|
|
14
19
|
FilterVisibility,
|
|
15
20
|
} from "./types.ts";
|
|
16
21
|
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
const escapeCsvField = (value: string): string => {
|
|
25
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
26
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const extractText = (node: ReactNode): string => {
|
|
32
|
+
if (node == null || typeof node === "boolean") return "";
|
|
33
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
34
|
+
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
35
|
+
if (typeof node === "object" && "props" in node) {
|
|
36
|
+
return extractText((node as any).props.children);
|
|
37
|
+
}
|
|
38
|
+
return "";
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
17
43
|
export interface DataTableToolbarProps<
|
|
18
44
|
T extends object,
|
|
19
45
|
Filters extends TObject,
|
|
@@ -26,6 +52,8 @@ export interface DataTableToolbarProps<
|
|
|
26
52
|
onFilterVisibilityChange: (visibility: FilterVisibility) => void;
|
|
27
53
|
actions?: Array<ActionProps & { label?: ReactNode }>;
|
|
28
54
|
onRefresh?: () => void;
|
|
55
|
+
items: T[];
|
|
56
|
+
withExport?: boolean;
|
|
29
57
|
// Checkbox-related props
|
|
30
58
|
selectedItems?: T[];
|
|
31
59
|
checkboxActions?: Array<CheckboxAction<T>>;
|
|
@@ -41,12 +69,59 @@ const DataTableToolbar = <T extends object, Filters extends TObject>({
|
|
|
41
69
|
onFilterVisibilityChange,
|
|
42
70
|
actions,
|
|
43
71
|
onRefresh,
|
|
72
|
+
items,
|
|
73
|
+
withExport,
|
|
44
74
|
selectedItems = [],
|
|
45
75
|
checkboxActions,
|
|
46
76
|
onClearSelection,
|
|
47
77
|
}: DataTableToolbarProps<T, Filters>) => {
|
|
48
78
|
const hasSelection = selectedItems.length > 0;
|
|
49
79
|
|
|
80
|
+
const exportableColumns = useCallback(() => {
|
|
81
|
+
return Object.entries(columns).filter(
|
|
82
|
+
([key, col]) => !col.actions && columnVisibility[key] !== false,
|
|
83
|
+
);
|
|
84
|
+
}, [columns, columnVisibility]);
|
|
85
|
+
|
|
86
|
+
const buildRows = useCallback((): string[][] => {
|
|
87
|
+
const cols = exportableColumns();
|
|
88
|
+
return items.map((item) =>
|
|
89
|
+
cols.map(([_key, col]) => {
|
|
90
|
+
if (!col.value) return "";
|
|
91
|
+
const node = col.value(item, {} as any);
|
|
92
|
+
return extractText(node);
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
}, [items, exportableColumns]);
|
|
96
|
+
|
|
97
|
+
const buildCsv = useCallback((): string => {
|
|
98
|
+
const cols = exportableColumns();
|
|
99
|
+
const header = cols.map(([_key, col]) => escapeCsvField(col.label));
|
|
100
|
+
const rows = buildRows().map((row) => row.map(escapeCsvField));
|
|
101
|
+
return [header.join(","), ...rows.map((r) => r.join(","))].join("\n");
|
|
102
|
+
}, [exportableColumns, buildRows]);
|
|
103
|
+
|
|
104
|
+
const exportCsv = useCallback(() => {
|
|
105
|
+
const csv = buildCsv();
|
|
106
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
107
|
+
const url = URL.createObjectURL(blob);
|
|
108
|
+
const a = document.createElement("a");
|
|
109
|
+
a.href = url;
|
|
110
|
+
a.download = "export.csv";
|
|
111
|
+
a.click();
|
|
112
|
+
URL.revokeObjectURL(url);
|
|
113
|
+
}, [buildCsv]);
|
|
114
|
+
|
|
115
|
+
const exportClipboard = useCallback(async () => {
|
|
116
|
+
const cols = exportableColumns();
|
|
117
|
+
const header = cols.map(([_key, col]) => col.label);
|
|
118
|
+
const rows = buildRows();
|
|
119
|
+
const text = [header.join("\t"), ...rows.map((r) => r.join("\t"))].join(
|
|
120
|
+
"\n",
|
|
121
|
+
);
|
|
122
|
+
await navigator.clipboard.writeText(text);
|
|
123
|
+
}, [exportableColumns, buildRows]);
|
|
124
|
+
|
|
50
125
|
const handleCheckboxAction = async (action: CheckboxAction<T>) => {
|
|
51
126
|
const ctx: CheckboxActionContext<T> = {
|
|
52
127
|
selectedItems,
|
|
@@ -70,6 +145,26 @@ const DataTableToolbar = <T extends object, Filters extends TObject>({
|
|
|
70
145
|
visibility={columnVisibility}
|
|
71
146
|
onVisibilityChange={onColumnVisibilityChange}
|
|
72
147
|
/>
|
|
148
|
+
{withExport && (
|
|
149
|
+
<ActionButton
|
|
150
|
+
variant="subtle"
|
|
151
|
+
icon={IconDownload}
|
|
152
|
+
menu={{
|
|
153
|
+
items: [
|
|
154
|
+
{
|
|
155
|
+
label: "Export as CSV",
|
|
156
|
+
icon: <IconDownload size={14} />,
|
|
157
|
+
onClick: exportCsv,
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
label: "Copy to clipboard",
|
|
161
|
+
icon: <IconClipboard size={14} />,
|
|
162
|
+
onClick: exportClipboard,
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
73
168
|
|
|
74
169
|
{hasSelection && (
|
|
75
170
|
<>
|
|
@@ -115,9 +210,7 @@ const DataTableToolbar = <T extends object, Filters extends TObject>({
|
|
|
115
210
|
props
|
|
116
211
|
),
|
|
117
212
|
)}
|
|
118
|
-
<ActionButton icon={IconRefresh} onClick={onRefresh}
|
|
119
|
-
Refresh
|
|
120
|
-
</ActionButton>
|
|
213
|
+
<ActionButton variant="subtle" icon={IconRefresh} onClick={onRefresh} />
|
|
121
214
|
</Flex>
|
|
122
215
|
</Flex>
|
|
123
216
|
);
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Button,
|
|
3
|
-
Checkbox,
|
|
4
|
-
Group,
|
|
5
|
-
Popover,
|
|
6
|
-
ScrollArea,
|
|
7
|
-
Stack,
|
|
8
|
-
Text,
|
|
9
|
-
} from "@mantine/core";
|
|
1
|
+
import { Checkbox, Flex, Popover, ScrollArea, Text } from "@mantine/core";
|
|
10
2
|
import { IconFilter } from "@tabler/icons-react";
|
|
11
3
|
import type { TObject } from "alepha";
|
|
12
4
|
import { useState } from "react";
|
|
@@ -63,9 +55,7 @@ const FilterPicker = ({
|
|
|
63
55
|
});
|
|
64
56
|
};
|
|
65
57
|
|
|
66
|
-
const visibleCount = filterKeys.filter(
|
|
67
|
-
(key) => visibility[key] !== false,
|
|
68
|
-
).length;
|
|
58
|
+
const visibleCount = filterKeys.filter((key) => visibility[key]).length;
|
|
69
59
|
|
|
70
60
|
return (
|
|
71
61
|
<Popover
|
|
@@ -83,7 +73,13 @@ const FilterPicker = ({
|
|
|
83
73
|
}}
|
|
84
74
|
>
|
|
85
75
|
<Popover.Target>
|
|
86
|
-
<
|
|
76
|
+
<div>
|
|
77
|
+
<ActionButton
|
|
78
|
+
variant="subtle"
|
|
79
|
+
icon={IconFilter}
|
|
80
|
+
onClick={() => setOpened((o) => !o)}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
87
83
|
</Popover.Target>
|
|
88
84
|
<Popover.Dropdown
|
|
89
85
|
bg="transparent"
|
|
@@ -93,43 +89,49 @@ const FilterPicker = ({
|
|
|
93
89
|
backdropFilter: "blur(20px)",
|
|
94
90
|
}}
|
|
95
91
|
>
|
|
96
|
-
<
|
|
97
|
-
|
|
92
|
+
<Flex
|
|
93
|
+
direction="column"
|
|
94
|
+
gap="xs"
|
|
95
|
+
bg={ui.colors.surface}
|
|
96
|
+
p="sm"
|
|
97
|
+
bdrs="sm"
|
|
98
|
+
>
|
|
99
|
+
<Flex justify="space-between">
|
|
98
100
|
<Text size="sm" fw={500}>
|
|
99
101
|
Filters ({visibleCount}/{filterKeys.length})
|
|
100
102
|
</Text>
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
+
<Flex gap={4}>
|
|
104
|
+
<ActionButton
|
|
103
105
|
size="compact-xs"
|
|
104
106
|
variant="subtle"
|
|
105
107
|
onClick={handleShowAll}
|
|
106
108
|
>
|
|
107
109
|
All
|
|
108
|
-
</
|
|
109
|
-
<
|
|
110
|
+
</ActionButton>
|
|
111
|
+
<ActionButton
|
|
110
112
|
size="compact-xs"
|
|
111
113
|
variant="subtle"
|
|
112
114
|
onClick={handleHideAll}
|
|
113
115
|
>
|
|
114
116
|
None
|
|
115
|
-
</
|
|
116
|
-
</
|
|
117
|
-
</
|
|
117
|
+
</ActionButton>
|
|
118
|
+
</Flex>
|
|
119
|
+
</Flex>
|
|
118
120
|
|
|
119
121
|
<ScrollArea.Autosize mah={300}>
|
|
120
|
-
<
|
|
122
|
+
<Flex direction="column" gap={4}>
|
|
121
123
|
{filterKeys.map((key) => (
|
|
122
124
|
<Checkbox
|
|
123
125
|
key={key}
|
|
124
126
|
label={getFieldLabel(schema, key)}
|
|
125
|
-
checked={visibility[key]
|
|
127
|
+
checked={visibility[key] === true}
|
|
126
128
|
onChange={(e) => handleToggle(key, e.currentTarget.checked)}
|
|
127
129
|
size="sm"
|
|
128
130
|
/>
|
|
129
131
|
))}
|
|
130
|
-
</
|
|
132
|
+
</Flex>
|
|
131
133
|
</ScrollArea.Autosize>
|
|
132
|
-
</
|
|
134
|
+
</Flex>
|
|
133
135
|
</Popover.Dropdown>
|
|
134
136
|
</Popover>
|
|
135
137
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TableProps, TableTrProps } from "@mantine/core";
|
|
1
|
+
import type { DrawerProps, TableProps, TableTrProps } from "@mantine/core";
|
|
2
2
|
import type {
|
|
3
3
|
Alepha,
|
|
4
4
|
Async,
|
|
@@ -13,6 +13,20 @@ import type { ReactNode } from "react";
|
|
|
13
13
|
import type { ActionProps } from "../buttons/ActionButton.tsx";
|
|
14
14
|
import type { TypeFormProps } from "../form/TypeForm.tsx";
|
|
15
15
|
|
|
16
|
+
// -----------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// -----------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_MAX_VISIBLE_COLUMNS = 8;
|
|
21
|
+
|
|
22
|
+
// -----------------------------------------------------------------------------
|
|
23
|
+
// Row Action Types
|
|
24
|
+
// -----------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
export type DataTableRowAction = ActionProps & {
|
|
27
|
+
visible?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
16
30
|
// -----------------------------------------------------------------------------
|
|
17
31
|
// Visibility Types
|
|
18
32
|
// -----------------------------------------------------------------------------
|
|
@@ -37,7 +51,7 @@ export interface DataTableColumnContext<Filters extends TObject> {
|
|
|
37
51
|
|
|
38
52
|
export interface DataTableColumn<T extends object, Filters extends TObject> {
|
|
39
53
|
label: string;
|
|
40
|
-
value
|
|
54
|
+
value?: (item: T, ctx: DataTableColumnContext<Filters>) => ReactNode;
|
|
41
55
|
fit?: boolean;
|
|
42
56
|
/**
|
|
43
57
|
* Enable sorting for this column. When true, clicking the header will sort by this column.
|
|
@@ -48,6 +62,19 @@ export interface DataTableColumn<T extends object, Filters extends TObject> {
|
|
|
48
62
|
* Follows Alepha sort convention: 'field' for ASC, '-field' for DESC.
|
|
49
63
|
*/
|
|
50
64
|
sortKey?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Row-level actions rendered as ActionButtons.
|
|
67
|
+
* Defaults: variant="subtle", size="xs", preventDefault=true.
|
|
68
|
+
* Use `visible` on each action to conditionally show/hide.
|
|
69
|
+
*/
|
|
70
|
+
actions?: (
|
|
71
|
+
item: T,
|
|
72
|
+
ctx: DataTableColumnContext<Filters>,
|
|
73
|
+
) => DataTableRowAction[];
|
|
74
|
+
/**
|
|
75
|
+
* Hide this column by default. Users can show it via the column picker.
|
|
76
|
+
*/
|
|
77
|
+
defaultHidden?: boolean;
|
|
51
78
|
}
|
|
52
79
|
|
|
53
80
|
// -----------------------------------------------------------------------------
|
|
@@ -119,13 +146,28 @@ export interface DataTableProps<T extends object, Filters extends TObject> {
|
|
|
119
146
|
*/
|
|
120
147
|
filters?: TObject;
|
|
121
148
|
|
|
122
|
-
panel?:
|
|
123
|
-
|
|
149
|
+
panel?:
|
|
150
|
+
| ((item: T) => ReactNode)
|
|
151
|
+
| {
|
|
152
|
+
render: (item: T) => ReactNode;
|
|
153
|
+
can?: (item: T) => boolean;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
drawer?:
|
|
157
|
+
| ((item: T) => ReactNode)
|
|
158
|
+
| {
|
|
159
|
+
render: (item: T) => ReactNode;
|
|
160
|
+
can?: (item: T) => boolean;
|
|
161
|
+
props?: Omit<DrawerProps, "opened" | "onClose" | "children">;
|
|
162
|
+
};
|
|
124
163
|
|
|
125
164
|
submitOnInit?: boolean;
|
|
126
165
|
submitEvery?: DurationLike;
|
|
127
166
|
|
|
128
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Enable export button in toolbar (CSV download + clipboard copy).
|
|
169
|
+
*/
|
|
170
|
+
withExport?: boolean;
|
|
129
171
|
|
|
130
172
|
/**
|
|
131
173
|
* Enable row selection with checkboxes. When true, a checkbox column is added as the first column.
|
|
@@ -133,8 +175,9 @@ export interface DataTableProps<T extends object, Filters extends TObject> {
|
|
|
133
175
|
withCheckbox?: boolean;
|
|
134
176
|
|
|
135
177
|
/**
|
|
136
|
-
* Function to get a unique key for each item.
|
|
137
|
-
* Used to track selected items
|
|
178
|
+
* Function to get a unique key for each item.
|
|
179
|
+
* Used to track selected items, panel expansion, and drawer identity.
|
|
180
|
+
* Falls back to item.id then JSON.stringify if not provided.
|
|
138
181
|
*/
|
|
139
182
|
getItemKey?: (item: T) => string;
|
|
140
183
|
|
|
@@ -150,38 +193,10 @@ export interface DataTableProps<T extends object, Filters extends TObject> {
|
|
|
150
193
|
*/
|
|
151
194
|
infinityScroll?: boolean;
|
|
152
195
|
|
|
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
196
|
/**
|
|
182
|
-
*
|
|
197
|
+
* Filter keys to show by default. Default: none (empty array).
|
|
183
198
|
*/
|
|
184
|
-
|
|
199
|
+
defaultFilters?: string[];
|
|
185
200
|
|
|
186
201
|
// -------------------------------------------------------------------------------------------------------------------
|
|
187
202
|
// Mantine Props
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export interface UseTableSelectionReturn<T> {
|
|
4
|
+
selectedItems: T[];
|
|
5
|
+
allSelected: boolean;
|
|
6
|
+
someSelected: boolean;
|
|
7
|
+
toggleItem: (item: T) => void;
|
|
8
|
+
toggleAll: () => void;
|
|
9
|
+
clear: () => void;
|
|
10
|
+
isSelected: (item: T) => boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const useTableSelection = <T>(
|
|
14
|
+
items: T[],
|
|
15
|
+
getItemKey: (item: T) => string,
|
|
16
|
+
enabled: boolean,
|
|
17
|
+
): UseTableSelectionReturn<T> => {
|
|
18
|
+
const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set());
|
|
19
|
+
|
|
20
|
+
const selectedItems = useMemo(() => {
|
|
21
|
+
if (!enabled) return [];
|
|
22
|
+
return items.filter((item) => selectedKeys.has(getItemKey(item)));
|
|
23
|
+
}, [items, selectedKeys, getItemKey, enabled]);
|
|
24
|
+
|
|
25
|
+
const allSelected = useMemo(() => {
|
|
26
|
+
if (items.length === 0) return false;
|
|
27
|
+
return items.every((item) => selectedKeys.has(getItemKey(item)));
|
|
28
|
+
}, [items, selectedKeys, getItemKey]);
|
|
29
|
+
|
|
30
|
+
const someSelected = useMemo(() => {
|
|
31
|
+
if (items.length === 0) return false;
|
|
32
|
+
const count = items.filter((item) =>
|
|
33
|
+
selectedKeys.has(getItemKey(item)),
|
|
34
|
+
).length;
|
|
35
|
+
return count > 0 && count < items.length;
|
|
36
|
+
}, [items, selectedKeys, getItemKey]);
|
|
37
|
+
|
|
38
|
+
const toggleItem = useCallback(
|
|
39
|
+
(item: T) => {
|
|
40
|
+
const key = getItemKey(item);
|
|
41
|
+
setSelectedKeys((prev) => {
|
|
42
|
+
const next = new Set(prev);
|
|
43
|
+
if (next.has(key)) next.delete(key);
|
|
44
|
+
else next.add(key);
|
|
45
|
+
return next;
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
[getItemKey],
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const toggleAll = useCallback(() => {
|
|
52
|
+
if (allSelected) {
|
|
53
|
+
setSelectedKeys((prev) => {
|
|
54
|
+
const next = new Set(prev);
|
|
55
|
+
for (const item of items) next.delete(getItemKey(item));
|
|
56
|
+
return next;
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
setSelectedKeys((prev) => {
|
|
60
|
+
const next = new Set(prev);
|
|
61
|
+
for (const item of items) next.add(getItemKey(item));
|
|
62
|
+
return next;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}, [allSelected, items, getItemKey]);
|
|
66
|
+
|
|
67
|
+
const clear = useCallback(() => setSelectedKeys(new Set()), []);
|
|
68
|
+
|
|
69
|
+
const isSelected = useCallback(
|
|
70
|
+
(item: T) => selectedKeys.has(getItemKey(item)),
|
|
71
|
+
[selectedKeys, getItemKey],
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
selectedItems,
|
|
76
|
+
allSelected,
|
|
77
|
+
someSelected,
|
|
78
|
+
toggleItem,
|
|
79
|
+
toggleAll,
|
|
80
|
+
clear,
|
|
81
|
+
isSelected,
|
|
82
|
+
};
|
|
83
|
+
};
|
package/src/core/styles.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Flex, Text, Title } from "@mantine/core";
|
|
2
2
|
import { IconBraces } from "@tabler/icons-react";
|
|
3
3
|
|
|
4
4
|
const components = [
|
|
@@ -13,16 +13,16 @@ const components = [
|
|
|
13
13
|
|
|
14
14
|
const DemoHome = () => {
|
|
15
15
|
return (
|
|
16
|
-
<
|
|
17
|
-
<
|
|
16
|
+
<Flex direction="column" gap="xl" p="xl">
|
|
17
|
+
<Flex>
|
|
18
18
|
<Title order={1} mb="xs">
|
|
19
19
|
Component Showcase
|
|
20
20
|
</Title>
|
|
21
21
|
<Text c="dimmed" size="lg">
|
|
22
22
|
Interactive demos and documentation for @alepha/ui components.
|
|
23
23
|
</Text>
|
|
24
|
-
</
|
|
25
|
-
</
|
|
24
|
+
</Flex>
|
|
25
|
+
</Flex>
|
|
26
26
|
);
|
|
27
27
|
};
|
|
28
28
|
|