blog/content/theme.js

132 lines
4.4 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
const sidebar = document.getElementById('sidebar');
const closeBtn = document.getElementById('close-sidebar');
const trigger = document.getElementById('read-more-trigger');
const body = document.body;
let manualClose = false;
// --- Sidebar Logic ---
function checkSidebarVisibility() {
if (manualClose) return;
const scrollTop = window.scrollY;
const windowHeight = window.innerHeight;
const docHeight = document.documentElement.scrollHeight;
// "Sidebar fix": Check if page is short (content fits in window or close to it)
// If content is short, sidebar should be visible immediately (if desktop)
if (docHeight <= windowHeight * 1.2 && window.innerWidth > 900) {
sidebar.classList.add('visible');
sidebar.style.opacity = 1;
sidebar.style.pointerEvents = 'auto';
return;
}
// Scroll Logic for longer pages
if (scrollTop + windowHeight > docHeight * 0.9) {
sidebar.classList.add('visible');
trigger.classList.add('visible');
// Dynamic Opacity near bottom
const remaining = docHeight - (scrollTop + windowHeight);
const threshold = docHeight * 0.1;
if (remaining < threshold) {
const opacity = 1 - (remaining / threshold);
sidebar.style.opacity = Math.min(Math.max(opacity, 0), 1);
if (opacity > 0) {
sidebar.style.pointerEvents = 'auto';
} else {
sidebar.style.pointerEvents = 'none';
}
} else {
// Should be redundant with visibility check but safe
}
} else {
// Hide if scrolled up?
// "It just is gone then... manual close... refresh if want it back"
// But scroll behavior implies transient visibility.
// We'll hide it if we scroll back up, unless it's short content.
sidebar.style.opacity = 0;
sidebar.style.pointerEvents = 'none';
}
}
window.addEventListener('scroll', checkSidebarVisibility);
window.addEventListener('resize', checkSidebarVisibility);
// Initial check
setTimeout(checkSidebarVisibility, 100); // Slight delay for rendering
// Mobile Trigger
trigger.addEventListener('click', () => {
sidebar.classList.add('force-visible');
sidebar.style.opacity = 1;
sidebar.style.pointerEvents = 'auto';
});
// Close Button
closeBtn.addEventListener('click', () => {
sidebar.classList.remove('visible');
sidebar.classList.remove('force-visible');
sidebar.style.opacity = 0;
sidebar.style.pointerEvents = 'none';
manualClose = true;
trigger.style.display = 'none';
});
// --- Dark Mode Logic ---
// Create Toggle Button
const sidebarContent = document.querySelector('.sidebar-content');
const themeBtn = document.createElement('button');
themeBtn.id = 'theme-toggle';
themeBtn.textContent = 'Toggle Theme';
sidebarContent.appendChild(themeBtn);
function setDarkMode(enable) {
if (enable) {
body.classList.add('dark-mode');
themeBtn.textContent = 'Switch to Light Mode';
} else {
body.classList.remove('dark-mode');
themeBtn.textContent = 'Switch to Dark Mode';
}
}
function getCookie(name) {
const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
return v ? v[2] : null;
}
function setCookie(name, value, hours) {
const d = new Date();
d.setTime(d.getTime() + (hours * 60 * 60 * 1000));
document.cookie = name + "=" + value + ";path=/;expires=" + d.toUTCString();
}
// Init Dark Mode
const cookieTheme = getCookie('theme');
if (cookieTheme === 'dark') {
setDarkMode(true);
} else if (cookieTheme === 'light') {
setDarkMode(false);
} else {
// Time based auto-detect
const hour = new Date().getHours();
if (hour < 6 || hour >= 18) {
setDarkMode(true);
} else {
setDarkMode(false);
}
}
themeBtn.addEventListener('click', () => {
const isDark = body.classList.contains('dark-mode');
setDarkMode(!isDark);
setCookie('theme', !isDark ? 'dark' : 'light', 24);
});
});