Skip to content

Commit 5cc71e4

Browse files
authored
Merge pull request #51 from ut-code/migrate-to-react
Migrate to React
2 parents 021c8bb + 18e88e2 commit 5cc71e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2288
-4639
lines changed

package-lock.json

+704-925
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+15-16
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,32 @@
1313
"lint:fix": "eslint . --fix"
1414
},
1515
"dependencies": {
16-
"@emotion/react": "^11.11.1",
17-
"@emotion/styled": "^11.11.0",
16+
"@emotion/react": "^11.13.3",
17+
"@emotion/styled": "^11.13.0",
1818
"@mui/icons-material": "^5.11.16",
1919
"@mui/material": "^5.13.5",
20-
"@pixi/color": "^7.2.4",
21-
"@pixi/graphics-extras": "^7.2.4",
22-
"@pixi/math-extras": "^7.2.4",
2320
"eventemitter3": "^5.0.1",
24-
"mnemonist": "^0.39.5",
21+
"memoize-one": "^6.0.0",
22+
"mnemonist": "^0.39.8",
2523
"nullthrows": "^1.1.1",
26-
"pixi.js": "^7.2.4",
27-
"react": "^18.2.0",
28-
"react-dom": "^18.2.0",
29-
"tiny-invariant": "^1.3.1",
24+
"react": "^18.3.1",
25+
"react-dom": "^18.3.1",
26+
"react-use": "^17.5.1",
27+
"tiny-invariant": "^1.3.3",
28+
"transformation-matrix": "^2.16.1",
3029
"zustand": "^4.3.9"
3130
},
3231
"devDependencies": {
33-
"@tsconfig/esm": "^1.0.3",
34-
"@tsconfig/strictest": "^2.0.1",
35-
"@types/react": "^18.2.12",
36-
"@types/react-dom": "^18.2.5",
37-
"@vitejs/plugin-react": "^4.0.0",
32+
"@tsconfig/esm": "^1.0.5",
33+
"@tsconfig/strictest": "^2.0.5",
34+
"@types/react": "^18.3.12",
35+
"@types/react-dom": "^18.3.1",
36+
"@vitejs/plugin-react": "^4.3.3",
3837
"eslint": "^8.43.0",
3938
"eslint-config-airbnb-typescript-prettier": "^5.0.0",
4039
"prettier": "^2.8.8",
4140
"type-fest": "^3.12.0",
42-
"typescript": "~5.0",
41+
"typescript": "^5.6.3",
4342
"vite": "^4.3.9"
4443
}
4544
}

src/App.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Box } from "@mui/material";
22
import { useState } from "react";
3-
import "@pixi/math-extras";
43
import GlobalHeader from "./components/GlobalHeader";
54
import type { CCComponentId } from "./store/component";
65
import EditPage from "./pages/edit";

src/common/observable.ts

-40
This file was deleted.

src/common/perspective.ts

-6
This file was deleted.

src/common/theme.ts

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
import { Color } from "@pixi/color";
21
import { createTheme } from "@mui/material";
32

43
// See https://www.figma.com/file/M3dC0Gk98IGSGlxY901rBh/
5-
export const blackColor = 0x000000;
6-
export const whiteColor = 0xffffff;
4+
export const blackColor = "#000000";
5+
export const whiteColor = "#ffffff";
76
export const grayColor = {
8-
main: 0x9e9e9e,
9-
darken2: 0x616161,
7+
main: "#9e9e9e",
8+
darken2: "#616161",
109
};
11-
export const primaryColor = 0x00d372;
12-
export const activeColor = 0x00aaff;
13-
export const errorColor = 0xff0000;
14-
export const editorBackgroundColor = 0xf3f3f3;
15-
export const editorGridColor = 0xdddddd;
10+
export const primaryColor = "#00d372";
11+
export const activeColor = "#00aaff";
12+
export const errorColor = "#ff0000";
13+
export const editorBackgroundColor = "#f3f3f3";
14+
export const editorGridColor = "#dddddd";
1615

1716
export const theme = createTheme({
1817
palette: {
19-
primary: { main: new Color(primaryColor).toHex() },
18+
primary: { main: primaryColor },
2019
},
2120
});

src/common/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type Point = { x: number; y: number };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import {
2+
ClickAwayListener,
3+
MenuList,
4+
Paper,
5+
MenuItem,
6+
Divider,
7+
} from "@mui/material";
8+
import nullthrows from "nullthrows";
9+
import invariant from "tiny-invariant";
10+
import {
11+
CCComponentStore,
12+
type CCComponentId,
13+
} from "../../../../store/component";
14+
import {
15+
type CCConnection,
16+
CCConnectionStore,
17+
} from "../../../../store/connection";
18+
import {
19+
type CCNodeId,
20+
type CCNode,
21+
CCNodeStore,
22+
} from "../../../../store/node";
23+
import { useComponentEditorStore } from "../store";
24+
import { useStore } from "../../../../store/react";
25+
26+
export type CCComponentEditorContextMenuProps = {
27+
onEditComponent: (componentId: CCComponentId) => void;
28+
};
29+
30+
export default function CCComponentEditorContextMenu({
31+
onEditComponent,
32+
}: CCComponentEditorContextMenuProps) {
33+
const { store } = useStore();
34+
const componentEditorState = useComponentEditorStore()();
35+
36+
if (!componentEditorState.contextMenuState) return null;
37+
38+
return (
39+
<ClickAwayListener onClickAway={componentEditorState.closeContextMenu}>
40+
<MenuList
41+
component={Paper}
42+
dense
43+
sx={{
44+
position: "absolute",
45+
top: `${componentEditorState.contextMenuState.position.y}px`,
46+
left: `${componentEditorState.contextMenuState.position.x}px`,
47+
width: "200px",
48+
}}
49+
>
50+
<MenuItem onClick={componentEditorState.closeContextMenu}>
51+
Create a node
52+
</MenuItem>
53+
{componentEditorState.selectedNodeIds.size > 0 && (
54+
<MenuItem
55+
onClick={() => {
56+
const oldNodes = [...componentEditorState.selectedNodeIds].map(
57+
(nodeId) => {
58+
const node = store.nodes.get(nodeId);
59+
invariant(node);
60+
return node;
61+
}
62+
);
63+
const oldConnections = [
64+
...componentEditorState.selectedConnectionIds,
65+
].map((connectionId) => {
66+
const connection = store.connections.get(connectionId);
67+
invariant(connection);
68+
return connection;
69+
});
70+
const newComponent = CCComponentStore.create({
71+
name: "New Component",
72+
});
73+
store.components.register(newComponent);
74+
const oldToNewNodeIdMap = new Map<CCNodeId, CCNodeId>();
75+
const newNodes = oldNodes.map<CCNode>((oldNode) => {
76+
const newNode = CCNodeStore.create({
77+
parentComponentId: newComponent.id,
78+
position: oldNode.position,
79+
componentId: oldNode.componentId,
80+
variablePins: [],
81+
});
82+
oldToNewNodeIdMap.set(oldNode.id, newNode.id);
83+
return newNode;
84+
});
85+
for (const node of newNodes) store.nodes.register(node);
86+
const newConnections = oldConnections.flatMap<CCConnection>(
87+
(oldConnection) => {
88+
const oldFromNodePin = nullthrows(
89+
store.nodePins.get(oldConnection.from)
90+
);
91+
const oldToNodePin = nullthrows(
92+
store.nodePins.get(oldConnection.to)
93+
);
94+
const newFromNodeId = nullthrows(
95+
oldToNewNodeIdMap.get(oldFromNodePin.nodeId)
96+
);
97+
const newToNodeId = nullthrows(
98+
oldToNewNodeIdMap.get(oldToNodePin.nodeId)
99+
);
100+
return CCConnectionStore.create({
101+
parentComponentId: newComponent.id,
102+
from: store.nodePins.getByImplementationNodeIdAndPinId(
103+
newFromNodeId,
104+
oldFromNodePin.componentPinId
105+
).id,
106+
to: store.nodePins.getByImplementationNodeIdAndPinId(
107+
newToNodeId,
108+
oldToNodePin.componentPinId
109+
).id,
110+
bentPortion: oldConnection.bentPortion,
111+
});
112+
}
113+
);
114+
for (const connection of newConnections)
115+
store.connections.register(connection);
116+
store.connections.unregister([
117+
...componentEditorState.selectedConnectionIds,
118+
]);
119+
store.nodes.unregister([...componentEditorState.selectedNodeIds]);
120+
componentEditorState.closeContextMenu();
121+
onEditComponent(newComponent.id);
122+
}}
123+
>
124+
Create a new component...
125+
</MenuItem>
126+
)}
127+
{(componentEditorState.selectedNodeIds.size > 0 ||
128+
componentEditorState.selectedConnectionIds.size > 0) && (
129+
<MenuItem
130+
onClick={() => {
131+
if (componentEditorState.selectedNodeIds.size > 0)
132+
store.nodes.unregister([
133+
...componentEditorState.selectedNodeIds,
134+
]);
135+
if (componentEditorState.selectedConnectionIds.size > 0)
136+
store.connections.unregister([
137+
...componentEditorState.selectedConnectionIds,
138+
]);
139+
componentEditorState.selectNode([], true);
140+
componentEditorState.selectConnection([], false);
141+
componentEditorState.closeContextMenu();
142+
}}
143+
>
144+
Delete
145+
</MenuItem>
146+
)}
147+
{(() => {
148+
if (componentEditorState.selectedNodeIds.size !== 1) return undefined;
149+
const iteratorResult = componentEditorState.selectedNodeIds
150+
.values()
151+
.next();
152+
invariant(!iteratorResult.done);
153+
const targetNode = store.nodes.get(iteratorResult.value);
154+
invariant(targetNode);
155+
const targetComponent = store.components.get(targetNode.componentId);
156+
invariant(targetComponent);
157+
if (targetComponent.isIntrinsic) return undefined;
158+
return (
159+
<>
160+
<Divider />
161+
<MenuItem
162+
onClick={() => {
163+
invariant(targetNode);
164+
componentEditorState.closeContextMenu();
165+
onEditComponent(targetNode.componentId);
166+
}}
167+
>
168+
Edit...
169+
</MenuItem>
170+
</>
171+
);
172+
})()}
173+
</MenuList>
174+
</ClickAwayListener>
175+
);
176+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { KeyboardDoubleArrowRight, Edit, Close } from "@mui/icons-material";
2+
import { Paper, Box, IconButton } from "@mui/material";
3+
import nullthrows from "nullthrows";
4+
import { useComponentEditorStore } from "../store";
5+
import { useStore } from "../../../../store/react";
6+
7+
export type CCComponentEditorTitleBarProps = {
8+
onEditorClose: () => void;
9+
onComponentPropertyDialogOpen: () => void;
10+
};
11+
12+
export default function CCComponentEditorTitleBar({
13+
onEditorClose,
14+
onComponentPropertyDialogOpen,
15+
}: CCComponentEditorTitleBarProps) {
16+
const componentEditorStore = useComponentEditorStore();
17+
const componentEditorState = componentEditorStore();
18+
const { store } = useStore();
19+
const component = nullthrows(
20+
store.components.get(componentEditorState.componentId)
21+
);
22+
23+
return (
24+
<Paper
25+
sx={{
26+
position: "absolute",
27+
top: "30px",
28+
left: "30px",
29+
width: "400px",
30+
display: "flex",
31+
alignItems: "center",
32+
gap: 1,
33+
p: 1,
34+
}}
35+
>
36+
<Box sx={{ color: "text.secondary" }}>Components</Box>
37+
<KeyboardDoubleArrowRight />
38+
<span>{component.name}</span>
39+
<IconButton size="small" onClick={onComponentPropertyDialogOpen}>
40+
<Edit fontSize="small" />
41+
</IconButton>
42+
<div aria-hidden style={{ flexGrow: 1 }} />
43+
<IconButton size="small" onClick={onEditorClose}>
44+
<Close fontSize="small" />
45+
</IconButton>
46+
</Paper>
47+
);
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Edit, PlayArrow, SkipNext } from "@mui/icons-material";
2+
import { Fab } from "@mui/material";
3+
import { useComponentEditorStore } from "../store";
4+
5+
export default function CCComponentEditorViewModeSwitcher() {
6+
const componentEditorState = useComponentEditorStore()();
7+
8+
return (
9+
<>
10+
<Fab
11+
style={{ position: "absolute", bottom: "40px", right: "40px" }}
12+
color="primary"
13+
onClick={() => {
14+
componentEditorState.setEditorMode(
15+
componentEditorState.editorMode === "edit" ? "play" : "edit"
16+
);
17+
componentEditorState.resetTimeStep();
18+
}}
19+
>
20+
{componentEditorState.editorMode === "edit" ? <PlayArrow /> : <Edit />}
21+
</Fab>
22+
{componentEditorState.editorMode === "play" && (
23+
<Fab
24+
style={{ position: "absolute", bottom: "40px", right: "120px" }}
25+
color="primary"
26+
onClick={() => componentEditorState.incrementTimeStep()}
27+
>
28+
<SkipNext />
29+
</Fab>
30+
)}
31+
</>
32+
);
33+
}

0 commit comments

Comments
 (0)