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

122 lines
4.1 KiB
TypeScript

"use client"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/app/_components/ui/select"
import { createRoot } from "react-dom/client"
import { useRef, useEffect, useState } from "react"
interface YearSelectorProps {
availableYears?: (number | null)[]
selectedYear: number
onYearChange: (year: number) => void
isLoading?: boolean
className?: string
}
interface YearSelectorProps {
availableYears?: (number | null)[];
selectedYear: number;
onYearChange: (year: number) => void;
isLoading?: boolean;
className?: string;
}
// React component for the year selector UI
function YearSelectorUI({
availableYears = [],
selectedYear,
onYearChange,
isLoading = false,
className = "w-[120px]"
}: 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-year-selector">
{isLoading ? (
<div className={`${className} h-9 rounded-md bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 animate-pulse`} />
) : (
<Select
value={selectedYear.toString()}
onValueChange={(value) => onYearChange(Number(value))}
disabled={isLoading}
>
<SelectTrigger className={className}>
<SelectValue placeholder="Year" />
</SelectTrigger>
{/* Ensure that the dropdown content renders correctly only on the client side */}
<SelectContent
container={containerRef.current || container || undefined}
style={{ zIndex: 2000 }}
>
{availableYears
?.filter((year) => year !== null)
.map((year) => (
<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} />;
}