diff --git a/flow-typed/myLibDef.js b/flow-typed/myLibDef.js index a2094ce..03fae64 100644 --- a/flow-typed/myLibDef.js +++ b/flow-typed/myLibDef.js @@ -1,2 +1,9 @@ declare var __DEV__: boolean; declare var __VERSION__: string; + +declare class SVGSVGElement extends Element { + className: string; + style: CSSStyleDeclaration; + + getPresentationAttribute(name: string): any; +} diff --git a/src/Bounds.js b/src/Bounds.js index a6d737f..34a64ee 100644 --- a/src/Bounds.js +++ b/src/Bounds.js @@ -46,7 +46,7 @@ export class Bounds { } } -export const parseBounds = (node: HTMLElement): Bounds => { +export const parseBounds = (node: HTMLElement | SVGSVGElement): Bounds => { return Bounds.fromClientRect(node.getBoundingClientRect()); }; diff --git a/src/NodeContainer.js b/src/NodeContainer.js index b13f57c..598cc96 100644 --- a/src/NodeContainer.js +++ b/src/NodeContainer.js @@ -85,7 +85,7 @@ export default class NodeContainer { index: number; constructor( - node: HTMLElement, + node: HTMLElement | SVGSVGElement, parent: ?NodeContainer, imageLoader: ImageLoader, index: number @@ -157,7 +157,11 @@ export default class NodeContainer { if (__DEV__) { this.name = `${node.tagName.toLowerCase()}${node.id ? `#${node.id}` - : ''}${node.className.split(' ').map(s => (s.length ? `.${s}` : '')).join('')}`; + : ''}${node.className + .toString() + .split(' ') + .map(s => (s.length ? `.${s}` : '')) + .join('')}`; } } getClipPaths(): Array { @@ -215,7 +219,16 @@ export default class NodeContainer { } } -const getImage = (node: HTMLElement, imageLoader: ImageLoader): ?string => { +const getImage = (node: HTMLElement | SVGSVGElement, imageLoader: ImageLoader): ?string => { + if ( + node instanceof node.ownerDocument.defaultView.SVGSVGElement || + node instanceof SVGSVGElement + ) { + const s = new XMLSerializer(); + return imageLoader.loadImage( + `data:image/svg+xml,${encodeURIComponent(s.serializeToString(node))}` + ); + } switch (node.tagName) { case 'IMG': // $FlowFixMe diff --git a/src/NodeParser.js b/src/NodeParser.js index 167d17b..98c1665 100644 --- a/src/NodeParser.js +++ b/src/NodeParser.js @@ -102,6 +102,28 @@ const parseNodeTree = ( } } } + } else if ( + childNode instanceof defaultView.SVGSVGElement || + childNode instanceof SVGSVGElement + ) { + const container = new NodeContainer(childNode, parent, imageLoader, index++); + const treatAsRealStackingContext = createsRealStackingContext(container, childNode); + if (treatAsRealStackingContext || createsStackingContext(container)) { + // for treatAsRealStackingContext:false, any positioned descendants and descendants + // which actually create a new stacking context should be considered part of the parent stacking context + const parentStack = + treatAsRealStackingContext || container.isPositioned() + ? stack.getRealParentStackingContext() + : stack; + const childStack = new StackingContext( + container, + parentStack, + treatAsRealStackingContext + ); + parentStack.contexts.push(childStack); + } else { + stack.children.push(container); + } } } }; @@ -144,7 +166,10 @@ const inlinePseudoElement = (node: HTMLElement, pseudoElt: ':before' | ':after') } }; -const createsRealStackingContext = (container: NodeContainer, node: HTMLElement): boolean => { +const createsRealStackingContext = ( + container: NodeContainer, + node: HTMLElement | SVGSVGElement +): boolean => { return ( container.isRootElement() || container.isPositionedWithZIndex() || @@ -158,7 +183,10 @@ const createsStackingContext = (container: NodeContainer): boolean => { return container.isPositioned() || container.isFloating(); }; -const isBodyWithTransparentRoot = (container: NodeContainer, node: HTMLElement): boolean => { +const isBodyWithTransparentRoot = ( + container: NodeContainer, + node: HTMLElement | SVGSVGElement +): boolean => { return ( node.nodeName === 'BODY' && container.parent instanceof NodeContainer &&