added active chart select
This commit is contained in:
commit
37264f1690
|
@ -118,7 +118,7 @@ const Data = () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
console.log("selectedChartId", selectedChartId);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dataSideBar">
|
<div className="dataSideBar">
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
|
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
|
||||||
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
|
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
|
||||||
|
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
|
@ -79,6 +80,8 @@ export const DraggableWidget = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const chartWidget = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||||
|
|
||||||
const deleteSelectedChart = async () => {
|
const deleteSelectedChart = async () => {
|
||||||
|
@ -96,13 +99,11 @@ export const DraggableWidget = ({
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
setOpenKebabId(null);
|
setOpenKebabId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getCurrentWidgetCount = (panel: Side) =>
|
const getCurrentWidgetCount = (panel: Side) =>
|
||||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||||
|
|
||||||
|
@ -140,7 +141,11 @@ export const DraggableWidget = ({
|
||||||
id: `${widget.id}-copy-${Date.now()}`,
|
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") {
|
if (response?.message === "Widget created successfully") {
|
||||||
setSelectedZone((prevZone: any) => ({
|
setSelectedZone((prevZone: any) => ({
|
||||||
|
@ -149,13 +154,11 @@ export const DraggableWidget = ({
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
setOpenKebabId(null);
|
setOpenKebabId(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (openKebabId === widget.id) {
|
if (openKebabId === widget.id) {
|
||||||
|
@ -204,13 +207,18 @@ export const DraggableWidget = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useClickOutside(chartWidget, () => {
|
||||||
|
setSelectedChartId(null);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
draggable
|
draggable
|
||||||
key={widget.id}
|
key={widget.id}
|
||||||
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
|
className={`chart-container ${
|
||||||
}`}
|
selectedChartId?.id === widget.id && "activeChart"
|
||||||
|
}`}
|
||||||
onPointerDown={handlePointerDown}
|
onPointerDown={handlePointerDown}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragEnter={handleDragEnter}
|
onDragEnter={handleDragEnter}
|
||||||
|
@ -220,6 +228,8 @@ export const DraggableWidget = ({
|
||||||
opacity: isPanelHidden ? 0 : 1,
|
opacity: isPanelHidden ? 0 : 1,
|
||||||
pointerEvents: isPanelHidden ? "none" : "auto",
|
pointerEvents: isPanelHidden ? "none" : "auto",
|
||||||
}}
|
}}
|
||||||
|
ref={chartWidget}
|
||||||
|
onClick={() => setSelectedChartId(widget)}
|
||||||
>
|
>
|
||||||
{/* Kebab Icon */}
|
{/* Kebab Icon */}
|
||||||
<div className="icon kebab" onClick={handleKebabClick}>
|
<div className="icon kebab" onClick={handleKebabClick}>
|
||||||
|
@ -230,8 +240,9 @@ export const DraggableWidget = ({
|
||||||
{openKebabId === widget.id && (
|
{openKebabId === widget.id && (
|
||||||
<div className="kebab-options" ref={widgetRef}>
|
<div className="kebab-options" ref={widgetRef}>
|
||||||
<div
|
<div
|
||||||
className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
|
className={`edit btn ${
|
||||||
}`}
|
isPanelFull(widget.panel) ? "btn-blur" : ""
|
||||||
|
}`}
|
||||||
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
|
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
|
||||||
>
|
>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
|
@ -249,7 +260,7 @@ export const DraggableWidget = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Render charts based on widget type */}
|
{/* Render charts based on widget type */}
|
||||||
|
|
||||||
{widget.type === "progress" && (
|
{widget.type === "progress" && (
|
||||||
<ProgressCard title={widget.title} data={widget.data} />
|
<ProgressCard title={widget.title} data={widget.data} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { WalletIcon } from "../../icons/3dChartIcons";
|
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
@ -21,6 +20,7 @@ import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent";
|
||||||
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
|
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
|
||||||
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
|
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
|
||||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||||
|
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
|
||||||
interface DraggingState {
|
interface DraggingState {
|
||||||
zone: string;
|
zone: string;
|
||||||
index: number;
|
index: number;
|
||||||
|
@ -50,12 +50,14 @@ const DroppedObjects: React.FC = () => {
|
||||||
);
|
);
|
||||||
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
|
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
|
||||||
|
|
||||||
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
|
const duplicateObject = useDroppedObjectsStore(
|
||||||
|
(state) => state.duplicateObject
|
||||||
|
);
|
||||||
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
|
const [draggingIndex, setDraggingIndex] = useState<DraggingState | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||||
const { setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const [activeEdges, setActiveEdges] = useState<{
|
const [activeEdges, setActiveEdges] = useState<{
|
||||||
vertical: "top" | "bottom";
|
vertical: "top" | "bottom";
|
||||||
horizontal: "left" | "right";
|
horizontal: "left" | "right";
|
||||||
|
@ -68,6 +70,12 @@ const DroppedObjects: React.FC = () => {
|
||||||
} | null>(null); // State to track the current position during drag
|
} | null>(null); // State to track the current position during drag
|
||||||
const animationRef = useRef<number | null>(null);
|
const animationRef = useRef<number | null>(null);
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
|
const chartWidgetFloating = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// useClickOutside(chartWidgetFloating, () => {
|
||||||
|
|
||||||
|
// setSelectedChartId(null);
|
||||||
|
// });
|
||||||
|
|
||||||
// Clean up animation frame on unmount
|
// Clean up animation frame on unmount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -83,7 +91,7 @@ const DroppedObjects: React.FC = () => {
|
||||||
const [zoneName, zone] = zoneEntries[0];
|
const [zoneName, zone] = zoneEntries[0];
|
||||||
|
|
||||||
function handleDuplicate(zoneName: string, index: number) {
|
function handleDuplicate(zoneName: string, index: number) {
|
||||||
setOpenKebabId(null)
|
setOpenKebabId(null);
|
||||||
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
|
duplicateObject(zoneName, index); // Call the duplicateObject method from the store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,19 +101,27 @@ const DroppedObjects: React.FC = () => {
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
let res = await deleteFloatingWidgetApi(id, organization);
|
let res = await deleteFloatingWidgetApi(id, organization);
|
||||||
console.log('res: ', res);
|
|
||||||
|
|
||||||
if (res.message === "FloatingWidget deleted successfully") {
|
if (res.message === "FloatingWidget deleted successfully") {
|
||||||
deleteObject(zoneName, index); // Call the deleteObject method from the store
|
deleteObject(zoneName, index); // Call the deleteObject method from the store
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
console.error("Error deleting floating widget:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
const handlePointerDown = (event: React.PointerEvent, index: number) => {
|
||||||
|
if (
|
||||||
|
(event.target as HTMLElement).closest(".kebab-options") ||
|
||||||
|
(event.target as HTMLElement).closest(".kebab")
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const obj = zone.objects[index];
|
const obj = zone.objects[index];
|
||||||
|
const element = event.currentTarget as HTMLElement;
|
||||||
|
|
||||||
|
// Set pointer capture to ensure we get all move events
|
||||||
|
element.setPointerCapture(event.pointerId);
|
||||||
|
|
||||||
const container = document.getElementById("real-time-vis-canvas");
|
const container = document.getElementById("real-time-vis-canvas");
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
|
@ -145,6 +161,145 @@ const DroppedObjects: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
setOffset([offsetY, offsetX]);
|
setOffset([offsetY, offsetX]);
|
||||||
|
|
||||||
|
// Add native event listeners for smoother tracking
|
||||||
|
const handlePointerMoveNative = (e: PointerEvent) => {
|
||||||
|
if (!draggingIndex || !offset) 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) {
|
||||||
|
console.error("Error in handlePointerUp:", 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) => {
|
const handlePointerMove = (event: React.PointerEvent) => {
|
||||||
|
@ -199,12 +354,8 @@ const DroppedObjects: React.FC = () => {
|
||||||
// Update the current position state for DistanceLines
|
// Update the current position state for DistanceLines
|
||||||
setCurrentPosition(newPosition);
|
setCurrentPosition(newPosition);
|
||||||
|
|
||||||
if (!animationRef.current) {
|
// Update position immediately without animation frame
|
||||||
animationRef.current = requestAnimationFrame(() => {
|
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
|
||||||
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
|
|
||||||
animationRef.current = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
|
const handlePointerUp = async (event: React.PointerEvent<HTMLDivElement>) => {
|
||||||
|
@ -218,11 +369,11 @@ const DroppedObjects: React.FC = () => {
|
||||||
const relativeX = event.clientX - rect.left;
|
const relativeX = event.clientX - rect.left;
|
||||||
const relativeY = event.clientY - rect.top;
|
const relativeY = event.clientY - rect.top;
|
||||||
|
|
||||||
// Only now determine the final position strategy
|
// Determine final position strategy
|
||||||
const finalPosition = determinePosition(rect, relativeX, relativeY);
|
const finalPosition = determinePosition(rect, relativeX, relativeY);
|
||||||
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
const [activeProp1, activeProp2] = getActiveProperties(finalPosition);
|
||||||
|
|
||||||
// Calculate final position using the new strategy
|
// Calculate final position
|
||||||
let finalY = 0;
|
let finalY = 0;
|
||||||
let finalX = 0;
|
let finalX = 0;
|
||||||
|
|
||||||
|
@ -246,6 +397,9 @@ const DroppedObjects: React.FC = () => {
|
||||||
...finalPosition,
|
...finalPosition,
|
||||||
[activeProp1]: finalY,
|
[activeProp1]: finalY,
|
||||||
[activeProp2]: finalX,
|
[activeProp2]: finalX,
|
||||||
|
// Clear opposite properties
|
||||||
|
[activeProp1 === "top" ? "bottom" : "top"]: "auto",
|
||||||
|
[activeProp2 === "left" ? "right" : "left"]: "auto",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save to backend
|
// Save to backend
|
||||||
|
@ -259,18 +413,20 @@ const DroppedObjects: React.FC = () => {
|
||||||
if (response.message === "Widget updated successfully") {
|
if (response.message === "Widget updated successfully") {
|
||||||
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
// Clean up
|
console.error("Error in handlePointerUp:", error);
|
||||||
|
} finally {
|
||||||
|
// Clean up regardless of success or failure
|
||||||
setDraggingIndex(null);
|
setDraggingIndex(null);
|
||||||
setOffset(null);
|
setOffset(null);
|
||||||
setActiveEdges(null); // Clear active edges
|
setActiveEdges(null);
|
||||||
setCurrentPosition(null); // Reset current position
|
setCurrentPosition(null);
|
||||||
|
|
||||||
|
// Cancel any pending animation frame
|
||||||
if (animationRef.current) {
|
if (animationRef.current) {
|
||||||
cancelAnimationFrame(animationRef.current);
|
cancelAnimationFrame(animationRef.current);
|
||||||
animationRef.current = null;
|
animationRef.current = null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error("Error in handlePointerUp:", error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -279,66 +435,6 @@ const DroppedObjects: React.FC = () => {
|
||||||
setOpenKebabId((prevId) => (prevId === id ? null : id));
|
setOpenKebabId((prevId) => (prevId === id ? null : id));
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderObjectContent = (obj: any) => {
|
|
||||||
switch (obj.className) {
|
|
||||||
case "floating total-card":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="header-wrapper">
|
|
||||||
<div className="header">{obj.header}</div>
|
|
||||||
<div className="data-values">
|
|
||||||
<div className="value">{obj.value}</div>
|
|
||||||
<div className="per">{obj.per}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="icon">
|
|
||||||
<WalletIcon />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
case "warehouseThroughput floating":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="header">
|
|
||||||
<h2>Warehouse Throughput</h2>
|
|
||||||
<p>
|
|
||||||
<span>(+5) more</span> in 2025
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="lineGraph" style={{ height: "100%" }}>
|
|
||||||
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
case "fleetEfficiency floating":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h2 className="header">Fleet Efficiency</h2>
|
|
||||||
<div className="progressContainer">
|
|
||||||
<div className="progress">
|
|
||||||
<div className="barOverflow">
|
|
||||||
<div
|
|
||||||
className="bar"
|
|
||||||
style={{ transform: `rotate(${obj.value}deg)` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="scaleLabels">
|
|
||||||
<span>0%</span>
|
|
||||||
<div className="centerText">
|
|
||||||
<div className="percentage">{obj.per}%</div>
|
|
||||||
<div className="status">Optimal</div>
|
|
||||||
</div>
|
|
||||||
<span>100%</span>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onPointerMove={handlePointerMove}
|
onPointerMove={handlePointerMove}
|
||||||
|
@ -348,7 +444,10 @@ const DroppedObjects: React.FC = () => {
|
||||||
{zone.objects.map((obj, index) => (
|
{zone.objects.map((obj, index) => (
|
||||||
<div
|
<div
|
||||||
key={`${zoneName}-${index}`}
|
key={`${zoneName}-${index}`}
|
||||||
className={obj.className}
|
className={`${obj.className} ${
|
||||||
|
selectedChartId?.id === obj.id && "activeChart"
|
||||||
|
}`}
|
||||||
|
ref={chartWidgetFloating}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top:
|
top:
|
||||||
|
@ -370,7 +469,7 @@ const DroppedObjects: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
onPointerDown={(event) => handlePointerDown(event, index)}
|
onPointerDown={(event) => handlePointerDown(event, index)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedChartId(obj)
|
setSelectedChartId(obj);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{obj.className === "floating total-card" ? (
|
{obj.className === "floating total-card" ? (
|
||||||
|
@ -394,19 +493,25 @@ const DroppedObjects: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
{openKebabId === obj.id && (
|
{openKebabId === obj.id && (
|
||||||
<div className="kebab-options">
|
<div className="kebab-options">
|
||||||
<div className="dublicate btn" onClick={(event) => {
|
<div
|
||||||
event.stopPropagation();
|
className="dublicate btn"
|
||||||
handleDuplicate(zoneName, index); // Call the duplicate handler
|
onClick={(event) => {
|
||||||
}}>
|
event.stopPropagation();
|
||||||
|
handleDuplicate(zoneName, index); // Call the duplicate handler
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<DublicateIcon />
|
<DublicateIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="label">Duplicate</div>
|
<div className="label">Duplicate</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="edit btn" onClick={(event) => {
|
<div
|
||||||
event.stopPropagation();
|
className="edit btn"
|
||||||
handleDelete(zoneName, obj.id, index); // Call the delete handler
|
onClick={(event) => {
|
||||||
}}>
|
event.stopPropagation();
|
||||||
|
handleDelete(zoneName, obj.id, index); // Call the delete handler
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</div>
|
</div>
|
||||||
|
@ -450,5 +555,3 @@ const DroppedObjects: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DroppedObjects;
|
export default DroppedObjects;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -264,3 +264,4 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Panel;
|
export default Panel;
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,6 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
try {
|
try {
|
||||||
const response = await getZone2dData(organization);
|
const response = await getZone2dData(organization);
|
||||||
console.log('response: ', response);
|
|
||||||
|
|
||||||
if (!Array.isArray(response)) {
|
if (!Array.isArray(response)) {
|
||||||
return;
|
return;
|
||||||
|
@ -84,9 +83,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
setZonesData(formattedData);
|
setZonesData(formattedData);
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetZoneData();
|
GetZoneData();
|
||||||
|
@ -137,7 +134,6 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
id: generateUniqueId(),
|
id: generateUniqueId(),
|
||||||
position: determinePosition(canvasRect, relativeX, relativeY),
|
position: determinePosition(canvasRect, relativeX, relativeY),
|
||||||
};
|
};
|
||||||
console.log('newObject: ', newObject);
|
|
||||||
|
|
||||||
let response = await addingFloatingWidgets(
|
let response = await addingFloatingWidgets(
|
||||||
selectedZone.zoneId,
|
selectedZone.zoneId,
|
||||||
|
@ -174,7 +170,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
} catch (error) { }
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -201,7 +197,9 @@ const RealTimeVisulization: React.FC = () => {
|
||||||
>
|
>
|
||||||
<Scene />
|
<Scene />
|
||||||
</div>
|
</div>
|
||||||
{activeModule === "visualization" && selectedZone.zoneName !== "" && <DroppedObjects />}
|
{activeModule === "visualization" && selectedZone.zoneName !== "" && (
|
||||||
|
<DroppedObjects />
|
||||||
|
)}
|
||||||
{/* <DroppedObjects /> */}
|
{/* <DroppedObjects /> */}
|
||||||
{activeModule === "visualization" && (
|
{activeModule === "visualization" && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -87,7 +87,7 @@ export function determinePosition(
|
||||||
|
|
||||||
if (relativeY < centerY) {
|
if (relativeY < centerY) {
|
||||||
if (relativeX < centerX) {
|
if (relativeX < centerX) {
|
||||||
console.log("Top-left");
|
|
||||||
position = {
|
position = {
|
||||||
top: relativeY,
|
top: relativeY,
|
||||||
left: relativeX,
|
left: relativeX,
|
||||||
|
@ -95,7 +95,7 @@ export function determinePosition(
|
||||||
bottom: "auto",
|
bottom: "auto",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.log("Top-right");
|
|
||||||
position = {
|
position = {
|
||||||
top: relativeY,
|
top: relativeY,
|
||||||
right: canvasRect.width - relativeX,
|
right: canvasRect.width - relativeX,
|
||||||
|
@ -105,7 +105,7 @@ export function determinePosition(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (relativeX < centerX) {
|
if (relativeX < centerX) {
|
||||||
console.log("Bottom-left");
|
|
||||||
position = {
|
position = {
|
||||||
bottom: canvasRect.height - relativeY,
|
bottom: canvasRect.height - relativeY,
|
||||||
left: relativeX,
|
left: relativeX,
|
||||||
|
@ -113,7 +113,7 @@ export function determinePosition(
|
||||||
top: "auto",
|
top: "auto",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
console.log("Bottom-right");
|
|
||||||
position = {
|
position = {
|
||||||
bottom: canvasRect.height - relativeY,
|
bottom: canvasRect.height - relativeY,
|
||||||
right: canvasRect.width - relativeX,
|
right: canvasRect.width - relativeX,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
export const useClickOutside = (
|
||||||
|
ref: React.RefObject<HTMLElement>,
|
||||||
|
callback: () => void
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add event listener to document
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
|
||||||
|
// Cleanup event listener on component unmount
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, [ref, callback]);
|
||||||
|
};
|
|
@ -1,10 +1,26 @@
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
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 assetVisibility from "../geomentries/assets/assetVisibility";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import * as Types from "../../../types/world/worldTypes";
|
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 { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||||
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
|
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
|
||||||
|
@ -14,320 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
|
||||||
import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
|
import { getFloorItems } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
|
||||||
import useModuleStore from "../../../store/useModuleStore";
|
import useModuleStore from "../../../store/useModuleStore";
|
||||||
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
||||||
const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url));
|
const assetManagerWorker = new Worker(
|
||||||
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
|
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 FloorItemsGroup = ({
|
||||||
const state: Types.ThreeState = useThree();
|
itemsGroup,
|
||||||
const { raycaster, controls }: any = state;
|
hoveredDeletableFloorItem,
|
||||||
const { renderDistance } = useRenderDistance();
|
AttachedObject,
|
||||||
const { toggleView } = useToggleView();
|
floorGroup,
|
||||||
const { floorItems, setFloorItems } = useFloorItems();
|
tempLoader,
|
||||||
const { camMode } = useCamMode();
|
isTempLoader,
|
||||||
const { deleteModels } = useDeleteModels();
|
plane,
|
||||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
}: any) => {
|
||||||
const { transformMode } = useTransformMode();
|
const state: Types.ThreeState = useThree();
|
||||||
const { setselectedFloorItem } = useselectedFloorItem();
|
const { raycaster, controls }: any = state;
|
||||||
const { activeTool } = useActiveTool();
|
const { renderDistance } = useRenderDistance();
|
||||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
const { toggleView } = useToggleView();
|
||||||
const { setLoadingProgress } = useLoadingProgress();
|
const { floorItems, setFloorItems } = useFloorItems();
|
||||||
const { activeModule } = useModuleStore();
|
const { camMode } = useCamMode();
|
||||||
const { socket } = useSocketStore();
|
const { deleteModels } = useDeleteModels();
|
||||||
const loader = new GLTFLoader();
|
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||||
const dracoLoader = new DRACOLoader();
|
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/');
|
dracoLoader.setDecoderPath(
|
||||||
loader.setDRACOLoader(dracoLoader);
|
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||||
|
);
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const email = localStorage.getItem('email');
|
const email = localStorage.getItem("email");
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
let totalAssets = 0;
|
let totalAssets = 0;
|
||||||
let loadedAssets = 0;
|
let loadedAssets = 0;
|
||||||
|
|
||||||
const updateLoadingProgress = (progress: number) => {
|
const updateLoadingProgress = (progress: number) => {
|
||||||
if (progress < 100) {
|
if (progress < 100) {
|
||||||
setLoadingProgress(progress);
|
setLoadingProgress(progress);
|
||||||
} else if (progress === 100) {
|
} else if (progress === 100) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLoadingProgress(100);
|
setLoadingProgress(100);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLoadingProgress(0);
|
setLoadingProgress(0);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getFloorItems(organization).then((data) => {
|
getFloorItems(organization).then((data) => {
|
||||||
console.log('data: ', data);
|
const uniqueItems = (data as Types.FloorItems).filter(
|
||||||
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
|
(item, index, self) =>
|
||||||
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
||||||
);
|
);
|
||||||
totalAssets = uniqueItems.length;
|
totalAssets = uniqueItems.length;
|
||||||
if (totalAssets === 0) {
|
if (totalAssets === 0) {
|
||||||
updateLoadingProgress(100);
|
updateLoadingProgress(100);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
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) => {
|
useEffect(() => {
|
||||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
assetManagerWorker.onmessage = async (event) => {
|
||||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
cancelOngoingTasks(); // Cancel the ongoing process
|
||||||
|
await assetManager(event.data, itemsGroup, loader);
|
||||||
|
};
|
||||||
|
}, [assetManagerWorker]);
|
||||||
|
|
||||||
loader.load(blobUrl, (gltf) => {
|
useEffect(() => {
|
||||||
URL.revokeObjectURL(blobUrl);
|
if (toggleView) return;
|
||||||
THREE.Cache.remove(blobUrl);
|
|
||||||
THREE.Cache.add(event.data.modelID, gltf);
|
|
||||||
|
|
||||||
loadedAssets++;
|
const uuids: string[] = [];
|
||||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
itemsGroup.current?.children.forEach((child: any) => {
|
||||||
updateLoadingProgress(progress);
|
uuids.push(child.uuid);
|
||||||
|
});
|
||||||
|
const cameraPosition = state.camera.position;
|
||||||
|
|
||||||
if (loadedAssets === totalAssets) {
|
assetManagerWorker.postMessage({
|
||||||
loadInitialFloorItems(itemsGroup, setFloorItems);
|
floorItems,
|
||||||
updateLoadingProgress(100);
|
cameraPosition,
|
||||||
}
|
uuids,
|
||||||
});
|
renderDistance,
|
||||||
}
|
});
|
||||||
};
|
}, [camMode, renderDistance]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
assetManagerWorker.onmessage = async (event) => {
|
const controls: any = state.controls;
|
||||||
cancelOngoingTasks(); // Cancel the ongoing process
|
const camera: any = state.camera;
|
||||||
await assetManager(event.data, itemsGroup, loader);
|
|
||||||
};
|
|
||||||
}, [assetManagerWorker]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
if (controls) {
|
||||||
if (toggleView) return
|
let intervalId: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
if (toggleView) return;
|
||||||
|
|
||||||
const uuids: string[] = [];
|
const uuids: string[] = [];
|
||||||
itemsGroup.current?.children.forEach((child: any) => {
|
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 });
|
assetManagerWorker.postMessage({
|
||||||
}, [camMode, renderDistance]);
|
floorItems,
|
||||||
|
cameraPosition,
|
||||||
|
uuids,
|
||||||
|
renderDistance,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const startInterval = () => {
|
||||||
const controls: any = state.controls;
|
if (!intervalId) {
|
||||||
const camera: any = state.camera;
|
intervalId = setInterval(handleChange, 50);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
const stopInterval = () => {
|
||||||
const canvasElement = state.gl.domElement;
|
handleChange();
|
||||||
let drag = false;
|
if (intervalId) {
|
||||||
let isLeftMouseDown = false;
|
clearInterval(intervalId);
|
||||||
|
intervalId = null;
|
||||||
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 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;
|
return () => {
|
||||||
|
controls.removeEventListener("rest", handleChange);
|
||||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
controls.removeEventListener("rest", stopInterval);
|
||||||
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane);
|
controls.removeEventListener("control", startInterval);
|
||||||
}
|
controls.removeEventListener("controlend", stopInterval);
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||||
|
|
||||||
const onDragOver = (event: any) => {
|
useEffect(() => {
|
||||||
event.preventDefault();
|
const canvasElement = state.gl.domElement;
|
||||||
};
|
let drag = false;
|
||||||
|
let isLeftMouseDown = false;
|
||||||
|
|
||||||
if (activeModule === "builder") {
|
const onMouseDown = (evt: any) => {
|
||||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
if (evt.button === 0) {
|
||||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
isLeftMouseDown = true;
|
||||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
drag = false;
|
||||||
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 () => {
|
const onMouseMove = () => {
|
||||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
if (isLeftMouseDown) {
|
||||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
drag = true;
|
||||||
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(() => {
|
const onMouseUp = async (evt: any) => {
|
||||||
|
if (controls) {
|
||||||
|
(controls as any).enabled = true;
|
||||||
|
}
|
||||||
|
if (evt.button === 0) {
|
||||||
|
isLeftMouseDown = false;
|
||||||
|
if (drag) return;
|
||||||
|
|
||||||
console.log('floorItems: ', floorItems);
|
|
||||||
}, [floorItems])
|
|
||||||
|
|
||||||
useFrame(() => {
|
|
||||||
if (controls)
|
|
||||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
|
||||||
if (deleteModels) {
|
if (deleteModels) {
|
||||||
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
|
DeleteFloorItems(
|
||||||
} else if (!deleteModels) {
|
itemsGroup,
|
||||||
if (hoveredDeletableFloorItem.current) {
|
hoveredDeletableFloorItem,
|
||||||
hoveredDeletableFloorItem.current = undefined;
|
setFloorItems,
|
||||||
setDeletableFloorItem(null);
|
socket
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
})
|
const Mode = transformMode;
|
||||||
|
|
||||||
return (
|
if (Mode !== null || activeTool === "cursor") {
|
||||||
<group ref={itemsGroup} name="itemsGroup">
|
if (!itemsGroup.current) return;
|
||||||
</group>
|
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;
|
||||||
|
|
|
@ -6,9 +6,9 @@ export const addingFloatingWidgets = async (
|
||||||
organization: string,
|
organization: string,
|
||||||
widget: {}
|
widget: {}
|
||||||
) => {
|
) => {
|
||||||
console.log('organization: ', organization);
|
|
||||||
console.log('widget: ', widget);
|
|
||||||
console.log('zoneId: ', zoneId);
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,
|
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
border-radius: $border-radius-medium;
|
border-radius: $border-radius-medium;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1000;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scene-container {
|
.scene-container {
|
||||||
|
@ -544,21 +544,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-wrapper {
|
.floating-wrapper {
|
||||||
.icon {
|
// .icon {
|
||||||
width: 25px !important;
|
// width: 25px !important;
|
||||||
height: 25px !important;
|
// height: 25px !important;
|
||||||
background-color: transparent;
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
.kebab {
|
.kebab {
|
||||||
width: 30px;
|
width: 25px;
|
||||||
height: 30px;
|
height: 25px;
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kebab-options {
|
.kebab-options {
|
||||||
|
@ -576,6 +576,12 @@
|
||||||
|
|
||||||
box-shadow: var(--box-shadow-medium);
|
box-shadow: var(--box-shadow-medium);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
width: 25px !important;
|
||||||
|
height: 25px !important;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
@ -607,18 +613,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dublicate {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-line {
|
.distance-line {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
border-color: var(--accent-color); /* Green color for visibility */
|
border-color: var(--accent-color);
|
||||||
|
/* Green color for visibility */
|
||||||
border-width: 1px;
|
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;
|
z-index: 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,51 +638,73 @@
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transform: translate(-50%, -50%); /* Center the label */
|
transform: translate(-50%, -50%);
|
||||||
|
/* Center the label */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specific styles for each type of line */
|
/* Specific styles for each type of line */
|
||||||
|
|
||||||
/* Top distance line */
|
/* Top distance line */
|
||||||
.distance-line.top {
|
.distance-line.top {
|
||||||
border-bottom: none; /* Remove bottom border for a single line */
|
border-bottom: none;
|
||||||
width: 2px; /* Thin vertical line */
|
/* Remove bottom border for a single line */
|
||||||
|
width: 2px;
|
||||||
|
/* Thin vertical line */
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-line.top .distance-label {
|
.distance-line.top .distance-label {
|
||||||
top: -10px; /* Position label above the line */
|
top: -10px;
|
||||||
left: 50%; /* Center horizontally */
|
/* Position label above the line */
|
||||||
|
left: 50%;
|
||||||
|
/* Center horizontally */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bottom distance line */
|
/* Bottom distance line */
|
||||||
.distance-line.bottom {
|
.distance-line.bottom {
|
||||||
border-top: none; /* Remove top border for a single line */
|
border-top: none;
|
||||||
width: 2px; /* Thin vertical line */
|
/* Remove top border for a single line */
|
||||||
|
width: 2px;
|
||||||
|
/* Thin vertical line */
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-line.bottom .distance-label {
|
.distance-line.bottom .distance-label {
|
||||||
bottom: -10px; /* Position label below the line */
|
bottom: -10px;
|
||||||
left: 50%; /* Center horizontally */
|
/* Position label below the line */
|
||||||
|
left: 50%;
|
||||||
|
/* Center horizontally */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Left distance line */
|
/* Left distance line */
|
||||||
.distance-line.left {
|
.distance-line.left {
|
||||||
border-right: none; /* Remove right border for a single line */
|
border-right: none;
|
||||||
height: 2px; /* Thin horizontal line */
|
/* Remove right border for a single line */
|
||||||
|
height: 2px;
|
||||||
|
/* Thin horizontal line */
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-line.left .distance-label {
|
.distance-line.left .distance-label {
|
||||||
left: -10px; /* Position label to the left of the line */
|
left: -10px;
|
||||||
top: 50%; /* Center vertically */
|
/* Position label to the left of the line */
|
||||||
|
top: 50%;
|
||||||
|
/* Center vertically */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Right distance line */
|
/* Right distance line */
|
||||||
.distance-line.right {
|
.distance-line.right {
|
||||||
border-left: none; /* Remove left border for a single line */
|
border-left: none;
|
||||||
height: 2px; /* Thin horizontal line */
|
/* Remove left border for a single line */
|
||||||
|
height: 2px;
|
||||||
|
/* Thin horizontal line */
|
||||||
}
|
}
|
||||||
|
|
||||||
.distance-line.right .distance-label {
|
.distance-line.right .distance-label {
|
||||||
right: -10px; /* Position label to the right of the line */
|
right: -10px;
|
||||||
top: 50%; /* Center vertically */
|
/* Position label to the right of the line */
|
||||||
|
top: 50%;
|
||||||
|
/* Center vertically */
|
||||||
|
}
|
||||||
|
|
||||||
|
.activeChart {
|
||||||
|
outline: 1px solid var(--accent-color);
|
||||||
|
z-index: 2 !important;
|
||||||
}
|
}
|
Loading…
Reference in New Issue