simulation #13
@@ -511,3 +511,99 @@ export function AI_Icon() {
|
|||||||
</svg>
|
</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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -93,3 +93,114 @@ export function SimulationIcon({ isActive }: { isActive: boolean }) {
|
|||||||
</svg>
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const Visualization = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="visualization-right-sideBar">
|
<div className="visualization-right-sideBar">
|
||||||
<ToggleHeader
|
<ToggleHeader
|
||||||
options={["Data", "Design"]}
|
options={["Data"]}
|
||||||
activeOption={activeOption}
|
activeOption={activeOption}
|
||||||
handleClick={handleToggleClick}
|
handleClick={handleToggleClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -285,9 +285,13 @@ const Tools: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="exitPlay" onClick={() => setIsPlaying(false)}>
|
<>
|
||||||
X
|
{activeModule !== "simulation" && (
|
||||||
</div>
|
<div className="exitPlay" onClick={() => setIsPlaying(false)}>
|
||||||
|
X
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ const PieChartComponent = ({
|
|||||||
.getPropertyValue("--accent-color")
|
.getPropertyValue("--accent-color")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
console.log("accentColor: ", accentColor);
|
|
||||||
const options = useMemo(
|
const options = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
|||||||
@@ -1,211 +1,220 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
CleanPannel,
|
CleanPannel,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
} from "../../icons/RealTimeVisulationIcons";
|
} from "../../icons/RealTimeVisulationIcons";
|
||||||
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
|
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
|
||||||
|
import { AddIcon } from "../../icons/ExportCommonIcons";
|
||||||
// Define the type for `Side`
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
// Define the type for `Side`
|
||||||
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
// Define the type for the props passed to the Buttons component
|
|
||||||
interface ButtonsProps {
|
// Define the type for the props passed to the Buttons component
|
||||||
selectedZone: {
|
interface ButtonsProps {
|
||||||
zoneName: string;
|
selectedZone: {
|
||||||
activeSides: Side[];
|
zoneName: string;
|
||||||
panelOrder: Side[];
|
activeSides: Side[];
|
||||||
lockedPanels: Side[];
|
panelOrder: Side[];
|
||||||
zoneId: string;
|
lockedPanels: Side[];
|
||||||
zoneViewPortTarget: number[];
|
zoneId: string;
|
||||||
zoneViewPortPosition: number[]
|
zoneViewPortTarget: number[];
|
||||||
widgets: {
|
zoneViewPortPosition: number[];
|
||||||
id: string;
|
widgets: {
|
||||||
type: string;
|
id: string;
|
||||||
title: string;
|
type: string;
|
||||||
panel: Side;
|
title: string;
|
||||||
data: any;
|
panel: Side;
|
||||||
}[];
|
data: any;
|
||||||
};
|
}[];
|
||||||
setSelectedZone: React.Dispatch<
|
};
|
||||||
React.SetStateAction<{
|
setSelectedZone: React.Dispatch<
|
||||||
zoneName: string;
|
React.SetStateAction<{
|
||||||
activeSides: Side[];
|
zoneName: string;
|
||||||
panelOrder: Side[];
|
activeSides: Side[];
|
||||||
lockedPanels: Side[];
|
panelOrder: Side[];
|
||||||
zoneId: string;
|
lockedPanels: Side[];
|
||||||
zoneViewPortTarget: number[];
|
zoneId: string;
|
||||||
zoneViewPortPosition: number[]
|
zoneViewPortTarget: number[];
|
||||||
widgets: {
|
zoneViewPortPosition: number[];
|
||||||
id: string;
|
widgets: {
|
||||||
type: string;
|
id: string;
|
||||||
title: string;
|
type: string;
|
||||||
panel: Side;
|
title: string;
|
||||||
data: any;
|
panel: Side;
|
||||||
}[];
|
data: any;
|
||||||
}>
|
}[];
|
||||||
>;
|
}>
|
||||||
hiddenPanels: Side[]; // Add this prop for hidden panels
|
>;
|
||||||
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
|
hiddenPanels: Side[]; // Add this prop for hidden panels
|
||||||
}
|
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
|
||||||
|
}
|
||||||
const AddButtons: React.FC<ButtonsProps> = ({
|
|
||||||
selectedZone,
|
const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
setSelectedZone,
|
selectedZone,
|
||||||
setHiddenPanels,
|
setSelectedZone,
|
||||||
hiddenPanels,
|
setHiddenPanels,
|
||||||
}) => {
|
hiddenPanels,
|
||||||
// Local state to track hidden panels
|
}) => {
|
||||||
|
// Local state to track hidden panels
|
||||||
// Function to toggle lock/unlock a panel
|
|
||||||
const toggleLockPanel = (side: Side) => {
|
// Function to toggle lock/unlock a panel
|
||||||
const newLockedPanels = selectedZone.lockedPanels.includes(side)
|
const toggleLockPanel = (side: Side) => {
|
||||||
? selectedZone.lockedPanels.filter((panel) => panel !== side)
|
const newLockedPanels = selectedZone.lockedPanels.includes(side)
|
||||||
: [...selectedZone.lockedPanels, side];
|
? selectedZone.lockedPanels.filter((panel) => panel !== side)
|
||||||
|
: [...selectedZone.lockedPanels, side];
|
||||||
const updatedZone = {
|
|
||||||
...selectedZone,
|
const updatedZone = {
|
||||||
lockedPanels: newLockedPanels,
|
...selectedZone,
|
||||||
};
|
lockedPanels: newLockedPanels,
|
||||||
|
};
|
||||||
// Update the selectedZone state
|
|
||||||
setSelectedZone(updatedZone);
|
// Update the selectedZone state
|
||||||
};
|
setSelectedZone(updatedZone);
|
||||||
|
};
|
||||||
// Function to toggle visibility of a panel
|
|
||||||
const toggleVisibility = (side: Side) => {
|
// Function to toggle visibility of a panel
|
||||||
const isHidden = hiddenPanels.includes(side);
|
const toggleVisibility = (side: Side) => {
|
||||||
if (isHidden) {
|
const isHidden = hiddenPanels.includes(side);
|
||||||
// If the panel is already hidden, remove it from the hiddenPanels array
|
if (isHidden) {
|
||||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
// If the panel is already hidden, remove it from the hiddenPanels array
|
||||||
} else {
|
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
||||||
// If the panel is visible, add it to the hiddenPanels array
|
} else {
|
||||||
setHiddenPanels([...hiddenPanels, side]);
|
// If the panel is visible, add it to the hiddenPanels array
|
||||||
}
|
setHiddenPanels([...hiddenPanels, side]);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
// Function to clean all widgets from a panel
|
|
||||||
const cleanPanel = (side: Side) => {
|
// Function to clean all widgets from a panel
|
||||||
const cleanedWidgets = selectedZone.widgets.filter(
|
const cleanPanel = (side: Side) => {
|
||||||
(widget) => widget.panel !== side
|
const cleanedWidgets = selectedZone.widgets.filter(
|
||||||
);
|
(widget) => widget.panel !== side
|
||||||
|
);
|
||||||
const updatedZone = {
|
|
||||||
...selectedZone,
|
const updatedZone = {
|
||||||
widgets: cleanedWidgets,
|
...selectedZone,
|
||||||
};
|
widgets: cleanedWidgets,
|
||||||
|
};
|
||||||
// Update the selectedZone state
|
|
||||||
setSelectedZone(updatedZone);
|
// Update the selectedZone state
|
||||||
};
|
setSelectedZone(updatedZone);
|
||||||
|
};
|
||||||
// Function to handle "+" button click
|
|
||||||
const handlePlusButtonClick = (side: Side) => {
|
// Function to handle "+" button click
|
||||||
if (selectedZone.activeSides.includes(side)) {
|
const handlePlusButtonClick = (side: Side) => {
|
||||||
// If the panel is already active, remove all widgets and close the panel
|
if (selectedZone.activeSides.includes(side)) {
|
||||||
const cleanedWidgets = selectedZone.widgets.filter(
|
// If the panel is already active, remove all widgets and close the panel
|
||||||
(widget) => widget.panel !== side
|
const cleanedWidgets = selectedZone.widgets.filter(
|
||||||
);
|
(widget) => widget.panel !== side
|
||||||
const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
|
);
|
||||||
|
const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
|
||||||
const updatedZone = {
|
|
||||||
...selectedZone,
|
const updatedZone = {
|
||||||
widgets: cleanedWidgets,
|
...selectedZone,
|
||||||
activeSides: newActiveSides,
|
widgets: cleanedWidgets,
|
||||||
panelOrder: newActiveSides,
|
activeSides: newActiveSides,
|
||||||
};
|
panelOrder: newActiveSides,
|
||||||
|
};
|
||||||
// Delete the selectedZone state
|
|
||||||
console.log('updatedZone: ', updatedZone);
|
// Delete the selectedZone state
|
||||||
setSelectedZone(updatedZone);
|
console.log("updatedZone: ", updatedZone);
|
||||||
} else {
|
setSelectedZone(updatedZone);
|
||||||
// If the panel is not active, activate it
|
} else {
|
||||||
const newActiveSides = [...selectedZone.activeSides, side];
|
// If the panel is not active, activate it
|
||||||
|
const newActiveSides = [...selectedZone.activeSides, side];
|
||||||
const updatedZone = {
|
|
||||||
...selectedZone,
|
const updatedZone = {
|
||||||
activeSides: newActiveSides,
|
...selectedZone,
|
||||||
panelOrder: newActiveSides,
|
activeSides: newActiveSides,
|
||||||
};
|
panelOrder: newActiveSides,
|
||||||
const email = localStorage.getItem('email')
|
};
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
const email = localStorage.getItem("email");
|
||||||
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
// console.log('response: ', response);
|
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
|
||||||
|
// console.log('response: ', response);
|
||||||
// Update the selectedZone state
|
|
||||||
console.log("updatedZone: ", updatedZone);
|
// Update the selectedZone state
|
||||||
setSelectedZone(updatedZone);
|
console.log("updatedZone: ", updatedZone);
|
||||||
}
|
setSelectedZone(updatedZone);
|
||||||
};
|
}
|
||||||
|
};
|
||||||
return (
|
|
||||||
<>
|
return (
|
||||||
<div>
|
<>
|
||||||
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
|
<div>
|
||||||
<div key={side} className={`side-button-container ${side}`}>
|
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
|
||||||
{/* "+" Button */}
|
<div key={side} className={`side-button-container ${side}`}>
|
||||||
<button
|
{/* "+" Button */}
|
||||||
className={`side-button ${side}`}
|
<button
|
||||||
onClick={() => handlePlusButtonClick(side)}
|
className={`side-button ${side}${
|
||||||
title={
|
selectedZone.activeSides.includes(side) ? " active" : ""
|
||||||
selectedZone.activeSides.includes(side)
|
}`}
|
||||||
? `Remove all items and close ${side} panel`
|
onClick={() => handlePlusButtonClick(side)}
|
||||||
: `Activate ${side} panel`
|
title={
|
||||||
}
|
selectedZone.activeSides.includes(side)
|
||||||
>
|
? `Remove all items and close ${side} panel`
|
||||||
+
|
: `Activate ${side} panel`
|
||||||
</button>
|
}
|
||||||
|
>
|
||||||
{/* Extra Buttons */}
|
<div className="add-icon">
|
||||||
{selectedZone.activeSides.includes(side) && (
|
<AddIcon />
|
||||||
<div className="extra-Bs">
|
</div>
|
||||||
{/* Hide Panel */}
|
</button>
|
||||||
<div
|
|
||||||
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
|
{/* Extra Buttons */}
|
||||||
}`}
|
{selectedZone.activeSides.includes(side) && (
|
||||||
title={
|
<div className="extra-Bs">
|
||||||
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
{/* Hide Panel */}
|
||||||
}
|
<div
|
||||||
onClick={() => toggleVisibility(side)}
|
className={`icon ${
|
||||||
>
|
hiddenPanels.includes(side) ? "active" : ""
|
||||||
<EyeIcon
|
}`}
|
||||||
fill={
|
title={
|
||||||
hiddenPanels.includes(side)
|
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
||||||
? "white"
|
}
|
||||||
: "#1D1E21"
|
onClick={() => toggleVisibility(side)}
|
||||||
}
|
>
|
||||||
/>
|
<EyeIcon
|
||||||
</div>
|
fill={hiddenPanels.includes(side) ? "var(--primary-color)" : "var(--text-color)"}
|
||||||
|
/>
|
||||||
{/* Clean Panel */}
|
</div>
|
||||||
<div
|
|
||||||
className="icon"
|
{/* Clean Panel */}
|
||||||
title="Clean Panel"
|
<div
|
||||||
onClick={() => cleanPanel(side)}
|
className="icon"
|
||||||
>
|
title="Clean Panel"
|
||||||
<CleanPannel />
|
onClick={() => cleanPanel(side)}
|
||||||
</div>
|
>
|
||||||
|
<CleanPannel />
|
||||||
{/* Lock/Unlock Panel */}
|
</div>
|
||||||
<div
|
|
||||||
className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
|
{/* Lock/Unlock Panel */}
|
||||||
}`}
|
<div
|
||||||
title={
|
className={`icon ${
|
||||||
selectedZone.lockedPanels.includes(side)
|
selectedZone.lockedPanels.includes(side) ? "active" : ""
|
||||||
? "Unlock Panel"
|
}`}
|
||||||
: "Lock Panel"
|
title={
|
||||||
}
|
selectedZone.lockedPanels.includes(side)
|
||||||
onClick={() => toggleLockPanel(side)}
|
? "Unlock Panel"
|
||||||
>
|
: "Lock Panel"
|
||||||
<LockIcon fill={selectedZone.lockedPanels.includes(side) ? "#ffffff" : "#1D1E21"} />
|
}
|
||||||
</div>
|
onClick={() => toggleLockPanel(side)}
|
||||||
</div>
|
>
|
||||||
)}
|
<LockIcon
|
||||||
</div>
|
fill={
|
||||||
))}
|
selectedZone.lockedPanels.includes(side)
|
||||||
</div>
|
? "var(--primary-color)"
|
||||||
</>
|
: "var(--text-color)"
|
||||||
);
|
}
|
||||||
};
|
/>
|
||||||
|
</div>
|
||||||
export default AddButtons;
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddButtons;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||||
import { Widget } from "../../../store/useWidgetStore";
|
import { Widget } from "../../../store/useWidgetStore";
|
||||||
|
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
|
||||||
|
import { InfoIcon } from "../../icons/ExportCommonIcons";
|
||||||
|
|
||||||
// Define the type for `Side`
|
// Define the type for `Side`
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
@@ -60,138 +62,135 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||||||
// Ref for the container element
|
// Ref for the container element
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
// Example state for selectedOption and options (adjust based on your actual use case)
|
// State to track overflow visibility
|
||||||
const [selectedOption, setSelectedOption] = React.useState<string | null>(
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||||
null
|
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||||
);
|
|
||||||
// console.log('setSelectedOption: ', setSelectedOption);
|
|
||||||
const [options, setOptions] = React.useState<string[]>([]);
|
|
||||||
// console.log('setOptions: ', setOptions);
|
|
||||||
|
|
||||||
// Scroll to the selected option when it changes
|
// Function to calculate overflow state
|
||||||
useEffect(() => {
|
const updateOverflowState = useCallback(() => {
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
|
|
||||||
if (container && selectedOption) {
|
|
||||||
// Handle scrolling to the selected option
|
|
||||||
const index = options.findIndex((option) => {
|
|
||||||
const formattedOption = formatOptionName(option);
|
|
||||||
const selectedFormattedOption =
|
|
||||||
selectedOption?.split("_")[1] || selectedOption;
|
|
||||||
return formattedOption === selectedFormattedOption;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (index !== -1) {
|
|
||||||
const optionElement = container.children[index] as HTMLElement;
|
|
||||||
if (optionElement) {
|
|
||||||
optionElement.scrollIntoView({
|
|
||||||
behavior: "smooth",
|
|
||||||
block: "nearest",
|
|
||||||
inline: "center",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [selectedOption, options]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const container = containerRef.current;
|
|
||||||
|
|
||||||
const handleWheel = (event: WheelEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (container) {
|
|
||||||
container.scrollBy({
|
|
||||||
left: event.deltaY * 2, // Adjust the multiplier for faster scrolling
|
|
||||||
behavior: "smooth",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let isDragging = false;
|
|
||||||
let startX: number;
|
|
||||||
let scrollLeft: number;
|
|
||||||
|
|
||||||
const handleMouseDown = (event: MouseEvent) => {
|
|
||||||
isDragging = true;
|
|
||||||
startX = event.pageX - (container?.offsetLeft || 0);
|
|
||||||
scrollLeft = container?.scrollLeft || 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
|
||||||
if (!isDragging || !container) return;
|
|
||||||
event.preventDefault();
|
|
||||||
const x = event.pageX - (container.offsetLeft || 0);
|
|
||||||
const walk = (x - startX) * 2; // Adjust the multiplier for faster dragging
|
|
||||||
container.scrollLeft = scrollLeft - walk;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
|
||||||
isDragging = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
|
||||||
isDragging = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (container) {
|
if (container) {
|
||||||
container.addEventListener("wheel", handleWheel, { passive: false });
|
const isOverflowing = container.scrollWidth > container.clientWidth;
|
||||||
container.addEventListener("mousedown", handleMouseDown);
|
const canScrollLeft = container.scrollLeft > 0;
|
||||||
container.addEventListener("mousemove", handleMouseMove);
|
const canScrollRight =
|
||||||
container.addEventListener("mouseup", handleMouseUp);
|
container.scrollLeft + container.clientWidth < container.scrollWidth;
|
||||||
container.addEventListener("mouseleave", handleMouseLeave);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
setShowLeftArrow(isOverflowing && canScrollLeft);
|
||||||
if (container) {
|
setShowRightArrow(isOverflowing && canScrollRight);
|
||||||
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)
|
useEffect(() => {
|
||||||
const formatOptionName = (option: string): string => {
|
const container = containerRef.current;
|
||||||
// Replace underscores with spaces and capitalize the first letter
|
|
||||||
return option.replace(/_/g, " ").replace(/^\w/, (c) => c.toUpperCase());
|
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 (
|
return (
|
||||||
<div
|
<div className="zone-wrapper">
|
||||||
ref={containerRef}
|
{/* Left Arrow */}
|
||||||
className={`zoon-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
|
{showLeftArrow && (
|
||||||
}`}
|
<button className="arrow left-arrow" onClick={handleScrollLeft}>
|
||||||
>
|
<MoveArrowLeft />
|
||||||
{Object.keys(zonesData).map((zoneName, index) => (
|
</button>
|
||||||
<div
|
)}
|
||||||
key={index}
|
|
||||||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
|
||||||
}`}
|
|
||||||
onClick={() => {
|
|
||||||
|
|
||||||
|
{/* Zones Wrapper */}
|
||||||
setSelectedZone({
|
{Object.keys(zonesData).length !== 0 ? (
|
||||||
zoneName,
|
<div ref={containerRef} className="zones-wrapper">
|
||||||
activeSides: zonesData[zoneName].activeSides || [],
|
{Object.keys(zonesData).map((zoneName, index) => (
|
||||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
<div
|
||||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
key={index}
|
||||||
widgets: zonesData[zoneName].widgets || [],
|
className={`zone ${
|
||||||
zoneId: zonesData[zoneName]?.zoneId || "",
|
selectedZone.zoneName === zoneName ? "active" : ""
|
||||||
zoneViewPortTarget: zonesData[zoneName].zoneViewPortTarget || [],
|
}`}
|
||||||
zoneViewPortPosition:
|
onClick={() => {
|
||||||
zonesData[zoneName].zoneViewPortPosition || [],
|
console.log("zoneName: ", zoneName);
|
||||||
});
|
setSelectedZone({
|
||||||
// setSelectedZone({
|
zoneName,
|
||||||
// zoneName,
|
activeSides: zonesData[zoneName].activeSides || [],
|
||||||
// ...zonesData[zoneName],
|
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||||
// });
|
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||||
}}
|
widgets: zonesData[zoneName].widgets || [],
|
||||||
>
|
zoneId: zonesData[zoneName]?.zoneId || "",
|
||||||
{zoneName}
|
zoneViewPortTarget:
|
||||||
|
zonesData[zoneName].zoneViewPortTarget || [],
|
||||||
|
zoneViewPortPosition:
|
||||||
|
zonesData[zoneName].zoneViewPortPosition || [],
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{zoneName}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,164 +1,317 @@
|
|||||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||||
import ProgressCard from "../realTimeVis/charts/ProgressCard";
|
import ProgressCard from "../realTimeVis/charts/ProgressCard";
|
||||||
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
|
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
|
||||||
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
|
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
|
||||||
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
|
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
|
||||||
import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent";
|
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
|
||||||
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
|
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
|
||||||
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
|
import {
|
||||||
|
DeleteIcon,
|
||||||
export const DraggableWidget = ({
|
DublicateIcon,
|
||||||
widget,
|
KebabIcon,
|
||||||
hiddenPanels, // Add this prop to track hidden panels
|
} from "../../icons/ExportCommonIcons";
|
||||||
index, onReorder
|
import { useEffect, useRef, useState } from "react";
|
||||||
}: {
|
|
||||||
widget: any;
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
hiddenPanels: string[]; // Array of hidden panel names
|
|
||||||
index: number; onReorder: (fromIndex: number, toIndex: number) => void
|
interface Widget {
|
||||||
}) => {
|
id: string;
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
type: string;
|
||||||
|
title: string;
|
||||||
const handlePointerDown = () => {
|
panel: Side;
|
||||||
if (selectedChartId?.id !== widget.id) {
|
data: any;
|
||||||
setSelectedChartId(widget);
|
}
|
||||||
}
|
|
||||||
};
|
export const DraggableWidget = ({
|
||||||
|
widget,
|
||||||
// Determine if the widget's panel is hidden
|
hiddenPanels,
|
||||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
index,
|
||||||
|
onReorder,
|
||||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
openKebabId,
|
||||||
event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget
|
setOpenKebabId,
|
||||||
};
|
selectedZone,
|
||||||
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
setSelectedZone,
|
||||||
event.preventDefault(); // Allow drop
|
}: {
|
||||||
};
|
selectedZone: {
|
||||||
|
zoneName: string;
|
||||||
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
activeSides: Side[];
|
||||||
event.preventDefault(); // Allow drop
|
panelOrder: Side[];
|
||||||
};
|
lockedPanels: Side[];
|
||||||
|
widgets: Widget[];
|
||||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
};
|
||||||
event.preventDefault();
|
setSelectedZone: React.Dispatch<
|
||||||
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); // Get the dragged widget's index
|
React.SetStateAction<{
|
||||||
const toIndex = index; // The index of the widget where the drop occurred
|
zoneName: string;
|
||||||
if (fromIndex !== toIndex) {
|
activeSides: Side[];
|
||||||
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
|
panelOrder: Side[];
|
||||||
}
|
lockedPanels: Side[];
|
||||||
};
|
zoneId: string;
|
||||||
|
zoneViewPortTarget: number[];
|
||||||
|
zoneViewPortPosition: number[];
|
||||||
return (
|
widgets: {
|
||||||
<>
|
id: string;
|
||||||
<div
|
type: string;
|
||||||
draggable
|
title: string;
|
||||||
key={widget.id}
|
panel: Side;
|
||||||
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
|
data: any;
|
||||||
}`}
|
}[];
|
||||||
onPointerDown={handlePointerDown}
|
}>
|
||||||
onDragStart={handleDragStart}
|
>;
|
||||||
onDragEnter={handleDragEnter}
|
|
||||||
onDragOver={handleDragOver}
|
widget: any;
|
||||||
onDrop={handleDrop}
|
hiddenPanels: string[];
|
||||||
style={{
|
index: number;
|
||||||
opacity: isPanelHidden ? 0 : 1, // Set opacity to 0 if the panel is hidden
|
onReorder: (fromIndex: number, toIndex: number) => void;
|
||||||
pointerEvents: isPanelHidden ? "none" : "auto", // Disable interaction when hidden
|
openKebabId: string | null;
|
||||||
}}
|
setOpenKebabId: (id: string | null) => void;
|
||||||
>
|
}) => {
|
||||||
{/* Render charts based on widget type */}
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
{widget.type === "progress" && (
|
const [panelDimensions, setPanelDimensions] = useState<{
|
||||||
<ProgressCard title={widget.title} data={widget.data} />
|
[side in Side]?: { width: number; height: number };
|
||||||
)}
|
}>({});
|
||||||
{widget.type === "line" && (
|
const handlePointerDown = () => {
|
||||||
<LineGraphComponent
|
if (selectedChartId?.id !== widget.id) {
|
||||||
type={widget.type}
|
setSelectedChartId(widget);
|
||||||
title={widget.title}
|
}
|
||||||
fontSize={widget.fontSize}
|
};
|
||||||
fontWeight={widget.fontWeight}
|
|
||||||
data={{
|
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||||
measurements: [
|
|
||||||
{ name: "testDevice", fields: "powerConsumption" },
|
const deleteSelectedChart = () => {
|
||||||
{ name: "furnace", fields: "powerConsumption" },
|
const updatedWidgets = selectedZone.widgets.filter(
|
||||||
],
|
(w: Widget) => w.id !== widget.id
|
||||||
interval: 1000,
|
);
|
||||||
duration: "1h",
|
|
||||||
}}
|
setSelectedZone((prevZone: any) => ({
|
||||||
/>
|
...prevZone,
|
||||||
)}
|
widgets: updatedWidgets,
|
||||||
{widget.type === "bar" && (
|
}));
|
||||||
<BarGraphComponent
|
|
||||||
type={widget.type}
|
setOpenKebabId(null);
|
||||||
title={widget.title}
|
};
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
const getCurrentWidgetCount = (panel: Side) =>
|
||||||
data={{
|
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||||
measurements: [
|
|
||||||
{ name: "testDevice", fields: "powerConsumption" },
|
const calculatePanelCapacity = (panel: Side) => {
|
||||||
{ name: "furnace", fields: "powerConsumption" },
|
const CHART_WIDTH = 150;
|
||||||
],
|
const CHART_HEIGHT = 150;
|
||||||
interval: 1000,
|
const FALLBACK_HORIZONTAL_CAPACITY = 5;
|
||||||
duration: "1h",
|
const FALLBACK_VERTICAL_CAPACITY = 3;
|
||||||
}}
|
|
||||||
/>
|
const dimensions = panelDimensions[panel];
|
||||||
)}
|
if (!dimensions) {
|
||||||
{/* {widget.type === "radar" && (
|
return panel === "top" || panel === "bottom"
|
||||||
<RadarGraphComponent
|
? FALLBACK_HORIZONTAL_CAPACITY
|
||||||
type={widget.type}
|
: FALLBACK_VERTICAL_CAPACITY;
|
||||||
title={widget.title}
|
}
|
||||||
fontSize={widget.fontSize}
|
|
||||||
fontWeight={widget.fontWeight}
|
return panel === "top" || panel === "bottom"
|
||||||
data={widget.data.measurements.map((item: any) => item.fields)}
|
? Math.floor(dimensions.width / CHART_WIDTH)
|
||||||
/>
|
: Math.floor(dimensions.height / CHART_HEIGHT);
|
||||||
)} */}
|
};
|
||||||
{widget.type === "pie" && (
|
|
||||||
<PieGraphComponent
|
const isPanelFull = (panel: Side) => {
|
||||||
type={widget.type}
|
const currentWidgetCount = getCurrentWidgetCount(panel);
|
||||||
title={widget.title}
|
const panelCapacity = calculatePanelCapacity(panel);
|
||||||
fontSize={widget.fontSize}
|
return currentWidgetCount >= panelCapacity;
|
||||||
fontWeight={widget.fontWeight}
|
};
|
||||||
data={{
|
|
||||||
measurements: [
|
const duplicateWidget = () => {
|
||||||
{ name: "testDevice", fields: "powerConsumption" },
|
const duplicatedWidget: Widget = {
|
||||||
{ name: "furnace", fields: "powerConsumption" },
|
...widget,
|
||||||
],
|
id: `${widget.id}-copy-${Date.now()}`,
|
||||||
interval: 1000,
|
};
|
||||||
duration: "1h",
|
|
||||||
}}
|
setSelectedZone((prevZone: any) => ({
|
||||||
/>
|
...prevZone,
|
||||||
)}
|
widgets: [...prevZone.widgets, duplicatedWidget],
|
||||||
{widget.type === "doughnut" && (
|
}));
|
||||||
<DoughnutGraphComponent
|
|
||||||
type={widget.type}
|
setOpenKebabId(null);
|
||||||
title={widget.title}
|
|
||||||
fontSize={widget.fontSize}
|
console.log("Duplicated widget with ID:", duplicatedWidget.id);
|
||||||
fontWeight={widget.fontWeight}
|
};
|
||||||
data={{
|
|
||||||
measurements: [
|
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
{ name: "testDevice", fields: "powerConsumption" },
|
event.stopPropagation();
|
||||||
{ name: "furnace", fields: "powerConsumption" },
|
if (openKebabId === widget.id) {
|
||||||
],
|
setOpenKebabId(null);
|
||||||
interval: 1000,
|
} else {
|
||||||
duration: "1h",
|
setOpenKebabId(widget.id);
|
||||||
}}
|
}
|
||||||
/>
|
};
|
||||||
)}
|
|
||||||
{widget.type === "polarArea" && (
|
const widgetRef = useRef<HTMLDivElement | null>(null);
|
||||||
<PolarAreaGraphComponent
|
|
||||||
type={widget.type}
|
useEffect(() => {
|
||||||
title={widget.title}
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
fontSize={widget.fontSize}
|
if (
|
||||||
fontWeight={widget.fontWeight}
|
widgetRef.current &&
|
||||||
data={{
|
!widgetRef.current.contains(event.target as Node)
|
||||||
measurements: [
|
) {
|
||||||
{ name: "testDevice", fields: "powerConsumption" },
|
setOpenKebabId(null);
|
||||||
{ name: "furnace", fields: "powerConsumption" },
|
}
|
||||||
],
|
};
|
||||||
interval: 1000,
|
|
||||||
duration: "1h",
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
}}
|
|
||||||
/>
|
return () => {
|
||||||
)}
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
</div>
|
};
|
||||||
</>
|
}, [setOpenKebabId]);
|
||||||
);
|
|
||||||
};
|
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.dataTransfer.setData("text/plain", index.toString()); // Store the index of the dragged widget
|
||||||
|
};
|
||||||
|
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault(); // Allow drop
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault(); // Allow drop
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
|
||||||
|
const toIndex = index; // The index of the widget where the drop occurred
|
||||||
|
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"
|
||||||
|
}`}
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
onDragStart={handleDragStart}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
style={{
|
||||||
|
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} />
|
||||||
|
)}
|
||||||
|
{widget.type === "line" && (
|
||||||
|
<LineGraphComponent
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
data={{
|
||||||
|
measurements: [
|
||||||
|
{ name: "testDevice", fields: "powerConsumption" },
|
||||||
|
{ name: "furnace", fields: "powerConsumption" },
|
||||||
|
],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{widget.type === "bar" && (
|
||||||
|
<BarGraphComponent
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
data={{
|
||||||
|
measurements: [
|
||||||
|
{ name: "testDevice", fields: "powerConsumption" },
|
||||||
|
{ name: "furnace", fields: "powerConsumption" },
|
||||||
|
],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{widget.type === "pie" && (
|
||||||
|
<PieGraphComponent
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
data={{
|
||||||
|
measurements: [
|
||||||
|
{ name: "testDevice", fields: "powerConsumption" },
|
||||||
|
{ name: "furnace", fields: "powerConsumption" },
|
||||||
|
],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{widget.type === "doughnut" && (
|
||||||
|
<DoughnutGraphComponent
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
data={{
|
||||||
|
measurements: [
|
||||||
|
{ name: "testDevice", fields: "powerConsumption" },
|
||||||
|
{ name: "furnace", fields: "powerConsumption" },
|
||||||
|
],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{widget.type === "polarArea" && (
|
||||||
|
<PolarAreaGraphComponent
|
||||||
|
type={widget.type}
|
||||||
|
title={widget.title}
|
||||||
|
fontSize={widget.fontSize}
|
||||||
|
fontWeight={widget.fontWeight}
|
||||||
|
data={{
|
||||||
|
measurements: [
|
||||||
|
{ name: "testDevice", fields: "powerConsumption" },
|
||||||
|
{ name: "furnace", fields: "powerConsumption" },
|
||||||
|
],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,251 +1,256 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
import { DraggableWidget } from "./DraggableWidget";
|
import { DraggableWidget } from "./DraggableWidget";
|
||||||
import { arrayMove } from "@dnd-kit/sortable";
|
import { arrayMove } from "@dnd-kit/sortable";
|
||||||
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
interface Widget {
|
interface Widget {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
title: string;
|
title: string;
|
||||||
panel: Side;
|
panel: Side;
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PanelProps {
|
interface PanelProps {
|
||||||
selectedZone: {
|
selectedZone: {
|
||||||
zoneName: string;
|
zoneName: string;
|
||||||
activeSides: Side[];
|
activeSides: Side[];
|
||||||
panelOrder: Side[];
|
panelOrder: Side[];
|
||||||
lockedPanels: Side[];
|
lockedPanels: Side[];
|
||||||
zoneId: string;
|
zoneId: string;
|
||||||
zoneViewPortTarget: number[];
|
zoneViewPortTarget: number[];
|
||||||
zoneViewPortPosition: number[]
|
zoneViewPortPosition: number[]
|
||||||
widgets: Widget[];
|
widgets: Widget[];
|
||||||
};
|
};
|
||||||
setSelectedZone: React.Dispatch<
|
setSelectedZone: React.Dispatch<
|
||||||
React.SetStateAction<{
|
React.SetStateAction<{
|
||||||
zoneName: string;
|
zoneName: string;
|
||||||
activeSides: Side[];
|
activeSides: Side[];
|
||||||
panelOrder: Side[];
|
panelOrder: Side[];
|
||||||
lockedPanels: Side[];
|
lockedPanels: Side[];
|
||||||
zoneId: string;
|
zoneId: string;
|
||||||
zoneViewPortTarget: number[];
|
zoneViewPortTarget: number[];
|
||||||
zoneViewPortPosition: number[]
|
zoneViewPortPosition: number[]
|
||||||
widgets: Widget[];
|
widgets: Widget[];
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: string[];
|
hiddenPanels: string[];
|
||||||
}
|
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
|
||||||
|
}
|
||||||
const generateUniqueId = () =>
|
|
||||||
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
const generateUniqueId = () =>
|
||||||
|
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
const Panel: React.FC<PanelProps> = ({
|
|
||||||
selectedZone,
|
const Panel: React.FC<PanelProps> = ({
|
||||||
setSelectedZone,
|
selectedZone,
|
||||||
hiddenPanels,
|
setSelectedZone,
|
||||||
}) => {
|
hiddenPanels,
|
||||||
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
setZonesData,
|
||||||
const [panelDimensions, setPanelDimensions] = useState<{
|
}) => {
|
||||||
[side in Side]?: { width: number; height: number };
|
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||||
}>({});
|
const [panelDimensions, setPanelDimensions] = useState<{
|
||||||
|
[side in Side]?: { width: number; height: number };
|
||||||
const { isPlaying } = usePlayButtonStore();
|
}>({});
|
||||||
|
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
|
||||||
const getPanelStyle = useMemo(
|
|
||||||
() => (side: Side) => {
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
|
||||||
const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
|
const getPanelStyle = useMemo(
|
||||||
const leftActive = previousPanels.includes("left");
|
() => (side: Side) => {
|
||||||
const rightActive = previousPanels.includes("right");
|
const currentIndex = selectedZone.panelOrder.indexOf(side);
|
||||||
const topActive = previousPanels.includes("top");
|
const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
|
||||||
const bottomActive = previousPanels.includes("bottom");
|
const leftActive = previousPanels.includes("left");
|
||||||
const panelSize = isPlaying ? 300 : 210;
|
const rightActive = previousPanels.includes("right");
|
||||||
|
const topActive = previousPanels.includes("top");
|
||||||
switch (side) {
|
const bottomActive = previousPanels.includes("bottom");
|
||||||
case "top":
|
const panelSize = isPlaying ? 300 : 210;
|
||||||
case "bottom":
|
|
||||||
return {
|
switch (side) {
|
||||||
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
case "top":
|
||||||
}px)`,
|
case "bottom":
|
||||||
height: `${panelSize - 2}px`,
|
return {
|
||||||
left: leftActive ? `${panelSize}px` : "0",
|
width: `calc(100% - ${
|
||||||
right: rightActive ? `${panelSize}px` : "0",
|
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
|
||||||
[side]: "0",
|
}px)`,
|
||||||
};
|
height: `${panelSize - 2}px`,
|
||||||
case "left":
|
left: leftActive ? `${panelSize}px` : "0",
|
||||||
case "right":
|
right: rightActive ? `${panelSize}px` : "0",
|
||||||
return {
|
[side]: "0",
|
||||||
width: `${panelSize - 2}px`,
|
};
|
||||||
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
case "left":
|
||||||
}px)`,
|
case "right":
|
||||||
top: topActive ? `${panelSize}px` : "0",
|
return {
|
||||||
bottom: bottomActive ? `${panelSize}px` : "0",
|
width: `${panelSize - 2}px`,
|
||||||
[side]: "0",
|
height: `calc(100% - ${
|
||||||
};
|
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
|
||||||
default:
|
}px)`,
|
||||||
return {};
|
top: topActive ? `${panelSize}px` : "0",
|
||||||
}
|
bottom: bottomActive ? `${panelSize}px` : "0",
|
||||||
},
|
[side]: "0",
|
||||||
[selectedZone.panelOrder, isPlaying]
|
};
|
||||||
);
|
default:
|
||||||
|
return {};
|
||||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
}
|
||||||
e.preventDefault();
|
},
|
||||||
const { draggedAsset } = useWidgetStore.getState();
|
[selectedZone.panelOrder, isPlaying]
|
||||||
if (!draggedAsset) return;
|
);
|
||||||
if (isPanelLocked(panel)) return;
|
|
||||||
|
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
e.preventDefault();
|
||||||
const maxCapacity = calculatePanelCapacity(panel);
|
const { draggedAsset } = useWidgetStore.getState();
|
||||||
|
if (!draggedAsset) return;
|
||||||
if (currentWidgetsCount >= maxCapacity) return;
|
if (isPanelLocked(panel)) return;
|
||||||
|
|
||||||
console.log('draggedAsset: ', draggedAsset);
|
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||||
console.log('panel: ', panel);
|
const maxCapacity = calculatePanelCapacity(panel);
|
||||||
addWidgetToPanel(draggedAsset, panel);
|
|
||||||
};
|
if (currentWidgetsCount >= maxCapacity) return;
|
||||||
|
|
||||||
const isPanelLocked = (panel: Side) =>
|
console.log("draggedAsset: ", draggedAsset);
|
||||||
selectedZone.lockedPanels.includes(panel);
|
console.log("panel: ", panel);
|
||||||
|
addWidgetToPanel(draggedAsset, panel);
|
||||||
const getCurrentWidgetCount = (panel: Side) =>
|
};
|
||||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
|
||||||
|
const isPanelLocked = (panel: Side) =>
|
||||||
const calculatePanelCapacity = (panel: Side) => {
|
selectedZone.lockedPanels.includes(panel);
|
||||||
const CHART_WIDTH = 150;
|
|
||||||
const CHART_HEIGHT = 150;
|
const getCurrentWidgetCount = (panel: Side) =>
|
||||||
const FALLBACK_HORIZONTAL_CAPACITY = 5;
|
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||||
const FALLBACK_VERTICAL_CAPACITY = 3;
|
|
||||||
|
const calculatePanelCapacity = (panel: Side) => {
|
||||||
const dimensions = panelDimensions[panel];
|
const CHART_WIDTH = 150;
|
||||||
if (!dimensions) {
|
const CHART_HEIGHT = 150;
|
||||||
return panel === "top" || panel === "bottom"
|
const FALLBACK_HORIZONTAL_CAPACITY = 5;
|
||||||
? FALLBACK_HORIZONTAL_CAPACITY
|
const FALLBACK_VERTICAL_CAPACITY = 3;
|
||||||
: FALLBACK_VERTICAL_CAPACITY;
|
|
||||||
}
|
const dimensions = panelDimensions[panel];
|
||||||
|
if (!dimensions) {
|
||||||
return panel === "top" || panel === "bottom"
|
return panel === "top" || panel === "bottom"
|
||||||
? Math.floor(dimensions.width / CHART_WIDTH)
|
? FALLBACK_HORIZONTAL_CAPACITY
|
||||||
: Math.floor(dimensions.height / CHART_HEIGHT);
|
: FALLBACK_VERTICAL_CAPACITY;
|
||||||
};
|
}
|
||||||
|
|
||||||
const addWidgetToPanel = (asset: any, panel: Side) => {
|
return panel === "top" || panel === "bottom"
|
||||||
const newWidget = {
|
? Math.floor(dimensions.width / CHART_WIDTH)
|
||||||
...asset,
|
: Math.floor(dimensions.height / CHART_HEIGHT);
|
||||||
id: generateUniqueId(),
|
};
|
||||||
panel,
|
|
||||||
};
|
// while dublicate check this and add
|
||||||
|
const addWidgetToPanel = (asset: any, panel: Side) => {
|
||||||
setSelectedZone((prev) => ({
|
const newWidget = {
|
||||||
...prev,
|
...asset,
|
||||||
widgets: [...prev.widgets, newWidget],
|
id: generateUniqueId(),
|
||||||
}));
|
panel,
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
setSelectedZone((prev) => ({
|
||||||
const observers: ResizeObserver[] = [];
|
...prev,
|
||||||
const currentPanelRefs = panelRefs.current;
|
widgets: [...prev.widgets, newWidget],
|
||||||
|
}));
|
||||||
selectedZone.activeSides.forEach((side) => {
|
};
|
||||||
const element = currentPanelRefs[side];
|
|
||||||
if (element) {
|
useEffect(() => {
|
||||||
const observer = new ResizeObserver((entries) => {
|
const observers: ResizeObserver[] = [];
|
||||||
for (const entry of entries) {
|
const currentPanelRefs = panelRefs.current;
|
||||||
const { width, height } = entry.contentRect;
|
|
||||||
setPanelDimensions((prev) => ({
|
selectedZone.activeSides.forEach((side) => {
|
||||||
...prev,
|
const element = currentPanelRefs[side];
|
||||||
[side]: { width, height },
|
if (element) {
|
||||||
}));
|
const observer = new ResizeObserver((entries) => {
|
||||||
}
|
for (const entry of entries) {
|
||||||
});
|
const { width, height } = entry.contentRect;
|
||||||
observer.observe(element);
|
setPanelDimensions((prev) => ({
|
||||||
observers.push(observer);
|
...prev,
|
||||||
}
|
[side]: { width, height },
|
||||||
});
|
}));
|
||||||
|
}
|
||||||
return () => {
|
});
|
||||||
observers.forEach((observer) => observer.disconnect());
|
observer.observe(element);
|
||||||
};
|
observers.push(observer);
|
||||||
}, [selectedZone.activeSides]);
|
}
|
||||||
|
});
|
||||||
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
|
||||||
if (!selectedZone) return; // Ensure selectedZone is not null
|
return () => {
|
||||||
console.log('selectedZone: ', selectedZone);
|
observers.forEach((observer) => observer.disconnect());
|
||||||
|
};
|
||||||
setSelectedZone((prev) => {
|
}, [selectedZone.activeSides]);
|
||||||
if (!prev) return prev; // Ensure prev is not null
|
|
||||||
|
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
|
||||||
// Filter widgets for the specified panel
|
if (!selectedZone) return; // Ensure selectedZone is not null
|
||||||
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
|
console.log("selectedZone: ", selectedZone);
|
||||||
|
|
||||||
// Reorder widgets within the same panel
|
setSelectedZone((prev) => {
|
||||||
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
if (!prev) return prev; // Ensure prev is not null
|
||||||
|
|
||||||
// Merge the reordered widgets back into the full list while preserving the order
|
// Filter widgets for the specified panel
|
||||||
const updatedWidgets = prev.widgets
|
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
|
||||||
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
|
|
||||||
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
|
// Reorder widgets within the same panel
|
||||||
|
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
|
||||||
return {
|
|
||||||
...prev,
|
// Merge the reordered widgets back into the full list while preserving the order
|
||||||
widgets: updatedWidgets,
|
const updatedWidgets = prev.widgets
|
||||||
};
|
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
|
||||||
});
|
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
|
||||||
};
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
widgets: updatedWidgets,
|
||||||
|
};
|
||||||
return (
|
});
|
||||||
<>
|
};
|
||||||
{selectedZone.activeSides.map((side) => (
|
|
||||||
<div
|
return (
|
||||||
key={side}
|
<>
|
||||||
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
|
{selectedZone.activeSides.map((side) => (
|
||||||
style={getPanelStyle(side)}
|
<div
|
||||||
onDrop={(e) => handleDrop(e, side)}
|
key={side}
|
||||||
onDragOver={(e) => e.preventDefault()}
|
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
|
||||||
ref={(el) => {
|
style={getPanelStyle(side)}
|
||||||
if (el) {
|
onDrop={(e) => handleDrop(e, side)}
|
||||||
panelRefs.current[side] = el;
|
onDragOver={(e) => e.preventDefault()}
|
||||||
} else {
|
ref={(el) => {
|
||||||
delete panelRefs.current[side];
|
if (el) {
|
||||||
}
|
panelRefs.current[side] = el;
|
||||||
}}
|
} else {
|
||||||
>
|
delete panelRefs.current[side];
|
||||||
<div
|
}
|
||||||
className={`panel-content ${isPlaying && "fullScreen"}`}
|
}}
|
||||||
style={{
|
>
|
||||||
pointerEvents: selectedZone.lockedPanels.includes(side)
|
<div
|
||||||
? "none"
|
className={`panel-content ${isPlaying && "fullScreen"}`}
|
||||||
: "auto",
|
style={{
|
||||||
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
pointerEvents: selectedZone.lockedPanels.includes(side)
|
||||||
}}
|
? "none"
|
||||||
>
|
: "auto",
|
||||||
{selectedZone.widgets
|
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
||||||
.filter((w) => w.panel === side)
|
}}
|
||||||
.map((widget, index) => (
|
>
|
||||||
<DraggableWidget
|
{selectedZone.widgets
|
||||||
hiddenPanels={hiddenPanels}
|
.filter((w) => w.panel === side)
|
||||||
widget={widget}
|
.map((widget, index) => (
|
||||||
key={widget.id}
|
<DraggableWidget
|
||||||
index={index}
|
hiddenPanels={hiddenPanels}
|
||||||
onReorder={(fromIndex, toIndex) =>
|
widget={widget}
|
||||||
handleReorder(fromIndex, toIndex, side)
|
key={widget.id}
|
||||||
}
|
index={index}
|
||||||
/>
|
onReorder={(fromIndex, toIndex) =>
|
||||||
))}
|
handleReorder(fromIndex, toIndex, side)
|
||||||
</div>
|
}
|
||||||
</div>
|
openKebabId={openKebabId}
|
||||||
))}
|
setOpenKebabId={setOpenKebabId}
|
||||||
</>
|
selectedZone={selectedZone}
|
||||||
);
|
setSelectedZone={setSelectedZone}
|
||||||
};
|
/>
|
||||||
|
))}
|
||||||
export default Panel;
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Panel;
|
||||||
|
|||||||
@@ -1,168 +1,168 @@
|
|||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
import Panel from "./Panel";
|
import Panel from "./Panel";
|
||||||
import AddButtons from "./AddButtons";
|
import AddButtons from "./AddButtons";
|
||||||
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||||
import DisplayZone from "./DisplayZone";
|
import DisplayZone from "./DisplayZone";
|
||||||
import Scene from "../../../modules/scene/scene";
|
import Scene from "../../../modules/scene/scene";
|
||||||
import useModuleStore from "../../../store/useModuleStore";
|
import useModuleStore from "../../../store/useModuleStore";
|
||||||
import { useDroppedObjectsStore, useZones } from "../../../store/store";
|
import { useDroppedObjectsStore, useZones } from "../../../store/store";
|
||||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||||
|
|
||||||
|
|
||||||
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
|
||||||
|
type FormattedZoneData = Record<
|
||||||
type FormattedZoneData = Record<
|
string,
|
||||||
string,
|
{
|
||||||
{
|
activeSides: Side[];
|
||||||
activeSides: Side[];
|
panelOrder: Side[];
|
||||||
panelOrder: Side[];
|
lockedPanels: Side[];
|
||||||
lockedPanels: Side[];
|
zoneId: string;
|
||||||
zoneId: string;
|
zoneViewPortTarget: number[];
|
||||||
zoneViewPortTarget: number[];
|
zoneViewPortPosition: number[]
|
||||||
zoneViewPortPosition: number[]
|
widgets: Widget[];
|
||||||
widgets: Widget[];
|
}
|
||||||
}
|
>;
|
||||||
>;
|
type Widget = {
|
||||||
type Widget = {
|
id: string;
|
||||||
id: string;
|
type: string;
|
||||||
type: string;
|
title: string;
|
||||||
title: string;
|
panel: Side;
|
||||||
panel: Side;
|
data: any;
|
||||||
data: any;
|
};
|
||||||
};
|
|
||||||
|
const RealTimeVisulization: React.FC = () => {
|
||||||
const RealTimeVisulization: React.FC = () => {
|
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
|
||||||
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { activeModule } = useModuleStore();
|
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
|
||||||
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
|
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
const { zones } = useZones()
|
||||||
const { zones } = useZones()
|
|
||||||
|
useEffect(() => {
|
||||||
useEffect(() => {
|
const data = Array.isArray(zones) ? zones : [];
|
||||||
const data = Array.isArray(zones) ? zones : [];
|
|
||||||
|
const formattedData = data.reduce<FormattedZoneData>((acc, zone) => {
|
||||||
const formattedData = data.reduce<FormattedZoneData>((acc, zone) => {
|
acc[zone.zoneName] = {
|
||||||
acc[zone.zoneName] = {
|
activeSides: [],
|
||||||
activeSides: [],
|
panelOrder: [],
|
||||||
panelOrder: [],
|
lockedPanels: [],
|
||||||
lockedPanels: [],
|
zoneId: zone.zoneId,
|
||||||
zoneId: zone.zoneId,
|
zoneViewPortTarget: zone.viewPortCenter,
|
||||||
zoneViewPortTarget: zone.viewPortCenter,
|
zoneViewPortPosition: zone.viewPortposition,
|
||||||
zoneViewPortPosition: zone.viewPortposition,
|
widgets: [],
|
||||||
widgets: [],
|
};
|
||||||
};
|
return acc;
|
||||||
return acc;
|
}, {});
|
||||||
}, {});
|
|
||||||
|
setZonesData(formattedData);
|
||||||
setZonesData(formattedData);
|
}, [zones]);
|
||||||
}, [zones]);
|
|
||||||
|
useEffect(() => {
|
||||||
useEffect(() => {
|
setZonesData((prev) => {
|
||||||
setZonesData((prev) => {
|
if (!selectedZone) return prev;
|
||||||
if (!selectedZone) return prev;
|
return {
|
||||||
return {
|
...prev,
|
||||||
...prev,
|
[selectedZone.zoneName]: {
|
||||||
[selectedZone.zoneName]: {
|
...prev[selectedZone.zoneName], // Keep existing properties
|
||||||
...prev[selectedZone.zoneName], // Keep existing properties
|
activeSides: selectedZone.activeSides || [],
|
||||||
activeSides: selectedZone.activeSides || [],
|
panelOrder: selectedZone.panelOrder || [],
|
||||||
panelOrder: selectedZone.panelOrder || [],
|
lockedPanels: selectedZone.lockedPanels || [],
|
||||||
lockedPanels: selectedZone.lockedPanels || [],
|
zoneId: selectedZone.zoneId || "",
|
||||||
zoneId: selectedZone.zoneId || "",
|
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
|
||||||
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
|
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
|
||||||
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
|
widgets: selectedZone.widgets || [],
|
||||||
widgets: selectedZone.widgets || [],
|
},
|
||||||
},
|
};
|
||||||
};
|
});
|
||||||
});
|
}, [selectedZone]);
|
||||||
}, [selectedZone]);
|
|
||||||
|
// const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
// const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
// console.log("Drop event fired! ✅");
|
||||||
// console.log("Drop event fired! ✅");
|
// event.preventDefault();
|
||||||
// event.preventDefault();
|
|
||||||
|
// const data = event.dataTransfer.getData("text/plain");
|
||||||
// const data = event.dataTransfer.getData("text/plain");
|
// if (!data) {
|
||||||
// if (!data) {
|
// console.log("❌ No data received on drop!");
|
||||||
// console.log("❌ No data received on drop!");
|
// return;
|
||||||
// return;
|
// }
|
||||||
// }
|
|
||||||
|
// try {
|
||||||
// try {
|
// const droppedData = JSON.parse(data);
|
||||||
// const droppedData = JSON.parse(data);
|
// console.log("✅ Dropped Data:", droppedData);
|
||||||
// console.log("✅ Dropped Data:", droppedData);
|
|
||||||
|
// console.log('droppedData: ', droppedData);
|
||||||
// console.log('droppedData: ', droppedData);
|
// setDroppedObjects((prev) => [...prev, droppedData]); // ✅ Add to state
|
||||||
// setDroppedObjects((prev) => [...prev, droppedData]); // ✅ Add to state
|
// console.log(droppedObjects);
|
||||||
// console.log(droppedObjects);
|
// } catch (error) {
|
||||||
// } catch (error) {
|
// console.error("❌ Error parsing dropped data:", error);
|
||||||
// console.error("❌ Error parsing dropped data:", error);
|
// }
|
||||||
// }
|
// };
|
||||||
// };
|
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
event.preventDefault();
|
||||||
event.preventDefault();
|
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event
|
||||||
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event
|
|
||||||
|
if (data) {
|
||||||
if (data) {
|
const droppedData = JSON.parse(data);
|
||||||
const droppedData = JSON.parse(data);
|
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store
|
||||||
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
return (
|
||||||
return (
|
<div
|
||||||
<div
|
ref={containerRef}
|
||||||
ref={containerRef}
|
id="real-time-vis-canvas"
|
||||||
id="real-time-vis-canvas"
|
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
||||||
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
|
style={{
|
||||||
style={{
|
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
||||||
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
|
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div
|
||||||
<div
|
className="scene-container"
|
||||||
className="scene-container"
|
style={{
|
||||||
style={{
|
height: "100%",
|
||||||
height: "100%",
|
width: "100%",
|
||||||
width: "100%",
|
borderRadius:
|
||||||
borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
|
isPlaying || activeModule !== "visualization" ? "" : "6px",
|
||||||
}}
|
}}
|
||||||
onDrop={(event) => handleDrop(event)}
|
onDrop={(event) => handleDrop(event)}
|
||||||
onDragOver={(event) => event.preventDefault()}
|
onDragOver={(event) => event.preventDefault()}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Scene />
|
<Scene />
|
||||||
</div>
|
</div>
|
||||||
{activeModule === "visualization" && (
|
{activeModule === "visualization" && (
|
||||||
<>
|
<>
|
||||||
<DisplayZone
|
<DisplayZone
|
||||||
zonesData={zonesData}
|
zonesData={zonesData}
|
||||||
selectedZone={selectedZone}
|
selectedZone={selectedZone}
|
||||||
setSelectedZone={setSelectedZone}
|
setSelectedZone={setSelectedZone}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isPlaying && selectedZone?.zoneName !== "" && (
|
{!isPlaying && selectedZone?.zoneName !== "" && (
|
||||||
<AddButtons
|
<AddButtons
|
||||||
hiddenPanels={hiddenPanels}
|
hiddenPanels={hiddenPanels}
|
||||||
setHiddenPanels={setHiddenPanels}
|
setHiddenPanels={setHiddenPanels}
|
||||||
selectedZone={selectedZone}
|
selectedZone={selectedZone}
|
||||||
setSelectedZone={setSelectedZone}
|
setSelectedZone={setSelectedZone}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Panel
|
<Panel
|
||||||
hiddenPanels={hiddenPanels}
|
selectedZone={selectedZone}
|
||||||
selectedZone={selectedZone}
|
setSelectedZone={setSelectedZone}
|
||||||
setSelectedZone={setSelectedZone}
|
hiddenPanels={hiddenPanels}
|
||||||
|
setZonesData={setZonesData}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RealTimeVisulization;
|
export default RealTimeVisulization;
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
// export default MultiLevelDropdown;
|
// export default MultiLevelDropdown;
|
||||||
|
|
||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||||
|
|
||||||
// Dropdown Item Component
|
// Dropdown Item Component
|
||||||
const DropdownItem = ({
|
const DropdownItem = ({
|
||||||
@@ -173,7 +174,13 @@ const NestedDropdown = ({
|
|||||||
className={`dropdown-trigger ${open ? "open" : ""}`}
|
className={`dropdown-trigger ${open ? "open" : ""}`}
|
||||||
onClick={() => setOpen(!open)}
|
onClick={() => setOpen(!open)}
|
||||||
>
|
>
|
||||||
{label} <span className="icon">{open ? "▼" : "▶"}</span>
|
{label}
|
||||||
|
<div
|
||||||
|
className="arrow-container"
|
||||||
|
style={{ rotate: open ? "" : "-90deg" }}
|
||||||
|
>
|
||||||
|
<ArrowIcon />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{open && (
|
{open && (
|
||||||
<div className="submenu">
|
<div className="submenu">
|
||||||
@@ -199,11 +206,11 @@ interface MultiLevelDropdownProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main Multi-Level Dropdown Component
|
// Main Multi-Level Dropdown Component
|
||||||
const MultiLevelDropdown = ({
|
const MultiLevelDropdown = ({
|
||||||
data,
|
data,
|
||||||
onSelect,
|
onSelect,
|
||||||
onUnselect,
|
onUnselect,
|
||||||
selectedValue
|
selectedValue,
|
||||||
}: MultiLevelDropdownProps) => {
|
}: MultiLevelDropdownProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -236,7 +243,7 @@ const MultiLevelDropdown = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Determine the display label
|
// Determine the display label
|
||||||
const displayLabel = selectedValue
|
const displayLabel = selectedValue
|
||||||
? `${selectedValue.name} - ${selectedValue.fields}`
|
? `${selectedValue.name} - ${selectedValue.fields}`
|
||||||
: "Dropdown trigger";
|
: "Dropdown trigger";
|
||||||
|
|
||||||
@@ -270,4 +277,3 @@ const MultiLevelDropdown = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default MultiLevelDropdown;
|
export default MultiLevelDropdown;
|
||||||
|
|
||||||
|
|||||||
133
app/src/components/ui/simulation/simulationPlayer.tsx
Normal file
133
app/src/components/ui/simulation/simulationPlayer.tsx
Normal 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;
|
||||||
@@ -37,6 +37,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
|||||||
<div className="assetPreview">
|
<div className="assetPreview">
|
||||||
<div className="image-preview">
|
<div className="image-preview">
|
||||||
<img src={assetImage} alt="" />
|
<img src={assetImage} alt="" />
|
||||||
|
{/* Add canvas here */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="asset-details-preview">
|
<div className="asset-details-preview">
|
||||||
|
|||||||
@@ -8,24 +8,51 @@ import {
|
|||||||
} from "../../components/icons/marketPlaceIcons";
|
} from "../../components/icons/marketPlaceIcons";
|
||||||
|
|
||||||
import assetImage from "../../assets/image/image.png";
|
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 (
|
return (
|
||||||
<div className="card-container">
|
<div className="card-container">
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="image-container">
|
<div className="image-container">
|
||||||
<img src={assetImage} alt="" />
|
<img src={assetImage} alt={assetName} />
|
||||||
</div>
|
</div>
|
||||||
<div className="assets-container">
|
<div className="assets-container">
|
||||||
<div className="name-container">
|
<div className="name-container">
|
||||||
<div className="asstes-container">Asset name</div>
|
<div className="assets-name">{assetName}</div>
|
||||||
<div className="assets-date">Uploaded on-12 Jan 23</div>
|
<div className="assets-date">{uploadedOn}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="details">
|
<div className="details">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<EyeIconBig />
|
<EyeIconBig />
|
||||||
1.5k
|
{views}
|
||||||
</div>
|
</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<CommentsIcon />
|
<CommentsIcon />
|
||||||
@@ -39,17 +66,17 @@ const Card: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="stars-container">
|
<div className="stars-container">
|
||||||
<div className="stars-wrapper">
|
<div className="stars-wrapper">
|
||||||
<StarsIconSmall />
|
{[...Array(5)].map((_, index) => (
|
||||||
<StarsIconSmall />
|
<StarsIconSmall key={index} />
|
||||||
<StarsIconSmall />
|
))}
|
||||||
<StarsIconSmall />
|
|
||||||
<StarsIconSmall />
|
|
||||||
</div>
|
</div>
|
||||||
<div className="units">
|
<div className="units">
|
||||||
₹ 36,500/<span>unit</span>
|
₹ {price}/<span>unit</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="buy-now-button">Buy now</div>
|
<div className="buy-now-button" onClick={handleCardSelect}>
|
||||||
|
Buy now
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,149 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import Card from "./Card";
|
import Card from "./Card";
|
||||||
|
import AssetPreview from "./AssetPreview";
|
||||||
|
import RenderOverlay from "../../components/templates/Overlay";
|
||||||
|
|
||||||
const CardsContainer: React.FC = () => {
|
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 (
|
return (
|
||||||
<div className="cards-container-container">
|
<div className="cards-container-container">
|
||||||
<div className="header">Products You May Like</div>
|
<div className="header">Products You May Like</div>
|
||||||
<div className="cards-wrapper-container">
|
<div className="cards-wrapper-container">
|
||||||
{array.map((index) => (
|
{array.map((asset) => (
|
||||||
<Card key={index} />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { Canvas } from "@react-three/fiber";
|
import { Canvas } from "@react-three/fiber";
|
||||||
import { Environment, KeyboardControls } from "@react-three/drei";
|
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 SelectionControls from "./controls/selection/selectionControls";
|
||||||
import MeasurementTool from "./tools/measurementTool";
|
import MeasurementTool from "./tools/measurementTool";
|
||||||
import Simulation from "../simulation/simulation";
|
import Simulation from "../simulation/simulation";
|
||||||
|
import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets";
|
||||||
|
|
||||||
// import Simulation from "./simulationtemp/simulation";
|
// import Simulation from "./simulationtemp/simulation";
|
||||||
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
|
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
|
||||||
|
|
||||||
@@ -27,6 +29,9 @@ export default function Scene() {
|
|||||||
{ name: "right", keys: ["ArrowRight", "d", "D"] },
|
{ name: "right", keys: ["ArrowRight", "d", "D"] },
|
||||||
], [])
|
], [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardControls map={map}>
|
<KeyboardControls map={map}>
|
||||||
<Canvas
|
<Canvas
|
||||||
@@ -36,13 +41,16 @@ export default function Scene() {
|
|||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
>
|
>
|
||||||
|
<DroppedObjects/>
|
||||||
<Controls />
|
<Controls />
|
||||||
<TransformControl />
|
<TransformControl />
|
||||||
<SelectionControls />
|
<SelectionControls />
|
||||||
<MeasurementTool />
|
<MeasurementTool />
|
||||||
<World />
|
<World />
|
||||||
<ZoneCentreTarget />
|
<ZoneCentreTarget />
|
||||||
|
{/* <Simulation /> */}
|
||||||
<Simulation />
|
<Simulation />
|
||||||
<PostProcessing />
|
<PostProcessing />
|
||||||
<Sun />
|
<Sun />
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
} from "../store/store";
|
} from "../store/store";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { usePlayButtonStore } from "../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../store/usePlayButtonStore";
|
||||||
import SimulationUI from "../modules/simulation/simulationUI";
|
|
||||||
import MarketPlace from "../modules/market/MarketPlace";
|
import MarketPlace from "../modules/market/MarketPlace";
|
||||||
import LoadingPage from "../components/templates/LoadingPage";
|
import LoadingPage from "../components/templates/LoadingPage";
|
||||||
|
import SimulationPlayer from "../components/ui/simulation/simulationPlayer";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -65,6 +65,7 @@ const Project: React.FC = () => {
|
|||||||
<RealTimeVisulization />
|
<RealTimeVisulization />
|
||||||
{activeModule !== "market" && <Tools />}
|
{activeModule !== "market" && <Tools />}
|
||||||
{/* <SimulationUI /> */}
|
{/* <SimulationUI /> */}
|
||||||
|
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-size: var(--font-size-regular);
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,28 +280,24 @@ input {
|
|||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 110%;
|
||||||
left: 0;
|
right: -16px;
|
||||||
background-color: #ffffff;
|
background-color: var(--background-color);
|
||||||
border: 1px solid #cccccc;
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: #{$box-shadow-medium};
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-height: 600px;
|
max-height: 400px;
|
||||||
|
|
||||||
.dropdown-content {
|
.dropdown-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
.nested-dropdown {
|
.nested-dropdown {
|
||||||
// &:first-child{
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,13 +305,13 @@ input {
|
|||||||
display: block;
|
display: block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #000000;
|
color: var(--text-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #f0f0f0;
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,15 +325,20 @@ input {
|
|||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #000000;
|
color: var(--text-color);
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
|
border-radius: #{$border-radius-small};
|
||||||
|
.arrow-container{
|
||||||
|
@include flex-center;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #f0f0f0;
|
background-color: var(--background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.open {
|
&.open {
|
||||||
background-color: #e0e0e0;
|
color: var(--accent-color);
|
||||||
|
background-color: var(--highlight-accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@@ -349,7 +350,7 @@ input {
|
|||||||
.submenu {
|
.submenu {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
border-left: 2px solid #cccccc;
|
border-left: 2px solid var(--border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
@@ -576,26 +577,26 @@ input {
|
|||||||
color: var(--text-disabled);
|
color: var(--text-disabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.entered-emails{
|
.entered-emails {
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
background: var(--background-color-gray);
|
background: var(--background-color-gray);
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
span{
|
span {
|
||||||
height: 14px;
|
height: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
line-height: 12px;
|
line-height: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: #{$border-radius-small};
|
border-radius: #{$border-radius-small};
|
||||||
&:hover{
|
&:hover {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.invite-button{
|
.invite-button {
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
|
|||||||
@@ -2,186 +2,356 @@
|
|||||||
@use "../../abstracts/mixins.scss" as *;
|
@use "../../abstracts/mixins.scss" as *;
|
||||||
|
|
||||||
.marketplace-wrapper {
|
.marketplace-wrapper {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
z-index: #{$z-index-marketplace};
|
z-index: #{$z-index-marketplace};
|
||||||
background-color: var(--background-color-secondary);
|
background-color: var(--background-color-secondary);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: 100px 50px;
|
padding: 100px 50px;
|
||||||
|
padding-bottom: 32px;
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
|
||||||
.marketplace-container {
|
.marketplace-container {
|
||||||
padding: 20px 2px;
|
position: relative;
|
||||||
height: calc(100vh - 120px);
|
padding: 20px 2px;
|
||||||
background-color: var(--background-color);
|
height: 100%;
|
||||||
box-shadow: #{$box-shadow-medium};
|
background-color: var(--background-color);
|
||||||
border-radius: #{$border-radius-extra-large};
|
box-shadow: #{$box-shadow-medium};
|
||||||
|
border-radius: #{$border-radius-extra-large};
|
||||||
|
}
|
||||||
|
|
||||||
|
.marketPlace {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
left: calc(120px / 2);
|
||||||
|
top: 100px;
|
||||||
|
padding: 14px;
|
||||||
|
padding-bottom: 60px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
.filter-search-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.search-wrapper {
|
||||||
|
min-width: 60%;
|
||||||
|
max-width: 684px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: $border-radius-large;
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: $box-shadow-medium;
|
||||||
|
border-radius: $border-radius-large;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none !important;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.regularDropdown-container {
|
||||||
|
max-width: 159px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 5px 20px;
|
||||||
|
border: 1px solid var(--accent-color);
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.stars {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.marketPlace {
|
.cards-container-container {
|
||||||
width: 100%;
|
padding: 0px 20px;
|
||||||
height: 100%;
|
display: flex;
|
||||||
overflow: auto;
|
flex-direction: column;
|
||||||
left: calc(120px / 2);
|
gap: 6px;
|
||||||
top: 100px;
|
|
||||||
padding: 14px;
|
|
||||||
padding-bottom: 60px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24px;
|
|
||||||
|
|
||||||
.filter-search-container {
|
.header {
|
||||||
|
color: var(--text-color);
|
||||||
|
font-weight: $medium-weight;
|
||||||
|
font-size: $xlarge;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-wrapper-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 28px;
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
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);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 12px;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
gap: 12px;
|
}
|
||||||
|
|
||||||
.search-wrapper {
|
.assets-container {
|
||||||
min-width: 60%;
|
display: flex;
|
||||||
max-width: 684px;
|
justify-content: space-between;
|
||||||
padding: 0;
|
|
||||||
border-radius: $border-radius-large ;
|
|
||||||
|
|
||||||
.search-container {
|
.name-container {
|
||||||
border: none !important;
|
display: flex;
|
||||||
box-shadow: $box-shadow-medium;
|
flex-direction: column;
|
||||||
border-radius: $border-radius-large ;
|
gap: 3px;
|
||||||
|
|
||||||
input {
|
.asstes-container {
|
||||||
border: none !important;
|
font-weight: #{$bold-weight};
|
||||||
outline: none;
|
font-size: $regular;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
.assets-date {
|
||||||
}
|
color: var(--accent-color);
|
||||||
|
font-size: $small;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.regularDropdown-container {
|
.details {
|
||||||
max-width: 159px;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
.button {
|
.content {
|
||||||
padding: 5px 20px;
|
|
||||||
border: 1px solid var(--accent-color);
|
|
||||||
border-radius: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rating-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
}
|
||||||
.stars {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cards-container-container {
|
.vendor-icon {
|
||||||
padding: 0px 20px;
|
font-weight: #{$bold-weight};
|
||||||
|
font-size: $regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stars-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
justify-content: space-between;
|
||||||
gap: 6px;
|
}
|
||||||
|
|
||||||
.header {
|
.buy-now-button {
|
||||||
color: var(--text-color);
|
width: 100%;
|
||||||
font-weight: $medium-weight;
|
background-color: var(--background-color-secondary);
|
||||||
font-size: $xlarge;
|
border-radius: $border-radius-extra-large;
|
||||||
}
|
padding: 8px 0;
|
||||||
|
@include flex-center;
|
||||||
.cards-wrapper-container {
|
color: var(--accent-color);
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
&:hover {
|
||||||
gap: 28px;
|
cursor: pointer;
|
||||||
|
|
||||||
.card-container {
|
|
||||||
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);
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 6px;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 12px;
|
|
||||||
left: 12px;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: var(--accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assets-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.name-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 3px;
|
|
||||||
|
|
||||||
.asstes-container {
|
|
||||||
font-weight: #{$bold-weight};
|
|
||||||
font-size: $regular ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assets-date {
|
|
||||||
color: var(--accent-color);
|
|
||||||
font-size: $small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.details {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.vendor-icon {
|
|
||||||
|
|
||||||
font-weight: #{$bold-weight};
|
|
||||||
font-size: $regular ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stars-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buy-now-button {
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--background-color-secondary);
|
|
||||||
border-radius: $border-radius-extra-large ;
|
|
||||||
padding: 8px 0;
|
|
||||||
@include flex-center;
|
|
||||||
color: var(--accent-color);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
114
app/src/styles/components/simulation/simulation.scss
Normal file
114
app/src/styles/components/simulation/simulation.scss
Normal 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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 250px;
|
// min-width: 1450px;
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -20,8 +20,9 @@
|
|||||||
@use 'components/tools';
|
@use 'components/tools';
|
||||||
@use 'components/visualization/floating/energyConsumed';
|
@use 'components/visualization/floating/energyConsumed';
|
||||||
@use 'components/visualization/ui/styledWidgets';
|
@use 'components/visualization/ui/styledWidgets';
|
||||||
@use './components/visualization/floating/common';
|
@use 'components/visualization/floating/common';
|
||||||
@use './components/marketPlace/marketPlace.scss';
|
@use 'components/marketPlace/marketPlace';
|
||||||
|
@use 'components/simulation/simulation';
|
||||||
|
|
||||||
// layout
|
// layout
|
||||||
@use 'layout/loading';
|
@use 'layout/loading';
|
||||||
|
|||||||
@@ -1,403 +1,519 @@
|
|||||||
@use "../abstracts/variables.scss" as *;
|
@use "../abstracts/variables.scss" as *;
|
||||||
|
@use "../abstracts/mixins.scss" as *;
|
||||||
// Main Container
|
|
||||||
.realTime-viz {
|
// Main Container
|
||||||
background-color: var(--background-color);
|
.realTime-viz {
|
||||||
border-radius: 20px;
|
background-color: var(--background-color);
|
||||||
box-shadow: $box-shadow-medium;
|
border-radius: 20px;
|
||||||
width: calc(100% - (320px + 270px + 90px));
|
box-shadow: $box-shadow-medium;
|
||||||
height: calc(100% - (200px + 80px));
|
width: calc(100% - (320px + 270px + 90px));
|
||||||
position: absolute;
|
height: calc(100% - (200px + 80px));
|
||||||
top: 50%;
|
position: absolute;
|
||||||
left: calc(270px + 45px);
|
top: 50%;
|
||||||
transform: translate(0, -50%);
|
left: calc(270px + 45px);
|
||||||
border-radius: #{$border-radius-medium};
|
transform: translate(0, -50%);
|
||||||
transition: all 0.2s;
|
border-radius: #{$border-radius-medium};
|
||||||
z-index: #{$z-index-default};
|
transition: all 0.2s;
|
||||||
|
z-index: #{$z-index-default};
|
||||||
.scene-container {
|
|
||||||
overflow: hidden;
|
.scene-container {
|
||||||
}
|
overflow: hidden;
|
||||||
|
}
|
||||||
.icon {
|
|
||||||
display: flex;
|
.icon {
|
||||||
align-items: center;
|
display: flex;
|
||||||
position: relative;
|
align-items: center;
|
||||||
}
|
position: relative;
|
||||||
|
}
|
||||||
.icons-container {
|
|
||||||
.icon {
|
.icons-container {
|
||||||
&:first-child {
|
.icon {
|
||||||
&::after {
|
&:first-child {
|
||||||
display: none;
|
&::after {
|
||||||
}
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.zoon-wrapper {
|
|
||||||
display: flex;
|
.zone-wrapper {
|
||||||
background-color: var(--background-color);
|
display: flex;
|
||||||
position: absolute;
|
background-color: var(--background-color);
|
||||||
bottom: 10px;
|
position: absolute;
|
||||||
left: 50%;
|
bottom: 10px;
|
||||||
transform: translate(-50%, 0);
|
left: 50%;
|
||||||
gap: 6px;
|
transform: translate(-50%, 0);
|
||||||
padding: 4px;
|
gap: 6px;
|
||||||
border-radius: 8px;
|
|
||||||
max-width: 80%;
|
border-radius: 8px;
|
||||||
overflow: auto;
|
max-width: 80%;
|
||||||
max-width: calc(100% - 450px);
|
overflow: auto;
|
||||||
&::-webkit-scrollbar {
|
// max-width: calc(100% - 450px);
|
||||||
display: none;
|
|
||||||
}
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
.zone {
|
}
|
||||||
width: auto;
|
|
||||||
background-color: var(--background-color);
|
.arrow {
|
||||||
border-radius: 6px;
|
background-color: var(--highlight-accent-color);
|
||||||
padding: 4px 8px;
|
color: var(--background-color);
|
||||||
white-space: nowrap;
|
}
|
||||||
font-size: $small;
|
|
||||||
}
|
.zones-wrapper {
|
||||||
|
padding: 6px;
|
||||||
.active {
|
display: flex;
|
||||||
background-color: var(--accent-color);
|
gap: 6px;
|
||||||
color: var(--background-color);
|
border-radius: #{$border-radius-medium};
|
||||||
// color: #FCFDFD !important;
|
overflow-x: auto;
|
||||||
}
|
&::-webkit-scrollbar {
|
||||||
}
|
display: none;
|
||||||
|
}
|
||||||
.zoon-wrapper.bottom {
|
}
|
||||||
bottom: 210px;
|
.no-zone {
|
||||||
}
|
@include flex-center;
|
||||||
|
gap: 4px;
|
||||||
@media (max-width: 1024px) {
|
padding: 4px;
|
||||||
width: 80%; // Increase width to take more space on smaller screens
|
color: var(--text-disabled);
|
||||||
height: 500px; // Reduce height to fit smaller screens
|
}
|
||||||
left: 50%; // Center horizontally
|
.zone {
|
||||||
|
width: auto;
|
||||||
.main-container {
|
background-color: var(--background-color);
|
||||||
margin: 0 15px; // Reduce margin for better spacing
|
border-radius: 6px;
|
||||||
}
|
padding: 4px 8px;
|
||||||
|
white-space: nowrap;
|
||||||
.zoon-wrapper {
|
font-size: $small;
|
||||||
bottom: 5px; // Adjust position for smaller screens
|
}
|
||||||
|
|
||||||
&.bottom {
|
.active {
|
||||||
bottom: 150px; // Adjust for bottom placement
|
background-color: var(--accent-color);
|
||||||
}
|
color: var(--background-color);
|
||||||
}
|
// color: #FCFDFD !important;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.content-container {
|
|
||||||
display: flex;
|
.zone-wrapper.bottom {
|
||||||
height: 100vh;
|
bottom: 210px;
|
||||||
transition: all 0.3s ease;
|
}
|
||||||
}
|
|
||||||
|
.content-container {
|
||||||
.main-container {
|
display: flex;
|
||||||
position: relative;
|
height: 100vh;
|
||||||
flex: 1;
|
transition: all 0.3s ease;
|
||||||
height: 600px;
|
}
|
||||||
background-color: rgb(235, 235, 235);
|
|
||||||
margin: 0 30px;
|
.main-container {
|
||||||
transition: height 0.3s ease, margin 0.3s ease;
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
.zoon-wrapper {
|
height: 600px;
|
||||||
display: flex;
|
background-color: rgb(235, 235, 235);
|
||||||
background-color: rgba(224, 223, 255, 0.5);
|
margin: 0 30px;
|
||||||
position: absolute;
|
transition: height 0.3s ease, margin 0.3s ease;
|
||||||
bottom: 10px;
|
|
||||||
left: 50%;
|
.zone-wrapper {
|
||||||
transform: translate(-50%, 0);
|
display: flex;
|
||||||
gap: 6px;
|
background-color: rgba(224, 223, 255, 0.5);
|
||||||
padding: 4px;
|
position: absolute;
|
||||||
border-radius: 8px;
|
bottom: 10px;
|
||||||
max-width: 80%;
|
left: 50%;
|
||||||
overflow: auto;
|
transform: translate(-50%, 0);
|
||||||
transition: transform 0.3s ease;
|
gap: 6px;
|
||||||
|
padding: 4px;
|
||||||
&::-webkit-scrollbar {
|
border-radius: 8px;
|
||||||
display: none;
|
max-width: 80%;
|
||||||
}
|
overflow: auto;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
.zone {
|
|
||||||
width: auto;
|
&::-webkit-scrollbar {
|
||||||
background-color: $background-color;
|
display: none;
|
||||||
border-radius: 6px;
|
}
|
||||||
padding: 4px 8px;
|
|
||||||
white-space: nowrap;
|
.zone {
|
||||||
cursor: pointer;
|
width: auto;
|
||||||
transition: background-color 0.3s ease;
|
background-color: $background-color;
|
||||||
|
border-radius: 6px;
|
||||||
&.active {
|
padding: 4px 8px;
|
||||||
background-color: var(--primary-color);
|
white-space: nowrap;
|
||||||
color: var(--accent-color);
|
cursor: pointer;
|
||||||
}
|
transition: background-color 0.3s ease;
|
||||||
}
|
|
||||||
|
&.active {
|
||||||
&.bottom {
|
background-color: var(--primary-color);
|
||||||
bottom: 210px;
|
color: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
&.bottom {
|
||||||
.panel {
|
bottom: 210px;
|
||||||
position: absolute;
|
}
|
||||||
background: white;
|
}
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
}
|
||||||
transition: all 0.3s ease;
|
|
||||||
border-radius: 6px;
|
.panel {
|
||||||
overflow: visible !important;
|
position: absolute;
|
||||||
|
background: white;
|
||||||
.panel-content {
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
position: relative;
|
transition: all 0.3s ease;
|
||||||
height: 100%;
|
border-radius: 6px;
|
||||||
padding: 10px;
|
overflow: visible !important;
|
||||||
overflow: auto;
|
z-index: $z-index-tools;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
.panel-content {
|
||||||
gap: 10px;
|
position: relative;
|
||||||
background-color: var(--background-color);
|
height: 100%;
|
||||||
|
padding: 10px;
|
||||||
&::-webkit-scrollbar {
|
display: flex;
|
||||||
display: none;
|
flex-direction: column;
|
||||||
}
|
gap: 6px;
|
||||||
|
background-color: var(--background-color);
|
||||||
.chart-container {
|
|
||||||
width: 100%;
|
&::-webkit-scrollbar {
|
||||||
height: 24% !important;
|
display: none;
|
||||||
|
}
|
||||||
min-height: 150px;
|
|
||||||
max-height: 100%;
|
.chart-container {
|
||||||
border: 1px dotted #a9a9a9;
|
width: 100%;
|
||||||
border-radius: 8px;
|
height: 25% !important;
|
||||||
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
|
min-height: 150px;
|
||||||
padding: 6px 0;
|
max-height: 100%;
|
||||||
background-color: white;
|
border: 1px dotted #a9a9a9;
|
||||||
}
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
|
||||||
.close-btn {
|
padding: 6px 0;
|
||||||
position: absolute;
|
background-color: white;
|
||||||
top: 5px;
|
position: relative;
|
||||||
right: 5px;
|
|
||||||
background: none;
|
.kebab {
|
||||||
border: none;
|
width: 30px;
|
||||||
cursor: pointer;
|
height: 30px;
|
||||||
color: var(--primary-color);
|
position: absolute;
|
||||||
}
|
top: 0px;
|
||||||
}
|
right: 0px;
|
||||||
|
z-index: 10;
|
||||||
&.top-panel,
|
cursor: pointer;
|
||||||
&.bottom-panel {
|
@include flex-center;
|
||||||
left: 0;
|
}
|
||||||
right: 0;
|
|
||||||
|
.kebab-options {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
.panel-content {
|
right: -100px;
|
||||||
display: flex;
|
transform: translate(0px, 0);
|
||||||
flex-direction: row;
|
background-color: var(--background-color);
|
||||||
height: 100%;
|
z-index: 10;
|
||||||
|
|
||||||
.chart-container {
|
display: flex;
|
||||||
height: 100% !important;
|
flex-direction: column;
|
||||||
width: 20%;
|
gap: 6px;
|
||||||
min-width: 150px;
|
border-radius: 4px;
|
||||||
}
|
|
||||||
}
|
box-shadow: var(--box-shadow-medium);
|
||||||
}
|
|
||||||
|
.btn {
|
||||||
&.top-panel {
|
display: flex;
|
||||||
top: 0;
|
gap: 6px;
|
||||||
}
|
align-items: center;
|
||||||
|
padding: 5px 10px;
|
||||||
&.bottom-panel {
|
color: var(--text-color);
|
||||||
bottom: 0;
|
|
||||||
}
|
&:hover {
|
||||||
|
.label {
|
||||||
&.left-panel {
|
color: var(--accent-color);
|
||||||
left: 0;
|
}
|
||||||
top: 0;
|
}
|
||||||
bottom: 0;
|
|
||||||
|
&:hover {
|
||||||
.chart-container {
|
background-color: var(--highlight-accent-color);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 180px;
|
|
||||||
}
|
svg {
|
||||||
}
|
&:first-child {
|
||||||
|
fill: var(--accent-color);
|
||||||
&.right-panel {
|
}
|
||||||
right: 0;
|
|
||||||
top: 0;
|
&:last-child {
|
||||||
bottom: 0;
|
fill: auto;
|
||||||
}
|
stroke: var(--accent-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.playingFlase{
|
}
|
||||||
.zoon-wrapper{
|
|
||||||
bottom: 300px !important;
|
.btn-blur {
|
||||||
}
|
color: var(--text-disabled);
|
||||||
}
|
cursor: not-allowed;
|
||||||
// Side Buttons
|
pointer-events: none;
|
||||||
.side-button-container {
|
}
|
||||||
position: absolute;
|
}
|
||||||
display: flex;
|
}
|
||||||
background-color: var(--background-color);
|
|
||||||
padding: 5px;
|
.close-btn {
|
||||||
border-radius: 8px;
|
position: absolute;
|
||||||
transition: transform 0.3s ease;
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
.extra-Bs {
|
background: none;
|
||||||
display: flex;
|
border: none;
|
||||||
align-items: center;
|
cursor: pointer;
|
||||||
gap: 12px;
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
.icon {
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
&.top-panel,
|
||||||
justify-content: center;
|
&.bottom-panel {
|
||||||
width: 18px;
|
left: 0;
|
||||||
height: 18px;
|
right: 0;
|
||||||
border-radius: 4px;
|
|
||||||
}
|
.panel-content {
|
||||||
|
display: flex;
|
||||||
.active {
|
flex-direction: row;
|
||||||
background-color: var(--accent-color);
|
height: 100%;
|
||||||
}
|
|
||||||
|
.chart-container {
|
||||||
&:hover {
|
height: 100% !important;
|
||||||
cursor: pointer;
|
width: 20%;
|
||||||
}
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.side-button {
|
}
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease;
|
&.top-panel {
|
||||||
width: 18px;
|
top: 0;
|
||||||
height: 18px;
|
}
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
&.bottom-panel {
|
||||||
// align-items: center;
|
bottom: 0;
|
||||||
background-color: var(--accent-color);
|
}
|
||||||
border: none;
|
|
||||||
color: var(--background-color);
|
&.left-panel {
|
||||||
border-radius: 4px;
|
left: 0;
|
||||||
}
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
&.top {
|
|
||||||
top: -30px;
|
.chart-container {
|
||||||
left: 50%;
|
width: 100%;
|
||||||
transform: translateX(-50%);
|
height: 180px;
|
||||||
flex-direction: row;
|
}
|
||||||
gap: 6px;
|
}
|
||||||
}
|
|
||||||
|
&.right-panel {
|
||||||
&.right {
|
right: 0;
|
||||||
right: -30px;
|
top: 0;
|
||||||
top: 50%;
|
bottom: 0;
|
||||||
transform: translateY(-50%);
|
}
|
||||||
flex-direction: column;
|
}
|
||||||
gap: 6px;
|
}
|
||||||
}
|
|
||||||
|
.playingFlase {
|
||||||
&.bottom {
|
.zone-wrapper.bottom {
|
||||||
bottom: -30px;
|
bottom: 300px;
|
||||||
left: 50%;
|
}
|
||||||
transform: translateX(-50%);
|
}
|
||||||
flex-direction: row;
|
|
||||||
gap: 6px;
|
// Side Buttons
|
||||||
}
|
.side-button-container {
|
||||||
|
position: absolute;
|
||||||
&.left {
|
display: flex;
|
||||||
left: -30px;
|
background-color: var(--background-color);
|
||||||
top: 50%;
|
padding: 2px;
|
||||||
transform: translateY(-50%);
|
border-radius: 2px;
|
||||||
flex-direction: column;
|
transition: transform 0.3s ease;
|
||||||
gap: 6px;
|
box-shadow: #{$box-shadow-medium};
|
||||||
}
|
|
||||||
}
|
.extra-Bs {
|
||||||
|
display: flex;
|
||||||
.right.side-button-container {
|
align-items: center;
|
||||||
.extra-Bs {
|
gap: 12px;
|
||||||
flex-direction: column;
|
|
||||||
}
|
.icon {
|
||||||
}
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
.left.side-button-container {
|
justify-content: center;
|
||||||
.extra-Bs {
|
width: 18px;
|
||||||
flex-direction: column;
|
height: 18px;
|
||||||
}
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Theme Container
|
.active {
|
||||||
.theme-container {
|
background-color: var(--accent-color);
|
||||||
width: 250px;
|
}
|
||||||
padding: 12px;
|
|
||||||
box-shadow: 1px -3px 4px 0px rgba(0, 0, 0, 0.11);
|
&:hover {
|
||||||
border-radius: 8px;
|
cursor: pointer;
|
||||||
background-color: white;
|
}
|
||||||
position: absolute;
|
}
|
||||||
top: 20px;
|
|
||||||
right: -100%;
|
.side-button {
|
||||||
transform: translate(-0%, 0);
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
h2 {
|
width: 18px;
|
||||||
font-size: 12px;
|
height: 18px;
|
||||||
margin-bottom: 8px;
|
display: flex;
|
||||||
color: #2b3344;
|
justify-content: center;
|
||||||
}
|
// align-items: center;
|
||||||
|
background-color: var(--accent-color);
|
||||||
.theme-preset-wrapper {
|
border: none;
|
||||||
display: flex;
|
color: var(--background-color);
|
||||||
gap: 5px;
|
border-radius: 4px;
|
||||||
flex-wrap: wrap;
|
.add-icon {
|
||||||
|
@include flex-center;
|
||||||
.theme-preset {
|
transition: rotate 0.2s;
|
||||||
display: flex;
|
}
|
||||||
gap: 2px;
|
path {
|
||||||
margin-bottom: 10px;
|
stroke: var(--primary-color);
|
||||||
border: 1px solid $border-color;
|
stroke-width: 2;
|
||||||
padding: 5px 10px;
|
}
|
||||||
border-radius: 4px;
|
}
|
||||||
transition: border 0.3s ease;
|
.active {
|
||||||
|
background: #ffe3e0;
|
||||||
&.active {
|
.add-icon {
|
||||||
border: 1px solid var(--primary-color);
|
rotate: 45deg;
|
||||||
|
path {
|
||||||
&::after {
|
stroke: #f65648;
|
||||||
content: "";
|
stroke-width: 2;
|
||||||
position: absolute;
|
}
|
||||||
top: 1px;
|
}
|
||||||
left: 1px;
|
}
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
&.top {
|
||||||
background-color: var(--primary-color);
|
top: -30px;
|
||||||
border-radius: 50%;
|
left: 50%;
|
||||||
}
|
transform: translateX(-50%);
|
||||||
}
|
flex-direction: row;
|
||||||
}
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-color {
|
&.right {
|
||||||
display: flex;
|
right: -30px;
|
||||||
justify-content: space-between;
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
.color-displayer {
|
flex-direction: column;
|
||||||
display: flex;
|
gap: 6px;
|
||||||
gap: 5px;
|
}
|
||||||
align-items: center;
|
|
||||||
border: 1px solid var(--accent-color);
|
&.bottom {
|
||||||
border-radius: 4px;
|
bottom: -30px;
|
||||||
padding: 0 5px;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
input {
|
flex-direction: row;
|
||||||
border: none;
|
gap: 6px;
|
||||||
outline: none;
|
}
|
||||||
border-radius: 50%;
|
|
||||||
}
|
&.left {
|
||||||
}
|
left: -30px;
|
||||||
}
|
top: 50%;
|
||||||
}
|
transform: translateY(-50%);
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right.side-button-container {
|
||||||
|
.extra-Bs {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.left.side-button-container {
|
||||||
|
.extra-Bs {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theme Container
|
||||||
|
.theme-container {
|
||||||
|
width: 250px;
|
||||||
|
padding: 12px;
|
||||||
|
box-shadow: 1px -3px 4px 0px rgba(0, 0, 0, 0.11);
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: -100%;
|
||||||
|
transform: translate(-0%, 0);
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #2b3344;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-preset-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.theme-preset {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border 0.3s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
left: 1px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-color {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.color-displayer {
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--accent-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 5px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user