diff --git a/README.md b/README.md index d1c68b5..4172b2c 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ -# Todo \ No newline at end of file +# Todo +https://nowapp.netlify.app/ \ No newline at end of file diff --git a/package.json b/package.json index caf6289..1f583a3 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "zustand": "^5.0.5" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/src/App.jsx b/src/App.jsx index 5427540..a3ffff2 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,16 @@ + +import { Header } from './components/Header'; +import { TodoForm } from './components/TodoForm'; +import { TodoList } from './components/TodoList'; +import './index.css'; + export const App = () => { return ( -

React Boilerplate

+ +
+
+ + +
) -} +} \ No newline at end of file diff --git a/src/Store/useTodoStore.jsx b/src/Store/useTodoStore.jsx new file mode 100644 index 0000000..479b2da --- /dev/null +++ b/src/Store/useTodoStore.jsx @@ -0,0 +1,24 @@ +import { create } from 'zustand'; +import { nanoid } from 'nanoid'; + +export const useTodoStore = create((set) => ({ + tasks: [ + { id: nanoid(), title: 'Your first task is to find what feels effortless to you.', completed: false }, + { id: nanoid(), title: 'Your second task is to put maximum effort into it.', completed: false }, + ], + addTask: (title) => + set((state) => ({ + tasks: [...state.tasks, { id: nanoid(), title, completed: false }], + })), + removeTask: (id) => + set((state) => ({ + tasks: state.tasks.filter((task) => task.id !== id), + })), + toggleTask: (id) => + set((state) => ({ + tasks: state.tasks.map((task) => + task.id === id ? { ...task, completed: !task.completed } : task + ), + })), +})) + diff --git a/src/components/EmptyState.jsx b/src/components/EmptyState.jsx new file mode 100644 index 0000000..05c0db8 --- /dev/null +++ b/src/components/EmptyState.jsx @@ -0,0 +1,8 @@ +import { React } from 'react'; + + +export const EmptyState = () => ( +
+

No tasks. Add one or two!

+
+) \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 0000000..637583e --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,13 @@ +import { useTodoStore } from '../Store/useTodoStore'; + +export const Header = () => { + const tasks = useTodoStore((state) => state.tasks); + const incomplete = tasks.filter((t) => !t.completed).length; + + return ( +
+

Now

+

{incomplete} tasks remaining

+
+ ); +} diff --git a/src/components/TodoForm.jsx b/src/components/TodoForm.jsx new file mode 100644 index 0000000..fc3acfb --- /dev/null +++ b/src/components/TodoForm.jsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; + +export const TodoForm = () => { + const [title, setTitle] = useState(''); + const addTask = useTodoStore((state) => state.addTask); + + const handleSubmit = (e) => { + e.preventDefault(); + if (!title.trim()) return; + addTask(title); + setTitle(''); + }; + + return ( +
+ setTitle(e.target.value)} + /> + +
+ ); +} \ No newline at end of file diff --git a/src/components/TodoItem.jsx b/src/components/TodoItem.jsx new file mode 100644 index 0000000..d162dc2 --- /dev/null +++ b/src/components/TodoItem.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; + +export const TodoItem = ({ task }) => { + const toggleTask = useTodoStore((state) => state.toggleTask); + const removeTask = useTodoStore((state) => state.removeTask); + + return ( +
  • +
    toggleTask(task.id)} + role="button" + tabIndex={0} + onKeyDown={(e) => e.key === 'Enter' && toggleTask(task.id)} + aria-label="Toggle task" + > + {task.title} +
    + +
  • + ); +} \ No newline at end of file diff --git a/src/components/TodoList.jsx b/src/components/TodoList.jsx new file mode 100644 index 0000000..0ad8ce0 --- /dev/null +++ b/src/components/TodoList.jsx @@ -0,0 +1,37 @@ +import { React } from 'react'; +import { useTodoStore } from '../Store/useTodoStore'; +import { TodoItem } from './TodoItem'; +import { EmptyState } from './EmptyState'; + +export const TodoList = () => { + const tasks = useTodoStore((state) => state.tasks); + const toggleTask = useTodoStore((state) => state.toggleTask); + const removeTask = useTodoStore((state) => state.removeTask); + + + + if (tasks.length === 0) return ; + + return ( + + ); +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index f7c0aef..4f91a3a 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,99 @@ :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; } + +body { + margin: 0; + background-color: #f5f0e8; + color: #000; +} + +.app-container { + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem; +} + +h1 { + font-size: 4rem; + margin-bottom: 2.5rem; + font-family: libre; +} + +p { + color: rgb(96, 95, 95); + +} + + + +.input-form { + padding: 2rem; + border-radius: 8px; + font-size: 1.5rem; + border-color: white; +} + +.task-submit { + padding: 2rem; + border-color: white; + border-radius: 8px; + font-size: 1.5rem; +} + +ul { + list-style-type: none; + font-size: 2rem; +} + +.checkbox-wrapper { + display: flex; + align-items: center; + gap: 0.6rem; + cursor: pointer; +} + +.checkbox-wrapper input[type="checkbox"] { + display: none; +} + +.custom-checkbox { + width: 18px; + height: 18px; + border: 2px solid #7a6c5d; + border-radius: 4px; + background-color: transparent; + position: relative; + flex-shrink: 0; +} + +.checkbox-wrapper input[type="checkbox"]:checked+.custom-checkbox::after { + content: ""; + position: absolute; + top: 2px; + left: 5px; + width: 4px; + height: 8px; + border: solid #7a6c5d; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +.task-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.4rem 0.6rem; + border-bottom: 1px solid #eee; + position: relative; +} + +.delete-button { + height: 32px; + border: 2px solid #7a6c5d; + border-radius: 4px; + background-color: transparent; + +} \ No newline at end of file