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
|
Option | Type | Default | Required | Description
|
||||||
--- | --- | --- | --- | ---
|
--- | --- | --- | --- | ---
|
||||||
`height` | `string` | - | | height of the map
|
`height` | `string` |`'400px'` | No | height of the map
|
||||||
`width` | `string` | - | | width of the map
|
`width` | `string` |`'100vw'` | No | width of the map
|
||||||
`center` | `LatLngExpression`| - | | initial map position
|
`center` | `LatLng` |`[50.6, 9.5]` | No | initial map position
|
||||||
`zoom` | `number` | - | | initial zoom level
|
`zoom` | `number` |`10` | No | initial zoom level
|
||||||
`places` | [`Item[]`](https://utopia-os.org/docs/utopia-ui/map-components/item)| - | | Array with Items
|
`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) | - | | 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
|
### Sample Data
|
||||||
```jsx
|
```jsx
|
||||||
|
|||||||
78
package-lock.json
generated
78
package-lock.json
generated
@ -10,8 +10,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"react-leaflet": "^4.0.0",
|
"react-leaflet": "^3.2.5",
|
||||||
"react-leaflet-cluster": "^2.0.0"
|
"react-leaflet-cluster": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/leaflet": "^1.7.11",
|
"@types/leaflet": "^1.7.11",
|
||||||
@ -32,6 +32,16 @@
|
|||||||
"react-dom": "^17.0.2"
|
"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": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||||
@ -961,40 +971,30 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-leaflet": {
|
"node_modules/react-leaflet": {
|
||||||
"version": "4.0.0",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-3.2.5.tgz",
|
||||||
"integrity": "sha512-qJJvoCNe12XHSWVUwhXYmMObPoSYy8h/hn0aDNvcBuq3O8zmVI5S2RdabhaDg/iWMCJ2jbCWZWtIU5VtztO9sg==",
|
"integrity": "sha512-Z3KZ+4SijsRbbrt2I1a3ZDY6+V6Pm91eYTdxTN18G6IOkFRsJo1BuSPLFnyFrlF3WDjQFPEcTPkEgD1VEeAoBg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-leaflet/core": "^2.0.0"
|
"@react-leaflet/core": "^1.1.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.7.1",
|
||||||
"react": "^18.0.0",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^17.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-leaflet-cluster": {
|
"node_modules/react-leaflet-cluster": {
|
||||||
"version": "2.0.0",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-1.0.4.tgz",
|
||||||
"integrity": "sha512-tREjHM3mlNwj7sJdV+i0QSYbrOeju3RtPThfe7ik0T2oH56OffgMCC+mAjLOR+OrQXLIPktJpFYHpNNZOEtyUA==",
|
"integrity": "sha512-7sUtH35vf0JQIgiRHl4DWWy9JumEAhqDHfrjOlxIfCoHdeFFtnmHvdCetz/HJswHLLatwNZicCLx5DOFZzhL6g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet.markercluster": "^1.5.3"
|
"leaflet.markercluster": "^1.5.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"react": "^18.0.0",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^17.0.2",
|
||||||
"react-leaflet": "^4.0.0"
|
"react-leaflet": "^3.2.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"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
@ -1279,6 +1279,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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": {
|
"@rollup/pluginutils": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||||
@ -2027,25 +2033,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-leaflet": {
|
"react-leaflet": {
|
||||||
"version": "4.0.0",
|
"version": "3.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-3.2.5.tgz",
|
||||||
"integrity": "sha512-qJJvoCNe12XHSWVUwhXYmMObPoSYy8h/hn0aDNvcBuq3O8zmVI5S2RdabhaDg/iWMCJ2jbCWZWtIU5VtztO9sg==",
|
"integrity": "sha512-Z3KZ+4SijsRbbrt2I1a3ZDY6+V6Pm91eYTdxTN18G6IOkFRsJo1BuSPLFnyFrlF3WDjQFPEcTPkEgD1VEeAoBg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@react-leaflet/core": "^2.0.0"
|
"@react-leaflet/core": "^1.1.1"
|
||||||
},
|
|
||||||
"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-cluster": {
|
"react-leaflet-cluster": {
|
||||||
"version": "2.0.0",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-leaflet-cluster/-/react-leaflet-cluster-1.0.4.tgz",
|
||||||
"integrity": "sha512-tREjHM3mlNwj7sJdV+i0QSYbrOeju3RtPThfe7ik0T2oH56OffgMCC+mAjLOR+OrQXLIPktJpFYHpNNZOEtyUA==",
|
"integrity": "sha512-7sUtH35vf0JQIgiRHl4DWWy9JumEAhqDHfrjOlxIfCoHdeFFtnmHvdCetz/HJswHLLatwNZicCLx5DOFZzhL6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"leaflet.markercluster": "^1.5.3"
|
"leaflet.markercluster": "^1.5.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "utopia-ui",
|
"name": "utopia-ui",
|
||||||
"version": "1.0.6",
|
"version": "1.0.7",
|
||||||
"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/",
|
||||||
@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet": "^1.8.0",
|
"leaflet": "^1.8.0",
|
||||||
"react-leaflet": "^4.0.0",
|
"react-leaflet": "^3.2.5",
|
||||||
"react-leaflet-cluster": "^2.0.0"
|
"react-leaflet-cluster": "^1.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,25 +7,50 @@ export interface MarkerPopupProps {
|
|||||||
tags: Tag[]
|
tags: Tag[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarkerPopup = (props:MarkerPopupProps) => {
|
const MarkerPopup = (props: MarkerPopupProps) => {
|
||||||
const item:Item = props.item;
|
const item: Item = props.item;
|
||||||
const tags:Tag[] = props.tags;
|
const tags: Tag[] = props.tags;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup>
|
<Popup maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[30,30]}>
|
||||||
<b style={{ fontSize: '1.0rem' }}>{item.name}</b>
|
<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>
|
<p style={{ whiteSpace: "pre-wrap" }} dangerouslySetInnerHTML={{ __html: replaceURLs(item.text) }} />
|
||||||
{item.tags&&
|
<p>
|
||||||
tags.map((tag:Tag) => (
|
|
||||||
<span className="badge" style={{backgroundColor: tag.color,margin: '.1rem', fontSize: "100%"}} key={tag.id}>#{tag.name}</span>
|
{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>
|
</Popup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MarkerPopup;
|
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 MarkerPopup from "./Components/Map/MarkerPopup";
|
||||||
import { Item, Tag } from "./types"
|
import { Item, Tag } from "./types"
|
||||||
import "./styles.scss"
|
import "./styles.scss"
|
||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import MarkerClusterGroup from 'react-leaflet-cluster'
|
||||||
|
|
||||||
export interface MapProps {
|
export interface MapProps {
|
||||||
height: string,
|
height: string,
|
||||||
width: string,
|
width: string,
|
||||||
center: number[],
|
center: LatLng,
|
||||||
zoom: number,
|
zoom: number,
|
||||||
places?: Item[],
|
places?: Item[],
|
||||||
events?: Item[],
|
events?: Item[],
|
||||||
@ -18,6 +20,15 @@ export interface MapProps {
|
|||||||
|
|
||||||
|
|
||||||
const UtopiaMap = (props: 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]));
|
let tagMap = new Map(props.tags?.map(key => [key.id, key]));
|
||||||
|
|
||||||
const getTags = (item: Item) => {
|
const getTags = (item: Item) => {
|
||||||
@ -30,10 +41,11 @@ const UtopiaMap = (props: MapProps) => {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
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
|
<TileLayer
|
||||||
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}>
|
||||||
{props.places &&
|
{props.places &&
|
||||||
props.places.map((place: Item) => {
|
props.places.map((place: Item) => {
|
||||||
let tags = getTags(place);
|
let tags = getTags(place);
|
||||||
@ -71,6 +83,7 @@ const UtopiaMap = (props: MapProps) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
</MarkerClusterGroup>
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|||||||
53
src/types.ts
53
src/types.ts
@ -1,28 +1,25 @@
|
|||||||
import { Component } from 'react' // Switch to 'react' if you use it
|
export interface Item {
|
||||||
import { MarkerClusterGroupOptions } from 'leaflet'
|
id: number,
|
||||||
|
date_created?: string,
|
||||||
export interface Item {
|
date_updated?: string | null,
|
||||||
id: number,
|
name: string,
|
||||||
date_created?: string,
|
text: string,
|
||||||
date_updated?: string | null,
|
position: Geometry,
|
||||||
name: string,
|
start?: string,
|
||||||
text: string,
|
end?: string,
|
||||||
position: Geometry,
|
tags?: number[],
|
||||||
start?: string,
|
[key: string]: any
|
||||||
end?: string,
|
}
|
||||||
tags?: number[],
|
|
||||||
[key: string]:any
|
export interface Geometry {
|
||||||
}
|
type: string;
|
||||||
|
coordinates: number[];
|
||||||
export interface Geometry {
|
}
|
||||||
type: string;
|
|
||||||
coordinates: number[];
|
export interface Tag {
|
||||||
}
|
|
||||||
|
color: string;
|
||||||
export interface Tag {
|
id: number;
|
||||||
|
name: string;
|
||||||
color: string;
|
|
||||||
id: number;
|
}
|
||||||
name: string;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user