Update implementation

This commit is contained in:
Svilen Markov
2025-05-04 16:49:20 +01:00
parent a2d8410fec
commit c2286f9a22
17 changed files with 583 additions and 402 deletions
+58 -107
View File
@@ -1,30 +1,7 @@
import { setupPopovers } from './popover.js';
import { setupMasonries } from './masonry.js';
import { throttledDebounce, isElementVisible, openURLInNewTab } from './utils.js';
document.addEventListener('DOMContentLoaded', () => {
const theme = localStorage.getItem('theme');
if (!theme) {
return;
}
const html = document.querySelector('html');
const jsonTheme = JSON.parse(theme);
if (jsonTheme.themeScheme === 'light') {
html.classList.remove('dark-scheme');
html.classList.add('light-scheme');
} else if (jsonTheme.themeScheme === 'dark') {
html.classList.add('dark-scheme');
html.classList.remove('light-scheme');
}
html.classList.add(jsonTheme.theme);
document.querySelector('[name=color-scheme]').setAttribute('content', jsonTheme.themeScheme);
Array.from(document.querySelectorAll('.dropdown-button span')).forEach((button) => {
button.textContent = jsonTheme.theme;
})
})
import { elem, find, findAll } from './templating.js';
async function fetchPageContent(pageData) {
// TODO: handle non 200 status codes/time outs
@@ -678,102 +655,77 @@ function setupTruncatedElementTitles() {
}
}
/**
* @typedef {Object} HslColorField
* @property {number} Hue
* @property {number} Saturation
* @property {number} Lightness
*/
async function changeTheme(key, onChanged) {
const themeStyleElem = find("#theme-style");
/**
* @typedef {Object} Theme
* @property {HslColorField} BackgroundColor
* @property {HslColorField} PrimaryColor
* @property {HslColorField} PositiveColor
* @property {HslColorField} NegativeColor
* @property {boolean} Light
* @property {number} ContrastMultiplier
* @property {number} TextSaturationMultiplier
*/
/**
* @typedef {Record<string, Theme>} ThemeCollection
*/
function setupThemeSwitcher() {
const presetsContainers = Array.from(document.querySelectorAll('.custom-presets'));
const userThemesKeys = Object.keys(userThemes);
presetsContainers.forEach((presetsContainer) => {
userThemesKeys.forEach(preset => {
const presetElement = document.createElement('div');
presetElement.className = 'theme-option';
presetElement.setAttribute('data-theme', preset);
presetElement.setAttribute('data-scheme', userThemes[preset].Light ? 'light' : 'dark');
presetElement.textContent = preset;
presetsContainer.appendChild(presetElement);
});
const response = await fetch(`${pageData.baseURL}/api/set-theme/${key}`, {
method: "POST",
});
const dropdownButtons = Array.from(document.querySelectorAll('.dropdown-button'));
const dropdownContents = Array.from(document.querySelectorAll('.dropdown-content'));
if (response.status != 200) {
alert("Failed to set theme: " + response.statusText);
return;
}
const newThemeStyle = await response.text();
dropdownButtons.forEach((dropdownButton) => {
dropdownButton.addEventListener('click', (e) => {
e.stopPropagation();
dropdownContents.forEach((dropdownContent) => {
dropdownContent.classList.toggle('show');
});
dropdownButton.classList.toggle('active');
});
});
const tempStyle = elem("style")
.html("* { transition: none !important; }")
.appendTo(document.head);
document.addEventListener('click', (e) => {
if (!e.target.closest('.theme-dropdown')) {
dropdownContents.forEach((dropdownContent) => {
dropdownContent.classList.remove('show');
});
dropdownButtons.forEach((dropdownButton) => {
dropdownButton.classList.remove('active');
});
themeStyleElem.html(newThemeStyle);
document.documentElement.setAttribute("data-scheme", response.headers.get("X-Scheme"));
typeof onChanged == "function" && onChanged();
setTimeout(() => { tempStyle.remove(); }, 10);
}
function initThemeSwitcher() {
find(".mobile-navigation .theme-choices").replaceWith(
find(".header-container .theme-choices").cloneNode(true)
);
const presetElems = findAll(".theme-choices .theme-preset");
let themePreviewElems = document.getElementsByClassName("current-theme-preview");
let isLoading = false;
presetElems.forEach((presetElement) => {
const themeKey = presetElement.dataset.key;
if (themeKey === undefined) {
return;
}
});
document.querySelectorAll('.theme-option').forEach(option => {
option.addEventListener('click', () => {
const selectedTheme = option.getAttribute('data-theme');
const selectedThemeScheme = option.getAttribute('data-scheme');
const previousTheme = localStorage.getItem('theme');
dropdownContents.forEach((dropdownContent) => {
dropdownContent.classList.remove('show');
});
dropdownButtons.forEach((dropdownButton) => {
const html = document.querySelector('html');
if (previousTheme) {
html.classList.remove(JSON.parse(previousTheme).theme);
}
dropdownButton.classList.remove('active');
dropdownButton.querySelector('span').textContent = option.textContent;
html.classList.add(selectedTheme);
if (themeKey == pageData.theme) {
presetElement.classList.add("current");
}
if (selectedThemeScheme === 'light') {
html.classList.remove('dark-scheme');
html.classList.add('light-scheme');
} else if (selectedThemeScheme === 'dark') {
html.classList.add('dark-scheme');
html.classList.remove('light-scheme');
}
presetElement.addEventListener("click", () => {
if (themeKey == pageData.theme) return;
if (isLoading) return;
document.querySelector('[name=color-scheme]').setAttribute('content', selectedThemeScheme);
localStorage.setItem('theme', JSON.stringify({
theme: selectedTheme,
themeScheme: selectedThemeScheme
}));
isLoading = true;
changeTheme(themeKey, function() {
isLoading = false;
pageData.theme = themeKey;
presetElems.forEach((e) => { e.classList.remove("current"); });
Array.from(themePreviewElems).forEach((preview) => {
preview.querySelector(".theme-preset").replaceWith(
presetElement.cloneNode(true)
);
})
presetElems.forEach((e) => {
if (e.dataset.key != themeKey) return;
e.classList.add("current");
});
});
});
});
})
}
async function setupPage() {
initThemeSwitcher();
const pageElement = document.getElementById("page");
const pageContentElement = document.getElementById("page-content");
const pageContent = await fetchPageContent(pageData);
@@ -781,7 +733,6 @@ async function setupPage() {
pageContentElement.innerHTML = pageContent;
try {
setupThemeSwitcher();
setupPopovers();
setupClocks()
await setupCalendars();