diff --git a/src/pages/VisProgPage/VisProg.module.css b/src/pages/VisProgPage/VisProg.module.css
index 11db5cc..00268eb 100644
--- a/src/pages/VisProgPage/VisProg.module.css
+++ b/src/pages/VisProgPage/VisProg.module.css
@@ -33,6 +33,12 @@
/* Node Styles */
+:global(.react-flow__node.selected) {
+ outline: 1px dashed blue !important;
+ border-radius: 5pt;
+ outline-offset: 4px;
+}
+
.default-node {
padding: 10px 15px;
background-color: canvas;
@@ -41,6 +47,8 @@
filter: drop-shadow(0 0 0.75rem black);
}
+
+
.node-norm {
outline: rgb(0, 149, 25) solid 2pt;
filter: drop-shadow(0 0 0.25rem forestgreen);
@@ -76,10 +84,13 @@
filter: drop-shadow(0 0 0.25rem plum);
}
+
+
.draggable-node {
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: black solid 2pt;
filter: drop-shadow(0 0 0.75rem black);
}
@@ -88,6 +99,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: forestgreen solid 2pt;
filter: drop-shadow(0 0 0.25rem forestgreen);
}
@@ -96,6 +108,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: yellow solid 2pt;
filter: drop-shadow(0 0 0.25rem yellow);
}
@@ -104,6 +117,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: teal solid 2pt;
filter: drop-shadow(0 0 0.25rem teal);
}
@@ -112,6 +126,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: dodgerblue solid 2pt;
filter: drop-shadow(0 0 0.25rem dodgerblue);
}
@@ -120,6 +135,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: orange solid 2pt;
filter: drop-shadow(0 0 0.25rem orange);
}
@@ -128,6 +144,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: red solid 2pt;
filter: drop-shadow(0 0 0.25rem red);
}
@@ -136,6 +153,7 @@
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
+ cursor: move;
outline: plum solid 2pt;
filter: drop-shadow(0 0 0.25rem plum);
}
@@ -152,4 +170,53 @@
.bottomRightHandle {
left: 60% !important;
+}
+
+.node-toolbar-tooltip {
+ background-color: darkgray;
+ color: white;
+ padding: 8px 12px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-family: sans-serif;
+ font-weight: bold;
+ cursor: help;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
+ white-space: nowrap;
+}
+
+.custom-tooltip {
+ pointer-events: none;
+ background-color: Canvas;
+ color: CanvasText;
+ padding: 8px 12px;
+ border-radius: 0 6px 6px 0;
+ outline: CanvasText solid 2px;
+ font-size: 14px;
+ filter: drop-shadow(0 0 0.25rem CanvasText);
+ white-space: nowrap;
+ position: relative;
+ animation: fadeIn 0.2s ease-out;
+}
+
+.custom-tooltip-header {
+ pointer-events: none;
+ background-color: CanvasText;
+ color: Canvas;
+ padding: 8px 12px;
+ border-radius: 6px 0 0 6px;
+ outline: CanvasText solid 2px;
+ font-size: 14px;
+ font-weight: bold;
+ font-variant-caps: small-caps;
+ filter: drop-shadow(0 0 0.25rem CanvasText);
+ white-space: nowrap;
+ position: relative;
+ animation: fadeIn 0.2s ease-out;
+}
+
+
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(5px); }
+ to { opacity: 1; transform: translateY(0); }
}
\ No newline at end of file
diff --git a/src/pages/VisProgPage/VisProg.tsx b/src/pages/VisProgPage/VisProg.tsx
index 486cc7f..a083bbf 100644
--- a/src/pages/VisProgPage/VisProg.tsx
+++ b/src/pages/VisProgPage/VisProg.tsx
@@ -7,7 +7,7 @@ import {
MarkerType,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
-import {useEffect} from "react";
+import {type CSSProperties, useEffect, useState} from "react";
import {useShallow} from 'zustand/react/shallow';
import orderPhaseNodeArray from "../../utils/orderPhaseNodes.ts";
import useProgramStore from "../../utils/programStore.ts";
@@ -79,7 +79,7 @@ const VisProgUI = () => {
endBatchAction,
scrollable
} = useFlowStore(useShallow(selector)); // instructs the editor to use the corresponding functions from the FlowStore
-
+ const [zoom, setZoom] = useState(1);
// adds ctrl+z and ctrl+y support to respectively undo and redo actions
useEffect(() => {
const handler = (e: KeyboardEvent) => {
@@ -91,7 +91,7 @@ const VisProgUI = () => {
});
return (
-
+
{
onNodeDragStart={beginBatchAction}
onNodeDragStop={endBatchAction}
preventScrolling={scrollable}
+ onMove={(_, viewport) => setZoom(viewport.zoom)}
snapToGrid
fitView
proOptions={{hideAttribution: true}}
diff --git a/src/pages/VisProgPage/visualProgrammingUI/NodeRegistry.ts b/src/pages/VisProgPage/visualProgrammingUI/NodeRegistry.ts
index 023440c..54f0241 100644
--- a/src/pages/VisProgPage/visualProgrammingUI/NodeRegistry.ts
+++ b/src/pages/VisProgPage/visualProgrammingUI/NodeRegistry.ts
@@ -3,7 +3,8 @@ import EndNode, {
EndConnectionSource,
EndDisconnectionTarget,
EndDisconnectionSource,
- EndReduce
+ EndReduce,
+ EndTooltip
} from "./nodes/EndNode";
import { EndNodeDefaults } from "./nodes/EndNode.default";
import StartNode, {
@@ -11,7 +12,8 @@ import StartNode, {
StartConnectionSource,
StartDisconnectionTarget,
StartDisconnectionSource,
- StartReduce
+ StartReduce,
+ StartTooltip
} from "./nodes/StartNode";
import { StartNodeDefaults } from "./nodes/StartNode.default";
import PhaseNode, {
@@ -19,7 +21,8 @@ import PhaseNode, {
PhaseConnectionSource,
PhaseDisconnectionTarget,
PhaseDisconnectionSource,
- PhaseReduce
+ PhaseReduce,
+ PhaseTooltip
} from "./nodes/PhaseNode";
import { PhaseNodeDefaults } from "./nodes/PhaseNode.default";
import NormNode, {
@@ -27,7 +30,8 @@ import NormNode, {
NormConnectionSource,
NormDisconnectionTarget,
NormDisconnectionSource,
- NormReduce
+ NormReduce,
+ NormTooltip
} from "./nodes/NormNode";
import { NormNodeDefaults } from "./nodes/NormNode.default";
import GoalNode, {
@@ -35,7 +39,8 @@ import GoalNode, {
GoalConnectionSource,
GoalDisconnectionTarget,
GoalDisconnectionSource,
- GoalReduce
+ GoalReduce,
+ GoalTooltip
} from "./nodes/GoalNode";
import { GoalNodeDefaults } from "./nodes/GoalNode.default";
import TriggerNode, {
@@ -43,10 +48,18 @@ import TriggerNode, {
TriggerConnectionSource,
TriggerDisconnectionTarget,
TriggerDisconnectionSource,
- TriggerReduce
+ TriggerReduce,
+ TriggerTooltip
} from "./nodes/TriggerNode";
import { TriggerNodeDefaults } from "./nodes/TriggerNode.default";
-import BasicBeliefNode, { BasicBeliefConnectionSource, BasicBeliefConnectionTarget, BasicBeliefDisconnectionSource, BasicBeliefDisconnectionTarget, BasicBeliefReduce } from "./nodes/BasicBeliefNode";
+import BasicBeliefNode, {
+ BasicBeliefConnectionSource,
+ BasicBeliefConnectionTarget,
+ BasicBeliefDisconnectionSource,
+ BasicBeliefDisconnectionTarget,
+ BasicBeliefReduce,
+ BasicBeliefTooltip
+} from "./nodes/BasicBeliefNode";
import { BasicBeliefNodeDefaults } from "./nodes/BasicBeliefNode.default";
/**
@@ -173,4 +186,17 @@ export const NodesInPhase = {
end: () => false,
phase: () => false,
basic_belief: () => false,
+}
+
+/**
+ * Collects the tooltips for all nodeTypes so they can be accessed by the tooltip component
+ */
+export const NodeTooltips = {
+ start: StartTooltip,
+ end: EndTooltip,
+ phase: PhaseTooltip,
+ norm: NormTooltip,
+ goal: GoalTooltip,
+ trigger: TriggerTooltip,
+ basic_belief: BasicBeliefTooltip,
}
\ No newline at end of file
diff --git a/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx b/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx
index a70e54d..defa934 100644
--- a/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx
+++ b/src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx
@@ -45,9 +45,9 @@ function createNode(id: string, type: string, position: XYPosition, data: Record
//* Initial nodes, created by using createNode. */
// Start and End don't need to apply the UUID, since they are technically never compiled into a program.
- const startNode = createNode('start', 'start', {x: 100, y: 100}, {label: "Start"}, false)
- const endNode = createNode('end', 'end', {x: 500, y: 100}, {label: "End"}, false)
- const initialPhaseNode = createNode(crypto.randomUUID(), 'phase', {x:200, y:100}, {label: "Phase 1", children : [], isFirstPhase: false, nextPhaseId: null})
+ const startNode = createNode('start', 'start', {x: 110, y: 100}, {label: "Start"}, false)
+ const endNode = createNode('end', 'end', {x: 590, y: 100}, {label: "End"}, false)
+ const initialPhaseNode = createNode(crypto.randomUUID(), 'phase', {x:235, y:100}, {label: "Phase 1", children : [], isFirstPhase: false, nextPhaseId: null})
const initialNodes : Node[] = [startNode, endNode, initialPhaseNode,];
diff --git a/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx b/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx
index 01e222e..338039a 100644
--- a/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx
+++ b/src/pages/VisProgPage/visualProgrammingUI/components/DragDropSidebar.tsx
@@ -3,7 +3,8 @@ import { useReactFlow, type XYPosition } from '@xyflow/react';
import { type ReactNode, useCallback, useRef, useState } from 'react';
import useFlowStore from '../VisProgStores';
import styles from '../../VisProg.module.css';
-import { NodeDefaults, type NodeTypes } from '../NodeRegistry'
+import { NodeDefaults, type NodeTypes} from '../NodeRegistry'
+import {Tooltip} from "./NodeComponents.tsx";
/**
* Props for a draggable node within the drag-and-drop toolbar.
@@ -47,14 +48,17 @@ function DraggableNode({ className, children, nodeType, onDrop }: DraggableNodeP
});
return (
-
- {children}
-
- );
+
+
+ )
}
/**
@@ -133,7 +137,7 @@ export function DndToolbar() {
}));
return (
-
+