diff --git a/src/components/BaselayerSections.tsx b/src/components/BaselayerSections.tsx index aa1d52d..f1929bd 100644 --- a/src/components/BaselayerSections.tsx +++ b/src/components/BaselayerSections.tsx @@ -1,109 +1,129 @@ -import { LayerSelectorProps } from './LayerSelector'; -import { CollapsibleSection } from './CollapsibleSection'; -import { EXTERNAL_BASELAYERS } from '../configs/mapSettings'; +import { useState, ReactNode, useCallback, memo } from 'react'; +import { LayerSelectorProps, NoMatches } from './LayerSelector'; +import CollapsibleSection from './CollapsibleSection'; +import { + EXTERNAL_BASELAYERS, + EXTERNAL_DETAILS_ID, +} from '../configs/mapSettings'; +import { getDefaultExpandedState, filterMapGroups } from '../utils/filterUtils'; +import { ChevronRightIcon } from './icons/ChevronRightIcon'; +import { ChevronDownIcon } from './icons/ChevronDownIcon'; type BaselayerSectionsProps = { mapGroups: LayerSelectorProps['mapGroups']; activeBaselayerId: LayerSelectorProps['activeBaselayerId']; isFlipped: LayerSelectorProps['isFlipped']; onBaselayerChange: LayerSelectorProps['onBaselayerChange']; + searchText: string; + markMatchingSearchText: ( + label: string, + shouldHighlight?: boolean + ) => string | ReactNode; }; -export function BaselayerSections({ +function BaselayerSections({ mapGroups, activeBaselayerId, isFlipped, onBaselayerChange, + searchText, + markMatchingSearchText, }: BaselayerSectionsProps) { + const [expandedState, setExpandedState] = useState>( + getDefaultExpandedState(mapGroups, activeBaselayerId) + ); + const { filteredMapGroups, matchedIds } = filterMapGroups( + mapGroups, + searchText + ); + const filteredExternalLayers = EXTERNAL_BASELAYERS.filter((bl) => + bl.name.toLowerCase().includes(searchText.toLowerCase()) + ); + const isEmpty = + filteredMapGroups.length + filteredExternalLayers.length === 0; + + const handleToggle = useCallback( + (id: string) => { + if (expandedState.has(id)) { + setExpandedState((prev) => { + const next = new Set(prev); + next.delete(id); + return next; + }); + } else { + setExpandedState((prev) => new Set(prev).add(id)); + } + }, + [expandedState] + ); + + if (isEmpty) { + return ; + } + return ( <> - {mapGroups.map((group, groupIndex) => ( + {filteredMapGroups.map((group) => ( - {group.maps.map((map, mapIndex) => ( - - {map.bands.map((band, bandIndex) => ( - - {band.layers.map((layer) => ( - - ))} - - ))} - - ))} - + key={group.name} + node={group} + nestedDepth={0} + onBaselayerChange={onBaselayerChange} + activeBaselayerId={activeBaselayerId} + searchText={searchText} + expandedState={expandedState} + markMatchingSearchText={markMatchingSearchText} + matchedIds={matchedIds} + highlightMatch={matchedIds.has(group.name)} + handleToggle={handleToggle} + /> ))} - { - - {EXTERNAL_BASELAYERS.map((bl) => ( -
- onBaselayerChange(bl.layer_id, 'layerMenu')} - disabled={bl.disabledState(isFlipped)} - /> - -
- ))} -
- } + {filteredExternalLayers.length > 0 && ( +
+
handleToggle(EXTERNAL_DETAILS_ID)} + className="layer-title-container" + > + {expandedState.has(EXTERNAL_DETAILS_ID) ? ( + + ) : ( + + )} + Comparison maps +
+ {expandedState.has(EXTERNAL_DETAILS_ID) && + filteredExternalLayers.map((bl) => ( +
+ onBaselayerChange(bl.layer_id, 'layerMenu')} + disabled={bl.disabledState(isFlipped)} + /> + +
+ ))} +
+ )} ); } + +export default memo(BaselayerSections); diff --git a/src/components/CenterMapFeature.tsx b/src/components/CenterMapFeature.tsx index 93f0b0a..5a08bdf 100644 --- a/src/components/CenterMapFeature.tsx +++ b/src/components/CenterMapFeature.tsx @@ -101,7 +101,7 @@ export function CenterMapFeature({ className="center-feature-form generic-form" onSubmit={onSubmit} > -