Add frontend support for svg zone selection
This commit is contained in:
parent
0075e8b303
commit
2c2a362b9a
6 changed files with 91 additions and 59 deletions
|
@ -40,3 +40,15 @@
|
||||||
.selected-segment {
|
.selected-segment {
|
||||||
background-color: #eeeeee55;
|
background-color: #eeeeee55;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zone-selector-svg path:hover {
|
||||||
|
fill: #eeeeee !important;
|
||||||
|
fill-opacity: 0.5 !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zone-selector-svg rect:hover {
|
||||||
|
fill: #eeeeee !important;
|
||||||
|
fill-opacity: 0.5 !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
27
frontend/src/components/steps/HabitatSVG.jsx
Normal file
27
frontend/src/components/steps/HabitatSVG.jsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const HabitatSVG = ({ name, ...rest }) => {
|
||||||
|
const ImportedIconRef = React.useRef(null);
|
||||||
|
const [loading, setLoading] = React.useState(false);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
const importIcon = async () => {
|
||||||
|
try {
|
||||||
|
ImportedIconRef.current = (await import(`!!@svgr/webpack?-svgo,+titleProp,+ref!../../assets/img/habitatSVG/${name}.svg`)).default;
|
||||||
|
} catch (err) {
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
importIcon();
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
|
if (!loading && ImportedIconRef.current) {
|
||||||
|
const { current: ImportedIcon } = ImportedIconRef;
|
||||||
|
return <ImportedIcon {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
|
@ -7,6 +7,8 @@ import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import FormLabel from '@mui/material/FormLabel';
|
import FormLabel from '@mui/material/FormLabel';
|
||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
import staticText from '../../../assets/data/staticText.json'
|
import staticText from '../../../assets/data/staticText.json'
|
||||||
|
import { HabitatSVG } from '../HabitatSVG'
|
||||||
|
|
||||||
|
|
||||||
export default function HabitatSelector(props) {
|
export default function HabitatSelector(props) {
|
||||||
const [habitats, setHabitats] = useState([])
|
const [habitats, setHabitats] = useState([])
|
||||||
|
@ -79,7 +81,7 @@ export default function HabitatSelector(props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{image.name}</p>
|
<p>{image.name}</p>
|
||||||
<img src={require(`../../../assets/img/habitats/${image.image_filename}`).default} alt={image.name} style={{ "width": "300px" }} />
|
<HabitatSVG name={image.image_filename} style={{ "height": 'auto', "width": "300px" }}/>
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,36 +1,41 @@
|
||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import SampleBackground from '../../../assets/img/stepBackgrounds/step1.jpg';
|
|
||||||
import SiteRepository from '../../../repository/SiteRepository';
|
import SiteRepository from '../../../repository/SiteRepository';
|
||||||
|
import { HabitatSVG } from '../HabitatSVG'
|
||||||
|
|
||||||
export default function ZoneSelector(props) {
|
export default function ZoneSelector(props) {
|
||||||
|
|
||||||
const [habitatImageObject, setHabitatImageObject] = useState({});
|
const [habitatImageObject, setHabitatImageObject] = useState({});
|
||||||
const [habitatImageFile, setHabitatImageFile] = useState(SampleBackground);
|
const [segmentMapping, setSegmentMapping] = useState({});
|
||||||
const [imageHeight, setImageHeight] = useState(0)
|
|
||||||
const [selectedZoneSegment, setZoneSegment] = useState(null);
|
const [selectedZoneSegment, setZoneSegment] = useState(null);
|
||||||
const imageContainer = useRef(null)
|
|
||||||
|
|
||||||
const getHabitatImage = (findAndSetImageHeightFunc) => {
|
const getHabitatImage = () => {
|
||||||
SiteRepository.getHabitatImage(props.filters.habitatImage).then(response => {
|
SiteRepository.getHabitatImage(props.filters.habitatImage).then(response => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
const imageData = response.data
|
const imageData = response.data
|
||||||
setHabitatImageObject(imageData)
|
setHabitatImageObject(imageData)
|
||||||
const imageSrc = require(`../../../assets/img/habitats/${imageData.image_filename}`).default
|
|
||||||
setHabitatImageFile(imageSrc)
|
|
||||||
findAndSetImageHeightFunc(imageSrc)
|
|
||||||
}
|
}
|
||||||
}).catch(e => {
|
|
||||||
findAndSetImageHeightFunc(habitatImageFile)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const setZoneOrRedirect = (zoneSegment) => {
|
const getZones = () => {
|
||||||
const redirectHabitat = zoneSegment && zoneSegment.zone && zoneSegment.zone.redirect_habitat;
|
SiteRepository.getZones().then(response => {
|
||||||
setZoneSegment(zoneSegment);
|
if (response.status === 200) {
|
||||||
|
const zoneData = response.data
|
||||||
|
let newSegmentMapping = {}
|
||||||
|
zoneData.forEach(zone => {
|
||||||
|
newSegmentMapping[zone.related_svg_segment] = zone
|
||||||
|
});
|
||||||
|
setSegmentMapping(newSegmentMapping);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setZoneOrRedirect = (zone) => {
|
||||||
|
const redirectHabitat = zone && zone.redirect_habitat;
|
||||||
|
|
||||||
// If there is a redirect habitat set then redirect otherwise set the zone as state
|
// If there is a redirect habitat set then redirect otherwise set the zone as state
|
||||||
const newFilterState = redirectHabitat ? { "habitat": { "id": redirectHabitat.id, "name": redirectHabitat.name }} : { "zone": zoneSegment.zone };
|
const newFilterState = redirectHabitat ? { "habitat": { "id": redirectHabitat.id, "name": redirectHabitat.name } } : { "zone": zone };
|
||||||
|
|
||||||
props.updateFilterState(newFilterState);
|
props.updateFilterState(newFilterState);
|
||||||
props.setRedirectBack(Boolean(redirectHabitat));
|
props.setRedirectBack(Boolean(redirectHabitat));
|
||||||
|
|
||||||
|
@ -38,53 +43,35 @@ export default function ZoneSelector(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Calculates the image ratio and uses this to calculate the height in pixels based on outer container width
|
// Retrieves the habitat image from the api if it's not loaded already
|
||||||
const findAndSetImageHeight = (imageSource) => {
|
Object.keys(habitatImageObject).length === 0 && getHabitatImage()
|
||||||
let img = new Image();
|
|
||||||
img.src = imageSource;
|
|
||||||
const newHeight = imageContainer.current ? imageContainer.current.clientWidth * (img.height / img.width) : 0;
|
|
||||||
setImageHeight(newHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves the habitat image from the api if it's not loaded already
|
// Retrieves the habitat image from the api if it's not loaded already
|
||||||
Object.keys(habitatImageObject).length === 0 && getHabitatImage(findAndSetImageHeight)
|
Object.keys(segmentMapping).length === 0 && getZones()
|
||||||
|
|
||||||
// Sets the image height such that the full image always displays on page resize
|
|
||||||
window.addEventListener("resize", () => findAndSetImageHeight(habitatImageFile), false);
|
|
||||||
|
|
||||||
// Temporarily bypass if there is no image available
|
|
||||||
if (props.filters.zone || !props.filters.habitatImage || (habitatImageObject && habitatImageObject.image_segments)) {
|
|
||||||
props.setNextDisabled(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const stepBackground = {
|
}, [props.filters.zone]);
|
||||||
backgroundImage: `url(${habitatImageFile})`,
|
|
||||||
backgroundSize: '100% auto',
|
const selectZone = (element) => {
|
||||||
backgroundRepeat: 'none',
|
if (['path', 'rect'].includes(element.tagName) && element.attributes.inkscapelabel && element.attributes.inkscapelabel.nodeValue) {
|
||||||
height: imageHeight,
|
if (selectedZoneSegment) {
|
||||||
width: '100%',
|
selectedZoneSegment.style['fill-opacity'] = 0;
|
||||||
display: 'flex'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
setZoneSegment(element)
|
||||||
<div ref={imageContainer} style={{ width: '100%' }}>
|
element.style.fill = "#eeeeee"
|
||||||
<p>{habitatImageObject && habitatImageObject.name}</p>
|
element.style['fill-opacity'] = 0.5;
|
||||||
{Object.keys(habitatImageObject).length === 0 ? (
|
|
||||||
// Sample segment selector (temporary if no image is available)
|
const zone = segmentMapping[element.attributes.inkscapelabel.nodeValue]
|
||||||
<div style={stepBackground}>
|
setZoneOrRedirect(zone)
|
||||||
<div className='selectable-section' style={{ width: '50%', height: '100%' }}></div>
|
}
|
||||||
<div className='selectable-section' style={{ width: '20%', height: '100%' }}></div>
|
}
|
||||||
<div className='selectable-section' style={{ width: '30%', height: '100%' }}></div>
|
|
||||||
</div>) :
|
return Object.keys(habitatImageObject).length > 0 ? (
|
||||||
// Actual zone selector
|
<div style={{ width: '100%' }}>
|
||||||
(<div style={stepBackground}>
|
<p>{habitatImageObject.name}</p>
|
||||||
{habitatImageObject && habitatImageObject.image_segments && Array.isArray(habitatImageObject.image_segments) && habitatImageObject.image_segments.map(segment =>
|
<div className="zone-selector-svg">
|
||||||
<div key={segment.id} onClick={() => { setZoneOrRedirect(segment) }} className={`selectable-section ${selectedZoneSegment === segment ? 'selected-segment' : ''}`} style={{ width: `${segment.segment_percentage_width}%`, height: '100%' }}></div>
|
<HabitatSVG onClick={(event) => selectZone(event.target)} name={habitatImageObject.image_filename} style={{ "height": 'auto', "width": "100%" }} />
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>) : <div></div>
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import axios from "axios"
|
||||||
|
|
||||||
// Create the axios object
|
// Create the axios object
|
||||||
const repo = axios.create({
|
const repo = axios.create({
|
||||||
baseURL: window.location.hostname == 'localhost' ? "http://localhost:9000/api" : "/api",
|
baseURL: window.location.hostname === 'localhost' ? "http://localhost:9000/api" : "/api",
|
||||||
});
|
});
|
||||||
|
|
||||||
repo.defaults.headers.post["access-control-allow-origin"] = "*";
|
repo.defaults.headers.post["access-control-allow-origin"] = "*";
|
||||||
|
|
|
@ -6,6 +6,10 @@ const SiteRepository = {
|
||||||
return Repository.get(`/habitats/`);
|
return Repository.get(`/habitats/`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getZones() {
|
||||||
|
return Repository.get(`/zones/`);
|
||||||
|
},
|
||||||
|
|
||||||
getHabitatImage(habitatImageID) {
|
getHabitatImage(habitatImageID) {
|
||||||
return Repository.get(`/habitatimage/${habitatImageID}/`);
|
return Repository.get(`/habitatimage/${habitatImageID}/`);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue