utopia-ui/lib/src/Components/Item/PopupView.tsx
2025-07-11 01:32:29 +02:00

185 lines
5.5 KiB
TypeScript

import { useContext, useMemo, useState } from 'react'
import { Marker, Tooltip } from 'react-leaflet'
import { useAppState } from '#components/AppShell/hooks/useAppState'
import {
useFilterTags,
useIsLayerVisible,
useIsGroupTypeVisible,
useVisibleGroupType,
useAllVisibleLayersInitialized,
} from '#components/Map/hooks/useFilter'
import { useItems } from '#components/Map/hooks/useItems'
import { useAddMarker, useAddPopup, useLeafletRefs } from '#components/Map/hooks/useLeafletRefs'
import { useSetMarkerClicked, useSelectPosition } from '#components/Map/hooks/useSelectPosition'
import { useGetItemTags, useAllTagsLoaded, useTags } from '#components/Map/hooks/useTags'
import LayerContext from '#components/Map/LayerContext'
import { ItemViewPopup } from '#components/Map/Subcomponents/ItemViewPopup'
import { encodeTag } from '#utils/FormatTags'
import { hashTagRegex } from '#utils/HashTagRegex'
import MarkerIconFactory from '#utils/MarkerIconFactory'
import { randomColor } from '#utils/RandomColor'
import TemplateItemContext from './TemplateItemContext'
import type { Item } from '#types/Item'
import type { Tag } from '#types/Tag'
import type { Popup } from 'leaflet'
/**
* @category Item
*/
export const PopupView = ({ children }: { children?: React.ReactNode }) => {
const layerContext = useContext(LayerContext)
const { name, markerDefaultColor, markerDefaultColor2, markerShape, markerIcon } = layerContext
const filterTags = useFilterTags()
const appState = useAppState()
const items = useItems()
const getItemTags = useGetItemTags()
const addMarker = useAddMarker()
const addPopup = useAddPopup()
const leafletRefs = useLeafletRefs()
const allTagsLoaded = useAllTagsLoaded()
const allVisibleLayersInitialized = useAllVisibleLayersInitialized()
const setMarkerClicked = useSetMarkerClicked()
const selectPosition = useSelectPosition()
const tags = useTags()
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([])
const [tagsReady, setTagsReady] = useState<boolean>(false)
const isLayerVisible = useIsLayerVisible()
const isGroupTypeVisible = useIsGroupTypeVisible()
const visibleGroupTypes = useVisibleGroupType()
const visibleItems = useMemo(
() =>
items
.filter((item) => item.layer?.name === name)
.filter((item) =>
filterTags.length === 0
? item
: filterTags.some((tag) =>
getItemTags(item).some(
(filterTag) =>
filterTag.name.toLocaleLowerCase() === tag.name.toLocaleLowerCase(),
),
),
)
.filter((item) => item.layer && isLayerVisible(item.layer))
.filter(
(item) =>
(item.group_type && isGroupTypeVisible(item.group_type)) ||
visibleGroupTypes.length === 0,
),
[
filterTags,
getItemTags,
isGroupTypeVisible,
isLayerVisible,
items,
name,
visibleGroupTypes.length,
],
)
return visibleItems.map((item: Item) => {
if (!(item.position?.coordinates[0] && item.position.coordinates[1])) return null
if (item.tags) {
item.text += '\n\n'
item.tags.map((tag) => {
if (!item.text?.includes(`#${encodeTag(tag)}`)) {
item.text += `#${encodeTag(tag)}`
}
return item.text
})
}
if (allTagsLoaded && allVisibleLayersInitialized) {
item.text?.match(hashTagRegex)?.map((tag) => {
if (
!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase()) &&
!newTagsToAdd.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())
) {
const newTag = {
id: crypto.randomUUID(),
name: tag.slice(1),
color: randomColor(),
}
setNewTagsToAdd((current) => [...current, newTag])
}
return null
})
!tagsReady && setTagsReady(true)
}
const itemTags = getItemTags(item)
const latitude = item.position.coordinates[1]
const longitude = item.position.coordinates[0]
let color1 = markerDefaultColor
let color2 = markerDefaultColor2
if (item.color) {
color1 = item.color
} else if (itemTags[0]) {
color1 = itemTags[0].color
}
if (itemTags[0] && item.color) {
color2 = itemTags[0].color
} else if (itemTags[1]) {
color2 = itemTags[1].color
}
return (
<TemplateItemContext.Provider value={item} key={item.id}>
<Marker
ref={(r) => {
if (!(item.id in leafletRefs && leafletRefs[item.id].marker === r)) {
r && addMarker(item, r)
}
}}
eventHandlers={{
click: () => {
selectPosition && setMarkerClicked(item)
},
}}
icon={MarkerIconFactory(
markerShape,
color1,
color2,
item.markerIcon ?? markerIcon,
appState.assetsApi.url,
)}
position={[latitude, longitude]}
>
<ItemViewPopup
ref={(r: Popup | null) => {
if (!(item.id in leafletRefs && leafletRefs[item.id].popup === r)) {
r && addPopup(item, r)
}
}}
item={item}
>
{children}
</ItemViewPopup>
<Tooltip offset={[0, -38]} direction='top'>
{item.name}
</Tooltip>
</Marker>
</TemplateItemContext.Provider>
)
})
}