Skip to content

Commit

Permalink
Merge pull request #294 from PrefectHQ/addUseIsStickyStuck
Browse files Browse the repository at this point in the history
add usePositionStickyObserver
  • Loading branch information
znicholasbrown authored Aug 23, 2023
2 parents 34d8371 + 2cf6a89 commit 539cf57
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './useMutationObserver'
export * from './useNow'
export * from './usePatchRef'
export * from './useResizeObserver'
export * from './usePositionStickyObserver'
export * from './useRouteParam'
export * from './useRouteQuery'
export * from './useRouteQueryParam'
Expand Down
34 changes: 34 additions & 0 deletions src/usePositionStickyObserver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# usePositionStickyObserver

This composition is abstracts away the logic necessary to determine if a `position: sticky;` element has gone into it's "stuck" mode. This is useful when you want to style the element differently when it's stuck, like if you want to add a background color.

## Example

```typescript
const stickyHeader = ref<HTMLElement>()
const { stuck } = usePositionStickyObserver(stickyHeader)

const classes = computed(() ({
header: {
'header--stuck': stuck.value,
}
}))
```

## Arguments

| Name | Type |
| ------- | ---------------------------------------------- |
| element | `HTMLElement \| Ref<HTMLElement \| undefined>` |
| options | '{}' |

### Options

| Name | Type |
| --------------- | ------------------------------------------------------------------------------------------------------ |
| rootMargin | `string`, [MDN DOcs](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) |
| boundingElement | `HTMLElement \| Ref<HTMLElement \| undefined>`. The scroll container, defaults to the body. |

## Returns

`UsePositionStickyObserverResponse`
1 change: 1 addition & 0 deletions src/usePositionStickyObserver/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './usePositionStickyObserver'
52 changes: 52 additions & 0 deletions src/usePositionStickyObserver/usePositionStickyObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Ref, computed, onMounted, ref } from 'vue'
import { MaybeRefOrGetter } from '@/types/maybe'
import { useIntersectionObserver } from '@/useIntersectionObserver'
import { toValue } from '@/utilities/vue'

export type UsePositionStickyObserverResponse = {
stuck: Ref<boolean>,
}

export type UsePositionStickyObserverOptions = {
rootMargin?: string,
boundingElement?: HTMLElement,
}

const usePositionStickyObserverDefaultOptions = {
rootMargin: '-1px 0px 0px 0px',
boundingElement: document.body,
}

export function usePositionStickyObserver(
element: MaybeRefOrGetter<HTMLElement | undefined>,
options?: MaybeRefOrGetter<UsePositionStickyObserverOptions>,
): UsePositionStickyObserverResponse {
const elementRef = computed(() => toValue(element))
const stuck = ref(false)

const observerOptions = computed(() => {
const { rootMargin: rootMarginOption, boundingElement: boundingElementOption } = toValue(options ?? {})
const rootMargin = rootMarginOption ?? usePositionStickyObserverDefaultOptions.rootMargin
const root = boundingElementOption ?? usePositionStickyObserverDefaultOptions.boundingElement

return {
threshold: [1],
rootMargin,
root,
}
})

function intersect(entries: IntersectionObserverEntry[]): void {
entries.forEach(entry => {
stuck.value = entry.intersectionRatio < 1
})
}

const { observe } = useIntersectionObserver(intersect, observerOptions)

onMounted(() => observe(elementRef))

return {
stuck,
}
}

0 comments on commit 539cf57

Please sign in to comment.