notetap / index.html
sumitjangir's picture
Add 3 files
7b00a56 verified
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Noted - Simple Note Taking App</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>
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
.dark ::-webkit-scrollbar-track {
background: #1f2937;
}
::-webkit-scrollbar-thumb {
background: #9ca3af;
border-radius: 4px;
}
.dark ::-webkit-scrollbar-thumb {
background: #4b5563;
}
/* Animation for notes */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.note-animation {
animation: fadeIn 0.3s ease-out forwards;
}
/* Custom checkbox */
.custom-checkbox {
appearance: none;
-webkit-appearance: none;
width: 20px;
height: 20px;
border: 2px solid #d1d5db;
border-radius: 4px;
cursor: pointer;
position: relative;
transition: all 0.2s;
}
.custom-checkbox:checked {
background-color: #3b82f6;
border-color: #3b82f6;
}
.custom-checkbox:checked::after {
content: "✓";
position: absolute;
color: white;
font-size: 12px;
left: 4px;
top: 1px;
}
.dark .custom-checkbox {
border-color: #4b5563;
}
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900 transition-colors duration-200 min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<!-- Header -->
<header class="flex justify-between items-center mb-8">
<div class="flex items-center">
<i class="fas fa-sticky-note text-blue-500 text-3xl mr-3"></i>
<h1 class="text-3xl font-bold text-gray-800 dark:text-white">Noted</h1>
</div>
<div class="flex items-center space-x-4">
<button id="themeToggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors">
<i class="fas fa-moon dark:hidden"></i>
<i class="fas fa-sun hidden dark:inline"></i>
</button>
<button id="clearAllBtn" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors hidden">
<i class="fas fa-trash mr-2"></i>Clear All
</button>
</div>
</header>
<!-- Search and Add Note -->
<div class="flex flex-col md:flex-row gap-4 mb-8">
<div class="relative flex-grow">
<input type="text" id="searchInput" placeholder="Search notes..." class="w-full px-4 py-3 pl-10 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:text-white">
<i class="fas fa-search absolute left-3 top-3.5 text-gray-400"></i>
</div>
<button id="addNoteBtn" class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors font-medium flex items-center justify-center">
<i class="fas fa-plus mr-2"></i> Add Note
</button>
</div>
<!-- Notes Grid -->
<div id="notesContainer" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
<!-- Notes will be added here dynamically -->
</div>
<!-- Empty State -->
<div id="emptyState" class="text-center py-16">
<i class="fas fa-sticky-note text-gray-300 dark:text-gray-700 text-6xl mb-4"></i>
<h3 class="text-xl font-medium text-gray-500 dark:text-gray-400 mb-2">No notes yet</h3>
<p class="text-gray-400 dark:text-gray-500 mb-4">Click "Add Note" to create your first note</p>
</div>
</div>
<!-- Note Editor Modal -->
<div id="noteEditorModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 hidden">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] flex flex-col">
<div class="flex justify-between items-center p-4 border-b border-gray-200 dark:border-gray-700">
<h3 class="text-lg font-medium text-gray-800 dark:text-white" id="editorTitle">New Note</h3>
<button id="closeEditorBtn" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<div class="p-4 flex-grow overflow-y-auto">
<input type="text" id="noteTitleInput" placeholder="Title" class="w-full px-4 py-3 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:text-white mb-4 font-medium">
<textarea id="noteContentInput" placeholder="Write your note here..." class="w-full px-4 py-3 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:text-white min-h-[200px]"></textarea>
<div class="mt-4 flex items-center">
<input type="checkbox" id="notePinnedInput" class="custom-checkbox mr-2">
<label for="notePinnedInput" class="text-gray-700 dark:text-gray-300 cursor-pointer">Pin this note</label>
</div>
</div>
<div class="p-4 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-3">
<button id="cancelNoteBtn" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
Cancel
</button>
<button id="saveNoteBtn" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors">
Save
</button>
</div>
</div>
</div>
<!-- Confirmation Modal -->
<div id="confirmModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 hidden">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<div class="flex items-center mb-4">
<i class="fas fa-exclamation-triangle text-yellow-500 text-2xl mr-3"></i>
<h3 class="text-lg font-medium text-gray-800 dark:text-white" id="confirmTitle">Confirm Action</h3>
</div>
<p class="text-gray-600 dark:text-gray-400 mb-6" id="confirmMessage">Are you sure you want to perform this action?</p>
<div class="flex justify-end space-x-3">
<button id="cancelConfirmBtn" class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
Cancel
</button>
<button id="confirmActionBtn" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors">
Confirm
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const notesContainer = document.getElementById('notesContainer');
const emptyState = document.getElementById('emptyState');
const addNoteBtn = document.getElementById('addNoteBtn');
const clearAllBtn = document.getElementById('clearAllBtn');
const searchInput = document.getElementById('searchInput');
const themeToggle = document.getElementById('themeToggle');
// Modal Elements
const noteEditorModal = document.getElementById('noteEditorModal');
const editorTitle = document.getElementById('editorTitle');
const noteTitleInput = document.getElementById('noteTitleInput');
const noteContentInput = document.getElementById('noteContentInput');
const notePinnedInput = document.getElementById('notePinnedInput');
const closeEditorBtn = document.getElementById('closeEditorBtn');
const cancelNoteBtn = document.getElementById('cancelNoteBtn');
const saveNoteBtn = document.getElementById('saveNoteBtn');
// Confirmation Modal Elements
const confirmModal = document.getElementById('confirmModal');
const confirmTitle = document.getElementById('confirmTitle');
const confirmMessage = document.getElementById('confirmMessage');
const cancelConfirmBtn = document.getElementById('cancelConfirmBtn');
const confirmActionBtn = document.getElementById('confirmActionBtn');
// App State
let notes = JSON.parse(localStorage.getItem('notes')) || [];
let currentNoteId = null;
let actionToConfirm = null;
let searchTerm = '';
// Initialize the app
function init() {
renderNotes();
updateEmptyState();
updateClearAllButton();
// Check for saved theme preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
}
// Render notes to the UI
function renderNotes() {
notesContainer.innerHTML = '';
// Filter notes based on search term
const filteredNotes = notes.filter(note =>
note.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
note.content.toLowerCase().includes(searchTerm.toLowerCase())
);
// Separate pinned and unpinned notes
const pinnedNotes = filteredNotes.filter(note => note.pinned);
const unpinnedNotes = filteredNotes.filter(note => !note.pinned);
// Render pinned notes first
if (pinnedNotes.length > 0) {
const pinnedSection = document.createElement('div');
pinnedSection.className = 'col-span-full mb-2';
pinnedSection.innerHTML = `
<h3 class="text-sm font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2 flex items-center">
<i class="fas fa-thumbtack mr-2"></i> Pinned Notes
</h3>
`;
notesContainer.appendChild(pinnedSection);
pinnedNotes.forEach(note => {
notesContainer.appendChild(createNoteElement(note));
});
// Add divider if there are both pinned and unpinned notes
if (unpinnedNotes.length > 0) {
const divider = document.createElement('div');
divider.className = 'col-span-full border-t border-gray-200 dark:border-gray-700 my-4';
notesContainer.appendChild(divider);
}
}
// Render unpinned notes
unpinnedNotes.forEach(note => {
notesContainer.appendChild(createNoteElement(note));
});
}
// Create a note element for the DOM
function createNoteElement(note) {
const noteElement = document.createElement('div');
noteElement.className = `note-animation bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden border border-gray-200 dark:border-gray-700 flex flex-col h-full ${note.pinned ? 'border-l-4 border-l-blue-500' : ''}`;
noteElement.dataset.id = note.id;
// Format the date
const date = new Date(note.updatedAt);
const formattedDate = date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
noteElement.innerHTML = `
<div class="p-4 flex-grow">
<div class="flex justify-between items-start mb-2">
<h3 class="font-medium text-gray-800 dark:text-white truncate">${note.title}</h3>
<button class="pin-btn text-gray-400 hover:text-blue-500 ml-2" data-id="${note.id}">
<i class="fas ${note.pinned ? 'fa-thumbtack text-blue-500' : 'fa-thumbtack'}"></i>
</button>
</div>
<p class="text-gray-600 dark:text-gray-400 text-sm mb-4 line-clamp-3">${note.content}</p>
<div class="text-xs text-gray-400 dark:text-gray-500">${formattedDate}</div>
</div>
<div class="px-4 py-3 bg-gray-50 dark:bg-gray-700 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-2">
<button class="edit-btn px-3 py-1 text-sm text-blue-500 hover:text-blue-700 dark:hover:text-blue-400">
<i class="fas fa-edit mr-1"></i> Edit
</button>
<button class="delete-btn px-3 py-1 text-sm text-red-500 hover:text-red-700 dark:hover:text-red-400">
<i class="fas fa-trash mr-1"></i> Delete
</button>
</div>
`;
return noteElement;
}
// Update empty state visibility
function updateEmptyState() {
if (notes.length === 0) {
emptyState.classList.remove('hidden');
} else {
emptyState.classList.add('hidden');
}
}
// Update clear all button visibility
function updateClearAllButton() {
if (notes.length > 0) {
clearAllBtn.classList.remove('hidden');
} else {
clearAllBtn.classList.add('hidden');
}
}
// Save notes to localStorage
function saveNotes() {
localStorage.setItem('notes', JSON.stringify(notes));
renderNotes();
updateEmptyState();
updateClearAllButton();
}
// Open note editor
function openNoteEditor(note = null) {
if (note) {
currentNoteId = note.id;
editorTitle.textContent = 'Edit Note';
noteTitleInput.value = note.title;
noteContentInput.value = note.content;
notePinnedInput.checked = note.pinned;
} else {
currentNoteId = null;
editorTitle.textContent = 'New Note';
noteTitleInput.value = '';
noteContentInput.value = '';
notePinnedInput.checked = false;
}
noteEditorModal.classList.remove('hidden');
noteTitleInput.focus();
}
// Close note editor
function closeNoteEditor() {
noteEditorModal.classList.add('hidden');
currentNoteId = null;
}
// Save note
function saveNote() {
const title = noteTitleInput.value.trim();
const content = noteContentInput.value.trim();
const pinned = notePinnedInput.checked;
if (!title && !content) {
// Don't save empty notes
closeNoteEditor();
return;
}
const now = new Date();
if (currentNoteId) {
// Update existing note
const noteIndex = notes.findIndex(n => n.id === currentNoteId);
if (noteIndex !== -1) {
notes[noteIndex] = {
...notes[noteIndex],
title: title || 'Untitled Note',
content,
pinned,
updatedAt: now.getTime()
};
}
} else {
// Create new note
const newNote = {
id: Date.now().toString(),
title: title || 'Untitled Note',
content,
pinned,
createdAt: now.getTime(),
updatedAt: now.getTime()
};
notes.unshift(newNote);
}
saveNotes();
closeNoteEditor();
}
// Delete note
function deleteNote(id) {
notes = notes.filter(note => note.id !== id);
saveNotes();
}
// Toggle note pinned status
function togglePinned(id) {
const noteIndex = notes.findIndex(n => n.id === id);
if (noteIndex !== -1) {
notes[noteIndex].pinned = !notes[noteIndex].pinned;
notes[noteIndex].updatedAt = new Date().getTime();
saveNotes();
}
}
// Show confirmation dialog
function showConfirmation(title, message, action) {
confirmTitle.textContent = title;
confirmMessage.textContent = message;
actionToConfirm = action;
confirmModal.classList.remove('hidden');
}
// Clear all notes
function clearAllNotes() {
notes = [];
saveNotes();
}
// Toggle dark/light theme
function toggleTheme() {
const html = document.documentElement;
if (html.classList.contains('dark')) {
html.classList.remove('dark');
localStorage.setItem('theme', 'light');
} else {
html.classList.add('dark');
localStorage.setItem('theme', 'dark');
}
}
// Event Listeners
addNoteBtn.addEventListener('click', () => openNoteEditor());
closeEditorBtn.addEventListener('click', closeNoteEditor);
cancelNoteBtn.addEventListener('click', closeNoteEditor);
saveNoteBtn.addEventListener('click', saveNote);
// Handle clicks on notes container (event delegation)
notesContainer.addEventListener('click', (e) => {
const noteElement = e.target.closest('.note-animation');
if (!noteElement) return;
const noteId = noteElement.dataset.id;
const note = notes.find(n => n.id === noteId);
if (e.target.classList.contains('edit-btn') || e.target.closest('.edit-btn')) {
openNoteEditor(note);
} else if (e.target.classList.contains('delete-btn') || e.target.closest('.delete-btn')) {
showConfirmation(
'Delete Note',
'Are you sure you want to delete this note? This action cannot be undone.',
() => deleteNote(noteId)
);
} else if (e.target.classList.contains('pin-btn') || e.target.closest('.pin-btn')) {
togglePinned(noteId);
}
});
// Clear all notes confirmation
clearAllBtn.addEventListener('click', () => {
showConfirmation(
'Clear All Notes',
'Are you sure you want to delete all notes? This action cannot be undone.',
clearAllNotes
);
});
// Confirmation modal events
cancelConfirmBtn.addEventListener('click', () => {
confirmModal.classList.add('hidden');
actionToConfirm = null;
});
confirmActionBtn.addEventListener
</html>