fix(export image): Add support for displaying cardinality relationships + background (#407)

This commit is contained in:
Guy Ben-Aharon
2024-11-16 16:57:13 +02:00
committed by GitHub
parent eaf75cedb0
commit 68474e75d5
3 changed files with 107 additions and 11 deletions

View File

@@ -5,12 +5,14 @@ import { toJpeg, toPng, toSvg } from 'html-to-image';
import { useReactFlow } from '@xyflow/react';
import { useChartDB } from '@/hooks/use-chartdb';
import { useFullScreenLoader } from '@/hooks/use-full-screen-spinner';
import { useTheme } from '@/hooks/use-theme';
export const ExportImageProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const { hideLoader, showLoader } = useFullScreenLoader();
const { setNodes, getViewport } = useReactFlow();
const { effectiveTheme } = useTheme();
const { diagramName } = useChartDB();
const downloadImage = useCallback(
@@ -59,13 +61,101 @@ export const ExportImageProvider: React.FC<React.PropsWithChildren> = ({
const imageCreateFn = imageCreatorMap[type];
setTimeout(async () => {
const dataUrl = await imageCreateFn(
window.document.querySelector(
'.react-flow__viewport'
) as HTMLElement,
{
const viewportElement = window.document.querySelector(
'.react-flow__viewport'
) as HTMLElement;
const markerDefs = document.querySelector(
'.marker-definitions defs'
);
const tempSvg = document.createElementNS(
'http://www.w3.org/2000/svg',
'svg'
);
tempSvg.style.position = 'absolute';
tempSvg.style.top = '0';
tempSvg.style.left = '0';
tempSvg.style.width = '100%';
tempSvg.style.height = '100%';
tempSvg.style.overflow = 'visible';
tempSvg.style.zIndex = '-50';
tempSvg.setAttribute(
'viewBox',
`0 0 ${reactFlowBounds.width} ${reactFlowBounds.height}`
);
const defs = document.createElementNS(
'http://www.w3.org/2000/svg',
'defs'
);
if (markerDefs) {
defs.innerHTML = markerDefs.innerHTML;
}
const pattern = document.createElementNS(
'http://www.w3.org/2000/svg',
'pattern'
);
pattern.setAttribute('id', 'background-pattern');
pattern.setAttribute('width', String(16 * viewport.zoom));
pattern.setAttribute('height', String(16 * viewport.zoom));
pattern.setAttribute('patternUnits', 'userSpaceOnUse');
pattern.setAttribute(
'patternTransform',
`translate(${viewport.x % (16 * viewport.zoom)} ${viewport.y % (16 * viewport.zoom)})`
);
const dot = document.createElementNS(
'http://www.w3.org/2000/svg',
'circle'
);
const dotSize = viewport.zoom * 0.5;
dot.setAttribute('cx', String(viewport.zoom));
dot.setAttribute('cy', String(viewport.zoom));
dot.setAttribute('r', String(dotSize));
const dotColor =
effectiveTheme === 'light' ? '#92939C' : '#777777';
dot.setAttribute('fill', dotColor);
pattern.appendChild(dot);
defs.appendChild(pattern);
tempSvg.appendChild(defs);
const backgroundRect = document.createElementNS(
'http://www.w3.org/2000/svg',
'rect'
);
const padding = 2000;
backgroundRect.setAttribute('x', String(-viewport.x - padding));
backgroundRect.setAttribute('y', String(-viewport.y - padding));
backgroundRect.setAttribute(
'width',
String(reactFlowBounds.width + 2 * padding)
);
backgroundRect.setAttribute(
'height',
String(reactFlowBounds.height + 2 * padding)
);
backgroundRect.setAttribute('fill', 'url(#background-pattern)');
tempSvg.appendChild(backgroundRect);
viewportElement.insertBefore(
tempSvg,
viewportElement.firstChild
);
try {
const dataUrl = await imageCreateFn(viewportElement, {
...(type === 'jpeg' || type === 'png'
? { backgroundColor: '#ffffff' }
? {
backgroundColor:
effectiveTheme === 'light'
? '#ffffff'
: '#141414',
}
: {}),
width: reactFlowBounds.width,
height: reactFlowBounds.height,
@@ -76,11 +166,13 @@ export const ExportImageProvider: React.FC<React.PropsWithChildren> = ({
},
quality: 1,
pixelRatio: scale,
}
);
});
downloadImage(dataUrl, type);
hideLoader();
downloadImage(dataUrl, type);
} finally {
viewportElement.removeChild(tempSvg);
hideLoader();
}
}, 0);
},
[
@@ -90,6 +182,7 @@ export const ExportImageProvider: React.FC<React.PropsWithChildren> = ({
imageCreatorMap,
setNodes,
showLoader,
effectiveTheme,
]
);