mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
dynamic router and context loading only if needed
This commit is contained in:
parent
da7c3b3734
commit
30d96bfd91
@ -1,71 +1,21 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import NavBar from './NavBar'
|
import NavBar from './NavBar'
|
||||||
import { BrowserRouter } from 'react-router-dom'
|
|
||||||
import { ToastContainer } from 'react-toastify'
|
|
||||||
import { QuestsProvider } from '../Gaming/hooks/useQuests'
|
|
||||||
import { AssetsProvider } from './hooks/useAssets'
|
|
||||||
import { SetAssetsApi } from './SetAssetsApi'
|
import { SetAssetsApi } from './SetAssetsApi'
|
||||||
import { AssetsApi } from '../../types'
|
import { AssetsApi } from '../../types'
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
||||||
import { PermissionsProvider } from '../Map/hooks/usePermissions'
|
|
||||||
import { TagsProvider } from '../Map/hooks/useTags'
|
|
||||||
import { FilterProvider } from '../Map/hooks/useFilter'
|
|
||||||
import { ItemsProvider } from '../Map/hooks/useItems'
|
|
||||||
import { LayersProvider } from '../Map/hooks/useLayers'
|
|
||||||
import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
|
||||||
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
|
||||||
import { ClusterRefProvider } from '../Map/hooks/useClusterRef'
|
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
|
import { ContextWrapper } from './ContextWrapper';
|
||||||
|
|
||||||
|
|
||||||
export function AppShell({ appName, children, assetsApi, userType }: { appName: string, children: React.ReactNode, assetsApi: AssetsApi, userType: string }) {
|
export function AppShell({ appName, children, assetsApi, userType }: { appName: string, children: React.ReactNode, assetsApi: AssetsApi, userType: string }) {
|
||||||
|
|
||||||
// Create a client
|
|
||||||
const queryClient = new QueryClient()
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<ContextWrapper>
|
||||||
<PermissionsProvider initialPermissions={[]}>
|
<div className='tw-flex tw-flex-col tw-h-full'>
|
||||||
<TagsProvider initialTags={[]}>
|
<SetAssetsApi assetsApi={assetsApi} />
|
||||||
<LayersProvider initialLayers={[]}>
|
<NavBar userType={userType} appName={appName}></NavBar>
|
||||||
<FilterProvider initialTags={[]}>
|
<div id="app-content" className="tw-flex-grow">
|
||||||
<ItemsProvider initialItems={[]}>
|
{children}
|
||||||
<SelectPositionProvider>
|
</div>
|
||||||
<LeafletRefsProvider initialLeafletRefs={{}}>
|
</div>
|
||||||
<QueryClientProvider client={queryClient}>
|
</ContextWrapper>
|
||||||
<AssetsProvider>
|
|
||||||
<ClusterRefProvider>
|
|
||||||
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
|
|
||||||
<QuestsProvider initialOpen={true}>
|
|
||||||
<ToastContainer position="top-right"
|
|
||||||
autoClose={2000}
|
|
||||||
hideProgressBar
|
|
||||||
newestOnTop={false}
|
|
||||||
closeOnClick
|
|
||||||
rtl={false}
|
|
||||||
pauseOnFocusLoss
|
|
||||||
draggable
|
|
||||||
pauseOnHover
|
|
||||||
theme="light" />
|
|
||||||
<div className='tw-flex tw-flex-col tw-h-full'>
|
|
||||||
<NavBar userType={userType} appName={appName}></NavBar>
|
|
||||||
<div id="app-content" className="tw-flex-grow">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</QuestsProvider>
|
|
||||||
</ClusterRefProvider>
|
|
||||||
</AssetsProvider>
|
|
||||||
</QueryClientProvider>
|
|
||||||
</LeafletRefsProvider>
|
|
||||||
</SelectPositionProvider>
|
|
||||||
</ItemsProvider>
|
|
||||||
</FilterProvider>
|
|
||||||
</LayersProvider>
|
|
||||||
</TagsProvider>
|
|
||||||
</PermissionsProvider>
|
|
||||||
</BrowserRouter>
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
98
src/Components/AppShell/ContextWrapper.tsx
Normal file
98
src/Components/AppShell/ContextWrapper.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
import { ToastContainer } from 'react-toastify'
|
||||||
|
import { QuestsProvider } from '../Gaming/hooks/useQuests'
|
||||||
|
import { ClusterRefProvider } from '../Map/hooks/useClusterRef'
|
||||||
|
import { FilterProvider } from '../Map/hooks/useFilter'
|
||||||
|
import { ItemsProvider } from '../Map/hooks/useItems'
|
||||||
|
import { LayersProvider } from '../Map/hooks/useLayers'
|
||||||
|
import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
||||||
|
import { PermissionsProvider } from '../Map/hooks/usePermissions'
|
||||||
|
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
||||||
|
import { TagsProvider } from '../Map/hooks/useTags'
|
||||||
|
import { AssetsProvider } from './hooks/useAssets'
|
||||||
|
import { useContext, createContext } from 'react';
|
||||||
|
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
|
||||||
|
// Helper context to determine if the ContextWrapper is already present.
|
||||||
|
const ContextCheckContext = createContext(false);
|
||||||
|
|
||||||
|
export const ContextWrapper = ({ children }) => {
|
||||||
|
const isWrapped = useContext(ContextCheckContext);
|
||||||
|
|
||||||
|
// Check if we are already inside a Router
|
||||||
|
let location;
|
||||||
|
try {
|
||||||
|
location = useLocation();
|
||||||
|
} catch (e) {
|
||||||
|
location = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 1: Only the Router is missing, but ContextWrapper is already provided
|
||||||
|
if (!location && isWrapped) {
|
||||||
|
console.log("Router is not present, but ContextWrapper is already provided. Wrapping with Router only.");
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
{children}
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 2: Neither Router nor ContextWrapper is present
|
||||||
|
if (!location && !isWrapped) {
|
||||||
|
console.log("Router and ContextWrapper are not present. Wrapping both.");
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<ContextCheckContext.Provider value={true}>
|
||||||
|
<Wrappers>
|
||||||
|
{children}
|
||||||
|
</Wrappers>
|
||||||
|
</ContextCheckContext.Provider>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 3: Only ContextWrapper is missing
|
||||||
|
if (location && !isWrapped) {
|
||||||
|
console.log("ContextWrapper is not present. Wrapping ContextWrapper.");
|
||||||
|
return (
|
||||||
|
<ContextCheckContext.Provider value={true}>
|
||||||
|
<Wrappers>{children}</Wrappers>
|
||||||
|
</ContextCheckContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case 4: Both Router and ContextWrapper are already present
|
||||||
|
console.log("Router and ContextWrapper are already present.");
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Wrappers = ({ children }) => {
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PermissionsProvider initialPermissions={[]}>
|
||||||
|
<TagsProvider initialTags={[]}>
|
||||||
|
<LayersProvider initialLayers={[]}>
|
||||||
|
<FilterProvider initialTags={[]}>
|
||||||
|
<ItemsProvider initialItems={[]}>
|
||||||
|
<SelectPositionProvider>
|
||||||
|
<LeafletRefsProvider initialLeafletRefs={{}}>
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<AssetsProvider>
|
||||||
|
<ClusterRefProvider>
|
||||||
|
<QuestsProvider initialOpen={true}>
|
||||||
|
{children}
|
||||||
|
</QuestsProvider>
|
||||||
|
</ClusterRefProvider>
|
||||||
|
</AssetsProvider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
</LeafletRefsProvider>
|
||||||
|
</SelectPositionProvider>
|
||||||
|
</ItemsProvider>
|
||||||
|
</FilterProvider>
|
||||||
|
</LayersProvider>
|
||||||
|
</TagsProvider>
|
||||||
|
</PermissionsProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,159 +1,13 @@
|
|||||||
import { TileLayer, MapContainer, useMapEvents, GeoJSON } from "react-leaflet";
|
import { UtopiaMapProps } from "../../types";
|
||||||
import "leaflet/dist/leaflet.css";
|
import { ContextWrapper } from "../AppShell/ContextWrapper";
|
||||||
import * as React from "react";
|
import { UtopiaMapInner } from "./UtopiaMapInner";
|
||||||
import { UtopiaMapProps } from "../../types"
|
|
||||||
import "./UtopiaMap.css"
|
|
||||||
import { LatLng } from "leaflet";
|
|
||||||
import MarkerClusterGroup from 'react-leaflet-cluster'
|
|
||||||
import AddButton from "./Subcomponents/AddButton";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
|
||||||
import { SearchControl } from "./Subcomponents/Controls/SearchControl";
|
|
||||||
import { Control } from "./Subcomponents/Controls/Control";
|
|
||||||
import { BrowserRouter, Outlet, useLocation } from "react-router-dom";
|
|
||||||
import { TagsControl } from "./Subcomponents/Controls/TagsControl";
|
|
||||||
import { useSelectPosition, useSetMapClicked, useSetSelectPosition } from "./hooks/useSelectPosition";
|
|
||||||
import { useClusterRef, useSetClusterRef } from "./hooks/useClusterRef";
|
|
||||||
import { Feature, Geometry as GeoJSONGeometry } from 'geojson';
|
|
||||||
import { FilterControl } from "./Subcomponents/Controls/FilterControl";
|
|
||||||
import { LayerControl } from "./Subcomponents/Controls/LayerControl";
|
|
||||||
import { useLayers } from "./hooks/useLayers";
|
|
||||||
import { useAddVisibleLayer } from "./hooks/useFilter";
|
|
||||||
import { GratitudeControl } from "./Subcomponents/Controls/GratitudeControl";
|
|
||||||
import { SelectPosition } from "./Subcomponents/SelectPosition";
|
|
||||||
|
|
||||||
const mapDivRef = React.createRef();
|
|
||||||
|
|
||||||
export const Router = ({ children }) => {
|
|
||||||
let location;
|
|
||||||
try {
|
|
||||||
location = useLocation();
|
|
||||||
} catch (e) {
|
|
||||||
location = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!location) {
|
|
||||||
return (
|
|
||||||
<BrowserRouter>
|
|
||||||
{children}
|
|
||||||
</BrowserRouter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function UtopiaMap({
|
|
||||||
height = "500px",
|
|
||||||
width = "100%",
|
|
||||||
center = [50.6, 9.5],
|
|
||||||
zoom = 10,
|
|
||||||
children,
|
|
||||||
geo,
|
|
||||||
showFilterControl = false,
|
|
||||||
showGratitudeControl = false,
|
|
||||||
showLayerControl = true
|
|
||||||
}
|
|
||||||
: UtopiaMapProps) {
|
|
||||||
|
|
||||||
function MapEventListener() {
|
|
||||||
useMapEvents({
|
|
||||||
click: (e) => {
|
|
||||||
resetMetaTags();
|
|
||||||
console.log(e.latlng.lat + ',' + e.latlng.lng);
|
|
||||||
selectNewItemPosition && setMapClicked({ position: e.latlng, setItemFormPopup: setItemFormPopup })
|
|
||||||
},
|
|
||||||
moveend: () => {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetMetaTags = () => {
|
|
||||||
let params = new URLSearchParams(window.location.search);
|
|
||||||
if (!location.pathname.includes("/item/")) {
|
|
||||||
window.history.pushState({}, "", `/` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
|
||||||
}
|
|
||||||
document.title = document.title.split("-")[0];
|
|
||||||
document.querySelector('meta[property="og:title"]')?.setAttribute("content", document.title);
|
|
||||||
document.querySelector('meta[property="og:description"]')?.setAttribute("content", `${document.querySelector('meta[name="description"]')?.getAttribute("content")}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const selectNewItemPosition = useSelectPosition();
|
|
||||||
const setSelectNewItemPosition = useSetSelectPosition();
|
|
||||||
const setClusterRef = useSetClusterRef();
|
|
||||||
const clusterRef = useClusterRef();
|
|
||||||
const setMapClicked = useSetMapClicked();
|
|
||||||
|
|
||||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const layers = useLayers();
|
|
||||||
const addVisibleLayer = useAddVisibleLayer();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
layers.map(l => addVisibleLayer(l))
|
|
||||||
|
|
||||||
}, [layers])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const onEachFeature = (feature: Feature<GeoJSONGeometry, any>, layer: L.Layer) => {
|
|
||||||
if (feature.properties) {
|
|
||||||
layer.bindPopup(feature.properties.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function UtopiaMap(props: UtopiaMapProps) {
|
||||||
return (
|
return (
|
||||||
<Router>
|
<ContextWrapper>
|
||||||
<div className={`tw-h-full ${(selectNewItemPosition != null ? "crosshair-cursor-enabled" : undefined)}`}>
|
<UtopiaMapInner {...props} />
|
||||||
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={new LatLng(center[0], center[1])} zoom={zoom} zoomControl={false} maxZoom={19}>
|
</ContextWrapper>
|
||||||
<Outlet></Outlet>
|
|
||||||
<Control position='topLeft' zIndex="1000" absolute>
|
|
||||||
<SearchControl />
|
|
||||||
<TagsControl />
|
|
||||||
</Control>
|
|
||||||
<Control position='bottomLeft' zIndex="999" absolute>
|
|
||||||
{/*{!embedded && (*/}
|
|
||||||
{/* <QuestControl></QuestControl>*/}
|
|
||||||
{/*)}*/}
|
|
||||||
{showFilterControl && <FilterControl />}
|
|
||||||
{/*todo: needed layer handling is located LayerControl*/}
|
|
||||||
{showLayerControl && <LayerControl></LayerControl>}
|
|
||||||
{showGratitudeControl && <GratitudeControl/>}
|
|
||||||
</Control>
|
|
||||||
<TileLayer
|
|
||||||
maxZoom={19}
|
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
||||||
url="https://tile.osmand.net/hd/{z}/{x}/{y}.png" />
|
|
||||||
<MarkerClusterGroup ref={(r) => setClusterRef(r)} showCoverageOnHover chunkedLoading maxClusterRadius={50} removeOutsideVisibleBounds={false}>
|
|
||||||
{
|
|
||||||
React.Children.toArray(children).map((child) =>
|
|
||||||
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>, itemFormPopup: ItemFormPopupProps | null, clusterRef: React.MutableRefObject<undefined> }>(child) ?
|
|
||||||
React.cloneElement(child, { setItemFormPopup: setItemFormPopup, itemFormPopup: itemFormPopup, clusterRef: clusterRef }) : child
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</MarkerClusterGroup>
|
|
||||||
{geo && <GeoJSON data={geo} onEachFeature={onEachFeature} eventHandlers={{
|
|
||||||
click: (e) => {
|
|
||||||
selectNewItemPosition && e.layer!.closePopup();
|
|
||||||
selectNewItemPosition && setMapClicked({ position: e.latlng, setItemFormPopup: setItemFormPopup })
|
|
||||||
},
|
|
||||||
}} />}
|
|
||||||
<MapEventListener />
|
|
||||||
</MapContainer>
|
|
||||||
<AddButton triggerAction={setSelectNewItemPosition}></AddButton>
|
|
||||||
{selectNewItemPosition != null &&
|
|
||||||
<SelectPosition setSelectNewItemPosition={setSelectNewItemPosition} />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Router>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { UtopiaMap };
|
export { UtopiaMap };
|
||||||
|
|||||||
131
src/Components/Map/UtopiaMapInner.tsx
Normal file
131
src/Components/Map/UtopiaMapInner.tsx
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { TileLayer, MapContainer, useMapEvents, GeoJSON } from "react-leaflet";
|
||||||
|
import "leaflet/dist/leaflet.css";
|
||||||
|
import * as React from "react";
|
||||||
|
import { UtopiaMapProps } from "../../types";
|
||||||
|
import "./UtopiaMap.css";
|
||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import MarkerClusterGroup from 'react-leaflet-cluster';
|
||||||
|
import AddButton from "./Subcomponents/AddButton";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
||||||
|
import { SearchControl } from "./Subcomponents/Controls/SearchControl";
|
||||||
|
import { Control } from "./Subcomponents/Controls/Control";
|
||||||
|
import { Outlet } from "react-router-dom";
|
||||||
|
import { TagsControl } from "./Subcomponents/Controls/TagsControl";
|
||||||
|
import { useSelectPosition, useSetMapClicked, useSetSelectPosition } from "./hooks/useSelectPosition";
|
||||||
|
import { useClusterRef, useSetClusterRef } from "./hooks/useClusterRef";
|
||||||
|
import { Feature, Geometry as GeoJSONGeometry } from 'geojson';
|
||||||
|
import { FilterControl } from "./Subcomponents/Controls/FilterControl";
|
||||||
|
import { LayerControl } from "./Subcomponents/Controls/LayerControl";
|
||||||
|
import { useLayers } from "./hooks/useLayers";
|
||||||
|
import { useAddVisibleLayer } from "./hooks/useFilter";
|
||||||
|
import { GratitudeControl } from "./Subcomponents/Controls/GratitudeControl";
|
||||||
|
import { SelectPosition } from "./Subcomponents/SelectPosition";
|
||||||
|
|
||||||
|
const mapDivRef = React.createRef();
|
||||||
|
|
||||||
|
export function UtopiaMapInner({
|
||||||
|
height = "500px",
|
||||||
|
width = "100%",
|
||||||
|
center = [50.6, 9.5],
|
||||||
|
zoom = 10,
|
||||||
|
children,
|
||||||
|
geo,
|
||||||
|
showFilterControl = false,
|
||||||
|
showGratitudeControl = false,
|
||||||
|
showLayerControl = true
|
||||||
|
}: UtopiaMapProps) {
|
||||||
|
|
||||||
|
// Hooks that rely on contexts, called after ContextWrapper is provided
|
||||||
|
const selectNewItemPosition = useSelectPosition();
|
||||||
|
const setSelectNewItemPosition = useSetSelectPosition();
|
||||||
|
const setClusterRef = useSetClusterRef();
|
||||||
|
const clusterRef = useClusterRef();
|
||||||
|
const setMapClicked = useSetMapClicked();
|
||||||
|
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||||
|
|
||||||
|
const layers = useLayers();
|
||||||
|
const addVisibleLayer = useAddVisibleLayer();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
layers.forEach(layer => addVisibleLayer(layer));
|
||||||
|
}, [layers]);
|
||||||
|
|
||||||
|
function MapEventListener() {
|
||||||
|
useMapEvents({
|
||||||
|
click: (e) => {
|
||||||
|
resetMetaTags();
|
||||||
|
console.log(e.latlng.lat + ',' + e.latlng.lng);
|
||||||
|
if (selectNewItemPosition) {
|
||||||
|
setMapClicked({ position: e.latlng, setItemFormPopup: setItemFormPopup });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
moveend: () => { }
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetMetaTags = () => {
|
||||||
|
let params = new URLSearchParams(window.location.search);
|
||||||
|
if (!window.location.pathname.includes("/item/")) {
|
||||||
|
window.history.pushState({}, "", `/` + `${params.toString() !== "" ? `?${params}` : ""}`);
|
||||||
|
}
|
||||||
|
document.title = document.title.split("-")[0];
|
||||||
|
document.querySelector('meta[property="og:title"]')?.setAttribute("content", document.title);
|
||||||
|
document.querySelector('meta[property="og:description"]')?.setAttribute("content", `${document.querySelector('meta[name="description"]')?.getAttribute("content")}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEachFeature = (feature: Feature<GeoJSONGeometry, any>, layer: L.Layer) => {
|
||||||
|
if (feature.properties) {
|
||||||
|
layer.bindPopup(feature.properties.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`tw-h-full ${selectNewItemPosition != null ? "crosshair-cursor-enabled" : undefined}`}>
|
||||||
|
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={new LatLng(center[0], center[1])} zoom={zoom} zoomControl={false} maxZoom={19}>
|
||||||
|
<Outlet />
|
||||||
|
<Control position="topLeft" zIndex="1000" absolute>
|
||||||
|
<SearchControl />
|
||||||
|
<TagsControl />
|
||||||
|
</Control>
|
||||||
|
<Control position="bottomLeft" zIndex="999" absolute>
|
||||||
|
{showFilterControl && <FilterControl />}
|
||||||
|
{showLayerControl && <LayerControl />}
|
||||||
|
{showGratitudeControl && <GratitudeControl />}
|
||||||
|
</Control>
|
||||||
|
<TileLayer
|
||||||
|
maxZoom={19}
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url="https://tile.osmand.net/hd/{z}/{x}/{y}.png"
|
||||||
|
/>
|
||||||
|
<MarkerClusterGroup ref={(r) => setClusterRef(r)} showCoverageOnHover chunkedLoading maxClusterRadius={50} removeOutsideVisibleBounds={false}>
|
||||||
|
{
|
||||||
|
React.Children.toArray(children).map((child) =>
|
||||||
|
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>, itemFormPopup: ItemFormPopupProps | null, clusterRef: React.MutableRefObject<undefined> }>(child)
|
||||||
|
? React.cloneElement(child, { setItemFormPopup: setItemFormPopup, itemFormPopup: itemFormPopup, clusterRef: clusterRef })
|
||||||
|
: child
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</MarkerClusterGroup>
|
||||||
|
{geo && (
|
||||||
|
<GeoJSON
|
||||||
|
data={geo}
|
||||||
|
onEachFeature={onEachFeature}
|
||||||
|
eventHandlers={{
|
||||||
|
click: (e) => {
|
||||||
|
if (selectNewItemPosition) {
|
||||||
|
e.layer!.closePopup();
|
||||||
|
setMapClicked({ position: e.latlng, setItemFormPopup: setItemFormPopup });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<MapEventListener />
|
||||||
|
</MapContainer>
|
||||||
|
<AddButton triggerAction={setSelectNewItemPosition} />
|
||||||
|
{selectNewItemPosition != null && <SelectPosition setSelectNewItemPosition={setSelectNewItemPosition} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user