USER
import React, { useEffect, useRef, useState } from 'react';
declare global {
interface Window {
ymaps: any;
}
}
interface Marker {
id: string;
coordinates: number[];
title: string;
}
const MapComponent: React.FC = () => {
const mapRef = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<any>(null);
const [markers, setMarkers] = useState<Marker[]>(() => {
const savedMarkers = localStorage.getItem('markers');
return savedMarkers ? JSON.parse(savedMarkers) : [];
});
const [tempMarker, setTempMarker] = useState<{ coordinates: number[] } | null>(null);
useEffect(() => {
if (window.ymaps) {
window.ymaps.ready().then(() => {
const newMap = new window.ymaps.Map(mapRef.current, {
center: [55.751574, 37.573856],
zoom: 9,
});
newMap.events.add('click', (e: any) => {
const coords = e.get('coords');
setTempMarker({ coordinates: coords });
});
setMap(newMap);
});
}
}, []);
useEffect(() => {
if (map) {
map.geoObjects.removeAll();
markers.forEach((marker) => {
const placemark = new window.ymaps.Placemark(
marker.coordinates,
{
balloonContent: marker.title,
},
{
draggable: true,
}
);
placemark.events.add('contextmenu', () => {
if (window.confirm('Удалить эту метку?')) {
setMarkers((prevMarkers) =>
prevMarkers.filter((m) => m.id !== marker.id)
);
}
});
placemark.events.add('dragend', (e: any) => {
const newCoords = e.get('target').geometry.getCoordinates();
setMarkers((prevMarkers) =>
prevMarkers.map((m) =>
m.id === marker.id ? { ...m, coordinates: newCoords } : m
)
);
});
map.geoObjects.add(placemark);
});
if (tempMarker) {
const tempPlacemark = new window.ymaps.Placemark(tempMarker.coordinates, {}, { preset: 'islands#dotIcon' });
map.geoObjects.add(tempPlacemark);
}
}
}, [markers, map, tempMarker]);
useEffect(() => {
localStorage.setItem('markers', JSON.stringify(markers));
}, [markers]);
return (
<div style={{ display: 'flex' }}>
<div
ref={mapRef}
style={{ width: '70%', height: '500px', marginRight: '20px' }}
></div>
<div style={{ width: '30%' }}>
<h2>Список меток</h2>
<ul>
{markers.map((marker) => (
<li key={marker.id}>
<input
type="text"
value={marker.title}
onChange={(e) => {
const newTitle = e.target.value;
setMarkers((prevMarkers) =>
prevMarkers.map((m) =>
m.id === marker.id ? { ...m, title: newTitle } : m
)
);
}}
/>
<button
onClick={() => {
map.setCenter(marker.coordinates, 14);
}}
>
Перейти
</button>
<button
onClick={() => {
if (window.confirm('Удалить эту метку?')) {
setMarkers((prevMarkers) =>
prevMarkers.filter((m) => m.id !== marker.id)
);
}
}}
>
Удалить
</button>
</li>
))}
</ul>
{tempMarker && (
<div>
<input
type="text"
placeholder="Введите название метки"
onKeyDown={(e) => {
if (e.key === 'Enter') {
const title = (e.target as HTMLInputElement).value;
if (title.trim()) {
const newMarker = {
id: Date.now().toString(),
coordinates: tempMarker.coordinates,
title,
};
setMarkers((prevMarkers) => [...prevMarkers, newMarker]);
setTempMarker(null);
}
}
}}
/>
<button onClick={() => setTempMarker(null)}>Отменить</button>
</div>
)}
</div>
</div>
);
};
export default MapComponent; добавь возможность создать метку протсо введя ее координат и названиеASSISTANT
To add a feature that allows users to create a marker by entering its coordinates and title manually, you can include a form in your component's sidebar. This form will collect the coordinates and title, then add a marker to the map when submitted. Here's how you can modify your `MapComponent` to achieve this functionality:
1. **Add state variables for the form inputs** to store the coordinates and title as they are entered by the user.
2. **Create a form** in the UI where users can input the coordinates and title of the new marker.
3. **Handle form submission** to add the new marker based on the entered values.
Here's the updated code including these changes:
```jsx
import React, { useEffect, useRef, useState } from 'react';
declare global {
interface Window {
ymaps: any;
}
}
interface Marker {
id: string;
coordinates: number[];
title: string;
}
const MapComponent: React.FC = () => {
const mapRef = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<any>(null);
const [markers, setMarkers] = useState<Marker[]>(() => {
const savedMarkers = localStorage.getItem('markers');
return savedMarkers ? JSON.parse(savedMarkers) : [];
});
const [tempMarker, setTempMarker] = useState<{ coordinates: number[] } | null>(null);
const [newMarkerCoords, setNewMarkerCoords] = useState<string>('');
const [newMarkerTitle, setNewMarkerTitle] = useState<string>('');
useEffect(() => {
if (window.ymaps) {
window.ymaps.ready().then(() => {
const newMap = new window.ymaps.Map(mapRef.current, {
center: [55.751574, 37.573856],
zoom: 9,
});
newMap.events.add('click', (e: any) => {
const coords = e.get('coords');
setTempMarker({ coordinates: coords });
});
setMap(newMap);
});
}
}, []);
useEffect(() => {
if (map) {
map.geoObjects.removeAll();
markers.forEach((marker) => {
const placemark = new window.ymaps.Placemark(
marker.coordinates,
{
balloonContent: marker.title,
},
{
draggable: true,
}
);
placemark.events.add('contextmenu', () => {
if (window.confirm('Удалить эту метку?')) {
setMarkers((prevMarkers) =>
prevMarkers.filter((m) => m.id !== marker.id)
);
}
});
placemark.events.add('dragend', (e: any) => {
const newCoords = e.get('target').geometry.getCoordinates();
setMarkers((prevMarkers) =>
prevMarkers.map((m) =>
m.id === marker.id ? { ...m, coordinates: newCoords } : m
)
);
});
map.geoObjects.add(placemark);
});
if (tempMarker) {
const tempPlacemark = new window.ymaps.Placemark(tempMarker.coordinates, {}, { preset: 'islands#dotIcon' });
map.geoObjects.add(tempPlacemark);
}
}
}, [markers, map, tempMarker]);
useEffect(() => {
localStorage.setItem('markers', JSON.stringify(markers));
}, [markers]);
const handleAddMarker = () => {
const coords = newMarkerCoords.split(',').map(coord => parseFloat(coord.trim()));
if (coords.length === 2 && coords.every(coord => !isNaN(coord))) {
const newMarker = {
id: Date.now().toString(),
coordinates: coords,
title: newMarkerTitle.trim(),
};
setMarkers((prevMarkers) => [...prevMarkers, newMarker]);
setNewMarkerCoords('');
setNewMarkerTitle('');
} else {
window.alert('Please enter valid coordinates.');
}
};
return (
<div style={{ display: 'flex' }}>
<div
ref={mapRef}
style={{ width: '70%', height: '500px', marginRight: '20px' }}
></div>
<div style={{ width: '30%' }}>
<h2>Список меток</h2>
<ul>
{markers.map((marker) => (
<li key={marker.id}>
<input
type="text"
value={marker.title}
onChange={(e) => {
const newTitle = e.target.value;
setMarkers((prevMarkers) =>
prevMarkers.map((m) =>
m.id === marker.id ? { ...m, title: newTitle } : m
)
);
}}
/>
<button
onClick={() => {
map.setCenter(marker.coordinates, 14);
}}
>
Перейти
</button>
<button
onClick={() => {
if (window.confirm('Удалить эту метку?')) {
setMarkers((prevMarkers) =>
prevMarkers.filter((m) => m.id !== marker.id)
);
}
}}
>
Удалить
</button>
</li>
))}
</ul>
{tempMarker && (
<div>
<input
type="text"
placeholder="Введите название метки"
onKeyDown={(e) => {
if (e.key === 'Enter') {
const title = (e.target as HTMLInputElement).value;
if (title.trim()) {
const newMarker = {
id: Date.now().toString(),
coordinates: tempMarker.coordinates,
title,
};
setMarkers((prevMarkers) => [...prevMarkers, newMarker]);
setTempMarker(null);
}
}
}}
/>
<button onClick={() => setTempMarker(null)}>Отменить</button>
</div>
)}
<div>
<h3>Добавить метку</h3>
<input
type="text"
value={newMarkerCoords}
onChange={(e) => setNewMarkerCoords(e.target.value)}
placeholder="Введите координаты (lat, lon)"
/>
<input
type="text"
value={newMarkerTitle}
onChange={(e) => setNewMarkerTitle(e.target.value)}
placeholder="Введите название"
/>
<button onClick={handleAddMarker}>Создать метку</button>
</div>
</div>
</div>
);
};
export default MapComponent;
```
### Explanation:
- **State Management**: New state variables `newMarkerCoords` and `newMarkerTitle` are used to handle the form inputs for coordinates and title.
- **Form for Adding Markers**: A section to accept user input for coordinates and a title is added. Users must enter valid coordinates in the format `lat, lon`.
- **Add Marker Functionality**: When the form is submitted (via the "Создать метку" button), the inputs are validated and, if valid, a new marker is added to the map.