|
1 | 1 | import React, { useCallback, useState } from "react"; |
2 | | -import { Box, Button, Stack, TextField, Typography } from "@mui/material"; |
| 2 | +import { |
| 3 | + Box, |
| 4 | + Button, |
| 5 | + TextField, |
| 6 | + Paper, |
| 7 | + List, |
| 8 | + ListItem, |
| 9 | + ListItemText, |
| 10 | + IconButton, |
| 11 | + InputAdornment, |
| 12 | + Alert, |
| 13 | +} from "@mui/material"; |
| 14 | +import { AddRounded, DeleteRounded, DevicesRounded } from "@mui/icons-material"; |
3 | 15 | import { useConfig } from "../hooks/useConfig"; |
4 | 16 | import { ContentLayout } from "../components/layouts/ContentLayout"; |
5 | 17 |
|
@@ -36,81 +48,100 @@ export function WLEDDeviceSelector() { |
36 | 48 | [alreadySelectedDevices], |
37 | 49 | ); |
38 | 50 |
|
| 51 | + const handleSubmit = useCallback(() => { |
| 52 | + if (isValidDevice(inputValue)) { |
| 53 | + handleAddDevice(inputValue); |
| 54 | + setInputValue(""); |
| 55 | + } |
| 56 | + }, [inputValue, isValidDevice, handleAddDevice]); |
| 57 | + |
39 | 58 | return ( |
40 | | - <ContentLayout title="Configure WLED Devices" titleVariant="h2" container> |
| 59 | + <ContentLayout container title="" hideTitle> |
41 | 60 | <Box |
42 | | - display="flex" |
43 | | - flexDirection="column" |
44 | | - justifyContent="center" |
45 | | - alignItems="center" |
46 | | - padding={2} |
| 61 | + sx={{ |
| 62 | + display: "flex", |
| 63 | + flexDirection: "column", |
| 64 | + gap: 2, |
| 65 | + my: 2, |
| 66 | + }} |
47 | 67 | > |
48 | | - <Stack spacing={2} direction="row" alignItems="center"> |
49 | | - <TextField |
50 | | - label="WLED Device IP/Hostname" |
51 | | - error={(inputValue && !isValidDevice(inputValue)) as boolean} |
52 | | - // helperText={ |
53 | | - // (inputValue && |
54 | | - // !isValidDevice(inputValue) && |
55 | | - // "Invalid IP address/hostname or already added") || |
56 | | - // undefined |
57 | | - // } causes bug in button alignment |
58 | | - value={inputValue} |
59 | | - onChange={(e) => setInputValue(e.target.value)} |
60 | | - onKeyDown={(e) => { |
61 | | - if (e.key === "Enter" && isValidDevice(inputValue)) { |
62 | | - handleAddDevice(inputValue); |
63 | | - setInputValue(""); |
64 | | - } |
65 | | - }} |
66 | | - /> |
67 | | - |
68 | | - <Button |
69 | | - variant="contained" |
70 | | - disabled={!isValidDevice(inputValue)} |
71 | | - sx={{ alignSelf: "center" }} |
72 | | - onClick={() => { |
73 | | - if (isValidDevice(inputValue)) { |
74 | | - handleAddDevice(inputValue); |
75 | | - setInputValue(""); |
76 | | - } |
77 | | - }} |
78 | | - > |
79 | | - Add |
80 | | - </Button> |
81 | | - </Stack> |
| 68 | + <TextField |
| 69 | + placeholder="Enter WLED device IP or hostname..." |
| 70 | + value={inputValue} |
| 71 | + onChange={(e) => setInputValue(e.target.value)} |
| 72 | + onKeyDown={(e) => { |
| 73 | + if (e.key === "Enter") { |
| 74 | + handleSubmit(); |
| 75 | + } |
| 76 | + }} |
| 77 | + error={inputValue !== "" && !isValidDevice(inputValue)} |
| 78 | + helperText={ |
| 79 | + inputValue !== "" && !isValidDevice(inputValue) |
| 80 | + ? "Invalid IP/hostname or already added" |
| 81 | + : " " |
| 82 | + } |
| 83 | + fullWidth |
| 84 | + slotProps={{ |
| 85 | + input: { |
| 86 | + startAdornment: ( |
| 87 | + <InputAdornment position="start"> |
| 88 | + <DevicesRounded /> |
| 89 | + </InputAdornment> |
| 90 | + ), |
| 91 | + endAdornment: ( |
| 92 | + <InputAdornment position="end"> |
| 93 | + <Button |
| 94 | + variant="contained" |
| 95 | + disabled={!isValidDevice(inputValue)} |
| 96 | + onClick={handleSubmit} |
| 97 | + startIcon={<AddRounded />} |
| 98 | + > |
| 99 | + Add |
| 100 | + </Button> |
| 101 | + </InputAdornment> |
| 102 | + ), |
| 103 | + }, |
| 104 | + }} |
| 105 | + sx={{ maxWidth: 600 }} |
| 106 | + /> |
82 | 107 |
|
83 | | - <Typography variant="h4" gutterBottom sx={{ marginTop: 5 }}> |
84 | | - Added Devices |
85 | | - </Typography> |
86 | | - |
87 | | - {alreadySelectedDevices.length === 0 && ( |
88 | | - <Typography variant="body1" gutterBottom color="text.secondary"> |
89 | | - No devices added |
90 | | - </Typography> |
| 108 | + {alreadySelectedDevices.length > 0 ? ( |
| 109 | + <Paper elevation={2}> |
| 110 | + <List disablePadding> |
| 111 | + {alreadySelectedDevices.map((deviceIp, index) => ( |
| 112 | + <ListItem |
| 113 | + key={deviceIp} |
| 114 | + divider={index < alreadySelectedDevices.length - 1} |
| 115 | + secondaryAction={ |
| 116 | + <IconButton |
| 117 | + edge="end" |
| 118 | + aria-label="delete" |
| 119 | + onClick={() => handleRemoveDevice(deviceIp)} |
| 120 | + color="error" |
| 121 | + > |
| 122 | + <DeleteRounded /> |
| 123 | + </IconButton> |
| 124 | + } |
| 125 | + > |
| 126 | + <ListItemText |
| 127 | + primary={deviceIp} |
| 128 | + slotProps={{ |
| 129 | + primary: { |
| 130 | + fontFamily: "monospace", |
| 131 | + fontSize: "0.95rem", |
| 132 | + }, |
| 133 | + }} |
| 134 | + /> |
| 135 | + </ListItem> |
| 136 | + ))} |
| 137 | + </List> |
| 138 | + </Paper> |
| 139 | + ) : ( |
| 140 | + <Alert severity="info"> |
| 141 | + No devices added yet. Enter an IP address or hostname above to add a |
| 142 | + WLED device. |
| 143 | + </Alert> |
91 | 144 | )} |
92 | | - |
93 | | - <Stack spacing={2}> |
94 | | - {alreadySelectedDevices.map((deviceIp) => ( |
95 | | - <Box key={deviceIp} display="flex" justifyContent="space-between"> |
96 | | - <Typography |
97 | | - variant="body1" |
98 | | - sx={{ alignSelf: "center", overflow: "hidden", mr: 2 }} |
99 | | - > |
100 | | - {deviceIp} |
101 | | - </Typography> |
102 | | - <Button |
103 | | - variant="contained" |
104 | | - color="error" |
105 | | - size="small" |
106 | | - sx={{ height: "100%", alignSelf: "center" }} |
107 | | - onClick={() => handleRemoveDevice(deviceIp)} |
108 | | - > |
109 | | - Remove |
110 | | - </Button> |
111 | | - </Box> |
112 | | - ))} |
113 | | - </Stack> |
114 | 145 | </Box> |
115 | 146 | </ContentLayout> |
116 | 147 | ); |
|
0 commit comments