├── 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 |
27 |
28 | 29 |
30 |
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 | <div className="countdown-container"> 29 | <Counter title="Dias" number={day} eventColor={eventColor} /> 30 | <Counter title="Horas" number={hour} eventColor={eventColor} /> 31 | <Counter title="Minutos" number={minute} eventColor={eventColor} /> 32 | <Counter title="Segundos" number={second} eventColor={eventColor} /> 33 | </div> 34 | </> 35 | ); 36 | }; 37 | 38 | export default Countdown; 39 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> -------------------------------------------------------------------------------- /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 | <div className="home"> 36 | <h2>Monte a sua contagem regressiva!</h2> 37 | <form className="countdown-form" onSubmit={handleSubmit}> 38 | <label> 39 | <span>Título:</span> 40 | <input 41 | type="text" 42 | name="title" 43 | placeholder="Digite o título do evento" 44 | onChange={(e) => setTitle(e.target.value)} 45 | required 46 | /> 47 | </label> 48 | <label> 49 | <span>Data do evento:</span> 50 | <input 51 | type="date" 52 | name="date" 53 | onChange={(e) => setDate(e.target.value)} 54 | required 55 | /> 56 | </label> 57 | <label> 58 | <span>Imagem:</span> 59 | <input 60 | type="text" 61 | placeholder="Insira a URL da imagem" 62 | onChange={(e) => setImage(e.target.value)} 63 | /> 64 | </label> 65 | <label> 66 | <span>Cor do tema:</span> 67 | <input 68 | type="color" 69 | name="color" 70 | onChange={(e) => setColor(e.target.value)} 71 | required 72 | /> 73 | </label> 74 | <input type="submit" value="Criar" /> 75 | </form> 76 | </div> 77 | ); 78 | }; 79 | 80 | export default Countdown; 81 | --------------------------------------------------------------------------------