Skip to content

Commit

Permalink
Больше подробностей (часть 1) (#5)
Browse files Browse the repository at this point in the history
* убирает лишнюю обертку
* исправляет моки для Амстердама
* добавляет карту
  • Loading branch information
denispan authored Mar 22, 2024
1 parent dc86b3c commit 7d2d92f
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 47 deletions.
62 changes: 62 additions & 0 deletions src/components/map/map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {useEffect, useRef} from 'react';
import {Icon, layerGroup, Marker} from 'leaflet';

import {CITIES, URL_MARKER_CURRENT, URL_MARKER_DEFAULT} from '../../const';
import 'leaflet/dist/leaflet.css';
import useMap from '../../hooks/use-map.tsx';
import {OfferShortInfo} from '../../types/offer.ts';
import {Nullable} from 'vitest';

type MapProps = {
city: typeof CITIES[number];
offers: OfferShortInfo[];
activeOffer: Nullable<OfferShortInfo>;
};

const defaultCustomIcon = new Icon({
iconUrl: URL_MARKER_DEFAULT,
iconSize: [40, 40],
iconAnchor: [20, 40]
});

const currentCustomIcon = new Icon({
iconUrl: URL_MARKER_CURRENT,
iconSize: [40, 40],
iconAnchor: [20, 40]
});

function Map({city, offers, activeOffer}: MapProps) {

const mapRef = useRef(null);
const map = useMap(mapRef, city);

useEffect(() => {
if (map) {
const markerLayer = layerGroup().addTo(map);
offers.forEach((offer) => {
const marker = new Marker({
lat: offer.location.latitude,
lng: offer.location.longitude
});

marker
.setIcon(
activeOffer !== undefined && activeOffer !== null && offer.id === activeOffer.id
? currentCustomIcon
: defaultCustomIcon
)
.addTo(markerLayer);
});

map.flyTo([city.location.latitude, city.location.longitude], 12);

return () => {
map.removeLayer(markerLayer);
};
}
}, [city, map, offers, activeOffer]);

return <section ref={mapRef} className="cities__map map" />;
}

export default Map;
11 changes: 10 additions & 1 deletion src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ const enum AuthorizationStatus {
const DATE_FORMAT = 'MMMM YYYY';
const DATE_FORMAT_ATTRIBUTE = 'YYYY-MM-DD';

const URL_MARKER_DEFAULT =
'https://assets.htmlacademy.ru/content/intensive/javascript-1/demo/interactive-map/pin.svg';

const URL_MARKER_CURRENT =
'https://assets.htmlacademy.ru/content/intensive/javascript-1/demo/interactive-map/main-pin.svg';


export {
RATING_STARS,
OFFERS_COUNT,
Expand All @@ -66,5 +73,7 @@ export {
DATE_FORMAT,
DATE_FORMAT_ATTRIBUTE,
MIN_STARS_COMMENT,
MIN_LENGTH_COMMENT
MIN_LENGTH_COMMENT,
URL_MARKER_DEFAULT,
URL_MARKER_CURRENT,
};
41 changes: 41 additions & 0 deletions src/hooks/use-map.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {MutableRefObject, useEffect, useRef, useState} from 'react';
import {Map, TileLayer} from 'leaflet';
import {CITIES} from '../const.ts';


function useMap(
mapRef: MutableRefObject<HTMLElement | null>,
city: typeof CITIES[number]
): Map | null {
const [map, setMap] = useState<Map | null>(null);
const isRenderedRef = useRef<boolean>(false);

useEffect(() => {
if (mapRef.current !== null && !isRenderedRef.current) {
const instance = new Map(mapRef.current, {
center: {
lat: city.location.latitude,
lng: city.location.longitude
},
zoom: 12
});

const layer = new TileLayer(
'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
{
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors &copy; <a href="https://carto.com/attributions">CARTO</a>'
}
);

instance.addLayer(layer);

setMap(instance);
isRenderedRef.current = true;
}
}, [mapRef, city]);

return map;
}

export default useMap;
80 changes: 40 additions & 40 deletions src/mocks/offers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ const OFFERS_SHORT_INFO: OfferShortInfo[] = [
'price': 307,
'previewImage': 'https://15.design.htmlacademy.pro/static/hotel/8.jpg',
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.868610000000004,
'longitude': 2.342499,
'latitude': 52.3909553943508,
'longitude': 4.85309666406198,
'zoom': 16
},
'isFavorite': true,
Expand All @@ -31,16 +31,16 @@ const OFFERS_SHORT_INFO: OfferShortInfo[] = [
'price': 528,
'previewImage': 'https://15.design.htmlacademy.pro/static/hotel/4.jpg',
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.858610000000006,
'longitude': 2.330499,
'latitude': 52.3609553943508,
'longitude': 4.85309666406198,
'zoom': 16
},
'isFavorite': false,
Expand All @@ -54,16 +54,16 @@ const OFFERS_SHORT_INFO: OfferShortInfo[] = [
'price': 923,
'previewImage': 'https://15.design.htmlacademy.pro/static/hotel/7.jpg',
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.834610000000005,
'longitude': 2.335499,
'latitude': 52.3909553943508,
'longitude': 4.929309666406198,
'zoom': 16
},
'isFavorite': false,
Expand All @@ -77,16 +77,16 @@ const OFFERS_SHORT_INFO: OfferShortInfo[] = [
'price': 123,
'previewImage': 'https://15.design.htmlacademy.pro/static/hotel/19.jpg',
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.85761,
'longitude': 2.358499,
'latitude': 52.3809553943508,
'longitude': 4.939309666406198,
'zoom': 16
},
'isFavorite': false,
Expand All @@ -111,16 +111,16 @@ const OFFERS_FULL_INFO: OfferFullInfo[] = [
'https://15.design.htmlacademy.pro/static/hotel/19.jpg'
],
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.868610000000004,
'longitude': 2.342499,
'latitude': 52.3909553943508,
'longitude': 4.85309666406198,
'zoom': 16
},
'goods': [
Expand Down Expand Up @@ -156,16 +156,16 @@ const OFFERS_FULL_INFO: OfferFullInfo[] = [
'https://15.design.htmlacademy.pro/static/hotel/5.jpg'
],
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.858610000000006,
'longitude': 2.330499,
'latitude': 52.3609553943508,
'longitude': 4.85309666406198,
'zoom': 16
},
'goods': [
Expand Down Expand Up @@ -205,16 +205,16 @@ const OFFERS_FULL_INFO: OfferFullInfo[] = [
'https://15.design.htmlacademy.pro/static/hotel/10.jpg'
],
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.834610000000005,
'longitude': 2.335499,
'latitude': 52.3909553943508,
'longitude': 4.929309666406198,
'zoom': 16
},
'goods': [
Expand Down Expand Up @@ -258,16 +258,16 @@ const OFFERS_FULL_INFO: OfferFullInfo[] = [
'https://15.design.htmlacademy.pro/static/hotel/18.jpg'
],
'city': {
'name': 'Paris',
'name': 'Amsterdam',
'location': {
'latitude': 48.85661,
'longitude': 2.351499,
'latitude': 52.37454,
'longitude': 4.897976,
'zoom': 13
}
},
'location': {
'latitude': 48.85761,
'longitude': 2.358499,
'latitude': 52.3809553943508,
'longitude': 4.939309666406198,
'zoom': 16
},
'goods': [
Expand Down
20 changes: 14 additions & 6 deletions src/pages/main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import Header from '../../components/header/header.tsx';
import LocationTab from '../../components/location-tab/location-tab.tsx';
import {OfferShortInfo} from '../../types/offer.ts';
import {useDocumentTitle} from '../../hooks/document-title.ts';
import {useState} from 'react';
import {Nullable} from 'vitest';
import {CITIES} from '../../const.ts';
import {useEffect, useState} from 'react';
import Map from '../../components/map/map.tsx';
import {useLocation} from 'react-router-dom';

export type MainProps = {
offers: OfferShortInfo[];
Expand All @@ -15,10 +18,13 @@ export type MainProps = {
function Main({offers, offersCount, title = 'Main'}: MainProps) {
useDocumentTitle(title);
const [activeOffer, setActiveOffer] = useState<Nullable<OfferShortInfo>>(null);
const location = useLocation();
const [citySlug, setCitySlug] = useState(location.pathname.split('/').pop());
useEffect(() => {
setCitySlug(location.pathname.split('/').pop());
}, [location]);

const onHoverOffer = (offer?: OfferShortInfo) => {
setActiveOffer(offer || null);
};
const currentCity = CITIES.find((city) => city.slug === citySlug);

return (
<div className="page page--gray page--main">
Expand Down Expand Up @@ -56,11 +62,13 @@ function Main({offers, offersCount, title = 'Main'}: MainProps) {
</ul>
</form>
<div className="cities__places-list places__list tabs__content">
{offers.map((offer: OfferShortInfo) => <OfferCard hoverHandler={onHoverOffer} componentType={'cities'} key={offer.id} offer={offer}/>)}
{offers.map((offer: OfferShortInfo) => <OfferCard hoverHandler={() => setActiveOffer(offer || null)} componentType={'cities'} key={offer.id} offer={offer}/>)}
</div>
</section>
<div className="cities__right-section">
<section className="cities__map map"></section>
{
currentCity && <Map city={currentCity} offers={offers} activeOffer={activeOffer} />
}
</div>
</div>
</div>
Expand Down

0 comments on commit 7d2d92f

Please sign in to comment.