|
1 | 1 | import {useState} from 'react' |
2 | 2 | import {TaskPreview} from './TaskPreview' |
3 | | -import {Task, TaskFilter, makeTask} from './task' |
| 3 | +import {makeTask, type Task, type TaskFilter} from './task' |
4 | 4 |
|
5 | 5 | const initialTasks = Array.from({length: 50}, makeTask) |
6 | 6 | const initialState: {tasks: Task[]; filters: readonly TaskFilter[]} = { |
7 | | - tasks: initialTasks, |
8 | | - filters: [{field: 'content', operator: 'like', value: 'male'}], |
| 7 | + tasks: initialTasks, |
| 8 | + filters: [{field: 'content', operator: 'like', value: 'male'}], |
9 | 9 | } |
10 | 10 |
|
11 | 11 | const makeTaskPredicate = (filters: readonly TaskFilter[]) => { |
12 | | - const taskPredicate = (task: Task) => { |
13 | | - const matches = filters.map((filter) => { |
14 | | - if (filter.operator == 'any_of') { |
15 | | - return filter.value.some((match) => task[filter.field].includes(match)) |
16 | | - } |
| 12 | + const taskPredicate = (task: Task) => { |
| 13 | + const matches = filters.map((filter) => { |
| 14 | + if (filter.operator === 'any_of') { |
| 15 | + return filter.value.some((match) => task[filter.field].includes(match)) |
| 16 | + } |
17 | 17 |
|
18 | | - if (filter.operator == 'none_of') { |
19 | | - return !filter.value.some((match) => task[filter.field].includes(match)) |
20 | | - } |
| 18 | + if (filter.operator === 'none_of') { |
| 19 | + return !filter.value.some((match) => task[filter.field].includes(match)) |
| 20 | + } |
21 | 21 |
|
22 | | - if (filter.operator == 'like') { |
23 | | - return task[filter.field].includes(filter.value) |
24 | | - } |
| 22 | + if (filter.operator === 'like') { |
| 23 | + return task[filter.field].includes(filter.value) |
| 24 | + } |
25 | 25 |
|
26 | | - if (filter.operator == 'unlike') { |
27 | | - return !task[filter.field].includes(filter.value) |
28 | | - } |
| 26 | + if (filter.operator === 'unlike') { |
| 27 | + return !task[filter.field].includes(filter.value) |
| 28 | + } |
29 | 29 |
|
30 | | - return true |
31 | | - }) |
| 30 | + return true |
| 31 | + }) |
32 | 32 |
|
33 | | - return matches.every(Boolean) |
34 | | - } |
35 | | - return taskPredicate |
| 33 | + return matches.every(Boolean) |
| 34 | + } |
| 35 | + return taskPredicate |
36 | 36 | } |
37 | 37 |
|
38 | 38 | export const App = () => { |
39 | | - const [state, setState] = useState(initialState) |
40 | | - const tasks = state.tasks.filter(makeTaskPredicate(state.filters)) |
41 | | - return ( |
42 | | - <div |
43 | | - style={{ |
44 | | - maxWidth: '1024px', |
45 | | - marginInline: 'auto', |
46 | | - display: 'flex', |
47 | | - flexDirection: 'column', |
48 | | - gap: '0.5em', |
49 | | - }} |
50 | | - > |
51 | | - <h1>Tasks</h1> |
52 | | - <div> |
53 | | - <button>add filter</button> |
| 39 | + const [state, setState] = useState(initialState) |
| 40 | + const tasks = state.tasks.filter(makeTaskPredicate(state.filters)) |
| 41 | + return ( |
| 42 | + <div |
| 43 | + style={{ |
| 44 | + maxWidth: '1024px', |
| 45 | + marginInline: 'auto', |
| 46 | + display: 'flex', |
| 47 | + flexDirection: 'column', |
| 48 | + gap: '0.5em', |
| 49 | + }} |
| 50 | + > |
| 51 | + <h1>Tasks</h1> |
| 52 | + <div> |
| 53 | + <button type="button">add filter</button> |
54 | 54 |
|
55 | | - <ul> |
56 | | - {state.filters.map((filter, i) => { |
57 | | - return ( |
58 | | - <li key={`${filter.field}_${i}`}> |
59 | | - <pre>{JSON.stringify(filter, null, 2)}</pre> |
60 | | - </li> |
61 | | - ) |
62 | | - })} |
63 | | - </ul> |
64 | | - </div> |
65 | | - <ul style={{display: 'flex', flexDirection: 'column', gap: '0.25em'}}> |
66 | | - {tasks.map((task) => ( |
67 | | - <li key={task.id}> |
68 | | - <TaskPreview |
69 | | - task={task} |
70 | | - onChange={(task) => { |
71 | | - setState({ |
72 | | - ...state, |
73 | | - tasks: state.tasks.map((t) => (t.id == task.id ? task : t)), |
74 | | - }) |
75 | | - }} |
76 | | - onRemove={() => { |
77 | | - setState({ |
78 | | - ...state, |
79 | | - tasks: state.tasks.filter((t) => t.id != task.id), |
80 | | - }) |
81 | | - }} |
82 | | - /> |
83 | | - </li> |
84 | | - ))} |
85 | | - </ul> |
86 | | - </div> |
87 | | - ) |
| 55 | + <ul> |
| 56 | + {state.filters.map((filter, i) => { |
| 57 | + return ( |
| 58 | + <li key={`${filter.field}_${i}`}> |
| 59 | + <pre>{JSON.stringify(filter, null, 2)}</pre> |
| 60 | + </li> |
| 61 | + ) |
| 62 | + })} |
| 63 | + </ul> |
| 64 | + </div> |
| 65 | + <ul style={{display: 'flex', flexDirection: 'column', gap: '0.25em'}}> |
| 66 | + {tasks.map((task) => ( |
| 67 | + <li key={task.id}> |
| 68 | + <TaskPreview |
| 69 | + task={task} |
| 70 | + onChange={(task) => { |
| 71 | + setState({ |
| 72 | + ...state, |
| 73 | + tasks: state.tasks.map((t) => (t.id === task.id ? task : t)), |
| 74 | + }) |
| 75 | + }} |
| 76 | + onRemove={() => { |
| 77 | + setState({ |
| 78 | + ...state, |
| 79 | + tasks: state.tasks.filter((t) => t.id !== task.id), |
| 80 | + }) |
| 81 | + }} |
| 82 | + /> |
| 83 | + </li> |
| 84 | + ))} |
| 85 | + </ul> |
| 86 | + </div> |
| 87 | + ) |
88 | 88 | } |
0 commit comments