updated real time viz ui
This commit is contained in:
parent
2da7011462
commit
92676cd12c
|
@ -58,8 +58,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.child {
|
||||
padding-left: 13%;
|
||||
// padding-left: 13%;
|
||||
width: 100%;
|
||||
gap: 6px;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,50 @@
|
|||
overflow-y: auto;
|
||||
padding-bottom: 40px;
|
||||
|
||||
.progressBar {
|
||||
height: auto !important;
|
||||
padding: 12px 10px 41px 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.stock {
|
||||
padding: 13px 5px;
|
||||
background-color: #E0DFFF80;
|
||||
border-radius: 6.33px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.stock-item {
|
||||
.stockValues {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
|
||||
.value {
|
||||
color: #4a90e2;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.stock-description {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
@ -63,6 +107,15 @@
|
|||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #4a90e2;
|
||||
color: #FCFDFD !important;
|
||||
}
|
||||
}
|
||||
|
||||
.zoon-wrapper.bottom {
|
||||
bottom: 210px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import asset1 from "../../assets/images/tempimages/asset1.png";
|
||||
import asset2 from "../../assets/images/tempimages/asset2.png";
|
||||
import asset3 from "../../assets/images/tempimages/asset3.png";
|
||||
|
@ -39,33 +39,41 @@ const SideBar: React.FC<SideBarProps> = ({ header, defaultActive }) => {
|
|||
const [filteredData, setFilteredData] = useState<FilteredAssets[]>([]);
|
||||
const [inputData, setInputData] = useState<string>("");
|
||||
|
||||
const [assets, setAssets] = useState<FilteredAssets[]>([]);
|
||||
const assets: FilteredAssets[] = useMemo(
|
||||
() => [
|
||||
{ filename: "Asset 1", thumbnail: asset1, modelfileID: "1" },
|
||||
{ filename: "Asset 2", thumbnail: asset2, modelfileID: "2" },
|
||||
{ filename: "Asset 3", thumbnail: asset3, modelfileID: "3" },
|
||||
{ filename: "Asset 4", thumbnail: asset4, modelfileID: "4" },
|
||||
{ filename: "Asset 5", thumbnail: asset5, modelfileID: "5" },
|
||||
{ filename: "Asset 6", thumbnail: asset6, modelfileID: "6" },
|
||||
{ filename: "Asset 7", thumbnail: asset7, modelfileID: "7" },
|
||||
{ filename: "Asset 8", thumbnail: asset8, modelfileID: "8" },
|
||||
{ filename: "Asset 9", thumbnail: asset9, modelfileID: "9" },
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
// const assets: FilteredAssets[] = [
|
||||
// { name: "Asset 1", img: asset1 },
|
||||
// { name: "Asset 2", img: asset2 },
|
||||
// { name: "Asset 3", img: asset3 },
|
||||
// { name: "Asset 4", img: asset4 },
|
||||
// { name: "Asset 5", img: asset5 },
|
||||
// { name: "Asset 6", img: asset6 },
|
||||
// { name: "Asset 7", img: asset7 },
|
||||
// { name: "Asset 8", img: asset8 },
|
||||
// { name: "Asset 9", img: asset9 },
|
||||
// ];
|
||||
// Memoized Input Change Handler
|
||||
const handleInputChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
setInputData(value);
|
||||
|
||||
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
setInputData(value);
|
||||
|
||||
const filtered = assets.filter((asset) =>
|
||||
asset.filename.toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
setFilteredData(filtered);
|
||||
};
|
||||
const filtered = assets.filter((asset) =>
|
||||
asset.filename.toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
setFilteredData(filtered);
|
||||
},
|
||||
[assets]
|
||||
);
|
||||
|
||||
// Update Active State Only When Necessary
|
||||
useEffect(() => {
|
||||
setActive(defaultActive || header[0]);
|
||||
}, [header, defaultActive]);
|
||||
if (defaultActive && defaultActive !== active) {
|
||||
setActive(defaultActive);
|
||||
}
|
||||
}, [defaultActive]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -86,6 +94,7 @@ const SideBar: React.FC<SideBarProps> = ({ header, defaultActive }) => {
|
|||
))}
|
||||
</div>
|
||||
|
||||
{/* Render Components Based on Active Header */}
|
||||
{active === "Outline" && <Outline />}
|
||||
{active === "Asset library" && <AssetLibrary />}
|
||||
{active === "Overview" && <Overview />}
|
||||
|
@ -96,4 +105,4 @@ const SideBar: React.FC<SideBarProps> = ({ header, defaultActive }) => {
|
|||
);
|
||||
};
|
||||
|
||||
export default SideBar;
|
||||
export default React.memo(SideBar);
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
import React, { useEffect, useRef, useMemo } from "react";
|
||||
import { Chart, ChartType } from "chart.js/auto";
|
||||
import { useThemeStore, useWidgetStore } from "../../../../store/store";
|
||||
import { useThemeStore } from "../../../../store/store";
|
||||
|
||||
// Define Props Interface
|
||||
interface ChartComponentProps {
|
||||
type: ChartType;
|
||||
title: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: "Light" | "Regular" | "Bold"; // Explicitly typing fontWeight
|
||||
type: any; // Type of chart (e.g., "bar", "line", etc.)
|
||||
title: string; // Title of the chart
|
||||
fontFamily?: string; // Optional font family for the chart title
|
||||
fontSize?: string; // Optional font size for the chart title
|
||||
fontWeight?: "Light" | "Regular" | "Bold"; // Optional font weight for the chart title
|
||||
data: {
|
||||
labels: string[]; // Labels for the x-axis
|
||||
datasets: {
|
||||
data: number[]; // Data points for the chart
|
||||
backgroundColor: string; // Background color for the chart
|
||||
borderColor: string; // Border color for the chart
|
||||
borderWidth: number; // Border width for the chart
|
||||
}[];
|
||||
}; // Data for the chart
|
||||
}
|
||||
|
||||
const ChartComponent = ({
|
||||
|
@ -16,11 +26,12 @@ const ChartComponent = ({
|
|||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "Regular", // Default to "Regular"
|
||||
data: propsData,
|
||||
}: ChartComponentProps) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const { themeColor } = useThemeStore();
|
||||
|
||||
// Memoize theme colors to prevent unnecessary recalculations
|
||||
// Memoize Theme Colors to Prevent Unnecessary Recalculations
|
||||
const buttonActionColor = useMemo(
|
||||
() => themeColor[0] || "#5c87df",
|
||||
[themeColor]
|
||||
|
@ -30,7 +41,7 @@ const ChartComponent = ({
|
|||
[themeColor]
|
||||
);
|
||||
|
||||
// Memoize font weight mapping
|
||||
// Memoize Font Weight Mapping
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
|
@ -40,19 +51,19 @@ const ChartComponent = ({
|
|||
[]
|
||||
);
|
||||
|
||||
// Parse and memoize fontSize
|
||||
// Parse and Memoize Font Size
|
||||
const fontSizeValue = useMemo(
|
||||
() => (fontSize ? parseInt(fontSize) : 12),
|
||||
[fontSize]
|
||||
);
|
||||
|
||||
// Determine and memoize font weight
|
||||
// Determine and Memoize Font Weight
|
||||
const fontWeightValue = useMemo(
|
||||
() => chartFontWeightMap[fontWeight], // No need for '|| "normal"' since fontWeight is guaranteed to be valid
|
||||
() => chartFontWeightMap[fontWeight],
|
||||
[fontWeight, chartFontWeightMap]
|
||||
);
|
||||
|
||||
// Memoize chart font style
|
||||
// Memoize Chart Font Style
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
|
@ -62,23 +73,10 @@ const ChartComponent = ({
|
|||
[fontFamily, fontSizeValue, fontWeightValue]
|
||||
);
|
||||
|
||||
// Memoize chart data
|
||||
const data = useMemo(
|
||||
() => ({
|
||||
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
|
||||
datasets: [
|
||||
{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: buttonActionColor,
|
||||
borderColor: buttonAbortColor,
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
}),
|
||||
[buttonActionColor, buttonAbortColor]
|
||||
);
|
||||
// Memoize Chart Data
|
||||
const data = useMemo(() => propsData, [propsData]);
|
||||
|
||||
// Memoize chart options
|
||||
// Memoize Chart Options
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
responsive: true,
|
||||
|
@ -97,6 +95,7 @@ const ChartComponent = ({
|
|||
[title, chartFontStyle]
|
||||
);
|
||||
|
||||
// Initialize Chart on Component Mount
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return;
|
||||
|
||||
|
@ -105,10 +104,21 @@ const ChartComponent = ({
|
|||
|
||||
const chart = new Chart(ctx, { type, data, options });
|
||||
|
||||
// Cleanup: Destroy the chart instance when the component unmounts
|
||||
return () => chart.destroy();
|
||||
}, [type, data, options]); // Only recreate chart when these essentials change
|
||||
}, [type, data, options]); // Only recreate the chart when these dependencies change
|
||||
|
||||
return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
|
||||
};
|
||||
|
||||
export default React.memo(ChartComponent);
|
||||
export default React.memo(ChartComponent, (prevProps, nextProps) => {
|
||||
// Custom comparison function to prevent unnecessary re-renders
|
||||
return (
|
||||
prevProps.type === nextProps.type &&
|
||||
prevProps.title === nextProps.title &&
|
||||
prevProps.fontFamily === nextProps.fontFamily &&
|
||||
prevProps.fontSize === nextProps.fontSize &&
|
||||
prevProps.fontWeight === nextProps.fontWeight &&
|
||||
JSON.stringify(prevProps.data) === JSON.stringify(nextProps.data)
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import RegularDropDown from "../../inputs/regularDropDown";
|
||||
import { LinkIcon, RemoveIcon } from "../../../../assets/images/svgExports";
|
||||
import { useWidgetStore } from "../../../../store/store";
|
||||
|
||||
interface Child {
|
||||
id: number;
|
||||
|
@ -14,132 +15,126 @@ interface Group {
|
|||
}
|
||||
|
||||
const Data = () => {
|
||||
const { selectedChartId } = useWidgetStore();
|
||||
const [selectedWidget] = useState("Widget 1");
|
||||
const [groups, setGroups] = useState<Group[]>([
|
||||
{
|
||||
id: 1,
|
||||
easing: "Connecter 1",
|
||||
children: [
|
||||
{ id: 1, easing: "Linear" },
|
||||
{ id: 2, easing: "Ease Out" },
|
||||
{ id: 3, easing: "Linear" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const handleLinkClick = (childId: number) => {
|
||||
setGroups((currentGroups) => {
|
||||
let sourceGroup: Group | undefined;
|
||||
let sourceChild: Child | undefined;
|
||||
// State to store groups for all widgets (using Widget.id as keys)
|
||||
const [chartDataGroups, setChartDataGroups] = useState<
|
||||
Record<string, Group[]>
|
||||
>({});
|
||||
|
||||
// Find the source group and child
|
||||
for (const group of currentGroups) {
|
||||
sourceChild = group.children.find((c) => c.id === childId);
|
||||
if (sourceChild) {
|
||||
sourceGroup = group;
|
||||
break;
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log("Selected Chart ID:", selectedChartId);
|
||||
|
||||
if (!sourceGroup || !sourceChild) return currentGroups;
|
||||
// Initialize data groups for the newly selected widget if it doesn't exist
|
||||
if (selectedChartId && !chartDataGroups[selectedChartId.id]) {
|
||||
setChartDataGroups((prev) => ({
|
||||
...prev,
|
||||
[selectedChartId.id]: [
|
||||
{
|
||||
id: Date.now(),
|
||||
easing: "Connecter 1",
|
||||
children: [
|
||||
{ id: Date.now(), easing: "Linear" },
|
||||
{ id: Date.now() + 1, easing: "Ease Out" },
|
||||
{ id: Date.now() + 2, easing: "Linear" },
|
||||
],
|
||||
},
|
||||
],
|
||||
}));
|
||||
}
|
||||
}, [selectedChartId]);
|
||||
|
||||
// Remove child from source group
|
||||
const updatedGroups = currentGroups
|
||||
.map((group) => ({
|
||||
...group,
|
||||
children: group.children.filter((c) => c.id !== childId),
|
||||
}))
|
||||
.filter((group) => group.children.length > 0);
|
||||
// Handle linking children between groups
|
||||
const handleLinkClick = (childId: number) => {};
|
||||
|
||||
// Find or create target group
|
||||
const targetGroup = updatedGroups.find(
|
||||
(g) => g.id !== sourceGroup!.id && g.easing === sourceGroup!.easing
|
||||
);
|
||||
// Remove a child from a group
|
||||
const removeChild = (groupId: number, childId: number) => {
|
||||
setChartDataGroups((currentGroups) => {
|
||||
if (!selectedChartId) return currentGroups;
|
||||
|
||||
if (targetGroup) {
|
||||
targetGroup.children.push(sourceChild);
|
||||
} else {
|
||||
updatedGroups.push({
|
||||
id: Date.now(),
|
||||
easing: sourceGroup.easing,
|
||||
children: [sourceChild],
|
||||
});
|
||||
}
|
||||
const currentChartData = currentGroups[selectedChartId.id] || [];
|
||||
|
||||
return updatedGroups;
|
||||
return {
|
||||
...currentGroups,
|
||||
[selectedChartId.id]: currentChartData.map((group) =>
|
||||
group.id === groupId
|
||||
? {
|
||||
...group,
|
||||
children: group.children.map((child) =>
|
||||
child.id === childId ? { ...child, easing: "Linear" } : child
|
||||
),
|
||||
}
|
||||
: group
|
||||
),
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const removeChild = (groupId: number, childId: number) => {
|
||||
setGroups((currentGroups) =>
|
||||
currentGroups.map((group) => {
|
||||
if (group.id === groupId) {
|
||||
return {
|
||||
...group,
|
||||
children: group.children.map((child) =>
|
||||
child.id === childId ? { ...child, easing: "Linear" } : child
|
||||
),
|
||||
};
|
||||
}
|
||||
return group;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
console.log("selectedChartId: ", selectedChartId?.title);
|
||||
return (
|
||||
<div className="dataSideBar">
|
||||
<div className="sideBarHeader">{selectedWidget}</div>
|
||||
{groups.map((group) => (
|
||||
<div key={group.id} className="selectedMain-container">
|
||||
<div className="selectedMain">
|
||||
<span className="bulletPoint">•</span>
|
||||
<main>Data from</main>
|
||||
<RegularDropDown
|
||||
header={group.easing}
|
||||
options={[
|
||||
"Connecter 1",
|
||||
"Connecter 2",
|
||||
"Connecter 3",
|
||||
"Connecter 4",
|
||||
]}
|
||||
onSelect={(easing) => {
|
||||
setGroups(
|
||||
groups.map((g) => (g.id === group.id ? { ...g, easing } : g))
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{group.children.map((child) => (
|
||||
<div key={child.id} className="selectedMain child">
|
||||
<main>Input {child.id}</main>
|
||||
{/* Pass the current easing as the header */}
|
||||
{selectedChartId?.title && (
|
||||
<div className="sideBarHeader">{selectedChartId?.title}</div>
|
||||
)}
|
||||
{selectedChartId &&
|
||||
chartDataGroups[selectedChartId.id]?.map((group) => (
|
||||
<div key={group.id} className="selectedMain-container">
|
||||
{/* <div className="selectedMain">
|
||||
<span className="bulletPoint">•</span>
|
||||
<main>Data from</main>
|
||||
<RegularDropDown
|
||||
header={child.easing}
|
||||
options={["Linear", "Ease In", "Ease Out", "Ease In-Out"]}
|
||||
header={group.easing}
|
||||
options={[
|
||||
"Connecter 1",
|
||||
"Connecter 2",
|
||||
"Connecter 3",
|
||||
"Connecter 4",
|
||||
]}
|
||||
onSelect={(easing) => {
|
||||
setGroups(
|
||||
groups.map((g) => ({
|
||||
...g,
|
||||
children: g.children.map((c) =>
|
||||
c.id === child.id ? { ...c, easing } : c
|
||||
),
|
||||
}))
|
||||
);
|
||||
setChartDataGroups((prev) => ({
|
||||
...prev,
|
||||
[selectedChartId.id]: prev[selectedChartId.id].map((g) =>
|
||||
g.id === group.id ? { ...g, easing } : g
|
||||
),
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<div className="icon" onClick={() => handleLinkClick(child.id)}>
|
||||
<LinkIcon />
|
||||
</div> */}
|
||||
{/* Render children only if there is a selected chart */}
|
||||
{group.children.map((child) => (
|
||||
<div key={child.id} className="selectedMain child">
|
||||
<main>Input</main>
|
||||
<RegularDropDown
|
||||
header={child.easing}
|
||||
options={["Linear", "Ease In", "Ease Out", "Ease In-Out"]}
|
||||
onSelect={(easing) => {
|
||||
setChartDataGroups((prev) => ({
|
||||
...prev,
|
||||
[selectedChartId.id]: prev[selectedChartId.id].map(
|
||||
(g) => ({
|
||||
...g,
|
||||
children: g.children.map((c) =>
|
||||
c.id === child.id ? { ...c, easing } : c
|
||||
),
|
||||
})
|
||||
),
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<div className="icon" onClick={() => handleLinkClick(child.id)}>
|
||||
<LinkIcon />
|
||||
</div>
|
||||
<div
|
||||
className="icon"
|
||||
onClick={() => removeChild(group.id, child.id)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="icon"
|
||||
onClick={() => removeChild(group.id, child.id)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
<div className="infoBox">
|
||||
<span className="infoIcon">i</span>
|
||||
<p>
|
||||
|
|
|
@ -1,22 +1,28 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import RegularDropDown from "../../inputs/regularDropDown";
|
||||
import { useWidgetStore } from "../../../../store/store";
|
||||
import styles from "./Design.module.scss"; // Import SCSS file
|
||||
import ChartComponent from "./chartComponent";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
// Define the Widget interface
|
||||
// Define Props Interface
|
||||
interface DesignProps {}
|
||||
interface Widget {
|
||||
id: string;
|
||||
type: string; // Change this if the type is more specific
|
||||
panel: Side; // You should define or import 'Side' type if not already defined
|
||||
type: string; // Chart type (e.g., "bar", "line")
|
||||
panel: "top" | "bottom" | "left" | "right"; // Panel location
|
||||
title: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: string;
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: {
|
||||
data: number[];
|
||||
backgroundColor: string;
|
||||
borderColor: string;
|
||||
borderWidth: number;
|
||||
}[];
|
||||
}; // Data for the chart
|
||||
}
|
||||
|
||||
const Design = () => {
|
||||
const [selectedName, setSelectedName] = useState("drop down");
|
||||
const [selectedElement, setSelectedElement] = useState("drop down");
|
||||
|
@ -24,66 +30,70 @@ const Design = () => {
|
|||
const [selectedSize, setSelectedSize] = useState("drop down");
|
||||
const [selectedWeight, setSelectedWeight] = useState("drop down");
|
||||
|
||||
// Zustand Store Hooks
|
||||
const { selectedChartId, setSelectedChartId, widgets, setWidgets } =
|
||||
useWidgetStore(); // Get selected chart ID and list of widgets
|
||||
useWidgetStore();
|
||||
|
||||
// Find the selected widget based on `selectedChartId`
|
||||
const selectedWidget = selectedChartId
|
||||
? widgets.find((widget) => widget.id === selectedChartId.id)
|
||||
: null;
|
||||
// Log Selected Chart ID for Debugging
|
||||
|
||||
// Function to update the selected widget
|
||||
// Handle Widget Updates
|
||||
const handleUpdateWidget = (updatedProperties: Partial<Widget>) => {
|
||||
if (!selectedWidget) {
|
||||
return;
|
||||
}
|
||||
if (!selectedChartId) return;
|
||||
|
||||
// Update the selectedChartId
|
||||
const updatedChartId = {
|
||||
...selectedChartId,
|
||||
...updatedProperties,
|
||||
};
|
||||
setSelectedChartId(updatedChartId);
|
||||
|
||||
// Update the widgets array
|
||||
const updatedWidgets = widgets.map((widget) =>
|
||||
widget.id === selectedWidget.id
|
||||
? { ...widget, ...updatedProperties } // Merge existing widget with updated properties
|
||||
widget.id === selectedChartId.id
|
||||
? { ...widget, ...updatedProperties }
|
||||
: widget
|
||||
);
|
||||
|
||||
// Update the global state with the new widgets array
|
||||
setWidgets(updatedWidgets);
|
||||
|
||||
// Update `selectedChartId` to reflect the changes
|
||||
if (selectedChartId) {
|
||||
const updatedChartId = {
|
||||
...selectedChartId,
|
||||
...updatedProperties, // Merge updated properties into `selectedChartId`
|
||||
};
|
||||
setSelectedChartId(updatedChartId);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Log the current state of widgets for debugging
|
||||
}, [widgets]);
|
||||
// Default Chart Data
|
||||
const defaultChartData = {
|
||||
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
|
||||
datasets: [
|
||||
{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: "#5c87df",
|
||||
borderColor: "#ffffff",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="design">
|
||||
{/* Display the selected widget's title */}
|
||||
{/* Title of the Selected Widget */}
|
||||
<div className="selectedWidget">
|
||||
{selectedWidget ? selectedWidget.title : "Widget 1"}
|
||||
</div>
|
||||
<div className="reviewChart">
|
||||
{/* Pass selectedWidget properties to ChartComponent */}
|
||||
{selectedWidget && (
|
||||
<ChartComponent
|
||||
type={selectedWidget.type}
|
||||
title={selectedWidget.title}
|
||||
/>
|
||||
)}
|
||||
{selectedChartId?.title || "Widget 1"}
|
||||
</div>
|
||||
|
||||
{/* Design Options */}
|
||||
{/* Chart Component */}
|
||||
<div className="reviewChart">
|
||||
{/* {selectedChartId && (
|
||||
<ChartComponent
|
||||
type={selectedChartId.type}
|
||||
title={selectedChartId.title}
|
||||
data={selectedChartId.data || defaultChartData} // Use widget data or default
|
||||
/>
|
||||
)} */}
|
||||
</div>
|
||||
|
||||
{/* Options Container */}
|
||||
<div className="optionsContainer">
|
||||
{/* Name Dropdown */}
|
||||
<div className="option">
|
||||
<span>Name</span>
|
||||
<RegularDropDown
|
||||
header={selectedWidget ? selectedWidget.title : "Select Name"}
|
||||
header={selectedChartId?.title || "Select Name"}
|
||||
options={["Option 1", "Option 2", "Option 3"]}
|
||||
onSelect={(value) => {
|
||||
setSelectedName(value);
|
||||
|
@ -92,22 +102,24 @@ const Design = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* Element Dropdown */}
|
||||
<div className="option">
|
||||
<span>Element</span>
|
||||
<RegularDropDown
|
||||
header={selectedElement}
|
||||
options={["Option 1", "Option 2", "Option 3"]}
|
||||
header={selectedChartId?.type || "Select Element"}
|
||||
options={["bar", "line", "pie", "doughnut", "radar", "polarArea"]} // Valid chart types
|
||||
onSelect={(value) => {
|
||||
setSelectedElement(value);
|
||||
handleUpdateWidget({ type: value }); // Update element type
|
||||
handleUpdateWidget({ type: value });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Font Family Dropdown */}
|
||||
<div className="option">
|
||||
<span>Font Family</span>
|
||||
<RegularDropDown
|
||||
header={selectedFont}
|
||||
header={selectedChartId?.fontFamily || "Select Font"}
|
||||
options={["Arial", "Roboto", "Sans-serif"]}
|
||||
onSelect={(value) => {
|
||||
setSelectedFont(value);
|
||||
|
@ -116,10 +128,11 @@ const Design = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* Size Dropdown */}
|
||||
<div className="option">
|
||||
<span>Size</span>
|
||||
<RegularDropDown
|
||||
header={selectedSize}
|
||||
header={selectedChartId?.fontSize || "Select Size"}
|
||||
options={["12px", "14px", "16px", "18px"]}
|
||||
onSelect={(value) => {
|
||||
setSelectedSize(value);
|
||||
|
@ -128,10 +141,11 @@ const Design = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* Weight Dropdown */}
|
||||
<div className="option">
|
||||
<span>Weight</span>
|
||||
<RegularDropDown
|
||||
header={selectedWeight}
|
||||
header={selectedChartId?.fontWeight || "Select Weight"}
|
||||
options={["Light", "Regular", "Bold"]}
|
||||
onSelect={(value) => {
|
||||
setSelectedWeight(value);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect, useRef } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useThemeStore, useWidgetStore } from "../../../../store/store";
|
||||
import { ChartType } from "chart.js/auto";
|
||||
import ChartComponent from "./chartComponent";
|
||||
|
@ -7,7 +7,6 @@ import {
|
|||
SearchIcon,
|
||||
ThemeIcon,
|
||||
} from "../../../../assets/images/svgExports";
|
||||
import RegularDropDown from "../../inputs/regularDropDown";
|
||||
|
||||
const Widgets = () => {
|
||||
const chartTypes: ChartType[] = [
|
||||
|
@ -18,63 +17,14 @@ const Widgets = () => {
|
|||
"radar",
|
||||
"polarArea",
|
||||
];
|
||||
|
||||
const { setDraggedAsset } = useWidgetStore((state) => state);
|
||||
const [selectedValue, setSelectedValue] = useState<string | null>(null);
|
||||
|
||||
const [viewMode, setViewMode] = useState<"2D" | "3D" | "Floating">("2D");
|
||||
|
||||
const switchView = (mode: "2D" | "3D" | "Floating") => {
|
||||
setViewMode(mode);
|
||||
};
|
||||
|
||||
const { themeColor, setThemeColor } = useThemeStore();
|
||||
const [theme, setTheme] = useState<boolean>(false); // Default is false
|
||||
const [activePresetIndex, setActivePresetIndex] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
const [customColor, setCustomColor] = useState<string>("#000000");
|
||||
|
||||
const themeContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const preset = [
|
||||
["#6F42C1", "#EEEEFE", "#B392F0"],
|
||||
["#F8CB47", "#F79002", "#F73F65"],
|
||||
["#FDA4B8", "#FF6A90", "#B91348"],
|
||||
["#D1BCF6", "#987BEB", "#6443C9"],
|
||||
["#FDC64B", "#EF9407", "#B54300"],
|
||||
["#69E9AB", "#0BB96E", "#087348"],
|
||||
["#85ADFC", "#246FFE", "#0050EB"],
|
||||
["#F570C7", "#27CEF7", "#FFAD1A"],
|
||||
["#6572F2", "#EE42B7", "#12B56E"],
|
||||
["#10BA68", "#FDB022", "#EA48B5"],
|
||||
["#10BA68", "#FDB022", "#EA48B5"],
|
||||
["#6F42C1", "#CEB2F6", "#EA48B5"],
|
||||
];
|
||||
|
||||
// Close theme container on outside click
|
||||
useEffect(() => {
|
||||
const themBtn = document.querySelector(".theme-switch");
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
// Check if the click is outside the theme container or theme button
|
||||
if (
|
||||
themeContainerRef.current &&
|
||||
!themeContainerRef.current.contains(event.target as Node) &&
|
||||
!themBtn?.contains(event.target as Node) // Ensure that the click is not inside the theme button
|
||||
) {
|
||||
setTheme(false); // Close the theme container when clicking outside
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside); // Cleanup event listener on unmount
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {}, [theme]);
|
||||
|
||||
return (
|
||||
<div className="widgets-layout">
|
||||
{/* Search Container */}
|
||||
|
@ -87,7 +37,6 @@ const Widgets = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Options and Theme Management */}
|
||||
<div className="ui-wrapper">
|
||||
<div className="options">
|
||||
|
@ -110,68 +59,24 @@ const Widgets = () => {
|
|||
Floating
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Theme Switcher */}
|
||||
<div
|
||||
className="theme-switch"
|
||||
onClick={() => setTheme(!theme)} // Toggle the theme container
|
||||
>
|
||||
<button className="theme-button">
|
||||
<ThemeIcon />
|
||||
<DropDownIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Theme Presets */}
|
||||
{theme && (
|
||||
<div className="theme-container" ref={themeContainerRef}>
|
||||
<h2>Presets</h2>
|
||||
<div className="theme-preset-wrapper">
|
||||
{preset.map((colors, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`theme-preset ${
|
||||
activePresetIndex === index ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setThemeColor(colors);
|
||||
setActivePresetIndex(index);
|
||||
}}
|
||||
>
|
||||
{colors.map((color, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="color"
|
||||
style={{ background: color }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="custom-color">
|
||||
<h3>Custom Color</h3>
|
||||
<div className="color-displayer">
|
||||
<input
|
||||
type="color"
|
||||
value={customColor}
|
||||
onChange={(e) => {
|
||||
const newColor = e.target.value;
|
||||
setCustomColor(newColor);
|
||||
setThemeColor([newColor, newColor, newColor]);
|
||||
}}
|
||||
/>
|
||||
{customColor}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chart Widgets */}
|
||||
<div className="chart-container">
|
||||
{chartTypes.map((type, index) => {
|
||||
const widgetTitle = `Widget ${index + 1}`;
|
||||
const sampleData = {
|
||||
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
|
||||
datasets: [
|
||||
{
|
||||
data: [65, 59, 80, 81, 56, 55, 40],
|
||||
backgroundColor: "#5c87df",
|
||||
borderColor: "#ffffff",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
|
@ -182,17 +87,110 @@ const Widgets = () => {
|
|||
type,
|
||||
id: `widget-${index + 1}`,
|
||||
title: widgetTitle,
|
||||
panel: "top", // Default panel assignment
|
||||
data: sampleData, // Include data in the dragged asset
|
||||
});
|
||||
}}
|
||||
onDragEnd={() => setDraggedAsset(null)}
|
||||
>
|
||||
<ChartComponent type={type} title={widgetTitle} />
|
||||
<ChartComponent
|
||||
type={type}
|
||||
title={widgetTitle}
|
||||
data={sampleData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div
|
||||
className="chart progressBar"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
setDraggedAsset({
|
||||
type: "progress", // New widget type
|
||||
id: `widget-7`,
|
||||
title: "Widget 7",
|
||||
panel: "top",
|
||||
data: {
|
||||
stocks: [
|
||||
{
|
||||
key: "units",
|
||||
value: 1000,
|
||||
description: "Initial stock",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}}
|
||||
onDragEnd={() => setDraggedAsset(null)}
|
||||
>
|
||||
{" "}
|
||||
<div className="header">Widget 7</div>
|
||||
<div className="stock">
|
||||
<span className="stock-item">
|
||||
<span className="stockValues">
|
||||
<div className="key">units</div>
|
||||
<div className="value">1000</div>
|
||||
</span>
|
||||
<div className="stock-description">Initial stock</div>
|
||||
</span>
|
||||
<div className="icon">Icon</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="chart progressBar"
|
||||
draggable
|
||||
onDragStart={(e) => {
|
||||
setDraggedAsset({
|
||||
type: "progress",
|
||||
id: `widget-8`,
|
||||
title: "Widget 8",
|
||||
panel: "top",
|
||||
data: {
|
||||
stocks: [
|
||||
{
|
||||
key: "units",
|
||||
value: 1000,
|
||||
description: "Initial stock",
|
||||
},
|
||||
{
|
||||
key: "units",
|
||||
value: 500,
|
||||
description: "Additional stock",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}}
|
||||
onDragEnd={() => setDraggedAsset(null)}
|
||||
>
|
||||
{" "}
|
||||
<div className="header">Widget 8</div>
|
||||
<div className="stock">
|
||||
<span className="stock-item">
|
||||
<span className="stockValues">
|
||||
<div className="key">units</div>
|
||||
<div className="value">1000</div>
|
||||
</span>
|
||||
<div className="stock-description">Initial stock</div>
|
||||
</span>
|
||||
<div className="icon">Icon</div>
|
||||
</div>
|
||||
<div className="stock">
|
||||
<span className="stock-item">
|
||||
<span className="stockValues">
|
||||
<div className="key">units</div>
|
||||
<div className="value">1000</div>
|
||||
</span>
|
||||
<div className="stock-description">Initial stock</div>
|
||||
</span>
|
||||
<div className="icon">Icon</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Widgets;
|
||||
|
||||
// along with my charts i need to additionallly drag and drop my 2 widget 7 and widget 8 styled card to pannel
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
|
||||
export const ProgressCard = ({ title, data }: {
|
||||
title: string;
|
||||
data: { stocks: Array<{ key: string; value: number; description: string }> };
|
||||
}) => (
|
||||
<div className="chart progressBar">
|
||||
<div className="header">{title}</div>
|
||||
{data.stocks.map((stock, index) => (
|
||||
<div key={index} className="stock">
|
||||
<span className="stock-item">
|
||||
<span className="stockValues">
|
||||
<div className="key">{stock.key}</div>
|
||||
<div className="value">{stock.value}</div>
|
||||
</span>
|
||||
<div className="stock-description">{stock.description}</div>
|
||||
</span>
|
||||
<div className="icon">Icon</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import React, { useMemo, useState, useEffect, useRef } from "react";
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
|
@ -9,20 +9,20 @@ import {
|
|||
} from "@dnd-kit/core";
|
||||
import {
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
arrayMove,
|
||||
useSortable,
|
||||
} from "@dnd-kit/sortable";
|
||||
|
||||
import { useWidgetStore } from "../../store/store"; // Assuming you have this store
|
||||
import ChartComponent from "../../components/ui/sideBar/realTimeViz/chartComponent"; // Assuming this exists
|
||||
import SideBar from "../../components/layout/sideBar"; // Assuming this exists
|
||||
import { useWidgetStore } from "../../store/store";
|
||||
import ChartComponent from "../../components/ui/sideBar/realTimeViz/chartComponent";
|
||||
import SideBar from "../../components/layout/sideBar";
|
||||
import {
|
||||
CleanPannel,
|
||||
DisableSorting,
|
||||
EyeIcon,
|
||||
LockIcon,
|
||||
} from "../../assets/images/svgExports"; // Assuming these are your icon components
|
||||
} from "../../assets/images/svgExports";
|
||||
import { ProgressCard } from "./progressCard ";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -38,15 +38,17 @@ const DraggableWidget = ({ widget }: { widget: any }) => {
|
|||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id: widget.id });
|
||||
const { setSelectedChartId } = useWidgetStore();
|
||||
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
|
||||
const style = {
|
||||
transform: transform
|
||||
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
|
||||
: undefined,
|
||||
transition: transition || "transform 200ms ease",
|
||||
};
|
||||
const style = useMemo(
|
||||
() => ({
|
||||
transform: transform
|
||||
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
|
||||
: undefined,
|
||||
transition: transition || "transform 200ms ease",
|
||||
}),
|
||||
[transform, transition]
|
||||
);
|
||||
|
||||
const handlePointerDown = () => {
|
||||
if (!isDragging) {
|
||||
|
@ -64,137 +66,276 @@ const DraggableWidget = ({ widget }: { widget: any }) => {
|
|||
style={style}
|
||||
onPointerDown={handlePointerDown}
|
||||
>
|
||||
<ChartComponent
|
||||
type={widget.type}
|
||||
title={widget.title}
|
||||
fontFamily={widget.fontFamily}
|
||||
fontSize={widget.fontSize}
|
||||
fontWeight={widget.fontWeight}
|
||||
/>
|
||||
{widget.type === "progress" ? (
|
||||
<ProgressCard title={widget.title} data={widget.data} />
|
||||
) : (
|
||||
<ChartComponent
|
||||
type={widget.type}
|
||||
title={widget.title}
|
||||
fontFamily={widget.fontFamily}
|
||||
fontSize={widget.fontSize}
|
||||
fontWeight={widget.fontWeight}
|
||||
data={widget.data}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const RealTimeVisualization = () => {
|
||||
const [zone, setZone] = useState([
|
||||
"Manufacturing unit",
|
||||
"Assembly unit",
|
||||
"Packing unit",
|
||||
"Warehouse",
|
||||
"Inventory",
|
||||
]);
|
||||
|
||||
const [activeSides, setActiveSides] = useState<Side[]>([]);
|
||||
const [panelOrder, setPanelOrder] = useState<Side[]>([]);
|
||||
const [selectedSide, setSelectedSide] = useState<Side | null>(null);
|
||||
const [lockedPanels, setLockedPanels] = useState<Side[]>([]);
|
||||
const [activeExtraButton, setActiveExtraButton] = useState<{
|
||||
[key in Side]?: string;
|
||||
const [selectedZone, setSelectedZone] = useState("Manufacturing unit");
|
||||
const [panelDimensions, setPanelDimensions] = useState<{
|
||||
[side in Side]?: { width: number; height: number };
|
||||
}>({});
|
||||
const { draggedAsset, addWidget, widgets, setWidgets } = useWidgetStore();
|
||||
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||
|
||||
const [zonesData, setZonesData] = useState<{
|
||||
[key: string]: {
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
lockedPanels: Side[];
|
||||
widgets: {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
panel: Side;
|
||||
data: any;
|
||||
}[];
|
||||
};
|
||||
}>({
|
||||
"Manufacturing unit": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Assembly unit": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Packing unit": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
Warehouse: {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
Inventory: {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {}, [zonesData]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor)
|
||||
);
|
||||
|
||||
const toggleSide = (side: Side) => {
|
||||
setActiveSides((prev) => {
|
||||
const newActive = prev.includes(side)
|
||||
? prev.filter((s) => s !== side)
|
||||
: [...prev, side];
|
||||
setPanelOrder(newActive);
|
||||
useEffect(() => {
|
||||
const observers: ResizeObserver[] = [];
|
||||
const currentPanelRefs = panelRefs.current;
|
||||
|
||||
if (prev.includes(side)) {
|
||||
setSelectedSide(null);
|
||||
} else {
|
||||
setSelectedSide(side);
|
||||
zonesData[selectedZone].activeSides.forEach((side) => {
|
||||
const element = currentPanelRefs[side];
|
||||
if (element) {
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
setPanelDimensions((prev) => ({
|
||||
...prev,
|
||||
[side]: { width, height },
|
||||
}));
|
||||
}
|
||||
});
|
||||
observer.observe(element);
|
||||
observers.push(observer);
|
||||
}
|
||||
});
|
||||
|
||||
return newActive;
|
||||
return () => {
|
||||
observers.forEach((observer) => observer.disconnect());
|
||||
};
|
||||
}, [zonesData[selectedZone].activeSides, selectedZone]);
|
||||
|
||||
const toggleSide = (side: Side) => {
|
||||
setZonesData((prev) => {
|
||||
const zoneData = prev[selectedZone];
|
||||
const newActiveSides = zoneData.activeSides.includes(side)
|
||||
? zoneData.activeSides.filter((s) => s !== side)
|
||||
: [...zoneData.activeSides, side];
|
||||
return {
|
||||
...prev,
|
||||
[selectedZone]: {
|
||||
...zoneData,
|
||||
activeSides: newActiveSides,
|
||||
panelOrder: newActiveSides,
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const toggleLockPanel = (side: Side) => {
|
||||
setLockedPanels((prev) =>
|
||||
prev.includes(side)
|
||||
? prev.filter((panel) => panel !== side)
|
||||
: [...prev, side]
|
||||
);
|
||||
setZonesData((prev) => {
|
||||
const zoneData = prev[selectedZone];
|
||||
const newLockedPanels = zoneData.lockedPanels.includes(side)
|
||||
? zoneData.lockedPanels.filter((panel) => panel !== side)
|
||||
: [...zoneData.lockedPanels, side];
|
||||
return {
|
||||
...prev,
|
||||
[selectedZone]: {
|
||||
...zoneData,
|
||||
lockedPanels: newLockedPanels,
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const getPanelStyle = (currentSide: Side) => {
|
||||
const currentIndex = panelOrder.indexOf(currentSide);
|
||||
const previousPanels = panelOrder.slice(0, currentIndex);
|
||||
const getPanelStyle = useMemo(
|
||||
() => (side: Side) => {
|
||||
const currentIndex = zonesData[selectedZone].panelOrder.indexOf(side);
|
||||
const previousPanels = zonesData[selectedZone].panelOrder.slice(
|
||||
0,
|
||||
currentIndex
|
||||
);
|
||||
const leftActive = previousPanels.includes("left");
|
||||
const rightActive = previousPanels.includes("right");
|
||||
const topActive = previousPanels.includes("top");
|
||||
const bottomActive = previousPanels.includes("bottom");
|
||||
|
||||
const leftActive = previousPanels.includes("left");
|
||||
const rightActive = previousPanels.includes("right");
|
||||
const topActive = previousPanels.includes("top");
|
||||
const bottomActive = previousPanels.includes("bottom");
|
||||
|
||||
switch (currentSide) {
|
||||
case "top":
|
||||
case "bottom":
|
||||
return {
|
||||
width: `calc(100% - ${
|
||||
(leftActive ? 204 : 0) + (rightActive ? 204 : 0)
|
||||
}px)`,
|
||||
left: leftActive ? "204px" : "0",
|
||||
right: rightActive ? "204px" : "0",
|
||||
[currentSide]: "0",
|
||||
height: "200px",
|
||||
};
|
||||
|
||||
case "left":
|
||||
case "right":
|
||||
return {
|
||||
height: `calc(100% - ${
|
||||
(topActive ? 204 : 0) + (bottomActive ? 204 : 0)
|
||||
}px)`,
|
||||
top: topActive ? "204px" : "0",
|
||||
bottom: bottomActive ? "204px" : "0",
|
||||
[currentSide]: "0",
|
||||
width: "200px",
|
||||
};
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
switch (side) {
|
||||
case "top":
|
||||
case "bottom":
|
||||
return {
|
||||
width: `calc(100% - ${
|
||||
(leftActive ? 204 : 0) + (rightActive ? 204 : 0)
|
||||
}px)`,
|
||||
left: leftActive ? "204px" : "0",
|
||||
right: rightActive ? "204px" : "0",
|
||||
[side]: "0",
|
||||
height: "200px",
|
||||
};
|
||||
case "left":
|
||||
case "right":
|
||||
return {
|
||||
height: `calc(100% - ${
|
||||
(topActive ? 204 : 0) + (bottomActive ? 204 : 0)
|
||||
}px)`,
|
||||
top: topActive ? "204px" : "0",
|
||||
bottom: bottomActive ? "204px" : "0",
|
||||
[side]: "0",
|
||||
width: "200px",
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
},
|
||||
[zonesData, selectedZone]
|
||||
);
|
||||
|
||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||
e.preventDefault();
|
||||
const { draggedAsset } = useWidgetStore.getState();
|
||||
if (draggedAsset) {
|
||||
addWidget({ ...draggedAsset, id: generateUniqueId(), panel });
|
||||
if (zonesData[selectedZone].lockedPanels.includes(panel)) return;
|
||||
|
||||
const currentWidgetsInPanel = zonesData[selectedZone].widgets.filter(
|
||||
(w) => w.panel === panel
|
||||
).length;
|
||||
|
||||
const dimensions = panelDimensions[panel];
|
||||
const CHART_WIDTH = 200; // Width of each chart for top/bottom panels
|
||||
const CHART_HEIGHT = 200; // Height of each chart for left/right panels
|
||||
let maxCharts = 0;
|
||||
|
||||
if (dimensions) {
|
||||
if (panel === "top" || panel === "bottom") {
|
||||
maxCharts = Math.floor(dimensions.width / CHART_WIDTH); // Use width for top/bottom
|
||||
} else {
|
||||
maxCharts = Math.floor(dimensions.height / CHART_HEIGHT); // Use height for left/right
|
||||
}
|
||||
} else {
|
||||
maxCharts = panel === "top" || panel === "bottom" ? 5 : 3; // Default values
|
||||
}
|
||||
|
||||
if (currentWidgetsInPanel >= maxCharts) {
|
||||
return;
|
||||
}
|
||||
|
||||
setZonesData((prev) => ({
|
||||
...prev,
|
||||
[selectedZone]: {
|
||||
...prev[selectedZone],
|
||||
widgets: [
|
||||
...prev[selectedZone].widgets,
|
||||
{
|
||||
...draggedAsset,
|
||||
id: generateUniqueId(),
|
||||
panel,
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: any) => {
|
||||
const { active, over } = event;
|
||||
|
||||
if (!over) return;
|
||||
|
||||
const oldIndex = widgets.findIndex((widget) => widget.id === active.id);
|
||||
const newPanel =
|
||||
widgets.find((widget) => widget.id === over.id)?.panel || active.panel;
|
||||
|
||||
if (active.panel === newPanel) {
|
||||
const newIndex = widgets.findIndex((widget) => widget.id === over.id);
|
||||
const reorderedWidgets = arrayMove(widgets, oldIndex, newIndex);
|
||||
setWidgets(reorderedWidgets);
|
||||
} else {
|
||||
const updatedWidgets = widgets.map((widget) =>
|
||||
widget.id === active.id ? { ...widget, panel: newPanel } : widget
|
||||
setZonesData((prev) => {
|
||||
const zoneData = prev[selectedZone];
|
||||
const oldIndex = zoneData.widgets.findIndex(
|
||||
(widget) => widget.id === active.id
|
||||
);
|
||||
const newPanel =
|
||||
zoneData.widgets.find((widget) => widget.id === over.id)?.panel ||
|
||||
active.panel;
|
||||
|
||||
const widgetsInNewPanel = updatedWidgets.filter(
|
||||
(w) => w.panel === newPanel
|
||||
);
|
||||
const newIndex = widgetsInNewPanel.findIndex((w) => w.id === over.id);
|
||||
|
||||
const reorderedWidgets = arrayMove(updatedWidgets, oldIndex, newIndex);
|
||||
setWidgets(reorderedWidgets);
|
||||
}
|
||||
if (active.panel === newPanel) {
|
||||
const newIndex = zoneData.widgets.findIndex(
|
||||
(widget) => widget.id === over.id
|
||||
);
|
||||
const reorderedWidgets = arrayMove(
|
||||
zoneData.widgets,
|
||||
oldIndex,
|
||||
newIndex
|
||||
);
|
||||
return {
|
||||
...prev,
|
||||
[selectedZone]: {
|
||||
...zoneData,
|
||||
widgets: reorderedWidgets,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const updatedWidgets = zoneData.widgets.map((widget) =>
|
||||
widget.id === active.id ? { ...widget, panel: newPanel } : widget
|
||||
);
|
||||
const widgetsInNewPanel = updatedWidgets.filter(
|
||||
(w) => w.panel === newPanel
|
||||
);
|
||||
const newIndex = widgetsInNewPanel.findIndex((w) => w.id === over.id);
|
||||
const reorderedWidgets = arrayMove(updatedWidgets, oldIndex, newIndex);
|
||||
return {
|
||||
...prev,
|
||||
[selectedZone]: {
|
||||
...zoneData,
|
||||
widgets: reorderedWidgets,
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -208,11 +349,20 @@ const RealTimeVisualization = () => {
|
|||
header={["Overview", "Widgets", "Templates"]}
|
||||
defaultActive={"Widgets"}
|
||||
/>
|
||||
|
||||
<div className="main-container relative">
|
||||
<div className="zoon-wrapper">
|
||||
{zone.map((zone, index) => (
|
||||
<div className="zone">{zone}</div>
|
||||
<div
|
||||
className={`zoon-wrapper ${
|
||||
zonesData[selectedZone].activeSides.includes("bottom") && "bottom"
|
||||
}`}
|
||||
>
|
||||
{Object.keys(zonesData).map((zone, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${selectedZone === zone && "active"}`}
|
||||
onClick={() => setSelectedZone(zone)}
|
||||
>
|
||||
{zone}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
|
||||
|
@ -224,100 +374,100 @@ const RealTimeVisualization = () => {
|
|||
>
|
||||
+
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="extra-buttons"
|
||||
style={{
|
||||
display: activeSides.includes(side) ? "flex" : "none",
|
||||
display: zonesData[selectedZone].activeSides.includes(side)
|
||||
? "flex"
|
||||
: "none",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`icon ${
|
||||
activeExtraButton[side] === "Disable Sorting"
|
||||
zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
title="Disable Sorting"
|
||||
onClick={() =>
|
||||
setActiveExtraButton((prev) => ({
|
||||
...prev,
|
||||
[side]: "Disable Sorting",
|
||||
}))
|
||||
}
|
||||
onClick={() => toggleLockPanel(side)}
|
||||
>
|
||||
<DisableSorting />
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${
|
||||
activeExtraButton[side] === "Hide Panel" ? "active" : ""
|
||||
zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
title="Hide Panel"
|
||||
onClick={() =>
|
||||
setActiveExtraButton((prev) => ({
|
||||
...prev,
|
||||
[side]: "Hide Panel",
|
||||
}))
|
||||
}
|
||||
onClick={() => toggleLockPanel(side)}
|
||||
>
|
||||
<EyeIcon />
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${
|
||||
activeExtraButton[side] === "Clean Panel" ? "active" : ""
|
||||
zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
title="Clean Panel"
|
||||
onClick={() =>
|
||||
setActiveExtraButton((prev) => ({
|
||||
...prev,
|
||||
[side]: "Clean Panel",
|
||||
}))
|
||||
}
|
||||
onClick={() => toggleLockPanel(side)}
|
||||
>
|
||||
<CleanPannel />
|
||||
</div>
|
||||
<div
|
||||
className={`icon ${
|
||||
activeExtraButton[side] === "Lock Panel" ? "active" : ""
|
||||
zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
title={
|
||||
lockedPanels.includes(side) ? "Unlock Panel" : "Lock Panel"
|
||||
zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "Unlock Panel"
|
||||
: "Lock Panel"
|
||||
}
|
||||
onClick={() => {
|
||||
toggleLockPanel(side);
|
||||
setActiveExtraButton((prev) => ({
|
||||
...prev,
|
||||
[side]: "Lock Panel",
|
||||
}));
|
||||
}}
|
||||
onClick={() => toggleLockPanel(side)}
|
||||
>
|
||||
<LockIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{activeSides.map((side) => (
|
||||
{zonesData[selectedZone].activeSides.map((side) => (
|
||||
<div
|
||||
key={side}
|
||||
className={`panel ${side}-panel absolute`}
|
||||
style={getPanelStyle(side)}
|
||||
style={getPanelStyle(side)} // Pass `side` to `getPanelStyle`
|
||||
onDrop={(e) => handleDrop(e, side)}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
ref={(el) => {
|
||||
if (el) {
|
||||
panelRefs.current[side] = el;
|
||||
} else {
|
||||
delete panelRefs.current[side];
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="panel-content"
|
||||
style={{
|
||||
pointerEvents: lockedPanels.includes(side) ? "none" : "auto",
|
||||
opacity: lockedPanels.includes(side) ? "0.8" : "1",
|
||||
pointerEvents: zonesData[selectedZone].lockedPanels.includes(
|
||||
side
|
||||
)
|
||||
? "none"
|
||||
: "auto",
|
||||
opacity: zonesData[selectedZone].lockedPanels.includes(side)
|
||||
? "0.8"
|
||||
: "1",
|
||||
}}
|
||||
>
|
||||
<SortableContext
|
||||
items={widgets
|
||||
items={zonesData[selectedZone].widgets
|
||||
.filter((w) => w.panel === side)
|
||||
.map((w) => w.id)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{widgets
|
||||
{zonesData[selectedZone].widgets
|
||||
.filter((w) => w.panel === side)
|
||||
.map((widget) => (
|
||||
<DraggableWidget widget={widget} key={widget.id} />
|
||||
|
@ -327,7 +477,6 @@ const RealTimeVisualization = () => {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<SideBar header={["Data", "Design"]} defaultActive={"Data"} />
|
||||
</div>
|
||||
</DndContext>
|
||||
|
|
|
@ -275,50 +275,6 @@ export const useDrieUIValue = create<any>((set: any) => ({
|
|||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
// Define Widget interface
|
||||
interface Widget {
|
||||
id: string;
|
||||
type: any;
|
||||
panel: Side;
|
||||
title: string; // Ensure title is a string
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: string;
|
||||
}
|
||||
|
||||
// Store interface
|
||||
interface WidgetStore {
|
||||
draggedAsset: { type: any; id: string; title: string } | null;
|
||||
widgets: Widget[];
|
||||
setDraggedAsset: (
|
||||
asset: { type: any; id: string; title: string } | null
|
||||
) => void;
|
||||
addWidget: (widget: Widget) => void;
|
||||
setWidgets: (widgets: Widget[]) => void;
|
||||
selectedChartId: Widget | null; // Change this to store an object of type Widget
|
||||
setSelectedChartId: (widget: Widget | {
|
||||
"type": "line",
|
||||
"id": "1741781992712-ecvq5t61y",
|
||||
"title": "Widget 2",
|
||||
"panel": "left",
|
||||
"fontWeight": "Light",
|
||||
"fontSize": "12px",
|
||||
"fontFamily": "Sans-serif"
|
||||
}) => void; // Update to accept a Widget object
|
||||
}
|
||||
|
||||
// Create the store with Zustand
|
||||
export const useWidgetStore = create<WidgetStore>((set) => ({
|
||||
draggedAsset: null,
|
||||
widgets: [],
|
||||
setDraggedAsset: (asset) => set({ draggedAsset: asset }),
|
||||
addWidget: (widget) =>
|
||||
set((state) => ({ widgets: [...state.widgets, widget] })),
|
||||
setWidgets: (widgets) => set({ widgets }),
|
||||
selectedChartId: null,
|
||||
setSelectedChartId: (widget) => set({ selectedChartId: widget }),
|
||||
}));
|
||||
|
||||
interface ThemeState {
|
||||
themeColor: string[]; // This should be an array of strings
|
||||
setThemeColor: (colors: string[]) => void; // This function will accept an array of strings
|
||||
|
@ -328,3 +284,52 @@ export const useThemeStore = create<ThemeState>((set) => ({
|
|||
themeColor: ["#5c87df", "#EEEEFE", "#969BA7"],
|
||||
setThemeColor: (colors) => set({ themeColor: colors }),
|
||||
}));
|
||||
// Define the WidgetStore interface
|
||||
// Define the WidgetStore interface
|
||||
interface Widget {
|
||||
id: string;
|
||||
type: string; // Can be chart type or "progress"
|
||||
panel: "top" | "bottom" | "left" | "right";
|
||||
title: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: string;
|
||||
data: {
|
||||
// Chart data
|
||||
labels?: string[];
|
||||
datasets?: Array<{
|
||||
data: number[];
|
||||
backgroundColor: string;
|
||||
borderColor: string;
|
||||
borderWidth: number;
|
||||
}>;
|
||||
// Progress card data
|
||||
stocks?: Array<{
|
||||
key: string;
|
||||
value: number;
|
||||
description: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
interface WidgetStore {
|
||||
draggedAsset: Widget | null; // The currently dragged widget asset
|
||||
widgets: Widget[]; // List of all widgets
|
||||
selectedChartId: Widget | null; // The currently selected chart/widget
|
||||
setDraggedAsset: (asset: Widget | null) => void; // Setter for draggedAsset
|
||||
addWidget: (widget: Widget) => void; // Add a new widget
|
||||
setWidgets: (widgets: Widget[]) => void; // Replace the entire widgets array
|
||||
setSelectedChartId: (widget: Widget | null) => void; // Set the selected chart/widget
|
||||
}
|
||||
|
||||
// Create the store with Zustand
|
||||
export const useWidgetStore = create<WidgetStore>((set) => ({
|
||||
draggedAsset: null,
|
||||
widgets: [],
|
||||
selectedChartId: null, // Initialize as null, not as an array
|
||||
setDraggedAsset: (asset) => set({ draggedAsset: asset }),
|
||||
addWidget: (widget) =>
|
||||
set((state) => ({ widgets: [...state.widgets, widget] })),
|
||||
setWidgets: (widgets) => set({ widgets }),
|
||||
setSelectedChartId: (widget) => set({ selectedChartId: widget }),
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue