Add new alignment icons and enhance dropdown components

- Introduced new alignment icons: AlignRightIcon, AlignJustifyIcon, AlignLeftIcon, FlexRowIcon, FlexColumnIcon, FlexRowReverseIcon, and FlexColumnReverseIcon.
- Updated MainScene to ensure loading progress is displayed correctly.
- Enhanced DataDetailedDropdown to include click outside functionality for closing the dropdown and improved eye dropper icon interaction.
- Created a new DataSourceSelector component for selecting data sources with an optional eye dropper feature.
- Cleaned up InputWithDropDown component by removing unnecessary whitespace.
- Improved styles in _simulationDashBoard.scss for better layout and responsiveness, including new design sections and alignment options.
This commit is contained in:
Nalvazhuthi
2025-12-16 18:01:17 +05:30
parent a06768e0f2
commit a9df8d98bb
8 changed files with 1252 additions and 996 deletions

View File

@@ -9,8 +9,10 @@ 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, EyeDroperIcon, ParametersIcon } from "../../../icons/ExportCommonIcons";
import { AddIcon, DeviceIcon, ParametersIcon } from "../../../icons/ExportCommonIcons";
import DataDetailedDropdown from "../../../ui/inputs/DataDetailedDropdown";
import RenameInput from "../../../ui/inputs/RenameInput";
import DataSourceSelector from "../../../ui/inputs/DataSourceSelector";
interface BlockEditorProps {
blockEditorRef: RefObject<HTMLDivElement>;
@@ -34,439 +36,169 @@ const BlockEditor: React.FC<BlockEditorProps> = ({
updateBlockZIndex,
}) => {
const [selectType, setSelectType] = useState("data")
const [selectDataMapping, setSelectDataMapping] = useState("singleMachine")
const [color, setColor] = useState("#000000");
return (
<div ref={blockEditorRef} className="panel block-editor-panel">
<div className="header">
<h4>Block Style</h4>
{/* <div className="delete icon">
<div className="delete icon">
<DeleteIcon />
</div> */}
</div>
<div className="type-switch">
<div
className={`type ${selectType === "design" ? "active" : ""}`}
onClick={() => setSelectType("design")}>
Design
</div>
<div
className={`type ${selectType === "data" ? "active" : ""}`}
onClick={() => setSelectType("data")}>
Data
</div>
</div>
{selectType === "design" && (
<>
<div className="form-group">
<label className="form-label">Position Type: </label>
<select
value={currentBlock.positionType || "relative"}
onChange={(e) => updateBlockPositionType(selectedBlock, e.target.value as "relative" | "absolute" | "fixed")}
className="form-select"
>
<option value="relative">Relative</option>
<option value="absolute">Absolute</option>
<option value="fixed">Fixed</option>
</select>
<div className="design-section-wrapper">
<div className="design-section">
<div className="section-header">Size</div>
<InputWithDropDown
label="Width"
value={String(currentBlock.size?.width || 400)} // Ensure the value is a string
placeholder={"Width"}
onChange={(newValue) => {
updateBlockSize(selectedBlock, {
...currentBlock.size!,
width: Number(newValue), // Make sure to convert the string back to a number here
})
}}
/>
<InputWithDropDown
label="Height"
value={String(currentBlock.size?.height || 300)}
placeholder={"Width"}
onChange={(newValue) => {
updateBlockSize(selectedBlock, {
...currentBlock.size!,
height: Number(newValue),
})
}}
/>
</div>
{/* <RegularDropDown
header={currentBlock.positionType || "relative"}
options={["Relative", "Absolute", "Fixed"]} // Pass layout names as options
onSelect={(option) => updateBlockPositionType(
selectedBlock,
option.toLowerCase() as "relative" | "absolute" | "fixed"
)}
search={false}
/> */}
</div>
{currentBlock.positionType === "absolute" && (
<>
<div className="form-group">
<label className="form-label">X Position: </label>
<input
type="number"
placeholder="X"
value={currentBlock.position?.x || 0}
onChange={(e) =>
updateBlockPosition(selectedBlock, {
...currentBlock.position!,
x: Number(e.target.value),
})
}
className="form-input"
/>
<div className="design-section">
<div className="section-header">Position</div>
<div className="select-type">
{["relative", "absolute"].map((position) => (
<div
key={position}
className={`type ${currentBlock.positionType === position ? "active" : ""}`}
onClick={() => updateBlockPositionType(selectedBlock, position as "relative" | "absolute" | "fixed")}
>
{position.charAt(0).toUpperCase() + position.slice(1)}
</div>
<div className="form-group">
<label className="form-label">Y Position: </label>
<input
type="number"
placeholder="Y"
value={currentBlock.position?.y || 0}
onChange={(e) =>
updateBlockPosition(selectedBlock, {
...currentBlock.position!,
y: Number(e.target.value),
})
))}
</div>
<div className="position-canvas">
<div className="canvas">
<div className="value padding-top">
<RenameInput
value={
getCurrentBlockStyleValue(currentBlock, "padding")
? String(getCurrentBlockStyleValue(currentBlock, "padding"))
: "120"
}
className="form-input"
/>
</div>
</>
)}
<div className="value padding-right">
<RenameInput
value={
getCurrentBlockStyleValue(currentBlock, "padding")
? String(getCurrentBlockStyleValue(currentBlock, "padding"))
: "120"
}
/>
<div className="form-group">
<label className="form-label">Background Color: </label>
<input
type="color"
value={rgbaToHex(getCurrentBlockStyleValue(currentBlock, "backgroundColor"))}
onChange={(e) => handleBackgroundColorChange(currentBlock, selectedBlock, updateBlockStyle, e.target.value)}
className="color-input"
/>
</div>
<div className="value padding-bottom">
<RenameInput
value={
getCurrentBlockStyleValue(currentBlock, "padding")
? String(getCurrentBlockStyleValue(currentBlock, "padding"))
: "120"
}
/>
</div>
<div className="value padding-left">
<RenameInput
value={
getCurrentBlockStyleValue(currentBlock, "padding")
? String(getCurrentBlockStyleValue(currentBlock, "padding"))
: "120"
}
/>
</div>
</div>
</div>
<div className="form-group">
{/* <label className="form-label">
Background Opacity:{" "}
{Math.round(
getAlphaFromRgba(
getCurrentBlockStyleValue(currentBlock, "backgroundColor")
) * 100
)}
%
</label>
<input
type="range"
min="0"
max="100"
value={
getAlphaFromRgba(
getCurrentBlockStyleValue(currentBlock, "backgroundColor")
) * 100
}
onChange={(e) =>
handleBackgroundAlphaChange(
currentBlock,
selectedBlock,
updateBlockStyle,
Number(e.target.value)
)
}
className="range-input"
/> */}
<InputRange
label="Background Opacity"
disabled={false}
value={Math.round(getAlphaFromRgba(getCurrentBlockStyleValue(currentBlock, "backgroundColor")) * 100)}
min={0}
max={100}
onChange={(value: number) => handleBackgroundAlphaChange(currentBlock, selectedBlock, updateBlockStyle, Number(value))}
// onPointerUp={updatedDist}
key={"6"}
/>
</div>
<div className="form-group">
{/* <label className="form-label">
Blur Amount:{" "}
{parseInt(
getCurrentBlockStyleValue(currentBlock, "backdropFilter")?.match(
/\d+/
)?.[0] || "10"
)}
px
</label>
<input
type="range"
min="0"
max="50"
value={parseInt(
getCurrentBlockStyleValue(currentBlock, "backdropFilter")?.match(
/\d+/
)?.[0] || "10"
)}
onChange={(e) =>
handleBlurAmountChange(
selectedBlock,
updateBlockStyle,
Number(e.target.value)
)
}
className="range-input"
/> */}
<InputRange
label="Blur Amount"
disabled={false}
value={parseInt(getCurrentBlockStyleValue(currentBlock, "backdropFilter")?.match(/\d+/)?.[0] || "10")}
min={0}
max={50}
onChange={(value: number) => handleBlurAmountChange(selectedBlock, updateBlockStyle, Number(value))}
// onPointerUp={updatedDist}
key={"6"}
/>
</div>
<div className="form-group">
<label className="form-label">Width: </label>
<input
type="number"
placeholder="Width"
value={currentBlock.size?.width || 400}
onChange={(e) =>
updateBlockSize(selectedBlock, {
...currentBlock.size!,
width: Number(e.target.value),
})
}
className="form-input"
/>
</div>
<div className="form-group">
<label className="form-label">Height: </label>
<input
type="number"
placeholder="Height"
value={currentBlock.size?.height || 300}
onChange={(e) =>
updateBlockSize(selectedBlock, {
...currentBlock.size!,
height: Number(e.target.value),
})
}
className="form-input"
/>
</div>
<div className="form-group">
<label className="form-label">Z-Index: </label>
<input type="number" placeholder="Z-Index" value={currentBlock.zIndex || 1} onChange={(e) => updateBlockZIndex(selectedBlock, Number(e.target.value))} className="form-input" />
</div>
<div className="form-group">
<label className="form-label">Padding: </label>
<input
type="number"
placeholder="Padding"
value={parseInt(getCurrentBlockStyleValue(currentBlock, "padding")) || 10}
onChange={(e) => updateBlockStyle(selectedBlock, { padding: Number(e.target.value) })}
className="form-input"
/>
</div>
<div className="form-group">
<label className="form-label">Border Radius: </label>
<input
type="number"
placeholder="Border Radius"
value={parseInt(getCurrentBlockStyleValue(currentBlock, "borderRadius")) || 8}
onChange={(e) =>
updateBlockStyle(selectedBlock, {
borderRadius: Number(e.target.value),
})
}
className="form-input"
/>
</div>
</>
)}
{selectType === "data" && (
<div className="data-details">
<div className="data-wrapper">
<div className="design-section-footer">
<InputWithDropDown
label="Title"
value={`title`}
placeholder={"Label 1"}
min={0.1}
step={0.1}
max={2}
onChange={() => { }}
label="Layer"
value={String(currentBlock.zIndex || 1)}
placeholder={"Layer"}
onChange={(newValue) => updateBlockZIndex(selectedBlock, Number(newValue))}
/>
<div className="data">
<DataDetailedDropdown
title="Data Source"
placeholder="Select assets"
sections={[
{
title: "Global",
items: [
{ id: "global", label: "Global", icon: <DeviceIcon /> },
],
},
{
title: "Assets",
items: [
{ id: "cmm-001", label: "CMM-001", icon: <DeviceIcon /> },
{ id: "cnc-1", label: "CNC-Lathe-0001", icon: <DeviceIcon /> },
{ id: "cnc-2", label: "CNC-drilling-tapping-3Axis", icon: <DeviceIcon /> },
{ id: "cnc-3", label: "CNC_0001", icon: <DeviceIcon /> },
],
},
]}
value={null}
onChange={() => { }}
dropDownHeader={"RT-Data"}
eyedroper={true}
/>
</div>
<div className="data">
<DataDetailedDropdown
title="Value"
placeholder="Select Value"
sections={[
{
title: "Selected Assets name",
items: [
{ id: "ambientTemp", label: "ambientTemp", icon: <ParametersIcon /> },
{ id: "measurementDeviation", label: "measurementDeviation", icon: <ParametersIcon /> },
{ id: "powerConsumption", label: "powerConsumption", icon: <ParametersIcon /> },
{ id: "probeX", label: "probePositionX", icon: <ParametersIcon /> },
{ id: "probeY", label: "probePositionY", icon: <ParametersIcon /> },
{ id: "probeZ", label: "probePositionZ", icon: <ParametersIcon /> },
],
},
]}
value={null}
onChange={() => { }}
dropDownHeader={"RT-Data-Value"}
/>
</div>
</div>
{/* Data Mapping */}
<div className="data-mapping">
<div className="heading">Data Mapping</div>
<div className="type-switch">
<div
className={`type ${selectDataMapping === "singleMachine" ? "active" : ""}`}
onClick={() => setSelectDataMapping("singleMachine")}
>
Single Machine
</div>
<div
className={`type ${selectDataMapping === "multipleeMachine" ? "active" : ""}`}
onClick={() => setSelectDataMapping("multipleeMachine")}
>
Multiple Machine
</div>
</div>
{selectDataMapping === "singleMachine" && (
<div className="fields-wrapper">
<div className="datas">
<div className="datas__label">Data Source</div>
<div className="datas__class">
<RegularDropDown
header={"Select value"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
<div className="icon"><EyeDroperIcon isActive={false} /></div>
</div>
</div>
<div className="datas">
<div className="datas__label">Input 1</div>
<div className="datas__class">
<RegularDropDown
header={"Select value"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
</div>
</div>
<div className="datas">
<div className="datas__label">Input 2</div>
<div className="datas__class">
<RegularDropDown
header={"Select value"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
</div>
</div>
<div className="add-field">
<div className="icon"><AddIcon /></div>
<div className="label">Add Field</div>
</div>
</div>
)}
{selectDataMapping === "multipleeMachine" && (
<div className="fields-wrapper">
<div className="datas">
<div className="datas__label">Common Value</div>
<div className="datas__class">
<RegularDropDown
header={"Select value"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
</div>
</div>
<div className="datas">
<div className="datas__label">Data Source</div>
<div className="datas__class">
<RegularDropDown
header={"Select assets"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
<div className="icon"><EyeDroperIcon isActive={false} /></div>
</div>
</div>
<div className="datas">
<div className="datas__label">Data Source</div>
<div className="datas__class">
<RegularDropDown
header={"Select assets"}
options={["1h", "2h", "12h"]}
onSelect={() => { }}
search={false}
/>
<div className="icon"><EyeDroperIcon isActive={false} /></div>
</div>
</div>
<div className="add-field">
<div className="icon"><AddIcon /></div>
<div className="label">Add Field</div>
</div>
</div>
)}
<InputRange
label={"Radius"}
min={0}
max={8}
value={parseInt(getCurrentBlockStyleValue(currentBlock, "borderRadius")) || 8}
onChange={(newValue) => {
updateBlockStyle(selectedBlock, {
borderRadius: Number(newValue),
})
}}
/>
</div>
</div>
)}
<div className="design-section">
<div className="section-header">Background</div>
<div className="data-picker">
<div className="label">Color</div>
<div className="left">
<input
type="color"
value={rgbaToHex(getCurrentBlockStyleValue(currentBlock, "backgroundColor"))}
// onChange={(e) => setColor(e.target.value)}
onChange={(e) => handleBackgroundColorChange(currentBlock, selectedBlock, updateBlockStyle, e.target.value)}
/>
<div className="colorValue">{color}</div>
</div>
</div>
<InputRange
label={"Opacity"}
min={0}
max={8}
value={Math.round(getAlphaFromRgba(getCurrentBlockStyleValue(currentBlock, "backgroundColor")) * 100)}
// onChange={(value: number) => handleBackgroundAlphaChange(currentBlock, selectedBlock, updateBlockStyle, Number(value))}
onChange={(value: number) => handleBlurAmountChange(selectedBlock, updateBlockStyle, Number(value))}
/>
<InputRange
label={"Blur"}
min={0}
max={8}
value={parseInt(getCurrentBlockStyleValue(currentBlock, "backdropFilter")?.match(/\d+/)?.[0] || "10")}
/>
</div>
</div>
</div>
);
};

View File

@@ -1774,3 +1774,92 @@ export const ParametersIcon = () => {
);
};
export const AlignRightIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 10H8M21 14H3M21 18H8M21 6H3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
);
};
export const AlignJustifyIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 10H21M3 14H21M3 18H21M3 6H21" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
);
};
export const AlignLeftIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 10H16M3 14H21M3 18H16M3 6H21" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
);
};
export const FlexRowIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.1782 5.79502L13.1782 18.2059C13.1782 19.3842 13.6809 19.8555 14.9299 19.8555H18.1033C19.3523 19.8555 19.855 19.3842 19.855 18.2059L19.855 5.79502C19.855 4.61677 19.3523 4.14547 18.1033 4.14547L14.9299 4.14547C13.6809 4.14547 13.1782 4.61677 13.1782 5.79502Z" stroke="white" stroke-width="0.7855" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4.14502 5.79502L4.14502 18.2059C4.14502 19.3842 4.64774 19.8555 5.89668 19.8555H9.07011C10.3191 19.8555 10.8218 19.3842 10.8218 18.2059L10.8218 5.79502C10.8218 4.61677 10.3191 4.14547 9.07011 4.14547L5.89668 4.14547C4.64774 4.14547 4.14502 4.61677 4.14502 5.79502Z" stroke="white" stroke-width="0.7855" stroke-linecap="round" stroke-linejoin="round" />
</svg>
);
};
export const FlexColumnIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.2055 13.1787H5.79457C4.61632 13.1787 4.14502 13.6814 4.14502 14.9304V18.1038C4.14502 19.3527 4.61632 19.8555 5.79457 19.8555H18.2055C19.3837 19.8555 19.855 19.3527 19.855 18.1038V14.9304C19.855 13.6814 19.3837 13.1787 18.2055 13.1787Z" stroke="white" stroke-width="0.7855" stroke-linecap="round" stroke-linejoin="round" />
<path d="M18.2055 4.14453H5.79457C4.61632 4.14453 4.14502 4.64725 4.14502 5.8962V9.06962C4.14502 10.3186 4.61632 10.8213 5.79457 10.8213H18.2055C19.3837 10.8213 19.855 10.3186 19.855 9.06962V5.8962C19.855 4.64725 19.3837 4.14453 18.2055 4.14453Z" stroke="white" stroke-width="0.7855" stroke-linecap="round" stroke-linejoin="round" />
</svg>
);
};
export const FlexRowReverseIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6424_3252)">
<path d="M9.74963 5.82994L9.74963 18.241C9.74963 19.4193 10.2524 19.8906 11.5013 19.8906H14.6748C15.9238 19.8906 16.4265 19.4193 16.4265 18.241V5.82994C16.4265 4.65167 15.9238 4.18037 14.6748 4.18037H11.5013C10.2524 4.18037 9.74963 4.65167 9.74963 5.82994Z" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
<path d="M0.716431 5.75865L0.716431 18.1698C0.716431 19.348 1.21916 19.8193 2.46812 19.8193H5.6416C6.89056 19.8193 7.39329 19.348 7.39329 18.1698L7.39329 5.75865C7.39329 4.58039 6.89056 4.10908 5.6416 4.10908H2.46812C1.21916 4.10908 0.716431 4.58039 0.716431 5.75865Z" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
</g>
<g clip-path="url(#clip1_6424_3252)">
<path d="M20.1401 4.99716C21.33 7.07912 21.1304 8.85883 19.5408 10.3352C20.3157 8.55061 19.8845 6.96667 18.4644 5.7629L17.2075 6.3372L18.3451 3.28529L21.397 4.42286L20.1401 4.99716Z" fill="white" stroke="white" stroke-width="0.460612" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_6424_3252">
<rect width="19.7634" height="20.6512" fill="white" transform="translate(0 3.34863)" />
</clipPath>
<clipPath id="clip1_6424_3252">
<rect width="9.37901" height="9.11321" fill="white" transform="matrix(0.400662 0.916226 -0.902414 0.43087 20.2422 0)" />
</clipPath>
</defs>
</svg>
);
};
export const FlexColumnReverseIcon = () => {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_6424_3258)">
<path d="M15.0643 13.1787H2.65C1.47143 13.1787 1 13.6816 1 14.9309V18.1051C1 19.3544 1.47143 19.8573 2.65 19.8573H15.0643C16.2429 19.8573 16.7143 19.3544 16.7143 18.1051V14.9309C16.7143 13.6816 16.2429 13.1787 15.0643 13.1787Z" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
<path d="M15.0643 4.14355H2.65C1.47143 4.14355 1 4.64641 1 5.8957V9.06999C1 10.3193 1.47143 10.8221 2.65 10.8221H15.0643C16.2429 10.8221 16.7143 10.3193 16.7143 9.06999V5.8957C16.7143 4.64641 16.2429 4.14355 15.0643 4.14355Z" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
</g>
<g clip-path="url(#clip1_6424_3258)">
<path d="M20.14 4.99716C21.3298 7.07912 21.1303 8.85883 19.5407 10.3352C20.3156 8.55061 19.8844 6.96667 18.4642 5.7629L17.2074 6.3372L18.345 3.28529L21.3969 4.42286L20.14 4.99716Z" fill="white" stroke="white" stroke-width="0.460612" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_6424_3258">
<rect width="19.7634" height="20.6512" fill="white" transform="translate(0 3.34863)" />
</clipPath>
<clipPath id="clip1_6424_3258">
<rect width="9.37901" height="9.11321" fill="white" transform="matrix(0.400662 0.916226 -0.902414 0.43087 20.2422 0)" />
</clipPath>
</defs>
</svg>
);
};

View File

@@ -188,7 +188,7 @@ function MainScene() {
{!selectedUser && (
<>
<KeyPressListener />
{/* {!createNewWindow && loadingProgress > 0 && <LoadingPage progress={loadingProgress} />} */}
{!createNewWindow && loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (
<>
{!toggleView && !isComparing && <ModuleToggle />}
@@ -254,7 +254,7 @@ function MainScene() {
}
onDragOver={(event) => event.preventDefault()}
>
{/* <Scene layout="Main Layout" /> */}
<Scene layout="Main Layout" />
</div>
</>
);

View File

@@ -1,4 +1,4 @@
import React, { useState, useMemo } from "react";
import React, { useState, useMemo, useRef, useEffect } from "react";
import { EyeDroperIcon, SearchIcon } from "../../icons/ExportCommonIcons";
export type DropdownItem = {
@@ -17,7 +17,7 @@ type DataDetailedDropdownProps = {
value?: DropdownItem | null;
onChange?: (item: DropdownItem) => void;
dropDownHeader?: string;
eyedroper?: boolean
eyedroper?: boolean;
};
const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
@@ -27,10 +27,24 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
value,
onChange,
dropDownHeader,
eyedroper
eyedroper,
}) => {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
const [isEyeDroperActive, setIsEyeDroperActive] = useState(false)
const containerRef = useRef<HTMLDivElement>(null);
// Close dropdown on outside click
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const filteredSections = useMemo(() => {
if (!search) return sections;
@@ -46,20 +60,19 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
}, [search, sections]);
return (
<div className="data-detailed-dropdown">
<div className="data-detailed-dropdown" ref={containerRef}>
<div className="title">{title}</div>
<div className="input-container">
<div className="input-wrapper">
<div
className="input"
onClick={() => setOpen((v) => !v)}
>
<div className="input" onClick={() => setOpen((v) => !v)}>
{value?.label || placeholder}
<div className="icon"></div>
</div>
{open && (
<div className="dropdown-panel">
<div className="heading">{dropDownHeader}</div>
{dropDownHeader && <div className="heading">{dropDownHeader}</div>}
<div className="search">
<div className="icon">
<SearchIcon />
@@ -76,9 +89,7 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
{section.title && (
<div className="data-header">
<div className="data-section-title">{section.title}</div>
<div className="data-section-count">
{section.items.length}
</div>
<div className="data-section-count">{section.items.length}</div>
</div>
)}
@@ -101,7 +112,14 @@ const DataDetailedDropdown: React.FC<DataDetailedDropdownProps> = ({
)}
</div>
{eyedroper && <div className="icon"><EyeDroperIcon isActive={false} /></div>}
{eyedroper && (
<div
className={`icon ${isEyeDroperActive ? "active" : ""}`}
onClick={() => setIsEyeDroperActive(!isEyeDroperActive)}
>
<EyeDroperIcon isActive={false} />
</div>
)}
</div>
</div>
);

View File

@@ -0,0 +1,55 @@
import React, { useState } from "react";
import RegularDropDown from "./RegularDropDown";
import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
type DataSourceSelectorProps = {
label?: string;
options: string[];
selected?: string;
onSelect: (value: string) => void;
eyeDropperActive?: boolean; // initial state
showEyeDropper?: boolean;
};
const DataSourceSelector: React.FC<DataSourceSelectorProps> = ({
label = "Data Source",
options,
selected,
onSelect,
showEyeDropper = true,
}) => {
// Local state to toggle EyeDropper
const [isEyeActive, setIsEyeActive] = useState(false);
return (
<div className="datas">
<div className="datas__label">{label}</div>
<div className="datas__class">
<RegularDropDown
header={selected || "Select value"}
options={options}
onSelect={onSelect}
search={false}
/>
{showEyeDropper && (
<div
className={`icon ${isEyeActive ? "active" : ""}`}
onClick={() => setIsEyeActive(!isEyeActive)}
style={{ cursor: "pointer" }}
>
{/* Pass the local state here */}
<EyeDroperIcon isActive={isEyeActive} />
</div>
)}
</div>
</div>
);
};
export default DataSourceSelector;

View File

@@ -88,3 +88,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
};
export default InputWithDropDown;

View File

@@ -272,6 +272,17 @@
box-shadow: 0px 4px 8px rgba(60, 60, 67, 0.1019607843);
z-index: 3;
display: flex;
flex-direction: column;
gap: 11px;
min-width: 280px;
height: fit-content;
min-width: 320px;
min-height: 60vh;
padding: 12px;
.header {
display: flex;
justify-content: space-between;
@@ -296,6 +307,223 @@
}
}
.icon {
display: flex;
}
.design-section {
padding: 4px;
outline: 1px solid var(--border-color);
outline-offset: -1px;
border-radius: 12px;
background: var(--background-color);
padding: 14px 12px;
display: flex;
flex-direction: column;
gap: 11px;
.value-field-container {
padding: 0;
margin: 0;
// padding: 6px 0px;
}
.select-type {
display: flex;
.type {
flex: 1;
padding: 4px 0;
text-align: center;
border-radius: 100px;
&.active {
background-color: var(--background-color-button);
}
}
}
.position-canvas {
background: #8D70AD33;
height: 110px;
border-radius: 17px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.canvas {
width: 60%;
height: 27px;
background: #E0DFFF80;
// position: relative;
.value {
position: absolute;
input {
// border: none;
// outline: none;
width: 20px;
background: transparent;
}
&:nth-child(1) {
top: 20px;
left: 50%;
transform: translateX(-50%);
}
&:nth-child(2) {
top: 50%;
right: 35px;
transform: translateY(-50%);
}
&:nth-child(3) {
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
&:nth-child(4) {
top: 50%;
left: 35px;
transform: translateY(-50%);
}
}
}
}
.alignments-section {
display: flex;
justify-content: center;
gap: 7px;
.section {
border-radius: 100px;
padding: 6px;
display: flex;
gap: 10px;
width: fit-content;
}
.icon {
&.active {
background-color: var(--background-color-button);
border-radius: 2px;
}
}
}
.input-range-container {
margin: 0;
padding: 0;
}
.data-picker {
display: flex;
justify-content: space-between;
align-items: center;
.label {
width: 50%;
white-space: nowrap;
}
input[type="color"] {
width: 42px;
padding: 0;
background: transparent;
outline: none;
}
.left {
width: 100%;
display: flex;
align-items: center;
gap: 6px;
.colorValue {
width: 100%;
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;
}
}
}
.design-section-footer {
display: flex;
gap: 7px;
label,
.input {
width: auto;
}
input {
width: 40px;
}
.input-range-container,
.value-field-container {
width: 100%;
display: flex;
gap: 6px;
.input-container {
width: 100%;
display: flex;
gap: 6px;
input[type="range"] {
margin: 0;
width: 70px;
}
}
}
}
}
.datas {
// width: 100% ;
display: flex;
justify-content: space-between;
align-items: center;
.datas__label,
.datas__class {
flex: 1;
}
}
.type-switch {
display: flex;
padding: 6px 12px;
.type {
flex: 1;
text-align: center;
padding: 4px 0;
border-radius: 19px;
cursor: pointer;
&.active {
background: var(--background-color-button);
}
}
}
&.data-model-panel {
left: 20px;
top: 50px;
@@ -311,131 +539,137 @@
}
}
&.block-editor-panel {
display: flex;
flex-direction: column;
gap: 11px;
min-width: 280px;
height: fit-content;
min-width: 320px;
min-height: 60vh;
padding: 12px;
// h4 {
// color: #4caf50;
// }
.type-switch {
display: flex;
padding: 6px 12px;
.type {
flex: 1;
text-align: center;
padding: 4px 0;
border-radius: 19px;
cursor: pointer;
&.active {
background: var(--background-color-button);
}
}
}
.data-details {
.design-section-wrapper {
display: flex;
flex-direction: column;
gap: 6px;
.data-wrapper {
display: flex;
flex-direction: column;
gap: 6px;
padding: 6px 12px;
.value-field-container {
margin: 0;
padding: 0;
}
.label,
.input.default {
width: auto;
flex: 1;
}
}
.data-details {
display: flex;
flex-direction: column;
gap: 6px;
.data-wrapper {
display: flex;
flex-direction: column;
gap: 6px;
padding: 6px 12px;
.value-field-container {
margin: 0;
padding: 0;
.label,
.input.default {
width: auto;
flex: 1;
}
.data {
display: flex;
justify-content: center;
align-items: center;
gap: 9px;
.datas_label,
.regularDropdown-container {
flex: 1;
}
input,
.input-wrapper {
background: transparent;
}
}
.data-mapping {
.data {
display: flex;
justify-content: center;
align-items: center;
gap: 9px;
.datas_label,
.regularDropdown-container {
flex: 1;
}
}
}
.data-mapping {
display: flex;
flex-direction: column;
gap: 9px;
background: var(--background-color);
border: 1px solid var(--border-color);
box-shadow: var(--box-shadow-medium);
padding: 15px 12px;
border-radius: 25px;
.heading {
text-align: center;
}
.type-switch {}
.fields-wrapper {
display: flex;
flex-direction: column;
gap: 9px;
background: var(--background-color);
border: 1px solid var(--border-color);
box-shadow: var(--box-shadow-medium);
padding: 15px 12px;
border-radius: 25px;
gap: 6px;
.heading {
text-align: center;
}
.type-switch {
padding: 0;
}
.fields-wrapper {
.datas {
width: 100%;
display: flex;
flex-direction: column;
gap: 2px;
align-items: center;
// padding: 6px 12px;
.datas {
width: 100%;
display: flex;
align-items: center;
padding: 6px 12px;
.datas__label,
.datas__class {
flex: 1;
}
.datas__label {
flex: 0.8;
}
.datas__class {
display: flex;
align-items: center;
gap: 4px;
}
.datas__label,
.datas__class {
flex: 1;
}
.add-field {
.datas__label {
flex: 0.8;
}
.datas__class {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
gap: 4px;
svg path {
stroke: #CCACFF !important;
.icon {
display: flex;
padding: 3px;
border-radius: 2px;
&.active {
background: var(--background-color-button);
}
}
.label {
color: #CCACFF;
.regularDropdown-container {
.icon {
padding: 0;
}
}
}
}
.add-field {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
svg path {
stroke: #CCACFF !important;
}
.label {
color: #CCACFF;
}
}
}
}
}
@@ -443,9 +677,33 @@
&.element-editor-panel {
right: 20px;
top: 50px;
min-width: 300px;
// min-width: 300px;
max-height: 84vh;
overflow: auto;
max-width: 320px;
.appearance {
.design-datas-wrapper {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px 16px;
.value-field-container {
label {
width: 80%;
text-wrap: nowrap;
}
}
}
}
.footer {
text-align: center;
color: #CCACFF;
}
}
}
@@ -679,9 +937,18 @@
.input-wrapper {
width: 100%;
background: transparent;
}
.icon {
display: flex;
padding: 2px;
border-radius: 2px;
&.active {
background: var(--background-color-button);
}
cursor: pointer;
}
}
@@ -700,6 +967,7 @@
.input {
display: flex;
justify-content: space-between;
align-items: center;
padding: 4px 8px;
}