Skip to content

Commit a43cd36

Browse files
committed
tc
1 parent 1ab651d commit a43cd36

File tree

10 files changed

+770
-60
lines changed

10 files changed

+770
-60
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
.draggingSource {
2+
opacity: 0.3;
3+
}
4+
5+
.dropTarget {
6+
background-color: #e8f0fe;
7+
}
8+
9+
.expandIconWrapper.isOpen {
10+
transform: rotate(90deg);
11+
}
12+
13+
.expandIconWrapper {
14+
align-items: center;
15+
font-size: 0;
16+
cursor: pointer;
17+
display: flex;
18+
justify-content: center;
19+
transform: rotate(0deg);
20+
margin: 0;
21+
padding: 0;
22+
}
23+
24+
.node {
25+
height: 32px;
26+
padding-inline-end: 8px;
27+
}
28+
.root {
29+
list-style: none;
30+
}
31+
.root ul {
32+
list-style: none;
33+
}
34+
.labelGridItem {
35+
padding-inline-start: 8px;
36+
}
37+
38+
.root li:hover {
39+
background-color: #f0f0f0;
40+
}
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
import { KeyboardArrowDown } from "@mui/icons-material";
2+
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
3+
import {
4+
Button,
5+
Dialog,
6+
DialogActions,
7+
DialogContent,
8+
List,
9+
ListItem,
10+
ListItemButton,
11+
Stack,
12+
Typography,
13+
} from "@mui/material";
14+
import { ValidatedTextField } from "components/common/ValidatedTextField";
15+
import { getDefaultGroupSettings } from "components/HudGroupingSettings/defaults";
16+
import * as React from "react";
17+
import { useState } from "react";
18+
import {
19+
getNonDupNewName,
20+
getStoredTreeData,
21+
Group,
22+
isDupName,
23+
saveTreeData,
24+
} from "./mainPageSettingsUtils";
25+
26+
function validRegex(value: string) {
27+
try {
28+
new RegExp(value);
29+
return true;
30+
} catch (e) {
31+
return false;
32+
}
33+
}
34+
35+
function EditSectionDialog({
36+
treeData,
37+
name,
38+
setGroup,
39+
}: {
40+
treeData: Group[];
41+
name: string;
42+
setGroup: (name: string, newName: string, regex: string) => void;
43+
}) {
44+
const [open, setOpen] = useState(false);
45+
46+
function isGoodName(value: string) {
47+
return value == name || !isDupName(treeData, value);
48+
}
49+
50+
return (
51+
<>
52+
<Button onClick={() => setOpen(true)}>Edit</Button>
53+
<Dialog
54+
open={open}
55+
closeAfterTransition={false}
56+
onClose={() => setOpen(false)}
57+
aria-modal
58+
>
59+
<DialogContent>
60+
<Stack
61+
spacing={2}
62+
component="form"
63+
noValidate
64+
autoComplete="off"
65+
sx={{
66+
"& .MuiTextField-root": {
67+
marginRight: 1,
68+
width: "25ch",
69+
},
70+
"& .MuiButton-root": {
71+
marginTop: 1,
72+
marginBottom: 1,
73+
marginLeft: 2,
74+
},
75+
}}
76+
onSubmit={(e) => {
77+
e.preventDefault();
78+
// @ts-ignore
79+
const regex = e.target[2].value;
80+
// @ts-ignore
81+
const newName = e.target[0].value;
82+
if (!validRegex(regex) || !isGoodName(newName)) {
83+
return;
84+
}
85+
setGroup(name, newName, regex);
86+
setOpen(false);
87+
}}
88+
>
89+
<ValidatedTextField
90+
name="Section name"
91+
isValid={isGoodName}
92+
initialValue={name}
93+
errorMessage="Cannot have duplicate names"
94+
/>
95+
<ValidatedTextField
96+
name="Filter"
97+
isValid={validRegex}
98+
initialValue={
99+
treeData.find((node) => node.name === name)?.regex.source ?? ""
100+
}
101+
errorMessage="Invalid regex"
102+
/>
103+
<DialogActions>
104+
<Button type="submit">Save and Close</Button>
105+
</DialogActions>
106+
</Stack>
107+
</DialogContent>
108+
</Dialog>
109+
</>
110+
);
111+
}
112+
113+
export default function SettingsModal({
114+
visible,
115+
handleClose,
116+
}: {
117+
visible: boolean;
118+
handleClose: () => void;
119+
}) {
120+
const [treeData, setTreeData] = useState(getStoredTreeData());
121+
const [orderBy, setOrderBy] = useState<"display" | "filter">("display");
122+
123+
function addSection() {
124+
setTreeData([
125+
{
126+
regex: new RegExp(""),
127+
name: getNonDupNewName(treeData),
128+
filterPriority: 0,
129+
displayPriority: 0,
130+
persistent: false,
131+
},
132+
...treeData.map((node) => {
133+
return {
134+
...node,
135+
filterPriority: node.filterPriority + 1,
136+
displayPriority: node.displayPriority + 1,
137+
};
138+
}),
139+
]);
140+
}
141+
142+
function removeSection(name: string) {
143+
setTreeData(treeData.filter((node) => node.name !== name));
144+
}
145+
146+
function moveItem(name: string, direction: "up" | "down") {
147+
const group = treeData.find((node) => node.name === name)!;
148+
const index =
149+
orderBy === "display" ? group.displayPriority : group.filterPriority;
150+
const swapWithIndex = index + (direction === "down" ? 1 : -1);
151+
152+
if (swapWithIndex < 0 || swapWithIndex >= treeData.length) {
153+
return;
154+
}
155+
const swapWith = treeData.find(
156+
(node) =>
157+
(orderBy === "display" ? node.displayPriority : node.filterPriority) ===
158+
swapWithIndex
159+
);
160+
161+
if (orderBy == "display") {
162+
group.displayPriority = swapWithIndex;
163+
swapWith!.displayPriority = index;
164+
} else {
165+
group.filterPriority = swapWithIndex;
166+
swapWith!.filterPriority = index;
167+
}
168+
setTreeData([...treeData]);
169+
}
170+
171+
function setItem(name: string, newName: string, regex: string) {
172+
setTreeData(
173+
treeData.map((node) => {
174+
if (node.name === name) {
175+
return {
176+
...node,
177+
regex: new RegExp(regex),
178+
name: newName,
179+
};
180+
}
181+
return node;
182+
})
183+
);
184+
}
185+
186+
const Node = React.memo(function Node({ data }: { data: Group }) {
187+
return (
188+
<ListItem>
189+
<ListItemButton>
190+
<Stack
191+
direction="row"
192+
alignItems="center"
193+
justifyContent="space-between"
194+
flexGrow={1}
195+
>
196+
<Stack>
197+
<Typography style={{ fontWeight: "bold" }}>
198+
{data.name}
199+
</Typography>
200+
<Typography>{data.regex.source}</Typography>
201+
</Stack>
202+
<Stack direction="row" alignItems={"center"}>
203+
<Button onClick={() => moveItem(data.name, "up")}>
204+
<KeyboardArrowUpIcon />
205+
</Button>
206+
<Button onClick={() => moveItem(data.name, "down")}>
207+
<KeyboardArrowDown />
208+
</Button>
209+
<EditSectionDialog
210+
treeData={treeData}
211+
name={data.name}
212+
setGroup={setItem}
213+
/>
214+
<Button onClick={(e) => removeSection(data.name)}>Delete</Button>
215+
</Stack>
216+
</Stack>
217+
</ListItemButton>
218+
</ListItem>
219+
);
220+
});
221+
222+
return (
223+
<Dialog
224+
open={visible}
225+
fullWidth={true}
226+
maxWidth="xl"
227+
onClose={handleClose}
228+
onClick={(e) => e.stopPropagation()}
229+
>
230+
<Stack
231+
spacing={2}
232+
direction="row"
233+
justifyContent="space-between"
234+
flexGrow={1}
235+
>
236+
<Stack spacing={2} direction="row">
237+
<Button onClick={addSection}>Add</Button>
238+
<Button
239+
onClick={() => {
240+
saveTreeData(treeData);
241+
handleClose();
242+
}}
243+
>
244+
Save
245+
</Button>
246+
<Button
247+
onClick={() => {
248+
saveTreeData(getDefaultGroupSettings());
249+
setTreeData(getDefaultGroupSettings());
250+
}}
251+
>
252+
Reset
253+
</Button>
254+
<Button
255+
onClick={() =>
256+
setOrderBy(orderBy == "display" ? "filter" : "display")
257+
}
258+
>
259+
Ordering by {orderBy} precedence
260+
</Button>
261+
</Stack>
262+
<Button onClick={handleClose} color={"error"}>
263+
Close
264+
</Button>
265+
</Stack>
266+
267+
<List>
268+
{treeData
269+
.sort((a, b) => {
270+
if (orderBy === "display") {
271+
return a.displayPriority - b.displayPriority;
272+
}
273+
return a.filterPriority - b.filterPriority;
274+
})
275+
.map((node) => (
276+
<Node key={node.name} data={node} />
277+
))}
278+
</List>
279+
</Dialog>
280+
);
281+
}

0 commit comments

Comments
 (0)