Skip to content

Commit

Permalink
release 1.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
smastrom committed Aug 18, 2023
1 parent 5785c86 commit 5d31f94
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 54 deletions.
15 changes: 2 additions & 13 deletions demo/Header.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
<script setup lang="ts">
import { ref, watchEffect } from 'vue'
import { ref } from 'vue'
import { useFixedHeader } from '../src'
const headerRef = ref<HTMLElement | null>(null)
const { styles, isEnter, isLeave } = useFixedHeader(headerRef, {
toggleVisibility: true,
})
watchEffect(() => {
console.table([
{
isEnter: isEnter.value,
isLeave: isLeave.value,
},
])
})
const { styles } = useFixedHeader(headerRef)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vue-use-fixed-header",
"description": "Turn your boring fixed header into a smart one with three lines of code.",
"private": false,
"version": "1.0.3",
"version": "1.0.4",
"type": "module",
"keywords": [
"vue",
Expand Down
63 changes: 27 additions & 36 deletions src/useFixedHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
watch,
computed,
readonly,
type CSSProperties,
type CSSProperties as CSS,
} from 'vue'

import { mergeDefined, isSSR, isReducedMotion } from './utils'
Expand All @@ -15,9 +15,9 @@ import { CAPTURE_DELTA_FRAME_COUNT, defaultOptions } from './constants'
import type { UseFixedHeaderOptions, MaybeTemplateRef, UseFixedHeaderReturn } from './types'

enum State {
READY = 'READY',
ENTER = 'ENTER',
LEAVE = 'LEAVE',
READY,
ENTER,
LEAVE,
}

export function useFixedHeader(
Expand All @@ -30,14 +30,13 @@ export function useFixedHeader(

let isListeningScroll = false
let isHovering = false
let isInstantRestoration = true

// Internal state

const styles = shallowRef<CSSProperties>({})
const styles = shallowRef<CSS>({})
const state = ref<State>(State.READY)

function setStyles(newStyles: CSSProperties) {
function setStyles(newStyles: CSS) {
styles.value = newStyles
}

Expand Down Expand Up @@ -88,21 +87,24 @@ export function useFixedHeader(
// Callbacks

/**
* Hides the header on page load before it has a chance to paint,
* only if scroll restoration is instant.
*
* If not instant (smooth-scroll) 'isBelowHeader' will resolve
* to false and the header will be visible until next scroll.
* 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 onInstantScrollRestoration() {
if (!mergedOptions.toggleVisibility) return
if (!isInstantRestoration) return

function onScrollRestoration() {
requestAnimationFrame(() => {
const isBelowHeader = getScrollTop() > getHeaderHeight() * 1.2
if (isBelowHeader) setStyles({ ...mergedOptions.leaveStyles, visibility: 'hidden' })
const isInstant = getScrollTop() > getHeaderHeight() * 1.2 // Resolves to false if scroll is smooth

if (isInstant) {
setStyles({
...mergedOptions.leaveStyles,
...(mergedOptions.toggleVisibility ? { visibility: 'hidden' } : {}),
transition: '', // We don't want transitions to play on page load...
})
} else {
setStyles({ ...mergedOptions.enterStyles, transition: '' }) //...same here
}
})
isInstantRestoration = false
}

function onVisible() {
Expand All @@ -112,9 +114,7 @@ export function useFixedHeader(

setStyles({
...mergedOptions.enterStyles,
...(mergedOptions.toggleVisibility
? { visibility: '' as CSSProperties['visibility'] }
: {}),
...(mergedOptions.toggleVisibility ? { visibility: '' as CSS['visibility'] } : {}),
...(isReducedMotion() ? { transition: 'none' } : {}),
})

Expand All @@ -140,12 +140,13 @@ export function useFixedHeader(

if (e.target !== unref(target)) return

setStyles({ ...mergedOptions.leaveStyles, visibility: 'hidden' })
setStyles({
...mergedOptions.leaveStyles,
...(mergedOptions.toggleVisibility ? { visibility: 'hidden' } : {}),
})
}

function toggleTransitionListener(isRemove = false) {
if (!mergedOptions.toggleVisibility) return

const el = unref(target)
if (!el) return

Expand Down Expand Up @@ -313,17 +314,7 @@ export function useFixedHeader(
addResizeObserver()
if (!isFixed()) return

/**
* Immediately hides the header on page load, this has effect
* only if scroll restoration is not smooth and 'toggleVisibility'
* is set to true.
*/
onInstantScrollRestoration()

/**
* Start listening scroll events, it will hide the header
* in case of smooth-scroll restoration.
*/
onScrollRestoration()
toggleListeners()
}

Expand Down
10 changes: 6 additions & 4 deletions tests/transitions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import { defaultOptions } from '../src/constants'

describe('Transitions', () => {
describe('Page load', () => {
it('Styles are not applied if header is visible', () => {
cy.mountApp().get('header').should('be.visible').and('not.have.attr', 'style')
it('Styles are applied if header is visible, except transition', () => {
const { transition, ...enterStyles } = defaultOptions.enterStyles
cy.mountApp().get('header').should('be.visible').checkStyles(enterStyles)
})

it('Styles are applied if header is hidden (in order to trigger futher enter transition)', () => {
it('Styles are applied if header is hidden (in order to trigger further enter transition)', () => {
cy.mountApp({
props: {
simulateInstantRestoration: true,
},
})

cy.get('header').should('be.hidden').checkStyles(defaultOptions.leaveStyles)
const { transition, ...leaveStyles } = defaultOptions.leaveStyles
cy.get('header').should('be.hidden').checkStyles(leaveStyles)
})
})

Expand Down

0 comments on commit 5d31f94

Please sign in to comment.