anycoder-1053eb97 / index.html
akhaliq's picture
akhaliq HF Staff
Upload folder using huggingface_hub
f5b3bec verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modern To-Do App</title>
<!-- Importing Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Importing FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
/* Color Palette */
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--secondary-gradient: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
--bg-color: #f3f4f6;
--glass-bg: rgba(255, 255, 255, 0.85);
--glass-border: rgba(255, 255, 255, 0.5);
--text-main: #1f2937;
--text-secondary: #6b7280;
--accent-color: #764ba2;
--danger-color: #ef4444;
--success-color: #10b981;
--shadow-lg: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Inter', sans-serif;
}
body {
background: var(--bg-color);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
background-image:
radial-gradient(circle at 10% 20%, rgba(118, 75, 162, 0.1) 0%, transparent 20%),
radial-gradient(circle at 90% 80%, rgba(102, 126, 234, 0.1) 0%, transparent 20%);
}
/* App Container */
.app-container {
width: 100%;
max-width: 500px;
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border);
border-radius: 24px;
box-shadow: var(--shadow-lg);
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
transition: all 0.3s ease;
}
/* Header */
header {
padding: 30px 30px 20px 30px;
background: var(--primary-gradient);
color: white;
position: relative;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
h1 {
font-size: 1.8rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.date-display {
font-size: 0.9rem;
opacity: 0.9;
font-weight: 500;
}
.credits {
font-size: 0.75rem;
opacity: 0.7;
text-align: right;
}
.credits a {
color: white;
text-decoration: none;
border-bottom: 1px dotted rgba(255,255,255,0.7);
}
/* Input Area */
.input-area {
padding: 25px 30px;
display: flex;
gap: 12px;
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.input-wrapper {
position: relative;
flex-grow: 1;
}
#todo-input {
width: 100%;
padding: 14px 16px 14px 45px;
border: 2px solid transparent;
background: #f9fafb;
border-radius: 12px;
font-size: 1rem;
color: var(--text-main);
transition: all 0.2s ease;
outline: none;
}
#todo-input:focus {
background: white;
border-color: var(--accent-color);
box-shadow: 0 0 0 4px rgba(118, 75, 162, 0.1);
}
.input-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
pointer-events: none;
}
#add-btn {
background: var(--primary-gradient);
color: white;
border: none;
width: 50px;
border-radius: 12px;
cursor: pointer;
font-size: 1.2rem;
transition: transform 0.2s, box-shadow 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
#add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(118, 75, 162, 0.3);
}
#add-btn:active {
transform: scale(0.95);
}
/* Filters */
.filters {
display: flex;
padding: 15px 30px 0;
gap: 10px;
}
.filter-btn {
background: none;
border: none;
padding: 8px 16px;
font-size: 0.9rem;
font-weight: 600;
color: var(--text-secondary);
cursor: pointer;
border-radius: 20px;
transition: all 0.2s;
}
.filter-btn.active {
background: rgba(118, 75, 162, 0.1);
color: var(--accent-color);
}
.filter-btn:hover:not(.active) {
background: rgba(0,0,0,0.05);
}
/* Todo List */
.todo-list {
list-style: none;
padding: 20px 30px;
overflow-y: auto;
max-height: 400px;
min-height: 200px;
}
.todo-item {
background: white;
border-radius: 12px;
padding: 12px 16px;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: var(--shadow-sm);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border-left: 4px solid transparent;
cursor: grab;
position: relative;
animation: slideIn 0.3s ease forwards;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideOut {
to { opacity: 0; transform: translateX(20px); }
}
.todo-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.todo-item.completed {
opacity: 0.7;
background: #f9fafb;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: var(--text-secondary);
}
.todo-item.priority-high {
border-left-color: var(--danger-color);
}
.todo-item.priority-medium {
border-left-color: #f59e0b;
}
.todo-item.priority-low {
border-left-color: var(--success-color);
}
/* Custom Checkbox */
.custom-checkbox {
width: 22px;
height: 22px;
border: 2px solid #d1d5db;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
flex-shrink: 0;
}
.todo-item.completed .custom-checkbox {
background: var(--success-color);
border-color: var(--success-color);
}
.custom-checkbox i {
color: white;
font-size: 0.8rem;
opacity: 0;
transform: scale(0.5);
transition: all 0.2s;
}
.todo-item.completed .custom-checkbox i {
opacity: 1;
transform: scale(1);
}
.todo-text {
flex-grow: 1;
font-size: 1rem;
color: var(--text-main);
word-break: break-word;
}
.actions {
display: flex;
gap: 8px;
opacity: 0;
transition: opacity 0.2s;
}
.todo-item:hover .actions {
opacity: 1;
}
/* Mobile specific: always show actions or use a swipe pattern logic,
but for simplicity in this demo, we keep hover or ensure they are touchable */
@media (max-width: 600px) {
.actions { opacity: 1; }
}
.action-btn {
background: none;
border: none;
cursor: pointer;
width: 32px;
height: 32px;
border-radius: 6px;
color: var(--text-secondary);
transition: background 0.2s, color 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.action-btn:hover {
background: #f3f4f6;
color: var(--text-main);
}
.delete-btn:hover {
background: #fee2e2;
color: var(--danger-color);
}
/* Priority Selector inside Todo Item (Optional UI expansion) */
.priority-selector {
position: absolute;
right: 0;
top: 100%;
background: white;
border-radius: 8px;
box-shadow: var(--shadow-lg);
display: none;
flex-direction: column;
z-index: 10;
overflow: hidden;
border: 1px solid #e5e7eb;
}
.priority-selector.show {
display: flex;
}
.p-option {
padding: 8px 16px;
font-size: 0.85rem;
cursor: pointer;
transition: background 0.1s;
}
.p-option:hover { background: #f3f4f6; }
/* Empty State */
.empty-state {
text-align: center;
padding: 40px 20px;
color: var(--text-secondary);
display: none;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 16px;
color: #d1d5db;
}
/* Footer Stats */
.footer-stats {
padding: 15px 30px;
border-top: 1px solid rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.85rem;
color: var(--text-secondary);
background: rgba(255,255,255,0.5);
}
.clear-btn {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 0.85rem;
transition: color 0.2s;
}
.clear-btn:hover {
color: var(--danger-color);
text-decoration: underline;
}
/* Toast Notification */
.toast {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: #1f2937;
color: white;
padding: 10px 20px;
border-radius: 30px;
font-size: 0.9rem;
opacity: 0;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100;
display: flex;
align-items: center;
gap: 8px;
}
.toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
</style>
</head>
<body>
<div class="app-container">
<header>
<div class="header-top">
<div>
<h1>My Tasks</h1>
<div class="date-display" id="date-display">Loading date...</div>
</div>
<div class="credits">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
</div>
</header>
<div class="input-area">
<div class="input-wrapper">
<i class="fas fa-plus input-icon"></i>
<input type="text" id="todo-input" placeholder="Add a new task..." autocomplete="off">
</div>
<button id="add-btn"><i class="fas fa-arrow-up"></i></button>
</div>
<div class="filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="active">Active</button>
<button class="filter-btn" data-filter="completed">Completed</button>
</div>
<ul class="todo-list" id="todo-list">
<!-- Todo items will be injected here -->
</ul>
<div class="empty-state" id="empty-state">
<i class="fas fa-clipboard-list"></i>
<p>No tasks found. Enjoy your day!</p>
</div>
<div class="footer-stats">
<span id="items-left">0 items left</span>
<button class="clear-btn" id="clear-completed">Clear Completed</button>
</div>
<div class="toast" id="toast">
<i class="fas fa-check-circle"></i>
<span id="toast-message">Action successful</span>
</div>
</div>
<script>
// --- State Management ---
let todos = JSON.parse(localStorage.getItem('myTodos')) || [];
let currentFilter = 'all';
// --- DOM Elements ---
const todoInput = document.getElementById('todo-input');
const addBtn = document.getElementById('add-btn');
const todoList = document.getElementById('todo-list');
const itemsLeft = document.getElementById('items-left');
const clearBtn = document.getElementById('clear-completed');
const filterBtns = document.querySelectorAll('.filter-btn');
const dateDisplay = document.getElementById('date-display');
const emptyState = document.getElementById('empty-state');
const toast = document.getElementById('toast');
const toastMsg = document.getElementById('toast-message');
// --- Initialization ---
document.addEventListener('DOMContentLoaded', () => {
renderTodos();
updateDate();
});
// --- Event Listeners ---
addBtn.addEventListener('click', addTodo);
todoInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addTodo();
});
clearBtn.addEventListener('click', () => {
const completedCount = todos.filter(t => t.completed).length;
if (completedCount === 0) return;
todos = todos.filter(todo => !todo.completed);
saveAndRender();
showToast(`${completedCount} tasks cleared`);
});
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
document.querySelector('.filter-btn.active').classList.remove('active');
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderTodos();
});
});
// --- Core Functions ---
function addTodo() {
const text = todoInput.value.trim();
if (text === '') {
showToast('Please enter a task', true);
return;
}
const newTodo = {
id: Date.now(),
text: text,
completed: false,
priority: 'medium' // default priority
};
todos.unshift(newTodo); // Add to top
todoInput.value = '';
todoInput.focus();
saveAndRender();
showToast('Task added successfully');
}
function toggleTodo(id) {
todos = todos.map(todo => {
if (todo.id === id) {
return { ...todo, completed: !todo.completed };
}
return todo;
});
saveAndRender();
}
function deleteTodo(id) {
const item = document.querySelector(`li[data-id="${id}"]`);
if (item) {
item.style.animation = 'slideOut 0.3s ease forwards';
setTimeout(() => {
todos = todos.filter(todo => todo.id !== id);
saveAndRender();
showToast('Task deleted');
}, 300);
}
}
function cyclePriority(id) {
const priorities = ['low', 'medium', 'high'];
todos = todos.map(todo => {
if (todo.id === id) {
const currentIndex = priorities.indexOf(todo.priority);
const nextIndex = (currentIndex + 1) % priorities.length;
return { ...todo, priority: priorities[nextIndex] };
}
return todo;
});
saveAndRender();
}
function saveAndRender() {
localStorage.setItem('myTodos', JSON.stringify(todos));
renderTodos();
}
function renderTodos() {
todoList.innerHTML = '';
let filteredTodos = todos;
if (currentFilter === 'active') {
filteredTodos = todos.filter(t => !t.completed);
} else if (currentFilter === 'completed') {
filteredTodos = todos.filter(t => t.completed);
}
// Update items left count
const activeCount = todos.filter(t => !t.completed).length;
itemsLeft.textContent = `${activeCount} ${activeCount === 1 ? 'item' : 'items'} left`;
// Show/Hide empty state
if (filteredTodos.length === 0) {
emptyState.style.display = 'block';
if(currentFilter === 'completed') emptyState.querySelector('p').textContent = "No completed tasks yet.";
else if(currentFilter === 'active') emptyState.querySelector('p').textContent = "No active tasks. You're all caught up!";
else emptyState.querySelector('p').textContent = "No tasks found. Enjoy your day!";
} else {
emptyState.style.display = 'none';
}
filteredTodos.forEach(todo => {
const li = document.createElement('li');
li.className = `todo-item ${todo.completed ? 'completed' : ''} priority-${todo.priority}`;
li.dataset.id = todo.id;
// Priority Icon Color Logic
let priorityIcon = 'fa-circle';
let priorityColor = 'color: #f59e0b'; // default medium (orange)
if(todo.priority === 'high') priorityColor = 'color: #ef4444'; // red
if(todo.priority === 'low') priorityColor = 'color: #10b981'; // green
li.innerHTML = `
<div class="custom-checkbox" onclick="toggleTodo(${todo.id})">
<i class="fas fa-check"></i>
</div>
<span class="todo-text">${escapeHtml(todo.text)}</span>
<div class="actions">
<button class="action-btn" onclick="cyclePriority(${todo.id})" title="Change Priority">
<i class="fas fa-flag" style="${priorityColor}"></i>
</button>
<button class="action-btn delete-btn" onclick="deleteTodo(${todo.id})" title="Delete Task">
<i class="fas fa-trash"></i>
</button>
</div>
`;
todoList.appendChild(li);
});
}
// --- Helper Functions ---
function updateDate() {
const options = { weekday: 'long', month: 'long', day: 'numeric' };
const today = new Date();
dateDisplay.textContent = today.toLocaleDateString('en-US', options);
}
function showToast(message, isError = false) {
toastMsg.textContent = message;
const icon = toast.querySelector('i');
if (isError) {
icon.className = 'fas fa-exclamation-circle';
toast.style.background = '#ef4444';
} else {
icon.className = 'fas fa-check-circle';
toast.style.background = '#1f2937';
}
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// --- Drag and Drop Logic (Basic) ---
let dragStartIndex;
function dragStart() {
dragStartIndex = +this.closest('li').getAttribute('data-index');
}
function dragDrop() {
const dragEndIndex = +this.closest('li').getAttribute('data-index');
swapItems(dragStartIndex, dragEndIndex);
this.classList.remove('over');
}
function swapItems(fromIndex, toIndex) {
const itemOne = todos[fromIndex];
const itemTwo = todos[toIndex];
todos[fromIndex] = itemTwo;
todos[toIndex] = itemOne;
saveAndRender();
}
/* Note: Implementing full native HTML5 Drag and Drop for this list structure requires
more extensive event handling. For this version, we focus on the core CRUD functionality
and keeping the UI responsive. */
</script>
</body>
</html>