jefflix-website/components/globe/globe-visualization.tsx

98 lines
2.8 KiB
TypeScript

'use client'
import { useRef, useEffect, useState, useCallback } from 'react'
import Globe from 'react-globe.gl'
export interface GlobePoint {
lat: number
lng: number
id: string
label: string
color: string
size?: number
}
interface GlobeVisualizationProps {
points: GlobePoint[]
onPointClick: (point: GlobePoint) => void
height?: number
autoRotate?: boolean
focusLat?: number
focusLng?: number
}
export function GlobeVisualization({
points,
onPointClick,
height = 500,
autoRotate = true,
focusLat,
focusLng,
}: GlobeVisualizationProps) {
const globeRef = useRef<any>(null)
const containerRef = useRef<HTMLDivElement>(null)
const [containerWidth, setContainerWidth] = useState(0)
// Responsive width
useEffect(() => {
if (!containerRef.current) return
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
setContainerWidth(entry.contentRect.width)
}
})
observer.observe(containerRef.current)
setContainerWidth(containerRef.current.clientWidth)
return () => observer.disconnect()
}, [])
// Configure controls after globe is ready
const handleGlobeReady = useCallback(() => {
const globe = globeRef.current
if (!globe) return
const controls = globe.controls()
if (controls) {
controls.autoRotate = autoRotate
controls.autoRotateSpeed = 0.5
controls.enableDamping = true
controls.dampingFactor = 0.1
}
// Set initial view
globe.pointOfView({ lat: focusLat ?? 20, lng: focusLng ?? 0, altitude: 2.5 }, 0)
}, [autoRotate, focusLat, focusLng])
// Focus on specific coordinates when they change
useEffect(() => {
if (globeRef.current && focusLat != null && focusLng != null) {
globeRef.current.pointOfView({ lat: focusLat, lng: focusLng, altitude: 1.5 }, 1000)
}
}, [focusLat, focusLng])
return (
<div ref={containerRef} className="w-full" style={{ height }}>
{containerWidth > 0 && (
<Globe
ref={globeRef}
width={containerWidth}
height={height}
globeImageUrl="/textures/earth-blue-marble.jpg"
backgroundColor="rgba(0,0,0,0)"
showAtmosphere={true}
atmosphereColor="rgba(100,150,255,0.3)"
atmosphereAltitude={0.15}
pointsData={points}
pointLat="lat"
pointLng="lng"
pointColor="color"
pointAltitude={0.01}
pointRadius={(d: any) => Math.max(0.15, Math.min(0.6, (d.size || 1) * 0.12))}
pointLabel={(d: any) => `<div class="text-sm font-medium px-2 py-1 bg-background/90 border border-border rounded shadow-lg">${d.label}</div>`}
onPointClick={(point: any) => onPointClick(point as GlobePoint)}
onGlobeReady={handleGlobeReady}
enablePointerInteraction={true}
/>
)}
</div>
)
}