Skip to content

Commit

Permalink
Create needed components
Browse files Browse the repository at this point in the history
  • Loading branch information
Olaleye-Blessing committed Jun 24, 2023
1 parent 7ffd471 commit 3d0dedd
Show file tree
Hide file tree
Showing 12 changed files with 478 additions and 5 deletions.
16 changes: 11 additions & 5 deletions src/App.tsx
@@ -1,9 +1,15 @@
function App() {
import Navbar from "./components/Navbar";
import Forecasts from "./components/forecasts/Index";

const App = () => {
return (
<h1 className="text-3xl font-bold underline">
Hello world!
</h1>
<div className="px-4 py-3 min-h-screen bg-gray-200">
<Navbar />
<main className="mt-8 max-w-5xl mx-auto">
<Forecasts />
</main>
</div>
);
}

export default App;
export default App
80 changes: 80 additions & 0 deletions src/components/Histories.tsx
@@ -0,0 +1,80 @@
import { useEffect, useRef } from "react";
import { ClockIcon } from "@heroicons/react/20/solid";

// Comment 1
const modalHeight = "!h-[calc(100vh-2.75rem)]"

const Histories = () => {
// Comment 2
const locations: string[] = JSON.parse(localStorage.getItem("locations") || "[]");
// Comment 3
const ulRef = useRef<HTMLDivElement>(null);

const toggleUl = () => {
const { current: container } = ulRef;

if (!container) return;

container.classList.toggle("!w-screen");
container.classList.toggle(modalHeight);
};

useEffect(() => {
const closeUIModal = () => {
const { current: container } = ulRef;

if (!container) return;

container.classList.remove("!w-screen");
container.classList.remove(modalHeight);
}

window.addEventListener("click", closeUIModal);

return () => window.removeEventListener("click", closeUIModal)
}, [])

return (
<div className="relative">
<button type="button" onClick={(e) => {
e.stopPropagation();
toggleUl();
}}>
<ClockIcon className="h-6 w-6" />
</button>
<div
ref={ulRef}
className="absolute top-8 right-0 z-10 overflow-hidden h-0 w-0 bg-white transition-all duration-300 ease-in-out flex items-start justify-end pl-3 bg-opacity-5"
>
<div className="w-full max-w-[15rem]" onClick={e => {
e.stopPropagation();
}}>
{locations.length === 0 ? (
<p className="text-center text-gray-700 font-extrabold">
No history
</p>
) : (
<ul className="bg-white shadow-xl rounded-lg overflow-y-auto h-full max-w-[15rem] max-h-60">
{locations.map((location) => (
<li
key={location}
className="flex items-center justify-between"
>
<button
className="text-ellipsis text-left overflow-hidden whitespace-nowrap px-4 py-2 hover:bg-gray-500 transition-colors duration-150 w-full"
type="button"
onClick={() => { console.log("Get Data") }}
>
{location}
</button>
</li>
))}
</ul>
)}
</div>
</div>
</div>
);
};

export default Histories;
20 changes: 20 additions & 0 deletions src/components/Navbar.tsx
@@ -0,0 +1,20 @@
import Histories from "./Histories"
import Search from "./Search"

const Navbar = () => {
return (
<nav className="flex item-center justify-between">
<div className="w-full max-w-5xl mx-auto flex item-center justify-between">
<div>
<a href="/" className="text-2xl font-semibold">
Forecast
</a>
</div>
<Search />
<Histories />
</div>
</nav>
)
}

export default Navbar
28 changes: 28 additions & 0 deletions src/components/Search.tsx
@@ -0,0 +1,28 @@
import { useState } from "react";

const Search = () => {
const [query, setQuery] = useState("");

return (
<form
className="w-full max-w-xs"
onSubmit={(e) => {
e.preventDefault();
console.log("Get data");
}}
>
<input
type="search"
name="search"
id="search"
aria-label="Search for city/country forecast"
placeholder="Search for city/country forecast"
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
);
};

export default Search;
13 changes: 13 additions & 0 deletions src/components/forecasts/Index.tsx
@@ -0,0 +1,13 @@
import Today from "./Today";
import Other from "./Other";

const Index = () => {
return (
<section className="text-white-1 lg:flex">
<Today />
<Other />
</section>
);
};

export default Index;
36 changes: 36 additions & 0 deletions src/components/forecasts/Other.tsx
@@ -0,0 +1,36 @@
import { forecasts } from "../../data/weather";

const dateOptions: Intl.DateTimeFormatOptions = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};

const Other = () => {
return (
<ul className="grid grid-cols-[repeat(auto-fit,minmax(20rem,1fr))] lg:flex-grow lg:flex-shrink">
{forecasts.map((forecast, key) => {
const date = new Date(forecast.dt * 1000);
const day = date.toLocaleDateString(undefined, dateOptions).split(", ")[0];

return <li key={key} className="bg-black-3 even:bg-black-1 flex flex-col items-center justify-center text-center">
<time className="bg-black-2 py-4 block w-full">{day}</time>
<figure className="mt-8">
<img
src={`https://openweathermap.org/img/wn/${forecast.weather[0].icon}@2x.png`}
alt="sun icon"
/>
</figure>
<p className="text-4xl mb-8 mt-4">
{forecast.main.temp}
<sup className="relative text-[1.8rem] -top-8 font-semibold">o</sup>
C
</p>
</li>
})}
</ul>
)
}

export default Other
58 changes: 58 additions & 0 deletions src/components/forecasts/Today.tsx
@@ -0,0 +1,58 @@
import { today as data } from "../../data/weather";
import { windDirection } from "../../utils/weather";
import WeatherCondition from "./WeatherCondition";

const dateOptions: Intl.DateTimeFormatOptions = {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
};

const timeOptions: Intl.DateTimeFormatOptions = {
hour: "2-digit",
minute: "2-digit",
};

const Today = () => {
const date = new Date(data.dt * 1000);

return (
<div className="bg-black-1">
<time
dateTime={date.toString()}
className="flex items-center justify-between bg-black-2 p-4"
>
<span>{date.toLocaleString(undefined, dateOptions)}</span>
<span>{date.toLocaleTimeString(undefined, timeOptions)}</span>
</time>
<div className="px-4 py-12 lg:flex lg:items-center lg:justify-center lg:flex-col lg:h-[90%]">
<h3 className="text-4xl mb-4">{data.name}</h3>
<p>{data.weather[0].main}</p>
<div className="mt-8 flex items-center justify-start">
<p className="text-7xl">
{data.main.temp}
<sup className="relative text-[1.8rem] -top-8 font-semibold">o</sup>
C
</p>
<figure className="w-16 h-16">
<img
src={`https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`}
alt="sun icon"
/>
</figure>
</div>
<div className="flex items-center justify-start space-x-2 mt-4">
<WeatherCondition icon="umbrella" value={data.main.humidity} />
<WeatherCondition icon="wind" value={`${data.wind.speed}m/sec`} />
<WeatherCondition
icon="direction"
value={windDirection(data.wind.deg)} // Comment 1
/>
</div>
</div>
</div>
);
};

export default Today;
28 changes: 28 additions & 0 deletions src/components/forecasts/WeatherCondition.tsx
@@ -0,0 +1,28 @@
import { FC } from "react";
import Umbrella from "../icons/Umbrella";
import Wind from "../icons/Wind";
import Direction from "../icons/Direction";

const Icons = {
"umbrella": <Umbrella />,
"wind": <Wind />,
"direction": <Direction />
}

interface Props {
value: string | number;
icon: keyof typeof Icons;
}

const WeatherCondition: FC<Props> = ({ value, icon }) => {
return (
<div className="flex items-center justify-start">
<figure className="w-5 h-5 mr-2">
{Icons[icon]}
</figure>
<p className="font-semibold">{value}</p>
</div>
)
}

export default WeatherCondition

0 comments on commit 3d0dedd

Please sign in to comment.