Fixed some styling issues, fixed icons

This commit is contained in:
2023-09-29 00:18:48 -04:00
parent 28886eb93b
commit 240aae606b
9 changed files with 145 additions and 93 deletions

View File

@@ -1,8 +1,22 @@
import { getAirport } from '@/api/airport'; 'use client';
import Link from 'next/link';
export default async function Page({ params }: { params: { icao: string } }) { import { getAirport } from '@/api/airport';
import { Airport } from '@/api/airport.types';
import Link from 'next/link';
import { useEffect, useState } from 'react';
export default function Page({ params }: { params: { icao: string } }) {
const [airport, setAirport] = useState<Airport | undefined>(undefined);
useEffect(() => {
async function loadAirport() {
const { data: airport } = await getAirport({ icao: params.icao }); const { data: airport } = await getAirport({ icao: params.icao });
setAirport(airport);
}
loadAirport();
}, []);
if (airport) {
return ( return (
<> <>
<div className=''> <div className=''>
@@ -11,4 +25,7 @@ export default async function Page({ params }: { params: { icao: string } }) {
</div> </div>
</> </>
); );
} else {
return <></>;
}
} }

View File

@@ -3,4 +3,5 @@ import Metar from '@/components/Metars';
export default function Page() { export default function Page() {
return <Metar />; return <Metar />;
// return <></>;
} }

View File

@@ -8,7 +8,7 @@ import { useEffect, useState } from 'react';
import ReactDOMServer from 'react-dom/server'; import ReactDOMServer from 'react-dom/server';
import { Marker, TileLayer, Tooltip, useMap, useMapEvents } from 'react-leaflet'; import { Marker, TileLayer, Tooltip, useMap, useMapEvents } from 'react-leaflet';
import MetarModal from './MetarModal'; import MetarModal from './MetarModal';
import { BsCircle, BsCircleFill } from 'react-icons/bs'; import { Avatar, MantineProvider } from '@mantine/core';
export default function MapTiles() { export default function MapTiles() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@@ -60,20 +60,10 @@ export default function MapTiles() {
} }
function iconSize() { function iconSize() {
if (zoomLevel <= 4) { if (zoomLevel <= 5) {
return 'text-xs'; return 'xs';
} else if (zoomLevel <= 5) { } else {
return 'text-sm'; return 'sm';
} else if (zoomLevel <= 6) {
return 'text-base';
} else if (zoomLevel <= 7) {
return 'text-lg';
} else if (zoomLevel <= 9) {
return 'text-2xl';
} else if (zoomLevel <= 11) {
return 'text-3xl';
} else if (zoomLevel >= 12) {
return 'text-4xl';
} }
} }
@@ -81,46 +71,56 @@ export default function MapTiles() {
if (airport.metar?.flight_category == 'VFR') { if (airport.metar?.flight_category == 'VFR') {
return new DivIcon({ return new DivIcon({
html: ReactDOMServer.renderToString( html: ReactDOMServer.renderToString(
<div> <MantineProvider>
<BsCircle className={`${iconSize()} rounded-full bg-emerald-700`} /> <Avatar variant='filled' color='green' radius='xl' size={iconSize()}>
<span className={`${iconSize()} text-white`}>V</span> V
</div> </Avatar>
</MantineProvider>
), ),
className: 'metar-marker-icon' className: 'metar-marker-icon'
}); });
} else if (airport.metar?.flight_category == 'MVFR') { } else if (airport.metar?.flight_category == 'MVFR') {
return new DivIcon({ return new DivIcon({
html: ReactDOMServer.renderToString( html: ReactDOMServer.renderToString(
<div> <MantineProvider>
<BsCircle className={`${iconSize()} rounded-full bg-blue-700`} /> <Avatar variant='filled' color='blue' radius='xl' size={iconSize()}>
<span className={`${iconSize()} text-white`}>M</span> M
</div> </Avatar>
</MantineProvider>
), ),
className: 'metar-marker-icon' className: 'metar-marker-icon'
}); });
} else if (airport.metar?.flight_category == 'IFR') { } else if (airport.metar?.flight_category == 'IFR') {
return new DivIcon({ return new DivIcon({
html: ReactDOMServer.renderToString( html: ReactDOMServer.renderToString(
<div> <MantineProvider>
<BsCircle className={`${iconSize()} rounded-full bg-red-700`} /> <Avatar variant='filled' color='red' radius='xl' size={iconSize()}>
<span className={`${iconSize()} text-white`}>I</span> I
</div> </Avatar>
</MantineProvider>
), ),
className: 'metar-marker-icon' className: 'metar-marker-icon'
}); });
} else if (airport.metar?.flight_category == 'LIFR') { } else if (airport.metar?.flight_category == 'LIFR') {
return new DivIcon({ return new DivIcon({
html: ReactDOMServer.renderToString( html: ReactDOMServer.renderToString(
<div> <MantineProvider>
<BsCircle className={`${iconSize()} rounded-full bg-purple-700`} /> <Avatar variant='filled' color='purple' radius='xl' size={iconSize()}>
<span className={`${iconSize()} text-white`}>L</span> L
</div> </Avatar>
</MantineProvider>
), ),
className: 'metar-marker-icon' className: 'metar-marker-icon'
}); });
} else { } else {
return new DivIcon({ return new DivIcon({
html: ReactDOMServer.renderToString(<BsCircleFill className={`text-black`} />), html: ReactDOMServer.renderToString(
<MantineProvider>
<Avatar variant='filled' color='black' radius='xl' size={iconSize()}>
U
</Avatar>
</MantineProvider>
),
className: 'metar-marker-icon' className: 'metar-marker-icon'
}); });
} }

View File

@@ -2,8 +2,9 @@
import { MapContainer } from 'react-leaflet'; import { MapContainer } from 'react-leaflet';
import MapTiles from './MapTiles'; import MapTiles from './MapTiles';
import './metars.css';
export default function Map({ className = '' }: { className?: string }) { export default function Map() {
return ( return (
<> <>
<MapContainer <MapContainer
@@ -13,7 +14,7 @@ export default function Map({ className = '' }: { className?: string }) {
minZoom={3} // Zoomed out minZoom={3} // Zoomed out
id='map-container' id='map-container'
style={{ height: '94.5vh' }} style={{ height: '94.5vh' }}
className={`${className} overflow-y-hidden overflow-x-hidden`} className={`overflow-y-hidden overflow-x-hidden`}
attributionControl={false} attributionControl={false}
> >
<MapTiles /> <MapTiles />

View File

@@ -5,9 +5,16 @@ import { Metar } from '@/api/metar.types';
import { FaArrowsSpin, FaLocationArrow } from 'react-icons/fa6'; import { FaArrowsSpin, FaLocationArrow } from 'react-icons/fa6';
import Link from 'next/link'; import Link from 'next/link';
import { AiFillStar, AiOutlineStar } from 'react-icons/ai'; import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
import { BsFillCloudRainFill, BsFillCloudRainHeavyFill, BsFillCloudSleetFill, BsFillCloudSnowFill, BsQuestionLg } from 'react-icons/bs'; import {
BsFillCloudRainFill,
BsFillCloudRainHeavyFill,
BsFillCloudSleetFill,
BsFillCloudSnowFill,
BsQuestionLg
} from 'react-icons/bs';
import { useState } from 'react'; import { useState } from 'react';
import { Grid, Modal, Tooltip } from '@mantine/core'; import { Grid, Modal, Tooltip } from '@mantine/core';
import './metars.css';
interface MetarModalProps { interface MetarModalProps {
airport: Airport; airport: Airport;
@@ -23,31 +30,17 @@ export default function MetarModal({ airport, isOpen, onClose }: MetarModalProps
} }
return ( return (
<Modal <Modal opened={isOpen} onClose={onClose} withCloseButton={false} size={'55rem'} className='modal'>
title={ <span className='title'>
<span className='flex justify-between'>
<Link href={`/airport/${airport.icao}`}> <Link href={`/airport/${airport.icao}`}>
{airport.icao} {airport.full_name} {airport.icao} {airport.full_name}
</Link> </Link>
{isFavorite ? ( {isFavorite ? (
<AiFillStar <AiFillStar size={24} className='star' onClick={() => handleFavorite(false)} />
size={24}
className='cursor-pointer text-blue-500 hover:text-blue-400'
onClick={() => handleFavorite(false)}
/>
) : ( ) : (
<AiOutlineStar <AiOutlineStar size={24} className='star' onClick={() => handleFavorite(true)} />
size={24}
className='cursor-pointer text-blue-500 hover:text-blue-400'
onClick={() => handleFavorite(true)}
/>
)} )}
</span> </span>
}
opened={isOpen}
onClose={onClose}
className='select-none'
>
<div className='min-w-0 flex-1'> <div className='min-w-0 flex-1'>
<hr /> <hr />
{airport.metar && <MetarInfo metar={airport.metar} />} {airport.metar && <MetarInfo metar={airport.metar} />}

View File

@@ -1,7 +1,7 @@
import { Metar } from '@/api/metar.types'; import { Metar } from '@/api/metar.types';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
export default async function Metar({ className = '' }: { className?: string }) { export default async function Metar() {
const Map = dynamic(() => import('@/components/Metars/MetarMap'), { const Map = dynamic(() => import('@/components/Metars/MetarMap'), {
loading: () => ( loading: () => (
<div className='grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8'> <div className='grid min-h-full place-items-center px-6 py-24 sm:py-32 lg:px-8'>
@@ -12,5 +12,5 @@ export default async function Metar({ className = '' }: { className?: string })
), ),
ssr: false ssr: false
}); });
return <Map className={className} />; return <Map />;
} }

View File

@@ -0,0 +1,18 @@
/* https://stackoverflow.com/questions/55291179/how-to-overlay-content-on-react-leaflet-z-index-problem */
.leaflet-control { z-index: 0 !important}
.leaflet-pane { z-index: 0 !important}
.leaflet-top, .leaflet-bottom {z-index: 0 !important}
.modal {
user-select: none;
}
.modal .title {
display: flex;
width: 100%;
justify-content: space-between;
}
.modal .star {
cursor: pointer;
}

View File

@@ -4,13 +4,14 @@ import Link from 'next/link';
import { AiOutlineUser } from 'react-icons/ai'; import { AiOutlineUser } from 'react-icons/ai';
import { useState } from 'react'; import { useState } from 'react';
import { getAirports } from '@/api/airport'; import { getAirports } from '@/api/airport';
import { useRouter } from 'next/navigation'; // import { useRouter } from 'next/navigation';
import { Autocomplete, Avatar } from '@mantine/core'; import { Autocomplete, Avatar } from '@mantine/core';
import './topbar.css';
export default function Topbar() { export default function Topbar() {
const [searchValue, setSearchValue] = useState(''); const [searchValue, setSearchValue] = useState('');
const [airports, setAirports] = useState<{ key: string; value: string; label: string }[]>([]); const [airports, setAirports] = useState<{ key: string; value: string; label: string }[]>([]);
const router = useRouter(); // const router = useRouter();
async function onChange(value: string) { async function onChange(value: string) {
setSearchValue(value); setSearchValue(value);
@@ -24,16 +25,17 @@ export default function Topbar() {
); );
} }
function onClick(value: string) { // function onClick(value: string) {
router.push(`/airport/${value}`); // router.push(`/airport/${value}`);
} // }
return ( return (
<nav style={{ display: 'flex', justifyContent: 'space-between' }}> <nav className='navbar'>
<div style={{ display: 'flex' }}> <div className='left'>
<Link href={'/'} style={{ paddingLeft: '2em', paddingRight: '2em', margin: 'auto' }}> <Link href={'/'} className='title'>
<span>Aviation Weather</span> <span>Aviation Weather</span>
</Link> </Link>
<div className='search'>
<Autocomplete <Autocomplete
autoFocus autoFocus
radius='xl' radius='xl'
@@ -45,7 +47,8 @@ export default function Topbar() {
onBlur={() => setSearchValue('')} onBlur={() => setSearchValue('')}
/> />
</div> </div>
<Link className='' href={'/profile'}> </div>
<Link className='avatar' href={'/profile'}>
<Avatar> <Avatar>
<AiOutlineUser /> <AiOutlineUser />
</Avatar> </Avatar>

View File

@@ -0,0 +1,19 @@
.navbar {
display: flex;
justify-content: space-between;
height: 46px;
}
.navbar .left {
display: flex;
}
.navbar .title {
padding-left: 2em;
padding-right: 2em;
margin: auto;
}
.navbar .left .search {
margin: auto;
}