mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
basic API implementation
This commit is contained in:
parent
eba7f0630b
commit
8bbb6bd1e2
@ -18,5 +18,5 @@ export default {
|
||||
}),
|
||||
typescript()
|
||||
],
|
||||
external: ['react', 'react-dom', 'leaflet', 'react-leaflet', 'react-toastify' , 'react-toastify/dist/ReactToastify.css', 'tw-elements' ,'react-router-dom', 'react-leaflet-cluster', 'leaflet/dist/leaflet.css', '@heroicons/react/20/solid']
|
||||
external: ['react', 'react-dom', 'leaflet', 'react-leaflet', 'react-toastify' , 'react-toastify/dist/ReactToastify.css', 'tw-elements' ,'react-router-dom', 'react-leaflet-cluster', '@tanstack/react-query', 'leaflet/dist/leaflet.css', '@heroicons/react/20/solid']
|
||||
}
|
||||
@ -5,11 +5,15 @@ import MarkerIconFactory from '../../Utils/MarkerIconFactory'
|
||||
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
||||
import { useTags } from './hooks/useTags'
|
||||
import { useAddItem, useItems, useResetItems } from './hooks/useItems'
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useAddLayer } from './hooks/useLayers'
|
||||
import ItemFormPopup, { ItemFormPopupProps } from './Subcomponents/ItemFormPopup'
|
||||
|
||||
export const Layer = (props: LayerProps) => {
|
||||
|
||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||
|
||||
|
||||
const tags = useTags();
|
||||
|
||||
// create a JS-Map with all Tags
|
||||
@ -19,11 +23,14 @@ export const Layer = (props: LayerProps) => {
|
||||
const getTags = (item: Item) => {
|
||||
const tags: Tag[] = [];
|
||||
item.tags && item.tags.forEach(element => {
|
||||
if (tagMap.has(element)) { tags.push(tagMap.get(element)!)}
|
||||
if (tagMap.has(element)) { tags.push(tagMap.get(element)!) }
|
||||
});
|
||||
return tags;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const items = useItems();
|
||||
const addItem = useAddItem()
|
||||
const addLayer = useAddLayer();
|
||||
@ -31,36 +38,54 @@ export const Layer = (props: LayerProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
resetItems(props);
|
||||
|
||||
props.data?.map(item => {
|
||||
if(item.position) {
|
||||
if (item.position) {
|
||||
item.layer = props;
|
||||
addItem(item);
|
||||
}
|
||||
}
|
||||
})
|
||||
addLayer(props);
|
||||
}, [props.data])
|
||||
|
||||
props.api?.getItems().then(result => {
|
||||
if (result.data) {
|
||||
result.data.map(item => {
|
||||
if (item.position) {
|
||||
addItem(({layer: props, api: props.api, ...item}));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
if(props.api || props.api) {
|
||||
addLayer(props);
|
||||
}
|
||||
|
||||
}, [props.data, props.api])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.filter(item => item.layer?.name === props.name)?.map((place: Item) => {
|
||||
const tags = getTags(place);
|
||||
let color1 = "#666";
|
||||
let color2 = "RGBA(35, 31, 32, 0.2)";
|
||||
if (tags[0]) {
|
||||
color1 = tags[0].color;
|
||||
}
|
||||
if (tags[1]) {
|
||||
color2 = tags[1].color;
|
||||
}
|
||||
return (
|
||||
<Marker icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={place.id} position={[place.position.coordinates[1], place.position.coordinates[0]]}>
|
||||
<ItemViewPopup item={place} tags={tags} setItemFormPopup={props.setItemFormPopup}/>
|
||||
</Marker>
|
||||
);
|
||||
})
|
||||
{items &&
|
||||
items.filter(item => item.layer?.name === props.name)?.map((place: Item) => {
|
||||
const tags = getTags(place);
|
||||
let color1 = "#666";
|
||||
let color2 = "RGBA(35, 31, 32, 0.2)";
|
||||
if (tags[0]) {
|
||||
color1 = tags[0].color;
|
||||
}
|
||||
if (tags[1]) {
|
||||
color2 = tags[1].color;
|
||||
}
|
||||
return (
|
||||
<Marker icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={place.id} position={[place.position.coordinates[1], place.position.coordinates[0]]}>
|
||||
<ItemViewPopup item={place} tags={tags} setItemFormPopup={props.setItemFormPopup} />
|
||||
</Marker>
|
||||
);
|
||||
})
|
||||
}
|
||||
{props.children}
|
||||
{props.itemFormPopup && props.itemFormPopup.layer.name == props.name &&
|
||||
<ItemFormPopup position={props.itemFormPopup.position} layer={props.itemFormPopup.layer} setItemFormPopup={setItemFormPopup} item={props.itemFormPopup.item} api={props.api}/>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,6 +6,9 @@ import { useLayers } from '../hooks/useLayers'
|
||||
export default function AddButton({setSelectMode} : {setSelectMode: React.Dispatch<React.SetStateAction<any>>}) {
|
||||
|
||||
const layers = useLayers();
|
||||
console.log(layers);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-5 tw-bottom-5" >
|
||||
@ -18,7 +21,8 @@ export default function AddButton({setSelectMode} : {setSelectMode: React.Dispat
|
||||
</button>
|
||||
<ul tabIndex={0} className="tw-dropdown-content tw-pr-2 tw-mb-0 tw-list-none">
|
||||
{layers.map((layer) => (
|
||||
<li key={layer.name} >
|
||||
layer.api?.createItem && (
|
||||
<li key={layer.name} >
|
||||
<a>
|
||||
<div className="tw-tooltip tw-tooltip-left" data-tip={layer.menuText}>
|
||||
<button tabIndex={0}
|
||||
@ -30,6 +34,8 @@ export default function AddButton({setSelectMode} : {setSelectMode: React.Dispat
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
|
||||
))}
|
||||
</ul>
|
||||
|
||||
|
||||
@ -3,18 +3,20 @@ import { LatLng } from 'leaflet'
|
||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useAddItem, useUpdateItem } from '../hooks/useItems'
|
||||
import { Geometry, LayerProps, Item} from '../../../types'
|
||||
import { Geometry, LayerProps, Item, ItemsApi} from '../../../types'
|
||||
|
||||
export interface ItemFormPopupProps {
|
||||
position: LatLng,
|
||||
layer: LayerProps,
|
||||
item?: Item,
|
||||
api?: ItemsApi<any>,
|
||||
setItemFormPopup: React.Dispatch<React.SetStateAction<any>>
|
||||
}
|
||||
|
||||
export default function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
const [name, setName] = useState('')
|
||||
const [text, setText] = useState('')
|
||||
const [spinner, setSpinner] = useState(false);
|
||||
|
||||
const map = useMap();
|
||||
const addItem = useAddItem();
|
||||
@ -24,11 +26,21 @@ export default function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
evt.preventDefault()
|
||||
console.log("New Item Popup is adding Item ...");
|
||||
if(props.item) {
|
||||
updateItem(new Item(props.item.id, name, text, new Geometry(props.position.lng, props.position.lat), props.layer))
|
||||
setSpinner(true);
|
||||
props.api?.updateItem!(new Item(props.item.id, name, text, new Geometry(props.position.lng, props.position.lat)))
|
||||
.then( () => updateItem(new Item(props.item!.id, name, text, new Geometry(props.position.lng, props.position.lat), props.layer, props.item!.api)))
|
||||
.then(()=> setSpinner(false))
|
||||
.then(()=> map.closePopup())
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
else {
|
||||
addItem(new Item(crypto.randomUUID(), name, text, new Geometry(props.position.lng, props.position.lat), props.layer))}
|
||||
map.closePopup();
|
||||
setSpinner(true);
|
||||
props.api?.createItem!(new Item(crypto.randomUUID(), name, text, new Geometry(props.position.lng, props.position.lat)))
|
||||
.then( () => addItem(new Item(crypto.randomUUID(), name, text, new Geometry(props.position.lng, props.position.lat), props.layer, props.api)))
|
||||
.then(()=> setSpinner(false))
|
||||
.then(()=> map.closePopup())
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
props.setItemFormPopup(null);
|
||||
}
|
||||
|
||||
@ -41,7 +53,6 @@ export default function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
if(props.item) {
|
||||
setName(props.item?.name);
|
||||
setText(props.item?.text);
|
||||
console.log('set name + txt');
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +70,7 @@ export default function ItemFormPopup(props: ItemFormPopupProps) {
|
||||
<div className='tw-flex tw-justify-center'><b className="tw-text-xl tw-font-bold">New {props.layer.name}</b></div>
|
||||
<input type="text" placeholder="Name" className="tw-input tw-input-bordered tw-w-full tw-max-w-xs tw-mt-5" value={name} onChange={e => setName(e.target.value)} />
|
||||
<textarea className="tw-textarea tw-textarea-bordered tw-w-full tw-mt-5 tw-leading-5 tw-h-40" placeholder="Text" value={text} onChange={e => setText(e.target.value)}></textarea>
|
||||
<div className='tw-flex tw-justify-center'><button className="tw-btn tw-mt-5 tw-place-self-center">Save</button></div>
|
||||
<div className='tw-flex tw-justify-center'><button className={spinner ? 'tw-btn tw-loading tw-mt-5 tw-place-self-center' : 'tw-btn tw-mt-5 tw-place-self-center'}>Save</button></div>
|
||||
</form>
|
||||
</LeafletPopup>
|
||||
)
|
||||
|
||||
@ -20,9 +20,12 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
|
||||
const map = useMap();
|
||||
|
||||
const removeItemFromMap = (event: React.MouseEvent<HTMLElement>) => {
|
||||
removeItem(item);
|
||||
props.item.api?.deleteItem!(props.item.id)
|
||||
.then( () => removeItem(item))
|
||||
.then(()=> map.closePopup())
|
||||
.catch(err => console.log(err));
|
||||
|
||||
event.stopPropagation();
|
||||
map.closePopup();
|
||||
}
|
||||
|
||||
const openEditPopup = (event: React.MouseEvent<HTMLElement>) => {
|
||||
@ -40,6 +43,7 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
|
||||
<b className="tw-text-xl tw-font-bold">{item.name}</b>
|
||||
</div>
|
||||
<div className='tw-col-span-1'>
|
||||
{item.api &&
|
||||
<div className="tw-dropdown tw-dropdown-bottom">
|
||||
<label tabIndex={0} className="tw-btn tw-m-1 tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700 tw-leading-3 tw-border-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
@ -47,22 +51,23 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
|
||||
</svg>
|
||||
</label>
|
||||
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box">
|
||||
<li>
|
||||
{item.api.updateItem && <li>
|
||||
<a className='tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700' onClick={openEditPopup}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
</li>}
|
||||
|
||||
{item.api.deleteItem && <li>
|
||||
<a className='tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700' onClick={removeItemFromMap}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
</li>}
|
||||
</ul>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
<div className='tw-overflow-y-auto tw-max-h-72'>
|
||||
|
||||
@ -7,7 +7,7 @@ import { LatLng } from "leaflet";
|
||||
import MarkerClusterGroup from 'react-leaflet-cluster'
|
||||
import AddButton from "./Subcomponents/AddButton";
|
||||
import { useState } from "react";
|
||||
import ItemFormPopup, { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
||||
import { ItemFormPopupProps } from "./Subcomponents/ItemFormPopup";
|
||||
import { ItemsProvider } from "./hooks/useItems";
|
||||
import { TagsProvider } from "./hooks/useTags";
|
||||
import { LayersProvider } from "./hooks/useLayers";
|
||||
@ -43,7 +43,7 @@ function UtopiaMap({
|
||||
: UtopiaMapProps) {
|
||||
|
||||
const [selectMode, setSelectMode] = useState<LayerProps | null>(null);
|
||||
const [newItemPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||
const [itemFormPopup, setItemFormPopup] = useState<ItemFormPopupProps | null>(null);
|
||||
|
||||
|
||||
|
||||
@ -59,14 +59,11 @@ function UtopiaMap({
|
||||
<MarkerClusterGroup showCoverageOnHover chunkedLoading maxClusterRadius={50}>
|
||||
{
|
||||
React.Children.toArray(children).map((child) =>
|
||||
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>> }>(child) ? React.cloneElement(child, { setItemFormPopup: setItemFormPopup }) : child
|
||||
React.isValidElement<{ setItemFormPopup: React.Dispatch<React.SetStateAction<ItemFormPopupProps>>, itemFormPopup: ItemFormPopupProps | null }>(child) ? React.cloneElement(child, { setItemFormPopup: setItemFormPopup, itemFormPopup: itemFormPopup }) : child
|
||||
)
|
||||
}
|
||||
</MarkerClusterGroup>
|
||||
<MapEventListener setSelectMode={setSelectMode} selectMode={selectMode} setItemFormPopup={setItemFormPopup} />
|
||||
{newItemPopup &&
|
||||
<ItemFormPopup position={newItemPopup.position} layer={newItemPopup.layer} setItemFormPopup={setItemFormPopup} item={newItemPopup.item} />
|
||||
}
|
||||
<AddButton setSelectMode={setSelectMode}></AddButton>
|
||||
</MapContainer>
|
||||
{selectMode != null &&
|
||||
|
||||
19
src/types.ts
19
src/types.ts
@ -21,8 +21,9 @@ export interface LayerProps {
|
||||
markerShape: string,
|
||||
markerDefaultColor: string,
|
||||
tags?: Tag[],
|
||||
api?: ItemsApi,
|
||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||
api?: ItemsApi<any>,
|
||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
|
||||
itemFormPopup?: ItemFormPopupProps | null
|
||||
}
|
||||
|
||||
export class Item {
|
||||
@ -36,12 +37,14 @@ export class Item {
|
||||
start?: string;
|
||||
end?: string;
|
||||
tags?: number[];
|
||||
constructor(id:string|number,name:string,text:string,position:Geometry, layer: LayerProps){
|
||||
api?: ItemsApi<any>
|
||||
constructor(id:string|number,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi<any>){
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.text = text;
|
||||
this.position = position;
|
||||
this.layer = layer;
|
||||
this.api = api;
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,9 +63,9 @@ export interface Tag {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ItemsApi {
|
||||
getItems(): Promise<void>,
|
||||
addItem(item : Item): Promise<void>,
|
||||
updateItem(item : Item): Promise<void>,
|
||||
deleteItem(id : number): Promise<void>,
|
||||
export interface ItemsApi<T> {
|
||||
getItems(): Promise<any>,
|
||||
createItem?(item : T): Promise<any>,
|
||||
updateItem?(item : T): Promise<any>,
|
||||
deleteItem?(id : number | string): Promise<any>,
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user