feat: Refactor DataSourceSelector to use structured options and implement RegularDropDownID for improved dropdown functionality

This commit is contained in:
Nalvazhuthi
2025-12-17 15:08:21 +05:30
parent 98cfc033c1
commit 4263ca3db7
4 changed files with 138 additions and 8 deletions

View File

@@ -359,7 +359,13 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
<div className="design-section"> <div className="design-section">
<DataSourceSelector <DataSourceSelector
label={"Chart Type"} label={"Chart Type"}
options={["Line Chart", "Bar Chart", "Pie Chart", "Area Chart", "Radar Chart"]} options={[
{ id: "line", label: "Line Chart" },
{ id: "bar", label: "Bar Chart" },
{ id: "pie", label: "Pie Chart" },
{ id: "area", label: "Area Chart" },
{ id: "radar", label: "Radar Chart" },
]}
onSelect={(newValue) => { onSelect={(newValue) => {
if (newValue === "Line Chart") { if (newValue === "Line Chart") {
updateGraphType(selectedBlock, selectedElement, "line"); updateGraphType(selectedBlock, selectedElement, "line");
@@ -568,7 +574,7 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
<div className="data-details"> <div className="data-details">
{element?.type === "label-value" && ( {element?.type === "label-value" && (
<div className="data-wrapper"> <div className="data-wrapper">
<InputWithDropDown label="Title" value={`title`} placeholder={"Label 1"} min={0.1} step={0.1} max={2} onChange={() => {}} /> <InputWithDropDown label="Title" value={`title`} placeholder={"Label 1"} min={0.1} step={0.1} max={2} onChange={() => { }} />
<div className="data"> <div className="data">
<DataDetailedDropdown <DataDetailedDropdown
title="Data Source" title="Data Source"
@@ -628,7 +634,14 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
{selectDataMapping === "singleMachine" && ( {selectDataMapping === "singleMachine" && (
<div className="fields-wrapper"> <div className="fields-wrapper">
{singleFields.map((field) => ( {singleFields.map((field) => (
<DataSourceSelector key={field.id} label={field.label} options={["1h", "2h", "12h"]} onSelect={() => {}} showEyeDropper={!!field.showEyeDropper} /> <DataSourceSelector
key={field.id}
label={field.label}
options={[
{ id: "global", label: "Global" }
]}
onSelect={() => { }} showEyeDropper={!!field.showEyeDropper}
/>
))} ))}
<div className="add-field" onClick={addField}> <div className="add-field" onClick={addField}>
@@ -645,7 +658,14 @@ const ElementEditor: React.FC<ElementEditorProps> = ({
{multipleFields.map((field) => ( {multipleFields.map((field) => (
<div className="datas" key={field.id}> <div className="datas" key={field.id}>
<div className="datas__class"> <div className="datas__class">
<DataSourceSelector label={field.label} options={["1h", "2h", "12h"]} onSelect={() => {}} showEyeDropper={field.label !== "Common Value"} /> <DataSourceSelector
label={field.label}
options={[
{ id: "global", label: "Global" }
]}
onSelect={() => { }}
showEyeDropper={field.label !== "Common Value"}
/>
</div> </div>
</div> </div>
))} ))}

View File

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

View File

@@ -1,10 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import RegularDropDown from "./RegularDropDown"; import RegularDropDown from "./RegularDropDown";
import { EyeDroperIcon } from "../../icons/ExportCommonIcons"; import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
import RegularDropDownID from "./RegularDropDownID";
type DataSourceSelectorProps = { type DataSourceSelectorProps = {
label?: string; label?: string;
options: string[]; options: {
id: string;
label: string;
}[];
selected?: string; selected?: string;
onSelect: (value: string) => void; onSelect: (value: string) => void;
eyeDropperActive?: boolean; // initial state eyeDropperActive?: boolean; // initial state
@@ -26,7 +30,7 @@ const DataSourceSelector: React.FC<DataSourceSelectorProps> = ({
<div className="datas__label">{label}</div> <div className="datas__label">{label}</div>
<div className="datas__class"> <div className="datas__class">
<RegularDropDown <RegularDropDownID
header={selected || "Select value"} header={selected || "Select value"}
options={options} options={options}
onSelect={onSelect} onSelect={onSelect}

View File

@@ -0,0 +1,106 @@
import React, { useState, useEffect, useRef } from "react";
import { createPortal } from "react-dom";
interface DropdownOption {
id: string;
label: string;
}
interface DropdownProps {
header: string;
options: DropdownOption[];
onSelect: (optionId: string) => void;
search?: boolean;
onClick?: () => void;
onChange?: () => void;
}
const RegularDropDownID: React.FC<DropdownProps> = ({
header,
options,
onSelect,
search = true,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const [filteredOptions, setFilteredOptions] = useState<DropdownOption[]>(options);
const dropdownRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<{ top: number; left: number; width: number }>({ top: 0, left: 0, width: 0 });
useEffect(() => {
if (!isOpen) {
setSelectedOption(null);
setSearchTerm("");
setFilteredOptions(options);
}
}, [isOpen, options]);
useEffect(() => {
setSelectedOption(null);
setSearchTerm("");
setFilteredOptions(options);
}, [header, options]);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
return () => document.removeEventListener("click", handleClickOutside);
}, []);
useEffect(() => {
if (isOpen && dropdownRef.current) {
const rect = dropdownRef.current.getBoundingClientRect();
setPosition({ top: rect.bottom + window.scrollY, left: rect.left + window.scrollX, width: rect.width });
}
}, [isOpen]);
const toggleDropdown = () => setIsOpen((p) => !p);
const handleOptionClick = (opt: DropdownOption) => {
setSelectedOption(opt.label);
onSelect(opt.id);
setIsOpen(false);
};
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const term = e.target.value;
setSearchTerm(term);
setFilteredOptions(options.filter(o => o.label.toLowerCase().includes(term.toLowerCase())));
};
return (
<div className="regularDropdown-container" ref={dropdownRef} onPointerLeave={() => setIsOpen(false)}>
<div className="dropdown-header flex-sb" onClick={toggleDropdown}>
<div className="key">{selectedOption || header}</div>
<div className="icon"></div>
</div>
{isOpen && createPortal(
<div className="dropdown-options" style={{ position: "absolute", top: position.top, left: position.left, width: position.width, zIndex: 9999 }}>
{search && (
<div className="dropdown-search">
<input type="text" placeholder="Search..." value={searchTerm} onChange={handleSearchChange} />
</div>
)}
{filteredOptions.length > 0 ? (
filteredOptions.map((opt) => (
<div className="option" key={opt.id} onClick={() => handleOptionClick(opt)} title={opt.label}>
{opt.label}
</div>
))
) : (
<div className="no-options">No options found</div>
)}
</div>,
document.body
)}
</div>
);
}
export default RegularDropDownID