231 lines
12 KiB
TypeScript

import * as React from 'react'
import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet'
import { Item, LayerProps, Tag } from '../../types'
import MarkerIconFactory from '../../Utils/MarkerIconFactory'
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
import { useEffect, useState } from 'react'
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
import { useFilterTags, useIsLayerVisible } from './hooks/useFilter'
import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } from './hooks/useTags'
import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs'
import { Popup } from 'leaflet'
import { useLocation } from 'react-router-dom';
import { getValue } from '../../Utils/GetValue'
import { hashTagRegex } from '../../Utils/HashTagRegex'
import { randomColor } from '../../Utils/RandomColor'
export const Layer = ({
data,
children,
name = 'places',
menuIcon = 'MapPinIcon',
menuText = 'add new place',
menuColor = '#2E7D32',
markerIcon = 'circle-solid',
markerShape = 'circle',
markerDefaultColor = '#777',
api,
itemNameField = 'name',
itemTextField = 'text',
itemAvatarField,
itemColorField,
itemOwnerField,
itemLatitudeField = 'position.coordinates.1',
itemLongitudeField = 'position.coordinates.0',
onlyOnePerOwner = false,
setItemFormPopup,
itemFormPopup,
clusterRef
}: LayerProps) => {
const filterTags = useFilterTags();
const items = useItems();
const setItemsApi = useSetItemsApi();
const setItemsData = useSetItemsData();
const getItemTags = useGetItemTags();
const addMarker = useAddMarker();
const addPopup = useAddPopup();
const leafletRefs = useLeafletRefs();
let location = useLocation();
const allTagsLoaded = useAllTagsLoaded();
const tags = useTags();
const addTag = useAddTag();
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([]);
const [tagsReady, setTagsReady] = useState<boolean>(false);
const map = useMap();
const isLayerVisible = useIsLayerVisible();
useEffect(() => {
data && setItemsData({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, onlyOnePerOwner, setItemFormPopup, itemFormPopup, clusterRef });
api && setItemsApi({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, onlyOnePerOwner, setItemFormPopup, itemFormPopup, clusterRef });
}, [data, api])
useMapEvents({
popupopen: (e) => {
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
window.history.pushState({}, "", `/${name}/${item.id}`)
let title = "";
if (item.name) title = item.name;
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
document.title = `${document.title.split("-")[0]} - ${title}`;
}
},
})
const openPopup = () => {
if (window.location.pathname.split("/").length <= 2 || window.location.pathname.split("/")[2] === "") {
map.closePopup();
}
else {
if (window.location.pathname.split("/")[1] == name) {
if (window.location.pathname.split("/")[2]) {
const id = window.location.pathname.split("/")[2]
const marker = leafletRefs[id]?.marker;
if (marker && marker != null) {
marker !== null && clusterRef?.current?.zoomToShowLayer(marker, () => {
marker.openPopup();
});
const item = leafletRefs[id]?.item;
let title = "";
if (item.name) title = item.name;
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
document.title = `${document.title.split("-")[0]} - ${title}`;
document.querySelector('meta[property="og:title"]')?.setAttribute("content", item.name);
document.querySelector('meta[property="og:description"]')?.setAttribute("content", item.text);
}
}
}
}
}
useEffect(() => {
openPopup();
}, [leafletRefs, location])
useEffect(() => {
}, [allTagsLoaded])
useEffect(() => {
if(tagsReady){
newTagsToAdd.map(newtag => {
addTag(newtag);
setNewTagsToAdd(current =>
current.filter(tag => {
return tag.id !== newtag.id;
}),
)
})
}
}, [tagsReady])
return (
<>
{items &&
items.
filter(item => item.layer?.name === name)?.
filter(item =>
filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id.toLocaleLowerCase() === tag.id.toLocaleLowerCase())))?.
filter(item => item.layer && isLayerVisible(item.layer)).
map((item: Item) => {
if (getValue(item, itemLongitudeField) && getValue(item, itemLatitudeField)) {
item[itemTextField] = getValue(item, itemTextField);
if (item?.tags) {
item[itemTextField] = item[itemTextField] + '\n\n';
item.tags.map(tag => {
if(!item[itemTextField].includes(`#${tag}`))
return (item[itemTextField] = item[itemTextField] + `#${tag} `)
return item[itemTextField]
});
}
if(allTagsLoaded) {
item[itemTextField].toLocaleLowerCase().match(hashTagRegex)?.map(tag=> {
if ((!tags.find((t) => t.id.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase()))&& !newTagsToAdd.find((t) => t.id.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
const newTag = {id: tag.slice(1).toLocaleLowerCase(), color: randomColor()};
setNewTagsToAdd(current => [...current, newTag]);
}
});
!tagsReady && setTagsReady(true);
}
const itemTtags = getItemTags(item);
const latitude = itemLatitudeField && item ? getValue(item, itemLatitudeField) : undefined;
const longitude = itemLongitudeField && item ? getValue(item, itemLongitudeField) : undefined;
let color1 = markerDefaultColor;
let color2 = "RGBA(35, 31, 32, 0.2)";
if (itemColorField) color1 = getValue(item, itemColorField);
else if (itemTtags && itemTtags[0]) {
color1 = itemTtags[0].color;
}
if (itemTtags && itemTtags[0] && itemColorField) color2 = itemTtags[0].color;
else if (itemTtags && itemTtags[1]) {
color2 = itemTtags[1].color;
}
return (
<Marker ref={(r) => {
if (!(item.id in leafletRefs && leafletRefs[item.id].marker == r))
r && addMarker(item, r);
}} icon={MarkerIconFactory(markerShape, color1, color2, markerIcon)} key={item.id} position={[latitude, longitude]}>
{
(children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ?
React.Children.toArray(children).map((child) =>
React.isValidElement(child) && child.props.__TYPE === "ItemView" ?
<ItemViewPopup ref={(r) => {
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
r && addPopup(item, r as Popup);
}} key={item.id + item.name}
item={item}
setItemFormPopup={setItemFormPopup}>
{child}
</ItemViewPopup>
: ""
)
:
<>
<ItemViewPopup key={item.id + item.name} ref={(r) => {
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
r && addPopup(item, r as Popup);
}}
item={item}
setItemFormPopup={setItemFormPopup} />
</>)
}
<Tooltip offset={[0, -38]} direction='top'>{item.name ? item.name : getValue(item, itemNameField)}</Tooltip>
</Marker>
);
}
else return null;
})
}
{//{children}}
}
{itemFormPopup && itemFormPopup.layer!.name == name &&
(children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemForm") ?
React.Children.toArray(children).map((child) =>
React.isValidElement(child) && child.props.__TYPE === "ItemForm" ?
<ItemFormPopup key={setItemFormPopup?.name} position={itemFormPopup!.position} layer={itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={itemFormPopup!.item} >{child}</ItemFormPopup>
: ""
)
:
<>
<ItemFormPopup position={itemFormPopup!.position} layer={itemFormPopup!.layer} setItemFormPopup={setItemFormPopup} item={itemFormPopup!.item} />
</>)
}
</>
)
}