Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 31 additions & 25 deletions src/components/LanguageSelector.astro
Original file line number Diff line number Diff line change
Expand Up @@ -61,39 +61,45 @@ const dropdownClass = isUp
</div>

<script>
document.querySelectorAll('[data-lang-wrapper]').forEach((wrapper) => {
const btn = wrapper.querySelector('[data-lang-btn]')
const dropdown = wrapper.querySelector('[data-lang-dropdown]')
const chevron = wrapper.querySelector('[data-lang-chevron]')
const isUp = wrapper.getAttribute('data-lang-direction') === 'up'
import { onHydration } from '../utils/hydration'

btn?.addEventListener('click', (e) => {
e.stopPropagation()
const isOpen = !dropdown?.classList.contains('hidden')
dropdown?.classList.toggle('hidden')
if (chevron && !isUp) {
chevron.classList.toggle('rotate-180', isOpen)
}
})
})
function initLangSelector() {
document.querySelectorAll('[data-lang-wrapper]').forEach((wrapper) => {
const btn = wrapper.querySelector('[data-lang-btn]')
const dropdown = wrapper.querySelector('[data-lang-dropdown]')
const chevron = wrapper.querySelector('[data-lang-chevron]')
const isUp = wrapper.getAttribute('data-lang-direction') === 'up'

document.addEventListener('click', () => {
document.querySelectorAll('[data-lang-dropdown]').forEach((dropdown) => {
dropdown.classList.add('hidden')
})
document.querySelectorAll('[data-lang-chevron]').forEach((chevron) => {
chevron.classList.remove('rotate-180')
btn?.addEventListener('click', (e) => {
e.stopPropagation()
const isOpen = !dropdown?.classList.contains('hidden')
dropdown?.classList.toggle('hidden')
if (chevron && !isUp) {
chevron.classList.toggle('rotate-180', isOpen)
}
})
})
})

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.addEventListener('click', () => {
document.querySelectorAll('[data-lang-dropdown]').forEach((dropdown) => {
dropdown.classList.add('hidden')
})
document.querySelectorAll('[data-lang-chevron]').forEach((chevron) => {
chevron.classList.remove('rotate-180')
})
}
})
})

document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('[data-lang-dropdown]').forEach((dropdown) => {
dropdown.classList.add('hidden')
})
document.querySelectorAll('[data-lang-chevron]').forEach((chevron) => {
chevron.classList.remove('rotate-180')
})
}
})
}

onHydration(initLangSelector)
</script>
44 changes: 25 additions & 19 deletions src/components/Sidebar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -136,27 +136,33 @@ import logoLight from '../assets/logo-light.png'
</aside>

<script>
const toggle = document.getElementById('theme-toggle')
const icon = toggle?.querySelector('.theme-icon')
const text = toggle?.querySelector('.theme-text')

function updateTheme() {
const isDark = document.documentElement.classList.contains('dark')
if (icon) {
icon.className = `theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`
}
if (text && toggle) {
text.textContent = isDark
? toggle.dataset.darkLabel || 'Light Mode'
: toggle.dataset.lightLabel || 'Dark Mode'
import { onHydration } from '../utils/hydration'

function initSidebarTheme() {
const toggle = document.getElementById('theme-toggle')
const icon = toggle?.querySelector('.theme-icon')
const text = toggle?.querySelector('.theme-text')

function updateTheme() {
const isDark = document.documentElement.classList.contains('dark')
if (icon) {
icon.className = `theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`
}
if (text && toggle) {
text.textContent = isDark
? toggle.dataset.darkLabel || 'Light Mode'
: toggle.dataset.lightLabel || 'Dark Mode'
}
}
}

toggle?.addEventListener('click', () => {
document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light')
toggle?.addEventListener('click', () => {
document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light')
updateTheme()
})

updateTheme()
})
}

updateTheme()
onHydration(initSidebarTheme)
</script>
96 changes: 51 additions & 45 deletions src/components/TopBar.astro
Original file line number Diff line number Diff line change
Expand Up @@ -84,64 +84,70 @@ const mappedArticles = rawArticles.map((a) => ({
</header>

<script define:vars={{ mappedArticles }}>
const mobileToggle = document.getElementById('mobile-theme-toggle')
const desktopToggle = document.getElementById('desktop-theme-toggle')
const mobileIcon = mobileToggle?.querySelector('.mobile-theme-icon')
const desktopIcon = desktopToggle?.querySelector('.desktop-theme-icon')

function updateMobileTheme() {
const isDark = document.documentElement.classList.contains('dark')
if (mobileIcon) {
mobileIcon.className = `mobile-theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`
import { onHydration } from '../utils/hydration'

function initTopBar() {
const mobileToggle = document.getElementById('mobile-theme-toggle')
const desktopToggle = document.getElementById('desktop-theme-toggle')
const mobileIcon = mobileToggle?.querySelector('.mobile-theme-icon')
const desktopIcon = desktopToggle?.querySelector('.desktop-theme-icon')

function updateMobileTheme() {
const isDark = document.documentElement.classList.contains('dark')
if (mobileIcon) {
mobileIcon.className = `mobile-theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`
}
if (desktopIcon) {
desktopIcon.className = `desktop-theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`
}
}
if (desktopIcon) {
desktopIcon.className = `desktop-theme-icon w-4 h-4 ${isDark ? 'i-lucide-sun' : 'i-lucide-moon'}`

function toggleTheme() {
document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light')
updateMobileTheme()
}
}

function toggleTheme() {
document.documentElement.classList.toggle('dark')
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? 'dark' : 'light')
updateMobileTheme()
}
mobileToggle?.addEventListener('click', toggleTheme)
desktopToggle?.addEventListener('click', toggleTheme)

mobileToggle?.addEventListener('click', toggleTheme)
desktopToggle?.addEventListener('click', toggleTheme)
const searchInput = document.getElementById('search-input')
const searchResults = document.getElementById('search-results')

const searchInput = document.getElementById('search-input')
const searchResults = document.getElementById('search-results')
const articles = mappedArticles

const articles = mappedArticles
searchInput?.addEventListener('input', (e) => {
const query = e.target.value
if (query.length < 2) {
searchResults?.classList.add('hidden')
return
}

searchInput?.addEventListener('input', (e) => {
const query = e.target.value
if (query.length < 2) {
searchResults?.classList.add('hidden')
return
}
const filtered = articles.filter((a) => a.title.toLowerCase().includes(query.toLowerCase())).slice(0, 5)

const filtered = articles.filter((a) => a.title.toLowerCase().includes(query.toLowerCase())).slice(0, 5)

if (filtered.length > 0 && searchResults) {
searchResults.innerHTML = filtered
.map(
(a) => `
if (filtered.length > 0 && searchResults) {
searchResults.innerHTML = filtered
.map(
(a) => `
<a href="/articles/${a.id}" class="block w-full text-left px-4 py-3 text-sm hover:bg-muted dark:hover:bg-muted-dark transition-colors border-b border-border/50 dark:border-border-dark/50 last:border-0 text-foreground dark:text-foreground-dark">
<div class="font-medium">${a.title}</div>
<div class="text-xs text-muted-foreground dark:text-muted-dark-foreground mt-0.5">${a.category}</div>
</a>
`,
)
.join('')
searchResults.classList.remove('hidden')
} else {
searchResults?.classList.add('hidden')
}
})
)
.join('')
searchResults.classList.remove('hidden')
} else {
searchResults?.classList.add('hidden')
}
})

searchInput?.addEventListener('blur', () => {
setTimeout(() => searchResults?.classList.add('hidden'), 200)
})

searchInput?.addEventListener('blur', () => {
setTimeout(() => searchResults?.classList.add('hidden'), 200)
})
updateMobileTheme()
}

updateMobileTheme()
onHydration(initTopBar)
</script>
Loading
Loading