major update in looks and feel

This commit is contained in:
becarta
2025-03-06 00:03:16 +01:00
parent 3b98eae137
commit d66123c029
40 changed files with 1123 additions and 422 deletions

View File

@@ -20,11 +20,11 @@ const languages = [
const currentLanguage = languages.find(lang => lang.code === currentLang) || languages[0];
---
<div class="relative inline-block text-left">
<div class="relative inline-block text-left language-dropdown">
<div>
<button
type="button"
class="inline-flex justify-center w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-800 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus-visible:ring-4 transition-colors duration-200"
class="inline-flex justify-center w-full rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-gray-800 focus:ring-indigo-500 dark:focus:ring-indigo-400 focus-visible:ring-4 transition-colors duration-200 dropdown-button"
id="menu-button"
aria-expanded="false"
aria-haspopup="true"
@@ -42,7 +42,7 @@ const currentLanguage = languages.find(lang => lang.code === currentLang) || lan
</div>
<div
class="hidden absolute left-0 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 dark:ring-gray-600 focus:outline-none transform opacity-0 scale-95 transition-all duration-200 max-h-[300px] overflow-y-auto w-full"
class="absolute left-0 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 dark:ring-gray-600 focus:outline-none transform opacity-0 scale-95 transition-all duration-200 max-h-[300px] overflow-y-auto w-full language-menu"
role="menu"
aria-orientation="vertical"
aria-labelledby="menu-button"
@@ -66,26 +66,52 @@ const currentLanguage = languages.find(lang => lang.code === currentLang) || lan
))}
</div>
</div>
<select id="language-select" class="block w-full text-left px-4 py-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white focus:bg-gray-100 dark:focus:bg-gray-700 focus:text-gray-900 dark:focus:text-white focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-indigo-400 transition-colors duration-200 rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 language-select">
{languages.map(lang => (
<option value={lang.code} selected={lang.code === currentLang}>
{lang.name}
</option>
))}
</select>
</div>
<style>
#language-menu.open-downward {
.language-dropdown {
@apply md:inline-block md:relative;
}
.language-select {
display: none;
}
@media (max-width: 767px) {
.language-dropdown {
display: none;
}
.language-select {
display: block;
}
}
.language-menu.open-downward {
top: 100%;
margin-top: 0.5rem;
transform-origin: top;
}
#language-menu.open-upward {
.language-menu.open-upward {
bottom: 100%;
margin-bottom: 0.5rem;
transform-origin: bottom;
}
#language-menu:not(.hidden).open-downward {
.language-menu:not(.hidden).open-downward {
animation: slideDown 0.2s ease-out forwards;
}
#language-menu:not(.hidden).open-upward {
.language-menu:not(.hidden).open-upward {
animation: slideUp 0.2s ease-out forwards;
}
@@ -110,14 +136,6 @@ const currentLanguage = languages.find(lang => lang.code === currentLang) || lan
transform: scale(1) translateY(0);
}
}
@media (max-width: 767px) {
.absolute.right-0.w-56.rounded-md.shadow-lg.bg-white.dark\:bg-gray-800.ring-1.ring-black.ring-opacity-5.dark\:ring-gray-600.focus\:outline-none.transform.opacity-0.scale-95.transition-all.duration-200.max-h-\[300px\].overflow-y-auto {
width: auto;
min-width: 120px;
max-width: calc(100vw - 6rem);
}
}
</style>
<script define:vars={{ supportedLanguages }}>
@@ -127,8 +145,9 @@ const currentLanguage = languages.find(lang => lang.code === currentLang) || lan
const chevronIcon = document.querySelector('#chevron-icon');
const selectedLanguageText = document.querySelector('#selected-language');
const languageButtons = document.querySelectorAll('[data-lang-code]');
const languageSelect = document.querySelector('#language-select');
if (!button || !menu || !chevronIcon || !selectedLanguageText) {
if (!button || !menu || !chevronIcon || !selectedLanguageText || !languageSelect) {
return;
}
@@ -280,41 +299,83 @@ const currentLanguage = languages.find(lang => lang.code === currentLang) || lan
// Construct the full URL
const newFullUrl = `${window.location.origin}${newUrl}`;
// Reload the page to ensure all content is updated to the new language
window.location.href = newFullUrl;
// Force a complete page reload to ensure all content is updated to the new language
// This bypasses any client-side caching and ensures a fresh server render
window.location.href = newFullUrl + '?t=' + Date.now();
});
});
// Close when clicking outside
document.addEventListener('click', (e) => {
const target = e.target;
if (isOpen && !menu.contains(target) && !button.contains(target)) {
closeMenu();
}
});
// Handle language selection from select element
languageSelect.addEventListener('change', (event) => {
const langCode = event.target.value;
if (!langCode) return;
// Handle keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && isOpen) {
closeMenu();
button.focus();
// Get current URL information
const currentUrl = new URL(window.location.href);
const currentPath = currentUrl.pathname.replace(/\/$/, '');
const currentHash = currentUrl.hash;
const pathSegments = currentPath.split('/').filter(Boolean);
// Check if we're on a language-specific path
const isLangPath = supportedLanguages.includes(pathSegments[0]);
// Get the previous language code
const previousLangCode = isLangPath ? pathSegments[0] : 'en';
// Extract the page path without language
let pagePath = '';
if (isLangPath && pathSegments.length > 1) {
// If we're on a language-specific path, get everything after the language code
pagePath = `/${pathSegments.slice(1).join('/')}`;
} else if (!isLangPath && pathSegments.length > 0) {
// If we're not on a language-specific path, use the current path
pagePath = `/${pathSegments.join('/')}`;
}
// Enhanced keyboard navigation with arrow keys
if (isOpen && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
e.preventDefault();
const menuItems = Array.from(menu.querySelectorAll('button[role="menuitem"]'));
const currentIndex = menuItems.findIndex(item => item === document.activeElement);
let newIndex;
if (e.key === 'ArrowDown') {
newIndex = currentIndex < menuItems.length - 1 ? currentIndex + 1 : 0;
} else {
newIndex = currentIndex > 0 ? currentIndex - 1 : menuItems.length - 1;
}
menuItems[newIndex].focus();
// Handle special case for root path
const isRootPath = pathSegments.length === 0 || (isLangPath && pathSegments.length === 1);
// Construct the new URL
let newUrl = isRootPath ? `/${langCode}` : `/${langCode}${pagePath}`;
// Clean up any potential double slashes
newUrl = newUrl.replace(/\/+/g, '/');
// Append hash fragment if it exists
if (currentHash) {
newUrl += currentHash;
}
// Store the language preference in localStorage and cookies
if (window.languageUtils) {
window.languageUtils.storeLanguagePreference(langCode);
} else {
// Fallback if languageUtils is not available
localStorage.setItem('preferredLanguage', langCode);
// Also set a cookie for server-side detection
const expirationDate = new Date();
expirationDate.setFullYear(expirationDate.getFullYear() + 1);
document.cookie = `preferredLanguage=${langCode}; expires=${expirationDate.toUTCString()}; path=/; SameSite=Lax`;
}
// Dispatch the language changed event
const reloadEvent = new CustomEvent('languageChanged', {
detail: {
langCode,
previousLangCode,
path: newUrl,
willReload: true
}
});
document.dispatchEvent(reloadEvent);
// Construct the full URL
const newFullUrl = `${window.location.origin}${newUrl}`;
// Force a complete page reload to ensure all content is updated to the new language
// This bypasses any client-side caching and ensures a fresh server render
window.location.href = newFullUrl + '?t=' + Date.now();
});
}