├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── AddTodoForm.tsx ├── App.tsx ├── TodoList.tsx ├── TodoListItem.css ├── TodoListItem.tsx ├── index.tsx ├── initialTodos.ts ├── react-app-env.d.ts └── types.d.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React TypeScript Todo App 2 | 3 | ## About this Repository 4 | 5 | This app was created during the React TypeScript Todo App tutorial (link forthcoming). I recommend following along with the tutorial video, but feel free to clone this app directly and explore the code! 6 | 7 | ## Clone and Run the App 8 | 9 | Clone the app: 10 | 11 | ```bash 12 | git clone https://github.com/nas5w/react-typescript-todo-app.git 13 | cd react-typescript-todo-app 14 | ``` 15 | 16 | Install dependencies: 17 | 18 | ```bash 19 | yarn 20 | ``` 21 | 22 | Run the app: 23 | 24 | ``` 25 | yarn start 26 | ``` 27 | 28 | ## Notes 29 | 30 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-typescript", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@types/jest": "24.0.18", 7 | "@types/node": "12.7.3", 8 | "@types/react": "16.9.2", 9 | "@types/react-dom": "16.9.0", 10 | "react": "^16.9.0", 11 | "react-dom": "^16.9.0", 12 | "react-scripts": "3.1.1", 13 | "typescript": "3.6.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nas5w/react-typescript-todo-app/37685b33fa42ea06fd888d7a47ab78f6a105e05e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nas5w/react-typescript-todo-app/37685b33fa42ea06fd888d7a47ab78f6a105e05e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nas5w/react-typescript-todo-app/37685b33fa42ea06fd888d7a47ab78f6a105e05e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /src/AddTodoForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, ChangeEvent, FormEvent } from "react"; 2 | import { AddTodo } from "./types"; 3 | 4 | interface AddTodoFormProps { 5 | addTodo: AddTodo; 6 | } 7 | 8 | export const AddTodoForm: React.FC = ({ addTodo }) => { 9 | const [newTodo, setNewTodo] = useState(""); 10 | 11 | const handleChange = (e: ChangeEvent) => { 12 | setNewTodo(e.target.value); 13 | }; 14 | 15 | const handleSubmit = (e: FormEvent) => { 16 | e.preventDefault(); 17 | addTodo(newTodo); 18 | setNewTodo(""); 19 | }; 20 | 21 | return ( 22 |
23 | 24 | 27 |
28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { initialTodos } from "./initialTodos"; 3 | import { TodoList } from "./TodoList"; 4 | import { AddTodoForm } from "./AddTodoForm"; 5 | 6 | const App: React.FC = () => { 7 | const [todos, setTodos] = useState>(initialTodos); 8 | 9 | const toggleComplete: ToggleComplete = selectedTodo => { 10 | const updatedTodos = todos.map(todo => { 11 | if (todo === selectedTodo) { 12 | return { ...todo, complete: !todo.complete }; 13 | } 14 | return todo; 15 | }); 16 | setTodos(updatedTodos); 17 | }; 18 | 19 | const addTodo: AddTodo = newTodo => { 20 | newTodo.trim() !== "" && 21 | setTodos([...todos, { text: newTodo, complete: false }]); 22 | }; 23 | 24 | return ( 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default App; 33 | -------------------------------------------------------------------------------- /src/TodoList.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Todo, ToggleComplete } from "./types"; 3 | import { TodoListItem } from "./TodoListItem"; 4 | 5 | interface TodoListProps { 6 | todos: Array; 7 | toggleComplete: ToggleComplete; 8 | } 9 | 10 | export const TodoList: React.FC = ({ 11 | todos, 12 | toggleComplete 13 | }) => { 14 | return ( 15 |
    16 | {todos.map(todo => ( 17 | 22 | ))} 23 |
24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/TodoListItem.css: -------------------------------------------------------------------------------- 1 | .complete { 2 | text-decoration: line-through; 3 | } 4 | -------------------------------------------------------------------------------- /src/TodoListItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./TodoListItem.css"; 3 | 4 | interface TodoListItemProps { 5 | todo: Todo; 6 | toggleComplete: ToggleComplete; 7 | } 8 | 9 | export const TodoListItem: React.FC = ({ 10 | todo, 11 | toggleComplete 12 | }) => { 13 | return ( 14 |
  • 15 | 23 |
  • 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | ReactDOM.render(, document.getElementById("root")); 6 | -------------------------------------------------------------------------------- /src/initialTodos.ts: -------------------------------------------------------------------------------- 1 | export const initialTodos: Array = [ 2 | { 3 | text: "Walk the dog", 4 | complete: true 5 | }, 6 | { 7 | text: "Make app", 8 | complete: false 9 | } 10 | ]; 11 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/types.d.ts: -------------------------------------------------------------------------------- 1 | type Todo = { 2 | text: string; 3 | complete: boolean; 4 | }; 5 | 6 | type ToggleComplete = (selectedTodo: Todo) => void; 7 | 8 | type AddTodo = (newTodo: string) => void; 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "react" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------