fix(source): enhancing layer control (#223)

* 3.0.82

* version number

* 3.0.84

* 3.0.85

* 3.0.86

* 3.0.87

* 3.0.88

* url layer parameter

* layer control customizing

* 3.0.89

* 3.0.90

* 3.0.91

* typing

* 3.0.92

* fixing bug and imports

* 3.0.93

* fixing bug on profile-item-create caused by PR 185
This commit is contained in:
Anton Tranelis 2025-05-23 10:04:34 +02:00 committed by GitHub
parent 82b1f39141
commit ed9906ae2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 103 additions and 45 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "utopia-ui",
"version": "3.0.86",
"version": "3.0.93",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "utopia-ui",
"version": "3.0.86",
"version": "3.0.93",
"license": "GPL-3.0-only",
"dependencies": {
"@heroicons/react": "^2.0.17",

View File

@ -1,6 +1,6 @@
{
"name": "utopia-ui",
"version": "3.0.86",
"version": "3.0.93",
"description": "Reuseable React Components to build mapping apps for real life communities and networks",
"repository": "https://github.com/utopia-os/utopia-ui",
"homepage": "https://utopia-os.org/",

View File

@ -5,8 +5,8 @@ import LayerSVG from '#assets/layer.svg'
import { useIsLayerVisible, useToggleVisibleLayer } from '#components/Map/hooks/useFilter'
import { useLayers } from '#components/Map/hooks/useLayers'
export function LayerControl() {
const [open, setOpen] = useState(false)
export function LayerControl({ expandLayerControl = false }: { expandLayerControl: boolean }) {
const [open, setOpen] = useState(expandLayerControl)
const layers = useLayers()

View File

@ -101,9 +101,7 @@ export function ItemFormPopup(props: Props) {
setSpinner(false)
map.closePopup()
} else {
const item = items.find(
(i) => i.user_created?.id === user?.id && i.layer?.id === popupForm.layer.id,
)
const item = items.find((i) => i.user_created?.id === user?.id && i.layer === popupForm.layer)
const uuid = crypto.randomUUID()
let success = false

View File

@ -1,8 +1,6 @@
import { useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { useAddFilterTag, useFilterTags, useResetFilterTags } from './hooks/useFilter'
import { useSetTagData, useSetTagApi, useTags } from './hooks/useTags'
import { useSetTagData, useSetTagApi } from './hooks/useTags'
import type { ItemsApi } from '#types/ItemsApi'
import type { Tag } from '#types/Tag'
@ -42,36 +40,5 @@ export function Tags({ data, api }: { data?: Tag[]; api?: ItemsApi<Tag> }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [api, data])
const location = useLocation()
const addFilterTag = useAddFilterTag()
const resetFilterTags = useResetFilterTags()
const tags = useTags()
const filterTags = useFilterTags()
useEffect(() => {
const params = new URLSearchParams(location.search)
const urlTags = params.get('tags')
const decodedTags = urlTags ? decodeURIComponent(urlTags) : ''
const decodedTagsArray = decodedTags.split(';')
if (
decodedTagsArray.some(
(ut) => !filterTags.find((ft) => ut.toLocaleLowerCase() === ft.name.toLocaleLowerCase()),
) ||
filterTags.some(
(ft) =>
!decodedTagsArray.find((ut) => ut.toLocaleLowerCase() === ft.name.toLocaleLowerCase()),
)
) {
resetFilterTags()
decodedTagsArray.map((urlTag) => {
const tag = tags.find((t) => t.name.toLocaleLowerCase() === urlTag.toLocaleLowerCase())
tag && addFilterTag(tag)
return null
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location, tags])
return <></>
}

View File

@ -55,6 +55,7 @@ function UtopiaMap({
showThemeControl = false,
defaultTheme,
donationWidget,
expandLayerControl,
}: {
/** height of the map (default '500px') */
height?: string
@ -82,6 +83,8 @@ function UtopiaMap({
defaultTheme?: string
/** ask to donate to the Utopia Project OpenCollective campaign (default false) */
donationWidget?: boolean
/** open layer control on map initialisation */
expandLayerControl?: boolean
}) {
return (
<ContextWrapper>
@ -100,6 +103,7 @@ function UtopiaMap({
donationWidget={donationWidget}
showThemeControl={showThemeControl}
defaultTheme={defaultTheme}
expandLayerControl={expandLayerControl}
>
{children}
</UtopiaMapInner>

View File

@ -17,7 +17,13 @@ import { useTheme } from '#components/AppShell/hooks/useTheme'
import { containsUUID } from '#utils/ContainsUUID'
import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef'
import { useAddVisibleLayer } from './hooks/useFilter'
import {
useAddFilterTag,
useAddVisibleLayer,
useFilterTags,
useResetFilterTags,
useToggleVisibleLayer,
} from './hooks/useFilter'
import { useLayers } from './hooks/useLayers'
import { useLeafletRefs } from './hooks/useLeafletRefs'
import { usePopupForm } from './hooks/usePopupForm'
@ -26,6 +32,7 @@ import {
useSetMapClicked,
useSetSelectPosition,
} from './hooks/useSelectPosition'
import { useTags } from './hooks/useTags'
import AddButton from './Subcomponents/AddButton'
import { Control } from './Subcomponents/Controls/Control'
import { FilterControl } from './Subcomponents/Controls/FilterControl'
@ -47,6 +54,7 @@ export function UtopiaMapInner({
showThemeControl = false,
defaultTheme = '',
donationWidget,
expandLayerControl,
}: {
children?: React.ReactNode
geo?: GeoJsonObject
@ -56,6 +64,7 @@ export function UtopiaMapInner({
donationWidget?: boolean
showThemeControl?: boolean
defaultTheme?: string
expandLayerControl?: boolean
}) {
const selectNewItemPosition = useSelectPosition()
const setSelectNewItemPosition = useSetSelectPosition()
@ -198,6 +207,63 @@ export function UtopiaMapInner({
}
}
const addFilterTag = useAddFilterTag()
const resetFilterTags = useResetFilterTags()
const tags = useTags()
const filterTags = useFilterTags()
useEffect(() => {
const params = new URLSearchParams(location.search)
const urlTags = params.get('tags')
const decodedTags = urlTags ? decodeURIComponent(urlTags) : ''
const decodedTagsArray = decodedTags.split(';').filter(Boolean)
const urlDiffersFromState =
decodedTagsArray.some(
(ut) => !filterTags.find((ft) => ut.toLowerCase() === ft.name.toLowerCase()),
) ||
filterTags.some(
(ft) => !decodedTagsArray.find((ut) => ut.toLowerCase() === ft.name.toLowerCase()),
)
if (urlDiffersFromState) {
resetFilterTags()
decodedTagsArray.forEach((urlTag) => {
const match = tags.find((t) => t.name.toLowerCase() === urlTag.toLowerCase())
if (match) addFilterTag(match)
})
}
}, [location, tags, filterTags, addFilterTag, resetFilterTags])
const toggleVisibleLayer = useToggleVisibleLayer()
const allLayers = useLayers()
const initializedRef = useRef(false)
useEffect(() => {
if (initializedRef.current || allLayers.length === 0) return
const params = new URLSearchParams(location.search)
const urlLayersParam = params.get('layers')
if (!urlLayersParam) {
initializedRef.current = true
return
}
const urlLayerNames = urlLayersParam.split(',').filter(Boolean)
const layerNamesToHide = allLayers
.map((l) => l.name)
.filter((name) => !urlLayerNames.includes(name))
layerNamesToHide.forEach((name) => {
const match = allLayers.find((l) => l.name === name)
if (match) toggleVisibleLayer(match)
})
initializedRef.current = true
}, [location, allLayers, toggleVisibleLayer])
return (
<div className={`tw:h-full ${selectNewItemPosition != null ? 'crosshair-cursor-enabled' : ''}`}>
<Outlet />
@ -207,7 +273,7 @@ export function UtopiaMapInner({
</Control>
<Control position='bottomLeft' zIndex='999' absolute>
{showFilterControl && <FilterControl />}
{showLayerControl && <LayerControl />}
{showLayerControl && <LayerControl expandLayerControl={expandLayerControl ?? false} />}
{showGratitudeControl && <GratitudeControl />}
</Control>
<TileLayer

View File

@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable no-case-declarations */
import { useCallback, useReducer, createContext, useContext, useState } from 'react'
import { useCallback, useReducer, createContext, useContext, useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { useLayers } from './useLayers'
@ -100,6 +100,28 @@ function useFilterManager(initialTags: Tag[]): {
}
}, initialLayers)
const allLayers = useLayers()
useEffect(() => {
if (allLayers.length === 0) return
const visibleNames = visibleLayers.map((l) => l.name)
const allNames = allLayers.map((l) => l.name)
const params = new URLSearchParams(location.search)
const allVisible =
visibleNames.length === allNames.length &&
visibleNames.every((name) => allNames.includes(name))
if (allVisible) {
params.delete('layers')
} else {
params.set('layers', visibleNames.join(','))
}
navigate(`${location.pathname}?${params.toString()}`, { replace: true })
}, [visibleLayers, allLayers, navigate])
const [visibleGroupTypes, dispatchGroupTypes] = useReducer(
(state: string[], action: ActionType) => {
switch (action.type) {

View File

@ -17,4 +17,5 @@ export interface UtopiaMapProps {
infoText?: string
donationWidget?: boolean
defaultTheme?: string
expandLayerControl?: boolean
}