From 84ebac763fb054af04cb30358847dc6c828b5135 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Tue, 1 Oct 2024 19:16:16 -0500 Subject: [PATCH] initial --- .gitignore | 24 +++++++ README.md | 50 +++++++++++++ biome.json | 32 +++++++++ bun.lockb | Bin 0 -> 124764 bytes eslint.config.js | 28 ++++++++ index.html | 13 ++++ package.json | 42 +++++++++++ public/vite.svg | 1 + src/App.css | 42 +++++++++++ src/App.tsx | 112 +++++++++++++++++++++++++++++ src/assets/react.svg | 1 + src/components/EditBox.module.scss | 7 ++ src/components/EditBox.tsx | 41 +++++++++++ src/components/Path.tsx | 30 ++++++++ src/components/Point.tsx | 19 +++++ src/index.css | 17 +++++ src/main.tsx | 10 +++ src/styles.module.scss | 11 +++ src/vite-env.d.ts | 10 +++ tsconfig.app.json | 24 +++++++ tsconfig.json | 7 ++ tsconfig.node.json | 22 ++++++ vite.config.ts | 7 ++ 23 files changed, 550 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 biome.json create mode 100755 bun.lockb create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package.json create mode 100644 public/vite.svg create mode 100644 src/App.css create mode 100644 src/App.tsx create mode 100644 src/assets/react.svg create mode 100644 src/components/EditBox.module.scss create mode 100644 src/components/EditBox.tsx create mode 100644 src/components/Path.tsx create mode 100644 src/components/Point.tsx create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/styles.module.scss create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..017fe2c --- /dev/null +++ b/biome.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.2/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 80 + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..5d46ce1c1db415d01e084c97faca27f72d9d7412 GIT binary patch literal 124764 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 Q=u=l!|4F0x|MK_$0BFy|2LJ#7 literal 0 HcmV?d00001 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/index.html b/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..88a626c --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "cubeviz", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@react-three/drei": "^9.114.0", + "@react-three/fiber": "^8.17.8", + "@types/three": "^0.169.0", + "meshline": "^3.3.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-katex": "^3.0.1", + "sass": "^1.79.3", + "three": "^0.169.0", + "three-stdlib": "^2.33.0" + }, + "devDependencies": { + "@biomejs/biome": "^1.9.2", + "@eslint/js": "^9.9.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@types/react-katex": "^3.0.4", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "^9.9.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "globals": "^15.9.0", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.1", + "vite": "^5.4.1" + }, + "trustedDependencies": [ + "@biomejs/biome" + ] +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..497920e --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,112 @@ +import { ReactNode, useCallback, useRef, useState } from "react"; +import { Canvas, extend, useFrame } from "@react-three/fiber"; +import { OrbitControls, OrthographicCamera } from "@react-three/drei"; +import { BoxGeometry, EdgesGeometry, LineBasicMaterial } from "three"; +import { InlineMath, BlockMath } from "react-katex"; +import { createPortal } from "react-dom"; + +import "./App.css"; +import styles from "./styles.module.scss"; +import "katex/dist/katex.min.css"; +import Point from "./components/Point"; +import Path from "./components/Path"; + +// https://threejs.org/manual/#en/align-html-elements-to-3d + +function createBox({ dimensions, ...props }) { + // This reference gives us direct access to the THREE.Mesh object + const ref = useRef(); + // Hold state for hovered and clicked events + const [hovered, hover] = useState(false); + const [clicked, click] = useState(false); + + const box = new BoxGeometry(1, 1, 1); + const edges = new EdgesGeometry(box); + + const label = ( +
+ helloge {JSON.stringify(clicked)} + +
+ ); + + // Return the view, these are regular Threejs elements expressed in JSX + const view = ( + click(!clicked)} + onPointerOver={(event) => hover(true)} + onPointerOut={(event) => hover(false)} + > + {/* */} + {/* */} + + + + ); + + return [view, label]; +} + +function App() { + const [labelContainerEl, setLabelContainerEl] = + useState(null); + const labelContainerRef = useCallback((el) => { + if (el) setLabelContainerEl(el); + }, []); + + let coords: [number, number, number][] = [ + [0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1], + ]; + + coords = coords.map((a) => [a[0] - 0.5, a[1] - 0.5, a[2] - 0.5]); + + const paths = coords + .flatMap((a) => coords.map((b) => [a, b])) + .filter( + ([[a1, a2, a3], [b1, b2, b3]]) => + [a1 === b1 ? 1 : 0, a2 === b2 ? 1 : 0, a3 === b3 ? 1 : 0].reduce( + (x, y) => x + y, + ) === 2 && + a1 <= b1 && + a2 <= b2 && + a3 <= b3, + ); + + return ( + <> + + + + + + + {coords.map((coord) => ( + + ))} + + {paths.map(([start, end]) => ( + + ))} + + + ); +} + +export default App; diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/EditBox.module.scss b/src/components/EditBox.module.scss new file mode 100644 index 0000000..d19aa3e --- /dev/null +++ b/src/components/EditBox.module.scss @@ -0,0 +1,7 @@ +.editBox { + padding: 4px; +} + +.empty { + color: rgba(0, 0, 0, 0.2) +} \ No newline at end of file diff --git a/src/components/EditBox.tsx b/src/components/EditBox.tsx new file mode 100644 index 0000000..ea5a7ff --- /dev/null +++ b/src/components/EditBox.tsx @@ -0,0 +1,41 @@ +import { useCallback, useState } from "react"; +import { InlineMath } from "react-katex"; + +import styles from "./EditBox.module.scss"; + +export interface EditBoxProps {} + +export default function EditBox({}: EditBoxProps) { + const [value, setValue] = useState(""); + const [isEditing, setIsEditing] = useState(false); + + const handleDblClick = useCallback(() => { + if (!isEditing) setIsEditing(true); + }, [isEditing]); + + const done = useCallback( + (evt) => { + evt.preventDefault(); + if (isEditing) setIsEditing(false); + }, + [isEditing], + ); + + return ( +
+ {isEditing ? ( +
+ setValue(evt.target.value)} + /> +
+ ) : value === "" ? ( + (empty) + ) : ( + + )} +
+ ); +} diff --git a/src/components/Path.tsx b/src/components/Path.tsx new file mode 100644 index 0000000..4af3a4c --- /dev/null +++ b/src/components/Path.tsx @@ -0,0 +1,30 @@ +import { extend, useFrame } from "@react-three/fiber"; +import { Html, Line } from "@react-three/drei"; +import { MeshLineGeometry, MeshLineMaterial, raycast } from "meshline"; +import EditBox from "./EditBox"; + +extend({ MeshLineGeometry, MeshLineMaterial }); + +export interface PointProps { + start: [number, number, number]; + end: [number, number, number]; +} + +export default function Path({ start, end }: PointProps) { + const midpoint: [number, number, number] = [ + (start[0] + end[0]) / 2.0, + (start[1] + end[1]) / 2.0, + (start[2] + end[2]) / 2.0, + ]; + + return ( + <> + + + + + + + + ); +} diff --git a/src/components/Point.tsx b/src/components/Point.tsx new file mode 100644 index 0000000..bb9115d --- /dev/null +++ b/src/components/Point.tsx @@ -0,0 +1,19 @@ +import { Html } from "@react-three/drei"; +import EditBox from "./EditBox"; + +export interface PointProps { + coord: [number, number, number]; +} + +export default function Point({ coord }: PointProps) { + return ( + + + + + + + + + ); +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..cdb8e50 --- /dev/null +++ b/src/index.css @@ -0,0 +1,17 @@ +body, html { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} + +body { + display: flex; +} + +#root { + width: 100%; + height: 100%; + margin: 0; + padding: 0; +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..6f4ac9b --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/styles.module.scss b/src/styles.module.scss new file mode 100644 index 0000000..75d6a64 --- /dev/null +++ b/src/styles.module.scss @@ -0,0 +1,11 @@ +.canvas { + position: relative; + width: 100%; + height: 100%; +} + +.labels { + position: absolute; + left: 0; + top: 0; +} \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..6564507 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,10 @@ +/// + +import type { Object3DNode, MaterialNode } from "@react-three/fiber"; + +declare module "@react-three/fiber" { + interface ThreeElements { + meshLineGeometry: Object3DNode; + meshLineMaterial: MaterialNode; + } +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..f0a2350 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..0d3d714 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +})