diff --git a/src/components/drawing.tsx b/src/components/drawing.tsx index 1cd9a36..e07e48e 100644 --- a/src/components/drawing.tsx +++ b/src/components/drawing.tsx @@ -1,10 +1,12 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { clsx } from "clsx"; import { nanoid } from "nanoid"; import { DrawingMode, Graph, Point, RubberBand } from "./graph"; import { Edge, GraphData, Node } from "../type"; -import { ProbabilitySelector, percentage, reduceToSum } from "./probability-selector"; + +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"; @@ -13,116 +15,6 @@ import DeleteIcon from "../assets/delete-icon.svg"; import "./drawing.scss"; -interface NodeModalProps { - node?: Node, - graph: GraphData; - onChange: (id: string, newNode: Node, newEdge: Edge[]) => void, - onCancel: () => void -} - -export const NodeModal = ({node, graph, onChange, onCancel}: NodeModalProps) => { - const [label, setLabel] = useState(node?.label || ""); - const [exactPercentages, _setExactPercentages] = useState([]); - - const setExactPercentages = useCallback((percentages: number[]) => { - // make sure percentages add up to exactly 100% - const allButLastSum = reduceToSum(percentages.slice(0, -1)); - percentages[percentages.length - 1] = 100 - allButLastSum; - _setExactPercentages(percentages); - }, [_setExactPercentages]); - - const fromEdges = useMemo(() => { - return node ? graph.edges.filter(e => e.from === node.id) : []; - }, [node, graph.edges]); - - const edgeLabels = useMemo(() => { - const labels = graph.nodes.reduce>((acc, cur) => { - acc[cur.id] = cur.label; - return acc; - }, {}); - return fromEdges.map(e => labels[e.to]); - }, [graph.nodes, fromEdges]); - - const edgeValues = useMemo(() => fromEdges.map(e => e.value), [fromEdges]); - const sum = useMemo(() => reduceToSum(edgeValues), [edgeValues]); - - useEffect(() => { - const percentages = edgeValues.map(edgeValue => percentage(edgeValue / sum)); - setExactPercentages(percentages); - }, [edgeValues, sum, setExactPercentages]); - - useEffect(() => { - setLabel(node?.label || ""); - }, [node, setLabel]); - - const handleSubmit = useCallback((e: React.FormEvent) => { - e.preventDefault(); - if (node) { - let valueIndex = 0; - const newEdges = graph.edges.map(edge => { - if (edge.from === node.id) { - const value = sum * exactPercentages[valueIndex++]; - return {...edge, value}; - } else { - return edge; - } - }); - onChange(node.id, {...node, label: label.trim()}, newEdges); - } - }, [label, node, exactPercentages, graph.edges, sum, onChange]); - - const handleChangeLabel = (e: React.ChangeEvent) => { - setLabel(e.target.value); - }; - - if (!node) { - return null; - } - - return ( - <> -
-
-
-
Update State
-
- - -
- - -
- -
-
- - ); -}; - -export const DragIcon = ({drawingMode}: {drawingMode: DrawingMode}) => { - const [style, setStyle] = useState({}); - - useEffect(() => { - const mouseHandler = (e: MouseEvent) => setStyle({left: e.clientX - 20, top: e.clientY - 20}); - window.addEventListener("mousemove", mouseHandler); - return () => window.removeEventListener("mousemove", mouseHandler); - }, []); - - if (drawingMode === "addEdge") { - return
; - } - if (drawingMode === "addNode") { - return
; - } - if (drawingMode === "delete") { - return
; - } - return null; -}; interface Props { highlightNode?: Node, diff --git a/src/components/drawing/drag-icon.tsx b/src/components/drawing/drag-icon.tsx new file mode 100644 index 0000000..121b1d4 --- /dev/null +++ b/src/components/drawing/drag-icon.tsx @@ -0,0 +1,26 @@ +import React, { useEffect, useState } from "react"; +import { DrawingMode } from "../graph"; +import AddNodeIcon from "../../assets/add-node-icon.svg"; +import AddEdgeIcon from "../../assets/add-edge-icon.svg"; +import DeleteIcon from "../../assets/delete-icon.svg"; + +export const DragIcon = ({ drawingMode }: { drawingMode: DrawingMode; }) => { + const [style, setStyle] = useState({}); + + useEffect(() => { + const mouseHandler = (e: MouseEvent) => setStyle({ left: e.clientX - 20, top: e.clientY - 20 }); + window.addEventListener("mousemove", mouseHandler); + return () => window.removeEventListener("mousemove", mouseHandler); + }, []); + + if (drawingMode === "addEdge") { + return
; + } + if (drawingMode === "addNode") { + return
; + } + if (drawingMode === "delete") { + return
; + } + return null; +}; diff --git a/src/components/drawing/node-modal.tsx b/src/components/drawing/node-modal.tsx new file mode 100644 index 0000000..85ad0b2 --- /dev/null +++ b/src/components/drawing/node-modal.tsx @@ -0,0 +1,92 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { ProbabilitySelector, percentage, reduceToSum } from "./probability-selector"; +import { Node, Edge, GraphData } from "../../type"; + +export interface NodeModalProps { + node?: Node, + graph: GraphData; + onChange: (id: string, newNode: Node, newEdge: Edge[]) => void, + onCancel: () => void +} + +export const NodeModal = ({ node, graph, onChange, onCancel }: NodeModalProps) => { + const [label, setLabel] = useState(node?.label || ""); + const [exactPercentages, _setExactPercentages] = useState([]); + + const setExactPercentages = useCallback((percentages: number[]) => { + // make sure percentages add up to exactly 100% + const allButLastSum = reduceToSum(percentages.slice(0, -1)); + percentages[percentages.length - 1] = 100 - allButLastSum; + _setExactPercentages(percentages); + }, [_setExactPercentages]); + + const fromEdges = useMemo(() => { + return node ? graph.edges.filter(e => e.from === node.id) : []; + }, [node, graph.edges]); + + const edgeLabels = useMemo(() => { + const labels = graph.nodes.reduce>((acc, cur) => { + acc[cur.id] = cur.label; + return acc; + }, {}); + return fromEdges.map(e => labels[e.to]); + }, [graph.nodes, fromEdges]); + + const edgeValues = useMemo(() => fromEdges.map(e => e.value), [fromEdges]); + const sum = useMemo(() => reduceToSum(edgeValues), [edgeValues]); + + useEffect(() => { + const percentages = edgeValues.map(edgeValue => percentage(edgeValue / sum)); + setExactPercentages(percentages); + }, [edgeValues, sum, setExactPercentages]); + + useEffect(() => { + setLabel(node?.label || ""); + }, [node, setLabel]); + + const handleSubmit = useCallback((e: React.FormEvent) => { + e.preventDefault(); + if (node) { + let valueIndex = 0; + const newEdges = graph.edges.map(edge => { + if (edge.from === node.id) { + const value = sum * exactPercentages[valueIndex++]; + return { ...edge, value }; + } else { + return edge; + } + }); + onChange(node.id, { ...node, label: label.trim() }, newEdges); + } + }, [label, node, exactPercentages, graph.edges, sum, onChange]); + + const handleChangeLabel = (e: React.ChangeEvent) => { + setLabel(e.target.value); + }; + + if (!node) { + return null; + } + + return ( + <> +
+
+
+
Update State
+
+ + +
+ + +
+ +
+
+ + ); +}; diff --git a/src/components/probability-selector.scss b/src/components/drawing/probability-selector.scss similarity index 100% rename from src/components/probability-selector.scss rename to src/components/drawing/probability-selector.scss diff --git a/src/components/probability-selector.tsx b/src/components/drawing/probability-selector.tsx similarity index 100% rename from src/components/probability-selector.tsx rename to src/components/drawing/probability-selector.tsx