mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-18 21:49:00 +00:00
fix(export image): Add support for displaying cardinality relationships + background (#407)
This commit is contained in:
@@ -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,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user