Skip to content

Commit 6786863

Browse files
committed
add immediate param update after setting change 🔁
1 parent 306bd4d commit 6786863

4 files changed

Lines changed: 135 additions & 50 deletions

File tree

src/components/datagraph/ContextView.tsx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { useNodes, AnyNodeState } from "../../nodes.context";
1+
import { useNodes } from "../../nodes.context";
22
import { useSelection } from "../../selection.context";
3+
import { isParamNodeState, NodeKind, NodeState, ParamKind } from "../../node.types";
34
import "./ContextView.css";
45
import { NodeSettings } from "../node/NodeSettings";
56

67
import { useCallback } from "react";
78

89
export function ContextView() {
910
const { getSelectedNode } = useSelection();
10-
const { getNode, updateNodeState } = useNodes();
11+
const { getNode, updateNodeSettings } = useNodes();
1112

1213
const node = getSelectedNode();
1314

@@ -18,19 +19,21 @@ export function ContextView() {
1819
const selectedNodeId = node?.nodeId || null;
1920

2021
const handleNodeSettingChange = useCallback(
21-
(nodeId: string, settingsKey: string, value: unknown) => {
22+
<T extends NodeKind>(
23+
nodeId: string,
24+
settingsKey: keyof NodeState<T>["settings"],
25+
value: NodeState<T>["settings"][keyof NodeState<T>["settings"]]
26+
) => {
2227
const nodeToChange = getNode(nodeId);
23-
if (!nodeToChange) return;
24-
updateNodeState(
25-
nodeId,
26-
(curr) =>
27-
({
28-
...curr,
29-
settings: { ...curr.settings, [settingsKey]: value },
30-
}) as AnyNodeState
31-
);
28+
if (!nodeToChange) throw new Error(`Node ${nodeId} not found`);
29+
if (!nodeToChange.settings)
30+
throw new Error(`Node ${nodeId} of kind ${nodeToChange.kind} has no settings`);
31+
updateNodeSettings(nodeToChange.kind, nodeId, (curr) => ({
32+
...curr,
33+
[settingsKey]: value,
34+
}));
3235
},
33-
[getNode, updateNodeState]
36+
[getNode, updateNodeSettings]
3437
);
3538

3639
return (
@@ -53,10 +56,14 @@ export function ContextView() {
5356
{node.outputPorts.map((port) => port.name).join(", ")}]
5457
</div>
5558
</div>
56-
<NodeSettings
57-
nodeId={selectedNodeId}
58-
onChange={(...args) => handleNodeSettingChange(selectedNodeId, ...args)}
59-
/>
59+
{isParamNodeState(node) && (
60+
<NodeSettings
61+
node={node}
62+
onChange={(key, value) =>
63+
handleNodeSettingChange<ParamKind>(selectedNodeId, key, value)
64+
}
65+
/>
66+
)}
6067
</>
6168
)}
6269
</aside>

src/components/node/NodeSettings.tsx

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
11
import { NodeKind, NodeState } from "../../node.types";
2-
import { useNodes } from "../../nodes.context";
3-
import { allUnits } from "../../unit-conversion";
2+
import { allUnits, Unit } from "../../unit-conversion";
43
import { DataField } from "../DataField";
54
import "./NodeSettings.css";
65

7-
export type NodeSettingsProps = {
8-
nodeId: string;
9-
onChange: (settingsKey: string, value: unknown) => void;
6+
export type NodeSettingsProps<T extends NodeKind> = {
7+
node: NodeState<T>;
8+
onChange: <K extends keyof NodeState<T>["settings"]>(
9+
settingsKey: K,
10+
value: NodeState<T>["settings"][K]
11+
) => void;
1012
};
1113

12-
export function NodeSettings(props: NodeSettingsProps) {
13-
const { getNode } = useNodes();
14-
15-
const { kind, settings } = getNode(props.nodeId)!;
14+
type NodeSettingsComponentProps<T extends NodeKind> = {
15+
settings: NodeState<T>["settings"];
16+
onChange: <K extends keyof NodeState<T>["settings"]>(
17+
settingsKey: K,
18+
value: NodeState<T>["settings"][K]
19+
) => void;
20+
};
1621

17-
switch (kind) {
22+
export function NodeSettings<T extends NodeKind>(props: NodeSettingsProps<T>) {
23+
switch (props.node.kind) {
1824
case "param:slider":
19-
return <SliderParamNodeSettings settings={settings} onChange={props.onChange} />;
25+
return (
26+
<SliderParamNodeSettings
27+
settings={props.node.settings as NodeSettingsComponentProps<"param:slider">["settings"]}
28+
onChange={props.onChange as NodeSettingsComponentProps<"param:slider">["onChange"]}
29+
/>
30+
);
2031
case "param:button":
21-
return <ButtonParamNodeSettings settings={settings} onChange={props.onChange} />;
32+
return (
33+
<ButtonParamNodeSettings
34+
settings={props.node.settings as NodeSettingsComponentProps<"param:button">["settings"]}
35+
onChange={props.onChange as NodeSettingsComponentProps<"param:button">["onChange"]}
36+
/>
37+
);
2238
case "param:input":
23-
return <InputParamNodeSettings settings={settings} onChange={props.onChange} />;
39+
return (
40+
<InputParamNodeSettings
41+
settings={props.node.settings as NodeSettingsComponentProps<"param:input">["settings"]}
42+
onChange={props.onChange as NodeSettingsComponentProps<"param:input">["onChange"]}
43+
/>
44+
);
2445
}
2546
}
2647

27-
type NodeSettingsComponentProps<T extends NodeKind> = {
28-
settings: NodeState<T>["settings"];
29-
onChange: (settingsKey: string, value: unknown) => void;
30-
};
31-
3248
function SliderParamNodeSettings(props: NodeSettingsComponentProps<"param:slider">) {
3349
return (
3450
<div className="nodesettings">
@@ -39,7 +55,7 @@ function SliderParamNodeSettings(props: NodeSettingsComponentProps<"param:slider
3955
name="unit"
4056
id="pet-select"
4157
value={props.settings.unit}
42-
onChange={(ev) => props.onChange("unit", ev.target.value)}
58+
onChange={(ev) => props.onChange("unit", ev.target.value as Unit)}
4359
>
4460
{allUnits.map((unit) => (
4561
<option key={unit} value={unit}>
@@ -56,9 +72,7 @@ function SliderParamNodeSettings(props: NodeSettingsComponentProps<"param:slider
5672
type="number"
5773
step={1}
5874
value={props.settings.min}
59-
onChange={(ev) => {
60-
props.onChange("min", parseFloat(ev.target.value));
61-
}}
75+
onChange={(ev) => props.onChange("min", parseFloat(ev.target.value))}
6276
/>
6377
}
6478
/>
@@ -69,9 +83,7 @@ function SliderParamNodeSettings(props: NodeSettingsComponentProps<"param:slider
6983
type="number"
7084
step={1}
7185
value={props.settings.max}
72-
onChange={(ev) => {
73-
props.onChange("max", parseFloat(ev.target.value));
74-
}}
86+
onChange={(ev) => props.onChange("max", parseFloat(ev.target.value))}
7587
/>
7688
}
7789
/>
@@ -82,9 +94,7 @@ function SliderParamNodeSettings(props: NodeSettingsComponentProps<"param:slider
8294
type="number"
8395
step={0.01}
8496
value={props.settings.step}
85-
onChange={(ev) => {
86-
props.onChange("step", parseFloat(ev.target.value));
87-
}}
97+
onChange={(ev) => props.onChange("step", parseFloat(ev.target.value))}
8898
/>
8999
}
90100
/>
@@ -102,7 +112,7 @@ function ButtonParamNodeSettings(props: NodeSettingsComponentProps<"param:button
102112
name="unit"
103113
id="pet-select"
104114
value={props.settings.unit}
105-
onChange={(ev) => props.onChange("unit", ev.target.value)}
115+
onChange={(ev) => props.onChange("unit", ev.target.value as Unit)}
106116
>
107117
{allUnits.map((unit) => (
108118
<option key={unit} value={unit}>
@@ -126,7 +136,7 @@ function InputParamNodeSettings(props: NodeSettingsComponentProps<"param:input">
126136
name="unit"
127137
id="pet-select"
128138
value={props.settings.unit}
129-
onChange={(ev) => props.onChange("unit", ev.target.value)}
139+
onChange={(ev) => props.onChange("unit", ev.target.value as Unit)}
130140
>
131141
{allUnits.map((unit) => (
132142
<option key={unit} value={unit}>

src/node.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,10 @@ export function isVisualizerNodeState(
112112
): nodeState is AnyVisualizerNodeState {
113113
return isVisualizerKind(nodeState.kind);
114114
}
115+
116+
export function isNodeStateOfKind<T extends NodeKind>(
117+
nodeState: AnyNodeState,
118+
kind: T
119+
): nodeState is NodeState<T> {
120+
return nodeState.kind === kind;
121+
}

src/nodes.context.tsx

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useDatagraph } from "./datagraph.context";
22
import {
3+
AnyParamNodeState,
4+
isNodeStateOfKind,
35
isParamKind,
46
isParamNodeState,
57
isVisualizerKind,
@@ -155,7 +157,42 @@ function useAllNodes() {
155157
[nodes]
156158
);
157159

158-
return { nodes, addNode, removeNode, updateNodeState, getNode, setParamValue };
160+
const updateNodeSettings = useCallback(
161+
async <T extends NodeKind>(
162+
kind: T,
163+
nodeId: string,
164+
updateNodeSettings: (current: NodeState<T>["settings"]) => NodeState<T>["settings"]
165+
) => {
166+
if (!ready) return;
167+
const node = nodes[nodeId];
168+
if (!node) throw new Error(`Node ${nodeId} not found`);
169+
if (!isNodeStateOfKind(node, kind)) throw new Error(`Node ${nodeId} is not of kind ${kind}`);
170+
const updatedSettings = updateNodeSettings(node.settings as NodeState<T>["settings"]);
171+
updateNodeState(
172+
nodeId,
173+
(current) =>
174+
({
175+
...current,
176+
settings: updatedSettings,
177+
}) as AnyNodeState
178+
);
179+
if (isParamKind(kind) && isParamNodeState(node)) {
180+
const pramSetings = updatedSettings as AnyParamNodeState["settings"];
181+
await setParamInGraph(nodeId, convertToCv(node.config.value, pramSetings.unit));
182+
}
183+
},
184+
[nodes, ready, setParamInGraph, updateNodeState]
185+
);
186+
187+
return {
188+
nodes,
189+
addNode,
190+
removeNode,
191+
updateNodeState,
192+
updateNodeSettings,
193+
getNode,
194+
setParamValue,
195+
};
159196
}
160197

161198
const nodesContext = createContext<{
@@ -170,6 +207,11 @@ const nodesContext = createContext<{
170207
updateNodeState: (nodeId: string, update: (current: AnyNodeState) => AnyNodeState) => void;
171208
getNode: (nodeId: string) => AnyNodeState | undefined;
172209
setParamValue: (nodeId: string, value: number) => Promise<void>;
210+
updateNodeSettings: <T extends NodeKind>(
211+
kind: T,
212+
nodeId: string,
213+
updateNodeSettings: (current: NodeState<T>["settings"]) => NodeState<T>["settings"]
214+
) => Promise<void>;
173215
}>({
174216
nodes: {},
175217
addNode: async () => {
@@ -187,14 +229,33 @@ const nodesContext = createContext<{
187229
setParamValue: async () => {
188230
throw new Error("Nodes context not initialized yet");
189231
},
232+
updateNodeSettings: async () => {
233+
throw new Error("Nodes context not initialized yet");
234+
},
190235
});
191236

192237
export function NodesProvider({ children }: { children: React.ReactNode }) {
193-
const { nodes, addNode, removeNode, updateNodeState, getNode, setParamValue } = useAllNodes();
238+
const {
239+
nodes,
240+
addNode,
241+
removeNode,
242+
updateNodeState,
243+
getNode,
244+
setParamValue,
245+
updateNodeSettings,
246+
} = useAllNodes();
194247

195248
return (
196249
<nodesContext.Provider
197-
value={{ nodes, addNode, removeNode, updateNodeState, getNode, setParamValue }}
250+
value={{
251+
nodes,
252+
addNode,
253+
removeNode,
254+
updateNodeState,
255+
getNode,
256+
setParamValue,
257+
updateNodeSettings,
258+
}}
198259
>
199260
{children}
200261
</nodesContext.Provider>

0 commit comments

Comments
 (0)