mirror of
https://github.com/HSLdevcom/digitransit-ui
synced 2025-09-21 05:02:45 +02:00
294 lines
9.3 KiB
JavaScript
294 lines
9.3 KiB
JavaScript
import moment from 'moment';
|
|
import cx from 'classnames';
|
|
import PropTypes from 'prop-types';
|
|
import React from 'react';
|
|
import { FormattedMessage, intlShape } from 'react-intl';
|
|
import Link from 'found/Link';
|
|
import Icon from './Icon';
|
|
import ItineraryMapAction from './ItineraryMapAction';
|
|
import ItineraryCircleLineWithIcon from './ItineraryCircleLineWithIcon';
|
|
import PlatformNumber from './PlatformNumber';
|
|
import ServiceAlertIcon from './ServiceAlertIcon';
|
|
import { getActiveAlertSeverityLevel } from '../util/alertUtils';
|
|
import { PREFIX_STOPS } from '../util/path';
|
|
import {
|
|
CityBikeNetworkType,
|
|
getCityBikeNetworkId,
|
|
getCityBikeNetworkConfig,
|
|
} from '../util/citybikes';
|
|
import { displayDistance } from '../util/geo-utils';
|
|
import { durationToString } from '../util/timeUtils';
|
|
import { splitStringToAddressAndPlace } from '../util/otpStrings';
|
|
import CityBikeLeg from './CityBikeLeg';
|
|
|
|
function WalkLeg(
|
|
{ children, focusAction, focusToLeg, index, leg, previousLeg },
|
|
{ config, intl },
|
|
) {
|
|
const distance = displayDistance(
|
|
parseInt(leg.mode !== 'WALK' ? 0 : leg.distance, 10),
|
|
config,
|
|
intl.formatNumber,
|
|
);
|
|
//
|
|
const duration = durationToString(
|
|
leg.mode !== 'WALK' ? 0 : leg.duration * 1000,
|
|
);
|
|
// If mode is not WALK, WalkLeg should get information from "to".
|
|
const toOrFrom = leg.mode !== 'WALK' ? 'to' : 'from';
|
|
const modeClassName = 'walk';
|
|
const fromMode = (leg[toOrFrom].stop && leg[toOrFrom].stop.vehicleMode) || '';
|
|
const isFirstLeg = i => i === 0;
|
|
const [address, place] = splitStringToAddressAndPlace(leg[toOrFrom].name);
|
|
|
|
const networkType = getCityBikeNetworkConfig(
|
|
getCityBikeNetworkId(
|
|
previousLeg &&
|
|
previousLeg.rentedBike &&
|
|
previousLeg[toOrFrom].bikeRentalStation &&
|
|
previousLeg[toOrFrom].bikeRentalStation.networks,
|
|
),
|
|
config,
|
|
).type;
|
|
|
|
const returnNotice =
|
|
previousLeg && previousLeg.rentedBike ? (
|
|
<FormattedMessage
|
|
id={
|
|
networkType === CityBikeNetworkType.Scooter
|
|
? 'return-scooter-to'
|
|
: 'return-cycle-to'
|
|
}
|
|
values={{ station: leg[toOrFrom] ? leg[toOrFrom].name : '' }}
|
|
defaultMessage="Return the bike to {station} station"
|
|
/>
|
|
) : null;
|
|
let appendClass;
|
|
const isScooter = networkType === CityBikeNetworkType.Scooter;
|
|
if (returnNotice) {
|
|
appendClass = 'return-citybike';
|
|
}
|
|
return (
|
|
<div key={index} className="row itinerary-row">
|
|
<span className="sr-only">
|
|
{returnNotice}
|
|
<FormattedMessage
|
|
id="itinerary-details.walk-leg"
|
|
values={{
|
|
time: moment(leg.startTime).format('HH:mm'),
|
|
to: intl.formatMessage({
|
|
id: `modes.to-${
|
|
leg.to.stop?.vehicleMode?.toLowerCase() || 'place'
|
|
}`,
|
|
defaultMessage: 'modes.to-stop',
|
|
}),
|
|
distance,
|
|
duration,
|
|
origin: leg[toOrFrom] ? leg[toOrFrom].name : '',
|
|
destination: leg.to ? leg.to.name : '',
|
|
}}
|
|
/>
|
|
</span>
|
|
<div className="small-2 columns itinerary-time-column" aria-hidden="true">
|
|
<div className="itinerary-time-column-time">
|
|
{moment(leg.mode === 'WALK' ? leg.startTime : leg.endTime).format(
|
|
'HH:mm',
|
|
)}
|
|
</div>
|
|
</div>
|
|
<ItineraryCircleLineWithIcon
|
|
appendClass={appendClass}
|
|
index={index}
|
|
modeClassName={modeClassName}
|
|
/>
|
|
<div
|
|
className={`small-9 columns itinerary-instruction-column ${leg.mode.toLowerCase()}`}
|
|
>
|
|
<span className="sr-only">
|
|
<FormattedMessage
|
|
id="itinerary-summary.show-on-map"
|
|
values={{ target: leg[toOrFrom].name || '' }}
|
|
/>
|
|
</span>
|
|
{isFirstLeg(index) ? (
|
|
<div className={cx('itinerary-leg-first-row', 'walk', 'first')}>
|
|
<div className="address-container">
|
|
<div className="address">
|
|
{address}
|
|
{leg[toOrFrom].stop && (
|
|
<Icon
|
|
img="icon-icon_arrow-collapse--right"
|
|
className="itinerary-arrow-icon"
|
|
color={config.colors.primary}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="place">{place}</div>
|
|
</div>
|
|
<ItineraryMapAction
|
|
target={leg[toOrFrom].name || ''}
|
|
focusAction={focusAction}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div
|
|
className={
|
|
returnNotice
|
|
? 'itinerary-leg-first-row-return-bike'
|
|
: 'itinerary-leg-first-row'
|
|
}
|
|
>
|
|
<div className="itinerary-leg-row">
|
|
{leg[toOrFrom].stop ? (
|
|
<Link
|
|
onClick={e => {
|
|
e.stopPropagation();
|
|
}}
|
|
to={`/${PREFIX_STOPS}/${leg[toOrFrom].stop.gtfsId}`}
|
|
>
|
|
{returnNotice || leg[toOrFrom].name}
|
|
{leg[toOrFrom].stop && (
|
|
<Icon
|
|
img="icon-icon_arrow-collapse--right"
|
|
className="itinerary-arrow-icon"
|
|
color={config.colors.primary}
|
|
/>
|
|
)}
|
|
<ServiceAlertIcon
|
|
className="inline-icon"
|
|
severityLevel={getActiveAlertSeverityLevel(
|
|
leg[toOrFrom].stop && leg[toOrFrom].stop.alerts,
|
|
leg.startTime / 1000,
|
|
)}
|
|
/>
|
|
</Link>
|
|
) : (
|
|
<div>
|
|
{returnNotice ? (
|
|
<CityBikeLeg
|
|
isScooter={isScooter}
|
|
stationName={leg[toOrFrom].name}
|
|
bikeRentalStation={leg[toOrFrom].bikeRentalStation}
|
|
returnBike
|
|
/>
|
|
) : (
|
|
leg[toOrFrom].name
|
|
)}
|
|
{leg[toOrFrom].stop && (
|
|
<Icon
|
|
img="icon-icon_arrow-collapse--right"
|
|
className="itinerary-arrow-icon"
|
|
color={config.colors.primary}
|
|
/>
|
|
)}
|
|
<ServiceAlertIcon
|
|
className="inline-icon"
|
|
severityLevel={getActiveAlertSeverityLevel(
|
|
leg[toOrFrom].stop && leg[toOrFrom].stop.alerts,
|
|
leg.startTime / 1000,
|
|
)}
|
|
/>
|
|
</div>
|
|
)}
|
|
<div className="stop-code-container">
|
|
{children}
|
|
{leg[toOrFrom].stop && (
|
|
<PlatformNumber
|
|
number={leg[toOrFrom].stop.platformCode}
|
|
short
|
|
isRailOrSubway={
|
|
fromMode === 'RAIL' || fromMode === 'SUBWAY'
|
|
}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{!returnNotice && (
|
|
<ItineraryMapAction
|
|
target={leg[toOrFrom].name || ''}
|
|
focusAction={focusAction}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="itinerary-leg-action">
|
|
<div className="itinerary-leg-action-content">
|
|
<FormattedMessage
|
|
id="walk-distance-duration"
|
|
values={{
|
|
distance: config.emphasizeDistance ? (
|
|
<b>{distance}</b>
|
|
) : (
|
|
distance
|
|
),
|
|
duration,
|
|
}}
|
|
defaultMessage="Walk {distance} ({duration})"
|
|
/>
|
|
<ItineraryMapAction
|
|
target=""
|
|
ariaLabelId="itinerary-summary-row.clickable-area-description"
|
|
focusAction={focusToLeg}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const walkLegShape = PropTypes.shape({
|
|
distance: PropTypes.number.isRequired,
|
|
duration: PropTypes.number.isRequired,
|
|
from: PropTypes.shape({
|
|
name: PropTypes.string.isRequired,
|
|
stop: PropTypes.shape({
|
|
alerts: PropTypes.array,
|
|
code: PropTypes.string,
|
|
gtfsId: PropTypes.string.isRequired,
|
|
platformCode: PropTypes.string,
|
|
vehicleMode: PropTypes.string,
|
|
}),
|
|
bikeRentalStation: PropTypes.shape({
|
|
networks: PropTypes.array,
|
|
}),
|
|
}).isRequired,
|
|
to: PropTypes.shape({
|
|
name: PropTypes.string.isRequired,
|
|
stop: PropTypes.shape({
|
|
alerts: PropTypes.array,
|
|
code: PropTypes.string,
|
|
gtfsId: PropTypes.string.isRequired,
|
|
platformCode: PropTypes.string,
|
|
vehicleMode: PropTypes.string,
|
|
}),
|
|
bikeRentalStation: PropTypes.shape({
|
|
networks: PropTypes.array,
|
|
}),
|
|
}).isRequired,
|
|
mode: PropTypes.string.isRequired,
|
|
rentedBike: PropTypes.bool,
|
|
startTime: PropTypes.number.isRequired,
|
|
endTime: PropTypes.number.isRequired,
|
|
});
|
|
|
|
WalkLeg.propTypes = {
|
|
children: PropTypes.node,
|
|
focusAction: PropTypes.func.isRequired,
|
|
index: PropTypes.number.isRequired,
|
|
leg: walkLegShape.isRequired,
|
|
previousLeg: walkLegShape,
|
|
focusToLeg: PropTypes.func.isRequired,
|
|
};
|
|
|
|
WalkLeg.defaultProps = {
|
|
previousLeg: undefined,
|
|
};
|
|
|
|
WalkLeg.contextTypes = {
|
|
config: PropTypes.object.isRequired,
|
|
intl: intlShape.isRequired,
|
|
};
|
|
|
|
export default WalkLeg;
|