From 8cf209c878af32a3744ed0579bf264d15c749ba5 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Tue, 20 May 2025 05:58:31 -0600 Subject: [PATCH] Redesigned Edge Styles via New Context Menu Panel --- .../Server/WebUI/src/Context_Menu_Sidebar.jsx | 415 ++++++++++++++++ Data/Server/WebUI/src/Flow_Editor.jsx | 454 +++++------------- 2 files changed, 530 insertions(+), 339 deletions(-) create mode 100644 Data/Server/WebUI/src/Context_Menu_Sidebar.jsx diff --git a/Data/Server/WebUI/src/Context_Menu_Sidebar.jsx b/Data/Server/WebUI/src/Context_Menu_Sidebar.jsx new file mode 100644 index 0000000..0821305 --- /dev/null +++ b/Data/Server/WebUI/src/Context_Menu_Sidebar.jsx @@ -0,0 +1,415 @@ +import React, { useState, useEffect } from "react"; +import { Box, Typography, Tabs, Tab, TextField, MenuItem, Button, Slider, IconButton, Tooltip } from "@mui/material"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; +import ContentPasteIcon from "@mui/icons-material/ContentPaste"; +import RestoreIcon from "@mui/icons-material/Restore"; +import { SketchPicker } from "react-color"; + +const SIDEBAR_WIDTH = 400; + +const DEFAULT_EDGE_STYLE = { + type: "bezier", + animated: true, + style: { strokeDasharray: "6 3", stroke: "#58a6ff", strokeWidth: 2 }, + label: "", + labelStyle: { fill: "#fff", fontWeight: "bold" }, + labelBgStyle: { fill: "#2c2c2c", fillOpacity: 0.85, rx: 16, ry: 16 }, + labelBgPadding: [8, 4], +}; + +let globalEdgeClipboard = null; + +function clone(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +export default function Context_Menu_Sidebar({ + open, + onClose, + edge, + updateEdge, +}) { + const [activeTab, setActiveTab] = useState(0); + const [editState, setEditState] = useState(() => (edge ? clone(edge) : {})); + const [colorPicker, setColorPicker] = useState({ field: null, anchor: null }); + + useEffect(() => { + if (edge && edge.id !== editState.id) setEditState(clone(edge)); + // eslint-disable-next-line + }, [edge]); + + const handleChange = (field, value) => { + setEditState((prev) => { + const updated = { ...prev }; + if (field === "label") updated.label = value; + else if (field === "labelStyle.fill") updated.labelStyle = { ...updated.labelStyle, fill: value }; + else if (field === "labelBgStyle.fill") updated.labelBgStyle = { ...updated.labelBgStyle, fill: value }; + else if (field === "labelBgStyle.rx") updated.labelBgStyle = { ...updated.labelBgStyle, rx: value, ry: value }; + else if (field === "labelBgPadding") updated.labelBgPadding = value; + else if (field === "labelBgStyle.fillOpacity") updated.labelBgStyle = { ...updated.labelBgStyle, fillOpacity: value }; + else if (field === "type") updated.type = value; + else if (field === "animated") updated.animated = value; + else if (field === "style.stroke") updated.style = { ...updated.style, stroke: value }; + else if (field === "style.strokeDasharray") updated.style = { ...updated.style, strokeDasharray: value }; + else if (field === "style.strokeWidth") updated.style = { ...updated.style, strokeWidth: value }; + else if (field === "labelStyle.fontWeight") updated.labelStyle = { ...updated.labelStyle, fontWeight: value }; + else updated[field] = value; + + if (field === "style.strokeDasharray") { + if (value === "") { + updated.animated = false; + updated.style = { ...updated.style, strokeDasharray: "" }; + } else { + updated.animated = true; + updated.style = { ...updated.style, strokeDasharray: value }; + } + } + updateEdge({ ...updated, id: prev.id }); + return updated; + }); + }; + + // Color Picker with right alignment + const openColorPicker = (field, event) => { + setColorPicker({ field, anchor: event.currentTarget }); + }; + + const closeColorPicker = () => { + setColorPicker({ field: null, anchor: null }); + }; + + const handleColorChange = (color) => { + handleChange(colorPicker.field, color.hex); + closeColorPicker(); + }; + + // Reset, Copy, Paste logic + const handleReset = () => { + setEditState(clone({ ...DEFAULT_EDGE_STYLE, id: edge.id })); + updateEdge({ ...DEFAULT_EDGE_STYLE, id: edge.id }); + }; + const handleCopy = () => { globalEdgeClipboard = clone(editState); }; + const handlePaste = () => { + if (globalEdgeClipboard) { + setEditState(clone({ ...globalEdgeClipboard, id: edge.id })); + updateEdge({ ...globalEdgeClipboard, id: edge.id }); + } + }; + + const renderColorButton = (label, field, value) => ( + + + {colorPicker.field === field && ( + + + + )} + + ); + + // Label tab + const renderLabelTab = () => ( + + + Label + + handleChange("label", e.target.value)} + sx={{ + mb: 2, + input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" }, + "& fieldset": { borderColor: "#444" }, + }} + /> + + + Text Color + {renderColorButton("Label Text Color", "labelStyle.fill", editState.labelStyle?.fill || "#fff")} + + + + Background + {renderColorButton("Label Background Color", "labelBgStyle.fill", editState.labelBgStyle?.fill || "#2c2c2c")} + + + + Padding + { + const val = e.target.value.split(",").map(x => parseInt(x.trim())).filter(x => !isNaN(x)); + if (val.length === 2) handleChange("labelBgPadding", val); + }} + sx={{ width: 80, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} + /> + + + + Background Style + = 11 ? "rounded" : "square"} + onChange={e => { + handleChange("labelBgStyle.rx", e.target.value === "rounded" ? 11 : 0); + }} + sx={{ + width: 150, + bgcolor: "#1e1e1e", + "& .MuiSelect-select": { color: "#fff" } + }} + > + Rounded + Square + + + + + Background Opacity + handleChange("labelBgStyle.fillOpacity", v)} + sx={{ width: 100, ml: 2 }} + /> + handleChange("labelBgStyle.fillOpacity", parseFloat(e.target.value) || 0)} + sx={{ width: 60, ml: 2, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} + /> + + + ); + + const renderStyleTab = () => ( + + + Edge Style + handleChange("type", e.target.value)} + sx={{ + width: 200, + bgcolor: "#1e1e1e", + "& .MuiSelect-select": { color: "#fff" } + }} + > + Step + Curved (Bezier) + Straight + Smoothstep + + + + + Edge Animation + { + const val = e.target.value; + handleChange("style.strokeDasharray", + val === "dashes" ? "6 3" : + val === "dots" ? "2 4" : "" + ); + }} + sx={{ + width: 200, + bgcolor: "#1e1e1e", + "& .MuiSelect-select": { color: "#fff" } + }} + > + Dashes + Dots + Solid + + + + + Color + {renderColorButton("Edge Color", "style.stroke", editState.style?.stroke || "#58a6ff")} + + + Edge Width + handleChange("style.strokeWidth", v)} + sx={{ width: 100, ml: 2 }} + /> + handleChange("style.strokeWidth", parseInt(e.target.value) || 1)} + sx={{ width: 60, ml: 2, input: { color: "#fff", bgcolor: "#1e1e1e", fontSize: "0.95rem" } }} + /> + + + ); + + // Always render the sidebar for animation! + if (!edge) return null; + + return ( + <> + {/* Overlay */} + + + {/* Sidebar */} + e.stopPropagation()} + > + + + + Edit Edge Properties + + + setActiveTab(v)} + variant="fullWidth" + textColor="inherit" + TabIndicatorProps={{ style: { backgroundColor: "#ccc" } }} + sx={{ + borderTop: "1px solid #333", + borderBottom: "1px solid #333", + minHeight: "36px", + height: "36px" + }} + > + + + + + + {/* Main fields scrollable */} + + {activeTab === 0 && renderLabelTab()} + {activeTab === 1 && renderStyleTab()} + + + {/* Sticky footer bar */} + + + + + + + + + + + + ); +} diff --git a/Data/Server/WebUI/src/Flow_Editor.jsx b/Data/Server/WebUI/src/Flow_Editor.jsx index f586d8e..cb3087a 100644 --- a/Data/Server/WebUI/src/Flow_Editor.jsx +++ b/Data/Server/WebUI/src/Flow_Editor.jsx @@ -1,7 +1,7 @@ -// //////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Flow_Editor.jsx - -// Import Node Configuration Sidebar +////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Flow_Editor.jsx +// Import Node Configuration Sidebar and new Context Menu Sidebar import NodeConfigurationSidebar from "./Node_Configuration_Sidebar"; +import ContextMenuSidebar from "./Context_Menu_Sidebar"; import React, { useState, useEffect, useCallback, useRef } from "react"; import ReactFlow, { @@ -12,24 +12,13 @@ import ReactFlow, { useReactFlow } from "reactflow"; -import { - Menu, - MenuItem, - MenuList, - Slider, - Box -} from "@mui/material"; - +import { Menu, MenuItem, Box } from "@mui/material"; import { Polyline as PolylineIcon, DeleteForever as DeleteForeverIcon, Edit as EditIcon } from "@mui/icons-material"; -import { - SketchPicker -} from "react-color"; - import "reactflow/dist/style.css"; export default function FlowEditor({ @@ -45,60 +34,88 @@ export default function FlowEditor({ const [drawerOpen, setDrawerOpen] = useState(false); const [selectedNodeId, setSelectedNodeId] = useState(null); - useEffect(() => { - window.BorealisOpenDrawer = (id) => { - setSelectedNodeId(id); - setDrawerOpen(true); - }; - return () => { - delete window.BorealisOpenDrawer; - }; - }, []); + // Edge Properties Sidebar State + const [edgeSidebarOpen, setEdgeSidebarOpen] = useState(false); + const [edgeSidebarEdgeId, setEdgeSidebarEdgeId] = useState(null); - const selectedNode = nodes.find((n) => n.id === selectedNodeId); + // Context Menus + const [nodeContextMenu, setNodeContextMenu] = useState(null); // { mouseX, mouseY, nodeId } + const [edgeContextMenu, setEdgeContextMenu] = useState(null); // { mouseX, mouseY, edgeId } + // Drag/snap helpers (untouched) const wrapperRef = useRef(null); const { project } = useReactFlow(); - - const [contextMenu, setContextMenu] = useState(null); - const [edgeContextMenu, setEdgeContextMenu] = useState(null); - const [selectedEdgeId, setSelectedEdgeId] = useState(null); - const [showColorPicker, setShowColorPicker] = useState(false); - const [colorPickerMode, setColorPickerMode] = useState(null); - const [labelPadding, setLabelPadding] = useState([8, 4]); - const [labelBorderRadius, setLabelBorderRadius] = useState(4); - const [labelOpacity, setLabelOpacity] = useState(0.8); - const [tempColor, setTempColor] = useState({ hex: "#58a6ff" }); - const [pickerPos, setPickerPos] = useState({ x: 0, y: 0 }); - - // helper-line state - // guides: array of { xFlow, xPx } or { yFlow, yPx } for stationary nodes const [guides, setGuides] = useState([]); - // activeGuides: array of { xPx } or { yPx } to draw const [activeGuides, setActiveGuides] = useState([]); - - // store moving node flow-size on drag start const movingFlowSize = useRef({ width: 0, height: 0 }); - const edgeStyles = { - step: "step", - curved: "bezier", - straight: "straight", - smoothstep: "smoothstep" + // ----- Node/Edge Definitions ----- + const selectedNode = nodes.find((n) => n.id === selectedNodeId); + const selectedEdge = edges.find((e) => e.id === edgeSidebarEdgeId); + + // --------- Context Menu Handlers ---------- + const handleRightClick = (e, node) => { + e.preventDefault(); + setNodeContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6, nodeId: node.id }); }; - const animationStyles = { - dashes: { animated: true, style: { strokeDasharray: "6 3" } }, - dots: { animated: true, style: { strokeDasharray: "2 4" } }, - none: { animated: false, style: {} } + const handleEdgeRightClick = (e, edge) => { + e.preventDefault(); + setEdgeContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6, edgeId: edge.id }); }; - // Compute edge-only guides and capture moving node flow-size + // --------- Node Context Menu Actions --------- + const handleDisconnectAllEdges = (nodeId) => { + setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId)); + setNodeContextMenu(null); + }; + + const handleRemoveNode = (nodeId) => { + setNodes((nds) => nds.filter((n) => n.id !== nodeId)); + setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId)); + setNodeContextMenu(null); + }; + + const handleEditNodeProps = (nodeId) => { + setSelectedNodeId(nodeId); + setDrawerOpen(true); + setNodeContextMenu(null); + }; + + // --------- Edge Context Menu Actions --------- + const handleUnlinkEdge = (edgeId) => { + setEdges((eds) => eds.filter((e) => e.id !== edgeId)); + setEdgeContextMenu(null); + }; + + const handleEditEdgeProps = (edgeId) => { + setEdgeSidebarEdgeId(edgeId); + setEdgeSidebarOpen(true); + setEdgeContextMenu(null); + }; + + // ----- Sidebar Closing ----- + const handleCloseNodeSidebar = () => { + setDrawerOpen(false); + setSelectedNodeId(null); + }; + + const handleCloseEdgeSidebar = () => { + setEdgeSidebarOpen(false); + setEdgeSidebarEdgeId(null); + }; + + // ----- Update Edge Callback for Sidebar ----- + const updateEdge = (updatedEdgeObj) => { + setEdges((eds) => + eds.map((e) => (e.id === updatedEdgeObj.id ? { ...e, ...updatedEdgeObj } : e)) + ); + }; + + // ----- Drag/Drop, Guides, Node Snap Logic (unchanged) ----- const computeGuides = useCallback((dragNode) => { if (!wrapperRef.current) return; const parentRect = wrapperRef.current.getBoundingClientRect(); - - // measure moving node in pixel space const dragEl = wrapperRef.current.querySelector( `.react-flow__node[data-id="${dragNode.id}"]` ); @@ -108,18 +125,11 @@ export default function FlowEditor({ const relTop = dr.top - parentRect.top; const relRight = relLeft + dr.width; const relBottom = relTop + dr.height; - - // project pixel corners to flow coords const pTL = project({ x: relLeft, y: relTop }); const pTR = project({ x: relRight, y: relTop }); const pBL = project({ x: relLeft, y: relBottom }); - - movingFlowSize.current = { - width: pTR.x - pTL.x, - height: pBL.y - pTL.y - }; + movingFlowSize.current = { width: pTR.x - pTL.x, height: pBL.y - pTL.y }; } - const lines = []; nodes.forEach((n) => { if (n.id === dragNode.id) return; @@ -132,57 +142,32 @@ export default function FlowEditor({ const relTop = r.top - parentRect.top; const relRight = relLeft + r.width; const relBottom = relTop + r.height; - - // project pixel to flow coords const pTL = project({ x: relLeft, y: relTop }); const pTR = project({ x: relRight, y: relTop }); const pBL = project({ x: relLeft, y: relBottom }); - - // vertical guides: left edge, right edge lines.push({ xFlow: pTL.x, xPx: relLeft }); lines.push({ xFlow: pTR.x, xPx: relRight }); - - // horizontal guides: top edge, bottom edge lines.push({ yFlow: pTL.y, yPx: relTop }); lines.push({ yFlow: pBL.y, yPx: relBottom }); }); setGuides(lines); }, [nodes, project]); - // Snap & show only matching guides within threshold during drag const onNodeDrag = useCallback((_, node) => { const threshold = 5; let snapX = null, snapY = null; const show = []; const { width: fw, height: fh } = movingFlowSize.current; - guides.forEach((ln) => { if (ln.xFlow != null) { - // moving left edge to stationary edges - if (Math.abs(node.position.x - ln.xFlow) < threshold) { - snapX = ln.xFlow; - show.push({ xPx: ln.xPx }); - } - // moving right edge to stationary edges - else if (Math.abs(node.position.x + fw - ln.xFlow) < threshold) { - snapX = ln.xFlow - fw; - show.push({ xPx: ln.xPx }); - } + if (Math.abs(node.position.x - ln.xFlow) < threshold) { snapX = ln.xFlow; show.push({ xPx: ln.xPx }); } + else if (Math.abs(node.position.x + fw - ln.xFlow) < threshold) { snapX = ln.xFlow - fw; show.push({ xPx: ln.xPx }); } } if (ln.yFlow != null) { - // moving top edge - if (Math.abs(node.position.y - ln.yFlow) < threshold) { - snapY = ln.yFlow; - show.push({ yPx: ln.yPx }); - } - // moving bottom edge - else if (Math.abs(node.position.y + fh - ln.yFlow) < threshold) { - snapY = ln.yFlow - fh; - show.push({ yPx: ln.yPx }); - } + if (Math.abs(node.position.y - ln.yFlow) < threshold) { snapY = ln.yFlow; show.push({ yPx: ln.yPx }); } + else if (Math.abs(node.position.y + fh - ln.yFlow) < threshold) { snapY = ln.yFlow - fh; show.push({ yPx: ln.yPx }); } } }); - if (snapX !== null || snapY !== null) { setNodes((nds) => applyNodeChanges( @@ -251,120 +236,23 @@ export default function FlowEditor({ setEdges((eds) => applyEdgeChanges(changes, eds)); }, [setEdges]); - const handleRightClick = (e, node) => { - e.preventDefault(); - setContextMenu({ mouseX: e.clientX + 2, mouseY: e.clientY - 6, nodeId: node.id }); - }; - - const handleEdgeRightClick = (e, edge) => { - e.preventDefault(); - setEdgeContextMenu({ edgeId: edge.id, mouseX: e.clientX + 2, mouseY: e.clientY - 6 }); - setSelectedEdgeId(edge.id); - }; - - const changeEdgeType = (newType) => { - setEdges((eds) => eds.map((e) => - e.id === selectedEdgeId ? { ...e, type: edgeStyles[newType] } : e - )); - setEdgeContextMenu(null); - }; - - const changeEdgeAnimation = (newAnim) => { - setEdges((eds) => eds.map((e) => { - if (e.id !== selectedEdgeId) return e; - const strokeColor = e.style?.stroke || "#58a6ff"; - const anim = animationStyles[newAnim] || {}; - return { - ...e, - animated: anim.animated, - style: { ...anim.style, stroke: strokeColor }, - markerEnd: e.markerEnd ? { ...e.markerEnd, color: strokeColor } : undefined - }; - })); - setEdgeContextMenu(null); - }; - - const handleColorChange = (color) => { - setEdges((eds) => eds.map((e) => { - if (e.id !== selectedEdgeId) return e; - const updated = { ...e }; - if (colorPickerMode === "stroke") { - updated.style = { ...e.style, stroke: color.hex }; - if (e.markerEnd) updated.markerEnd = { ...e.markerEnd, color: color.hex }; - } else if (colorPickerMode === "labelText") { - updated.labelStyle = { ...e.labelStyle, fill: color.hex }; - } else if (colorPickerMode === "labelBg") { - updated.labelBgStyle = { ...e.labelBgStyle, fill: color.hex, fillOpacity: labelOpacity }; - } - return updated; - })); - }; - - const handleAddLabel = () => { - setEdges((eds) => eds.map((e) => - e.id === selectedEdgeId ? { ...e, label: "New Label" } : e - )); - setEdgeContextMenu(null); - }; - - const handleEditLabel = () => { - const newText = prompt("Enter label text:"); - if (newText !== null) { - setEdges((eds) => eds.map((e) => - e.id === selectedEdgeId ? { ...e, label: newText } : e - )); - } - setEdgeContextMenu(null); - }; - - const handleRemoveLabel = () => { - setEdges((eds) => eds.map((e) => - e.id === selectedEdgeId ? { ...e, label: undefined } : e - )); - setEdgeContextMenu(null); - }; - - const handlePickColor = (mode) => { - setColorPickerMode(mode); - setTempColor({ hex: "#58a6ff" }); - setPickerPos({ x: edgeContextMenu?.mouseX || 0, y: edgeContextMenu?.mouseY || 0 }); - setShowColorPicker(true); - }; - - const applyLabelStyleExtras = () => { - setEdges((eds) => eds.map((e) => - e.id === selectedEdgeId - ? { - ...e, - labelBgPadding: labelPadding, - labelBgStyle: { - ...e.labelBgStyle, - fillOpacity: labelOpacity, - rx: labelBorderRadius, - ry: labelBorderRadius - } - } - : e - )); - setEdgeContextMenu(null); - }; - useEffect(() => { const nodeCountEl = document.getElementById("nodeCount"); if (nodeCountEl) nodeCountEl.innerText = nodes.length; }, [nodes]); - const nodeDef = selectedNode - ? Object.values(categorizedNodes).flat().find((def) => def.type === selectedNode.type) - : null; - - return ( -
+ const nodeDef = selectedNode + ? Object.values(categorizedNodes).flat().find((def) => def.type === selectedNode.type) + : null; + // --------- MAIN RENDER ---------- + return ( +
+ {/* Node Config Sidebar */} + {/* Edge Properties Sidebar */} + { + // Provide id if missing + if (!edge.id && edgeSidebarEdgeId) edge.id = edgeSidebarEdgeId; + updateEdge(edge); + }} + /> - {/* helper lines */} + {/* Helper lines for snapping */} {activeGuides.map((ln, i) => ln.xPx != null ? (
setContextMenu(null)} + open={Boolean(nodeContextMenu)} + onClose={() => setNodeContextMenu(null)} anchorReference="anchorPosition" - anchorPosition={contextMenu ? { top: contextMenu.mouseY, left: contextMenu.mouseX } : undefined} + anchorPosition={nodeContextMenu ? { top: nodeContextMenu.mouseY, left: nodeContextMenu.mouseX } : undefined} PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} > - { - if (contextMenu?.nodeId) { - setEdges((eds) => eds.filter((e) => - e.source !== contextMenu.nodeId && e.target !== contextMenu.nodeId - )); - } - setContextMenu(null); - }}> + handleEditNodeProps(nodeContextMenu.nodeId)}> + + Edit Properties + + handleDisconnectAllEdges(nodeContextMenu.nodeId)}> Disconnect All Edges - { - if (contextMenu?.nodeId) { - setNodes((nds) => nds.filter((n) => n.id !== contextMenu.nodeId)); - setEdges((eds) => eds.filter((e) => - e.source !== contextMenu.nodeId && e.target !== contextMenu.nodeId - )); - } - setContextMenu(null); - }}> + handleRemoveNode(nodeContextMenu.nodeId)}> Remove Node + {/* Edge Context Menu */} setEdgeContextMenu(null)} @@ -459,129 +349,15 @@ export default function FlowEditor({ anchorPosition={edgeContextMenu ? { top: edgeContextMenu.mouseY, left: edgeContextMenu.mouseX } : undefined} PaperProps={{ sx: { bgcolor: "#1e1e1e", color: "#fff", fontSize: "13px" } }} > - setEdges((eds) => eds.filter((e) => e.id !== selectedEdgeId))}> + handleEditEdgeProps(edgeContextMenu.edgeId)}> + + Edit Properties + + handleUnlinkEdge(edgeContextMenu.edgeId)}> Unlink Edge - - Edge Styles - - changeEdgeType("step")}>Step - changeEdgeType("curved")}>Curved - changeEdgeType("straight")}>Straight - changeEdgeType("smoothstep")}>Smoothstep - - - - Animations - - changeEdgeAnimation("dashes")}>Dashes - changeEdgeAnimation("dots")}>Dots - changeEdgeAnimation("none")}>Solid Line - - - - Label - - Add - Remove - - - Edit - - handlePickColor("labelText")}>Text Color - handlePickColor("labelBg")}>Background Color - - Padding: - { - const parts = e.target.value.split(",").map((v) => parseInt(v.trim())); - if (parts.length === 2 && parts.every(Number.isFinite)) setLabelPadding(parts); - }} - /> - - - Radius: - { - const val = parseInt(e.target.value); - if (!isNaN(val)) setLabelBorderRadius(val); - }} - /> - - - Opacity: - - setLabelOpacity(v)} - step={0.05} - min={0} - max={1} - style={{ width: 100 }} - /> - { - const v = parseFloat(e.target.value); - if (!isNaN(v)) setLabelOpacity(v); - }} - /> - - - Apply Label Style Changes - - - handlePickColor("stroke")}>Color - - {showColorPicker && ( -
- setTempColor(c)} /> -
- -
-
- )}
); }