App.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { useState, useRef } from 'react'
  2. import './App.css'
  3. interface Todo {
  4. id: number,
  5. text: string,
  6. completed: boolean
  7. }
  8. type Filter = 'all' | 'active' | 'completed';
  9. function filterTodos(todos: Todo[], filter: Filter): Todo[] {
  10. switch (filter) {
  11. case 'all':
  12. return todos;
  13. case 'active':
  14. return todos.filter(todo => !todo.completed);
  15. case 'completed':
  16. return todos.filter(todo => todo.completed);
  17. default:
  18. return todos;
  19. }
  20. }
  21. function Header({ addTodo }: { addTodo: (text: string) => void }) {
  22. const inpRef = useRef<HTMLInputElement>(null);
  23. const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  24. if (e.keyCode === 13) {
  25. addTodo(inpRef.current!.value);
  26. inpRef.current!.value = ""
  27. }
  28. }
  29. return (
  30. <div>
  31. <header className="header">
  32. <h1>todos</h1>
  33. <input
  34. ref={inpRef}
  35. autoFocus
  36. autoComplete="off"
  37. placeholder="输入您要完成的任务?"
  38. className="new-todo"
  39. onKeyDown={handleKeyDown}
  40. />
  41. </header>
  42. </div>
  43. )
  44. }
  45. function Main({ onToggleAll, allCompleted }: { onToggleAll: (val: boolean) => void, allCompleted: boolean }) {
  46. const handleToggle = () => {
  47. allCompleted = !allCompleted;
  48. onToggleAll(allCompleted)
  49. }
  50. return (
  51. <div>
  52. <input id="toggle-all" type="checkbox" className="toggle-all" onChange={handleToggle} checked={allCompleted} />
  53. <label htmlFor="toggle-all"></label>
  54. </div>
  55. )
  56. }
  57. function TodoItem({ todo, onDelete, onActive }: { todo: Todo, onDelete: (id: number) => void, onActive: (id: number) => void }) {
  58. const deleteTodo = (val: number) => {
  59. onDelete(val)
  60. }
  61. const activeTodo = (val: number) => {
  62. onActive(val)
  63. }
  64. return (
  65. <li className={todo.completed ? 'completed' : ''}>
  66. <div className="view">
  67. <input type="checkbox" className="toggle" checked={todo.completed} onChange={() => activeTodo(todo.id)} />
  68. <label>{todo.text}</label>
  69. <button className="destroy" onClick={() => deleteTodo(todo.id)}></button>
  70. </div>
  71. <input type="text" className="edit" />
  72. </li>
  73. )
  74. }
  75. function Footer({ count, clearAll, filter, tabFilter }: { count: number, clearAll: () => void, filter: Filter, tabFilter: (val: string) => void }) {
  76. console.log(filter, 'filter')
  77. return (
  78. <div>
  79. <span className="todo-count"><strong>{count}</strong> items left </span>
  80. <ul className="filters">
  81. <li><a href="#/all" onClick={() => tabFilter('all')} className={filter == 'all' ? 'selected' : ''}>All</a></li>
  82. <li><a href="#/active" onClick={() => tabFilter('active')} className={filter === 'active' ? 'selected' : ''}>Active</a></li>
  83. <li><a href="#/completed" onClick={() => tabFilter('completed')} className={filter === 'completed' ? 'selected' : ''}>Completed</a></li>
  84. </ul>
  85. <button className="clear-completed" onClick={clearAll}>Clear completed</button>
  86. </div>
  87. )
  88. }
  89. function App() {
  90. const [todos, setTodos] = useState<Todo[]>([
  91. {
  92. id: 1, text: '吃饭', completed: false
  93. },
  94. {
  95. id: 2, text: '睡觉', completed: false
  96. },
  97. {
  98. id: 3, text: '打豆豆', completed: true
  99. }
  100. ])
  101. const completeCount = todos.filter((item) => !item.completed).length;
  102. const isCompleted = todos.every((item) => item.completed);
  103. const [isFilter, setIsFilter] = useState<Filter>('all');
  104. const newArr = filterTodos(todos, isFilter);
  105. const handleAdd = (val: string) => {
  106. setTodos([...todos, {
  107. id: Date.now(),
  108. text: val,
  109. completed: false
  110. }])
  111. }
  112. const handleDelete = (val: number) => {
  113. setTodos((prevTodos) => { return prevTodos.filter((todo) => todo.id !== val) })
  114. }
  115. const handleAvtive = (val: number) => {
  116. setTodos((prevTodos) => prevTodos.map((todo) => {
  117. return todo.id === val ? { ...todo, completed: !todo.completed } : todo
  118. }))
  119. }
  120. const handleAll = (val: boolean) => {
  121. setTodos((prev) => prev.map((todo) => {
  122. return { ...todo, completed: val }
  123. }))
  124. }
  125. const handleClear = () => {
  126. setTodos([...todos.filter((todo) => !todo.completed)])
  127. }
  128. const handleSet = (val: Filter) => {
  129. setIsFilter(val)
  130. }
  131. return (
  132. <>
  133. <div id="app">
  134. <section className="todoapp">
  135. <Header addTodo={handleAdd}></Header>
  136. <section className="main">
  137. <Main onToggleAll={handleAll} allCompleted={isCompleted}></Main>
  138. <ul className="todo-list">
  139. {
  140. newArr.map((todo) => {
  141. return (
  142. <TodoItem key={todo.id} todo={todo} onDelete={handleDelete} onActive={handleAvtive}></TodoItem>
  143. )
  144. })
  145. }
  146. </ul>
  147. </section>
  148. <footer className="footer">
  149. <Footer count={completeCount} clearAll={handleClear} filter={isFilter} tabFilter={handleSet}></Footer>
  150. </footer>
  151. </section>
  152. </div>
  153. </>
  154. )
  155. }
  156. export default App