diff --git a/src/KartaPage.tsx b/src/KartaPage.tsx index e929180..b46fc02 100644 --- a/src/KartaPage.tsx +++ b/src/KartaPage.tsx @@ -1,154 +1,275 @@ -import { Paper, Flex, ScrollArea, Autocomplete } from '@mantine/core'; -import { ArticleCardVertical } from './ArticleCard'; -import Map, { Source, Layer, ScaleControl } from 'react-map-gl/maplibre'; -import { useState, useEffect, useRef } from 'react'; -import Fuse from 'fuse.js' +import { Paper, Flex, ScrollArea, Autocomplete, Title } from "@mantine/core"; +import { ArticleCardVertical } from "./ArticleCard"; +import Map, { + Source, + 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-mapbox.json'; -import pin from './assets/pin.png' -import plus from './assets/plus.png' -import minus from './assets/minus.png' +import mapstyle from "./assets/basemap-mapbox.json"; +import plus from "./assets/plus.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() { const mapRef = useRef(null); const [initial, setInitial] = useState(null); const [articles, setArticles] = useState(null); - const [search, setSearch] = useState(''); + const [search, setSearch] = useState(""); 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 const handleMapLoad = (e) => { - if (mapRef.current) { - const pinImage = new Image(); - pinImage.onload = () => { - if (!mapRef.current.hasImage('pin')) { - mapRef.current.addImage('pin-marker', pinImage); - } - } - pinImage.src = pin; - } - } + const icons = ["pin", "prishvin", "ostrovskiy", "brusov"]; + const pinImage = new Image(); + pinImage.src = pin; + pinImage.onload = () => mapRef.current.addImage("pin-marker", pinImage); + const prishvinImage = new Image(); + prishvinImage.src = prishvin; + prishvinImage.onload = () => + 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 useEffect(() => { fetch(`${host}/api/articles?populate=*`) - .then(r => r.json()) - .then(d => { - setInitial(d.data) - setArticles(d.data) - }) - }, []) - + .then((r) => r.json()) + .then((d) => { + setInitial(d.data); + setArticles(d.data); + }); + }, []); // Search - const fuse = initial !== null && new Fuse(initial, { - keys: [ - "attributes.tags.value" - ] - }); + const fuse = + initial !== null && + new Fuse(initial, { + keys: ["attributes.tags.value"], + }); useEffect(() => { - const foundArticles = fuse && fuse.search(search).map(e => e.item) - const updatedArticles = search.length > 0 ? foundArticles : initial - setArticles(updatedArticles) - }, [search]) + const foundArticles = fuse && fuse.search(search).map((e) => e.item); + const updatedArticles = search.length > 0 ? foundArticles : initial; + setArticles(updatedArticles); + }, [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) => { - setSelected(id) - const { longitude, latitude } = articles.find(e => e.id == id).attributes - id > 0 && articles && mapRef.current?.flyTo({ center: [longitude, latitude] }) - } + setSelected(id); + }; // Zoom - const ZoomControl = () => { + const ZoomControl = (props) => { const zoomIn = () => { - mapRef.current.zoomTo(mapRef.current.getZoom() + 1) - } + mapRef.current.zoomTo(mapRef.current.getZoom() + 1); + }; const zoomOut = () => { - mapRef.current.zoomTo(mapRef.current.getZoom() - 1) - } + mapRef.current.zoomTo(mapRef.current.getZoom() - 1); + }; return ( - - - + + + + + + + - - ) - } + ); + }; return ( -
+
{ e.features[0] ? setSelected(e.features[0].properties.id) : setSelected(-1) }} - onMouseEnter={() => setCursor('pointer')} - onMouseLeave={() => setCursor('grab')} + interactiveLayerIds={["points-layer"]} + onClick={(e) => { + // console.log(e.features); + 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} > a.id)]} + id="points-layer" + type="circle" + source-layer="layer" + filter={ + articles === null || articles.length == 0 + ? false + : ["in", "id", ...articles.map((a) => a.id)] + } paint={{ - "circle-color": ['match', ['get', 'id'], selected, '#FF0000', '#1c9099'], + "circle-color": [ + "match", + ["get", "id"], + selected, + "#e66a5a", + "#F2994A", + ], "circle-radius": 8, - "circle-opacity": 0.7, + "circle-opacity": ["step", ["zoom"], 0.8, 7, 0], }} - maxzoom={7} /> a.id)]} + id="icons-layer" + type="symbol" + source-layer="layer" + filter={ + articles === null || articles.length == 0 + ? false + : ["in", "id", ...articles.map((a) => a.id)] + } layout={{ - 'icon-image': 'pin-marker', - 'icon-size': 1 + "icon-image": "pin-marker", + "icon-size": 1, }} minzoom={7} + maxzoom={14} + /> + 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} /> - - + + + - - + + + + Статьи + - + - {articles !== null && articles.length > 0 && articles.map(article => { - const articleInfo = { - "id": article.id, - "image": article.attributes.cover.data !== null ? host + article.attributes.cover.data.attributes.url : "", - "category": "Памятные места", - "title": article.attributes.title, - "address": article.attributes.address, - "coordinates": article.attributes.coordinates, - "selected": selected, - "handleAddressClick": handleAddressClick - } - - return - } - )} + {articles !== null && + articles.length > 0 && + articles.map((article) => ( + + ))}
); - - -} \ No newline at end of file +} diff --git a/src/assets/logos/association.png b/src/assets/logos/association.png new file mode 100644 index 0000000..f18ac34 Binary files /dev/null and b/src/assets/logos/association.png differ diff --git a/src/assets/logos/fund.png b/src/assets/logos/fund.png new file mode 100644 index 0000000..33c1879 Binary files /dev/null and b/src/assets/logos/fund.png differ diff --git a/src/assets/logos/glm.png b/src/assets/logos/glm.png new file mode 100644 index 0000000..d1c5d5e Binary files /dev/null and b/src/assets/logos/glm.png differ diff --git a/src/assets/ostrovskiy.png b/src/assets/ostrovskiy.png new file mode 100644 index 0000000..22573a8 Binary files /dev/null and b/src/assets/ostrovskiy.png differ diff --git a/src/assets/prishvin.png b/src/assets/prishvin.png new file mode 100644 index 0000000..867775f Binary files /dev/null and b/src/assets/prishvin.png differ