diff --git a/src/assets/add-state.svg b/src/assets/add-state.svg
new file mode 100644
index 0000000..b8129b1
--- /dev/null
+++ b/src/assets/add-state.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/add-transition.svg b/src/assets/add-transition.svg
new file mode 100644
index 0000000..13ecd04
--- /dev/null
+++ b/src/assets/add-transition.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/assets/delete.svg b/src/assets/delete.svg
new file mode 100644
index 0000000..7d8e030
--- /dev/null
+++ b/src/assets/delete.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/dropdown-down-arrow-icon.svg b/src/assets/dropdown-down-arrow-icon.svg
new file mode 100644
index 0000000..f825c62
--- /dev/null
+++ b/src/assets/dropdown-down-arrow-icon.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/src/assets/dropdown-up-arrow-icon.svg b/src/assets/dropdown-up-arrow-icon.svg
new file mode 100644
index 0000000..bce0fdb
--- /dev/null
+++ b/src/assets/dropdown-up-arrow-icon.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/src/assets/fit-view.svg b/src/assets/fit-view.svg
new file mode 100644
index 0000000..25a1c8a
--- /dev/null
+++ b/src/assets/fit-view.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/home.svg b/src/assets/home.svg
new file mode 100644
index 0000000..35b5107
--- /dev/null
+++ b/src/assets/home.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/src/assets/pause.svg b/src/assets/pause.svg
new file mode 100644
index 0000000..8f33890
--- /dev/null
+++ b/src/assets/pause.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/assets/play.svg b/src/assets/play.svg
new file mode 100644
index 0000000..9db7303
--- /dev/null
+++ b/src/assets/play.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/assets/plus.svg b/src/assets/plus.svg
new file mode 100644
index 0000000..92067cc
--- /dev/null
+++ b/src/assets/plus.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/recenter.svg b/src/assets/recenter.svg
new file mode 100644
index 0000000..dc5106f
--- /dev/null
+++ b/src/assets/recenter.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/assets/reset.svg b/src/assets/reset.svg
new file mode 100644
index 0000000..e570d0e
--- /dev/null
+++ b/src/assets/reset.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/src/assets/select.svg b/src/assets/select.svg
new file mode 100644
index 0000000..e53b9ba
--- /dev/null
+++ b/src/assets/select.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/src/assets/step.svg b/src/assets/step.svg
new file mode 100644
index 0000000..07a0c13
--- /dev/null
+++ b/src/assets/step.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/assets/text.svg b/src/assets/text.svg
new file mode 100644
index 0000000..2f5e358
--- /dev/null
+++ b/src/assets/text.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/components/app.scss b/src/components/app.scss
index 1310b28..e241817 100644
--- a/src/components/app.scss
+++ b/src/components/app.scss
@@ -87,6 +87,25 @@
flex: 1;
flex-grow: 0;
gap: 10px;
+ background-color: #dddddd7f;
+ border-radius: 3px;
+ padding: 5px;
+
+ button {
+ width: fit-content;
+ padding: 3px 5px;
+ border-radius: 3px;
+ border: solid 1px #177991;
+ background-color: #fff;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ gap: 3px;
+
+ &:hover {
+ background-color: rgba(114, 191, 202, 0.12);
+ }
+ }
.generate {
display: flex;
@@ -102,15 +121,22 @@
label {
display: block;
white-space: nowrap;
+ font-weight: bold;
}
select {
padding: 2px;
- width: 125px;
+ width: 100%;
+ border-radius: 1.5px;
+ border: solid 1px #177991;
+ background-color: #fff;
}
input {
padding: 2px;
width: 55px;
+ border-radius: 1.5px;
+ border: solid 1px #177991;
+ background-color: #fff;
}
}
@@ -123,10 +149,6 @@
.buttons {
display: flex;
gap: 5px;
-
- button {
- width: fit-content;
- }
}
}
@@ -134,14 +156,15 @@
display: flex;
flex-direction: column;
flex: 1;
- gap: 10px;
+ gap: 5px;
.output {
display: flex;
flex-direction: column;
// gap: 10px;
flex: 1;
- border: 1px solid #000;
+ border: 1px solid #979797;
+ background-color: white;
position: relative;
.inner-output {
@@ -163,6 +186,19 @@
&.expanded {
flex-direction: column;
+
+ .firstItem {
+ display: flex;
+ flex-direction: row;
+
+ .collapse {
+ flex-grow: 1;
+ text-align: right;
+ width: 10px;
+ height: 10px;
+ margin-top: -2px;
+ }
+ }
}
&.collapsed {
flex-direction: row;
@@ -170,6 +206,9 @@
.expand {
flex-grow: 1;
text-align: right;
+ width: 10px;
+ height: 10px;
+ margin-top: -2px;
}
}
}
@@ -181,7 +220,9 @@
}
.buttons {
-
+ button {
+ padding: 8px;
+ }
}
}
}
diff --git a/src/components/app.tsx b/src/components/app.tsx
index 55462e5..ceab954 100644
--- a/src/components/app.tsx
+++ b/src/components/app.tsx
@@ -3,10 +3,16 @@ import { clsx } from "clsx";
import { useCODAP } from "../hooks/use-codap";
import { useGraph } from "../hooks/use-graph";
-import { Graph } from "./graph";
import { useGenerator } from "../hooks/use-generator";
import { Edge, Node } from "../type";
import { Drawing } from "./drawing";
+import { Dataset } from "./dataset";
+
+import StepIcon from "../assets/step.svg";
+import PlayIcon from "../assets/play.svg";
+import PauseIcon from "../assets/pause.svg";
+import DropdownUpArrowIcon from "../assets/dropdown-up-arrow-icon.svg";
+import DropdownDownArrowIcon from "../assets/dropdown-down-arrow-icon.svg";
import "./app.scss";
@@ -34,7 +40,10 @@ const SequenceOutputHeader = ({ group }: { group: SequenceGroup }) => {
if (expanded) {
return (
-
Starting State: {startingState}
+
+
Starting State: {startingState}
+
+
Max Length: {lengthLimit}
Delimiter: {delimiter}
@@ -48,7 +57,7 @@ const SequenceOutputHeader = ({ group }: { group: SequenceGroup }) => {
{lengthLimit}
/
{delimiter}
- …
+
);
};
@@ -266,6 +275,7 @@ export const App = () => {
const uiForGenerate = () => {
const disabled = graphEmpty();
const playLabel = generationMode === "playing" ? "Pause" : (generationMode === "paused" ? "Resume" : "Play");
+ const PlayOrPauseIcon = generationMode === "playing" ? PauseIcon : PlayIcon;
const onPlayClick = generationMode === "playing"
? handlePause
: (generationMode === "paused" ? handleResume : handlePlay);
@@ -303,18 +313,26 @@ export const App = () => {
/>
+
+
+
+
TBD
+
+
@@ -427,8 +445,7 @@ export const App = () => {
setSelectedNodeId={setSelectedNodeId}
/>
:
- {
highlightAllNextNodes={highlightAllNextNodes}
selectedNodeId={selectedNodeId}
animating={animating}
- allowDragging={true && !animating}
- autoArrange={true}
setSelectedNodeId={setSelectedNodeId}
/>
}
diff --git a/src/components/dataset.scss b/src/components/dataset.scss
new file mode 100644
index 0000000..634d3a8
--- /dev/null
+++ b/src/components/dataset.scss
@@ -0,0 +1,5 @@
+.dataset {
+ display: flex;
+ flex-direction: row;
+ flex: 1;
+}
\ No newline at end of file
diff --git a/src/components/dataset.tsx b/src/components/dataset.tsx
new file mode 100644
index 0000000..3b8b11e
--- /dev/null
+++ b/src/components/dataset.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import { Tool, Toolbar } from "./toolbar";
+import { Graph } from "./graph";
+import { Node, Edge, GraphData } from "../type";
+
+import "./dataset.scss";
+
+interface Props {
+ highlightNode?: Node,
+ highlightLoopOnNode?: Node,
+ highlightEdge?: Edge,
+ highlightAllNextNodes: boolean;
+ graph: GraphData;
+ selectedNodeId?: string;
+ animating: boolean;
+ setSelectedNodeId: (id?: string, skipToggle?: boolean) => void;
+}
+
+const tools: Tool[] = ["select","fitView","recenter","reset","home"];
+
+export const Dataset = (props: Props) => {
+ const {highlightNode, highlightLoopOnNode, highlightEdge, highlightAllNextNodes,
+ graph, setSelectedNodeId, selectedNodeId, animating} = props;
+
+ const handleToolSelected = (tool: Tool) => {
+ // TBD
+ };
+
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/drawing.scss b/src/components/drawing.scss
index c62178f..3ce7fbf 100644
--- a/src/components/drawing.scss
+++ b/src/components/drawing.scss
@@ -3,29 +3,6 @@
flex-direction: row;
flex: 1;
- .sidebar {
- display: flex;
- flex-direction: column;
- gap: 5px;
- padding: 5px;
- background-color: #ddd;
-
- button {
- width: 30px;
- height: 30px;
- padding: 2px;
-
- &.selected {
- background-color: #999;
-
- svg path {
- stroke: white;
- fill: white;
- }
- }
- }
- }
-
.dragIcon {
position: absolute;
width: 30px;
diff --git a/src/components/drawing.tsx b/src/components/drawing.tsx
index b47df40..956408d 100644
--- a/src/components/drawing.tsx
+++ b/src/components/drawing.tsx
@@ -1,5 +1,4 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import { clsx } from "clsx";
+import React, { useCallback, useEffect, useState } from "react";
import { nanoid } from "nanoid";
import { DrawingMode, Graph, Point, RubberBand } from "./graph";
@@ -7,14 +6,12 @@ import { Edge, GraphData, Node } from "../type";
import { DragIcon } from "./drawing/drag-icon";
import { NodeModal } from "./drawing/node-modal";
-
-import SelectIcon from "../assets/select-icon.svg";
-import AddNodeIcon from "../assets/add-node-icon.svg";
-import AddEdgeIcon from "../assets/add-edge-icon.svg";
-import DeleteIcon from "../assets/delete-icon.svg";
+import { Tool, Toolbar } from "./toolbar";
import "./drawing.scss";
+const tools: Tool[] = ["select","addNode","addEdge","addText","delete","fitView","recenter","reset","home"];
+const drawingTools: Tool[] = ["select", "addNode", "addEdge", "delete"];
interface Props {
highlightNode?: Node,
@@ -43,12 +40,11 @@ export const Drawing = (props: Props) => {
}
}, [drawingMode, _setSelectedNodeId]);
- const sidebarRef = useRef(null);
-
const translateToGraphPoint = (e: MouseEvent|React.MouseEvent): Point => {
+ // the offsets were determined visually to put the state centered on the mouse
return {
- x: e.clientX - (sidebarRef?.current?.clientWidth || 0),
- y: e.clientY - (sidebarRef?.current?.clientTop || 0)
+ x: e.clientX - 50,
+ y: e.clientY - 12,
};
};
@@ -82,24 +78,25 @@ export const Drawing = (props: Props) => {
return () => window.removeEventListener("keydown", listenForEscape);
}, [drawingMode, setDrawingMode, clearSelections]);
+ const handleToolSelected = (tool: Tool) => {
+ if (drawingTools.includes(tool)) {
+ setDrawingMode(tool as DrawingMode);
+ clearSelections();
+ }
+ };
+
+ /*
+
+ keep for now until ok is given to delete
+
const handleSetSelectMode = useCallback(() => {
setDrawingMode("select");
clearSelections();
}, [setDrawingMode, clearSelections]);
- const handleSetAddNodeMode = useCallback(() => {
- setDrawingMode("addNode");
- clearSelections();
- }, [setDrawingMode, clearSelections]);
- const handleSetAddEdgeMode = useCallback(() => {
- setDrawingMode("addEdge");
- clearSelections();
- }, [setDrawingMode, clearSelections]);
- const handleSetDeleteMode = useCallback(() => {
- setDrawingMode("delete");
- clearSelections();
- }, [setDrawingMode, clearSelections]);
+ */
const addNode = useCallback(({x, y}: {x: number, y: number}) => {
+ console.log("ADD NODE");
setGraph(prev => {
const id = nanoid();
const label = `State ${prev.nodes.length + 1}`;
@@ -127,26 +124,18 @@ export const Drawing = (props: Props) => {
}, [setGraph]);
const handleClicked = useCallback((e: React.MouseEvent) => {
+ console.log("HANDLE CLICKED");
if (drawingMode === "addNode") {
addNode(translateToGraphPoint(e));
- handleSetSelectMode();
+ // handleSetSelectMode();
} else if (drawingMode === "addEdge") {
const onSVGBackground = ((e.target as HTMLElement)?.tagName || "").toLowerCase() === "svg";
if (onSVGBackground) {
clearSelections();
- handleSetSelectMode();
+ // handleSetSelectMode();
}
}
- }, [drawingMode, addNode, handleSetSelectMode, clearSelections]);
-
- // allow nodes to be "dragged" from the toolbar to the canvas
- const handleMouseUp = useCallback((e: React.MouseEvent) => {
- if (drawingMode === "addNode") {
- handleClicked(e);
- e.preventDefault();
- e.stopPropagation();
- }
- }, [drawingMode, handleClicked]);
+ }, [drawingMode, addNode/*, handleSetSelectMode */, clearSelections]);
const handleNodeClicked = useCallback((id: string, onLoop?: boolean) => {
const node = getNode(id);
@@ -159,7 +148,9 @@ export const Drawing = (props: Props) => {
setFirstEdgeNode(node);
} else {
addEdge({from: firstEdgeNode.id, to: node.id});
- handleSetSelectMode();
+ setFirstEdgeNode(undefined);
+ setRubberBand(undefined);
+ // handleSetSelectMode();
}
}
@@ -188,9 +179,9 @@ export const Drawing = (props: Props) => {
edges
};
});
- handleSetSelectMode();
+ // handleSetSelectMode();
}
- }, [addEdge, drawingMode, getNode, firstEdgeNode, setFirstEdgeNode, setGraph, handleSetSelectMode]);
+ }, [addEdge, drawingMode, getNode, firstEdgeNode, setFirstEdgeNode, setGraph/*, handleSetSelectMode */]);
const handleNodeDoubleClicked = useCallback((id: string) => {
if (drawingMode === "select") {
@@ -198,9 +189,11 @@ export const Drawing = (props: Props) => {
}
if (drawingMode === "addEdge") {
addEdge({from: id, to: id});
- handleSetSelectMode();
+ setFirstEdgeNode(undefined);
+ setRubberBand(undefined);
+ // handleSetSelectMode();
}
- }, [drawingMode, addEdge, handleSetSelectMode, getNode]);
+ }, [drawingMode, addEdge/*, handleSetSelectMode */, getNode]);
const handleEdgeClicked = useCallback(({from, to}: {from: string, to: string}) => {
if (drawingMode === "delete") {
@@ -214,9 +207,9 @@ export const Drawing = (props: Props) => {
return prev;
}
});
- handleSetSelectMode();
+ // handleSetSelectMode();
}
- }, [setGraph, drawingMode, handleSetSelectMode]);
+ }, [setGraph, drawingMode/*, handleSetSelectMode */]);
const handleDragStop = useCallback((id: string, {x, y}: Point) => {
setGraph(prev => {
@@ -251,36 +244,7 @@ export const Drawing = (props: Props) => {
return (
-
-
-
-
-
-
+
{
selectedNodeId={selectedNodeId}
animating={animating}
onClick={handleClicked}
- onMouseUp={handleMouseUp}
onNodeClick={handleNodeClicked}
onNodeDoubleClick={handleNodeDoubleClicked}
onEdgeClick={handleEdgeClicked}
diff --git a/src/components/graph.tsx b/src/components/graph.tsx
index 8b01366..4ef256e 100644
--- a/src/components/graph.tsx
+++ b/src/components/graph.tsx
@@ -54,7 +54,6 @@ type Props = {
selectedNodeId?: string;
animating: boolean;
onClick?: (e: React.MouseEvent) => void;
- onMouseUp?: (e: React.MouseEvent) => void;
onNodeClick?: (id: string, onLoop?: boolean) => void;
onNodeDoubleClick?: (id: string) => void;
onEdgeClick?: (options: {from: string, to: string}) => void;
@@ -236,7 +235,7 @@ const lineDashArray = (edge: D3Edge) => edge.value ? "" : "4";
export const Graph = (props: Props) => {
const {graph, highlightNode, highlightLoopOnNode, highlightEdge, highlightAllNextNodes,
allowDragging, autoArrange, mode, rubberBand, drawingMode,
- onClick, onMouseUp, onNodeClick, onNodeDoubleClick, onEdgeClick, onDragStop,
+ onClick, onNodeClick, onNodeDoubleClick, onEdgeClick, onDragStop,
selectedNodeId, setSelectedNodeId, animating} = props;
const svgRef = useRef(null);
const wrapperRef = useRef(null);
@@ -760,16 +759,10 @@ export const Graph = (props: Props) => {
}
}, [autoArrange, onClick]);
- const handleMouseUp = useCallback((e: React.MouseEvent) => {
- if (!autoArrange && onMouseUp) {
- onMouseUp(e);
- }
- }, [autoArrange, onMouseUp]);
-
const viewBox = mode === "drawing" ? `0 0 ${width} ${height}` : `${-width / 2} ${-height / 2} ${width} ${height}`;
return (
-
+