@alepha/ui 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/{AdminApiKeys-GMORg-1l.js → AdminApiKeys-CoTOTfgU.js} +4 -3
- package/dist/admin/{AdminApiKeys-GMORg-1l.js.map → AdminApiKeys-CoTOTfgU.js.map} +1 -1
- package/dist/admin/{AdminAudits-pkWrjq1Z.js → AdminAudits-BmsxFbDa.js} +4 -3
- package/dist/admin/{AdminAudits-pkWrjq1Z.js.map → AdminAudits-BmsxFbDa.js.map} +1 -1
- package/dist/admin/{AdminFiles-WeQbsCsl.js → AdminFiles-BBB8knca.js} +4 -3
- package/dist/admin/{AdminFiles-WeQbsCsl.js.map → AdminFiles-BBB8knca.js.map} +1 -1
- package/dist/admin/{AdminJobs-B-q9iGO3.js → AdminJobs-C604joTz.js} +4 -3
- package/dist/admin/{AdminJobs-B-q9iGO3.js.map → AdminJobs-C604joTz.js.map} +1 -1
- package/dist/admin/{AdminLayout-BqZiXx4H.js → AdminLayout-CsjvpeD1.js} +6 -9
- package/dist/admin/AdminLayout-CsjvpeD1.js.map +1 -0
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js → AdminNotifications-LwR6RKrx.js} +4 -3
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js.map → AdminNotifications-LwR6RKrx.js.map} +1 -1
- package/dist/admin/AdminParameters-B_83Vie9.js +767 -0
- package/dist/admin/AdminParameters-B_83Vie9.js.map +1 -0
- package/dist/admin/{AdminSessions-DzIOxM3b.js → AdminSessions-CWnPosdd.js} +4 -3
- package/dist/admin/{AdminSessions-DzIOxM3b.js.map → AdminSessions-CWnPosdd.js.map} +1 -1
- package/dist/admin/{AdminUserAudits-CiUPN2BC.js → AdminUserAudits-nHv636E_.js} +4 -3
- package/dist/admin/{AdminUserAudits-CiUPN2BC.js.map → AdminUserAudits-nHv636E_.js.map} +1 -1
- package/dist/admin/{AdminUserCreate-BwQKr4xE.js → AdminUserCreate-CjYD3Kjc.js} +4 -3
- package/dist/admin/{AdminUserCreate-BwQKr4xE.js.map → AdminUserCreate-CjYD3Kjc.js.map} +1 -1
- package/dist/admin/{AdminUserDetails-uqtC5aJ1.js → AdminUserDetails-Ccq-LsZ0.js} +4 -3
- package/dist/admin/{AdminUserDetails-uqtC5aJ1.js.map → AdminUserDetails-Ccq-LsZ0.js.map} +1 -1
- package/dist/admin/{AdminUserLayout-CiPay35T.js → AdminUserLayout-7s41DiF_.js} +6 -7
- package/dist/admin/AdminUserLayout-7s41DiF_.js.map +1 -0
- package/dist/admin/{AdminUserSessions-DAE8Nf1F.js → AdminUserSessions-Ds3ODq_d.js} +4 -3
- package/dist/admin/{AdminUserSessions-DAE8Nf1F.js.map → AdminUserSessions-Ds3ODq_d.js.map} +1 -1
- package/dist/admin/{AdminUserSettings-EbahaV2a.js → AdminUserSettings-CGh4gROo.js} +4 -3
- package/dist/admin/{AdminUserSettings-EbahaV2a.js.map → AdminUserSettings-CGh4gROo.js.map} +1 -1
- package/dist/admin/{AdminUsers-Dcjh0KNW.js → AdminUsers-CvPiBzQK.js} +4 -3
- package/dist/admin/{AdminUsers-Dcjh0KNW.js.map → AdminUsers-CvPiBzQK.js.map} +1 -1
- package/dist/admin/index.d.ts +22 -10
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +47 -48
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/rolldown-runtime-CjeV3_4I.js +18 -0
- package/dist/auth/{AuthLayout-Dj5K4SIN.js → AuthLayout-CdJcrPs4.js} +2 -3
- package/dist/auth/{AuthLayout-Dj5K4SIN.js.map → AuthLayout-CdJcrPs4.js.map} +1 -1
- package/dist/{demo/IconGoogle-CbBF8Hqq.js → auth/IconGoogle-Bm18QD2q.js} +2 -4
- package/dist/auth/{IconGoogle-DpSlPZ1u.js.map → IconGoogle-Bm18QD2q.js.map} +1 -1
- package/dist/auth/{Login-BBqTosqZ.js → Login-DS_OqA0G.js} +7 -6
- package/dist/auth/Login-DS_OqA0G.js.map +1 -0
- package/dist/auth/{Profile-Bxj8Nwom.js → Profile-Di7N7HZL.js} +2 -3
- package/dist/auth/{Profile-Bxj8Nwom.js.map → Profile-Di7N7HZL.js.map} +1 -1
- package/dist/auth/{Register-Ce675Crg.js → Register-BRR2_gux.js} +7 -6
- package/dist/auth/Register-BRR2_gux.js.map +1 -0
- package/dist/auth/{ResetPassword-DWdt7c40.js → ResetPassword-oQu72lod.js} +4 -3
- package/dist/auth/{ResetPassword-DWdt7c40.js.map → ResetPassword-oQu72lod.js.map} +1 -1
- package/dist/auth/{VerifyEmail-CI4JwByV.js → VerifyEmail-DC6HPZjd.js} +4 -3
- package/dist/auth/{VerifyEmail-CI4JwByV.js.map → VerifyEmail-DC6HPZjd.js.map} +1 -1
- package/dist/auth/index.d.ts +14 -14
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +13 -13
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/rolldown-runtime-CjeV3_4I.js +18 -0
- package/dist/core/index.d.ts +147 -68
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +349 -287
- package/dist/core/index.js.map +1 -1
- package/dist/demo/{DemoDataTable-CguplbR7.js → DemoDataTable-DCsJq8v5.js} +4 -5
- package/dist/demo/DemoDataTable-DCsJq8v5.js.map +1 -0
- package/dist/demo/{DemoHome-Cce2bWmg.js → DemoHome-DpRrPlBC.js} +4 -3
- package/dist/demo/{DemoHome-Cce2bWmg.js.map → DemoHome-DpRrPlBC.js.map} +1 -1
- package/dist/demo/{DemoJsonViewer-Dgdk3Txb.js → DemoJsonViewer-zeucGKHV.js} +6 -5
- package/dist/demo/DemoJsonViewer-zeucGKHV.js.map +1 -0
- package/dist/demo/{DemoLayout-B20TEuhV.js → DemoLayout-PhgbAAiQ.js} +6 -5
- package/dist/demo/DemoLayout-PhgbAAiQ.js.map +1 -0
- package/dist/demo/{DemoLogin-CvCG2WVh.js → DemoLogin-DSzP0Lkv.js} +8 -10
- package/dist/demo/DemoLogin-DSzP0Lkv.js.map +1 -0
- package/dist/demo/{DemoRegister-CmeHbOAs.js → DemoRegister-DavFBsCz.js} +8 -10
- package/dist/demo/DemoRegister-DavFBsCz.js.map +1 -0
- package/dist/demo/{DemoResetPassword-CKO5iA_6.js → DemoResetPassword-BS2rIAQK.js} +5 -7
- package/dist/demo/DemoResetPassword-BS2rIAQK.js.map +1 -0
- package/dist/demo/{DemoSidebar-MVmQKfMt.js → DemoSidebar-zNkUmHRl.js} +4 -5
- package/dist/demo/DemoSidebar-zNkUmHRl.js.map +1 -0
- package/dist/demo/{DemoTypeForm-w-qtfRlC.js → DemoTypeForm-B9q7oT0b.js} +4 -5
- package/dist/demo/DemoTypeForm-B9q7oT0b.js.map +1 -0
- package/dist/demo/{DemoVerifyEmail-C8FFJT5A.js → DemoVerifyEmail-Bi4SdWz0.js} +5 -7
- package/dist/demo/DemoVerifyEmail-Bi4SdWz0.js.map +1 -0
- package/dist/{auth/IconGoogle-DpSlPZ1u.js → demo/IconGoogle-CTeZyrek.js} +2 -4
- package/dist/demo/{IconGoogle-CbBF8Hqq.js.map → IconGoogle-CTeZyrek.js.map} +1 -1
- package/dist/demo/{Showcase-CQrMWars.js → Showcase-C9btr_SJ.js} +3 -5
- package/dist/demo/Showcase-C9btr_SJ.js.map +1 -0
- package/dist/demo/index.d.ts +2 -2
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +15 -15
- package/dist/demo/rolldown-runtime-CjeV3_4I.js +18 -0
- package/package.json +5 -3
- package/src/admin/AdminRouter.ts +15 -24
- package/src/admin/components/AdminLayout.tsx +6 -9
- package/src/admin/components/parameters/AdminParameters.tsx +154 -76
- package/src/admin/components/parameters/ParameterDetails.tsx +153 -93
- package/src/admin/components/parameters/ParameterEmptyState.tsx +27 -0
- package/src/admin/components/parameters/ParameterHistory.tsx +15 -20
- package/src/admin/components/parameters/ParameterTree.tsx +280 -104
- package/src/admin/components/parameters/types.ts +3 -3
- package/src/admin/primitives/$uiAdmin.ts +2 -2
- package/src/auth/AuthRouter.ts +1 -0
- package/src/core/components/buttons/ActionButton.tsx +4 -15
- package/src/core/components/buttons/DarkModeButton.tsx +8 -4
- package/src/core/components/buttons/ToggleSidebarButton.tsx +3 -5
- package/src/core/components/form/Control.tsx +10 -32
- package/src/core/components/form/ControlArray.tsx +200 -89
- package/src/core/components/form/TypeForm.browser.spec.tsx +727 -0
- package/src/core/components/layout/AlephaMantineProvider.tsx +1 -0
- package/src/core/components/layout/Breadcrumb.tsx +91 -0
- package/src/core/components/layout/{AdminShell.tsx → DashboardShell.tsx} +77 -32
- package/src/core/components/layout/Sidebar.tsx +58 -18
- package/src/core/constants/ui.ts +1 -1
- package/src/core/helpers/renderIcon.tsx +5 -2
- package/src/core/index.ts +9 -5
- package/src/core/styles.css +7 -7
- package/src/core/utils/string.ts +28 -4
- package/src/demo/components/DemoLayout.tsx +6 -2
- package/dist/admin/AdminApiKeys-DsmGnHNh.js +0 -3
- package/dist/admin/AdminAudits-8SM96viT.js +0 -3
- package/dist/admin/AdminFiles-B56ocq4H.js +0 -3
- package/dist/admin/AdminJobs-CED1syCn.js +0 -3
- package/dist/admin/AdminLayout-BqZiXx4H.js.map +0 -1
- package/dist/admin/AdminNotifications-B0B1rdc4.js +0 -3
- package/dist/admin/AdminParameters-BU3lATdJ.js +0 -3
- package/dist/admin/AdminParameters-CfDUpc78.js +0 -575
- package/dist/admin/AdminParameters-CfDUpc78.js.map +0 -1
- package/dist/admin/AdminSessions-BDGK2MS6.js +0 -3
- package/dist/admin/AdminUserAudits-Cj79gENT.js +0 -3
- package/dist/admin/AdminUserCreate-Cq-mUmBs.js +0 -3
- package/dist/admin/AdminUserDetails-DRjVAPFd.js +0 -3
- package/dist/admin/AdminUserLayout-CGzmHHby.js +0 -3
- package/dist/admin/AdminUserLayout-CiPay35T.js.map +0 -1
- package/dist/admin/AdminUserSessions-DcdzuNZ9.js +0 -3
- package/dist/admin/AdminUserSettings-D7V6-ceX.js +0 -3
- package/dist/admin/AdminUsers-D9nyzGqQ.js +0 -3
- package/dist/auth/Login-BBqTosqZ.js.map +0 -1
- package/dist/auth/Login-CoU63mMR.js +0 -4
- package/dist/auth/Register-BV_oa_AK.js +0 -4
- package/dist/auth/Register-Ce675Crg.js.map +0 -1
- package/dist/auth/ResetPassword-D5wC8GAA.js +0 -3
- package/dist/auth/VerifyEmail-DAfqVm5s.js +0 -3
- package/dist/demo/DemoDataTable-CguplbR7.js.map +0 -1
- package/dist/demo/DemoHome-DC9qkMNe.js +0 -3
- package/dist/demo/DemoJsonViewer-DIssGVlJ.js +0 -4
- package/dist/demo/DemoJsonViewer-Dgdk3Txb.js.map +0 -1
- package/dist/demo/DemoLayout-B20TEuhV.js.map +0 -1
- package/dist/demo/DemoLayout-DSRyf4qJ.js +0 -3
- package/dist/demo/DemoLogin-CvCG2WVh.js.map +0 -1
- package/dist/demo/DemoRegister-CmeHbOAs.js.map +0 -1
- package/dist/demo/DemoResetPassword-CKO5iA_6.js.map +0 -1
- package/dist/demo/DemoSidebar-MVmQKfMt.js.map +0 -1
- package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +0 -1
- package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +0 -1
- package/dist/demo/Showcase-CQrMWars.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionButton, Flex, Text } from "@alepha/ui";
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
|
-
|
|
4
|
+
Box,
|
|
5
5
|
Group,
|
|
6
6
|
Loader,
|
|
7
7
|
ScrollArea,
|
|
@@ -21,7 +21,6 @@ export interface ParameterHistoryProps {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const ParameterHistory = ({
|
|
24
|
-
selectedConfig,
|
|
25
24
|
history,
|
|
26
25
|
loading,
|
|
27
26
|
onRollback,
|
|
@@ -29,16 +28,6 @@ const ParameterHistory = ({
|
|
|
29
28
|
const { l } = useI18n();
|
|
30
29
|
|
|
31
30
|
const renderContent = () => {
|
|
32
|
-
if (!selectedConfig) {
|
|
33
|
-
return (
|
|
34
|
-
<Flex flex={1} justify="center" align="center">
|
|
35
|
-
<Text c="dimmed" size="xs">
|
|
36
|
-
Select a parameter
|
|
37
|
-
</Text>
|
|
38
|
-
</Flex>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
31
|
if (loading) {
|
|
43
32
|
return (
|
|
44
33
|
<Flex flex={1} justify="center" align="center">
|
|
@@ -51,7 +40,7 @@ const ParameterHistory = ({
|
|
|
51
40
|
return (
|
|
52
41
|
<Flex flex={1} justify="center" align="center">
|
|
53
42
|
<Text c="dimmed" size="xs">
|
|
54
|
-
|
|
43
|
+
Empty
|
|
55
44
|
</Text>
|
|
56
45
|
</Flex>
|
|
57
46
|
);
|
|
@@ -124,22 +113,28 @@ const ParameterHistory = ({
|
|
|
124
113
|
};
|
|
125
114
|
|
|
126
115
|
return (
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
w={300}
|
|
116
|
+
<Box
|
|
117
|
+
w={160}
|
|
130
118
|
h="100%"
|
|
131
|
-
|
|
119
|
+
p="xs"
|
|
120
|
+
style={{
|
|
121
|
+
flexShrink: 0,
|
|
122
|
+
overflow: "hidden",
|
|
123
|
+
display: "flex",
|
|
124
|
+
flexDirection: "column",
|
|
125
|
+
borderLeft: "1px solid var(--mantine-color-default-border)",
|
|
126
|
+
}}
|
|
132
127
|
>
|
|
133
|
-
<Stack gap="xs" h="100%">
|
|
128
|
+
<Stack gap="xs" h="100%" style={{ minHeight: 0 }}>
|
|
134
129
|
<Group gap="xs">
|
|
135
130
|
<IconHistory size={16} color="var(--mantine-color-dimmed)" />
|
|
136
131
|
<Text size="sm" fw={500}>
|
|
137
|
-
|
|
132
|
+
History
|
|
138
133
|
</Text>
|
|
139
134
|
</Group>
|
|
140
135
|
{renderContent()}
|
|
141
136
|
</Stack>
|
|
142
|
-
</
|
|
137
|
+
</Box>
|
|
143
138
|
);
|
|
144
139
|
};
|
|
145
140
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { ActionButton, Text } from "@alepha/ui";
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
|
-
|
|
4
|
+
Collapse,
|
|
5
5
|
Group,
|
|
6
6
|
ScrollArea,
|
|
7
7
|
Stack,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type TreeNodeData,
|
|
11
|
-
useTree,
|
|
8
|
+
TextInput,
|
|
9
|
+
UnstyledButton,
|
|
12
10
|
} from "@mantine/core";
|
|
13
11
|
import {
|
|
14
12
|
IconChevronDown,
|
|
@@ -16,130 +14,308 @@ import {
|
|
|
16
14
|
IconFolder,
|
|
17
15
|
IconFolderOpen,
|
|
18
16
|
IconRefresh,
|
|
17
|
+
IconSearch,
|
|
19
18
|
IconSettings,
|
|
20
19
|
} from "@tabler/icons-react";
|
|
21
|
-
import type {
|
|
22
|
-
import {
|
|
20
|
+
import type { ParameterTreeNode } from "alepha/api/parameters";
|
|
21
|
+
import { memo, useCallback, useMemo, useState } from "react";
|
|
23
22
|
|
|
24
23
|
export interface ParameterTreeProps {
|
|
25
|
-
treeData:
|
|
24
|
+
treeData: ParameterTreeNode[];
|
|
26
25
|
selectedConfig: string | null;
|
|
27
26
|
onSelect: (name: string) => void;
|
|
28
27
|
onRefresh: () => void;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Filters tree nodes by search query.
|
|
32
|
+
*/
|
|
33
|
+
const filterTree = (
|
|
34
|
+
nodes: ParameterTreeNode[],
|
|
35
|
+
query: string,
|
|
36
|
+
): ParameterTreeNode[] => {
|
|
37
|
+
if (!query.trim()) return nodes;
|
|
38
|
+
|
|
39
|
+
const lowerQuery = query.toLowerCase();
|
|
40
|
+
|
|
41
|
+
return nodes
|
|
42
|
+
.map((node) => {
|
|
43
|
+
const filteredChildren = filterTree(node.children, query);
|
|
44
|
+
const nameMatches = node.name.toLowerCase().includes(lowerQuery);
|
|
45
|
+
const pathMatches = node.path.toLowerCase().includes(lowerQuery);
|
|
46
|
+
|
|
47
|
+
if (nameMatches || pathMatches || filteredChildren.length > 0) {
|
|
48
|
+
return {
|
|
49
|
+
...node,
|
|
50
|
+
children: filteredChildren,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
})
|
|
56
|
+
.filter((node): node is ParameterTreeNode => node !== null);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface TreeNodeProps {
|
|
60
|
+
node: ParameterTreeNode;
|
|
61
|
+
level: number;
|
|
62
|
+
selectedConfig: string | null;
|
|
63
|
+
onSelect: (name: string) => void;
|
|
64
|
+
expandedNodes: Set<string>;
|
|
65
|
+
onToggle: (path: string) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Memoized tree node to prevent unnecessary re-renders.
|
|
70
|
+
*/
|
|
71
|
+
const TreeNode = memo(
|
|
72
|
+
({
|
|
53
73
|
node,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
level,
|
|
75
|
+
selectedConfig,
|
|
76
|
+
onSelect,
|
|
77
|
+
expandedNodes,
|
|
78
|
+
onToggle,
|
|
79
|
+
}: TreeNodeProps) => {
|
|
80
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
81
|
+
const hasChildren = node.children.length > 0;
|
|
82
|
+
const isExpanded = expandedNodes.has(node.path);
|
|
83
|
+
const isSelected = selectedConfig === node.path;
|
|
63
84
|
const isLeaf = !hasChildren;
|
|
64
|
-
|
|
85
|
+
|
|
86
|
+
const handleClick = useCallback(() => {
|
|
87
|
+
if (hasChildren) {
|
|
88
|
+
onToggle(node.path);
|
|
89
|
+
} else {
|
|
90
|
+
onSelect(node.path);
|
|
91
|
+
}
|
|
92
|
+
}, [hasChildren, node.path, onToggle, onSelect]);
|
|
93
|
+
|
|
94
|
+
const handleMouseEnter = useCallback(() => setIsHovered(true), []);
|
|
95
|
+
const handleMouseLeave = useCallback(() => setIsHovered(false), []);
|
|
65
96
|
|
|
66
97
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
<Box>
|
|
99
|
+
<UnstyledButton
|
|
100
|
+
onClick={handleClick}
|
|
101
|
+
onMouseEnter={handleMouseEnter}
|
|
102
|
+
onMouseLeave={handleMouseLeave}
|
|
103
|
+
w="100%"
|
|
104
|
+
style={{ display: "block" }}
|
|
105
|
+
>
|
|
106
|
+
<Group
|
|
107
|
+
gap={6}
|
|
108
|
+
wrap="nowrap"
|
|
109
|
+
p="4px 8px"
|
|
110
|
+
pl={8 + level * 16}
|
|
111
|
+
style={{
|
|
112
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
113
|
+
backgroundColor:
|
|
114
|
+
isSelected || isHovered
|
|
115
|
+
? "var(--mantine-color-default-hover)"
|
|
116
|
+
: undefined,
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{hasChildren ? (
|
|
120
|
+
<>
|
|
121
|
+
<Box
|
|
122
|
+
style={{
|
|
123
|
+
display: "flex",
|
|
124
|
+
alignItems: "center",
|
|
125
|
+
justifyContent: "center",
|
|
126
|
+
width: 16,
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{isExpanded ? (
|
|
130
|
+
<IconChevronDown
|
|
131
|
+
size={14}
|
|
132
|
+
color="var(--mantine-color-dimmed)"
|
|
133
|
+
/>
|
|
134
|
+
) : (
|
|
135
|
+
<IconChevronRight
|
|
136
|
+
size={14}
|
|
137
|
+
color="var(--mantine-color-dimmed)"
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</Box>
|
|
141
|
+
{isExpanded ? (
|
|
142
|
+
<IconFolderOpen
|
|
143
|
+
size={16}
|
|
144
|
+
color="var(--mantine-color-dimmed)"
|
|
145
|
+
style={{ flexShrink: 0 }}
|
|
146
|
+
/>
|
|
147
|
+
) : (
|
|
148
|
+
<IconFolder
|
|
149
|
+
size={16}
|
|
150
|
+
color="var(--mantine-color-dimmed)"
|
|
151
|
+
style={{ flexShrink: 0 }}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
98
155
|
) : (
|
|
99
|
-
|
|
156
|
+
<>
|
|
157
|
+
<Box w={16} />
|
|
158
|
+
<IconSettings
|
|
159
|
+
size={16}
|
|
160
|
+
color={
|
|
161
|
+
isSelected
|
|
162
|
+
? "var(--mantine-color-blue-6)"
|
|
163
|
+
: "var(--mantine-color-dimmed)"
|
|
164
|
+
}
|
|
165
|
+
style={{ flexShrink: 0 }}
|
|
166
|
+
/>
|
|
167
|
+
</>
|
|
100
168
|
)}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
169
|
+
<Text
|
|
170
|
+
size="sm"
|
|
171
|
+
fw={isSelected ? 600 : 400}
|
|
172
|
+
c={isSelected ? undefined : isLeaf ? undefined : "dimmed"}
|
|
173
|
+
style={{
|
|
174
|
+
whiteSpace: "nowrap",
|
|
175
|
+
overflow: "hidden",
|
|
176
|
+
textOverflow: "ellipsis",
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{node.name}
|
|
180
|
+
</Text>
|
|
181
|
+
</Group>
|
|
182
|
+
</UnstyledButton>
|
|
183
|
+
|
|
184
|
+
{hasChildren && (
|
|
185
|
+
<Collapse in={isExpanded}>
|
|
186
|
+
{node.children.map((child: ParameterTreeNode) => (
|
|
187
|
+
<TreeNode
|
|
188
|
+
key={child.path}
|
|
189
|
+
node={child}
|
|
190
|
+
level={level + 1}
|
|
191
|
+
selectedConfig={selectedConfig}
|
|
192
|
+
onSelect={onSelect}
|
|
193
|
+
expandedNodes={expandedNodes}
|
|
194
|
+
onToggle={onToggle}
|
|
195
|
+
/>
|
|
196
|
+
))}
|
|
197
|
+
</Collapse>
|
|
107
198
|
)}
|
|
108
|
-
|
|
109
|
-
{node.label}
|
|
110
|
-
</Text>
|
|
111
|
-
</Group>
|
|
199
|
+
</Box>
|
|
112
200
|
);
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
TreeNode.displayName = "TreeNode";
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Collects all folder paths to expand by default.
|
|
208
|
+
*/
|
|
209
|
+
const collectAllFolderPaths = (nodes: ParameterTreeNode[]): Set<string> => {
|
|
210
|
+
const paths = new Set<string>();
|
|
211
|
+
|
|
212
|
+
const traverse = (nodeList: ParameterTreeNode[]) => {
|
|
213
|
+
for (const node of nodeList) {
|
|
214
|
+
if (node.children.length > 0) {
|
|
215
|
+
paths.add(node.path);
|
|
216
|
+
traverse(node.children);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
113
219
|
};
|
|
114
220
|
|
|
221
|
+
traverse(nodes);
|
|
222
|
+
return paths;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const ParameterTree = ({
|
|
226
|
+
treeData,
|
|
227
|
+
selectedConfig,
|
|
228
|
+
onSelect,
|
|
229
|
+
onRefresh,
|
|
230
|
+
}: ParameterTreeProps) => {
|
|
231
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
232
|
+
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() =>
|
|
233
|
+
collectAllFolderPaths(treeData),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Filter tree by search query
|
|
237
|
+
const filteredTreeData = useMemo(
|
|
238
|
+
() => filterTree(treeData, searchQuery),
|
|
239
|
+
[treeData, searchQuery],
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const handleToggle = useCallback((path: string) => {
|
|
243
|
+
setExpandedNodes((prev) => {
|
|
244
|
+
const next = new Set(prev);
|
|
245
|
+
if (next.has(path)) {
|
|
246
|
+
next.delete(path);
|
|
247
|
+
} else {
|
|
248
|
+
next.add(path);
|
|
249
|
+
}
|
|
250
|
+
return next;
|
|
251
|
+
});
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
const handleSearchChange = useCallback(
|
|
255
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
256
|
+
setSearchQuery(e.currentTarget.value);
|
|
257
|
+
},
|
|
258
|
+
[],
|
|
259
|
+
);
|
|
260
|
+
|
|
115
261
|
return (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
262
|
+
<Box
|
|
263
|
+
w={280}
|
|
264
|
+
h="100%"
|
|
265
|
+
p="sm"
|
|
266
|
+
style={{
|
|
267
|
+
flexShrink: 0,
|
|
268
|
+
display: "flex",
|
|
269
|
+
flexDirection: "column",
|
|
270
|
+
borderRight: "1px solid var(--mantine-color-default-border)",
|
|
271
|
+
}}
|
|
272
|
+
>
|
|
273
|
+
<Stack gap="sm" h="100%" style={{ minHeight: 0 }}>
|
|
274
|
+
<Group justify="space-between" gap="xs">
|
|
275
|
+
<Text size="sm" fw={600}>
|
|
120
276
|
Parameters
|
|
121
277
|
</Text>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
</Tooltip>
|
|
278
|
+
<ActionButton
|
|
279
|
+
variant="subtle"
|
|
280
|
+
size="compact-xs"
|
|
281
|
+
onClick={onRefresh}
|
|
282
|
+
tooltip="Refresh"
|
|
283
|
+
>
|
|
284
|
+
<IconRefresh size={14} />
|
|
285
|
+
</ActionButton>
|
|
131
286
|
</Group>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
287
|
+
|
|
288
|
+
<TextInput
|
|
289
|
+
placeholder="Search..."
|
|
290
|
+
size="xs"
|
|
291
|
+
leftSection={<IconSearch size={14} />}
|
|
292
|
+
value={searchQuery}
|
|
293
|
+
onChange={handleSearchChange}
|
|
294
|
+
/>
|
|
295
|
+
|
|
296
|
+
<ScrollArea flex={1} offsetScrollbars style={{ minHeight: 0 }}>
|
|
297
|
+
{filteredTreeData.length === 0 ? (
|
|
298
|
+
<Text size="xs" c="dimmed" ta="center" py="md">
|
|
299
|
+
{searchQuery ? "No matching parameters" : "No parameters"}
|
|
300
|
+
</Text>
|
|
301
|
+
) : (
|
|
302
|
+
<Stack gap={0} style={{ gap: 1 }}>
|
|
303
|
+
{filteredTreeData.map((node) => (
|
|
304
|
+
<TreeNode
|
|
305
|
+
key={node.path}
|
|
306
|
+
node={node}
|
|
307
|
+
level={0}
|
|
308
|
+
selectedConfig={selectedConfig}
|
|
309
|
+
onSelect={onSelect}
|
|
310
|
+
expandedNodes={expandedNodes}
|
|
311
|
+
onToggle={handleToggle}
|
|
312
|
+
/>
|
|
313
|
+
))}
|
|
314
|
+
</Stack>
|
|
315
|
+
)}
|
|
140
316
|
</ScrollArea>
|
|
141
317
|
</Stack>
|
|
142
|
-
</
|
|
318
|
+
</Box>
|
|
143
319
|
);
|
|
144
320
|
};
|
|
145
321
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Parameter } from "alepha/api/parameters";
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface ParameterValue {
|
|
4
4
|
current?: Parameter;
|
|
5
5
|
next?: Parameter;
|
|
6
6
|
/**
|
|
7
|
-
* Default value from the registered $
|
|
7
|
+
* Default value from the registered $parameter primitive.
|
|
8
8
|
*/
|
|
9
9
|
defaultValue?: unknown;
|
|
10
10
|
/**
|
|
@@ -12,7 +12,7 @@ export interface ConfigValue {
|
|
|
12
12
|
*/
|
|
13
13
|
currentValue?: unknown;
|
|
14
14
|
/**
|
|
15
|
-
* TypeBox/JSON schema for the
|
|
15
|
+
* TypeBox/JSON schema for the parameter (as JSON from API).
|
|
16
16
|
*/
|
|
17
17
|
schema?: Record<string, unknown>;
|
|
18
18
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DashboardShellProps } from "@alepha/ui";
|
|
2
2
|
import { $context } from "alepha";
|
|
3
3
|
import { AdminRouter } from "../AdminRouter.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register Admin UI components and get the AdminRouter instance.
|
|
7
7
|
*/
|
|
8
|
-
export const $uiAdmin = (optsFn?: (a: AdminRouter) =>
|
|
8
|
+
export const $uiAdmin = (optsFn?: (a: AdminRouter) => DashboardShellProps) => {
|
|
9
9
|
const { alepha } = $context();
|
|
10
10
|
const adminRouter = alepha.inject(AdminRouter);
|
|
11
11
|
|
package/src/auth/AuthRouter.ts
CHANGED
|
@@ -243,17 +243,14 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
243
243
|
const props = { ..._props };
|
|
244
244
|
const { tooltip, menu, icon, ...restProps } = props;
|
|
245
245
|
|
|
246
|
-
if (props.variant === "subtle") {
|
|
247
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
246
|
+
if (props.variant === "subtle" || props.variant === "outline") {
|
|
248
247
|
restProps.color ??= "gray";
|
|
249
248
|
}
|
|
250
249
|
|
|
251
250
|
if (props.intent) {
|
|
252
251
|
if (props.intent === "none") {
|
|
253
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
254
252
|
restProps.color ??= "gray";
|
|
255
253
|
} else if (props.intent === "primary") {
|
|
256
|
-
restProps.c ??= "white";
|
|
257
254
|
restProps.color ??= theme.primaryColor;
|
|
258
255
|
} else if (props.intent === "success") {
|
|
259
256
|
restProps.c ??= "white";
|
|
@@ -262,7 +259,6 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
262
259
|
restProps.c ??= "white";
|
|
263
260
|
restProps.color ??= "red";
|
|
264
261
|
} else if (props.intent === "warning") {
|
|
265
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
266
262
|
restProps.color ??= "yellow";
|
|
267
263
|
} else if (props.intent === "info") {
|
|
268
264
|
restProps.c ??= "white";
|
|
@@ -271,18 +267,11 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
271
267
|
}
|
|
272
268
|
|
|
273
269
|
if (props.icon) {
|
|
270
|
+
const sizes = ui.sizes.icon as Record<string, number>;
|
|
274
271
|
const icon = isComponentType(props.icon) ? (
|
|
275
|
-
<props.icon size={
|
|
272
|
+
<props.icon size={sizes[props.size || "md"]} />
|
|
276
273
|
) : (
|
|
277
|
-
<
|
|
278
|
-
w={24} // TODO: make size configurable
|
|
279
|
-
variant={"transparent"}
|
|
280
|
-
size={"sm"}
|
|
281
|
-
c={"var(--mantine-color-text)"}
|
|
282
|
-
{...props.themeIconProps}
|
|
283
|
-
>
|
|
284
|
-
{props.icon as ReactNode}
|
|
285
|
-
</ThemeIcon>
|
|
274
|
+
<span>{props.icon as ReactNode}</span>
|
|
286
275
|
);
|
|
287
276
|
|
|
288
277
|
if (!props.children) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMantineColorScheme } from "@mantine/core";
|
|
2
2
|
import { IconMoon, IconSun } from "@tabler/icons-react";
|
|
3
|
+
import { ui } from "../../constants/ui.ts";
|
|
3
4
|
import ActionButton, { type ActionProps } from "./ActionButton.tsx";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -19,17 +20,20 @@ const DarkModeButton = (props: Partial<ActionProps>) => {
|
|
|
19
20
|
setColorScheme(current === "dark" ? "light" : "dark");
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
const size = props.size ?? "md";
|
|
24
|
+
const iconSize =
|
|
25
|
+
(ui.sizes.icon as Record<string, number>)[size] ?? ui.sizes.icon.md;
|
|
26
|
+
|
|
22
27
|
return (
|
|
23
28
|
<ActionButton
|
|
24
29
|
onClick={toggleColorScheme}
|
|
25
30
|
variant={props.variant ?? "subtle"}
|
|
26
|
-
size={
|
|
31
|
+
size={size}
|
|
27
32
|
aria-label="Toggle color scheme"
|
|
28
|
-
px={"xs"}
|
|
29
33
|
icon={
|
|
30
34
|
<>
|
|
31
|
-
<IconSun className="alepha-light-hidden" />
|
|
32
|
-
<IconMoon className="alepha-dark-hidden" />
|
|
35
|
+
<IconSun size={iconSize} className="alepha-light-hidden" />
|
|
36
|
+
<IconMoon size={iconSize} className="alepha-dark-hidden" />
|
|
33
37
|
</>
|
|
34
38
|
}
|
|
35
39
|
{...props}
|
|
@@ -14,11 +14,9 @@ const ToggleSidebarButton = (props: Props) => {
|
|
|
14
14
|
return (
|
|
15
15
|
<ActionButton
|
|
16
16
|
icon={
|
|
17
|
-
sidebar.collapsed
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<IconLayoutSidebarLeftCollapse />
|
|
21
|
-
)
|
|
17
|
+
sidebar.collapsed
|
|
18
|
+
? IconLayoutSidebarRightCollapse
|
|
19
|
+
: IconLayoutSidebarLeftCollapse
|
|
22
20
|
}
|
|
23
21
|
visibleFrom={"sm"}
|
|
24
22
|
variant={"subtle"}
|