/** * Composable for smooth header collapse on scroll * Returns pixel positions for all fixed layers */ export const useScrollCollapse = (headerHeight = 112, searchBarHeight = 48) => { const scrollY = ref(0) // Layer positions in pixels // Layers 1+2 (MainNav + SubNav): slide up as user scrolls const headerOffset = computed(() => -Math.min(scrollY.value, headerHeight)) // Layer 3 (SearchBar): slides down to top:0 const searchBarTop = computed(() => Math.max(0, headerHeight - scrollY.value)) // Layer 4 (Map): follows SearchBar, minimum is searchBarHeight const mapTop = computed(() => Math.max(searchBarHeight, headerHeight + searchBarHeight - scrollY.value)) // Content padding-top const contentPaddingTop = computed(() => Math.max(searchBarHeight, headerHeight + searchBarHeight - scrollY.value)) // Is header fully collapsed? const isCollapsed = computed(() => scrollY.value >= headerHeight) // Expand - scroll to top const expand = () => { if (import.meta.client) { window.scrollTo({ top: 0, behavior: 'smooth' }) } } // Collapse - scroll down to hide header const collapse = () => { if (import.meta.client) { window.scrollTo({ top: headerHeight, behavior: 'smooth' }) } } const onScroll = () => { if (import.meta.client) { scrollY.value = window.scrollY } } onMounted(() => { if (import.meta.client) { window.addEventListener('scroll', onScroll, { passive: true }) onScroll() // Initial value } }) onUnmounted(() => { if (import.meta.client) { window.removeEventListener('scroll', onScroll) } }) return { scrollY: readonly(scrollY), headerOffset, searchBarTop, mapTop, contentPaddingTop, isCollapsed, expand, collapse } }