major update in looks and feel
This commit is contained in:
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user