mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Add support for variable links
This commit is contained in:
parent
cf71176bc5
commit
a5eaba9ab3
6 changed files with 271 additions and 127 deletions
|
@ -4,6 +4,7 @@ import { QuerySubs, TypeDescriptor } from "../../engine/subs";
|
||||||
import { Variable } from "../../schema";
|
import { Variable } from "../../schema";
|
||||||
import DrawHeadConstructor from "../Content/HeadConstructor";
|
import DrawHeadConstructor from "../Content/HeadConstructor";
|
||||||
import { contentStyles } from "./../Content";
|
import { contentStyles } from "./../Content";
|
||||||
|
import { VariableName } from "./VariableName";
|
||||||
|
|
||||||
interface VariableElProps {
|
interface VariableElProps {
|
||||||
variable: Variable;
|
variable: Variable;
|
||||||
|
@ -51,23 +52,6 @@ function Helper({
|
||||||
desc: TypeDescriptor | undefined;
|
desc: TypeDescriptor | undefined;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { bg } = contentStyles(desc);
|
const { bg } = contentStyles(desc);
|
||||||
const varHeader =
|
|
||||||
!nested || raw ? (
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
"ring-1 ring-inset ring-black-100 px-1 bg-white rounded-md cursor",
|
|
||||||
nested ? "text-md" : "p-0.5"
|
|
||||||
)}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
onClick?.(variable);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{variable}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -76,7 +60,13 @@ function Helper({
|
||||||
nested ? "text-sm" : "p-0.5 pl-0 text-base"
|
nested ? "text-sm" : "p-0.5 pl-0 text-base"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{varHeader}
|
{(!nested || raw) && (
|
||||||
|
<VariableName
|
||||||
|
variable={variable}
|
||||||
|
onClick={onClick}
|
||||||
|
className={nested ? "text-md" : "p-0.5"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{children ? <span className="px-1">{children}</span> : <></>}
|
{children ? <span className="px-1">{children}</span> : <></>}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { QuerySubs } from "../../engine/subs";
|
||||||
|
import { Variable } from "../../schema";
|
||||||
|
import { VariableName } from "./VariableName";
|
||||||
|
|
||||||
|
export interface VariableLinkProps {
|
||||||
|
variable: Variable;
|
||||||
|
subs: QuerySubs;
|
||||||
|
onClick?: (variable: Variable) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VariableLink({
|
||||||
|
variable,
|
||||||
|
subs,
|
||||||
|
onClick,
|
||||||
|
}: VariableLinkProps): JSX.Element {
|
||||||
|
const root = subs.get_root_key(variable);
|
||||||
|
|
||||||
|
if (variable === root) {
|
||||||
|
throw new Error("VariableLink: variable is root");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx("rounded-md whitespace-nowrap space-x-1")}>
|
||||||
|
<VariableName className="inline-block" variable={variable} />
|
||||||
|
<span>→</span>
|
||||||
|
<VariableName
|
||||||
|
className="inline-block"
|
||||||
|
variable={root}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { Variable } from "../../schema";
|
||||||
|
|
||||||
|
export interface VariableNameProps {
|
||||||
|
variable: Variable;
|
||||||
|
onClick?: (variable: Variable) => void;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VariableName({
|
||||||
|
variable,
|
||||||
|
onClick,
|
||||||
|
className,
|
||||||
|
}: VariableNameProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
"ring-1 ring-inset ring-black-100 px-1 bg-white rounded-md",
|
||||||
|
onClick && "cursor-pointer",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick?.(variable);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{variable}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
|
@ -64,3 +64,5 @@ export function contentStyles(desc: TypeDescriptor | undefined): ContentStyles {
|
||||||
return { name: "Error", bg: "bg-red-400" };
|
return { name: "Error", bg: "bg-red-400" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const LinkStyles: ContentStyles = { name: "Link", bg: "bg-slate-500" };
|
||||||
|
|
|
@ -2,13 +2,20 @@ import clsx from "clsx";
|
||||||
import { Handle, Position } from "reactflow";
|
import { Handle, Position } from "reactflow";
|
||||||
import { Variable } from "../../schema";
|
import { Variable } from "../../schema";
|
||||||
import { assertExhaustive } from "../../utils/exhaustive";
|
import { assertExhaustive } from "../../utils/exhaustive";
|
||||||
import { contentStyles } from "../Content";
|
import { contentStyles, LinkStyles } from "../Content";
|
||||||
import { VariableElPretty } from "../Common/Variable";
|
import { VariableElPretty } from "../Common/Variable";
|
||||||
import { SubsSnapshot, TypeDescriptor } from "../../engine/subs";
|
import { SubsSnapshot, TypeDescriptor } from "../../engine/subs";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { TypedEmitter } from "tiny-typed-emitter";
|
import { TypedEmitter } from "tiny-typed-emitter";
|
||||||
|
import { VariableLink } from "../Common/VariableLink";
|
||||||
|
|
||||||
type AddSubVariableLink = (from: Variable, subVariable: Variable) => void;
|
type AddSubVariableLink = ({
|
||||||
|
from,
|
||||||
|
variable,
|
||||||
|
}: {
|
||||||
|
from: Variable;
|
||||||
|
variable: Variable;
|
||||||
|
}) => void;
|
||||||
|
|
||||||
export interface VariableMessageEvents {
|
export interface VariableMessageEvents {
|
||||||
focus: (variable: Variable) => void;
|
focus: (variable: Variable) => void;
|
||||||
|
@ -17,7 +24,7 @@ export interface VariableMessageEvents {
|
||||||
export interface VariableNodeProps {
|
export interface VariableNodeProps {
|
||||||
data: {
|
data: {
|
||||||
subs: SubsSnapshot;
|
subs: SubsSnapshot;
|
||||||
variable: Variable;
|
rawVariable: Variable;
|
||||||
addSubVariableLink: AddSubVariableLink;
|
addSubVariableLink: AddSubVariableLink;
|
||||||
isOutlined: boolean;
|
isOutlined: boolean;
|
||||||
ee: TypedEmitter<VariableMessageEvents>;
|
ee: TypedEmitter<VariableMessageEvents>;
|
||||||
|
@ -32,8 +39,8 @@ export default function VariableNode({
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
}: VariableNodeProps): JSX.Element {
|
}: VariableNodeProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
variable,
|
|
||||||
subs,
|
subs,
|
||||||
|
rawVariable,
|
||||||
addSubVariableLink,
|
addSubVariableLink,
|
||||||
isOutlined: isOutlinedProp,
|
isOutlined: isOutlinedProp,
|
||||||
ee: eeProp,
|
ee: eeProp,
|
||||||
|
@ -43,10 +50,10 @@ export default function VariableNode({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eeProp.on("focus", (focusVar: Variable) => {
|
eeProp.on("focus", (focusVar: Variable) => {
|
||||||
if (focusVar !== variable) return;
|
if (focusVar !== rawVariable) return;
|
||||||
setIsOutlined(true);
|
setIsOutlined(true);
|
||||||
});
|
});
|
||||||
}, [eeProp, variable]);
|
}, [eeProp, rawVariable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isOutlined) return;
|
if (!isOutlined) return;
|
||||||
|
@ -59,36 +66,82 @@ export default function VariableNode({
|
||||||
};
|
};
|
||||||
}, [isOutlined]);
|
}, [isOutlined]);
|
||||||
|
|
||||||
const desc = subs.get_root(variable);
|
const varType = subs.get(rawVariable);
|
||||||
const styles = contentStyles(desc);
|
if (!varType) throw new Error("VariableNode: no entry for variable");
|
||||||
const basis: BasisProps = {
|
|
||||||
subs,
|
|
||||||
origin: variable,
|
|
||||||
addSubVariableLink,
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = Object.entries(
|
let renderContent: JSX.Element;
|
||||||
VariableNodeContent(variable, desc, basis)
|
let bgStyles: string;
|
||||||
).filter((el): el is [string, JSX.Element] => !!el[1]);
|
const isContent = varType.type === "descriptor";
|
||||||
|
switch (varType.type) {
|
||||||
|
case "link": {
|
||||||
|
bgStyles = LinkStyles.bg;
|
||||||
|
|
||||||
let expandedContent = <></>;
|
renderContent = (
|
||||||
if (content.length > 0) {
|
<VariableLink
|
||||||
expandedContent = (
|
subs={subs}
|
||||||
<ul className="text-sm text-left mt-2 space-y-1">
|
variable={rawVariable}
|
||||||
{content.map(([key, value], i) => (
|
onClick={() =>
|
||||||
<li key={i} className="space-x-2">
|
addSubVariableLink({
|
||||||
{key}: {value}
|
from: rawVariable,
|
||||||
</li>
|
variable: subs.get_root_key(rawVariable),
|
||||||
))}
|
})
|
||||||
</ul>
|
}
|
||||||
);
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "descriptor": {
|
||||||
|
const variable = rawVariable;
|
||||||
|
const desc: TypeDescriptor = varType;
|
||||||
|
|
||||||
|
const styles = contentStyles(desc);
|
||||||
|
bgStyles = styles.bg;
|
||||||
|
const basis: BasisProps = {
|
||||||
|
subs,
|
||||||
|
origin: variable,
|
||||||
|
addSubVariableLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = Object.entries(
|
||||||
|
VariableNodeContent(variable, desc, basis)
|
||||||
|
).filter((el): el is [string, JSX.Element] => !!el[1]);
|
||||||
|
|
||||||
|
let expandedContent = <></>;
|
||||||
|
if (content.length > 0) {
|
||||||
|
expandedContent = (
|
||||||
|
<ul className="text-sm text-left mt-2 space-y-1">
|
||||||
|
{content.map(([key, value], i) => (
|
||||||
|
<li key={i} className="space-x-2">
|
||||||
|
{key}: {value}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContent = (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<VariableElPretty variable={variable} subs={subs} />
|
||||||
|
</div>
|
||||||
|
{expandedContent}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
assertExhaustive(varType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
styles.bg,
|
bgStyles,
|
||||||
"bg-opacity-50 py-2 px-4 rounded-lg border transition ease-in-out duration-700",
|
"bg-opacity-50 rounded-lg transition ease-in-out duration-700",
|
||||||
|
isContent ? "py-2 px-4 border" : "p-0",
|
||||||
isOutlined && "ring-2 ring-blue-500",
|
isOutlined && "ring-2 ring-blue-500",
|
||||||
"text-center font-mono"
|
"text-center font-mono"
|
||||||
)}
|
)}
|
||||||
|
@ -97,15 +150,14 @@ export default function VariableNode({
|
||||||
type="target"
|
type="target"
|
||||||
position={targetPosition ?? Position.Top}
|
position={targetPosition ?? Position.Top}
|
||||||
isConnectable={false}
|
isConnectable={false}
|
||||||
|
style={{ background: "transparent", border: "none" }}
|
||||||
/>
|
/>
|
||||||
<div>
|
{renderContent}
|
||||||
<VariableElPretty variable={variable} subs={subs} />
|
|
||||||
</div>
|
|
||||||
{expandedContent}
|
|
||||||
<Handle
|
<Handle
|
||||||
type="source"
|
type="source"
|
||||||
position={sourcePosition ?? Position.Bottom}
|
position={sourcePosition ?? Position.Bottom}
|
||||||
isConnectable={false}
|
isConnectable={false}
|
||||||
|
style={{ background: "transparent", border: "none" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -238,7 +290,7 @@ function SubVariable({
|
||||||
<VariableElPretty
|
<VariableElPretty
|
||||||
variable={variable}
|
variable={variable}
|
||||||
subs={subs}
|
subs={subs}
|
||||||
onClick={() => addSubVariableLink(origin, variable)}
|
onClick={() => addSubVariableLink({ from: origin, variable })}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ import ReactFlow, {
|
||||||
useStore,
|
useStore,
|
||||||
ReactFlowState,
|
ReactFlowState,
|
||||||
Position,
|
Position,
|
||||||
|
MarkerType,
|
||||||
|
EdgeMarkerType,
|
||||||
} from "reactflow";
|
} from "reactflow";
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Variable } from "../../schema";
|
import { Variable } from "../../schema";
|
||||||
|
@ -154,7 +156,9 @@ async function computeLayoutedElements({
|
||||||
//height: 50,
|
//height: 50,
|
||||||
})),
|
})),
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
edges: edges,
|
edges: edges.map((edge) => ({
|
||||||
|
...edge,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const layoutedGraph = await elk.layout(graph);
|
const layoutedGraph = await elk.layout(graph);
|
||||||
|
@ -197,20 +201,22 @@ function newVariable(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNodeChange(node: Node, existingNodes: Node[]): NodeChange | null {
|
function canAddVariable(variableName: string, existingNodes: Node[]): boolean {
|
||||||
if (existingNodes.some((n) => n.id === node.id)) {
|
return !existingNodes.some((n) => n.id === variableName);
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
function canAddEdge(edgeName: string, existingEdges: Edge[]): boolean {
|
||||||
|
return !existingEdges.some((e) => e.id === edgeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNode(node: Node): NodeChange {
|
||||||
return {
|
return {
|
||||||
type: "add",
|
type: "add",
|
||||||
item: node,
|
item: node,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEdgeChange(edge: Edge, existingEdges: Edge[]): EdgeChange | null {
|
function addEdge(edge: Edge): EdgeChange {
|
||||||
if (existingEdges.some((e) => e.id === edge.id)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
type: "add",
|
type: "add",
|
||||||
item: edge,
|
item: edge,
|
||||||
|
@ -322,13 +328,26 @@ function Graph({
|
||||||
const initialEdges: Edge[] = [];
|
const initialEdges: Edge[] = [];
|
||||||
|
|
||||||
const ee = useRef(new TypedEmitter<VariableMessageEvents>());
|
const ee = useRef(new TypedEmitter<VariableMessageEvents>());
|
||||||
|
// Allow an unbounded number of listeners since we attach a listener for each
|
||||||
|
// variable.
|
||||||
|
ee.current.setMaxListeners(Infinity);
|
||||||
|
|
||||||
|
const [variablesNeedingFocus, setVariablesNeedingFocus] = useState<
|
||||||
|
Set<Variable>
|
||||||
|
>(new Set());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (variablesNeedingFocus.size === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const variable of variablesNeedingFocus) {
|
||||||
|
ee.current.emit("focus", variable);
|
||||||
|
}
|
||||||
|
setVariablesNeedingFocus(new Set());
|
||||||
|
}, [variablesNeedingFocus]);
|
||||||
|
|
||||||
const [layoutConfig, setLayoutConfig] =
|
const [layoutConfig, setLayoutConfig] =
|
||||||
useState<LayoutConfiguration>(LAYOUT_CONFIG_DOWN);
|
useState<LayoutConfiguration>(LAYOUT_CONFIG_DOWN);
|
||||||
const [elements, setElements] = useState<LayoutedElements>({
|
|
||||||
nodes: initialNodes,
|
|
||||||
edges: initialEdges,
|
|
||||||
});
|
|
||||||
|
|
||||||
useAutoLayout(layoutConfig);
|
useAutoLayout(layoutConfig);
|
||||||
useKeydown({
|
useKeydown({
|
||||||
|
@ -337,6 +356,11 @@ function Graph({
|
||||||
onKeydown,
|
onKeydown,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [elements, setElements] = useState<LayoutedElements>({
|
||||||
|
nodes: initialNodes,
|
||||||
|
edges: initialEdges,
|
||||||
|
});
|
||||||
|
|
||||||
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
const onNodesChange = useCallback((changes: NodeChange[]) => {
|
||||||
setElements(({ nodes, edges }) => {
|
setElements(({ nodes, edges }) => {
|
||||||
return {
|
return {
|
||||||
|
@ -355,81 +379,93 @@ function Graph({
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addSubVariableLink = useCallback(
|
interface AddNewVariableParams {
|
||||||
(fromN: Variable, subLinkN: Variable) => {
|
from?: Variable;
|
||||||
fromN = subs.get_root_key(fromN);
|
variable: Variable;
|
||||||
subLinkN = subs.get_root_key(subLinkN);
|
}
|
||||||
const from = fromN.toString();
|
|
||||||
const to = subLinkN.toString();
|
const addNewVariable = useCallback(
|
||||||
|
({ from, variable }: AddNewVariableParams) => {
|
||||||
|
const variablesToFocus = new Set<Variable>();
|
||||||
|
|
||||||
setElements(({ nodes, edges }) => {
|
setElements(({ nodes, edges }) => {
|
||||||
const optNewNode = addNodeChange(
|
let fromVariable: Variable | undefined = from;
|
||||||
newVariable(
|
let toVariable: Variable | undefined = variable;
|
||||||
to,
|
|
||||||
{
|
|
||||||
subs,
|
|
||||||
variable: subLinkN,
|
|
||||||
addSubVariableLink,
|
|
||||||
isOutlined: true,
|
|
||||||
ee: ee.current,
|
|
||||||
},
|
|
||||||
layoutConfig.isHorizontal
|
|
||||||
),
|
|
||||||
nodes
|
|
||||||
);
|
|
||||||
const newNodes = optNewNode
|
|
||||||
? applyNodeChanges([optNewNode], nodes)
|
|
||||||
: nodes;
|
|
||||||
|
|
||||||
const optNewEdge = addEdgeChange(
|
const nodeChanges: NodeChange[] = [];
|
||||||
{ id: `${from}->${to}`, source: from, target: to },
|
const edgeChanges: EdgeChange[] = [];
|
||||||
edges
|
|
||||||
);
|
while (toVariable !== undefined) {
|
||||||
const newEdges = optNewEdge
|
const toVariableName = toVariable.toString();
|
||||||
? applyEdgeChanges([optNewEdge], edges)
|
if (canAddVariable(toVariableName, nodes)) {
|
||||||
: edges;
|
const newVariableNode = newVariable(
|
||||||
|
toVariable.toString(),
|
||||||
|
{
|
||||||
|
subs,
|
||||||
|
rawVariable: toVariable,
|
||||||
|
addSubVariableLink: addNewVariable,
|
||||||
|
isOutlined: true,
|
||||||
|
ee: ee.current,
|
||||||
|
},
|
||||||
|
layoutConfig.isHorizontal
|
||||||
|
);
|
||||||
|
|
||||||
|
nodeChanges.push(addNode(newVariableNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromVariable !== undefined) {
|
||||||
|
const edgeName = `${fromVariable}->${toVariable}`;
|
||||||
|
if (canAddEdge(edgeName, edges)) {
|
||||||
|
let markerEnd: EdgeMarkerType | undefined;
|
||||||
|
if (subs.get_root_key(fromVariable) === toVariable) {
|
||||||
|
markerEnd = {
|
||||||
|
type: MarkerType.ArrowClosed,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const newEdge = addEdge({
|
||||||
|
id: `${fromVariable}->${toVariable}`,
|
||||||
|
source: fromVariable.toString(),
|
||||||
|
target: toVariableName,
|
||||||
|
markerEnd,
|
||||||
|
});
|
||||||
|
|
||||||
|
edgeChanges.push(newEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variablesToFocus.add(toVariable);
|
||||||
|
|
||||||
|
fromVariable = toVariable;
|
||||||
|
const rootToVariable = subs.get_root_key(toVariable);
|
||||||
|
if (toVariable !== rootToVariable) {
|
||||||
|
toVariable = rootToVariable;
|
||||||
|
} else {
|
||||||
|
toVariable = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newNodes = applyNodeChanges(nodeChanges, nodes);
|
||||||
|
const newEdges = applyEdgeChanges(edgeChanges, edges);
|
||||||
|
|
||||||
return { nodes: newNodes, edges: newEdges };
|
return { nodes: newNodes, edges: newEdges };
|
||||||
});
|
});
|
||||||
|
|
||||||
ee.current.emit("focus", subLinkN);
|
setVariablesNeedingFocus(variablesToFocus);
|
||||||
},
|
},
|
||||||
[layoutConfig, subs]
|
[layoutConfig.isHorizontal, subs]
|
||||||
);
|
);
|
||||||
|
|
||||||
const addNode = useCallback(
|
const addNewVariableNode = useCallback(
|
||||||
(variableN: Variable) => {
|
(variable: Variable) => {
|
||||||
variableN = subs.get_root_key(variableN);
|
addNewVariable({ variable });
|
||||||
const variable = variableN.toString();
|
|
||||||
|
|
||||||
setElements(({ nodes, edges }) => {
|
|
||||||
const optNewNode = addNodeChange(
|
|
||||||
newVariable(
|
|
||||||
variable,
|
|
||||||
{
|
|
||||||
subs,
|
|
||||||
variable: variableN,
|
|
||||||
addSubVariableLink,
|
|
||||||
isOutlined: true,
|
|
||||||
ee: ee.current,
|
|
||||||
},
|
|
||||||
layoutConfig.isHorizontal
|
|
||||||
),
|
|
||||||
nodes
|
|
||||||
);
|
|
||||||
const newNodes = optNewNode
|
|
||||||
? applyNodeChanges([optNewNode], nodes)
|
|
||||||
: nodes;
|
|
||||||
|
|
||||||
return { nodes: newNodes, edges: edges };
|
|
||||||
});
|
|
||||||
|
|
||||||
ee.current.emit("focus", variableN);
|
|
||||||
},
|
},
|
||||||
[subs, addSubVariableLink, layoutConfig]
|
[addNewVariable]
|
||||||
);
|
);
|
||||||
|
|
||||||
onVariable(addNode);
|
onVariable(addNewVariableNode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue