├── 10_todo_redux └── todoredux │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── AddTodo.jsx │ │ └── TodoList.jsx │ ├── index.css │ ├── main.jsx │ ├── slices │ │ └── todoSlice.js │ └── store.js │ └── vite.config.js ├── 11_gerador_pdf └── geradorpdf │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── GeneratePDF.jsx │ │ ├── ImageUpload.jsx │ │ └── TextStyleConfig.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 12_calculadora_imposto └── calcimposto │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── TaxForm.jsx │ │ └── TaxReport.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 13_pomodoro └── pomodoro │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── PomodoroTimer.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 14_carousel └── carousel │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Carousel.jsx │ │ ├── Indicators.jsx │ │ └── Slide.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 15_tabela_ordenavel └── tabelaordenavel │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── SearchBar.jsx │ │ ├── TableHeader.jsx │ │ └── TableRow.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 16_infinite_scroll └── infinitescroll │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── InfiniteScroll.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 18_blog_com_axios ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── public │ └── vite.svg ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── axios │ │ └── config.js │ ├── components │ │ ├── Navbar.css │ │ └── Navbar.jsx │ ├── index.css │ ├── main.jsx │ └── routes │ │ ├── Admin.css │ │ ├── Admin.jsx │ │ ├── EditPost.jsx │ │ ├── Home.css │ │ ├── Home.jsx │ │ ├── NewPost.css │ │ ├── NewPost.jsx │ │ ├── Post.css │ │ └── Post.jsx └── vite.config.js ├── 19_multistep_form ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── public │ └── vite.svg ├── src │ ├── App.css │ ├── App.jsx │ ├── components │ │ ├── ReviewForm.css │ │ ├── ReviewForm.jsx │ │ ├── Steps.css │ │ ├── Steps.jsx │ │ ├── Thanks.css │ │ ├── Thanks.jsx │ │ └── UserForm.jsx │ ├── hooks │ │ └── useForm.jsx │ ├── index.css │ └── main.jsx └── vite.config.js ├── 1_calculadora └── calculadora │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Calculator.css │ │ └── Calculator.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 20_github_api ├── .gitignore ├── index.html ├── package-lock.json ├── package.json ├── public │ └── vite.svg ├── src │ ├── App.module.css │ ├── App.tsx │ ├── components │ │ ├── BackBtn.module.css │ │ ├── BackBtn.tsx │ │ ├── Error.tsx │ │ ├── Loader.module.css │ │ ├── Loader.tsx │ │ ├── Repo.module.css │ │ ├── Repo.tsx │ │ ├── Search.module.css │ │ ├── Search.tsx │ │ ├── User.module.css │ │ └── User.tsx │ ├── index.css │ ├── main.tsx │ ├── routes │ │ ├── Home.tsx │ │ ├── Repos.module.css │ │ └── Repos.tsx │ ├── types │ │ ├── repo.ts │ │ └── user.ts │ └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── 22_form_com_react_hook_e_ts ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── vite.svg ├── src │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── Form │ │ │ └── Form.tsx │ ├── hooks │ │ └── useForms.tsx │ ├── index.css │ ├── main.tsx │ ├── schemas │ │ └── formSchema.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── 25_newsletter_form ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public │ └── vite.svg ├── src │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── Form.tsx │ ├── index.css │ ├── main.tsx │ ├── types │ │ └── User.ts │ ├── utils │ │ └── validate.ts │ └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts ├── 2_cronometro └── cronometro │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── LapList.jsx │ │ ├── Timer.css │ │ ├── Timer.jsx │ │ ├── TimerControls.jsx │ │ └── TimerDisplay.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 3_conversor_moeda └── conversor_moeda │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── CurrencyConverter.css │ │ └── CurrencyConverter.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 4_clima └── clima │ ├── .env │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.jsx │ ├── AppStyles.js │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Busca.jsx │ │ ├── BuscaStyles.js │ │ ├── ClimaAtual.jsx │ │ ├── ClimaAtualStyles.js │ │ ├── Previsao.jsx │ │ └── PrevisaoStyles.js │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 5_citacoes └── citacoes │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── Citacao.jsx │ ├── data.js │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 6_album └── album │ ├── .env │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── Foto.jsx │ │ ├── FotoAmpliada.jsx │ │ ├── FotoList.jsx │ │ └── SearchBar.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 7_markdown_viewer └── markdownviewer │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── Toolbar.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js ├── 8_relogio_mundial └── relogiomundial │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ └── vite.svg │ ├── src │ ├── App.css │ ├── App.jsx │ ├── assets │ │ └── react.svg │ ├── components │ │ └── TimeZoneClock.jsx │ ├── index.css │ └── main.jsx │ └── vite.config.js └── 9_jogo_da_velha_com_ia └── jogodavelhaia ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public └── vite.svg ├── src ├── App.css ├── App.jsx ├── assets │ └── react.svg ├── components │ ├── Board.jsx │ └── Square.jsx ├── index.css └── main.jsx └── vite.config.js /10_todo_redux/todoredux/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/.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 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todoredux", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@reduxjs/toolkit": "^1.9.5", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-redux": "^8.1.2", 17 | "redux": "^4.2.1" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.15", 21 | "@types/react-dom": "^18.2.7", 22 | "@vitejs/plugin-react": "^4.0.3", 23 | "eslint": "^8.45.0", 24 | "eslint-plugin-react": "^7.32.2", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.3", 27 | "vite": "^4.4.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import TodoList from "./components/TodoList"; 3 | import AddTodo from "./components/AddTodo"; 4 | 5 | const App = () => { 6 | return ( 7 |
8 |

Lista de Tarefas com Redux

9 | 10 | 11 |
12 | ); 13 | }; 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/components/AddTodo.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useDispatch } from "react-redux"; 3 | import { addTodo } from "../slices/todoSlice"; 4 | 5 | function AddTodo() { 6 | const [input, setInput] = useState(""); 7 | const dispatch = useDispatch(); 8 | 9 | const handleSubmit = (e) => { 10 | e.preventDefault(); 11 | if (input.trim() === "") return; 12 | dispatch(addTodo(input)); 13 | setInput(""); 14 | }; 15 | 16 | return ( 17 |
18 | setInput(e.target.value)} 22 | placeholder="Adicione uma tarefa..." 23 | /> 24 | 25 |
26 | ); 27 | } 28 | 29 | export default AddTodo; 30 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/components/TodoList.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSelector, useDispatch } from "react-redux"; 3 | import { toggleTodo, removeTodo, filterTodos } from "../slices/todoSlice"; 4 | 5 | function TodoList() { 6 | const { list, filter } = useSelector((state) => state.todos); 7 | const dispatch = useDispatch(); 8 | 9 | const filteredList = list.filter((todo) => { 10 | if (filter === "all") return true; 11 | if (filter === "completed") return todo.completed; 12 | if (filter === "incomplete") return !todo.completed; 13 | return true; 14 | }); 15 | 16 | return ( 17 |
18 | 19 | 22 | 25 | 42 |
43 | ); 44 | } 45 | 46 | export default TodoList; 47 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/index.css: -------------------------------------------------------------------------------- 1 | body, 2 | h1, 3 | ul, 4 | li { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | body { 10 | background-color: #f4f4f4; 11 | font-family: Helvetica; 12 | } 13 | 14 | h1 { 15 | text-align: center; 16 | margin-bottom: 20px; 17 | } 18 | 19 | .container { 20 | max-width: 500px; 21 | margin: 50px auto; 22 | background-color: #fff; 23 | padding: 20px; 24 | border-radius: 5px; 25 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 26 | } 27 | 28 | ul { 29 | list-style: none; 30 | } 31 | 32 | li { 33 | background: #eee; 34 | margin: 8px 0; 35 | padding: 12px; 36 | text-align: left; 37 | border-radius: 4px; 38 | display: flex; 39 | justify-content: space-between; 40 | align-items: center; 41 | } 42 | 43 | form { 44 | margin-bottom: 20px; 45 | } 46 | 47 | input, 48 | button { 49 | padding: 10px 20px; 50 | margin-right: 10px; 51 | } 52 | 53 | button { 54 | border: none; 55 | border-radius: 4px; 56 | cursor: pointer; 57 | } 58 | 59 | button:hover { 60 | background-color: #ccc; 61 | } 62 | 63 | li span { 64 | cursor: pointer; 65 | } 66 | 67 | li span.line-through { 68 | text-decoration: line-through; 69 | color: #888; 70 | } 71 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | 6 | import { Provider } from "react-redux"; 7 | import { store } from "./store"; 8 | 9 | ReactDOM.createRoot(document.getElementById("root")).render( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/slices/todoSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | export const todoSlice = createSlice({ 4 | name: "todos", 5 | initialState: { 6 | list: [], 7 | filter: "all", // 'all', 'completed', 'incomplete' 8 | }, 9 | reducers: { 10 | addTodo: (state, action) => { 11 | state.list.push({ 12 | id: new Date().toISOString(), 13 | text: action.payload, 14 | completed: false, 15 | }); 16 | }, 17 | toggleTodo: (state, action) => { 18 | const todo = state.list.find((todo) => todo.id === action.payload); 19 | if (todo) { 20 | todo.completed = !todo.completed; 21 | } 22 | }, 23 | removeTodo: (state, action) => { 24 | state.list = state.list.filter((todo) => todo.id !== action.payload); 25 | }, 26 | filterTodos: (state, action) => { 27 | state.filter = action.payload; 28 | }, 29 | }, 30 | }); 31 | 32 | export const { addTodo, toggleTodo, removeTodo, filterTodos } = 33 | todoSlice.actions; 34 | 35 | export default todoSlice.reducer; 36 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/src/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import todoReducer from "./slices/todoSlice"; 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | todos: todoReducer, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /10_todo_redux/todoredux/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 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/.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 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geradorpdf", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "pdfmake": "^0.2.7", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@vitejs/plugin-react": "^4.0.3", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import GeneratePDF from "./components/GeneratePDF"; 3 | 4 | function App() { 5 | return ( 6 |
7 |

Gerador de PDF Dinâmico

8 | 9 |
10 | ); 11 | } 12 | 13 | export default App; 14 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/components/ImageUpload.jsx: -------------------------------------------------------------------------------- 1 | const ImageUpload = ({ setImage }) => { 2 | const handleImageUpload = (e) => { 3 | const file = e.target.files[0]; 4 | const reader = new FileReader(); 5 | 6 | reader.readAsDataURL(file); 7 | reader.onloadend = () => { 8 | setImage(reader.result); 9 | }; 10 | }; 11 | 12 | return ( 13 |
14 | 23 |
24 | ); 25 | }; 26 | 27 | export default ImageUpload; 28 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/components/TextStyleConfig.jsx: -------------------------------------------------------------------------------- 1 | const TextStyleConfig = ({ 2 | fontSize, 3 | setFontSize, 4 | fontColor, 5 | setFontColor, 6 | isBold, 7 | setIsBold, 8 | }) => { 9 | return ( 10 |
11 | 20 | 29 | 37 |
38 | ); 39 | }; 40 | 41 | export default TextStyleConfig; 42 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Courier; 3 | } 4 | 5 | h1 { 6 | text-align: center; 7 | } 8 | 9 | .container { 10 | padding: 20px; 11 | max-width: 500px; 12 | margin: 0 auto; 13 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 14 | background-color: #fff; 15 | } 16 | 17 | .label { 18 | display: flex; 19 | flex-direction: column; 20 | margin-bottom: 20px; 21 | font-size: 16px; 22 | } 23 | 24 | .input { 25 | padding: 10px; 26 | font-size: 16px; 27 | border: 1px solid #ccc; 28 | border-radius: 4px; 29 | } 30 | 31 | .button { 32 | padding: 10px 20px; 33 | font-size: 16px; 34 | background-color: #007bff; 35 | color: #fff; 36 | border: none; 37 | border-radius: 4px; 38 | cursor: pointer; 39 | } 40 | 41 | .button:hover { 42 | background-color: #0056b3; 43 | } 44 | 45 | .textStyleConfig { 46 | padding: 10px; 47 | border: 1px solid #ccc; 48 | border-radius: 4px; 49 | margin-bottom: 20px; 50 | display: flex; 51 | flex-direction: column; 52 | gap: 15px; 53 | } 54 | 55 | .imageUpload { 56 | padding: 10px; 57 | border: 1px solid #ccc; 58 | border-radius: 4px; 59 | margin-bottom: 20px; 60 | } 61 | 62 | .configLabel { 63 | font-size: 16px; 64 | margin-bottom: 10px; 65 | } 66 | 67 | .colorPicker { 68 | cursor: pointer; 69 | } 70 | 71 | .fileInput { 72 | cursor: pointer; 73 | } 74 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /11_gerador_pdf/geradorpdf/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 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/.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 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calcimposto", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emotion/react": "^11.11.1", 14 | "@emotion/styled": "^11.11.0", 15 | "@mui/material": "^5.14.7", 16 | "formik": "^2.4.3", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "react-scripts": "^5.0.1", 20 | "yup": "^1.2.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.2.15", 24 | "@types/react-dom": "^18.2.7", 25 | "@vitejs/plugin-react": "^4.0.3", 26 | "eslint": "^8.45.0", 27 | "eslint-plugin-react": "^7.32.2", 28 | "eslint-plugin-react-hooks": "^4.6.0", 29 | "eslint-plugin-react-refresh": "^0.4.3", 30 | "vite": "^4.4.5" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Container, Typography } from "@mui/material"; 3 | import TaxForm from "./components/TaxForm"; 4 | import TaxReport from "./components/TaxReport"; 5 | 6 | const App = () => { 7 | const [taxData, setTaxData] = useState(null); 8 | 9 | const calculateTax = (values) => { 10 | let tax = 0; 11 | const income = parseFloat(values.income); 12 | 13 | if (income <= 10000) { 14 | tax = income * 0.05; 15 | } else if (income <= 20000) { 16 | tax = income * 0.1; 17 | } else { 18 | tax = income * 0.15; 19 | } 20 | 21 | const taxData = { 22 | ...values, 23 | tax, 24 | }; 25 | 26 | setTaxData(taxData); 27 | }; 28 | 29 | console.log(taxData); 30 | 31 | return ( 32 | 33 | 34 | Calculadora de Impostos 35 | 36 | 37 | {taxData && } 38 | 39 | ); 40 | }; 41 | 42 | export default App; 43 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/src/components/TaxReport.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Container, Typography } from "@mui/material"; 3 | 4 | const TaxReport = ({ taxData }) => { 5 | return ( 6 | 7 | Relatório de Impostos 8 | Nome: {taxData.name} 9 | Idade: {taxData.age} anos 10 | 11 | Renda: 12 | {parseFloat(taxData.income).toLocaleString("pt-BR", { 13 | style: "currency", 14 | currency: "BRL", 15 | })} 16 | 17 | 18 | Imposto a Pagar: 19 | {parseFloat(taxData.tax).toLocaleString("pt-BR", { 20 | style: "currency", 21 | currency: "BRL", 22 | })} 23 | 24 | 25 | ); 26 | }; 27 | 28 | export default TaxReport; 29 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_react/992dfa03ff6f17b6d328471db595404494c26f7a/12_calculadora_imposto/calcimposto/src/index.css -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /12_calculadora_imposto/calcimposto/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 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/.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 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pomodoro", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@chakra-ui/react": "^2.8.0", 14 | "@emotion/react": "^11.11.1", 15 | "@emotion/styled": "^11.11.0", 16 | "framer-motion": "^10.16.2", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0" 19 | }, 20 | "devDependencies": { 21 | "@types/react": "^18.2.15", 22 | "@types/react-dom": "^18.2.7", 23 | "@vitejs/plugin-react": "^4.0.3", 24 | "eslint": "^8.45.0", 25 | "eslint-plugin-react": "^7.32.2", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.4.3", 28 | "vite": "^4.4.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ChakraProvider, CSSReset, Box, Text } from "@chakra-ui/react"; 3 | import PomodoroTimer from "./components/PomodoroTimer"; 4 | 5 | function App() { 6 | return ( 7 | 8 | 9 | 10 | Pomodoro 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_react/992dfa03ff6f17b6d328471db595404494c26f7a/13_pomodoro/pomodoro/src/index.css -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /13_pomodoro/pomodoro/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 | -------------------------------------------------------------------------------- /14_carousel/carousel/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /14_carousel/carousel/.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 | -------------------------------------------------------------------------------- /14_carousel/carousel/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /14_carousel/carousel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /14_carousel/carousel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carousel", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.2.15", 18 | "@types/react-dom": "^18.2.7", 19 | "@vitejs/plugin-react": "^4.0.3", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /14_carousel/carousel/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | import Carousel from "./components/Carousel"; 4 | 5 | const imageUrls = [ 6 | "https://images.unsplash.com/photo-1501854140801-50d01698950b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1950&q=80", 7 | "https://images.unsplash.com/photo-1426604966848-d7adac402bff?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80", 8 | "https://images.unsplash.com/photo-1470252649378-9c29740c9fa8?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2070&q=80", 9 | ]; 10 | 11 | const App = () => { 12 | return ( 13 |
14 |

Carousel

15 | 16 |
17 | ); 18 | }; 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/components/Carousel.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Slide from "./Slide"; 3 | import Indicators from "./Indicators"; 4 | 5 | const Carousel = ({ imageUrls }) => { 6 | const [activeIndex, setActiveIndex] = useState(0); 7 | const [manualChange, setManualChange] = useState(false); 8 | 9 | useEffect(() => { 10 | const interval = setInterval(() => { 11 | if (!manualChange) { 12 | setActiveIndex((prevIndex) => (prevIndex + 1) % imageUrls.length); 13 | } 14 | setManualChange(false); // Reset manual change 15 | }, 5000); 16 | 17 | return () => clearInterval(interval); 18 | }, [manualChange, imageUrls.length]); 19 | 20 | const goPrev = () => { 21 | setManualChange(true); 22 | setActiveIndex( 23 | (prevIndex) => (prevIndex - 1 + imageUrls.length) % imageUrls.length 24 | ); 25 | }; 26 | 27 | const goNext = () => { 28 | setManualChange(true); 29 | setActiveIndex((prevIndex) => (prevIndex + 1) % imageUrls.length); 30 | }; 31 | 32 | return ( 33 |
34 | {imageUrls.map((url, index) => ( 35 | 36 | ))} 37 | 38 | 41 | 44 |
45 | ); 46 | }; 47 | 48 | export default Carousel; 49 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/components/Indicators.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Indicators = ({ activeIndex, length }) => { 4 | return ( 5 |
6 | {Array.from({ length }, (_, index) => ( 7 |
11 | ))} 12 |
13 | ); 14 | }; 15 | 16 | export default Indicators; 17 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/components/Slide.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Slide = ({ url, isActive }) => { 4 | return ( 5 |
6 | {isActive && } 7 |
8 | ); 9 | }; 10 | 11 | export default Slide; 12 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Helvetica; 3 | } 4 | 5 | /* Estilos para o carrossel */ 6 | h1 { 7 | text-align: center; 8 | } 9 | 10 | .carousel { 11 | width: 100%; 12 | max-width: 800px; 13 | margin: auto; 14 | overflow: hidden; 15 | position: relative; 16 | } 17 | 18 | /* Estilos para cada slide */ 19 | .slide { 20 | opacity: 0; 21 | position: absolute; 22 | width: 100%; 23 | transition: opacity 1s; 24 | } 25 | 26 | /* Estilo para o slide ativo */ 27 | .slide.active { 28 | opacity: 1; 29 | position: relative; 30 | transition: opacity 1s; 31 | } 32 | 33 | /* Imagens dentro dos slides */ 34 | .slide img { 35 | width: 100%; 36 | } 37 | 38 | /* Estilos para os indicadores */ 39 | .indicators { 40 | position: absolute; 41 | bottom: 0; 42 | width: 100%; 43 | display: flex; 44 | justify-content: center; 45 | padding: 15px; 46 | } 47 | 48 | /* Estilo de cada indicador */ 49 | .indicator { 50 | width: 10px; 51 | height: 10px; 52 | background: #6e6b6b; 53 | border-radius: 50%; 54 | margin: 0 5px; 55 | transition: background 0.3s; 56 | } 57 | 58 | /* Estilo para o indicador ativo */ 59 | .indicator.active { 60 | background: #fff; 61 | } 62 | 63 | /* Estilos para os botões */ 64 | .carousel-button { 65 | position: absolute; 66 | top: 50%; 67 | transform: translateY(-50%); 68 | background: rgba(0, 0, 0, 0.5); 69 | color: #fff; 70 | border: none; 71 | padding: 10px; 72 | cursor: pointer; 73 | z-index: 10; 74 | } 75 | 76 | .carousel-button.prev { 77 | left: 0; 78 | } 79 | 80 | .carousel-button.next { 81 | right: 0; 82 | } 83 | -------------------------------------------------------------------------------- /14_carousel/carousel/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /14_carousel/carousel/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 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/.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 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tabelaordenavel", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.2.15", 18 | "@types/react-dom": "^18.2.7", 19 | "@vitejs/plugin-react": "^4.0.3", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/components/SearchBar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const SearchBar = ({ searchTerm, setSearchTerm }) => { 4 | return ( 5 | setSearchTerm(e.target.value)} 10 | /> 11 | ); 12 | }; 13 | 14 | export default SearchBar; 15 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/components/TableHeader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TableHeader = ({ onColumnClick }) => { 4 | return ( 5 | 6 | 7 | onColumnClick("nome")}>Nome 8 | onColumnClick("idade")}>Idade 9 | onColumnClick("cargo")}>Cargo 10 | 11 | 12 | ); 13 | }; 14 | 15 | export default TableHeader; 16 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/components/TableRow.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TableRow = ({ row }) => { 4 | return ( 5 | 6 | {row.nome} 7 | {row.idade} 8 | {row.cargo} 9 | 10 | ); 11 | }; 12 | 13 | export default TableRow; 14 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Verdana; 3 | } 4 | 5 | .container { 6 | max-width: 800px; 7 | margin: 20px auto; 8 | } 9 | 10 | table { 11 | width: 100%; 12 | border-collapse: collapse; 13 | margin-top: 20px; 14 | } 15 | 16 | th, 17 | td { 18 | border: 1px solid #ddd; 19 | text-align: left; 20 | padding: 8px; 21 | } 22 | 23 | th { 24 | background-color: #f2f2f2; 25 | cursor: pointer; 26 | } 27 | 28 | tr:nth-child(even) { 29 | background-color: #f2f2f2; 30 | } 31 | 32 | /* Estilo para a barra de pesquisa */ 33 | .search-bar { 34 | margin: 20px 0; 35 | padding: 10px; 36 | width: 100%; 37 | border: 1px solid #ccc; 38 | border-radius: 4px; 39 | box-sizing: border-box; 40 | } 41 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /15_tabela_ordenavel/tabelaordenavel/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 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/.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 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infinitescroll", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.5.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@vitejs/plugin-react": "^4.0.3", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import InfiniteScroll from "./components/InfiniteScroll"; 3 | 4 | function App() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/src/index.css: -------------------------------------------------------------------------------- 1 | /* Estilo global */ 2 | body { 3 | font-family: Arial, sans-serif; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | /* Estilo para o componente InfiniteScroll */ 9 | h1 { 10 | text-align: center; 11 | margin-top: 20px; 12 | } 13 | 14 | ul { 15 | list-style: none; 16 | padding: 0; 17 | margin: 20px auto; 18 | max-width: 800px; 19 | } 20 | 21 | li { 22 | border: 1px solid #ccc; 23 | margin: 8px 0; 24 | padding: 16px; 25 | } 26 | 27 | p { 28 | margin: 0; 29 | } 30 | 31 | /* Estilo para a mensagem de carregamento */ 32 | p { 33 | text-align: center; 34 | font-size: 18px; 35 | color: #333; 36 | } 37 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /16_infinite_scroll/infinitescroll/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 | -------------------------------------------------------------------------------- /18_blog_com_axios/.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 | -------------------------------------------------------------------------------- /18_blog_com_axios/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /18_blog_com_axios/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-axios", 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 | "axios": "^1.1.3", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.4.3" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.24", 19 | "@types/react-dom": "^18.0.8", 20 | "@vitejs/plugin-react": "^2.2.0", 21 | "vite": "^3.2.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /18_blog_com_axios/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/App.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 1000px; 3 | margin: 2rem auto; 4 | } 5 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | 3 | import Navbar from "./components/Navbar"; 4 | 5 | import "./App.css"; 6 | 7 | function App() { 8 | return ( 9 |
10 | 11 |
12 | 13 |
14 |
15 | ); 16 | } 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/axios/config.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const blogFetch = axios.create({ 4 | baseURL: "https://jsonplaceholder.typicode.com", 5 | headers: { 6 | "Content-Type": "application/json", 7 | }, 8 | }); 9 | 10 | export default blogFetch; 11 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/components/Navbar.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | padding: 1rem 2rem; 6 | background-color: #17191f; 7 | } 8 | 9 | .navbar h2 a { 10 | opacity: 1; 11 | } 12 | 13 | .navbar ul { 14 | display: flex; 15 | gap: 1rem; 16 | } 17 | 18 | .new-btn { 19 | font-weight: bold; 20 | border: 1px solid #fff; 21 | padding: 0.5rem 1rem; 22 | border-radius: 1rem; 23 | opacity: 1; 24 | } 25 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | import "./Navbar.css"; 4 | 5 | const Navbar = () => { 6 | return ( 7 | 25 | ); 26 | }; 27 | 28 | export default Navbar; 29 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-family: "Blinker", sans-serif; 5 | } 6 | 7 | body { 8 | background-color: #0e1217; 9 | color: #fff; 10 | } 11 | 12 | ul { 13 | list-style: none; 14 | } 15 | 16 | a { 17 | text-decoration: none; 18 | color: #fff; 19 | opacity: 0.7; 20 | transition: 0.3s; 21 | } 22 | 23 | a:hover { 24 | opacity: 1; 25 | } 26 | 27 | p { 28 | color: #a9abb3; 29 | } 30 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | 4 | import { createBrowserRouter, RouterProvider, Route } from "react-router-dom"; 5 | 6 | import App from "./App"; 7 | 8 | import Home from "./routes/Home"; 9 | import Post from "./routes/Post"; 10 | import NewPost from "./routes/NewPost"; 11 | import Admin from "./routes/Admin"; 12 | import EditPost from "./routes/EditPost"; 13 | 14 | import "./index.css"; 15 | 16 | const router = createBrowserRouter([ 17 | { 18 | element: , 19 | children: [ 20 | { 21 | path: "/", 22 | element: , 23 | }, 24 | { 25 | path: "/posts/:id", 26 | element: , 27 | }, 28 | { 29 | path: "/new", 30 | element: , 31 | }, 32 | { 33 | path: "/admin", 34 | element: , 35 | }, 36 | , 37 | { 38 | path: "/posts/edit/:id", 39 | element: , 40 | }, 41 | ], 42 | }, 43 | ]); 44 | 45 | ReactDOM.createRoot(document.getElementById("root")).render( 46 | 47 | 48 | 49 | ); 50 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/Admin.css: -------------------------------------------------------------------------------- 1 | .admin h1 { 2 | text-align: center; 3 | margin-bottom: 2rem; 4 | } 5 | 6 | .actions { 7 | display: flex; 8 | gap: 1rem; 9 | } 10 | 11 | .edit-btn { 12 | background-color: #13c9e9; 13 | color: #fff; 14 | } 15 | 16 | .delete-btn { 17 | background-color: #eb1717; 18 | color: #fff; 19 | } 20 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/Home.css: -------------------------------------------------------------------------------- 1 | .home h1 { 2 | text-transform: uppercase; 3 | text-align: center; 4 | margin-bottom: 1.5rem; 5 | } 6 | 7 | .post { 8 | display: flex; 9 | flex-direction: column; 10 | margin-bottom: 1.5rem; 11 | padding-bottom: 1.5rem; 12 | border-bottom: 1px solid #777; 13 | } 14 | 15 | .post h2, 16 | .post p { 17 | margin-bottom: 1rem; 18 | } 19 | 20 | .post h2::first-letter { 21 | text-transform: uppercase; 22 | } 23 | 24 | .btn { 25 | background-color: #fff; 26 | color: #0e1217; 27 | border: 1px solid #fff; 28 | opacity: 1; 29 | padding: 0.5rem 1rem; 30 | border-radius: 1rem; 31 | max-width: 100px; 32 | text-align: center; 33 | cursor: pointer; 34 | transition: 0.3s; 35 | } 36 | 37 | .btn:hover { 38 | color: #fff; 39 | background-color: #0e1217; 40 | } 41 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/Home.jsx: -------------------------------------------------------------------------------- 1 | import blogFetch from "../axios/config"; 2 | 3 | import { useState, useEffect } from "react"; 4 | 5 | import { Link } from "react-router-dom"; 6 | 7 | import "./Home.css"; 8 | 9 | const Home = () => { 10 | const [posts, setPosts] = useState([]); 11 | 12 | const getPosts = async () => { 13 | try { 14 | const response = await blogFetch.get("/posts"); 15 | 16 | const data = response.data; 17 | 18 | setPosts(data); 19 | } catch (error) { 20 | console.log(error); 21 | } 22 | }; 23 | 24 | useEffect(() => { 25 | getPosts(); 26 | }, []); 27 | 28 | return ( 29 |
30 |

Últimos posts

31 | {posts.length === 0 ? ( 32 |

Carregando...

33 | ) : ( 34 | posts.map((post) => ( 35 |
36 |

{post.title}

37 |

{post.body}

38 | 39 | Ler mais 40 | 41 |
42 | )) 43 | )} 44 |
45 | ); 46 | }; 47 | 48 | export default Home; 49 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/NewPost.css: -------------------------------------------------------------------------------- 1 | .new-post h2 { 2 | text-align: center; 3 | margin-bottom: 1rem; 4 | } 5 | 6 | form { 7 | max-width: 500px; 8 | width: 100%; 9 | margin: 0 auto; 10 | } 11 | 12 | .form-control { 13 | display: flex; 14 | flex-direction: column; 15 | margin-bottom: 1rem; 16 | } 17 | 18 | .form-control label { 19 | margin-bottom: 0.5rem; 20 | } 21 | 22 | .form-control input, 23 | .form-control textarea { 24 | padding: 0.5rem; 25 | border-radius: 5px; 26 | border: none; 27 | } 28 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/NewPost.jsx: -------------------------------------------------------------------------------- 1 | import blogFetch from "../axios/config"; 2 | 3 | import { useState } from "react"; 4 | 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | import "./NewPost.css"; 8 | 9 | const NewPost = () => { 10 | const navigate = useNavigate(); 11 | 12 | const [title, setTitle] = useState(); 13 | const [body, setBody] = useState(); 14 | 15 | const createPost = async (e) => { 16 | e.preventDefault(); 17 | 18 | const post = { title, body, userId: 1 }; 19 | 20 | await blogFetch.post("/posts", { 21 | body: post, 22 | }); 23 | 24 | navigate("/"); 25 | }; 26 | 27 | return ( 28 |
29 |

Inserir novo Post:

30 |
createPost(e)}> 31 |
32 | 33 | setTitle(e.target.value)} 39 | /> 40 |
41 |
42 | 43 | 49 |
50 | 51 |
52 |
53 | ); 54 | }; 55 | 56 | export default NewPost; 57 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/Post.css: -------------------------------------------------------------------------------- 1 | .post-container h2 { 2 | text-align: center; 3 | } 4 | 5 | .post-container .post { 6 | border: none; 7 | } 8 | -------------------------------------------------------------------------------- /18_blog_com_axios/src/routes/Post.jsx: -------------------------------------------------------------------------------- 1 | import blogFetch from "../axios/config"; 2 | 3 | import { useState, useEffect } from "react"; 4 | 5 | import { useParams } from "react-router-dom"; 6 | 7 | import './Post.css' 8 | 9 | const Post = () => { 10 | const { id } = useParams(); 11 | const [post, setPost] = useState([]); 12 | 13 | const getPost = async () => { 14 | try { 15 | const response = await blogFetch.get(`/posts/${id}`); 16 | 17 | const data = response.data; 18 | 19 | setPost(data); 20 | } catch (error) { 21 | console.log(error); 22 | } 23 | }; 24 | 25 | useEffect(() => { 26 | getPost(); 27 | }, []); 28 | 29 | return ( 30 |
31 | {!post.title ? ( 32 |

Carregando...

33 | ) : ( 34 |
35 |

{post.title}

36 |

{post.body}

37 |
38 | )} 39 |
40 | ); 41 | }; 42 | 43 | export default Post; 44 | -------------------------------------------------------------------------------- /18_blog_com_axios/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 | -------------------------------------------------------------------------------- /19_multistep_form/.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 | -------------------------------------------------------------------------------- /19_multistep_form/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Formulário Multistep com React 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /19_multistep_form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-multistep-form", 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-icons": "^4.6.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.0.24", 18 | "@types/react-dom": "^18.0.8", 19 | "@vitejs/plugin-react": "^2.2.0", 20 | "vite": "^3.2.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /19_multistep_form/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /19_multistep_form/src/App.css: -------------------------------------------------------------------------------- 1 | .app { 2 | padding: 2rem; 3 | } 4 | 5 | .header { 6 | text-align: center; 7 | margin-bottom: 2rem; 8 | } 9 | 10 | .header h2 { 11 | font-size: 2rem; 12 | margin-bottom: 1rem; 13 | } 14 | 15 | /* Forms */ 16 | .form-container { 17 | max-width: 600px; 18 | margin: 0 auto; 19 | background-color: #fff; 20 | padding: 1.5rem; 21 | box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; 22 | } 23 | 24 | .inputs-container { 25 | min-height: 280px; 26 | } 27 | 28 | .form-container form { 29 | max-width: 400px; 30 | margin: 0 auto; 31 | } 32 | 33 | .form-control { 34 | display: flex; 35 | flex-direction: column; 36 | gap: 0.6rem; 37 | margin-bottom: 2rem; 38 | } 39 | 40 | label { 41 | font-weight: bold; 42 | color: #777; 43 | font-size: 0.9rem; 44 | } 45 | 46 | input, 47 | textarea { 48 | padding: 0.6rem; 49 | border: none; 50 | border-radius: 3px; 51 | box-shadow: rgba(99, 99, 99, 0.2) 0px 2px 8px 0px; 52 | } 53 | 54 | textarea { 55 | height: 120px; 56 | } 57 | 58 | /* Actions */ 59 | .actions { 60 | display: flex; 61 | justify-content: flex-end; 62 | gap: 1rem; 63 | } 64 | 65 | .actions button { 66 | font-size: 0.9rem; 67 | padding: 0.5rem 1rem; 68 | gap: 0.5rem; 69 | display: flex; 70 | align-items: center; 71 | border: 2px solid transparent; 72 | border-radius: 3px; 73 | background-color: #dfdfdf; 74 | cursor: pointer; 75 | transition: 0.3s; 76 | } 77 | 78 | .actions button:hover { 79 | background-color: #cfcfcf; 80 | } 81 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/ReviewForm.css: -------------------------------------------------------------------------------- 1 | .form-control.score-container { 2 | flex-direction: row; 3 | justify-content: center; 4 | gap: 2rem; 5 | } 6 | 7 | .score-container svg { 8 | font-size: 1.6rem; 9 | cursor: pointer; 10 | transition: 0.3s; 11 | } 12 | 13 | .score-container svg:hover { 14 | fill: #7076f4; 15 | } 16 | 17 | .score-container input:checked ~ svg { 18 | fill: #7076f4; 19 | } 20 | 21 | .radio-container { 22 | text-align: center; 23 | position: relative; 24 | } 25 | 26 | .radio-container input { 27 | opacity: 0; 28 | top: 15px; 29 | left: 20px; 30 | position: absolute; 31 | } 32 | 33 | .radio-container p { 34 | font-style: italic; 35 | font-weight: 300; 36 | font-size: 0.7rem; 37 | margin-top: 0.4rem; 38 | } 39 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/Steps.css: -------------------------------------------------------------------------------- 1 | .steps { 2 | display: flex; 3 | gap: 4rem; 4 | justify-content: center; 5 | align-items: center; 6 | max-width: 400px; 7 | margin: 0 auto; 8 | position: relative; 9 | margin-bottom: 2rem; 10 | } 11 | 12 | .steps::after { 13 | content: ""; 14 | width: 380px; 15 | border-bottom: 1px solid #ccc; 16 | position: absolute; 17 | top: 20px; 18 | } 19 | 20 | .step { 21 | text-align: center; 22 | background-color: #fff; 23 | z-index: 1; 24 | padding: 0.5rem; 25 | width: 120px; 26 | } 27 | 28 | .step svg { 29 | font-size: 1.6rem; 30 | margin-bottom: 0.2rem; 31 | } 32 | 33 | .active { 34 | font-weight: bold; 35 | } 36 | 37 | .active > svg { 38 | fill: #7076f4; 39 | } 40 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/Steps.jsx: -------------------------------------------------------------------------------- 1 | import { AiOutlineUser, AiOutlineStar } from "react-icons/ai"; 2 | import { FiSend } from "react-icons/fi"; 3 | 4 | import "./Steps.css"; 5 | 6 | const Steps = ({ currentStep }) => { 7 | return ( 8 |
9 |
10 | 11 |

Identificação

12 |
13 |
= 1 ? "active" : ""}`}> 14 | 15 |

Avaliação

16 |
17 |
= 2 ? "active" : ""}`}> 18 | 19 |

Envio

20 |
21 |
22 | ); 23 | }; 24 | 25 | export default Steps; 26 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/Thanks.css: -------------------------------------------------------------------------------- 1 | .thanks-container { 2 | text-align: center; 3 | } 4 | 5 | .thanks-container h2 { 6 | margin-bottom: 1rem; 7 | } 8 | 9 | .thanks-container p { 10 | font-size: 0.9rem; 11 | margin-bottom: 0.5rem; 12 | } 13 | 14 | .thanks-container h3 { 15 | margin: 1rem 0; 16 | } 17 | 18 | .review-data { 19 | text-align: left; 20 | } 21 | 22 | .review-data svg { 23 | margin-left: 5px; 24 | } 25 | 26 | .review-data span { 27 | font-weight: bold; 28 | } 29 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/Thanks.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | BsFillEmojiHeartEyesFill, 3 | BsFillEmojiSmileFill, 4 | BsFillEmojiNeutralFill, 5 | BsFillEmojiFrownFill, 6 | } from "react-icons/bs"; 7 | 8 | import "./Thanks.css"; 9 | 10 | const emojiData = { 11 | unsatisfied: , 12 | neutral: , 13 | satisfied: , 14 | very_satisfied: , 15 | }; 16 | 17 | const Thanks = ({ data }) => { 18 | return ( 19 |
20 |

Falta pouco...

21 |

22 | A sua opinião é muito importante, em breve você receberá um cupom de 10% 23 | de desconto para a sua próxima compra. 24 |

25 |

Para concluir sua avaliação clique no botão de Enviar abaixo.

26 |

Aqui está o resumo da sua avaliação {data.name}:

27 |

28 | Satisfação com o produto: 29 | {emojiData[data.review]} 30 |

31 |

32 | Comentário: {data.comment} 33 |

34 |
35 | ); 36 | }; 37 | 38 | export default Thanks; 39 | -------------------------------------------------------------------------------- /19_multistep_form/src/components/UserForm.jsx: -------------------------------------------------------------------------------- 1 | const UserForm = ({ data, updateFieldHandler }) => { 2 | return ( 3 |
4 |
5 | 6 | updateFieldHandler("name", e.target.value)} 14 | /> 15 |
16 |
17 | 18 | updateFieldHandler("email", e.target.value)} 26 | /> 27 |
28 |
29 | ); 30 | }; 31 | 32 | export default UserForm; 33 | -------------------------------------------------------------------------------- /19_multistep_form/src/hooks/useForm.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export function useForm(steps) { 4 | const [currentStep, setCurrentStep] = useState(0); 5 | 6 | function changeStep(i, e) { 7 | if (e) e.preventDefault(); 8 | 9 | if (i < 0 || i >= steps.length) return; 10 | 11 | setCurrentStep(i); 12 | } 13 | 14 | return { 15 | currentStep, 16 | isLastStep: currentStep + 1 === steps.length ? true : false, 17 | currentComponent: steps[currentStep], 18 | changeStep, 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /19_multistep_form/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | font-family: "Barlow", sans-serif; 6 | color: #333; 7 | } 8 | 9 | p { 10 | color: #777; 11 | } 12 | 13 | body { 14 | background-color: #f9fafc; 15 | } 16 | -------------------------------------------------------------------------------- /19_multistep_form/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 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /19_multistep_form/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 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { browser: true, es2020: true }, 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:react/recommended', 8 | 'plugin:react/jsx-runtime', 9 | 'plugin:react-hooks/recommended', 10 | ], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/.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 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calculadora", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.2.14", 18 | "@types/react-dom": "^18.2.6", 19 | "@vitejs/plugin-react": "^4.0.1", 20 | "eslint": "^8.44.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.1", 24 | "vite": "^4.4.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/src/App.jsx: -------------------------------------------------------------------------------- 1 | 2 | import Calculator from './components/Calculator' 3 | import './App.css' 4 | 5 | function App() { 6 | 7 | return ( 8 | <> 9 | 10 | 11 | ) 12 | } 13 | 14 | export default App 15 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/src/components/Calculator.css: -------------------------------------------------------------------------------- 1 | .calculator { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | width: 200px; 7 | margin: auto; 8 | } 9 | 10 | .display { 11 | width: 100%; 12 | height: 50px; 13 | border: 1px solid #000; 14 | margin-bottom: 10px; 15 | text-align: right; 16 | padding: 5px; 17 | } 18 | 19 | .complete-operation { 20 | width: 100%; 21 | min-height: 30px; 22 | border: 1px solid #000; 23 | margin-bottom: 10px; 24 | text-align: right; 25 | padding: 5px; 26 | font-size: 0.8rem; 27 | overflow: auto; 28 | } 29 | 30 | .buttons { 31 | display: grid; 32 | grid-template-columns: repeat(4, 1fr); 33 | gap: 10px; 34 | } 35 | 36 | button { 37 | padding: 10px; 38 | font-size: 16px; 39 | } 40 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /1_calculadora/calculadora/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 | -------------------------------------------------------------------------------- /20_github_api/.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 | -------------------------------------------------------------------------------- /20_github_api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | GitHub Finder 8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /20_github_api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github_react", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "react": "^18.2.0", 13 | "react-dom": "^18.2.0", 14 | "react-icons": "^4.7.1", 15 | "react-router-dom": "^6.5.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.0.26", 19 | "@types/react-dom": "^18.0.9", 20 | "@vitejs/plugin-react": "^3.0.0", 21 | "typescript": "^4.9.3", 22 | "vite": "^4.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /20_github_api/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /20_github_api/src/App.module.css: -------------------------------------------------------------------------------- 1 | .app { 2 | max-width: 500px; 3 | margin: 0 auto; 4 | } 5 | 6 | .app h1 { 7 | text-align: center; 8 | margin-bottom: 1rem; 9 | } 10 | -------------------------------------------------------------------------------- /20_github_api/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from "react-router-dom"; 2 | 3 | import classes from "./App.module.css"; 4 | 5 | function App() { 6 | return ( 7 |
8 |

GitHub Finder

9 | 10 |
11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /20_github_api/src/components/BackBtn.module.css: -------------------------------------------------------------------------------- 1 | .back_btn { 2 | position: absolute; 3 | top: 1rem; 4 | left: 1rem; 5 | padding: 0.5rem 1rem; 6 | color: #9da5d1; 7 | background-color: #2b3566; 8 | cursor: pointer; 9 | border: none; 10 | border-radius: 5px; 11 | opacity: 0.9; 12 | } 13 | 14 | .back_btn:hover { 15 | opacity: 1; 16 | } 17 | -------------------------------------------------------------------------------- /20_github_api/src/components/BackBtn.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom"; 2 | 3 | import classes from "./BackBtn.module.css"; 4 | 5 | const BackBtn = () => { 6 | const navigate = useNavigate(); 7 | 8 | return ( 9 | <> 10 | 13 | 14 | ); 15 | }; 16 | 17 | export default BackBtn; 18 | -------------------------------------------------------------------------------- /20_github_api/src/components/Error.tsx: -------------------------------------------------------------------------------- 1 | const Error = () => { 2 | return ( 3 |
4 |

Usuário não encontrado!

5 |
6 | ); 7 | }; 8 | 9 | export default Error; 10 | -------------------------------------------------------------------------------- /20_github_api/src/components/Loader.module.css: -------------------------------------------------------------------------------- 1 | @keyframes rotate { 2 | from { 3 | -webkit-transform: rotate(0deg); 4 | } 5 | to { 6 | -webkit-transform: rotate(360deg); 7 | } 8 | } 9 | 10 | .loader { 11 | animation: rotate 2s linear infinite; 12 | position: absolute; 13 | left: 50%; 14 | margin-top: 40px; 15 | font-size: 50px; 16 | margin-left: -25px; 17 | } 18 | -------------------------------------------------------------------------------- /20_github_api/src/components/Loader.tsx: -------------------------------------------------------------------------------- 1 | import { FaSpinner } from "react-icons/fa"; 2 | 3 | import classes from "./Loader.module.css"; 4 | 5 | const Loader = () => { 6 | return ( 7 | <> 8 | 9 | 10 | ); 11 | }; 12 | 13 | export default Loader; 14 | -------------------------------------------------------------------------------- /20_github_api/src/components/Repo.module.css: -------------------------------------------------------------------------------- 1 | .repo { 2 | background-color: #0e1129; 3 | padding: 2rem; 4 | border-radius: 10px; 5 | border: 2px solid #9da5d1; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: start; 9 | gap: 1rem; 10 | flex: 1 0 50%; 11 | } 12 | 13 | .stats { 14 | display: flex; 15 | gap: 1rem; 16 | } 17 | 18 | .stats div { 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | gap: 0.4rem; 23 | border-radius: 3px; 24 | border: 1px solid #9da5d1; 25 | padding-right: 0.4rem; 26 | } 27 | 28 | .stats svg { 29 | background-color: #4ed8c7; 30 | padding: 0.2rem 0.4rem; 31 | border-radius: 3px; 32 | font-size: 2rem; 33 | } 34 | 35 | .repo_btn { 36 | display: flex; 37 | align-items: center; 38 | padding: 0.5rem 1rem; 39 | color: #9da5d1; 40 | background-color: #2b3566; 41 | border-radius: 5px; 42 | gap: 0.5rem; 43 | } 44 | -------------------------------------------------------------------------------- /20_github_api/src/components/Repo.tsx: -------------------------------------------------------------------------------- 1 | import { RepoProps } from "../types/repo"; 2 | 3 | import { AiOutlineStar, AiOutlineFork } from "react-icons/ai"; 4 | import { BsCodeSlash } from "react-icons/bs"; 5 | import { RiGitRepositoryLine } from "react-icons/ri"; 6 | 7 | import classes from "./Repo.module.css"; 8 | 9 | const Repo = ({ 10 | name, 11 | language, 12 | html_url, 13 | forks_count, 14 | stargazers_count, 15 | }: RepoProps) => { 16 | return ( 17 |
18 |

{name}

19 |

20 | {language} 21 |

22 |
23 |
24 | 25 | {stargazers_count} 26 |
27 |
28 | 29 | {forks_count} 30 |
31 |
32 | 33 | Ver código 34 | 35 | 36 |
37 | ); 38 | }; 39 | 40 | export default Repo; 41 | -------------------------------------------------------------------------------- /20_github_api/src/components/Search.module.css: -------------------------------------------------------------------------------- 1 | .search { 2 | background-color: #2b3566; 3 | padding: 2rem; 4 | border-radius: 1rem; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | flex-direction: column; 9 | gap: 1rem; 10 | margin-bottom: 1.5rem; 11 | } 12 | 13 | .search p { 14 | color: #9da5d1; 15 | } 16 | 17 | .search_container { 18 | display: flex; 19 | gap: 0.5rem; 20 | } 21 | 22 | .search_container input, 23 | .search_container button { 24 | padding: 0.6rem; 25 | border-radius: 3px; 26 | border: none; 27 | color: #2b3566; 28 | } 29 | 30 | .search_container button { 31 | background-color: #0e1129; 32 | cursor: pointer; 33 | } 34 | -------------------------------------------------------------------------------- /20_github_api/src/components/Search.tsx: -------------------------------------------------------------------------------- 1 | type SearchProps = { 2 | loadUser: (userName: string) => Promise; 3 | }; 4 | 5 | import { useState, KeyboardEvent } from "react"; 6 | 7 | import { BsSearch } from "react-icons/bs"; 8 | 9 | import classes from "./Search.module.css"; 10 | 11 | const Search = ({ loadUser }: SearchProps) => { 12 | const [userName, setUserName] = useState(""); 13 | 14 | const handleKeyDown = (e: KeyboardEvent) => { 15 | if (e.key === "Enter") { 16 | loadUser(userName); 17 | } 18 | }; 19 | 20 | return ( 21 |
22 |

Busque por um usuário:

23 |

Conheça seus melhores repositórios

24 |
25 | setUserName(e.target.value)} 28 | onKeyDown={handleKeyDown} 29 | placeholder="Digite o nome do usuário" 30 | /> 31 | 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default Search; 40 | -------------------------------------------------------------------------------- /20_github_api/src/components/User.module.css: -------------------------------------------------------------------------------- 1 | .user { 2 | background-color: #2b3566; 3 | padding: 2rem; 4 | border-radius: 1rem; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | flex-direction: column; 9 | gap: 1.2rem; 10 | } 11 | 12 | .user img { 13 | width: 140px; 14 | height: 140px; 15 | border: 4px solid #644aff; 16 | border-radius: 50%; 17 | } 18 | 19 | .location { 20 | display: flex; 21 | align-items: center; 22 | gap: 0.4rem; 23 | } 24 | 25 | .location svg { 26 | fill: #4ed8c7; 27 | font-size: 1.5rem; 28 | } 29 | 30 | .location span { 31 | color: #9da5d1; 32 | font-weight: bold; 33 | } 34 | 35 | .stats { 36 | display: flex; 37 | } 38 | 39 | .stats div { 40 | padding: 0 1rem; 41 | display: flex; 42 | flex-direction: column; 43 | gap: 0.4rem; 44 | } 45 | 46 | .stats div:first-child { 47 | border-right: 1px solid #9da5d1; 48 | } 49 | 50 | .stats .number { 51 | background-color: #4ed8c7; 52 | padding: 0.2rem 0.4rem; 53 | border-radius: 3px; 54 | } 55 | 56 | .user a { 57 | text-align: center; 58 | background-color: #644aff; 59 | padding: 1rem; 60 | border-radius: 5px; 61 | opacity: 0.8; 62 | transition: 0.3s; 63 | } 64 | 65 | .user a:hover { 66 | opacity: 1; 67 | } 68 | -------------------------------------------------------------------------------- /20_github_api/src/components/User.tsx: -------------------------------------------------------------------------------- 1 | import { UserProps } from "../types/user"; 2 | 3 | import { MdLocationPin } from "react-icons/md"; 4 | 5 | import { Link } from "react-router-dom"; 6 | 7 | import classes from "./User.module.css"; 8 | 9 | const User = ({ 10 | login, 11 | avatar_url, 12 | followers, 13 | following, 14 | location, 15 | }: UserProps) => { 16 | return ( 17 |
18 | {login} 19 |

{login}

20 |

21 | {location} 22 |

23 |
24 |
25 |

Seguidores:

26 |

{followers}

27 |
28 |
29 |

Seguindo:

30 |

{following}

31 |
32 |
33 | Ver melhores projetos 34 |
35 | ); 36 | }; 37 | 38 | export default User; 39 | -------------------------------------------------------------------------------- /20_github_api/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | font-family: "Inter", sans-serif; 6 | color: #fff; 7 | } 8 | 9 | body { 10 | background-color: #0e1129; 11 | padding: 2rem; 12 | } 13 | 14 | p { 15 | text-align: center; 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | } 21 | -------------------------------------------------------------------------------- /20_github_api/src/main.tsx: -------------------------------------------------------------------------------- 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 Repos from "./routes/Repos"; 11 | 12 | const router = createBrowserRouter([ 13 | { 14 | path: "/", 15 | element: , 16 | children: [ 17 | { 18 | path: "/", 19 | element: , 20 | }, 21 | { 22 | path: "/repos/:username", 23 | element: , 24 | }, 25 | ], 26 | }, 27 | ]); 28 | 29 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 30 | 31 | 32 | 33 | ); 34 | -------------------------------------------------------------------------------- /20_github_api/src/routes/Home.tsx: -------------------------------------------------------------------------------- 1 | import { UserProps } from "../types/user"; 2 | 3 | import { useState } from "react"; 4 | 5 | import Search from "../components/Search"; 6 | import User from "../components/User"; 7 | import Error from "../components/Error"; 8 | import Loader from "../components/Loader"; 9 | 10 | const Home = () => { 11 | const [user, setUser] = useState(null); 12 | const [error, setError] = useState(false); 13 | const [isLoading, setIsLoading] = useState(false); 14 | 15 | const loadUser = async function (userName: string) { 16 | setUser(null); 17 | setIsLoading(true); 18 | 19 | const res = await fetch(`https://api.github.com/users/${userName}`); 20 | 21 | const data = await res.json(); 22 | 23 | setIsLoading(false); 24 | 25 | if (res.status === 404) { 26 | setError(true); 27 | return; 28 | } 29 | 30 | setError(false); 31 | 32 | const { avatar_url, login, location, followers, following } = data; 33 | 34 | const userData: UserProps = { 35 | avatar_url, 36 | login, 37 | location, 38 | followers, 39 | following, 40 | }; 41 | 42 | setUser(userData); 43 | }; 44 | 45 | return ( 46 |
47 | 48 | {isLoading && } 49 | {user && } 50 | {error && } 51 |
52 | ); 53 | }; 54 | 55 | export default Home; 56 | -------------------------------------------------------------------------------- /20_github_api/src/routes/Repos.module.css: -------------------------------------------------------------------------------- 1 | .repos h2 { 2 | font-size: 1.2rem; 3 | text-align: center; 4 | margin-bottom: 2rem; 5 | } 6 | 7 | .repos_container { 8 | background-color: #2b3566; 9 | padding: 2rem; 10 | border-radius: 1rem; 11 | display: flex; 12 | align-items: center; 13 | flex-wrap: wrap; 14 | gap: 1.2rem; 15 | } 16 | -------------------------------------------------------------------------------- /20_github_api/src/types/repo.ts: -------------------------------------------------------------------------------- 1 | export type RepoProps = { 2 | name: string; 3 | html_url: string; 4 | language: string; 5 | stargazers_count: number; 6 | forks_count: number; 7 | }; 8 | -------------------------------------------------------------------------------- /20_github_api/src/types/user.ts: -------------------------------------------------------------------------------- 1 | export type UserProps = { 2 | avatar_url: string; 3 | login: string; 4 | location: string; 5 | followers: number; 6 | following: number; 7 | }; 8 | -------------------------------------------------------------------------------- /20_github_api/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /20_github_api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /20_github_api/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /20_github_api/vite.config.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/.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 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Hook Form com TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hook-form-ts", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@hookform/resolvers": "^3.2.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-hook-form": "^7.45.4", 17 | "zod": "^3.21.4" 18 | }, 19 | "devDependencies": { 20 | "@types/react": "^18.2.15", 21 | "@types/react-dom": "^18.2.7", 22 | "@typescript-eslint/eslint-plugin": "^6.0.0", 23 | "@typescript-eslint/parser": "^6.0.0", 24 | "@vitejs/plugin-react": "^4.0.3", 25 | "autoprefixer": "^10.4.14", 26 | "eslint": "^8.45.0", 27 | "eslint-plugin-react-hooks": "^4.6.0", 28 | "eslint-plugin-react-refresh": "^0.4.3", 29 | "postcss": "^8.4.27", 30 | "tailwindcss": "^3.3.3", 31 | "typescript": "^5.0.2", 32 | "vite": "^4.4.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Form } from "./components/Form/Form"; 2 | 3 | export const App = () => { 4 | return ( 5 |
6 |

React Hook Form Com TypeScript

7 |
8 |
9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/hooks/useForms.tsx: -------------------------------------------------------------------------------- 1 | import { zodResolver } from "@hookform/resolvers/zod"; 2 | import { useForm } from "react-hook-form"; 3 | import { FormSchema, formSchema } from "../schemas/formSchema"; 4 | 5 | export const useForms = () => { 6 | const { 7 | register, 8 | handleSubmit, 9 | formState: { errors }, 10 | } = useForm({ 11 | resolver: zodResolver(formSchema), 12 | }); 13 | 14 | return { register, handleSubmit, errors }; 15 | }; 16 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { App } from "./App.tsx"; 4 | import "./index.css"; 5 | 6 | ReactDOM.createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/schemas/formSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const formSchema = z 4 | .object({ 5 | name: z 6 | .string() 7 | .nonempty("Campo obrigatório.") 8 | .min(3, "O número de mínimo de caracteres é 3."), 9 | lastname: z 10 | .string() 11 | .nonempty("Campo obrigatório.") 12 | .min(3, "O número de mínimo de caracteres é 3."), 13 | gender: z.string().refine((field) => field !== "select", { 14 | message: "Você precisa escolher um gênero.", 15 | }), 16 | email: z 17 | .string() 18 | .nonempty("Campo obrigatório.") 19 | .email("Utilize um e-mail válido."), 20 | password: z.string().min(6, "A senha precisa ter no mínimo 6 caracteres."), 21 | confirmpassword: z 22 | .string() 23 | .min(6, "A senha precisa ter no mínimo 6 caracteres."), 24 | agree: z.boolean().refine((field) => field === true, { 25 | message: "Você precisa concordar com os termos.", 26 | }), 27 | }) 28 | .refine((field) => field.password === field.confirmpassword, { 29 | message: "As senhas precisam ser iguais", 30 | path: ["confirmpassword"], 31 | }); 32 | 33 | export type FormSchema = z.infer; 34 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /22_form_com_react_hook_e_ts/vite.config.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /25_newsletter_form/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /25_newsletter_form/.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 | -------------------------------------------------------------------------------- /25_newsletter_form/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | parserOptions: { 18 | ecmaVersion: 'latest', 19 | sourceType: 'module', 20 | project: ['./tsconfig.json', './tsconfig.node.json'], 21 | tsconfigRootDir: __dirname, 22 | }, 23 | ``` 24 | 25 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 26 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 27 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 28 | -------------------------------------------------------------------------------- /25_newsletter_form/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Formulário com TypeScript 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /25_newsletter_form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newsletter-form", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.2.15", 18 | "@types/react-dom": "^18.2.7", 19 | "@typescript-eslint/eslint-plugin": "^6.0.0", 20 | "@typescript-eslint/parser": "^6.0.0", 21 | "@vitejs/plugin-react": "^4.0.3", 22 | "autoprefixer": "^10.4.16", 23 | "eslint": "^8.45.0", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.3", 26 | "postcss": "^8.4.31", 27 | "tailwindcss": "^3.3.5", 28 | "typescript": "^5.0.2", 29 | "vite": "^4.4.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /25_newsletter_form/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /25_newsletter_form/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /25_newsletter_form/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Form } from "./components/Form"; 2 | 3 | function App() { 4 | return ( 5 |
6 |

Inscreva-se

7 |

8 | Assine nossa Newsletter e mantenha-se informado 9 |

10 |
11 | 12 |
13 |

14 | Ao se inscrever, você passará a receber os nossos e-mails com as 15 | melhores dicas, novidades e ofertas. 16 |

17 |
18 | ); 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /25_newsletter_form/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /25_newsletter_form/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /25_newsletter_form/src/types/User.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | name?: string; 3 | email?: string; 4 | agree?: boolean | string; 5 | }; 6 | -------------------------------------------------------------------------------- /25_newsletter_form/src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | import { User } from "../types/User"; 2 | 3 | type Error = { 4 | [key: string]: string; 5 | }; 6 | 7 | export const validate = (data: User) => { 8 | const errors: Error = {}; 9 | 10 | if (!data.name) { 11 | errors["name"] = "O nome é obrigatório"; 12 | } 13 | 14 | if (!data.email) { 15 | errors["email"] = "O e-mail é obrigatório"; 16 | } 17 | 18 | if (!data.agree) { 19 | errors["agree"] = "Você precisa concordar com os termos"; 20 | } 21 | 22 | return errors; 23 | }; 24 | -------------------------------------------------------------------------------- /25_newsletter_form/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /25_newsletter_form/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /25_newsletter_form/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /25_newsletter_form/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /25_newsletter_form/vite.config.ts: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { browser: true, es2020: true }, 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:react/recommended', 8 | 'plugin:react/jsx-runtime', 9 | 'plugin:react-hooks/recommended', 10 | ], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/.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 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cronometro", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.2.14", 18 | "@types/react-dom": "^18.2.6", 19 | "@vitejs/plugin-react": "^4.0.1", 20 | "eslint": "^8.44.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.1", 24 | "vite": "^4.4.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/App.jsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import Timer from "./components/Timer"; 3 | 4 | function App() { 5 | return ( 6 | <> 7 | 8 | 9 | ); 10 | } 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/components/LapList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const LapList = ({ laps }) => { 4 | return ( 5 |
6 |

Voltas:

7 |
    8 | {laps.map((lap, index) => ( 9 |
  • 10 | Volta {index + 1}: {lap} 11 |
  • 12 | ))} 13 |
14 |
15 | ) 16 | }; 17 | 18 | export default LapList; 19 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/components/Timer.css: -------------------------------------------------------------------------------- 1 | .timer-container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | min-height: 50vh; 7 | width: 400px; 8 | background-color: #f5f5f5; 9 | font-family: Arial, sans-serif; 10 | padding: 20px; 11 | } 12 | 13 | .timer-display { 14 | border: 2px solid #333; 15 | padding: 20px; 16 | margin: 20px; 17 | font-size: 48px; 18 | background-color: #fff; 19 | } 20 | 21 | .timer-controls { 22 | display: flex; 23 | justify-content: center; 24 | } 25 | 26 | .timer-controls button { 27 | background-color: #007bff; 28 | border: none; 29 | color: white; 30 | padding: 15px 32px; 31 | text-align: center; 32 | text-decoration: none; 33 | display: inline-block; 34 | font-size: 16px; 35 | margin: 4px 2px; 36 | cursor: pointer; 37 | border-radius: 4px; 38 | } 39 | 40 | .timer-controls button:hover { 41 | background-color: #0046d5; 42 | } 43 | 44 | .timer-laps { 45 | width: 100%; 46 | margin: auto; 47 | } 48 | 49 | .timer-laps ul { 50 | list-style-type: none; 51 | display: flex; 52 | flex-wrap: wrap; 53 | } 54 | 55 | .timer-laps li { 56 | border-bottom: 1px solid #ddd; 57 | padding: 8px 16px; 58 | } 59 | 60 | .timer-laps li:nth-child(odd) { 61 | background-color: #ddd; 62 | } 63 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/components/TimerControls.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const TimerControls = ({ timerOn, onStart, onStop, onReset, onLap }) => { 4 | return ( 5 |
6 | {!timerOn && } 7 | {timerOn && } 8 | {timerOn && } 9 | 10 |
11 | ) 12 | }; 13 | 14 | export default TimerControls; 15 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/components/TimerDisplay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const TimerDisplay = ({ time }) => { 4 | return ( 5 |
6 | {time} 7 |
8 | ) 9 | }; 10 | 11 | export default TimerDisplay; 12 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | display: flex; 10 | place-items: center; 11 | } 12 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /2_cronometro/cronometro/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 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | env: { browser: true, es2020: true }, 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:react/recommended', 8 | 'plugin:react/jsx-runtime', 9 | 'plugin:react-hooks/recommended', 10 | ], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/.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 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "conversor_moeda", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.4.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.14", 19 | "@types/react-dom": "^18.2.6", 20 | "@vitejs/plugin-react": "^4.0.1", 21 | "eslint": "^8.44.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.1", 25 | "vite": "^4.4.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_react/992dfa03ff6f17b6d328471db595404494c26f7a/3_conversor_moeda/conversor_moeda/src/App.css -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/src/App.jsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import CurrencyConverter from "./components/CurrencyConverter"; 3 | 4 | function App() { 5 | return ( 6 | <> 7 | 8 | 9 | ); 10 | } 11 | 12 | export default App; 13 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/src/components/CurrencyConverter.css: -------------------------------------------------------------------------------- 1 | .converter { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | padding: 2rem; 6 | border-radius: 10px; 7 | width: 400px; 8 | margin: 2rem auto; 9 | background-color: #f8f9fa; 10 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 11 | } 12 | 13 | .converter h2 { 14 | color: #343a40; 15 | font-size: 1.8rem; 16 | margin-bottom: 2rem; 17 | } 18 | 19 | .converter input, 20 | .converter select { 21 | width: 100%; 22 | padding: 0.7rem 1rem; 23 | box-sizing: border-box; 24 | border-radius: 4px; 25 | font-size: 1rem; 26 | color: #495057; 27 | background-color: #fff; 28 | border: 1px solid #ced4da; 29 | } 30 | 31 | .converter input { 32 | margin-bottom: 30px; 33 | } 34 | 35 | .converter span, 36 | .converter p { 37 | margin: 0.5rem; 38 | font-size: 1rem; 39 | color: #495057; 40 | } 41 | 42 | .converter p { 43 | font-size: 1.2rem; 44 | } 45 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: Helvetica; 4 | } 5 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /3_conversor_moeda/conversor_moeda/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 | -------------------------------------------------------------------------------- /4_clima/clima/.env: -------------------------------------------------------------------------------- 1 | VITE_API_KEY=ba605efc18f1572f61892fe426f18a1a -------------------------------------------------------------------------------- /4_clima/clima/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /4_clima/clima/.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 | -------------------------------------------------------------------------------- /4_clima/clima/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /4_clima/clima/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /4_clima/clima/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clima", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.4.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "styled-components": "^6.0.7" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.15", 20 | "@types/react-dom": "^18.2.7", 21 | "@vitejs/plugin-react": "^4.0.3", 22 | "eslint": "^8.45.0", 23 | "eslint-plugin-react": "^7.32.2", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.3", 26 | "vite": "^4.4.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /4_clima/clima/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/Busca.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BuscaContainer, InputCidade, BotaoBuscar } from "./BuscaStyles"; 3 | 4 | const Busca = ({ cidade, setCidade, buscarClima }) => { 5 | return ( 6 | 7 | setCidade(e.target.value)} 11 | placeholder="Digite uma cidade" 12 | /> 13 | Buscar 14 | 15 | ); 16 | }; 17 | 18 | export default Busca; 19 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/BuscaStyles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const BuscaContainer = styled.div` 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | margin-bottom: 20px; 8 | `; 9 | 10 | export const InputCidade = styled.input` 11 | padding: 10px 15px; 12 | border: none; 13 | border-radius: 4px; 14 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); 15 | font-size: 16px; 16 | outline: none; 17 | width: 60%; 18 | margin-right: 10px; 19 | 20 | &:focus { 21 | border: 1px solid #007bff; 22 | } 23 | `; 24 | 25 | export const BotaoBuscar = styled.button` 26 | padding: 10px 15px; 27 | background-color: #007bff; 28 | color: #fff; 29 | border: none; 30 | border-radius: 4px; 31 | cursor: pointer; 32 | font-size: 16px; 33 | transition: background-color 0.2s ease; 34 | 35 | &:hover { 36 | background-color: #0056b3; 37 | } 38 | `; 39 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/ClimaAtual.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ClimaInfo } from "./ClimaAtualStyles"; 3 | 4 | const ClimaAtual = ({ clima }) => { 5 | return ( 6 | 7 |

{clima.name}

8 | {clima.weather[0].description} 12 |

{clima.main.temp}°C

13 |

{clima.weather[0].description}

14 |
15 | ); 16 | }; 17 | 18 | export default ClimaAtual; 19 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/ClimaAtualStyles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const ClimaInfo = styled.div` 4 | text-align: center; 5 | 6 | h3 { 7 | font-size: 1.5rem; 8 | margin-bottom: 10px; 9 | } 10 | 11 | img { 12 | width: 80px; 13 | height: 80px; 14 | margin-bottom: 10px; 15 | } 16 | 17 | p { 18 | margin-bottom: 5px; 19 | } 20 | `; 21 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/Previsao.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { PrevisaoContainer } from "./PrevisaoStyles"; 3 | 4 | const Previsao = ({ previsoes }) => { 5 | return ( 6 | 7 |

Previsão para as próximas horas

8 |
    9 | {previsoes.map((previsao, index) => ( 10 |
  • 11 | {previsao.weather[0].description} 15 | {previsao.main.temp}°C - {previsao.weather[0].description} 16 |
  • 17 | ))} 18 |
19 |
20 | ); 21 | }; 22 | 23 | export default Previsao; 24 | -------------------------------------------------------------------------------- /4_clima/clima/src/components/PrevisaoStyles.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const PrevisaoContainer = styled.div` 4 | margin-top: 20px; 5 | padding: 15px; 6 | background-color: #f7f7f7; 7 | border-radius: 5px; 8 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); 9 | 10 | h4 { 11 | text-align: center; 12 | margin-bottom: 10px; 13 | color: #333; 14 | } 15 | 16 | ul { 17 | list-style-type: none; 18 | padding: 0; 19 | 20 | li { 21 | display: flex; 22 | align-items: center; 23 | justify-content: space-between; 24 | padding: 10px; 25 | margin-bottom: 5px; 26 | background-color: #fff; 27 | border-radius: 3px; 28 | box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1); 29 | color: #333; 30 | 31 | img { 32 | margin-right: 10px; 33 | } 34 | } 35 | } 36 | `; 37 | -------------------------------------------------------------------------------- /4_clima/clima/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | -------------------------------------------------------------------------------- /4_clima/clima/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /4_clima/clima/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 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/.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 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "citacoes", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.1", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@vitejs/plugin-react": "^4.0.3", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Citacao from "./components/Citacao"; 3 | import citacoes from "./data"; 4 | 5 | function App() { 6 | const [indice, setIndice] = useState(0); 7 | 8 | function proximaCitacao() { 9 | setIndice((indiceAtual) => (indiceAtual + 1) % citacoes.length); 10 | } 11 | 12 | return ( 13 |
14 | 15 | 18 |
19 | ); 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/src/components/Citacao.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | function Citacao({ texto, autor }) { 4 | const [traducao, setTraducao] = useState(""); 5 | 6 | // Resetar a tradução quando o texto da citação mudar 7 | useEffect(() => { 8 | setTraducao(""); 9 | }, [texto]); 10 | 11 | async function traduzirCitação(idioma) { 12 | try { 13 | const resposta = await fetch("https://libretranslate.de/translate", { 14 | method: "POST", 15 | body: JSON.stringify({ 16 | q: texto, 17 | source: "pt", 18 | target: idioma, 19 | }), 20 | headers: { "Content-Type": "application/json" }, 21 | }); 22 | 23 | const data = await resposta.json(); 24 | setTraducao(data.translatedText); 25 | } catch (erro) { 26 | console.error("Erro ao traduzir citação:", erro); 27 | } 28 | } 29 | 30 | return ( 31 |
32 |
33 |

{traducao ? traducao : texto}

34 |
{autor}
35 |
36 | 42 | 48 |
49 | ); 50 | } 51 | 52 | export default Citacao; 53 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import "./index.css"; 5 | 6 | import "bootstrap/dist/css/bootstrap.min.css"; 7 | 8 | ReactDOM.createRoot(document.getElementById("root")).render( 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /5_citacoes/citacoes/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 | -------------------------------------------------------------------------------- /6_album/album/.env: -------------------------------------------------------------------------------- 1 | VITE_UNSPLASH_API_KEY=d0MPOt4hyt1U3sMLR3I83-9Oqqw3PHnYfHoaBL4mqXA -------------------------------------------------------------------------------- /6_album/album/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /6_album/album/.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 | -------------------------------------------------------------------------------- /6_album/album/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /6_album/album/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /6_album/album/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "album", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.5.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@vitejs/plugin-react": "^4.0.3", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /6_album/album/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /6_album/album/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matheusbattisti/projetos_react/992dfa03ff6f17b6d328471db595404494c26f7a/6_album/album/src/App.css -------------------------------------------------------------------------------- /6_album/album/src/components/Foto.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Foto = ({ dados, setFotoAmpliada }) => { 4 | return ( 5 |
setFotoAmpliada(dados)}> 6 | {dados.alt_description} 7 |
8 | ); 9 | }; 10 | 11 | export default Foto; 12 | -------------------------------------------------------------------------------- /6_album/album/src/components/FotoAmpliada.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const FotoAmpliada = ({ foto, setFotoAmpliada }) => { 4 | return ( 5 |
setFotoAmpliada(null)} 8 | > 9 |
10 | {foto.alt_description} 11 |
12 |
13 | ); 14 | }; 15 | 16 | export default FotoAmpliada; 17 | -------------------------------------------------------------------------------- /6_album/album/src/components/FotoList.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Foto from "./Foto"; 3 | 4 | const FotoList = ({ fotos, setFotoAmpliada }) => { 5 | return ( 6 |
7 | {fotos.map((foto) => ( 8 | 9 | ))} 10 |
11 | ); 12 | }; 13 | 14 | export default FotoList; 15 | -------------------------------------------------------------------------------- /6_album/album/src/components/SearchBar.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const SearchBar = ({ setQuery, setCategoria, setActivateSearch }) => { 4 | const [localQuery, setLocalQuery] = useState(""); 5 | const categorias = [ 6 | "Natureza", 7 | "Pessoas", 8 | "Tecnologia", 9 | "Animais", 10 | "Esportes", 11 | ]; 12 | 13 | return ( 14 |
15 | setLocalQuery(e.target.value)} 19 | placeholder="Pesquisar fotos..." 20 | /> 21 | 29 | 42 |
43 | ); 44 | }; 45 | 46 | export default SearchBar; 47 | -------------------------------------------------------------------------------- /6_album/album/src/index.css: -------------------------------------------------------------------------------- 1 | /* Container geral */ 2 | .container { 3 | margin: 20px auto; 4 | width: 80%; 5 | text-align: center; 6 | } 7 | 8 | /* Estilo da barra de pesquisa */ 9 | .search-bar { 10 | display: flex; 11 | justify-content: center; 12 | margin-bottom: 20px; 13 | } 14 | 15 | .search-bar input { 16 | width: 300px; 17 | padding: 10px; 18 | margin-right: 10px; 19 | } 20 | 21 | .search-bar button { 22 | padding: 11px 20px; 23 | } 24 | 25 | .search-bar select { 26 | padding: 10px; 27 | margin-left: 10px; 28 | } 29 | 30 | /* Estilo para a lista de fotos */ 31 | .album { 32 | display: grid; 33 | grid-template-columns: repeat(4, 1fr); 34 | gap: 16px; 35 | } 36 | 37 | .foto img { 38 | width: 100%; 39 | height: 100%; 40 | max-height: 400px; 41 | border-radius: 8px; 42 | cursor: pointer; 43 | } 44 | 45 | /* Estilo para a foto ampliada */ 46 | .foto-ampliada-backdrop { 47 | position: fixed; 48 | top: 0; 49 | left: 0; 50 | width: 100%; 51 | height: 100%; 52 | background: rgba(0, 0, 0, 0.8); 53 | display: flex; 54 | justify-content: center; 55 | align-items: center; 56 | } 57 | 58 | .foto-ampliada-container { 59 | max-width: 90%; 60 | max-height: 90%; 61 | } 62 | 63 | .foto-ampliada-container img { 64 | max-width: 45%; 65 | max-height: 100%; 66 | border-radius: 8px; 67 | } 68 | -------------------------------------------------------------------------------- /6_album/album/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /6_album/album/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 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/.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 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdownviewer", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "marked": "^7.0.5", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@vitejs/plugin-react": "^4.0.3", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /7_markdown_viewer/markdownviewer/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import { marked } from "marked"; 3 | import Toolbar from "./components/Toolbar"; 4 | 5 | const App = () => { 6 | const [text, setText] = useState( 7 | localStorage.getItem("markdownText") || "# Hello, Markdown!" 8 | ); 9 | const textAreaRef = useRef(null); 10 | 11 | useEffect(() => { 12 | localStorage.setItem("markdownText", text); 13 | }, [text]); 14 | 15 | const insertText = (before, after) => { 16 | const textArea = textAreaRef.current; 17 | const start = textArea.selectionStart; 18 | const end = textArea.selectionEnd; 19 | const previousText = textArea.value; 20 | const beforeText = previousText.substring(0, start); 21 | const selectedText = previousText.substring(start, end); 22 | const afterText = previousText.substring(end); 23 | 24 | const newText = `${beforeText}${before}${selectedText}${after}${afterText}`; 25 | setText(newText); 26 | textArea.focus(); 27 | }; 28 | 29 | const renderText = () => { 30 | return { __html: marked(text) }; 31 | }; 32 | 33 | return ( 34 |
35 | 36 |