197 lines
5.6 KiB
TypeScript
197 lines
5.6 KiB
TypeScript
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
import { DraggableWidget } from "./draggableWidget";
|
|
import { useWidgetStore } from "../../store/store";
|
|
|
|
type Side = "top" | "bottom" | "left" | "right";
|
|
|
|
interface PanelProps {
|
|
selectedZone: {
|
|
zoneName: string; // Add zoneName property
|
|
activeSides: Side[];
|
|
panelOrder: Side[];
|
|
lockedPanels: Side[];
|
|
widgets: {
|
|
id: string;
|
|
type: string;
|
|
title: string;
|
|
panel: Side;
|
|
data: any;
|
|
}[];
|
|
};
|
|
setSelectedZone: React.Dispatch<
|
|
React.SetStateAction<{
|
|
zoneName: string; // Ensure zoneName is included in the state type
|
|
activeSides: Side[];
|
|
panelOrder: Side[];
|
|
lockedPanels: Side[];
|
|
widgets: {
|
|
id: string;
|
|
type: string;
|
|
title: string;
|
|
panel: Side;
|
|
data: any;
|
|
}[];
|
|
}>
|
|
>;
|
|
}
|
|
|
|
const generateUniqueId = () =>
|
|
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
const Panel: React.FC<PanelProps> = ({ selectedZone, setSelectedZone }) => {
|
|
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
|
const [panelDimensions, setPanelDimensions] = useState<{
|
|
[side in Side]?: { width: number; height: number };
|
|
}>({});
|
|
|
|
const getPanelStyle = useMemo(
|
|
() => (side: Side) => {
|
|
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
|
const previousPanels = 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");
|
|
|
|
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 {};
|
|
}
|
|
},
|
|
[selectedZone.panelOrder]
|
|
);
|
|
|
|
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
|
e.preventDefault();
|
|
const { draggedAsset } = useWidgetStore.getState();
|
|
if (draggedAsset) {
|
|
if (selectedZone.lockedPanels.includes(panel)) return;
|
|
|
|
const currentWidgetsInPanel = 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;
|
|
}
|
|
|
|
const updatedZone = {
|
|
...selectedZone,
|
|
widgets: [
|
|
...selectedZone.widgets,
|
|
{
|
|
...draggedAsset,
|
|
id: generateUniqueId(),
|
|
panel,
|
|
},
|
|
],
|
|
};
|
|
|
|
// Update the selectedZone state
|
|
setSelectedZone(updatedZone);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const observers: ResizeObserver[] = [];
|
|
const currentPanelRefs = panelRefs.current;
|
|
|
|
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 () => {
|
|
observers.forEach((observer) => observer.disconnect());
|
|
};
|
|
}, [selectedZone.activeSides]);
|
|
|
|
return (
|
|
<>
|
|
{selectedZone.activeSides.map((side) => (
|
|
<div
|
|
key={side}
|
|
className={`panel ${side}-panel absolute`}
|
|
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: selectedZone.lockedPanels.includes(side)
|
|
? "none"
|
|
: "auto",
|
|
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
|
}}
|
|
>
|
|
{selectedZone.widgets
|
|
.filter((w) => w.panel === side)
|
|
.map((widget) => (
|
|
<DraggableWidget widget={widget} key={widget.id} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Panel;
|