├── src
├── assets
│ └── newyear.jpg
├── components
│ ├── Title.css
│ ├── Title.jsx
│ ├── Counter.css
│ └── Counter.jsx
├── index.css
├── context
│ └── CountdownContext.jsx
├── App.css
├── App.jsx
├── routes
│ ├── Home.css
│ ├── Countdown.jsx
│ └── Home.jsx
├── main.jsx
└── hooks
│ └── useCountdown.jsx
├── vite.config.js
├── .gitignore
├── index.html
├── package.json
└── public
└── vite.svg
/src/assets/newyear.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matheusbattisti/countdown/HEAD/src/assets/newyear.jpg
--------------------------------------------------------------------------------
/src/components/Title.css:
--------------------------------------------------------------------------------
1 | .title {
2 | text-align: center;
3 | margin-bottom: 1rem;
4 | font-size: 2.4rem;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | font-family: "Helvetica";
5 | }
6 |
7 | body {
8 | min-height: 100vh;
9 | }
10 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/components/Title.jsx:
--------------------------------------------------------------------------------
1 | import "./Title.css";
2 |
3 | const Title = ({ title, eventColor }) => {
4 | return (
5 |
6 | {title}
7 |
8 | );
9 | };
10 |
11 | export default Title;
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/src/components/Counter.css:
--------------------------------------------------------------------------------
1 | .counter {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | justify-content: center;
6 | gap: 1rem;
7 | }
8 |
9 | .counter-number {
10 | background-color: #000;
11 | color: #fff;
12 | padding: 1rem;
13 | border-radius: 0.7rem;
14 | font-size: 4rem;
15 | font-weight: bold;
16 | min-width: 80px;
17 | text-align: center;
18 | }
19 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/Counter.jsx:
--------------------------------------------------------------------------------
1 | import "./Counter.css";
2 |
3 | const Counter = ({ title, number, eventColor }) => {
4 | return (
5 |
6 |
7 | {number}
8 |
9 |
10 | {title}
11 |
12 |
13 | );
14 | };
15 |
16 | export default Counter;
17 |
--------------------------------------------------------------------------------
/src/context/CountdownContext.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 |
3 | const CountdownContext = React.createContext(null);
4 |
5 | const CountdownProvider = ({ children }) => {
6 | const [event, setEvent] = useState(null);
7 |
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | };
14 |
15 | export { CountdownContext, CountdownProvider };
16 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | min-height: 100vh;
3 | background-size: cover;
4 | background-position: center;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | }
9 |
10 | .container {
11 | min-width: 50%;
12 | min-height: 300px;
13 | background-color: rgba(255, 255, 255, 0.8);
14 | padding: 2rem;
15 | border-radius: 1rem;
16 | }
17 |
18 | .countdown-container {
19 | display: flex;
20 | justify-content: center;
21 | align-items: center;
22 | gap: 2rem;
23 | height: 200px;
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "countdown",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-router-dom": "^6.6.1"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.26",
18 | "@types/react-dom": "^18.0.9",
19 | "@vitejs/plugin-react": "^3.0.0",
20 | "vite": "^4.0.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 |
3 | import { useContext } from "react";
4 |
5 | import { CountdownContext } from "./context/CountdownContext";
6 |
7 | import NewYear from "./assets/newyear.jpg";
8 |
9 | import "./App.css";
10 |
11 | function App() {
12 | const { event } = useContext(CountdownContext);
13 |
14 | let eventImage = null;
15 |
16 | if (event) eventImage = event.image;
17 |
18 | return (
19 |
31 | );
32 | }
33 |
34 | export default App;
35 |
--------------------------------------------------------------------------------
/src/routes/Home.css:
--------------------------------------------------------------------------------
1 | .home h2 {
2 | text-align: center;
3 | font-size: 1.6rem;
4 | margin-bottom: 1rem;
5 | }
6 |
7 | .countdown-form {
8 | display: flex;
9 | flex-direction: column;
10 | gap: 1rem;
11 | max-width: 400px;
12 | margin: 0 auto;
13 | }
14 |
15 | .countdown-form label {
16 | display: flex;
17 | flex-direction: column;
18 | gap: 0.5rem;
19 | }
20 |
21 | .countdown-form span {
22 | font-weight: bold;
23 | }
24 |
25 | .countdown-form input {
26 | padding: 0.5rem;
27 | border-radius: 4px;
28 | border: 1px solid #aaa;
29 | }
30 |
31 | .countdown-form input[type="color"] {
32 | padding: 2px;
33 | }
34 |
35 | .countdown-form input[type="submit"] {
36 | background-color: #000;
37 | color: #fff;
38 | font-weight: bold;
39 | width: 120px;
40 | padding: 0.8rem;
41 | cursor: pointer;
42 | opacity: 0.8;
43 | transition: 0.4s;
44 | }
45 |
46 | .countdown-form input[type="submit"]:hover {
47 | opacity: 1;
48 | }
49 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 | import "./index.css";
5 |
6 | import { createBrowserRouter, RouterProvider } from "react-router-dom";
7 |
8 | // pages
9 | import Home from "./routes/Home";
10 | import Countdown from "./routes/Countdown";
11 |
12 | // Context
13 | import { CountdownProvider } from "./context/CountdownContext";
14 |
15 | const router = createBrowserRouter([
16 | {
17 | path: "/",
18 | element: ,
19 | children: [
20 | {
21 | path: "/",
22 | element: ,
23 | },
24 | {
25 | path: "/countdown",
26 | element: ,
27 | },
28 | ],
29 | },
30 | ]);
31 |
32 | ReactDOM.createRoot(document.getElementById("root")).render(
33 |
34 |
35 |
36 |
37 |
38 | );
39 |
--------------------------------------------------------------------------------
/src/hooks/useCountdown.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 |
3 | const useCountdown = (date) => {
4 | const [day, setDay] = useState();
5 | const [hour, setHour] = useState();
6 | const [minute, setMinute] = useState();
7 | const [second, setSecond] = useState();
8 |
9 | const countdown = () => {
10 | const countDate = new Date(date).getTime();
11 |
12 | const now = new Date().getTime();
13 |
14 | const gap = countDate - now;
15 |
16 | const second = 1000;
17 | const minute = second * 60;
18 | const hour = minute * 60;
19 | const day = hour * 24;
20 |
21 | const dayNumber = Math.floor(gap / day);
22 | const hourNumber = Math.floor((gap % day) / hour);
23 | const minuteNumber = Math.floor((gap % hour) / minute);
24 | const secondNumber = Math.floor((gap % minute) / second);
25 |
26 | setDay(dayNumber);
27 | setHour(hourNumber);
28 | setMinute(minuteNumber);
29 | setSecond(secondNumber);
30 | };
31 |
32 | setInterval(countdown, 1000);
33 |
34 | return [day, hour, minute, second];
35 | };
36 |
37 | export default useCountdown;
38 |
--------------------------------------------------------------------------------
/src/routes/Countdown.jsx:
--------------------------------------------------------------------------------
1 | import Title from "../components/Title";
2 | import Counter from "../components/Counter";
3 |
4 | import { useContext, useEffect } from "react";
5 | import { CountdownContext } from "../context/CountdownContext";
6 |
7 | import useCountdown from "../hooks/useCountdown";
8 | import { Navigate } from "react-router-dom";
9 |
10 | const Countdown = () => {
11 | const { event } = useContext(CountdownContext);
12 |
13 | if (!event) return ;
14 |
15 | let eventTitle = null;
16 |
17 | if (event.title) eventTitle = event.title;
18 |
19 | let eventColor = null;
20 |
21 | if (event.color) eventColor = event.color;
22 |
23 | const [day, hour, minute, second] = useCountdown(event.date);
24 |
25 | return (
26 | <>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | >
35 | );
36 | };
37 |
38 | export default Countdown;
39 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/routes/Home.jsx:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from "react";
2 | import { CountdownContext } from "../context/CountdownContext";
3 | import { useNavigate } from "react-router-dom";
4 |
5 | import "./Home.css";
6 |
7 | const Countdown = () => {
8 | const [title, setTitle] = useState();
9 | const [date, setDate] = useState();
10 | const [image, setImage] = useState();
11 | const [color, setColor] = useState();
12 |
13 | const { event, setEvent } = useContext(CountdownContext);
14 |
15 | const navigate = useNavigate();
16 |
17 | const handleSubmit = (e) => {
18 | e.preventDefault();
19 |
20 | const eventObject = {
21 | title,
22 | date,
23 | image,
24 | color,
25 | };
26 |
27 | setEvent(eventObject);
28 |
29 | console.log(eventObject);
30 |
31 | navigate("/countdown");
32 | };
33 |
34 | return (
35 |
36 |
Monte a sua contagem regressiva!
37 |
76 |
77 | );
78 | };
79 |
80 | export default Countdown;
81 |
--------------------------------------------------------------------------------