189 lines
5.9 KiB
TypeScript
189 lines
5.9 KiB
TypeScript
import {
|
|
Avatar,
|
|
Button,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogContentText,
|
|
DialogTitle,
|
|
List,
|
|
ListItem,
|
|
ListItemAvatar,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
ListSubheader,
|
|
Typography,
|
|
Theme
|
|
} from "@mui/material";
|
|
import { makeStyles } from "@mui/styles";
|
|
import { DragHandle as DragIcon } from "@mui/icons-material";
|
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
|
import { useDeviceAPI, usePrevious, useSnackbar } from "hooks";
|
|
import { cloneDeep } from "lodash";
|
|
import { Component, Device } from "models";
|
|
import { sortComponents } from "pbHelpers/Component";
|
|
import { pond, quack } from "protobuf-ts/pond";
|
|
import { useEffect, useState } from "react";
|
|
import { DragDropContext, Draggable, Droppable, DropResult } from "react-beautiful-dnd";
|
|
import { useThemeType } from "../hooks/useThemeType";
|
|
import { useGlobalState } from "providers";
|
|
|
|
const useStyles = makeStyles((_theme: Theme) => {
|
|
const themeType = useThemeType()
|
|
return ({
|
|
item: {
|
|
backgroundColor: "rgba(0,0,0,0)",
|
|
"&:hover": {
|
|
backgroundColor:
|
|
themeType === "light" ? "rgba(0,0,0,0.07)" : "rgba(255,255,255,0.07)"
|
|
}
|
|
}
|
|
})}
|
|
);
|
|
|
|
interface ItemProps {
|
|
component: Component;
|
|
index: number;
|
|
}
|
|
|
|
function ComponentItem(props: ItemProps) {
|
|
const classes = useStyles();
|
|
const { component, index } = props;
|
|
const name = component.name();
|
|
return (
|
|
<Draggable draggableId={component.key()} index={index}>
|
|
{provided => (
|
|
<ListItem
|
|
ref={provided.innerRef}
|
|
{...provided.draggableProps}
|
|
{...provided.dragHandleProps}
|
|
className={classes.item}>
|
|
<ListItemIcon>
|
|
<DragIcon />
|
|
</ListItemIcon>
|
|
<ListItemText primary={<Typography color="textPrimary">{name}</Typography>} />
|
|
<ListItemAvatar>
|
|
<Avatar alt={name}>{index + 1}</Avatar>
|
|
</ListItemAvatar>
|
|
</ListItem>
|
|
)}
|
|
</Draggable>
|
|
);
|
|
}
|
|
|
|
interface ListProps {
|
|
open: boolean;
|
|
close: (refresh: boolean) => void;
|
|
device: Device;
|
|
components: Component[];
|
|
devicePreferences: pond.DevicePreferences;
|
|
}
|
|
|
|
const filteredComponents = (components: Component[]) => {
|
|
const componentBlacklist = [
|
|
quack.ComponentType.COMPONENT_TYPE_POWER,
|
|
quack.ComponentType.COMPONENT_TYPE_MODEM
|
|
];
|
|
return components.filter(c => !componentBlacklist.includes(c.settings.type));
|
|
};
|
|
|
|
export default function ComponentOrder(props: ListProps) {
|
|
const { open, close, device, devicePreferences } = props;
|
|
const [{as}] = useGlobalState();
|
|
const deviceAPI = useDeviceAPI();
|
|
const { error, success } = useSnackbar();
|
|
const prevDisplayOrder = usePrevious(devicePreferences.childDisplayOrder);
|
|
const [components, setComponents] = useState<Component[]>(
|
|
Array.from(filteredComponents(props.components).values()).sort((a, b: Component) =>
|
|
sortComponents(a, b, devicePreferences.childDisplayOrder)
|
|
)
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
props.components.length !== components.length ||
|
|
prevDisplayOrder !== devicePreferences.childDisplayOrder
|
|
) {
|
|
setComponents(
|
|
Array.from(filteredComponents(props.components).values()).sort((a, b: Component) =>
|
|
sortComponents(a, b, devicePreferences.childDisplayOrder)
|
|
)
|
|
);
|
|
}
|
|
}, [props.components, prevDisplayOrder, devicePreferences.childDisplayOrder, components.length]);
|
|
|
|
const updateComponentOrder = (componentOrder: Component[]) => {
|
|
let updatedPreferences = cloneDeep(devicePreferences);
|
|
updatedPreferences.childDisplayOrder = componentOrder.map(c => c.locationString());
|
|
deviceAPI
|
|
.updatePreferences(device.id(), updatedPreferences, undefined, undefined, as)
|
|
.then(() => success("Successfully update the component display order"))
|
|
.catch(() => {
|
|
error("Error occured while updating the component display order");
|
|
})
|
|
.finally(() => close(true));
|
|
};
|
|
|
|
const submit = () => {
|
|
updateComponentOrder(components);
|
|
};
|
|
|
|
const reorder = (components: Component[], startIndex: number, endIndex: number): Component[] => {
|
|
const result = Array.from(components);
|
|
const [removed] = result.splice(startIndex, 1);
|
|
result.splice(endIndex, 0, removed);
|
|
return result;
|
|
};
|
|
|
|
const onDragEnd = (result: DropResult) => {
|
|
if (!result.destination) {
|
|
return;
|
|
}
|
|
setComponents(reorder(components, result.source.index, result.destination.index));
|
|
};
|
|
|
|
return (
|
|
<ResponsiveDialog open={open} onClose={() => close(false)} maxWidth="sm" fullWidth>
|
|
<DialogTitle id="component-order-dialog-title">
|
|
Component Display Order
|
|
<Typography variant="body2" color="textSecondary">
|
|
{device.name()}
|
|
</Typography>
|
|
</DialogTitle>
|
|
<DialogContent>
|
|
{components.length < 1 ? (
|
|
<DialogContentText>No components to reorder</DialogContentText>
|
|
) : (
|
|
<DragDropContext onDragEnd={onDragEnd}>
|
|
<Droppable droppableId="list">
|
|
{provided => (
|
|
<List
|
|
ref={provided.innerRef}
|
|
{...provided.droppableProps}
|
|
subheader={
|
|
<ListSubheader disableSticky>
|
|
<Typography variant="body2" color="textSecondary">
|
|
Drag and Drop to change the display order
|
|
</Typography>
|
|
</ListSubheader>
|
|
}>
|
|
{components.map((component, index) => (
|
|
<ComponentItem key={component.key()} component={component} index={index} />
|
|
))}
|
|
{provided.placeholder}
|
|
</List>
|
|
)}
|
|
</Droppable>
|
|
</DragDropContext>
|
|
)}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => close(false)} color="primary">
|
|
Close
|
|
</Button>
|
|
<Button onClick={submit} color="primary">
|
|
Submit
|
|
</Button>
|
|
</DialogActions>
|
|
</ResponsiveDialog>
|
|
);
|
|
}
|