From 8687f60b0e74bda3b5fd3c4f2f751a2b6695b776 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 19 Oct 2024 19:59:22 -0500 Subject: [PATCH] update to save state into the hash --- bun.lockb | Bin 125148 -> 125180 bytes src/App.tsx | 145 +++++++++++++++++++++++------ src/SettingsContext.tsx | 14 ++- src/components/EditBox.module.scss | 1 + src/components/EditBox.tsx | 22 +++-- src/components/Path.tsx | 6 +- src/components/Point.tsx | 6 +- 7 files changed, 151 insertions(+), 43 deletions(-) diff --git a/bun.lockb b/bun.lockb index ea578d9e0842a975bd9572d9fb994f476604ccbe..c6639ba7b50a995b63432b49251f11227ec355f0 100755 GIT binary patch delta 109 zcmca}k^Rp__6dHBIgS2X{TZ$NI2hxM^b8I347N}7V|;GNsJ^|jfpK!WXhU<8ZgyqMl*=AuxL)siphp= GCjbB=HYDNz delta 80 zcmex!k^Rm^_6dHBK8^lc{TZ$NIG7k9VEaTr#^;8NJliW97$>JoLPZ%$f=Yu+TwPK; YUNC}%e5O0RU{q#VG$&`pWW%=;0IrW0fdBvi diff --git a/src/App.tsx b/src/App.tsx index 1ee5347..20f1dd5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { Canvas } from "@react-three/fiber"; +import { Canvas, useThree } from "@react-three/fiber"; import { OrbitControls } from "@react-three/drei"; import styles from "./styles.module.scss"; @@ -6,39 +6,105 @@ import "katex/dist/katex.min.css"; import Point from "./components/Point"; import Path from "./components/Path"; import { SettingsBox } from "./SettingsContext"; +import { useCallback, useState } from "react"; +import { atom, useAtom, useSetAtom } from "jotai"; // https://threejs.org/manual/#en/align-html-elements-to-3d +type coord = [number, number, number]; +const coords: coord[] = [ + [0, 0, 0], + [0, 0, 1], + [0, 1, 0], + [0, 1, 1], + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [1, 1, 1], +]; + +const ppCoord = (c: coord): string => c.map((n) => n.toString()).join(""); + +const offset = (a: coord): coord => [a[0] - 0.5, a[1] - 0.5, a[2] - 0.5]; + +const offsetCoords: [string, coord][] = coords.map((a) => [ + ppCoord(a), + offset(a), +]); + +function getInitialValue() { + try { + const h = location.hash; + if (h.length === 0) return defaultValues; + return JSON.parse(atob(h.replace("#", ""))); + } catch (e) { + console.log("error", e); + return defaultValues; + } +} + +export const stateAtom = atom<{ [_: string]: string }>(getInitialValue()); + +export const updateStateAtom = atom(null, (get, set, newValue) => { + set(stateAtom, newValue); + location.hash = btoa(JSON.stringify(newValue)); +}); + +function AdjustCamera() { + useThree(({ camera }) => { + camera.position.z = 2; + }); + return <>; +} + +const paths: [string, [coord, coord]][] = offsetCoords + .flatMap((a) => offsetCoords.map((b) => [a, b])) + .filter( + ([[_a, [a1, a2, a3]], [_b, [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 + ) + .map(([[aname, acoord], [bname, bcoord]]) => [ + aname + bname, + [acoord, bcoord], + ]); + +const defaultValues: [string, string] = Object.fromEntries([ + ...offsetCoords.map(([a]) => [a, ""]), + ...paths.map(([a]) => [a, ""]), +]); + +function parseCoord(s: string): coord { + return offset(s.split("").map((n) => parseInt(n))); +} + +function parsePath(s: string): [coord, coord] { + return [parseCoord(s.substring(0, 3)), parseCoord(s.substring(3, 6))]; +} + function App() { - 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], - ]; + const [state] = useAtom(stateAtom); + const updateStateFunc = useSetAtom(updateStateAtom); - coords = coords.map((a) => [a[0] - 0.5, a[1] - 0.5, a[2] - 0.5]); + console.log("state", state); - 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, - ); + const updateState = useCallback( + (z: string, value: string) => { + const newState = { ...state, [z]: value }; + updateStateFunc(newState); + }, + [state] + ); return (
+ - {coords.map((coord) => ( - - ))} - - {paths.map(([start, end]) => ( - - ))} + {Object.entries(state).map(([thing, value]) => { + if (thing.length === 6) { + const [start, end] = parsePath(thing); + return ( + updateState(thing, newValue)} + value={value} + /> + ); + } else if (thing.length === 3) { + const coord = parseCoord(thing); + return ( + updateState(thing, newValue)} + value={value} + /> + ); + } + })}
diff --git a/src/SettingsContext.tsx b/src/SettingsContext.tsx index 630f8db..4fe0b82 100644 --- a/src/SettingsContext.tsx +++ b/src/SettingsContext.tsx @@ -1,13 +1,25 @@ -import { atom, useAtom } from "jotai"; +import { atom, useAtom, useAtomValue, useSetAtom } from "jotai"; import styles from "./styles.module.scss"; +import { stateAtom, updateStateAtom } from "./App"; +import { useCallback } from "react"; export const showEmptyAtom = atom(true); export function SettingsBox() { + const state = useAtomValue(stateAtom); + const updateState = useSetAtom(updateStateAtom); const [showEmpty, setShowEmpty] = useAtom(showEmptyAtom); + const doClear = useCallback(() => { + updateState( + Object.fromEntries(Object.entries(state).map(([a, _]) => [a, ""])) + ); + }, [state]); + return (
+ + void; +} -export default function EditBox() { - const [value, setValue] = useState(""); +export default function EditBox({ onUpdate, value }: EditBoxProps) { + const [innerValue, setInnerValue] = useState(""); const [isEditing, setIsEditing] = useState(false); const showEmpty = useAtomValue(showEmptyAtom); + useEffect(() => { + setInnerValue(value); + }, [value]); + const handleDblClick = useCallback(() => { if (!isEditing) setIsEditing(true); }, [isEditing]); const done = useCallback( (evt: FormEvent) => { + onUpdate?.(innerValue); evt.preventDefault(); if (isEditing) setIsEditing(false); }, - [isEditing], + [value, isEditing, innerValue] ); return ( @@ -32,8 +40,8 @@ export default function EditBox() { // biome-ignore lint/a11y/noAutofocus: autoFocus={true} onBlur={done} - value={value} - onChange={(evt) => setValue(evt.target.value)} + value={innerValue} + onChange={(evt) => setInnerValue(evt.target.value)} placeholder="Type latex code..." /> diff --git a/src/components/Path.tsx b/src/components/Path.tsx index 1ccbb2a..41439dc 100644 --- a/src/components/Path.tsx +++ b/src/components/Path.tsx @@ -8,9 +8,11 @@ extend({ MeshLineGeometry, MeshLineMaterial }); export interface PointProps { start: [number, number, number]; end: [number, number, number]; + value: string; + onEdit?: (newValue: string) => void; } -export default function Path({ start, end }: PointProps) { +export default function Path({ start, end, onEdit, value }: PointProps) { const midpoint: [number, number, number] = [ (start[0] + end[0]) / 2.0, (start[1] + end[1]) / 2.0, @@ -23,7 +25,7 @@ export default function Path({ start, end }: PointProps) { - + ); diff --git a/src/components/Point.tsx b/src/components/Point.tsx index bb9115d..3f0b334 100644 --- a/src/components/Point.tsx +++ b/src/components/Point.tsx @@ -3,16 +3,18 @@ import EditBox from "./EditBox"; export interface PointProps { coord: [number, number, number]; + value: string; + onEdit?: (newValue: string) => void; } -export default function Point({ coord }: PointProps) { +export default function Point({ coord, onEdit, value }: PointProps) { return ( - + );