From d0191e3579cf0d3124daca188572afddddea3cdc Mon Sep 17 00:00:00 2001 From: leo tatico Date: Tue, 1 Aug 2023 10:14:57 -0500 Subject: [PATCH 01/13] main --- package-lock.json | 15 +++++++ package.json | 1 + public/index.html | 3 +- src/App.css | 38 ------------------ src/App.js | 93 +++++++++++++++++++++++++++++++++++--------- src/CompleteIcon.js | 14 +++++++ src/CreateButton.css | 25 ++++++++++++ src/CreateButton.js | 18 +++++++++ src/TodoCounter.css | 11 ++++++ src/TodoCounter.js | 11 ++++++ src/TodoIcon.css | 40 +++++++++++++++++++ src/TodoIcon.js | 21 ++++++++++ src/TodoItem.css | 21 ++++++++++ src/TodoItem.js | 24 ++++++++++++ src/TodoList.css | 5 +++ src/TodoList.js | 12 ++++++ src/TodoSearch.css | 24 ++++++++++++ src/TodoSearch.js | 23 +++++++++++ src/check.svg | 11 ++++++ src/delete.svg | 12 ++++++ src/deleteIcon.js | 14 +++++++ src/index.css | 16 ++++---- 22 files changed, 387 insertions(+), 65 deletions(-) delete mode 100644 src/App.css create mode 100644 src/CompleteIcon.js create mode 100644 src/CreateButton.css create mode 100644 src/CreateButton.js create mode 100644 src/TodoCounter.css create mode 100644 src/TodoCounter.js create mode 100644 src/TodoIcon.css create mode 100644 src/TodoIcon.js create mode 100644 src/TodoItem.css create mode 100644 src/TodoItem.js create mode 100644 src/TodoList.css create mode 100644 src/TodoList.js create mode 100644 src/TodoSearch.css create mode 100644 src/TodoSearch.js create mode 100644 src/check.svg create mode 100644 src/delete.svg create mode 100644 src/deleteIcon.js diff --git a/package-lock.json b/package-lock.json index f26ec7a8b..101e4a9ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "react": "^18", "react-dom": "^18", + "react-icons": "^4.10.1", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -13423,6 +13424,14 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-icons": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -25601,6 +25610,12 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "react-icons": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", + "requires": {} + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 0c4b9cdc0..dd20a62ea 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "dependencies": { "react": "^18", "react-dom": "^18", + "react-icons": "^4.10.1", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/public/index.html b/public/index.html index e5b37b15c..a040f9fc0 100644 --- a/public/index.html +++ b/public/index.html @@ -24,7 +24,8 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - React App + + Todo's Machine Tatico diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 4661df052..000000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #233553; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #97ca3f; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 952c8f1fd..7b4e45bfa 100644 --- a/src/App.js +++ b/src/App.js @@ -1,24 +1,81 @@ -import logo from './platzi.webp'; -import './App.css'; +import React from 'react'; +import { TodoCounter } from './TodoCounter'; +import { TodoSearch } from './TodoSearch'; +import { TodoList } from './TodoList'; +import { TodoItem } from './TodoItem' +import { CreateTodoButton } from './CreateButton'; + + +const defaultTodos = [ + { text: 'Cortar cebolla', completed: true }, + { text: 'Tomar el Curso de Intro a React.js', completed: false }, + { text: 'Llorar con la Llorona', completed: false }, + { text: 'LALALALALA', completed: false }, + { text: 'usar estados derivados', completed: true }, +]; + + function App() { + const [todos, setTodos] = React.useState(defaultTodos); + const [searchValue, setSearchValue] = React.useState(''); + + const completedTodos = todos.filter( + todo => !!todo.completed + ).length; + const totalTodos = todos.length; + + const searchedTodos = todos.filter( + (todo) => { + const todoText = todo.text.toLowerCase(); + const searchText = searchValue.toLowerCase(); + return todoText.includes(searchText); + } + ); + + const completeTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text == text + ); + newTodos[todoIndex].completed = true; + setTodos(newTodos); + }; + + const deleteTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text == text + ); + newTodos.splice(todoIndex, 1); + setTodos(newTodos); + }; + return ( -
-
- logo -

- Edita el archivo src/App.js y guarda para recargar. -

- - Learn React - -
-
+ <> + + + + + {searchedTodos.map(todo => ( + completeTodo(todo.text)} + onDelete={() => deleteTodo(todo.text)} + /> + ))} + + + + ); } diff --git a/src/CompleteIcon.js b/src/CompleteIcon.js new file mode 100644 index 000000000..c94aa95c7 --- /dev/null +++ b/src/CompleteIcon.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { TodoIcon } from './TodoIcon'; + +function CompleteIcon({ completed, onComplete }) { + return ( + + ); +} + +export { CompleteIcon }; \ No newline at end of file diff --git a/src/CreateButton.css b/src/CreateButton.css new file mode 100644 index 000000000..8f2d39f21 --- /dev/null +++ b/src/CreateButton.css @@ -0,0 +1,25 @@ +.CreateTodoButton { + background-color: #61DAFA; + box-shadow: 0px 5px 25px rgba(97, 218, 250, 0.5); + border: none; + border-radius: 50%; + cursor: pointer; + font-size: 50px; + position: fixed; + bottom: 24px; + right: 24px; + font-weight: bold; + color: #FAFAFA; + display: flex; + justify-content: center; + align-items: center; + height: 64px; + width: 64px; + + transform: rotate(0); + transition: .3s ease; +} + +.CreateTodoButton:hover { + transform: rotate(224deg); +} \ No newline at end of file diff --git a/src/CreateButton.js b/src/CreateButton.js new file mode 100644 index 000000000..ffabaa3cf --- /dev/null +++ b/src/CreateButton.js @@ -0,0 +1,18 @@ +import './CreateButton.css'; + +function CreateTodoButton() { + return ( + + ); + } + +export { CreateTodoButton }; \ No newline at end of file diff --git a/src/TodoCounter.css b/src/TodoCounter.css new file mode 100644 index 000000000..9044cce16 --- /dev/null +++ b/src/TodoCounter.css @@ -0,0 +1,11 @@ +.TodoCounter { + font-size: 24px; + text-align: center; + margin: 0; + padding: 48px; + font-weight: normal; +} + +.TodoCounter span { + font-weight: bold; +} \ No newline at end of file diff --git a/src/TodoCounter.js b/src/TodoCounter.js new file mode 100644 index 000000000..0ae179859 --- /dev/null +++ b/src/TodoCounter.js @@ -0,0 +1,11 @@ +import './TodoCounter.css'; + +function TodoCounter({ total, completed }) { + return ( +

+ Has completado {completed} de {total} TODOs +

+ ); +} + +export { TodoCounter }; diff --git a/src/TodoIcon.css b/src/TodoIcon.css new file mode 100644 index 000000000..90a015d28 --- /dev/null +++ b/src/TodoIcon.css @@ -0,0 +1,40 @@ +.Icon-container { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + height: 48px; + width: 48px; + font-size: 24px; + font-weight: bold; + /* background-color: #CCC; */ +} + +.Icon-container-check { + position: absolute; + left: 12px; +} +.Icon-container-check--active { + color: #4caf50; +} + +.Icon-container-delete { + position: absolute; + top: -24px; + right: 0; +} +.Icon-container-delete:hover { + color: red; +} + +.Icon-svg { + width: 24px; + height: 24px; +} + +.Icon-container-check:hover .Icon-svg { + fill: green; +} +.Icon-container-delete:hover .Icon-svg { + fill: red; +} \ No newline at end of file diff --git a/src/TodoIcon.js b/src/TodoIcon.js new file mode 100644 index 000000000..9558889b7 --- /dev/null +++ b/src/TodoIcon.js @@ -0,0 +1,21 @@ +import { ReactComponent as CheckSVG } from './check.svg'; +import { ReactComponent as DeleteSVG } from './delete.svg'; +import './TodoIcon.css'; + +const iconTypes = { + "check": (color) => , + "delete": (color) => , +}; + +function TodoIcon({ type, color, onClick }) { + return ( + + {iconTypes[type](color)} + + ) +} + +export { TodoIcon }; \ No newline at end of file diff --git a/src/TodoItem.css b/src/TodoItem.css new file mode 100644 index 000000000..e749aeedd --- /dev/null +++ b/src/TodoItem.css @@ -0,0 +1,21 @@ +.TodoItem { + background-color: #FAFAFA; + position: relative; + display: flex; + justify-content: center; + align-items: center; + margin-top: 24px; + box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.15); +} + +.TodoItem-p { + margin: 24px 0 24px 24px; + width: calc(100% - 120px); + font-size: 18px; + line-height: 24px; + font-weight: 400; +} + +.TodoItem-p--complete { + text-decoration: line-through; +} \ No newline at end of file diff --git a/src/TodoItem.js b/src/TodoItem.js new file mode 100644 index 000000000..a5c01ccf4 --- /dev/null +++ b/src/TodoItem.js @@ -0,0 +1,24 @@ +import { CompleteIcon } from './CompleteIcon' +import { DeleteIcon } from './deleteIcon' +import './TodoItem.css'; + +function TodoItem(props) { + return ( +
  • + + +

    + {props.text} +

    + + +
  • + ); +} + +export { TodoItem }; \ No newline at end of file diff --git a/src/TodoList.css b/src/TodoList.css new file mode 100644 index 000000000..2cff0b35a --- /dev/null +++ b/src/TodoList.css @@ -0,0 +1,5 @@ +.TodoList { + margin: 0; + padding: 0 0 56px 0; + list-style: none; +} \ No newline at end of file diff --git a/src/TodoList.js b/src/TodoList.js new file mode 100644 index 000000000..e7bf2213d --- /dev/null +++ b/src/TodoList.js @@ -0,0 +1,12 @@ +import './TodoList.css'; + +function TodoList({children}){ + return ( +
      + {children} +
    + + ); +} + +export { TodoList }; \ No newline at end of file diff --git a/src/TodoSearch.css b/src/TodoSearch.css new file mode 100644 index 000000000..0be798c39 --- /dev/null +++ b/src/TodoSearch.css @@ -0,0 +1,24 @@ +.TodoSearch { + background: #F9FBFC; + border-radius: 2px; + border: 2px solid #202329; + margin: 0 24px; + height: 64px; + width: calc(100% - 62px); + font-size: 24px; + text-align: center; + font-family: 'Montserrat'; + font-weight: 400; + color: #1E1E1F; + box-shadow: 0px 5px 50px rgba(32, 35, 41, 0.25); +} + +.TodoSearch::placeholder { + color: #A5A5A5; + font-family: 'Montserrat'; + font-weight: 400; +} + +.TodoSearch:focus { + outline-color: #61DAFA; +} \ No newline at end of file diff --git a/src/TodoSearch.js b/src/TodoSearch.js new file mode 100644 index 000000000..8e2402afc --- /dev/null +++ b/src/TodoSearch.js @@ -0,0 +1,23 @@ +import React from 'react'; +import './TodoSearch.css'; + + +function TodoSearch({ + searchValue, + setSearchValue, +}) { + + + return ( + { + setSearchValue(event.target.value); + }} + /> + ); +} + +export { TodoSearch }; \ No newline at end of file diff --git a/src/check.svg b/src/check.svg new file mode 100644 index 000000000..b8c316695 --- /dev/null +++ b/src/check.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/src/delete.svg b/src/delete.svg new file mode 100644 index 000000000..46abcf3e6 --- /dev/null +++ b/src/delete.svg @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/src/deleteIcon.js b/src/deleteIcon.js new file mode 100644 index 000000000..c54046671 --- /dev/null +++ b/src/deleteIcon.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { TodoIcon } from './TodoIcon'; + +function DeleteIcon({ onDelete }) { + return ( + + ); +} + +export { DeleteIcon }; \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e8c..d8f9a2878 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,13 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + padding: 0; + background: #F2F4F5; + color: #1E1E1F; + font-family: 'Montserrat', arial , helvetica, sans-serif; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; +#root { + margin: 0 24px; + position: relative; + min-height: 100vh; } From 97c85192e1f7a1b530edc4dcabebc5f05a65f42a Mon Sep 17 00:00:00 2001 From: leo tatico Date: Tue, 1 Aug 2023 11:44:05 -0500 Subject: [PATCH 02/13] actualizado --- src/App.js | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/App.js b/src/App.js index 7b4e45bfa..f08efb9e8 100644 --- a/src/App.js +++ b/src/App.js @@ -6,18 +6,31 @@ import { TodoItem } from './TodoItem' import { CreateTodoButton } from './CreateButton'; -const defaultTodos = [ - { text: 'Cortar cebolla', completed: true }, - { text: 'Tomar el Curso de Intro a React.js', completed: false }, - { text: 'Llorar con la Llorona', completed: false }, - { text: 'LALALALALA', completed: false }, - { text: 'usar estados derivados', completed: true }, -]; +//const defaultTodos = [ +// { text: 'Cortar cebolla', completed: true }, +// { text: 'Tomar el Curso de Intro a React.js', completed: false }, +// { text: 'Llorar con la Llorona', completed: false }, +// { text: 'LALALALALA', completed: false }, +// { text: 'usar estados derivados', completed: true }, +//]; +//localStorage.setItem(TODOS_V1, defaultTodos); +//localStorage.removeItem('TODOS_1') function App() { - const [todos, setTodos] = React.useState(defaultTodos); + const localStorageTodos = localStorage.getItem('TODOS_V1'); + + let parsedTodos; + + if (!localStorageTodos) { + localStorage.setItem('TODOS_V1', JSON.stringify([])); + parsedTodos = []; + } else { + parsedTodos = JSON.parse(localStorageTodos); + } + + const [todos, setTodos] = React.useState(parsedTodos); const [searchValue, setSearchValue] = React.useState(''); const completedTodos = todos.filter( @@ -33,22 +46,28 @@ function App() { } ); + const saveTodos = (newTodos) => { + localStorage.setItem('TODOS_V1', JSON.stringify(newTodos)); + + setTodos(newTodos); + }; + const completeTodo = (text) => { const newTodos = [...todos]; const todoIndex = newTodos.findIndex( - (todo) => todo.text == text + (todo) => todo.text = text ); newTodos[todoIndex].completed = true; - setTodos(newTodos); + saveTodos(newTodos); }; const deleteTodo = (text) => { const newTodos = [...todos]; const todoIndex = newTodos.findIndex( - (todo) => todo.text == text + (todo) => todo.text = text ); newTodos.splice(todoIndex, 1); - setTodos(newTodos); + saveTodos(newTodos); }; return ( From 58b856d9058c2ddba724ae06afeec03988cdd5d9 Mon Sep 17 00:00:00 2001 From: leo tatico Date: Tue, 1 Aug 2023 15:31:15 -0500 Subject: [PATCH 03/13] actualizado --- src/App.js | 101 ------------------ src/App/AppUI.js | 44 ++++++++ src/App/index.js | 64 +++++++++++ src/App/useLocalStore.js | 25 +++++ src/{ => CreateButton}/CreateButton.css | 0 .../index.js} | 0 src/{ => TodoCounter}/TodoCounter.css | 0 src/{TodoCounter.js => TodoCounter/index.js} | 0 src/{ => TodoIcon}/CompleteIcon.js | 2 +- src/{ => TodoIcon}/TodoIcon.css | 0 src/{ => TodoIcon}/check.svg | 0 src/{ => TodoIcon}/delete.svg | 0 src/{ => TodoIcon}/deleteIcon.js | 2 +- src/{TodoIcon.js => TodoIcon/index.js} | 0 src/{ => TodoItem}/TodoItem.css | 0 src/{TodoItem.js => TodoItem/index.js} | 4 +- src/{ => TodoList}/TodoList.css | 0 src/{TodoList.js => TodoList/index.js} | 0 src/{ => TodoSearch}/TodoSearch.css | 0 src/{TodoSearch.js => TodoSearch/index.js} | 0 20 files changed, 137 insertions(+), 105 deletions(-) delete mode 100644 src/App.js create mode 100644 src/App/AppUI.js create mode 100644 src/App/index.js create mode 100644 src/App/useLocalStore.js rename src/{ => CreateButton}/CreateButton.css (100%) rename src/{CreateButton.js => CreateButton/index.js} (100%) rename src/{ => TodoCounter}/TodoCounter.css (100%) rename src/{TodoCounter.js => TodoCounter/index.js} (100%) rename src/{ => TodoIcon}/CompleteIcon.js (85%) rename src/{ => TodoIcon}/TodoIcon.css (100%) rename src/{ => TodoIcon}/check.svg (100%) rename src/{ => TodoIcon}/delete.svg (100%) rename src/{ => TodoIcon}/deleteIcon.js (82%) rename src/{TodoIcon.js => TodoIcon/index.js} (100%) rename src/{ => TodoItem}/TodoItem.css (100%) rename src/{TodoItem.js => TodoItem/index.js} (79%) rename src/{ => TodoList}/TodoList.css (100%) rename src/{TodoList.js => TodoList/index.js} (100%) rename src/{ => TodoSearch}/TodoSearch.css (100%) rename src/{TodoSearch.js => TodoSearch/index.js} (100%) diff --git a/src/App.js b/src/App.js deleted file mode 100644 index f08efb9e8..000000000 --- a/src/App.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from 'react'; -import { TodoCounter } from './TodoCounter'; -import { TodoSearch } from './TodoSearch'; -import { TodoList } from './TodoList'; -import { TodoItem } from './TodoItem' -import { CreateTodoButton } from './CreateButton'; - - -//const defaultTodos = [ -// { text: 'Cortar cebolla', completed: true }, -// { text: 'Tomar el Curso de Intro a React.js', completed: false }, -// { text: 'Llorar con la Llorona', completed: false }, -// { text: 'LALALALALA', completed: false }, -// { text: 'usar estados derivados', completed: true }, -//]; - -//localStorage.setItem(TODOS_V1, defaultTodos); -//localStorage.removeItem('TODOS_1') - - -function App() { - const localStorageTodos = localStorage.getItem('TODOS_V1'); - - let parsedTodos; - - if (!localStorageTodos) { - localStorage.setItem('TODOS_V1', JSON.stringify([])); - parsedTodos = []; - } else { - parsedTodos = JSON.parse(localStorageTodos); - } - - const [todos, setTodos] = React.useState(parsedTodos); - const [searchValue, setSearchValue] = React.useState(''); - - const completedTodos = todos.filter( - todo => !!todo.completed - ).length; - const totalTodos = todos.length; - - const searchedTodos = todos.filter( - (todo) => { - const todoText = todo.text.toLowerCase(); - const searchText = searchValue.toLowerCase(); - return todoText.includes(searchText); - } - ); - - const saveTodos = (newTodos) => { - localStorage.setItem('TODOS_V1', JSON.stringify(newTodos)); - - setTodos(newTodos); - }; - - const completeTodo = (text) => { - const newTodos = [...todos]; - const todoIndex = newTodos.findIndex( - (todo) => todo.text = text - ); - newTodos[todoIndex].completed = true; - saveTodos(newTodos); - }; - - const deleteTodo = (text) => { - const newTodos = [...todos]; - const todoIndex = newTodos.findIndex( - (todo) => todo.text = text - ); - newTodos.splice(todoIndex, 1); - saveTodos(newTodos); - }; - - return ( - <> - - - - - {searchedTodos.map(todo => ( - completeTodo(todo.text)} - onDelete={() => deleteTodo(todo.text)} - /> - ))} - - - - - ); -} - -export default App; diff --git a/src/App/AppUI.js b/src/App/AppUI.js new file mode 100644 index 000000000..02db79696 --- /dev/null +++ b/src/App/AppUI.js @@ -0,0 +1,44 @@ +import { TodoCounter } from '../TodoCounter'; +import { TodoSearch } from '../TodoSearch'; +import { TodoList } from '../TodoList'; +import { TodoItem } from '../TodoItem'; +import { CreateTodoButton } from '../CreateTodoButton'; + +function AppUI({ + completedTodos, + totalTodos, + searchValue, + setSearchValue, + searchedTodos, + completeTodo, + deleteTodo, +}) { + return ( + <> + + + + + {searchedTodos.map(todo => ( + completeTodo(todo.text)} + onDelete={() => deleteTodo(todo.text)} + /> + ))} + + + + + ); +} + +export { AppUI }; \ No newline at end of file diff --git a/src/App/index.js b/src/App/index.js new file mode 100644 index 000000000..2f65850f6 --- /dev/null +++ b/src/App/index.js @@ -0,0 +1,64 @@ +import React from 'react'; +import { AppUI } from './AppUI'; +import { useLocalStorage } from './useLocalStorage'; + +// const defaultTodos = [ +// { text: 'Cortar cebolla', completed: true }, +// { text: 'Tomar el Curso de Intro a React.js', completed: false }, +// { text: 'Llorar con la Llorona', completed: false }, +// { text: 'LALALALALA', completed: false }, +// { text: 'Usar estados derivados', completed: true }, +// ]; + +// localStorage.setItem('TODOS_V1', JSON.stringify(defaultTodos)); +// localStorage.removeItem('TODOS_V1'); + +function App() { + const [todos, saveTodos] = useLocalStorage('TODOS_V1', []); + const [searchValue, setSearchValue] = React.useState(''); + + const completedTodos = todos.filter( + todo => !!todo.completed + ).length; + const totalTodos = todos.length; + + const searchedTodos = todos.filter( + (todo) => { + const todoText = todo.text.toLowerCase(); + const searchText = searchValue.toLowerCase(); + return todoText.includes(searchText); + } + ); + + const completeTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text === text + ); + newTodos[todoIndex].completed = true; + saveTodos(newTodos); + }; + + const deleteTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text === text + ); + newTodos.splice(todoIndex, 1); + saveTodos(newTodos); + }; + + return ( + + ); +} + +export default App; diff --git a/src/App/useLocalStore.js b/src/App/useLocalStore.js new file mode 100644 index 000000000..6fbc3bad2 --- /dev/null +++ b/src/App/useLocalStore.js @@ -0,0 +1,25 @@ +import React from 'react'; + +function useLocalStorage(itemName, initialValue) { + const localStorageItem = localStorage.getItem(itemName); + + let parsedItem; + + if (!localStorageItem) { + localStorage.setItem(itemName, JSON.stringify(initialValue)); + parsedItem = initialValue; + } else { + parsedItem = JSON.parse(localStorageItem); + } + + const [item, setItem] = React.useState(parsedItem); + + const saveItem = (newItem) => { + localStorage.setItem(itemName, JSON.stringify(newItem)); + setItem(newItem); + }; + + return [item, saveItem]; +} + +export { useLocalStorage }; \ No newline at end of file diff --git a/src/CreateButton.css b/src/CreateButton/CreateButton.css similarity index 100% rename from src/CreateButton.css rename to src/CreateButton/CreateButton.css diff --git a/src/CreateButton.js b/src/CreateButton/index.js similarity index 100% rename from src/CreateButton.js rename to src/CreateButton/index.js diff --git a/src/TodoCounter.css b/src/TodoCounter/TodoCounter.css similarity index 100% rename from src/TodoCounter.css rename to src/TodoCounter/TodoCounter.css diff --git a/src/TodoCounter.js b/src/TodoCounter/index.js similarity index 100% rename from src/TodoCounter.js rename to src/TodoCounter/index.js diff --git a/src/CompleteIcon.js b/src/TodoIcon/CompleteIcon.js similarity index 85% rename from src/CompleteIcon.js rename to src/TodoIcon/CompleteIcon.js index c94aa95c7..6dbf371a6 100644 --- a/src/CompleteIcon.js +++ b/src/TodoIcon/CompleteIcon.js @@ -1,5 +1,5 @@ import React from 'react'; -import { TodoIcon } from './TodoIcon'; +import { TodoIcon } from './'; function CompleteIcon({ completed, onComplete }) { return ( diff --git a/src/TodoIcon.css b/src/TodoIcon/TodoIcon.css similarity index 100% rename from src/TodoIcon.css rename to src/TodoIcon/TodoIcon.css diff --git a/src/check.svg b/src/TodoIcon/check.svg similarity index 100% rename from src/check.svg rename to src/TodoIcon/check.svg diff --git a/src/delete.svg b/src/TodoIcon/delete.svg similarity index 100% rename from src/delete.svg rename to src/TodoIcon/delete.svg diff --git a/src/deleteIcon.js b/src/TodoIcon/deleteIcon.js similarity index 82% rename from src/deleteIcon.js rename to src/TodoIcon/deleteIcon.js index c54046671..65ec4f4e7 100644 --- a/src/deleteIcon.js +++ b/src/TodoIcon/deleteIcon.js @@ -1,5 +1,5 @@ import React from 'react'; -import { TodoIcon } from './TodoIcon'; +import { TodoIcon } from './'; function DeleteIcon({ onDelete }) { return ( diff --git a/src/TodoIcon.js b/src/TodoIcon/index.js similarity index 100% rename from src/TodoIcon.js rename to src/TodoIcon/index.js diff --git a/src/TodoItem.css b/src/TodoItem/TodoItem.css similarity index 100% rename from src/TodoItem.css rename to src/TodoItem/TodoItem.css diff --git a/src/TodoItem.js b/src/TodoItem/index.js similarity index 79% rename from src/TodoItem.js rename to src/TodoItem/index.js index a5c01ccf4..0c7d0c4b1 100644 --- a/src/TodoItem.js +++ b/src/TodoItem/index.js @@ -1,5 +1,5 @@ -import { CompleteIcon } from './CompleteIcon' -import { DeleteIcon } from './deleteIcon' +import { CompleteIcon } from '../TodoIcon/CompleteIcon' +import { DeleteIcon } from '../TodoIcon/deleteIcon' import './TodoItem.css'; function TodoItem(props) { diff --git a/src/TodoList.css b/src/TodoList/TodoList.css similarity index 100% rename from src/TodoList.css rename to src/TodoList/TodoList.css diff --git a/src/TodoList.js b/src/TodoList/index.js similarity index 100% rename from src/TodoList.js rename to src/TodoList/index.js diff --git a/src/TodoSearch.css b/src/TodoSearch/TodoSearch.css similarity index 100% rename from src/TodoSearch.css rename to src/TodoSearch/TodoSearch.css diff --git a/src/TodoSearch.js b/src/TodoSearch/index.js similarity index 100% rename from src/TodoSearch.js rename to src/TodoSearch/index.js From f45801d7ce8437b97f77ce93e19506a834b295c7 Mon Sep 17 00:00:00 2001 From: leo tatico Date: Fri, 4 Aug 2023 12:12:50 -0500 Subject: [PATCH 04/13] nueva actualizacion --- src/App/AppUI.js | 68 ++++++++++++++++++------------- src/App/index.js | 61 +++------------------------ src/App/useLocalStore.js | 25 ------------ src/CreateButton/index.js | 26 ++++++------ src/EmptyTodos/index.js | 9 ++++ src/TodoContext/index.js | 63 ++++++++++++++++++++++++++++ src/TodoContext/useLocalStore.js | 63 ++++++++++++++++++++++++++++ src/TodosError/index.js | 10 +++++ src/TodosLoading/TodosLoading.css | 59 +++++++++++++++++++++++++++ src/TodosLoading/index.js | 15 +++++++ 10 files changed, 276 insertions(+), 123 deletions(-) delete mode 100644 src/App/useLocalStore.js create mode 100644 src/EmptyTodos/index.js create mode 100644 src/TodoContext/index.js create mode 100644 src/TodoContext/useLocalStore.js create mode 100644 src/TodosError/index.js create mode 100644 src/TodosLoading/TodosLoading.css create mode 100644 src/TodosLoading/index.js diff --git a/src/App/AppUI.js b/src/App/AppUI.js index 02db79696..c876124a0 100644 --- a/src/App/AppUI.js +++ b/src/App/AppUI.js @@ -2,39 +2,49 @@ import { TodoCounter } from '../TodoCounter'; import { TodoSearch } from '../TodoSearch'; import { TodoList } from '../TodoList'; import { TodoItem } from '../TodoItem'; -import { CreateTodoButton } from '../CreateTodoButton'; +import { TodosLoading } from '../TodosLoading'; +import { TodosError } from '../TodosError'; +import { EmptyTodos } from '../EmptyTodos'; +import { CreateTodoButton } from '../CreateButton'; +import { TodoContext } from '../TodoContext'; -function AppUI({ - completedTodos, - totalTodos, - searchValue, - setSearchValue, - searchedTodos, - completeTodo, - deleteTodo, -}) { +function AppUI() { return ( <> - - + + - - {searchedTodos.map(todo => ( - completeTodo(todo.text)} - onDelete={() => deleteTodo(todo.text)} - /> - ))} - + + {({ + loading, + error, + searchedTodos, + completeTodo, + deleteTodo, + }) => ( + + {loading && ( + <> + + + + + )} + {error && } + {(!loading && searchedTodos.length === 0) && } + + {searchedTodos.map(todo => ( + completeTodo(todo.text)} + onDelete={() => deleteTodo(todo.text)} + /> + ))} + + )} + diff --git a/src/App/index.js b/src/App/index.js index 2f65850f6..e067cfdce 100644 --- a/src/App/index.js +++ b/src/App/index.js @@ -1,64 +1,13 @@ import React from 'react'; +import { TodoProvider } from '../TodoContext'; import { AppUI } from './AppUI'; -import { useLocalStorage } from './useLocalStorage'; - -// const defaultTodos = [ -// { text: 'Cortar cebolla', completed: true }, -// { text: 'Tomar el Curso de Intro a React.js', completed: false }, -// { text: 'Llorar con la Llorona', completed: false }, -// { text: 'LALALALALA', completed: false }, -// { text: 'Usar estados derivados', completed: true }, -// ]; - -// localStorage.setItem('TODOS_V1', JSON.stringify(defaultTodos)); -// localStorage.removeItem('TODOS_V1'); function App() { - const [todos, saveTodos] = useLocalStorage('TODOS_V1', []); - const [searchValue, setSearchValue] = React.useState(''); - - const completedTodos = todos.filter( - todo => !!todo.completed - ).length; - const totalTodos = todos.length; - - const searchedTodos = todos.filter( - (todo) => { - const todoText = todo.text.toLowerCase(); - const searchText = searchValue.toLowerCase(); - return todoText.includes(searchText); - } - ); - - const completeTodo = (text) => { - const newTodos = [...todos]; - const todoIndex = newTodos.findIndex( - (todo) => todo.text === text - ); - newTodos[todoIndex].completed = true; - saveTodos(newTodos); - }; - - const deleteTodo = (text) => { - const newTodos = [...todos]; - const todoIndex = newTodos.findIndex( - (todo) => todo.text === text - ); - newTodos.splice(todoIndex, 1); - saveTodos(newTodos); - }; - return ( - + + + ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/App/useLocalStore.js b/src/App/useLocalStore.js deleted file mode 100644 index 6fbc3bad2..000000000 --- a/src/App/useLocalStore.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -function useLocalStorage(itemName, initialValue) { - const localStorageItem = localStorage.getItem(itemName); - - let parsedItem; - - if (!localStorageItem) { - localStorage.setItem(itemName, JSON.stringify(initialValue)); - parsedItem = initialValue; - } else { - parsedItem = JSON.parse(localStorageItem); - } - - const [item, setItem] = React.useState(parsedItem); - - const saveItem = (newItem) => { - localStorage.setItem(itemName, JSON.stringify(newItem)); - setItem(newItem); - }; - - return [item, saveItem]; -} - -export { useLocalStorage }; \ No newline at end of file diff --git a/src/CreateButton/index.js b/src/CreateButton/index.js index ffabaa3cf..deb6148a2 100644 --- a/src/CreateButton/index.js +++ b/src/CreateButton/index.js @@ -1,18 +1,18 @@ import './CreateButton.css'; function CreateTodoButton() { - return ( - - ); - } - + } + >+ + ); +} + export { CreateTodoButton }; \ No newline at end of file diff --git a/src/EmptyTodos/index.js b/src/EmptyTodos/index.js new file mode 100644 index 000000000..6b0604948 --- /dev/null +++ b/src/EmptyTodos/index.js @@ -0,0 +1,9 @@ +import React from 'react'; + +function EmptyTodos() { + return ( +

    ¡Crea tu primer TODO!

    + ); +} + +export { EmptyTodos }; \ No newline at end of file diff --git a/src/TodoContext/index.js b/src/TodoContext/index.js new file mode 100644 index 000000000..526e6b250 --- /dev/null +++ b/src/TodoContext/index.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { useLocalStorage } from './useLocalStore'; + +const TodoContext = React.createContext(); + +function TodoProvider({ children }) { + const { + item: todos, + saveItem: saveTodos, + loading, + error, + } = useLocalStorage('TODOS_V1', []); + const [searchValue, setSearchValue] = React.useState(''); + + const completedTodos = todos.filter( + todo => !!todo.completed + ).length; + const totalTodos = todos.length; + + const searchedTodos = todos.filter( + (todo) => { + const todoText = todo.text.toLowerCase(); + const searchText = searchValue.toLowerCase(); + return todoText.includes(searchText); + } + ); + + const completeTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text === text + ); + newTodos[todoIndex].completed = true; + saveTodos(newTodos); + }; + + const deleteTodo = (text) => { + const newTodos = [...todos]; + const todoIndex = newTodos.findIndex( + (todo) => todo.text === text + ); + newTodos.splice(todoIndex, 1); + saveTodos(newTodos); + }; + + return ( + + {children} + + ); +} + +export { TodoContext, TodoProvider }; diff --git a/src/TodoContext/useLocalStore.js b/src/TodoContext/useLocalStore.js new file mode 100644 index 000000000..f22cf9ce7 --- /dev/null +++ b/src/TodoContext/useLocalStore.js @@ -0,0 +1,63 @@ +import React from 'react'; + +function useLocalStorage(itemName, initialValue) { + const [item, setItem] = React.useState(initialValue); + const [loading, setLoading] = React.useState(true); + const [error, setErorr] = React.useState(false); + + + + + React.useEffect(() => { + setTimeout(() => { + try{ + const localStorageItem = localStorage.getItem(itemName); + + let parsedItem + + if (!localStorageItem) { + localStorage.setItem(itemName, JSON.stringify(initialValue)); + parsedItem = initialValue; + } else { + parsedItem = JSON.parse(localStorageItem); + setItem(parsedItem); + } + + setLoading(false); + } catch(error){ + setLoading(false) + setError(true) + } + }, 2000); + }, []); + + + + + const saveItem = (newItem) => { + localStorage.setItem(itemName, JSON.stringify(newItem)); + setItem(newItem); + }; + + return { + item, + saveItem, + loading, + error, + }; // se retorna un objeto porque en los hooks los arrays funcionan distinto, + // para cuando se llame el custom hooks desde cualquier componente ya sea mas facil saber cual es cual. +} + +export { useLocalStorage }; + + +// const defaultTodos = [ +// { text: 'Cortar cebolla', completed: true }, +// { text: 'Tomar el Curso de Intro a React.js', completed: false }, +// { text: 'Llorar con la Llorona', completed: false }, +// { text: 'LALALALALA', completed: false }, +// { text: 'Usar estados derivados', completed: true }, +// ]; + +// localStorage.setItem('TODOS_V1', JSON.stringify(defaultTodos)); +// localStorage.removeItem('TODOS_V1'); \ No newline at end of file diff --git a/src/TodosError/index.js b/src/TodosError/index.js new file mode 100644 index 000000000..753e7d61b --- /dev/null +++ b/src/TodosError/index.js @@ -0,0 +1,10 @@ +import React from 'react'; + + +function TodosError() { + return( +

    Error...

    + ) +} + +export { TodosError }; \ No newline at end of file diff --git a/src/TodosLoading/TodosLoading.css b/src/TodosLoading/TodosLoading.css new file mode 100644 index 000000000..7870c96de --- /dev/null +++ b/src/TodosLoading/TodosLoading.css @@ -0,0 +1,59 @@ +.LoadingTodo-container { + position: relative; + display: flex; + justify-content: center; + align-items: center; + margin-top: 24px; + box-shadow: 0px 5px 50px rgba(30, 69, 146, 0.15); + border-radius: 10px; + padding: 12px 0; +} + +.LoadingTodo-text { + margin: 24px 0 24px 24px; + width: calc(100% - 120px); + font-size: 18px; + line-height: 24px; + font-weight: 400; +} + +.LoadingTodo-completeIcon, +.LoadingTodo-deleteIcon { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50px; + height: 48px; + width: 48px; +} +.LoadingTodo-completeIcon { + position: absolute; + left: 12px; +} +.LoadingTodo-deleteIcon { + position: absolute; + top: -24px; + right: 0; +} + + +.LoadingTodo-container, +.LoadingTodo-completeIcon, +.LoadingTodo-deleteIcon { + background: linear-gradient(90deg, rgba(250,250,250,1), rgb(87, 84, 84)); + background-size: 400% 400%; + animation: loadingAnimation 3s ease-in-out infinite; +} + +@keyframes loadingAnimation { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} \ No newline at end of file diff --git a/src/TodosLoading/index.js b/src/TodosLoading/index.js new file mode 100644 index 000000000..eea53cb9c --- /dev/null +++ b/src/TodosLoading/index.js @@ -0,0 +1,15 @@ +import React from 'react'; +import './TodosLoading.css'; + + +function TodosLoading() { + return ( +
    + +

    + +
    + ); +} + +export { TodosLoading }; \ No newline at end of file From 4d85589795bf84a876cc634d74efcd4696fc9474 Mon Sep 17 00:00:00 2001 From: leo tatico Date: Tue, 8 Aug 2023 11:50:09 -0500 Subject: [PATCH 05/13] teletransportacion --- public/index.html | 1 + src/App/AppUI.js | 70 +++++++++++++++++++++++----------------- src/TodoContext/index.js | 3 ++ src/TodoCounter/index.js | 10 ++++-- src/TodoSearch/index.js | 12 +++---- src/modal/index.js | 15 +++++++++ 6 files changed, 73 insertions(+), 38 deletions(-) create mode 100644 src/modal/index.js diff --git a/public/index.html b/public/index.html index a040f9fc0..e0b392900 100644 --- a/public/index.html +++ b/public/index.html @@ -30,6 +30,7 @@
    +