/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ import FlagIcon from '@heroicons/react/24/outline/FlagIcon' import MagnifyingGlassIcon from '@heroicons/react/24/outline/MagnifyingGlassIcon' import axios from 'axios' import { LatLng, LatLngBounds, marker } from 'leaflet' import { useRef, useState } from 'react' import SVG from 'react-inlinesvg' import { useMap, useMapEvents } from 'react-leaflet' import { useNavigate } from 'react-router-dom' import { useAppState } from '#components/AppShell/hooks/useAppState' import { useDebounce } from '#components/Map/hooks/useDebounce' import { useAddFilterTag } from '#components/Map/hooks/useFilter' import { useItems } from '#components/Map/hooks/useItems' import { useLeafletRefs } from '#components/Map/hooks/useLeafletRefs' import { useTags } from '#components/Map/hooks/useTags' import useWindowDimensions from '#components/Map/hooks/useWindowDimension' import { decodeTag } from '#utils/FormatTags' import MarkerIconFactory from '#utils/MarkerIconFactory' import { LocateControl } from './LocateControl' import { SidebarControl } from './SidebarControl' import type { Item } from '#types/Item' export const SearchControl = () => { const windowDimensions = useWindowDimensions() const [popupOpen, setPopupOpen] = useState(false) const [value, setValue] = useState('') const [geoResults, setGeoResults] = useState([]) const [tagsResults, setTagsResults] = useState([]) const [itemsResults, setItemsResults] = useState([]) const [hideSuggestions, setHideSuggestions] = useState(true) const map = useMap() const tags = useTags() const items = useItems() const leafletRefs = useLeafletRefs() const addFilterTag = useAddFilterTag() const appState = useAppState() useMapEvents({ popupopen: () => { setPopupOpen(true) }, popupclose: () => { setPopupOpen(false) }, }) const navigate = useNavigate() useDebounce( () => { const searchGeo = async () => { try { const { data } = await axios.get(`https://photon.komoot.io/api/?q=${value}&limit=5`) setGeoResults(data.features) // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { // eslint-disable-next-line no-console console.log(error) } } searchGeo() setItemsResults( items.filter((item) => { return ( value.length > 2 && ((item.layer?.listed && item.name.toLowerCase().includes(value.toLowerCase())) || item.text?.toLowerCase().includes(value.toLowerCase())) ) }), ) let phrase = value if (value.startsWith('#')) phrase = value.substring(1) setTagsResults(tags.filter((tag) => tag.name.toLowerCase().includes(phrase.toLowerCase()))) }, 500, [value], ) const hide = async () => { setTimeout(() => { setHideSuggestions(true) }, 200) } const searchInput = useRef(null) return ( <> {!(windowDimensions.height < 500 && popupOpen && hideSuggestions) && (
{appState.embedded && }
setValue(e.target.value)} onFocus={() => { setHideSuggestions(false) if (windowDimensions.width < 500) map.closePopup() }} onBlur={() => hide()} /> {value.length > 0 && ( )}
{hideSuggestions || (Array.from(geoResults).length === 0 && itemsResults.length === 0 && tagsResults.length === 0 && !isGeoCoordinate(value)) || value.length === 0 ? ( '' ) : (
{tagsResults.length > 0 && (
{tagsResults.slice(0, 3).map((tag) => (
{ addFilterTag(tag) }} > #{decodeTag(tag.name)}
))}
)} {itemsResults.length > 0 && tagsResults.length > 0 && (
)} {itemsResults.slice(0, 5).map((item) => (
{ const marker = Object.entries(leafletRefs).find((r) => r[1].item === item)?.[1] .marker if (marker) { navigate(`/${item.id}?${new URLSearchParams(window.location.search)}`) } else { navigate( 'item/' + item.id + '?' + new URLSearchParams(window.location.search), ) } }} > {item.layer?.markerIcon.image ? (
{ code = code.replace(/fill=".*?"/g, 'fill="currentColor"') code = code.replace(/stroke=".*?"/g, 'stroke="currentColor"') return code }} />
) : (
)}
{item.name}
))} {Array.from(geoResults).length > 0 && (itemsResults.length > 0 || tagsResults.length > 0) && (
)} {Array.from(geoResults).map((geo) => (
{ searchInput.current?.blur() marker(new LatLng(geo.geometry.coordinates[1], geo.geometry.coordinates[0]), { icon: MarkerIconFactory('circle', '#777', 'RGBA(35, 31, 32, 0.2)'), }) .addTo(map) .bindPopup( `

${geo?.properties.name ? geo?.properties.name : value}

${capitalizeFirstLetter(geo?.properties?.osm_value)}`, ) .openPopup() .addEventListener('popupclose', (e) => { // eslint-disable-next-line no-console console.log(e.target.remove()) }) if (geo.properties.extent) map.fitBounds( new LatLngBounds( new LatLng(geo.properties.extent[1], geo.properties.extent[0]), new LatLng(geo.properties.extent[3], geo.properties.extent[2]), ), ) else map.setView( new LatLng(geo.geometry.coordinates[1], geo.geometry.coordinates[0]), 15, { duration: 1 }, ) hide() }} >
{geo?.properties.name ? geo?.properties.name : value}
{geo?.properties?.city && `${capitalizeFirstLetter(geo?.properties?.city)}, `}{' '} {geo?.properties?.osm_value && geo?.properties?.osm_value !== 'yes' && geo?.properties?.osm_value !== 'primary' && geo?.properties?.osm_value !== 'path' && geo?.properties?.osm_value !== 'secondary' && geo?.properties?.osm_value !== 'residential' && geo?.properties?.osm_value !== 'unclassified' && `${capitalizeFirstLetter(geo?.properties?.osm_value)}, `}{' '} {geo.properties.state && `${geo.properties.state}, `}{' '} {geo.properties.country && geo.properties.country}

))} {isGeoCoordinate(value) && (
{ marker( new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]), { icon: MarkerIconFactory('circle', '#777', 'RGBA(35, 31, 32, 0.2)'), }, ) .addTo(map) .bindPopup( `

${extractCoordinates(value)![0]}, ${extractCoordinates(value)![1]}

`, ) .openPopup() .addEventListener('popupclose', (e) => { // eslint-disable-next-line no-console console.log(e.target.remove()) }) map.setView( new LatLng(extractCoordinates(value)![0], extractCoordinates(value)![1]), 15, { duration: 1 }, ) }} >
{value}
{'Coordiante'}
)}
)}
)} ) } function isGeoCoordinate(input) { const geokoordinatenRegex = // eslint-disable-next-line security/detect-unsafe-regex /^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/ return geokoordinatenRegex.test(input) } function extractCoordinates(input): number[] | null { const result = input.split(',') if (result) { const latitude = parseFloat(result[0]) const longitude = parseFloat(result[1]) if (!isNaN(latitude) && !isNaN(longitude)) { return [latitude, longitude] } } return null // Invalid input or error } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1) }