Skip to content

Commit

Permalink
Core - Drop scrollRestoration SPA logic, fine-tune resizeObserver cal…
Browse files Browse the repository at this point in the history
…lback handling
  • Loading branch information
smastrom committed Nov 16, 2023
1 parent 7d9c2b7 commit 1dd92b9
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 116 deletions.
49 changes: 8 additions & 41 deletions packages/vue-use-fixed-header/src/useFixedHeader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { shallowRef, ref, unref, watch, computed, readonly, type CSSProperties as CSS } from 'vue'

import { isSSR, useReducedMotion } from './utils'
import { useReducedMotion, isBrowser } from './utils'
import { TRANSITION_STYLES } from './constants'

import type { UseFixedHeaderOptions, MaybeTemplateRef } from './types'
Expand Down Expand Up @@ -29,10 +29,9 @@ export function useFixedHeader(

const internal = {
resizeObserver: undefined as ResizeObserver | undefined,
skipInitialObserverCb: true,
initResizeObserver: false,
isListeningScroll: false,
isHovering: false,
isMount: true,
}

const styles = shallowRef<CSS>({})
Expand Down Expand Up @@ -80,47 +79,15 @@ export function useFixedHeader(

// Callbacks

/**
* 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).
*/
function onScrollRestoration() {
window.requestAnimationFrame(() => {
if (!internal.isMount) return

if (!isFixed()) {
internal.isMount = false
return
}

const isInstant = getScrollTop() > getHeaderHeight() * 1.2 // Resolves to false if scroll is smooth

if (isInstant) {
setStyles({
transform: leaveStyles.transform,
...(transitionOpacity() ? { opacity: 0 } : {}),
visibility: 'hidden',
})
} else {
setStyles({
transform: enterStyles.transform,
...(transitionOpacity() ? { opacity: 1 } : {}),
})
}

internal.isMount = false
})
}

/**
* Resize observer is added wheter or not the header is fixed/sticky
* as it is in charge of toggling scroll/pointer listeners if it
* turns from fixed/sticky to something else and vice-versa.
*/
function addResizeObserver() {
internal.resizeObserver = new ResizeObserver(() => {
if (internal.skipInitialObserverCb) return (internal.skipInitialObserverCb = false)
// Skip the initial call
if (!internal.initResizeObserver) return (internal.initResizeObserver = true)
toggleListeners()
})

Expand Down Expand Up @@ -189,7 +156,7 @@ export function useFixedHeader(
// Scroll Events

function createScrollHandler() {
let prevTop = 0
let prevTop = isBrowser ? getScrollTop() : 0

return () => {
const scrollTop = getScrollTop()
Expand Down Expand Up @@ -276,22 +243,22 @@ export function useFixedHeader(
removePointerListener()
}

!isSSR &&
isBrowser &&
watch(
() => [unref(target), getRoot(), isReduced.value, unref(options.watch)],
([headerEl, rootEl, isReduced], _, onCleanup) => {
const shouldInit = !isReduced && headerEl && (rootEl || rootEl === null)

if (shouldInit) {
addResizeObserver()
onScrollRestoration()
toggleListeners()
}

onCleanup(() => {
removeListeners()
internal.resizeObserver?.disconnect()
removeStyles()
internal.resizeObserver?.disconnect()
internal.initResizeObserver = false
})
},
{ immediate: true, flush: 'post' },
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-use-fixed-header/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { onBeforeUnmount, onMounted, ref } from 'vue'

export const isSSR = typeof window === 'undefined'
export const isBrowser = typeof window !== 'undefined'

export function useReducedMotion() {
const isReduced = ref(false)

if (isSSR) return isReduced
if (!isBrowser) return isReduced

const query = window.matchMedia('(prefers-reduced-motion: reduce)')

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
<script setup lang="ts">
import { onBeforeMount, ref } from 'vue'
import { ref } from 'vue'
import { useFixedHeader } from '../../src'
const props = defineProps<{
instantScrollRestoration?: boolean
}>()
const containerRef = ref<HTMLElement | null>(null)
const headerRef = ref<HTMLElement | null>(null)
onBeforeMount(() => {
if (!props.instantScrollRestoration) return
window.requestAnimationFrame(() => {
if (!containerRef.value) throw new Error('containerRef is null')
containerRef.value.scroll(0, window.innerHeight / 3)
})
})
const { styles } = useFixedHeader(headerRef, {
root: containerRef,
})
Expand Down
1 change: 0 additions & 1 deletion packages/vue-use-fixed-header/tests/components/Watch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,3 @@ const position = computed(() => (props.watch.value ? 'relative' : 'fixed'))
background: red;
}
</style>
../../src/useFixedHeader
14 changes: 1 addition & 13 deletions packages/vue-use-fixed-header/tests/components/WindowScroll.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
<script setup lang="ts">
import { onBeforeMount, ref } from 'vue'
import { ref } from 'vue'
import { useFixedHeader } from '../../src'
const props = defineProps<{
instantScrollRestoration?: boolean
}>()
const headerRef = ref<HTMLElement | null>(null)
onBeforeMount(() => {
if (!props.instantScrollRestoration) return
window.requestAnimationFrame(() => {
window.scroll(0, window.innerHeight / 3)
})
})
const { styles } = useFixedHeader(headerRef)
</script>

Expand Down
8 changes: 3 additions & 5 deletions packages/vue-use-fixed-header/tests/page-load.cy.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
function testIstantRestoration() {
return cy
.mountApp({
props: {
instantScrollRestoration: true,
},
})
.mountApp()
.getScrollSubject()
.scrollTo('bottom', { duration: 0 })
.get('header')
.should('not.be.visible')
}
Expand Down
18 changes: 18 additions & 0 deletions packages/vue-use-fixed-header/tests/styles.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { TRANSITION_STYLES } from '../src/constants'

describe('Styles', () => {
const { enterStyles, leaveStyles } = TRANSITION_STYLES

it('Transitions are toggled properly', () => {
cy.mountApp()
.scrollDown()
.get('header')
.should('be.hidden')
.checkStyles(leaveStyles)

.scrollUp()
.get('header')
.should('be.visible')
.checkStyles(enterStyles)
})
})
38 changes: 0 additions & 38 deletions packages/vue-use-fixed-header/tests/styles.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/vue-use-fixed-header/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "ES2015",
"module": "ES2020",
"moduleResolution": "Node",
"strict": true,
Expand Down

0 comments on commit 1dd92b9

Please sign in to comment.