import { useState, useCallback } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { Slider } from '../ui/Slider'; import { IconButton } from '../ui/IconButton'; import { PillToggle } from '../ui/PillToggle'; import { DestinationDropdown } from '../ui/DestinationDropdown'; import InfoPopup from '../ui/InfoPopup'; import { TravelTimeInfoPopup } from '../ui/TravelTimeInfoPopup'; import { CloseIcon } from '../ui/icons/CloseIcon'; import { EyeIcon } from '../ui/icons/EyeIcon'; import { InfoIcon } from '../ui/icons/InfoIcon'; import { SliderLabels } from './filters/SliderLabels'; import { formatNumber } from '../../lib/format'; import type { FeatureMeta } from '../../types'; import { useTravelDestinations } from '../../hooks/useTravelDestinations'; import { MAX_TRAVEL_MINUTES, MODE_ICONS, useTranslatedModes, type TransportMode, } from '../../hooks/useTravelTime'; interface TravelTimeCardProps { mode: TransportMode; slug: string; label: string; timeRange: [number, number] | null; useBest: boolean; noChange: boolean; oneChange: boolean; noBuses: boolean; isPinned: boolean; isActive: boolean; dragValue: [number, number] | null; onTogglePin: () => void; onSetDestination: (slug: string, label: string, lat: number, lon: number) => void; onTimeRangeChange: (range: [number, number]) => void; onDragStart: (range: [number, number]) => void; onDragChange: (value: [number, number]) => void; onDragEnd: () => void; onToggleBest: () => void; onToggleNoChange: () => void; onToggleOneChange: () => void; onToggleNoBuses: () => void; onRemove: () => void; filterImpact?: number; destinationDropdownPortal?: boolean; } export function TravelTimeCard({ mode, slug, label, timeRange, useBest, noChange, oneChange, noBuses, isPinned, isActive, dragValue, onTogglePin, onSetDestination, onTimeRangeChange, onDragStart, onDragChange, onDragEnd, onToggleBest, onToggleNoChange, onToggleOneChange, onToggleNoBuses, onRemove, filterImpact, destinationDropdownPortal = true, }: TravelTimeCardProps) { const { t } = useTranslation(); const modes = useTranslatedModes(); const { destinations, loading: destinationsLoading } = useTravelDestinations(mode); const [showInfo, setShowInfo] = useState(false); const [showBestInfo, setShowBestInfo] = useState(false); const [showNoChangeInfo, setShowNoChangeInfo] = useState(false); const [showOneChangeInfo, setShowOneChangeInfo] = useState(false); const [showNoBusesInfo, setShowNoBusesInfo] = useState(false); const handleDestinationSelect = useCallback( (selectedSlug: string, selectedLabel: string, lat: number, lon: number) => { onSetDestination(selectedSlug, selectedLabel, lat, lon); }, [onSetDestination] ); const sliderMin = 0; const sliderMax = MAX_TRAVEL_MINUTES; const displayRange = isActive && dragValue ? dragValue : (timeRange ?? [sliderMin, sliderMax]); // Synthetic feature so the time labels reuse the shared SliderLabels (matching // every other filter card): editable, thumb-following, with the minute unit. const travelFeature: FeatureMeta = { name: 'travelTime', type: 'numeric', min: sliderMin, max: sliderMax, suffix: ` ${t('common.minute')}`, raw: true, }; const ModeIcon = MODE_ICONS[mode]; return (
{/* Header */}
{t('travel.travelTime', { mode: modes.label(mode) })}
setShowInfo(true)} title={t('filters.aboutData')} size="md"> {slug && ( )} onRemove()} title={t('travel.removeTravelTime')}>
{/* Destination */} onSetDestination('', '', 0, 0)} placeholder={t('travel.selectDestination')} portal={destinationDropdownPortal} /> {/* Transit-only toggles, shown when destination is set */} {slug && mode === 'transit' && (
setShowBestInfo(true)} title={t('travel.bestCaseTitle')}>
setShowNoChangeInfo(true)} title={t('travel.noChangeTitle')}>
setShowOneChangeInfo(true)} title={t('travel.oneChangeTitle')} >
setShowNoBusesInfo(true)} title={t('travel.noBusesTitle')}>
)} {showInfo && setShowInfo(false)} />} {showBestInfo && ( setShowBestInfo(false)}>

}} />

)} {showNoChangeInfo && ( setShowNoChangeInfo(false)}>

}} />

)} {showOneChangeInfo && ( setShowOneChangeInfo(false)}>

}} />

)} {showNoBusesInfo && ( setShowNoBusesInfo(false)}>

}} />

)} {/* Time range slider: only show when we have data */} {slug && (
{t('travel.maxTime')} onDragChange([min, max])} onPointerDown={() => onDragStart(displayRange)} onPointerUp={() => onDragEnd()} /> = sliderMax} raw showUnit feature={travelFeature} onValueChange={onTimeRangeChange} /> {filterImpact != null && filterImpact > 0 && (

{t('filters.filtersOut', { value: formatNumber(filterImpact) })}

)}
)}
); }