import React, { useCallback } from "react"; import FromToLocationPicker from "@opentripplanner/from-to-location-picker"; import { RentalVehicle, VehicleRentalStation } from "@opentripplanner/types/otp2"; // eslint-disable-next-line prettier/prettier import type { Company, ConfiguredCompany, Location, Stop, StopEventHandler, } from "@opentripplanner/types"; import { FocusTrapWrapper } from "@opentripplanner/building-blocks"; import { flatten } from "flat"; import { FormattedMessage, useIntl } from "react-intl"; import { Styled } from "@opentripplanner/base-map"; import coreUtils from "@opentripplanner/core-utils"; import { makeDefaultGetEntityName, type StopIdAgencyMap } from "./util"; import { ViewStopButton } from "./styled"; // Load the default messages. import defaultEnglishMessages from "../i18n/en-US.yml"; // HACK: We should flatten the messages loaded above because // the YAML loaders behave differently between webpack and our version of jest: // - the yaml loader for webpack returns a nested object, // - the yaml loader for jest returns messages with flattened ids. export const defaultMessages: { [key: string]: string } = flatten(defaultEnglishMessages); export type Feed = { feedId: string; publisher: { name: string; }; }; const generateLocation = (entity: MapPopupEntity, name: string) => { // @ts-expect-error some of these values may be null, but that's ok const { lon: entityLon, lat: entityLat, x, y } = entity const lat = entityLat || y const lon = entityLon || x if (!lat || !lon) return null return { lat, lon, name }; } const StationHubDetails = ({ availableVehicles, availableSpaces }: { availableVehicles: number, availableSpaces: number }) => { return (
) } const StopDetails = ({ id, feedName, setViewedStop }: { id: string, feedName?: string, setViewedStop: () => void; }) => { return ( {id && } ) } type MapPopupEntity = Stop | VehicleRentalStation | RentalVehicle type Props = { closePopup?: (arg?: boolean) => void configCompanies?: ConfiguredCompany[]; entity: MapPopupEntity getEntityName?: (entity: MapPopupEntity, configCompanies: Company[], feedName?: string, includeParenthetical?: boolean) => string; getEntityPrefix?: (entity: MapPopupEntity) => JSX.Element feeds?: Feed[] setLocation?: ({ location, locationType }: { location: Location, locationType: string }) => void; setViewedStop?: StopEventHandler; }; const entityIsStop = (entity: MapPopupEntity): entity is Stop => "gtfsId" in entity; /** * Renders a map popup for a stop, scooter, or shared bike */ export function MapPopup({ closePopup = () => {}, configCompanies, entity, getEntityName, getEntityPrefix, setLocation, setViewedStop, feeds, }: Props): JSX.Element { const intl = useIntl() if (!entity) return <> const getNameFunc = getEntityName || makeDefaultGetEntityName(intl, defaultMessages); // Find the feed name using the logic from generateLabel in otp.ts let feedName: string | undefined; if (feeds && entity.id) { const feedId = entity.id.split(":")[0]; const feed = feeds.find(f => f.feedId === feedId); feedName = feed?.publisher?.name; } const name = getNameFunc(entity, configCompanies, feedName); const titleName = getNameFunc(entity, configCompanies, feedName, false); const stationNetwork = "rentalNetwork" in entity && (coreUtils.itinerary.getCompaniesLabelFromNetworks(entity?.rentalNetwork?.networkId, configCompanies) || entity?.rentalNetwork?.networkId); const vehiclesAvailable = "availableVehicles" in entity; const entityIsStationHub = vehiclesAvailable && entity?.availableVehicles !== undefined; const stopId = entityIsStop(entity) ? entity.code : ""; const id = `focus-${encodeURIComponent(entity.id).replace(/%/g, "")}-popup` return ( {getEntityPrefix && getEntityPrefix(entity)} {/* render dock info if it is available */} {entityIsStationHub && } {/* render stop viewer link if available */} {setViewedStop && entityIsStop(entity) && ( setViewedStop(entity as Stop), [entity])} /> )} {/* The "Set as [from/to]" ButtonGroup */} {setLocation && ( )} ); } export default MapPopup; export { type StopIdAgencyMap };