diff --git a/site/src/components/ShapeBuilder/index.js b/site/src/components/ShapeBuilder/index.js index 82b022f..2302927 100644 --- a/site/src/components/ShapeBuilder/index.js +++ b/site/src/components/ShapeBuilder/index.js @@ -1,6 +1,6 @@ // /* global window */ import React, { useEffect, useRef, useState } from "react"; -import { Wrapper, CanvasContainer, OutputBox, StyledSVG, CopyButton } from "./shapeBuilder.styles"; +import { Wrapper, CanvasContainer, OutputBox, StyledSVG, CopyButton, CoordinateDisplay } from "./shapeBuilder.styles"; import { Button, Typography, Box, CopyIcon } from "@sistent/sistent"; import { SVG, extend as SVGextend } from "@svgdotjs/svg.js"; import draw from "@svgdotjs/svg.draw.js"; @@ -15,6 +15,10 @@ const ShapeBuilder = () => { const [error, setError] = useState(null); const [showCopied, setShowCopied] = useState(false); + const [mouseCoords, setMouseCoords] = useState({ x: 0, y: 0, normalized: { x: 0, y: 0 } }); + const [isMouseInCanvas, setIsMouseInCanvas] = useState(false); + const [showCoordinates, setShowCoordinates] = useState(true); + const handleCopyToClipboard = async () => { if (!result.trim()) return; @@ -70,6 +74,43 @@ const ShapeBuilder = () => { poly.move(0, 0); showCytoArray(); }; + + const handleMouseMove = (e) => { + const svg = boardRef.current; + if (!svg) return; + + const rect = svg.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + const centerX = rect.width / 2; + const centerY = rect.height / 2; + const normalizedX = (x - centerX) / centerX; + const normalizedY = (y - centerY) / centerY; + + setMouseCoords({ + x: Math.round(x), + y: Math.round(y), + normalized: { + x: parseFloat(normalizedX.toFixed(3)), + y: parseFloat(normalizedY.toFixed(3)) + }, + + screenX: e.clientX, + screenY: e.clientY + }); + }; + + const handleMouseEnter = () => { + setIsMouseInCanvas(true); + }; + + const handleMouseLeave = () => { + setIsMouseInCanvas(false); + }; + + const toggleCoordinates = () => { + setShowCoordinates(prev => !prev); + }; const handleKeyDown = (e) => { const poly = polyRef.current; @@ -177,6 +218,9 @@ const ShapeBuilder = () => { width="100%" height="100%" onDoubleClick={closeShape} + onMouseMove={handleMouseMove} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} > @@ -185,6 +229,18 @@ const ShapeBuilder = () => { + + {isMouseInCanvas && showCoordinates && ( + + X: {mouseCoords.normalized.x}, Y: {mouseCoords.normalized.y} + + )} + {error && (
{ + diff --git a/site/src/components/ShapeBuilder/shapeBuilder.styles.js b/site/src/components/ShapeBuilder/shapeBuilder.styles.js index aa53f48..f65882d 100644 --- a/site/src/components/ShapeBuilder/shapeBuilder.styles.js +++ b/site/src/components/ShapeBuilder/shapeBuilder.styles.js @@ -138,6 +138,23 @@ export const CopyButton = styled.button` } `; +export const CoordinateDisplay = styled.div` + position: absolute; + pointer-events: none; + background-color: ${({ theme }) => theme?.mode === "light" ? "rgba(255, 255, 255, 0.95)" : "rgba(43, 43, 43, 0.95)"}; + border: 2px solid #00B39F; + border-radius: 6px; + padding: 6px 10px; + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 12px; + font-weight: 600; + color: ${({ theme }) => theme?.mode === "light" ? "#111" : "#fff"}; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + z-index: 1000; + user-select: none; + white-space: nowrap; +`; + // export const Wrapper = styled.div` // padding: 2rem; // background-color: ${({ theme }) => theme.palette.background.default};