diff --git a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx index 5fa9586..986914d 100644 --- a/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx +++ b/app/src/components/SimulationDashboard/components/block/BlockEditor.tsx @@ -1,4 +1,4 @@ -import { useState, type RefObject } from "react"; +import { useState, useRef, useEffect, type RefObject } from "react"; import { Block } from "../../../../types/exportedTypes"; import { getAlphaFromRgba, rgbaToHex } from "../../functions/helpers/colorHandlers"; import { getCurrentBlockStyleValue } from "../../functions/helpers/getCurrentBlockStyleValue"; @@ -9,7 +9,7 @@ import InputRange from "../../../ui/inputs/InputRange"; import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import { DeleteIcon } from "../../../icons/ContextMenuIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; -import { AddIcon, DeviceIcon, ParametersIcon } from "../../../icons/ExportCommonIcons"; +import { AddIcon, DeviceIcon, ParametersIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons"; import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown"; import RenameInput from "../../../ui/inputs/RenameInput"; import DataSourceSelector from "../../../ui/inputs/DataSourceSelector"; @@ -38,9 +38,119 @@ const BlockEditor: React.FC = ({ handleRemoveBlock, }) => { const [color, setColor] = useState("#000000"); + // Dragging for block editor + const panelRef = useRef(null); + const [position, setPosition] = useState<{ x: number; y: number }>(() => { + if (typeof window !== "undefined") { + // approximate initial left using a default panel width of 300 + return { x: Math.max(0, window.innerWidth - 300 - 40), y: 80 }; + } + return { x: 120, y: 80 }; + }); + const initialPositionRef = useRef<{ x: number; y: number } | null>(null); + const draggingRef = useRef(false); + const startXRef = useRef(0); + const startYRef = useRef(0); + const startLeftRef = useRef(0); + const startTopRef = useRef(0); + + // compute exact initial position once we have panel dimensions + useEffect(() => { + const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const nx = Math.max(0, window.innerWidth - width - 40); + const ny = 80; + initialPositionRef.current = { x: nx, y: ny }; + setPosition({ x: nx, y: ny }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + return () => { + // cleanup in case + draggingRef.current = false; + }; + }, []); + + const startDrag = (ev: React.PointerEvent) => { + + if (ev.detail > 1) return; + + const panel = panelRef.current || (blockEditorRef && (blockEditorRef as any).current); + if (!panel) return; + + panel.setPointerCapture?.(ev.pointerId); + + draggingRef.current = true; + startXRef.current = ev.clientX; + startYRef.current = ev.clientY; + startLeftRef.current = position.x; + startTopRef.current = position.y; + + const onPointerMove = (e: PointerEvent) => { + if (!draggingRef.current) return; + const dx = e.clientX - startXRef.current; + const dy = e.clientY - startYRef.current; + + const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const height = panelEl?.offsetHeight || 300; + + const maxX = window.innerWidth - width - 8; + const maxY = window.innerHeight - height - 8; + + let nx = startLeftRef.current + dx; + let ny = startTopRef.current + dy; + + nx = Math.max(0, Math.min(nx, maxX)); + ny = Math.max(0, Math.min(ny, maxY)); + + setPosition({ x: nx, y: ny }); + }; + + const onPointerUp = (e: PointerEvent) => { + draggingRef.current = false; + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerUp); + panel.releasePointerCapture?.(e.pointerId); + }; + + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", onPointerUp); + }; + + const resetPosition = () => { + + console.log("adsasdasdadasd"); + if (initialPositionRef.current) { + setPosition(initialPositionRef.current); + return; + } + const panelEl = panelRef.current || (blockEditorRef && (blockEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const nx = Math.max(0, window.innerWidth - width - 40); + setPosition({ x: nx, y: 80 }); + }; return ( -
+
{ + panelRef.current = el; + if (blockEditorRef && typeof blockEditorRef === "object" && "current" in blockEditorRef) { + (blockEditorRef as any).current = el; + } + }} + className="panel block-editor-panel" + style={{ position: "fixed", left: position.x, top: position.y, zIndex: 999 }} + > +
+ +
+

Block Style

= ({ setShowSwapUI, }) => { return ( -
+
{element?.type === "graph" && (
= ({ const [selectType, setSelectType] = useState("design"); const [selectDataMapping, setSelectDataMapping] = useState(element?.type === "graph" && element.dataBinding?.dataType === "multiple-machine" ? "multipleMachine" : "singleMachine"); + // Dragging state for the panel + const panelRef = useRef(null); + const [position, setPosition] = useState<{ x: number; y: number }>(() => { + if (typeof window !== "undefined") { + // approximate initial left using a default panel width of 300 + return { x: Math.max(0, window.innerWidth - 300 - 40), y: 80 }; + } + return { x: 100, y: 80 }; + }); + const initialPositionRef = useRef<{ x: number; y: number } | null>(null); + + // On mount, compute exact initial position based on panel width and store it + useEffect(() => { + const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const nx = Math.max(0, window.innerWidth - width - 40); + const ny = 80; + initialPositionRef.current = { x: nx, y: ny }; + setPosition({ x: nx, y: ny }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const draggingRef = useRef(false); + const startXRef = useRef(0); + const startYRef = useRef(0); + const startLeftRef = useRef(0); + const startTopRef = useRef(0); + + useEffect(() => { + const onPointerMove = (ev: PointerEvent) => { + if (!draggingRef.current) return; + const dx = ev.clientX - startXRef.current; + const dy = ev.clientY - startYRef.current; + const panel = panelRef.current; + const width = panel?.offsetWidth || 300; + const height = panel?.offsetHeight || 300; + const maxX = window.innerWidth - width - 8; + const maxY = window.innerHeight - height - 8; + let nx = startLeftRef.current + dx; + let ny = startTopRef.current + dy; + if (nx < 0) nx = 0; + if (ny < 0) ny = 0; + if (nx > maxX) nx = maxX; + if (ny > maxY) ny = maxY; + setPosition({ x: nx, y: ny }); + }; + + const onPointerUp = () => { + draggingRef.current = false; + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerUp); + }; + + return () => { + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerUp); + }; + }, []); + + const startDrag = (ev: React.PointerEvent) => { + ev.preventDefault(); + const panel = panelRef.current || (elementEditorRef && (elementEditorRef as any).current); + if (!panel) return; + panel.setPointerCapture?.(ev.pointerId); + draggingRef.current = true; + startXRef.current = ev.clientX; + startYRef.current = ev.clientY; + startLeftRef.current = position.x; + startTopRef.current = position.y; + + const onPointerMove = (e: PointerEvent) => { + if (!draggingRef.current) return; + const dx = e.clientX - startXRef.current; + const dy = e.clientY - startYRef.current; + const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const height = panelEl?.offsetHeight || 300; + const maxX = window.innerWidth - width - 8; + const maxY = window.innerHeight - height - 8; + let nx = startLeftRef.current + dx; + let ny = startTopRef.current + dy; + if (nx < 0) nx = 0; + if (ny < 0) ny = 0; + if (nx > maxX) nx = maxX; + if (ny > maxY) ny = maxY; + setPosition({ x: nx, y: ny }); + }; + + const onPointerUp = (e: PointerEvent) => { + draggingRef.current = false; + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", onPointerUp); + try { + (panel as Element).releasePointerCapture?.((e as any).pointerId); + } catch (err) { } + }; + + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", onPointerUp); + }; + + const resetPosition = () => { + if (initialPositionRef.current) { + setPosition(initialPositionRef.current); + return; + } + const panelEl = panelRef.current || (elementEditorRef && (elementEditorRef as any).current); + const width = panelEl?.offsetWidth || 300; + const nx = Math.max(0, window.innerWidth - width - 40); + setPosition({ x: nx, y: 80 }); + }; + const getAssetDropdownItems = useCallback(() => { if (!product?.eventDatas) return []; @@ -348,7 +459,19 @@ const ElementEditor: React.FC = ({ }; return ( -
+
{ + panelRef.current = el; + if (elementEditorRef && typeof elementEditorRef === "object" && "current" in elementEditorRef) { + (elementEditorRef as any).current = el; + } + }} + className="panel element-editor-panel" + style={{ position: "fixed", left: position.x, top: position.y, zIndex: 1000 }} + > +
+ +

Element Style

= ({ updateElementData(selectedBlock, selectedElement, { label: value }); }} /> + { }} />
= ({
setOpen((v) => !v)}> - {value?.label || placeholder} +
{value?.label || placeholder}
diff --git a/app/src/components/ui/inputs/RegularDropDown.tsx b/app/src/components/ui/inputs/RegularDropDown.tsx index 211ecae..8077e2c 100644 --- a/app/src/components/ui/inputs/RegularDropDown.tsx +++ b/app/src/components/ui/inputs/RegularDropDown.tsx @@ -92,13 +92,7 @@ const RegularDropDown: React.FC = ({ }; return ( -
{ - setIsOpen(false); - }} - > +
{/* Header */}
{selectedOption || header}
diff --git a/app/src/components/ui/inputs/RegularDropDownID.tsx b/app/src/components/ui/inputs/RegularDropDownID.tsx index 4aa7ca8..2060a90 100644 --- a/app/src/components/ui/inputs/RegularDropDownID.tsx +++ b/app/src/components/ui/inputs/RegularDropDownID.tsx @@ -74,7 +74,7 @@ const RegularDropDownID: React.FC = ({ }; return ( -
setIsOpen(false)}> +
{selectedOption || header}
diff --git a/app/src/styles/components/_input.scss b/app/src/styles/components/_input.scss index e628dcb..7bd9f6f 100644 --- a/app/src/styles/components/_input.scss +++ b/app/src/styles/components/_input.scss @@ -225,6 +225,9 @@ input[type="number"] { position: relative; cursor: pointer; + .key{ + text-overflow: none; + } .dropdown-header { height: 100%; display: flex; @@ -260,7 +263,7 @@ input[type="number"] { } .option { - padding: 2px 4px; + padding: 6px 6px; cursor: pointer; flex-direction: row !important; border-radius: #{$border-radius-medium}; diff --git a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss index 8de5c8f..af2a618 100644 --- a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss +++ b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss @@ -282,6 +282,15 @@ min-height: 60vh; padding: 12px; + .resize-icon { + display: flex; + justify-content: center; + align-items: center; + z-index: 100; + svg { + cursor: grab; + } + } .header { display: flex; @@ -556,6 +565,7 @@ } } + .data-details { display: flex; flex-direction: column; @@ -683,6 +693,9 @@ max-width: 320px; + position: fixed; + top: 80px; + right: 40px; .appearance { .design-datas-wrapper { @@ -930,7 +943,7 @@ } .input-container { - width: 100%; + min-width: 100px; display: flex; align-items: center; gap: 6px; @@ -940,6 +953,12 @@ background: transparent; } + .key { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .icon { display: flex; padding: 2px;