Files
chatwoot/app/javascript/portal/portalThemeHelper.js

144 lines
4.6 KiB
JavaScript

import { adjustColorForContrast } from '../shared/helpers/colorHelper.js';
const getResolvedTheme = theme => {
// Helper to get resolved theme (handles 'system' -> 'dark'/'light')
if (theme === 'system') {
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
return theme;
};
export const setPortalHoverColor = theme => {
// This function is to set the hover color for the portal
const resolvedTheme = getResolvedTheme(theme);
const portalColor = window.portalConfig.portalColor;
const bgColor = resolvedTheme === 'dark' ? '#151718' : 'white';
const hoverColor = adjustColorForContrast(portalColor, bgColor);
// Set hover color for border and text dynamically
document.documentElement.style.setProperty(
'--dynamic-hover-color',
hoverColor
);
};
export const removeQueryParamsFromUrl = (queryParam = 'theme') => {
// This function is to remove the theme query param from the URL
// This is done so that the theme is not persisted in the URL
// This is called when the theme is switched from the dropdown
const url = new URL(window.location.href);
const param = url.searchParams.get(queryParam);
if (param) {
url.searchParams.delete(queryParam);
window.history.replaceState({}, '', url.toString()); // Convert URL to string
}
};
export const updateThemeInHeader = theme => {
// This function is to update the theme selection in the header in real time
const themeToggleButton = document.getElementById('toggle-appearance');
if (!themeToggleButton) return;
const allThemeButtons = themeToggleButton.querySelectorAll('.theme-button');
if (!allThemeButtons.length) return;
allThemeButtons.forEach(button => {
const isActive = button.dataset.theme === theme;
button.classList.toggle('hidden', !isActive);
button.classList.toggle('flex', isActive);
});
};
export const switchTheme = theme => {
// Update localStorage
if (theme === 'system') {
localStorage.removeItem('theme');
} else {
localStorage.theme = theme;
}
const resolvedTheme = getResolvedTheme(theme);
document.documentElement.classList.remove('dark', 'light');
document.documentElement.classList.add(resolvedTheme);
setPortalHoverColor(theme);
updateThemeInHeader(theme);
removeQueryParamsFromUrl();
// Update both dropdown data attributes
document.querySelectorAll('.appearance-menu').forEach(menu => {
menu.dataset.currentTheme = theme;
});
};
export const initializeThemeHandlers = () => {
const toggle = document.getElementById('toggle-appearance');
const dropdown = document.getElementById('appearance-dropdown');
if (!toggle || !dropdown) return;
// Toggle appearance dropdown
toggle.addEventListener('click', e => {
e.stopPropagation();
dropdown.dataset.dropdownOpen = String(
dropdown.dataset.dropdownOpen !== 'true'
);
});
document.addEventListener('click', ({ target }) => {
if (toggle.contains(target)) return;
const themeBtn = target.closest('.appearance-menu button[data-theme]');
const menu = themeBtn?.closest('.appearance-menu');
if (themeBtn && menu) {
switchTheme(themeBtn.dataset.theme);
menu.dataset.dropdownOpen = 'false';
if (menu.id === 'mobile-appearance-dropdown') {
// Set the mobile menu toggle to false after a delay to ensure the transition is completed
setTimeout(() => {
const mobileToggle = document.getElementById('mobile-menu-toggle');
if (mobileToggle) mobileToggle.checked = false;
}, 300);
}
return;
}
// Close the desktop appearance dropdown if clicked outside
if (
dropdown.dataset.dropdownOpen === 'true' &&
!dropdown.contains(target)
) {
dropdown.dataset.dropdownOpen = 'false';
}
});
};
export const initializeMediaQueryListener = () => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', () => {
if (['light', 'dark'].includes(localStorage.theme)) return;
switchTheme('system');
});
};
export const initializeTheme = () => {
if (window.portalConfig.isPlainLayoutEnabled === 'true') return;
// start with updating the theme in the header, this will set the current theme on the button
// and set the hover color at the start of init, this is set again when the theme is switched
switchTheme(localStorage.theme || 'system');
window.updateThemeInHeader = updateThemeInHeader;
// add the event listeners for the dropdown toggle and theme buttons
initializeThemeHandlers();
// add the media query listener to update the theme when the system theme changes
initializeMediaQueryListener();
};