floatingwidgets Api and 3dwidget frontend and 2d widget delete Api and zonecameraUpdation

This commit is contained in:
2025-03-29 19:21:20 +05:30
parent 71310bdbdd
commit 6ccdb28f52
34 changed files with 792 additions and 327 deletions

View File

@@ -119,7 +119,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
};
// Delete the selectedZone state
console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone);
} else {
const updatePanelData = async () => {
@@ -141,13 +141,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
// API call
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
console.log("response: ", response);
// Update state
console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone);
} catch (error) {
console.error("Error updating panel data:", error);
}
};

View File

@@ -4,6 +4,7 @@ import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
@@ -147,23 +148,41 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
};
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
let response = await getSelect2dZoneData(zoneId, organization)
setSelectedZone({
zoneName,
activeSides: response.activeSides,
panelOrder: response.panelOrder,
lockedPanels: response.lockedPanels,
widgets: response.widgets,
zoneId: zoneId,
zoneViewPortTarget:
response.viewPortCenter,
zoneViewPortPosition:
response.viewPortposition,
});
try {
if (selectedZone?.zoneId === zoneId) {
console.log("Zone is already selected:", zoneName);
return;
}
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
// Fetch data from backend
let response = await getSelect2dZoneData(zoneId, organization);
let res = await getFloatingZoneData(zoneId, organization);
// Set the selected zone in the store
useDroppedObjectsStore.getState().setZone(zoneName, zoneId);
if (Array.isArray(res)) {
res.forEach((val) => {
useDroppedObjectsStore.getState().addObject(zoneName, val);
});
}
// Update selected zone state
setSelectedZone({
zoneName,
activeSides: response.activeSides || [],
panelOrder: response.panelOrder || [],
lockedPanels: response.lockedPanels || [],
widgets: response.widgets || [],
zoneId: zoneId,
zoneViewPortTarget: response.viewPortCenter || {},
zoneViewPortPosition: response.viewPortposition || {},
});
} catch (error) {
console.log('error: ', error);
}
}
return (
<div
className={`zone-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
@@ -185,7 +204,6 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
}`}
onClick={() => {
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)
}}
>

View File

@@ -11,6 +11,8 @@ import {
KebabIcon,
} from "../../icons/ExportCommonIcons";
import { useEffect, useRef, useState } from "react";
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
type Side = "top" | "bottom" | "left" | "right";
@@ -34,9 +36,9 @@ export const DraggableWidget = ({
}: {
selectedZone: {
zoneName: string;
zoneId: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
@@ -79,21 +81,28 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = () => {
console.log('widget.id: ', widget.id);
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
console.log('updatedWidgets: ', updatedWidgets);
const deleteSelectedChart = async () => {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const response = await deleteWidgetApi(widget.id, organization);
if (response?.message === "Widget deleted successfully") {
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
}
} catch (error) {
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
setOpenKebabId(null);
} finally {
setOpenKebabId(null);
}
};
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
@@ -121,21 +130,32 @@ export const DraggableWidget = ({
return currentWidgetCount >= panelCapacity;
};
const duplicateWidget = () => {
const duplicatedWidget: Widget = {
...widget,
id: `${widget.id}-copy-${Date.now()}`,
};
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
const duplicateWidget = async () => {
try {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
setOpenKebabId(null);
const duplicatedWidget: Widget = {
...widget,
id: `${widget.id}-copy-${Date.now()}`,
};
const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget);
if (response?.message === "Widget created successfully") {
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
}
} catch (error) {
} finally {
setOpenKebabId(null);
}
};
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
if (openKebabId === widget.id) {
@@ -176,7 +196,6 @@ 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 toIndex = index; // The index of the widget where the drop occurred

View File

@@ -1,7 +1,7 @@
import { useThree } from "@react-three/fiber";
import React, { useState, useEffect } from "react";
import { useAsset3dWidget } from "../../../store/store";
import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore";
import { ThreeState } from "../../../types/world/worldTypes";
import * as THREE from "three";
@@ -9,71 +9,80 @@ import Throughput from "../../layout/3D-cards/cards/Throughput";
import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
export default function Dropped3dWidgets() {
const { widgetSelect } = useAsset3dWidget();
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore();
const { raycaster, gl, scene }: ThreeState = useThree();
const { selectedZone } = useSelectedZoneStore(); // Get currently selected zone
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption()
// 🔥 Store widget positions per zone
const [zoneWidgets, setZoneWidgets] = useState<Record<
string, // Zone ID
Record<string, [number, number, number][]> // Widget type -> Positions array
>>({});
// 🔥 Store multiple instances per widget type
const [widgetPositions, setWidgetPositions] = useState<Record<string, [number, number, number][]>>({});
useEffect(() => {
if (activeModule !== "visualization") return;
if (widgetSubOption === "Floating") return
// if (activeModule !== "visualization") return;
const canvasElement = gl.domElement;
const onDrop = (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior
if (widgetSubOption === "3D") {
if (selectedZone.zoneName === "") return
if (!widgetSelect?.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
const Assets = group1.children
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
.filter(Boolean) as THREE.Object3D[];
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
const Assets = group1.children
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
.filter(Boolean) as THREE.Object3D[];
const intersects = raycaster.intersectObjects(Assets);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
// ✅ Allow multiple instances by storing positions in an array
setWidgetPositions((prev) => ({
...prev,
[widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]],
}));
setZoneWidgets((prev) => ({
...prev,
[selectedZone.zoneId]: {
...(prev[selectedZone.zoneId] || {}),
[widgetSelect]: [...(prev[selectedZone.zoneId]?.[widgetSelect] || []), [x, y, z]],
},
}));
}
}
};
canvasElement.addEventListener("drop", onDrop);
return () => {
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("drop", onDrop)
// setWidgetSelect()
};
}, [widgetSelect, activeModule]);
}, [widgetSelect, activeModule, widgetSubOption]);
return (
<>
{widgetPositions["ui-Widget 1"]?.map((pos, index) => (
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 1"]?.map((pos, index) => (
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 2"]?.map((pos, index) => (
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 2"]?.map((pos, index) => (
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 3"]?.map((pos, index) => (
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 3"]?.map((pos, index) => (
<StateWorking key={`Widget3-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 4"]?.map((pos, index) => (
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 4"]?.map((pos, index) => (
<Throughput key={`Widget4-${index}`} position={pos} />
))}
</>
);
}

View File

@@ -1,59 +1,79 @@
import { WalletIcon } from "../../icons/3dChartIcons";
import { useEffect, useRef, useState } from "react";
import { Line } from "react-chartjs-2";
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
import {
useDroppedObjectsStore,
Zones,
} from "../../../store/useDroppedObjectsStore";
import useModuleStore from "../../../store/useModuleStore";
import { determinePosition } from "./functions/determinePosition";
import { getActiveProperties } from "./functions/getActiveProperties";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
const DroppedObjects: React.FC = () => {
const zones = useDroppedObjectsStore((state) => state.zones);
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
const updateObjectPosition = useDroppedObjectsStore(
(state) => state.updateObjectPosition
);
const [draggingIndex, setDraggingIndex] = useState<{
zone: string;
index: number;
} | null>(null);
const [offset, setOffset] = useState<[number, number] | null>(null);
const positionRef = useRef<[number, number] | null>(null);
const animationRef = useRef<number | null>(null);
const { activeModule } = useModuleStore();
// useEffect(() => {
// const initialZones: Record<string, Zones> = {
// "Zone 1": {
// zoneName: "Zone 1",
// zoneId: "2e996073-546c-470c-8323-55bd3700c6aa",
// objects: [
// {
// header: "Todays Money",
// value: 53000, // ✅ Converted to number
// per: "+55%",
// className: "floating total-card",
// position: [146, 214], // ✅ No need for 'as' here
// },
// {
// header: "New Clients",
// value: 250, // ✅ Converted to number
// per: "+12%",
// className: "floating total-card",
// position: [344, 295],
// },
// ],
// },
// };
// useDroppedObjectsStore.setState({ zones: initialZones });
// }, []);
// Get the first zone and its objects
const zoneEntries = Object.entries(zones);
if (zoneEntries.length === 0) return null; // No zone, nothing to render
const [zoneName, zone] = zoneEntries[0]; // Only render the first zone
// Handle pointer down event
function handlePointerDown(event: React.PointerEvent, index: number) {
const obj = zone.objects[index];
const offsetX = event.clientX - obj.position[1];
const offsetY = event.clientY - obj.position[0];
const container = document.getElementById("real-time-vis-canvas");
if (!container) return;
const rect = container.getBoundingClientRect();
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Determine which properties are active for this object
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
// Calculate the offset based on the active properties
let offsetX = 0;
let offsetY = 0;
if (activeProp1 === "top") {
offsetY =
relativeY -
(typeof obj.position.top === "number" ? obj.position.top : 0);
} else if (activeProp1 === "bottom") {
offsetY =
rect.height -
relativeY -
(typeof obj.position.bottom === "number" ? obj.position.bottom : 0);
}
if (activeProp2 === "left") {
offsetX =
relativeX -
(typeof obj.position.left === "number" ? obj.position.left : 0);
} else if (activeProp2 === "right") {
offsetX =
rect.width -
relativeX -
(typeof obj.position.right === "number" ? obj.position.right : 0);
}
setDraggingIndex({ zone: zoneName, index });
setOffset([offsetY, offsetX]);
}
// Handle pointer move event
function handlePointerMove(event: React.PointerEvent) {
if (!draggingIndex || !offset) return;
@@ -61,34 +81,92 @@ const DroppedObjects: React.FC = () => {
if (!container) return;
const rect = container.getBoundingClientRect();
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
let newX = event.clientX - offset[1];
let newY = event.clientY - offset[0];
// Determine which properties are active for the dragged object
const obj = zone.objects[draggingIndex.index];
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
// Calculate the new position based on the active properties
let newX = 0;
let newY = 0;
if (activeProp2 === "left") {
newX = relativeX - offset[1];
} else if (activeProp2 === "right") {
newX = rect.width - (relativeX + offset[1]);
}
if (activeProp1 === "top") {
newY = relativeY - offset[0];
} else if (activeProp1 === "bottom") {
newY = rect.height - (relativeY + offset[0]);
}
// Ensure the object stays within the canvas boundaries
newX = Math.max(0, Math.min(rect.width - 50, newX));
newY = Math.max(0, Math.min(rect.height - 50, newY));
// Update the position reference
positionRef.current = [newY, newX];
// Update the object's position using requestAnimationFrame for smoother animations
if (!animationRef.current) {
animationRef.current = requestAnimationFrame(() => {
if (positionRef.current) {
updateObjectPosition(zoneName, draggingIndex.index, positionRef.current);
updateObjectPosition(zoneName, draggingIndex.index, {
...obj.position,
[activeProp1]: positionRef.current[0],
[activeProp2]: positionRef.current[1],
});
}
animationRef.current = null;
});
}
}
function handlePointerUp() {
setDraggingIndex(null);
setOffset(null);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
// Handle pointer up event
async function handlePointerUp(event: React.MouseEvent<HTMLDivElement>) {
try {
if (!draggingIndex || !offset) return;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const container = document.getElementById("real-time-vis-canvas");
if (!container) throw new Error("Canvas container not found");
const rect = container.getBoundingClientRect();
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
// Recalculate the position using determinePosition
const newPosition = determinePosition(rect, relativeX, relativeY);
// Validate the dragging index and get the object
if (!zone.objects[draggingIndex.index]) {
throw new Error("Dragged object not found in the zone");
}
const obj = { ...zone.objects[draggingIndex.index], position: newPosition };
let response = await addingFloatingWidgets(zone.zoneId, organization, obj);
if (response.message === "Widget updated successfully") {
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
}
// Reset states
setDraggingIndex(null);
setOffset(null);
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
}
} catch (error) {
}
}
return (
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
{zone.objects.map((obj, index) => (
@@ -96,14 +174,29 @@ const DroppedObjects: React.FC = () => {
key={`${zoneName}-${index}`}
className={obj.className}
style={{
top: obj.position[0] + "px",
left: obj.position[1] + "px",
transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
position: "absolute",
top:
typeof obj.position.top !== "string"
? `${obj.position.top}px`
: "auto",
left:
typeof obj.position.left !== "string"
? `${obj.position.left}px`
: "auto",
right:
typeof obj.position.right !== "string"
? `${obj.position.right}px`
: "auto",
bottom:
typeof obj.position.bottom !== "string"
? `${obj.position.bottom}px`
: "auto",
// transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
}}
onPointerDown={(event) => handlePointerDown(event, index)}
>
{obj.className === "floating total-card" ? (
<div>
<>
<div className="header-wrapper">
<div className="header">{obj.header}</div>
<div className="data-values">
@@ -114,9 +207,9 @@ const DroppedObjects: React.FC = () => {
<div className="icon">
<WalletIcon />
</div>
</div>
</>
) : obj.className === "warehouseThroughput floating" ? (
<div>
<>
<div className="header">
<h2>Warehouse Throughput</h2>
<p>
@@ -126,9 +219,9 @@ const DroppedObjects: React.FC = () => {
<div className="lineGraph" style={{ height: "100%" }}>
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
</div>
</div>
</>
) : obj.className === "fleetEfficiency floating" ? (
<div>
<>
<h2 className="header">Fleet Efficiency</h2>
<div className="progressContainer">
<div className="progress">
@@ -148,7 +241,7 @@ const DroppedObjects: React.FC = () => {
</div>
<span>100%</span>
</div>
</div>
</>
) : null}
</div>
))}
@@ -156,59 +249,4 @@ const DroppedObjects: React.FC = () => {
);
};
export default DroppedObjects;
// import { Html } from "@react-three/drei";
// import { useDroppedObjectsStore } from "../../../store/store";
// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons";
// import SimpleCard from "../realTimeVis/floating/SimpleCard";
// const ICON_MAP: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
// WalletIcon,
// GlobeIcon,
// DocumentIcon,
// CartIcon
// };
// const DroppedObjects: React.FC = () => {
// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
//
// return (
// <>
// {objects.map((obj, index) => {
// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists
// return (
// <SimpleCard
// key={index}
// position={obj.position}
// header={obj.header}
// icon={IconComponent} // ✅ No need to look it up in ICON_MAP
// value={obj.value}
// per={obj.per}
// />
// );
// })}
// </>
// );
// };
// export default DroppedObjects;

View File

@@ -149,7 +149,7 @@ const Panel: React.FC<PanelProps> = ({
};
try {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
console.log("response: ", response);
if (response.message === "Widget created successfully") {
setSelectedZone((prev) => ({
...prev,

View File

@@ -9,9 +9,11 @@ import useModuleStore from "../../../store/useModuleStore";
import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { useAsset3dWidget, useZones } from "../../../store/store";
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
import { useAsset3dWidget, useWidgetSubOption, useZones } from "../../../store/store";
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
import { generateUniqueId } from "../../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
type Side = "top" | "bottom" | "left" | "right";
@@ -48,6 +50,8 @@ const RealTimeVisulization: React.FC = () => {
const { zones } = useZones()
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption()
useEffect(() => {
async function GetZoneData() {
const email = localStorage.getItem("email") || "";
@@ -72,12 +76,12 @@ const RealTimeVisulization: React.FC = () => {
setZonesData(formattedData);
} catch (error) {
console.log('error: ', error);
}
}
GetZoneData();
}, []); // Removed `zones` from dependencies
}, [activeModule]); // Removed `zones` from dependencies
useEffect(() => {
setZonesData((prev) => {
@@ -97,48 +101,68 @@ const RealTimeVisulization: React.FC = () => {
};
});
}, [selectedZone]);
useEffect(() => {
}, [floatingWidgets])
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const data = event.dataTransfer.getData("text/plain");
if (widgetSelect !== "") return;
if (!data || selectedZone.zoneName === "") return;
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
try {
event.preventDefault();
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const droppedData = JSON.parse(data);
const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) return;
const data = event.dataTransfer.getData("text/plain");
// if (widgetSelect !== "") return;
if (widgetSubOption === "3D") return
if (!data || selectedZone.zoneName === "") return;
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top;
const droppedData = JSON.parse(data);
const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) throw new Error("Canvas element not found");
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top;
const newObject = {
...droppedData,
id: generateUniqueId(),
position: determinePosition(canvasRect, relativeX, relativeY),
};
let response = await addingFloatingWidgets(selectedZone.zoneId, organization, newObject);
// Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) {
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
}
// Add the dropped object to the zone if the API call is successful
if (response.message === "FloatWidget created successfully") {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
}
// Update floating widgets state
setFloatingWidgets((prevWidgets) => ({
...prevWidgets,
[selectedZone.zoneName]: {
...prevWidgets[selectedZone.zoneName],
zoneName: selectedZone.zoneName,
zoneId: selectedZone.zoneId,
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
},
}));
} catch (error) {
const newObject = {
...droppedData,
position: [relativeY, relativeX], // Y first because of top/left style
};
// Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) {
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
}
// Add the dropped object to the zone
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
setFloatingWidgets((prevWidgets) => ({
...prevWidgets,
[selectedZone.zoneName]: {
...prevWidgets[selectedZone.zoneName],
zoneName: selectedZone.zoneName,
zoneId: selectedZone.zoneId,
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
},
}));
};
return (
<div
ref={containerRef}

View File

@@ -0,0 +1,41 @@
import { getActiveProperties } from "./getActiveProperties";
export const convertAutoToNumeric = (
canvasRect: DOMRect,
position: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
}
): { top: number; left: number; right: number; bottom: number } => {
const { width, height } = canvasRect;
// Determine which properties are active
const [activeProp1, activeProp2] = getActiveProperties(position);
let top = typeof position.top !== "string" ? position.top : 0;
let left = typeof position.left !== "string" ? position.left : 0;
let right = typeof position.right !== "string" ? position.right : 0;
let bottom = typeof position.bottom !== "string" ? position.bottom : 0;
// Calculate missing properties based on active properties
if (activeProp1 === "top") {
bottom = height - top;
} else if (activeProp1 === "bottom") {
top = height - bottom;
}
if (activeProp2 === "left") {
right = width - left;
} else if (activeProp2 === "right") {
left = width - right;
}
return {
top,
left,
right,
bottom,
};
};

View File

@@ -0,0 +1,64 @@
export function determinePosition(
canvasRect: DOMRect,
relativeX: number,
relativeY: number
): {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
} {
// Calculate the midpoints of the canvas
const centerX = canvasRect.width / 2;
const centerY = canvasRect.height / 2;
// Initialize position with default values
let position: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
};
if (relativeY < centerY) {
// Top half
if (relativeX < centerX) {
// Left side
position = {
top: relativeY,
left: relativeX,
right: "auto",
bottom: "auto",
};
} else {
// Right side
position = {
top: relativeY,
right: canvasRect.width - relativeX,
left: "auto",
bottom: "auto",
};
}
} else {
// Bottom half
if (relativeX < centerX) {
// Left side
position = {
bottom: canvasRect.height - relativeY,
left: relativeX,
right: "auto",
top: "auto",
};
} else {
// Right side
position = {
bottom: canvasRect.height - relativeY,
right: canvasRect.width - relativeX,
left: "auto",
top: "auto",
};
}
}
return position;
}

View File

@@ -0,0 +1,17 @@
export const getActiveProperties = (position: {
top: number | "auto";
left: number | "auto";
right: number | "auto";
bottom: number | "auto";
}) => {
let activeProps: ["top", "left"] | ["bottom", "right"] = ["top", "left"]; // Default to top-left
if (
typeof position.bottom !== "string" &&
typeof position.right !== "string"
) {
activeProps = ["bottom", "right"];
}
return activeProps;
};

View File

@@ -14,6 +14,7 @@ export default function ZoneCentreTarget() {
const { Edit, setEdit } = useEditPosition();
useEffect(() => {
if (
selectedZone.zoneViewPortTarget &&
@@ -39,22 +40,22 @@ export default function ZoneCentreTarget() {
if (centrePoint.length > 0) {
let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
// let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
// let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
// const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
const worldUp = new THREE.Vector3(0, 0, 1);
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
const offsetPosition = up.clone().multiplyScalar(20);
camPosition.add(offsetPosition);
// const worldUp = new THREE.Vector3(0, 0, 1);
// const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
// const up = new THREE.Vector3().crossVectors(direction, right).normalize();
// const offsetPosition = up.clone().multiplyScalar(20);
// camPosition.add(offsetPosition);
const setCam = async () => {
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
setTimeout(() => {
controls?.setLookAt(
...camPosition.toArray(),
...selectedZone.zoneViewPortPosition,
selectedZone.zoneViewPortTarget[0],
selectedZone.zoneViewPortTarget[1],
selectedZone.zoneViewPortTarget[2],
@@ -65,21 +66,9 @@ export default function ZoneCentreTarget() {
setCam();
} else {
let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
const worldUp = new THREE.Vector3(0, 0, 1);
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
const offsetPosition = up.clone().multiplyScalar(20);
camPosition.add(offsetPosition);
const setCam = async () => {
controls?.setLookAt(
...camPosition.toArray(),
...selectedZone.zoneViewPortPosition,
selectedZone.zoneViewPortTarget[0],
selectedZone.zoneViewPortTarget[1],
selectedZone.zoneViewPortTarget[2],