mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
update item position, item parents, focus item on profile load an open cluster
This commit is contained in:
parent
c77972a3be
commit
8484379113
@ -13,7 +13,8 @@ import { FilterProvider } from '../Map/hooks/useFilter'
|
|||||||
import { ItemsProvider } from '../Map/hooks/useItems'
|
import { ItemsProvider } from '../Map/hooks/useItems'
|
||||||
import { LayersProvider } from '../Map/hooks/useLayers'
|
import { LayersProvider } from '../Map/hooks/useLayers'
|
||||||
import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
import { LeafletRefsProvider } from '../Map/hooks/useLeafletRefs'
|
||||||
import { SelectPositionProvider } from '../Map/hooks/useSetItemPosition'
|
import { SelectPositionProvider } from '../Map/hooks/useSelectPosition'
|
||||||
|
import { ClusterRefProvider } from '../Map/hooks/useClusterRef'
|
||||||
|
|
||||||
export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
|
export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
|
||||||
|
|
||||||
@ -32,23 +33,25 @@ export function AppShell({ appName, nameWidth, children, assetsApi }: { appName:
|
|||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AssetsProvider>
|
<AssetsProvider>
|
||||||
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
|
<ClusterRefProvider>
|
||||||
<QuestsProvider initialOpen={true}>
|
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
|
||||||
<ToastContainer position="top-right"
|
<QuestsProvider initialOpen={true}>
|
||||||
autoClose={2000}
|
<ToastContainer position="top-right"
|
||||||
hideProgressBar
|
autoClose={2000}
|
||||||
newestOnTop={false}
|
hideProgressBar
|
||||||
closeOnClick
|
newestOnTop={false}
|
||||||
rtl={false}
|
closeOnClick
|
||||||
pauseOnFocusLoss
|
rtl={false}
|
||||||
draggable
|
pauseOnFocusLoss
|
||||||
pauseOnHover
|
draggable
|
||||||
theme="light" />
|
pauseOnHover
|
||||||
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
|
theme="light" />
|
||||||
<div id="app-content" className="tw-flex tw-!pl-[77px]">
|
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
|
||||||
{children}
|
<div id="app-content" className="tw-flex tw-!pl-[77px]">
|
||||||
</div>
|
{children}
|
||||||
</QuestsProvider>
|
</div>
|
||||||
|
</QuestsProvider>
|
||||||
|
</ClusterRefProvider>
|
||||||
</AssetsProvider>
|
</AssetsProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
|||||||
import { useAllItemsLoaded, useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
import { useAllItemsLoaded, useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
||||||
import { useFilterTags, useIsLayerVisible } from './hooks/useFilter'
|
import { useFilterTags, useIsLayerVisible, useResetFilterTags } from './hooks/useFilter'
|
||||||
import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } from './hooks/useTags'
|
import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } from './hooks/useTags'
|
||||||
import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs'
|
import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs'
|
||||||
import { Popup } from 'leaflet'
|
import { Popup } from 'leaflet'
|
||||||
@ -15,6 +15,7 @@ import { getValue } from '../../Utils/GetValue'
|
|||||||
import { hashTagRegex } from '../../Utils/HashTagRegex'
|
import { hashTagRegex } from '../../Utils/HashTagRegex'
|
||||||
import { randomColor } from '../../Utils/RandomColor'
|
import { randomColor } from '../../Utils/RandomColor'
|
||||||
import { encodeTag } from '../../Utils/FormatTags'
|
import { encodeTag } from '../../Utils/FormatTags'
|
||||||
|
import { useSetMarkerClicked } from './hooks/useSelectPosition'
|
||||||
|
|
||||||
export const Layer = ({
|
export const Layer = ({
|
||||||
data,
|
data,
|
||||||
@ -27,6 +28,7 @@ export const Layer = ({
|
|||||||
markerShape = 'circle',
|
markerShape = 'circle',
|
||||||
markerDefaultColor = '#777',
|
markerDefaultColor = '#777',
|
||||||
api,
|
api,
|
||||||
|
itemType,
|
||||||
itemNameField = 'name',
|
itemNameField = 'name',
|
||||||
itemTextField = 'text',
|
itemTextField = 'text',
|
||||||
itemAvatarField,
|
itemAvatarField,
|
||||||
@ -53,12 +55,15 @@ export const Layer = ({
|
|||||||
const addMarker = useAddMarker();
|
const addMarker = useAddMarker();
|
||||||
const addPopup = useAddPopup();
|
const addPopup = useAddPopup();
|
||||||
const leafletRefs = useLeafletRefs();
|
const leafletRefs = useLeafletRefs();
|
||||||
|
const resetFilterTags = useResetFilterTags();
|
||||||
|
|
||||||
let location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const allTagsLoaded = useAllTagsLoaded();
|
const allTagsLoaded = useAllTagsLoaded();
|
||||||
const allItemsLoaded = useAllItemsLoaded();
|
const allItemsLoaded = useAllItemsLoaded();
|
||||||
|
|
||||||
|
const setMarkerClicked = useSetMarkerClicked();
|
||||||
|
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
const addTag = useAddTag();
|
const addTag = useAddTag();
|
||||||
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([]);
|
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([]);
|
||||||
@ -71,16 +76,16 @@ export const Layer = ({
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
data && setItemsData({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, itemTagsField, itemOffersField, itemNeedsField, onlyOnePerOwner, customEditLink, setItemFormPopup, itemFormPopup, clusterRef });
|
data && setItemsData({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemType, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, itemTagsField, itemOffersField, itemNeedsField, onlyOnePerOwner, customEditLink, setItemFormPopup, itemFormPopup, clusterRef });
|
||||||
api && setItemsApi({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, itemTagsField, itemOffersField, itemNeedsField, onlyOnePerOwner, customEditLink, setItemFormPopup, itemFormPopup, clusterRef });
|
api && setItemsApi({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemType, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, itemTagsField, itemOffersField, itemNeedsField, onlyOnePerOwner, customEditLink, setItemFormPopup, itemFormPopup, clusterRef });
|
||||||
}, [data, api])
|
}, [data, api])
|
||||||
|
|
||||||
useMapEvents({
|
useMapEvents({
|
||||||
popupopen: (e) => {
|
popupopen: (e) => {
|
||||||
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
|
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) {
|
if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
|
||||||
let params = new URLSearchParams(window.location.search);
|
let params = new URLSearchParams(window.location.search);
|
||||||
window.history.pushState({}, "", `/${name}/${item.id}`+ `${params.toString() !== "" ? `?${params}` : ""}`)
|
window.history.pushState({}, "", `/${name}/${item.id}` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
||||||
let title = "";
|
let title = "";
|
||||||
if (item.name) title = item.name;
|
if (item.name) title = item.name;
|
||||||
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
||||||
@ -98,8 +103,9 @@ export const Layer = ({
|
|||||||
if (window.location.pathname.split("/")[2]) {
|
if (window.location.pathname.split("/")[2]) {
|
||||||
const id = window.location.pathname.split("/")[2]
|
const id = window.location.pathname.split("/")[2]
|
||||||
const marker = leafletRefs[id]?.marker;
|
const marker = leafletRefs[id]?.marker;
|
||||||
if (marker && marker != null) {
|
resetFilterTags();
|
||||||
marker !== null && clusterRef?.current?.zoomToShowLayer(marker, () => {
|
if (marker && filterTags.length == 0) {
|
||||||
|
marker !== null && clusterRef?.zoomToShowLayer(marker, () => {
|
||||||
marker.openPopup();
|
marker.openPopup();
|
||||||
});
|
});
|
||||||
const item = leafletRefs[id]?.item;
|
const item = leafletRefs[id]?.item;
|
||||||
@ -113,7 +119,6 @@ export const Layer = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -157,7 +162,7 @@ export const Layer = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (allTagsLoaded && allItemsLoaded) {
|
if (allTagsLoaded && allItemsLoaded) {
|
||||||
item[itemTextField].match(hashTagRegex)?.map(tag => {
|
item[itemTextField].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())) {
|
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() };
|
const newTag = { id: crypto.randomUUID(), name: tag.slice(1), color: randomColor() };
|
||||||
@ -188,7 +193,13 @@ export const Layer = ({
|
|||||||
<Marker ref={(r) => {
|
<Marker ref={(r) => {
|
||||||
if (!(item.id in leafletRefs && leafletRefs[item.id].marker == r))
|
if (!(item.id in leafletRefs && leafletRefs[item.id].marker == r))
|
||||||
r && addMarker(item, r);
|
r && addMarker(item, r);
|
||||||
}} icon={MarkerIconFactory(markerShape, color1, color2, markerIcon)} key={item.id} position={[latitude, longitude]}>
|
}}
|
||||||
|
eventHandlers={{
|
||||||
|
click: () => {
|
||||||
|
setMarkerClicked(item)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
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") ?
|
(children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ?
|
||||||
React.Children.toArray(children).map((child) =>
|
React.Children.toArray(children).map((child) =>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useAddFilterTag, useFilterTags, useResetFilterTags, useSetSearchPhrase } from '../../hooks/useFilter'
|
import { useAddFilterTag, useFilterTags, useResetFilterTags } from '../../hooks/useFilter'
|
||||||
import useWindowDimensions from '../../hooks/useWindowDimension';
|
import useWindowDimensions from '../../hooks/useWindowDimension';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
@ -15,10 +15,11 @@ import * as L from 'leaflet';
|
|||||||
import MarkerIconFactory from '../../../../Utils/MarkerIconFactory';
|
import MarkerIconFactory from '../../../../Utils/MarkerIconFactory';
|
||||||
import { decodeTag } from '../../../../Utils/FormatTags';
|
import { decodeTag } from '../../../../Utils/FormatTags';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useClusterRef } from '../../hooks/useClusterRef';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const SearchControl = ({ clusterRef }) => {
|
export const SearchControl = () => {
|
||||||
|
|
||||||
const windowDimensions = useWindowDimensions();
|
const windowDimensions = useWindowDimensions();
|
||||||
const [popupOpen, setPopupOpen] = useState(false);
|
const [popupOpen, setPopupOpen] = useState(false);
|
||||||
@ -36,6 +37,7 @@ export const SearchControl = ({ clusterRef }) => {
|
|||||||
const addFilterTag = useAddFilterTag();
|
const addFilterTag = useAddFilterTag();
|
||||||
const resetFilterTags = useResetFilterTags();
|
const resetFilterTags = useResetFilterTags();
|
||||||
const filterTags = useFilterTags();
|
const filterTags = useFilterTags();
|
||||||
|
const clusterRef = useClusterRef();
|
||||||
|
|
||||||
useMapEvents({
|
useMapEvents({
|
||||||
popupopen: () => {
|
popupopen: () => {
|
||||||
@ -115,17 +117,9 @@ export const SearchControl = ({ clusterRef }) => {
|
|||||||
<div key={item.id} className='tw-cursor-pointer hover:tw-font-bold' onClick={() => {
|
<div key={item.id} className='tw-cursor-pointer hover:tw-font-bold' onClick={() => {
|
||||||
const marker = Object.entries(leafletRefs).find(r => r[1].item == item)?.[1].marker;
|
const marker = Object.entries(leafletRefs).find(r => r[1].item == item)?.[1].marker;
|
||||||
if(marker){
|
if(marker){
|
||||||
if (filterTags.length > 0) {
|
navigate(`/${item.layer.name}/${item.id}`)
|
||||||
marker !== null && window.history.pushState({}, "", `/${item.layer.name}/${item.id}`)
|
|
||||||
resetFilterTags();
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
marker !== null && clusterRef?.current?.zoomToShowLayer(marker, () => {
|
|
||||||
marker?.openPopup();
|
|
||||||
hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
navigate("item/"+item.id)
|
navigate("item/"+item.id)
|
||||||
|
|||||||
@ -96,7 +96,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
const item = items.find(item => item.layer == props.layer && item.user_created?.id == user?.id);
|
const item = items.find(item => item.layer == props.layer && item.user_created?.id == user?.id);
|
||||||
item && removeItem(item);
|
item && removeItem(item);
|
||||||
}
|
}
|
||||||
addItem({...formItem, id: uuid, layer: props.layer, user_created: user});
|
addItem({...formItem, id: uuid, layer: props.layer, user_created: user, type: props.layer.itemType });
|
||||||
toast.success("New item created");
|
toast.success("New item created");
|
||||||
resetFilterTags();
|
resetFilterTags();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,16 +5,17 @@ import { getValue } from "../../../../Utils/GetValue";
|
|||||||
import { useAssetApi } from '../../../AppShell/hooks/useAssets'
|
import { useAssetApi } from '../../../AppShell/hooks/useAssets'
|
||||||
import DialogModal from "../../../Templates/DialogModal";
|
import DialogModal from "../../../Templates/DialogModal";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useSetSelectPosition } from "../../hooks/useSetItemPosition";
|
import { useMap } from "react-leaflet";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function HeaderView({ item, api, editCallback, deleteCallback, itemNameField, itemAvatarField, loading, hideMenu = false, big = false, updatePosition = false }: {
|
export function HeaderView({ item, api, editCallback, deleteCallback, setPositionCallback, itemNameField, itemAvatarField, loading, hideMenu = false, big = false }: {
|
||||||
item: Item,
|
item: Item,
|
||||||
api?: ItemsApi<any>,
|
api?: ItemsApi<any>,
|
||||||
editCallback?: any,
|
editCallback?: any,
|
||||||
deleteCallback?: any,
|
deleteCallback?: any,
|
||||||
|
setPositionCallback?: any,
|
||||||
itemNameField?: string,
|
itemNameField?: string,
|
||||||
itemAvatarField?: string,
|
itemAvatarField?: string,
|
||||||
loading?: boolean,
|
loading?: boolean,
|
||||||
@ -29,7 +30,6 @@ export function HeaderView({ item, api, editCallback, deleteCallback, itemNameFi
|
|||||||
const hasUserPermission = useHasUserPermission();
|
const hasUserPermission = useHasUserPermission();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const assetsApi = useAssetApi();
|
const assetsApi = useAssetApi();
|
||||||
const setSelectPosition = useSetSelectPosition();
|
|
||||||
|
|
||||||
const avatar = itemAvatarField && getValue(item, itemAvatarField) ? assetsApi.url + getValue(item, itemAvatarField) + `${big ? "?width=160&heigth=160": "?width=80&heigth=80"}` : item.layer?.itemAvatarField && item && getValue(item, item.layer?.itemAvatarField) && assetsApi.url + getValue(item, item.layer?.itemAvatarField) + `${big ? "?width=160&heigth=160": "?width=80&heigth=80"}`;
|
const avatar = itemAvatarField && getValue(item, itemAvatarField) ? assetsApi.url + getValue(item, itemAvatarField) + `${big ? "?width=160&heigth=160": "?width=80&heigth=80"}` : item.layer?.itemAvatarField && item && getValue(item, item.layer?.itemAvatarField) && assetsApi.url + getValue(item, item.layer?.itemAvatarField) + `${big ? "?width=160&heigth=160": "?width=80&heigth=80"}`;
|
||||||
const title = itemNameField ? getValue(item, itemNameField) : item.layer?.itemNameField && item && getValue(item, item.layer?.itemNameField);
|
const title = itemNameField ? getValue(item, itemNameField) : item.layer?.itemNameField && item && getValue(item, item.layer?.itemNameField);
|
||||||
@ -63,21 +63,21 @@ export function HeaderView({ item, api, editCallback, deleteCallback, itemNameFi
|
|||||||
</svg>
|
</svg>
|
||||||
</label>
|
</label>
|
||||||
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000">
|
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-z-1000">
|
||||||
{((api?.updateItem && hasUserPermission(api.collectionName!, "update", item)) || item.layer?.customEditLink) && <li>
|
{((api?.updateItem && hasUserPermission(api.collectionName!, "update", item)) || item.layer?.customEditLink) && editCallback && <li>
|
||||||
<a className="!tw-text-base-content tw-cursor-pointer" onClick={() => item.layer?.customEditLink? navigate(item.layer.customEditLink) : editCallback}>
|
<a className="!tw-text-base-content tw-cursor-pointer" onClick={() => item.layer?.customEditLink? navigate(item.layer.customEditLink) : editCallback()}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>}
|
</li>}
|
||||||
{((api?.updateItem && hasUserPermission(api.collectionName!, "update", item))) && updatePosition &&<li>
|
{((api?.updateItem && hasUserPermission(api.collectionName!, "update", item))) && setPositionCallback &&<li>
|
||||||
<a className="!tw-text-base-content tw-cursor-pointer" onClick={() => {setSelectPosition(item), navigate("/")}}>
|
<a className="!tw-text-base-content tw-cursor-pointer" onClick={setPositionCallback}>
|
||||||
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 512 512" className="tw-w-5 tw-h-5" xmlns="http://www.w3.org/2000/svg">
|
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 512 512" className="tw-w-5 tw-h-5" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M256 0c17.7 0 32 14.3 32 32V42.4c93.7 13.9 167.7 88 181.6 181.6H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H469.6c-13.9 93.7-88 167.7-181.6 181.6V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V469.6C130.3 455.7 56.3 381.7 42.4 288H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H42.4C56.3 130.3 130.3 56.3 224 42.4V32c0-17.7 14.3-32 32-32zM107.4 288c12.5 58.3 58.4 104.1 116.6 116.6V384c0-17.7 14.3-32 32-32s32 14.3 32 32v20.6c58.3-12.5 104.1-58.4 116.6-116.6H384c-17.7 0-32-14.3-32-32s14.3-32 32-32h20.6C392.1 165.7 346.3 119.9 288 107.4V128c0 17.7-14.3 32-32 32s-32-14.3-32-32V107.4C165.7 119.9 119.9 165.7 107.4 224H128c17.7 0 32 14.3 32 32s-14.3 32-32 32H107.4zM256 224a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"></path>
|
<path d="M256 0c17.7 0 32 14.3 32 32V42.4c93.7 13.9 167.7 88 181.6 181.6H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H469.6c-13.9 93.7-88 167.7-181.6 181.6V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V469.6C130.3 455.7 56.3 381.7 42.4 288H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H42.4C56.3 130.3 130.3 56.3 224 42.4V32c0-17.7 14.3-32 32-32zM107.4 288c12.5 58.3 58.4 104.1 116.6 116.6V384c0-17.7 14.3-32 32-32s32 14.3 32 32v20.6c58.3-12.5 104.1-58.4 116.6-116.6H384c-17.7 0-32-14.3-32-32s14.3-32 32-32h20.6C392.1 165.7 346.3 119.9 288 107.4V128c0 17.7-14.3 32-32 32s-32-14.3-32-32V107.4C165.7 119.9 119.9 165.7 107.4 224H128c17.7 0 32 14.3 32 32s-14.3 32-32 32H107.4zM256 224a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</li>}
|
</li>}
|
||||||
{api?.deleteItem && hasUserPermission(api.collectionName!, "delete", item) && <li>
|
{api?.deleteItem && hasUserPermission(api.collectionName!, "delete", item) && deleteCallback &&<li>
|
||||||
<a className='tw-cursor-pointer !tw-text-error' onClick={openDeleteModal}>
|
<a className='tw-cursor-pointer !tw-text-error' onClick={openDeleteModal}>
|
||||||
{loading ? <span className="tw-loading tw-loading-spinner tw-loading-sm"></span>
|
{loading ? <span className="tw-loading tw-loading-spinner tw-loading-sm"></span>
|
||||||
:
|
:
|
||||||
|
|||||||
@ -4,8 +4,11 @@ import { getValue } from '../../../../Utils/GetValue'
|
|||||||
import { Item } from '../../../../types'
|
import { Item } from '../../../../types'
|
||||||
|
|
||||||
export const PopupButton = ({url, parameterField, text, colorField, item} : {url: string, parameterField?: string, text: string, colorField?: string, item? : Item}) => {
|
export const PopupButton = ({url, parameterField, text, colorField, item} : {url: string, parameterField?: string, text: string, colorField?: string, item? : Item}) => {
|
||||||
|
let params = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={`${url}/${parameterField? getValue(item,parameterField):``}`}><button style={{backgroundColor: `${colorField && getValue(item,colorField)? getValue(item,colorField) : item?.layer?.markerDefaultColor}`}} className="tw-btn tw-text-white tw-btn-sm tw-float-right tw-mt-1">{text}</button></Link>
|
<Link to={`${url}/${parameterField? getValue(item,parameterField):``}?${params}`}><button style={{backgroundColor: `${colorField && getValue(item,colorField)? getValue(item,colorField) : item?.layer?.markerDefaultColor}`}} className="tw-btn tw-text-white tw-btn-sm tw-float-right tw-mt-1">{text}</button></Link>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { LatLng } from 'leaflet'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useRemoveItem } from '../hooks/useItems'
|
import { useRemoveItem } from '../hooks/useItems'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
import { useSetSelectPosition } from '../hooks/useSelectPosition'
|
||||||
|
|
||||||
|
|
||||||
export interface ItemViewPopupProps {
|
export interface ItemViewPopupProps {
|
||||||
@ -26,6 +27,7 @@ export const ItemViewPopup = React.forwardRef((props: ItemViewPopupProps, ref: a
|
|||||||
const [loading, setLoading] = React.useState<boolean>(false);
|
const [loading, setLoading] = React.useState<boolean>(false);
|
||||||
const removeItem = useRemoveItem();
|
const removeItem = useRemoveItem();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setSelectPosition = useSetSelectPosition();
|
||||||
|
|
||||||
|
|
||||||
const [infoExpanded, setInfoExpanded] = useState<Boolean>(false);
|
const [infoExpanded, setInfoExpanded] = useState<Boolean>(false);
|
||||||
@ -61,7 +63,7 @@ export const ItemViewPopup = React.forwardRef((props: ItemViewPopupProps, ref: a
|
|||||||
return (
|
return (
|
||||||
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
|
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
|
||||||
<div className='tw-bg-base-100 tw-text-base-content'>
|
<div className='tw-bg-base-100 tw-text-base-content'>
|
||||||
<HeaderView api={props.item.layer?.api} item={props.item} editCallback={handleEdit} deleteCallback={handleDelete} updatePosition/>
|
<HeaderView api={props.item.layer?.api} item={props.item} editCallback={handleEdit} deleteCallback={handleDelete} setPositionCallback={()=>{map.closePopup();setSelectPosition(props.item); navigate("/")}} loading={loading} />
|
||||||
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'>
|
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'>
|
||||||
{props.children ?
|
{props.children ?
|
||||||
|
|
||||||
|
|||||||
@ -14,9 +14,10 @@ import { QuestControl } from "./Subcomponents/Controls/QuestControl";
|
|||||||
import { Control } from "./Subcomponents/Controls/Control";
|
import { Control } from "./Subcomponents/Controls/Control";
|
||||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
import { TagsControl } from "./Subcomponents/Controls/TagsControl";
|
import { TagsControl } from "./Subcomponents/Controls/TagsControl";
|
||||||
import { useSelectPosition, useSetSelectPosition } from "./hooks/useSetItemPosition";
|
import { useSelectPosition, useSetSelectPosition } from "./hooks/useSelectPosition";
|
||||||
import { useUpdateItem } from "./hooks/useItems";
|
import { useUpdateItem } from "./hooks/useItems";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import { useClusterRef, useSetClusterRef } from "./hooks/useClusterRef";
|
||||||
|
|
||||||
|
|
||||||
export interface MapEventListenerProps {
|
export interface MapEventListenerProps {
|
||||||
@ -44,15 +45,14 @@ function UtopiaMap({
|
|||||||
window.history.pushState({}, "", `/` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
window.history.pushState({}, "", `/` + `${params.toString() !== "" ? `?${params}` : ""}`)
|
||||||
document.title = document.title.split("-")[0];
|
document.title = document.title.split("-")[0];
|
||||||
document.querySelector('meta[property="og:title"]')?.setAttribute("content", document.title);
|
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")}`);
|
document.querySelector('meta[property="og:description"]')?.setAttribute("content", `${document.querySelector('meta[name="description"]')?.getAttribute("content")}`);
|
||||||
|
|
||||||
console.log(e.latlng.lat + ',' + e.latlng.lng);
|
console.log(e.latlng.lat + ',' + e.latlng.lng);
|
||||||
if (selectNewItemPosition != null) {
|
if (selectNewItemPosition != null) {
|
||||||
if ('menuIcon' in selectNewItemPosition) {
|
if ('menuIcon' in selectNewItemPosition) {
|
||||||
props.setItemFormPopup({ layer: props.selectNewItemPosition, position: e.latlng })
|
props.setItemFormPopup({ layer: props.selectNewItemPosition, position: e.latlng })
|
||||||
props.setSelectNewItemPosition(null)
|
props.setSelectNewItemPosition(null)
|
||||||
}
|
}
|
||||||
if ('position' in selectNewItemPosition) {
|
if ('text' in selectNewItemPosition) {
|
||||||
const position = new Geometry(e.latlng.lng,e.latlng.lat);
|
const position = new Geometry(e.latlng.lng,e.latlng.lat);
|
||||||
itemUpdate({...selectNewItemPosition as Item, position: position })
|
itemUpdate({...selectNewItemPosition as Item, position: position })
|
||||||
setSelectNewItemPosition(null);
|
setSelectNewItemPosition(null);
|
||||||
@ -61,14 +61,13 @@ function UtopiaMap({
|
|||||||
},
|
},
|
||||||
moveend: (e) => {
|
moveend: (e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemUpdate = async (updatedItem: Item) => {
|
const itemUpdate = async (updatedItem: Item) => {
|
||||||
console.log(updatedItem);
|
|
||||||
console.log(updatedItem?.layer?.api?.updateItem!);
|
|
||||||
let success = false;
|
let success = false;
|
||||||
try {
|
try {
|
||||||
await updatedItem?.layer?.api?.updateItem!({id: updatedItem.id, position: updatedItem.position })
|
await updatedItem?.layer?.api?.updateItem!({id: updatedItem.id, position: updatedItem.position })
|
||||||
@ -85,10 +84,11 @@ function UtopiaMap({
|
|||||||
|
|
||||||
const selectNewItemPosition = useSelectPosition();
|
const selectNewItemPosition = useSelectPosition();
|
||||||
const setSelectNewItemPosition = useSetSelectPosition();
|
const setSelectNewItemPosition = useSetSelectPosition();
|
||||||
const clusterRef = React.useRef();
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const updateItem = useUpdateItem();
|
const updateItem = useUpdateItem();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setClusterRef = useSetClusterRef();
|
||||||
|
const clusterRef = useClusterRef();
|
||||||
|
|
||||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||||
|
|
||||||
@ -100,13 +100,11 @@ function UtopiaMap({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
|
|
||||||
<div className={(selectNewItemPosition != null ? "crosshair-cursor-enabled" : undefined)}>
|
<div className={(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}>
|
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={new LatLng(center[0], center[1])} zoom={zoom} zoomControl={false} maxZoom={19}>
|
||||||
<Outlet></Outlet>
|
<Outlet></Outlet>
|
||||||
<Control position='topLeft' zIndex="1000">
|
<Control position='topLeft' zIndex="1000">
|
||||||
<SearchControl clusterRef={clusterRef} />
|
<SearchControl />
|
||||||
<TagsControl />
|
<TagsControl />
|
||||||
</Control>
|
</Control>
|
||||||
<Control position='bottomLeft' zIndex="999">
|
<Control position='bottomLeft' zIndex="999">
|
||||||
@ -117,7 +115,7 @@ function UtopiaMap({
|
|||||||
maxZoom={19}
|
maxZoom={19}
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url="https://tile.osmand.net/hd/{z}/{x}/{y}.png" />
|
url="https://tile.osmand.net/hd/{z}/{x}/{y}.png" />
|
||||||
<MarkerClusterGroup ref={clusterRef} showCoverageOnHover chunkedLoading maxClusterRadius={50} removeOutsideVisibleBounds={false}>
|
<MarkerClusterGroup ref={(r)=> setClusterRef(r)} showCoverageOnHover chunkedLoading maxClusterRadius={50} removeOutsideVisibleBounds={false}>
|
||||||
{
|
{
|
||||||
React.Children.toArray(children).map((child) =>
|
React.Children.toArray(children).map((child) =>
|
||||||
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>, itemFormPopup: ItemFormPopupProps | null, clusterRef: React.MutableRefObject<undefined> }>(child) ?
|
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>, itemFormPopup: ItemFormPopupProps | null, clusterRef: React.MutableRefObject<undefined> }>(child) ?
|
||||||
|
|||||||
40
src/Components/Map/hooks/useClusterRef.tsx
Normal file
40
src/Components/Map/hooks/useClusterRef.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { createContext, useContext, useState } from "react";
|
||||||
|
|
||||||
|
type UseClusterRefManagerResult = ReturnType<typeof useClusterRefManager>;
|
||||||
|
|
||||||
|
const ClusterRefContext = createContext<UseClusterRefManagerResult>({
|
||||||
|
clusterRef: {} as React.MutableRefObject<undefined>,
|
||||||
|
setClusterRef: () => { },
|
||||||
|
});
|
||||||
|
|
||||||
|
function useClusterRefManager(): {
|
||||||
|
clusterRef: any
|
||||||
|
setClusterRef: React.Dispatch<React.SetStateAction<React.MutableRefObject<undefined>>>;
|
||||||
|
} {
|
||||||
|
const [clusterRef, setClusterRef] = useState<React.MutableRefObject<undefined>>({} as React.MutableRefObject<undefined>);
|
||||||
|
|
||||||
|
return { clusterRef, setClusterRef };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const ClusterRefProvider: React.FunctionComponent<{
|
||||||
|
children?: React.ReactNode
|
||||||
|
}> = ({ children }) => (
|
||||||
|
<ClusterRefContext.Provider value={useClusterRefManager()}>
|
||||||
|
{children}
|
||||||
|
</ClusterRefContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useClusterRef = (): any=> {
|
||||||
|
const { clusterRef } = useContext(ClusterRefContext);
|
||||||
|
return clusterRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetClusterRef = (): UseClusterRefManagerResult["setClusterRef"] => {
|
||||||
|
const { setClusterRef } = useContext(ClusterRefContext);
|
||||||
|
return setClusterRef;
|
||||||
|
}
|
||||||
|
|
||||||
107
src/Components/Map/hooks/useSelectPosition.tsx
Normal file
107
src/Components/Map/hooks/useSelectPosition.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import { createContext, useContext, useEffect, useState } from "react";
|
||||||
|
import { Item, LayerProps } from '../../../types';
|
||||||
|
import { useUpdateItem } from "./useItems";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { useHasUserPermission } from "./usePermissions";
|
||||||
|
|
||||||
|
type UseSelectPositionManagerResult = ReturnType<typeof useSelectPositionManager>;
|
||||||
|
|
||||||
|
const SelectPositionContext = createContext<UseSelectPositionManagerResult>({
|
||||||
|
selectPosition: null,
|
||||||
|
setSelectPosition: () => { },
|
||||||
|
setMarkerClicked: () => { },
|
||||||
|
});
|
||||||
|
|
||||||
|
function useSelectPositionManager(): {
|
||||||
|
selectPosition: Item | LayerProps | null;
|
||||||
|
setSelectPosition: React.Dispatch<React.SetStateAction<Item | LayerProps | null>>;
|
||||||
|
setMarkerClicked: React.Dispatch<React.SetStateAction<Item>>;
|
||||||
|
} {
|
||||||
|
const [selectPosition, setSelectPosition] = useState<LayerProps | null | Item>(null);
|
||||||
|
const [markerClicked, setMarkerClicked] = useState<Item>();
|
||||||
|
const updateItem = useUpdateItem();
|
||||||
|
const hasUserPermission = useHasUserPermission();
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectPosition && markerClicked && 'text' in selectPosition) {
|
||||||
|
itemUpdate({ ...selectPosition, parent: markerClicked.id })
|
||||||
|
}
|
||||||
|
}, [markerClicked])
|
||||||
|
|
||||||
|
const itemUpdate = async (updatedItem: Item) => {
|
||||||
|
if (markerClicked?.layer?.api?.collectionName && hasUserPermission(markerClicked?.layer?.api?.collectionName, "update", markerClicked)) {
|
||||||
|
let success = false;
|
||||||
|
try {
|
||||||
|
await updatedItem?.layer?.api?.updateItem!({ id: updatedItem.id, parent: updatedItem.parent, position: null })
|
||||||
|
success = true;
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.toString());
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
await updateItem({ ...updatedItem, parent: updatedItem.parent, position: undefined })
|
||||||
|
await linkItem(updatedItem.id);
|
||||||
|
toast.success("Item position updated");
|
||||||
|
setSelectPosition(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setSelectPosition(null);
|
||||||
|
toast.error("you don't have permission to add items to " + markerClicked?.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkItem = async (id: string) => {
|
||||||
|
if (markerClicked) {
|
||||||
|
let new_relations = markerClicked.relations || [];
|
||||||
|
console.log(new_relations);
|
||||||
|
console.log(id);
|
||||||
|
|
||||||
|
|
||||||
|
if (!new_relations.some(r => r.related_items_id == id)) {
|
||||||
|
new_relations?.push({ items_id: markerClicked.id, related_items_id: id })
|
||||||
|
const updatedItem = { id: markerClicked.id, relations: new_relations }
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
try {
|
||||||
|
await markerClicked?.layer?.api?.updateItem!(updatedItem)
|
||||||
|
success = true;
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.toString());
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
updateItem({ ...markerClicked, relations: new_relations })
|
||||||
|
toast.success("Item linked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { selectPosition, setSelectPosition, setMarkerClicked };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const SelectPositionProvider: React.FunctionComponent<{
|
||||||
|
children?: React.ReactNode
|
||||||
|
}> = ({ children }) => (
|
||||||
|
<SelectPositionContext.Provider value={useSelectPositionManager()}>
|
||||||
|
{children}
|
||||||
|
</SelectPositionContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useSelectPosition = (): Item | LayerProps | null => {
|
||||||
|
const { selectPosition } = useContext(SelectPositionContext);
|
||||||
|
return selectPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetSelectPosition = (): UseSelectPositionManagerResult["setSelectPosition"] => {
|
||||||
|
const { setSelectPosition } = useContext(SelectPositionContext);
|
||||||
|
return setSelectPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useSetMarkerClicked = (): UseSelectPositionManagerResult["setMarkerClicked"] => {
|
||||||
|
const { setMarkerClicked } = useContext(SelectPositionContext);
|
||||||
|
return setMarkerClicked;
|
||||||
|
}
|
||||||
@ -1,35 +0,0 @@
|
|||||||
import { createContext, useContext, useState } from "react";
|
|
||||||
import { Item, LayerProps } from '../../../types';
|
|
||||||
|
|
||||||
type UseSelectPositionManagerResult = ReturnType<typeof useSelectPositionManager>;
|
|
||||||
|
|
||||||
const SelectPositionContext = createContext<UseSelectPositionManagerResult>({
|
|
||||||
selectPosition: null,
|
|
||||||
setSelectPosition: () => { },
|
|
||||||
});
|
|
||||||
|
|
||||||
function useSelectPositionManager(): {
|
|
||||||
selectPosition: Item | LayerProps | null;
|
|
||||||
setSelectPosition: React.Dispatch<React.SetStateAction< Item | LayerProps | null>>;
|
|
||||||
} {
|
|
||||||
const [selectPosition, setSelectPosition] = useState<LayerProps | null | Item>(null);
|
|
||||||
return { selectPosition, setSelectPosition };
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SelectPositionProvider: React.FunctionComponent<{
|
|
||||||
children?: React.ReactNode
|
|
||||||
}> = ({ children }) => (
|
|
||||||
<SelectPositionContext.Provider value={useSelectPositionManager()}>
|
|
||||||
{children}
|
|
||||||
</SelectPositionContext.Provider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const useSelectPosition = (): Item | LayerProps | null => {
|
|
||||||
const { selectPosition } = useContext(SelectPositionContext);
|
|
||||||
return selectPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSetSelectPosition = (): UseSelectPositionManagerResult["setSelectPosition"] => {
|
|
||||||
const { setSelectPosition } = useContext(SelectPositionContext);
|
|
||||||
return setSelectPosition;
|
|
||||||
}
|
|
||||||
@ -8,7 +8,7 @@ import { LatLng } from 'leaflet';
|
|||||||
import { PopupStartEndInput, StartEndView, TextView } from '../Map';
|
import { PopupStartEndInput, StartEndView, TextView } from '../Map';
|
||||||
import useWindowDimensions from '../Map/hooks/useWindowDimension';
|
import useWindowDimensions from '../Map/hooks/useWindowDimension';
|
||||||
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
import { useAddTag, useTags } from '../Map/hooks/useTags';
|
||||||
import { useResetFilterTags } from '../Map/hooks/useFilter';
|
import { useFilterTags, useResetFilterTags } from '../Map/hooks/useFilter';
|
||||||
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
import { useHasUserPermission } from '../Map/hooks/usePermissions';
|
||||||
import { TextAreaInput, TextInput } from '../Input';
|
import { TextAreaInput, TextInput } from '../Input';
|
||||||
import { hashTagRegex } from '../../Utils/HashTagRegex';
|
import { hashTagRegex } from '../../Utils/HashTagRegex';
|
||||||
@ -19,44 +19,38 @@ import { useLayers } from '../Map/hooks/useLayers';
|
|||||||
import { ActionButton } from './ActionsButton';
|
import { ActionButton } from './ActionsButton';
|
||||||
import { LinkedItemsHeaderView } from './LinkedItemsHeaderView';
|
import { LinkedItemsHeaderView } from './LinkedItemsHeaderView';
|
||||||
import { HeaderView } from '../Map/Subcomponents/ItemPopupComponents/HeaderView';
|
import { HeaderView } from '../Map/Subcomponents/ItemPopupComponents/HeaderView';
|
||||||
import { useSelectPosition } from '../Map/hooks/useSetItemPosition';
|
import { useSelectPosition, useSetSelectPosition } from '../Map/hooks/useSelectPosition';
|
||||||
|
import { useClusterRef } from '../Map/hooks/useClusterRef';
|
||||||
import { useLeafletRefs } from '../Map/hooks/useLeafletRefs';
|
import { useLeafletRefs } from '../Map/hooks/useLeafletRefs';
|
||||||
|
|
||||||
export function OverlayItemProfile() {
|
export function OverlayItemProfile() {
|
||||||
|
|
||||||
|
const [updatePermission, setUpdatePermission] = useState<boolean>(false);
|
||||||
|
const [relations, setRelations] = useState<Array<Item>>([]);
|
||||||
|
const [activeTab, setActiveTab] = useState<number>(1);
|
||||||
|
const [addItemPopupType, setAddItemPopupType] = useState<string>("");
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const items = useItems();
|
const items = useItems();
|
||||||
const updateItem = useUpdateItem();
|
const updateItem = useUpdateItem();
|
||||||
const [item, setItem] = useState<Item>({} as Item)
|
const [item, setItem] = useState<Item>({} as Item)
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
const windowDimension = useWindowDimensions();
|
const windowDimension = useWindowDimensions();
|
||||||
|
|
||||||
const [updatePermission, setUpdatePermission] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const layers = useLayers();
|
const layers = useLayers();
|
||||||
const selectPosition = useSelectPosition();
|
const selectPosition = useSelectPosition();
|
||||||
|
|
||||||
const removeItem = useRemoveItem();
|
const removeItem = useRemoveItem();
|
||||||
|
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [relations, setRelations] = useState<Array<Item>>([]);
|
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<number>(1);
|
|
||||||
|
|
||||||
const [addItemPopupType, setAddItemPopupType] = useState<string>("");
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const addTag = useAddTag();
|
const addTag = useAddTag();
|
||||||
const resetFilterTags = useResetFilterTags();
|
const resetFilterTags = useResetFilterTags();
|
||||||
|
const filterTags = useFilterTags();
|
||||||
const addItem = useAddItem();
|
const addItem = useAddItem();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
const hasUserPermission = useHasUserPermission();
|
const hasUserPermission = useHasUserPermission();
|
||||||
|
const setSelectPosition = useSetSelectPosition();
|
||||||
|
const clusterRef = useClusterRef();
|
||||||
|
const leafletRefs = useLeafletRefs();
|
||||||
|
|
||||||
const tabRef = useRef<HTMLFormElement>(null);
|
const tabRef = useRef<HTMLFormElement>(null);
|
||||||
|
|
||||||
@ -68,11 +62,6 @@ export function OverlayItemProfile() {
|
|||||||
scroll();
|
scroll();
|
||||||
}, [addItemPopupType])
|
}, [addItemPopupType])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log(addItemPopupType);
|
|
||||||
|
|
||||||
}, [addItemPopupType])
|
|
||||||
|
|
||||||
|
|
||||||
const updateActiveTab = (id: number) => {
|
const updateActiveTab = (id: number) => {
|
||||||
setActiveTab(id);
|
setActiveTab(id);
|
||||||
@ -85,18 +74,31 @@ export function OverlayItemProfile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
const itemId = location.pathname.split("/")[2];
|
const itemId = location.pathname.split("/")[2];
|
||||||
const item = items.find(i => i.id === itemId);
|
const item = items.find(i => i.id === itemId);
|
||||||
item && setItem(item);
|
item && setItem(item);
|
||||||
const bounds = map.getBounds();
|
resetFilterTags();
|
||||||
const x = bounds.getEast() - bounds.getWest()
|
if (item && filterTags.length == 0) {
|
||||||
if (windowDimension.width > 768)
|
if(item.position) {
|
||||||
if (item?.position && item?.position.coordinates[0])
|
const marker = Object.entries(leafletRefs).find(r => r[1].item == item)?.[1].marker;
|
||||||
map.setView(new LatLng(item?.position.coordinates[1]!, item?.position.coordinates[0]! + x / 4))
|
marker && clusterRef?.zoomToShowLayer(marker, () => {
|
||||||
|
const bounds = map.getBounds();
|
||||||
|
const x = bounds.getEast() - bounds.getWest()
|
||||||
|
map.setView(new LatLng(item?.position?.coordinates[1]!, item?.position?.coordinates[0]! + x / 4), undefined, {duration: 1})}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const parent = items.find(i => i.id == item.parent);
|
||||||
|
const marker = Object.entries(leafletRefs).find(r => r[1].item == parent)?.[1].marker;
|
||||||
|
marker && clusterRef?.zoomToShowLayer(marker, () => {
|
||||||
|
const bounds = map.getBounds();
|
||||||
|
const x = bounds.getEast() - bounds.getWest()
|
||||||
|
map.setView(new LatLng(parent?.position?.coordinates[1]!, parent?.position?.coordinates[0]! + x / 4), undefined, {duration: 1})}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [items, activeTab, leafletRefs])
|
||||||
}, [location, items, activeTab])
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -121,8 +123,6 @@ export function OverlayItemProfile() {
|
|||||||
}, [item])
|
}, [item])
|
||||||
|
|
||||||
|
|
||||||
const [selecting, setSelecting] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
selectPosition && map.closePopup();
|
selectPosition && map.closePopup();
|
||||||
}, [selectPosition])
|
}, [selectPosition])
|
||||||
@ -233,17 +233,13 @@ export function OverlayItemProfile() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{item &&
|
{item &&
|
||||||
<MapOverlayPage className={`tw-mx-4 tw-mt-4 tw-max-h-[calc(100dvh-96px)] tw-h-[calc(100dvh-96px)] md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-min-w-80 tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0 tw-transition-opacity tw-duration-500 ${!selectPosition ? 'tw-opacity-100 tw-pointer-events-auto' : 'tw-opacity-0 tw-pointer-events-none'}`}>
|
<MapOverlayPage className={`tw-mx-4 tw-mt-4 tw-max-h-[calc(100dvh-96px)] tw-h-[calc(100dvh-96px)] md:tw-w-[calc(50%-32px)] tw-w-[calc(100%-32px)] tw-min-w-80 tw-max-w-3xl !tw-left-auto tw-top-0 tw-bottom-0 tw-transition-opacity tw-duration-500 ${!selectPosition ? 'tw-opacity-100 tw-pointer-events-auto' : 'tw-opacity-0 tw-pointer-events-none'}`}>
|
||||||
|
|
||||||
<>
|
<>
|
||||||
<HeaderView api={item.layer?.api} item={item} deleteCallback={handleDelete} editCallback={() => navigate("/edit-item/" + item.id)} big updatePosition/>
|
<HeaderView api={item.layer?.api} item={item} deleteCallback={handleDelete} editCallback={() => navigate("/edit-item/" + item.id)} setPositionCallback={()=>{map.closePopup();setSelectPosition(item); navigate("/")}} big />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div className='tw-h-full'>
|
<div className='tw-h-full'>
|
||||||
|
|
||||||
<div role="tablist" className="tw-tabs tw-tabs-lifted tw-mt-2 tw-mb-2">
|
<div role="tablist" className="tw-tabs tw-tabs-lifted tw-mt-2 tw-mb-2">
|
||||||
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Info" checked={activeTab == 1 && true} onChange={() => updateActiveTab(1)} />
|
<input type="radio" name="my_tabs_2" role="tab" className={`tw-tab [--tab-border-color:var(--fallback-bc,oklch(var(--bc)/0.2))]`} aria-label="Info" checked={activeTab == 1 && true} onChange={() => updateActiveTab(1)} />
|
||||||
<div role="tabpanel" className="tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden">
|
<div role="tabpanel" className="tw-tab-content tw-bg-base-100 tw-rounded-box tw-h-[calc(100dvh-280px)] tw-overflow-y-auto fade tw-pt-2 tw-pb-4 tw-mb-4 tw-overflow-x-hidden">
|
||||||
|
|||||||
@ -21,6 +21,7 @@ export interface LayerProps {
|
|||||||
markerShape: string,
|
markerShape: string,
|
||||||
markerDefaultColor: string,
|
markerDefaultColor: string,
|
||||||
api?: ItemsApi<any>,
|
api?: ItemsApi<any>,
|
||||||
|
itemType: string,
|
||||||
itemNameField?: string,
|
itemNameField?: string,
|
||||||
itemTextField?: string,
|
itemTextField?: string,
|
||||||
itemAvatarField?: string,
|
itemAvatarField?: string,
|
||||||
@ -35,7 +36,7 @@ export interface LayerProps {
|
|||||||
customEditLink?: string,
|
customEditLink?: string,
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
|
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
|
||||||
itemFormPopup?: ItemFormPopupProps | null,
|
itemFormPopup?: ItemFormPopupProps | null,
|
||||||
clusterRef?: React.MutableRefObject<any>
|
clusterRef?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Item {
|
export class Item {
|
||||||
@ -51,6 +52,7 @@ export class Item {
|
|||||||
tags?: string[];
|
tags?: string[];
|
||||||
layer?: LayerProps;
|
layer?: LayerProps;
|
||||||
relations?: Relation[];
|
relations?: Relation[];
|
||||||
|
parent?:string;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
constructor(id:string,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi<any>){
|
constructor(id:string,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi<any>){
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user