Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cosmic Todo List</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @keyframes float { | |
| 0% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| 100% { transform: translateY(0px); } | |
| } | |
| .floating { | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| .task-item:hover .task-actions { | |
| opacity: 1; | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #6e8efb, #a777e3); | |
| } | |
| .checkbox:checked + .checkmark { | |
| background-color: #a777e3; | |
| border-color: #a777e3; | |
| } | |
| .checkbox:checked + .checkmark:after { | |
| display: block; | |
| } | |
| .checkmark:after { | |
| content: ""; | |
| position: absolute; | |
| display: none; | |
| left: 6px; | |
| top: 2px; | |
| width: 4px; | |
| height: 8px; | |
| border: solid white; | |
| border-width: 0 2px 2px 0; | |
| transform: rotate(45deg); | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen bg-gray-100 font-sans"> | |
| <div class="container mx-auto px-4 py-8 max-w-3xl"> | |
| <!-- Header with floating animation --> | |
| <div class="text-center mb-10 floating"> | |
| <h1 class="text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-purple-500 to-pink-500 mb-2"> | |
| Cosmic Todo | |
| </h1> | |
| <p class="text-gray-600">Organize your universe one task at a time</p> | |
| </div> | |
| <!-- Main card --> | |
| <div class="bg-white rounded-2xl shadow-xl overflow-hidden transition-all duration-300 hover:shadow-2xl"> | |
| <!-- Gradient header --> | |
| <div class="gradient-bg p-6 text-white"> | |
| <div class="flex justify-between items-center"> | |
| <h2 class="text-2xl font-semibold">My Tasks</h2> | |
| <span id="task-count" class="bg-white text-purple-600 px-3 py-1 rounded-full text-sm font-bold">0 tasks</span> | |
| </div> | |
| <!-- Add task form --> | |
| <div class="mt-6 flex"> | |
| <input | |
| type="text" | |
| id="new-task" | |
| placeholder="Add a new cosmic task..." | |
| class="flex-grow px-4 py-3 rounded-l-lg focus:outline-none text-gray-800" | |
| > | |
| <button | |
| id="add-task" | |
| class="bg-pink-500 hover:bg-pink-600 text-white px-6 py-3 rounded-r-lg transition-colors" | |
| > | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Task list --> | |
| <div class="p-6"> | |
| <div id="task-list" class="space-y-3"> | |
| <!-- Tasks will be added here dynamically --> | |
| <div class="text-center py-10 text-gray-400" id="empty-state"> | |
| <i class="fas fa-meteor text-4xl mb-3"></i> | |
| <p>No tasks in your universe yet!</p> | |
| </div> | |
| </div> | |
| <!-- Filter buttons --> | |
| <div class="mt-6 flex justify-center space-x-2"> | |
| <button class="filter-btn active px-4 py-2 rounded-lg bg-purple-100 text-purple-600" data-filter="all"> | |
| All | |
| </button> | |
| <button class="filter-btn px-4 py-2 rounded-lg hover:bg-purple-50" data-filter="active"> | |
| Active | |
| </button> | |
| <button class="filter-btn px-4 py-2 rounded-lg hover:bg-purple-50" data-filter="completed"> | |
| Completed | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Floating action button --> | |
| <button id="clear-completed" class="fixed bottom-6 right-6 bg-pink-500 text-white w-14 h-14 rounded-full shadow-lg hover:bg-pink-600 transition-colors flex items-center justify-center"> | |
| <i class="fas fa-trash-alt"></i> | |
| </button> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // DOM elements | |
| const taskInput = document.getElementById('new-task'); | |
| const addTaskBtn = document.getElementById('add-task'); | |
| const taskList = document.getElementById('task-list'); | |
| const emptyState = document.getElementById('empty-state'); | |
| const taskCount = document.getElementById('task-count'); | |
| const filterButtons = document.querySelectorAll('.filter-btn'); | |
| const clearCompletedBtn = document.getElementById('clear-completed'); | |
| let tasks = JSON.parse(localStorage.getItem('tasks')) || []; | |
| let currentFilter = 'all'; | |
| // Initialize the app | |
| function init() { | |
| renderTasks(); | |
| updateTaskCount(); | |
| // Event listeners | |
| addTaskBtn.addEventListener('click', addTask); | |
| taskInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') addTask(); | |
| }); | |
| filterButtons.forEach(btn => { | |
| btn.addEventListener('click', function() { | |
| filterButtons.forEach(b => b.classList.remove('active', 'bg-purple-100', 'text-purple-600')); | |
| btn.classList.add('active', 'bg-purple-100', 'text-purple-600'); | |
| currentFilter = btn.dataset.filter; | |
| renderTasks(); | |
| }); | |
| }); | |
| clearCompletedBtn.addEventListener('click', clearCompletedTasks); | |
| } | |
| // Add a new task | |
| function addTask() { | |
| const taskText = taskInput.value.trim(); | |
| if (taskText === '') return; | |
| const newTask = { | |
| id: Date.now(), | |
| text: taskText, | |
| completed: false, | |
| createdAt: new Date().toISOString() | |
| }; | |
| tasks.unshift(newTask); | |
| saveTasks(); | |
| renderTasks(); | |
| updateTaskCount(); | |
| taskInput.value = ''; | |
| taskInput.focus(); | |
| } | |
| // Render tasks based on current filter | |
| function renderTasks() { | |
| let filteredTasks = []; | |
| switch(currentFilter) { | |
| case 'active': | |
| filteredTasks = tasks.filter(task => !task.completed); | |
| break; | |
| case 'completed': | |
| filteredTasks = tasks.filter(task => task.completed); | |
| break; | |
| default: | |
| filteredTasks = [...tasks]; | |
| } | |
| if (filteredTasks.length === 0) { | |
| emptyState.style.display = 'block'; | |
| taskList.innerHTML = ''; | |
| taskList.appendChild(emptyState); | |
| } else { | |
| emptyState.style.display = 'none'; | |
| taskList.innerHTML = ''; | |
| filteredTasks.forEach(task => { | |
| const taskElement = createTaskElement(task); | |
| taskList.appendChild(taskElement); | |
| }); | |
| } | |
| } | |
| // Create task HTML element | |
| function createTaskElement(task) { | |
| const taskElement = document.createElement('div'); | |
| taskElement.className = `task-item group flex items-center justify-between p-4 bg-white rounded-lg border border-gray-100 hover:bg-purple-50 transition-colors relative overflow-hidden`; | |
| taskElement.dataset.id = task.id; | |
| // Add a subtle shine effect on hover | |
| const shine = document.createElement('div'); | |
| shine.className = 'absolute inset-0 bg-gradient-to-r from-transparent via-white/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none'; | |
| shine.style.transform = 'translateX(-100%)'; | |
| // Checkbox and text | |
| const taskContent = document.createElement('div'); | |
| taskContent.className = 'flex items-center flex-grow'; | |
| const label = document.createElement('label'); | |
| label.className = 'flex items-center cursor-pointer'; | |
| const checkbox = document.createElement('input'); | |
| checkbox.type = 'checkbox'; | |
| checkbox.className = 'checkbox opacity-0 absolute'; | |
| checkbox.checked = task.completed; | |
| checkbox.addEventListener('change', () => toggleTaskComplete(task.id)); | |
| const checkmark = document.createElement('span'); | |
| checkmark.className = `checkmark w-5 h-5 border-2 border-gray-300 rounded-md flex-shrink-0 ${task.completed ? 'bg-purple-500 border-purple-500' : ''}`; | |
| const taskText = document.createElement('span'); | |
| taskText.className = `ml-3 ${task.completed ? 'line-through text-gray-400' : 'text-gray-700'}`; | |
| taskText.textContent = task.text; | |
| label.appendChild(checkbox); | |
| label.appendChild(checkmark); | |
| label.appendChild(taskText); | |
| // Task actions (edit and delete) | |
| const taskActions = document.createElement('div'); | |
| taskActions.className = 'task-actions flex space-x-2 opacity-0 group-hover:opacity-100 transition-opacity'; | |
| const editBtn = document.createElement('button'); | |
| editBtn.className = 'text-gray-400 hover:text-purple-500 transition-colors'; | |
| editBtn.innerHTML = '<i class="fas fa-edit"></i>'; | |
| editBtn.addEventListener('click', () => editTask(task.id)); | |
| const deleteBtn = document.createElement('button'); | |
| deleteBtn.className = 'text-gray-400 hover:text-pink-500 transition-colors'; | |
| deleteBtn.innerHTML = '<i class="fas fa-trash-alt"></i>'; | |
| deleteBtn.addEventListener('click', () => deleteTask(task.id)); | |
| taskActions.appendChild(editBtn); | |
| taskActions.appendChild(deleteBtn); | |
| taskContent.appendChild(label); | |
| taskElement.appendChild(shine); | |
| taskElement.appendChild(taskContent); | |
| taskElement.appendChild(taskActions); | |
| // Add shine animation on hover | |
| taskElement.addEventListener('mouseenter', () => { | |
| shine.style.transform = 'translateX(100%)'; | |
| shine.style.transition = 'transform 0.7s ease-in-out'; | |
| }); | |
| return taskElement; | |
| } | |
| // Toggle task completion status | |
| function toggleTaskComplete(taskId) { | |
| tasks = tasks.map(task => { | |
| if (task.id === taskId) { | |
| return {...task, completed: !task.completed}; | |
| } | |
| return task; | |
| }); | |
| saveTasks(); | |
| renderTasks(); | |
| updateTaskCount(); | |
| } | |
| // Edit task | |
| function editTask(taskId) { | |
| const task = tasks.find(t => t.id === taskId); | |
| if (!task) return; | |
| const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`); | |
| const taskTextElement = taskElement.querySelector('span:not(.checkmark)'); | |
| const input = document.createElement('input'); | |
| input.type = 'text'; | |
| input.value = task.text; | |
| input.className = 'ml-3 px-2 py-1 border border-gray-300 rounded flex-grow'; | |
| taskTextElement.replaceWith(input); | |
| input.focus(); | |
| const handleBlur = () => { | |
| const newText = input.value.trim(); | |
| if (newText !== '' && newText !== task.text) { | |
| task.text = newText; | |
| saveTasks(); | |
| renderTasks(); | |
| } else { | |
| renderTasks(); | |
| } | |
| }; | |
| input.addEventListener('blur', handleBlur); | |
| input.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| handleBlur(); | |
| } | |
| }); | |
| } | |
| // Delete task | |
| function deleteTask(taskId) { | |
| tasks = tasks.filter(task => task.id !== taskId); | |
| saveTasks(); | |
| renderTasks(); | |
| updateTaskCount(); | |
| } | |
| // Clear completed tasks | |
| function clearCompletedTasks() { | |
| tasks = tasks.filter(task => !task.completed); | |
| saveTasks(); | |
| renderTasks(); | |
| updateTaskCount(); | |
| // Show confirmation animation | |
| clearCompletedBtn.innerHTML = '<i class="fas fa-check"></i>'; | |
| setTimeout(() => { | |
| clearCompletedBtn.innerHTML = '<i class="fas fa-trash-alt"></i>'; | |
| }, 1000); | |
| } | |
| // Update task counter | |
| function updateTaskCount() { | |
| const totalTasks = tasks.length; | |
| const completedTasks = tasks.filter(task => task.completed).length; | |
| if (totalTasks === 0) { | |
| taskCount.textContent = '0 tasks'; | |
| } else if (completedTasks === totalTasks) { | |
| taskCount.textContent = `All done! 🎉`; | |
| } else { | |
| taskCount.textContent = `${completedTasks}/${totalTasks} completed`; | |
| } | |
| } | |
| // Save tasks to localStorage | |
| function saveTasks() { | |
| localStorage.setItem('tasks', JSON.stringify(tasks)); | |
| } | |
| // Initialize the app | |
| init(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=aircrushin/todo" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |