"use client" import { useEffect, useRef } from "react" import { useMap } from "react-map-gl/mapbox" import { CRIME_RATE_COLORS } from "@/app/_utils/const/map" import { $Enums } from "@prisma/client" export interface DistrictExtrusionLayerProps { visible?: boolean focusedDistrictId: string | null crimeDataByDistrict: Record tilesetId: string beforeId?: string } export default function DistrictExtrusionLayer({ visible = true, focusedDistrictId, crimeDataByDistrict, tilesetId, beforeId, }: DistrictExtrusionLayerProps) { const { current: map } = useMap() const layersAdded = useRef(false) useEffect(() => { if (!map || !visible) return const onStyleLoad = () => { if (!map) return try { // Make sure the districts source exists if (!map.getMap().getSource("districts")) { map.getMap().addSource("districts", { type: "vector", url: `mapbox://${tilesetId}`, }) } // Get the first symbol layer let firstSymbolId = beforeId if (!firstSymbolId) { const layers = map.getStyle().layers for (const layer of layers) { if (layer.type === "symbol") { firstSymbolId = layer.id break } } } if (!map.getMap().getLayer("district-extrusion")) { map.getMap().addLayer( { id: "district-extrusion", type: "fill-extrusion", source: "districts", "source-layer": "Districts", paint: { "fill-extrusion-color": [ "case", ["has", "kode_kec"], [ "match", ["get", "kode_kec"], focusedDistrictId || "", crimeDataByDistrict[focusedDistrictId || ""]?.level === "low" ? CRIME_RATE_COLORS.low : crimeDataByDistrict[focusedDistrictId || ""]?.level === "medium" ? CRIME_RATE_COLORS.medium : crimeDataByDistrict[focusedDistrictId || ""]?.level === "high" ? CRIME_RATE_COLORS.high : CRIME_RATE_COLORS.default, "transparent", ], "transparent", ], "fill-extrusion-height": [ "case", ["has", "kode_kec"], ["match", ["get", "kode_kec"], focusedDistrictId || "", 500, 0], 0, ], "fill-extrusion-base": 0, "fill-extrusion-opacity": 0.8, }, filter: ["==", ["get", "kode_kec"], focusedDistrictId || ""], }, firstSymbolId, ) layersAdded.current = true } } catch (error) { console.error("Error adding extrusion layer:", error) } } if (map.isStyleLoaded()) { onStyleLoad() } else { map.once("style.load", onStyleLoad) } }, [map, visible, tilesetId, beforeId]) useEffect(() => { if (!map || !layersAdded.current) return try { if (map.getMap().getLayer("district-extrusion")) { map.getMap().setFilter("district-extrusion", ["==", ["get", "kode_kec"], focusedDistrictId || ""]) map.getMap().setPaintProperty("district-extrusion", "fill-extrusion-color", [ "case", ["has", "kode_kec"], [ "match", ["get", "kode_kec"], focusedDistrictId || "", crimeDataByDistrict[focusedDistrictId || ""]?.level === "low" ? CRIME_RATE_COLORS.low : crimeDataByDistrict[focusedDistrictId || ""]?.level === "medium" ? CRIME_RATE_COLORS.medium : crimeDataByDistrict[focusedDistrictId || ""]?.level === "high" ? CRIME_RATE_COLORS.high : CRIME_RATE_COLORS.default, "transparent", ], "transparent", ]) if (focusedDistrictId) { const startHeight = 0 const targetHeight = 800 const duration = 700 const startTime = performance.now() const animateHeight = (currentTime: number) => { const elapsed = currentTime - startTime const progress = Math.min(elapsed / duration, 1) const easedProgress = progress * (2 - progress) const currentHeight = startHeight + (targetHeight - startHeight) * easedProgress map .getMap() .setPaintProperty("district-extrusion", "fill-extrusion-height", [ "case", ["has", "kode_kec"], ["match", ["get", "kode_kec"], focusedDistrictId, currentHeight, 0], 0, ]) if (progress < 1) { requestAnimationFrame(animateHeight) } } requestAnimationFrame(animateHeight) } else { const startHeight = 800 const targetHeight = 0 const duration = 500 const startTime = performance.now() const animateHeightDown = (currentTime: number) => { const elapsed = currentTime - startTime const progress = Math.min(elapsed / duration, 1) const easedProgress = progress * (2 - progress) const currentHeight = startHeight + (targetHeight - startHeight) * easedProgress map .getMap() .setPaintProperty("district-extrusion", "fill-extrusion-height", [ "case", ["has", "kode_kec"], ["match", ["get", "kode_kec"], "", currentHeight, 0], 0, ]) if (progress < 1) { requestAnimationFrame(animateHeightDown) } } requestAnimationFrame(animateHeightDown) } } } catch (error) { console.error("Error updating extrusion layer:", error) } }, [map, focusedDistrictId, crimeDataByDistrict]) return null }