mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
update Items
This commit is contained in:
parent
3f540ff2d4
commit
de929beb45
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"description": "Reuseable React Components to build mapping apps for all kinds of communities ",
|
"description": "Reuseable React Components to build mapping apps for all kinds of communities ",
|
||||||
"repository": "https://github.com/utopia-os/utopia-ui",
|
"repository": "https://github.com/utopia-os/utopia-ui",
|
||||||
"homepage:": "https://utopia.os/",
|
"homepage:": "https://utopia.os/",
|
||||||
|
|||||||
@ -33,8 +33,10 @@ export const Layer = (props: LayerProps) => {
|
|||||||
item.layer = props;
|
item.layer = props;
|
||||||
addItem(item);
|
addItem(item);
|
||||||
})
|
})
|
||||||
|
addLayer(props);
|
||||||
|
|
||||||
}, [])
|
}, [])
|
||||||
addLayer(props);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -50,7 +52,7 @@ export const Layer = (props: LayerProps) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Marker icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={place.id} position={[place.position.coordinates[1], place.position.coordinates[0]]}>
|
<Marker icon={MarkerIconFactory(props.markerShape, color1, color2, props.markerIcon)} key={place.id} position={[place.position.coordinates[1], place.position.coordinates[0]]}>
|
||||||
<Popup item={place} tags={tags} />
|
<Popup item={place} tags={tags} setNewItemPopup={props.setNewItemPopup}/>
|
||||||
</Marker>
|
</Marker>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -6,8 +6,7 @@ import { useLayers } from '../hooks/useLayers'
|
|||||||
export default function AddButton({setSelectMode} : {setSelectMode: React.Dispatch<React.SetStateAction<any>>}) {
|
export default function AddButton({setSelectMode} : {setSelectMode: React.Dispatch<React.SetStateAction<any>>}) {
|
||||||
|
|
||||||
const layers = useLayers();
|
const layers = useLayers();
|
||||||
console.log(layers);
|
console.log("Layers: " +layers);
|
||||||
console.log(useLayers());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dropdown dropdown-top dropdown-end dropdown-hover z-500 absolute right-5 bottom-5" >
|
<div className="dropdown dropdown-top dropdown-end dropdown-hover z-500 absolute right-5 bottom-5" >
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { LatLng } from 'leaflet'
|
import { LatLng } from 'leaflet'
|
||||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||||
import { useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useAddItem } from '../hooks/useItems'
|
import { useAddItem, useUpdateItem } from '../hooks/useItems'
|
||||||
import { Geometry, Layer, Item} from '../../../types'
|
import { Geometry, Layer, Item} from '../../../types'
|
||||||
|
|
||||||
export interface NewItemPopupProps {
|
export interface NewItemPopupProps {
|
||||||
position: LatLng,
|
position: LatLng,
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
|
item?: Item,
|
||||||
setNewItemPopup: React.Dispatch<React.SetStateAction<any>>
|
setNewItemPopup: React.Dispatch<React.SetStateAction<any>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,22 +18,42 @@ export default function NewItemPopup(props: NewItemPopupProps) {
|
|||||||
|
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
const addItem = useAddItem();
|
const addItem = useAddItem();
|
||||||
|
const updateItem = useUpdateItem();
|
||||||
|
|
||||||
const handleSubmit = (evt: any) => {
|
const handleSubmit = (evt: any) => {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
console.log("New Item Popup is adding Item ...");
|
console.log("New Item Popup is adding Item ...");
|
||||||
|
if(props.item) {
|
||||||
addItem(new Item(Math.floor(Math.random() * 1000) + 200, name, text, new Geometry(props.position.lng, props.position.lat), props.layer))
|
updateItem(new Item(props.item.id, name, text, new Geometry(props.position.lng, props.position.lat), props.layer))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addItem(new Item(crypto.randomUUID(), name, text, new Geometry(props.position.lng, props.position.lat), props.layer))}
|
||||||
map.closePopup();
|
map.closePopup();
|
||||||
props.setNewItemPopup(null);
|
props.setNewItemPopup(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetPopup = () => {
|
||||||
|
setName('');
|
||||||
|
setText('');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("popup opend");
|
const setItemValues = () => {
|
||||||
|
if(props.item) {
|
||||||
|
setName(props.item?.name);
|
||||||
|
setText(props.item?.text);
|
||||||
|
console.log('set name + txt');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItemValues();
|
||||||
|
},[props.item])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LeafletPopup maxHeight={300} minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}
|
<LeafletPopup maxHeight={300} minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}
|
||||||
|
eventHandlers={{
|
||||||
|
remove: resetPopup
|
||||||
|
}}
|
||||||
position={props.position}>
|
position={props.position}>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className='flex justify-center'><b className="text-xl font-bold">New {props.layer.name}</b></div>
|
<div className='flex justify-center'><b className="text-xl font-bold">New {props.layer.name}</b></div>
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
|
import { LatLng } from 'leaflet'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
||||||
import { Item, Tag } from '../../../types'
|
import { Item, Tag } from '../../../types'
|
||||||
import { replaceURLs } from '../../../Utils/ReplaceURLs'
|
import { replaceURLs } from '../../../Utils/ReplaceURLs'
|
||||||
import { useRemoveItem } from '../hooks/useItems'
|
import { useRemoveItem } from '../hooks/useItems'
|
||||||
|
import { NewItemPopupProps } from './NewItemPopup'
|
||||||
|
|
||||||
export interface UtopiaPopupProps {
|
export interface UtopiaPopupProps {
|
||||||
item: Item,
|
item: Item,
|
||||||
tags: Tag[],
|
tags: Tag[],
|
||||||
|
setNewItemPopup?: React.Dispatch<React.SetStateAction<NewItemPopupProps | null>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -17,11 +20,18 @@ const Popup = (props: UtopiaPopupProps) => {
|
|||||||
const map = useMap();
|
const map = useMap();
|
||||||
|
|
||||||
const removeItemFromMap = (event: React.MouseEvent<HTMLElement>) => {
|
const removeItemFromMap = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
removeItem(item)
|
removeItem(item);
|
||||||
event.stopPropagation()
|
event.stopPropagation();
|
||||||
map.closePopup();
|
map.closePopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openEditPopup = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
map.closePopup();
|
||||||
|
if(props.setNewItemPopup)
|
||||||
|
props.setNewItemPopup({position: new LatLng(item.position.coordinates[1],item.position.coordinates[0]), layer: item.layer, item: item, setNewItemPopup: props.setNewItemPopup})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LeafletPopup maxHeight={300} minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}>
|
<LeafletPopup maxHeight={300} minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}>
|
||||||
<div className='flex flex-row'>
|
<div className='flex flex-row'>
|
||||||
@ -37,7 +47,7 @@ const Popup = (props: UtopiaPopupProps) => {
|
|||||||
</label>
|
</label>
|
||||||
<ul tabIndex={0} className="dropdown-content menu p-2 shadow bg-base-100 rounded-box">
|
<ul tabIndex={0} className="dropdown-content menu p-2 shadow bg-base-100 rounded-box">
|
||||||
<li>
|
<li>
|
||||||
<a className='bg-white hover:bg-white text-gray-500 hover:text-gray-700'>
|
<a className='bg-white hover:bg-white text-gray-500 hover:text-gray-700' onClick={openEditPopup}>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 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" />
|
<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>
|
</svg>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { ItemsProvider } from "./hooks/useItems";
|
|||||||
import { TagsProvider } from "./hooks/useTags";
|
import { TagsProvider } from "./hooks/useTags";
|
||||||
import { LayersProvider } from "./hooks/useLayers";
|
import { LayersProvider } from "./hooks/useLayers";
|
||||||
|
|
||||||
|
|
||||||
export interface MapEventListenerProps {
|
export interface MapEventListenerProps {
|
||||||
selectMode: Layer | null,
|
selectMode: Layer | null,
|
||||||
setSelectMode: React.Dispatch<React.SetStateAction<any>>,
|
setSelectMode: React.Dispatch<React.SetStateAction<any>>,
|
||||||
@ -44,6 +45,8 @@ function UtopiaMap({
|
|||||||
const [selectMode, setSelectMode] = useState<Layer | null>(null);
|
const [selectMode, setSelectMode] = useState<Layer | null>(null);
|
||||||
const [newItemPopup, setNewItemPopup] = useState<NewItemPopupProps | null>(null);
|
const [newItemPopup, setNewItemPopup] = useState<NewItemPopupProps | null>(null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LayersProvider initialLayers={[]}>
|
<LayersProvider initialLayers={[]}>
|
||||||
<TagsProvider initialTags={[]}>
|
<TagsProvider initialTags={[]}>
|
||||||
@ -54,11 +57,15 @@ function UtopiaMap({
|
|||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
||||||
<MarkerClusterGroup showCoverageOnHover chunkedLoading maxClusterRadius={50}>
|
<MarkerClusterGroup showCoverageOnHover chunkedLoading maxClusterRadius={50}>
|
||||||
{children}
|
{
|
||||||
|
React.Children.toArray(children).map((child) =>
|
||||||
|
React.isValidElement<{ setNewItemPopup: React.Dispatch<React.SetStateAction<NewItemPopupProps | null>> }>(child) ? React.cloneElement(child, { setNewItemPopup: setNewItemPopup }) : child
|
||||||
|
)
|
||||||
|
}
|
||||||
</MarkerClusterGroup>
|
</MarkerClusterGroup>
|
||||||
<MapEventListener setSelectMode={setSelectMode} selectMode={selectMode} setNewItemPopup={setNewItemPopup} />
|
<MapEventListener setSelectMode={setSelectMode} selectMode={selectMode} setNewItemPopup={setNewItemPopup} />
|
||||||
{newItemPopup &&
|
{newItemPopup &&
|
||||||
<NewItemPopup position={newItemPopup.position} layer={newItemPopup.layer} setNewItemPopup={setNewItemPopup} />
|
<NewItemPopup position={newItemPopup.position} layer={newItemPopup.layer} setNewItemPopup={setNewItemPopup} item={newItemPopup.item}/>
|
||||||
}
|
}
|
||||||
<AddButton setSelectMode={setSelectMode}></AddButton>
|
<AddButton setSelectMode={setSelectMode}></AddButton>
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
@ -71,12 +78,10 @@ function UtopiaMap({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ItemsProvider>
|
</ItemsProvider>
|
||||||
</TagsProvider>
|
</TagsProvider>
|
||||||
</LayersProvider>
|
</LayersProvider>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Item } from "../../../types";
|
|||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| { type: "ADD"; item: Item }
|
| { type: "ADD"; item: Item }
|
||||||
|
| { type: "UPDATE"; item: Item }
|
||||||
| { type: "REMOVE"; item: Item };
|
| { type: "REMOVE"; item: Item };
|
||||||
|
|
||||||
type UseItemManagerResult = ReturnType<typeof useItemsManager>;
|
type UseItemManagerResult = ReturnType<typeof useItemsManager>;
|
||||||
@ -11,12 +12,14 @@ type UseItemManagerResult = ReturnType<typeof useItemsManager>;
|
|||||||
const ItemContext = createContext<UseItemManagerResult>({
|
const ItemContext = createContext<UseItemManagerResult>({
|
||||||
items: [],
|
items: [],
|
||||||
addItem: () => {},
|
addItem: () => {},
|
||||||
|
updateItem: () => {},
|
||||||
removeItem: () => {}
|
removeItem: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
function useItemsManager (initialItems: Item[]): {
|
function useItemsManager (initialItems: Item[]): {
|
||||||
items: Item[];
|
items: Item[];
|
||||||
addItem: (item: Item) => void;
|
addItem: (item: Item) => void;
|
||||||
|
updateItem: (item: Item) => void;
|
||||||
removeItem: (item: Item) => void;
|
removeItem: (item: Item) => void;
|
||||||
} {
|
} {
|
||||||
const [items, dispatch] = useReducer((state: Item[], action: ActionType) => {
|
const [items, dispatch] = useReducer((state: Item[], action: ActionType) => {
|
||||||
@ -26,6 +29,13 @@ function useItemsManager (initialItems: Item[]): {
|
|||||||
...state,
|
...state,
|
||||||
action.item,
|
action.item,
|
||||||
];
|
];
|
||||||
|
case "UPDATE":
|
||||||
|
return state.map((item) => {
|
||||||
|
if (item.id === action.item.id) {
|
||||||
|
return action.item
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
});
|
||||||
case "REMOVE":
|
case "REMOVE":
|
||||||
return state.filter(item => item !== action.item);
|
return state.filter(item => item !== action.item);
|
||||||
default:
|
default:
|
||||||
@ -40,13 +50,20 @@ function useItemsManager (initialItems: Item[]): {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const updateItem = useCallback((item: Item) => {
|
||||||
|
dispatch({
|
||||||
|
type: "UPDATE",
|
||||||
|
item,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const removeItem = useCallback((item: Item) => {
|
const removeItem = useCallback((item: Item) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "REMOVE",
|
type: "REMOVE",
|
||||||
item,
|
item,
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
return { items, addItem, removeItem };
|
return { items, updateItem, addItem, removeItem };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ItemsProvider: React.FunctionComponent<{
|
export const ItemsProvider: React.FunctionComponent<{
|
||||||
@ -67,6 +84,11 @@ export const useAddItem = (): UseItemManagerResult["addItem"] => {
|
|||||||
return addItem;
|
return addItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateItem = (): UseItemManagerResult["updateItem"] => {
|
||||||
|
const { updateItem } = useContext(ItemContext);
|
||||||
|
return updateItem;
|
||||||
|
};
|
||||||
|
|
||||||
export const useRemoveItem = (): UseItemManagerResult["removeItem"] => {
|
export const useRemoveItem = (): UseItemManagerResult["removeItem"] => {
|
||||||
const { removeItem } = useContext(ItemContext);
|
const { removeItem } = useContext(ItemContext);
|
||||||
return removeItem;
|
return removeItem;
|
||||||
|
|||||||
@ -20,17 +20,10 @@ function useLayerManager(initialLayers: Layer[]): {
|
|||||||
const [layers, dispatch] = useReducer((state: Layer[], action: ActionType) => {
|
const [layers, dispatch] = useReducer((state: Layer[], action: ActionType) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "ADD LAYER":
|
case "ADD LAYER":
|
||||||
{
|
return [
|
||||||
if (!state.includes(action.layer))
|
...state,
|
||||||
state.push(action.layer);
|
action.layer,
|
||||||
return state;
|
];
|
||||||
}
|
|
||||||
case "ADD ITEM":
|
|
||||||
{
|
|
||||||
if(!state.find(layer => layer.name === action.layer.name)?.data.find(item => item.id === action.item.id))
|
|
||||||
state.find(layer => layer.name === action.layer.name)?.data.push(action.item)
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/types.ts
10
src/types.ts
@ -1,4 +1,5 @@
|
|||||||
import { LatLng } from "leaflet";
|
import { LatLng } from "leaflet";
|
||||||
|
import { NewItemPopupProps } from "./Components/Map/Subcomponents/NewItemPopup";
|
||||||
|
|
||||||
export interface UtopiaMap {
|
export interface UtopiaMap {
|
||||||
height?: string,
|
height?: string,
|
||||||
@ -21,20 +22,21 @@ export interface Layer {
|
|||||||
markerShape: string,
|
markerShape: string,
|
||||||
markerDefaultColor: string,
|
markerDefaultColor: string,
|
||||||
tags?: Tag[],
|
tags?: Tag[],
|
||||||
|
setNewItemPopup?: React.Dispatch<React.SetStateAction<NewItemPopupProps | null>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Item {
|
export class Item {
|
||||||
id: number;
|
id: string | number;
|
||||||
date_created?: string;
|
date_created?: string;
|
||||||
date_updated?: string | null;
|
date_updated?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
text: string;
|
text: string;
|
||||||
position: Geometry;
|
position: Geometry;
|
||||||
layer?: Layer;
|
layer: Layer;
|
||||||
start?: string;
|
start?: string;
|
||||||
end?: string;
|
end?: string;
|
||||||
tags?: number[];
|
tags?: number[];
|
||||||
constructor(id:number,name:string,text:string,position:Geometry, layer: Layer){
|
constructor(id:string|number,name:string,text:string,position:Geometry, layer: Layer){
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
@ -54,7 +56,7 @@ export class Geometry {
|
|||||||
|
|
||||||
export interface Tag {
|
export interface Tag {
|
||||||
color: string;
|
color: string;
|
||||||
id: number;
|
id: string | number;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user