mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
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:
parent
82b1f39141
commit
ed9906ae2f
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.86",
|
"version": "3.0.93",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "3.0.86",
|
"version": "3.0.93",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@heroicons/react": "^2.0.17",
|
"@heroicons/react": "^2.0.17",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"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",
|
"description": "Reuseable React Components to build mapping apps for real life communities and networks",
|
||||||
"repository": "https://github.com/utopia-os/utopia-ui",
|
"repository": "https://github.com/utopia-os/utopia-ui",
|
||||||
"homepage": "https://utopia-os.org/",
|
"homepage": "https://utopia-os.org/",
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import LayerSVG from '#assets/layer.svg'
|
|||||||
import { useIsLayerVisible, useToggleVisibleLayer } from '#components/Map/hooks/useFilter'
|
import { useIsLayerVisible, useToggleVisibleLayer } from '#components/Map/hooks/useFilter'
|
||||||
import { useLayers } from '#components/Map/hooks/useLayers'
|
import { useLayers } from '#components/Map/hooks/useLayers'
|
||||||
|
|
||||||
export function LayerControl() {
|
export function LayerControl({ expandLayerControl = false }: { expandLayerControl: boolean }) {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(expandLayerControl)
|
||||||
|
|
||||||
const layers = useLayers()
|
const layers = useLayers()
|
||||||
|
|
||||||
|
|||||||
@ -101,9 +101,7 @@ export function ItemFormPopup(props: Props) {
|
|||||||
setSpinner(false)
|
setSpinner(false)
|
||||||
map.closePopup()
|
map.closePopup()
|
||||||
} else {
|
} else {
|
||||||
const item = items.find(
|
const item = items.find((i) => i.user_created?.id === user?.id && i.layer === popupForm.layer)
|
||||||
(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
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useLocation } from 'react-router-dom'
|
|
||||||
|
|
||||||
import { useAddFilterTag, useFilterTags, useResetFilterTags } from './hooks/useFilter'
|
import { useSetTagData, useSetTagApi } from './hooks/useTags'
|
||||||
import { useSetTagData, useSetTagApi, useTags } from './hooks/useTags'
|
|
||||||
|
|
||||||
import type { ItemsApi } from '#types/ItemsApi'
|
import type { ItemsApi } from '#types/ItemsApi'
|
||||||
import type { Tag } from '#types/Tag'
|
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
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [api, data])
|
}, [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 <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,7 @@ function UtopiaMap({
|
|||||||
showThemeControl = false,
|
showThemeControl = false,
|
||||||
defaultTheme,
|
defaultTheme,
|
||||||
donationWidget,
|
donationWidget,
|
||||||
|
expandLayerControl,
|
||||||
}: {
|
}: {
|
||||||
/** height of the map (default '500px') */
|
/** height of the map (default '500px') */
|
||||||
height?: string
|
height?: string
|
||||||
@ -82,6 +83,8 @@ function UtopiaMap({
|
|||||||
defaultTheme?: string
|
defaultTheme?: string
|
||||||
/** ask to donate to the Utopia Project OpenCollective campaign (default false) */
|
/** ask to donate to the Utopia Project OpenCollective campaign (default false) */
|
||||||
donationWidget?: boolean
|
donationWidget?: boolean
|
||||||
|
/** open layer control on map initialisation */
|
||||||
|
expandLayerControl?: boolean
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<ContextWrapper>
|
<ContextWrapper>
|
||||||
@ -100,6 +103,7 @@ function UtopiaMap({
|
|||||||
donationWidget={donationWidget}
|
donationWidget={donationWidget}
|
||||||
showThemeControl={showThemeControl}
|
showThemeControl={showThemeControl}
|
||||||
defaultTheme={defaultTheme}
|
defaultTheme={defaultTheme}
|
||||||
|
expandLayerControl={expandLayerControl}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</UtopiaMapInner>
|
</UtopiaMapInner>
|
||||||
|
|||||||
@ -17,7 +17,13 @@ import { useTheme } from '#components/AppShell/hooks/useTheme'
|
|||||||
import { containsUUID } from '#utils/ContainsUUID'
|
import { containsUUID } from '#utils/ContainsUUID'
|
||||||
|
|
||||||
import { useClusterRef, useSetClusterRef } from './hooks/useClusterRef'
|
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 { useLayers } from './hooks/useLayers'
|
||||||
import { useLeafletRefs } from './hooks/useLeafletRefs'
|
import { useLeafletRefs } from './hooks/useLeafletRefs'
|
||||||
import { usePopupForm } from './hooks/usePopupForm'
|
import { usePopupForm } from './hooks/usePopupForm'
|
||||||
@ -26,6 +32,7 @@ import {
|
|||||||
useSetMapClicked,
|
useSetMapClicked,
|
||||||
useSetSelectPosition,
|
useSetSelectPosition,
|
||||||
} from './hooks/useSelectPosition'
|
} from './hooks/useSelectPosition'
|
||||||
|
import { useTags } from './hooks/useTags'
|
||||||
import AddButton from './Subcomponents/AddButton'
|
import AddButton from './Subcomponents/AddButton'
|
||||||
import { Control } from './Subcomponents/Controls/Control'
|
import { Control } from './Subcomponents/Controls/Control'
|
||||||
import { FilterControl } from './Subcomponents/Controls/FilterControl'
|
import { FilterControl } from './Subcomponents/Controls/FilterControl'
|
||||||
@ -47,6 +54,7 @@ export function UtopiaMapInner({
|
|||||||
showThemeControl = false,
|
showThemeControl = false,
|
||||||
defaultTheme = '',
|
defaultTheme = '',
|
||||||
donationWidget,
|
donationWidget,
|
||||||
|
expandLayerControl,
|
||||||
}: {
|
}: {
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
geo?: GeoJsonObject
|
geo?: GeoJsonObject
|
||||||
@ -56,6 +64,7 @@ export function UtopiaMapInner({
|
|||||||
donationWidget?: boolean
|
donationWidget?: boolean
|
||||||
showThemeControl?: boolean
|
showThemeControl?: boolean
|
||||||
defaultTheme?: string
|
defaultTheme?: string
|
||||||
|
expandLayerControl?: boolean
|
||||||
}) {
|
}) {
|
||||||
const selectNewItemPosition = useSelectPosition()
|
const selectNewItemPosition = useSelectPosition()
|
||||||
const setSelectNewItemPosition = useSetSelectPosition()
|
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 (
|
return (
|
||||||
<div className={`tw:h-full ${selectNewItemPosition != null ? 'crosshair-cursor-enabled' : ''}`}>
|
<div className={`tw:h-full ${selectNewItemPosition != null ? 'crosshair-cursor-enabled' : ''}`}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
@ -207,7 +273,7 @@ export function UtopiaMapInner({
|
|||||||
</Control>
|
</Control>
|
||||||
<Control position='bottomLeft' zIndex='999' absolute>
|
<Control position='bottomLeft' zIndex='999' absolute>
|
||||||
{showFilterControl && <FilterControl />}
|
{showFilterControl && <FilterControl />}
|
||||||
{showLayerControl && <LayerControl />}
|
{showLayerControl && <LayerControl expandLayerControl={expandLayerControl ?? false} />}
|
||||||
{showGratitudeControl && <GratitudeControl />}
|
{showGratitudeControl && <GratitudeControl />}
|
||||||
</Control>
|
</Control>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||||
/* eslint-disable no-case-declarations */
|
/* 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 { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
import { useLayers } from './useLayers'
|
import { useLayers } from './useLayers'
|
||||||
@ -100,6 +100,28 @@ function useFilterManager(initialTags: Tag[]): {
|
|||||||
}
|
}
|
||||||
}, initialLayers)
|
}, 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(
|
const [visibleGroupTypes, dispatchGroupTypes] = useReducer(
|
||||||
(state: string[], action: ActionType) => {
|
(state: string[], action: ActionType) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|||||||
1
src/types/UtopiaMapProps.d.ts
vendored
1
src/types/UtopiaMapProps.d.ts
vendored
@ -17,4 +17,5 @@ export interface UtopiaMapProps {
|
|||||||
infoText?: string
|
infoText?: string
|
||||||
donationWidget?: boolean
|
donationWidget?: boolean
|
||||||
defaultTheme?: string
|
defaultTheme?: string
|
||||||
|
expandLayerControl?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user