Dwinzo_dev/app/src/components/layout/sidebarRight/visualization/design/Design.tsx

329 lines
9.9 KiB
TypeScript

import { useState, useEffect, useRef } from "react";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import { WalletIcon } from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard";
interface Widget {
id: string;
type?: string;
panel: "top" | "bottom" | "left" | "right";
title?: string;
header?: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: string;
className?: string;
data?: {
labels: string[];
datasets: {
data: number[];
backgroundColor: string;
borderColor: string;
borderWidth: number;
}[];
};
value?: string;
per?: string;
}
interface ChartElement {
tagName: string;
className: string;
textContent: string;
selector: string;
}
const Design = () => {
const [selectedFont, setSelectedFont] = useState("drop down");
const [selectedSize, setSelectedSize] = useState("drop down");
const [selectedWeight, setSelectedWeight] = useState("drop down");
const [elementColor, setElementColor] = useState("#6f42c1");
const [showColorPicker, setShowColorPicker] = useState(false);
const [chartElements, setChartElements] = useState<ChartElement[]>([]);
const [selectedElementToStyle, setSelectedElementToStyle] = useState<
string | null
>(null);
const [nameInput, setNameInput] = useState("");
const chartRef = useRef<HTMLDivElement>(null);
const { selectedChartId, setSelectedChartId, widgets, setWidgets } =
useWidgetStore();
// Initialize name input and extract elements when selectedChartId changes
useEffect(() => {
setNameInput(selectedChartId?.header || selectedChartId?.title || "");
if (!chartRef.current) return;
const timer = setTimeout(() => {
const chartContainer = chartRef.current;
if (!chartContainer) return;
const elements = Array.from(chartContainer.querySelectorAll("*"))
.filter((el) => {
const tagName = el.tagName.toLowerCase();
return !["script", "style", "meta", "link", "head"].includes(tagName);
})
.map((el, index) => {
const tagName = el.tagName.toLowerCase();
const className =
typeof el.className === "string" ? el.className : "";
const textContent = el.textContent?.trim() || "";
let selector = tagName;
if (className && typeof className === "string") {
const classList = className
.split(/\s+/)
.filter((c) => c.length > 0);
if (classList.length > 0) {
selector += "." + classList.join(".");
}
}
if (!className || className.trim() === "") {
const parent = el.parentElement;
if (parent) {
const siblings = Array.from(parent.children).filter(
(child) => child.tagName.toLowerCase() === tagName
);
const position = siblings.indexOf(el) + 1;
selector += `:nth-of-type(${position})`;
}
}
return {
tagName,
className,
textContent,
selector,
};
});
setChartElements(elements);
}, 300);
return () => clearTimeout(timer);
}, [selectedChartId]);
const applyStyles = () => {
if (!selectedElementToStyle || !chartRef.current) return;
const element = chartRef.current.querySelector(selectedElementToStyle);
if (!element) return;
const elementToStyle = element as HTMLElement;
if (selectedFont !== "drop down") {
elementToStyle.style.fontFamily = selectedFont;
}
if (selectedSize !== "drop down") {
elementToStyle.style.fontSize = selectedSize;
}
if (selectedWeight !== "drop down") {
elementToStyle.style.fontWeight = selectedWeight.toLowerCase();
}
if (elementColor) {
elementToStyle.style.color = elementColor;
}
};
useEffect(() => {
applyStyles();
}, [
selectedFont,
selectedSize,
selectedWeight,
elementColor,
selectedElementToStyle,
]);
const handleUpdateWidget = (updatedProperties: Partial<Widget>) => {
if (!selectedChartId) return;
const updatedChartId = {
...selectedChartId,
...updatedProperties,
};
setSelectedChartId(updatedChartId);
const updatedWidgets = widgets.map((widget) =>
widget.id === selectedChartId.id
? { ...widget, ...updatedProperties }
: widget
);
setWidgets(updatedWidgets);
};
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newName = e.target.value;
setNameInput(newName);
if (selectedChartId?.title) {
handleUpdateWidget({ title: newName });
} else if (selectedChartId?.header) {
handleUpdateWidget({ header: newName });
}
};
const defaultChartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
const elementOptions = chartElements.map((el) => {
let displayName = el.tagName;
if (el.className) displayName += `.${el.className}`;
if (el.textContent)
displayName += ` (${el.textContent.substring(0, 20)}${
el.textContent.length > 20 ? "..." : ""
})`;
return {
display: displayName,
value: el.selector,
};
});
return (
<div className="design">
<div className="selectedWidget">
{selectedChartId?.title || selectedChartId?.header || "Widget 1"}
</div>
<div className="reviewChart" ref={chartRef}>
{selectedChartId?.title ? (
<ChartComponent
type={selectedChartId.type || "bar"}
title={selectedChartId.title}
data={selectedChartId.data || defaultChartData}
/>
) : (
<SimpleCard
header={selectedChartId?.header || ""}
icon={WalletIcon}
value={selectedChartId?.value || ""}
per={selectedChartId?.per || ""}
/>
)}
</div>
<div className="optionsContainer">
<div className="option">
<span>Element to Style</span>
<RegularDropDown
header={selectedElementToStyle || "Select Element"}
options={
elementOptions.length > 0
? elementOptions.map((opt) => opt.display)
: ["No elements found"]
}
onSelect={(value) => {
const selected = elementOptions.find(
(opt) => opt.display === value
);
setSelectedElementToStyle(selected?.value || null);
}}
/>
</div>
<div className="option">
<span>Name</span>
<input
type="text"
value={nameInput}
onChange={handleNameChange}
placeholder="Enter name"
/>
</div>
{selectedChartId?.title && (
<div className="option">
<span>Chart Type</span>
<RegularDropDown
header={selectedChartId?.type || "Select Type"}
options={["bar", "line", "pie", "doughnut", "radar", "polarArea"]}
onSelect={(value) => {
handleUpdateWidget({ type: value });
}}
/>
</div>
)}
<div className="option">
<span>Font Family</span>
<RegularDropDown
header={selectedChartId?.fontFamily || "Select Font"}
options={["Arial", "Roboto", "Sans-serif"]}
onSelect={(value) => setSelectedFont(value)}
/>
</div>
<div className="option">
<span>Size</span>
<RegularDropDown
header={selectedChartId?.fontSize || "Select Size"}
options={["12px", "14px", "16px", "18px"]}
onSelect={(value) => setSelectedSize(value)}
/>
</div>
<div className="option">
<span>Weight</span>
<RegularDropDown
header={selectedChartId?.fontWeight || "Select Weight"}
options={["Light", "Regular", "Bold"]}
onSelect={(value) => setSelectedWeight(value)}
/>
</div>
<div className="option">
<div
className="header"
onClick={() => setShowColorPicker((prev) => !prev)}
>
<span>Element Color</span>
<div className="icon"></div>
</div>
{showColorPicker && (
<div className="colorDisplayer">
<input
type="color"
value={elementColor}
onChange={(e) => {
setElementColor(e.target.value);
if (selectedChartId?.data) {
handleUpdateWidget({
data: {
...selectedChartId.data,
datasets: [
{
...selectedChartId.data.datasets[0],
backgroundColor: e.target.value,
},
],
},
});
}
}}
/>
<span style={{ marginLeft: "10px" }}>{elementColor}</span>
</div>
)}
</div>
</div>
</div>
);
};
export default Design;