|
1 | | -import React, { useCallback } from 'react'; |
| 1 | +import React, { useCallback, useRef } from 'react'; |
2 | 2 |
|
3 | | -const TodoItem = ({ todo, id, onRemoveTodo, onToggleTodoDone, onEditTodo, isDone }) => { |
4 | | - const removeTodoHandler = useCallback(() => onRemoveTodo(id), [id, onRemoveTodo]) |
| 3 | +const TodoItem = ({ todo, id, onRemoveTodo, onToggleTodoDone, onEditTodo, isDone, setCustomError }) => { |
| 4 | + const removeTodoHandler = useCallback(() => onRemoveTodo(id), [id, onRemoveTodo]); |
| 5 | + |
| 6 | + const toggleTodoDoneHandler = useCallback(() => onToggleTodoDone(id), [id, onToggleTodoDone]); |
5 | 7 |
|
6 | | - const toggleTodoDoneHandler = useCallback(() => onToggleTodoDone(id), [id, onToggleTodoDone]) |
7 | 8 | const editTodoHandler = useCallback(event => { |
8 | 9 | if (event.keyCode === 13) { // Detect ENTER key down |
9 | 10 | event.preventDefault(); // Prevent adding a new line because it's supposed to be single line |
| 11 | + |
| 12 | + const { value } = event.target; |
| 13 | + |
| 14 | + if (value.length < 3) { |
| 15 | + setCustomError('Todo text is too short.'); |
| 16 | + |
| 17 | + return; |
| 18 | + } |
| 19 | + |
| 20 | + if (value.length > 20) { |
| 21 | + setCustomError('Todo text is too long.'); |
| 22 | + |
| 23 | + return; |
| 24 | + } |
| 25 | + |
10 | 26 | onEditTodo(id, event.target.value); |
| 27 | + |
| 28 | + setCustomError(null) // Reset customError |
| 29 | + |
11 | 30 | event.target.blur(); // Make the current input lost focus after finishing onEditTodo |
12 | 31 | } |
13 | | - }, [id, onEditTodo]) |
| 32 | + }, [id, onEditTodo, setCustomError]); |
| 33 | + |
| 34 | + const checkboxRef = useRef(null); |
14 | 35 |
|
15 | 36 | return ( |
16 | 37 | <li> |
17 | 38 | <input |
| 39 | + type="checkbox" |
| 40 | + ref={checkboxRef} |
| 41 | + checked={!!isDone} |
| 42 | + onChange={toggleTodoDoneHandler} |
| 43 | + /> |
| 44 | + <span onClick={() => checkboxRef.current.click()} /> |
| 45 | + <input |
| 46 | + type="text" |
18 | 47 | defaultValue={todo} // innerHTML of the editable div |
19 | | - disabled={false} // use true to disable editing |
20 | 48 | onKeyDown={editTodoHandler} // handle innerHTML change |
21 | | - tagName='span' // Use a custom HTML tag (uses a div by default) |
22 | 49 | style={{textDecoration: isDone ? 'line-through' : 'none'}} |
23 | 50 | /> |
24 | | - <button onClick={removeTodoHandler}>Remove</button> |
25 | | - <button onClick={toggleTodoDoneHandler}>{`Mark as ${isDone ? 'pending': 'done'}`}</button> |
| 51 | + <button onClick={removeTodoHandler}> |
| 52 | + <span role="img" aria-labelledby="trash" />🗑️ |
| 53 | + </button> |
26 | 54 | </li> |
27 | 55 | ) |
28 | 56 | }; |
|
0 commit comments