mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
185 lines
5.5 KiB
TypeScript
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>
|
|
)
|
|
})
|
|
}
|