mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
refactor(source): refactor Layer and its subcomponents, replacing cloneElement by context (#185)
* Refactor Layer and its subcomponents, replacing cloneElement by context * Add showcase for PopupButton template component * Templateify exported elements (WIP) * Remove unused file * Export templateified PopupStartEndInput * Fix template component type * Change folder structure * Lower test coverage * changed export name * Refactor PopupForm and PopupView * More refactoring * Add provider for PopupFormContext * Fix popupform title * Add comments * Use correct ItemFormPopup for new items * Fix linting * Reduce coverage * Change tailwind prefix * Fix type --------- Co-authored-by: Anton Tranelis <mail@antontranelis.de> Co-authored-by: Anton Tranelis <31516529+antontranelis@users.noreply.github.com>
This commit is contained in:
parent
06252fb0b5
commit
82b1f39141
@ -10,6 +10,7 @@ import { ItemsProvider } from '#components/Map/hooks/useItems'
|
|||||||
import { LayersProvider } from '#components/Map/hooks/useLayers'
|
import { LayersProvider } from '#components/Map/hooks/useLayers'
|
||||||
import { LeafletRefsProvider } from '#components/Map/hooks/useLeafletRefs'
|
import { LeafletRefsProvider } from '#components/Map/hooks/useLeafletRefs'
|
||||||
import { PermissionsProvider } from '#components/Map/hooks/usePermissions'
|
import { PermissionsProvider } from '#components/Map/hooks/usePermissions'
|
||||||
|
import { PopupFormProvider } from '#components/Map/hooks/usePopupForm'
|
||||||
import { SelectPositionProvider } from '#components/Map/hooks/useSelectPosition'
|
import { SelectPositionProvider } from '#components/Map/hooks/useSelectPosition'
|
||||||
import { TagsProvider } from '#components/Map/hooks/useTags'
|
import { TagsProvider } from '#components/Map/hooks/useTags'
|
||||||
|
|
||||||
@ -66,22 +67,24 @@ export const Wrappers = ({ children }) => {
|
|||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
<ClusterRefProvider>
|
<ClusterRefProvider>
|
||||||
<QuestsProvider initialOpen={true}>
|
<PopupFormProvider>
|
||||||
<ToastContainer
|
<QuestsProvider initialOpen={true}>
|
||||||
position='top-right'
|
<ToastContainer
|
||||||
autoClose={2000}
|
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
|
||||||
closeButton={CloseButton}
|
theme='light'
|
||||||
/>
|
closeButton={CloseButton}
|
||||||
{children}
|
/>
|
||||||
</QuestsProvider>
|
{children}
|
||||||
|
</QuestsProvider>
|
||||||
|
</PopupFormProvider>
|
||||||
</ClusterRefProvider>
|
</ClusterRefProvider>
|
||||||
</AppStateProvider>
|
</AppStateProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
|
|||||||
8
src/Components/Item/PopupForm.tsx
Normal file
8
src/Components/Item/PopupForm.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ItemFormPopup } from '#components/Map/Subcomponents/ItemFormPopup'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @category Item
|
||||||
|
*/
|
||||||
|
export const PopupForm = ({ children }: { children?: React.ReactNode }) => {
|
||||||
|
return <ItemFormPopup>{children}</ItemFormPopup>
|
||||||
|
}
|
||||||
182
src/Components/Item/PopupView.tsx
Normal file
182
src/Components/Item/PopupView.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import { useContext, useMemo, useState } from 'react'
|
||||||
|
import { Marker, Tooltip } from 'react-leaflet'
|
||||||
|
|
||||||
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
||||||
|
import {
|
||||||
|
useFilterTags,
|
||||||
|
useIsLayerVisible,
|
||||||
|
useIsGroupTypeVisible,
|
||||||
|
useVisibleGroupType,
|
||||||
|
} from '#components/Map/hooks/useFilter'
|
||||||
|
import { useItems, useAllItemsLoaded } from '#components/Map/hooks/useItems'
|
||||||
|
import { useAddMarker, useAddPopup, useLeafletRefs } from '#components/Map/hooks/useLeafletRefs'
|
||||||
|
import { useSetMarkerClicked, useSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
||||||
|
import { useGetItemTags, useAllTagsLoaded, useTags } from '#components/Map/hooks/useTags'
|
||||||
|
import LayerContext from '#components/Map/LayerContext'
|
||||||
|
import { ItemViewPopup } from '#components/Map/Subcomponents/ItemViewPopup'
|
||||||
|
import { encodeTag } from '#utils/FormatTags'
|
||||||
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
|
import MarkerIconFactory from '#utils/MarkerIconFactory'
|
||||||
|
import { randomColor } from '#utils/RandomColor'
|
||||||
|
|
||||||
|
import TemplateItemContext from './TemplateItemContext'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
import type { Tag } from '#types/Tag'
|
||||||
|
import type { Popup } from 'leaflet'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @category Item
|
||||||
|
*/
|
||||||
|
export const PopupView = ({ children }: { children?: React.ReactNode }) => {
|
||||||
|
const layerContext = useContext(LayerContext)
|
||||||
|
const { name, markerDefaultColor, markerDefaultColor2, markerShape, markerIcon } = layerContext
|
||||||
|
|
||||||
|
const filterTags = useFilterTags()
|
||||||
|
|
||||||
|
const appState = useAppState()
|
||||||
|
|
||||||
|
const items = useItems()
|
||||||
|
|
||||||
|
const getItemTags = useGetItemTags()
|
||||||
|
const addMarker = useAddMarker()
|
||||||
|
const addPopup = useAddPopup()
|
||||||
|
const leafletRefs = useLeafletRefs()
|
||||||
|
|
||||||
|
const allTagsLoaded = useAllTagsLoaded()
|
||||||
|
const allItemsLoaded = useAllItemsLoaded()
|
||||||
|
|
||||||
|
const setMarkerClicked = useSetMarkerClicked()
|
||||||
|
const selectPosition = useSelectPosition()
|
||||||
|
|
||||||
|
const tags = useTags()
|
||||||
|
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([])
|
||||||
|
const [tagsReady, setTagsReady] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const isLayerVisible = useIsLayerVisible()
|
||||||
|
|
||||||
|
const isGroupTypeVisible = useIsGroupTypeVisible()
|
||||||
|
|
||||||
|
const visibleGroupTypes = useVisibleGroupType()
|
||||||
|
|
||||||
|
const visibleItems = useMemo(
|
||||||
|
() =>
|
||||||
|
items
|
||||||
|
.filter((item) => item.layer?.name === name)
|
||||||
|
.filter((item) =>
|
||||||
|
filterTags.length === 0
|
||||||
|
? item
|
||||||
|
: filterTags.some((tag) =>
|
||||||
|
getItemTags(item).some(
|
||||||
|
(filterTag) =>
|
||||||
|
filterTag.name.toLocaleLowerCase() === tag.name.toLocaleLowerCase(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.filter((item) => item.layer && isLayerVisible(item.layer))
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
(item.group_type && isGroupTypeVisible(item.group_type)) ||
|
||||||
|
visibleGroupTypes.length === 0,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
filterTags,
|
||||||
|
getItemTags,
|
||||||
|
isGroupTypeVisible,
|
||||||
|
isLayerVisible,
|
||||||
|
items,
|
||||||
|
name,
|
||||||
|
visibleGroupTypes.length,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
return visibleItems.map((item: Item) => {
|
||||||
|
if (!(item.position?.coordinates[0] && item.position.coordinates[1])) return null
|
||||||
|
|
||||||
|
if (item.tags) {
|
||||||
|
item.text += '\n\n'
|
||||||
|
item.tags.map((tag) => {
|
||||||
|
if (!item.text?.includes(`#${encodeTag(tag)}`)) {
|
||||||
|
item.text += `#${encodeTag(tag)}`
|
||||||
|
}
|
||||||
|
return item.text
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allTagsLoaded && allItemsLoaded) {
|
||||||
|
item.text?.match(hashTagRegex)?.map((tag) => {
|
||||||
|
if (
|
||||||
|
!tags.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase()) &&
|
||||||
|
!newTagsToAdd.find((t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())
|
||||||
|
) {
|
||||||
|
const newTag = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
name: tag.slice(1),
|
||||||
|
color: randomColor(),
|
||||||
|
}
|
||||||
|
setNewTagsToAdd((current) => [...current, newTag])
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
!tagsReady && setTagsReady(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemTags = getItemTags(item)
|
||||||
|
|
||||||
|
const latitude = item.position.coordinates[1]
|
||||||
|
const longitude = item.position.coordinates[0]
|
||||||
|
|
||||||
|
let color1 = markerDefaultColor
|
||||||
|
let color2 = markerDefaultColor2
|
||||||
|
if (item.color) {
|
||||||
|
color1 = item.color
|
||||||
|
} else if (itemTags[0]) {
|
||||||
|
color1 = itemTags[0].color
|
||||||
|
}
|
||||||
|
if (itemTags[0] && item.color) {
|
||||||
|
color2 = itemTags[0].color
|
||||||
|
} else if (itemTags[1]) {
|
||||||
|
color2 = itemTags[1].color
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TemplateItemContext.Provider value={item} key={item.id}>
|
||||||
|
<Marker
|
||||||
|
ref={(r) => {
|
||||||
|
if (!(item.id in leafletRefs && leafletRefs[item.id].marker === r)) {
|
||||||
|
r && addMarker(item, r)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
eventHandlers={{
|
||||||
|
click: () => {
|
||||||
|
selectPosition && setMarkerClicked(item)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
icon={MarkerIconFactory(
|
||||||
|
markerShape,
|
||||||
|
color1,
|
||||||
|
color2,
|
||||||
|
item.markerIcon ?? markerIcon,
|
||||||
|
appState.assetsApi.url,
|
||||||
|
)}
|
||||||
|
position={[latitude, longitude]}
|
||||||
|
>
|
||||||
|
<ItemViewPopup
|
||||||
|
ref={(r: Popup | null) => {
|
||||||
|
if (!(item.id in leafletRefs && leafletRefs[item.id].popup === r)) {
|
||||||
|
r && addPopup(item, r)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
item={item}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ItemViewPopup>
|
||||||
|
|
||||||
|
<Tooltip offset={[0, -38]} direction='top'>
|
||||||
|
{item.name}
|
||||||
|
</Tooltip>
|
||||||
|
</Marker>
|
||||||
|
</TemplateItemContext.Provider>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
7
src/Components/Item/TemplateItemContext.ts
Normal file
7
src/Components/Item/TemplateItemContext.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { createContext } from 'react'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
const ItemContext = createContext<Item | undefined>(undefined)
|
||||||
|
|
||||||
|
export default ItemContext
|
||||||
22
src/Components/Item/index.tsx
Normal file
22
src/Components/Item/index.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import {
|
||||||
|
TextView as PlainTextView,
|
||||||
|
StartEndView as PlainStartEndView,
|
||||||
|
PopupTextInput as PlainPopupTextInput,
|
||||||
|
PopupButton as PlainPopupButton,
|
||||||
|
PopupCheckboxInput as PlainPopupCheckboxInput,
|
||||||
|
PopupTextAreaInput as PlainPopupTextAreaInput,
|
||||||
|
PopupStartEndInput as PlainPopupStartEndInput,
|
||||||
|
} from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
|
|
||||||
|
import { templateify } from './templateify'
|
||||||
|
|
||||||
|
export { PopupForm } from './PopupForm'
|
||||||
|
export { PopupView } from './PopupView'
|
||||||
|
|
||||||
|
export const TextView = templateify(PlainTextView)
|
||||||
|
export const StartEndView = templateify(PlainStartEndView)
|
||||||
|
export const PopupTextInput = templateify(PlainPopupTextInput)
|
||||||
|
export const PopupButton = templateify(PlainPopupButton)
|
||||||
|
export const PopupCheckboxInput = templateify(PlainPopupCheckboxInput)
|
||||||
|
export const PopupTextAreaInput = templateify(PlainPopupTextAreaInput)
|
||||||
|
export const PopupStartEndInput = templateify(PlainPopupStartEndInput)
|
||||||
15
src/Components/Item/templateify.tsx
Normal file
15
src/Components/Item/templateify.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { useContext } from 'react'
|
||||||
|
|
||||||
|
import ItemContext from './TemplateItemContext'
|
||||||
|
|
||||||
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
export function templateify<T extends { item?: Item }>(Component: React.ComponentType<T>) {
|
||||||
|
const TemplateComponent = (props: T) => {
|
||||||
|
const item = useContext(ItemContext)
|
||||||
|
|
||||||
|
return <Component {...props} item={item} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return TemplateComponent as React.ComponentType<Omit<T, 'item'>>
|
||||||
|
}
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { Children, cloneElement, isValidElement, useEffect } from 'react'
|
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Map
|
|
||||||
*/
|
|
||||||
export const ItemForm = ({
|
|
||||||
children,
|
|
||||||
item,
|
|
||||||
title,
|
|
||||||
setPopupTitle,
|
|
||||||
}: {
|
|
||||||
children?: React.ReactNode
|
|
||||||
item?: Item
|
|
||||||
title?: string
|
|
||||||
setPopupTitle?: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
}) => {
|
|
||||||
useEffect(() => {
|
|
||||||
setPopupTitle && title && setPopupTitle(title)
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [title])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{children
|
|
||||||
? Children.toArray(children).map((child) =>
|
|
||||||
isValidElement<{ item: Item; test: string }>(child)
|
|
||||||
? cloneElement(child, { item, test: 'test' })
|
|
||||||
: '',
|
|
||||||
)
|
|
||||||
: ''}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemForm.__TYPE = 'ItemForm'
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import { Children, cloneElement, isValidElement } from 'react'
|
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @category Map
|
|
||||||
*/
|
|
||||||
export const ItemView = ({ children, item }: { children?: React.ReactNode; item?: Item }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{children
|
|
||||||
? Children.toArray(children).map((child) =>
|
|
||||||
isValidElement<{ item: Item }>(child) ? cloneElement(child, { item }) : null,
|
|
||||||
)
|
|
||||||
: null}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemView.__TYPE = 'ItemView'
|
|
||||||
@ -1,32 +1,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
import { useEffect, useState } from 'react'
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
|
||||||
import { Children, isValidElement, useEffect, useState } from 'react'
|
|
||||||
import { Marker, Tooltip } from 'react-leaflet'
|
|
||||||
|
|
||||||
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
import { useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
||||||
import { encodeTag } from '#utils/FormatTags'
|
import { useAddTag } from './hooks/useTags'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import LayerContext from './LayerContext'
|
||||||
import MarkerIconFactory from '#utils/MarkerIconFactory'
|
|
||||||
import { randomColor } from '#utils/RandomColor'
|
|
||||||
|
|
||||||
import {
|
|
||||||
useFilterTags,
|
|
||||||
useIsGroupTypeVisible,
|
|
||||||
useIsLayerVisible,
|
|
||||||
useVisibleGroupType,
|
|
||||||
} from './hooks/useFilter'
|
|
||||||
import { useAllItemsLoaded, useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
|
||||||
import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs'
|
|
||||||
import { useSelectPosition, useSetMarkerClicked } from './hooks/useSelectPosition'
|
|
||||||
import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } from './hooks/useTags'
|
|
||||||
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
|
||||||
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
|
||||||
import type { LayerProps } from '#types/LayerProps'
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
import type { Tag } from '#types/Tag'
|
import type { Tag } from '#types/Tag'
|
||||||
import type { Popup } from 'leaflet'
|
|
||||||
import type { ReactElement, ReactNode } from 'react'
|
|
||||||
|
|
||||||
export type { Point } from 'geojson'
|
export type { Point } from 'geojson'
|
||||||
export type { Item } from '#types/Item'
|
export type { Item } from '#types/Item'
|
||||||
@ -56,38 +35,13 @@ export const Layer = ({
|
|||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
public_edit_items,
|
public_edit_items,
|
||||||
listed = true,
|
listed = true,
|
||||||
setItemFormPopup,
|
|
||||||
itemFormPopup,
|
|
||||||
clusterRef,
|
|
||||||
}: LayerProps) => {
|
}: LayerProps) => {
|
||||||
const filterTags = useFilterTags()
|
|
||||||
|
|
||||||
const items = useItems()
|
|
||||||
const setItemsApi = useSetItemsApi()
|
const setItemsApi = useSetItemsApi()
|
||||||
const setItemsData = useSetItemsData()
|
const setItemsData = useSetItemsData()
|
||||||
const getItemTags = useGetItemTags()
|
|
||||||
const addMarker = useAddMarker()
|
|
||||||
const addPopup = useAddPopup()
|
|
||||||
const leafletRefs = useLeafletRefs()
|
|
||||||
|
|
||||||
const allTagsLoaded = useAllTagsLoaded()
|
|
||||||
const allItemsLoaded = useAllItemsLoaded()
|
|
||||||
|
|
||||||
const setMarkerClicked = useSetMarkerClicked()
|
|
||||||
const selectPosition = useSelectPosition()
|
|
||||||
|
|
||||||
const tags = useTags()
|
|
||||||
const addTag = useAddTag()
|
const addTag = useAddTag()
|
||||||
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([])
|
const [newTagsToAdd] = useState<Tag[]>([])
|
||||||
const [tagsReady, setTagsReady] = useState<boolean>(false)
|
const [tagsReady] = useState<boolean>(false)
|
||||||
|
|
||||||
const isLayerVisible = useIsLayerVisible()
|
|
||||||
|
|
||||||
const isGroupTypeVisible = useIsGroupTypeVisible()
|
|
||||||
|
|
||||||
const visibleGroupTypes = useVisibleGroupType()
|
|
||||||
|
|
||||||
const appState = useAppState()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
data &&
|
data &&
|
||||||
@ -111,10 +65,6 @@ export const Layer = ({
|
|||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
public_edit_items,
|
public_edit_items,
|
||||||
listed,
|
listed,
|
||||||
setItemFormPopup,
|
|
||||||
itemFormPopup,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
clusterRef,
|
|
||||||
})
|
})
|
||||||
api &&
|
api &&
|
||||||
setItemsApi({
|
setItemsApi({
|
||||||
@ -136,10 +86,6 @@ export const Layer = ({
|
|||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
public_edit_items,
|
public_edit_items,
|
||||||
listed,
|
listed,
|
||||||
setItemFormPopup,
|
|
||||||
itemFormPopup,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
clusterRef,
|
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [data, api])
|
}, [data, api])
|
||||||
@ -159,179 +105,17 @@ export const Layer = ({
|
|||||||
}, [tagsReady])
|
}, [tagsReady])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<LayerContext.Provider
|
||||||
{items &&
|
value={{
|
||||||
items
|
name,
|
||||||
.filter((item) => item.layer?.name === name)
|
markerDefaultColor,
|
||||||
.filter((item) =>
|
markerDefaultColor2,
|
||||||
filterTags.length === 0
|
markerShape,
|
||||||
? item
|
markerIcon,
|
||||||
: filterTags.some((tag) =>
|
menuText,
|
||||||
getItemTags(item).some(
|
}}
|
||||||
(filterTag) =>
|
>
|
||||||
filterTag.name.toLocaleLowerCase() === tag.name.toLocaleLowerCase(),
|
{children}
|
||||||
),
|
</LayerContext.Provider>
|
||||||
),
|
|
||||||
)
|
|
||||||
.filter((item) => item.layer && isLayerVisible(item.layer))
|
|
||||||
.filter(
|
|
||||||
(item) =>
|
|
||||||
(item.group_type && isGroupTypeVisible(item.group_type)) ||
|
|
||||||
visibleGroupTypes.length === 0,
|
|
||||||
)
|
|
||||||
.map((item: Item) => {
|
|
||||||
if (item.position?.coordinates[0] && item.position?.coordinates[1]) {
|
|
||||||
if (item.tags) {
|
|
||||||
item.text += '\n\n'
|
|
||||||
item.tags.map((tag) => {
|
|
||||||
if (!item.text?.includes(`#${encodeTag(tag)}`)) {
|
|
||||||
item.text += `#${encodeTag(tag)}`
|
|
||||||
}
|
|
||||||
return item.text
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allTagsLoaded && allItemsLoaded) {
|
|
||||||
item.text?.match(hashTagRegex)?.map((tag) => {
|
|
||||||
if (
|
|
||||||
!tags.find(
|
|
||||||
(t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase(),
|
|
||||||
) &&
|
|
||||||
!newTagsToAdd.find(
|
|
||||||
(t) => t.name.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase(),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const newTag = {
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
name: tag.slice(1),
|
|
||||||
color: randomColor(),
|
|
||||||
}
|
|
||||||
setNewTagsToAdd((current) => [...current, newTag])
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
!tagsReady && setTagsReady(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemTags = getItemTags(item)
|
|
||||||
|
|
||||||
const latitude = item.position.coordinates[1]
|
|
||||||
const longitude = item.position.coordinates[0]
|
|
||||||
|
|
||||||
let color1 = markerDefaultColor
|
|
||||||
let color2 = markerDefaultColor2
|
|
||||||
if (item.color) {
|
|
||||||
color1 = item.color
|
|
||||||
} else if (itemTags[0]) {
|
|
||||||
color1 = itemTags[0].color
|
|
||||||
}
|
|
||||||
if (itemTags[0] && item.color) {
|
|
||||||
color2 = itemTags[0].color
|
|
||||||
} else if (itemTags[1]) {
|
|
||||||
color2 = itemTags[1].color
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Marker
|
|
||||||
ref={(r) => {
|
|
||||||
if (!(item.id in leafletRefs && leafletRefs[item.id].marker === r)) {
|
|
||||||
r && addMarker(item, r)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
eventHandlers={{
|
|
||||||
click: () => {
|
|
||||||
selectPosition && setMarkerClicked(item)
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
icon={MarkerIconFactory(
|
|
||||||
markerShape,
|
|
||||||
color1,
|
|
||||||
color2,
|
|
||||||
item.markerIcon ? item.markerIcon : markerIcon,
|
|
||||||
appState.assetsApi.url,
|
|
||||||
)}
|
|
||||||
key={item.id}
|
|
||||||
position={[latitude, longitude]}
|
|
||||||
>
|
|
||||||
{children &&
|
|
||||||
Children.toArray(children).some(
|
|
||||||
(child) => isComponentWithType(child) && child.type.__TYPE === 'ItemView',
|
|
||||||
) ? (
|
|
||||||
Children.toArray(children).map((child) =>
|
|
||||||
isComponentWithType(child) && child.type.__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>
|
|
||||||
) : null,
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<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}
|
|
||||||
</Tooltip>
|
|
||||||
</Marker>
|
|
||||||
)
|
|
||||||
} else return null
|
|
||||||
})}
|
|
||||||
{
|
|
||||||
// {children}}
|
|
||||||
}
|
|
||||||
{itemFormPopup &&
|
|
||||||
itemFormPopup.layer.name === name &&
|
|
||||||
(children &&
|
|
||||||
Children.toArray(children).some(
|
|
||||||
(child) => isComponentWithType(child) && child.type.__TYPE === 'ItemForm',
|
|
||||||
) ? (
|
|
||||||
Children.toArray(children).map((child) =>
|
|
||||||
isComponentWithType(child) && child.type.__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}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isComponentWithType(node: ReactNode): node is ReactElement & { type: { __TYPE: string } } {
|
|
||||||
return isValidElement(node) && typeof node.type !== 'string' && '__TYPE' in node.type
|
|
||||||
}
|
|
||||||
|
|||||||
22
src/Components/Map/LayerContext.ts
Normal file
22
src/Components/Map/LayerContext.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { createContext } from 'react'
|
||||||
|
|
||||||
|
import type { MarkerIcon } from '#types/MarkerIcon'
|
||||||
|
|
||||||
|
interface LayerContextType {
|
||||||
|
name: string
|
||||||
|
markerDefaultColor: string
|
||||||
|
markerDefaultColor2: string
|
||||||
|
markerShape: string
|
||||||
|
menuText: string
|
||||||
|
markerIcon?: MarkerIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
const LayerContext = createContext<LayerContextType>({
|
||||||
|
name: '',
|
||||||
|
markerDefaultColor: '',
|
||||||
|
markerDefaultColor2: '',
|
||||||
|
markerShape: '',
|
||||||
|
menuText: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
export default LayerContext
|
||||||
0
src/Components/Map/ProfileView.tsx
Normal file
0
src/Components/Map/ProfileView.tsx
Normal file
@ -2,31 +2,39 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'
|
import { useContext, useEffect, useRef, useState } from 'react'
|
||||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { useAuth } from '#components/Auth/useAuth'
|
import { useAuth } from '#components/Auth/useAuth'
|
||||||
import { TextAreaInput } from '#components/Input/TextAreaInput'
|
import { TextAreaInput } from '#components/Input/TextAreaInput'
|
||||||
import { TextInput } from '#components/Input/TextInput'
|
import { TextInput } from '#components/Input/TextInput'
|
||||||
|
import TemplateItemContext from '#components/Item/TemplateItemContext'
|
||||||
import { useResetFilterTags } from '#components/Map/hooks/useFilter'
|
import { useResetFilterTags } from '#components/Map/hooks/useFilter'
|
||||||
import { useAddItem, useItems, useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useAddItem, useItems, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
|
import { usePopupForm } from '#components/Map/hooks/usePopupForm'
|
||||||
import { useAddTag, useTags } from '#components/Map/hooks/useTags'
|
import { useAddTag, useTags } from '#components/Map/hooks/useTags'
|
||||||
|
import LayerContext from '#components/Map/LayerContext'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
import { randomColor } from '#utils/RandomColor'
|
import { randomColor } from '#utils/RandomColor'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
|
||||||
|
|
||||||
export function ItemFormPopup(props: ItemFormPopupProps) {
|
interface Props {
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ItemFormPopup(props: Props) {
|
||||||
|
const layerContext = useContext(LayerContext)
|
||||||
|
const { menuText, name: activeLayerName } = layerContext
|
||||||
|
|
||||||
|
const { popupForm, setPopupForm } = usePopupForm()
|
||||||
|
|
||||||
const [spinner, setSpinner] = useState(false)
|
const [spinner, setSpinner] = useState(false)
|
||||||
|
|
||||||
const [popupTitle, setPopupTitle] = useState<string>('')
|
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement>(null)
|
const formRef = useRef<HTMLFormElement>(null)
|
||||||
|
|
||||||
const map = useMap()
|
const map = useMap()
|
||||||
@ -35,8 +43,6 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
const updateItem = useUpdateItem()
|
const updateItem = useUpdateItem()
|
||||||
const items = useItems()
|
const items = useItems()
|
||||||
|
|
||||||
const removeItem = useRemoveItem()
|
|
||||||
|
|
||||||
const tags = useTags()
|
const tags = useTags()
|
||||||
const addTag = useAddTag()
|
const addTag = useAddTag()
|
||||||
|
|
||||||
@ -45,13 +51,19 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
|
|
||||||
const handleSubmit = async (evt: any) => {
|
const handleSubmit = async (evt: any) => {
|
||||||
|
if (!popupForm) {
|
||||||
|
throw new Error('Popup form is not defined')
|
||||||
|
}
|
||||||
const formItem: Item = {} as Item
|
const formItem: Item = {} as Item
|
||||||
Array.from(evt.target).forEach((input: HTMLInputElement) => {
|
Array.from(evt.target).forEach((input: HTMLInputElement) => {
|
||||||
if (input.name) {
|
if (input.name) {
|
||||||
formItem[input.name] = input.value
|
formItem[input.name] = input.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
formItem.position = { type: 'Point', coordinates: [props.position.lng, props.position.lat] }
|
formItem.position = {
|
||||||
|
type: 'Point',
|
||||||
|
coordinates: [popupForm.position.lng, popupForm.position.lat],
|
||||||
|
}
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
|
|
||||||
const name = formItem.name ? formItem.name : user?.first_name
|
const name = formItem.name ? formItem.name : user?.first_name
|
||||||
@ -73,32 +85,34 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
if (props.item) {
|
if (popupForm.item) {
|
||||||
let success = false
|
let success = false
|
||||||
try {
|
try {
|
||||||
await props.layer.api?.updateItem!({ ...formItem, id: props.item.id })
|
await popupForm.layer.api?.updateItem!({ ...formItem, id: popupForm.item.id })
|
||||||
success = true
|
success = true
|
||||||
// eslint-disable-next-line no-catch-all/no-catch-all
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
updateItem({ ...props.item, ...formItem })
|
updateItem({ ...popupForm.item, ...formItem })
|
||||||
toast.success('Item updated')
|
toast.success('Item updated')
|
||||||
}
|
}
|
||||||
setSpinner(false)
|
setSpinner(false)
|
||||||
map.closePopup()
|
map.closePopup()
|
||||||
} else {
|
} else {
|
||||||
const item = items.find((i) => i.user_created?.id === user?.id && i.layer === props.layer)
|
const item = items.find(
|
||||||
|
(i) => i.user_created?.id === user?.id && i.layer?.id === popupForm.layer.id,
|
||||||
|
)
|
||||||
|
|
||||||
const uuid = crypto.randomUUID()
|
const uuid = crypto.randomUUID()
|
||||||
let success = false
|
let success = false
|
||||||
try {
|
try {
|
||||||
props.layer.userProfileLayer &&
|
popupForm.layer.userProfileLayer &&
|
||||||
item &&
|
item &&
|
||||||
(await props.layer.api?.updateItem!({ ...formItem, id: item.id }))
|
(await popupForm.layer.api?.updateItem!({ ...formItem, id: item.id }))
|
||||||
;(!props.layer.userProfileLayer || !item) &&
|
;(!popupForm.layer.userProfileLayer || !item) &&
|
||||||
(await props.layer.api?.createItem!({
|
(await popupForm.layer.api?.createItem!({
|
||||||
...formItem,
|
...formItem,
|
||||||
name,
|
name,
|
||||||
id: uuid,
|
id: uuid,
|
||||||
@ -109,14 +123,14 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
toast.error(error.toString())
|
toast.error(error.toString())
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
if (props.layer.userProfileLayer && item) updateItem({ ...item, ...formItem })
|
if (popupForm.layer.userProfileLayer && item) updateItem({ ...item, ...formItem })
|
||||||
if (!props.layer.userProfileLayer || !item) {
|
if (!popupForm.layer.userProfileLayer || !item) {
|
||||||
addItem({
|
addItem({
|
||||||
...formItem,
|
...formItem,
|
||||||
name: (formItem.name ? formItem.name : user?.first_name) ?? '',
|
name: (formItem.name ? formItem.name : user?.first_name) ?? '',
|
||||||
user_created: user ?? undefined,
|
user_created: user ?? undefined,
|
||||||
id: uuid,
|
id: uuid,
|
||||||
layer: props.layer,
|
layer: popupForm.layer,
|
||||||
public_edit: !user,
|
public_edit: !user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -126,7 +140,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
setSpinner(false)
|
setSpinner(false)
|
||||||
map.closePopup()
|
map.closePopup()
|
||||||
}
|
}
|
||||||
props.setItemFormPopup!(null)
|
setPopupForm(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetPopup = () => {
|
const resetPopup = () => {
|
||||||
@ -137,77 +151,75 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
resetPopup()
|
resetPopup()
|
||||||
}, [props.position])
|
}, [popupForm?.position])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LeafletPopup
|
popupForm &&
|
||||||
minWidth={275}
|
popupForm.layer.name === activeLayerName && (
|
||||||
maxWidth={275}
|
<LeafletPopup
|
||||||
autoPanPadding={[20, 80]}
|
minWidth={275}
|
||||||
eventHandlers={{
|
maxWidth={275}
|
||||||
remove: () => {
|
autoPanPadding={[20, 80]}
|
||||||
setTimeout(function () {
|
eventHandlers={{
|
||||||
resetPopup()
|
remove: () => {
|
||||||
}, 100)
|
setTimeout(function () {
|
||||||
},
|
resetPopup()
|
||||||
}}
|
}, 100)
|
||||||
position={props.position}
|
},
|
||||||
>
|
}}
|
||||||
<form ref={formRef} onReset={resetPopup} autoComplete='off' onSubmit={(e) => handleSubmit(e)}>
|
position={popupForm.position}
|
||||||
{props.item ? (
|
>
|
||||||
<div className='tw:h-3'></div>
|
<form
|
||||||
) : (
|
ref={formRef}
|
||||||
|
onReset={resetPopup}
|
||||||
|
autoComplete='off'
|
||||||
|
onSubmit={(e) => handleSubmit(e)}
|
||||||
|
>
|
||||||
|
{popupForm.item ? (
|
||||||
|
<div className='tw:h-3'></div>
|
||||||
|
) : (
|
||||||
|
<div className='tw:flex tw:justify-center'>
|
||||||
|
<b className='tw:text-xl tw:text-center tw:font-bold'>{menuText}</b>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{props.children ? (
|
||||||
|
<TemplateItemContext.Provider value={popupForm.item}>
|
||||||
|
{props.children}
|
||||||
|
</TemplateItemContext.Provider>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<TextInput
|
||||||
|
type='text'
|
||||||
|
placeholder='Name'
|
||||||
|
dataField='name'
|
||||||
|
defaultValue={popupForm.item ? popupForm.item.name : ''}
|
||||||
|
inputStyle=''
|
||||||
|
/>
|
||||||
|
<TextAreaInput
|
||||||
|
key={popupForm.position.toString()}
|
||||||
|
placeholder='Text'
|
||||||
|
dataField='text'
|
||||||
|
defaultValue={popupForm.item?.text ?? ''}
|
||||||
|
inputStyle='tw:h-40 tw:mt-5'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className='tw:flex tw:justify-center'>
|
<div className='tw:flex tw:justify-center'>
|
||||||
<b className='tw:text-xl tw:text-center tw:font-bold'>{props.layer.menuText}</b>
|
<button
|
||||||
|
className={
|
||||||
|
spinner
|
||||||
|
? 'tw:btn tw:btn-disabled tw:mt-5 tw:place-self-center'
|
||||||
|
: 'tw:btn tw:mt-5 tw:place-self-center'
|
||||||
|
}
|
||||||
|
type='submit'
|
||||||
|
>
|
||||||
|
{spinner ? <span className='tw:loading tw:loading-spinner'></span> : 'Save'}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</form>
|
||||||
|
</LeafletPopup>
|
||||||
{props.children ? (
|
)
|
||||||
Children.toArray(props.children).map((child) =>
|
|
||||||
isValidElement<{
|
|
||||||
item: Item
|
|
||||||
test: string
|
|
||||||
setPopupTitle: React.Dispatch<React.SetStateAction<string>>
|
|
||||||
}>(child)
|
|
||||||
? cloneElement(child, {
|
|
||||||
item: props.item,
|
|
||||||
key: props.position.toString(),
|
|
||||||
setPopupTitle,
|
|
||||||
})
|
|
||||||
: '',
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<TextInput
|
|
||||||
type='text'
|
|
||||||
placeholder='Name'
|
|
||||||
dataField='name'
|
|
||||||
defaultValue={props.item ? props.item.name : ''}
|
|
||||||
inputStyle=''
|
|
||||||
/>
|
|
||||||
<TextAreaInput
|
|
||||||
key={props.position.toString()}
|
|
||||||
placeholder='Text'
|
|
||||||
dataField='text'
|
|
||||||
defaultValue={props.item?.text ?? ''}
|
|
||||||
inputStyle='tw:h-40 tw:mt-5'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className='tw:flex tw:justify-center'>
|
|
||||||
<button
|
|
||||||
className={
|
|
||||||
spinner
|
|
||||||
? 'tw:btn tw:btn-disabled tw:mt-5 tw:place-self-center'
|
|
||||||
: 'tw:btn tw:mt-5 tw:place-self-center'
|
|
||||||
}
|
|
||||||
type='submit'
|
|
||||||
>
|
|
||||||
{spinner ? <span className='tw:loading tw:loading-spinner'></span> : 'Save'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</LeafletPopup>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { TextInput } from '#components/Input'
|
|||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
interface StartEndInputProps {
|
export interface StartEndInputProps {
|
||||||
item?: Item
|
item?: Item
|
||||||
showLabels?: boolean
|
showLabels?: boolean
|
||||||
updateStartValue?: (value: string) => void
|
updateStartValue?: (value: string) => void
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable no-console */
|
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||||
@ -177,8 +176,6 @@ export const TextView = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (href?.startsWith('#')) {
|
if (href?.startsWith('#')) {
|
||||||
console.log(href.slice(1).toLowerCase())
|
|
||||||
console.log(tags)
|
|
||||||
const tag = tags.find(
|
const tag = tags.find(
|
||||||
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
|
(t) => t.name.toLowerCase() === decodeURI(href).slice(1).toLowerCase(),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
export { PopupTextAreaInput } from './PopupTextAreaInput'
|
||||||
|
export { PopupStartEndInput } from './PopupStartEndInput'
|
||||||
|
export { PopupTextInput } from './PopupTextInput'
|
||||||
|
export { PopupCheckboxInput } from './PopupCheckboxInput'
|
||||||
|
export { TextView } from './TextView'
|
||||||
|
export { StartEndView } from './StartEndView'
|
||||||
|
export { PopupButton } from './PopupButton'
|
||||||
@ -8,12 +8,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
import { LatLng } from 'leaflet'
|
import { LatLng } from 'leaflet'
|
||||||
import { Children, cloneElement, forwardRef, isValidElement, useState } from 'react'
|
import { forwardRef, useState } from 'react'
|
||||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import { useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
|
import { usePopupForm } from '#components/Map/hooks/usePopupForm'
|
||||||
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
||||||
import { timeAgo } from '#utils/TimeAgo'
|
import { timeAgo } from '#utils/TimeAgo'
|
||||||
|
|
||||||
@ -21,12 +22,10 @@ import { HeaderView } from './ItemPopupComponents/HeaderView'
|
|||||||
import { TextView } from './ItemPopupComponents/TextView'
|
import { TextView } from './ItemPopupComponents/TextView'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
|
||||||
|
|
||||||
export interface ItemViewPopupProps {
|
export interface ItemViewPopupProps {
|
||||||
item: Item
|
item: Item
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
// eslint-disable-next-line react/display-name
|
||||||
@ -37,22 +36,26 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
|||||||
const updadateItem = useUpdateItem()
|
const updadateItem = useUpdateItem()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const setSelectPosition = useSetSelectPosition()
|
const setSelectPosition = useSetSelectPosition()
|
||||||
|
const { setPopupForm } = usePopupForm()
|
||||||
|
|
||||||
const [infoExpanded, setInfoExpanded] = useState<boolean>(false)
|
const [infoExpanded, setInfoExpanded] = useState<boolean>(false)
|
||||||
|
|
||||||
const handleEdit = (event: React.MouseEvent<HTMLElement>) => {
|
const handleEdit = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
map.closePopup()
|
map.closePopup()
|
||||||
props.setItemFormPopup &&
|
|
||||||
props.setItemFormPopup({
|
if (!props.item.layer) {
|
||||||
position: new LatLng(
|
throw new Error('Layer is not defined')
|
||||||
props.item.position?.coordinates[1]!,
|
}
|
||||||
props.item.position?.coordinates[0]!,
|
|
||||||
),
|
setPopupForm({
|
||||||
layer: props.item.layer!,
|
position: new LatLng(
|
||||||
item: props.item,
|
props.item.position?.coordinates[1]!,
|
||||||
setItemFormPopup: props.setItemFormPopup,
|
props.item.position?.coordinates[0]!,
|
||||||
})
|
),
|
||||||
|
layer: props.item.layer,
|
||||||
|
item: props.item,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDelete = async (event: React.MouseEvent<HTMLElement>) => {
|
const handleDelete = async (event: React.MouseEvent<HTMLElement>) => {
|
||||||
@ -98,15 +101,7 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
|||||||
loading={loading}
|
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 ?? <TextView text={props.item.text} itemId={props.item.id} />}
|
||||||
Children.toArray(props.children).map((child) =>
|
|
||||||
isValidElement<{ item: Item; test: string }>(child)
|
|
||||||
? cloneElement(child, { item: props.item })
|
|
||||||
: '',
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<TextView text={props.item.text} itemId={props.item.id} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:mt-1'>
|
<div className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:mt-1'>
|
||||||
{infoExpanded ? (
|
{infoExpanded ? (
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||||
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
import { TileLayer, useMapEvents, GeoJSON, useMap } from 'react-leaflet'
|
import { TileLayer, useMapEvents, GeoJSON, useMap } from 'react-leaflet'
|
||||||
import MarkerClusterGroup from 'react-leaflet-cluster'
|
import MarkerClusterGroup from 'react-leaflet-cluster'
|
||||||
import { Outlet, useLocation } from 'react-router-dom'
|
import { Outlet, useLocation } from 'react-router-dom'
|
||||||
@ -20,6 +20,7 @@ import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef'
|
|||||||
import { useAddVisibleLayer } from './hooks/useFilter'
|
import { useAddVisibleLayer } from './hooks/useFilter'
|
||||||
import { useLayers } from './hooks/useLayers'
|
import { useLayers } from './hooks/useLayers'
|
||||||
import { useLeafletRefs } from './hooks/useLeafletRefs'
|
import { useLeafletRefs } from './hooks/useLeafletRefs'
|
||||||
|
import { usePopupForm } from './hooks/usePopupForm'
|
||||||
import {
|
import {
|
||||||
useSelectPosition,
|
useSelectPosition,
|
||||||
useSetMapClicked,
|
useSetMapClicked,
|
||||||
@ -35,7 +36,6 @@ import { TagsControl } from './Subcomponents/Controls/TagsControl'
|
|||||||
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
||||||
import { SelectPosition } from './Subcomponents/SelectPosition'
|
import { SelectPosition } from './Subcomponents/SelectPosition'
|
||||||
|
|
||||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
|
||||||
import type { Feature, Geometry as GeoJSONGeometry, GeoJsonObject } from 'geojson'
|
import type { Feature, Geometry as GeoJSONGeometry, GeoJsonObject } from 'geojson'
|
||||||
|
|
||||||
export function UtopiaMapInner({
|
export function UtopiaMapInner({
|
||||||
@ -62,17 +62,15 @@ export function UtopiaMapInner({
|
|||||||
const setClusterRef = useSetClusterRef()
|
const setClusterRef = useSetClusterRef()
|
||||||
const clusterRef = useClusterRef()
|
const clusterRef = useClusterRef()
|
||||||
const setMapClicked = useSetMapClicked()
|
const setMapClicked = useSetMapClicked()
|
||||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null)
|
const { setPopupForm } = usePopupForm()
|
||||||
|
|
||||||
useTheme(defaultTheme)
|
|
||||||
|
|
||||||
const layers = useLayers()
|
const layers = useLayers()
|
||||||
const addVisibleLayer = useAddVisibleLayer()
|
const addVisibleLayer = useAddVisibleLayer()
|
||||||
const leafletRefs = useLeafletRefs()
|
const leafletRefs = useLeafletRefs()
|
||||||
|
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const map = useMap()
|
const map = useMap()
|
||||||
|
|
||||||
|
useTheme(defaultTheme)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
layers.forEach((layer) => addVisibleLayer(layer))
|
layers.forEach((layer) => addVisibleLayer(layer))
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -119,7 +117,7 @@ export function UtopiaMapInner({
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(e.latlng.lat + ',' + e.latlng.lng)
|
console.log(e.latlng.lat + ',' + e.latlng.lng)
|
||||||
if (selectNewItemPosition) {
|
if (selectNewItemPosition) {
|
||||||
setMapClicked({ position: e.latlng, setItemFormPopup })
|
setMapClicked({ position: e.latlng, setItemFormPopup: setPopupForm })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moveend: () => {},
|
moveend: () => {},
|
||||||
@ -224,15 +222,7 @@ export function UtopiaMapInner({
|
|||||||
maxClusterRadius={50}
|
maxClusterRadius={50}
|
||||||
removeOutsideVisibleBounds={false}
|
removeOutsideVisibleBounds={false}
|
||||||
>
|
>
|
||||||
{Children.toArray(children).map((child) =>
|
{children}
|
||||||
isValidElement<{
|
|
||||||
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>
|
|
||||||
itemFormPopup: ItemFormPopupProps | null
|
|
||||||
clusterRef: React.MutableRefObject<undefined>
|
|
||||||
}>(child)
|
|
||||||
? cloneElement(child, { setItemFormPopup, itemFormPopup, clusterRef })
|
|
||||||
: child,
|
|
||||||
)}
|
|
||||||
</MarkerClusterGroup>
|
</MarkerClusterGroup>
|
||||||
{geo && (
|
{geo && (
|
||||||
<GeoJSON
|
<GeoJSON
|
||||||
@ -242,7 +232,7 @@ export function UtopiaMapInner({
|
|||||||
click: (e) => {
|
click: (e) => {
|
||||||
if (selectNewItemPosition) {
|
if (selectNewItemPosition) {
|
||||||
e.layer.closePopup()
|
e.layer.closePopup()
|
||||||
setMapClicked({ position: e.latlng, setItemFormPopup })
|
setMapClicked({ position: e.latlng, setItemFormPopup: setPopupForm })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
34
src/Components/Map/hooks/usePopupForm.tsx
Normal file
34
src/Components/Map/hooks/usePopupForm.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { createContext, useContext, useState } from 'react'
|
||||||
|
|
||||||
|
import type { PopupFormState } from '#types/PopupFormState'
|
||||||
|
|
||||||
|
type UsePopupFormManagerResult = ReturnType<typeof usePopupFormManager>
|
||||||
|
|
||||||
|
const PoupFormContext = createContext<UsePopupFormManagerResult>({
|
||||||
|
popupForm: {} as PopupFormState | null,
|
||||||
|
setPopupForm: () => {
|
||||||
|
/* empty function */
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function usePopupFormManager(): {
|
||||||
|
popupForm: PopupFormState | null
|
||||||
|
setPopupForm: React.Dispatch<React.SetStateAction<PopupFormState | null>>
|
||||||
|
} {
|
||||||
|
const [popupForm, setPopupForm] = useState<PopupFormState | null>(null)
|
||||||
|
|
||||||
|
return { popupForm, setPopupForm }
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children?: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopupFormProvider: React.FunctionComponent<Props> = ({ children }: Props) => (
|
||||||
|
<PoupFormContext.Provider value={usePopupFormManager()}>{children}</PoupFormContext.Provider>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const usePopupForm = (): UsePopupFormManagerResult => {
|
||||||
|
const { popupForm, setPopupForm } = useContext(PoupFormContext)
|
||||||
|
return { popupForm, setPopupForm }
|
||||||
|
}
|
||||||
@ -15,14 +15,14 @@ import { useUpdateItem } from './useItems'
|
|||||||
import { useHasUserPermission } from './usePermissions'
|
import { useHasUserPermission } from './usePermissions'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
|
||||||
import type { LayerProps } from '#types/LayerProps'
|
import type { LayerProps } from '#types/LayerProps'
|
||||||
|
import type { PopupFormState } from '#types/PopupFormState'
|
||||||
import type { Point } from 'geojson'
|
import type { Point } from 'geojson'
|
||||||
import type { LatLng } from 'leaflet'
|
import type { LatLng } from 'leaflet'
|
||||||
|
|
||||||
interface PolygonClickedProps {
|
interface PolygonClickedProps {
|
||||||
position: LatLng
|
position: LatLng
|
||||||
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
setItemFormPopup: React.Dispatch<React.SetStateAction<PopupFormState | null>>
|
||||||
}
|
}
|
||||||
|
|
||||||
type UseSelectPositionManagerResult = ReturnType<typeof useSelectPositionManager>
|
type UseSelectPositionManagerResult = ReturnType<typeof useSelectPositionManager>
|
||||||
@ -60,7 +60,9 @@ function useSelectPositionManager(): {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectPosition != null) {
|
if (selectPosition != null) {
|
||||||
|
// selectPosition can be null, Layer or Item
|
||||||
if ('menuIcon' in selectPosition) {
|
if ('menuIcon' in selectPosition) {
|
||||||
|
// if selectPosition is a Layer
|
||||||
mapClicked &&
|
mapClicked &&
|
||||||
mapClicked.setItemFormPopup({
|
mapClicked.setItemFormPopup({
|
||||||
layer: selectPosition,
|
layer: selectPosition,
|
||||||
@ -69,6 +71,7 @@ function useSelectPositionManager(): {
|
|||||||
setSelectPosition(null)
|
setSelectPosition(null)
|
||||||
}
|
}
|
||||||
if ('text' in selectPosition) {
|
if ('text' in selectPosition) {
|
||||||
|
// if selectPosition is an Item
|
||||||
const position =
|
const position =
|
||||||
mapClicked?.position.lng &&
|
mapClicked?.position.lng &&
|
||||||
({
|
({
|
||||||
|
|||||||
@ -2,8 +2,7 @@ export { UtopiaMap } from './UtopiaMap'
|
|||||||
export * from './Layer'
|
export * from './Layer'
|
||||||
export { Tags } from './Tags'
|
export { Tags } from './Tags'
|
||||||
export * from './Permissions'
|
export * from './Permissions'
|
||||||
export { ItemForm } from './ItemForm'
|
/*
|
||||||
export { ItemView } from './ItemView'
|
|
||||||
export { PopupTextAreaInput } from './Subcomponents/ItemPopupComponents/PopupTextAreaInput'
|
export { PopupTextAreaInput } from './Subcomponents/ItemPopupComponents/PopupTextAreaInput'
|
||||||
export { PopupStartEndInput } from './Subcomponents/ItemPopupComponents/PopupStartEndInput'
|
export { PopupStartEndInput } from './Subcomponents/ItemPopupComponents/PopupStartEndInput'
|
||||||
export { PopupTextInput } from './Subcomponents/ItemPopupComponents/PopupTextInput'
|
export { PopupTextInput } from './Subcomponents/ItemPopupComponents/PopupTextInput'
|
||||||
@ -11,3 +10,4 @@ export { PopupCheckboxInput } from './Subcomponents/ItemPopupComponents/PopupChe
|
|||||||
export { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
export { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
||||||
export { StartEndView } from './Subcomponents/ItemPopupComponents/StartEndView'
|
export { StartEndView } from './Subcomponents/ItemPopupComponents/StartEndView'
|
||||||
export { PopupButton } from './Subcomponents/ItemPopupComponents/PopupButton'
|
export { PopupButton } from './Subcomponents/ItemPopupComponents/PopupButton'
|
||||||
|
*/
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||||
import { PopupStartEndInput } from '#components/Map'
|
import { PopupStartEndInput } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { StartEndView } from '#components/Map'
|
import { StartEndView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { get } from 'radash'
|
import { get } from 'radash'
|
||||||
|
|
||||||
import { TextView } from '#components/Map'
|
import { TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
import { TextView } from '#components/Map'
|
import { TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
import { ContactInfoView } from '#components/Profile/Subcomponents/ContactInfoView'
|
import { ContactInfoView } from '#components/Profile/Subcomponents/ContactInfoView'
|
||||||
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
|
import { GroupSubHeaderView } from '#components/Profile/Subcomponents/GroupSubHeaderView'
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { TextView } from '#components/Map'
|
import { TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
|
|
||||||
import type { Item } from '#types/Item'
|
import type { Item } from '#types/Item'
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import { useCallback, useEffect, useState } from 'react'
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { TextAreaInput } from '#components/Input'
|
import { TextAreaInput } from '#components/Input'
|
||||||
import { PopupStartEndInput, TextView } from '#components/Map'
|
|
||||||
import { useUpdateItem } from '#components/Map/hooks/useItems'
|
import { useUpdateItem } from '#components/Map/hooks/useItems'
|
||||||
|
import { PopupStartEndInput, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
||||||
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
||||||
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
|
import { TagsWidget } from '#components/Profile/Subcomponents/TagsWidget'
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
|||||||
import { Link, useNavigate } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
||||||
import { StartEndView, TextView } from '#components/Map'
|
|
||||||
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
|
import { useAddFilterTag } from '#components/Map/hooks/useFilter'
|
||||||
import { useItems } from '#components/Map/hooks/useItems'
|
import { useItems } from '#components/Map/hooks/useItems'
|
||||||
|
import { StartEndView, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
import { ActionButton } from '#components/Profile/Subcomponents/ActionsButton'
|
||||||
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
import { LinkedItemsHeaderView } from '#components/Profile/Subcomponents/LinkedItemsHeaderView'
|
||||||
import { TagView } from '#components/Templates/TagView'
|
import { TagView } from '#components/Templates/TagView'
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { StartEndView, TextView } from '#components/Map'
|
|
||||||
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
|
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
|
||||||
|
import { StartEndView, TextView } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
import { HeaderView } from '#components/Map/Subcomponents/ItemPopupComponents/HeaderView'
|
||||||
|
|
||||||
import { DateUserInfo } from './DateUserInfo'
|
import { DateUserInfo } from './DateUserInfo'
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
import { useAuth } from '#components/Auth/useAuth'
|
import { useAuth } from '#components/Auth/useAuth'
|
||||||
import { TextInput, TextAreaInput } from '#components/Input'
|
import { TextInput, TextAreaInput } from '#components/Input'
|
||||||
import { PopupStartEndInput } from '#components/Map'
|
|
||||||
import { useFilterTags } from '#components/Map/hooks/useFilter'
|
import { useFilterTags } from '#components/Map/hooks/useFilter'
|
||||||
import { useAddItem, useItems, useRemoveItem } from '#components/Map/hooks/useItems'
|
import { useAddItem, useItems, useRemoveItem } from '#components/Map/hooks/useItems'
|
||||||
import { useLayers } from '#components/Map/hooks/useLayers'
|
import { useLayers } from '#components/Map/hooks/useLayers'
|
||||||
@ -17,6 +16,7 @@ import { useAddTag, useGetItemTags, useTags } from '#components/Map/hooks/useTag
|
|||||||
import { Control } from '#components/Map/Subcomponents/Controls/Control'
|
import { Control } from '#components/Map/Subcomponents/Controls/Control'
|
||||||
import { SearchControl } from '#components/Map/Subcomponents/Controls/SearchControl'
|
import { SearchControl } from '#components/Map/Subcomponents/Controls/SearchControl'
|
||||||
import { TagsControl } from '#components/Map/Subcomponents/Controls/TagsControl'
|
import { TagsControl } from '#components/Map/Subcomponents/Controls/TagsControl'
|
||||||
|
import { PopupStartEndInput } from '#components/Map/Subcomponents/ItemPopupComponents'
|
||||||
import { PlusButton } from '#components/Profile/Subcomponents/PlusButton'
|
import { PlusButton } from '#components/Profile/Subcomponents/PlusButton'
|
||||||
import { hashTagRegex } from '#utils/HashTagRegex'
|
import { hashTagRegex } from '#utils/HashTagRegex'
|
||||||
import { randomColor } from '#utils/RandomColor'
|
import { randomColor } from '#utils/RandomColor'
|
||||||
|
|||||||
@ -8,6 +8,7 @@ export * from './Components/Profile'
|
|||||||
export * from './Components/Gaming'
|
export * from './Components/Gaming'
|
||||||
export * from './Components/Templates'
|
export * from './Components/Templates'
|
||||||
export * from './Components/Input'
|
export * from './Components/Input'
|
||||||
|
export * from './Components/Item'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|||||||
5
src/types/LayerProps.d.ts
vendored
5
src/types/LayerProps.d.ts
vendored
@ -1,5 +1,4 @@
|
|||||||
import type { Item } from './Item'
|
import type { Item } from './Item'
|
||||||
import type { ItemFormPopupProps } from './ItemFormPopupProps'
|
|
||||||
import type { ItemsApi } from './ItemsApi'
|
import type { ItemsApi } from './ItemsApi'
|
||||||
import type { ItemType } from './ItemType'
|
import type { ItemType } from './ItemType'
|
||||||
import type { MarkerIcon } from './MarkerIcon'
|
import type { MarkerIcon } from './MarkerIcon'
|
||||||
@ -27,8 +26,4 @@ export interface LayerProps {
|
|||||||
public_edit_items?: boolean
|
public_edit_items?: boolean
|
||||||
listed?: boolean
|
listed?: boolean
|
||||||
item_presets?: Record<string, unknown>
|
item_presets?: Record<string, unknown>
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
|
||||||
itemFormPopup?: ItemFormPopupProps | null
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
clusterRef?: any
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,10 +2,8 @@ import type { Item } from './Item'
|
|||||||
import type { LayerProps } from './LayerProps'
|
import type { LayerProps } from './LayerProps'
|
||||||
import type { LatLng } from 'leaflet'
|
import type { LatLng } from 'leaflet'
|
||||||
|
|
||||||
export interface ItemFormPopupProps {
|
export interface PopupFormState {
|
||||||
position: LatLng
|
position: LatLng
|
||||||
layer: LayerProps
|
layer: LayerProps
|
||||||
item?: Item
|
item?: Item
|
||||||
children?: React.ReactNode
|
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user