diff --git a/index.html b/index.html index 117e48c..0f44ce4 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,9 @@ - + - React + TS + DMMO
diff --git a/src/App.tsx b/src/App.tsx index 38bce97..2bdc14e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,10 +3,12 @@ import { Toaster } from 'react-hot-toast' function App() { return ( -
-

Todo App Tutorial

+
+ + +
) } -export default App +export default App \ No newline at end of file diff --git a/src/components/AddTodo.tsx b/src/components/AddTodo.tsx index 8d21bc2..fed6148 100644 --- a/src/components/AddTodo.tsx +++ b/src/components/AddTodo.tsx @@ -1,8 +1,48 @@ + import React, { useEffect, useRef, useState } from 'react' import { toast } from 'react-hot-toast' -import { useTodo } from '../context' +import { useTodo } from '../context/useTodo' import { Input } from './Input' export const AddTodo = () => { - return
Add To Do
-} + const [input, setInput] = useState(''); + // const [todos, setTodos] = useState([]); + const inputRef = useRef(null); + const {addTodo} = useTodo(); + const handleSubmit=(e:React.FormEvent)=>{ + e.preventDefault(); + if(input.trim()!==''){ + addTodo(input); + setInput(''); + toast.success('Todo added successfully'); + }else { + toast.error('Todo field cannot be empty!'); + } + // setTodos([...todos, input]); + } + useEffect(()=>{ + if(inputRef.current){ + inputRef.current.focus(); + } + },[]); + return ( +
+
+ setInput(e.target.value)} + type="text" + className="w-full px-5 py-2 bg-transparent border-2 outline-none border-zinc-600 rounded-xl placeholder:text-zinc-500 focus:border-white" + placeholder="start typing ..." + /> + +
+
+ ) +} \ No newline at end of file diff --git a/src/components/Input.tsx b/src/components/Input.tsx index ad0b3c1..846ecd6 100644 --- a/src/components/Input.tsx +++ b/src/components/Input.tsx @@ -3,6 +3,15 @@ import cn from 'classnames' import React from 'react' -export const Input = () => { - return
Input
-} +export const Input = forwardRef>(({className, ...rest}, ref) => { + return ( + + ) +}); diff --git a/src/components/TodoItem.tsx b/src/components/TodoItem.tsx index b6e8621..ecf51a3 100644 --- a/src/components/TodoItem.tsx +++ b/src/components/TodoItem.tsx @@ -1,6 +1,6 @@ -import type { Todo } from '../context' import { useEffect, useRef, useState } from 'react' -import { useTodo } from '../context' +import { Todo } from '../context/TodoContext' +import { useTodo } from '../context/useTodo' import { Input } from './Input' import { BsCheck2Square } from 'react-icons/bs' import { TbRefresh } from 'react-icons/tb' @@ -11,5 +11,119 @@ import cn from 'classnames' import { motion } from 'framer-motion' export const TodoItem = (props: { todo: Todo }) => { - return
Todo Item
-} + const { todo } = props + + const [editingTodoText, setEditingTodoText] = useState('') + const [editingTodoId, setEditingTodoId] = useState(null) + + const { deleteTodo, editTodo, updateTodoStatus } = useTodo() + + const editInputRef = useRef(null) + + useEffect(() => { + if (editingTodoId !== null && editInputRef.current) { + editInputRef.current.focus() + } + }, [editingTodoId]) + + const handleEdit = (todoId: string, todoText: string) => { + setEditingTodoId(todoId) + setEditingTodoText(todoText) + + if (editInputRef.current) { + editInputRef.current.focus() + } + } + + const handleUpdate = (todoId: string) => { + if (editingTodoText.trim() !== '') { + editTodo(todoId, editingTodoText) + setEditingTodoId(null) + setEditingTodoText('') + toast.success('Todo updated successfully!') + } else { + toast.error('Todo field cannot be empty!') + } + } + + const handleDelete = (todoId: string) => { + deleteTodo(todoId) + toast.success('Todo deleted successfully!') + } + + const handleStatusUpdate = (todoId: string) => { + updateTodoStatus(todoId) + toast.success('Todo status updated successfully!') + } + + return ( + + {editingTodoId === todo.id ? ( + + setEditingTodoText(e.target.value)} + /> + + + ) : ( +
+ + {todo.text} + +
+ +
+ + +
+
+
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/components/TodoList.tsx b/src/components/TodoList.tsx index 582296b..712ee8d 100644 --- a/src/components/TodoList.tsx +++ b/src/components/TodoList.tsx @@ -1,7 +1,25 @@ import { TodoItem } from './TodoItem' import { useTodo } from '../context' import { SiStarship } from 'react-icons/si' +import { motion } from 'framer-motion' export const TodoList = () => { - return
TodoList
+ const {todos} = useTodo(); + if(!todos.length){ + return ( +
+

+ + You have nothing to do! +

+
+ ) + } + return ( + + {[...todos.filter(todo => todo.status === 'undone'), ...todos.filter(todo => todo.status === 'completed')].map(todo => ( + + ))} + + ) } diff --git a/src/context/TodoContext.tsx b/src/context/TodoContext.tsx index 04776f2..a8488d5 100644 --- a/src/context/TodoContext.tsx +++ b/src/context/TodoContext.tsx @@ -7,13 +7,45 @@ export interface Todo { text: string status: 'undone' | 'completed' } +interface TodoContextProps { + todos: Todo[] + addTodo: (text: string) => void + deleteTodo:(id: string) => void + editTodo:(id: string, text: string) => void + updateTodoStatus:(id:string)=>void -export const TodoContext = createContext(undefined) +} +export const TodoContext = createContext( + undefined, +) export const TodoProvider = (props: { children: React.ReactNode }) => { + const [todos, setTodos] = useState([]) + const addTodo = (text: string) => { + const newTodo: Todo = { + id: nanoid(), + text, + status: 'undone', + } + setTodos([...todos, newTodo]) + } + const deleteTodo = (id: string) => { + setTodos(todos.filter(todo => todo.id !== id)) + } + const editTodo = (id: string, text: string) => { + setTodos(prevTodos=>prevTodos.map(todo=>todo.id===id?{...todo,text}:todo)) + } + const updateTodoStatus = (id: string) => { + setTodos(prevTodos=>prevTodos.map(todo=>todo.id===id?{...todo,status:todo.status==='undone'?'completed':'undone'}:todo)) + } + const value: TodoContextProps = { + todos, + addTodo, + deleteTodo, + editTodo, + updateTodoStatus, + } return ( - - {props.children} - + {props.children} ) } diff --git a/src/context/useTodo.ts b/src/context/useTodo.ts index dc77667..9bece8d 100644 --- a/src/context/useTodo.ts +++ b/src/context/useTodo.ts @@ -2,5 +2,7 @@ import { useContext } from 'react' import { TodoContext } from './TodoContext' export const useTodo = () => { - return + const context = useContext(TodoContext) + if (!context) throw new Error('useTodo must be used within a TodoProvider') + return context } diff --git a/src/main.tsx b/src/main.tsx index a5fb77c..855906c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,6 +6,8 @@ import { TodoProvider } from './context' ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - + + + , )