mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
181 lines
5.9 KiB
TypeScript
181 lines
5.9 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
import { DomEvent } from 'leaflet'
|
|
import { useEffect, useRef, useState } from 'react'
|
|
import SVG from 'react-inlinesvg'
|
|
|
|
import PlusSVG from '#assets/plus.svg'
|
|
import { useAppState } from '#components/AppShell/hooks/useAppState'
|
|
import { useLayers } from '#components/Map/hooks/useLayers'
|
|
import { useHasUserPermission } from '#components/Map/hooks/usePermissions'
|
|
import useWindowDimensions from '#components/Map/hooks/useWindowDimension'
|
|
|
|
import type {
|
|
MouseEvent as ReactMouseEvent,
|
|
TouchEvent as ReactTouchEvent,
|
|
PointerEvent as ReactPointerEvent,
|
|
} from 'react'
|
|
|
|
export default function AddButton({
|
|
triggerAction,
|
|
}: {
|
|
triggerAction: React.Dispatch<React.SetStateAction<any>>
|
|
}) {
|
|
const layers = useLayers()
|
|
const hasUserPermission = useHasUserPermission()
|
|
const appState = useAppState()
|
|
const { width } = useWindowDimensions()
|
|
const isMobile = width < 768
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
const container = containerRef.current
|
|
if (!container) return
|
|
|
|
DomEvent.disableClickPropagation(container)
|
|
DomEvent.disableScrollPropagation(container)
|
|
|
|
const stopPointerPropagation = (event: PointerEvent) => {
|
|
event.stopPropagation()
|
|
}
|
|
|
|
DomEvent.on(container, 'pointerdown pointerup pointermove', stopPointerPropagation)
|
|
|
|
return () => {
|
|
DomEvent.off(container, 'pointerdown pointerup pointermove', stopPointerPropagation)
|
|
}
|
|
}, [])
|
|
|
|
const canAddItems = () => {
|
|
let canAdd = false
|
|
layers.map((layer) => {
|
|
if (
|
|
layer.api?.createItem &&
|
|
hasUserPermission(layer.api.collectionName!, 'create', undefined, layer) &&
|
|
layer.listed
|
|
)
|
|
canAdd = true
|
|
return null
|
|
})
|
|
return canAdd
|
|
}
|
|
|
|
const stopPropagation = (
|
|
event:
|
|
| ReactMouseEvent<HTMLElement>
|
|
| ReactTouchEvent<HTMLElement>
|
|
| ReactPointerEvent<HTMLElement>,
|
|
): void => {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
if (
|
|
'nativeEvent' in event &&
|
|
typeof event.nativeEvent.stopImmediatePropagation === 'function'
|
|
) {
|
|
event.nativeEvent.stopImmediatePropagation()
|
|
}
|
|
}
|
|
|
|
const handleLayerSelect = (
|
|
event: ReactMouseEvent<HTMLButtonElement> | ReactTouchEvent<HTMLButtonElement>,
|
|
layer: (typeof layers)[number],
|
|
) => {
|
|
stopPropagation(event)
|
|
triggerAction(layer)
|
|
setIsOpen(false)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{canAddItems() ? (
|
|
<div
|
|
ref={containerRef}
|
|
className={`tw:dropdown tw:dropdown-top tw:dropdown-end tw:z-500 tw:absolute tw:right-4 tw:bottom-4 ${isOpen ? 'tw:dropdown-open' : ''}`}
|
|
>
|
|
<button
|
|
type='button'
|
|
className='tw:z-500 tw:btn tw:btn-circle tw:btn-lg tw:shadow tw:bg-base-100'
|
|
onPointerDown={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onPointerUp={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onMouseDown={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onMouseUp={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onClick={(event) => {
|
|
stopPropagation(event)
|
|
setIsOpen((prev) => !prev)
|
|
}}
|
|
onTouchStart={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onTouchEnd={(event) => {
|
|
stopPropagation(event)
|
|
setIsOpen((prev) => !prev)
|
|
}}
|
|
>
|
|
<SVG src={PlusSVG} className='tw:h-5 tw:w-5' />
|
|
</button>
|
|
<ul
|
|
tabIndex={0}
|
|
className='tw:dropdown-content tw:pr-1 tw:list-none tw:space-y-3 tw:pb-3'
|
|
>
|
|
{layers.map(
|
|
(layer) =>
|
|
layer.api?.createItem &&
|
|
hasUserPermission(layer.api.collectionName!, 'create', undefined, layer) &&
|
|
layer.listed && (
|
|
<li key={layer.name}>
|
|
<a>
|
|
<div
|
|
className={`tw:tooltip tw:tooltip-left ${isMobile ? 'tw:tooltip-open' : ''}`}
|
|
data-tip={layer.menuText}
|
|
>
|
|
<button
|
|
tabIndex={0}
|
|
className='tw:z-500 tw:border-0 tw:p-0 tw:w-10 tw:h-10 tw:cursor-pointer tw:rounded-full tw:mouse tw:drop-shadow-md tw:transition tw:ease-in tw:duration-200 tw:focus:outline-hidden tw:flex tw:items-center tw:justify-center'
|
|
style={{ backgroundColor: layer.menuColor || '#777' }}
|
|
onMouseDown={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onMouseUp={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onClick={(event) => {
|
|
handleLayerSelect(event, layer)
|
|
}}
|
|
onTouchStart={(event) => {
|
|
stopPropagation(event)
|
|
}}
|
|
onTouchEnd={(event) => {
|
|
handleLayerSelect(event, layer)
|
|
}}
|
|
>
|
|
<img
|
|
src={appState.assetsApi.url + layer.markerIcon.image}
|
|
style={{
|
|
filter: 'invert(100%) brightness(200%)',
|
|
width: `${(layer.markerIcon.size ?? 18) * 1.3}px`,
|
|
}}
|
|
/>
|
|
</button>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
),
|
|
)}
|
|
</ul>
|
|
</div>
|
|
) : (
|
|
''
|
|
)}
|
|
</>
|
|
)
|
|
}
|