FEAT: Added weather card which nicely displays the weather conditions from a METAR object #2
							
								
								
									
										99
									
								
								src/lib/components/Cards/WeatherCard.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/lib/components/Cards/WeatherCard.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
    import Card from './Card.svelte';
 | 
			
		||||
    import type { Metar } from '$lib/api/types/metar';
 | 
			
		||||
 | 
			
		||||
    export let metar: Metar;
 | 
			
		||||
 | 
			
		||||
    // Determine condition from METAR fields
 | 
			
		||||
    let condition = 'Unknown';
 | 
			
		||||
 | 
			
		||||
    $: {
 | 
			
		||||
        // Prefer wxString, then clouds[0].cover, then metarType
 | 
			
		||||
        if (metar.wxString) {
 | 
			
		||||
            condition = metar.wxString;
 | 
			
		||||
        } else if (metar.clouds && metar.clouds.length > 0 && metar.clouds[0].cover) {
 | 
			
		||||
            condition = metar.clouds[0].cover;
 | 
			
		||||
        } else if (metar.metarType) {
 | 
			
		||||
            condition = metar.metarType;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Choose icon and style based on condition
 | 
			
		||||
    let icon = '';
 | 
			
		||||
    let bg = '';
 | 
			
		||||
    let effect = '';
 | 
			
		||||
 | 
			
		||||
    $: {
 | 
			
		||||
        const cond = condition?.toLowerCase();
 | 
			
		||||
        if (cond.includes('cavok') || cond.includes('clear') || cond.includes('clr')) {
 | 
			
		||||
            icon = '☀️';
 | 
			
		||||
            bg = 'bg-yellow-100 border-yellow-300 text-yellow-700';
 | 
			
		||||
            effect = '';
 | 
			
		||||
        } else if (cond.includes('rain') || cond.includes('shower') || cond.includes('tsra') || cond.includes('ra') || cond.includes('br')) {
 | 
			
		||||
            icon = '🌧️';
 | 
			
		||||
            bg = 'bg-blue-100 border-blue-300 text-blue-700';
 | 
			
		||||
            effect = 'rain-effect';
 | 
			
		||||
        } else if (cond.includes('cloud') || cond.includes('ovc') || cond.includes('bkn') || cond.includes('sct') || cond.includes('few')) {
 | 
			
		||||
            icon = '☁️';
 | 
			
		||||
            bg = 'bg-gray-100 border-gray-300 text-gray-700';
 | 
			
		||||
            effect = '';
 | 
			
		||||
        } else if (cond.includes('snow')) {
 | 
			
		||||
            icon = '❄️';
 | 
			
		||||
            bg = 'bg-blue-50 border-blue-200 text-blue-500';
 | 
			
		||||
            effect = 'snow-effect';
 | 
			
		||||
        } else if (cond.includes('fog') || cond.includes('mist')) {
 | 
			
		||||
            icon = '🌫️';
 | 
			
		||||
            bg = 'bg-gray-200 border-gray-400 text-gray-600';
 | 
			
		||||
            effect = '';
 | 
			
		||||
        } else {
 | 
			
		||||
            icon = '🌡️';
 | 
			
		||||
            bg = 'bg-slate-100 border-slate-300 text-slate-700';
 | 
			
		||||
            effect = '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<Card containerStyle={`rounded-xl shadow-md border-2 ${bg} relative overflow-hidden`}>
 | 
			
		||||
    <div slot="content" class="flex flex-col items-center py-4">
 | 
			
		||||
        <div class="text-4xl mb-2">{icon}</div>
 | 
			
		||||
        <div class="text-xl font-bold">{metar.icaoId}</div>
 | 
			
		||||
        <div class="text-md mb-1">{metar.name}</div>
 | 
			
		||||
        <div class="text-2xl font-semibold">{metar.temp}°C</div>
 | 
			
		||||
        <div class="text-sm italic mb-2">{metar.altim}hPa</div>
 | 
			
		||||
        <div class="text-xs text-gray-500 truncate w-full text-center"><code>{metar.rawOb}</code></div>
 | 
			
		||||
    </div>
 | 
			
		||||
    {#if effect === 'rain-effect'}
 | 
			
		||||
        <div class="absolute inset-0 pointer-events-none rain"></div>
 | 
			
		||||
    {/if}
 | 
			
		||||
    {#if effect === 'snow-effect'}
 | 
			
		||||
        <div class="absolute inset-0 pointer-events-none snow"></div>
 | 
			
		||||
    {/if}
 | 
			
		||||
</Card>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
    .rain {
 | 
			
		||||
        background-image: repeating-linear-gradient(
 | 
			
		||||
            120deg,
 | 
			
		||||
            rgba(0, 0, 255, 0.1) 0 2px,
 | 
			
		||||
            transparent 2px 8px
 | 
			
		||||
        );
 | 
			
		||||
        animation: rainmove 0.7s linear infinite;
 | 
			
		||||
    }
 | 
			
		||||
    @keyframes rainmove {
 | 
			
		||||
        0% { background-position: 0 0; }
 | 
			
		||||
        100% { background-position: 20px 40px; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .snow {
 | 
			
		||||
        background-image: radial-gradient(white 1.5px, transparent 2px),
 | 
			
		||||
                          radial-gradient(white 1.5px, transparent 2px);
 | 
			
		||||
        background-size: 20px 20px;
 | 
			
		||||
        background-position: 0 0, 10px 10px;
 | 
			
		||||
        opacity: 0.5;
 | 
			
		||||
        animation: snowmove 1.5s linear infinite;
 | 
			
		||||
    }
 | 
			
		||||
    @keyframes snowmove {
 | 
			
		||||
        0% { background-position: 0 0, 10px 10px; }
 | 
			
		||||
        100% { background-position: 0 20px, 10px 30px; }
 | 
			
		||||
    }
 | 
			
		||||
</style>
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
    import { aviationWeatherApi } from '$lib/api/weatherservice';
 | 
			
		||||
    import type { Metar } from '$lib/api/types/metar'
 | 
			
		||||
    import { onDestroy } from 'svelte';
 | 
			
		||||
    import WeatherCard from '$lib/components/Cards/WeatherCard.svelte';
 | 
			
		||||
 | 
			
		||||
    let metar: Metar | null = null;
 | 
			
		||||
    let loading = false;
 | 
			
		||||
@@ -54,15 +55,8 @@
 | 
			
		||||
                <span class="text-red-600 text-xl">{error}</span>
 | 
			
		||||
            {:else if metar}
 | 
			
		||||
                <div class="text-left text-lg space-y-2">
 | 
			
		||||
                    <div><strong>ICAO:</strong> {metar.icaoId}</div>
 | 
			
		||||
                    <div><strong>Airport:</strong> {metar.name}</div>
 | 
			
		||||
                    <div><strong>Observed:</strong> {metar.reportTime}</div>
 | 
			
		||||
                    <div><strong>Temperature:</strong> {metar.temp}°C</div>
 | 
			
		||||
                    <div><strong>Dew Point:</strong> {metar.dewp}°C</div>
 | 
			
		||||
                    <div><strong>Wind:</strong> {metar.wdir}° at {metar.wspd}kt</div>
 | 
			
		||||
                    <div><strong>Visibility:</strong> {metar.visib}</div>
 | 
			
		||||
                    <div><strong>Pressure:</strong> {metar.altim} hPa</div>
 | 
			
		||||
                    <div><strong>Raw METAR:</strong> <code>{metar.rawOb}</code></div>
 | 
			
		||||
                    <WeatherCard metar={metar}/>
 | 
			
		||||
                    {console.log(metar)}
 | 
			
		||||
                </div>
 | 
			
		||||
            {:else}
 | 
			
		||||
                <Loading />
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user