diff --git a/package-lock.json b/package-lock.json index dde8a9c..b90a6f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "esc-pos-encoder": "^2.0.1", "file-saver": "^2.0.5", "framer-motion": "^11.5.4", + "html2canvas": "^1.4.1", "jszip": "^3.10.1", "lucide-react": "^0.383.0", "next": "14.2.3", @@ -3339,6 +3340,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3684,6 +3694,15 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-unit-converter": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", @@ -5145,6 +5164,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -7922,6 +7954,15 @@ "node": ">=8" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8220,6 +8261,15 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index c22af86..03ae0d8 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "esc-pos-encoder": "^2.0.1", "file-saver": "^2.0.5", "framer-motion": "^11.5.4", + "html2canvas": "^1.4.1", "jszip": "^3.10.1", "lucide-react": "^0.383.0", "next": "14.2.3", diff --git a/src/components/Canvas.tsx b/src/components/Canvas.tsx index 4644239..c84704b 100644 --- a/src/components/Canvas.tsx +++ b/src/components/Canvas.tsx @@ -9,6 +9,7 @@ import React, { import { SmoothieChart, TimeSeries } from "smoothie"; import { useTheme } from "next-themes"; import { BitSelection } from "./DataPass"; +import html2canvas from "html2canvas"; interface CanvasProps { data: string; @@ -34,6 +35,37 @@ const Canvas: React.FC = ({ () => [], [] ); + const [screenshotUrl, setScreenshotUrl] = useState(null); + const gridRef = useRef(null); + + const captureScreenshot = useCallback(() => { + if (gridRef.current) { + html2canvas(gridRef.current).then((canvas) => { + const url = canvas.toDataURL(); + setScreenshotUrl(url); + }); + } + }, []); + + useEffect(() => { + setIsGlobalPaused(!isDisplay); + + if (!isDisplay) { + captureScreenshot(); + } else { + setScreenshotUrl(null); + } + + chartRef.current.forEach((chart) => { + if (chart) { + if (isDisplay) { + chart.start(); + } else { + chart.stop(); + } + } + }); + }, [isDisplay, captureScreenshot]); const getChannelColor = useCallback( (index: number) => { @@ -282,9 +314,10 @@ const Canvas: React.FC = ({ return (
{channels.map((channel, index) => { if (channel) { @@ -308,6 +341,12 @@ const Canvas: React.FC = ({ } return null; })} + {screenshotUrl && ( +
+ )}
);