feat: add per-country coloring and individual region clicks to continent selector
Enhance the continent selector with colorful individual regions: Per-Country Coloring: - Each country/region now has its own distinct color using the game's color algorithm - Same 8-color deterministic palette as the main game - Unselected continents shown at 20% opacity for subtle visibility - Selected continent regions shown at full color intensity - Hovered regions get enhanced borders and highlighting Individual Region Interaction: - Click any individual country to select its continent - Hover over any country highlights that specific region - Smooth transitions between states (0.15s) - Thicker stroke on hover (1.5px) for clear feedback - Visual feedback shows exactly which country you're pointing at Benefits: - Much more visually appealing and educational - Users can see individual countries within each continent - Clearer geographic boundaries and relationships - More engaging interaction model - Same visual language as the main game map 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
07e92240e8
commit
2e9f409f26
|
|
@ -5,6 +5,7 @@ import { css } from '@styled/css'
|
||||||
import { useTheme } from '@/contexts/ThemeContext'
|
import { useTheme } from '@/contexts/ThemeContext'
|
||||||
import { WORLD_MAP } from '../maps'
|
import { WORLD_MAP } from '../maps'
|
||||||
import { getContinentForCountry, CONTINENTS, type ContinentId } from '../continents'
|
import { getContinentForCountry, CONTINENTS, type ContinentId } from '../continents'
|
||||||
|
import { getRegionColor } from '../mapColors'
|
||||||
|
|
||||||
interface ContinentSelectorProps {
|
interface ContinentSelectorProps {
|
||||||
selectedContinent: ContinentId | 'all'
|
selectedContinent: ContinentId | 'all'
|
||||||
|
|
@ -18,6 +19,7 @@ export function ContinentSelector({
|
||||||
const { resolvedTheme } = useTheme()
|
const { resolvedTheme } = useTheme()
|
||||||
const isDark = resolvedTheme === 'dark'
|
const isDark = resolvedTheme === 'dark'
|
||||||
const [hoveredContinent, setHoveredContinent] = useState<ContinentId | 'all' | null>(null)
|
const [hoveredContinent, setHoveredContinent] = useState<ContinentId | 'all' | null>(null)
|
||||||
|
const [hoveredRegion, setHoveredRegion] = useState<string | null>(null)
|
||||||
|
|
||||||
// Group regions by continent
|
// Group regions by continent
|
||||||
const regionsByContinent = new Map<ContinentId | 'all', typeof WORLD_MAP.regions>()
|
const regionsByContinent = new Map<ContinentId | 'all', typeof WORLD_MAP.regions>()
|
||||||
|
|
@ -34,23 +36,29 @@ export function ContinentSelector({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get color for continent based on state
|
// Get color for a region based on its continent's state
|
||||||
const getContinentColor = (continentId: ContinentId | 'all'): string => {
|
const getRegionColorForSelector = (
|
||||||
|
regionId: string,
|
||||||
|
continentId: ContinentId | 'all'
|
||||||
|
): string => {
|
||||||
const isSelected = selectedContinent === continentId
|
const isSelected = selectedContinent === continentId
|
||||||
const isHovered = hoveredContinent === continentId
|
const isHovered = hoveredContinent === continentId || hoveredRegion === regionId
|
||||||
|
|
||||||
if (isSelected) {
|
// Use the game's color algorithm, but adjust opacity based on selection state
|
||||||
return isDark ? '#3b82f6' : '#2563eb' // Solid blue for selected
|
const baseColor = getRegionColor(regionId, isSelected, isHovered, isDark)
|
||||||
|
|
||||||
|
// If this continent is not selected and not hovered, make it more transparent
|
||||||
|
if (!isSelected && !isHovered) {
|
||||||
|
// Extract the color and add low opacity
|
||||||
|
return baseColor.includes('#') ? `${baseColor}33` : baseColor // 20% opacity
|
||||||
}
|
}
|
||||||
if (isHovered) {
|
|
||||||
return isDark ? '#60a5fa66' : '#3b82f655' // Semi-transparent blue for hover
|
return baseColor
|
||||||
}
|
|
||||||
return isDark ? '#4b556333' : '#d1d5db44' // Very light for unselected
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getContinentStroke = (continentId: ContinentId | 'all'): string => {
|
const getRegionStroke = (continentId: ContinentId | 'all', regionId: string): string => {
|
||||||
const isSelected = selectedContinent === continentId
|
const isSelected = selectedContinent === continentId
|
||||||
const isHovered = hoveredContinent === continentId
|
const isHovered = hoveredContinent === continentId || hoveredRegion === regionId
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return isDark ? '#60a5fa' : '#1d4ed8'
|
return isDark ? '#60a5fa' : '#1d4ed8'
|
||||||
|
|
@ -61,6 +69,15 @@ export function ContinentSelector({
|
||||||
return isDark ? '#374151' : '#9ca3af'
|
return isDark ? '#374151' : '#9ca3af'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getRegionStrokeWidth = (continentId: ContinentId | 'all', regionId: string): number => {
|
||||||
|
const isSelected = selectedContinent === continentId
|
||||||
|
const isHovered = hoveredContinent === continentId || hoveredRegion === regionId
|
||||||
|
|
||||||
|
if (isHovered) return 1.5
|
||||||
|
if (isSelected) return 1
|
||||||
|
return 0.3
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-component="continent-selector">
|
<div data-component="continent-selector">
|
||||||
<div
|
<div
|
||||||
|
|
@ -110,28 +127,27 @@ export function ContinentSelector({
|
||||||
if (regions.length === 0) return null
|
if (regions.length === 0) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<g key={continent.id} data-continent={continent.id}>
|
||||||
key={continent.id}
|
{/* All regions in this continent - each individually clickable */}
|
||||||
data-continent={continent.id}
|
|
||||||
onMouseEnter={() => setHoveredContinent(continent.id)}
|
|
||||||
onMouseLeave={() => setHoveredContinent(null)}
|
|
||||||
onClick={() => onSelectContinent(continent.id)}
|
|
||||||
style={{
|
|
||||||
cursor: 'pointer',
|
|
||||||
transition: 'all 0.2s ease',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* All regions in this continent */}
|
|
||||||
{regions.map((region) => (
|
{regions.map((region) => (
|
||||||
<path
|
<path
|
||||||
key={region.id}
|
key={region.id}
|
||||||
d={region.path}
|
d={region.path}
|
||||||
fill={getContinentColor(continent.id)}
|
fill={getRegionColorForSelector(region.id, continent.id)}
|
||||||
stroke={getContinentStroke(continent.id)}
|
stroke={getRegionStroke(continent.id, region.id)}
|
||||||
strokeWidth={selectedContinent === continent.id ? 1 : 0.5}
|
strokeWidth={getRegionStrokeWidth(continent.id, region.id)}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
setHoveredContinent(continent.id)
|
||||||
|
setHoveredRegion(region.id)
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
setHoveredContinent(null)
|
||||||
|
setHoveredRegion(null)
|
||||||
|
}}
|
||||||
|
onClick={() => onSelectContinent(continent.id)}
|
||||||
style={{
|
style={{
|
||||||
pointerEvents: 'none',
|
cursor: 'pointer',
|
||||||
transition: 'all 0.2s ease',
|
transition: 'all 0.15s ease',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue