clustering and popups styled

This commit is contained in:
AT 2022-07-11 14:33:39 +02:00
parent d6052537c8
commit 962d45834e
6 changed files with 125 additions and 92 deletions

View File

@ -45,13 +45,13 @@ You can find some Sample Data (places, events, tags) for test purpose below
Option | Type | Default | Required | Description
--- | --- | --- | --- | ---
`height` | `string` | - | | height of the map
`width` | `string` | - | | width of the map
`center` | `LatLngExpression`| - | | initial map position
`zoom` | `number` | - | | initial zoom level
`places` | [`Item[]`](https://utopia-os.org/docs/utopia-ui/map-components/item)| - | | Array with Items
`events` | [`Item[]`](https://utopia-os.org/docs/utopia-ui/map-components/item) | - | | Array with Items
`height` | `string` |`'400px'` | No | height of the map
`width` | `string` |`'100vw'` | No | width of the map
`center` | `LatLng` |`[50.6, 9.5]` | No | initial map position
`zoom` | `number` |`10` | No | initial zoom level
`places` | [`Item[]`](https://utopia-os.org/docs/utopia-ui/map-components/item)| | No | Array with Items
`events` | [`Item[]`](https://utopia-os.org/docs/utopia-ui/map-components/item)| | No | Array with Items
`tags` | [`Tag[]`](https://utopia-os.org/docs/utopia-ui/map-components/tag) | | No | Array with Tags
### Sample Data
```jsx

78
package-lock.json generated
View File

@ -10,8 +10,8 @@
"license": "ISC",
"dependencies": {
"leaflet": "^1.8.0",
"react-leaflet": "^4.0.0",
"react-leaflet-cluster": "^2.0.0"
"react-leaflet": "^3.2.5",
"react-leaflet-cluster": "^1.0.4"
},
"devDependencies": {
"@types/leaflet": "^1.7.11",
@ -32,6 +32,16 @@
"react-dom": "^17.0.2"
}
},
"node_modules/@react-leaflet/core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-1.1.1.tgz",
"integrity": "sha512-7PGLWa9MZ5x/cWy8EH2VzI4T8q5WpuHbixzCDXqixP/WyqwIrg5NDUPgYuFnB4IEIZF+6nA265mYzswFo/h1Pw==",
"peerDependencies": {
"leaflet": "^1.7.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
},
"node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
@ -961,40 +971,30 @@
}
},
"node_modules/react-leaflet": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.0.tgz",
"integrity": "sha512-qJJvoCNe12XHSWVUwhXYmMObPoSYy8h/hn0aDNvcBuq3O8zmVI5S2RdabhaDg/iWMCJ2jbCWZWtIU5VtztO9sg==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-3.2.5.tgz",
"integrity": "sha512-Z3KZ+4SijsRbbrt2I1a3ZDY6+V6Pm91eYTdxTN18G6IOkFRsJo1BuSPLFnyFrlF3WDjQFPEcTPkEgD1VEeAoBg==",
"dependencies": {
"@react-leaflet/core": "^2.0.0"
"@react-leaflet/core": "^1.1.1"
},
"peerDependencies": {
"leaflet": "^1.8.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"leaflet": "^1.7.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
},
"node_modules/react-leaflet-cluster": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-2.0.0.tgz",
"integrity": "sha512-tREjHM3mlNwj7sJdV+i0QSYbrOeju3RtPThfe7ik0T2oH56OffgMCC+mAjLOR+OrQXLIPktJpFYHpNNZOEtyUA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-1.0.4.tgz",
"integrity": "sha512-7sUtH35vf0JQIgiRHl4DWWy9JumEAhqDHfrjOlxIfCoHdeFFtnmHvdCetz/HJswHLLatwNZicCLx5DOFZzhL6g==",
"dependencies": {
"leaflet.markercluster": "^1.5.3"
},
"peerDependencies": {
"leaflet": "^1.8.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-leaflet": "^4.0.0"
}
},
"node_modules/react-leaflet/node_modules/@react-leaflet/core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.0.0.tgz",
"integrity": "sha512-SQQ5DCQIaLzvslN6wCXs5OWqtlvk1Ubv2n5d7zTM8SDl9hM5Rr2wVy7/nOCIY958D75/ovhq6ZoSvT7GLCX6sg==",
"peerDependencies": {
"leaflet": "^1.8.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-leaflet": "^3.2.0"
}
},
"node_modules/readdirp": {
@ -1279,6 +1279,12 @@
}
},
"dependencies": {
"@react-leaflet/core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-1.1.1.tgz",
"integrity": "sha512-7PGLWa9MZ5x/cWy8EH2VzI4T8q5WpuHbixzCDXqixP/WyqwIrg5NDUPgYuFnB4IEIZF+6nA265mYzswFo/h1Pw==",
"requires": {}
},
"@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
@ -2027,25 +2033,17 @@
}
},
"react-leaflet": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.0.tgz",
"integrity": "sha512-qJJvoCNe12XHSWVUwhXYmMObPoSYy8h/hn0aDNvcBuq3O8zmVI5S2RdabhaDg/iWMCJ2jbCWZWtIU5VtztO9sg==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-3.2.5.tgz",
"integrity": "sha512-Z3KZ+4SijsRbbrt2I1a3ZDY6+V6Pm91eYTdxTN18G6IOkFRsJo1BuSPLFnyFrlF3WDjQFPEcTPkEgD1VEeAoBg==",
"requires": {
"@react-leaflet/core": "^2.0.0"
},
"dependencies": {
"@react-leaflet/core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.0.0.tgz",
"integrity": "sha512-SQQ5DCQIaLzvslN6wCXs5OWqtlvk1Ubv2n5d7zTM8SDl9hM5Rr2wVy7/nOCIY958D75/ovhq6ZoSvT7GLCX6sg==",
"requires": {}
}
"@react-leaflet/core": "^1.1.1"
}
},
"react-leaflet-cluster": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-2.0.0.tgz",
"integrity": "sha512-tREjHM3mlNwj7sJdV+i0QSYbrOeju3RtPThfe7ik0T2oH56OffgMCC+mAjLOR+OrQXLIPktJpFYHpNNZOEtyUA==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-1.0.4.tgz",
"integrity": "sha512-7sUtH35vf0JQIgiRHl4DWWy9JumEAhqDHfrjOlxIfCoHdeFFtnmHvdCetz/HJswHLLatwNZicCLx5DOFZzhL6g==",
"requires": {
"leaflet.markercluster": "^1.5.3"
}

View File

@ -1,6 +1,6 @@
{
"name": "utopia-ui",
"version": "1.0.6",
"version": "1.0.7",
"description": "Reuseable React Components to build mapping apps for all kinds of communities ",
"repository": "https://github.com/utopia-os/utopia-ui",
"homepage:": "https://utopia.os/",
@ -35,7 +35,7 @@
},
"dependencies": {
"leaflet": "^1.8.0",
"react-leaflet": "^4.0.0",
"react-leaflet-cluster": "^2.0.0"
"react-leaflet": "^3.2.5",
"react-leaflet-cluster": "^1.0.4"
}
}

View File

@ -7,25 +7,50 @@ export interface MarkerPopupProps {
tags: Tag[]
}
const MarkerPopup = (props:MarkerPopupProps) => {
const item:Item = props.item;
const tags:Tag[] = props.tags;
const MarkerPopup = (props: MarkerPopupProps) => {
const item: Item = props.item;
const tags: Tag[] = props.tags;
return (
<Popup>
<Popup maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[30,30]}>
<b style={{ fontSize: '1.0rem' }}>{item.name}</b>
<p>{item.start || ""} {item.end || ""}</p>
{item.start && item.end &&
<p>{new Date(item.start).toISOString().substring(0, 10) || ""} - {new Date(item.end).toISOString().substring(0, 10) || ""}</p>
}
<p>{item.text}</p>
{item.tags&&
tags.map((tag:Tag) => (
<span className="badge" style={{backgroundColor: tag.color,margin: '.1rem', fontSize: "100%"}} key={tag.id}>#{tag.name}</span>
))}
<p style={{ whiteSpace: "pre-wrap" }} dangerouslySetInnerHTML={{ __html: replaceURLs(item.text) }} />
<p>
{item.tags &&
tags.map((tag: Tag) => (
<span className="" style={{ fontWeight: "bold", display: "inline-block", color: "#fff", padding: ".2rem", borderRadius: ".2rem", backgroundColor: tag.color, margin: '.2rem', fontSize: "100%" }} key={tag.id}>#{tag.name}</span>
))
}
</p>
</Popup>
)
}
export default MarkerPopup;
function replaceURLs(message: string): string {
if (!message) return "";
var urlRegex = /(^| )(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,10}(:[0-9]{1,10})?(\/.*)?$/gm;
message = message.replace(urlRegex, function (url) {
var hyperlink = url.replace(' ', '');
if (!hyperlink.match('^https?:\/\/')) {
hyperlink = 'http://' + hyperlink;
}
return '<a href="' + hyperlink + '" target="_blank" rel="noopener noreferrer">' + url + '</a>'
});
var mailRegex = /([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi;
message = message.replace(mailRegex, function (mail) {
return '<a href="mailto:' + mail + '">' + mail + '</a>'
});
return message;
}

View File

@ -5,11 +5,13 @@ import MarkerIconFactory from './Utils/MarkerIconFactory';
import MarkerPopup from "./Components/Map/MarkerPopup";
import { Item, Tag } from "./types"
import "./styles.scss"
import { LatLng } from "leaflet";
import MarkerClusterGroup from 'react-leaflet-cluster'
export interface MapProps {
height: string,
width: string,
center: number[],
center: LatLng,
zoom: number,
places?: Item[],
events?: Item[],
@ -18,6 +20,15 @@ export interface MapProps {
const UtopiaMap = (props: MapProps) => {
let center: LatLng = new LatLng(50.6, 9.5);
if (props.center) center = props.center;
let zoom: number = 10;
if (props.zoom) zoom = props.zoom;
let height: string = "400px";
if (props.height) height = props.height;
let width: string = "100vw";
if (props.width) width = props.width;
let tagMap = new Map(props.tags?.map(key => [key.id, key]));
const getTags = (item: Item) => {
@ -30,10 +41,11 @@ const UtopiaMap = (props: MapProps) => {
return (
<MapContainer style={{ height: props.height, width: props.width }} center={props.center} zoom={props.zoom} >
<MapContainer style={{ height: height, width: width }} center={center} zoom={zoom} >
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<MarkerClusterGroup showCoverageOnHover chunkedLoading maxClusterRadius={50}>
{props.places &&
props.places.map((place: Item) => {
let tags = getTags(place);
@ -71,6 +83,7 @@ const UtopiaMap = (props: MapProps) => {
)
})
}
</MarkerClusterGroup>
</MapContainer>
);

View File

@ -1,28 +1,25 @@
import { Component } from 'react' // Switch to 'react' if you use it
import { MarkerClusterGroupOptions } from 'leaflet'
export interface Item {
id: number,
date_created?: string,
date_updated?: string | null,
name: string,
text: string,
position: Geometry,
start?: string,
end?: string,
tags?: number[],
[key: string]:any
}
export interface Geometry {
type: string;
coordinates: number[];
}
export interface Tag {
color: string;
id: number;
name: string;
}
export interface Item {
id: number,
date_created?: string,
date_updated?: string | null,
name: string,
text: string,
position: Geometry,
start?: string,
end?: string,
tags?: number[],
[key: string]: any
}
export interface Geometry {
type: string;
coordinates: number[];
}
export interface Tag {
color: string;
id: number;
name: string;
}