diff options
Diffstat (limited to 'modern/src/attributes')
-rw-r--r-- | modern/src/attributes/AddAttributeDialog.js | 83 | ||||
-rw-r--r-- | modern/src/attributes/EditAttributesView.js | 141 | ||||
-rw-r--r-- | modern/src/attributes/userAttributes.js | 68 |
3 files changed, 292 insertions, 0 deletions
diff --git a/modern/src/attributes/AddAttributeDialog.js b/modern/src/attributes/AddAttributeDialog.js new file mode 100644 index 00000000..ee4c48c1 --- /dev/null +++ b/modern/src/attributes/AddAttributeDialog.js @@ -0,0 +1,83 @@ +import React, { useState } from 'react'; +import { Button, Dialog, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, TextField } from "@material-ui/core"; + +import t from '../common/localization'; +import { Autocomplete, createFilterOptions } from '@material-ui/lab'; + +const AddAttributeDialog = ({ open, onResult, definitions }) => { + const filter = createFilterOptions({ + stringify: option => option.name, + }); + + const options = Object.entries(definitions).map(([key, value]) => ({ + key, + name: value.name, + type: value.type, + })); + + const [key, setKey] = useState(); + const [type, setType] = useState('string'); + + return ( + <Dialog open={open} fullWidth maxWidth="xs"> + <DialogContent> + <Autocomplete + onChange={(_, option) => { + setKey(option && typeof option === 'object' ? option.key : option); + if (option && option.type) { + setType(option.type); + } + }} + filterOptions={(options, params) => { + const filtered = filter(options, params); + if (params.inputValue) { + filtered.push({ + key: params.inputValue, + name: params.inputValue, + }); + } + return filtered; + }} + options={options} + getOptionLabel={option => { + return option && typeof option === 'object' ? option.name : option; + }} + renderOption={option => option.name} + freeSolo + renderInput={(params) => ( + <TextField {...params} label={t('sharedAttribute')} variant="filled" margin="normal" /> + )} + /> + <FormControl + variant="filled" + margin="normal" + fullWidth + disabled={key in definitions}> + <InputLabel>{t('sharedType')}</InputLabel> + <Select + value={type} + onChange={e => setType(e.target.value)}> + <MenuItem value={'string'}>{t('sharedTypeString')}</MenuItem> + <MenuItem value={'number'}>{t('sharedTypeNumber')}</MenuItem> + <MenuItem value={'boolean'}>{t('sharedTypeBoolean')}</MenuItem> + </Select> + </FormControl> + </DialogContent> + <DialogActions> + <Button + color="primary" + disabled={!key} + onClick={() => onResult({ key, type })}> + {t('sharedAdd')} + </Button> + <Button + autoFocus + onClick={() => onResult(null)}> + {t('sharedCancel')} + </Button> + </DialogActions> + </Dialog> + ) +} + +export default AddAttributeDialog; diff --git a/modern/src/attributes/EditAttributesView.js b/modern/src/attributes/EditAttributesView.js new file mode 100644 index 00000000..9491acc5 --- /dev/null +++ b/modern/src/attributes/EditAttributesView.js @@ -0,0 +1,141 @@ +import React, { useState } from 'react'; + +import t from '../common/localization'; + +import { Button, Checkbox, FilledInput, FormControl, FormControlLabel, Grid, IconButton, InputAdornment, InputLabel, makeStyles } from "@material-ui/core"; +import CloseIcon from '@material-ui/icons/Close'; +import AddIcon from '@material-ui/icons/Add'; +import AddAttributeDialog from './AddAttributeDialog'; + +const useStyles = makeStyles(theme => ({ + addButton: { + marginTop: theme.spacing(2), + marginBottom: theme.spacing(1), + }, + removeButton: { + marginRight: theme.spacing(1.5), + }, +})); + +const EditAttributesView = ({ attributes, setAttributes, definitions }) => { + const classes = useStyles(); + + const [addDialogShown, setAddDialogShown] = useState(false); + + const convertToList = (attributes) => { + let booleanList = []; + let otherList = []; + for (const key in attributes) { + const value = attributes[key]; + const type = getAttributeType(value); + if (type === 'boolean') { + booleanList.push({ key, value, type }); + } else { + otherList.push({ key, value, type }); + } + } + return otherList.concat(booleanList); + } + + const handleAddResult = (definition) => { + setAddDialogShown(false); + if (definition) { + switch(definition.type) { + case 'number': + updateAttribute(definition.key, 0); + break; + case 'boolean': + updateAttribute(definition.key, false); + break; + default: + updateAttribute(definition.key, ""); + break; + } + } + } + + const updateAttribute = (key, value) => { + let updatedAttributes = {...attributes}; + updatedAttributes[key] = value; + setAttributes(updatedAttributes); + }; + + const deleteAttribute = (key) => { + let updatedAttributes = {...attributes}; + delete updatedAttributes[key]; + setAttributes(updatedAttributes); + }; + + const getAttributeName = (key) => { + const definition = definitions[key]; + return definition ? definition.name : key; + }; + + const getAttributeType = (value) => { + if (typeof value === 'number') { + return 'number'; + } else if (typeof value === 'boolean') { + return 'boolean'; + } else { + return 'string'; + } + }; + + return ( + <> + {convertToList(attributes).map(({ key, value, type }) => { + if (type === 'boolean') { + return ( + <Grid container direction="row" justify="space-between"> + <FormControlLabel + control={ + <Checkbox + checked={value} + onChange={e => updateAttribute(key, e.target.checked)} + /> + } + label={getAttributeName(key)} /> + <IconButton className={classes.removeButton} onClick={() => deleteAttribute(key)}> + <CloseIcon /> + </IconButton> + </Grid> + ); + } else { + return ( + <FormControl variant="filled" margin="normal" key={key}> + <InputLabel>{getAttributeName(key)}</InputLabel> + <FilledInput + type={type === 'number' ? 'number' : 'text'} + defaultValue={value} + onChange={e => updateAttribute(key, e.target.value)} + endAdornment={ + <InputAdornment position="end"> + <IconButton onClick={() => deleteAttribute(key)}> + <CloseIcon /> + </IconButton> + </InputAdornment> + } + /> + </FormControl> + ); + } + })} + <Button + size="large" + variant="outlined" + color="primary" + onClick={() => setAddDialogShown(true)} + startIcon={<AddIcon />} + className={classes.addButton}> + {t('sharedAdd')} + </Button> + <AddAttributeDialog + open={addDialogShown} + onResult={handleAddResult} + definitions={definitions} + /> + </> + ); +} + +export default EditAttributesView; diff --git a/modern/src/attributes/userAttributes.js b/modern/src/attributes/userAttributes.js new file mode 100644 index 00000000..bcec29f2 --- /dev/null +++ b/modern/src/attributes/userAttributes.js @@ -0,0 +1,68 @@ +import t from '../common/localization' + +export default { + 'notificationTokens': { + name: t('attributeNotificationTokens'), + type: 'string', + }, + 'web.liveRouteLength': { + name: t('attributeWebLiveRouteLength'), + type: 'number', + }, + 'web.selectZoom': { + name: t('attributeWebSelectZoom'), + type: 'number', + }, + 'web.maxZoom': { + name: t('attributeWebMaxZoom'), + type: 'number', + }, + 'ui.disableReport': { + name: t('attributeUiDisableReport'), + type: 'boolean', + }, + 'ui.disableEvents': { + name: t('attributeUiDisableEvents'), + type: 'boolean', + }, + 'ui.disableVehicleFetures': { + name: t('attributeUiDisableVehicleFetures'), + type: 'boolean', + }, + 'ui.disableDrivers': { + name: t('attributeUiDisableDrivers'), + type: 'boolean', + }, + 'ui.disableComputedAttributes': { + name: t('attributeUiDisableComputedAttributes'), + type: 'boolean', + }, + 'ui.disableCalendars': { + name: t('attributeUiDisableCalendars'), + type: 'boolean', + }, + 'ui.disableMaintenance': { + name: t('attributeUiDisableMaintenance'), + type: 'boolean', + }, + 'ui.hidePositionAttributes': { + name: t('attributeUiHidePositionAttributes'), + type: 'string', + }, + 'distanceUnit': { + name: t('settingsDistanceUnit'), + type: 'string', + }, + 'speedUnit': { + name: t('settingsSpeedUnit'), + type: 'string', + }, + 'volumeUnit': { + name: t('settingsVolumeUnit'), + type: 'string', + }, + 'timezone': { + name: t('sharedTimezone'), + type: 'string', + }, +}; |