feat: introduce BlockEditor for interactive block style, size, and position management within the simulation dashboard.
This commit is contained in:
@@ -272,7 +272,7 @@ const BlockEditor: React.FC<BlockEditorProps> = ({
|
||||
/>
|
||||
|
||||
<InputWithDropDown
|
||||
label="Corner Radius"
|
||||
label="Border Radius"
|
||||
min={0}
|
||||
value={String(
|
||||
parseInt(getCurrentBlockStyleValue(currentBlock, "borderRadius")) ||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { RefObject, useState } from "react";
|
||||
import React, { RefObject, useEffect, useState } from "react";
|
||||
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
|
||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import {
|
||||
@@ -78,6 +78,13 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
setShowSwapUI,
|
||||
}) => {
|
||||
const [color, setColor] = useState("#000000");
|
||||
|
||||
useEffect(() => {
|
||||
setColor(
|
||||
rgbaToHex(getCurrentElementStyleValue(currentElement, "backgroundColor") || "#000000")
|
||||
);
|
||||
}, [currentElement]);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 6 }} ref={elementEditorRef}>
|
||||
{element?.type === "graph" && (
|
||||
@@ -176,8 +183,8 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
<div className="section">
|
||||
<div
|
||||
className={`icon ${getCurrentElementStyleValue(currentElement, "textAlign") === "right"
|
||||
? "active"
|
||||
: ""
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -189,9 +196,9 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${getCurrentElementStyleValue(currentElement, "textAlign") ===
|
||||
"justify"
|
||||
? "active"
|
||||
: ""
|
||||
"justify"
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -203,8 +210,8 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${getCurrentElementStyleValue(currentElement, "textAlign") === "left"
|
||||
? "active"
|
||||
: ""
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -218,8 +225,8 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
<div className="section">
|
||||
<div
|
||||
className={`icon ${((currentElement.style.flexDirection as string) || "row") === "row"
|
||||
? "active"
|
||||
: ""
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -231,9 +238,9 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${((currentElement.style.flexDirection as string) || "row") ===
|
||||
"column"
|
||||
? "active"
|
||||
: ""
|
||||
"column"
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -245,9 +252,9 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${((currentElement.style.flexDirection as string) || "row") ===
|
||||
"row-reverse"
|
||||
? "active"
|
||||
: ""
|
||||
"row-reverse"
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -259,9 +266,9 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${((currentElement.style.flexDirection as string) || "row") ===
|
||||
"column-reverse"
|
||||
? "active"
|
||||
: ""
|
||||
"column-reverse"
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() =>
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -274,27 +281,52 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-section-footer">
|
||||
<InputWithDropDown
|
||||
label="Layer"
|
||||
value={String(currentElement.zIndex || 1)}
|
||||
placeholder={"Layer"}
|
||||
onChange={(newValue: string) => updateElementZIndex(selectedBlock, selectedElement, Number(newValue))}
|
||||
/>
|
||||
<InputRange
|
||||
label={"Radius"}
|
||||
min={0}
|
||||
max={8}
|
||||
value={
|
||||
parseInt(
|
||||
getCurrentElementStyleValue(currentElement, "borderRadius") || ""
|
||||
) || 8
|
||||
}
|
||||
onChange={(newValue: number) => {
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
borderRadius: Number(newValue),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="layer-system">
|
||||
<InputWithDropDown
|
||||
label="Layer"
|
||||
value={String(currentElement.zIndex || 1)}
|
||||
placeholder={"Layer"}
|
||||
onChange={(newValue: string) =>
|
||||
updateElementZIndex(
|
||||
selectedBlock,
|
||||
selectedElement,
|
||||
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>
|
||||
|
||||
@@ -361,9 +393,6 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
<div className="left">
|
||||
<input
|
||||
type="color"
|
||||
defaultValue={rgbaToHex(
|
||||
getCurrentElementStyleValue(currentElement, "backgroundColor")
|
||||
)}
|
||||
value={color}
|
||||
onChange={(e) => {
|
||||
updateElementStyle(selectedBlock, selectedElement, {
|
||||
@@ -399,12 +428,13 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
<InputRange
|
||||
label={"Opacity"}
|
||||
min={0}
|
||||
max={100}
|
||||
value={Math.round(getAlphaFromRgba(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)") * 100)}
|
||||
max={1}
|
||||
step={0.1}
|
||||
value={getAlphaFromRgba(getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)")}
|
||||
onChange={(value: number) => {
|
||||
const currentBg = getCurrentElementStyleValue(currentElement, "backgroundColor") || "rgba(0,0,0,1)";
|
||||
const currentHex = rgbaToHex(currentBg);
|
||||
const newBg = hexToRgba(currentHex, Number(value) / 100);
|
||||
const newBg = hexToRgba(currentHex, Number(value));
|
||||
updateElementStyle(selectedBlock, selectedElement, { backgroundColor: newBg });
|
||||
}}
|
||||
/>
|
||||
@@ -417,6 +447,21 @@ const ElementDesign: React.FC<ElementDesignProps> = ({
|
||||
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>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
|
||||
* > {
|
||||
*> {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
|
||||
&.edit-mode {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
outline: 1px solid var(--border-color-accent);
|
||||
}
|
||||
@@ -185,6 +186,7 @@
|
||||
&.edit-mode {
|
||||
transition: all 0.2s;
|
||||
outline: 1px solid transparent;
|
||||
|
||||
&:hover {
|
||||
outline-color: var(--border-color);
|
||||
transform: translateY(-2px);
|
||||
@@ -200,6 +202,7 @@
|
||||
&.selected {
|
||||
outline: 2px solid var(--border-color-accent) !important;
|
||||
background-color: rgba(98, 0, 255, 0.089);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(0);
|
||||
}
|
||||
@@ -365,7 +368,6 @@
|
||||
.value-field-container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
// padding: 6px 0px;
|
||||
}
|
||||
|
||||
.select-type {
|
||||
@@ -498,11 +500,9 @@
|
||||
|
||||
.colorValue {
|
||||
width: 100%;
|
||||
background: linear-gradient(
|
||||
90.85deg,
|
||||
rgba(240, 228, 255, 0.3) 3.6%,
|
||||
rgba(211, 174, 253, 0.3) 96.04%
|
||||
);
|
||||
background: linear-gradient(90.85deg,
|
||||
rgba(240, 228, 255, 0.3) 3.6%,
|
||||
rgba(211, 174, 253, 0.3) 96.04%);
|
||||
text-align: center;
|
||||
padding: 4px 0;
|
||||
border-radius: 100px;
|
||||
@@ -514,10 +514,12 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
|
||||
.layer-system {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
|
||||
.increase-z,
|
||||
.decrease-z,
|
||||
.reset {
|
||||
@@ -530,13 +532,16 @@
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: var(--background-color-accent);
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.increase-z {
|
||||
svg {
|
||||
rotate: 180deg;
|
||||
@@ -556,7 +561,7 @@
|
||||
gap: 0px;
|
||||
|
||||
.label {
|
||||
min-width: 82px;
|
||||
min-width: 92px;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
@@ -568,6 +573,7 @@
|
||||
margin: 0;
|
||||
width: 136px;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
margin: 0;
|
||||
width: 48px;
|
||||
@@ -760,6 +766,7 @@
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
right: 40px;
|
||||
|
||||
.appearance {
|
||||
.design-datas-wrapper {
|
||||
display: grid;
|
||||
@@ -1125,4 +1132,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user