Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
# Todo
# Todo

Netlify-link: https://project-todo-aj.netlify.app/
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
"preview": "vite preview"
},
"dependencies": {
"lottie-react": "^2.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"styled-components": "^6.3.6",
"zustand": "^5.0.10"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
Expand Down
Binary file added public/trash-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/Animation.json

Large diffs are not rendered by default.

32 changes: 31 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
import styled from "styled-components"

import { Header } from "./components/Header"
import { ToDoInput } from "./components/ToDoInput"
import { CountTasks } from "./components/ToDoCounter"
import { ToDoTasks } from "./components/ToDoTasks"
import { Footer } from "./components/Footer"

export const App = () => {
return (
<h1>React Boilerplate</h1>
<>
<Page>

<Header />

<ToDoInput />

<CountTasks />

<ToDoTasks />

</Page>

<Footer />

</>
)
}

const Page = styled.div`
min-height: 100vh;
display: flex;
flex-direction: column;
padding-bottom: 44px;
`
19 changes: 19 additions & 0 deletions src/components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import styled from "styled-components"

export const Footer = () => {
return (
<StyledFooter>ToDoList by Asako & Julia</StyledFooter>
)
}

const StyledFooter = styled.footer`
position: fixed;
left: 0;
right: 0;
bottom: 0;
height: 44px;
padding: 16px 0;
display: flex;
flex-direction: column;
align-items: center;
`
29 changes: 29 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import styled from "styled-components"

export const Header = () => {
return (
<StyledDiv>
<svg width="85" height="64" viewBox="0 0 85 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 40H44L52 32H20C18.8667 32 17.9167 32.3833 17.15 33.15C16.3833 33.9167 16 34.8667 16 36C16 37.1333 16.3833 38.0833 17.15 38.85C17.9167 39.6167 18.8667 40 20 40ZM20 24H36C37.1333 24 38.0833 23.6167 38.85 22.85C39.6167 22.0833 40 21.1333 40 20C40 18.8667 39.6167 17.9167 38.85 17.15C38.0833 16.3833 37.1333 16 36 16H20C18.8667 16 17.9167 16.3833 17.15 17.15C16.3833 17.9167 16 18.8667 16 20C16 21.1333 16.3833 22.0833 17.15 22.85C17.9167 23.6167 18.8667 24 20 24ZM28 56H8C5.8 56 3.91667 55.2167 2.35 53.65C0.783333 52.0833 0 50.2 0 48V8C0 5.8 0.783333 3.91667 2.35 2.35C3.91667 0.783333 5.8 0 8 0H72C74.2 0 76.0833 0.783333 77.65 2.35C79.2167 3.91667 80 5.8 80 8V12H72V8H8V48H36L28 56ZM83.6 29.2C83.9333 29.5333 84.1 29.9 84.1 30.3C84.1 30.7 83.9333 31.0667 83.6 31.4L80 35L73 28L76.6 24.4C76.9333 24.0667 77.3 23.9 77.7 23.9C78.1 23.9 78.4667 24.0667 78.8 24.4L83.6 29.2ZM77.6 37.4L52.2 62.8C51.8 63.2 51.35 63.5 50.85 63.7C50.35 63.9 49.8333 64 49.3 64H46C45.4667 64 45 63.8 44.6 63.4C44.2 63 44 62.5333 44 62V58.7C44 58.1667 44.1 57.65 44.3 57.15C44.5 56.65 44.8 56.2 45.2 55.8L70.6 30.4L77.6 37.4Z" fill="#3B75FF" />
</svg>
<StyledText>
<h1>TODO LIST</h1>
</StyledText>
</StyledDiv>
)
}

const StyledDiv = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 50px;
`

const StyledText = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`
23 changes: 23 additions & 0 deletions src/components/Lottie.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Lottie from "lottie-react"
import Animation from "../Animation.json"

export const LottieAnime = () => {
const options = {
animationData: Animation,
style: {
height: 500,
margin: "0 auto",
},
loop: true,
autoplay: true
}

return (
<Lottie
animationData={options.animationData}
style={options.style}
loop={options.loop}
autoplay={options.autoplay}
/>
)
}
37 changes: 37 additions & 0 deletions src/components/ToDoCounter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import styled from "styled-components"
import { useTasksStore } from "../stores/tasksStore"

export const CountTasks = () => {

const active = useTasksStore((s) => s.tasks.filter((t) => !t.done).length)
const done = useTasksStore((s) => s.tasks.filter((t) => t.done).length)

return (
<StyledDiv>
<h2>Your tasks</h2>
<CounterContainer>
<TaskBox><h3>Active: {active}</h3></TaskBox>
<TaskBox><h3>Done: {done}</h3></TaskBox>
</CounterContainer>
</StyledDiv>
)
}

const StyledDiv = styled.div`
margin-top: 40px;
margin-bottom: 20px;
`

const CounterContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
`

const TaskBox = styled.div`
border: 2px solid ${({ theme }) => theme.colors.border};
border-radius: 5px;
padding: 15px;
width: 45%;
background-color: ${({ theme }) => theme.colors.cardBg};
`
66 changes: 66 additions & 0 deletions src/components/ToDoInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useState } from "react";
import { useTasksStore } from '../stores/tasksStore'
import styled from "styled-components"

export const ToDoInput = () => {
const addTask = useTasksStore((state) => state.addTask)
const [newTask, setNewTask] = useState({ text: "", done: false })
const isValid = newTask.text.length >= 1

const handleAddTask = (event) => {
event.preventDefault()
if (!isValid) return
addTask(newTask)
setNewTask({ text: "" })
}

return (
<StyledDiv>
<StyledInput
type="text"
value={newTask.text}
onChange={(e) => setNewTask({ ...newTask, text: e.target.value })}
placeholder="Add a new task..."
/>
<StyledButton
onClick={handleAddTask}
aria-label="Submit button"
>
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="60" height="60" rx="5" fill="#3B75FF" />
<path d="M30.166 14C31.2706 14 32.166 14.8954 32.166 16V27.8887H43.7773C44.8818 27.8888 45.7773 28.7842 45.7773 29.8887C45.7773 30.9932 44.8818 31.8885 43.7773 31.8887H32.166V43.7773C32.1659 44.8818 31.2705 45.7773 30.166 45.7773C29.0617 45.7771 28.1661 44.8817 28.166 43.7773V31.8887H16C14.8954 31.8887 14 30.9932 14 29.8887C14 28.7841 14.8954 27.8887 16 27.8887H28.166V16C28.166 14.8956 29.0616 14.0002 30.166 14Z" fill="white" />
</svg>
</StyledButton>
</StyledDiv >
)
}

const StyledDiv = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
border: 2px solid ${({ theme }) => theme.colors.border};
border-radius: 5px;
padding: 5px;
margin-top: 15px;
`

const StyledInput = styled.textarea`
flex: 1;
border: none;
min-height: 60px;
padding: 5px;
font-size: 18px;
`

const StyledButton = styled.button`
border: none;
padding-left: 3px;
background-color: ${({ theme }) => theme.colors.cardBg};

&:hover {
cursor: pointer;
transform: scale(1.05);
}
`
86 changes: 86 additions & 0 deletions src/components/ToDoTasks.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useTasksStore } from '../stores/tasksStore'
import styled from "styled-components"
import { LottieAnime } from './Lottie'

export const ToDoTasks = () => {
const tasks = useTasksStore((state) => state.tasks);
const removeTask = useTasksStore((state) => state.removeTask)
const toggleTask = useTasksStore((state) => state.toggleTask)

const handleRemoveTask = (taskToRemove) => {
removeTask(taskToRemove)
}

const handleOnTaskChange = (_, task) => {
toggleTask(task);
}

return (
<>
{tasks.length === 0 ? (
<LottieAnime />
) : (
<ul>
{tasks.map((task, index) => (
<StyledLi key={index}>
<StyledSpan>
<StyledCheckbox
type="checkbox"
onChange={(e) => handleOnTaskChange(e.target.checked, task)}
checked={task.done}
/>
{task.text}
</StyledSpan>
<StyledButton
onClick={() => handleRemoveTask(task)}
aria-label="Remove button"
>
<img src="/trash-2.png" alt="Remove button" />
</StyledButton>
</StyledLi>
))}
</ul >
)}
</>
)
}

const StyledLi = styled.li`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
background-color: ${({ theme }) => theme.colors.cardBg};
border: 2px solid ${({ theme }) => theme.colors.border};
border-radius: 5px;
list-style: none;
padding: 10px;
margin-top: 15px;
`

const StyledCheckbox = styled.input`
width: 25px;
height: 25px;
margin-right: 10px;
accent-color: ${({ theme }) => theme.colors.iconBlue};

&:hover {
cursor: pointer;
transform: scale(1.05);

}
`

const StyledButton = styled.button`
border: none;
background-color: ${({ theme }) => theme.colors.cardBg};

&:hover {
cursor: pointer;}
`

const StyledSpan = styled.label`
display: flex;
flex-direction: row;
align-items: center;
`
4 changes: 2 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:root {
/* :root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
}
} */
20 changes: 14 additions & 6 deletions src/main.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import React from 'react'
import { GlobalStyle } from "./styles/GlobalStyles"
import { ThemeProvider } from "styled-components"
import { theme } from "./styles/Theme"
import ReactDOM from 'react-dom/client'

import { App } from './App.jsx'
import { App } from './App'

import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
<>
<ThemeProvider theme={theme}>

<GlobalStyle />

<App />

</ThemeProvider>
</>
)
Loading