Skip to content

Commit ffa3b14

Browse files
committed
fix: support bounds='parent' for SVG elements and flexbox layouts
- Use getBoundingClientRect() as fallback for SVG elements which lack clientWidth/clientHeight properties - Calculate SVG element offsets using bounding rectangles since they don't have offsetLeft/offsetTop - Accept Element (not just HTMLElement) for bounds container to support SVG parent elements - HTMLElements still use the fast path (clientWidth/clientHeight) Fixes #213 Closes #594
1 parent 679e269 commit ffa3b14

File tree

3 files changed

+36
-14
lines changed

3 files changed

+36
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
*.iml
33
node_modules/
44
build/
5+
coverage/

lib/utils/domFns.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,35 +68,40 @@ export function removeEvent(el: ?Node, event: string, handler: Function, inputOp
6868
}
6969
}
7070

71-
export function outerHeight(node: HTMLElement): number {
71+
export function outerHeight(node: Element): number {
7272
// This is deliberately excluding margin for our calculations, since we are using
7373
// offsetTop which is including margin. See getBoundPosition
74-
let height = node.clientHeight;
74+
// Use clientHeight for HTMLElements (faster), getBoundingClientRect for SVG
75+
let height = (node: any).clientHeight ?? Math.round(node.getBoundingClientRect().height);
7576
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
7677
height += int(computedStyle.borderTopWidth);
7778
height += int(computedStyle.borderBottomWidth);
7879
return height;
7980
}
8081

81-
export function outerWidth(node: HTMLElement): number {
82+
export function outerWidth(node: Element): number {
8283
// This is deliberately excluding margin for our calculations, since we are using
8384
// offsetLeft which is including margin. See getBoundPosition
84-
let width = node.clientWidth;
85+
// Use clientWidth for HTMLElements (faster), getBoundingClientRect for SVG
86+
let width = (node: any).clientWidth ?? Math.round(node.getBoundingClientRect().width);
8587
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
8688
width += int(computedStyle.borderLeftWidth);
8789
width += int(computedStyle.borderRightWidth);
8890
return width;
8991
}
90-
export function innerHeight(node: HTMLElement): number {
91-
let height = node.clientHeight;
92+
93+
export function innerHeight(node: Element): number {
94+
// Use clientHeight for HTMLElements (faster), getBoundingClientRect for SVG
95+
let height = (node: any).clientHeight ?? Math.round(node.getBoundingClientRect().height);
9296
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
9397
height -= int(computedStyle.paddingTop);
9498
height -= int(computedStyle.paddingBottom);
9599
return height;
96100
}
97101

98-
export function innerWidth(node: HTMLElement): number {
99-
let width = node.clientWidth;
102+
export function innerWidth(node: Element): number {
103+
// Use clientWidth for HTMLElements (faster), getBoundingClientRect for SVG
104+
let width = (node: any).clientWidth ?? Math.round(node.getBoundingClientRect().width);
100105
const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node);
101106
width -= int(computedStyle.paddingLeft);
102107
width -= int(computedStyle.paddingRight);

lib/utils/positionFns.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,35 @@ export function getBoundPosition(draggable: Draggable, x: number, y: number): [n
3030
boundNode = rootNode.querySelector(bounds);
3131
}
3232

33-
if (!(boundNode instanceof ownerWindow.HTMLElement)) {
33+
// Accept Element (not just HTMLElement) to support SVG elements
34+
if (!(boundNode instanceof ownerWindow.Element)) {
3435
throw new Error('Bounds selector "' + bounds + '" could not find an element.');
3536
}
36-
const boundNodeEl: HTMLElement = boundNode; // for Flow, can't seem to refine correctly
37+
const boundNodeEl: Element = boundNode; // for Flow, can't seem to refine correctly
3738
const nodeStyle = ownerWindow.getComputedStyle(node);
3839
const boundNodeStyle = ownerWindow.getComputedStyle(boundNodeEl);
40+
41+
// Calculate node offset. HTMLElements have offsetLeft/offsetTop, but SVG elements don't.
42+
// For SVG elements, compute offset from bounding rectangles.
43+
let nodeOffsetLeft: number, nodeOffsetTop: number;
44+
if (node instanceof ownerWindow.HTMLElement) {
45+
nodeOffsetLeft = node.offsetLeft;
46+
nodeOffsetTop = node.offsetTop;
47+
} else {
48+
// For SVG elements, calculate offset relative to parent using getBoundingClientRect
49+
const nodeRect = node.getBoundingClientRect();
50+
const boundNodeRect = boundNodeEl.getBoundingClientRect();
51+
nodeOffsetLeft = nodeRect.left - boundNodeRect.left - int(boundNodeStyle.borderLeftWidth);
52+
nodeOffsetTop = nodeRect.top - boundNodeRect.top - int(boundNodeStyle.borderTopWidth);
53+
}
54+
3955
// Compute bounds. This is a pain with padding and offsets but this gets it exactly right.
4056
bounds = {
41-
left: -node.offsetLeft + int(boundNodeStyle.paddingLeft) + int(nodeStyle.marginLeft),
42-
top: -node.offsetTop + int(boundNodeStyle.paddingTop) + int(nodeStyle.marginTop),
43-
right: innerWidth(boundNodeEl) - outerWidth(node) - node.offsetLeft +
57+
left: -nodeOffsetLeft + int(boundNodeStyle.paddingLeft) + int(nodeStyle.marginLeft),
58+
top: -nodeOffsetTop + int(boundNodeStyle.paddingTop) + int(nodeStyle.marginTop),
59+
right: innerWidth(boundNodeEl) - outerWidth(node) - nodeOffsetLeft +
4460
int(boundNodeStyle.paddingRight) - int(nodeStyle.marginRight),
45-
bottom: innerHeight(boundNodeEl) - outerHeight(node) - node.offsetTop +
61+
bottom: innerHeight(boundNodeEl) - outerHeight(node) - nodeOffsetTop +
4662
int(boundNodeStyle.paddingBottom) - int(nodeStyle.marginBottom)
4763
};
4864
}

0 commit comments

Comments
 (0)