/
step2.tsx
76 lines (67 loc) · 2.11 KB
/
step2.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { useState } from "react";
/** リスト表示の対象となる、個々のToDoを表す型。*/
export type TodoItem = {
/** 表示や操作の対象を識別するために利用する、全ての`TodoItem`の中で一意な値。 */
id: number;
/** ToDoの内容となる文字列。 */
text: string;
/** 完了すると`true`となる。 */
done: boolean;
};
type TodoListItemProps = {
item: TodoItem;
};
/** ToDoリストの個々のToDoとなるReactコンポーネント。 */
function TodoListItem({ item }: TodoListItemProps) {
return (
<div className="TodoItem">
<p style={{ textDecoration: item.done ? "line-through" : "none" }}>
{item.text}
</p>
</div>
);
}
type ValueViewerProps = {
value: any;
};
/** `value`の内容を`JSON.stringify`して表示する、動作確認用コンポーネント。 */
function ValueViewer({ value }: ValueViewerProps) {
return (
<pre className="ValueViewer">{JSON.stringify(value, undefined, 2)}</pre>
);
}
/** ToDoリストの初期値。 */
const INITIAL_TODO: TodoItem[] = [
{ id: 1, text: "todo-item-1", done: false },
{ id: 2, text: "todo-item-2", done: true },
];
/** アプリケーション本体となるReactコンポーネント。 */
export default function App() {
const todoItems = INITIAL_TODO;
const [keyword, setKeyword] = useState("");
const filteredTodoItems = todoItems.filter((item) => {
return item.text.includes(keyword);
});
return (
<div className="App">
<h1>ToDo</h1>
<div className="App_todo-list-control">
<input
placeholder="キーワードフィルタ"
value={keyword}
onChange={(ev) => setKeyword(ev.target.value)}
/>
</div>
{filteredTodoItems.length === 0 ? (
<div className="dimmed">該当するToDoはありません</div>
) : (
<div className="App_todo-list">
{filteredTodoItems.map((item) => (
<TodoListItem key={item.id} item={item} />
))}
</div>
)}
<ValueViewer value={{ keyword, todoItems, filteredTodoItems }} />
</div>
);
}