Skip to content

Commit bfbf732

Browse files
committed
update 20251019
1 parent 7a44072 commit bfbf732

File tree

15 files changed

+265
-141
lines changed

15 files changed

+265
-141
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ jobs:
77
- run: npm ci
88
- run: npm run type-check
99
- run: npm run check
10+
- run: npm test

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"react-use": "^17.6.0",
3030
"tiny-invariant": "^1.3.3",
3131
"transformation-matrix": "^3.1.0",
32+
"zod": "^4.1.12",
3233
"zustand": "^5.0.8"
3334
},
3435
"devDependencies": {

src/components/ComponentPropertyDialog.tsx

Lines changed: 117 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,157 @@
11
import {
2+
Box,
23
Button,
34
Dialog,
45
DialogActions,
56
DialogContent,
67
DialogTitle,
8+
FormLabel,
9+
Stack,
710
TextField,
811
} from "@mui/material";
12+
import nullthrows from "nullthrows";
913
import { useState } from "react";
14+
import { z } from "zod";
15+
import type CCStore from "../store";
16+
import type { CCComponentId } from "../store/component";
17+
import type { CCComponentPinId } from "../store/componentPin";
18+
import { useStore } from "../store/react";
1019

1120
export type ComponentPropertyDialogProps = {
21+
componentId: CCComponentId;
1222
defaultName: string;
13-
onAccept(newName: string): void;
23+
onClose(): void;
1424
onCancel(): void;
1525
};
1626

27+
const stateSchema = z.object({
28+
name: z.string().nonempty(),
29+
pinNameById: z.map(z.custom<CCComponentPinId>(), z.string().nonempty()),
30+
});
31+
function extractStateFromStore(
32+
store: CCStore,
33+
componentId: CCComponentId,
34+
): z.input<typeof stateSchema> {
35+
const component = nullthrows(store.components.get(componentId));
36+
return {
37+
name: component.name,
38+
pinNameById: new Map(
39+
store.componentPins
40+
.getManyByComponentId(componentId)
41+
.map((pin) => [pin.id, pin.name]),
42+
),
43+
};
44+
}
45+
function applyStateToStore(
46+
store: CCStore,
47+
componentId: CCComponentId,
48+
state: z.output<typeof stateSchema>,
49+
) {
50+
store.components.update(componentId, { name: state.name });
51+
for (const [pinId, pinName] of state.pinNameById) {
52+
store.componentPins.update(pinId, { name: pinName });
53+
}
54+
}
55+
1756
export function ComponentPropertyDialog({
18-
defaultName,
19-
onAccept,
20-
onCancel,
57+
componentId,
58+
onClose,
2159
}: ComponentPropertyDialogProps) {
22-
const [newName, setNewName] = useState(defaultName);
60+
const { store } = useStore();
61+
const component = nullthrows(store.components.get(componentId));
62+
const [state, setState] = useState(() =>
63+
extractStateFromStore(store, componentId),
64+
);
65+
const result = stateSchema.safeParse(state);
2366

2467
return (
25-
<Dialog maxWidth="xs" fullWidth open onClose={onCancel}>
68+
<Dialog maxWidth="sm" fullWidth open onClose={onClose}>
2669
<form
2770
onSubmit={(e) => {
2871
e.preventDefault();
29-
if (!newName) return;
30-
onAccept(newName);
72+
if (!result.success) return;
73+
applyStateToStore(store, componentId, result.data);
74+
onClose();
3175
}}
3276
>
33-
<DialogTitle>Component property</DialogTitle>
77+
<DialogTitle>{component.name} Properties</DialogTitle>
3478
<DialogContent>
79+
<FormLabel component="div">Name</FormLabel>
3580
<TextField
36-
label="Name"
37-
value={newName}
81+
size="small"
82+
value={state.name}
3883
fullWidth
39-
onChange={(e) => setNewName(e.target.value)}
84+
onChange={(e) => setState({ ...state, name: e.target.value })}
4085
placeholder="Name"
86+
sx={{ mt: 0.5 }}
4187
/>
88+
<Box
89+
sx={{
90+
display: "grid",
91+
gridTemplateColumns: "1fr 1fr",
92+
gap: 2,
93+
mt: 2,
94+
}}
95+
>
96+
<Stack sx={{ gap: 0.5 }}>
97+
<FormLabel component="div">Input Pins</FormLabel>
98+
{store.componentPins
99+
.getManyByComponentId(componentId)
100+
.filter((pin) => pin.type === "input")
101+
.map((pin) => (
102+
<TextField
103+
key={pin.id}
104+
size="small"
105+
value={state.pinNameById.get(pin.id)}
106+
fullWidth
107+
onChange={(e) =>
108+
setState({
109+
...state,
110+
pinNameById: new Map(state.pinNameById).set(
111+
pin.id,
112+
e.target.value,
113+
),
114+
})
115+
}
116+
/>
117+
))}
118+
</Stack>
119+
<Stack sx={{ gap: 0.5 }}>
120+
<FormLabel component="div">Output Pins</FormLabel>
121+
{store.componentPins
122+
.getManyByComponentId(componentId)
123+
.filter((pin) => pin.type === "output")
124+
.map((pin) => (
125+
<TextField
126+
key={pin.id}
127+
size="small"
128+
value={state.pinNameById.get(pin.id)}
129+
fullWidth
130+
onChange={(e) =>
131+
setState({
132+
...state,
133+
pinNameById: new Map(state.pinNameById).set(
134+
pin.id,
135+
e.target.value,
136+
),
137+
})
138+
}
139+
/>
140+
))}
141+
</Stack>
142+
</Box>
42143
</DialogContent>
43144
<DialogActions>
44-
<Button onClick={onCancel} color="inherit">
145+
<Button onClick={onClose} color="inherit">
45146
Cancel
46147
</Button>
47148
<Button
48149
variant="outlined"
49-
color="inherit"
150+
color="primary"
50151
type="submit"
51-
disabled={!newName}
152+
disabled={!result.success}
52153
>
53-
Create
154+
Apply
54155
</Button>
55156
</DialogActions>
56157
</form>

src/pages/edit/Editor/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ function CCComponentEditorContent({
5252
<CCComponentEditorNodePinPropertyEditor />
5353
{isComponentPropertyDialogOpen && (
5454
<ComponentPropertyDialog
55+
componentId={componentId}
5556
defaultName={component.name}
56-
onAccept={(newName) => {
57-
store.components.update(componentId, { name: newName });
57+
onClose={() => {
5858
setIsComponentPropertyDialogOpen(false);
5959
}}
6060
onCancel={() => {

src/pages/edit/Editor/renderer/InputValue.tsx

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import getCCComponentEditorRendererNodeGeometry from "./Node.geometry";
1111
export type CCComponentEditorRendererInputValueProps = {
1212
nodePinId: CCNodePinId;
1313
};
14+
// TODO: Change name to CCComponentEditorRendererComponentPin
1415
export default function CCComponentEditorRendererInputValue({
1516
nodePinId,
1617
}: CCComponentEditorRendererInputValueProps) {
@@ -22,16 +23,35 @@ export default function CCComponentEditorRendererInputValue({
2223
);
2324
const type = interfaceComponentPin.type;
2425

25-
const nodePinValue =
26-
type === "input"
27-
? nullthrows(componentEditorState.getInputValue(interfaceComponentPin.id))
28-
: nullthrows(componentEditorState.getNodePinValue(nodePinId));
29-
const updateInputValue = () => {
30-
componentEditorState.setInputValue(
31-
interfaceComponentPin.id,
32-
wrappingIncrementSimulationValue(nodePinValue),
33-
);
34-
};
26+
const { label, onClick } =
27+
componentEditorState.editorMode === "edit"
28+
? {
29+
label: interfaceComponentPin.name,
30+
onClick: null,
31+
}
32+
: {
33+
label: stringifySimulationValue(
34+
type === "input"
35+
? nullthrows(
36+
componentEditorState.getInputValue(interfaceComponentPin.id),
37+
)
38+
: nullthrows(componentEditorState.getNodePinValue(nodePinId)),
39+
),
40+
onClick:
41+
type === "input"
42+
? () => {
43+
const nodePinValue = nullthrows(
44+
componentEditorState.getInputValue(
45+
interfaceComponentPin.id,
46+
),
47+
);
48+
componentEditorState.setInputValue(
49+
interfaceComponentPin.id,
50+
wrappingIncrementSimulationValue(nodePinValue),
51+
);
52+
}
53+
: null,
54+
};
3555

3656
const nodePinPosition = nullthrows(
3757
getCCComponentEditorRendererNodeGeometry(
@@ -53,9 +73,9 @@ export default function CCComponentEditorRendererInputValue({
5373
stroke={theme.palette.textPrimary}
5474
fill={theme.palette.white}
5575
strokeWidth={1}
56-
{...(type === "input"
76+
{...(onClick
5777
? {
58-
onPointerDown: updateInputValue,
78+
onPointerDown: onClick,
5979
style: { cursor: "pointer" },
6080
}
6181
: {})}
@@ -70,7 +90,7 @@ export default function CCComponentEditorRendererInputValue({
7090
dy={1}
7191
style={{ pointerEvents: "none" }}
7292
>
73-
{stringifySimulationValue(nodePinValue)}
93+
{label}
7494
</text>
7595
</>
7696
);

src/pages/edit/Editor/renderer/NodePin.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,12 @@ export default function CCComponentEditorRendererNodePin({
134134
},
135135
});
136136

137-
const isSimulationMode = useComponentEditorStore()(
138-
(s) => s.editorMode === "play",
139-
);
140137
const interfaceComponentPin =
141138
store.componentPins.getByImplementation(nodePinId);
142139

143140
return (
144141
<>
145-
{isSimulationMode && interfaceComponentPin && (
142+
{interfaceComponentPin && (
146143
<CCComponentEditorRendererInputValue nodePinId={nodePinId} />
147144
)}
148145
<g {...draggableProps} style={{ cursor: "pointer" }}>

0 commit comments

Comments
 (0)