diff --git a/src/Components/Map/Layer.tsx b/src/Components/Map/Layer.tsx index 0646af4c..c97a3fac 100644 --- a/src/Components/Map/Layer.tsx +++ b/src/Components/Map/Layer.tsx @@ -1,12 +1,15 @@ import * as React from 'react' -import { Marker, Tooltip } from 'react-leaflet' +import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet' import { Item, LayerProps } from '../../types' import MarkerIconFactory from '../../Utils/MarkerIconFactory' import { ItemViewPopup } from './Subcomponents/ItemViewPopup' -import { useItems, useResetItems, useSetItemsApi, useSetItemsData } from './hooks/useItems' +import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems' import { useEffect, useState } from 'react' import { ItemFormPopupProps, ItemFormPopup } from './Subcomponents/ItemFormPopup' import { useFilterTags, useSearchPhrase } from './hooks/useFilter' +import { useGetItemTags } from './hooks/useTags' +import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs' +import { Popup } from 'leaflet' export const Layer = (props: LayerProps) => { @@ -18,32 +21,63 @@ export const Layer = (props: LayerProps) => { const items = useItems(); const setItemsApi = useSetItemsApi(); const setItemsData = useSetItemsData(); - - const resetItems = useResetItems(); + const getItemTags = useGetItemTags(); + const addMarker = useAddMarker(); + const addPopup = useAddPopup(); + const leafletRefs = useLeafletRefs(); const searchPhrase = useSearchPhrase(); + const map = useMap(); + + useEffect(() => { - resetItems(props); + props.data && setItemsData(props); props.api && setItemsApi(props); }, [props.data, props.api]) + const openPopup = () => { + if (window.location.pathname.split("/")[1] == props.name) { + const id = window.location.pathname.split("/")[2] + const marker = leafletRefs[id]?.marker; + if (marker && marker != null) { + marker !== null && props.clusterRef?.current?.zoomToShowLayer(marker, () => { + marker.openPopup(); + }); + } + } + } + + useMapEvents({ + popupopen: (e) => { + const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item; + if(item?.layer?.name == props.name ) + window.history.pushState({},"",`/${props.name}/${item.id}`) + }, + + + }) + + useEffect(() => { + openPopup(); + }, [leafletRefs]) + + return ( <> {items && items. filter(item => item.layer?.name === props.name)?. filter(item => - // filterTags.length == 0 ? item : item.tags?.some(tag => filterTags.some(filterTag => filterTag.id === tag.id)))?. - filterTags.length == 0 ? item : filterTags.every(tag => item.tags?.some(filterTag => filterTag.id === tag.id)))?. + filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id === tag.id)))?. filter(item => { return searchPhrase === '' ? item : item.name.toLowerCase().includes(searchPhrase.toLowerCase()) }). map((item: Item) => { - const tags = item.tags; + const tags = getItemTags(item); let color1 = "#666"; let color2 = "RGBA(35, 31, 32, 0.2)"; @@ -54,20 +88,29 @@ export const Layer = (props: LayerProps) => { color2 = tags[1].color; } return ( - + { + if (!(item.id in leafletRefs)) + r && addMarker(item, r); + }} icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={item.id} position={[item.position.coordinates[1], item.position.coordinates[0]]}> { (props.children && React.Children.toArray(props.children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ? React.Children.toArray(props.children).map((child) => React.isValidElement(child) && child.props.__TYPE === "ItemView" ? - {child} + { + if (!(item.id in leafletRefs)) + r && addPopup(item, r as Popup); + }} key={item.id + item.name} item={item} setItemFormPopup={props.setItemFormPopup} >{child} : "" ) : <> - + { + if (!(item.id in leafletRefs)) + r && addPopup(item, r as Popup); + }} item={item} setItemFormPopup={props.setItemFormPopup} /> ) } - {item.name} + {item.name} ); }) diff --git a/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx b/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx index 2ef265c9..58ae0eb0 100644 --- a/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx +++ b/src/Components/Map/Subcomponents/ItemPopupComponents/HeaderView.tsx @@ -5,7 +5,7 @@ import { ItemFormPopupProps } from "../ItemFormPopup"; import { LatLng } from "leaflet"; import { Item } from "../../../../types"; import { toast } from "react-toastify"; -import { useHasUserPermission, usePermissions } from "../../hooks/usePermissions"; +import { useHasUserPermission } from "../../hooks/usePermissions"; @@ -19,7 +19,6 @@ export function HeaderView({ item, setItemFormPopup }: { const map = useMap(); const hasUserPermission = useHasUserPermission(); - const permissions = usePermissions(); const removeItemFromMap = async (event: React.MouseEvent) => { @@ -48,11 +47,6 @@ export function HeaderView({ item, setItemFormPopup }: { setItemFormPopup({ position: new LatLng(item.position.coordinates[1], item.position.coordinates[0]), layer: item.layer!, item: item, setItemFormPopup: setItemFormPopup }) } - console.log(item.layer.api.collectionName); - console.log(permissions); - -console.log( hasUserPermission(item.api?.collectionName!,"update") ); - return (
diff --git a/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx b/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx index 4cb3594d..ba27c11d 100644 --- a/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx +++ b/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx @@ -2,9 +2,9 @@ import * as React from 'react' import { Item } from '../../../../types' import { useAddTag, useTags } from '../../hooks/useTags'; import reactStringReplace from 'react-string-replace'; -import { useAddFilterTag, useResetFilterTags } from '../../hooks/useFilter'; +import { useAddFilterTag } from '../../hooks/useFilter'; import { hashTagRegex } from '../../../../Utils/HashTagRegex'; -import { fixUrls, mailRegex, urlRegex } from '../../../../Utils/ReplaceURLs'; +import { fixUrls, mailRegex } from '../../../../Utils/ReplaceURLs'; import { useMap } from 'react-leaflet'; import { randomColor } from '../../../../Utils/RandomColor'; import { useEffect, useRef } from 'react'; @@ -16,7 +16,6 @@ export const TextView = ({ item }: { item?: Item }) => { const groupRef = useRef(null); const addFilterTag = useAddFilterTag(); - const resetFilterTags = useResetFilterTags(); const map = useMap(); diff --git a/src/Components/Map/Subcomponents/ItemViewPopup.tsx b/src/Components/Map/Subcomponents/ItemViewPopup.tsx index 9d6a7fb1..94745d9f 100644 --- a/src/Components/Map/Subcomponents/ItemViewPopup.tsx +++ b/src/Components/Map/Subcomponents/ItemViewPopup.tsx @@ -1,21 +1,21 @@ import * as React from 'react' -import { Popup as LeafletPopup, useMap } from 'react-leaflet' +import { Popup as LeafletPopup} from 'react-leaflet' import { Item } from '../../../types' import { ItemFormPopupProps } from './ItemFormPopup' import { HeaderView } from './ItemPopupComponents/HeaderView' import { TextView } from './ItemPopupComponents/TextView' + export interface ItemViewPopupProps { item: Item, children?: React.ReactNode; setItemFormPopup?: React.Dispatch> } -export const ItemViewPopup = (props: ItemViewPopupProps) => { - const item: Item = props.item; +export const ItemViewPopup = React.forwardRef((props: ItemViewPopupProps, ref: any) => { return ( - +
@@ -35,5 +35,5 @@ export const ItemViewPopup = (props: ItemViewPopupProps) => {
) -} +}) diff --git a/src/Components/Map/UtopiaMap.tsx b/src/Components/Map/UtopiaMap.tsx index 2afd60fe..dc0ae1af 100644 --- a/src/Components/Map/UtopiaMap.tsx +++ b/src/Components/Map/UtopiaMap.tsx @@ -14,6 +14,7 @@ import { LayersProvider } from "./hooks/useLayers"; import { FilterProvider } from "./hooks/useFilter"; import { FilterControl } from "./Subcomponents/FilterControl"; import { PermissionsProvider } from "./hooks/usePermissions"; +import { LeafletRefsProvider } from "./hooks/useLeafletRefs"; export interface MapEventListenerProps { @@ -25,7 +26,7 @@ export interface MapEventListenerProps { function MapEventListener(props: MapEventListenerProps) { useMapEvents({ click: (e) => { - + window.history.pushState({}, "", "/"); console.log(e.latlng.lat + ',' + e.latlng.lng); if (props.selectNewItemPosition != null) { props.setItemFormPopup({ layer: props.selectNewItemPosition, position: e.latlng }) @@ -34,8 +35,8 @@ function MapEventListener(props: MapEventListenerProps) { }, resize: () => { console.log("resize"); + }, - } }) return null } @@ -53,6 +54,7 @@ function UtopiaMap({ const [selectNewItemPosition, setSelectNewItemPosition] = useState(null); const [itemFormPopup, setItemFormPopup] = useState(null); + const clusterRef = React.useRef(); return ( @@ -61,33 +63,35 @@ function UtopiaMap({ -
- - - - - { - React.Children.toArray(children).map((child) => - React.isValidElement<{ setItemFormPopup: React.Dispatch>, itemFormPopup: ItemFormPopupProps | null }>(child) ? - React.cloneElement(child, { setItemFormPopup: setItemFormPopup, itemFormPopup: itemFormPopup }) : child - ) - } - - - - - {selectNewItemPosition != null && -
-
-
- Select {selectNewItemPosition.name} position! + +
+ + + + + { + React.Children.toArray(children).map((child) => + React.isValidElement<{ setItemFormPopup: React.Dispatch>, itemFormPopup: ItemFormPopupProps | null, clusterRef: React.MutableRefObject }>(child) ? + React.cloneElement(child, { setItemFormPopup: setItemFormPopup, itemFormPopup: itemFormPopup, clusterRef: clusterRef }) : child + ) + } + + + + + {selectNewItemPosition != null && +
+
+
+ Select {selectNewItemPosition.name} position! +
-
- } -
+ } +
+ diff --git a/src/Components/Map/hooks/useLeafletRefs.tsx b/src/Components/Map/hooks/useLeafletRefs.tsx new file mode 100644 index 00000000..2b26192f --- /dev/null +++ b/src/Components/Map/hooks/useLeafletRefs.tsx @@ -0,0 +1,95 @@ +import { useCallback, useReducer, createContext, useContext, useEffect } from "react"; +import * as React from "react"; +import { Item } from "../../../types"; +import { Marker, Popup } from "leaflet"; + +type LeafletRef = { + item: Item, + marker: Marker, + popup: Popup +} + +type ActionType = + | { type: "ADD_MARKER"; item: Item, marker: Marker } + | { type: "ADD_POPUP"; item: Item, popup: Popup } + ; + + +type UseLeafletRefsManagerResult = ReturnType; + +const LeafletRefsContext = createContext({ + leafletRefs: {}, + addMarker: () => { }, + addPopup: () => { }, +}); + +function useLeafletRefsManager(initialLeafletRefs: {}): { + leafletRefs: Record; + addMarker: (item: Item, marker: Marker) => void; + addPopup: (item: Item, popup: Popup) => void; +} { + + + + const [leafletRefs, dispatch] = useReducer((state: Record, action: ActionType) => { + switch (action.type) { + case "ADD_MARKER": + return { + ...state, + [action.item.id]: { ...state[action.item.id], marker: action.marker, item: action.item }} + case "ADD_POPUP": + return { + ...state, + [action.item.id]: { ...state[action.item.id], popup: action.popup, item: action.item }} + default: + throw new Error(); + } + }, initialLeafletRefs); + + + + + const addMarker = useCallback((item: Item, marker: Marker) => { + dispatch({ + type: "ADD_MARKER", + item, + marker + }); + }, []); + + const addPopup = useCallback((item: Item, popup: Popup) => { + dispatch({ + type: "ADD_POPUP", + item, + popup + }); + }, []); + + + + return { leafletRefs, addMarker, addPopup }; +} + +export const LeafletRefsProvider: React.FunctionComponent<{ + initialLeafletRefs: {}, children?: React.ReactNode +}> = ({ initialLeafletRefs, children }) => ( + + {children} + +); + +export const useLeafletRefs = (): Record => { + const { leafletRefs } = useContext(LeafletRefsContext); + return leafletRefs; +}; + +export const useAddMarker = (): UseLeafletRefsManagerResult["addMarker"] => { + const { addMarker } = useContext(LeafletRefsContext); + return addMarker; +}; + +export const useAddPopup = (): UseLeafletRefsManagerResult["addPopup"] => { + const { addPopup } = useContext(LeafletRefsContext); + return addPopup; +}; + diff --git a/src/types.ts b/src/types.ts index 16b1abbd..cec5f371 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { LatLng } from "leaflet"; +import { LatLng, LatLngTuple, latLng } from "leaflet"; import { ItemFormPopupProps } from "./Components/Map/Subcomponents/ItemFormPopup"; export interface UtopiaMapProps { @@ -22,7 +22,8 @@ export interface LayerProps { markerDefaultColor: string, api?: ItemsApi, setItemFormPopup?: React.Dispatch>, - itemFormPopup?: ItemFormPopupProps | null + itemFormPopup?: ItemFormPopupProps | null, + clusterRef?: React.MutableRefObject } export class Item { @@ -36,6 +37,7 @@ export class Item { end?: string; api?: ItemsApi; tags?: Tag[]; + layer?: LayerProps; [key: string]: any; constructor(id:string,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi){ this.id = id;