diff --git a/packages/vue-use-fixed-header/src/useFixedHeader.ts b/packages/vue-use-fixed-header/src/useFixedHeader.ts index 216af8b..942d405 100644 --- a/packages/vue-use-fixed-header/src/useFixedHeader.ts +++ b/packages/vue-use-fixed-header/src/useFixedHeader.ts @@ -1,6 +1,6 @@ import { shallowRef, ref, unref, watch, computed, readonly, type CSSProperties as CSS } from 'vue' -import { isSSR, useReducedMotion } from './utils' +import { useReducedMotion, isMetaFramework, isBrowser } from './utils' import { TRANSITION_STYLES } from './constants' import type { UseFixedHeaderOptions, MaybeTemplateRef } from './types' @@ -32,10 +32,17 @@ export function useFixedHeader( skipInitialObserverCb: true, isListeningScroll: false, isHovering: false, - isMount: true, + isScrollRestoration: true, } - const styles = shallowRef({}) + const styles = shallowRef( + isMetaFramework + ? { + transform: enterStyles.transform, + ...(transitionOpacity() ? { opacity: 1 } : {}), + } + : {}, // will be set on scroll restoration + ) const state = ref(State.READY) const setStyles = (newStyles: CSS) => (styles.value = newStyles) @@ -84,15 +91,19 @@ export function useFixedHeader( * Hides the header on page load before it has a chance to paint * if scroll restoration is instant. If not, applies the enter * styles immediately (without transitions). + * + * This is not possible if the header is server-rendered. + * In that case, the header will be visible and start transitioning + * when manually scrolled. */ function onScrollRestoration() { + if (isMetaFramework) return + window.requestAnimationFrame(() => { - if (!internal.isMount) return + if (!internal.isScrollRestoration) return + internal.isScrollRestoration = false - if (!isFixed()) { - internal.isMount = false - return - } + if (!isFixed()) return const isInstant = getScrollTop() > getHeaderHeight() * 1.2 // Resolves to false if scroll is smooth @@ -109,7 +120,7 @@ export function useFixedHeader( }) } - internal.isMount = false + internal.isScrollRestoration = false }) } @@ -189,7 +200,7 @@ export function useFixedHeader( // Scroll Events function createScrollHandler() { - let prevTop = 0 + let prevTop = isBrowser ? getScrollTop() : 0 return () => { const scrollTop = getScrollTop() @@ -276,7 +287,7 @@ export function useFixedHeader( removePointerListener() } - !isSSR && + isBrowser && watch( () => [unref(target), getRoot(), isReduced.value, unref(options.watch)], ([headerEl, rootEl, isReduced], _, onCleanup) => { @@ -290,8 +301,8 @@ export function useFixedHeader( onCleanup(() => { removeListeners() - internal.resizeObserver?.disconnect() removeStyles() + internal.resizeObserver?.disconnect() }) }, { immediate: true, flush: 'post' }, diff --git a/packages/vue-use-fixed-header/src/utils.ts b/packages/vue-use-fixed-header/src/utils.ts index 577ea57..62ae7c6 100644 --- a/packages/vue-use-fixed-header/src/utils.ts +++ b/packages/vue-use-fixed-header/src/utils.ts @@ -1,11 +1,17 @@ import { onBeforeUnmount, onMounted, ref } from 'vue' -export const isSSR = typeof window === 'undefined' +export const isBrowser = typeof window !== 'undefined' + +const isNuxt = isBrowser && '__NUXT__' in window +const isAstro = isBrowser && 'Astro' in window +const isQuasar = isBrowser && '__Q_META__' in window + +export const isMetaFramework = isNuxt || isAstro || isQuasar export function useReducedMotion() { const isReduced = ref(false) - if (isSSR) return isReduced + if (!isBrowser) return isReduced const query = window.matchMedia('(prefers-reduced-motion: reduce)')