|
1 | | -import { useState, useEffect } from 'react'; |
| 1 | +import { useState, useEffect, memo } from 'react'; |
2 | 2 | import { MapContainer, TileLayer, Marker, Circle, useMap, useMapEvents } from 'react-leaflet'; |
3 | 3 | import L from 'leaflet'; |
4 | 4 | import { |
|
8 | 8 | RefreshCw, |
9 | 9 | Settings2, |
10 | 10 | LocateFixed, |
11 | | - Info |
| 11 | + Info, |
| 12 | + X |
12 | 13 | } from 'lucide-react'; |
13 | 14 | import { motion, AnimatePresence } from 'motion/react'; |
14 | 15 | import { fetchQuantumNumbers, calculateAttractor, AttractorResult } from './services/quantumService'; |
@@ -51,6 +52,20 @@ function MapClickHandler({ onMapClick }: { onMapClick: (lat: number, lon: number |
51 | 52 | return null; |
52 | 53 | } |
53 | 54 |
|
| 55 | +const RadiusSlider = memo(function RadiusSlider({ radius, setRadius }: { radius: number, setRadius: (val: number) => void }) { |
| 56 | + return ( |
| 57 | + <input |
| 58 | + type="range" |
| 59 | + min="500" |
| 60 | + max="5000" |
| 61 | + step="100" |
| 62 | + value={radius} |
| 63 | + onChange={(e) => setRadius(parseInt(e.target.value))} |
| 64 | + className="w-full h-2 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-purple-500 mb-6" |
| 65 | + /> |
| 66 | + ); |
| 67 | +}); |
| 68 | + |
54 | 69 | export default function App() { |
55 | 70 | const [startPos, setStartPos] = useState<[number, number]>([48.8566, 2.3522]); // Default Paris |
56 | 71 | const [radius, setRadius] = useState(1000); |
@@ -112,7 +127,6 @@ export default function App() { |
112 | 127 |
|
113 | 128 | const handleMapClick = (lat: number, lon: number) => { |
114 | 129 | setStartPos([lat, lon]); |
115 | | - setAttractor(null); |
116 | 130 | }; |
117 | 131 |
|
118 | 132 | return ( |
@@ -190,8 +204,16 @@ export default function App() { |
190 | 204 | <span className="text-xs font-bold uppercase tracking-widest text-purple-400">Destination Found</span> |
191 | 205 | <h3 className="text-lg font-bold text-white">Quantum Attractor</h3> |
192 | 206 | </div> |
193 | | - <div className="bg-purple-500/20 px-2 py-1 rounded text-[10px] font-mono text-purple-300 border border-purple-500/30"> |
194 | | - Score: {attractor.densityScore.toFixed(2)} |
| 207 | + <div className="flex items-center gap-2"> |
| 208 | + <div className="bg-purple-500/20 px-2 py-1 rounded text-[10px] font-mono text-purple-300 border border-purple-500/30"> |
| 209 | + Score: {attractor.densityScore.toFixed(2)} |
| 210 | + </div> |
| 211 | + <button |
| 212 | + onClick={() => setAttractor(null)} |
| 213 | + className="w-7 h-7 flex items-center justify-center bg-black/20 hover:bg-black/40 rounded-full text-zinc-400 hover:text-white transition-colors border border-white/5" |
| 214 | + > |
| 215 | + <X className="w-4 h-4" /> |
| 216 | + </button> |
195 | 217 | </div> |
196 | 218 | </div> |
197 | 219 |
|
@@ -229,15 +251,7 @@ export default function App() { |
229 | 251 | <span className="text-sm font-bold text-purple-400">{radius}m</span> |
230 | 252 | </div> |
231 | 253 |
|
232 | | - <input |
233 | | - type="range" |
234 | | - min="500" |
235 | | - max="5000" |
236 | | - step="100" |
237 | | - value={radius} |
238 | | - onChange={(e) => setRadius(parseInt(e.target.value))} |
239 | | - className="w-full h-2 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-purple-500 mb-6" |
240 | | - /> |
| 254 | + <RadiusSlider radius={radius} setRadius={setRadius} /> |
241 | 255 |
|
242 | 256 | <button |
243 | 257 | onClick={handleGenerate} |
|
0 commit comments