From 0c76ae7928b4687bf6076b64585b95abb4d46b55 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Thu, 29 Jan 2026 20:22:57 +0100 Subject: [PATCH] Testing functionality --- app/app.css | 2 +- app/routes/home.tsx | 191 +++++++++++++++++++++++++++++++++++--------- package-lock.json | 44 +++++++++- package.json | 8 +- 4 files changed, 203 insertions(+), 42 deletions(-) diff --git a/app/app.css b/app/app.css index 8082405..c679462 100644 --- a/app/app.css +++ b/app/app.css @@ -7,7 +7,7 @@ html, body { - @apply bg-bg text-slate-200; + @apply bg-neutral-900 text-slate-200; @media (prefers-color-scheme: dark) { color-scheme: dark; diff --git a/app/routes/home.tsx b/app/routes/home.tsx index d3bc27d..b80b83d 100644 --- a/app/routes/home.tsx +++ b/app/routes/home.tsx @@ -1,71 +1,190 @@ import type React from "react"; +import { useEffect, useRef, useState } from "react"; +import * as papa from "papaparse"; import type { Route } from "./+types/home"; export function meta({ }: Route.MetaArgs) { return [ - { title: "New React Router App" }, - { name: "description", content: "Welcome to React Router!" }, + { title: "3dplot-gen" }, + { name: "description", content: "CSV → 3D Plot generator" }, ]; } +type Row = Record; + export default function Home() { + const plotRef = useRef(null); + + const [rows, setRows] = useState([]); + const [columns, setColumns] = useState([]); + const [x, setX] = useState(""); + const [y, setY] = useState(""); + const [z, setZ] = useState(""); + + // Render plot when axes change + useEffect(() => { + if (!plotRef.current || !x || !y || !z) return; + + let cancelled = false; + + (async () => { + const Plotly = await import("plotly.js-dist-min"); + if (cancelled) return; + + const trace = { + type: "scatter3d", + mode: "markers", + x: rows.map(r => r[x]), + y: rows.map(r => r[y]), + z: rows.map(r => r[z]), + marker: { + size: 4, + color: rows.map(r => r[z]), + colorscale: "Viridis", + }, + }; + + Plotly.newPlot( + plotRef.current!, + [trace as any], + { + paper_bgcolor: "#0b0d12", + plot_bgcolor: "#0b0d12", + font: { color: "#cfd3ff" }, + scene: { + xaxis: { title: { text: x } }, + yaxis: { title: { text: y } }, + zaxis: { title: { text: z } }, + }, + margin: { l: 0, r: 0, t: 0, b: 0 }, + }, + { responsive: true } + ); + })(); + + return () => { + cancelled = true; + }; + }, [rows, x, y, z]); + + + function onFile(file: File) { + papa.parse(file, { + header: true, + dynamicTyping: true, + skipEmptyLines: true, + complete: (res) => { + const data = res.data as Row[]; + setRows(data); + setColumns(Object.keys(data[0])); + }, + }); + } + + async function exportPNG() { + const Plotly = await import("plotly.js-dist-min"); + + if (!plotRef.current) return; + + Plotly.toImage(plotRef.current, { + format: "png", + width: 1600, + height: 900, + }).then((url) => { + const a = document.createElement("a"); + a.href = url; + a.download = "3dplot-gen.png"; + a.click(); + }); + } + return (
{/* Sidebar */} {/* Plot */}
-
+
); } -function Control({ label, children }: { label: string, children: React.ReactNode }) { +function Control({ + label, + children, +}: { + label: string; + children: React.ReactNode; +}) { return (
{children}
); -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index ea32556..398add5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,11 @@ "dependencies": { "@react-router/node": "7.12.0", "@react-router/serve": "7.12.0", + "@types/papaparse": "^5.5.2", + "@types/plotly.js-dist-min": "^2.3.4", "isbot": "^5.1.31", + "papaparse": "^5.5.3", + "plotly.js-dist-min": "^3.3.1", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router": "7.12.0" @@ -16,7 +20,7 @@ "devDependencies": { "@react-router/dev": "7.12.0", "@tailwindcss/vite": "^4.1.13", - "@types/node": "^22", + "@types/node": "^22.19.7", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "tailwindcss": "^4.1.13", @@ -1763,12 +1767,35 @@ "version": "22.19.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/papaparse": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-gFnFp/JMzLHCwRf7tQHrNnfhN4eYBVYYI897CGX4MY1tzY9l2aLkVyx2IlKZ/SAqDbB3I1AOZW5gTMGGsqWliA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/plotly.js": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/plotly.js/-/plotly.js-3.0.9.tgz", + "integrity": "sha512-nHKn7czWIPN7rT5wWI5qhML2O1Prm/Gx0NNe1MVr5GUL1zuzxbvIDmG7hlKMMtDsEtNfNQLMlMwk0CRtd3uqhg==", + "license": "MIT" + }, + "node_modules/@types/plotly.js-dist-min": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/plotly.js-dist-min/-/plotly.js-dist-min-2.3.4.tgz", + "integrity": "sha512-ISwLFV6Zs/v3DkaRFLyk2rvYAfVdnYP2VVVy7h+fBDWw52sn7sMUzytkWiN4M75uxr1uz1uiBioePTDpAfoFIg==", + "license": "MIT", + "dependencies": { + "@types/plotly.js": "*" + } + }, "node_modules/@types/react": { "version": "19.2.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", @@ -3225,6 +3252,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3286,6 +3319,12 @@ "dev": true, "license": "MIT" }, + "node_modules/plotly.js-dist-min": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/plotly.js-dist-min/-/plotly.js-dist-min-3.3.1.tgz", + "integrity": "sha512-ZxKM9DlEoEF3wBzGRPGHt6gWTJrm5N81J9AgX9UBX/Qjc9L4lRxtPBPq+RmBJWoA71j1X5Z1ouuguLkdoo88tg==", + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -3828,7 +3867,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, "license": "MIT" }, "node_modules/unpipe": { diff --git a/package.json b/package.json index d7ca862..bd0d72e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,11 @@ "dependencies": { "@react-router/node": "7.12.0", "@react-router/serve": "7.12.0", + "@types/papaparse": "^5.5.2", + "@types/plotly.js-dist-min": "^2.3.4", "isbot": "^5.1.31", + "papaparse": "^5.5.3", + "plotly.js-dist-min": "^3.3.1", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router": "7.12.0" @@ -19,7 +23,7 @@ "devDependencies": { "@react-router/dev": "7.12.0", "@tailwindcss/vite": "^4.1.13", - "@types/node": "^22", + "@types/node": "^22.19.7", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", "tailwindcss": "^4.1.13", @@ -27,4 +31,4 @@ "vite": "^7.1.7", "vite-tsconfig-paths": "^5.1.4" } -} \ No newline at end of file +}