PromucFlow_constructor/app/client/src/widgets/OpenStreetMapWidget/component/index.tsx

159 lines
5.3 KiB
TypeScript
Raw Normal View History

2025-11-12 18:43:13 +00:00
import React, { useEffect } from "react";
import type { WidgetProps } from "widgets/BaseWidget";
import styled from "styled-components";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import { FullScreen, useFullScreenHandle } from "react-full-screen";
import { Icon } from "@blueprintjs/core";
// Контейнер для карты с относительным позиционированием
const MapWrapper = styled.div`
position: relative;
width: 100%;
height: 100%;
`;
// Кнопка для переключения полноэкранного режима
const FullscreenButton = styled.button`
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background: white;
border: 1px solid #ccc;
border-radius: 4px;
padding: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: background-color 0.2s;
&:hover {
background-color: #f0f0f0;
}
&:active {
background-color: #e0e0e0;
}
`;
// 🧩 фикс, чтобы маркеры отображались (иначе пустые иконки)
delete (L.Icon.Default.prototype as any)._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
iconUrl: require("leaflet/dist/images/marker-icon.png"),
shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
});
// Компонент для обновления размера карты при изменении размера контейнера
function MapResizer() {
const map = useMap();
useEffect(() => {
// Обновляем размер карты при монтировании
const timer = setTimeout(() => {
map.invalidateSize();
}, 100);
return () => clearTimeout(timer);
}, [map]);
// Обновляем размер при изменении размера окна
useEffect(() => {
const handleResize = () => {
map.invalidateSize();
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [map]);
// Обновляем размер при переключении полноэкранного режима
useEffect(() => {
const handleFullscreenChange = () => {
// Небольшая задержка, чтобы контейнер успел изменить размер
setTimeout(() => {
map.invalidateSize();
}, 200);
};
document.addEventListener("fullscreenchange", handleFullscreenChange);
document.addEventListener("webkitfullscreenchange", handleFullscreenChange);
document.addEventListener("mozfullscreenchange", handleFullscreenChange);
document.addEventListener("MSFullscreenChange", handleFullscreenChange);
return () => {
document.removeEventListener("fullscreenchange", handleFullscreenChange);
document.removeEventListener("webkitfullscreenchange", handleFullscreenChange);
document.removeEventListener("mozfullscreenchange", handleFullscreenChange);
document.removeEventListener("MSFullscreenChange", handleFullscreenChange);
};
}, [map]);
return null;
}
function OpenStreetMapComponent(props: OpenStreetMapComponentProps) {
// Получаем значения из props или используем значения по умолчанию
const centerLat = props.centerLat ?? 55.751244;
const centerLng = props.centerLng ?? 37.618423;
const zoom = props.zoom ?? 7;
const markerLat = props.markerLat ?? centerLat;
const markerLng = props.markerLng ?? centerLng;
const markerText = props.markerText ?? "Привет! Это Москва 🏙️";
const center: [number, number] = [centerLat, centerLng];
const markerPosition: [number, number] = [markerLat, markerLng];
// Хук для управления полноэкранным режимом
const fullScreenHandle = useFullScreenHandle();
return (
<MapWrapper>
<FullScreen handle={fullScreenHandle}>
<MapContainer
center={center}
zoom={zoom}
style={{ height: "500px", width: "500px" }}
>
<MapResizer />
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors'
/>
<Marker position={markerPosition}>
<Popup>{markerText}</Popup>
</Marker>
</MapContainer>
</FullScreen>
{/* Кнопка для переключения полноэкранного режима */}
<FullscreenButton
onClick={fullScreenHandle.active ? fullScreenHandle.exit : fullScreenHandle.enter}
title={fullScreenHandle.active ? "Выйти из полноэкранного режима" : "Развернуть на весь экран"}
>
<Icon
icon={fullScreenHandle.active ? "minimize" : "maximize"}
iconSize={16}
/>
</FullscreenButton>
</MapWrapper>
);
}
export interface OpenStreetMapComponentProps extends WidgetProps {
centerLat?: number;
centerLng?: number;
zoom?: number;
markerLat?: number;
markerLng?: number;
markerText?: string;
}
export default OpenStreetMapComponent;