implement components

This commit is contained in:
bracesproul
2025-03-07 10:47:08 -08:00
parent 066b219107
commit 7ebcbb3a28
19 changed files with 472 additions and 37 deletions

View File

@@ -0,0 +1,5 @@
```bash
npx create-react-app todo-app --template typescript
cd todo-app
mkdir -p src/{components,styles,utils}
```

View File

@@ -0,0 +1,21 @@
```tsx
// src/components/TodoItem.tsx
import React from 'react';
import styles from '../styles/TodoItem.module.css';
interface TodoItemProps {
id: string;
text: string;
completed: boolean;
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
export const TodoItem: React.FC<TodoItemProps> = ({ id, text, completed, onToggle, onDelete }) => (
<div className={styles.todoItem}>
<input type='checkbox' checked={completed} onChange={() => onToggle(id)} />
<span className={completed ? styles.completed : ''}>{text}</span>
<button onClick={() => onDelete(id)}>Delete</button>
</div>
);
```

View File

@@ -0,0 +1,22 @@
```tsx
// src/context/TodoContext.tsx
import React, { createContext, useContext, useReducer } from 'react';
type Todo = { id: string; text: string; completed: boolean; };
type TodoState = { todos: Todo[]; };
type TodoAction =
| { type: 'ADD_TODO'; payload: string }
| { type: 'TOGGLE_TODO'; payload: string }
| { type: 'DELETE_TODO'; payload: string };
const TodoContext = createContext<{
state: TodoState;
dispatch: React.Dispatch<TodoAction>;
} | undefined>(undefined);
export const TodoProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(todoReducer, { todos: [] });
return <TodoContext.Provider value={{ state, dispatch }}>{children}</TodoContext.Provider>;
};
```

View File

@@ -0,0 +1,33 @@
```tsx
// src/components/AddTodo.tsx
import React, { useState } from 'react';
import styles from '../styles/AddTodo.module.css';
export const AddTodo: React.FC<{ onAdd: (text: string) => void }> = ({ onAdd }) => {
const [text, setText] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!text.trim()) {
setError('Todo text cannot be empty');
return;
}
onAdd(text.trim());
setText('');
setError('');
};
return (
<form onSubmit={handleSubmit} className={styles.form}>
<input
value={text}
onChange={(e) => setText(e.target.value)}
placeholder='Add a new todo'
/>
{error && <div className={styles.error}>{error}</div>}
<button type='submit'>Add Todo</button>
</form>
);
};
```

View File

@@ -0,0 +1,22 @@
```tsx
// src/components/TodoFilters.tsx
import React from 'react';
type FilterType = 'all' | 'active' | 'completed';
export const TodoFilters: React.FC<{
currentFilter: FilterType;
onFilterChange: (filter: FilterType) => void;
onSortChange: (ascending: boolean) => void;
}> = ({ currentFilter, onFilterChange, onSortChange }) => (
<div>
<select value={currentFilter} onChange={(e) => onFilterChange(e.target.value as FilterType)}>
<option value='all'>All</option>
<option value='active'>Active</option>
<option value='completed'>Completed</option>
</select>
<button onClick={() => onSortChange(true)}>Sort A-Z</button>
<button onClick={() => onSortChange(false)}>Sort Z-A</button>
</div>
);
```

View File

@@ -0,0 +1,13 @@
```tsx
// src/utils/storage.ts
const STORAGE_KEY = 'todos';
export const saveTodos = (todos: Todo[]) => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
};
export const loadTodos = (): Todo[] => {
const stored = localStorage.getItem(STORAGE_KEY);
return stored ? JSON.parse(stored) : [];
};
```