Merge remote-tracking branch 'origin/main-demo-ui' into main-dev
This commit is contained in:
@@ -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<BlockEditorProps> = ({
|
||||
handleRemoveBlock,
|
||||
}) => {
|
||||
const [color, setColor] = useState("#000000");
|
||||
// Dragging for block editor
|
||||
const panelRef = useRef<HTMLDivElement | null>(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 (
|
||||
<div ref={blockEditorRef} className="panel block-editor-panel">
|
||||
<div
|
||||
ref={(el) => {
|
||||
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 }}
|
||||
>
|
||||
<div
|
||||
className="resize-icon"
|
||||
onDoubleClick={resetPosition}
|
||||
onPointerDown={startDrag}
|
||||
>
|
||||
<ResizeHeightIcon />
|
||||
</div>
|
||||
|
||||
<div className="header">
|
||||
<h4>Block Style</h4>
|
||||
<div
|
||||
|
||||
@@ -56,7 +56,7 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
setShowSwapUI,
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
||||
{element?.type === "graph" && (
|
||||
<div className="design-section">
|
||||
<DataSourceSelector
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useCallback, useMemo, useState, type RefObject } from "react";
|
||||
import { useCallback, useMemo, useState, useRef, useEffect, type RefObject } from "react";
|
||||
import { ExtendedCSSProperties, UIElement } from "../../../../types/exportedTypes";
|
||||
import { DeleteIcon } from "../../../icons/ContextMenuIcons";
|
||||
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import { AddIcon, DeviceIcon, ParametersIcon } from "../../../icons/ExportCommonIcons";
|
||||
import { AddIcon, DeviceIcon, ParametersIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
|
||||
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
|
||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||
import ElementDesign from "./ElementDesign";
|
||||
@@ -64,6 +64,117 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
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<HTMLDivElement | null>(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<ElementEditorProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={elementEditorRef} className="panel element-editor-panel">
|
||||
<div
|
||||
ref={(el) => {
|
||||
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 }}
|
||||
>
|
||||
<div className="resize-icon" onPointerDown={startDrag} onDoubleClick={resetPosition}>
|
||||
<ResizeHeightIcon />
|
||||
</div>
|
||||
<div className="header">
|
||||
<h4>Element Style</h4>
|
||||
<div
|
||||
@@ -410,6 +533,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
|
||||
updateElementData(selectedBlock, selectedElement, { label: value });
|
||||
}}
|
||||
/>
|
||||
<InputWithDropDown label="Title" value={`title`} placeholder={"Label 1"} min={0.1} step={0.1} max={2} onChange={() => { }} />
|
||||
<div className="data">
|
||||
<DataDetailedDropdown
|
||||
title="Data Source"
|
||||
|
||||
@@ -65,7 +65,7 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
|
||||
<div className="input-container">
|
||||
<div className="input-wrapper">
|
||||
<div className="input" onClick={() => setOpen((v) => !v)}>
|
||||
{value?.label || placeholder}
|
||||
<div className="key">{value?.label || placeholder}</div>
|
||||
<div className="icon">▾</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -92,13 +92,7 @@ const RegularDropDown: React.FC<DropdownProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="regularDropdown-container"
|
||||
ref={dropdownRef}
|
||||
onPointerLeave={() => {
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
<div className="regularDropdown-container" ref={dropdownRef}>
|
||||
{/* Header */}
|
||||
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
||||
<div className="key">{selectedOption || header}</div>
|
||||
|
||||
@@ -74,7 +74,7 @@ const RegularDropDownID: React.FC<DropdownProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="regularDropdown-container" ref={dropdownRef} onPointerLeave={() => setIsOpen(false)}>
|
||||
<div className="regularDropdown-container" ref={dropdownRef}>
|
||||
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
|
||||
<div className="key">{selectedOption || header}</div>
|
||||
<div className="icon">▾</div>
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user