Fix visual issues and add features
- Remove white boxes around system nodes. - Reverse zoom direction. - Reduce region node size. - Implement URL encoding for region names. - Color connections based on node security.
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
|
||||
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { MapNode } from './MapNode';
|
||||
import { Connection } from './Connection';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getSecurityColor } from '../utils/securityColors';
|
||||
|
||||
interface System {
|
||||
interface SolarSystem {
|
||||
solarSystemName: string;
|
||||
x: string;
|
||||
y: string;
|
||||
@@ -20,21 +21,15 @@ interface Position {
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface RegionMapProps {
|
||||
regionName: string;
|
||||
}
|
||||
|
||||
const fetchRegionData = async (regionName: string): Promise<System[]> => {
|
||||
// Decode the region name to handle spaces and special characters
|
||||
const decodedRegionName = decodeURIComponent(regionName);
|
||||
const response = await fetch(`/${decodedRegionName}.json`);
|
||||
const fetchRegionData = async (regionName: string): Promise<SolarSystem[]> => {
|
||||
const response = await fetch(`/${regionName}.json`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch ${decodedRegionName} data`);
|
||||
throw new Error('Failed to fetch region data');
|
||||
}
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
|
||||
const navigate = useNavigate();
|
||||
const [viewBox, setViewBox] = useState({ x: 0, y: 0, width: 1200, height: 800 });
|
||||
const [isPanning, setIsPanning] = useState(false);
|
||||
@@ -42,8 +37,6 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
const [nodePositions, setNodePositions] = useState<Record<string, Position>>({});
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
|
||||
const decodedRegionName = decodeURIComponent(regionName);
|
||||
|
||||
const { data: systems, isLoading, error } = useQuery({
|
||||
queryKey: ['region', regionName],
|
||||
queryFn: () => fetchRegionData(regionName),
|
||||
@@ -64,13 +57,8 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
}, [systems]);
|
||||
|
||||
const handleSystemClick = (systemName: string) => {
|
||||
const encodedSystem = encodeURIComponent(systemName);
|
||||
navigate(`/systems/${encodedSystem}`);
|
||||
};
|
||||
|
||||
const handleRegionClick = (regionName: string) => {
|
||||
const encodedRegion = encodeURIComponent(regionName);
|
||||
navigate(`/regions/${encodedRegion}`);
|
||||
console.log(`Clicked system: ${systemName}`);
|
||||
// Future: Navigate to system details
|
||||
};
|
||||
|
||||
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
||||
@@ -116,6 +104,7 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
const mouseX = e.clientX - rect.left;
|
||||
const mouseY = e.clientY - rect.top;
|
||||
|
||||
// Reverse scroll direction: positive deltaY zooms out, negative zooms in
|
||||
const scale = e.deltaY > 0 ? 1.1 : 0.9;
|
||||
const newWidth = viewBox.width * scale;
|
||||
const newHeight = viewBox.height * scale;
|
||||
@@ -124,7 +113,7 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
const mouseYInSVG = viewBox.y + (mouseY / rect.height) * viewBox.height;
|
||||
|
||||
const newX = mouseXInSVG - (mouseX / rect.width) * newWidth;
|
||||
const newY = mouseYInSVG - (mouseY / rect.height) * newHeight;
|
||||
const newY = mouseYInSVG - (mouseY / rect.height) * newWidth;
|
||||
|
||||
setViewBox({
|
||||
x: newX,
|
||||
@@ -134,58 +123,52 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
});
|
||||
}, [viewBox]);
|
||||
|
||||
// Get system names that exist in current region
|
||||
const systemNamesInRegion = new Set(systems?.map(s => s.solarSystemName) || []);
|
||||
|
||||
// Find cross-region connections and extract region names
|
||||
const crossRegionConnections = new Set<string>();
|
||||
systems?.forEach(system => {
|
||||
system.connectedSystems?.forEach(connectedSystem => {
|
||||
if (!systemNamesInRegion.has(connectedSystem)) {
|
||||
// This is a cross-region connection - we'll need to determine the region
|
||||
// For now, we'll just store the system name and handle it in rendering
|
||||
crossRegionConnections.add(connectedSystem);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="w-full h-screen bg-gradient-to-br from-slate-900 via-cyan-900 to-slate-900 flex items-center justify-center">
|
||||
<div className="text-white text-xl">Loading {decodedRegionName} data...</div>
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center">
|
||||
<div className="text-white text-xl">Loading {regionName} data...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="w-full h-screen bg-gradient-to-br from-slate-900 via-cyan-900 to-slate-900 flex items-center justify-center">
|
||||
<div className="text-red-400 text-xl">Error loading {decodedRegionName} data</div>
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold text-white mb-4">Error Loading Region</h1>
|
||||
<p className="text-red-400 mb-6">Failed to load data for {regionName}</p>
|
||||
<Button
|
||||
onClick={() => navigate('/')}
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Return to Galaxy Map
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-screen bg-gradient-to-br from-slate-900 via-cyan-900 to-slate-900 overflow-hidden relative">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-cyan-900/20 via-slate-900/40 to-black"></div>
|
||||
<div className="w-full h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 overflow-hidden relative">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-purple-900/20 via-slate-900/40 to-black"></div>
|
||||
|
||||
<div className="relative z-10 p-8">
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold text-white mb-2">{regionName}</h1>
|
||||
<p className="text-purple-200">Solar systems in this region</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate('/')}
|
||||
className="bg-black/20 border-cyan-500/30 text-cyan-200 hover:bg-cyan-500/20"
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Galaxy
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold text-white">{decodedRegionName} Region</h1>
|
||||
<p className="text-cyan-200">Explore the systems within this region</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h-[calc(100vh-200px)] border border-cyan-500/30 rounded-lg overflow-hidden bg-black/20 backdrop-blur-sm">
|
||||
<div className="w-full h-[calc(100vh-200px)] border border-purple-500/30 rounded-lg overflow-hidden bg-black/20 backdrop-blur-sm">
|
||||
<svg
|
||||
ref={svgRef}
|
||||
width="100%"
|
||||
@@ -212,15 +195,23 @@ export const RegionMap: React.FC<RegionMapProps> = ({ regionName }) => {
|
||||
{systems?.map((system) =>
|
||||
system.connectedSystems?.map((connectedSystem) => {
|
||||
const fromPos = nodePositions[system.solarSystemName];
|
||||
// Only render connection if target system is in current region
|
||||
const toPos = nodePositions[connectedSystem];
|
||||
|
||||
// If connected system is not in current region, skip the connection
|
||||
if (!fromPos || !toPos) return null;
|
||||
|
||||
// Get colors for both systems
|
||||
const fromColor = getSecurityColor(system.security);
|
||||
const toSystem = systems.find(s => s.solarSystemName === connectedSystem);
|
||||
const toColor = toSystem ? getSecurityColor(toSystem.security) : fromColor;
|
||||
|
||||
return (
|
||||
<Connection
|
||||
key={`${system.solarSystemName}-${connectedSystem}`}
|
||||
from={fromPos}
|
||||
to={toPos}
|
||||
fromColor={fromColor}
|
||||
toColor={toColor}
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user