MIF_E31221222/sigap-website/app/_components/map/controls/year-selector.tsx

147 lines
4.8 KiB
TypeScript

"use client";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/app/_components/ui/select";
import { createRoot } from "react-dom/client";
import { useEffect, useRef, useState } from "react";
import { Skeleton } from "../../ui/skeleton";
interface YearSelectorProps {
availableYears?: (number | null)[];
selectedYear: number | "all";
onYearChange: (year: number | "all") => void;
includeAllOption?: boolean;
isLoading?: boolean;
className?: string;
disabled?: boolean;
}
// React component for the year selector UI
function YearSelectorUI({
availableYears = [],
selectedYear,
onYearChange,
includeAllOption = true,
isLoading = false,
className = "w-[120px]",
disabled = false,
}: YearSelectorProps) {
const containerRef = useRef<HTMLDivElement>(null);
const [isClient, setIsClient] = useState(false);
useEffect(() => {
// This will ensure that the document is only used in the client-side context
setIsClient(true);
}, []);
// Conditionally access the document only when running on the client
const container = isClient ? document.getElementById("root") : null;
return (
<div ref={containerRef} className="mapboxgl-month-selector">
{isLoading
? (
<div className="flex items-center justify-center h-8">
<Skeleton className="h-full w-full rounded-md" />
</div>
)
: (
<Select
value={selectedYear.toString()}
onValueChange={(value) =>
onYearChange(
value === "all" ? "all" : Number(value),
)}
disabled={disabled}
>
<SelectTrigger
className={`${className} ${
disabled
? "opacity-60 cursor-not-allowed bg-muted"
: ""
}`}
>
<SelectValue placeholder="Month" />
</SelectTrigger>
<SelectContent
container={containerRef.current || container ||
undefined}
style={{ zIndex: 2000 }}
className={`${className}`}
>
{includeAllOption && (
<SelectItem value="all">All Years</SelectItem>
)}
{availableYears.map((year) => (
year !== null && (
<SelectItem
key={year}
value={year.toString()}
>
{year}
</SelectItem>
)
))}
</SelectContent>
</Select>
)}
</div>
);
}
// Mapbox GL control class implementation
export class YearSelectorControl {
private _map: any;
private _container!: HTMLElement;
private _root: any;
private props: YearSelectorProps;
constructor(props: YearSelectorProps) {
this.props = props;
}
onAdd(map: any) {
this._map = map;
this._container = document.createElement("div");
this._container.className = "mapboxgl-ctrl mapboxgl-ctrl-group";
this._container.style.padding = "5px";
// Set position to relative to keep dropdown content in context
this._container.style.position = "relative";
// Higher z-index to ensure dropdown appears above map elements
this._container.style.zIndex = "50";
// Create React root for rendering our component
this._root = createRoot(this._container);
this._root.render(<YearSelectorUI {...this.props} />);
return this._container;
}
onRemove() {
if (this._container && this._container.parentNode) {
this._container.parentNode.removeChild(this._container);
}
// Unmount React component properly
if (this._root) {
this._root.unmount();
}
this._map = undefined;
}
}
// Export original React component as default for backward compatibility
export default function YearSelector(props: YearSelectorProps) {
// This wrapper allows the component to be used both as a React component
// and to help create a MapboxGL control
return <YearSelectorUI {...props} />;
}