forked from semi-23e/nextjs-todo-tutorial
77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import TodoItem from './TodoItem';
|
|
|
|
type Todo = {
|
|
id: number;
|
|
text: string;
|
|
completed: boolean;
|
|
};
|
|
|
|
type FilterType = 'all' | 'active' | 'completed';
|
|
|
|
type TodoListProps = {
|
|
todos: Todo[];
|
|
filter: FilterType;
|
|
editingId: number | null;
|
|
editText: string;
|
|
onToggleTodo: (id: number) => void;
|
|
onStartEdit: (todo: Todo) => void;
|
|
onSaveEdit: () => void;
|
|
onCancelEdit: () => void;
|
|
onDeleteClick: (id: number) => void;
|
|
onEditTextChange: (text: string) => void;
|
|
};
|
|
|
|
const TodoList = React.memo(function TodoList({
|
|
todos,
|
|
filter,
|
|
editingId,
|
|
editText,
|
|
onToggleTodo,
|
|
onStartEdit,
|
|
onSaveEdit,
|
|
onCancelEdit,
|
|
onDeleteClick,
|
|
onEditTextChange,
|
|
}: TodoListProps) {
|
|
// フィルタリング処理をメモ化
|
|
const filteredTodos = useMemo(() => {
|
|
switch (filter) {
|
|
case 'active':
|
|
return todos.filter(todo => !todo.completed);
|
|
case 'completed':
|
|
return todos.filter(todo => todo.completed);
|
|
default:
|
|
return todos;
|
|
}
|
|
}, [todos, filter]);
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{filteredTodos.length === 0 ? (
|
|
<p className="text-gray-500 text-center py-8">
|
|
{filter === 'active' && 'すべて完了しました!'}
|
|
{filter === 'completed' && '完了したTODOがありません'}
|
|
{filter === 'all' && 'TODOがありません'}
|
|
</p>
|
|
) : (
|
|
filteredTodos.map((todo) => (
|
|
<TodoItem
|
|
key={todo.id}
|
|
todo={todo}
|
|
isEditing={editingId === todo.id}
|
|
editText={editText}
|
|
onToggle={() => onToggleTodo(todo.id)}
|
|
onStartEdit={() => onStartEdit(todo)}
|
|
onSaveEdit={onSaveEdit}
|
|
onCancelEdit={onCancelEdit}
|
|
onDelete={() => onDeleteClick(todo.id)}
|
|
onEditTextChange={onEditTextChange}
|
|
/>
|
|
))
|
|
)}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export default TodoList; |