Skip to content

Commit

Permalink
solution
Browse files Browse the repository at this point in the history
  • Loading branch information
pasha1932 committed Oct 12, 2024
1 parent f1536f7 commit 52d1ac1
Show file tree
Hide file tree
Showing 13 changed files with 419 additions and 153 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
},
"devDependencies": {
"@cypress/react18": "^2.0.1",
"@mate-academy/scripts": "^1.8.5",
"@mate-academy/scripts": "^1.9.12",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/node": "^20.14.10",
Expand Down
157 changes: 13 additions & 144 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,156 +1,25 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react';
/* eslint-disable jsx-a11y/label-has-associated-control */
import React, { useContext } from 'react';
import { Header } from './components/Header/Header';
import { Footer } from './components/Footer/Footer';
import { TodoList } from './components/TodoList/TodoList';
import { TodosContext } from './components/TodoAppContext/TodoAppContext';

export const App: React.FC = () => {
const { todos } = useContext(TodosContext);

// console.log(todos.length);

return (
<div className="todoapp">
<h1 className="todoapp__title">todos</h1>

<div className="todoapp__content">
<header className="todoapp__header">
{/* this button should have `active` class only if all todos are completed */}
<button
type="button"
className="todoapp__toggle-all active"
data-cy="ToggleAllButton"
/>

{/* Add a todo on form submit */}
<form>
<input
data-cy="NewTodoField"
type="text"
className="todoapp__new-todo"
placeholder="What needs to be done?"
/>
</form>
</header>

<section className="todoapp__main" data-cy="TodoList">
{/* This is a completed todo */}
<div data-cy="Todo" className="todo completed">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
checked
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Completed Todo
</span>

{/* Remove button appears only on hover */}
<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>

{/* This todo is an active todo */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Not Completed Todo
</span>

<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>

{/* This todo is being edited */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

{/* This form is shown instead of the title and remove button */}
<form>
<input
data-cy="TodoTitleField"
type="text"
className="todo__title-field"
placeholder="Empty todo will be deleted"
value="Todo is being edited now"
/>
</form>
</div>

{/* This todo is in loadind state */}
<div data-cy="Todo" className="todo">
<label className="todo__status-label">
<input
data-cy="TodoStatus"
type="checkbox"
className="todo__status"
/>
</label>

<span data-cy="TodoTitle" className="todo__title">
Todo is being saved now
</span>

<button type="button" className="todo__remove" data-cy="TodoDelete">
×
</button>
</div>
</section>

{/* Hide the footer if there are no todos */}
<footer className="todoapp__footer" data-cy="Footer">
<span className="todo-count" data-cy="TodosCounter">
3 items left
</span>

{/* Active link should have the 'selected' class */}
<nav className="filter" data-cy="Filter">
<a
href="#/"
className="filter__link selected"
data-cy="FilterLinkAll"
>
All
</a>

<a
href="#/active"
className="filter__link"
data-cy="FilterLinkActive"
>
Active
</a>
<Header />

<a
href="#/completed"
className="filter__link"
data-cy="FilterLinkCompleted"
>
Completed
</a>
</nav>
<TodoList />

{/* this button should be disabled if there are no completed todos */}
<button
type="button"
className="todoapp__clear-completed"
data-cy="ClearCompletedButton"
>
Clear completed
</button>
</footer>
{todos.length !== 0 && <Footer />}
</div>
</div>
);
Expand Down
74 changes: 74 additions & 0 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useContext } from 'react';
import {
TodosContext,
TodosDispatchContext,
} from '../TodoAppContext/TodoAppContext';
import classNames from 'classnames';
import { TodoStatus } from '../../types/TodoStatus';

export const Footer: React.FC = () => {
const { todos, filterStatus } = useContext(TodosContext);
const dispatch = useContext(TodosDispatchContext);
const uncomletedTodos = todos.filter(todo => !todo.completed);

return (
<footer className="todoapp__footer" data-cy="Footer">
<span className="todo-count" data-cy="TodosCounter">
{uncomletedTodos.length + ' items left'}
</span>

{/* Active link should have the 'selected' class */}
<nav className="filter" data-cy="Filter">
<a
href="#/"
className={classNames('filter__link', {
selected: filterStatus === TodoStatus.All,
})}
data-cy="FilterLinkAll"
onClick={() =>
dispatch({ type: 'filterBy', payload: TodoStatus.All })
}
>
All
</a>

<a
href="#/active"
className={classNames('filter__link', {
selected: filterStatus === TodoStatus.Active,
})}
data-cy="FilterLinkActive"
onClick={() =>
dispatch({ type: 'filterBy', payload: TodoStatus.Active })
}
>
Active
</a>

<a
href="#/completed"
className={classNames('filter__link', {
selected: filterStatus === TodoStatus.Completed,
})}
data-cy="FilterLinkCompleted"
onClick={() =>
dispatch({ type: 'filterBy', payload: TodoStatus.Completed })
}
>
Completed
</a>
</nav>

{/* this button should be disabled if there are no completed todos */}
<button
type="button"
className="todoapp__clear-completed"
data-cy="ClearCompletedButton"
disabled={todos.length === uncomletedTodos.length}
onClick={() => dispatch({ type: 'deleteCompletedTodos' })}
>
Clear completed
</button>
</footer>
);
};
73 changes: 73 additions & 0 deletions src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useContext, useEffect, useRef, useState } from 'react';
import {
TodosContext,
TodosDispatchContext,
} from '../TodoAppContext/TodoAppContext';
import classNames from 'classnames';
import { Todo } from '../../types/Todo';

export const Header: React.FC = () => {
const [title, setTitle] = useState('');
const dispatch = useContext(TodosDispatchContext);
const { todos } = useContext(TodosContext);
const [isSubmitting, setIsSubmitting] = useState(false);
const completedTodosLength = todos.filter(el => el.completed).length;
const field = useRef<HTMLInputElement>(null);

useEffect(() => {
field.current?.focus();
}, [isSubmitting, todos]);

const handleAdd = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);

if (!title.trim()) {
return;
}

const newTodo: Todo = {
id: +new Date(),
title: title.trim(),
completed: false,
};

dispatch({
type: 'addTodo',
payload: newTodo,
});
setTitle('');
setIsSubmitting(false);
};

return (
<header className="todoapp__header">
{/* this button should have `active` class only if all todos are completed */}
{todos.length !== 0 && (
<button
type="button"
className={classNames('todoapp__toggle-all', {
active: todos.length === completedTodosLength,
})}
data-cy="ToggleAllButton"
onClick={() => {
dispatch({ type: 'toggleAllStatuses' });
}}
/>
)}

{/* Add a todo on form submit */}
<form onSubmit={handleAdd}>
<input
data-cy="NewTodoField"
type="text"
className="todoapp__new-todo"
placeholder="What needs to be done?"
ref={field}
value={title}
onChange={e => setTitle(e.target.value)}
/>
</form>
</header>
);
};
Loading

0 comments on commit 52d1ac1

Please sign in to comment.