-
Notifications
You must be signed in to change notification settings - Fork 536
/
useAnchoredPosition.ts
53 lines (47 loc) · 1.99 KB
/
useAnchoredPosition.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import React from 'react'
import {PositionSettings, getAnchoredPosition, AnchorPosition} from '../behaviors/anchoredPosition'
import {useProvidedRefOrCreate} from './useProvidedRefOrCreate'
import {useResizeObserver} from './useResizeObserver'
export interface AnchoredPositionHookSettings extends Partial<PositionSettings> {
floatingElementRef?: React.RefObject<Element>
anchorElementRef?: React.RefObject<Element>
}
/**
* Calculates the top and left values for an absolutely-positioned floating element
* to be anchored to some anchor element. Returns refs for the floating element
* and the anchor element, along with the position.
* @param settings Settings for calculating the anchored position.
* @param dependencies Dependencies to determine when to re-calculate the position.
* @returns An object of {top: number, left: number} to absolutely-position the
* floating element.
*/
export function useAnchoredPosition(
settings?: AnchoredPositionHookSettings,
dependencies: React.DependencyList = []
): {
floatingElementRef: React.RefObject<Element>
anchorElementRef: React.RefObject<Element>
position: AnchorPosition | undefined
} {
const floatingElementRef = useProvidedRefOrCreate(settings?.floatingElementRef)
const anchorElementRef = useProvidedRefOrCreate(settings?.anchorElementRef)
const [position, setPosition] = React.useState<AnchorPosition | undefined>(undefined)
const updatePosition = React.useCallback(
() => {
if (floatingElementRef.current instanceof Element && anchorElementRef.current instanceof Element) {
setPosition(getAnchoredPosition(floatingElementRef.current, anchorElementRef.current, settings))
} else {
setPosition(undefined)
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[floatingElementRef, anchorElementRef, ...dependencies]
)
React.useLayoutEffect(updatePosition, [updatePosition])
useResizeObserver(updatePosition)
return {
floatingElementRef,
anchorElementRef,
position
}
}