Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/fossflow-lib/dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/fossflow-lib/dist/utils/exportOptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export declare const transformToCompactFormat: (model: Model) => {
export declare const transformFromCompactFormat: (compactModel: any) => Model;
export declare const exportAsJSON: (model: Model) => void;
export declare const exportAsCompactJSON: (model: Model) => void;
export declare const exportAsImage: (el: HTMLDivElement, size?: Size, scale?: number) => Promise<string>;
export declare const exportAsImage: (el: HTMLDivElement, size?: Size, scale?: number, bgcolor?: string) => Promise<string>;
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
});
}, [uiStateActions]);

const [transparentBackground, setTransparentBackground] = useState(false);

const [backgroundColor, setBackgroundColor] = useState<string>(
customVars.customPalette.diagramBg
);

const exportImage = useCallback(() => {
if (!containerRef.current || isExporting.current) {
return;
Expand All @@ -107,7 +113,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
height: bounds.height
};

exportAsImage(containerRef.current as HTMLDivElement, containerSize, exportScale)
exportAsImage(containerRef.current as HTMLDivElement, containerSize, exportScale, transparentBackground ? 'transparent' : backgroundColor)
.then((data) => {
setImageData(data);
isExporting.current = false;
Expand All @@ -117,7 +123,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
setExportError(true);
isExporting.current = false;
});
}, [bounds, exportScale]);
}, [bounds, exportScale, transparentBackground, backgroundColor]);

// Crop the image based on selected area
const cropImage = useCallback((cropArea: CropArea, sourceImage: string) => {
Expand Down Expand Up @@ -248,6 +254,17 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);

// Draw checkerboard if transparent background
if (transparentBackground) {
const squareSize = 10;
for (let y = 0; y < canvas.height; y += squareSize) {
for (let x = 0; x < canvas.width; x += squareSize) {
ctx.fillStyle = (x / squareSize + y / squareSize) % 2 === 0 ? '#f0f0f0' : 'transparent';
ctx.fillRect(x, y, squareSize, squareSize);
}
}
}

// Draw the image scaled to fit canvas
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

Expand Down Expand Up @@ -306,7 +323,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
};

img.src = imageData;
}, [imageData, isInCropMode, cropArea]);
}, [imageData, isInCropMode, cropArea, transparentBackground]);

const [showGrid, setShowGrid] = useState(false);
const handleShowGridChange = (checked: boolean) => {
Expand All @@ -318,9 +335,15 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
setExpandLabels(checked);
};

const [backgroundColor, setBackgroundColor] = useState<string>(
customVars.customPalette.diagramBg
);
const handleTransparentBackgroundChange = (checked: boolean) => {
setTransparentBackground(checked);
if (checked) {
setBackgroundColor('transparent');
} else {
setBackgroundColor(customVars.customPalette.diagramBg);
}
};

const handleBackgroundColorChange = (color: string) => {
setBackgroundColor(color);
};
Expand Down Expand Up @@ -365,7 +388,7 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
}, 200);
return () => clearTimeout(timer);
}
}, [showGrid, backgroundColor, expandLabels, exportImage, cropToContent, exportScale]);
}, [showGrid, backgroundColor, expandLabels, exportImage, cropToContent, exportScale, transparentBackground]);

useEffect(() => {
if (!imageData) {
Expand Down Expand Up @@ -491,7 +514,10 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
sx={{
maxWidth: '100%',
maxHeight: '300px',
objectFit: 'contain'
objectFit: 'contain',
backgroundImage: transparentBackground ? 'linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%)' : undefined,
backgroundSize: transparentBackground ? '20px 20px' : undefined,
backgroundPosition: transparentBackground ? '0 0, 0 10px, 10px -10px, -10px 0px' : undefined
}}
src={displayImage}
alt="preview"
Expand Down Expand Up @@ -547,6 +573,20 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
<ColorPicker
value={backgroundColor}
onChange={handleBackgroundColorChange}
disabled={transparentBackground}
/>
}
/>

<FormControlLabel
label="Transparent background"
control={
<Checkbox
size="small"
checked={transparentBackground}
onChange={(event) => {
handleTransparentBackgroundChange(event.target.checked);
}}
/>
}
/>
Expand Down
4 changes: 1 addition & 3 deletions packages/fossflow-lib/src/components/Renderer/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ export const Renderer = ({ showGrid, backgroundColor }: RendererProps) => {
width: '100%',
height: '100%',
zIndex: 0,
bgcolor: (theme) => {
return backgroundColor ?? theme.customVars.customPalette.diagramBg;
}
bgcolor: (theme) => backgroundColor === 'transparent' ? 'transparent' : (backgroundColor ?? theme.customVars.customPalette.diagramBg)
}}
>
<SceneLayer>
Expand Down
7 changes: 4 additions & 3 deletions packages/fossflow-lib/src/utils/exportOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ export const exportAsCompactJSON = (model: Model) => {
export const exportAsImage = async (
el: HTMLDivElement,
size?: Size,
scale: number = 1
scale: number = 1,
bgcolor: string = '#ffffff'
) => {
// Calculate scaled dimensions
const width = size ? size.width * scale : el.clientWidth * scale;
Expand All @@ -189,7 +190,7 @@ export const exportAsImage = async (
width,
height,
cacheBust: true,
bgcolor: '#ffffff',
bgcolor,
quality: 1.0,
// Apply CSS transform for high-quality scaling
style: scale !== 1 ? {
Expand All @@ -208,7 +209,7 @@ export const exportAsImage = async (
width,
height,
cacheBust: true,
bgcolor: '#ffffff'
bgcolor
});
}
};