📄 Source: script.js
// Theme switching
// Theme switching
// ============ THEME SWITCHER ============
(function() {
// Load saved theme
const savedTheme = localStorage.getItem('app-theme') || 'light';
document.documentElement.setAttribute('data-theme', savedTheme);
// Function to update button states
function updateThemeButtons(activeTheme) {
document.querySelectorAll('.theme-btn').forEach(btn => {
const theme = btn.getAttribute('data-theme');
if (theme === activeTheme) {
btn.style.background = 'var(--info)';
btn.style.color = 'white';
btn.style.transform = 'scale(1.1)';
btn.style.boxShadow = '0 0 15px rgba(59, 130, 246, 0.3)';
} else {
btn.style.background = 'transparent';
btn.style.color = 'inherit';
btn.style.transform = 'scale(1)';
btn.style.boxShadow = 'none';
}
});
}
// Initial button states
updateThemeButtons(savedTheme);
// Add click handlers to all theme buttons
document.querySelectorAll('.theme-btn').forEach(btn => {
btn.addEventListener('click', function(e) {
e.preventDefault();
const theme = this.getAttribute('data-theme');
// Apply theme
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('app-theme', theme);
// Update button states
updateThemeButtons(theme);
// Also update any theme buttons in other parts of the page
// (in case they were loaded dynamically)
document.querySelectorAll('.theme-btn').forEach(otherBtn => {
const otherTheme = otherBtn.getAttribute('data-theme');
if (otherTheme === theme) {
otherBtn.style.background = 'var(--info)';
otherBtn.style.color = 'white';
otherBtn.style.transform = 'scale(1.1)';
otherBtn.style.boxShadow = '0 0 15px rgba(59, 130, 246, 0.3)';
} else {
otherBtn.style.background = 'transparent';
otherBtn.style.color = 'inherit';
otherBtn.style.transform = 'scale(1)';
otherBtn.style.boxShadow = 'none';
}
});
});
});
// Also respond to system theme changes (optional)
// Uncomment if you want to detect system theme changes
/*
const darkModeMedia = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMedia.addEventListener('change', (e) => {
if (!localStorage.getItem('app-theme')) {
const theme = e.matches ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', theme);
updateThemeButtons(theme);
}
});
*/
})();
// ============ AUTO-HIDE MESSAGES ============
document.querySelectorAll('.success-message, .error-message').forEach(msg => {
setTimeout(() => {
msg.style.transition = 'opacity 0.5s';
msg.style.opacity = '0';
setTimeout(() => msg.remove(), 500);
}, 5000);
});
// ============ CHARACTER COUNTER ============
function updateCharCounter(input, maxChars) {
const current = input.value.length;
const counter = input.parentElement.querySelector('.char-counter');
if (counter) {
counter.textContent = `${current} / ${maxChars}`;
counter.className = 'char-counter' + (current > maxChars * 0.9 ? ' warning' : '') + (current >= maxChars ? ' danger' : '');
}
}
// ============ COPY TO CLIPBOARD ============
function copyToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
showToast('✅ Copied to clipboard!');
}).catch(() => {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
showToast('✅ Copied to clipboard!');
}
// ============ TOAST NOTIFICATION ============
function showToast(message) {
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
background: var(--bg-card);
color: var(--text-primary);
padding: 0.8rem 1.5rem;
border-radius: 2rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
z-index: 9999;
font-weight: 600;
border: 1px solid var(--border-color);
animation: slideUp 0.3s ease;
`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.style.transition = 'opacity 0.3s';
toast.style.opacity = '0';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
// ============ KEYBOARD SHORTCUTS ============
document.addEventListener('keydown', function(e) {
// Ctrl+S to save (useful for forms)
if (e.ctrlKey && e.key === 's') {
const form = document.querySelector('form');
if (form) {
e.preventDefault();
form.submit();
}
}
});
// ============ CONFIRM DELETE ============
function confirmDelete(message = 'Are you sure you want to delete this?') {
return confirm(message);
}
// Auto-hide messages after 5 seconds
document.querySelectorAll('.success-message, .error-message').forEach(msg => {
setTimeout(() => {
msg.style.transition = 'opacity 0.5s';
msg.style.opacity = '0';
setTimeout(() => msg.remove(), 500);
}, 5000);
});
// Form validation helpers
function validateForm(formId) {
const form = document.getElementById(formId);
if (!form) return true;
let valid = true;
const requiredFields = form.querySelectorAll('[required]');
requiredFields.forEach(field => {
if (!field.value.trim()) {
field.style.borderColor = 'var(--error)';
valid = false;
} else {
field.style.borderColor = '';
}
});
return valid;
}
// Character counter for text inputs
function updateCharCounter(input, maxChars) {
const current = input.value.length;
const counter = input.parentElement.querySelector('.char-counter');
if (counter) {
counter.textContent = `${current} / ${maxChars}`;
counter.className = 'char-counter' + (current > maxChars * 0.9 ? ' warning' : '') + (current >= maxChars ? ' danger' : '');
}
}
// Confirm delete
function confirmDelete(message = 'Are you sure you want to delete this?') {
return confirm(message);
}
// Copy to clipboard
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
alert('Copied to clipboard!');
}).catch(() => {
// Fallback
const input = document.createElement('input');
input.value = text;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
alert('Copied to clipboard!');
});
}
← Back