329 lines
9.9 KiB
TypeScript
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;
|