From 18bc292879113e7a196cb94de99a5f505460ddcf Mon Sep 17 00:00:00 2001 From: Shahriar <31452340+ShahriarKh@users.noreply.github.com> Date: Sat, 20 Jan 2024 01:06:55 +0330 Subject: [PATCH] export as image --- admin/src/components/ExportModal.js | 129 ++++++++++++++++++++++++++++ admin/src/pages/HomePage/index.js | 21 +++-- admin/src/store/index.js | 6 ++ package-lock.json | 6 ++ package.json | 1 + 5 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 admin/src/components/ExportModal.js diff --git a/admin/src/components/ExportModal.js b/admin/src/components/ExportModal.js new file mode 100644 index 0000000..8543bbc --- /dev/null +++ b/admin/src/components/ExportModal.js @@ -0,0 +1,129 @@ +import { + ModalLayout, + ModalHeader, + ModalFooter, + ModalBody, + Button, + Typography, + SingleSelect, + SingleSelectOption, + NumberInput, +} from "@strapi/design-system"; +import { toJpeg, toPng, toSvg } from "html-to-image"; +import { useCallback, useState } from "react"; +import { useDigramStore } from "../store"; +import { useTheme } from "styled-components"; + +export function ExportModal({ imageRef }) { + const theme = useTheme(); + + const { setShowModal } = useDigramStore(); + + const [format, setFormat] = useState("png"); + const [quality, setQuality] = useState(0.95); + + function downloadImage(dataUrl, fileExtension) { + const link = document.createElement("a"); + link.download = `strapi-diagram-${new Date() + .toISOString() + .replace(/[-:T.]/g, "") + .slice(0, -5)}.${fileExtension}`; + link.href = dataUrl; + link.click(); + } + + const exportDiagram = useCallback(() => { + if (imageRef.current === null) { + return; + } + + const filter = (node) => { + const exclusionClasses = ["cte-plugin-controls"]; + return !exclusionClasses.some((classname) => + node.classList?.contains(classname) + ); + }; + + if (format == "png") { + toPng(imageRef.current, { + cacheBust: true, + filter: filter, + style: { + background: theme.colors.neutral100, + }, + }) + .then((dataUrl) => { + downloadImage(dataUrl, "png"); + }) + .catch((err) => { + console.log(err); + }); + } else if (format == "svg") { + toSvg(imageRef.current, { + cacheBust: true, + filter: filter, + style: { + background: theme.colors.neutral100, + }, + }) + .then((dataUrl) => { + downloadImage(dataUrl, "svg"); + }) + .catch((err) => { + console.log(err); + }); + } else if (format == "jpeg") { + toJpeg(imageRef.current, { + cacheBust: true, + filter: filter, + quality: quality, + style: { + background: theme.colors.neutral100, + }, + }) + .then((dataUrl) => { + downloadImage(dataUrl, "jpeg"); + }) + .catch((err) => { + console.log(err); + }); + } + }, [imageRef, quality, format]); + + return ( + setShowModal(false)}> + + + Export Diagram + + + + { + setFormat(undefined); + }} + value={format} + onChange={setFormat} + > + SVG + PNG + JPEG + + + {format == "jpeg" && ( + setQuality(value)} + value={quality} + /> + )} + + Export} + /> + + ); +} diff --git a/admin/src/pages/HomePage/index.js b/admin/src/pages/HomePage/index.js index ccbcfe5..7243f92 100644 --- a/admin/src/pages/HomePage/index.js +++ b/admin/src/pages/HomePage/index.js @@ -15,6 +15,7 @@ import OptionsBar from "../../components/OptionsBar"; import "reactflow/dist/style.css"; import "./styles.css"; import { useDigramStore } from "../../store"; +import { ExportModal } from "../../components/ExportModal"; const useEffectSkipInitial = (func, deps) => { const didMount = useRef(false); @@ -40,6 +41,8 @@ const HomePage = () => { toggleOption, options, setData, + showModal, + setShowModal, } = useDigramStore(); const nodeTypes = useMemo(() => ({ special: CustomNode }), []); @@ -52,14 +55,14 @@ const HomePage = () => { [] ); - const refresh = async () => { + const regenrate = async () => { const { data } = await get(`/strapi-content-type-explorer/get-types`); setData(data); drawDiagram(); }; useEffectSkipInitial(() => { - refresh(); + regenrate(); }, [options.showAdminTypes, options.showPluginTypes]); useEffect(() => { @@ -75,23 +78,30 @@ const HomePage = () => { options.showDefaultFields, ]); + const ref = useRef(null); + return (
}> + } secondaryAction={ - } />
{ color={getBackgroundColor(options.backgroundPattern, theme)} /> + {showModal && }
); diff --git a/admin/src/store/index.js b/admin/src/store/index.js index f7ba188..9dc4cf4 100644 --- a/admin/src/store/index.js +++ b/admin/src/store/index.js @@ -14,6 +14,7 @@ export const useDigramStore = create( nodes: [], edges: [], data: [], + showModal: false, options: { snapToGrid: false, showTypes: true, @@ -32,6 +33,11 @@ export const useDigramStore = create( data: contentTypesData, }); }, + setShowModal: (bool) => { + set({ + showModal: bool, + }); + }, toggleOption: (optionName, optionValue = null) => { let newOptions = { ...get().options, diff --git a/package-lock.json b/package-lock.json index 646ab0d..47ea74b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@tisoap/react-flow-smart-edge": "^3.0.0", + "html-to-image": "^1.11.11", "reactflow": "^11.10.2", "zustand": "^4.4.7" }, @@ -8927,6 +8928,11 @@ "node": ">=12" } }, + "node_modules/html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==" + }, "node_modules/html-webpack-plugin": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", diff --git a/package.json b/package.json index a1dd0a3..566d37c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@tisoap/react-flow-smart-edge": "^3.0.0", + "html-to-image": "^1.11.11", "reactflow": "^11.10.2", "zustand": "^4.4.7" },