svelte-todo-23 / src /App.svelte
akhaliq's picture
akhaliq HF Staff
Upload src/App.svelte with huggingface_hub
2cfb5d6 verified
<!-- src/App.svelte content here -->
<script lang="ts">
import { onMount } from 'svelte';
interface Todo {
id: number;
text: string;
completed: boolean;
}
let todos: Todo[] = [];
let newTodo = '';
let nextId = 1;
onMount(() => {
const savedTodos = localStorage.getItem('todos');
if (savedTodos) {
todos = JSON.parse(savedTodos);
nextId = todos.length > 0 ? Math.max(...todos.map(t => t.id)) + 1 : 1;
}
});
$: localStorage.setItem('todos', JSON.stringify(todos));
function addTodo() {
if (newTodo.trim() === '') return;
todos = [...todos, {
id: nextId++,
text: newTodo.trim(),
completed: false
}];
newTodo = '';
}
function toggleTodo(id: number) {
todos = todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
}
function deleteTodo(id: number) {
todos = todos.filter(todo => todo.id !== id);
}
function handleKeyPress(event: KeyboardEvent) {
if (event.key === 'Enter') {
addTodo();
}
}
</script>
<div class="app">
<header>
<h1>Todo App</h1>
</header>
<main>
<div class="input-container">
<input
type="text"
bind:value={newTodo}
on:keydown={handleKeyPress}
placeholder="Add a new todo..."
aria-label="Add a new todo"
/>
<button on:click={addTodo} class="add-btn" disabled={!newTodo.trim()}>
Add
</button>
</div>
<div class="todos-container">
{#if todos.length === 0}
<p class="empty-state">No todos yet. Add one above!</p>
{:else}
<ul class="todos-list" role="list">
{#each todos as todo (todo.id)}
<li class="todo-item" class:completed={todo.completed}>
<input
type="checkbox"
checked={todo.completed}
on:change={() => toggleTodo(todo.id)}
aria-label={todo.completed ? `Mark ${todo.text} as incomplete` : `Mark ${todo.text} as complete`}
/>
<span class="todo-text">{todo.text}</span>
<button
class="delete-btn"
on:click={() => deleteTodo(todo.id)}
aria-label={`Delete ${todo.text}`}
>
×
</button>
</li>
{/each}
</ul>
{/if}
</div>
{#if todos.length > 0}
<div class="stats">
<span>{todos.filter(t => !t.completed).length} remaining</span>
<span>{todos.filter(t => t.completed).length} completed</span>
</div>
{/if}
</main>
</div>
<style>
.app {
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
header {
text-align: center;
margin-bottom: 2rem;
}
h1 {
color: #333;
font-weight: 600;
}
.input-container {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
}
input {
flex: 1;
padding: 0.75rem;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
input:focus {
outline: none;
border-color: #4a90e2;
}
.add-btn {
padding: 0.75rem 1.5rem;
background-color: #4a90e2;
color: white;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.2s;
}
.add-btn:hover:not(:disabled) {
background-color: #357abd;
}
.add-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.todos-container {
min-height: 200px;
}
.empty-state {
text-align: center;
color: #777;
font-style: italic;
padding: 2rem;
}
.todos-list {
list-style: none;
padding: 0;
margin: 0;
}
.todo-item {
display: flex;
align-items: center;
padding: 1rem;
border-bottom: 1px solid #eee;
gap: 1rem;
transition: background-color 0.2s;
}
.todo-item:hover {
background-color: #f9f9f9;
}
.todo-item.completed {
opacity: 0.7;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: #888;
}
.todo-text {
flex: 1;
font-size: 1.1rem;
}
.delete-btn {
background: none;
border: none;
color: #e74c3c;
font-size: 1.5rem;
cursor: pointer;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background-color 0.2s;
}
.delete-btn:hover {
background-color: rgba(231, 76, 60, 0.1);
}
.stats {
display: flex;
justify-content: space-between;
margin-top: 1.5rem;
padding: 1rem;
background-color: #f5f5f5;
border-radius: 4px;
font-size: 0.9rem;
color: #555;
}
@media (max-width: 600px) {
.app {
padding: 1rem;
}
.input-container {
flex-direction: column;
}
.add-btn {
padding: 0.75rem;
}
}
</style>