Skip to content

Commit

Permalink
Don't forget the tutorial!
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt-Hurd committed Aug 7, 2024
1 parent 8239424 commit dfc86f3
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/components/BranchNotesDisplay/BranchNotesDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const BranchNotesDisplay: React.FC = () => {
StorageManager.setItem(`${route.game.name}_${route.name}_${route.version}_b_${branchIndex}`, content);
};

return <NoteEditor notes={notes} onNotesChange={handleNotesChange} />;
return <NoteEditor notes={notes} onNotesChange={handleNotesChange} id="branchNotesDisplay" />;
};

export default BranchNotesDisplay;
11 changes: 10 additions & 1 deletion src/components/MapDisplay/MapDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@ const MapDisplay: React.FC = () => {
};

return (
<MapContainer style={style} bounds={outerBounds} zoom={0} maxZoom={7} minZoom={1} crs={crs} keyboard={false}>
<MapContainer
style={style}
bounds={outerBounds}
zoom={0}
maxZoom={7}
minZoom={1}
crs={crs}
keyboard={false}
id="mapDisplay"
>
<RouteMarkers branch={route.branches[branchIndex]} activeThing={activeThing} />
<RouteLines />
<MapUpdate activePoint={activePoint} />
Expand Down
5 changes: 3 additions & 2 deletions src/components/NoteEditor/NoteEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ Quill.register("modules/blotFormatter", BlotFormatter);

interface NoteEditorProps {
notes: string;
id: string;
onNotesChange: (content: string) => void;
}

const NoteEditor: React.FC<NoteEditorProps> = ({ notes, onNotesChange }) => {
const NoteEditor: React.FC<NoteEditorProps> = ({ notes, onNotesChange, id }) => {
const quillRef = useRef<ReactQuill>(null);

const insertToEditor = useCallback(
Expand All @@ -56,7 +57,7 @@ const NoteEditor: React.FC<NoteEditorProps> = ({ notes, onNotesChange }) => {
}
}, [quillRef, selectLocalImage]);

return <ReactQuill ref={quillRef} modules={modules} value={notes} onChange={onNotesChange} />;
return <ReactQuill ref={quillRef} modules={modules} value={notes} onChange={onNotesChange} id={id} />;
};

export default NoteEditor;
2 changes: 1 addition & 1 deletion src/components/PointNotesDisplay/PointNotesDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const PointNotesDisplay: React.FC = () => {
StorageManager.setItem(`${route.game.name}_${route.name}_${route.version}_p_${branchIndex}_${pointIndex}`, content);
};

return <NoteEditor notes={notes} onNotesChange={handleNotesChange} />;
return <NoteEditor notes={notes} onNotesChange={handleNotesChange} id="pointNotesDisplay" />;
};

export default PointNotesDisplay;
2 changes: 1 addition & 1 deletion src/components/ProgressDisplay/ProgressDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const ProgressDisplay: React.FC = () => {
}, [route, branchIndex, pointIndex]);

return (
<div className={"progress-window"}>
<div className={"progress-window"} id="progressDisplay">
{" "}
<button className={"overlay-window-button"} onClick={openOverlayWindow}>
Open Overlay Window
Expand Down
2 changes: 1 addition & 1 deletion src/components/RouteListDisplay/RouteListDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const RouteListDisplay: React.FC = () => {
};

return (
<div className="routeList">
<div className="routeList" id="routeListDisplay">
{route.branches.map((branch, bIdx) => (
<div className="routeList__branch" key={bIdx}>
<div className="routeList__branchName">
Expand Down
43 changes: 43 additions & 0 deletions src/components/RunMosaic/RunMosaic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -199,6 +241,7 @@ const RunMosaic: React.FC = () => {

return (
<div className={"run-mosaic " + darkModeClass}>
<TutorialDisplay steps={tutorialSteps} />
{availableDisplays.length !== 0 && (
<Toolbar
onButtonClick={handleToolbarButtonClick}
Expand Down
137 changes: 137 additions & 0 deletions src/components/TutorialDisplay/TutorialDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "framer-motion";

const Tutorial = ({ steps }: { steps: Array<any> }) => {
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 (
<AnimatePresence>
{showTutorial && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 9999,
}}
>
<svg
width="100%"
height="100%"
style={{
position: "absolute",
top: 0,
left: 0,
}}
>
<defs>
<mask id="spotlight-mask">
<rect width="100%" height="100%" fill="white" />
<rect x={spotlight.x} y={spotlight.y} width={spotlight.width} height={spotlight.height} fill="black" />
</mask>
</defs>
<rect width="100%" height="100%" fill="rgba(0, 0, 0, 0.5)" mask="url(#spotlight-mask)" />
</svg>

<motion.div
ref={tooltipRef}
initial={{ scale: 0.8, opacity: 0 }}
animate={{
scale: 1,
opacity: 1,
x: position.x,
y: position.y,
}}
exit={{ scale: 0.8, opacity: 0 }}
transition={{ type: "spring", damping: 10, stiffness: 100 }}
style={{
backgroundColor: "white",
padding: "20px",
borderRadius: "10px",
maxWidth: "300px",
textAlign: "center",
position: "absolute",
transform: "translate(-50%, 0)",
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1)",
}}
>
<h2
style={{
color: "black",
}}
>
{steps[currentStep].title}
</h2>
<p
style={{
color: "black",
}}
>
{steps[currentStep].description}
</p>
<button onClick={nextStep}>{currentStep < steps.length - 1 ? "Next" : "Finish"}</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};

export default Tutorial;
2 changes: 1 addition & 1 deletion src/components/UpcomingDisplay/UpcomingDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const UpcomingDisplay: React.FC = () => {
}, [route, branchIndex, pointIndex]);

return (
<div className={"upcoming-window"}>
<div className={"upcoming-window"} id="upcomingDisplay">
<p>Before next proving ground:</p>
<p>Points: {points}</p>
<p>Rock Koroks: {rockKorokCount}</p>
Expand Down

0 comments on commit dfc86f3

Please sign in to comment.