204 lines
6.0 KiB
TypeScript
204 lines
6.0 KiB
TypeScript
import React, { useEffect, useRef, useState, useCallback } from "react";
|
|
import { Widget } from "../../../store/useWidgetStore";
|
|
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
|
|
import { InfoIcon } from "../../icons/ExportCommonIcons";
|
|
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
|
|
|
// Define the type for `Side`
|
|
type Side = "top" | "bottom" | "left" | "right";
|
|
|
|
interface DisplayZoneProps {
|
|
zonesData: {
|
|
[key: string]: {
|
|
activeSides: Side[];
|
|
panelOrder: Side[];
|
|
lockedPanels: Side[];
|
|
widgets: Widget[];
|
|
zoneId: string;
|
|
zoneViewPortTarget: number[];
|
|
zoneViewPortPosition: number[];
|
|
};
|
|
};
|
|
selectedZone: {
|
|
zoneName: string;
|
|
activeSides: Side[];
|
|
panelOrder: Side[];
|
|
lockedPanels: Side[];
|
|
zoneId: string;
|
|
zoneViewPortTarget: number[];
|
|
zoneViewPortPosition: number[];
|
|
widgets: {
|
|
id: string;
|
|
type: string;
|
|
title: string;
|
|
panel: Side;
|
|
data: any;
|
|
}[];
|
|
};
|
|
setSelectedZone: React.Dispatch<
|
|
React.SetStateAction<{
|
|
zoneName: string;
|
|
activeSides: Side[];
|
|
panelOrder: Side[];
|
|
lockedPanels: Side[];
|
|
zoneId: string;
|
|
zoneViewPortTarget: number[];
|
|
zoneViewPortPosition: number[];
|
|
widgets: {
|
|
id: string;
|
|
type: string;
|
|
title: string;
|
|
panel: Side;
|
|
data: any;
|
|
}[];
|
|
}>
|
|
>;
|
|
}
|
|
|
|
const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|
zonesData,
|
|
selectedZone,
|
|
setSelectedZone,
|
|
}) => {
|
|
// Ref for the container element
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
// State to track overflow visibility
|
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
|
const [showRightArrow, setShowRightArrow] = useState(false);
|
|
|
|
// Function to calculate overflow state
|
|
const updateOverflowState = useCallback(() => {
|
|
const container = containerRef.current;
|
|
|
|
if (container) {
|
|
const isOverflowing = container.scrollWidth > container.clientWidth;
|
|
const canScrollLeft = container.scrollLeft > 0;
|
|
const canScrollRight =
|
|
container.scrollLeft + container.clientWidth < container.scrollWidth;
|
|
|
|
setShowLeftArrow(isOverflowing && canScrollLeft);
|
|
setShowRightArrow(isOverflowing && canScrollRight);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const container = containerRef.current;
|
|
|
|
if (container) {
|
|
// Initial calculation after the DOM has been rendered
|
|
const handleInitialRender = () => {
|
|
requestAnimationFrame(updateOverflowState);
|
|
};
|
|
|
|
handleInitialRender();
|
|
|
|
// Update on window resize or scroll
|
|
const handleResize = () => updateOverflowState();
|
|
const handleScroll = () => updateOverflowState();
|
|
|
|
// Add mouse wheel listener for horizontal scrolling
|
|
const handleWheel = (event: WheelEvent) => {
|
|
event.preventDefault(); // Prevent default vertical scrolling
|
|
if (container) {
|
|
container.scrollBy({
|
|
left: event.deltaY * 2, // Translate vertical scroll to horizontal scroll
|
|
behavior: "smooth",
|
|
});
|
|
}
|
|
};
|
|
|
|
container.addEventListener("scroll", handleScroll);
|
|
window.addEventListener("resize", handleResize);
|
|
container.addEventListener("wheel", handleWheel, { passive: false });
|
|
|
|
return () => {
|
|
container.removeEventListener("scroll", handleScroll);
|
|
window.removeEventListener("resize", handleResize);
|
|
container.removeEventListener("wheel", handleWheel);
|
|
};
|
|
}
|
|
}, [updateOverflowState]);
|
|
|
|
// Handle scrolling with navigation arrows
|
|
const handleScrollLeft = () => {
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
container.scrollBy({
|
|
left: -200, // Scroll left by 200px
|
|
behavior: "smooth",
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleScrollRight = () => {
|
|
const container = containerRef.current;
|
|
if (container) {
|
|
container.scrollBy({
|
|
left: 200, // Scroll right by 200px
|
|
behavior: "smooth",
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`zone-wrapper ${
|
|
selectedZone?.activeSides?.includes("bottom") && "bottom"
|
|
}`}
|
|
>
|
|
{/* Left Arrow */}
|
|
{showLeftArrow && (
|
|
<button className="arrow left-arrow" onClick={handleScrollLeft}>
|
|
<MoveArrowLeft />
|
|
</button>
|
|
)}
|
|
|
|
{/* Zones Wrapper */}
|
|
{Object.keys(zonesData).length !== 0 ? (
|
|
<div ref={containerRef} className="zones-wrapper">
|
|
{Object.keys(zonesData).map((zoneName, index) => (
|
|
<div
|
|
key={index}
|
|
className={`zone ${
|
|
selectedZone.zoneName === zoneName ? "active" : ""
|
|
}`}
|
|
onClick={() => {
|
|
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
|
setSelectedZone({
|
|
zoneName,
|
|
activeSides: zonesData[zoneName].activeSides || [],
|
|
panelOrder: zonesData[zoneName].panelOrder || [],
|
|
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
|
widgets: zonesData[zoneName].widgets || [],
|
|
zoneId: zonesData[zoneName]?.zoneId || "",
|
|
zoneViewPortTarget:
|
|
zonesData[zoneName].zoneViewPortTarget || [],
|
|
zoneViewPortPosition:
|
|
zonesData[zoneName].zoneViewPortPosition || [],
|
|
});
|
|
}}
|
|
>
|
|
{zoneName}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="no-zone">
|
|
<InfoIcon />
|
|
No zones? Create one!
|
|
</div>
|
|
)}
|
|
|
|
{/* Right Arrow */}
|
|
{showRightArrow && (
|
|
<button className="arrow right-arrow" onClick={handleScrollRight}>
|
|
<MoveArrowRight />
|
|
</button>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DisplayZone;
|