Updated UI
This commit is contained in:
@@ -29,3 +29,39 @@ body,
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.map-button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
z-index: 1000;
|
||||
|
||||
color: #000;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-width: 2px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
line-height: 30px; /* Vertically center text */
|
||||
font-weight: bold;
|
||||
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.map-button.active {
|
||||
background-color: #228be6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.map-button.active:hover {
|
||||
background-color: #187ed7;
|
||||
}
|
||||
|
||||
.map-button:hover {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MapContainer, TileLayer, ZoomControl } from 'react-leaflet';
|
||||
import { LayersControl, MapContainer, TileLayer, useMapEvents, ZoomControl } from 'react-leaflet';
|
||||
import '@mantine/core/styles.css';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import './App.css';
|
||||
@@ -12,8 +12,8 @@ import { useEffect, useState } from 'react';
|
||||
import { Airport } from '@lib/airport.types.ts';
|
||||
import AirportDrawer from '@components/AirportDrawer.tsx';
|
||||
import { getWeatherMapUrl } from '@lib/rainViewer.ts';
|
||||
import { Switch } from '@mantine/core';
|
||||
|
||||
import { IconRadar } from '@tabler/icons-react';
|
||||
import Cookies from 'js-cookie';
|
||||
// Fix Leaflet's default icon path issues with Webpack
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
@@ -26,13 +26,19 @@ L.Icon.Default.mergeOptions({
|
||||
});
|
||||
|
||||
const openStreetMapUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
const lightLayerUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
|
||||
const darkLayerUrl = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png';
|
||||
// const dark1Url = 'https://maps.rainviewer.com/data/v3/5/10/11.pbf';
|
||||
// const dark2Url = 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/2/0/3.pbf';
|
||||
const defaultZoom = 6;
|
||||
const defaultCenter: L.LatLngExpression = [38.944444, -77.455833];
|
||||
|
||||
function App() {
|
||||
const [airport, setAirport] = useState<Airport | null>(null);
|
||||
const [rainViewerUrl, setRainViewerUrl] = useState<string | null>(null);
|
||||
const [showRadar, setShowRadar] = useState<boolean>(false);
|
||||
const initialRadarValue = Cookies.get('showRadar') === 'true';
|
||||
const [showRadar, setShowRadar] = useState<boolean>(initialRadarValue);
|
||||
const [baseLayer, setBaseLayer] = useState<string>(Cookies.get('selectedBaseLayer') || 'Open Street Map');
|
||||
|
||||
useEffect(() => {
|
||||
if (showRadar) {
|
||||
@@ -40,7 +46,25 @@ function App() {
|
||||
setRainViewerUrl(url);
|
||||
});
|
||||
}
|
||||
}, [showRadar])
|
||||
}, [showRadar]);
|
||||
|
||||
function toggleRadar() {
|
||||
setShowRadar(prev => {
|
||||
const newValue = !prev;
|
||||
Cookies.set('showRadar', newValue.toString(), { expires: 7 });
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
function BaseLayerChangeHandler() {
|
||||
useMapEvents({
|
||||
baselayerchange: (e) => {
|
||||
setBaseLayer(e.name);
|
||||
Cookies.set('selectedBaseLayer', e.name, { expires: 7 });
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
@@ -61,23 +85,27 @@ function App() {
|
||||
scrollWheelZoom={true}
|
||||
zoomControl={false}
|
||||
>
|
||||
<LayersControl>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Open Street Map'} name={'Open Street Map'}>
|
||||
<TileLayer url={openStreetMapUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Carto Light'} name={'Carto Light'}>
|
||||
<TileLayer url={lightLayerUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Carto Dark'} name={'Carto Dark'}>
|
||||
<TileLayer url={darkLayerUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
</LayersControl>
|
||||
{rainViewerUrl && showRadar && <TileLayer url={rainViewerUrl} opacity={0.5} zIndex={5} />}
|
||||
<ZoomControl position={'bottomright'} />
|
||||
<TileLayer url={openStreetMapUrl} />
|
||||
{ rainViewerUrl && showRadar && (
|
||||
<TileLayer url={rainViewerUrl} opacity={0.5} zIndex={5} />
|
||||
)}
|
||||
<AirportLayer setAirport={setAirport} />
|
||||
<BaseLayerChangeHandler />
|
||||
</MapContainer>
|
||||
<div style={{
|
||||
position: 'absolute', top: '100px', right: '10px', zIndex: 1000, backgroundColor: '#fff',
|
||||
borderRadius: '8px', boxShadow: '0 2px 6px rgba(0,0,0,0.15)', padding: '10px'
|
||||
}}>
|
||||
<Switch
|
||||
label="Radar"
|
||||
checked={showRadar}
|
||||
onChange={(event) => setShowRadar(event.currentTarget.checked)}
|
||||
/>
|
||||
</div>
|
||||
<IconRadar
|
||||
onClick={toggleRadar}
|
||||
style={{ bottom: '80px' }}
|
||||
className={`map-button ${showRadar ? 'active' : ''}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -84,9 +84,9 @@ export default function AirportLayer({ setAirport }: { setAirport: (airport: Air
|
||||
|
||||
return (
|
||||
<>
|
||||
{sortedAirports.map((airport, index) => {
|
||||
return <AirportMarker airport={airport} index={index} setAirport={setAirport} />;
|
||||
})}
|
||||
{sortedAirports.map((airport, index) => (
|
||||
<AirportMarker key={index} airport={airport} index={index} setAirport={setAirport} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,9 +126,6 @@ export function Header() {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(Cookies.get('logged_in'));
|
||||
console.log(Cookies.get('session'));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box>
|
||||
|
||||
@@ -13,22 +13,19 @@ async function getWeatherMaps(): Promise<WeatherMaps | undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/{z}/{x}/{y}/2/1_1.png';
|
||||
// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/10/290/391/2/1_1.png'
|
||||
// https://api.rainviewer.com/public/weather-maps.json
|
||||
export async function getWeatherMapUrl(): Promise<string | null> {
|
||||
const weatherMaps = await getWeatherMaps();
|
||||
if (weatherMaps != undefined) {
|
||||
let url = weatherMaps.host;
|
||||
// url = 'https://cdn.rainviewer.com';
|
||||
let latest = "";
|
||||
if (weatherMaps.radar.nowcast.length > 0) {
|
||||
latest = weatherMaps.radar.nowcast[weatherMaps.radar.nowcast.length - 1].path;
|
||||
} else if (weatherMaps.radar.past.length > 0) {
|
||||
if (weatherMaps.radar.past.length > 0) {
|
||||
latest = weatherMaps.radar.past[weatherMaps.radar.past.length - 1].path;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
url += latest + "/256/{z}/{x}/{y}/2/1_1.png";
|
||||
// url += latest + "/256/{z}/{x}/{y}/255/1_1_1_0.webp";
|
||||
return url;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user