diff --git a/src/components/RunMosaic/RunMosaic.tsx b/src/components/RunMosaic/RunMosaic.tsx
index c1be78b..2752ff1 100644
--- a/src/components/RunMosaic/RunMosaic.tsx
+++ b/src/components/RunMosaic/RunMosaic.tsx
@@ -3,6 +3,7 @@ import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import dropRight from "lodash/dropRight";
import { filter } from "lodash";
+import TutorialDisplay from "../TutorialDisplay/TutorialDisplay";
import {
Mosaic,
@@ -53,6 +54,47 @@ const RunMosaic: React.FC = () => {
const dispatch = useAppDispatch();
const darkMode = useSelector((state: RootState) => state.userPreferences.darkMode);
+ const tutorialSteps = [
+ {
+ title: "Map",
+ description:
+ "This is the primary map. \nNavigate using the mouse.\nTo Advance or Decrement the route, use the Left/Right arrow keys.",
+ elementId: "mapDisplay",
+ },
+ {
+ title: "Route",
+ description: "Here's the list of all of the upcoming points on the map. Some may have additional comments.",
+ elementId: "routeListDisplay",
+ },
+ {
+ title: "Upcoming",
+ description: "This displays information that informs you of how many of a specific interaction are coming up.",
+ elementId: "upcomingDisplay",
+ },
+ {
+ title: "Progress",
+ description:
+ "This keeps track of how many of each item you have already collected, with optional offsets to account for mistakes.",
+ elementId: "progressDisplay",
+ },
+ {
+ title: "Overlay",
+ description: "The overlay can be opened and chroma-keyed to provide information on livestreams.",
+ elementId: "progressDisplay",
+ },
+ {
+ title: "Point Notes",
+ description: "Here you can add notes for specific points. These will be saved between sessions.",
+ elementId: "pointNotesDisplay",
+ },
+ {
+ title: "Branch Notes",
+ description:
+ "Here you can add notes that stay visible for entire branches. These will be saved between sessions.",
+ elementId: "branchNotesDisplay",
+ },
+ ];
+
const initialLayoutStorage = StorageManager.getItem("layout");
const initialLayout = initialLayoutStorage
? JSON.parse(initialLayoutStorage)
@@ -199,6 +241,7 @@ const RunMosaic: React.FC = () => {
return (
+
{availableDisplays.length !== 0 && (
}) => {
+ const [currentStep, setCurrentStep] = useState(0);
+ const [showTutorial, setShowTutorial] = useState(true);
+ const [position, setPosition] = useState({ x: 0, y: 0 });
+ const [spotlight, setSpotlight] = useState({ x: 0, y: 0, width: 0, height: 0 });
+ const tooltipRef = useRef(null);
+
+ const calculatePosition = (targetRect: DOMRect, tooltipRect: { width: number; height: number }) => {
+ const margin = 10; // Margin from viewport edges
+ let x = targetRect.left + targetRect.width / 2;
+ let y = targetRect.bottom + 20; // 20px below the target element
+
+ // Adjust horizontal position if tooltip goes off-screen
+ if (x + tooltipRect.width / 2 > window.innerWidth - margin) {
+ x = window.innerWidth - tooltipRect.width / 2 - margin;
+ } else if (x - tooltipRect.width / 2 < margin) {
+ x = tooltipRect.width / 2 + margin;
+ }
+
+ // Adjust vertical position if tooltip goes off-screen
+ if (y + tooltipRect.height > window.innerHeight - margin) {
+ y = targetRect.top - tooltipRect.height - 20; // 20px above the target element
+ }
+
+ return { x, y };
+ };
+
+ useEffect(() => {
+ if (showTutorial && steps[currentStep].elementId) {
+ const element = document.getElementById(steps[currentStep].elementId);
+ if (element && tooltipRef.current) {
+ const targetRect = element.getBoundingClientRect();
+ const tooltipRect = (tooltipRef.current as HTMLElement).getBoundingClientRect();
+ const newPosition = calculatePosition(targetRect, tooltipRect);
+ setPosition(newPosition);
+ setSpotlight({
+ x: targetRect.left,
+ y: targetRect.top,
+ width: targetRect.width,
+ height: targetRect.height,
+ });
+ }
+ } else {
+ setSpotlight({ x: 0, y: 0, width: 0, height: 0 });
+ }
+ }, [currentStep, showTutorial, steps]);
+
+ const nextStep = () => {
+ if (currentStep < steps.length - 1) {
+ setCurrentStep(currentStep + 1);
+ } else {
+ setShowTutorial(false);
+ }
+ };
+
+ return (
+
+ {showTutorial && (
+
+
+
+
+
+ {steps[currentStep].title}
+
+
+ {steps[currentStep].description}
+
+
+
+
+ )}
+
+ );
+};
+
+export default Tutorial;
diff --git a/src/components/UpcomingDisplay/UpcomingDisplay.tsx b/src/components/UpcomingDisplay/UpcomingDisplay.tsx
index d91a572..38386b3 100644
--- a/src/components/UpcomingDisplay/UpcomingDisplay.tsx
+++ b/src/components/UpcomingDisplay/UpcomingDisplay.tsx
@@ -53,7 +53,7 @@ const UpcomingDisplay: React.FC = () => {
}, [route, branchIndex, pointIndex]);
return (
-
+
Before next proving ground:
Points: {points}
Rock Koroks: {rockKorokCount}