Skip to content

Commit c193afe

Browse files
committed
feat: refactor map components to use MapView and implement ThemeModeProvider for theme management
1 parent c22b878 commit c193afe

File tree

14 files changed

+283
-370
lines changed

14 files changed

+283
-370
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ dist-ssr
2424
*.sw?
2525
.env
2626
dist-example
27+
dist-sandbox
2728

2829
tsconfig.tsbuildinfo

sandbox/App.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { ThemeProvider } from "@tracktor/design-system";
2-
import { useState } from "react";
1+
// src/App.tsx
32
import { Route, Routes } from "react-router-dom";
3+
import { ThemeModeProvider } from "sandbox/context/ThemeProvider";
44
import FeaturesExample from "sandbox/examples/FeaturesExample";
55
import IsochroneExample from "sandbox/examples/IsochroneExample";
66
import MarkersExample from "sandbox/examples/MarkersExample";
@@ -10,21 +10,19 @@ import LandingPage from "sandbox/features/LandingPage";
1010
import MapProvider from "@/context/MapProvider";
1111

1212
const App = () => {
13-
const [themeMode, setThemeMode] = useState<"light" | "dark">("dark");
14-
1513
return (
16-
<ThemeProvider theme={themeMode}>
14+
<ThemeModeProvider>
1715
<MapProvider licenseMuiX={import.meta.env.VITE_MUI_LICENSE_KEY} licenceMapbox={import.meta.env.VITE_MAPBOX_ACCESS_TOKEN}>
1816
<Routes>
1917
<Route path="/" element={<LandingPage />} />
20-
<Route path="/markers" element={<MarkersExample themeMode={themeMode} setThemeMode={setThemeMode} />} />
21-
<Route path="/features" element={<FeaturesExample themeMode={themeMode} setThemeMode={setThemeMode} />} />
22-
<Route path="/route" element={<RouteExample themeMode={themeMode} setThemeMode={setThemeMode} />} />
23-
<Route path="/nearest-marker" element={<NearestMarkerExample themeMode={themeMode} setThemeMode={setThemeMode} />} />
24-
<Route path="/isochrone" element={<IsochroneExample themeMode={themeMode} setThemeMode={setThemeMode} />} />
18+
<Route path="/markers" element={<MarkersExample />} />
19+
<Route path="/features" element={<FeaturesExample />} />
20+
<Route path="/route" element={<RouteExample />} />
21+
<Route path="/nearest-marker" element={<NearestMarkerExample />} />
22+
<Route path="/isochrone" element={<IsochroneExample />} />
2523
</Routes>
2624
</MapProvider>
27-
</ThemeProvider>
25+
</ThemeModeProvider>
2826
);
2927
};
3028

sandbox/context/ThemeProvider.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ThemeProvider } from "@tracktor/design-system";
2+
import { createContext, ReactNode, useContext, useState } from "react";
3+
4+
type ThemeMode = "light" | "dark";
5+
6+
interface ThemeModeContextValue {
7+
themeMode: ThemeMode;
8+
toggleTheme: () => void;
9+
setThemeMode: (mode: ThemeMode) => void;
10+
}
11+
12+
const ThemeModeContext = createContext<ThemeModeContextValue | undefined>(undefined);
13+
14+
export const ThemeModeProvider = ({ children }: { children: ReactNode }) => {
15+
const [themeMode, setThemeMode] = useState<ThemeMode>("dark");
16+
17+
const toggleTheme = () => setThemeMode((prev) => (prev === "dark" ? "light" : "dark"));
18+
19+
return (
20+
<ThemeModeContext.Provider value={{ setThemeMode, themeMode, toggleTheme }}>
21+
<ThemeProvider theme={themeMode}>{children}</ThemeProvider>
22+
</ThemeModeContext.Provider>
23+
);
24+
};
25+
26+
export const useThemeMode = (): ThemeModeContextValue => {
27+
const ctx = useContext(ThemeModeContext);
28+
if (!ctx) {
29+
throw new Error("useThemeMode must be used inside ThemeModeProvider");
30+
}
31+
return ctx;
32+
};

sandbox/examples/FeaturesExample.tsx

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Box, Button, MenuItem, Select, Stack, Typography } from "@tracktor/design-system";
22
import type { Feature, FeatureCollection, LineString, Point, Polygon } from "geojson";
33
import { useCallback, useMemo, useState } from "react";
4+
import MapSidebar from "sandbox/features/MapSideBar";
45
import Navbar from "sandbox/features/Navbar";
5-
import MarkerMap from "@/features/MarkerMap/MarkerMap.tsx";
6+
import ThemeSwitch from "sandbox/features/ThemeSwitch";
7+
import MapView from "@/features/MapView/MapView";
68

79
const randomCoordInFrance = () => [2 + (Math.random() - 0.5) * 6, 46 + (Math.random() - 0.5) * 6];
810

@@ -51,12 +53,7 @@ const createRandomPolygon = (): Feature<Polygon> => {
5153
};
5254
};
5355

54-
interface FeaturesExampleProps {
55-
themeMode: "light" | "dark";
56-
setThemeMode: (mode: "light" | "dark") => void;
57-
}
58-
59-
const FeaturesExample = ({ themeMode, setThemeMode }: FeaturesExampleProps) => {
56+
const FeaturesExample = () => {
6057
const [featureList, setFeatureList] = useState<Feature[]>([]);
6158
const [baseMapView, setBaseMapView] = useState<"street" | "satellite">("street");
6259

@@ -106,32 +103,11 @@ const FeaturesExample = ({ themeMode, setThemeMode }: FeaturesExampleProps) => {
106103
<Navbar />
107104
<Stack direction="row" sx={{ height: "100vh", overflow: "hidden", width: "100vw" }}>
108105
<Box sx={{ flex: 1 }}>
109-
<MarkerMap markers={markers} features={features} fitBounds baseMapView={baseMapView} height="100%" width="100%" />
106+
<MapView markers={markers} features={features} fitBounds baseMapView={baseMapView} height="100%" width="100%" />
107+
<ThemeSwitch />
110108
</Box>
111109

112-
<Box
113-
sx={{
114-
backgroundColor: "background.paper",
115-
borderColor: "divider",
116-
borderLeft: "1px solid",
117-
color: "text.primary",
118-
display: "flex",
119-
flexDirection: "column",
120-
gap: 2,
121-
overflowY: "auto",
122-
p: 2,
123-
width: 300,
124-
}}
125-
>
126-
<Typography variant="h6">🧭 Options</Typography>
127-
128-
<Typography variant="body2" color="text.secondary">
129-
Theme
130-
</Typography>
131-
<Button variant="outlined" onClick={() => setThemeMode(themeMode === "dark" ? "light" : "dark")}>
132-
{themeMode === "dark" ? "Light mode" : "Dark mode"}
133-
</Button>
134-
110+
<MapSidebar>
135111
<Typography variant="body2" color="text.secondary">
136112
Base Map View
137113
</Typography>
@@ -161,7 +137,7 @@ const FeaturesExample = ({ themeMode, setThemeMode }: FeaturesExampleProps) => {
161137
🗑 Clear all
162138
</Button>
163139
)}
164-
</Box>
140+
</MapSidebar>
165141
</Stack>
166142
</>
167143
);

sandbox/examples/IsochroneExample.tsx

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { Box, Button, MenuItem, Select, Stack, Switch, Typography } from "@tracktor/design-system";
1+
import { Box, MenuItem, Select, Stack, Switch, Typography } from "@tracktor/design-system";
22
import { useEffect, useMemo, useState } from "react";
33
import type { ProjectionSpecification } from "react-map-gl";
4+
import MapSidebar from "sandbox/features/MapSideBar";
45
import Navbar from "sandbox/features/Navbar";
5-
import MarkerMap from "@/features/MarkerMap/MarkerMap";
6+
import ThemeSwitch from "sandbox/features/ThemeSwitch";
7+
import MapView from "@/features/MapView/MapView";
68
import type { RoutingProfile } from "@/services/core/interface";
79
import isPointInGeoJSON from "@/utils/isPointInGeoJSON";
810

@@ -12,12 +14,7 @@ const predefinedOrigins = [
1214
{ coords: [-0.5792, 44.8378], id: "bordeaux", name: "Bordeaux" },
1315
];
1416

15-
interface IsochroneExampleProps {
16-
themeMode: "light" | "dark";
17-
setThemeMode: (mode: "light" | "dark") => void;
18-
}
19-
20-
const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) => {
17+
const IsochroneExample = () => {
2118
const [projection, setProjection] = useState<ProjectionSpecification>({ name: "mercator" });
2219
const [profile, setProfile] = useState<RoutingProfile>("driving");
2320
const [origin, setOrigin] = useState(predefinedOrigins[0]);
@@ -26,13 +23,12 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
2623
const [cooperativeGestures, setCooperativeGestures] = useState(true);
2724
const [doubleClickZoom, setDoubleClickZoom] = useState(true);
2825

29-
// ✅ markers autour de l’origine
3026
const generateMarkers = (base: typeof origin) =>
3127
Array.from({ length: 12 }, (_, i) => {
3228
const offsetLng = (Math.random() - 0.5) * 0.5;
3329
const offsetLat = (Math.random() - 0.5) * 0.5;
3430
return {
35-
color: "#f59e0b", // orange par défaut
31+
color: "#f59e0b",
3632
id: i + 1,
3733
lat: base.coords[1] + offsetLat,
3834
lng: base.coords[0] + offsetLng,
@@ -79,7 +75,7 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
7975
<Stack direction="row" sx={{ height: "100vh", overflow: "hidden", width: "100vw" }}>
8076
{/* 🗺️ MAP */}
8177
<Box sx={{ flex: 1 }}>
82-
<MarkerMap
78+
<MapView
8379
height="100%"
8480
width="100%"
8581
markers={allMarkers}
@@ -94,30 +90,10 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
9490
profile,
9591
}}
9692
/>
93+
<ThemeSwitch />
9794
</Box>
9895

99-
{/* ⚙️ PANNEAU DE CONTRÔLE */}
100-
<Box
101-
sx={{
102-
backgroundColor: "background.paper",
103-
borderColor: "divider",
104-
borderLeft: "1px solid",
105-
color: "text.primary",
106-
display: "flex",
107-
flexDirection: "column",
108-
gap: 2,
109-
overflowY: "auto",
110-
p: 2,
111-
width: 300,
112-
}}
113-
>
114-
<Typography variant="h6">🕒 Isochrone + Reachability</Typography>
115-
116-
<Button variant="outlined" onClick={() => setThemeMode(themeMode === "dark" ? "light" : "dark")}>
117-
{themeMode === "dark" ? "Light mode" : "Dark mode"}
118-
</Button>
119-
120-
{/* 🔽 Origine */}
96+
<MapSidebar>
12197
<Typography variant="body2" color="text.secondary">
12298
Origin
12399
</Typography>
@@ -141,7 +117,6 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
141117
))}
142118
</Select>
143119

144-
{/* 🛣️ Profile */}
145120
<Typography variant="body2" color="text.secondary">
146121
Profile
147122
</Typography>
@@ -151,7 +126,6 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
151126
<MenuItem value="cycling">🚴 Cycling</MenuItem>
152127
</Select>
153128

154-
{/* ⏱️ Intervalles */}
155129
<Typography variant="body2" color="text.secondary">
156130
Isochrone intervals (minutes)
157131
</Typography>
@@ -172,7 +146,6 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
172146
<MenuItem value="15,30,45">15 / 30 / 45 min</MenuItem>
173147
</Select>
174148

175-
{/* 🌍 Projection */}
176149
<Typography variant="body2" color="text.secondary">
177150
Projection
178151
</Typography>
@@ -186,7 +159,6 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
186159
<MenuItem value="naturalEarth">Natural Earth</MenuItem>
187160
</Select>
188161

189-
{/* ✋ Interactions */}
190162
<Typography variant="body2" color="text.secondary">
191163
Interactions
192164
</Typography>
@@ -204,7 +176,7 @@ const IsochroneExample = ({ themeMode, setThemeMode }: IsochroneExampleProps) =>
204176
<Typography variant="caption" color="text.secondary" sx={{ mt: 1 }}>
205177
🟢 inside isochrone | ⚫ outside | 🟠 pending
206178
</Typography>
207-
</Box>
179+
</MapSidebar>
208180
</Stack>
209181
</>
210182
);

sandbox/examples/MarkersExample.tsx

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import HomeIcon from "@mui/icons-material/Home";
22
import {
33
Avatar,
44
Box,
5-
Button,
65
Card,
76
CardContent,
87
Chip,
@@ -17,9 +16,11 @@ import {
1716
} from "@tracktor/design-system";
1817
import { useMemo, useState } from "react";
1918
import type { ProjectionSpecification } from "react-map-gl";
19+
import MapSidebar from "sandbox/features/MapSideBar";
2020
import Navbar from "sandbox/features/Navbar";
21+
import ThemeSwitch from "sandbox/features/ThemeSwitch";
2122
import { VariantMarker, variantMarkerColor } from "@/components/Markers/Markers";
22-
import MarkerMap from "@/features/MarkerMap/MarkerMap";
23+
import MapView from "@/features/MapView/MapView";
2324
import { MarkerProps } from "@/types/MarkerProps";
2425

2526
const MAX_MARKERS = 1000;
@@ -86,12 +87,7 @@ const generateMarkers = (
8687
};
8788
});
8889

89-
interface MarkerExampleProps {
90-
themeMode: "light" | "dark";
91-
setThemeMode: (mode: "light" | "dark") => void;
92-
}
93-
94-
const MarkersExample = ({ themeMode, setThemeMode }: MarkerExampleProps) => {
90+
const MarkersExample = () => {
9591
const [baseMapView, setBaseMapView] = useState<"street" | "satellite">("street");
9692
const [cooperativeGestures, setCooperativeGestures] = useState(true);
9793
const [doubleClickZoom, setDoubleClickZoom] = useState(true);
@@ -115,7 +111,7 @@ const MarkersExample = ({ themeMode, setThemeMode }: MarkerExampleProps) => {
115111
<Stack direction="row" sx={{ height: "100vh", overflow: "hidden", width: "100vw" }}>
116112
{/* 🗺️ Map */}
117113
<Box sx={{ flex: 1 }}>
118-
<MarkerMap
114+
<MapView
119115
openPopup={openPopupId}
120116
markers={markers}
121117
height="100%"
@@ -127,33 +123,12 @@ const MarkersExample = ({ themeMode, setThemeMode }: MarkerExampleProps) => {
127123
projection={projection}
128124
openPopupOnHover={openPopupOnHover}
129125
/>
126+
127+
<ThemeSwitch />
130128
</Box>
131129

132130
{/* ⚙️ Sidebar panel */}
133-
<Box
134-
sx={{
135-
backgroundColor: "background.paper",
136-
borderColor: "divider",
137-
borderLeft: "1px solid",
138-
color: "text.primary",
139-
display: "flex",
140-
flexDirection: "column",
141-
gap: 2,
142-
overflowY: "auto",
143-
p: 2,
144-
width: 300,
145-
}}
146-
>
147-
<Typography variant="h6">🧭 Options</Typography>
148-
149-
{/* Theme toggle */}
150-
<Typography variant="body2" color="text.secondary">
151-
Theme
152-
</Typography>
153-
<Button variant="outlined" onClick={() => setThemeMode(themeMode === "dark" ? "light" : "dark")}>
154-
{themeMode === "dark" ? "Light mode" : "Dark mode"}
155-
</Button>
156-
131+
<MapSidebar>
157132
{/* Base map style */}
158133
<Typography variant="body2" color="text.secondary">
159134
Base Map View
@@ -234,7 +209,7 @@ const MarkersExample = ({ themeMode, setThemeMode }: MarkerExampleProps) => {
234209
<MenuItem value="equirectangular">Equirectangular</MenuItem>
235210
<MenuItem value="naturalEarth">Natural Earth</MenuItem>
236211
</Select>
237-
</Box>
212+
</MapSidebar>
238213
</Stack>
239214
</>
240215
);

0 commit comments

Comments
 (0)