add icons, change onClick, geolocate, change layout

master
gman 2 years ago
parent 68bb753e98
commit b913c169fb

@ -1,154 +1,275 @@
import { Paper, Flex, ScrollArea, Autocomplete } from '@mantine/core'; import { Paper, Flex, ScrollArea, Autocomplete, Title } from "@mantine/core";
import { ArticleCardVertical } from './ArticleCard'; import { ArticleCardVertical } from "./ArticleCard";
import Map, { Source, Layer, ScaleControl } from 'react-map-gl/maplibre'; import Map, {
import { useState, useEffect, useRef } from 'react'; Source,
import Fuse from 'fuse.js' Layer,
ScaleControl,
GeolocateControl,
} from "react-map-gl/maplibre";
import { useState, useEffect, useRef } from "react";
import Fuse from "fuse.js";
// import mapstyle from './assets/basemap.json'; // import mapstyle from './assets/basemap.json';
import mapstyle from './assets/basemap-mapbox.json'; import mapstyle from "./assets/basemap-mapbox.json";
import pin from './assets/pin.png' import plus from "./assets/plus.png";
import plus from './assets/plus.png' import minus from "./assets/minus.png";
import minus from './assets/minus.png'
import pin from "./assets/pin.png";
import prishvin from "./assets/prishvin.png";
import ostrovskiy from "./assets/ostrovskiy.png";
import brusov from "./assets/brusov.png";
export function KartaPage() { export function KartaPage() {
const mapRef = useRef(null); const mapRef = useRef(null);
const [initial, setInitial] = useState(null); const [initial, setInitial] = useState(null);
const [articles, setArticles] = useState(null); const [articles, setArticles] = useState(null);
const [search, setSearch] = useState(''); const [search, setSearch] = useState("");
const [selected, setSelected] = useState(-1); const [selected, setSelected] = useState(-1);
const [cursor, setCursor] = useState('grab'); const [cursor, setCursor] = useState("grab");
const host = "http://strapi.litmusmap.ru" const host = "http://strapi.litmusmap.ru";
// Load icons // Load icons
const handleMapLoad = (e) => { const handleMapLoad = (e) => {
if (mapRef.current) { const icons = ["pin", "prishvin", "ostrovskiy", "brusov"];
const pinImage = new Image(); const pinImage = new Image();
pinImage.onload = () => { pinImage.src = pin;
if (!mapRef.current.hasImage('pin')) { pinImage.onload = () => mapRef.current.addImage("pin-marker", pinImage);
mapRef.current.addImage('pin-marker', pinImage); const prishvinImage = new Image();
} prishvinImage.src = prishvin;
} prishvinImage.onload = () =>
pinImage.src = pin; mapRef.current.addImage("prishvin-marker", prishvinImage);
} const ostrovskiyImage = new Image();
} ostrovskiyImage.src = ostrovskiy;
ostrovskiyImage.onload = () =>
mapRef.current.addImage("ostrovskiy-marker", ostrovskiyImage);
const brusovImage = new Image();
brusovImage.src = brusov;
brusovImage.onload = () =>
mapRef.current.addImage("brusov-marker", brusovImage);
};
// Load articles // Load articles
useEffect(() => { useEffect(() => {
fetch(`${host}/api/articles?populate=*`) fetch(`${host}/api/articles?populate=*`)
.then(r => r.json()) .then((r) => r.json())
.then(d => { .then((d) => {
setInitial(d.data) setInitial(d.data);
setArticles(d.data) setArticles(d.data);
}) });
}, []) }, []);
// Search // Search
const fuse = initial !== null && new Fuse(initial, { const fuse =
keys: [ initial !== null &&
"attributes.tags.value" new Fuse(initial, {
] keys: ["attributes.tags.value"],
}); });
useEffect(() => { useEffect(() => {
const foundArticles = fuse && fuse.search(search).map(e => e.item) const foundArticles = fuse && fuse.search(search).map((e) => e.item);
const updatedArticles = search.length > 0 ? foundArticles : initial const updatedArticles = search.length > 0 ? foundArticles : initial;
setArticles(updatedArticles) setArticles(updatedArticles);
}, [search]) }, [search]);
// Select article interaction
useEffect(() => {
if (selected > 0) {
const selectedArticleIndex = articles.findIndex((a) => a.id == selected);
const selectedArticle = articles[selectedArticleIndex];
const reorderedArticles = [
selectedArticle,
...articles.filter((a) => a.id !== selected),
];
setArticles(reorderedArticles);
mapRef.current?.flyTo({
center: [
selectedArticle.attributes.longitude,
selectedArticle.attributes.latitude,
],
zoom: 10,
});
}
}, [selected]);
// List-map interaction
const handleAddressClick = (id) => { const handleAddressClick = (id) => {
setSelected(id) setSelected(id);
const { longitude, latitude } = articles.find(e => e.id == id).attributes };
id > 0 && articles && mapRef.current?.flyTo({ center: [longitude, latitude] })
}
// Zoom // Zoom
const ZoomControl = () => { const ZoomControl = (props) => {
const zoomIn = () => { const zoomIn = () => {
mapRef.current.zoomTo(mapRef.current.getZoom() + 1) mapRef.current.zoomTo(mapRef.current.getZoom() + 1);
} };
const zoomOut = () => { const zoomOut = () => {
mapRef.current.zoomTo(mapRef.current.getZoom() - 1) mapRef.current.zoomTo(mapRef.current.getZoom() - 1);
} };
return ( return (
<Flex style={{ <Flex
position: 'absolute', style={{
top: '1rem', position: "absolute",
left: '1rem', bottom: props.bottom,
}}> right: props.right,
<Paper onClick={zoomIn} bg={'none'} sx={{"&:hover": {backgroundColor: "white", cursor: 'pointer'}}} maw={48} mah={48}><img src={plus} style={{width: '48px', height: '48px'}}></img></Paper> }}
<Paper onClick={zoomOut} bg={'none'} sx={{"&:hover": {backgroundColor: "white", cursor: 'pointer'}}} maw={48} mah={48}><img src={minus} style={{width: '48px', height: '48px'}}></img></Paper> >
<Paper
onClick={zoomOut}
radius="xl"
bg={"none"}
sx={{ "&:hover": { backgroundColor: "white", cursor: "pointer" } }}
maw={48}
mah={48}
>
<img src={minus} style={{ width: "48px", height: "48px" }}></img>
</Paper>
<Paper
onClick={zoomIn}
radius="xl"
bg={"none"}
sx={{ "&:hover": { backgroundColor: "white", cursor: "pointer" } }}
maw={48}
mah={48}
>
<img src={plus} style={{ width: "48px", height: "48px" }}></img>
</Paper>
</Flex> </Flex>
);
) };
}
return ( return (
<div style={{ position: 'relative' }}> <div style={{ position: "relative" }}>
<Map <Map
ref={mapRef} ref={mapRef}
initialViewState={{ initialViewState={{
longitude: 60, longitude: 30,
latitude: 60, latitude: 60,
zoom: 4 zoom: 4,
}} }}
minZoom={3.5}
maxZoom={16}
maxBounds={[
[-20, 35],
[200, 80],
]}
style={{ style={{
height: '90vh', height: "90vh",
}} }}
cursor={cursor} cursor={cursor}
mapStyle={mapstyle} mapStyle={mapstyle}
interactiveLayerIds={['points-layer']} interactiveLayerIds={["points-layer"]}
onClick={e => { e.features[0] ? setSelected(e.features[0].properties.id) : setSelected(-1) }} onClick={(e) => {
onMouseEnter={() => setCursor('pointer')} // console.log(e.features);
onMouseLeave={() => setCursor('grab')} e.features[0]
? (setSelected(e.features[0].properties.id),
mapRef.current.flyTo({
center: e.features[0].geometry.coordinates,
zoom: mapRef.current.getZoom() + 6,
}))
: setSelected(-1);
}}
onMouseEnter={() => setCursor("pointer")}
onMouseLeave={() => setCursor("grab")}
onLoad={handleMapLoad} onLoad={handleMapLoad}
> >
<Source <Source
id='points' id="points"
type='vector' type="vector"
tiles={[`${host}/tiles/{z}/{x}/{y}.pbf`]} tiles={[`${host}/tiles/{z}/{x}/{y}.pbf`]}
maxzoom={10} maxzoom={10}
> >
<Layer <Layer
id='points-layer' id="points-layer"
type='circle' type="circle"
source-layer='layer' source-layer="layer"
filter={articles === null || articles.length == 0 ? false : ['in', 'id', ...articles.map(a => a.id)]} filter={
articles === null || articles.length == 0
? false
: ["in", "id", ...articles.map((a) => a.id)]
}
paint={{ paint={{
"circle-color": ['match', ['get', 'id'], selected, '#FF0000', '#1c9099'], "circle-color": [
"match",
["get", "id"],
selected,
"#e66a5a",
"#F2994A",
],
"circle-radius": 8, "circle-radius": 8,
"circle-opacity": 0.7, "circle-opacity": ["step", ["zoom"], 0.8, 7, 0],
}} }}
maxzoom={7}
/> />
<Layer <Layer
id='points2-layer' id="icons-layer"
type='symbol' type="symbol"
source-layer='layer' source-layer="layer"
filter={articles === null || articles.length == 0 ? false : ['in', 'id', ...articles.map(a => a.id)]} filter={
articles === null || articles.length == 0
? false
: ["in", "id", ...articles.map((a) => a.id)]
}
layout={{ layout={{
'icon-image': 'pin-marker', "icon-image": "pin-marker",
'icon-size': 1 "icon-size": 1,
}} }}
minzoom={7} minzoom={7}
maxzoom={14}
/>
<Layer
id="portret-layer"
type="symbol"
source-layer="layer"
filter={
articles === null || articles.length == 0
? false
: ["in", "id", ...articles.map((a) => a.id)]
}
layout={{
"icon-image": [
"match",
["get", "id"],
6,
"ostrovskiy-marker",
7,
"brusov-marker",
1,
"prishvin-marker",
"pin-marker",
],
"icon-size": 0.1,
}}
minzoom={14}
/> />
</Source> </Source>
<ScaleControl /> <ScaleControl position="bottom-right" />
<ZoomControl /> <ZoomControl bottom="35px" right="4px" />
<GeolocateControl
showAccuracyCircle={false}
position="bottom-right"
style={{
marginBottom: "55px",
marginRight: "14px",
background: "none",
boxShadow: "none",
}}
/>
</Map> </Map>
<Paper shadow={'md'} withBorder style={{ <Paper
position: 'absolute', shadow={"md"}
top: '1rem', withBorder
right: '1rem', style={{
width: '36rem', position: "absolute",
height: '83vh', top: "1rem",
opacity: '90%', left: "1rem",
padding: '1rem 0 1rem 1rem' width: "36rem",
}}> height: "83vh",
opacity: "95%",
padding: "1rem 0 1rem 1rem",
}}
>
<Title order={3} mb={10}>
Статьи
</Title>
<Autocomplete <Autocomplete
mr={20} mr={20}
mb={30} mb={30}
@ -158,7 +279,7 @@ export function KartaPage() {
onChange={setSearch} onChange={setSearch}
data={[]} data={[]}
/> />
<ScrollArea h={'73vh'} type="auto"> <ScrollArea h={"67vh"} type="auto">
<Flex <Flex
mih={50} mih={50}
gap="md" gap="md"
@ -168,26 +289,29 @@ export function KartaPage() {
wrap="wrap" wrap="wrap"
mr={20} mr={20}
> >
{articles !== null && articles.length > 0 && articles.map(article => { {articles !== null &&
const articleInfo = { articles.length > 0 &&
"id": article.id, articles.map((article) => (
"image": article.attributes.cover.data !== null ? host + article.attributes.cover.data.attributes.url : "", <ArticleCardVertical
"category": "Памятные места", key={article.id}
"title": article.attributes.title, id={article.id}
"address": article.attributes.address, image={
"coordinates": article.attributes.coordinates, article.attributes.cover.data !== null
"selected": selected, ? host + article.attributes.cover.data.attributes.url
"handleAddressClick": handleAddressClick : ""
} }
category="музеи"
return <ArticleCardVertical key={article.id} {...articleInfo} /> title={article.attributes.title}
} address={article.attributes.address}
)} longitude={article.attributes.longitude}
latitude={article.attributes.latitude}
selected={selected}
clickAddressAction={handleAddressClick}
/>
))}
</Flex> </Flex>
</ScrollArea> </ScrollArea>
</Paper> </Paper>
</div> </div>
); );
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Loading…
Cancel
Save