Files
docupulse/static/js/settings.js
2025-05-28 16:01:18 +02:00

253 lines
9.6 KiB
JavaScript

/**
* @fileoverview Manages the application settings functionality.
* This file handles color settings, tab persistence, and UI customization.
* It provides functionality to:
* - Manage primary and secondary color schemes
* - Handle settings tab navigation and persistence
* - Convert and manipulate colors (hex, RGB)
* - Update UI elements with new color schemes
*/
document.addEventListener('DOMContentLoaded', function() {
const primaryColorInput = document.getElementById('primaryColor');
const secondaryColorInput = document.getElementById('secondaryColor');
const colorSettingsForm = document.getElementById('colorSettingsForm');
// Tab persistence
const settingsTabs = document.querySelectorAll('#settingsTabs button[data-bs-toggle="tab"]');
const tabContent = document.querySelectorAll('.tab-pane');
/**
* Activates a specific settings tab and updates the UI accordingly.
* Also persists the selected tab in localStorage.
* @function
* @param {string} tabId - The ID of the tab to activate
*/
function activateTab(tabId) {
// Remove active class from all tabs and content
settingsTabs.forEach(tab => {
tab.classList.remove('active');
tab.setAttribute('aria-selected', 'false');
});
tabContent.forEach(content => {
content.classList.remove('show', 'active');
});
// Activate the selected tab
const selectedTab = document.querySelector(`#${tabId}-tab`);
const selectedContent = document.getElementById(tabId);
if (selectedTab && selectedContent) {
selectedTab.classList.add('active');
selectedTab.setAttribute('aria-selected', 'true');
selectedContent.classList.add('show', 'active');
// Save to localStorage
localStorage.setItem('settingsActiveTab', tabId);
}
}
// Add click event listeners to tabs
settingsTabs.forEach(tab => {
tab.addEventListener('click', (e) => {
const tabId = e.target.getAttribute('data-bs-target').substring(1);
activateTab(tabId);
});
});
// Restore active tab from localStorage or URL hash
const savedTab = localStorage.getItem('settingsActiveTab') || window.location.hash.substring(1) || 'colors';
activateTab(savedTab);
/**
* Converts a hexadecimal color code to RGB values.
* @function
* @param {string} hex - The hexadecimal color code (e.g., '#FF0000')
* @returns {Object|null} Object containing r, g, b values or null if invalid hex
*/
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
/**
* Converts RGB values to a hexadecimal color code.
* @function
* @param {number} r - Red value (0-255)
* @param {number} g - Green value (0-255)
* @param {number} b - Blue value (0-255)
* @returns {string} Hexadecimal color code
*/
function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(x => {
const hex = x.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}).join('');
}
/**
* Lightens a color by a specified amount.
* @function
* @param {string} color - The hexadecimal color to lighten
* @param {number} amount - The amount to lighten (0-1)
* @returns {string} The lightened hexadecimal color
*/
function lightenColor(color, amount) {
const rgb = hexToRgb(color);
if (!rgb) return color;
return rgbToHex(
Math.min(255, Math.round(rgb.r + (255 - rgb.r) * amount)),
Math.min(255, Math.round(rgb.g + (255 - rgb.g) * amount)),
Math.min(255, Math.round(rgb.b + (255 - rgb.b) * amount))
);
}
/**
* Darkens a color by a specified amount.
* @function
* @param {string} color - The hexadecimal color to darken
* @param {number} amount - The amount to darken (0-1)
* @returns {string} The darkened hexadecimal color
*/
function darkenColor(color, amount) {
const rgb = hexToRgb(color);
if (!rgb) return color;
return rgbToHex(
Math.max(0, Math.round(rgb.r * (1 - amount))),
Math.max(0, Math.round(rgb.g * (1 - amount))),
Math.max(0, Math.round(rgb.b * (1 - amount)))
);
}
/**
* Updates all color-related UI elements based on a new color value.
* Updates CSS variables, chart colors, and color previews.
* @function
* @param {string} color - The new color value in hexadecimal
* @param {boolean} isPrimary - Whether this is the primary color (true) or secondary color (false)
*/
function updateAllColors(color, isPrimary) {
const prefix = isPrimary ? 'primary' : 'secondary';
// Calculate derived colors
const lightColor = lightenColor(color, 0.15);
const bgLightColor = lightenColor(color, 0.9);
const opacity15 = color + '26'; // 15% opacity in hex
// Calculate chart colors
const chartColors = {
base: color,
light: lightenColor(color, 0.2),
lighter: lightenColor(color, 0.4),
lightest: lightenColor(color, 0.6),
pale: lightenColor(color, 0.8)
};
// Update CSS variables for the entire website
document.documentElement.style.setProperty(`--${prefix}-color`, color);
document.documentElement.style.setProperty(`--${prefix}-light`, lightColor);
document.documentElement.style.setProperty(`--${prefix}-bg-light`, bgLightColor);
document.documentElement.style.setProperty(`--${prefix}-opacity-15`, opacity15);
// Update chart color variables
document.documentElement.style.setProperty(`--chart-${prefix}`, chartColors.base);
document.documentElement.style.setProperty(`--chart-${prefix}-light`, chartColors.light);
document.documentElement.style.setProperty(`--chart-${prefix}-lighter`, chartColors.lighter);
document.documentElement.style.setProperty(`--chart-${prefix}-lightest`, chartColors.lightest);
document.documentElement.style.setProperty(`--chart-${prefix}-pale`, chartColors.pale);
// Update derived colors in the preview section
const derivedColorsSection = document.getElementById(`${prefix}DerivedColors`);
if (derivedColorsSection) {
const previews = derivedColorsSection.querySelectorAll('.color-preview');
previews.forEach(preview => {
const colorType = preview.getAttribute('data-color-type');
let previewColor;
switch (colorType) {
case 'base':
previewColor = color;
break;
case 'light':
previewColor = lightColor;
break;
case 'bg-light':
previewColor = bgLightColor;
break;
case 'opacity':
previewColor = opacity15;
break;
}
preview.style.backgroundColor = previewColor;
});
}
// Update the color input value
if (isPrimary) {
primaryColorInput.value = color;
} else {
secondaryColorInput.value = color;
}
}
// Event listeners for color inputs
primaryColorInput.addEventListener('change', () => {
updateAllColors(primaryColorInput.value, true);
});
secondaryColorInput.addEventListener('change', () => {
updateAllColors(secondaryColorInput.value, false);
});
// Also listen for input events for real-time updates
primaryColorInput.addEventListener('input', () => {
updateAllColors(primaryColorInput.value, true);
});
secondaryColorInput.addEventListener('input', () => {
updateAllColors(secondaryColorInput.value, false);
});
/**
* Initializes the color settings from the current CSS variables.
* Updates input values and color previews.
* @function
*/
function initializeColors() {
// Get the current computed CSS variable values
const computedPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim();
const computedSecondaryColor = getComputedStyle(document.documentElement).getPropertyValue('--secondary-color').trim();
console.log('[Settings] Initializing colors:', {
primary: computedPrimaryColor,
secondary: computedSecondaryColor,
timestamp: new Date().toISOString()
});
// Update input values
primaryColorInput.value = computedPrimaryColor;
secondaryColorInput.value = computedSecondaryColor;
// Update all color previews
updateAllColors(computedPrimaryColor, true);
updateAllColors(computedSecondaryColor, false);
}
// Initialize colors when the page loads
initializeColors();
// Handle form submission
if (colorSettingsForm) {
colorSettingsForm.addEventListener('submit', function(e) {
console.log('[Settings] Form submitted with values:', {
primary: primaryColorInput.value,
secondary: secondaryColorInput.value,
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
timestamp: new Date().toISOString()
});
});
}
});