Added kebab for delete widgets
This commit is contained in:
@@ -511,3 +511,99 @@ export function AI_Icon() {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const KebabIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="13"
|
||||
height="3"
|
||||
viewBox="0 0 13 3"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<ellipse
|
||||
cx="1.54798"
|
||||
cy="1.35112"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<ellipse
|
||||
cx="6.04868"
|
||||
cy="1.35131"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
<ellipse
|
||||
cx="10.5476"
|
||||
cy="1.35131"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const DublicateIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.3125 11.375C14.3125 11.7545 14.0045 12.0625 13.625 12.0625H8.125C7.7455 12.0625 7.4375 11.7545 7.4375 11.375V5.875C7.4375 5.4955 7.7455 5.1875 8.125 5.1875H13.625C14.0045 5.1875 14.3125 5.4955 14.3125 5.875V11.375ZM13.625 4.5H8.125C7.36566 4.5 6.75 5.11566 6.75 5.875V11.375C6.75 12.1343 7.36566 12.75 8.125 12.75H13.625C14.3843 12.75 15 12.1343 15 11.375V5.875C15 5.11566 14.3843 4.5 13.625 4.5ZM11.5625 14.125C11.5625 14.5045 11.2545 14.8125 10.875 14.8125H5.375C4.9955 14.8125 4.6875 14.5045 4.6875 14.125V8.625C4.6875 8.2455 4.9955 7.9375 5.375 7.9375H6.0625V7.25H5.375C4.61566 7.25 4 7.86566 4 8.625V14.125C4 14.8843 4.61566 15.5 5.375 15.5H10.875C11.6343 15.5 12.25 14.8843 12.25 14.125V13.4375H11.5625V14.125Z"
|
||||
fill="#6F42C1"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeleteIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.33301 10V13.3334"
|
||||
stroke="#5D5F63"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11 10V13.3334"
|
||||
stroke="#5D5F63"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.33301 6.66406H15"
|
||||
stroke="#5D5F63"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.66699 8.66406V13.9976C5.66699 15.1022 6.56245 15.9976 7.66705 15.9976H11.6672C12.7718 15.9976 13.6672 15.1022 13.6672 13.9976V8.66406"
|
||||
stroke="#5D5F63"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.66699 5.33337C7.66699 4.59697 8.26396 4 9.00037 4H10.3337C11.0702 4 11.6671 4.59697 11.6671 5.33337V6.66675H7.66699V5.33337Z"
|
||||
stroke="#5D5F63"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ const Visualization = () => {
|
||||
return (
|
||||
<div className="visualization-right-sideBar">
|
||||
<ToggleHeader
|
||||
options={["Data", "Design"]}
|
||||
options={["Data"]}
|
||||
activeOption={activeOption}
|
||||
handleClick={handleToggleClick}
|
||||
/>
|
||||
|
||||
@@ -53,7 +53,6 @@ const PieChartComponent = ({
|
||||
.getPropertyValue("--accent-color")
|
||||
.trim();
|
||||
|
||||
console.log("accentColor: ", accentColor);
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
responsive: true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { Widget } from "../../../store/useWidgetStore";
|
||||
|
||||
// Define the type for `Side`
|
||||
@@ -51,135 +51,139 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
// Ref for the container element
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Example state for selectedOption and options (adjust based on your actual use case)
|
||||
const [selectedOption, setSelectedOption] = React.useState<string | null>(
|
||||
null
|
||||
);
|
||||
// console.log('setSelectedOption: ', setSelectedOption);
|
||||
const [options, setOptions] = React.useState<string[]>([]);
|
||||
// console.log('setOptions: ', setOptions);
|
||||
// State to track overflow visibility
|
||||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||
|
||||
// Scroll to the selected option when it changes
|
||||
useEffect(() => {
|
||||
// Function to calculate overflow state
|
||||
const updateOverflowState = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
|
||||
if (container && selectedOption) {
|
||||
// Handle scrolling to the selected option
|
||||
const index = options.findIndex((option) => {
|
||||
const formattedOption = formatOptionName(option);
|
||||
const selectedFormattedOption =
|
||||
selectedOption?.split("_")[1] || selectedOption;
|
||||
return formattedOption === selectedFormattedOption;
|
||||
});
|
||||
|
||||
if (index !== -1) {
|
||||
const optionElement = container.children[index] as HTMLElement;
|
||||
if (optionElement) {
|
||||
optionElement.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "center",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [selectedOption, options]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
|
||||
const handleWheel = (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
if (container) {
|
||||
container.scrollBy({
|
||||
left: event.deltaY * 2, // Adjust the multiplier for faster scrolling
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let isDragging = false;
|
||||
let startX: number;
|
||||
let scrollLeft: number;
|
||||
|
||||
const handleMouseDown = (event: MouseEvent) => {
|
||||
isDragging = true;
|
||||
startX = event.pageX - (container?.offsetLeft || 0);
|
||||
scrollLeft = container?.scrollLeft || 0;
|
||||
};
|
||||
|
||||
const handleMouseMove = (event: MouseEvent) => {
|
||||
if (!isDragging || !container) return;
|
||||
event.preventDefault();
|
||||
const x = event.pageX - (container.offsetLeft || 0);
|
||||
const walk = (x - startX) * 2; // Adjust the multiplier for faster dragging
|
||||
container.scrollLeft = scrollLeft - walk;
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
isDragging = false;
|
||||
};
|
||||
|
||||
if (container) {
|
||||
container.addEventListener("wheel", handleWheel, { passive: false });
|
||||
container.addEventListener("mousedown", handleMouseDown);
|
||||
container.addEventListener("mousemove", handleMouseMove);
|
||||
container.addEventListener("mouseup", handleMouseUp);
|
||||
container.addEventListener("mouseleave", handleMouseLeave);
|
||||
}
|
||||
const isOverflowing = container.scrollWidth > container.clientWidth;
|
||||
const canScrollLeft = container.scrollLeft > 0;
|
||||
const canScrollRight =
|
||||
container.scrollLeft + container.clientWidth < container.scrollWidth;
|
||||
|
||||
return () => {
|
||||
if (container) {
|
||||
container.removeEventListener("wheel", handleWheel);
|
||||
container.removeEventListener("mousedown", handleMouseDown);
|
||||
container.removeEventListener("mousemove", handleMouseMove);
|
||||
container.removeEventListener("mouseup", handleMouseUp);
|
||||
container.removeEventListener("mouseleave", handleMouseLeave);
|
||||
}
|
||||
};
|
||||
console.log("isOverflowing:", isOverflowing);
|
||||
console.log("canScrollLeft:", canScrollLeft);
|
||||
console.log("canScrollRight:", canScrollRight);
|
||||
|
||||
setShowLeftArrow(isOverflowing && canScrollLeft);
|
||||
setShowRightArrow(isOverflowing && canScrollRight);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Helper function to format option names (customize as needed)
|
||||
const formatOptionName = (option: string): string => {
|
||||
// Replace underscores with spaces and capitalize the first letter
|
||||
return option.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase());
|
||||
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
|
||||
ref={containerRef}
|
||||
className={`zoon-wrapper ${selectedZone.activeSides.includes("bottom") && "bottom"
|
||||
}`}
|
||||
>
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
console.log('zoneName: ', zoneName);
|
||||
<div className="zoon-wrapper">
|
||||
{/* Left Arrow */}
|
||||
{showLeftArrow && (
|
||||
<button className="arrow left-arrow" onClick={handleScrollLeft}>
|
||||
{"<"}
|
||||
</button>
|
||||
)}
|
||||
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||
widgets: zonesData[zoneName].widgets || [],
|
||||
})
|
||||
// setSelectedZone({
|
||||
// zoneName,
|
||||
// ...zonesData[zoneName],
|
||||
// });
|
||||
console.log(selectedZone);
|
||||
}}
|
||||
>
|
||||
{zoneName}
|
||||
</div>
|
||||
))}
|
||||
{/* Zones Wrapper */}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="zones-wrapper"
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "6px",
|
||||
padding: "4px",
|
||||
borderRadius: "8px",
|
||||
overflowX: "auto",
|
||||
maxWidth: "calc(100% - 10px)", // Uncomment this line if needed
|
||||
}}
|
||||
>
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${
|
||||
selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
console.log("zoneName: ", zoneName);
|
||||
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||
widgets: zonesData[zoneName].widgets || [],
|
||||
});
|
||||
}}
|
||||
>
|
||||
{zoneName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Right Arrow */}
|
||||
{showRightArrow && (
|
||||
<button className="arrow right-arrow" onClick={handleScrollRight}>
|
||||
{">"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,20 +3,58 @@ import ProgressCard from "../realTimeVis/charts/ProgressCard";
|
||||
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
|
||||
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
|
||||
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
|
||||
import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent";
|
||||
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
|
||||
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
|
||||
import {
|
||||
DeleteIcon,
|
||||
DublicateIcon,
|
||||
KebabIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
interface Widget {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
panel: Side;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export const DraggableWidget = ({
|
||||
widget,
|
||||
hiddenPanels, // Add this prop to track hidden panels
|
||||
index, onReorder
|
||||
index,
|
||||
onReorder,
|
||||
openKebabId,
|
||||
setOpenKebabId,
|
||||
selectedZone,
|
||||
setSelectedZone,
|
||||
}: {
|
||||
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[];
|
||||
}>
|
||||
>;
|
||||
widget: any;
|
||||
hiddenPanels: string[]; // Array of hidden panel names
|
||||
index: number; onReorder: (fromIndex: number, toIndex: number) => void
|
||||
index: number;
|
||||
onReorder: (fromIndex: number, toIndex: number) => void;
|
||||
openKebabId: string | null; // ID of the widget with an open kebab menu
|
||||
setOpenKebabId: (id: string | null) => void; // Function to update the open kebab menu
|
||||
}) => {
|
||||
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
|
||||
const handlePointerDown = () => {
|
||||
@@ -29,7 +67,7 @@ export const DraggableWidget = ({
|
||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget
|
||||
event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget
|
||||
};
|
||||
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault(); // Allow drop
|
||||
@@ -41,21 +79,89 @@ export const DraggableWidget = ({
|
||||
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); // Get the dragged widget's index
|
||||
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
|
||||
const toIndex = index; // The index of the widget where the drop occurred
|
||||
if (fromIndex !== toIndex) {
|
||||
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
|
||||
}
|
||||
};
|
||||
// Handle kebab icon click to toggle kebab options
|
||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation(); // Prevent click from propagating to parent elements
|
||||
if (openKebabId === widget.id) {
|
||||
// If the current kebab is already open, close it
|
||||
setOpenKebabId(null);
|
||||
} else {
|
||||
// Open the kebab for the clicked widget and close others
|
||||
setOpenKebabId(widget.id);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSelectedChart = () => {
|
||||
// Filter out the widget to be deleted
|
||||
const updatedWidgets = selectedZone.widgets.filter(
|
||||
(w: Widget) => w.id !== widget.id
|
||||
);
|
||||
|
||||
// Update the selectedZone state
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: updatedWidgets, // Replace the widgets array with the updated one
|
||||
}));
|
||||
|
||||
// Close the kebab menu after deletion
|
||||
setOpenKebabId(null);
|
||||
};
|
||||
const widgetRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
// Handle click outside to close the kebab menu
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
widgetRef.current &&
|
||||
!widgetRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setOpenKebabId(null); // Close the kebab menu if the click is outside
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
|
||||
// Cleanup event listener on component unmount
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [setOpenKebabId]);
|
||||
|
||||
|
||||
const duplicateWidget = () => {
|
||||
// Create a copy of the current widget with a new unique ID
|
||||
const duplicatedWidget: Widget = {
|
||||
...widget,
|
||||
id: `${widget.id}-copy-${Date.now()}`, // Generate a unique ID using a timestamp
|
||||
};
|
||||
|
||||
// Add the duplicated widget to the selectedZone's widgets array
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: [...prevZone.widgets, duplicatedWidget],
|
||||
}));
|
||||
|
||||
// Close the kebab menu after duplication
|
||||
setOpenKebabId(null);
|
||||
|
||||
console.log("Duplicated widget with ID:", duplicatedWidget.id);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
draggable
|
||||
key={widget.id}
|
||||
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
|
||||
}`}
|
||||
className={`chart-container ${
|
||||
selectedChartId?.id === widget.id && "activeChart"
|
||||
}`}
|
||||
onPointerDown={handlePointerDown}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnter={handleDragEnter}
|
||||
@@ -66,6 +172,32 @@ export const DraggableWidget = ({
|
||||
pointerEvents: isPanelHidden ? "none" : "auto", // Disable interaction when hidden
|
||||
}}
|
||||
>
|
||||
{/* Kebab Icon */}
|
||||
<div className="icon kebab" onClick={handleKebabClick}>
|
||||
<KebabIcon />
|
||||
</div>
|
||||
|
||||
{/* Kebab Options */}
|
||||
{openKebabId === widget.id && (
|
||||
<div
|
||||
className="kebab-options"
|
||||
ref={widgetRef} // Attach the ref to the widget container
|
||||
>
|
||||
<div className="edit btn">
|
||||
<div className="icon">
|
||||
<DublicateIcon />
|
||||
</div>
|
||||
<div className="label">Duplicate</div>
|
||||
</div>
|
||||
<div className="edit btn" onClick={deleteSelectedChart}>
|
||||
<div className="icon">
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
<div className="label">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Render charts based on widget type */}
|
||||
{widget.type === "progress" && (
|
||||
<ProgressCard title={widget.title} data={widget.data} />
|
||||
|
||||
@@ -32,6 +32,7 @@ interface PanelProps {
|
||||
}>
|
||||
>;
|
||||
hiddenPanels: string[];
|
||||
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
|
||||
}
|
||||
|
||||
const generateUniqueId = () =>
|
||||
@@ -41,6 +42,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||
selectedZone,
|
||||
setSelectedZone,
|
||||
hiddenPanels,
|
||||
setZonesData,
|
||||
}) => {
|
||||
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||
const [panelDimensions, setPanelDimensions] = useState<{
|
||||
@@ -63,8 +65,9 @@ const Panel: React.FC<PanelProps> = ({
|
||||
case "top":
|
||||
case "bottom":
|
||||
return {
|
||||
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||
}px)`,
|
||||
width: `calc(100% - ${
|
||||
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||
}px)`,
|
||||
height: `${panelSize - 2}px`,
|
||||
left: leftActive ? `${panelSize}px` : "0",
|
||||
right: rightActive ? `${panelSize}px` : "0",
|
||||
@@ -74,8 +77,9 @@ const Panel: React.FC<PanelProps> = ({
|
||||
case "right":
|
||||
return {
|
||||
width: `${panelSize - 2}px`,
|
||||
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||
}px)`,
|
||||
height: `calc(100% - ${
|
||||
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||
}px)`,
|
||||
top: topActive ? `${panelSize}px` : "0",
|
||||
bottom: bottomActive ? `${panelSize}px` : "0",
|
||||
[side]: "0",
|
||||
@@ -98,8 +102,8 @@ const Panel: React.FC<PanelProps> = ({
|
||||
|
||||
if (currentWidgetsCount >= maxCapacity) return;
|
||||
|
||||
console.log('draggedAsset: ', draggedAsset);
|
||||
console.log('panel: ', panel);
|
||||
console.log("draggedAsset: ", draggedAsset);
|
||||
console.log("panel: ", panel);
|
||||
addWidgetToPanel(draggedAsset, panel);
|
||||
};
|
||||
|
||||
@@ -168,7 +172,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||
|
||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
||||
console.log('selectedZone: ', selectedZone);
|
||||
console.log("selectedZone: ", selectedZone);
|
||||
|
||||
setSelectedZone((prev) => {
|
||||
if (!prev) return prev; // Ensure prev is not null
|
||||
@@ -191,9 +195,8 @@ const Panel: React.FC<PanelProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedZone.activeSides.map((side) => (
|
||||
@@ -231,6 +234,10 @@ const Panel: React.FC<PanelProps> = ({
|
||||
onReorder={(fromIndex, toIndex) =>
|
||||
handleReorder(fromIndex, toIndex, side)
|
||||
}
|
||||
openKebabId={openKebabId}
|
||||
setOpenKebabId={setOpenKebabId}
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -241,5 +248,3 @@ const Panel: React.FC<PanelProps> = ({
|
||||
};
|
||||
|
||||
export default Panel;
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { getZonesApi } from "../../../services/realTimeVisulization/zoneData/getZones";
|
||||
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
type FormattedZoneData = Record<
|
||||
@@ -41,7 +40,87 @@ const RealTimeVisulization: React.FC = () => {
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||
const [zonesData, setZonesData] = useState<{
|
||||
[key: string]: {
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
lockedPanels: Side[];
|
||||
widgets: Widget[];
|
||||
};
|
||||
}>({
|
||||
"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: [],
|
||||
},
|
||||
"Inventory 1": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 2": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 3": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 4": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 5": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 6": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
"Inventory 7": {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
widgets: [],
|
||||
},
|
||||
});
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
|
||||
// useEffect(() => {
|
||||
@@ -73,10 +152,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||
// GetZoneData();
|
||||
// }, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
console.log('zonesData: ', zonesData);
|
||||
}, [zonesData]);
|
||||
useEffect(() => {}, [zonesData]);
|
||||
|
||||
// useEffect(() => {
|
||||
// setZonesData((prev) => {
|
||||
@@ -111,7 +187,8 @@ const RealTimeVisulization: React.FC = () => {
|
||||
style={{
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
borderRadius:
|
||||
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||
}}
|
||||
>
|
||||
<Scene />
|
||||
@@ -137,6 +214,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||
selectedZone={selectedZone}
|
||||
setSelectedZone={setSelectedZone}
|
||||
hiddenPanels={hiddenPanels}
|
||||
setZonesData={setZonesData}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -39,7 +39,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
||||
useEffect(() => {
|
||||
async function GetZoneData() {
|
||||
const response = await getZonesApi("hexrfactory")
|
||||
console.log('response: ', response.data);
|
||||
|
||||
setZoneDataList([{ id: "1", name: "zone1" },
|
||||
{ id: "2", name: "Zone 2" },])
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ interface ListProps {
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({ items = [] }) => {
|
||||
console.log('items: ', items);
|
||||
return (
|
||||
<>
|
||||
{items.length > 0 ? (
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
min-width: 250px;
|
||||
// min-width: 1450px;
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@use "../abstracts/variables.scss" as *;
|
||||
@use "../abstracts/mixins.scss" as *;
|
||||
|
||||
// Main Container
|
||||
.realTime-viz {
|
||||
@@ -43,15 +44,28 @@
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
gap: 6px;
|
||||
padding: 4px;
|
||||
|
||||
border-radius: 8px;
|
||||
max-width: 80%;
|
||||
overflow: auto;
|
||||
max-width: calc(100% - 450px);
|
||||
// max-width: calc(100% - 450px);
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
background-color: var(--accent-color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.zones-wrapper {
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.zone {
|
||||
width: auto;
|
||||
background-color: var(--background-color);
|
||||
@@ -155,7 +169,8 @@
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
@@ -176,6 +191,65 @@
|
||||
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
|
||||
padding: 6px 0;
|
||||
background-color: white;
|
||||
position: relative;
|
||||
|
||||
.kebab {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
z-index: 10;
|
||||
|
||||
@include flex-center;
|
||||
}
|
||||
|
||||
.kebab-options {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: -100px;
|
||||
transform: translate(0px, 0);
|
||||
background-color: var(--background-color);
|
||||
z-index: 10;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
border-radius: 4px;
|
||||
|
||||
box-shadow: var(--box-shadow-medium);
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
color: var(--text-color);
|
||||
|
||||
.label {
|
||||
&:hover {
|
||||
color: var(--accent-color);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-accent-color);
|
||||
width: 100%;
|
||||
|
||||
svg {
|
||||
&:first-child {
|
||||
fill: var(--accent-color);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
fill: auto;
|
||||
stroke: var(--accent-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
@@ -236,11 +310,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.playingFlase{
|
||||
.zoon-wrapper{
|
||||
.playingFlase {
|
||||
.zoon-wrapper {
|
||||
bottom: 300px !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Side Buttons
|
||||
.side-button-container {
|
||||
position: absolute;
|
||||
@@ -401,3 +476,40 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
padding: 6px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.left-arrow {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right-arrow {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.zone {
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zone.active {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
Reference in New Issue
Block a user