feat: introduce BlockEditor for interactive block style, size, and position management within the simulation dashboard.

This commit is contained in:
2025-12-20 13:18:41 +05:30
parent 7646bfa07b
commit da85c33266
3 changed files with 108 additions and 56 deletions

View File

@@ -272,7 +272,7 @@ const BlockEditor: React.FC<BlockEditorProps> = ({
/> />
<InputWithDropDown <InputWithDropDown
label="Corner Radius" label="Border Radius"
min={0} min={0}
value={String( value={String(
parseInt(getCurrentBlockStyleValue(currentBlock, "borderRadius")) || parseInt(getCurrentBlockStyleValue(currentBlock, "borderRadius")) ||

View File

@@ -1,4 +1,4 @@
import React, { RefObject, useState } from "react"; import React, { RefObject, useEffect, useState } from "react";
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector"; import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
import RenameInput from "../../../ui/inputs/RenameInput"; import RenameInput from "../../../ui/inputs/RenameInput";
import { import {
@@ -78,6 +78,13 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
setShowSwapUI, setShowSwapUI,
}) => { }) => {
const [color, setColor] = useState("#000000"); const [color, setColor] = useState("#000000");
useEffect(() => {
setColor(
rgbaToHex(getCurrentElementStyleValue(currentElement, "backgroundColor") || "#000000")
);
}, [currentElement]);
return ( return (
<div style={{ display: "flex", flexDirection: "column", gap: 6 }} ref={elementEditorRef}> <div style={{ display: "flex", flexDirection: "column", gap: 6 }} ref={elementEditorRef}>
{element?.type === "graph" && ( {element?.type === "graph" && (
@@ -274,27 +281,52 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
</div> </div>
</div> </div>
<div className="design-section-footer"> <div className="design-section-footer">
<div className="layer-system">
<InputWithDropDown <InputWithDropDown
label="Layer" label="Layer"
value={String(currentElement.zIndex || 1)} value={String(currentElement.zIndex || 1)}
placeholder={"Layer"} placeholder={"Layer"}
onChange={(newValue: string) => updateElementZIndex(selectedBlock, selectedElement, Number(newValue))} onChange={(newValue: string) =>
/> updateElementZIndex(
<InputRange selectedBlock,
label={"Radius"} selectedElement,
min={0} Number(newValue)
max={8} )
value={
parseInt(
getCurrentElementStyleValue(currentElement, "borderRadius") || ""
) || 8
} }
onChange={(newValue: number) => {
updateElementStyle(selectedBlock, selectedElement, {
borderRadius: Number(newValue),
});
}}
/> />
<button
className="increase-z"
onClick={() => {
updateElementZIndex(
selectedBlock,
selectedElement,
Number(currentElement.zIndex ? currentElement.zIndex + 1 : 1)
);
}}
>
<ArrowIcon />
</button>
<button
className="decrease-z"
onClick={() => {
updateElementZIndex(
selectedBlock,
selectedElement,
Number(currentElement.zIndex ? currentElement.zIndex - 1 : 1)
);
}}
>
<ArrowIcon />
</button>
<button
className="reset"
onClick={() => {
updateElementZIndex(selectedBlock, selectedElement, Number(1));
}}
>
<ResetIcon />
</button>
</div>
</div> </div>
</div> </div>
@@ -361,9 +393,6 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
<div className="left"> <div className="left">
<input <input
type="color" type="color"
defaultValue={rgbaToHex(
getCurrentElementStyleValue(currentElement, "backgroundColor")
)}
value={color} value={color}
onChange={(e) => { onChange={(e) => {
updateElementStyle(selectedBlock, selectedElement, { updateElementStyle(selectedBlock, selectedElement, {
@@ -399,12 +428,13 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
<InputRange <InputRange
label={"Opacity"} label={"Opacity"}
min={0} min={0}
max={100} max={1}
value={Math.round(getAlphaFromRgba(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)") * 100)} step={0.1}
value={getAlphaFromRgba(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)")}
onChange={(value: number) => { onChange={(value: number) => {
const currentBg = getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)"; const currentBg = getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)";
const currentHex = rgbaToHex(currentBg); const currentHex = rgbaToHex(currentBg);
const newBg = hexToRgba(currentHex, Number(value) / 100); const newBg = hexToRgba(currentHex, Number(value));
updateElementStyle(selectedBlock, selectedElement, { backgroundColor: newBg }); updateElementStyle(selectedBlock, selectedElement, { backgroundColor: newBg });
}} }}
/> />
@@ -417,6 +447,21 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` }); updateElementStyle(selectedBlock, selectedElement, { backdropFilter: `blur(${Number(value)}px)` });
}} }}
/> />
<InputRange
label={"Border Radius"}
min={0}
max={8}
value={
parseInt(
getCurrentElementStyleValue(currentElement, "borderRadius") || ""
) || 8
}
onChange={(newValue: number) => {
updateElementStyle(selectedBlock, selectedElement, {
borderRadius: Number(newValue),
});
}}
/>
</div> </div>
</div> </div>
); );

View File

@@ -12,7 +12,7 @@
left: 0; left: 0;
pointer-events: none; pointer-events: none;
* > { *> {
pointer-events: auto; pointer-events: auto;
} }
@@ -88,6 +88,7 @@
&.edit-mode { &.edit-mode {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
outline: 1px solid var(--border-color-accent); outline: 1px solid var(--border-color-accent);
} }
@@ -185,6 +186,7 @@
&.edit-mode { &.edit-mode {
transition: all 0.2s; transition: all 0.2s;
outline: 1px solid transparent; outline: 1px solid transparent;
&:hover { &:hover {
outline-color: var(--border-color); outline-color: var(--border-color);
transform: translateY(-2px); transform: translateY(-2px);
@@ -200,6 +202,7 @@
&.selected { &.selected {
outline: 2px solid var(--border-color-accent) !important; outline: 2px solid var(--border-color-accent) !important;
background-color: rgba(98, 0, 255, 0.089); background-color: rgba(98, 0, 255, 0.089);
&:hover { &:hover {
transform: translateY(0); transform: translateY(0);
} }
@@ -365,7 +368,6 @@
.value-field-container { .value-field-container {
padding: 0; padding: 0;
margin: 0; margin: 0;
// padding: 6px 0px;
} }
.select-type { .select-type {
@@ -498,11 +500,9 @@
.colorValue { .colorValue {
width: 100%; width: 100%;
background: linear-gradient( background: linear-gradient(90.85deg,
90.85deg,
rgba(240, 228, 255, 0.3) 3.6%, rgba(240, 228, 255, 0.3) 3.6%,
rgba(211, 174, 253, 0.3) 96.04% rgba(211, 174, 253, 0.3) 96.04%);
);
text-align: center; text-align: center;
padding: 4px 0; padding: 4px 0;
border-radius: 100px; border-radius: 100px;
@@ -514,10 +514,12 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 7px; gap: 7px;
.layer-system { .layer-system {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 3px; gap: 3px;
.increase-z, .increase-z,
.decrease-z, .decrease-z,
.reset { .reset {
@@ -530,13 +532,16 @@
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
transition: background 0.2s; transition: background 0.2s;
&:hover { &:hover {
background: var(--background-color-accent); background: var(--background-color-accent);
} }
svg { svg {
pointer-events: none; pointer-events: none;
} }
} }
.increase-z { .increase-z {
svg { svg {
rotate: 180deg; rotate: 180deg;
@@ -556,7 +561,7 @@
gap: 0px; gap: 0px;
.label { .label {
min-width: 82px; min-width: 92px;
} }
.input-container { .input-container {
@@ -568,6 +573,7 @@
margin: 0; margin: 0;
width: 136px; width: 136px;
} }
input[type="number"] { input[type="number"] {
margin: 0; margin: 0;
width: 48px; width: 48px;
@@ -760,6 +766,7 @@
position: fixed; position: fixed;
top: 80px; top: 80px;
right: 40px; right: 40px;
.appearance { .appearance {
.design-datas-wrapper { .design-datas-wrapper {
display: grid; display: grid;