mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2025-12-24 06:58:48 -05:00
feat: adds image export options to toggle grid and change bg color
This commit is contained in:
43
package-lock.json
generated
43
package-lock.json
generated
@@ -19,6 +19,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"gsap": "^3.11.4",
|
||||
"immer": "^10.0.2",
|
||||
"mui-color-input": "^2.0.3",
|
||||
"paper": "^0.12.17",
|
||||
"pathfinding": "^0.4.18",
|
||||
"react-hook-form": "^7.43.2",
|
||||
@@ -641,6 +642,14 @@
|
||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.0.3.tgz",
|
||||
"integrity": "sha512-e9nEVehVJwkymQpkGhdSNzLT2Lr9UTTby+JePq4Z2SxBbOQjY7pLgSouAaXvfaGQVSAaY0U4eJdwfSDmCbItcw==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@@ -10860,6 +10869,27 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mui-color-input": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mui-color-input/-/mui-color-input-2.0.3.tgz",
|
||||
"integrity": "sha512-rAd040qQ0Y+8dk4gE8kkCiJ/vCgA0j4vv1quJ43BfORTFE3uHarHj0xY1Vo9CPbojtx1f5vW+CjckYPRIZPIRg==",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^5.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/multicast-dns": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
|
||||
@@ -15006,6 +15036,11 @@
|
||||
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
|
||||
"dev": true
|
||||
},
|
||||
"@ctrl/tinycolor": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.0.3.tgz",
|
||||
"integrity": "sha512-e9nEVehVJwkymQpkGhdSNzLT2Lr9UTTby+JePq4Z2SxBbOQjY7pLgSouAaXvfaGQVSAaY0U4eJdwfSDmCbItcw=="
|
||||
},
|
||||
"@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@@ -22662,6 +22697,14 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"mui-color-input": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mui-color-input/-/mui-color-input-2.0.3.tgz",
|
||||
"integrity": "sha512-rAd040qQ0Y+8dk4gE8kkCiJ/vCgA0j4vv1quJ43BfORTFE3uHarHj0xY1Vo9CPbojtx1f5vW+CjckYPRIZPIRg==",
|
||||
"requires": {
|
||||
"@ctrl/tinycolor": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"multicast-dns": {
|
||||
"version": "7.2.5",
|
||||
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz",
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"gsap": "^3.11.4",
|
||||
"immer": "^10.0.2",
|
||||
"mui-color-input": "^2.0.3",
|
||||
"paper": "^0.12.17",
|
||||
"pathfinding": "^0.4.18",
|
||||
"react-hook-form": "^7.43.2",
|
||||
|
||||
@@ -20,7 +20,8 @@ const App = ({
|
||||
height = '100%',
|
||||
onModelUpdated,
|
||||
enableDebugTools = false,
|
||||
editorMode = 'EDITABLE'
|
||||
editorMode = 'EDITABLE',
|
||||
renderer
|
||||
}: IsoflowProps) => {
|
||||
const uiStateActions = useUiStateStore((state) => {
|
||||
return state.actions;
|
||||
@@ -71,7 +72,7 @@ const App = ({
|
||||
transform: 'translateZ(0)'
|
||||
}}
|
||||
>
|
||||
<Renderer />
|
||||
<Renderer {...renderer} />
|
||||
<UiOverlay />
|
||||
</Box>
|
||||
</>
|
||||
|
||||
26
src/components/ColorSelector/ColorPicker.tsx
Normal file
26
src/components/ColorSelector/ColorPicker.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
MuiColorButtonProps,
|
||||
MuiColorInput,
|
||||
MuiColorInputProps
|
||||
} from 'mui-color-input';
|
||||
import React from 'react';
|
||||
import { ColorSwatch } from './ColorSwatch';
|
||||
|
||||
interface Props extends Omit<MuiColorInputProps, 'ref'> {}
|
||||
|
||||
const ColorButtonElement = ({ bgColor, onClick }: MuiColorButtonProps) => {
|
||||
return <ColorSwatch hex={bgColor} onClick={onClick} />;
|
||||
};
|
||||
export const ColorPicker = ({ value, onChange }: Props) => {
|
||||
return (
|
||||
<MuiColorInput
|
||||
size="small"
|
||||
variant="standard"
|
||||
format="hex"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
InputProps={{ disableUnderline: true, type: 'hidden' }}
|
||||
Adornment={ColorButtonElement}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -4,7 +4,7 @@ import { Box, Button } from '@mui/material';
|
||||
export type Props = {
|
||||
hex: string;
|
||||
isActive?: boolean;
|
||||
onClick: () => void;
|
||||
onClick: React.MouseEventHandler<HTMLButtonElement> | undefined;
|
||||
};
|
||||
|
||||
export const ColorSwatch = ({ hex, onClick, isActive }: Props) => {
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
Box,
|
||||
Button,
|
||||
Stack,
|
||||
Alert
|
||||
Alert,
|
||||
Checkbox,
|
||||
FormControlLabel,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { useModelStore } from 'src/stores/modelStore';
|
||||
import {
|
||||
@@ -27,6 +30,8 @@ import { useDiagramUtils } from 'src/hooks/useDiagramUtils';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
import { Isoflow } from 'src/Isoflow';
|
||||
import { Loader } from 'src/components/Loader/Loader';
|
||||
import { customVars } from 'src/styles/theme';
|
||||
import { ColorPicker } from 'src/components/ColorSelector/ColorPicker';
|
||||
|
||||
interface Props {
|
||||
quality?: number;
|
||||
@@ -87,6 +92,22 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
downloadFileUtil(data, generateGenericFilename('png'));
|
||||
}, [imageData]);
|
||||
|
||||
const [showGrid, setShowGrid] = useState(false);
|
||||
const handleShowGridChange = (checked: boolean) => {
|
||||
setShowGrid(checked);
|
||||
};
|
||||
|
||||
const [backgroundColor, setBackgroundColor] = useState<string>(
|
||||
customVars.customPalette.diagramBg
|
||||
);
|
||||
const handleBackgroundColorChange = (color: string) => {
|
||||
setBackgroundColor(color);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setImageData(undefined);
|
||||
}, [showGrid, backgroundColor]);
|
||||
|
||||
return (
|
||||
<Dialog open onClose={onClose}>
|
||||
<DialogTitle>Export as image</DialogTitle>
|
||||
@@ -131,6 +152,10 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
fitToView: true,
|
||||
view: currentView
|
||||
}}
|
||||
renderer={{
|
||||
showGrid,
|
||||
backgroundColor
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
@@ -148,9 +173,8 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{imageData && (
|
||||
<Stack alignItems="center" spacing={2}>
|
||||
<Stack alignItems="center" spacing={2}>
|
||||
{imageData && (
|
||||
<Box
|
||||
component="img"
|
||||
sx={{
|
||||
@@ -162,6 +186,37 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
src={imageData}
|
||||
alt="preview"
|
||||
/>
|
||||
)}
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Box component="fieldset">
|
||||
<Typography variant="caption" component="legend">
|
||||
Options
|
||||
</Typography>
|
||||
|
||||
<FormControlLabel
|
||||
label="Show grid"
|
||||
control={
|
||||
<Checkbox
|
||||
size="small"
|
||||
checked={showGrid}
|
||||
onChange={(event) => {
|
||||
handleShowGridChange(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<FormControlLabel
|
||||
label="Background color"
|
||||
control={
|
||||
<ColorPicker
|
||||
value={backgroundColor}
|
||||
onChange={handleBackgroundColorChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
{imageData && (
|
||||
<Stack sx={{ width: '100%' }} alignItems="flex-end">
|
||||
<Stack direction="row" spacing={2}>
|
||||
<Button variant="text" onClick={onClose}>
|
||||
@@ -170,8 +225,8 @@ export const ExportImageDialog = ({ onClose, quality = 1.5 }: Props) => {
|
||||
<Button onClick={downloadFile}>Download as PNG</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
)}
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{exportError && (
|
||||
<Alert severity="error">Could not export image</Alert>
|
||||
|
||||
@@ -13,8 +13,9 @@ import { SizeIndicator } from 'src/components/DebugUtils/SizeIndicator';
|
||||
import { SceneLayer } from 'src/components/SceneLayer/SceneLayer';
|
||||
import { TransformControlsManager } from 'src/components/TransformControlsManager/TransformControlsManager';
|
||||
import { useScene } from 'src/hooks/useScene';
|
||||
import { RendererProps } from 'src/types/rendererProps';
|
||||
|
||||
export const Renderer = () => {
|
||||
export const Renderer = ({ showGrid, backgroundColor }: RendererProps) => {
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const interactionsRef = useRef<HTMLDivElement>();
|
||||
const enableDebugTools = useUiStateStore((state) => {
|
||||
@@ -47,7 +48,7 @@ export const Renderer = () => {
|
||||
height: '100%',
|
||||
zIndex: 0,
|
||||
bgcolor: (theme) => {
|
||||
return theme.customVars.customPalette.diagramBg;
|
||||
return backgroundColor ?? theme.customVars.customPalette.diagramBg;
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -63,7 +64,7 @@ export const Renderer = () => {
|
||||
left: 0
|
||||
}}
|
||||
>
|
||||
<Grid />
|
||||
{showGrid && <Grid />}
|
||||
</Box>
|
||||
{mode.showCursor && (
|
||||
<SceneLayer>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { EditorModeEnum, MainMenuOptions } from './common';
|
||||
import type { Model } from './model';
|
||||
import type { RendererProps } from './rendererProps';
|
||||
|
||||
export type InitialData = Model & {
|
||||
fitToView?: boolean;
|
||||
@@ -14,4 +15,5 @@ export interface IsoflowProps {
|
||||
height?: number | string;
|
||||
enableDebugTools?: boolean;
|
||||
editorMode?: keyof typeof EditorModeEnum;
|
||||
renderer?: RendererProps;
|
||||
}
|
||||
|
||||
4
src/types/rendererProps.ts
Normal file
4
src/types/rendererProps.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface RendererProps {
|
||||
showGrid?: boolean;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user