236 lines
6.0 KiB
TypeScript
236 lines
6.0 KiB
TypeScript
import { Alpine } from "./alpine";
|
|
// Theme management functionality
|
|
|
|
type ThemeType =
|
|
| "tokyo-night"
|
|
| "dracula"
|
|
| "nord"
|
|
| "solarized-dark"
|
|
| "monokai"
|
|
| "one-dark-pro"
|
|
| "material-dark"
|
|
| "catppuccin-mocha"
|
|
| "catppuccin-macchiato"
|
|
| "catppuccin-frappe"
|
|
| "catppuccin-latte";
|
|
|
|
const DEFAULT_THEME: ThemeType = "tokyo-night";
|
|
const THEME_STORAGE_KEY = "theme";
|
|
const TOKEN_STORAGE_KEY = "token";
|
|
|
|
// Apply theme to document body
|
|
const applyTheme = (theme: string): void => {
|
|
// Apply regular theme only
|
|
document.body.className = `theme-${theme}`;
|
|
document.body.style.background = "";
|
|
document.body.style.backgroundSize = "";
|
|
document.body.style.backgroundAttachment = "";
|
|
|
|
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
};
|
|
|
|
// Load theme from localStorage or use default
|
|
const loadTheme = (): void => {
|
|
const storedTheme = localStorage.getItem(
|
|
THEME_STORAGE_KEY,
|
|
) as ThemeType | null;
|
|
const theme = storedTheme || DEFAULT_THEME;
|
|
applyTheme(theme);
|
|
};
|
|
|
|
// Change: Remove DOM element lookup, accept theme as parameter
|
|
const changeTheme = async (theme: string): Promise<void> => {
|
|
applyTheme(theme);
|
|
|
|
// Save to server if logged in
|
|
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
|
if (!token) return;
|
|
|
|
try {
|
|
const response = await fetch("/api/auth/theme", {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
body: JSON.stringify({ theme }),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.log("Theme save failed");
|
|
}
|
|
} catch (error) {
|
|
console.log("Theme save failed", error);
|
|
}
|
|
};
|
|
|
|
// Load user's theme from server if logged in
|
|
const loadUserTheme = async (): Promise<void> => {
|
|
const token = localStorage.getItem(TOKEN_STORAGE_KEY);
|
|
if (!token) return;
|
|
|
|
try {
|
|
const response = await fetch("/api/auth/profile", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
if (data.theme) {
|
|
applyTheme(data.theme as string);
|
|
}
|
|
}
|
|
} catch {
|
|
// Silently fail - user will get default theme
|
|
}
|
|
};
|
|
|
|
// Initialize theme system
|
|
const initializeTheme = (): void => {
|
|
loadTheme();
|
|
loadUserTheme();
|
|
|
|
// Set theme select value to current theme
|
|
const themeSelect = document.getElementById(
|
|
"theme-select",
|
|
) as HTMLSelectElement;
|
|
if (themeSelect) {
|
|
const currentTheme =
|
|
localStorage.getItem(THEME_STORAGE_KEY) || DEFAULT_THEME;
|
|
themeSelect.value = currentTheme;
|
|
}
|
|
|
|
// Setup smooth scroll for anchor links
|
|
setupSmoothScroll();
|
|
};
|
|
|
|
// Setup smooth scrolling for anchor links
|
|
const setupSmoothScroll = (): void => {
|
|
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
|
|
anchor.addEventListener("click", (e) => {
|
|
e.preventDefault();
|
|
const href = anchor.getAttribute("href");
|
|
if (!href) return;
|
|
|
|
const target = document.querySelector(href);
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "start",
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// Wood paneling management functionality
|
|
|
|
type WoodPanelingType = "none" | "wood-light" | "wood-dark" | "wood-mahogany";
|
|
|
|
const WOOD_STORAGE_KEY = "wood-paneling";
|
|
|
|
// Apply wood paneling to collections container
|
|
const applyWoodPaneling = (paneling: WoodPanelingType): void => {
|
|
const container = document.getElementById("collections-container");
|
|
const body = document.body;
|
|
if (!container || !body) return;
|
|
|
|
// Remove all wood background classes from container
|
|
container.classList.remove(
|
|
"bg-wood-light",
|
|
"bg-wood-dark",
|
|
"bg-wood-mahogany",
|
|
);
|
|
container.removeAttribute("data-wood");
|
|
|
|
// Remove from body
|
|
body.classList.remove("bg-wood-light", "bg-wood-dark", "bg-wood-mahogany");
|
|
|
|
if (paneling !== "none") {
|
|
// Add selected wood background class to both container and body
|
|
const woodClass = `bg-${paneling}`;
|
|
container.classList.add(woodClass);
|
|
container.setAttribute("data-wood", paneling);
|
|
|
|
// Add to body for overscroll area
|
|
body.classList.add(woodClass);
|
|
}
|
|
|
|
// Save to localStorage
|
|
localStorage.setItem(WOOD_STORAGE_KEY, paneling);
|
|
};
|
|
|
|
// Load wood paneling from localStorage on page load
|
|
const loadWoodPaneling = (): void => {
|
|
const stored = localStorage.getItem(
|
|
WOOD_STORAGE_KEY,
|
|
) as WoodPanelingType | null;
|
|
if (stored) {
|
|
applyWoodPaneling(stored);
|
|
} else {
|
|
// Default to none
|
|
applyWoodPaneling("none");
|
|
}
|
|
};
|
|
|
|
// Change wood paneling (called from theme dropdown)
|
|
const changeWoodPaneling = (paneling: WoodPanelingType): void => {
|
|
applyWoodPaneling(paneling);
|
|
|
|
// Update active indicators
|
|
updateWoodPanelingIndicators();
|
|
|
|
// Alpine closes dropdown automatically via template state
|
|
};
|
|
|
|
// Update visual indicators for wood paneling buttons
|
|
const updateWoodPanelingIndicators = (): void => {
|
|
const currentWood = localStorage.getItem(WOOD_STORAGE_KEY) || "none";
|
|
|
|
// Update wood paneling buttons
|
|
document.querySelectorAll(".wood-paneling-btn").forEach((btn) => {
|
|
const wood = btn.getAttribute("data-wood");
|
|
if (wood === currentWood) {
|
|
// Active state - use CSS class instead of inline style
|
|
btn.classList.add("bg-wood-active");
|
|
btn.classList.remove("bg-wood-inactive");
|
|
} else {
|
|
// Inactive state
|
|
btn.classList.remove("bg-wood-active");
|
|
btn.classList.add("bg-wood-inactive");
|
|
}
|
|
});
|
|
};
|
|
|
|
// Auto-initialize when DOM is ready
|
|
if (typeof document !== "undefined") {
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initializeTheme();
|
|
loadWoodPaneling();
|
|
updateWoodPanelingIndicators();
|
|
});
|
|
} else {
|
|
initializeTheme();
|
|
loadWoodPaneling();
|
|
updateWoodPanelingIndicators();
|
|
}
|
|
}
|
|
|
|
Alpine.store("theme", {
|
|
changeTheme,
|
|
changeWoodPaneling,
|
|
});
|
|
|
|
export {
|
|
applyTheme,
|
|
changeTheme,
|
|
changeWoodPaneling,
|
|
initializeTheme,
|
|
loadTheme,
|
|
loadUserTheme,
|
|
loadWoodPaneling,
|
|
updateWoodPanelingIndicators,
|
|
};
|
|
export type { ThemeType };
|