"use client"; import type { Map as MapLibreMap, MapLayerMouseEvent } from "maplibre-gl"; import type { RefObject } from "react"; import { useEffect } from "react"; import type { DetailFeature } from "../types"; import { getFeatureId, getWaterNetworkSourceId, toDetailFeature } from "../map/feature-adapter"; import { INTERACTIVE_HIT_LAYER_IDS } from "../map/layers"; import type { WaterNetworkSourceId } from "../map/sources"; const HOVER_LAYER_IDS: Record = { conduits: ["pipes-hover-outline", "pipes-hover"], junctions: ["junctions-hover"], orifices: ["orifices-hover-outline", "orifices-hover"], pumps: ["pumps-hover-outline", "pumps-hover"], outfalls: ["outfalls-hover"] }; const SELECTED_LAYER_IDS: Record = { conduits: ["pipes-selected-halo", "pipes-selected-outline", "pipes-selected"], junctions: ["junctions-selected-halo", "junctions-selected"], orifices: ["orifices-selected-halo", "orifices-selected-outline", "orifices-selected"], pumps: ["pumps-selected-halo", "pumps-selected-outline", "pumps-selected"], outfalls: ["outfalls-selected-halo", "outfalls-selected"] }; type UseMapInteractionsOptions = { mapRef: RefObject; mapReady: boolean; onSelectFeature: (feature: DetailFeature) => void; selectedFeature: DetailFeature | null; }; export function useMapInteractions({ mapRef, mapReady, onSelectFeature, selectedFeature }: UseMapInteractionsOptions) { useEffect(() => { const map = mapRef.current; if (!mapReady || !map) { return; } const activeMap = map; let hoveredFeature: { id: string; sourceId: WaterNetworkSourceId } | null = null; function clearHoveredFeature() { if (!hoveredFeature) { return; } HOVER_LAYER_IDS[hoveredFeature.sourceId].forEach((layerId) => { activeMap.setFilter(layerId, ["==", ["get", "id"], ""]); }); hoveredFeature = null; } function handleMouseMove(event: MapLayerMouseEvent) { const feature = event.features?.[0]; if (!feature) { return; } activeMap.getCanvas().style.cursor = "pointer"; const id = getFeatureId(feature); const sourceId = getWaterNetworkSourceId(feature); if (!id || !sourceId) { clearHoveredFeature(); return; } if (hoveredFeature?.id === id && hoveredFeature.sourceId === sourceId) { return; } clearHoveredFeature(); HOVER_LAYER_IDS[sourceId].forEach((layerId) => { activeMap.setFilter(layerId, ["==", ["get", "id"], id]); }); hoveredFeature = { id, sourceId }; } function handleMouseLeave() { activeMap.getCanvas().style.cursor = ""; clearHoveredFeature(); } function handleClick(event: MapLayerMouseEvent) { const feature = event.features?.[0]; if (feature) { onSelectFeature(toDetailFeature(feature)); } } activeMap.on("mousemove", INTERACTIVE_HIT_LAYER_IDS, handleMouseMove); activeMap.on("mouseleave", INTERACTIVE_HIT_LAYER_IDS, handleMouseLeave); activeMap.on("click", INTERACTIVE_HIT_LAYER_IDS, handleClick); return () => { activeMap.off("mousemove", INTERACTIVE_HIT_LAYER_IDS, handleMouseMove); activeMap.off("mouseleave", INTERACTIVE_HIT_LAYER_IDS, handleMouseLeave); activeMap.off("click", INTERACTIVE_HIT_LAYER_IDS, handleClick); }; }, [mapRef, mapReady, onSelectFeature]); useEffect(() => { const map = mapRef.current; if (!mapReady || !map) { return; } Object.values(SELECTED_LAYER_IDS).flat().forEach((layerId) => { map.setFilter(layerId, ["==", ["get", "id"], ""]); }); if (!selectedFeature?.id) { return; } SELECTED_LAYER_IDS[selectedFeature.layer].forEach((layerId) => { map.setFilter(layerId, ["==", ["get", "id"], selectedFeature.id]); }); }, [mapReady, mapRef, selectedFeature]); }