mirror of
https://github.com/responsively-org/responsively-app
synced 2024-11-14 16:37:27 +00:00
a lot of improvements
- allow edit custom profiles - improve styles - remove test custom profile
This commit is contained in:
parent
0b0b6e2a02
commit
30e6897d08
4 changed files with 137 additions and 60 deletions
|
@ -1,4 +1,4 @@
|
|||
import React, {useState, useRef} from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import cx from 'classnames';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Table from '@material-ui/core/Table';
|
||||
|
@ -12,6 +12,7 @@ import CancelOutlinedIcon from '@material-ui/icons/CancelOutlined';
|
|||
import AddCircleOutlineOutlinedIcon from '@material-ui/icons/AddCircleOutlineOutlined';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import NumberFormat from 'react-number-format';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
|
||||
import commonStyles from '../../common.styles.css';
|
||||
import styles from './styles.css';
|
||||
|
@ -41,9 +42,9 @@ export default function ProfileManager({
|
|||
profiles,
|
||||
onSave
|
||||
}) {
|
||||
const tableRef = useRef(null);
|
||||
const [currentProfiles, updateProfiles] = useState(profiles);
|
||||
const [newElement, setNewElement] = useState({type: 'Custom'});
|
||||
const [editModeRows, toggleEditModeRows] = useState({})
|
||||
|
||||
const newElementIsInvalid = (newElement.title != null && (newElement.title.trim() == "" || newElement.title.length > 20 || currentProfiles.filter(x => x.title === newElement.title).length !== 0));
|
||||
|
||||
|
@ -51,8 +52,6 @@ export default function ProfileManager({
|
|||
if (!newElementIsInvalid && newElement.title != null) {
|
||||
setNewElement({type: 'Custom'});
|
||||
updateProfiles([...currentProfiles, newElement]);
|
||||
tableRef.current.scrollTop = 6;
|
||||
console.log(tableRef.current);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +59,23 @@ export default function ProfileManager({
|
|||
updateProfiles(currentProfiles.filter(p => p.title !== title));
|
||||
}
|
||||
|
||||
const updateProfile = (title, key, value) => {
|
||||
const profile = currentProfiles.find(x => x.title === title);
|
||||
if (profile != null && profile.type === 'Custom') {
|
||||
profile[key] = value;
|
||||
updateProfiles([...currentProfiles]);
|
||||
}
|
||||
}
|
||||
|
||||
const toggleEditMode = (row) => {
|
||||
if (row.type !== 'Custom') return;
|
||||
editModeRows[row.title] = !editModeRows[row.title];
|
||||
toggleEditModeRows({...editModeRows});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(styles.profileManagerContainer)}>
|
||||
<TableContainer ref={tableRef} className={cx(styles.profilesContainer)}>
|
||||
<TableContainer className={cx(styles.profilesContainer)}>
|
||||
<Table size="small">
|
||||
<TableHead className={cx(styles.profilesHeader)}>
|
||||
<TableRow>
|
||||
|
@ -70,18 +83,66 @@ export default function ProfileManager({
|
|||
<TableCell style={{ width: "21%" }} align="right">Download</TableCell>
|
||||
<TableCell style={{ width: "21%" }} align="right">Upload</TableCell>
|
||||
<TableCell style={{ width: "21%" }} align="right">Latency</TableCell>
|
||||
<TableCell style={{ width: "5%" }} align="right"></TableCell>
|
||||
<TableCell style={{ width: "5%" }} align="right"/>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{currentProfiles.map((row) => (
|
||||
<TableRow key={row.title} className={cx(styles.profilesRow)}>
|
||||
<TableCell component="th" scope="row">
|
||||
<TableCell component="th" scope="row" className={cx({[styles.customProfile]: row.type === 'Custom'})} onClick={() => toggleEditMode(row)}>
|
||||
{row.title}
|
||||
</TableCell>
|
||||
<TableCell align="right">{row.downloadKps}</TableCell>
|
||||
<TableCell align="right">{row.uploadKps}</TableCell>
|
||||
<TableCell align="right">{row.latencyMs}</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.type !== 'Online' &&
|
||||
<TextField
|
||||
className={cx(styles.numericField, {[styles.numericFieldDisabled]: row.type !== 'Custom' || !editModeRows[row.title]})}
|
||||
value={row.downloadKps == null ? '' : row.downloadKps}
|
||||
onChange={(e) => updateProfile(row.title, 'downloadKps', e.target.value)}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder={row.type !== 'Custom' || !editModeRows[row.title] ? "": "(optional)"}
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">Kb/s</InputAdornment>,
|
||||
}}
|
||||
disabled={row.type !== 'Custom' || !editModeRows[row.title]}
|
||||
/>
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.type !== 'Online' &&
|
||||
<TextField
|
||||
className={cx(styles.numericField, {[styles.numericFieldDisabled]: row.type !== 'Custom' || !editModeRows[row.title]})}
|
||||
value={row.uploadKps == null ? '' : row.uploadKps}
|
||||
onChange={(e) => updateProfile(row.title, 'uploadKps', e.target.value)}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder={row.type !== 'Custom' || !editModeRows[row.title] ? "": "(optional)"}
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">Kb/s</InputAdornment>,
|
||||
}}
|
||||
disabled={row.type !== 'Custom' || !editModeRows[row.title]}
|
||||
/>
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.type !== 'Online' &&
|
||||
<TextField
|
||||
className={cx(styles.numericField, {[styles.numericFieldDisabled]: row.type !== 'Custom' || !editModeRows[row.title]})}
|
||||
value={row.latencyMs == null ? '' : row.latencyMs}
|
||||
onChange={(e) => updateProfile(row.title, 'latencyMs', e.target.value)}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder={row.type !== 'Custom' || !editModeRows[row.title] ? "": "(optional)"}
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">ms</InputAdornment>,
|
||||
}}
|
||||
disabled={row.type !== 'Custom' || !editModeRows[row.title]}
|
||||
/>
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{row.type === 'Custom' && <CancelOutlinedIcon className={cx(styles.actionIcon)} onClick={() => removeProfile(row.title)} />}
|
||||
</TableCell>
|
||||
|
@ -94,52 +155,56 @@ export default function ProfileManager({
|
|||
<TableHead className={cx(styles.profilesHeader)}>
|
||||
<TableRow>
|
||||
<TableCell style={{ width: "32%" }}>
|
||||
<TextField
|
||||
autoFocus
|
||||
value={newElement.title || ''}
|
||||
onChange={(e) => setNewElement({...newElement, title: e.target.value})}
|
||||
error={newElementIsInvalid}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
<TextField
|
||||
autoFocus
|
||||
value={newElement.title || ''}
|
||||
onChange={(e) => setNewElement({...newElement, title: e.target.value})}
|
||||
error={newElementIsInvalid}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="New Profile Name"
|
||||
className={cx(styles.titleField)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "21%" }} align="right">
|
||||
<TextField
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.downloadKps || ''}
|
||||
onChange={(e) => setNewElement({...newElement, downloadKps: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Kb/s (optional)"
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.downloadKps == null ? '' : newElement.downloadKps}
|
||||
onChange={(e) => setNewElement({...newElement, downloadKps: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="(optional)"
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">Kb/s</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "21%" }} align="right">
|
||||
<TextField
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.uploadKps || ''}
|
||||
onChange={(e) => setNewElement({...newElement, uploadKps: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="Kb/s (optional)"
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.uploadKps == null ? '' : newElement.uploadKps}
|
||||
onChange={(e) => setNewElement({...newElement, uploadKps: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="(optional)"
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">Kb/s</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: "21%" }} align="right">
|
||||
<TextField
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.latencyMs || ''}
|
||||
onChange={(e) => setNewElement({...newElement, latencyMs: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="ms (optional)"
|
||||
className={cx(styles.numericField)}
|
||||
value={newElement.latencyMs == null ? '' : newElement.latencyMs}
|
||||
onChange={(e) => setNewElement({...newElement, latencyMs: e.target.value})}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
placeholder="(optional)"
|
||||
InputProps={{
|
||||
inputComponent: NumberFormatCustom,
|
||||
endAdornment: <InputAdornment position="end">ms</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
|
|
|
@ -1,39 +1,58 @@
|
|||
.profileManagerContainer {
|
||||
height: 600px;
|
||||
padding: 20px;
|
||||
height: 600px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.profilesContainer {
|
||||
max-height: 430px;
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
max-height: 430px;
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.profilesHeader > tr > th {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.profilesRow > th,
|
||||
td {
|
||||
padding-right: 16px !important;
|
||||
}
|
||||
|
||||
.profilesRow > th {
|
||||
margin-right: 16px;
|
||||
cursor: default;
|
||||
}
|
||||
.profilesRow > th.customProfile {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.actionIcon {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.actionIcon:hover {
|
||||
color: #6075ef;
|
||||
color: #6075ef;
|
||||
}
|
||||
|
||||
.saveButton {
|
||||
position: absolute !important;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
position: absolute !important;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.numericField * {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
.numericFieldDisabled fieldset {
|
||||
border: 0 !important;
|
||||
}
|
||||
.numericFieldDisabled input {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.titleField * {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ const selectStyles = {
|
|||
? '#ffffff20'
|
||||
: null,
|
||||
color: 'white',
|
||||
|
||||
|
||||
':active': {
|
||||
...selectStyles[':active'],
|
||||
backgroundColor: !isDisabled && '#ffffff40',
|
||||
|
@ -102,7 +102,7 @@ export default function NetworkThrottling({
|
|||
component="span"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
Customize Profiles
|
||||
Manage Profiles
|
||||
</Button>
|
||||
<Dialog className={cx(styles.profileManagerDialog)} maxWidth="md" fullWidth open={open} scroll="paper" onClose={closeDialog}>
|
||||
<AppBar className={classes.appBar} color="secondary">
|
||||
|
|
|
@ -278,13 +278,6 @@ function getDefaultNetworkThrottlingProfiles(): NetworkThrottlingProfileType[] {
|
|||
downloadKps: 1475,
|
||||
uploadKps: 675,
|
||||
latencyMs: 563
|
||||
},
|
||||
{
|
||||
type: 'Custom',
|
||||
title: 'Modem 56K',
|
||||
downloadKps: 56,
|
||||
uploadKps: 56,
|
||||
latencyMs: 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue