diff --git a/.gitignore b/.gitignore index 466f3266..2dcdb2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.iml node_modules/ build/ +coverage/ diff --git a/lib/utils/domFns.js b/lib/utils/domFns.js index 3744af01..99b0fd74 100644 --- a/lib/utils/domFns.js +++ b/lib/utils/domFns.js @@ -68,35 +68,40 @@ export function removeEvent(el: ?Node, event: string, handler: Function, inputOp } } -export function outerHeight(node: HTMLElement): number { +export function outerHeight(node: Element): number { // This is deliberately excluding margin for our calculations, since we are using // offsetTop which is including margin. See getBoundPosition - let height = node.clientHeight; + // Use clientHeight for HTMLElements (faster), getBoundingClientRect for SVG + let height = (node: any).clientHeight ?? Math.round(node.getBoundingClientRect().height); const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); height += int(computedStyle.borderTopWidth); height += int(computedStyle.borderBottomWidth); return height; } -export function outerWidth(node: HTMLElement): number { +export function outerWidth(node: Element): number { // This is deliberately excluding margin for our calculations, since we are using // offsetLeft which is including margin. See getBoundPosition - let width = node.clientWidth; + // Use clientWidth for HTMLElements (faster), getBoundingClientRect for SVG + let width = (node: any).clientWidth ?? Math.round(node.getBoundingClientRect().width); const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); width += int(computedStyle.borderLeftWidth); width += int(computedStyle.borderRightWidth); return width; } -export function innerHeight(node: HTMLElement): number { - let height = node.clientHeight; + +export function innerHeight(node: Element): number { + // Use clientHeight for HTMLElements (faster), getBoundingClientRect for SVG + let height = (node: any).clientHeight ?? Math.round(node.getBoundingClientRect().height); const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); height -= int(computedStyle.paddingTop); height -= int(computedStyle.paddingBottom); return height; } -export function innerWidth(node: HTMLElement): number { - let width = node.clientWidth; +export function innerWidth(node: Element): number { + // Use clientWidth for HTMLElements (faster), getBoundingClientRect for SVG + let width = (node: any).clientWidth ?? Math.round(node.getBoundingClientRect().width); const computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); width -= int(computedStyle.paddingLeft); width -= int(computedStyle.paddingRight); diff --git a/lib/utils/positionFns.js b/lib/utils/positionFns.js index 248d4d0b..18649809 100644 --- a/lib/utils/positionFns.js +++ b/lib/utils/positionFns.js @@ -30,19 +30,35 @@ export function getBoundPosition(draggable: Draggable, x: number, y: number): [n boundNode = rootNode.querySelector(bounds); } - if (!(boundNode instanceof ownerWindow.HTMLElement)) { + // Accept Element (not just HTMLElement) to support SVG elements + if (!(boundNode instanceof ownerWindow.Element)) { throw new Error('Bounds selector "' + bounds + '" could not find an element.'); } - const boundNodeEl: HTMLElement = boundNode; // for Flow, can't seem to refine correctly + const boundNodeEl: Element = boundNode; // for Flow, can't seem to refine correctly const nodeStyle = ownerWindow.getComputedStyle(node); const boundNodeStyle = ownerWindow.getComputedStyle(boundNodeEl); + + // Calculate node offset. HTMLElements have offsetLeft/offsetTop, but SVG elements don't. + // For SVG elements, compute offset from bounding rectangles. + let nodeOffsetLeft: number, nodeOffsetTop: number; + if (node instanceof ownerWindow.HTMLElement) { + nodeOffsetLeft = node.offsetLeft; + nodeOffsetTop = node.offsetTop; + } else { + // For SVG elements, calculate offset relative to parent using getBoundingClientRect + const nodeRect = node.getBoundingClientRect(); + const boundNodeRect = boundNodeEl.getBoundingClientRect(); + nodeOffsetLeft = nodeRect.left - boundNodeRect.left - int(boundNodeStyle.borderLeftWidth); + nodeOffsetTop = nodeRect.top - boundNodeRect.top - int(boundNodeStyle.borderTopWidth); + } + // Compute bounds. This is a pain with padding and offsets but this gets it exactly right. bounds = { - left: -node.offsetLeft + int(boundNodeStyle.paddingLeft) + int(nodeStyle.marginLeft), - top: -node.offsetTop + int(boundNodeStyle.paddingTop) + int(nodeStyle.marginTop), - right: innerWidth(boundNodeEl) - outerWidth(node) - node.offsetLeft + + left: -nodeOffsetLeft + int(boundNodeStyle.paddingLeft) + int(nodeStyle.marginLeft), + top: -nodeOffsetTop + int(boundNodeStyle.paddingTop) + int(nodeStyle.marginTop), + right: innerWidth(boundNodeEl) - outerWidth(node) - nodeOffsetLeft + int(boundNodeStyle.paddingRight) - int(nodeStyle.marginRight), - bottom: innerHeight(boundNodeEl) - outerHeight(node) - node.offsetTop + + bottom: innerHeight(boundNodeEl) - outerHeight(node) - nodeOffsetTop + int(boundNodeStyle.paddingBottom) - int(nodeStyle.marginBottom) }; }