mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Refactor PopupForm and PopupView
This commit is contained in:
parent
a6c0c273c7
commit
eb054f4d1f
@ -1,6 +1,3 @@
|
||||
import { useContext } from 'react'
|
||||
|
||||
import LayerContext from '#components/Map/LayerContext'
|
||||
import { ItemFormPopup } from '#components/Map/Subcomponents/ItemFormPopup'
|
||||
|
||||
import TemplateItemContext from './TemplateItemContext'
|
||||
@ -9,10 +6,8 @@ import TemplateItemContext from './TemplateItemContext'
|
||||
* @category Item
|
||||
*/
|
||||
export const PopupForm = ({ children }: { children?: React.ReactNode }) => {
|
||||
const { itemFormPopup, setItemFormPopup } = useContext(LayerContext)
|
||||
|
||||
return (
|
||||
itemFormPopup && (
|
||||
<ItemFormPopup
|
||||
key={setItemFormPopup?.name}
|
||||
position={itemFormPopup.position}
|
||||
|
||||
@ -29,14 +29,7 @@ import type { Popup } from 'leaflet'
|
||||
*/
|
||||
export const PopupView = ({ children }: { children?: React.ReactNode }) => {
|
||||
const cardViewContext = useContext(LayerContext)
|
||||
const {
|
||||
name,
|
||||
markerDefaultColor,
|
||||
markerDefaultColor2,
|
||||
markerShape,
|
||||
markerIcon,
|
||||
setItemFormPopup,
|
||||
} = cardViewContext
|
||||
const { name, markerDefaultColor, markerDefaultColor2, markerShape, markerIcon } = cardViewContext
|
||||
|
||||
const filterTags = useFilterTags()
|
||||
|
||||
@ -94,10 +87,6 @@ export const PopupView = ({ children }: { children?: React.ReactNode }) => {
|
||||
],
|
||||
)
|
||||
|
||||
if (!setItemFormPopup) {
|
||||
throw new Error('setItemFormPopup is not defined')
|
||||
}
|
||||
|
||||
return visibleItems.map((item: Item) => {
|
||||
if (!(item.position?.coordinates[0] && item.position.coordinates[1])) return null
|
||||
|
||||
@ -170,7 +159,6 @@ export const PopupView = ({ children }: { children?: React.ReactNode }) => {
|
||||
}
|
||||
}}
|
||||
item={item}
|
||||
setItemFormPopup={setItemFormPopup}
|
||||
>
|
||||
{children}
|
||||
</ItemViewPopup>
|
||||
|
||||
@ -35,9 +35,6 @@ export const Layer = ({
|
||||
// eslint-disable-next-line camelcase
|
||||
public_edit_items,
|
||||
listed = true,
|
||||
setItemFormPopup,
|
||||
itemFormPopup,
|
||||
clusterRef,
|
||||
}: LayerProps) => {
|
||||
const setItemsApi = useSetItemsApi()
|
||||
const setItemsData = useSetItemsData()
|
||||
@ -68,10 +65,6 @@ export const Layer = ({
|
||||
// eslint-disable-next-line camelcase
|
||||
public_edit_items,
|
||||
listed,
|
||||
setItemFormPopup,
|
||||
itemFormPopup,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
clusterRef,
|
||||
})
|
||||
api &&
|
||||
setItemsApi({
|
||||
@ -93,10 +86,6 @@ export const Layer = ({
|
||||
// eslint-disable-next-line camelcase
|
||||
public_edit_items,
|
||||
listed,
|
||||
setItemFormPopup,
|
||||
itemFormPopup,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
clusterRef,
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, api])
|
||||
@ -123,8 +112,6 @@ export const Layer = ({
|
||||
markerDefaultColor2,
|
||||
markerShape,
|
||||
markerIcon,
|
||||
itemFormPopup,
|
||||
setItemFormPopup,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import { createContext } from 'react'
|
||||
|
||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||
|
||||
interface LayerContextType {
|
||||
name: string
|
||||
markerDefaultColor: string
|
||||
markerDefaultColor2: string
|
||||
markerShape: string
|
||||
markerIcon: string
|
||||
itemFormPopup: ItemFormPopupProps | null | undefined
|
||||
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>> | undefined
|
||||
}
|
||||
|
||||
const LayerContext = createContext<LayerContextType>({
|
||||
@ -18,8 +14,6 @@ const LayerContext = createContext<LayerContextType>({
|
||||
markerDefaultColor2: '',
|
||||
markerShape: '',
|
||||
markerIcon: '',
|
||||
itemFormPopup: undefined,
|
||||
setItemFormPopup: undefined,
|
||||
})
|
||||
|
||||
export default LayerContext
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
/* eslint-disable @typescript-eslint/prefer-optional-chain */
|
||||
/* 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-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
@ -14,18 +13,22 @@ import { useAuth } from '#components/Auth/useAuth'
|
||||
import { TextAreaInput } from '#components/Input/TextAreaInput'
|
||||
import { TextInput } from '#components/Input/TextInput'
|
||||
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 { hashTagRegex } from '#utils/HashTagRegex'
|
||||
import { randomColor } from '#utils/RandomColor'
|
||||
|
||||
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 [spinner, setSpinner] = useState(false)
|
||||
|
||||
const [popupTitle, setPopupTitle] = useState<string>('')
|
||||
const [, setPopupTitle] = useState<string>('')
|
||||
|
||||
const formRef = useRef<HTMLFormElement>(null)
|
||||
|
||||
@ -35,8 +38,6 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
const updateItem = useUpdateItem()
|
||||
const items = useItems()
|
||||
|
||||
const removeItem = useRemoveItem()
|
||||
|
||||
const tags = useTags()
|
||||
const addTag = useAddTag()
|
||||
|
||||
@ -44,14 +45,22 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
|
||||
const { user } = useAuth()
|
||||
|
||||
const { popupForm, setPopupForm } = usePopupForm()
|
||||
|
||||
const handleSubmit = async (evt: any) => {
|
||||
if (!popupForm) {
|
||||
throw new Error('Popup form is not defined')
|
||||
}
|
||||
const formItem: Item = {} as Item
|
||||
Array.from(evt.target).forEach((input: HTMLInputElement) => {
|
||||
if (input.name) {
|
||||
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()
|
||||
|
||||
const name = formItem.name ? formItem.name : user?.first_name
|
||||
@ -126,7 +135,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
setSpinner(false)
|
||||
map.closePopup()
|
||||
}
|
||||
props.setItemFormPopup!(null)
|
||||
setPopupForm(null)
|
||||
}
|
||||
|
||||
const resetPopup = () => {
|
||||
|
||||
@ -8,12 +8,13 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
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 { useNavigate } from 'react-router-dom'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { useRemoveItem, useUpdateItem } from '#components/Map/hooks/useItems'
|
||||
import { usePopupForm } from '#components/Map/hooks/usePopupForm'
|
||||
import { useSetSelectPosition } from '#components/Map/hooks/useSelectPosition'
|
||||
import { timeAgo } from '#utils/TimeAgo'
|
||||
|
||||
@ -21,12 +22,10 @@ import { HeaderView } from './ItemPopupComponents/HeaderView'
|
||||
import { TextView } from './ItemPopupComponents/TextView'
|
||||
|
||||
import type { Item } from '#types/Item'
|
||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||
|
||||
export interface ItemViewPopupProps {
|
||||
item: Item
|
||||
children?: React.ReactNode
|
||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
@ -37,22 +36,22 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
||||
const updadateItem = useUpdateItem()
|
||||
const navigate = useNavigate()
|
||||
const setSelectPosition = useSetSelectPosition()
|
||||
const { setPopupForm } = usePopupForm()
|
||||
|
||||
const [infoExpanded, setInfoExpanded] = useState<boolean>(false)
|
||||
|
||||
const handleEdit = (event: React.MouseEvent<HTMLElement>) => {
|
||||
event.stopPropagation()
|
||||
map.closePopup()
|
||||
props.setItemFormPopup &&
|
||||
props.setItemFormPopup({
|
||||
position: new LatLng(
|
||||
props.item.position?.coordinates[1]!,
|
||||
props.item.position?.coordinates[0]!,
|
||||
),
|
||||
layer: props.item.layer!,
|
||||
item: props.item,
|
||||
setItemFormPopup: props.setItemFormPopup,
|
||||
})
|
||||
|
||||
setPopupForm({
|
||||
position: new LatLng(
|
||||
props.item.position?.coordinates[1]!,
|
||||
props.item.position?.coordinates[0]!,
|
||||
),
|
||||
layer: props.item.layer!,
|
||||
item: props.item,
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = async (event: React.MouseEvent<HTMLElement>) => {
|
||||
@ -97,16 +96,8 @@ export const ItemViewPopup = forwardRef((props: ItemViewPopupProps, ref: any) =>
|
||||
}}
|
||||
loading={loading}
|
||||
/>
|
||||
<div className='tw:overflow-y-auto tw:overflow-x-hidden tw:max-h-64 fade'>
|
||||
{props.children ? (
|
||||
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 className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64 fade'>
|
||||
{props.children ?? <TextView text={props.item.text} itemId={props.item.id} />}
|
||||
</div>
|
||||
<div className='tw:flex tw:-mb-1 tw:flex-row tw:mr-2 tw:mt-1'>
|
||||
{infoExpanded ? (
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* 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 MarkerClusterGroup from 'react-leaflet-cluster'
|
||||
import { Outlet, useLocation } from 'react-router-dom'
|
||||
@ -20,6 +20,7 @@ import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef'
|
||||
import { useAddVisibleLayer } from './hooks/useFilter'
|
||||
import { useLayers } from './hooks/useLayers'
|
||||
import { useLeafletRefs } from './hooks/useLeafletRefs'
|
||||
import { usePopupForm } from './hooks/usePopupForm'
|
||||
import {
|
||||
useSelectPosition,
|
||||
useSetMapClicked,
|
||||
@ -35,7 +36,6 @@ import { TagsControl } from './Subcomponents/Controls/TagsControl'
|
||||
import { TextView } from './Subcomponents/ItemPopupComponents/TextView'
|
||||
import { SelectPosition } from './Subcomponents/SelectPosition'
|
||||
|
||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||
import type { Feature, Geometry as GeoJSONGeometry, GeoJsonObject } from 'geojson'
|
||||
|
||||
export function UtopiaMapInner({
|
||||
@ -62,7 +62,7 @@ export function UtopiaMapInner({
|
||||
const setClusterRef = useSetClusterRef()
|
||||
const clusterRef = useClusterRef()
|
||||
const setMapClicked = useSetMapClicked()
|
||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null)
|
||||
const { setPopupForm } = usePopupForm()
|
||||
|
||||
useTheme(defaultTheme)
|
||||
|
||||
@ -119,7 +119,7 @@ export function UtopiaMapInner({
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(e.latlng.lat + ',' + e.latlng.lng)
|
||||
if (selectNewItemPosition) {
|
||||
setMapClicked({ position: e.latlng, setItemFormPopup })
|
||||
setMapClicked({ position: e.latlng, setItemFormPopup: setPopupForm })
|
||||
}
|
||||
},
|
||||
moveend: () => {},
|
||||
@ -224,15 +224,7 @@ export function UtopiaMapInner({
|
||||
maxClusterRadius={50}
|
||||
removeOutsideVisibleBounds={false}
|
||||
>
|
||||
{Children.toArray(children).map((child) =>
|
||||
isValidElement<{
|
||||
setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>
|
||||
itemFormPopup: ItemFormPopupProps | null
|
||||
clusterRef: React.MutableRefObject<undefined>
|
||||
}>(child)
|
||||
? cloneElement(child, { setItemFormPopup, itemFormPopup, clusterRef })
|
||||
: child,
|
||||
)}
|
||||
{children}
|
||||
</MarkerClusterGroup>
|
||||
{geo && (
|
||||
<GeoJSON
|
||||
@ -242,7 +234,7 @@ export function UtopiaMapInner({
|
||||
click: (e) => {
|
||||
if (selectNewItemPosition) {
|
||||
e.layer.closePopup()
|
||||
setMapClicked({ position: e.latlng, setItemFormPopup })
|
||||
setMapClicked({ position: e.latlng, setItemFormPopup: setPopupForm })
|
||||
}
|
||||
},
|
||||
}}
|
||||
|
||||
32
src/Components/Map/hooks/usePopupForm.tsx
Normal file
32
src/Components/Map/hooks/usePopupForm.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import { createContext, useContext, useState } from 'react'
|
||||
|
||||
import type { ItemFormPopupProps } from '#types/ItemFormPopupProps'
|
||||
|
||||
type UsePopupFormManagerResult = ReturnType<typeof usePopupFormManager>
|
||||
|
||||
const PoupFormContext = createContext<UsePopupFormManagerResult>({
|
||||
popupForm: {} as ItemFormPopupProps | null,
|
||||
setPopupForm: () => {},
|
||||
})
|
||||
|
||||
function usePopupFormManager(): {
|
||||
popupForm: ItemFormPopupProps | null
|
||||
setPopupForm: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||
} {
|
||||
const [popupForm, setPopupForm] = useState<ItemFormPopupProps | null>(null)
|
||||
|
||||
return { popupForm, setPopupForm }
|
||||
}
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const ClusterRefProvider: React.FunctionComponent<Props> = ({ children }: Props) => (
|
||||
<PoupFormContext.Provider value={usePopupFormManager()}>{children}</PoupFormContext.Provider>
|
||||
)
|
||||
|
||||
export const usePopupForm = (): UsePopupFormManagerResult => {
|
||||
const { popupForm, setPopupForm } = useContext(PoupFormContext)
|
||||
return { popupForm, setPopupForm }
|
||||
}
|
||||
1
src/types/ItemFormPopupProps.d.ts
vendored
1
src/types/ItemFormPopupProps.d.ts
vendored
@ -7,5 +7,4 @@ export interface ItemFormPopupProps {
|
||||
layer: LayerProps
|
||||
item?: Item
|
||||
children?: React.ReactNode
|
||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||
}
|
||||
|
||||
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 { ItemFormPopupProps } from './ItemFormPopupProps'
|
||||
import type { ItemsApi } from './ItemsApi'
|
||||
import type { ItemType } from './ItemType'
|
||||
|
||||
@ -26,8 +25,4 @@ export interface LayerProps {
|
||||
public_edit_items?: boolean
|
||||
listed?: boolean
|
||||
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
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ export default defineConfig({
|
||||
exclude: [...configDefaults.exclude, 'src/**/*.cy.tsx'],
|
||||
reporter: ['html', 'json-summary'],
|
||||
thresholds: {
|
||||
lines: 1,
|
||||
lines: 2,
|
||||
functions: 54,
|
||||
branches: 56,
|
||||
statements: 1,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user