'use client' import { useState, useCallback, useMemo, useEffect, memo, type KeyboardEvent } from 'react' import { useMapLayers } from '../hooks/useMapLayers' import type { LayerSwitcherProps, LayerConfig } from '../types' const positionStyles: Record = { 'top-left': { top: 10, left: 10 }, 'top-right': { top: 10, right: 10 }, 'bottom-left': { bottom: 10, left: 10 }, 'bottom-right': { bottom: 10, right: 10 }, } function LayerSwitcherComponent({ layers, position = 'top-right', title = 'Layers', collapsible = true, defaultCollapsed = false, showToggleAll = false, className = '', style, onChange, }: LayerSwitcherProps) { const { setLayerVisibility } = useMapLayers() const [collapsed, setCollapsed] = useState(defaultCollapsed) const initialVisibility = useMemo(() => { const initial: Record = {} layers.forEach((layer) => { initial[layer.id] = layer.defaultVisible !== false }) return initial }, [layers]) const [visibility, setVisibility] = useState>(initialVisibility) // Keep visibility in sync when the `layers` prop changes: add entries for // new layers, drop stale ones, preserve user toggles for existing layers. useEffect(() => { setVisibility((prev) => { const next: Record = {} for (const layer of layers) { next[layer.id] = layer.id in prev ? prev[layer.id] : layer.defaultVisible !== false } return next }) }, [layers]) const handleToggle = useCallback( (layerId: string) => { setVisibility((prev) => { const newVisible = !prev[layerId] setLayerVisibility(layerId, newVisible) onChange?.(layerId, newVisible) return { ...prev, [layerId]: newVisible } }) }, [setLayerVisibility, onChange] ) const handleToggleAll = useCallback( (visible: boolean) => { const newVisibility: Record = {} layers.forEach((layer) => { newVisibility[layer.id] = visible setLayerVisibility(layer.id, visible) onChange?.(layer.id, visible) }) setVisibility(newVisibility) }, [layers, setLayerVisibility, onChange] ) const allVisible = useMemo(() => Object.values(visibility).every(Boolean), [visibility]) const noneVisible = useMemo(() => Object.values(visibility).every((v) => !v), [visibility]) const containerStyle = useMemo( () => ({ position: 'absolute', ...positionStyles[position], backgroundColor: 'var(--color-popover)', color: 'var(--color-popover-foreground)', border: '1px solid var(--color-border)', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.15)', padding: collapsed ? 8 : 12, minWidth: collapsed ? 'auto' : 150, zIndex: 1, ...style, }), [position, collapsed, style] ) const headerStyle = useMemo( () => ({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, cursor: collapsible ? 'pointer' : 'default', fontWeight: 600, fontSize: 12, color: 'var(--color-foreground)', marginBottom: collapsed ? 0 : 8, }), [collapsible, collapsed] ) const itemStyle = useMemo( () => ({ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0', fontSize: 12, color: 'var(--color-muted-foreground)', cursor: 'pointer', }), [] ) const checkboxStyle = useMemo( () => ({ width: 14, height: 14, cursor: 'pointer', }), [] ) const groups = useMemo(() => { return layers.reduce( (acc, layer) => { const group = layer.group || '' if (!acc[group]) acc[group] = [] acc[group].push(layer) return acc }, {} as Record ) }, [layers]) const handleHeaderClick = collapsible ? () => setCollapsed((c) => !c) : undefined return (
{ if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() setCollapsed((c) => !c) } }, } : {})} > {title} {collapsible && ( )}
{!collapsed && (
{showToggleAll && (
)} {Object.entries(groups).map(([group, groupLayers]) => (
{group && (
{group}
)} {groupLayers.map((layer) => ( ))}
))}
)}
) } export const LayerSwitcher = memo(LayerSwitcherComponent)