diff --git a/src/useMeasure/index.dom.test.ts b/src/useMeasure/index.dom.test.ts index abe132bf..4619151c 100644 --- a/src/useMeasure/index.dom.test.ts +++ b/src/useMeasure/index.dom.test.ts @@ -1,7 +1,7 @@ import {act, renderHook} from '@testing-library/react-hooks/dom'; import {useEffect} from 'react'; import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest'; -import {useMeasure} from '../index.js'; +import {useMeasure, borderBoxMeasurer} from '../index.js'; describe('useMeasure', () => { const observeSpy = vi.fn(); @@ -104,4 +104,44 @@ describe('useMeasure', () => { expect(result.current[1]).toStrictEqual({current: div}); expect(result.current[0]).toStrictEqual(measures); }); + + it('should set state by border-box sizing model', () => { + const div = document.createElement('div'); + const {result} = renderHook(() => { + const measure = useMeasure(true, borderBoxMeasurer); + + useEffect(() => { + measure[1].current = div; + }); + + return measure; + }); + + const measures = { + width: 9, + height: 9, + }; + + const entry = { + target: div, + contentRect: {width: 5, height: 5}, + borderBoxSize: [ + { + blockSize: 9, + inlineSize: 9, + }, + ], + contentBoxSize: {}, + } as unknown as ResizeObserverEntry; + + ResizeObserverSpy.mock.calls[0][0]([entry]); + expect(result.current[0]).toBeUndefined(); + + act(() => { + vi.advanceTimersByTime(1); + }); + + expect(result.current[1]).toStrictEqual({current: div}); + expect(result.current[0]).toStrictEqual(measures); + }); }); diff --git a/src/useMeasure/index.ts b/src/useMeasure/index.ts index d45b2c9a..dec4649e 100644 --- a/src/useMeasure/index.ts +++ b/src/useMeasure/index.ts @@ -8,13 +8,34 @@ export type Measures = { height: number; }; +export type Measurer = (entry: ResizeObserverEntry) => Measures; + +/** + * Measures ResizeObserverEntry by `content-box` sizing model + * Default measurer for `useMeasure` + */ +export const contentBoxMeasurer: Measurer = entry => ({ + width: entry.contentRect.width, + height: entry.contentRect.height, +}); + +/** + * Measures ResizeObserverEntry by `border-box` sizing model + */ +export const borderBoxMeasurer: Measurer = entry => ({ + height: entry.borderBoxSize[0].blockSize, + width: entry.borderBoxSize[0].inlineSize, +}); + /** * Uses ResizeObserver to track element dimensions and re-render component when they change. * * @param enabled Whether resize observer is enabled or not. + * @param observerEntryMatcher A custom measurer function to get measurements based on some box sizing model. `Content-box` sizing is default */ export function useMeasure( enabled = true, + measurer = contentBoxMeasurer, ): [Measures | undefined, MutableRefObject] { const [element, setElement] = useState(null); const elementRef = useHookableRef(null, (v) => { @@ -25,7 +46,7 @@ export function useMeasure( const [measures, setMeasures] = useState(); const [observerHandler] = useRafCallback((entry) => { - setMeasures({width: entry.contentRect.width, height: entry.contentRect.height}); + setMeasures(measurer(entry)); }); useResizeObserver(element, observerHandler, enabled);