Dwinzo_dev/app/src/components/ui/componets/Panel.tsx

211 lines
5.9 KiB
TypeScript
Raw Normal View History

2025-03-25 06:17:41 +00:00
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
interface PanelProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
}>
>;
2025-03-25 10:25:48 +00:00
hiddenPanels: string[];
2025-03-25 06:17:41 +00:00
}
const generateUniqueId = () =>
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2025-03-25 10:25:48 +00:00
const Panel: React.FC<PanelProps> = ({
selectedZone,
setSelectedZone,
hiddenPanels,
}) => {
2025-03-25 06:17:41 +00:00
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
2025-03-26 08:22:10 +00:00
const { isPlaying } = usePlayButtonStore();
2025-03-25 06:17:41 +00:00
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");
2025-03-26 08:22:10 +00:00
const panelSize = isPlaying ? 300 : 210;
2025-03-25 06:17:41 +00:00
switch (side) {
case "top":
case "bottom":
return {
width: `calc(100% - ${
2025-03-25 08:55:51 +00:00
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
2025-03-25 06:17:41 +00:00
}px)`,
2025-03-25 08:55:51 +00:00
height: `${panelSize - 5}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
2025-03-25 06:17:41 +00:00
[side]: "0",
};
case "left":
case "right":
return {
2025-03-25 08:55:51 +00:00
width: `${panelSize - 5}px`,
2025-03-25 06:17:41 +00:00
height: `calc(100% - ${
2025-03-25 08:55:51 +00:00
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
2025-03-25 06:17:41 +00:00
}px)`,
2025-03-25 08:55:51 +00:00
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
2025-03-25 06:17:41 +00:00
[side]: "0",
};
default:
return {};
}
},
2025-03-26 08:22:10 +00:00
[selectedZone.panelOrder, isPlaying]
2025-03-25 06:17:41 +00:00
);
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
if (currentWidgetsCount >= maxCapacity) return;
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
addWidgetToPanel(draggedAsset, panel);
};
2025-03-25 08:55:51 +00:00
const isPanelLocked = (panel: Side) =>
2025-03-25 06:17:41 +00:00
selectedZone.lockedPanels.includes(panel);
2025-03-25 08:55:51 +00:00
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
2025-03-25 06:17:41 +00:00
const calculatePanelCapacity = (panel: Side) => {
2025-03-26 08:22:10 +00:00
const CHART_WIDTH = 150;
const CHART_HEIGHT = 150;
2025-03-25 06:17:41 +00:00
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
const dimensions = panelDimensions[panel];
if (!dimensions) {
2025-03-25 08:55:51 +00:00
return panel === "top" || panel === "bottom"
2025-03-25 06:17:41 +00:00
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
}
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
};
2025-03-25 08:55:51 +00:00
2025-03-25 06:17:41 +00:00
const addWidgetToPanel = (asset: any, panel: Side) => {
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
2025-03-25 08:55:51 +00:00
setSelectedZone((prev) => ({
2025-03-25 06:17:41 +00:00
...prev,
2025-03-25 08:55:51 +00:00
widgets: [...prev.widgets, newWidget],
2025-03-25 06:17:41 +00:00
}));
};
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 ${isPlaying && ""}`}
style={getPanelStyle(side)}
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 ${isPlaying && "fullScreen"}`}
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) => (
2025-03-25 10:25:48 +00:00
<DraggableWidget
hiddenPanels={hiddenPanels}
widget={widget}
key={widget.id}
/>
2025-03-25 06:17:41 +00:00
))}
</div>
</div>
))}
</>
);
};
export default Panel;