react-laag icon indicating copy to clipboard operation
react-laag copied to clipboard

How to pass a clientRect in v2?

Open nathggns opened this issue 1 year ago • 1 comments

I'm migrating from useToggleLayer to useLayer as upgrading from v1 to v2, but I need to be able to pass a specific clientRect when opening. This API appears to have disappeared in the upgrade, but no alternative is mentioned in the migration guide

There's a closed issue at https://github.com/everweij/react-laag/issues/83 for this but no suggestion to fix, so I'm reopening.

nathggns avatar Nov 21 '23 11:11 nathggns

If anyone is still interested, this hook should work alright. (I used useMousePositionAsTrigger as an example)

import useResizeObserver from '@react-hook/resize-observer'
import * as React from 'react'
import { IBounds } from 'react-laag'

export type UseRefAsTriggerResult = {
	ref: React.RefObject<HTMLElement>
	hasRefBounds: boolean
	trigger: {
		getBounds: () => IBounds
		getParent?: () => HTMLElement
	}
}

const EMPTY_BOUNDS: IBounds = {
	top: 0,
	left: 0,
	right: 1,
	bottom: 1,
	width: 1,
	height: 1,
}

export const useRefAsTrigger = (propRef?: React.RefObject<HTMLElement>): UseRefAsTriggerResult => {
	// Use the prop ref if it's provided, otherwise create a new one
	const _ref = React.useRef<HTMLElement>(null)
	const ref = propRef ?? _ref

	const [bounds, setBounds] = React.useState<IBounds>(EMPTY_BOUNDS)

	const setBoundsFromElement = React.useCallback((el: HTMLElement) => {
		const rect = el.getBoundingClientRect()
		setBounds({ bottom: rect.bottom, top: rect.top, left: rect.left, right: rect.right, height: rect.height, width: rect.width })
	}, [])

	// Set the bounds from the ref
	useResizeObserver(ref, e => {
		setBoundsFromElement(e.target as HTMLElement)
	})
	React.useEffect(() => {
		if (ref.current) setBoundsFromElement(ref.current)
	}, [ref])

	const hasRefBounds = bounds !== EMPTY_BOUNDS

	return { ref, hasRefBounds, trigger: { getBounds: () => bounds, getParent: ref.current ? () => ref.current! : undefined } }
}

Basic usage:

const { trigger, hasRefBounds } = useRefAsTrigger(buttonRef)
const { renderLayer, layerProps } = useLayer({
  isOpen: hasRefBounds,
  trigger,
})

rseyferth avatar Feb 07 '24 14:02 rseyferth