Skip to content

Commit e5107dc

Browse files
authored
fix(shared): report border-box dimensions from useResize (#2432)
1 parent 1fb03f4 commit e5107dc

3 files changed

Lines changed: 78 additions & 3 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@react-spring/shared': patch
3+
---
4+
5+
fix: `useResize` now reports border-box dimensions (includes padding and border)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { resizeElement } from './resizeElement'
2+
3+
describe('resizeElement', () => {
4+
beforeEach(() => {
5+
vi.useRealTimers()
6+
})
7+
8+
const observeOnce = (target: HTMLElement) =>
9+
new Promise<{ width: number; height: number }>(resolve => {
10+
const cleanup = resizeElement(rect => {
11+
cleanup()
12+
resolve({ width: rect.width, height: rect.height })
13+
}, target)
14+
})
15+
16+
it('reports the border-box size, including padding and border', async () => {
17+
const target = document.createElement('div')
18+
target.style.cssText =
19+
'width: 100px; height: 100px; padding: 20px; border: 5px solid; box-sizing: content-box;'
20+
document.body.appendChild(target)
21+
22+
try {
23+
const size = await observeOnce(target)
24+
25+
// content-box 100x100 + padding 20 + border 5 = border-box 150x150
26+
expect(size).toEqual({ width: 150, height: 150 })
27+
} finally {
28+
target.remove()
29+
}
30+
})
31+
32+
it('maps inline/block to width/height for vertical writing modes', async () => {
33+
const target = document.createElement('div')
34+
target.style.cssText =
35+
'width: 100px; height: 200px; writing-mode: vertical-rl; box-sizing: border-box;'
36+
document.body.appendChild(target)
37+
38+
try {
39+
const size = await observeOnce(target)
40+
41+
expect(size).toEqual({ width: 100, height: 200 })
42+
} finally {
43+
target.remove()
44+
}
45+
})
46+
})

packages/shared/src/dom-events/resize/resizeElement.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,33 @@ let observer: ResizeObserver | undefined
44

55
const resizeHandlers = new WeakMap<Element, Set<OnResizeCallback>>()
66

7+
const getBorderBoxSize = (entry: ResizeObserverEntry) => {
8+
// `borderBoxSize` is an array in modern browsers and a plain object in
9+
// older Firefox; both expose `inlineSize`/`blockSize`. Fall back to
10+
// `contentRect` when the entry doesn't carry border-box data.
11+
const boxSize = Array.isArray(entry.borderBoxSize)
12+
? entry.borderBoxSize[0]
13+
: (entry.borderBoxSize as unknown as ResizeObserverSize | undefined)
14+
15+
if (!boxSize) return entry.contentRect
16+
17+
// `contentRect` always reports visual width/height regardless of writing
18+
// mode; `inlineSize`/`blockSize` are logical, so flip them back for
19+
// vertical/sideways writing modes to keep the public shape consistent.
20+
const writingMode = getComputedStyle(entry.target).writingMode
21+
const isVertical =
22+
writingMode.startsWith('vertical-') || writingMode.startsWith('sideways-')
23+
24+
return isVertical
25+
? { width: boxSize.blockSize, height: boxSize.inlineSize }
26+
: { width: boxSize.inlineSize, height: boxSize.blockSize }
27+
}
28+
729
const handleObservation = (entries: ResizeObserverEntry[]) =>
8-
entries.forEach(({ target, contentRect }) => {
9-
return resizeHandlers.get(target)?.forEach(handler => handler(contentRect))
30+
entries.forEach(entry => {
31+
return resizeHandlers
32+
.get(entry.target)
33+
?.forEach(handler => handler(getBorderBoxSize(entry)))
1034
})
1135

1236
export function resizeElement(handler: OnResizeCallback, target: HTMLElement) {
@@ -40,7 +64,7 @@ export function resizeElement(handler: OnResizeCallback, target: HTMLElement) {
4064
elementHandlers.add(handler)
4165

4266
if (observer) {
43-
observer.observe(target)
67+
observer.observe(target, { box: 'border-box' })
4468
}
4569

4670
/**

0 commit comments

Comments
 (0)