update: make marker clickable, add scroll-to-ivew behavior

This commit is contained in:
BunyodL 2025-05-02 14:35:33 +05:00
parent ed526338dd
commit bb5b331b06
3 changed files with 64 additions and 16 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -8,7 +8,7 @@ import {
List, List,
MapPin, MapPin,
} from 'lucide-react'; } from 'lucide-react';
import { useMemo, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { Stations } from '@/app/api-utlities/@types'; import { Stations } from '@/app/api-utlities/@types';
@ -46,7 +46,6 @@ interface FilterPanelProps {
setActiveFilterTab: (tab: string) => void; setActiveFilterTab: (tab: string) => void;
resetFilters: () => void; resetFilters: () => void;
resetCities: () => void; resetCities: () => void;
t: (key: string) => string;
} }
// Пропсы для панели списка станций // Пропсы для панели списка станций
@ -58,7 +57,6 @@ interface StationListPanelProps {
activeFilters: string[]; activeFilters: string[];
activeCities: string[]; activeCities: string[];
setSelectedStation: (id: number | null) => void; setSelectedStation: (id: number | null) => void;
t: (key: string) => string;
filterToFieldMap: { [key: string]: keyof Stations[number] }; filterToFieldMap: { [key: string]: keyof Stations[number] };
allFilters: string[]; allFilters: string[];
resetFilters: () => void; resetFilters: () => void;
@ -80,8 +78,9 @@ function FilterPanel({
setActiveFilterTab, setActiveFilterTab,
resetFilters, resetFilters,
resetCities, resetCities,
t,
}: FilterPanelProps) { }: FilterPanelProps) {
const { t } = useTextController();
return ( return (
<div <div
className={`absolute top-0 bottom-0 left-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${ className={`absolute top-0 bottom-0 left-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${
@ -207,12 +206,38 @@ function StationListPanel({
activeFilters, activeFilters,
activeCities, activeCities,
setSelectedStation, setSelectedStation,
t,
filterToFieldMap, filterToFieldMap,
allFilters, allFilters,
resetCities, resetCities,
resetFilters, resetFilters,
}: StationListPanelProps) { }: StationListPanelProps) {
const { t } = useTextController();
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!selectedStation || !scrollContainerRef.current) return;
const selectedStationItem = document.getElementById(
`station_${selectedStation}`,
);
if (selectedStationItem) {
const container = scrollContainerRef.current;
const itemRect = selectedStationItem.getBoundingClientRect();
const containerRect = container.getBoundingClientRect();
// Calculate the item's position relative to the container
const itemTopRelativeToContainer =
itemRect.top - containerRect.top + container.scrollTop - 10;
// Scroll the container to bring the item into view
container.scrollTo({
top: itemTopRelativeToContainer,
behavior: 'smooth',
});
}
}, [selectedStation]);
return ( return (
<div <div
className={`absolute top-0 right-0 bottom-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${ className={`absolute top-0 right-0 bottom-0 z-20 transform bg-white shadow-lg transition-transform duration-300 ${
@ -229,7 +254,11 @@ function StationListPanel({
<Badge>{stations.length}</Badge> <Badge>{stations.length}</Badge>
</div> </div>
</div> </div>
<div className='overflow-y-auto' style={{ height: 'calc(100% - 60px)' }}> <div
className='overflow-y-auto'
style={{ height: 'calc(100% - 60px)' }}
ref={scrollContainerRef}
>
{stations.length > 0 ? ( {stations.length > 0 ? (
<div className='p-2'> <div className='p-2'>
{stations.map((station) => { {stations.map((station) => {
@ -240,6 +269,7 @@ function StationListPanel({
return ( return (
<div <div
key={station.id} key={station.id}
id={`station_${station.id}`}
className={`mb-2 cursor-pointer rounded-lg border p-3 transition-colors ${ className={`mb-2 cursor-pointer rounded-lg border p-3 transition-colors ${
selectedStation === station.id selectedStation === station.id
? 'border-blue-500 bg-blue-50' ? 'border-blue-500 bg-blue-50'
@ -454,7 +484,6 @@ export default function GasStationMap({ stations }: GasStationMapProps) {
setActiveFilterTab={setActiveFilterTab} setActiveFilterTab={setActiveFilterTab}
resetFilters={resetFilters} resetFilters={resetFilters}
resetCities={resetCities} resetCities={resetCities}
t={t}
/> />
{/* Station list panel */} {/* Station list panel */}
@ -466,7 +495,6 @@ export default function GasStationMap({ stations }: GasStationMapProps) {
activeFilters={activeFilters} activeFilters={activeFilters}
activeCities={activeCities} activeCities={activeCities}
setSelectedStation={setSelectedStation} setSelectedStation={setSelectedStation}
t={t}
filterToFieldMap={filterToFieldMap} filterToFieldMap={filterToFieldMap}
allFilters={allFilters} allFilters={allFilters}
resetFilters={resetFilters} resetFilters={resetFilters}
@ -475,7 +503,11 @@ export default function GasStationMap({ stations }: GasStationMapProps) {
{/* Map */} {/* Map */}
<div className='h-full w-full'> <div className='h-full w-full'>
<YandexMap points={points} /> <YandexMap
points={points}
selectedStation={selectedStation}
setSelectedStation={setSelectedStation}
/>
</div> </div>
{/* Control buttons */} {/* Control buttons */}

View File

@ -1,17 +1,21 @@
'use client';
import { Map, Placemark, YMaps } from '@pbe/react-yandex-maps'; import { Map, Placemark, YMaps } from '@pbe/react-yandex-maps';
import React from 'react'; import React, { Dispatch, SetStateAction } from 'react';
import { Point } from '../model'; import { Point } from '../model';
type YandexMapProps = { type YandexMapProps = {
points: Point[]; points: Point[];
selectedStation: number | null;
setSelectedStation: Dispatch<SetStateAction<number | null>>;
}; };
const mapCenter = [55.751574, 37.573856]; const mapCenter = [55.751574, 37.573856];
export const YandexMap = ({ points }: YandexMapProps) => { export const YandexMap = ({
points,
selectedStation,
setSelectedStation,
}: YandexMapProps) => {
return ( return (
<YMaps <YMaps
query={{ query={{
@ -22,7 +26,7 @@ export const YandexMap = ({ points }: YandexMapProps) => {
> >
<Map <Map
defaultState={{ defaultState={{
center: points[0].coordinates || mapCenter, center: points[0]?.coordinates || mapCenter,
zoom: 11, zoom: 11,
behaviors: ['drag', 'multiTouch', 'dblClickZoom', 'scrollZoom'], behaviors: ['drag', 'multiTouch', 'dblClickZoom', 'scrollZoom'],
}} }}
@ -41,10 +45,22 @@ export const YandexMap = ({ points }: YandexMapProps) => {
geometry={point.coordinates} geometry={point.coordinates}
options={{ options={{
iconLayout: 'default#image', iconLayout: 'default#image',
iconImageHref: '/map/oriyo-marker.png', iconImageHref:
iconImageSize: [64, 64], !selectedStation || selectedStation === point.id
? '/map/oriyo-marker.png'
: '/map/oriyo-inactive-marker.png',
iconImageSize: selectedStation === point.id ? [70, 70] : [64, 64],
iconImageOffset: [-24, -36], iconImageOffset: [-24, -36],
}} }}
onClick={() =>
setSelectedStation(() => {
if (selectedStation !== null && selectedStation === point.id) {
return null;
}
return point.id;
})
}
/> />
))} ))}
</Map> </Map>