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 {
|
||||
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 FormControl from '@mui/material/FormControl';
|
||||
import staticText from '../../../assets/data/staticText.json'
|
||||
import { HabitatSVG } from '../HabitatSVG'
|
||||
|
||||
|
||||
export default function HabitatSelector(props) {
|
||||
const [habitats, setHabitats] = useState([])
|
||||
|
@ -79,7 +81,7 @@ export default function HabitatSelector(props) {
|
|||
return (
|
||||
<div>
|
||||
<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>)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +1,41 @@
|
|||
import { useEffect, useState, useRef } from 'react';
|
||||
import SampleBackground from '../../../assets/img/stepBackgrounds/step1.jpg';
|
||||
import SiteRepository from '../../../repository/SiteRepository';
|
||||
|
||||
import { HabitatSVG } from '../HabitatSVG'
|
||||
|
||||
export default function ZoneSelector(props) {
|
||||
|
||||
const [habitatImageObject, setHabitatImageObject] = useState({});
|
||||
const [habitatImageFile, setHabitatImageFile] = useState(SampleBackground);
|
||||
const [imageHeight, setImageHeight] = useState(0)
|
||||
const [segmentMapping, setSegmentMapping] = useState({});
|
||||
const [selectedZoneSegment, setZoneSegment] = useState(null);
|
||||
const imageContainer = useRef(null)
|
||||
|
||||
const getHabitatImage = (findAndSetImageHeightFunc) => {
|
||||
const getHabitatImage = () => {
|
||||
SiteRepository.getHabitatImage(props.filters.habitatImage).then(response => {
|
||||
if (response.status === 200) {
|
||||
const imageData = response.data
|
||||
setHabitatImageObject(imageData)
|
||||
const imageSrc = require(`../../../assets/img/habitats/${imageData.image_filename}`).default
|
||||
setHabitatImageFile(imageSrc)
|
||||
findAndSetImageHeightFunc(imageSrc)
|
||||
}
|
||||
}).catch(e => {
|
||||
findAndSetImageHeightFunc(habitatImageFile)
|
||||
})
|
||||
}
|
||||
|
||||
const setZoneOrRedirect = (zoneSegment) => {
|
||||
const redirectHabitat = zoneSegment && zoneSegment.zone && zoneSegment.zone.redirect_habitat;
|
||||
setZoneSegment(zoneSegment);
|
||||
const getZones = () => {
|
||||
SiteRepository.getZones().then(response => {
|
||||
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
|
||||
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.setRedirectBack(Boolean(redirectHabitat));
|
||||
|
||||
|
@ -38,53 +43,35 @@ export default function ZoneSelector(props) {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Calculates the image ratio and uses this to calculate the height in pixels based on outer container width
|
||||
const findAndSetImageHeight = (imageSource) => {
|
||||
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
|
||||
Object.keys(habitatImageObject).length === 0 && getHabitatImage()
|
||||
|
||||
// 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);
|
||||
}, [props.filters.zone]);
|
||||
|
||||
const selectZone = (element) => {
|
||||
if (['path', 'rect'].includes(element.tagName) && element.attributes.inkscapelabel && element.attributes.inkscapelabel.nodeValue) {
|
||||
if (selectedZoneSegment) {
|
||||
selectedZoneSegment.style['fill-opacity'] = 0;
|
||||
}
|
||||
|
||||
setZoneSegment(element)
|
||||
element.style.fill = "#eeeeee"
|
||||
element.style['fill-opacity'] = 0.5;
|
||||
|
||||
const zone = segmentMapping[element.attributes.inkscapelabel.nodeValue]
|
||||
setZoneOrRedirect(zone)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const stepBackground = {
|
||||
backgroundImage: `url(${habitatImageFile})`,
|
||||
backgroundSize: '100% auto',
|
||||
backgroundRepeat: 'none',
|
||||
height: imageHeight,
|
||||
width: '100%',
|
||||
display: 'flex'
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={imageContainer} style={{ width: '100%' }}>
|
||||
<p>{habitatImageObject && habitatImageObject.name}</p>
|
||||
{Object.keys(habitatImageObject).length === 0 ? (
|
||||
// Sample segment selector (temporary if no image is available)
|
||||
<div style={stepBackground}>
|
||||
<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>) :
|
||||
// Actual zone selector
|
||||
(<div style={stepBackground}>
|
||||
{habitatImageObject && habitatImageObject.image_segments && Array.isArray(habitatImageObject.image_segments) && habitatImageObject.image_segments.map(segment =>
|
||||
<div key={segment.id} onClick={() => { setZoneOrRedirect(segment) }} className={`selectable-section ${selectedZoneSegment === segment ? 'selected-segment' : ''}`} style={{ width: `${segment.segment_percentage_width}%`, height: '100%' }}></div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
return Object.keys(habitatImageObject).length > 0 ? (
|
||||
<div style={{ width: '100%' }}>
|
||||
<p>{habitatImageObject.name}</p>
|
||||
<div className="zone-selector-svg">
|
||||
<HabitatSVG onClick={(event) => selectZone(event.target)} name={habitatImageObject.image_filename} style={{ "height": 'auto', "width": "100%" }} />
|
||||
</div>
|
||||
</div>) : <div></div>
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import axios from "axios"
|
|||
|
||||
// Create the axios object
|
||||
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"] = "*";
|
||||
|
|
|
@ -6,6 +6,10 @@ const SiteRepository = {
|
|||
return Repository.get(`/habitats/`);
|
||||
},
|
||||
|
||||
getZones() {
|
||||
return Repository.get(`/zones/`);
|
||||
},
|
||||
|
||||
getHabitatImage(habitatImageID) {
|
||||
return Repository.get(`/habitatimage/${habitatImageID}/`);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue