Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/components/Map/GoogleMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@ import {GMapProps} from '../../models';
import {LocaleContext} from '../../context/localeContext/localeContext';
import {MobileContext} from '../../context/mobileContext';
import {getMapHeight} from './helpers';
import {Lang} from '../../utils/configure';

const b = block('map');

function getScriptSrc(apiKey: string, scriptSrc: string, address: string, lang: 'ru' | 'en') {
return `${scriptSrc}?key=${apiKey}&language=${lang}&q=${encodeURI(address)}`;
interface GoogleMapLinkParams {
apiKey: string;
scriptSrc: string;
address: string;
lang: Lang;
zoom?: number;
}

function getScriptSrc(params: GoogleMapLinkParams) {
const {apiKey, scriptSrc, address, lang, zoom} = params;

return `${scriptSrc}?key=${apiKey}&language=${lang}${zoom ? '&zoom=' + zoom : ''}&q=${encodeURI(
address,
)}`;
}

const GoogleMap: React.FC<GMapProps> = (props) => {
const {address} = props;
const {address, zoom} = props;
const {apiKey, scriptSrc} = useContext(MapsContext);
const {lang = 'ru'} = useContext(LocaleContext);
const {lang = Lang.Ru} = useContext(LocaleContext);
const isMobile = useContext(MobileContext);

const [height, setHeight] = useState<number | undefined>(undefined);
const ref = useRef<HTMLIFrameElement>(null);
const src = useMemo(
() => getScriptSrc(apiKey, scriptSrc, address, lang),
[apiKey, scriptSrc, address, lang],
() => getScriptSrc({apiKey, scriptSrc, address, lang, zoom}),
[apiKey, scriptSrc, address, lang, zoom],
);

useEffect(() => {
Expand Down
6 changes: 6 additions & 0 deletions src/components/Map/Map.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ $block: '.#{$ns}map';
overflow: hidden;
display: flex;

&_hidden {
opacity: 0;
}

&__spinner {
margin: 0 auto;
align-self: center;
position: absolute;
}

&__wrapper {
Expand All @@ -21,5 +26,6 @@ $block: '.#{$ns}map';
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
}
2 changes: 0 additions & 2 deletions src/components/Map/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ Map

`type: "map"`

`center: number[]` - Geo coordinates of the map center, required for `Yandex maps`

`zoom?: number` - Map zoom level. In google maps values ranging from 0 (the whole world) to 21 (individual buildings). In Yandex maps values ranging from 1 (the whole world) to 16 (individual buildings)

`address?: string;` - URL-escaped place name, address. You need to use it for `Google maps`
Expand Down
43 changes: 26 additions & 17 deletions src/components/Map/YMap/YMap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Coordinate} from '../../../models/constructor-items/common';

import {YMapMarker, YMapMarkerLabel} from '../../../models';
import {YMapProps, YMapMarker, YMapMarkerLabel} from '../../../models';

enum GeoObjectTypes {
Properties = 'properties',
Expand All @@ -19,6 +19,8 @@ const geoObjectPropsAndOptions = {
preset: GeoObjectTypes.Options,
};

type PlacemarksProps = Pick<YMapProps, 'zoom' | 'markers'>;

export class YMap {
private ymap: Ymaps.Map;
private mapRef: HTMLDivElement | null;
Expand All @@ -29,18 +31,18 @@ export class YMap {
this.mapRef = mapRef;
}

async showPlacemarks(markers: YMapMarker[]) {
async showPlacemarks(props: PlacemarksProps) {
this.clearOldPlacemarks();

for (const marker of markers) {
for (const marker of props.markers) {
if (marker.address) {
await this.findAddress(marker);
} else if (marker.coordinate) {
this.findCoordinate(marker);
}
}

this.recalcZoomAndCenter();
this.recalcZoomAndCenter(props);
}

async findAddress(marker: YMapMarker) {
Expand All @@ -66,11 +68,7 @@ export class YMap {
}

private drawPlaceMarkStyle(geoObject: Ymaps.GeoObject, marker: YMapMarker) {
if (!marker.label) {
return;
}

const {iconColor, preset = DEFAULT_PLACEMARKS_PRESET} = marker.label;
const {iconColor, preset = DEFAULT_PLACEMARKS_PRESET} = marker.label || {};
let localIconColor: string | undefined = iconColor;

// You can set the preset option together with the iconColor option only if it not a 'Stretchy' preset
Expand All @@ -90,8 +88,9 @@ export class YMap {
);
}

private recalcZoomAndCenter() {
private recalcZoomAndCenter(props: PlacemarksProps) {
const coordsLength = this.coords.length;
const {zoom = 0} = props;

if (!coordsLength) {
return;
Expand All @@ -105,17 +104,27 @@ export class YMap {
rightTop = [Math.max(rightTop[0], point[0]), Math.max(rightTop[1], point[1])];
});

const newMapParams = window.ymaps.util.bounds.getCenterAndZoom(
[leftBottom, rightTop],
[this.mapRef?.clientWidth, this.mapRef?.clientHeight],
undefined,
{margin: DEFAULT_MAP_CONTROL_BUTTON_HEIGHT},
);
let newMapParams = {
zoom,
center: [],
};

if (zoom) {
// compute only the center
newMapParams.center = window.ymaps.util.bounds.getCenter([leftBottom, rightTop]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get the situation when window.ymaps or window.ymaps.util or window.ymaps.util.bounds is undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. we create new Map after getting window.ymaps => we get to this place after getting window.ymaps
  2. util and util.bounds are always available

} else {
newMapParams = window.ymaps.util.bounds.getCenterAndZoom(
[leftBottom, rightTop],
[this.mapRef?.clientWidth, this.mapRef?.clientHeight],
undefined,
{margin: DEFAULT_MAP_CONTROL_BUTTON_HEIGHT},
);
}

this.ymap.setCenter(newMapParams.center);

// Use default zoom for one placemark
if (coordsLength > 1) {
if (coordsLength > 1 && !zoom) {
this.ymap.setZoom(newMapParams.zoom);
}
}
Expand Down
30 changes: 20 additions & 10 deletions src/components/Map/YMap/YandexMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ import {getMapHeight} from '../helpers';
const b = block('map');
const DEFAULT_CONTAINER_ID = 'ymap';
const DEFAULT_ZOOM = 9;
// Center - is a required parameter for creating a new map
// We use this init center to create a map
// The real center of the map will be calculated later, using the coordinates of the markers
const INITIAL_CENTER = [0, 0];

const YandexMap: React.FC<YMapProps> = (props) => {
const {markers, zoom, center, id} = props;
const {markers, zoom, id} = props;
const {apiKey, scriptSrc, nonce} = useContext(MapsContext);
const isMobile = useContext(MobileContext);

Expand All @@ -30,17 +34,14 @@ const YandexMap: React.FC<YMapProps> = (props) => {
const ref = useRef<HTMLDivElement>(null);

const [loading, setLoading] = useState<boolean>(false);
const [ready, setReady] = useState<boolean>(false);
const [attemptsIndex, setAttemptsIndex] = useState<number>(0);
const onTryAgain = useCallback(() => {
setAttemptsIndex(attemptsIndex + 1);
}, [attemptsIndex]);

useEffect(() => {
(async function () {
if (!center) {
return;
}

setLoading(true);

await YMapsApiLoader.loadApi(apiKey, scriptSrc, lang, nonce);
Expand All @@ -51,7 +52,7 @@ const YandexMap: React.FC<YMapProps> = (props) => {
new window.ymaps.Map(
containerId,
{
center,
center: INITIAL_CENTER,
zoom: zoom || DEFAULT_ZOOM,
},
{autoFitToViewport: 'always'},
Expand All @@ -63,7 +64,7 @@ const YandexMap: React.FC<YMapProps> = (props) => {

setLoading(false);
})();
}, [apiKey, lang, scriptSrc, containerId, zoom, center, nonce, attemptsIndex, setLoading]);
}, [apiKey, lang, scriptSrc, containerId, zoom, nonce, attemptsIndex, setLoading]);

useEffect(() => {
const updateSize = _.debounce(() => {
Expand All @@ -82,11 +83,18 @@ const YandexMap: React.FC<YMapProps> = (props) => {

useEffect(() => {
if (ymap) {
ymap.showPlacemarks(markers);
// show with computed center and placemarks
const showPlacemarks = async () => {
await ymap.showPlacemarks({markers, zoom});

setReady(true);
};

showPlacemarks();
}
});

if (!center) return null;
if (!markers) return null;

return (
<ErrorWrapper
Expand All @@ -96,7 +104,9 @@ const YandexMap: React.FC<YMapProps> = (props) => {
handler={onTryAgain}
className={b('wrapper')}
>
<div id={containerId} className={b()} ref={ref} style={{height}}>
<div className={b('wrapper')}>
{/* hidden - to show the map after calculating the center */}
<div id={containerId} className={b({hidden: !ready})} ref={ref} style={{height}} />
{loading ? <Spin size="xl" className={b('spinner')} /> : null}
</div>
</ErrorWrapper>
Expand Down
3 changes: 0 additions & 3 deletions src/components/Map/__stories__/data.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{
"ymap": {
"zoom": 9,
"id": "common-places",
"center": [55.753994, 37.622093],
"markers": [
{
"address": "Moscow Arbat",
Expand All @@ -27,7 +25,6 @@
]
},
"gmap": {
"zoom": 9,
"address": "Anthony Fokkerweg 1, 1059 CM Amsterdam"
}
}
2 changes: 0 additions & 2 deletions src/models/constructor-items/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ export type Coordinate = number[];

export interface MapBaseProps {
zoom?: number;
center?: Coordinate;
}
export interface GMapProps extends MapBaseProps {
address: string;
Expand All @@ -253,7 +252,6 @@ export interface GMapProps extends MapBaseProps {
export interface YMapProps extends MapBaseProps {
markers: YMapMarker[];
id: string;
center: Coordinate;
}

export interface YMapMarker {
Expand Down
4 changes: 0 additions & 4 deletions src/schema/validators/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,10 +549,6 @@ export const MapProps = {
zoom: {
type: 'number',
},
center: {
type: 'array',
items: {type: 'number'},
},
address: {
type: 'string',
},
Expand Down