feat: add checkbox to toggle direction arrows
This commit is contained in:
@@ -130,7 +130,7 @@ function MapViewManager({ coords, mapState, setMapState, loading, setSubmenu })
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PolylineWithArrows({ coords }) {
|
function PolylineWithArrows({ coords, showDirection }) {
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
const polylineRef = useRef(null);
|
const polylineRef = useRef(null);
|
||||||
const decoratorRef = useRef(null);
|
const decoratorRef = useRef(null);
|
||||||
@@ -148,25 +148,29 @@ function PolylineWithArrows({ coords }) {
|
|||||||
polylineRef.current = polyline;
|
polylineRef.current = polyline;
|
||||||
map.addLayer(polyline);
|
map.addLayer(polyline);
|
||||||
|
|
||||||
const decorator = leaflet.polylineDecorator(polyline, {
|
if (showDirection) {
|
||||||
patterns: [
|
const decorator = leaflet.polylineDecorator(polyline, {
|
||||||
{
|
patterns: [
|
||||||
offset: 25,
|
{
|
||||||
repeat: 100,
|
offset: 25,
|
||||||
symbol: leaflet.Symbol.arrowHead({
|
repeat: 100,
|
||||||
pixelSize: 15,
|
symbol: leaflet.Symbol.arrowHead({
|
||||||
polygon: false,
|
pixelSize: 15,
|
||||||
pathOptions: {
|
polygon: false,
|
||||||
stroke: true,
|
pathOptions: {
|
||||||
color: 'blue',
|
stroke: true,
|
||||||
weight: 3
|
color: 'blue',
|
||||||
}
|
weight: 3
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
]
|
}
|
||||||
});
|
]
|
||||||
decoratorRef.current = decorator;
|
});
|
||||||
map.addLayer(decorator);
|
decoratorRef.current = decorator;
|
||||||
|
map.addLayer(decorator);
|
||||||
|
} else {
|
||||||
|
decoratorRef.current = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -177,12 +181,12 @@ function PolylineWithArrows({ coords }) {
|
|||||||
map.removeLayer(decoratorRef.current);
|
map.removeLayer(decoratorRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [coords, map]);
|
}, [coords, map, showDirection]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Map({end, duration, slider, mapState, setMapState, setSubmenu}) {
|
function Map({end, duration, slider, mapState, setMapState, setSubmenu, showDirection}) {
|
||||||
const [data, loading] = useSensor('owntracks', 'OwnTracks', end, duration);
|
const [data, loading] = useSensor('owntracks', 'OwnTracks', end, duration);
|
||||||
|
|
||||||
const range = useMemo(() => parseSlider(end, duration, slider), [end, duration, slider]);
|
const range = useMemo(() => parseSlider(end, duration, slider), [end, duration, slider]);
|
||||||
@@ -242,7 +246,7 @@ function Map({end, duration, slider, mapState, setMapState, setSubmenu}) {
|
|||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url='https://maptiles.p.rapidapi.com/en/map/v1/{z}/{x}/{y}.png?rapidapi-key=4375b0b1d8msh0c9e7fa3efb9adfp1769dfjsnd603a0387fea'
|
url='https://maptiles.p.rapidapi.com/en/map/v1/{z}/{x}/{y}.png?rapidapi-key=4375b0b1d8msh0c9e7fa3efb9adfp1769dfjsnd603a0387fea'
|
||||||
/>
|
/>
|
||||||
<PolylineWithArrows coords={coords} />
|
<PolylineWithArrows coords={coords} showDirection={showDirection} />
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
@@ -259,7 +263,7 @@ function Map({end, duration, slider, mapState, setMapState, setSubmenu}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu}) {
|
function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, setSubmenu, showDirection, setShowDirection}) {
|
||||||
const [showRange, setShowRange] = useState(false);
|
const [showRange, setShowRange] = useState(false);
|
||||||
|
|
||||||
const chooseDuration = (x) => {
|
const chooseDuration = (x) => {
|
||||||
@@ -427,6 +431,16 @@ function Menu({duration, setDuration, end, setEnd, slider, setSlider, submenu, s
|
|||||||
<h2>Misc</h2>
|
<h2>Misc</h2>
|
||||||
<button onClick={() => setSubmenu(false)}>×</button>
|
<button onClick={() => setSubmenu(false)}>×</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ padding: '0.5rem 1rem' }}>
|
||||||
|
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={showDirection}
|
||||||
|
onChange={e => setShowDirection(e.target.checked)}
|
||||||
|
/>
|
||||||
|
Show direction
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<button onClick={resetToDefaults}>Reset to defaults</button>
|
<button onClick={resetToDefaults}>Reset to defaults</button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
@@ -471,6 +485,7 @@ function App() {
|
|||||||
const initialLat = params.get('lat');
|
const initialLat = params.get('lat');
|
||||||
const initialLng = params.get('lng');
|
const initialLng = params.get('lng');
|
||||||
const initialZoom = params.get('zoom');
|
const initialZoom = params.get('zoom');
|
||||||
|
const initialShowDirection = params.get('showDirection') === 'true';
|
||||||
|
|
||||||
const initialDuration = (initialDurationId && durations[parseInt(initialDurationId, 10)]) ? durations[parseInt(initialDurationId, 10)] : durations[0];
|
const initialDuration = (initialDurationId && durations[parseInt(initialDurationId, 10)]) ? durations[parseInt(initialDurationId, 10)] : durations[0];
|
||||||
const initialEnd = initialEndTimestamp ? moment.unix(initialEndTimestamp) : moment();
|
const initialEnd = initialEndTimestamp ? moment.unix(initialEndTimestamp) : moment();
|
||||||
@@ -484,6 +499,7 @@ function App() {
|
|||||||
zoom: initialZoom ? parseInt(initialZoom, 10) : 13,
|
zoom: initialZoom ? parseInt(initialZoom, 10) : 13,
|
||||||
});
|
});
|
||||||
const [submenu, setSubmenu] = useState(false);
|
const [submenu, setSubmenu] = useState(false);
|
||||||
|
const [showDirection, setShowDirection] = useState(initialShowDirection);
|
||||||
|
|
||||||
const isInitialMount = useRef(true);
|
const isInitialMount = useRef(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -501,6 +517,9 @@ function App() {
|
|||||||
params.set('duration', duration.id);
|
params.set('duration', duration.id);
|
||||||
params.set('end', end.unix());
|
params.set('end', end.unix());
|
||||||
params.set('slider', slider.join(','));
|
params.set('slider', slider.join(','));
|
||||||
|
if (showDirection) {
|
||||||
|
params.set('showDirection', 'true');
|
||||||
|
}
|
||||||
if (mapState.center) {
|
if (mapState.center) {
|
||||||
params.set('lat', mapState.center[0].toFixed(5));
|
params.set('lat', mapState.center[0].toFixed(5));
|
||||||
params.set('lng', mapState.center[1].toFixed(5));
|
params.set('lng', mapState.center[1].toFixed(5));
|
||||||
@@ -512,7 +531,7 @@ function App() {
|
|||||||
return () => {
|
return () => {
|
||||||
clearTimeout(handler);
|
clearTimeout(handler);
|
||||||
};
|
};
|
||||||
}, [duration, end, slider, mapState]);
|
}, [duration, end, slider, mapState, showDirection]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -525,6 +544,8 @@ function App() {
|
|||||||
setSlider={setSlider}
|
setSlider={setSlider}
|
||||||
submenu={submenu}
|
submenu={submenu}
|
||||||
setSubmenu={setSubmenu}
|
setSubmenu={setSubmenu}
|
||||||
|
showDirection={showDirection}
|
||||||
|
setShowDirection={setShowDirection}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Map
|
<Map
|
||||||
@@ -534,6 +555,7 @@ function App() {
|
|||||||
mapState={mapState}
|
mapState={mapState}
|
||||||
setMapState={setMapState}
|
setMapState={setMapState}
|
||||||
setSubmenu={setSubmenu}
|
setSubmenu={setSubmenu}
|
||||||
|
showDirection={showDirection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user