style: balance desktop hourly forecast card
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Pogoda z danych IMGW. Prosto. Pięknie. Aktualnie.**
|
**Pogoda z danych IMGW. Prosto. Pięknie. Aktualnie.**
|
||||||
|
|
||||||
`wtr.` to nowoczesna pogodowa PWA dla Polski oparta o publiczne dane IMGW i jawnie oznaczoną prognozę modelową Open-Meteo. Aplikacja prezentuje bieżące odczyty synoptyczne, prognozę godzinową i 7-dniową, stacje hydrologiczne oraz ostrzeżenia w spokojnym, mobilnym interfejsie z gradientami, kartami glassmorphism i subtelnymi animacjami. Dashboard pokazuje wykresy temperatury i opadu dla bieżącego dnia. Każdy dzień prognozy można także otworzyć w animowanym widoku szczegółowym z przebiegiem godzinowym oraz wykresami.
|
`wtr.` to nowoczesna pogodowa PWA dla Polski oparta o publiczne dane IMGW i jawnie oznaczoną prognozę modelową Open-Meteo. Aplikacja prezentuje bieżące odczyty synoptyczne, prognozę godzinową i 7-dniową, stacje hydrologiczne oraz ostrzeżenia w spokojnym, mobilnym interfejsie z gradientami, kartami glassmorphism i subtelnymi animacjami. Dashboard pokazuje rozszerzony desktopowy podgląd godzin, a także wykresy temperatury i opadu dla bieżącego dnia. Każdy dzień prognozy można także otworzyć w animowanym widoku szczegółowym z przebiegiem godzinowym oraz wykresami.
|
||||||
|
|
||||||
Interfejs jest dostępny po polsku i angielsku. Wybrany język jest zapisywany lokalnie w przeglądarce. Oryginalne treści ostrzeżeń oraz nazwy stacji pochodzą bezpośrednio z API IMGW i nie są automatycznie tłumaczone.
|
Interfejs jest dostępny po polsku i angielsku. Wybrany język jest zapisywany lokalnie w przeglądarce. Oryginalne treści ostrzeżeń oraz nazwy stacji pochodzą bezpośrednio z API IMGW i nie są automatycznie tłumaczone.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { CalendarDays, ChevronRight, Clock3, CloudSun, Droplets, ExternalLink, RefreshCw } from "lucide-react";
|
import { CalendarDays, ChevronRight, Clock3, CloudRain, CloudSun, Droplets, ExternalLink, RefreshCw, ThermometerSun, Wind } from "lucide-react";
|
||||||
import { DayForecastCharts } from "@/components/charts/day-forecast-charts";
|
import { DayForecastCharts } from "@/components/charts/day-forecast-charts";
|
||||||
import { DayForecastModal } from "@/components/forecast/day-forecast-modal";
|
import { DayForecastModal } from "@/components/forecast/day-forecast-modal";
|
||||||
import { ForecastIcon } from "@/components/forecast/forecast-icon";
|
import { ForecastIcon } from "@/components/forecast/forecast-icon";
|
||||||
@@ -15,6 +15,7 @@ import { useI18n } from "@/lib/i18n";
|
|||||||
import {
|
import {
|
||||||
formatForecastRainfall,
|
formatForecastRainfall,
|
||||||
formatForecastTemperature,
|
formatForecastTemperature,
|
||||||
|
formatForecastWind,
|
||||||
getForecastCondition,
|
getForecastCondition,
|
||||||
getHourlyForecastForDay,
|
getHourlyForecastForDay,
|
||||||
getUpcomingHourlyForecast,
|
getUpcomingHourlyForecast,
|
||||||
@@ -91,10 +92,10 @@ export function ForecastPanel({ latitude, longitude, locationName }: { latitude?
|
|||||||
<EmptyState title={t("forecast.emptyTitle")} description={t("forecast.emptyDescription")} />
|
<EmptyState title={t("forecast.emptyTitle")} description={t("forecast.emptyDescription")} />
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="grid items-start gap-3 lg:grid-cols-[1.35fr_1fr]">
|
<div className="grid items-start gap-3 lg:grid-cols-[1.35fr_1fr] lg:items-stretch">
|
||||||
<Card className="overflow-hidden p-4 sm:p-5">
|
<Card className="flex flex-col overflow-hidden p-4 sm:p-5 lg:h-full">
|
||||||
<h3 className="flex items-center gap-2 text-sm font-semibold"><Clock3 className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.hourly")}</h3>
|
<h3 className="flex items-center gap-2 text-sm font-semibold"><Clock3 className="size-4 text-sky-600 dark:text-sky-300" />{t("forecast.hourly")}</h3>
|
||||||
<div className="weather-scrollbar -mx-4 mt-4 overflow-x-auto px-4 pb-2 sm:-mx-5 sm:px-5">
|
<div className="weather-scrollbar -mx-4 mt-4 overflow-x-auto px-4 pb-2 sm:-mx-5 sm:px-5 lg:my-auto lg:pt-4">
|
||||||
<ul className="flex min-w-max gap-2">
|
<ul className="flex min-w-max gap-2">
|
||||||
{upcomingHours.map((hour, index) => (
|
{upcomingHours.map((hour, index) => (
|
||||||
<motion.li
|
<motion.li
|
||||||
@@ -102,13 +103,27 @@ export function ForecastPanel({ latitude, longitude, locationName }: { latitude?
|
|||||||
initial={{ opacity: 0, y: 8 }}
|
initial={{ opacity: 0, y: 8 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: Math.min(index * 0.018, 0.3), duration: 0.25 }}
|
transition={{ delay: Math.min(index * 0.018, 0.3), duration: 0.25 }}
|
||||||
className="w-[4.6rem] rounded-2xl border border-white/35 bg-white/25 px-2 py-3 text-center dark:border-white/10 dark:bg-white/5"
|
className="w-[4.6rem] rounded-2xl border border-white/35 bg-white/25 px-2 py-3 text-center dark:border-white/10 dark:bg-white/5 lg:w-[5.5rem] lg:py-4"
|
||||||
title={getForecastCondition(hour.weatherCode, language)}
|
title={getForecastCondition(hour.weatherCode, language)}
|
||||||
>
|
>
|
||||||
<p className="text-xs font-medium text-slate-600 dark:text-slate-300">{formatHour(hour.time)}</p>
|
<p className="text-xs font-medium text-slate-600 dark:text-slate-300">{formatHour(hour.time)}</p>
|
||||||
<ForecastIcon code={hour.weatherCode} className="mx-auto my-3 size-6 text-sky-600 dark:text-sky-300" />
|
<ForecastIcon code={hour.weatherCode} className="mx-auto my-3 size-6 text-sky-600 dark:text-sky-300" />
|
||||||
<p className="text-lg font-semibold tracking-tight">{formatForecastTemperature(hour.temperature, language)}</p>
|
<p className="text-lg font-semibold tracking-tight">{formatForecastTemperature(hour.temperature, language)}</p>
|
||||||
<p className="mt-2 flex items-center justify-center gap-1 text-[0.66rem] text-sky-700 dark:text-sky-300"><Droplets className="size-3" />{hour.precipitationProbability === null ? "—" : `${hour.precipitationProbability}%`}</p>
|
<p className="mt-2 flex items-center justify-center gap-1 text-[0.66rem] text-sky-700 dark:text-sky-300"><Droplets className="size-3" />{hour.precipitationProbability === null ? "—" : `${hour.precipitationProbability}%`}</p>
|
||||||
|
<div className="mt-3 hidden space-y-1.5 border-t border-white/35 pt-3 text-[0.66rem] text-slate-600 dark:border-white/10 dark:text-slate-300 lg:block">
|
||||||
|
<p className="flex items-center justify-center gap-1" title={t("forecast.apparentTemperature")}>
|
||||||
|
<ThermometerSun className="size-3 text-sky-600 dark:text-sky-300" />
|
||||||
|
{formatForecastTemperature(hour.feelsLike, language)}
|
||||||
|
</p>
|
||||||
|
<p className="flex items-center justify-center gap-1" title={t("weather.wind")}>
|
||||||
|
<Wind className="size-3 text-sky-600 dark:text-sky-300" />
|
||||||
|
{formatForecastWind(hour.windSpeed, language)}
|
||||||
|
</p>
|
||||||
|
<p className="flex items-center justify-center gap-1" title={t("forecast.precipitation")}>
|
||||||
|
<CloudRain className="size-3 text-sky-600 dark:text-sky-300" />
|
||||||
|
{formatForecastRainfall(hour.precipitation, language)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</motion.li>
|
</motion.li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user