akhaliq HF Staff commited on
Commit
93725e8
·
verified ·
1 Parent(s): 86496a9

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +761 -19
index.html CHANGED
@@ -1,19 +1,761 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FocusFlow | Modern Todo App</title>
7
+ <!-- Importing Google Fonts -->
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11
+ <!-- Importing Phosphor Icons (Lightweight icon library) -->
12
+ <script src="https://unpkg.com/@phosphor-icons/web"></script>
13
+
14
+ <style>
15
+ :root {
16
+ /* Light Theme Variables */
17
+ --bg-body: #f3f4f6;
18
+ --bg-card: #ffffff;
19
+ --text-primary: #111827;
20
+ --text-secondary: #6b7280;
21
+ --text-muted: #9ca3af;
22
+ --accent-color: #6366f1;
23
+ --accent-hover: #4f46e5;
24
+ --danger-color: #ef4444;
25
+ --success-color: #10b981;
26
+ --border-color: #e5e7eb;
27
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
28
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
29
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
30
+ --radius-md: 0.75rem;
31
+ --radius-lg: 1rem;
32
+ --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
33
+ }
34
+
35
+ [data-theme="dark"] {
36
+ /* Dark Theme Variables */
37
+ --bg-body: #111827;
38
+ --bg-card: #1f2937;
39
+ --text-primary: #f9fafb;
40
+ --text-secondary: #9ca3af;
41
+ --text-muted: #6b7280;
42
+ --accent-color: #818cf8;
43
+ --accent-hover: #6366f1;
44
+ --border-color: #374151;
45
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
46
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4);
47
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.5);
48
+ }
49
+
50
+ * {
51
+ box-sizing: border-box;
52
+ margin: 0;
53
+ padding: 0;
54
+ }
55
+
56
+ body {
57
+ font-family: 'Inter', sans-serif;
58
+ background-color: var(--bg-body);
59
+ color: var(--text-primary);
60
+ line-height: 1.5;
61
+ min-height: 100vh;
62
+ display: flex;
63
+ justify-content: center;
64
+ padding: 2rem 1rem;
65
+ transition: background-color 0.3s ease, color 0.3s ease;
66
+ }
67
+
68
+ /* Container */
69
+ .app-container {
70
+ width: 100%;
71
+ max-width: 600px;
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: 1.5rem;
75
+ }
76
+
77
+ /* Header */
78
+ header {
79
+ display: flex;
80
+ justify-content: space-between;
81
+ align-items: center;
82
+ padding-bottom: 0.5rem;
83
+ }
84
+
85
+ .header-title h1 {
86
+ font-size: 1.875rem;
87
+ font-weight: 700;
88
+ letter-spacing: -0.025em;
89
+ background: linear-gradient(135deg, var(--accent-color), #ec4899);
90
+ -webkit-background-clip: text;
91
+ -webkit-text-fill-color: transparent;
92
+ }
93
+
94
+ .header-title p {
95
+ color: var(--text-secondary);
96
+ font-size: 0.875rem;
97
+ margin-top: 0.25rem;
98
+ }
99
+
100
+ .header-actions {
101
+ display: flex;
102
+ gap: 0.5rem;
103
+ }
104
+
105
+ .icon-btn {
106
+ background: var(--bg-card);
107
+ border: 1px solid var(--border-color);
108
+ color: var(--text-secondary);
109
+ width: 2.5rem;
110
+ height: 2.5rem;
111
+ border-radius: 50%;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ cursor: pointer;
116
+ transition: var(--transition);
117
+ font-size: 1.25rem;
118
+ }
119
+
120
+ .icon-btn:hover {
121
+ color: var(--accent-color);
122
+ border-color: var(--accent-color);
123
+ transform: translateY(-2px);
124
+ box-shadow: var(--shadow-sm);
125
+ }
126
+
127
+ /* Input Section */
128
+ .input-card {
129
+ background: var(--bg-card);
130
+ padding: 0.5rem;
131
+ border-radius: var(--radius-lg);
132
+ box-shadow: var(--shadow-md);
133
+ display: flex;
134
+ gap: 0.5rem;
135
+ border: 1px solid var(--border-color);
136
+ }
137
+
138
+ .input-wrapper {
139
+ flex: 1;
140
+ position: relative;
141
+ display: flex;
142
+ align-items: center;
143
+ }
144
+
145
+ .input-wrapper i {
146
+ position: absolute;
147
+ left: 1rem;
148
+ color: var(--text-muted);
149
+ font-size: 1.25rem;
150
+ }
151
+
152
+ #todo-input {
153
+ width: 100%;
154
+ padding: 1rem 1rem 1rem 3rem;
155
+ border: none;
156
+ background: transparent;
157
+ font-size: 1rem;
158
+ color: var(--text-primary);
159
+ outline: none;
160
+ }
161
+
162
+ #todo-input::placeholder {
163
+ color: var(--text-muted);
164
+ }
165
+
166
+ #add-btn {
167
+ background: var(--accent-color);
168
+ color: white;
169
+ border: none;
170
+ padding: 0 1.5rem;
171
+ border-radius: var(--radius-md);
172
+ font-weight: 600;
173
+ cursor: pointer;
174
+ transition: var(--transition);
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 0.5rem;
178
+ }
179
+
180
+ #add-btn:hover {
181
+ background: var(--accent-hover);
182
+ transform: translateY(-1px);
183
+ }
184
+
185
+ #add-btn:active {
186
+ transform: translateY(0);
187
+ }
188
+
189
+ /* Filters */
190
+ .filters {
191
+ display: flex;
192
+ gap: 0.5rem;
193
+ overflow-x: auto;
194
+ padding-bottom: 0.5rem;
195
+ scrollbar-width: none; /* Firefox */
196
+ }
197
+
198
+ .filters::-webkit-scrollbar {
199
+ display: none; /* Chrome/Safari */
200
+ }
201
+
202
+ .filter-btn {
203
+ background: transparent;
204
+ border: 1px solid transparent;
205
+ color: var(--text-secondary);
206
+ padding: 0.5rem 1rem;
207
+ border-radius: 2rem;
208
+ font-size: 0.875rem;
209
+ font-weight: 500;
210
+ cursor: pointer;
211
+ transition: var(--transition);
212
+ white-space: nowrap;
213
+ }
214
+
215
+ .filter-btn:hover {
216
+ background: rgba(99, 102, 241, 0.1);
217
+ color: var(--accent-color);
218
+ }
219
+
220
+ .filter-btn.active {
221
+ background: var(--accent-color);
222
+ color: white;
223
+ box-shadow: var(--shadow-sm);
224
+ }
225
+
226
+ /* Todo List */
227
+ .todo-list {
228
+ list-style: none;
229
+ display: flex;
230
+ flex-direction: column;
231
+ gap: 0.75rem;
232
+ min-height: 200px;
233
+ }
234
+
235
+ .todo-item {
236
+ background: var(--bg-card);
237
+ padding: 1rem;
238
+ border-radius: var(--radius-md);
239
+ box-shadow: var(--shadow-sm);
240
+ border: 1px solid var(--border-color);
241
+ display: flex;
242
+ align-items: center;
243
+ gap: 1rem;
244
+ transition: var(--transition);
245
+ animation: slideIn 0.3s ease-out;
246
+ position: relative;
247
+ cursor: grab;
248
+ }
249
+
250
+ .todo-item:active {
251
+ cursor: grabbing;
252
+ }
253
+
254
+ .todo-item.dragging {
255
+ opacity: 0.5;
256
+ border: 2px dashed var(--accent-color);
257
+ background: transparent;
258
+ }
259
+
260
+ @keyframes slideIn {
261
+ from { opacity: 0; transform: translateY(10px); }
262
+ to { opacity: 1; transform: translateY(0); }
263
+ }
264
+
265
+ .todo-checkbox {
266
+ appearance: none;
267
+ width: 1.5rem;
268
+ height: 1.5rem;
269
+ border: 2px solid var(--border-color);
270
+ border-radius: 50%;
271
+ cursor: pointer;
272
+ position: relative;
273
+ flex-shrink: 0;
274
+ transition: var(--transition);
275
+ }
276
+
277
+ .todo-checkbox:checked {
278
+ background-color: var(--success-color);
279
+ border-color: var(--success-color);
280
+ }
281
+
282
+ .todo-checkbox:checked::after {
283
+ content: '✔';
284
+ color: white;
285
+ position: absolute;
286
+ top: 50%;
287
+ left: 50%;
288
+ transform: translate(-50%, -50%);
289
+ font-size: 0.875rem;
290
+ }
291
+
292
+ .todo-content {
293
+ flex: 1;
294
+ font-size: 1rem;
295
+ color: var(--text-primary);
296
+ transition: var(--transition);
297
+ word-break: break-word;
298
+ }
299
+
300
+ .todo-item.completed .todo-content {
301
+ text-decoration: line-through;
302
+ color: var(--text-muted);
303
+ }
304
+
305
+ .todo-actions {
306
+ display: flex;
307
+ gap: 0.5rem;
308
+ opacity: 0;
309
+ transition: opacity 0.2s;
310
+ }
311
+
312
+ .todo-item:hover .todo-actions {
313
+ opacity: 1;
314
+ }
315
+
316
+ /* Mobile specific: always show actions */
317
+ @media (max-width: 600px) {
318
+ .todo-actions {
319
+ opacity: 1;
320
+ }
321
+ }
322
+
323
+ .action-btn {
324
+ background: transparent;
325
+ border: none;
326
+ color: var(--text-muted);
327
+ cursor: pointer;
328
+ padding: 0.25rem;
329
+ border-radius: 0.25rem;
330
+ transition: var(--transition);
331
+ display: flex;
332
+ align-items: center;
333
+ justify-content: center;
334
+ }
335
+
336
+ .action-btn:hover {
337
+ background: rgba(0,0,0,0.05);
338
+ color: var(--text-primary);
339
+ }
340
+
341
+ .delete-btn:hover {
342
+ color: var(--danger-color);
343
+ background: rgba(239, 68, 68, 0.1);
344
+ }
345
+
346
+ /* Empty State */
347
+ .empty-state {
348
+ display: flex;
349
+ flex-direction: column;
350
+ align-items: center;
351
+ justify-content: center;
352
+ padding: 3rem;
353
+ color: var(--text-muted);
354
+ text-align: center;
355
+ }
356
+
357
+ .empty-state i {
358
+ font-size: 3rem;
359
+ margin-bottom: 1rem;
360
+ color: var(--border-color);
361
+ }
362
+
363
+ /* Stats Footer */
364
+ .stats-footer {
365
+ display: flex;
366
+ justify-content: space-between;
367
+ align-items: center;
368
+ padding: 1rem;
369
+ color: var(--text-secondary);
370
+ font-size: 0.875rem;
371
+ background: var(--bg-card);
372
+ border-radius: var(--radius-md);
373
+ border: 1px solid var(--border-color);
374
+ }
375
+
376
+ .clear-btn {
377
+ background: none;
378
+ border: none;
379
+ color: var(--text-secondary);
380
+ cursor: pointer;
381
+ font-size: 0.875rem;
382
+ transition: var(--transition);
383
+ }
384
+
385
+ .clear-btn:hover {
386
+ color: var(--danger-color);
387
+ text-decoration: underline;
388
+ }
389
+
390
+ /* Toast Notification */
391
+ .toast {
392
+ position: fixed;
393
+ bottom: 2rem;
394
+ left: 50%;
395
+ transform: translateX(-50%) translateY(100px);
396
+ background: var(--text-primary);
397
+ color: var(--bg-card);
398
+ padding: 0.75rem 1.5rem;
399
+ border-radius: 2rem;
400
+ box-shadow: var(--shadow-lg);
401
+ font-size: 0.875rem;
402
+ opacity: 0;
403
+ transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s;
404
+ z-index: 1000;
405
+ display: flex;
406
+ align-items: center;
407
+ gap: 0.5rem;
408
+ }
409
+
410
+ .toast.show {
411
+ transform: translateX(-50%) translateY(0);
412
+ opacity: 1;
413
+ }
414
+
415
+ /* Credit Link */
416
+ .credit-link {
417
+ text-align: center;
418
+ font-size: 0.75rem;
419
+ color: var(--text-muted);
420
+ margin-top: 1rem;
421
+ text-decoration: none;
422
+ transition: color 0.2s;
423
+ }
424
+
425
+ .credit-link:hover {
426
+ color: var(--accent-color);
427
+ }
428
+
429
+ .credit-link span {
430
+ font-weight: 600;
431
+ }
432
+ </style>
433
+ </head>
434
+ <body>
435
+
436
+ <div class="app-container">
437
+ <!-- Header -->
438
+ <header>
439
+ <div class="header-title">
440
+ <h1>FocusFlow</h1>
441
+ <p id="date-display">Loading date...</p>
442
+ </div>
443
+ <div class="header-actions">
444
+ <button class="icon-btn" id="theme-toggle" aria-label="Toggle Theme">
445
+ <i class="ph ph-moon"></i>
446
+ </button>
447
+ </div>
448
+ </header>
449
+
450
+ <!-- Input Area -->
451
+ <div class="input-card">
452
+ <div class="input-wrapper">
453
+ <i class="ph ph-plus"></i>
454
+ <input type="text" id="todo-input" placeholder="What needs to be done?" autocomplete="off">
455
+ </div>
456
+ <button id="add-btn">Add</button>
457
+ </div>
458
+
459
+ <!-- Filters -->
460
+ <div class="filters">
461
+ <button class="filter-btn active" data-filter="all">All</button>
462
+ <button class="filter-btn" data-filter="active">Active</button>
463
+ <button class="filter-btn" data-filter="completed">Completed</button>
464
+ </div>
465
+
466
+ <!-- Todo List -->
467
+ <ul class="todo-list" id="todo-list">
468
+ <!-- Items will be injected here via JS -->
469
+ </ul>
470
+
471
+ <!-- Empty State (Hidden by default) -->
472
+ <div class="empty-state" id="empty-state" style="display: none;">
473
+ <i class="ph ph-clipboard-text"></i>
474
+ <p>No tasks found. Enjoy your day!</p>
475
+ </div>
476
+
477
+ <!-- Footer Stats -->
478
+ <div class="stats-footer">
479
+ <span id="items-left">0 items left</span>
480
+ <button class="clear-btn" id="clear-completed">Clear Completed</button>
481
+ </div>
482
+
483
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="credit-link">
484
+ Built with <span>anycoder</span>
485
+ </a>
486
+ </div>
487
+
488
+ <!-- Toast Notification -->
489
+ <div class="toast" id="toast">
490
+ <i class="ph ph-check-circle"></i>
491
+ <span id="toast-message">Action successful</span>
492
+ </div>
493
+
494
+ <script>
495
+ // --- State Management ---
496
+ let todos = JSON.parse(localStorage.getItem('todos')) || [];
497
+ let currentFilter = 'all';
498
+
499
+ // --- DOM Elements ---
500
+ const todoInput = document.getElementById('todo-input');
501
+ const addBtn = document.getElementById('add-btn');
502
+ const todoList = document.getElementById('todo-list');
503
+ const emptyState = document.getElementById('empty-state');
504
+ const itemsLeftLabel = document.getElementById('items-left');
505
+ const clearCompletedBtn = document.getElementById('clear-completed');
506
+ const filterBtns = document.querySelectorAll('.filter-btn');
507
+ const themeToggle = document.getElementById('theme-toggle');
508
+ const dateDisplay = document.getElementById('date-display');
509
+ const toast = document.getElementById('toast');
510
+ const toastMessage = document.getElementById('toast-message');
511
+
512
+ // --- Initialization ---
513
+ function init() {
514
+ renderTodos();
515
+ updateDate();
516
+ applyTheme();
517
+ }
518
+
519
+ // --- Date Display ---
520
+ function updateDate() {
521
+ const options = { weekday: 'long', month: 'long', day: 'numeric' };
522
+ const today = new Date();
523
+ dateDisplay.textContent = today.toLocaleDateString('en-US', options);
524
+ }
525
+
526
+ // --- Theme Handling ---
527
+ function applyTheme() {
528
+ const savedTheme = localStorage.getItem('theme') || 'light';
529
+ document.documentElement.setAttribute('data-theme', savedTheme);
530
+ updateThemeIcon(savedTheme);
531
+ }
532
+
533
+ function toggleTheme() {
534
+ const currentTheme = document.documentElement.getAttribute('data-theme');
535
+ const newTheme = currentTheme === 'light' ? 'dark' : 'light';
536
+ document.documentElement.setAttribute('data-theme', newTheme);
537
+ localStorage.setItem('theme', newTheme);
538
+ updateThemeIcon(newTheme);
539
+ }
540
+
541
+ function updateThemeIcon(theme) {
542
+ const icon = themeToggle.querySelector('i');
543
+ if (theme === 'dark') {
544
+ icon.className = 'ph ph-sun';
545
+ } else {
546
+ icon.className = 'ph ph-moon';
547
+ }
548
+ }
549
+
550
+ // --- Core Functions ---
551
+ function saveTodos() {
552
+ localStorage.setItem('todos', JSON.stringify(todos));
553
+ renderTodos();
554
+ }
555
+
556
+ function addTodo() {
557
+ const text = todoInput.value.trim();
558
+ if (text === '') {
559
+ showToast('Please enter a task', 'error');
560
+ return;
561
+ }
562
+
563
+ const newTodo = {
564
+ id: Date.now(),
565
+ text: text,
566
+ completed: false
567
+ };
568
+
569
+ todos.unshift(newTodo); // Add to top
570
+ todoInput.value = '';
571
+ saveTodos();
572
+ showToast('Task added successfully');
573
+ }
574
+
575
+ function toggleTodo(id) {
576
+ todos = todos.map(todo => {
577
+ if (todo.id === id) {
578
+ return { ...todo, completed: !todo.completed };
579
+ }
580
+ return todo;
581
+ });
582
+ saveTodos();
583
+ }
584
+
585
+ function deleteTodo(id) {
586
+ todos = todos.filter(todo => todo.id !== id);
587
+ saveTodos();
588
+ showToast('Task deleted');
589
+ }
590
+
591
+ function clearCompleted() {
592
+ const completedCount = todos.filter(t => t.completed).length;
593
+ if (completedCount === 0) return;
594
+
595
+ todos = todos.filter(todo => !todo.completed);
596
+ saveTodos();
597
+ showToast('Completed tasks cleared');
598
+ }
599
+
600
+ // --- Rendering ---
601
+ function renderTodos() {
602
+ todoList.innerHTML = '';
603
+
604
+ let filteredTodos = todos;
605
+ if (currentFilter === 'active') {
606
+ filteredTodos = todos.filter(t => !t.completed);
607
+ } else if (currentFilter === 'completed') {
608
+ filteredTodos = todos.filter(t => t.completed);
609
+ }
610
+
611
+ // Update Empty State
612
+ if (filteredTodos.length === 0) {
613
+ emptyState.style.display = 'flex';
614
+ todoList.style.display = 'none';
615
+ } else {
616
+ emptyState.style.display = 'none';
617
+ todoList.style.display = 'flex';
618
+ }
619
+
620
+ // Update Stats
621
+ const activeCount = todos.filter(t => !t.completed).length;
622
+ itemsLeftLabel.textContent = `${activeCount} item${activeCount !== 1 ? 's' : ''} left`;
623
+
624
+ // Create Elements
625
+ filteredTodos.forEach(todo => {
626
+ const li = document.createElement('li');
627
+ li.className = `todo-item ${todo.completed ? 'completed' : ''}`;
628
+ li.draggable = true;
629
+ li.dataset.id = todo.id;
630
+
631
+ li.innerHTML = `
632
+ <input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''}>
633
+ <span class="todo-content">${escapeHtml(todo.text)}</span>
634
+ <div class="todo-actions">
635
+ <button class="action-btn delete-btn" aria-label="Delete">
636
+ <i class="ph ph-trash"></i>
637
+ </button>
638
+ </div>
639
+ `;
640
+
641
+ // Event Listeners for Item
642
+ const checkbox = li.querySelector('.todo-checkbox');
643
+ checkbox.addEventListener('change', () => toggleTodo(todo.id));
644
+
645
+ const deleteBtn = li.querySelector('.delete-btn');
646
+ deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
647
+
648
+ // Drag Events
649
+ li.addEventListener('dragstart', handleDragStart);
650
+ li.addEventListener('dragend', handleDragEnd);
651
+
652
+ todoList.appendChild(li);
653
+ });
654
+ }
655
+
656
+ // --- Drag and Drop Logic ---
657
+ let draggedItem = null;
658
+
659
+ function handleDragStart(e) {
660
+ draggedItem = this;
661
+ setTimeout(() => this.classList.add('dragging'), 0);
662
+ }
663
+
664
+ function handleDragEnd(e) {
665
+ this.classList.remove('dragging');
666
+ draggedItem = null;
667
+
668
+ // Reorder array based on DOM order
669
+ const newOrderIds = Array.from(todoList.children).map(li => parseInt(li.dataset.id));
670
+
671
+ // Sort todos array based on new ID order
672
+ // Note: This simple sort only works perfectly if 'all' filter is active.
673
+ // For simplicity in this demo, we only allow reordering in 'all' mode visually,
674
+ // but to keep state consistent, we map the full list.
675
+ if(currentFilter === 'all') {
676
+ const idTodoMap = new Map(todos.map(t => [t.id, t]));
677
+ todos = newOrderIds.map(id => idTodoMap.get(id)).filter(t => t !== undefined);
678
+ // Filter undefined just in case, though map should cover all
679
+ saveTodos(); // Save new order
680
+ }
681
+ }
682
+
683
+ todoList.addEventListener('dragover', (e) => {
684
+ e.preventDefault();
685
+ const afterElement = getDragAfterElement(todoList, e.clientY);
686
+ const draggable = document.querySelector('.dragging');
687
+ if (afterElement == null) {
688
+ todoList.appendChild(draggable);
689
+ } else {
690
+ todoList.insertBefore(draggable, afterElement);
691
+ }
692
+ });
693
+
694
+ function getDragAfterElement(container, y) {
695
+ const draggableElements = [...container.querySelectorAll('.todo-item:not(.dragging)')];
696
+
697
+ return draggableElements.reduce((closest, child) => {
698
+ const box = child.getBoundingClientRect();
699
+ const offset = y - box.top - box.height / 2;
700
+ if (offset < 0 && offset > closest.offset) {
701
+ return { offset: offset, element: child };
702
+ } else {
703
+ return closest;
704
+ }
705
+ }, { offset: Number.NEGATIVE_INFINITY }).element;
706
+ }
707
+
708
+ // --- Utilities ---
709
+ function escapeHtml(text) {
710
+ const div = document.createElement('div');
711
+ div.textContent = text;
712
+ return div.innerHTML;
713
+ }
714
+
715
+ function showToast(message, type = 'success') {
716
+ toastMessage.textContent = message;
717
+ const icon = toast.querySelector('i');
718
+
719
+ if (type === 'error') {
720
+ icon.className = 'ph ph-warning-circle';
721
+ toast.style.backgroundColor = 'var(--danger-color)';
722
+ } else {
723
+ icon.className = 'ph ph-check-circle';
724
+ toast.style.backgroundColor = 'var(--text-primary)';
725
+ }
726
+
727
+ toast.classList.add('show');
728
+ setTimeout(() => {
729
+ toast.classList.remove('show');
730
+ }, 3000);
731
+ }
732
+
733
+ // --- Event Listeners ---
734
+ addBtn.addEventListener('click', addTodo);
735
+
736
+ todoInput.addEventListener('keypress', (e) => {
737
+ if (e.key === 'Enter') addTodo();
738
+ });
739
+
740
+ clearCompletedBtn.addEventListener('click', clearCompleted);
741
+
742
+ themeToggle.addEventListener('click', toggleTheme);
743
+
744
+ filterBtns.forEach(btn => {
745
+ btn.addEventListener('click', () => {
746
+ // Update UI classes
747
+ filterBtns.forEach(b => b.classList.remove('active'));
748
+ btn.classList.add('active');
749
+
750
+ // Update Logic
751
+ currentFilter = btn.dataset.filter;
752
+ renderTodos();
753
+ });
754
+ });
755
+
756
+ // Run Init
757
+ init();
758
+
759
+ </script>
760
+ </body>
761
+ </html>