Skip to content

Commit

Permalink
Support schema.Location in pixlet serve (tidbyt#585)
Browse files Browse the repository at this point in the history
  • Loading branch information
dinosaursrarr authored Feb 2, 2023
1 parent f5cc5df commit df0639f
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/features/schema/FieldDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PhotoSelect from './fields/photoselect/PhotoSelect';
import Toggle from './fields/Toggle';
import DateTime from './fields/DateTime';
import Dropdown from './fields/Dropdown';
import LocationForm from './fields/location/LocationForm';
import TextInput from './fields/TextInput';
import Typeahead from './fields/Typeahead';
import Typography from '@mui/material/Typography';
Expand All @@ -17,7 +18,7 @@ export default function FieldDetails({ field }) {
case 'dropdown':
return <Dropdown field={field} />
case 'location':
return <Typography>schema.Location() is not yet supported in pixlet, but is supported in the community repo. Be on the lookout for this field to be available in a future release.</Typography>
return <LocationForm field={field} />
case 'locationbased':
return <Typography>schema.LocationBased() is not yet supported in pixlet, but is supported in the community repo. Be on the lookout for this field to be available in a future release.</Typography>
case 'oauth2':
Expand Down
79 changes: 79 additions & 0 deletions src/features/schema/fields/location/InputSlider.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Largely based on https://mui.com/material-ui/react-slider/#InputSlider.js
import React, { useState } from 'react';

import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import Slider from '@mui/material/Slider';
import MuiInput from '@mui/material/Input';

const Input = styled(MuiInput)`
width: 80px;
`;

export default function InputSlider({ min, max, step, defaultValue, onChange}) {
const [value, setValue] = useState(defaultValue);

const handleSliderChange = (event, newValue) => {
setValue(newValue);
onChange(event);
};

const handleInputChange = (event) => {
if (event.target.value === '') {
setValue('');
onChange(event);
return;
}
const value = Number(event.target.value);
if (value < min) {
setValue(min);
} else if (value > max) {
setValue(max);
} else {
setValue(value);
}
onChange(event);
};

const handleBlur = () => {
if (value < min) {
setValue(min);
} else if (value > max) {
setValue(max);
}
};

return (
<Box sx={{ width: 250 }}>
<Grid container spacing={2} alignItems="center">
<Grid item xs>
<Slider
value={value}
min={min}
max={max}
step={step}
onChange={handleSliderChange}
aria-labelledby="input-slider"
/>
</Grid>
<Grid item>
<Input
value={value}
size="small"
onChange={handleInputChange}
onBlur={handleBlur}
inputProps={{
step: {step},
min: {min},
max: {max},
type: 'number',
'aria-labelledby': 'input-slider',
}}
/>
</Grid>
</Grid>
</Box>
);
}
107 changes: 107 additions & 0 deletions src/features/schema/fields/location/LocationForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';

import InputSlider from './InputSlider';
import { set } from '../../../config/configSlice';

export default function LocationForm({ field }) {
const [value, setValue] = useState({
// Default to Brooklyn, because that's where tidbyt folks
// are and we can only dispatch a location object which
// has all fields set.
'lat': 40.6782,
'lng': -73.9442,
'locality': 'Brooklyn, New York',
'timezone': 'America/New_York',
// But overwrite with app-specific defaults set in config.
...field.default
});

const config = useSelector(state => state.config);

const dispatch = useDispatch();

useEffect(() => {
if (field.id in config) {
setValue(JSON.parse(config[field.id].value));
} else if (field.default) {
dispatch(set({
id: field.id,
value: field.default,
}));
}
}, [])

const setPart = (partName, partValue) => {
let newValue = {...value};
newValue[partName] = partValue;
setValue(newValue);
dispatch(set({
id: field.id,
value: JSON.stringify(newValue),
}));
}

const onChangeLatitude = (event) => {
setPart('lat', event.target.value);
}

const onChangeLongitude = (event) => {
setPart('lng', event.target.value);
}

const onChangeLocality = (event) => {
setPart('locality', event.target.value);
}

const onChangeTimezone = (event) => {
setPart('timezone', event.target.value);
}

return (
<FormControl fullWidth>
<Typography>Latitude</Typography>
<InputSlider
min={-90}
max={90}
step={0.1}
onChange={onChangeLatitude}
defaultValue={value['lat']}
>
</InputSlider>
<Typography>Longitude</Typography>
<InputSlider
min={-180}
max={180}
step={0.1}
onChange={onChangeLongitude}
defaultValue={value['lng']}
>
</InputSlider>
<Typography>Locality</Typography>
<TextField
fullWidth
variant="outlined"
onChange={onChangeLocality}
style={{ marginBottom: '0.5rem' }}
defaultValue={value['locality']}
/>
<Typography>Timezone</Typography>
<Select
onChange={onChangeTimezone}
defaultValue={value['timezone']}
>
{Intl.supportedValuesOf('timeZone').map((zone) => {
return <MenuItem value={zone}>{zone}</MenuItem>
})}
</Select>
</FormControl>
);
}

0 comments on commit df0639f

Please sign in to comment.