Merge pull request 'ui' (#33) from ui into main
Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/33
This commit is contained in:
commit
9c141726ef
|
@ -0,0 +1,32 @@
|
|||
// ConfirmationDialog.tsx
|
||||
import React from "react";
|
||||
|
||||
interface ConfirmationPopupProps {
|
||||
message: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const ConfirmationPopup: React.FC<ConfirmationPopupProps> = ({
|
||||
message,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}) => {
|
||||
return (
|
||||
<div className="confirmation-overlay">
|
||||
<div className="confirmation-modal">
|
||||
<p className="message">{message}</p>
|
||||
<div className="buttton-wrapper">
|
||||
<div className="confirmation-button" onClick={onConfirm}>
|
||||
OK
|
||||
</div>
|
||||
<div className="confirmation-button" onClick={onCancel}>
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmationPopup;
|
|
@ -119,7 +119,7 @@ const Data = () => {
|
|||
};
|
||||
});
|
||||
};
|
||||
// console.log("selectedChartId", selectedChartId);
|
||||
|
||||
|
||||
return (
|
||||
<div className="dataSideBar">
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
|
||||
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
|
||||
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||
import { useSocketStore } from "../../../store/store";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
@ -83,6 +84,8 @@ export const DraggableWidget = ({
|
|||
}
|
||||
};
|
||||
|
||||
const chartWidget = useRef<HTMLDivElement>(null);
|
||||
|
||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||
|
||||
const deleteSelectedChart = async () => {
|
||||
|
@ -118,13 +121,11 @@ export const DraggableWidget = ({
|
|||
// }));
|
||||
// }
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setOpenKebabId(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const getCurrentWidgetCount = (panel: Side) =>
|
||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||
|
||||
|
@ -162,7 +163,11 @@ export const DraggableWidget = ({
|
|||
id: `${widget.id}-copy-${Date.now()}`,
|
||||
};
|
||||
|
||||
const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget);
|
||||
const response = await duplicateWidgetApi(
|
||||
selectedZone.zoneId,
|
||||
organization,
|
||||
duplicatedWidget
|
||||
);
|
||||
|
||||
if (response?.message === "Widget created successfully") {
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
|
@ -171,13 +176,11 @@ export const DraggableWidget = ({
|
|||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setOpenKebabId(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
if (openKebabId === widget.id) {
|
||||
|
@ -227,22 +230,29 @@ export const DraggableWidget = ({
|
|||
};
|
||||
console.log("widget.type", widget.type);
|
||||
|
||||
// useClickOutside(chartWidget, () => {
|
||||
// setSelectedChartId(null);
|
||||
// });
|
||||
|
||||
console.log('isPanelHidden: ', isPanelHidden);
|
||||
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}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
style={{
|
||||
opacity: isPanelHidden ? 0 : 1,
|
||||
pointerEvents: isPanelHidden ? "none" : "auto",
|
||||
}}
|
||||
ref={chartWidget}
|
||||
onClick={() => setSelectedChartId(widget)}
|
||||
>
|
||||
{/* Kebab Icon */}
|
||||
<div className="icon kebab" onClick={handleKebabClick}>
|
||||
|
@ -253,8 +263,9 @@ export const DraggableWidget = ({
|
|||
{openKebabId === widget.id && (
|
||||
<div className="kebab-options" ref={widgetRef}>
|
||||
<div
|
||||
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
className={`edit btn ${
|
||||
isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||
}`}
|
||||
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
|
||||
>
|
||||
<div className="icon">
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store";
|
||||
|
@ -16,22 +15,25 @@ import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zone
|
|||
import { use3DWidget } from "../../../store/useDroppedObjectsStore";
|
||||
|
||||
export default function Dropped3dWidgets() {
|
||||
const { widgetSelect } = useAsset3dWidget();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { raycaster, gl, scene }: ThreeState = useThree();
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||
const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone
|
||||
// 🔥 Store widget data (id, type, position) based on the selected zone
|
||||
const [zoneWidgetData, setZoneWidgetData] = useState<
|
||||
Record<string, { id: string; type: string; position: [number, number, number] }[]>
|
||||
>({});
|
||||
const { setWidgets3D } = use3DWidget()
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return
|
||||
if (selectedZone.zoneName === "") return;
|
||||
const { widgetSelect } = useAsset3dWidget();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { raycaster, gl, scene }: ThreeState = useThree();
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||
const { selectedZone } = useSelectedZoneStore(); // Get the currently active zone
|
||||
// 🔥 Store widget data (id, type, position) based on the selected zone
|
||||
const [zoneWidgetData, setZoneWidgetData] = useState<
|
||||
Record<
|
||||
string,
|
||||
{ id: string; type: string; position: [number, number, number] }[]
|
||||
>
|
||||
>({});
|
||||
const { setWidgets3D } = use3DWidget();
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (selectedZone.zoneName === "") return;
|
||||
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
async function get3dWidgetData() {
|
||||
let result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
|
||||
|
@ -44,71 +46,94 @@ export default function Dropped3dWidgets() {
|
|||
position: widget.position,
|
||||
}));
|
||||
|
||||
setZoneWidgetData((prev) => ({
|
||||
...prev,
|
||||
[selectedZone.zoneId]: formattedWidgets,
|
||||
}));
|
||||
}
|
||||
setZoneWidgetData((prev) => ({
|
||||
...prev,
|
||||
[selectedZone.zoneId]: formattedWidgets,
|
||||
}));
|
||||
}
|
||||
|
||||
get3dWidgetData();
|
||||
get3dWidgetData();
|
||||
}, [selectedZone.zoneId, activeModule]);
|
||||
// useEffect(() => {
|
||||
// // ✅ Set data only for the selected zone, keeping existing state structure
|
||||
// setZoneWidgetData((prev) => ({
|
||||
// ...prev,
|
||||
// [selectedZone.zoneId]: [
|
||||
// {
|
||||
// "id": "1743322674626-50mucpb1c",
|
||||
// "type": "ui-Widget 1",
|
||||
// "position": [120.94655021768133, 4.142360029666558, 124.39283546121099]
|
||||
// },
|
||||
// {
|
||||
// "id": "1743322682086-je2h9x33v",
|
||||
// "type": "ui-Widget 2",
|
||||
// "position": [131.28751045879255, 0.009999999999970264, 133.92059801984362]
|
||||
// }
|
||||
// ]
|
||||
// }));
|
||||
// }, [selectedZone.zoneId]); // ✅ Only update when the zone changes
|
||||
|
||||
}, [selectedZone.zoneId, activeModule]);
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (widgetSubOption === "Floating") return;
|
||||
if (selectedZone.zoneName === "") return;
|
||||
const canvasElement = gl.domElement;
|
||||
const onDrop = async (event: DragEvent) => {
|
||||
event.preventDefault(); // Prevent default browser behavior
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
if (!widgetSelect.startsWith("ui")) return;
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
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;
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (widgetSubOption === "Floating") return;
|
||||
if (widgetSubOption === "2D") return;
|
||||
if (selectedZone.zoneName === "") return
|
||||
const canvasElement = gl.domElement;
|
||||
const onDrop = async (event: DragEvent) => {
|
||||
event.preventDefault(); // Prevent default browser behavior
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
if (!widgetSelect.startsWith("ui")) return;
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
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;
|
||||
|
||||
// ✅ Explicitly define position as a tuple
|
||||
const newWidget: { id: string; type: string; position: [number, number, number] } = {
|
||||
id: generateUniqueId(),
|
||||
type: widgetSelect,
|
||||
position: [x, y, z], // Ensures TypeScript recognizes it as a tuple
|
||||
};
|
||||
|
||||
|
||||
let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
|
||||
console.log('response: ', response);
|
||||
|
||||
if (response.message === "Widget created successfully") {
|
||||
// ✅ Store widgets uniquely for each zone
|
||||
setZoneWidgetData((prev) => ({
|
||||
...prev,
|
||||
[selectedZone.zoneId]: [...(prev[selectedZone.zoneId] || []), newWidget],
|
||||
}));
|
||||
}
|
||||
}
|
||||
// ✅ Explicitly define position as a tuple
|
||||
const newWidget: {
|
||||
id: string;
|
||||
type: string;
|
||||
position: [number, number, number];
|
||||
} = {
|
||||
id: generateUniqueId(),
|
||||
type: widgetSelect,
|
||||
position: [x, y, z], // Ensures TypeScript recognizes it as a tuple
|
||||
};
|
||||
|
||||
let response = await adding3dWidgets(
|
||||
selectedZone.zoneId,
|
||||
organization,
|
||||
newWidget
|
||||
);
|
||||
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
};
|
||||
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
|
||||
// ✅ Store widgets uniquely for each zone
|
||||
setZoneWidgetData((prev) => ({
|
||||
...prev,
|
||||
[selectedZone.zoneId]: [
|
||||
...(prev[selectedZone.zoneId] || []),
|
||||
newWidget,
|
||||
],
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Get widgets for the currently active zone
|
||||
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
|
||||
console.log('activeZoneWidgets: ', activeZoneWidgets);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
};
|
||||
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]);
|
||||
|
||||
// Get widgets for the currently active zone
|
||||
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -130,6 +155,3 @@ export default function Dropped3dWidgets() {
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
|
@ -21,6 +20,9 @@ import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent";
|
|||
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
|
||||
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
|
||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||
interface DraggingState {
|
||||
zone: string;
|
||||
index: number;
|
||||
|
@ -43,19 +45,24 @@ interface DraggingState {
|
|||
};
|
||||
}
|
||||
const DroppedObjects: React.FC = () => {
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||
const updateObjectPosition = useDroppedObjectsStore(
|
||||
(state) => state.updateObjectPosition
|
||||
);
|
||||
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
|
||||
|
||||
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
|
||||
|
||||
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
|
||||
const duplicateObject = useDroppedObjectsStore(
|
||||
(state) => state.duplicateObject
|
||||
);
|
||||
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
|
||||
null
|
||||
);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
const { setSelectedChartId } = useWidgetStore();
|
||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||
const [activeEdges, setActiveEdges] = useState<{
|
||||
vertical: "top" | "bottom";
|
||||
horizontal: "left" | "right";
|
||||
|
@ -68,6 +75,11 @@ const DroppedObjects: React.FC = () => {
|
|||
} | null>(null); // State to track the current position during drag
|
||||
const animationRef = useRef<number | null>(null);
|
||||
const { activeModule } = useModuleStore();
|
||||
const chartWidget = useRef<HTMLDivElement>(null);
|
||||
|
||||
// useClickOutside(chartWidget, () => {
|
||||
// setSelectedChartId(null);
|
||||
// });
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
||||
|
@ -100,7 +112,7 @@ const DroppedObjects: React.FC = () => {
|
|||
const [zoneName, zone] = zoneEntries[0];
|
||||
|
||||
function handleDuplicate(zoneName: string, index: number) {
|
||||
setOpenKebabId(null)
|
||||
setOpenKebabId(null);
|
||||
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
|
||||
}
|
||||
|
||||
|
@ -110,17 +122,13 @@ const DroppedObjects: React.FC = () => {
|
|||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
let res = await deleteFloatingWidgetApi(id, organization);
|
||||
console.log('res: ', res);
|
||||
|
||||
if (res.message === "FloatingWidget deleted successfully") {
|
||||
deleteObject(zoneName, id, index); // Call the deleteObject method from the store
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error deleting floating widget:", error);
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
|
||||
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
||||
if ((event.target as HTMLElement).closest(".kebab-options") || (event.target as HTMLElement).closest(".kebab")) {
|
||||
return; // Prevent dragging when clicking on the kebab menu or its options
|
||||
|
@ -167,11 +175,150 @@ const DroppedObjects: React.FC = () => {
|
|||
}
|
||||
|
||||
setOffset([offsetY, offsetX]);
|
||||
|
||||
// Add native event listeners for smoother tracking
|
||||
const handlePointerMoveNative = (e: PointerEvent) => {
|
||||
if (!draggingIndex || !offset) return;
|
||||
if (isPlaying === true) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relativeX = e.clientX - rect.left;
|
||||
const relativeY = e.clientY - rect.top;
|
||||
|
||||
// Use requestAnimationFrame for smooth updates
|
||||
animationRef.current = requestAnimationFrame(() => {
|
||||
// Dynamically determine the current position strategy
|
||||
const newPositionStrategy = determinePosition(
|
||||
rect,
|
||||
relativeX,
|
||||
relativeY
|
||||
);
|
||||
const [activeProp1, activeProp2] =
|
||||
getActiveProperties(newPositionStrategy);
|
||||
|
||||
// Update active edges for distance lines
|
||||
const vertical = activeProp1 === "top" ? "top" : "bottom";
|
||||
const horizontal = activeProp2 === "left" ? "left" : "right";
|
||||
setActiveEdges({ vertical, horizontal });
|
||||
|
||||
// Calculate new position based on the active properties
|
||||
let newY = 0;
|
||||
let newX = 0;
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
newY = relativeY - offset[0];
|
||||
} else {
|
||||
newY = rect.height - (relativeY + offset[0]);
|
||||
}
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
newX = relativeX - offset[1];
|
||||
} else {
|
||||
newX = rect.width - (relativeX + offset[1]);
|
||||
}
|
||||
|
||||
// Apply boundaries
|
||||
newX = Math.max(0, Math.min(rect.width - 50, newX));
|
||||
newY = Math.max(0, Math.min(rect.height - 50, newY));
|
||||
|
||||
// Create new position object
|
||||
const newPosition = {
|
||||
...newPositionStrategy,
|
||||
[activeProp1]: newY,
|
||||
[activeProp2]: newX,
|
||||
// Clear opposite properties
|
||||
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||
};
|
||||
|
||||
// Update the current position state for DistanceLines
|
||||
setCurrentPosition(newPosition);
|
||||
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
|
||||
});
|
||||
};
|
||||
|
||||
const handlePointerUpNative = async (e: PointerEvent) => {
|
||||
// Clean up native event listeners
|
||||
element.removeEventListener("pointermove", handlePointerMoveNative);
|
||||
element.removeEventListener("pointerup", handlePointerUpNative);
|
||||
element.releasePointerCapture(e.pointerId);
|
||||
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relativeX = e.clientX - rect.left;
|
||||
const relativeY = e.clientY - rect.top;
|
||||
|
||||
// Determine final position strategy
|
||||
const finalPosition = determinePosition(rect, relativeX, relativeY);
|
||||
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||
|
||||
// Calculate final position
|
||||
let finalY = 0;
|
||||
let finalX = 0;
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
finalY = relativeY - offset[0];
|
||||
} else {
|
||||
finalY = rect.height - (relativeY + offset[0]);
|
||||
}
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
finalX = relativeX - offset[1];
|
||||
} else {
|
||||
finalX = rect.width - (relativeX + offset[1]);
|
||||
}
|
||||
|
||||
// Apply boundaries
|
||||
finalX = Math.max(0, Math.min(rect.width - 50, finalX));
|
||||
finalY = Math.max(0, Math.min(rect.height - 50, finalY));
|
||||
|
||||
const boundedPosition = {
|
||||
...finalPosition,
|
||||
[activeProp1]: finalY,
|
||||
[activeProp2]: finalX,
|
||||
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||
};
|
||||
|
||||
try {
|
||||
// Save to backend
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const response = await addingFloatingWidgets(
|
||||
zone.zoneId,
|
||||
organization,
|
||||
{
|
||||
...zone.objects[draggingIndex.index],
|
||||
position: boundedPosition,
|
||||
}
|
||||
);
|
||||
|
||||
if (response.message === "Widget updated successfully") {
|
||||
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setDraggingIndex(null);
|
||||
setOffset(null);
|
||||
setActiveEdges(null);
|
||||
setCurrentPosition(null);
|
||||
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
animationRef.current = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add native event listeners
|
||||
element.addEventListener("pointermove", handlePointerMoveNative);
|
||||
element.addEventListener("pointerup", handlePointerUpNative);
|
||||
};
|
||||
|
||||
const handlePointerMove = (event: React.PointerEvent) => {
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
if (isPlaying === true) return;
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return;
|
||||
|
||||
|
@ -234,6 +381,7 @@ const DroppedObjects: React.FC = () => {
|
|||
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
|
||||
try {
|
||||
if (!draggingIndex || !offset) return;
|
||||
if (isPlaying === true) return;
|
||||
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return;
|
||||
|
@ -242,11 +390,11 @@ const DroppedObjects: React.FC = () => {
|
|||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
// Only now determine the final position strategy
|
||||
// Determine final position strategy
|
||||
const finalPosition = determinePosition(rect, relativeX, relativeY);
|
||||
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||
|
||||
// Calculate final position using the new strategy
|
||||
// Calculate final position
|
||||
let finalY = 0;
|
||||
let finalX = 0;
|
||||
|
||||
|
@ -328,29 +476,48 @@ const DroppedObjects: React.FC = () => {
|
|||
{zone.objects.map((obj, index) => (
|
||||
<div
|
||||
key={`${zoneName}-${index}`}
|
||||
className={obj.className}
|
||||
className={`${obj.className} ${
|
||||
selectedChartId?.id === obj.id && "activeChart"
|
||||
}`}
|
||||
ref={chartWidget}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top:
|
||||
typeof obj.position.top === "number"
|
||||
? `${obj.position.top}px`
|
||||
? `calc(${obj.position.top}px + ${
|
||||
isPlaying && selectedZone.activeSides.includes("top")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
left:
|
||||
typeof obj.position.left === "number"
|
||||
? `${obj.position.left}px`
|
||||
? `calc(${obj.position.left}px + ${
|
||||
isPlaying && selectedZone.activeSides.includes("left")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
right:
|
||||
typeof obj.position.right === "number"
|
||||
? `${obj.position.right}px`
|
||||
? `calc(${obj.position.right}px + ${
|
||||
isPlaying && selectedZone.activeSides.includes("right")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
bottom:
|
||||
typeof obj.position.bottom === "number"
|
||||
? `${obj.position.bottom}px`
|
||||
? `calc(${obj.position.bottom}px + ${
|
||||
isPlaying && selectedZone.activeSides.includes("bottom")
|
||||
? 90
|
||||
: 0
|
||||
}px)`
|
||||
: "auto",
|
||||
}}
|
||||
onPointerDown={(event) => handlePointerDown(event, index)}
|
||||
onClick={() => {
|
||||
setSelectedChartId(obj)
|
||||
onPointerDown={(event) => {
|
||||
setSelectedChartId(obj);
|
||||
handlePointerDown(event, index);
|
||||
}}
|
||||
>
|
||||
{obj.className === "floating total-card" ? (
|
||||
|
@ -387,10 +554,13 @@ const DroppedObjects: React.FC = () => {
|
|||
</div>
|
||||
<div className="label">Duplicate</div>
|
||||
</div>
|
||||
<div className="edit btn" onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDelete(zoneName, obj.id, index); // Call the delete handler
|
||||
}}>
|
||||
<div
|
||||
className="edit btn"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
handleDelete(zoneName, obj.id, index); // Call the delete handler
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
|
@ -403,7 +573,8 @@ const DroppedObjects: React.FC = () => {
|
|||
))}
|
||||
|
||||
{/* Render DistanceLines component during drag */}
|
||||
{draggingIndex !== null &&
|
||||
{isPlaying === false &&
|
||||
draggingIndex !== null &&
|
||||
activeEdges !== null &&
|
||||
currentPosition !== null && (
|
||||
<DistanceLines
|
||||
|
@ -436,4 +607,4 @@ const DroppedObjects: React.FC = () => {
|
|||
|
||||
export default DroppedObjects;
|
||||
|
||||
|
||||
// always place the mousePointer in the same point in the floatingCard even in pointerMove
|
||||
|
|
|
@ -25,7 +25,7 @@ interface PanelProps {
|
|||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
zoneViewPortPosition: number[]
|
||||
zoneViewPortPosition: number[];
|
||||
widgets: Widget[];
|
||||
};
|
||||
setSelectedZone: React.Dispatch<
|
||||
|
@ -37,7 +37,7 @@ interface PanelProps {
|
|||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
zoneViewPortPosition: number[]
|
||||
zoneViewPortPosition: number[];
|
||||
widgets: Widget[];
|
||||
}>
|
||||
>;
|
||||
|
@ -78,8 +78,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",
|
||||
|
@ -89,8 +90,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",
|
||||
|
@ -142,7 +144,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||
// while dublicate check this and add
|
||||
const addWidgetToPanel = async (asset: any, panel: Side) => {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const newWidget = {
|
||||
...asset,
|
||||
id: generateUniqueId(),
|
||||
|
@ -207,7 +209,6 @@ const Panel: React.FC<PanelProps> = ({
|
|||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
||||
|
||||
|
||||
setSelectedZone((prev) => {
|
||||
if (!prev) return prev; // Ensure prev is not null
|
||||
|
||||
|
@ -234,7 +235,9 @@ const Panel: React.FC<PanelProps> = ({
|
|||
{selectedZone.activeSides.map((side) => (
|
||||
<div
|
||||
key={side}
|
||||
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
|
||||
className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${
|
||||
hiddenPanels.includes(side) ? "hidePanel" : ""
|
||||
}`}
|
||||
style={getPanelStyle(side)}
|
||||
onDrop={(e) => handleDrop(e, side)}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
|
|
|
@ -7,7 +7,7 @@ import DisplayZone from "./DisplayZone";
|
|||
import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import {
|
||||
useAsset3dWidget,
|
||||
|
@ -20,6 +20,9 @@ import { generateUniqueId } from "../../../functions/generateUniqueId";
|
|||
import { determinePosition } from "./functions/determinePosition";
|
||||
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
|
||||
import SocketRealTimeViz from "../../../modules/visualization/realTimeVizSocket.dev";
|
||||
import RenderOverlay from "../../templates/Overlay";
|
||||
import ConfirmationPopup from "../../layout/confirmationPopup/ConfirmationPopup";
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -53,6 +56,9 @@ const RealTimeVisulization: React.FC = () => {
|
|||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { zones } = useZones();
|
||||
|
||||
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
|
||||
|
||||
const [floatingWidgets, setFloatingWidgets] = useState<
|
||||
Record<string, { zoneName: string; zoneId: string; objects: any[] }>
|
||||
>({});
|
||||
|
@ -87,9 +93,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||
{}
|
||||
);
|
||||
setZonesData(formattedData);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
GetZoneData();
|
||||
|
@ -142,7 +146,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||
id: generateUniqueId(),
|
||||
position: determinePosition(canvasRect, relativeX, relativeY),
|
||||
};
|
||||
console.log('newObject: ', newObject);
|
||||
|
||||
let response = await addingFloatingWidgets(
|
||||
selectedZone.zoneId,
|
||||
|
@ -179,7 +182,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||
],
|
||||
},
|
||||
}));
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -193,6 +196,20 @@ const RealTimeVisulization: React.FC = () => {
|
|||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
>
|
||||
{/* <RenderOverlay>
|
||||
<EditWidgetOption
|
||||
options={["Dublicate", "Vertical Move", "Horizontal Move", "Delete"]}
|
||||
/>
|
||||
</RenderOverlay> */}
|
||||
{openConfirmationPopup && (
|
||||
<RenderOverlay>
|
||||
<ConfirmationPopup
|
||||
message={"Are you sure want to delete?"}
|
||||
onConfirm={() => console.log("confirm")}
|
||||
onCancel={() => setOpenConfirmationPopup(false)}
|
||||
/>
|
||||
</RenderOverlay>
|
||||
)}
|
||||
<div
|
||||
className="scene-container"
|
||||
style={{
|
||||
|
|
|
@ -21,7 +21,7 @@ export function determinePosition(
|
|||
|
||||
if (relativeY < centerY) {
|
||||
if (relativeX < centerX) {
|
||||
console.log("Top-left");
|
||||
|
||||
position = {
|
||||
top: relativeY - 41.5,
|
||||
left: relativeX - 125,
|
||||
|
@ -29,7 +29,7 @@ export function determinePosition(
|
|||
bottom: "auto",
|
||||
};
|
||||
} else {
|
||||
console.log("Top-right");
|
||||
|
||||
position = {
|
||||
top: relativeY - 41.5,
|
||||
right: canvasRect.width - relativeX - 125,
|
||||
|
@ -39,7 +39,7 @@ export function determinePosition(
|
|||
}
|
||||
} else {
|
||||
if (relativeX < centerX) {
|
||||
console.log("Bottom-left");
|
||||
|
||||
position = {
|
||||
bottom: canvasRect.height - relativeY - 41.5,
|
||||
left: relativeX - 125,
|
||||
|
@ -47,7 +47,7 @@ export function determinePosition(
|
|||
top: "auto",
|
||||
};
|
||||
} else {
|
||||
console.log("Bottom-right");
|
||||
|
||||
position = {
|
||||
bottom: canvasRect.height - relativeY - 41.5,
|
||||
right: canvasRect.width - relativeX - 125,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
export const useClickOutside = (
|
||||
ref: React.RefObject<HTMLElement>,
|
||||
callback: () => void
|
||||
) => {
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (ref.current && !event.composedPath().includes(ref.current)) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
};
|
||||
}, [ref, callback]);
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
|
||||
interface EditWidgetOptionProps {
|
||||
options: string[];
|
||||
}
|
||||
|
||||
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({ options }) => {
|
||||
return (
|
||||
<div className="editWidgetOptions-wrapper">
|
||||
<div className="editWidgetOptions">
|
||||
{options.map((option, index) => (
|
||||
<div className="option" key={index}>
|
||||
{option}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditWidgetOption;
|
|
@ -1,10 +1,26 @@
|
|||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
|
||||
import {
|
||||
useActiveTool,
|
||||
useAsset3dWidget,
|
||||
useCamMode,
|
||||
useDeletableFloorItem,
|
||||
useDeleteModels,
|
||||
useFloorItems,
|
||||
useLoadingProgress,
|
||||
useRenderDistance,
|
||||
useselectedFloorItem,
|
||||
useSelectedItem,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
useTransformMode,
|
||||
} from "../../../store/store";
|
||||
import assetVisibility from "../geomentries/assets/assetVisibility";
|
||||
import { useEffect } from "react";
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import assetManager, { cancelOngoingTasks } from "../geomentries/assets/assetManager";
|
||||
import assetManager, {
|
||||
cancelOngoingTasks,
|
||||
} from "../geomentries/assets/assetManager";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
|
||||
|
@ -14,318 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
|
|||
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
||||
const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url));
|
||||
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
|
||||
const assetManagerWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/assetManagerWorker.js",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
const gltfLoaderWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
|
||||
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane }: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setselectedFloorItem } = useselectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
const FloorItemsGroup = ({
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
AttachedObject,
|
||||
floorGroup,
|
||||
tempLoader,
|
||||
isTempLoader,
|
||||
plane,
|
||||
}: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setselectedFloorItem } = useselectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
dracoLoader.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||
);
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem('email');
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
getFloorAssets(organization).then((data) => {
|
||||
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
|
||||
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
||||
);
|
||||
totalAssets = uniqueItems.length;
|
||||
if (totalAssets === 0) {
|
||||
updateLoadingProgress(100);
|
||||
return;
|
||||
}
|
||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
||||
getFloorAssets(organization).then((data) => {
|
||||
const uniqueItems = (data as Types.FloorItems).filter(
|
||||
(item, index, self) =>
|
||||
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
||||
);
|
||||
totalAssets = uniqueItems.length;
|
||||
if (totalAssets === 0) {
|
||||
updateLoadingProgress(100);
|
||||
return;
|
||||
}
|
||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
||||
});
|
||||
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(event.data.modelID, gltf);
|
||||
|
||||
loadedAssets++;
|
||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||
updateLoadingProgress(progress);
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(event.data.modelID, gltf);
|
||||
useEffect(() => {
|
||||
if (toggleView) return;
|
||||
|
||||
loadedAssets++;
|
||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||
updateLoadingProgress(progress);
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = state.camera.position;
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
}, [camMode, renderDistance]);
|
||||
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
|
||||
useEffect(() => {
|
||||
if (toggleView) return
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
if (toggleView) return;
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = state.camera.position;
|
||||
const cameraPosition = camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
|
||||
}, [camMode, renderDistance]);
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
if (toggleView) return
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
|
||||
};
|
||||
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
}
|
||||
};
|
||||
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
controls.addEventListener('rest', handleChange);
|
||||
controls.addEventListener('rest', stopInterval);
|
||||
controls.addEventListener('control', startInterval);
|
||||
controls.addEventListener('controlend', stopInterval);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener('rest', handleChange);
|
||||
controls.removeEventListener('rest', stopInterval);
|
||||
controls.removeEventListener('control', startInterval);
|
||||
controls.removeEventListener('controlend', stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
}
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
if (deleteModels) {
|
||||
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket);
|
||||
}
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
// let currentObject = intersects[0].object;
|
||||
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setselectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5 });
|
||||
|
||||
setselectedFloorItem(AttachedObject.current!);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
controls.addEventListener("rest", handleChange);
|
||||
controls.addEventListener("rest", stopInterval);
|
||||
controls.addEventListener("control", startInterval);
|
||||
controls.addEventListener("controlend", stopInterval);
|
||||
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane);
|
||||
}
|
||||
return () => {
|
||||
controls.removeEventListener("rest", handleChange);
|
||||
controls.removeEventListener("rest", stopInterval);
|
||||
controls.removeEventListener("control", startInterval);
|
||||
controls.removeEventListener("controlend", stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("dblclick", onDblClick);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
} else {
|
||||
if (controls) {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('floorItems: ', floorItems);
|
||||
}, [floorItems])
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteModels) {
|
||||
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
|
||||
} else if (!deleteModels) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
DeleteFloorItems(
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setFloorItems,
|
||||
socket
|
||||
);
|
||||
}
|
||||
})
|
||||
const Mode = transformMode;
|
||||
|
||||
return (
|
||||
<group ref={itemsGroup} name="itemsGroup">
|
||||
</group>
|
||||
)
|
||||
}
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
// let currentObject = intersects[0].object;
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setselectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(
|
||||
AttachedObject.current
|
||||
);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center
|
||||
.clone()
|
||||
.addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(
|
||||
newPosition.x,
|
||||
newPosition.y,
|
||||
newPosition.z,
|
||||
true
|
||||
);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
|
||||
setselectedFloorItem(AttachedObject.current!);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(
|
||||
raycaster,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
floorGroup,
|
||||
setFloorItems,
|
||||
itemsGroup,
|
||||
isTempLoader,
|
||||
tempLoader,
|
||||
socket,
|
||||
selectedItem,
|
||||
setSelectedItem,
|
||||
plane
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("dblclick", onDblClick);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
} else {
|
||||
if (controls) {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [
|
||||
deleteModels,
|
||||
transformMode,
|
||||
controls,
|
||||
selectedItem,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
activeTool,
|
||||
activeModule,
|
||||
]);
|
||||
|
||||
useEffect(() => {}, [floorItems]);
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteModels) {
|
||||
DeletableHoveredFloorItems(
|
||||
state,
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setDeletableFloorItem
|
||||
);
|
||||
} else if (!deleteModels) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return <group ref={itemsGroup} name="itemsGroup"></group>;
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ModuleToggle from "../components/ui/ModuleToggle";
|
||||
import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft";
|
||||
import SideBarRight from "../components/layout/sidebarRight/SideBarRight";
|
||||
|
@ -20,6 +20,8 @@ import { usePlayButtonStore } from "../store/usePlayButtonStore";
|
|||
import MarketPlace from "../modules/market/MarketPlace";
|
||||
import LoadingPage from "../components/templates/LoadingPage";
|
||||
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
|
||||
import RenderOverlay from "../components/templates/Overlay";
|
||||
import MenuBar from "../components/ui/menu/menu";
|
||||
|
||||
const Project: React.FC = () => {
|
||||
let navigate = useNavigate();
|
||||
|
@ -30,6 +32,7 @@ const Project: React.FC = () => {
|
|||
const { setFloorItems } = useFloorItems();
|
||||
const { setWallItems } = useWallItems();
|
||||
const { setZones } = useZones();
|
||||
const [openMenu, setOpenMenu] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setFloorItems([]);
|
||||
|
@ -61,6 +64,9 @@ const Project: React.FC = () => {
|
|||
<SideBarRight />
|
||||
</>
|
||||
)}
|
||||
{/* <RenderOverlay>
|
||||
<MenuBar setOpenMenu={setOpenMenu} />
|
||||
</RenderOverlay> */}
|
||||
{activeModule === "market" && <MarketPlace />}
|
||||
<RealTimeVisulization />
|
||||
{activeModule !== "market" && <Tools />}
|
||||
|
|
|
@ -6,9 +6,9 @@ export const addingFloatingWidgets = async (
|
|||
organization: string,
|
||||
widget: {}
|
||||
) => {
|
||||
console.log('organization: ', organization);
|
||||
console.log('widget: ', widget);
|
||||
console.log('zoneId: ', zoneId);
|
||||
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
.confirmation-overlay {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: var(--background-color-secondary);
|
||||
backdrop-filter: blur(2px);
|
||||
|
||||
.confirmation-modal {
|
||||
min-width: 35%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 5;
|
||||
background-color: var(--background-color);
|
||||
padding: 14px 12px;
|
||||
border-radius: 6px;
|
||||
|
||||
.buttton-wrapper {
|
||||
padding-top: 12px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: end;
|
||||
gap: 12px;
|
||||
|
||||
.confirmation-button {
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
|
||||
&:first-child {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
background-color: #ffe3e0;
|
||||
color: #f65648;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
@use 'components/marketPlace/marketPlace';
|
||||
@use 'components/simulation/simulation';
|
||||
@use 'components/menu/menu';
|
||||
@use 'components/confirmationPopUp';
|
||||
|
||||
// layout
|
||||
@use 'layout/loading';
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
border-radius: $border-radius-medium;
|
||||
padding: 18px;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.scene-container {
|
||||
|
@ -172,6 +172,7 @@
|
|||
border-radius: 6px;
|
||||
overflow: visible !important;
|
||||
z-index: $z-index-tools;
|
||||
overflow: auto;
|
||||
|
||||
.panel-content {
|
||||
position: relative;
|
||||
|
@ -320,6 +321,10 @@
|
|||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.panel.hidePanel {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.playingFlase {
|
||||
|
@ -551,8 +556,8 @@
|
|||
}
|
||||
|
||||
.kebab {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute !important;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
|
@ -577,6 +582,12 @@
|
|||
|
||||
box-shadow: var(--box-shadow-medium);
|
||||
|
||||
.icon {
|
||||
width: 25px !important;
|
||||
height: 25px !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
@ -608,18 +619,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
.dublicate {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.distance-line {
|
||||
position: absolute;
|
||||
border-style: dashed;
|
||||
border-color: var(--accent-color); /* Green color for visibility */
|
||||
border-color: var(--accent-color);
|
||||
/* Green color for visibility */
|
||||
border-width: 1px;
|
||||
pointer-events: none; /* Ensure lins don't interfere with dragging */
|
||||
pointer-events: none;
|
||||
/* Ensure lins don't interfere with dragging */
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
|
@ -632,51 +644,112 @@
|
|||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
transform: translate(-50%, -50%); /* Center the label */
|
||||
transform: translate(-50%, -50%);
|
||||
/* Center the label */
|
||||
}
|
||||
|
||||
/* Specific styles for each type of line */
|
||||
|
||||
/* Top distance line */
|
||||
.distance-line.top {
|
||||
border-bottom: none; /* Remove bottom border for a single line */
|
||||
width: 2px; /* Thin vertical line */
|
||||
border-bottom: none;
|
||||
/* Remove bottom border for a single line */
|
||||
width: 2px;
|
||||
/* Thin vertical line */
|
||||
}
|
||||
|
||||
.distance-line.top .distance-label {
|
||||
top: -10px; /* Position label above the line */
|
||||
left: 50%; /* Center horizontally */
|
||||
top: -10px;
|
||||
/* Position label above the line */
|
||||
left: 50%;
|
||||
/* Center horizontally */
|
||||
}
|
||||
|
||||
/* Bottom distance line */
|
||||
.distance-line.bottom {
|
||||
border-top: none; /* Remove top border for a single line */
|
||||
width: 2px; /* Thin vertical line */
|
||||
border-top: none;
|
||||
/* Remove top border for a single line */
|
||||
width: 2px;
|
||||
/* Thin vertical line */
|
||||
}
|
||||
|
||||
.distance-line.bottom .distance-label {
|
||||
bottom: -10px; /* Position label below the line */
|
||||
left: 50%; /* Center horizontally */
|
||||
bottom: -10px;
|
||||
/* Position label below the line */
|
||||
left: 50%;
|
||||
/* Center horizontally */
|
||||
}
|
||||
|
||||
/* Left distance line */
|
||||
.distance-line.left {
|
||||
border-right: none; /* Remove right border for a single line */
|
||||
height: 2px; /* Thin horizontal line */
|
||||
border-right: none;
|
||||
/* Remove right border for a single line */
|
||||
height: 2px;
|
||||
/* Thin horizontal line */
|
||||
}
|
||||
|
||||
.distance-line.left .distance-label {
|
||||
left: -10px; /* Position label to the left of the line */
|
||||
top: 50%; /* Center vertically */
|
||||
left: -10px;
|
||||
/* Position label to the left of the line */
|
||||
top: 50%;
|
||||
/* Center vertically */
|
||||
}
|
||||
|
||||
/* Right distance line */
|
||||
.distance-line.right {
|
||||
border-left: none; /* Remove left border for a single line */
|
||||
height: 2px; /* Thin horizontal line */
|
||||
border-left: none;
|
||||
/* Remove left border for a single line */
|
||||
height: 2px;
|
||||
/* Thin horizontal line */
|
||||
}
|
||||
|
||||
.distance-line.right .distance-label {
|
||||
right: -10px; /* Position label to the right of the line */
|
||||
top: 50%; /* Center vertically */
|
||||
right: -10px;
|
||||
/* Position label to the right of the line */
|
||||
top: 50%;
|
||||
/* Center vertically */
|
||||
}
|
||||
|
||||
.activeChart {
|
||||
outline: 1px solid var(--accent-color);
|
||||
z-index: 2 !important;
|
||||
}
|
||||
|
||||
|
||||
.editWidgetOptions-wrapper {
|
||||
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.editWidgetOptions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--background-color);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
|
||||
.option {
|
||||
padding: 8px 10px;
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-accent-color);
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
color: #f65648;
|
||||
|
||||
&:hover {
|
||||
background-color: #ffe3e0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,4 +18,4 @@ export function toggleTheme() {
|
|||
localStorage.setItem('theme', newTheme);
|
||||
}
|
||||
|
||||
setTheme();
|
||||
setTheme();
|
||||
|
|
Loading…
Reference in New Issue