commit c79fb7de78c6d561d841098d82b64e1865a547bf Author: Michael Zhang Date: Thu Jun 27 19:03:02 2024 -0500 start diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..7f434f4 --- /dev/null +++ b/biome.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "enabled": true, + "indentWidth": 2, + "indentStyle": "space" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..c178a16 Binary files /dev/null and b/bun.lockb differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..a49aafd --- /dev/null +++ b/index.html @@ -0,0 +1,2 @@ + +
\ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..977cb3a --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "d3": "^7.9.0", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.3", + "@types/d3": "^7.4.3", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react-swc": "^3.7.0", + "sass": "^1.77.6", + "vite": "^5.3.2" + } +} diff --git a/src/App.module.scss b/src/App.module.scss new file mode 100644 index 0000000..dd52477 --- /dev/null +++ b/src/App.module.scss @@ -0,0 +1,21 @@ +.container { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} + +circle { + fill: cadetblue; +} + +line { + stroke: #ccc; +} + +text { + text-anchor: middle; + font-family: "Helvetica Neue", Helvetica, sans-serif; + fill: #666; + font-size: 16px; +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..a8bc93f --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,148 @@ +import * as d3 from "d3"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import styles from "./App.module.scss"; +import { createPortal } from "react-dom"; + +interface NodeInfo extends d3.SimulationNodeDatum { + name: string; +} + +const nodes: NodeInfo[] = [ + { name: "A" }, + { name: "B" }, + { name: "C" }, + { name: "D" }, + { name: "E" }, + { name: "F" }, + { name: "G" }, + { name: "H" }, +]; + +const links = [ + { source: 0, target: 1 }, + { source: 0, target: 2 }, + { source: 0, target: 3 }, + { source: 1, target: 6 }, + { source: 3, target: 4 }, + { source: 3, target: 7 }, + { source: 4, target: 5 }, + { source: 4, target: 7 }, +]; + +export default function App() { + const svgRef = useRef(); + const simulationRef = useRef(); + + // Resize listener + const [rect, setRect] = useState(null); + useEffect(() => { + new ResizeObserver((entries) => { + for (const entry of entries) { + setRect(entry.contentRect); + } + }).observe(document.body); + }, []); + + const [simulationNodes, setSimulationNodes] = useState(() => nodes); + const ticked = useCallback((simulation: d3.Simulation) => { + const nodes = simulation.nodes(); + // setSimulationNodes(nodes); + // d3.select("svg .links") + // .selectAll("line") + // .data(links) + // .join("line") + // .attr("x1", (d) => d.source.x) + // .attr("x2", (d) => d.target.x) + // .attr("y1", (d) => d.source.y) + // .attr("y2", (d) => d.target.y); + + d3.select("svg .nodes") + .selectAll("rect") + .data(nodes) + .join("rect") + // .append("foreignObject") + .text((d) => d.name) + .attr("x", (d) => d.x) + .attr("y", (d) => d.y) + .attr("dy", (d) => 5); + }, []); + + useEffect(() => { + if (!rect) return; + + const simulation = d3 + .forceSimulation(nodes) + .force("charge", d3.forceManyBody()) + .force("link", d3.forceLink(links)) + .force("center", d3.forceCenter(rect.width / 2, rect.height / 2)); + + simulation.on("tick", () => ticked(simulation)); + + simulationRef.current = simulation; + }, [ticked, rect]); + + return ( +
+ {rect && ( + + Hello + + + {simulationNodes.map((node, idx) => { + console.log("node", node); + return ( + + {node.name} + + ); + })} + + + )} +
+ ); +} + +function ForeignObjectWrapper({ id }) { + const el = useMemo(() => { + return document.getElementById(id); + }, [id]); + return createPortal(, el); +} + +function Counter() { + const [counter, setCounter] = useState(0); + + //increase counter + const increase = () => { + setCounter((count) => count + 1); + }; + + //decrease counter + const decrease = () => { + setCounter((count) => count - 1); + }; + + //reset counter + const reset = () => { + setCounter(0); + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/global.scss b/src/global.scss new file mode 100644 index 0000000..b0c76d2 --- /dev/null +++ b/src/global.scss @@ -0,0 +1,8 @@ +html, +body, +#root { + width: 100%; + height: 100%; + padding: 0; + margin: 0; +} \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..413105c --- /dev/null +++ b/src/index.tsx @@ -0,0 +1,5 @@ +import App from "./App"; +import { createRoot } from "react-dom/client"; +import "./global.scss"; + +createRoot(document.getElementById("root")).render(); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6cacb64 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "jsx": "react-jsx" + } +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..cc93912 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc"; + +defineConfig({ + plugins: [react()], +});