Skip to content

Commit c933e85

Browse files
committed
BUG: Don't mutate live DOM nodes when exporting SVG.
1 parent 3b37295 commit c933e85

File tree

3 files changed

+30
-13
lines changed

3 files changed

+30
-13
lines changed

packages/frontend/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
"@viz-js/viz": "^3.7.0",
4040
"catcolab-api": "link:../backend/pkg",
4141
"catlog-wasm": "link:../catlog-wasm/pkg",
42-
"computed-style-to-inline-style": "^4.0.0",
4342
"echarts": "^5.5.1",
4443
"echarts-solid": "^0.2.0",
4544
"firebase": "^10.14.0",

packages/frontend/pnpm-lock.yaml

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/frontend/src/visualization/export_svg.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import computedStyleToInlineStyle from "computed-style-to-inline-style";
21
import download from "js-file-download";
32

43
/** Export an `<svg>` element with inlined CSS styles.
54
65
Returns the source of an SVG document.
76
*/
87
export function exportSVG(svg: SVGSVGElement): string {
9-
computedStyleToInlineStyle(svg, { recursive: true });
10-
118
const serializer = new XMLSerializer();
12-
let source = serializer.serializeToString(svg);
9+
const node = computedStyleToInlineStyle(svg);
10+
let source = serializer.serializeToString(node);
1311

1412
// Add XML namespaces.
1513
if (!source.match(/^<svg[^>]*?\sxmlns=(['"`])https?\:\/\/www\.w3\.org\/2000\/svg\1/)) {
@@ -31,3 +29,31 @@ export function downloadSVG(svg: SVGSVGElement, filename: string) {
3129
const source = exportSVG(svg);
3230
return download(source, filename, "image/svg+xml");
3331
}
32+
33+
/** Convert an HTML or SVG element's style from computed to inline.
34+
35+
Adapted from <https://github.com/lukehorvat/computed-style-to-inline-style>
36+
but fixed to avoid mutating the original DOM node: see issue 4 on that repo.
37+
*/
38+
export function computedStyleToInlineStyle(element: StylableElement): Node {
39+
const cloned = element.cloneNode(true);
40+
recurseComputedStyleToInlineStyle(element, cloned as StylableElement);
41+
return cloned;
42+
}
43+
44+
function recurseComputedStyleToInlineStyle(element: StylableElement, cloned: StylableElement) {
45+
for (let i = 0; i < element.children.length; i++) {
46+
recurseComputedStyleToInlineStyle(
47+
element.children[i] as StylableElement,
48+
cloned.children[i] as StylableElement,
49+
);
50+
}
51+
52+
const computedStyle = getComputedStyle(element);
53+
for (const property of computedStyle) {
54+
// biome-ignore lint/suspicious/noExplicitAny: types are busted?
55+
cloned.style[property as any] = computedStyle.getPropertyValue(property);
56+
}
57+
}
58+
59+
type StylableElement = HTMLElement | SVGElement;

0 commit comments

Comments
 (0)