MIF_E31221222/sigap-website/app/_components/map/map.tsx

178 lines
5.6 KiB
TypeScript

"use client"
import type React from "react"
import { useState, useCallback, useRef } from "react"
import ReactMapGL, {
type ViewState,
NavigationControl,
ScaleControl,
type MapRef,
FullscreenControl,
GeolocateControl,
} from "react-map-gl/mapbox"
import { BASE_LATITUDE, BASE_LONGITUDE, BASE_ZOOM, MAP_STYLE, MAPBOX_STYLES, MapboxStyle } from "@/app/_utils/const/map"
import { Search } from "lucide-react"
import "mapbox-gl/dist/mapbox-gl.css"
import MapSidebar from "./controls/map-sidebar"
import SidebarToggle from "./controls/map-toggle"
import TimeControls from "./controls/time-control"
import SeverityIndicator from "./controls/severity-indicator"
import MapFilterControl from "./controls/map-filter-control"
import { useFullscreen } from "@/app/_hooks/use-fullscreen"
import MapControls from "./controls/map-control"
interface MapViewProps {
children?: React.ReactNode
initialViewState?: Partial<ViewState>
mapStyle?: MapboxStyle
className?: string
width?: string | number
height?: string | number
mapboxApiAccessToken?: string
onMoveEnd?: (viewState: ViewState) => void
customControls?: React.ReactNode
crimes?: Array<{
id: string
district_name: string
district_id?: string
number_of_crime?: number
level?: "low" | "medium" | "high" | "critical"
incidents: any[]
}>
selectedYear?: number | string
selectedMonth?: number | string
availableYears?: (number | null)[]
yearsLoading?: boolean
onYearChange?: (year: number) => void
onMonthChange?: (month: number | "all") => void
onApplyFilters?: () => void
onResetFilters?: () => void
}
export default function MapView({
children,
initialViewState,
mapStyle = MAPBOX_STYLES.Standard,
className = "w-full h-96",
width = "100%",
height = "100%",
mapboxApiAccessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN,
onMoveEnd,
customControls,
crimes = [],
selectedYear,
selectedMonth,
availableYears = [],
yearsLoading = false,
onYearChange = () => { },
onMonthChange = () => { },
onApplyFilters = () => { },
onResetFilters = () => { },
}: MapViewProps) {
const [mapRef, setMapRef] = useState<MapRef | null>(null)
const [activeControl, setActiveControl] = useState<string>("crime-rate")
const [activeTime, setActiveTime] = useState<string>("today")
const [sidebarOpen, setSidebarOpen] = useState<boolean>(false)
const mapContainerRef = useRef<HTMLDivElement>(null)
// Use the custom fullscreen hook instead of manual event listeners
const { isFullscreen } = useFullscreen(mapContainerRef)
const defaultViewState: Partial<ViewState> = {
longitude: BASE_LONGITUDE,
latitude: BASE_LATITUDE,
zoom: BASE_ZOOM,
bearing: 0,
pitch: 0,
...initialViewState,
}
const handleMapLoad = useCallback((event: any) => {
setMapRef(event.target)
}, [])
const handleMoveEnd = useCallback(
(event: any) => {
if (onMoveEnd) {
onMoveEnd(event.viewState)
}
},
[onMoveEnd],
)
const handleControlChange = (control: string) => {
setActiveControl(control)
}
const handleTimeChange = (time: string) => {
setActiveTime(time)
}
const toggleSidebar = () => {
setSidebarOpen(!sidebarOpen)
}
return (
<div ref={mapContainerRef} className={`relative ${className}`}>
{/* Main content with left padding when sidebar is open */}
<div className={`relative h-full transition-all duration-300 ${isFullscreen && sidebarOpen ? "ml-80" : "ml-0"}`}>
<ReactMapGL
ref={(ref) => setMapRef(ref)}
mapStyle={mapStyle}
mapboxAccessToken={mapboxApiAccessToken}
initialViewState={defaultViewState}
onLoad={handleMapLoad}
onMoveEnd={handleMoveEnd}
interactiveLayerIds={["district-fill", "clusters", "unclustered-point"]}
attributionControl={false}
style={{ width: "100%", height: "100%" }}
>
{children}
<FullscreenControl position="top-right" />
<NavigationControl position="top-right" />
{/* Custom controls that depend on fullscreen state */}
{isFullscreen && <GeolocateControl position="top-right" />}
{/* Custom Filter Control with isFullscreen prop */}
{/* {isFullscreen && <MapFilterControl
position="top-right"
selectedYear={Number(selectedYear) || 2024}
selectedMonth={selectedMonth === "all" ? "all" : Number(selectedMonth) || "all"}
availableYears={availableYears}
yearsLoading={yearsLoading}
onYearChange={onYearChange}
onMonthChange={onMonthChange}
onApplyFilters={onApplyFilters}
onResetFilters={onResetFilters}
isFullscreen={isFullscreen}
/>
} */}
{/* Sidebar and other controls only in fullscreen */}
{isFullscreen && (
<>
<MapSidebar
isOpen={sidebarOpen}
onToggle={toggleSidebar}
crimes={crimes}
selectedYear={selectedYear}
selectedMonth={selectedMonth}
/>
<SidebarToggle isOpen={sidebarOpen} onToggle={toggleSidebar} />
<MapControls onControlChange={handleControlChange} activeControl={activeControl} />
<TimeControls onTimeChange={handleTimeChange} activeTime={activeTime} />
<SeverityIndicator />
</>
)}
</ReactMapGL>
</div>
{/* Debug indicator - uncomment for debugging
<div className="absolute bottom-4 left-4 bg-black bg-opacity-70 text-white text-xs p-1 rounded z-50">
Fullscreen: {isFullscreen ? "Yes" : "No"}
</div> */}
</div>
)
}