Merge remote-tracking branch 'origin/main' into simulation

This commit is contained in:
Vishnu 2025-03-27 17:56:31 +05:30
commit 146f62347a
24 changed files with 2630 additions and 1535 deletions

View File

@ -511,3 +511,99 @@ export function AI_Icon() {
</svg>
);
}
export const KebabIcon = () => {
return (
<svg
width="13"
height="3"
viewBox="0 0 13 3"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<ellipse
cx="1.54798"
cy="1.35112"
rx="1.4993"
ry="1.27477"
fill="#E1E1E1"
/>
<ellipse
cx="6.04868"
cy="1.35131"
rx="1.4993"
ry="1.27477"
fill="#E1E1E1"
/>
<ellipse
cx="10.5476"
cy="1.35131"
rx="1.4993"
ry="1.27477"
fill="#E1E1E1"
/>
</svg>
);
};
export const DublicateIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14.3125 11.375C14.3125 11.7545 14.0045 12.0625 13.625 12.0625H8.125C7.7455 12.0625 7.4375 11.7545 7.4375 11.375V5.875C7.4375 5.4955 7.7455 5.1875 8.125 5.1875H13.625C14.0045 5.1875 14.3125 5.4955 14.3125 5.875V11.375ZM13.625 4.5H8.125C7.36566 4.5 6.75 5.11566 6.75 5.875V11.375C6.75 12.1343 7.36566 12.75 8.125 12.75H13.625C14.3843 12.75 15 12.1343 15 11.375V5.875C15 5.11566 14.3843 4.5 13.625 4.5ZM11.5625 14.125C11.5625 14.5045 11.2545 14.8125 10.875 14.8125H5.375C4.9955 14.8125 4.6875 14.5045 4.6875 14.125V8.625C4.6875 8.2455 4.9955 7.9375 5.375 7.9375H6.0625V7.25H5.375C4.61566 7.25 4 7.86566 4 8.625V14.125C4 14.8843 4.61566 15.5 5.375 15.5H10.875C11.6343 15.5 12.25 14.8843 12.25 14.125V13.4375H11.5625V14.125Z"
fill="#6F42C1"
/>
</svg>
);
};
export const DeleteIcon = () => {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.33301 10V13.3334"
stroke="#5D5F63"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11 10V13.3334"
stroke="#5D5F63"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M4.33301 6.66406H15"
stroke="#5D5F63"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.66699 8.66406V13.9976C5.66699 15.1022 6.56245 15.9976 7.66705 15.9976H11.6672C12.7718 15.9976 13.6672 15.1022 13.6672 13.9976V8.66406"
stroke="#5D5F63"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.66699 5.33337C7.66699 4.59697 8.26396 4 9.00037 4H10.3337C11.0702 4 11.6671 4.59697 11.6671 5.33337V6.66675H7.66699V5.33337Z"
stroke="#5D5F63"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

View File

@ -93,3 +93,114 @@ export function SimulationIcon({ isActive }: { isActive: boolean }) {
</svg>
);
}
// simulation player icons
export function ResetIcon() {
return (
<svg
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.54182 4.09637C3.33333 2.73422 4.80832 1.81836 6.49721 1.81836C9.02194 1.81836 11.0686 3.86506 11.0686 6.38979C11.0686 8.91452 9.02194 10.9612 6.49721 10.9612C3.97248 10.9612 1.92578 8.91452 1.92578 6.38979"
stroke="var(--text-color)"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M7.64118 6.38895C7.64118 7.02013 7.12951 7.53181 6.49833 7.53181C5.86714 7.53181 5.35547 7.02013 5.35547 6.38895C5.35547 5.75777 5.86714 5.24609 6.49833 5.24609C7.12951 5.24609 7.64118 5.75777 7.64118 6.38895Z"
fill="var(--text-color)"
stroke="var(--text-color)"
strokeWidth="0.571429"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M4.78125 4.10407H2.49554V1.81836"
stroke="var(--text-color)"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function PlayStopIcon() {
return (
<svg
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 2.88867V9.88867M11 2.88867V9.88867M2 3.99171V8.78562C2 9.28847 2 9.53987 2.09943 9.65627C2.1857 9.75732 2.31512 9.81092 2.44756 9.80047C2.60019 9.78847 2.77796 9.61072 3.13352 9.25517L5.5305 6.85817C5.69485 6.69382 5.777 6.61167 5.8078 6.51692C5.83485 6.43357 5.83485 6.34377 5.8078 6.26042C5.777 6.16567 5.69485 6.08352 5.5305 5.91917L3.13352 3.52219C2.77796 3.16664 2.60019 2.98886 2.44756 2.97685C2.31512 2.96643 2.1857 3.02004 2.09943 3.12105C2 3.23747 2 3.48888 2 3.99171Z"
stroke="var(--text-color)"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function ExitIcon() {
return (
<svg
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.08203 6.34311L5.03647 3.38867M2.08203 6.34311L5.03647 9.29755M2.08203 6.34311H5.9228C7.22276 6.34334 9.34995 7.05241 10.7681 9.88867"
stroke="var(--text-color)"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function MoveArrowRight() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.58363 6.16637L15.4173 12L9.58363 17.8336"
stroke="var(--accent-color)"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}
export function MoveArrowLeft() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.4164 6.16637L8.58274 12L14.4164 17.8336"
stroke="var(--accent-color)"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}

View File

@ -13,7 +13,7 @@ const Visualization = () => {
return (
<div className="visualization-right-sideBar">
<ToggleHeader
options={["Data", "Design"]}
options={["Data"]}
activeOption={activeOption}
handleClick={handleToggleClick}
/>

View File

@ -285,11 +285,15 @@ const Tools: React.FC = () => {
</div>
</>
) : (
<>
{activeModule !== "simulation" && (
<div className="exitPlay" onClick={() => setIsPlaying(false)}>
X
</div>
)}
</>
)}
</>
);
};

View File

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

View File

@ -5,6 +5,7 @@ import {
LockIcon,
} from "../../icons/RealTimeVisulationIcons";
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
import { AddIcon } from "../../icons/ExportCommonIcons";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
@ -18,7 +19,7 @@ interface ButtonsProps {
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[]
zoneViewPortPosition: number[];
widgets: {
id: string;
type: string;
@ -35,7 +36,7 @@ interface ButtonsProps {
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[]
zoneViewPortPosition: number[];
widgets: {
id: string;
type: string;
@ -116,7 +117,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
};
// Delete the selectedZone state
console.log('updatedZone: ', updatedZone);
console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone);
} else {
// If the panel is not active, activate it
@ -127,8 +128,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
// console.log('response: ', response);
@ -145,7 +146,9 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div key={side} className={`side-button-container ${side}`}>
{/* "+" Button */}
<button
className={`side-button ${side}`}
className={`side-button ${side}${
selectedZone.activeSides.includes(side) ? " active" : ""
}`}
onClick={() => handlePlusButtonClick(side)}
title={
selectedZone.activeSides.includes(side)
@ -153,7 +156,9 @@ const AddButtons: React.FC<ButtonsProps> = ({
: `Activate ${side} panel`
}
>
+
<div className="add-icon">
<AddIcon />
</div>
</button>
{/* Extra Buttons */}
@ -161,7 +166,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div className="extra-Bs">
{/* Hide Panel */}
<div
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
className={`icon ${
hiddenPanels.includes(side) ? "active" : ""
}`}
title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
@ -169,11 +175,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
onClick={() => toggleVisibility(side)}
>
<EyeIcon
fill={
hiddenPanels.includes(side)
? "white"
: "#1D1E21"
}
fill={hiddenPanels.includes(side) ? "var(--primary-color)" : "var(--text-color)"}
/>
</div>
@ -188,7 +190,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
{/* Lock/Unlock Panel */}
<div
className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
className={`icon ${
selectedZone.lockedPanels.includes(side) ? "active" : ""
}`}
title={
selectedZone.lockedPanels.includes(side)
@ -197,7 +200,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
}
onClick={() => toggleLockPanel(side)}
>
<LockIcon fill={selectedZone.lockedPanels.includes(side) ? "#ffffff" : "#1D1E21"} />
<LockIcon
fill={
selectedZone.lockedPanels.includes(side)
? "var(--primary-color)"
: "var(--text-color)"
}
/>
</div>
</div>
)}

View File

@ -1,5 +1,7 @@
import React, { useEffect, useRef } from "react";
import React, { useEffect, useRef, useState, useCallback } from "react";
import { Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
@ -60,118 +62,104 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
// Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null);
// Example state for selectedOption and options (adjust based on your actual use case)
const [selectedOption, setSelectedOption] = React.useState<string | null>(
null
);
// console.log('setSelectedOption: ', setSelectedOption);
const [options, setOptions] = React.useState<string[]>([]);
// console.log('setOptions: ', setOptions);
// State to track overflow visibility
const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false);
// Scroll to the selected option when it changes
useEffect(() => {
// Function to calculate overflow state
const updateOverflowState = useCallback(() => {
const container = containerRef.current;
if (container && selectedOption) {
// Handle scrolling to the selected option
const index = options.findIndex((option) => {
const formattedOption = formatOptionName(option);
const selectedFormattedOption =
selectedOption?.split("_")[1] || selectedOption;
return formattedOption === selectedFormattedOption;
});
if (index !== -1) {
const optionElement = container.children[index] as HTMLElement;
if (optionElement) {
optionElement.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center",
});
}
}
}
}, [selectedOption, options]);
useEffect(() => {
const container = containerRef.current;
const handleWheel = (event: WheelEvent) => {
event.preventDefault();
if (container) {
container.scrollBy({
left: event.deltaY * 2, // Adjust the multiplier for faster scrolling
behavior: "smooth",
});
const isOverflowing = container.scrollWidth > container.clientWidth;
const canScrollLeft = container.scrollLeft > 0;
const canScrollRight =
container.scrollLeft + container.clientWidth < container.scrollWidth;
setShowLeftArrow(isOverflowing && canScrollLeft);
setShowRightArrow(isOverflowing && canScrollRight);
}
};
let isDragging = false;
let startX: number;
let scrollLeft: number;
const handleMouseDown = (event: MouseEvent) => {
isDragging = true;
startX = event.pageX - (container?.offsetLeft || 0);
scrollLeft = container?.scrollLeft || 0;
};
const handleMouseMove = (event: MouseEvent) => {
if (!isDragging || !container) return;
event.preventDefault();
const x = event.pageX - (container.offsetLeft || 0);
const walk = (x - startX) * 2; // Adjust the multiplier for faster dragging
container.scrollLeft = scrollLeft - walk;
};
const handleMouseUp = () => {
isDragging = false;
};
const handleMouseLeave = () => {
isDragging = false;
};
if (container) {
container.addEventListener("wheel", handleWheel, { passive: false });
container.addEventListener("mousedown", handleMouseDown);
container.addEventListener("mousemove", handleMouseMove);
container.addEventListener("mouseup", handleMouseUp);
container.addEventListener("mouseleave", handleMouseLeave);
}
return () => {
if (container) {
container.removeEventListener("wheel", handleWheel);
container.removeEventListener("mousedown", handleMouseDown);
container.removeEventListener("mousemove", handleMouseMove);
container.removeEventListener("mouseup", handleMouseUp);
container.removeEventListener("mouseleave", handleMouseLeave);
}
};
}, []);
// Helper function to format option names (customize as needed)
const formatOptionName = (option: string): string => {
// Replace underscores with spaces and capitalize the first letter
return option.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase());
useEffect(() => {
const container = containerRef.current;
if (container) {
// Initial calculation after the DOM has been rendered
const handleInitialRender = () => {
requestAnimationFrame(updateOverflowState);
};
handleInitialRender();
// Update on window resize or scroll
const handleResize = () => updateOverflowState();
const handleScroll = () => updateOverflowState();
// Add mouse wheel listener for horizontal scrolling
const handleWheel = (event: WheelEvent) => {
event.preventDefault(); // Prevent default vertical scrolling
if (container) {
container.scrollBy({
left: event.deltaY * 2, // Translate vertical scroll to horizontal scroll
behavior: "smooth",
});
}
};
container.addEventListener("scroll", handleScroll);
window.addEventListener("resize", handleResize);
container.addEventListener("wheel", handleWheel, { passive: false });
return () => {
container.removeEventListener("scroll", handleScroll);
window.removeEventListener("resize", handleResize);
container.removeEventListener("wheel", handleWheel);
};
}
}, [updateOverflowState]);
// Handle scrolling with navigation arrows
const handleScrollLeft = () => {
const container = containerRef.current;
if (container) {
container.scrollBy({
left: -200, // Scroll left by 200px
behavior: "smooth",
});
}
};
const handleScrollRight = () => {
const container = containerRef.current;
if (container) {
container.scrollBy({
left: 200, // Scroll right by 200px
behavior: "smooth",
});
}
};
return (
<div
ref={containerRef}
className={`zoon-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
}`}
>
<div className="zone-wrapper">
{/* Left Arrow */}
{showLeftArrow && (
<button className="arrow left-arrow" onClick={handleScrollLeft}>
<MoveArrowLeft />
</button>
)}
{/* Zones Wrapper */}
{Object.keys(zonesData).length !== 0 ? (
<div ref={containerRef} className="zones-wrapper">
{Object.keys(zonesData).map((zoneName, index) => (
<div
key={index}
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
className={`zone ${
selectedZone.zoneName === zoneName ? "active" : ""
}`}
onClick={() => {
console.log("zoneName: ", zoneName);
setSelectedZone({
zoneName,
activeSides: zonesData[zoneName].activeSides || [],
@ -179,20 +167,31 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
lockedPanels: zonesData[zoneName].lockedPanels || [],
widgets: zonesData[zoneName].widgets || [],
zoneId: zonesData[zoneName]?.zoneId || "",
zoneViewPortTarget: zonesData[zoneName].zoneViewPortTarget || [],
zoneViewPortTarget:
zonesData[zoneName].zoneViewPortTarget || [],
zoneViewPortPosition:
zonesData[zoneName].zoneViewPortPosition || [],
});
// setSelectedZone({
// zoneName,
// ...zonesData[zoneName],
// });
}}
>
{zoneName}
</div>
))}
</div>
) : (
<div className="no-zone">
<InfoIcon />
No zones? Create one!
</div>
)}
{/* Right Arrow */}
{showRightArrow && (
<button className="arrow right-arrow" onClick={handleScrollRight}>
<MoveArrowRight />
</button>
)}
</div>
);
};

View File

@ -3,32 +3,166 @@ import ProgressCard from "../realTimeVis/charts/ProgressCard";
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent";
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
import {
DeleteIcon,
DublicateIcon,
KebabIcon,
} from "../../icons/ExportCommonIcons";
import { useEffect, useRef, useState } from "react";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
export const DraggableWidget = ({
widget,
hiddenPanels, // Add this prop to track hidden panels
index, onReorder
hiddenPanels,
index,
onReorder,
openKebabId,
setOpenKebabId,
selectedZone,
setSelectedZone,
}: {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
zoneViewPortPosition: number[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
}>
>;
widget: any;
hiddenPanels: string[]; // Array of hidden panel names
index: number; onReorder: (fromIndex: number, toIndex: number) => void
hiddenPanels: string[];
index: number;
onReorder: (fromIndex: number, toIndex: number) => void;
openKebabId: string | null;
setOpenKebabId: (id: string | null) => void;
}) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const handlePointerDown = () => {
if (selectedChartId?.id !== widget.id) {
setSelectedChartId(widget);
}
};
// Determine if the widget's panel is hidden
const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = () => {
const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id
);
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: updatedWidgets,
}));
setOpenKebabId(null);
};
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 150;
const CHART_HEIGHT = 150;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
};
const isPanelFull = (panel: Side) => {
const currentWidgetCount = getCurrentWidgetCount(panel);
const panelCapacity = calculatePanelCapacity(panel);
return currentWidgetCount >= panelCapacity;
};
const duplicateWidget = () => {
const duplicatedWidget: Widget = {
...widget,
id: `${widget.id}-copy-${Date.now()}`,
};
setSelectedZone((prevZone: any) => ({
...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget],
}));
setOpenKebabId(null);
console.log("Duplicated widget with ID:", duplicatedWidget.id);
};
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.stopPropagation();
if (openKebabId === widget.id) {
setOpenKebabId(null);
} else {
setOpenKebabId(widget.id);
}
};
const widgetRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
widgetRef.current &&
!widgetRef.current.contains(event.target as Node)
) {
setOpenKebabId(null);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [setOpenKebabId]);
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget
event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget
};
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // Allow drop
@ -40,20 +174,20 @@ export const DraggableWidget = ({
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); // Get the dragged widget's index
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
const toIndex = index; // The index of the widget where the drop occurred
if (fromIndex !== toIndex) {
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
}
};
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}
@ -61,10 +195,38 @@ export const DraggableWidget = ({
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{
opacity: isPanelHidden ? 0 : 1, // Set opacity to 0 if the panel is hidden
pointerEvents: isPanelHidden ? "none" : "auto", // Disable interaction when hidden
opacity: isPanelHidden ? 0 : 1,
pointerEvents: isPanelHidden ? "none" : "auto",
}}
>
{/* Kebab Icon */}
<div className="icon kebab" onClick={handleKebabClick}>
<KebabIcon />
</div>
{/* Kebab Options */}
{openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}>
<div
className={`edit btn ${
isPanelFull(widget.panel) ? "btn-blur" : ""
}`}
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div className="edit btn" onClick={deleteSelectedChart}>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
</div>
)}
{/* Render charts based on widget type */}
{widget.type === "progress" && (
<ProgressCard title={widget.title} data={widget.data} />
@ -101,15 +263,6 @@ export const DraggableWidget = ({
}}
/>
)}
{/* {widget.type === "radar" && (
<RadarGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={widget.data.measurements.map((item: any) => item.fields)}
/>
)} */}
{widget.type === "pie" && (
<PieGraphComponent
type={widget.type}

View File

@ -38,6 +38,7 @@ interface PanelProps {
}>
>;
hiddenPanels: string[];
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
}
const generateUniqueId = () =>
@ -47,11 +48,13 @@ const Panel: React.FC<PanelProps> = ({
selectedZone,
setSelectedZone,
hiddenPanels,
setZonesData,
}) => {
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const { isPlaying } = usePlayButtonStore();
@ -69,7 +72,8 @@ const Panel: React.FC<PanelProps> = ({
case "top":
case "bottom":
return {
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
width: `calc(100% - ${
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0",
@ -80,7 +84,8 @@ const Panel: React.FC<PanelProps> = ({
case "right":
return {
width: `${panelSize - 2}px`,
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
height: `calc(100% - ${
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
@ -104,8 +109,8 @@ const Panel: React.FC<PanelProps> = ({
if (currentWidgetsCount >= maxCapacity) return;
console.log('draggedAsset: ', draggedAsset);
console.log('panel: ', panel);
console.log("draggedAsset: ", draggedAsset);
console.log("panel: ", panel);
addWidgetToPanel(draggedAsset, panel);
};
@ -133,6 +138,7 @@ const Panel: React.FC<PanelProps> = ({
: Math.floor(dimensions.height / CHART_HEIGHT);
};
// while dublicate check this and add
const addWidgetToPanel = (asset: any, panel: Side) => {
const newWidget = {
...asset,
@ -174,7 +180,7 @@ const Panel: React.FC<PanelProps> = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
console.log('selectedZone: ', selectedZone);
console.log("selectedZone: ", selectedZone);
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
@ -197,9 +203,6 @@ const Panel: React.FC<PanelProps> = ({
});
};
return (
<>
{selectedZone.activeSides.map((side) => (
@ -237,6 +240,10 @@ const Panel: React.FC<PanelProps> = ({
onReorder={(fromIndex, toIndex) =>
handleReorder(fromIndex, toIndex, side)
}
openKebabId={openKebabId}
setOpenKebabId={setOpenKebabId}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
))}
</div>
@ -247,5 +254,3 @@ const Panel: React.FC<PanelProps> = ({
};
export default Panel;

View File

@ -10,7 +10,6 @@ import { useDroppedObjectsStore, useZones } from "../../../store/store";
import DroppedObjects from "./DroppedFloatingWidgets";
type Side = "top" | "bottom" | "left" | "right";
type FormattedZoneData = Record<
@ -128,7 +127,8 @@ const RealTimeVisulization: React.FC = () => {
style={{
height: "100%",
width: "100%",
borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()}
@ -154,10 +154,10 @@ const RealTimeVisulization: React.FC = () => {
)}
<Panel
hiddenPanels={hiddenPanels}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
hiddenPanels={hiddenPanels}
setZonesData={setZonesData}
/>
</>
)}

View File

@ -141,6 +141,7 @@
// export default MultiLevelDropdown;
import React, { useState, useRef, useEffect } from "react";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
// Dropdown Item Component
const DropdownItem = ({
@ -173,7 +174,13 @@ const NestedDropdown = ({
className={`dropdown-trigger ${open ? "open" : ""}`}
onClick={() => setOpen(!open)}
>
{label} <span className="icon">{open ? "▼" : "▶"}</span>
{label}
<div
className="arrow-container"
style={{ rotate: open ? "" : "-90deg" }}
>
<ArrowIcon />
</div>
</div>
{open && (
<div className="submenu">
@ -203,7 +210,7 @@ const MultiLevelDropdown = ({
data,
onSelect,
onUnselect,
selectedValue
selectedValue,
}: MultiLevelDropdownProps) => {
const [open, setOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
@ -270,4 +277,3 @@ const MultiLevelDropdown = ({
};
export default MultiLevelDropdown;

View File

@ -0,0 +1,133 @@
import React, { useState, useRef, useEffect } from "react";
import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
const SimulationPlayer: React.FC = () => {
const [speed, setSpeed] = useState<number>(1);
const [playSimulation, setPlaySimulation] = useState(false);
const { setIsPlaying } = usePlayButtonStore();
const sliderRef = useRef<HTMLDivElement>(null);
const isDragging = useRef(false);
// Button functions
const handleReset = () => {
setSpeed(1);
};
const handlePlayStop = () => {
setPlaySimulation(!playSimulation);
};
const handleExit = () => {
setPlaySimulation(false);
setIsPlaying(false);
};
// Slider functions starts
const handleSpeedChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSpeed(parseFloat(event.target.value));
};
const calculateHandlePosition = () => {
return ((speed - 0.5) / (50 - 0.5)) * 100;
};
const handleMouseDown = () => {
isDragging.current = true;
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
};
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging.current || !sliderRef.current) return;
const sliderRect = sliderRef.current.getBoundingClientRect();
const offsetX = e.clientX - sliderRect.left;
const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1);
const newValue = 0.5 + percentage * (50 - 0.5);
setSpeed(parseFloat(newValue.toFixed(1)));
};
const handleMouseUp = () => {
isDragging.current = false;
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
useEffect(() => {
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, []);
// Slider function ends
return (
<div className="simulation-player-wrapper">
<div className="simulation-player-container">
<div className="controls-container">
<div
className="simulation-button-container"
onClick={() => {
handleReset();
}}
>
<ResetIcon />
Reset
</div>
<div
className="simulation-button-container"
onClick={() => {
handlePlayStop();
}}
>
<PlayStopIcon />
{playSimulation ? "Play" : "Stop"}
</div>
<div
className="simulation-button-container"
onClick={() => {
handleExit();
}}
>
<ExitIcon />
Exit
</div>
</div>
<div className="speed-control-container">
<div className="min-value">0.5x</div>
<div className="slider-container" ref={sliderRef}>
<div className="marker marker-10"></div>
<div className="marker marker-20"></div>
<div className="marker marker-30"></div>
<div className="marker marker-40"></div>
<div className="marker marker-50"></div>
<div className="marker marker-60"></div>
<div className="marker marker-70"></div>
<div className="marker marker-80"></div>
<div className="marker marker-90"></div>
<div className="custom-slider">
<div
className={`slider-handle ${isDragging ? "dragging" : ""}`}
style={{ left: `${calculateHandlePosition()}%` }}
onMouseDown={handleMouseDown}
>
{speed.toFixed(1)}x
</div>
<input
type="range"
min="0.5"
max="50"
step="0.1"
value={speed}
onChange={handleSpeedChange}
className="slider-input"
/>
</div>
</div>
<div className="max-value">50x</div>
</div>
</div>
</div>
);
};
export default SimulationPlayer;

View File

@ -37,6 +37,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
<div className="assetPreview">
<div className="image-preview">
<img src={assetImage} alt="" />
{/* Add canvas here */}
</div>
<div className="asset-details-preview">

View File

@ -8,24 +8,51 @@ import {
} from "../../components/icons/marketPlaceIcons";
import assetImage from "../../assets/image/image.png";
const Card: React.FC = () => {
interface CardProps {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
onSelectCard: (cardData: {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
}) => void;
}
const Card: React.FC<CardProps> = ({
assetName,
uploadedOn,
price,
rating,
views,
onSelectCard,
}) => {
const handleCardSelect = () => {
onSelectCard({ assetName, uploadedOn, price, rating, views });
};
return (
<div className="card-container">
<div className="icon">
<DownloadIcon />
</div>
<div className="image-container">
<img src={assetImage} alt="" />
<img src={assetImage} alt={assetName} />
</div>
<div className="assets-container">
<div className="name-container">
<div className="asstes-container">Asset name</div>
<div className="assets-date">Uploaded on-12 Jan 23</div>
<div className="assets-name">{assetName}</div>
<div className="assets-date">{uploadedOn}</div>
</div>
<div className="details">
<div className="content">
<EyeIconBig />
1.5k
{views}
</div>
<div className="content">
<CommentsIcon />
@ -39,17 +66,17 @@ const Card: React.FC = () => {
</div>
<div className="stars-container">
<div className="stars-wrapper">
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
{[...Array(5)].map((_, index) => (
<StarsIconSmall key={index} />
))}
</div>
<div className="units">
36,500/<span>unit</span>
{price}/<span>unit</span>
</div>
</div>
<div className="buy-now-button">Buy now</div>
<div className="buy-now-button" onClick={handleCardSelect}>
Buy now
</div>
</div>
);
};

View File

@ -1,15 +1,149 @@
import React from "react";
import React, { useState } from "react";
import Card from "./Card";
import AssetPreview from "./AssetPreview";
import RenderOverlay from "../../components/templates/Overlay";
const CardsContainer: React.FC = () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const array = [
{
id: 1,
name: "Asset 1",
uploadedOn: "12 Jan 23",
price: 36500,
rating: 4.5,
views: 500,
},
{
id: 2,
name: "Asset 2",
uploadedOn: "14 Jan 23",
price: 45000,
rating: 4.0,
views: 500,
},
{
id: 3,
name: "Asset 3",
uploadedOn: "15 Jan 23",
price: 52000,
rating: 4.8,
views: 500,
},
{
id: 4,
name: "Asset 4",
uploadedOn: "18 Jan 23",
price: 37000,
rating: 3.9,
views: 500,
},
{
id: 5,
name: "Asset 5",
uploadedOn: "20 Jan 23",
price: 60000,
rating: 5.0,
views: 500,
},
{
id: 6,
name: "Asset 6",
uploadedOn: "22 Jan 23",
price: 46000,
rating: 4.2,
views: 500,
},
{
id: 7,
name: "Asset 7",
uploadedOn: "25 Jan 23",
price: 38000,
rating: 4.3,
views: 500,
},
{
id: 8,
name: "Asset 8",
uploadedOn: "27 Jan 23",
price: 41000,
rating: 4.1,
views: 500,
},
{
id: 9,
name: "Asset 9",
uploadedOn: "30 Jan 23",
price: 55000,
rating: 4.6,
views: 500,
},
{
id: 10,
name: "Asset 10",
uploadedOn: "2 Feb 23",
price: 49000,
rating: 4.4,
views: 500,
},
{
id: 11,
name: "Asset 11",
uploadedOn: "5 Feb 23",
price: 62000,
rating: 5.0,
views: 500,
},
{
id: 12,
name: "Asset 12",
uploadedOn: "7 Feb 23",
price: 53000,
rating: 4.7,
views: 500,
},
];
const [selectedCard, setSelectedCard] = useState<{
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
} | null>(null);
const handleCardSelect = (cardData: {
assetName: string;
uploadedOn: string;
price: number;
rating: number;
views: number;
}) => {
setSelectedCard(cardData);
};
return (
<div className="cards-container-container">
<div className="header">Products You May Like</div>
<div className="cards-wrapper-container">
{array.map((index) => (
<Card key={index} />
{array.map((asset) => (
<Card
key={asset.id}
assetName={asset.name}
uploadedOn={asset.uploadedOn}
price={asset.price}
rating={asset.rating}
views={asset.views}
onSelectCard={handleCardSelect}
/>
))}
{/* <RenderOverlay> */}
{selectedCard && (
<AssetPreview
selectedCard={selectedCard}
setSelectedCard={setSelectedCard}
/>
)}
{/* </RenderOverlay> */}
</div>
</div>
);

View File

@ -1,4 +1,4 @@
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { Canvas } from "@react-three/fiber";
import { Environment, KeyboardControls } from "@react-three/drei";
@ -15,6 +15,8 @@ import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr";
import SelectionControls from "./controls/selection/selectionControls";
import MeasurementTool from "./tools/measurementTool";
import Simulation from "../simulation/simulation";
import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets";
// import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
@ -27,6 +29,9 @@ export default function Scene() {
{ name: "right", keys: ["ArrowRight", "d", "D"] },
], [])
return (
<KeyboardControls map={map}>
<Canvas
@ -36,13 +41,16 @@ export default function Scene() {
onContextMenu={(e) => {
e.preventDefault();
}}
>
<DroppedObjects/>
<Controls />
<TransformControl />
<SelectionControls />
<MeasurementTool />
<World />
<ZoneCentreTarget />
{/* <Simulation /> */}
<Simulation />
<PostProcessing />
<Sun />

View File

@ -17,9 +17,9 @@ import {
} from "../store/store";
import { useNavigate } from "react-router-dom";
import { usePlayButtonStore } from "../store/usePlayButtonStore";
import SimulationUI from "../modules/simulation/simulationUI";
import MarketPlace from "../modules/market/MarketPlace";
import LoadingPage from "../components/templates/LoadingPage";
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
const Project: React.FC = () => {
let navigate = useNavigate();
@ -65,6 +65,7 @@ const Project: React.FC = () => {
<RealTimeVisulization />
{activeModule !== "market" && <Tools />}
{/* <SimulationUI /> */}
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
</div>
);
};

View File

@ -5,3 +5,10 @@
user-select: none;
font-size: var(--font-size-regular);
}
input[type="password"]::-ms-reveal, /* For Microsoft Edge */
input[type="password"]::-ms-clear, /* For Edge clear button */
input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button */
input[type="password"]::-webkit-inner-spin-button { /* Just in case */
display: none;
}

View File

@ -280,28 +280,24 @@ input {
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
background-color: #ffffff;
border: 1px solid #cccccc;
top: 110%;
right: -16px;
background-color: var(--background-color);
border: 1px solid var(--border-color);
border-radius: 5px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
box-shadow: #{$box-shadow-medium};
z-index: 1000;
min-width: 200px;
overflow: auto;
max-height: 600px;
max-height: 400px;
.dropdown-content {
display: flex;
flex-direction: column;
gap: 6px;
.nested-dropdown {
// &:first-child{
margin-left: 0;
// }
}
padding: 10px;
}
@ -309,13 +305,13 @@ input {
display: block;
padding: 5px 10px;
text-decoration: none;
color: #000000;
color: var(--text-color);
font-size: 14px;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: #f0f0f0;
background-color: var(--background-color);
}
}
@ -329,15 +325,20 @@ input {
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
color: #000000;
color: var(--text-color);
transition: background-color 0.3s ease;
border-radius: #{$border-radius-small};
.arrow-container{
@include flex-center;
}
&:hover {
background-color: #f0f0f0;
background-color: var(--background-color);
}
&.open {
background-color: #e0e0e0;
color: var(--accent-color);
background-color: var(--highlight-accent-color);
}
.icon {
@ -349,7 +350,7 @@ input {
.submenu {
margin-top: 5px;
padding-left: 20px;
border-left: 2px solid #cccccc;
border-left: 2px solid var(--border-color);
display: flex;
flex-direction: column;
gap: 6px;
@ -576,26 +577,26 @@ input {
color: var(--text-disabled);
}
}
.entered-emails{
.entered-emails {
@include flex-center;
gap: 2px;
background: var(--background-color-gray);
padding: 0 4px;
border-radius: #{$border-radius-large};
span{
span {
height: 14px;
width: 14px;
line-height: 12px;
text-align: center;
border-radius: #{$border-radius-small};
&:hover{
&:hover {
background: var(--accent-color);
color: var(--primary-color);
}
}
}
}
.invite-button{
.invite-button {
padding: 4px 12px;
border-radius: #{$border-radius-large};
background: var(--accent-color);

View File

@ -9,10 +9,13 @@
position: absolute;
left: 0;
padding: 100px 50px;
padding-bottom: 32px;
backdrop-filter: blur(6px);
.marketplace-container {
position: relative;
padding: 20px 2px;
height: calc(100vh - 120px);
height: 100%;
background-color: var(--background-color);
box-shadow: #{$box-shadow-medium};
border-radius: #{$border-radius-extra-large};
@ -40,17 +43,16 @@
min-width: 60%;
max-width: 684px;
padding: 0;
border-radius: $border-radius-large ;
border-radius: $border-radius-large;
.search-container {
border: none !important;
box-shadow: $box-shadow-medium;
border-radius: $border-radius-large ;
border-radius: $border-radius-large;
input {
border: none !important;
outline: none;
}
}
}
@ -73,7 +75,6 @@
.stars {
display: flex;
align-items: center;
}
}
}
@ -99,8 +100,8 @@
width: calc(25% - 23px);
border-radius: 18px;
padding: 12px;
box-shadow: 0px 2px 10.5px 0px #0000000D;
border: 1px solid var(--background-accent-transparent, #E0DFFF80);
box-shadow: 0px 2px 10.5px 0px #0000000d;
border: 1px solid var(--background-accent-transparent, #e0dfff80);
position: relative;
display: flex;
flex-direction: column;
@ -135,7 +136,7 @@
.asstes-container {
font-weight: #{$bold-weight};
font-size: $regular ;
font-size: $regular;
}
.assets-date {
@ -158,9 +159,8 @@
}
.vendor-icon {
font-weight: #{$bold-weight};
font-size: $regular ;
font-size: $regular;
}
.stars-container {
@ -171,7 +171,7 @@
.buy-now-button {
width: 100%;
background-color: var(--background-color-secondary);
border-radius: $border-radius-extra-large ;
border-radius: $border-radius-extra-large;
padding: 8px 0;
@include flex-center;
color: var(--accent-color);
@ -185,3 +185,173 @@
}
}
}
.assetPreview-wrapper {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 3;
.assetPreview {
width: 100%;
height: 100%;
background-color: var(--background-color);
display: flex;
gap: 12px;
z-index: 100;
border-radius: 20px;
}
// Image Preview Section
.image-preview {
width: 50%;
height: 100%;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
// Asset Details Section
.asset-details-preview {
width: 50%;
padding: 50px 20px;
overflow-y: auto;
}
// Organization Section (Top part with image and details)
.organization {
display: flex;
align-items: center;
margin-bottom: 20px;
gap: 10px;
.image {
@include flex-center;
height: 30px;
width: 30px;
min-height: 26px;
min-width: 26px;
border-radius: 50%;
font-weight: var(--font-weight-bold);
color: var(--background-color);
background-color: var(--accent-color);
}
.organization-details {
display: flex;
flex-direction: column;
.organization-name {
font-weight: bold;
margin-bottom: 5px;
font-weight: #{$bold-weight};
font-size: $regular;
}
.follow {
color: var(--accent-color);
cursor: pointer;
}
}
}
// Asset Details
.asset-details {
margin-top: 20px;
.asset-name {
font-size: 1.5em;
font-weight: bold;
margin-bottom: 10px;
font-weight: #{$bold-weight};
font-size: $large;
}
.asset-description {
margin-bottom: 20px;
color: #666;
}
.asset-review {
width: fit-content;
padding: 5px 10px;
display: flex;
align-items: center;
margin-bottom: 20px;
outline: 1px solid #909090cc;
border-radius: 6px;
.asset-rating {
display: flex;
align-items: center;
gap: 4px;
margin-right: 10px;
font-weight: bold;
position: relative;
font-weight: #{$bold-weight};
font-size: $regular;
&::after {
margin-left: 5px;
content: "";
display: block;
width: 2px;
height: 12px;
background-color: #ccc;
}
}
.asset-view {
font-weight: #{$bold-weight};
font-size: $regular;
}
}
.asset-price {
font-size: $xxlarge;
font-weight: bold;
margin-bottom: 20px;
}
}
// Button Container and Button Styles
.button-container {
display: flex;
gap: 10px;
}
.button {
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
text-align: center;
&:first-child {
outline: 1px solid var(--accent-color);
color: var(--accent-color);
}
&:last-child {
background-color: var(--accent-color);
color: var(--background-color);
}
}
.closeButton {
color: var(--accent-color);
position: absolute;
top: 18px;
left: 18px;
@include flex-center;
cursor: pointer;
font-size: var(--font-size-large);
}
}

View File

@ -0,0 +1,114 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins" as *;
.simulation-player-wrapper {
position: fixed;
bottom: 32px;
left: 50%;
z-index: 2;
transform: translate(-50%, 0);
.simulation-player-container {
.controls-container {
@include flex-center;
gap: 12px;
margin-bottom: 4px;
.simulation-button-container {
@include flex-center;
gap: 2px;
padding: 6px 8px;
min-width: 64px;
background-color: var(--background-color);
border-radius: #{$border-radius-small};
cursor: pointer;
&:hover {
background-color: var(--highlight-accent-color);
color: var(--accent-color);
path {
stroke: var(--accent-color);
}
}
}
}
.speed-control-container {
@include flex-center;
gap: 18px;
padding: 5px 16px;
background: var(--background-color);
border-radius: #{$border-radius-medium};
box-sizing: #{$box-shadow-medium};
.min-value,
.max-value {
font-weight: var(--font-weight-bold);
}
.slider-container {
width: 580px;
max-width: 80vw;
height: 28px;
background: var(--background-color-gray);
border-radius: #{$border-radius-small};
position: relative;
padding: 4px 26px;
.custom-slider {
height: 100%;
width: 100%;
position: relative;
.slider-input {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
z-index: 3;
cursor: pointer;
}
.slider-handle {
position: absolute;
width: 42px;
line-height: 20px;
text-align: center;
background: var(--accent-color);
color: var(--primary-color);
border-radius: #{$border-radius-small};
transform: translateX(-50%);
cursor: pointer;
z-index: 2;
}
}
.marker{
position: absolute;
background-color: var(--text-disabled);
width: 2px;
height: 12px;
border-radius: 1px;
top: 8px;
}
.marker.marker-10{
left: 10%;
}
.marker.marker-20{
left: 20%;
}
.marker.marker-30{
left: 30%;
}
.marker.marker-40{
left: 40%;
}
.marker.marker-50{
left: 50%;
}
.marker.marker-60{
left: 60%;
}
.marker.marker-70{
left: 70%;
}
.marker.marker-80{
left: 80%;
}
.marker.marker-90{
left: 90%;
}
}
}
}
}

View File

@ -8,7 +8,7 @@
flex-direction: column;
gap: 6px;
width: 100%;
min-width: 250px;
// min-width: 1450px;
.header {
display: flex;
justify-content: center;

View File

@ -20,8 +20,9 @@
@use 'components/tools';
@use 'components/visualization/floating/energyConsumed';
@use 'components/visualization/ui/styledWidgets';
@use './components/visualization/floating/common';
@use './components/marketPlace/marketPlace.scss';
@use 'components/visualization/floating/common';
@use 'components/marketPlace/marketPlace';
@use 'components/simulation/simulation';
// layout
@use 'layout/loading';

View File

@ -1,4 +1,5 @@
@use "../abstracts/variables.scss" as *;
@use "../abstracts/mixins.scss" as *;
// Main Container
.realTime-viz {
@ -35,7 +36,7 @@
}
}
.zoon-wrapper {
.zone-wrapper {
display: flex;
background-color: var(--background-color);
position: absolute;
@ -43,15 +44,37 @@
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
padding: 4px;
border-radius: 8px;
max-width: 80%;
overflow: auto;
max-width: calc(100% - 450px);
// max-width: calc(100% - 450px);
&::-webkit-scrollbar {
display: none;
}
.arrow {
background-color: var(--highlight-accent-color);
color: var(--background-color);
}
.zones-wrapper {
padding: 6px;
display: flex;
gap: 6px;
border-radius: #{$border-radius-medium};
overflow-x: auto;
&::-webkit-scrollbar {
display: none;
}
}
.no-zone {
@include flex-center;
gap: 4px;
padding: 4px;
color: var(--text-disabled);
}
.zone {
width: auto;
background-color: var(--background-color);
@ -68,28 +91,10 @@
}
}
.zoon-wrapper.bottom {
.zone-wrapper.bottom {
bottom: 210px;
}
@media (max-width: 1024px) {
width: 80%; // Increase width to take more space on smaller screens
height: 500px; // Reduce height to fit smaller screens
left: 50%; // Center horizontally
.main-container {
margin: 0 15px; // Reduce margin for better spacing
}
.zoon-wrapper {
bottom: 5px; // Adjust position for smaller screens
&.bottom {
bottom: 150px; // Adjust for bottom placement
}
}
}
.content-container {
display: flex;
height: 100vh;
@ -104,7 +109,7 @@
margin: 0 30px;
transition: height 0.3s ease, margin 0.3s ease;
.zoon-wrapper {
.zone-wrapper {
display: flex;
background-color: rgba(224, 223, 255, 0.5);
position: absolute;
@ -150,15 +155,15 @@
transition: all 0.3s ease;
border-radius: 6px;
overflow: visible !important;
z-index: $z-index-tools;
.panel-content {
position: relative;
height: 100%;
padding: 10px;
overflow: auto;
display: flex;
flex-direction: column;
gap: 10px;
gap: 6px;
background-color: var(--background-color);
&::-webkit-scrollbar {
@ -167,8 +172,7 @@
.chart-container {
width: 100%;
height: 24% !important;
height: 25% !important;
min-height: 150px;
max-height: 100%;
border: 1px dotted #a9a9a9;
@ -176,6 +180,70 @@
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
padding: 6px 0;
background-color: white;
position: relative;
.kebab {
width: 30px;
height: 30px;
position: absolute;
top: 0px;
right: 0px;
z-index: 10;
cursor: pointer;
@include flex-center;
}
.kebab-options {
position: absolute;
top: 12px;
right: -100px;
transform: translate(0px, 0);
background-color: var(--background-color);
z-index: 10;
display: flex;
flex-direction: column;
gap: 6px;
border-radius: 4px;
box-shadow: var(--box-shadow-medium);
.btn {
display: flex;
gap: 6px;
align-items: center;
padding: 5px 10px;
color: var(--text-color);
&:hover {
.label {
color: var(--accent-color);
}
}
&:hover {
background-color: var(--highlight-accent-color);
width: 100%;
svg {
&:first-child {
fill: var(--accent-color);
}
&:last-child {
fill: auto;
stroke: var(--accent-color);
}
}
}
}
.btn-blur {
color: var(--text-disabled);
cursor: not-allowed;
pointer-events: none;
}
}
}
.close-btn {
@ -194,8 +262,6 @@
left: 0;
right: 0;
.panel-content {
display: flex;
flex-direction: row;
@ -236,19 +302,21 @@
}
}
.playingFlase{
.zoon-wrapper{
bottom: 300px !important;
.playingFlase {
.zone-wrapper.bottom {
bottom: 300px;
}
}
// Side Buttons
.side-button-container {
position: absolute;
display: flex;
background-color: var(--background-color);
padding: 5px;
border-radius: 8px;
padding: 2px;
border-radius: 2px;
transition: transform 0.3s ease;
box-shadow: #{$box-shadow-medium};
.extra-Bs {
display: flex;
@ -285,6 +353,24 @@
border: none;
color: var(--background-color);
border-radius: 4px;
.add-icon {
@include flex-center;
transition: rotate 0.2s;
}
path {
stroke: var(--primary-color);
stroke-width: 2;
}
}
.active {
background: #ffe3e0;
.add-icon {
rotate: 45deg;
path {
stroke: #f65648;
stroke-width: 2;
}
}
}
&.top {
@ -401,3 +487,33 @@
}
}
}
.arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
border: none;
cursor: pointer;
z-index: 10;
height: 100%;
}
.left-arrow {
left: 0;
}
.right-arrow {
right: 0;
}
.zone {
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
.zone.active {
background-color: #007bff;
color: white;
}