Added kebab for delete widgets

This commit is contained in:
Nalvazhuthi
2025-03-27 10:54:40 +05:30
parent 5ffb952637
commit a73c893040
11 changed files with 580 additions and 155 deletions

View File

@@ -53,7 +53,6 @@ const PieChartComponent = ({
.getPropertyValue("--accent-color")
.trim();
console.log("accentColor: ", accentColor);
const options = useMemo(
() => ({
responsive: true,

View File

@@ -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>
);
};

View File

@@ -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} />

View File

@@ -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;

View File

@@ -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}
/>
</>
)}

View File

@@ -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" },])
}

View File

@@ -8,7 +8,6 @@ interface ListProps {
}
const List: React.FC<ListProps> = ({ items = [] }) => {
console.log('items: ', items);
return (
<>
{items.length > 0 ? (