refactor: rename TopNavigation to TopControl and update related imports in CrimeMap

style: update MapLegend text color for better visibility
This commit is contained in:
vergiLgood1 2025-05-04 05:18:12 +07:00
parent 75df66a621
commit 5d13ff532b
4 changed files with 8 additions and 133 deletions

View File

@ -1,126 +0,0 @@
import { Checkbox } from "../../ui/checkbox";
import { Label } from "../../ui/label";
import { RadioGroup, RadioGroupItem } from "../../ui/radio-group";
import { ControlPosition, IControl, Map } from "mapbox-gl"
import { useControl } from "react-map-gl/mapbox"
import React, { useEffect } from "react"
import { createRoot } from "react-dom/client"
interface MapLayerControlProps {
position?: ControlPosition
isFullscreen?: boolean
}
// React component for layer control content
const LayerControlContent = () => {
return (
<div className="space-y-3">
<div className="text-sm font-medium">Lapisan peta</div>
<div className="flex items-center space-x-2">
<Checkbox id="sambaran-petir" />
<Label htmlFor="sambaran-petir" className="text-xs text-white">
Sambaran petir
</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="jalur-badai" defaultChecked />
<Label htmlFor="jalur-badai" className="text-xs text-white">
Jalur badai guntur
</Label>
</div>
<div className="mt-4">
<div className="text-sm font-medium mb-2">Waktu</div>
<RadioGroup defaultValue="4jam">
<div className="flex items-center space-x-2">
<RadioGroupItem value="4jam" id="4jam" />
<Label htmlFor="4jam" className="text-xs text-white">
4 Jam
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="10hari" id="10hari" />
<Label htmlFor="10hari" className="text-xs text-white">
10 Hari
</Label>
</div>
</RadioGroup>
</div>
</div>
)
}
class LayerControlMapboxControl implements IControl {
private container: HTMLElement;
private map?: Map;
private props: MapLayerControlProps;
private root: ReturnType<typeof createRoot> | null = null;
private isUnmounting: boolean = false;
constructor(props: MapLayerControlProps) {
this.props = props;
this.container = document.createElement("div");
this.container.className = "mapboxgl-ctrl";
}
onAdd(map: Map): HTMLElement {
this.map = map;
this.container.className = 'mapboxgl-ctrl mapboxgl-ctrl-layer absolute bottom-36 left-0 z-20 bg-black/70 text-white p-3 rounded-tr-lg';
this.render();
return this.container;
}
onRemove(): void {
if (this.isUnmounting) return;
this.isUnmounting = true;
requestAnimationFrame(() => {
if (this.root) {
this.root.unmount();
this.root = null;
}
if (this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
this.map = undefined;
this.isUnmounting = false;
});
}
updateProps(props: MapLayerControlProps): void {
this.props = props;
this.render();
}
render(): void {
if (this.props.isFullscreen === false) {
if (this.container.style.display !== 'none') {
this.container.style.display = 'none';
}
return;
} else {
this.container.style.display = 'block';
}
if (!this.root) {
this.root = createRoot(this.container);
}
this.root.render(<LayerControlContent />);
}
}
export function MapLayerControl({ position = 'bottom-left', isFullscreen }: MapLayerControlProps) {
const control = useControl<LayerControlMapboxControl>(
() => new LayerControlMapboxControl({ position, isFullscreen }),
{ position }
);
useEffect(() => {
control.updateProps({ position, isFullscreen });
}, [control, position, isFullscreen]);
return null;
}

View File

@ -9,7 +9,7 @@ interface MapLegendProps {
export default function MapLegend({ position = "bottom-right" }: MapLegendProps) { export default function MapLegend({ position = "bottom-right" }: MapLegendProps) {
return ( return (
// <Overlay position={position}> // <Overlay position={position}>
<div className="flex flex-row text-xs font-semibold font-sans text-white"> <div className="flex flex-row text-xs font-semibold font-sans text-background">
<div className={`flex items-center gap-1.5 py-0 px-8 rounded-l-md border-y border-1 `} style={{ backgroundColor: `${CRIME_RATE_COLORS.low}` }}> <div className={`flex items-center gap-1.5 py-0 px-8 rounded-l-md border-y border-1 `} style={{ backgroundColor: `${CRIME_RATE_COLORS.low}` }}>
<span>Low</span> <span>Low</span>
</div> </div>

View File

@ -28,7 +28,7 @@ import {
} from "lucide-react" } from "lucide-react"
import { ITopTooltipsMapId } from "./map-tooltips" import { ITopTooltipsMapId } from "./map-tooltips"
interface TopNavigationProps { interface TopControlProps {
onControlChange?: (controlId: ITopTooltipsMapId) => void onControlChange?: (controlId: ITopTooltipsMapId) => void
activeControl?: string activeControl?: string
selectedYear: number selectedYear: number
@ -41,7 +41,7 @@ interface TopNavigationProps {
categories?: string[] categories?: string[]
} }
export default function TopNavigation({ export default function TopControl({
onControlChange, onControlChange,
activeControl, activeControl,
selectedYear, selectedYear,
@ -52,7 +52,7 @@ export default function TopNavigation({
setSelectedCategory, setSelectedCategory,
availableYears = [2022, 2023, 2024], availableYears = [2022, 2023, 2024],
categories = [], categories = [],
}: TopNavigationProps) { }: TopControlProps) {
const [showSelectors, setShowSelectors] = useState(false) const [showSelectors, setShowSelectors] = useState(false)
const containerRef = useRef<HTMLDivElement>(null) const containerRef = useRef<HTMLDivElement>(null)
@ -71,7 +71,7 @@ export default function TopNavigation({
{ id: "heatmap" as ITopTooltipsMapId, icon: <Map size={20} />, label: "Crime Heatmap" }, { id: "heatmap" as ITopTooltipsMapId, icon: <Map size={20} />, label: "Crime Heatmap" },
{ id: "trends" as ITopTooltipsMapId, icon: <BarChart2 size={20} />, label: "Crime Trends" }, { id: "trends" as ITopTooltipsMapId, icon: <BarChart2 size={20} />, label: "Crime Trends" },
{ id: "patrol" as ITopTooltipsMapId, icon: <Shield size={20} />, label: "Patrol Areas" }, { id: "patrol" as ITopTooltipsMapId, icon: <Shield size={20} />, label: "Patrol Areas" },
{ id: "clusters" as ITopTooltipsMapId, icon: <Users size={20} />, label: "Offender Clusters" }, { id: "clusters" as ITopTooltipsMapId, icon: <Users size={20} />, label: "Clusters" },
{ id: "timeline" as ITopTooltipsMapId, icon: <Clock size={20} />, label: "Time Analysis" }, { id: "timeline" as ITopTooltipsMapId, icon: <Clock size={20} />, label: "Time Analysis" },
] ]

View File

@ -14,13 +14,14 @@ import MapLegend from "./controls/map-legend"
import { useGetAvailableYears, useGetCrimeCategories, useGetCrimes } from "@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_queries/queries" import { useGetAvailableYears, useGetCrimeCategories, useGetCrimes } from "@/app/(pages)/(admin)/dashboard/crime-management/crime-overview/_queries/queries"
import { ITopTooltipsMapId } from "./controls/map-tooltips" import { ITopTooltipsMapId } from "./controls/map-tooltips"
import MapSelectors from "./controls/map-selector" import MapSelectors from "./controls/map-selector"
import TopNavigation from "./controls/map-navigations"
import CrimeSidebar from "./sidebar/map-sidebar" import CrimeSidebar from "./sidebar/map-sidebar"
import SidebarToggle from "./sidebar/sidebar-toggle" import SidebarToggle from "./sidebar/sidebar-toggle"
import { cn } from "@/app/_lib/utils" import { cn } from "@/app/_lib/utils"
import CrimePopup from "./pop-up/crime-popup" import CrimePopup from "./pop-up/crime-popup"
import { $Enums, crime_categories, crime_incidents, crimes, demographics, districts, geographics, locations } from "@prisma/client" import { $Enums, crime_categories, crime_incidents, crimes, demographics, districts, geographics, locations } from "@prisma/client"
import { CrimeTimelapse } from "./controls/crime-timelapse" import { CrimeTimelapse } from "./controls/crime-timelapse"
import TopControl from "./controls/map-navigations"
// Updated CrimeIncident type to match the structure in crime_incidents // Updated CrimeIncident type to match the structure in crime_incidents
interface CrimeIncident { interface CrimeIncident {
@ -281,7 +282,7 @@ export default function CrimeMap() {
<> <>
<Overlay position="top" className="m-0 bg-transparent shadow-none p-0 border-none"> <Overlay position="top" className="m-0 bg-transparent shadow-none p-0 border-none">
<div className="flex justify-center"> <div className="flex justify-center">
<TopNavigation <TopControl
activeControl={activeControl} activeControl={activeControl}
onControlChange={setActiveControl} onControlChange={setActiveControl}
selectedYear={selectedYear} selectedYear={selectedYear}