import { useState, useEffect } from "react";
import CircularProgress from "@mui/material/CircularProgress";
import Box from "@mui/material/Box";
import FormControl from "@mui/material/FormControl";
import FormHelperText from "@mui/material/FormHelperText";
import InputLabel from "@mui/material/InputLabel";
import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Chip from "@mui/material/Chip";
import Dialog /*, { DialogProps }*/ from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import InfoOutlined from "@mui/icons-material/InfoOutlined";
import Grid from "@mui/material/Grid";
import CGroupCheckbox from "./CGroupCheckbox";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
keepMounted: true,
};
function CGroupDialog({
cgroups,
open,
onClose: handleClose,
onSelect: handleSelect,
selected,
}: {
cgroups: string[];
open: boolean;
onClose: () => void;
onSelect: (cgroups: string[]) => void;
selected: string[];
}) {
// Note: For performance, Checkboxes are memoized, resulting in captured states to be stale
// when get updated. We have to use transition functions for setState.
// We maintain a additional somewhat duplicate state here to avoid receive a complex
// onSelect callback from the upstream, for ergonomics.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_selected_, setSelected_] = useState([] as string[]);
const handleCheck = (name: string, checked: boolean) => {
setSelected_((prev: string[]) => {
const set = new Set(prev);
if (checked) {
set.add(name);
} else {
set.delete(name);
}
handleSelect(Array.from(set));
return Array.from(set);
});
};
// this might trigger an extra unnecessary render. but it won't cause an actually trouble
// eslint-disable-next-line react-hooks/set-state-in-effect
useEffect(() => setSelected_(selected), [selected]);
const handleClear = () => {
handleSelect([]);
};
const handleInvert = () => {
const set = new Set(selected);
handleSelect(cgroups.filter((name) => !set.has(name)));
};
return (
<Dialog
open={open}
onClose={handleClose}
scroll="paper"
aria-labelledby="cgroups-dialog-title"
aria-describedby="cgroups-dialog-description"
keepMounted
>
<DialogTitle id="cgroups-dialog-title">
<Box display="flex" alignItems="center" gap={1}>
<span>CGroups / 轉換組</span>
<IconButton
size="small"
href="https://zh.wikipedia.org/wiki/WP:CGROUP"
target="_blank"
rel="noopener"
color="primary"
sx={{ p: 0.5 }}
>
<InfoOutlined fontSize="small" />
</IconButton>
</Box>
</DialogTitle>
<DialogContent dividers>
{cgroups.map((name) => (
<CGroupCheckbox
key={name}
name={name}
checked={selected.indexOf(name) > -1}
onCheck={handleCheck}
/>
// <FormControlLabel key={name} control={<input type="checkbox" id={name} name={name} value={name} />} label={name} />
))}
<FormHelperText>Press Ctrl + F to search</FormHelperText>
</DialogContent>
<DialogActions>
<Grid container direction="row" justifyContent="space-between">
<Grid>
<Button onClick={handleClear} color="primary">
Clear / 清空
</Button>
<Button onClick={handleInvert} color="primary">
Invert / 反選
</Button>
</Grid>
<Grid>
<Button onClick={handleClose} color="secondary">
Ok / 好
</Button>
</Grid>
</Grid>
</DialogActions>
</Dialog>
);
}
export default function CGroupSelect({
cgroups,
selected,
onSelect: handleSelect,
disabled,
}: {
cgroups: string[] | null;
selected: string[];
onSelect: (selected: string[]) => void;
disabled?: boolean;
}) {
const loading = cgroups === null;
const isDisabled = loading || disabled;
const [dialogOpen, setDialogOpen] = useState(false);
// const handleDelete = (name: string) => {
// const set = new Set(selected);
// set.delete(name);
// handleSelect(Array.from(set));
// };
return (
<>
<FormControl variant="standard" sx={{ m: 1 }}>
<InputLabel id="cgroups-select-label" color="primary">CGroups / 轉換組</InputLabel>
<Select
labelId="cgroups-select-label"
id="cgroups-select"
multiple
value={selected.length > 0 ? selected : ["placeholder"]}
open={false}
onOpen={() => setDialogOpen(true)}
style={{ width: "100%" }}
fullWidth={true}
disabled={isDisabled}
color="primary"
// input={<Input id="select-multiple-chip" />}
renderValue={(selected) => (
<Box
sx={{
display: "flex",
justifyContent: "center",
flexWrap: "wrap",
listStyle: "none",
p: 0.5,
m: 0,
}}
>
{loading ? (
<CircularProgress size={24} color="inherit" />
) : (selected as string[]).length === 0 ||
(selected as string[])[0] === "placeholder" ? (
<Chip
key="add more"
label="Select ... / 選擇 ..."
color="primary"
sx={{ m: 0.3, cursor: "pointer" }}
variant="outlined"
/>
) : (
(selected as string[]).map((name) => (
<Chip
key={name}
label={name}
// the ondelete event seems to be shadowed anyway
// onDelete={(event) => {
// event.preventDefault();
// event.stopPropagation();
// handleDelete(name);
// }}
sx={{ m: 0.3, cursor: "pointer" }}
variant="outlined"
/>
))
)}
</Box>
)}
MenuProps={MenuProps}
>
{selected.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
<CGroupDialog
cgroups={cgroups || []}
selected={selected}
onSelect={handleSelect}
open={dialogOpen}
onClose={() => setDialogOpen(false)}
/>
</>
);
}