@navigoo/map-components 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,36 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, ReactNode } from 'react';
2
2
  import { Place, Route } from '../lib/type';
3
3
  import { ApiClient } from '../lib/api';
4
- import styles from './styles.module.css';
5
4
 
6
5
  interface DetourRouteSearchProps {
7
6
  apiClient: ApiClient;
8
7
  setRoutes: (routes: Route[]) => void;
9
8
  setSelectedRouteIndex: (index: number) => void;
9
+ className?: string;
10
+ searchGroupClassName?: string;
11
+ labelClassName?: string;
12
+ inputClassName?: string;
13
+ resultsClassName?: string;
14
+ resultItemClassName?: string;
15
+ errorClassName?: string;
16
+ buttonClassName?: string;
17
+ disabledButtonClassName?: string;
10
18
  }
11
19
 
12
- const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRoutes, setSelectedRouteIndex }) => {
20
+ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({
21
+ apiClient,
22
+ setRoutes,
23
+ setSelectedRouteIndex,
24
+ className,
25
+ searchGroupClassName,
26
+ labelClassName,
27
+ inputClassName,
28
+ resultsClassName,
29
+ resultItemClassName,
30
+ errorClassName,
31
+ buttonClassName,
32
+ disabledButtonClassName,
33
+ }) => {
13
34
  const [startQuery, setStartQuery] = useState('');
14
35
  const [detourQuery, setDetourQuery] = useState('');
15
36
  const [endQuery, setEndQuery] = useState('');
@@ -120,9 +141,9 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
120
141
  };
121
142
 
122
143
  return (
123
- <div className={styles.routeSearchSection}>
124
- <div className={styles.searchGroup}>
125
- <label htmlFor="start" className={styles.label}>Départ</label>
144
+ <div className={className}>
145
+ <div className={searchGroupClassName}>
146
+ <label htmlFor="start" className={labelClassName}>Départ</label>
126
147
  <input
127
148
  id="start"
128
149
  type="text"
@@ -132,15 +153,15 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
132
153
  handleSearch(e.target.value, setStartResults);
133
154
  }}
134
155
  placeholder="Point de départ"
135
- className={styles.searchInput}
156
+ className={inputClassName}
136
157
  />
137
158
  {startResults.length > 0 && (
138
- <ul className={styles.searchResults}>
159
+ <ul className={resultsClassName}>
139
160
  {startResults.map((place) => (
140
161
  <li
141
162
  key={place.id}
142
163
  onClick={() => handleSelectStart(place)}
143
- className={styles.searchResultItem}
164
+ className={resultItemClassName}
144
165
  >
145
166
  {place.name}
146
167
  </li>
@@ -148,8 +169,8 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
148
169
  </ul>
149
170
  )}
150
171
  </div>
151
- <div className={styles.searchGroup}>
152
- <label htmlFor="detour" className={styles.label}>Détour</label>
172
+ <div className={searchGroupClassName}>
173
+ <label htmlFor="detour" className={labelClassName}>Détour</label>
153
174
  <input
154
175
  id="detour"
155
176
  type="text"
@@ -159,15 +180,15 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
159
180
  handleSearch(e.target.value, setDetourResults);
160
181
  }}
161
182
  placeholder="Point de détour"
162
- className={styles.searchInput}
183
+ className={inputClassName}
163
184
  />
164
185
  {detourResults.length > 0 && (
165
- <ul className={styles.searchResults}>
186
+ <ul className={resultsClassName}>
166
187
  {detourResults.map((place) => (
167
188
  <li
168
189
  key={place.id}
169
190
  onClick={() => handleSelectDetour(place)}
170
- className={styles.searchResultItem}
191
+ className={resultItemClassName}
171
192
  >
172
193
  {place.name}
173
194
  </li>
@@ -175,8 +196,8 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
175
196
  </ul>
176
197
  )}
177
198
  </div>
178
- <div className={styles.searchGroup}>
179
- <label htmlFor="end" className={styles.label}>Destination</label>
199
+ <div className={searchGroupClassName}>
200
+ <label htmlFor="end" className={labelClassName}>Destination</label>
180
201
  <input
181
202
  id="end"
182
203
  type="text"
@@ -186,15 +207,15 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
186
207
  handleSearch(e.target.value, setEndResults);
187
208
  }}
188
209
  placeholder="Point d'arrivée"
189
- className={styles.searchInput}
210
+ className={inputClassName}
190
211
  />
191
212
  {endResults.length > 0 && (
192
- <ul className={styles.searchResults}>
213
+ <ul className={resultsClassName}>
193
214
  {endResults.map((place) => (
194
215
  <li
195
216
  key={place.id}
196
217
  onClick={() => handleSelectEnd(place)}
197
- className={styles.searchResultItem}
218
+ className={resultItemClassName}
198
219
  >
199
220
  {place.name}
200
221
  </li>
@@ -202,11 +223,11 @@ const DetourRouteSearch: React.FC<DetourRouteSearchProps> = ({ apiClient, setRou
202
223
  </ul>
203
224
  )}
204
225
  </div>
205
- {error && <div className={styles.error}>{error}</div>}
226
+ {error && <div className={errorClassName}>{error}</div>}
206
227
  <button
207
228
  onClick={handleCalculateRoute}
208
229
  disabled={loading || !selectedStart || !selectedDetour || !selectedEnd}
209
- className={`${styles.searchButton} ${loading || !selectedStart || !selectedDetour || !selectedEnd ? styles.disabled : ''}`}
230
+ className={loading || !selectedStart || !selectedDetour || !selectedEnd ? disabledButtonClassName : buttonClassName}
210
231
  >
211
232
  {loading ? 'Calcul...' : 'Calculer l\'itinéraire'}
212
233
  </button>
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import L from 'leaflet';
3
3
  import 'leaflet/dist/leaflet.css';
4
4
  import { parse } from 'wellknown';
@@ -28,6 +28,77 @@ const MapView: React.FC<MapViewProps> = ({
28
28
  const routeLayerRef = useRef<L.LayerGroup | null>(null);
29
29
  const clickMarkerRef = useRef<L.Marker | null>(null);
30
30
  const routePolylinesRef = useRef<L.Polyline[]>([]);
31
+ const [ipLocation, setIpLocation] = useState<{ lat: number; lng: number } | null>(null);
32
+
33
+ // Fonction pour obtenir la localisation par IP
34
+ const getLocationByIP = async (): Promise<{ lat: number; lng: number } | null> => {
35
+ try {
36
+ const response = await fetch('https://ipapi.co/json/');
37
+ const data = await response.json();
38
+
39
+ if (data.latitude && data.longitude) {
40
+ return {
41
+ lat: data.latitude,
42
+ lng: data.longitude
43
+ };
44
+ }
45
+ return null;
46
+ } catch (error) {
47
+ console.error('Erreur de géolocalisation par IP:', error);
48
+ return null;
49
+ }
50
+ };
51
+
52
+ // Fonction pour obtenir la localisation par le navigateur
53
+ const getBrowserLocation = (): Promise<{ lat: number; lng: number }> => {
54
+ return new Promise((resolve, reject) => {
55
+ if (!navigator.geolocation) {
56
+ reject(new Error('Géolocalisation non supportée'));
57
+ return;
58
+ }
59
+
60
+ navigator.geolocation.getCurrentPosition(
61
+ (position) => {
62
+ resolve({
63
+ lat: position.coords.latitude,
64
+ lng: position.coords.longitude
65
+ });
66
+ },
67
+ (error) => {
68
+ reject(error);
69
+ },
70
+ {
71
+ enableHighAccuracy: true,
72
+ timeout: 10000,
73
+ maximumAge: 60000
74
+ }
75
+ );
76
+ });
77
+ };
78
+
79
+ // Effet pour la géolocalisation au démarrage
80
+ useEffect(() => {
81
+ const initializeLocation = async () => {
82
+ try {
83
+ // Essayer d'abord la géolocalisation du navigateur
84
+ const browserLocation = await getBrowserLocation();
85
+ setIpLocation(browserLocation);
86
+ } catch (browserError) {
87
+ console.log('Géolocalisation navigateur échouée, tentative par IP...', browserError);
88
+
89
+ // Fallback sur la géolocalisation par IP
90
+ const ipLocation = await getLocationByIP();
91
+ if (ipLocation) {
92
+ setIpLocation(ipLocation);
93
+ } else {
94
+ // Fallback final sur une position par défaut (centre du monde)
95
+ setIpLocation({ lat: 20, lng: 0 });
96
+ }
97
+ }
98
+ };
99
+
100
+ initializeLocation();
101
+ }, []);
31
102
 
32
103
  const parseWKTLineString = (wkt: string): [number, number][] => {
33
104
  try {
@@ -52,18 +123,20 @@ const MapView: React.FC<MapViewProps> = ({
52
123
 
53
124
  useEffect(() => {
54
125
  if (mapContainerRef.current && !mapRef.current) {
126
+ // Position par défaut centrée sur le monde, sera mise à jour par la géolocalisation
127
+ const defaultCenter = ipLocation || { lat: 20, lng: 0 };
128
+
55
129
  mapRef.current = L.map(mapContainerRef.current, {
56
- center: [3.8480, 11.5021], // Centre par défaut : Yaoundé
130
+ center: [defaultCenter.lat, defaultCenter.lng],
57
131
  zoom: 12,
58
- minZoom: 11,
59
- maxZoom: 16,
60
- maxBounds: [[3.7, 11.4], [4.0, 11.6]],
61
- maxBoundsViscosity: 1.0,
132
+ minZoom: 2, // Zoom minimal réduit pour voir le monde entier
133
+ maxZoom: 18, // Zoom maximal augmenté pour plus de détails
134
+ // Suppression des limites de la carte
62
135
  });
63
136
 
64
137
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
65
138
  attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
66
- maxZoom: 16,
139
+ maxZoom: 18,
67
140
  }).addTo(mapRef.current);
68
141
 
69
142
  L.Icon.Default.mergeOptions({
@@ -91,13 +164,26 @@ const MapView: React.FC<MapViewProps> = ({
91
164
  });
92
165
  }
93
166
 
167
+ // Mettre à jour le centre de la carte quand la localisation par IP est disponible
168
+ if (mapRef.current && ipLocation && !userLocation && !searchedPlace && !routes) {
169
+ mapRef.current.setView([ipLocation.lat, ipLocation.lng], 12, { animate: true });
170
+
171
+ // Ajouter un marqueur pour la position détectée
172
+ if (!markerRef.current) {
173
+ markerRef.current = L.marker([ipLocation.lat, ipLocation.lng])
174
+ .addTo(mapRef.current)
175
+ .bindPopup(`<b>Votre position approximative</b><br>Détectée par IP<br>Lat: ${ipLocation.lat.toFixed(6)}<br>Lng: ${ipLocation.lng.toFixed(6)}`)
176
+ .openPopup();
177
+ }
178
+ }
179
+
94
180
  return () => {
95
181
  if (mapRef.current) {
96
182
  mapRef.current.remove();
97
183
  mapRef.current = null;
98
184
  }
99
185
  };
100
- }, [apiClient]);
186
+ }, [apiClient, ipLocation]);
101
187
 
102
188
  useEffect(() => {
103
189
  if (!mapRef.current) return;
@@ -190,10 +276,14 @@ const MapView: React.FC<MapViewProps> = ({
190
276
  centerOnPoint(searchedPlace.coordinates.lat, searchedPlace.coordinates.lng, searchedPlace.name);
191
277
  } else if (userLocation) {
192
278
  centerOnPoint(userLocation.latitude, userLocation.longitude, 'Votre position');
279
+ } else if (ipLocation) {
280
+ // Centrer sur la position IP si aucune autre position n'est disponible
281
+ centerOnPoint(ipLocation.lat, ipLocation.lng, 'Votre position approximative', 12);
193
282
  } else {
194
- mapRef.current!.setView([3.8480, 11.5021], 12, { animate: true });
283
+ // Position de fallback centrée sur le monde
284
+ mapRef.current!.setView([20, 0], 2, { animate: true });
195
285
  }
196
- }, [apiClient, userLocation, searchedPlace, routes, selectedRouteIndex, setSelectedRouteIndex]);
286
+ }, [apiClient, userLocation, searchedPlace, routes, selectedRouteIndex, setSelectedRouteIndex, ipLocation]);
197
287
 
198
288
  return <div className="w-full h-screen" ref={mapContainerRef} />;
199
289
  };
@@ -1,15 +1,36 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { Place, Route, GeolocationResult } from '../lib/type';
2
+ import { Place, Route } from '../lib/type';
3
3
  import { ApiClient } from '../lib/api';
4
- import styles from './styles.module.css';
5
4
 
6
5
  interface RouteSearchProps {
7
6
  apiClient: ApiClient;
8
7
  setRoutes: (routes: Route[]) => void;
9
8
  setSelectedRouteIndex: (index: number) => void;
9
+ className?: string;
10
+ searchGroupClassName?: string;
11
+ labelClassName?: string;
12
+ inputClassName?: string;
13
+ resultsClassName?: string;
14
+ resultItemClassName?: string;
15
+ errorClassName?: string;
16
+ buttonClassName?: string;
17
+ disabledButtonClassName?: string;
10
18
  }
11
19
 
12
- const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSelectedRouteIndex }) => {
20
+ const RouteSearch: React.FC<RouteSearchProps> = ({
21
+ apiClient,
22
+ setRoutes,
23
+ setSelectedRouteIndex,
24
+ className,
25
+ searchGroupClassName,
26
+ labelClassName,
27
+ inputClassName,
28
+ resultsClassName,
29
+ resultItemClassName,
30
+ errorClassName,
31
+ buttonClassName,
32
+ disabledButtonClassName,
33
+ }) => {
13
34
  const [startQuery, setStartQuery] = useState('');
14
35
  const [endQuery, setEndQuery] = useState('');
15
36
  const [startResults, setStartResults] = useState<Place[]>([]);
@@ -93,9 +114,9 @@ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSele
93
114
  };
94
115
 
95
116
  return (
96
- <div className={styles.routeSearchSection}>
97
- <div className={styles.searchGroup}>
98
- <label htmlFor="start" className={styles.label}>Départ</label>
117
+ <div className={className}>
118
+ <div className={searchGroupClassName}>
119
+ <label htmlFor="start" className={labelClassName}>Départ</label>
99
120
  <input
100
121
  id="start"
101
122
  type="text"
@@ -105,15 +126,15 @@ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSele
105
126
  handleSearch(e.target.value, setStartResults);
106
127
  }}
107
128
  placeholder="Point de départ"
108
- className={styles.searchInput}
129
+ className={inputClassName}
109
130
  />
110
131
  {startResults.length > 0 && (
111
- <ul className={styles.searchResults}>
132
+ <ul className={resultsClassName}>
112
133
  {startResults.map((place) => (
113
134
  <li
114
135
  key={place.id}
115
136
  onClick={() => handleSelectStart(place)}
116
- className={styles.searchResultItem}
137
+ className={resultItemClassName}
117
138
  >
118
139
  {place.name}
119
140
  </li>
@@ -121,8 +142,8 @@ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSele
121
142
  </ul>
122
143
  )}
123
144
  </div>
124
- <div className={styles.searchGroup}>
125
- <label htmlFor="end" className={styles.label}>Destination</label>
145
+ <div className={searchGroupClassName}>
146
+ <label htmlFor="end" className={labelClassName}>Destination</label>
126
147
  <input
127
148
  id="end"
128
149
  type="text"
@@ -132,15 +153,15 @@ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSele
132
153
  handleSearch(e.target.value, setEndResults);
133
154
  }}
134
155
  placeholder="Point d'arrivée"
135
- className={styles.searchInput}
156
+ className={inputClassName}
136
157
  />
137
158
  {endResults.length > 0 && (
138
- <ul className={styles.searchResults}>
159
+ <ul className={resultsClassName}>
139
160
  {endResults.map((place) => (
140
161
  <li
141
162
  key={place.id}
142
163
  onClick={() => handleSelectEnd(place)}
143
- className={styles.searchResultItem}
164
+ className={resultItemClassName}
144
165
  >
145
166
  {place.name}
146
167
  </li>
@@ -148,11 +169,11 @@ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSele
148
169
  </ul>
149
170
  )}
150
171
  </div>
151
- {error && <div className={styles.error}>{error}</div>}
172
+ {error && <div className={errorClassName}>{error}</div>}
152
173
  <button
153
174
  onClick={handleCalculateRoute}
154
175
  disabled={loading || !selectedStart || !selectedEnd}
155
- className={`${styles.searchButton} ${loading || !selectedStart || !selectedEnd ? styles.disabled : ''}`}
176
+ className={loading || !selectedStart || !selectedEnd ? disabledButtonClassName : buttonClassName}
156
177
  >
157
178
  {loading ? 'Calcul...' : 'Calculer l\'itinéraire'}
158
179
  </button>
@@ -1,15 +1,30 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Place, GeolocationResult } from '../lib/type';
3
3
  import { ApiClient } from '../lib/api';
4
- import styles from './styles.module.css';
5
4
 
6
5
  interface SearchBarProps {
7
6
  apiClient: ApiClient;
8
7
  setUserLocation: (location: GeolocationResult | null) => void;
9
8
  setSearchedPlace: (place: Place | null) => void;
9
+ className?: string;
10
+ inputClassName?: string;
11
+ buttonClassName?: string;
12
+ errorClassName?: string;
13
+ resultsClassName?: string;
14
+ resultItemClassName?: string;
10
15
  }
11
16
 
12
- const SearchBar: React.FC<SearchBarProps> = ({ apiClient, setUserLocation, setSearchedPlace }) => {
17
+ const SearchBar: React.FC<SearchBarProps> = ({
18
+ apiClient,
19
+ setUserLocation,
20
+ setSearchedPlace,
21
+ className,
22
+ inputClassName,
23
+ buttonClassName,
24
+ errorClassName,
25
+ resultsClassName,
26
+ resultItemClassName,
27
+ }) => {
13
28
  const [query, setQuery] = useState('');
14
29
  const [loading, setLoading] = useState(false);
15
30
  const [results, setResults] = useState<Place[]>([]);
@@ -47,26 +62,26 @@ const SearchBar: React.FC<SearchBarProps> = ({ apiClient, setUserLocation, setSe
47
62
  };
48
63
 
49
64
  return (
50
- <div className={styles.searchSection}>
51
- <div className={styles.searchGroup}>
52
- <label htmlFor="search" className={styles.label}>Nom du lieu</label>
65
+ <div className={className}>
66
+ <div>
67
+ <label htmlFor="search">Nom du lieu</label>
53
68
  <input
54
69
  id="search"
55
70
  type="text"
56
71
  value={query}
57
72
  onChange={(e) => setQuery(e.target.value)}
58
73
  placeholder="Rechercher un lieu"
59
- className={styles.searchInput}
74
+ className={inputClassName}
60
75
  />
61
76
  </div>
62
- {error && <div className={styles.error}>{error}</div>}
77
+ {error && <div className={errorClassName}>{error}</div>}
63
78
  {results.length > 0 && (
64
- <ul className={styles.searchResults}>
79
+ <ul className={resultsClassName}>
65
80
  {results.map((place) => (
66
81
  <li
67
82
  key={place.id}
68
83
  onClick={() => handleSelect(place)}
69
- className={styles.searchResultItem}
84
+ className={resultItemClassName}
70
85
  >
71
86
  {place.name}
72
87
  </li>
@@ -76,7 +91,7 @@ const SearchBar: React.FC<SearchBarProps> = ({ apiClient, setUserLocation, setSe
76
91
  <button
77
92
  onClick={handleSearch}
78
93
  disabled={loading || !query}
79
- className={`${styles.searchButton} ${loading || !query ? styles.disabled : ''}`}
94
+ className={buttonClassName}
80
95
  >
81
96
  {loading ? 'Recherche...' : 'Rechercher'}
82
97
  </button>
@@ -1,7 +1,28 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import styles from './styles.module.css';
3
2
 
4
- const TransportOptions: React.FC = () => {
3
+ interface TransportOptionsProps {
4
+ className?: string;
5
+ headerClassName?: string;
6
+ titleClassName?: string;
7
+ toggleButtonClassName?: string;
8
+ optionsClassName?: string;
9
+ optionClassName?: string;
10
+ selectedOptionClassName?: string;
11
+ iconClassName?: string;
12
+ nameClassName?: string;
13
+ }
14
+
15
+ const TransportOptions: React.FC<TransportOptionsProps> = ({
16
+ className,
17
+ headerClassName,
18
+ titleClassName,
19
+ toggleButtonClassName,
20
+ optionsClassName,
21
+ optionClassName,
22
+ selectedOptionClassName,
23
+ iconClassName,
24
+ nameClassName,
25
+ }) => {
5
26
  const [showOptions, setShowOptions] = useState(false);
6
27
  const [selectedTransport, setSelectedTransport] = useState('taxi');
7
28
 
@@ -16,37 +37,34 @@ const TransportOptions: React.FC = () => {
16
37
  }, [selectedTransport]);
17
38
 
18
39
  return (
19
- <div className={styles.optionsSection}>
20
- <div className={styles.optionsHeader}>
21
- <h3 className={styles.optionsTitle}>Moyen de transport</h3>
40
+ <div className={className}>
41
+ <div className={headerClassName}>
42
+ <h3 className={titleClassName}>Moyen de transport</h3>
22
43
  <button
23
44
  onClick={() => setShowOptions(!showOptions)}
24
- className={styles.toggleButton}
45
+ className={toggleButtonClassName}
25
46
  >
26
47
  {showOptions ? 'Réduire' : 'Options'}
27
48
  </button>
28
49
  </div>
29
50
  {showOptions && (
30
- <div className={styles.transportOptions}>
51
+ <div className={optionsClassName}>
31
52
  {transportOptions.map((option) => (
32
53
  <button
33
54
  key={option.id}
34
55
  onClick={() => setSelectedTransport(option.id)}
35
- className={`${styles.transportOption} ${
36
- selectedTransport === option.id ? styles.selected : ''
37
- }`}
56
+ className={`${optionClassName} ${selectedTransport === option.id ? selectedOptionClassName : ''}`}
38
57
  style={{
39
58
  backgroundColor: selectedTransport === option.id ? option.color : undefined,
40
59
  }}
41
60
  >
42
- <span className={styles.transportIcon}>{option.icon}</span>
43
- <span className={styles.transportName}>{option.name}</span>
61
+ <span className={iconClassName}>{option.icon}</span>
62
+ <span className={nameClassName}>{option.name}</span>
44
63
  </button>
45
64
  ))}
46
65
  </div>
47
66
  )}
48
67
  </div>
49
-
50
68
  );
51
69
  };
52
70
 
@@ -1,7 +1,28 @@
1
1
  import React, { useState } from 'react';
2
- import styles from './styles.module.css';
3
2
 
4
- const TripType: React.FC = () => {
3
+ interface TripTypeProps {
4
+ className?: string;
5
+ headerClassName?: string;
6
+ titleClassName?: string;
7
+ toggleButtonClassName?: string;
8
+ optionsClassName?: string;
9
+ optionClassName?: string;
10
+ selectedOptionClassName?: string;
11
+ iconClassName?: string;
12
+ nameClassName?: string;
13
+ }
14
+
15
+ const TripType: React.FC<TripTypeProps> = ({
16
+ className,
17
+ headerClassName,
18
+ titleClassName,
19
+ toggleButtonClassName,
20
+ optionsClassName,
21
+ optionClassName,
22
+ selectedOptionClassName,
23
+ iconClassName,
24
+ nameClassName,
25
+ }) => {
5
26
  const [showOptions, setShowOptions] = useState(false);
6
27
  const [selectedTripType, setSelectedTripType] = useState('individuel');
7
28
 
@@ -11,31 +32,29 @@ const TripType: React.FC = () => {
11
32
  ];
12
33
 
13
34
  return (
14
- <div className={styles.optionsSection}>
15
- <div className={styles.optionsHeader}>
16
- <h3 className={styles.optionsTitle}>Type de trajet</h3>
35
+ <div className={className}>
36
+ <div className={headerClassName}>
37
+ <h3 className={titleClassName}>Type de trajet</h3>
17
38
  <button
18
39
  onClick={() => setShowOptions(!showOptions)}
19
- className={styles.toggleButton}
40
+ className={toggleButtonClassName}
20
41
  >
21
42
  {showOptions ? 'Réduire' : 'Options'}
22
43
  </button>
23
44
  </div>
24
45
  {showOptions && (
25
- <div className={styles.transportOptions}>
46
+ <div className={optionsClassName}>
26
47
  {tripTypeOptions.map((option) => (
27
48
  <button
28
49
  key={option.id}
29
50
  onClick={() => setSelectedTripType(option.id)}
30
- className={`${styles.transportOption} ${
31
- selectedTripType === option.id ? styles.selected : ''
32
- }`}
51
+ className={`${optionClassName} ${selectedTripType === option.id ? selectedOptionClassName : ''}`}
33
52
  style={{
34
53
  backgroundColor: selectedTripType === option.id ? option.color : undefined,
35
54
  }}
36
55
  >
37
- <span className={styles.transportIcon}>{option.icon}</span>
38
- <span className={styles.transportName}>{option.name}</span>
56
+ <span className={iconClassName}>{option.icon}</span>
57
+ <span className={nameClassName}>{option.name}</span>
39
58
  </button>
40
59
  ))}
41
60
  </div>