|
| 1 | +<script setup lang="ts"> |
| 2 | +const isVisible = ref(false) |
| 3 | +const isScrollable = ref(true) |
| 4 | +const lastScrollY = ref(0) |
| 5 | +const footerRef = ref<HTMLElement>() |
| 6 | +
|
| 7 | +function checkScrollable() { |
| 8 | + const mainContent = document.getElementById('main-content') |
| 9 | + if (!mainContent) return true |
| 10 | + return mainContent.scrollHeight > window.innerHeight |
| 11 | +} |
| 12 | +
|
| 13 | +function onScroll() { |
| 14 | + const currentY = window.scrollY |
| 15 | + const diff = lastScrollY.value - currentY |
| 16 | + const nearBottom = currentY + window.innerHeight >= document.documentElement.scrollHeight - 50 |
| 17 | +
|
| 18 | + // Scrolling UP or near bottom -> show |
| 19 | + if (Math.abs(diff) > 10) { |
| 20 | + isVisible.value = diff > 0 || nearBottom |
| 21 | + lastScrollY.value = currentY |
| 22 | + } |
| 23 | +
|
| 24 | + // At top -> hide |
| 25 | + if (currentY < 100) { |
| 26 | + isVisible.value = false |
| 27 | + } |
| 28 | +
|
| 29 | + // Near bottom -> always show |
| 30 | + if (nearBottom) { |
| 31 | + isVisible.value = true |
| 32 | + } |
| 33 | +} |
| 34 | +
|
| 35 | +function updateFooterPadding() { |
| 36 | + const height = isScrollable.value && footerRef.value ? footerRef.value.offsetHeight : 0 |
| 37 | + document.documentElement.style.setProperty('--footer-height', `${height}px`) |
| 38 | +} |
| 39 | +
|
| 40 | +onMounted(() => { |
| 41 | + nextTick(() => { |
| 42 | + lastScrollY.value = window.scrollY |
| 43 | + isScrollable.value = checkScrollable() |
| 44 | + updateFooterPadding() |
| 45 | + }) |
| 46 | +
|
| 47 | + window.addEventListener('scroll', onScroll, { passive: true }) |
| 48 | + window.addEventListener( |
| 49 | + 'resize', |
| 50 | + () => { |
| 51 | + isScrollable.value = checkScrollable() |
| 52 | + updateFooterPadding() |
| 53 | + }, |
| 54 | + { passive: true }, |
| 55 | + ) |
| 56 | +}) |
| 57 | +
|
| 58 | +onUnmounted(() => { |
| 59 | + window.removeEventListener('scroll', onScroll) |
| 60 | +}) |
| 61 | +</script> |
| 62 | + |
1 | 63 | <template> |
2 | | - <footer class="border-t border-border mt-auto"> |
3 | | - <div class="container py-8 flex flex-col gap-4 text-fg-subtle text-sm"> |
| 64 | + <footer |
| 65 | + ref="footerRef" |
| 66 | + class="border-t border-border bg-bg" |
| 67 | + :class=" |
| 68 | + isScrollable |
| 69 | + ? [ |
| 70 | + 'fixed bottom-0 left-0 right-0 z-40 transition-transform duration-300 ease-out', |
| 71 | + isVisible ? 'translate-y-0' : 'translate-y-full', |
| 72 | + ] |
| 73 | + : 'mt-auto' |
| 74 | + " |
| 75 | + > |
| 76 | + <div class="container py-6 flex flex-col gap-3 text-fg-subtle text-sm"> |
4 | 77 | <div class="flex flex-col sm:flex-row items-center justify-between gap-4"> |
5 | 78 | <p class="font-mono m-0">a better browser for the npm registry</p> |
6 | 79 | <div class="flex items-center gap-6"> |
|
0 commit comments