feat: Add ability to hide completed tasks (#1210)

This commit is contained in:
Symon Baikov
2025-07-21 18:33:02 +02:00
committed by GitHub
parent fc9c94b3b6
commit d8fbf2f909
17 changed files with 158 additions and 55 deletions
@@ -16,12 +16,19 @@ import Task from './Task';
import styles from './TaskList.module.scss';
const TaskList = React.memo(({ id }) => {
const selectTaskListById = useMemo(() => selectors.makeSelectTaskListById(), []);
const selectTasksByTaskListId = useMemo(() => selectors.makeSelectTasksByTaskListId(), []);
const taskLists = useSelector((state) => selectTaskListById(state, id));
const tasks = useSelector((state) => selectTasksByTaskListId(state, id));
const [isOpened, toggleOpened] = useToggle();
const filteredTasks = useMemo(
() => (taskLists.hideCompletedTasks ? tasks.filter((task) => !task.isCompleted) : tasks),
[taskLists.hideCompletedTasks, tasks],
);
// TODO: move to selector?
const completedTasksTotal = useMemo(
() => tasks.reduce((result, task) => (task.isCompleted ? result + 1 : result), 0),
@@ -30,10 +37,14 @@ const TaskList = React.memo(({ id }) => {
const handleToggleClick = useCallback(
(event) => {
if (filteredTasks.length === 0) {
return;
}
event.stopPropagation();
toggleOpened();
},
[toggleOpened],
[toggleOpened, filteredTasks.length],
);
if (tasks.length === 0) {
@@ -44,7 +55,7 @@ const TaskList = React.memo(({ id }) => {
<>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,
jsx-a11y/no-static-element-interactions */}
<div className={styles.button} onClick={handleToggleClick}>
<div className={styles.progressRow} onClick={handleToggleClick}>
<span className={styles.progressWrapper}>
<Progress
autoSuccess
@@ -56,14 +67,18 @@ const TaskList = React.memo(({ id }) => {
/>
</span>
<span
className={classNames(styles.count, isOpened ? styles.countOpened : styles.countClosed)}
className={classNames(
styles.count,
filteredTasks.length > 0 && styles.countOpenable,
filteredTasks.length > 0 && (isOpened ? styles.countOpened : styles.countClosed),
)}
>
{completedTasksTotal}/{tasks.length}
</span>
</div>
{isOpened && (
{isOpened && filteredTasks.length > 0 && (
<ul className={styles.tasks}>
{tasks.map((task) => (
{filteredTasks.map((task) => (
<Task key={task.id} id={task.id} />
))}
</ul>
@@ -4,29 +4,21 @@
*/
:global(#app) {
.button {
background: transparent;
border: none;
line-height: 0;
margin: 0 -8px;
outline: none;
padding: 0px 8px 8px;
width: calc(100% + 16px);
}
.count {
color: #888;
display: inline-block;
font-size: 12px;
line-height: 12px;
text-align: right;
vertical-align: top;
width: 50px;
&:after {
content: "";
opacity: 0.4;
}
}
.countOpenable {
&:after {
margin-left: 2px;
}
&:hover {
opacity: 0.75;
@@ -35,13 +27,11 @@
.countOpened:after {
background: url("data:image/gif;base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIPnI+py+0/hJzz0IruwjsVADs=") no-repeat center right;
margin-left: 2px;
padding: 6px 6px 0px;
}
.countClosed:after {
background: url("data:image/gif;base64,R0lGODlhCwALAJEAAAAAAP///xUVFf///yH5BAEAAAMALAAAAAALAAsAAAIRnC2nKLnT4or00Puy3rx7VQAAOw==") no-repeat center right;
margin-left: 2px;
padding: 0 6px 6px;
}
@@ -49,11 +39,17 @@
margin: 0;
}
.progressRow {
display: flex;
gap: 8px;
justify-content: space-between;
margin: 0 -8px;
padding: 0px 8px 8px;
}
.progressWrapper {
display: inline-block;
padding: 3px 0;
vertical-align: top;
width: calc(100% - 50px);
width: 100%;
}
.tasks {