mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
clustering and popups styled
This commit is contained in:
parent
d6052537c8
commit
962d45834e
14
README.md
14
README.md
@ -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
78
package-lock.json
generated
@ -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"
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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='© <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>
|
||||
|
||||
);
|
||||
|
||||
53
src/types.ts
53
src/types.ts
@ -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;
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user