blog/src/components/Button.astro
2025-05-25 21:14:17 +01:00

122 lines
4.2 KiB
Text

---
import LanguageSwitcherIcon from '@/assets/icons/language-switcher.svg'
import ThemeToggleIcon from '@/assets/icons/theme-toggle.svg'
import { moreLocales, themeConfig } from '@/config'
import { getNextGlobalLangPath, getNextSupportedLangPath } from '@/i18n/path'
import { isPostPage, isTagPage } from '@/utils/page'
interface Props {
supportedLangs: string[]
}
const { light: { background: lightMode }, dark: { background: darkMode } } = themeConfig.color
const reduceMotion = themeConfig.global.reduceMotion
const { supportedLangs } = Astro.props
const currentPath = Astro.url.pathname
const isPost = isPostPage(currentPath)
const isTag = isTagPage(currentPath)
// Check if there are other languages to switch
const showLanguageSwitcher = moreLocales.length > 0
// Check if only the supported language switch list is used
const useSupportedLangs = isPost || (isTag && supportedLangs.length > 0)
// Choose a language switch list according to the page type
const nextUrl = useSupportedLangs
? getNextSupportedLangPath(currentPath, supportedLangs) // Switch between supported languages in post/tag page
: getNextGlobalLangPath(currentPath) // Switch between all languages in other pages
---
<div
class:list={[
'absolute right-7.25vw top-14.6 flex gap-6 min-[823px]:max-[1024px]:right-[calc(50vw-22rem)]',
'lg:(fixed bottom-48 right-[max(5rem,calc(50vw-35rem))] top-auto w-14rem)',
]}
>
<!-- Language Switcher -->
{showLanguageSwitcher && (
<a
href={nextUrl}
class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
aria-label="Switch website language"
>
<LanguageSwitcherIcon
aria-hidden="true"
fill="currentColor"
/>
</a>
)}
<!-- Theme Toggle -->
<button
id="theme-toggle-button"
aria-label="Switch light/dark theme"
class="aspect-square w-4 c-secondary active:scale-90 hover:c-primary"
>
<ThemeToggleIcon
aria-hidden="true"
fill="currentColor"
/>
</button>
</div>
<!-- Theme Toggle Script >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<script
is:inline
define:vars={{ lightMode, darkMode, reduceMotion }}
>
// Update theme
function updateTheme() {
// Toggle website theme
document.documentElement.classList.toggle('dark')
// Get current theme
const isDark = document.documentElement.classList.contains('dark')
// Update meta theme color
const metaThemeColor = document.head.querySelector('meta[name="theme-color"]')
if (metaThemeColor) {
metaThemeColor.setAttribute('content', isDark ? darkMode : lightMode)
}
// Update theme configuration in local storage
localStorage.setItem('theme', isDark ? 'dark' : 'light')
document.dispatchEvent(new Event('theme-changed'))
}
// Bind click event to the button
function setupThemeToggle() {
// Add reduce-motion class if enabled in config
if (reduceMotion) {
document.documentElement.classList.add('reduce-motion')
}
// Locate theme toggle button
const themeToggleButton = document.getElementById('theme-toggle-button')
// Add click listener to the button
if (themeToggleButton) {
themeToggleButton.addEventListener('click', () => {
// If reduceMotion is enabled or browser doesn't support View Transitions API, update theme directly
if (reduceMotion || !document.startViewTransition) {
updateTheme()
return
}
// Temporarily add markers during animation to implement view transition and disable CSS transitions
document.documentElement.style.setProperty('view-transition-name', 'animation-theme-toggle')
document.documentElement.setAttribute('data-theme-changing', '')
// If browser supports View Transitions API, use it to update theme
const themeTransition = document.startViewTransition(updateTheme)
// Remove markers after animation
themeTransition.finished.then(() => {
document.documentElement.style.removeProperty('view-transition-name')
document.documentElement.removeAttribute('data-theme-changing')
})
})
}
}
// Initialize click event (on first load or page transition)
setupThemeToggle()
document.addEventListener('astro:after-swap', setupThemeToggle)
</script>