diff --git a/app/component/itinerary/navigator/NaviBoardingInfo.js b/app/component/itinerary/navigator/NaviBoardingInfo.js new file mode 100644 index 0000000000..ab72c8aad9 --- /dev/null +++ b/app/component/itinerary/navigator/NaviBoardingInfo.js @@ -0,0 +1,55 @@ +import React from 'react'; +import cx from 'classnames'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import RouteNumberContainer from '../../RouteNumberContainer'; +import { routeShape } from '../../../util/shapes'; +import Icon from '../../Icon'; + +const NaviBoardingInfo = ({ + route, + mode, + headsign, + translationValues, + withExpandIcon, +}) => { + return ( +
+
+ {withExpandIcon && ( + + )} + +
{headsign}
+
+
+ +
+
+ ); +}; + +NaviBoardingInfo.propTypes = { + route: routeShape.isRequired, + mode: PropTypes.string.isRequired, + headsign: PropTypes.string.isRequired, + // eslint-disable-next-line react/forbid-prop-types + translationValues: PropTypes.object.isRequired, + withExpandIcon: PropTypes.bool, +}; +NaviBoardingInfo.defaultProps = { + withExpandIcon: false, +}; + +export default NaviBoardingInfo; diff --git a/app/component/itinerary/navigator/NaviCard.js b/app/component/itinerary/navigator/NaviCard.js index 9f4aed3cae..0059c20feb 100644 --- a/app/component/itinerary/navigator/NaviCard.js +++ b/app/component/itinerary/navigator/NaviCard.js @@ -108,7 +108,12 @@ export default function NaviCard(
{mainCardContent}
{cardExpanded && ( - + )}
); diff --git a/app/component/itinerary/navigator/NaviCardExtension.js b/app/component/itinerary/navigator/NaviCardExtension.js index f343f2bbbc..646a9d6c6c 100644 --- a/app/component/itinerary/navigator/NaviCardExtension.js +++ b/app/component/itinerary/navigator/NaviCardExtension.js @@ -8,15 +8,17 @@ import PlatformNumber from '../../PlatformNumber'; import { getZoneLabel, getHeadsignFromRouteLongName, + legTime, + legTimeStr, } from '../../../util/legUtils'; import ZoneIcon from '../../ZoneIcon'; import { legShape, configShape } from '../../../util/shapes'; -import { getDestinationProperties, LEGTYPE } from './NaviUtils'; +import { getDestinationProperties, LEGTYPE, withRealTime } from './NaviUtils'; import { getRouteMode } from '../../../util/modeUtils'; - import RouteNumberContainer from '../../RouteNumberContainer'; +import NaviBoardingInfo from './NaviBoardingInfo'; -const NaviCardExtension = ({ legType, leg, nextLeg }, { config }) => { +const NaviCardExtension = ({ legType, leg, nextLeg, time }, { config }) => { const { stop, name, rentalVehicle, vehicleParking, vehicleRentalStation } = leg ? leg.to : nextLeg.from; const { code, platformCode, zoneId, vehicleMode } = stop || {}; @@ -73,10 +75,10 @@ const NaviCardExtension = ({ legType, leg, nextLeg }, { config }) => { ); } - const stopInformation = () => { + const stopInformation = (expandIcon = false) => { return (
- + {expandIcon && } { }} />
+ {stopInformation(true)} + + ); + } + if (legType === LEGTYPE.MOVE && nextLeg?.transitLeg) { + const { headsign, route, start } = nextLeg; + const hs = headsign || nextLeg.trip?.tripHeadsign; + const remainingDuration = Math.max( + Math.ceil((legTime(start) - time) / 60000), + 0, + ); // ms to minutes, >= 0 + const rt = nextLeg.realtimeState === 'UPDATED'; + const values = { + duration: withRealTime(rt, remainingDuration), + legTime: withRealTime(rt, legTimeStr(start)), + }; + const routeMode = getRouteMode(route, config); + return ( +
{stopInformation()} +
+
); } + return (
- {stopInformation()} + {stopInformation(true)}
); }; - NaviCardExtension.propTypes = { leg: legShape, nextLeg: legShape, legType: PropTypes.string, + time: PropTypes.number.isRequired, }; NaviCardExtension.defaultProps = { diff --git a/app/component/itinerary/navigator/NaviInstructions.js b/app/component/itinerary/navigator/NaviInstructions.js index 5c3cb2e78b..88e1cff96c 100644 --- a/app/component/itinerary/navigator/NaviInstructions.js +++ b/app/component/itinerary/navigator/NaviInstructions.js @@ -5,19 +5,20 @@ import cx from 'classnames'; import { displayDistance } from '../../../util/geo-utils'; import { legShape, configShape } from '../../../util/shapes'; import { legDestination, legTimeStr, legTime } from '../../../util/legUtils'; -import RouteNumber from '../../RouteNumber'; -import { LEGTYPE, getLocalizedMode, getToLocalizedMode } from './NaviUtils'; +import { + LEGTYPE, + getLocalizedMode, + getToLocalizedMode, + withRealTime, +} from './NaviUtils'; import { durationToString } from '../../../util/timeUtils'; import { getRouteMode } from '../../../util/modeUtils'; +import NaviBoardingInfo from './NaviBoardingInfo'; export default function NaviInstructions( { leg, nextLeg, instructions, legType, time, position, tailLength }, { intl, config }, ) { - const withRealTime = (rt, children) => ( - {children} - ); - if (legType === LEGTYPE.MOVE) { return ( <> @@ -25,6 +26,10 @@ export default function NaviInstructions(   {legDestination(intl, leg, null, nextLeg)} +   + {nextLeg?.transitLeg && ( + + )}
@@ -34,7 +39,7 @@ export default function NaviInstructions( ); } - if (legType === LEGTYPE.WAIT && nextLeg.transitLeg) { + if (legType === LEGTYPE.WAIT && nextLeg?.transitLeg) { const { mode, headsign, route, start } = nextLeg; const hs = headsign || nextLeg.trip?.tripHeadsign; @@ -48,11 +53,6 @@ export default function NaviInstructions( legTime: withRealTime(rt, legTimeStr(start)), }; const routeMode = getRouteMode(route, config); - const iconColor = - config.colors.iconColors[`mode-${routeMode}`] || - route.color || - 'currentColor'; - return ( <>
@@ -62,25 +62,12 @@ export default function NaviInstructions( defaultMessage="Get on the {mode}" />
-
-
- -
{hs}
-
-
- -
-
+ ); } diff --git a/app/component/itinerary/navigator/NaviUtils.js b/app/component/itinerary/navigator/NaviUtils.js index a49ad47c69..041a30c29c 100644 --- a/app/component/itinerary/navigator/NaviUtils.js +++ b/app/component/itinerary/navigator/NaviUtils.js @@ -1,5 +1,6 @@ import distance from '@digitransit-search-util/digitransit-search-util-distance'; import React from 'react'; +import cx from 'classnames'; import { FormattedMessage } from 'react-intl'; import { ExtendedRouteTypes } from '../../../constants'; import { getFaresFromLegs, formatFare } from '../../../util/fareUtils'; @@ -252,7 +253,7 @@ export const getAdditionalMessages = ( ) { // Todo: multiple fares? const fares = getFaresFromLegs([nextLeg], config); - if (fares?.length) { + if (fares?.length && !fares[0].isUnknown) { msgs.push({ severity: 'INFO', content: ( @@ -650,3 +651,7 @@ export const LEGTYPE = { END: 'END', WAIT_IN_VEHICLE: 'WAIT_IN_VEHICLE', }; + +export const withRealTime = (rt, children) => ( + {children} +); diff --git a/app/component/itinerary/navigator/navigator.scss b/app/component/itinerary/navigator/navigator.scss index f4413b88de..e8173b0d29 100644 --- a/app/component/itinerary/navigator/navigator.scss +++ b/app/component/itinerary/navigator/navigator.scss @@ -39,6 +39,29 @@ $fixed-width-padding: 16px; } } +.bar { + border-radius: $border-radius; +} + +.route-number { + .vcenter-children { + .vehicle-number { + color: white; + margin-top: 5px; + } + + display: flex; + } +} + +.headsign { + margin-left: 10px; + font-size: $font-size-small; + max-width: 85%; + text-align: left; + align-content: center; +} + .bold { font-weight: $font-weight-medium; } @@ -46,6 +69,7 @@ $fixed-width-padding: 16px; .notification-header { font-size: $font-size-normal; font-weight: $font-weight-medium; + text-align: left; } .navi-card-container { @@ -66,6 +90,39 @@ $fixed-width-padding: 16px; } } +.route-info { + display: flex; + align-self: flex-start; + margin-bottom: var(--space-xcs); + + .route-number { + display: flex; + flex-direction: row; + margin-top: 4px; + + .vcenter-children { + .vehicle-number { + color: white; + margin-top: 5px; + } + + display: flex; + } + } + + .expand { + margin-left: var(--space-m); + display: flex; + + .icon { + margin-right: var(--space-s); + margin-top: 5px; + width: 16px; + height: 16px; + } + } +} + .navi-top-card { margin: 0 var(--space-s) 5px var(--space-s); border-radius: 8px; @@ -95,6 +152,32 @@ $fixed-width-padding: 16px; width: 100%; margin: var(--space-m) var(--space-l); + .boarding { + display: flex; + margin-top: 3px; + align-items: flex-start; + flex-direction: column; + + &.with-icon { + margin-left: var(--space-xl); + + .wait-duration { + margin-top: var(--space-xs); + margin-left: var(--space-l); + } + } + + .wait-duration { + margin-top: var(--space-xs); + } + + .icon { + margin-top: 2px; + height: 25px; + width: 25px; + } + } + .content { display: flex; flex-direction: row; @@ -126,52 +209,6 @@ $fixed-width-padding: 16px; width: 80%; text-align: left; } - - .wait-leg { - display: flex; - margin-top: 3px; - flex-direction: column; - align-items: flex-start; - - .route-info { - display: flex; - align-self: flex-start; - margin-bottom: var(--space-xcs); - } - - .wait-duration { - margin-top: var(--space-xs); - } - - .bar { - border-radius: $border-radius; - } - - .route-number { - .vcenter-children { - .vehicle-number { - color: white; - margin-top: 5px; - } - - display: flex; - } - } - - .headsign { - margin-left: 10px; - font-size: $font-size-small; - max-width: 85%; - text-align: left; - align-content: center; - } - - .icon { - margin-top: 2px; - height: 25px; - width: 25px; - } - } } .duration { @@ -191,6 +228,11 @@ $fixed-width-padding: 16px; margin-bottom: var(--space-s); margin-top: var(--space-m); + &.no-gap { + margin-top: 0; + margin-bottom: 0; + } + .extension-divider { height: 1px; background: #ddd; @@ -266,10 +308,35 @@ $fixed-width-padding: 16px; margin-left: var(--space-xl); } + .wait-leg { + display: flex; + margin-top: 3px; + flex-direction: column; + align-items: flex-start; + margin-left: var(--space-m); + + .wait-duration { + margin-top: var(--space-xs); + } + + .icon { + margin-top: 2px; + height: 25px; + width: 25px; + } + } + .icon-expand { margin-top: 5px; - width: var(--space-m); - height: var(--space-m); + width: 24px; + height: 24px; + } + + .icon-expand-small { + margin-top: 5px; + width: 16px; + height: 16px; + margin-right: var(--space-s); } .destination-icon { diff --git a/app/translations.js b/app/translations.js index f95d18220c..c1a05d8316 100644 --- a/app/translations.js +++ b/app/translations.js @@ -1338,12 +1338,13 @@ const translations = { 'navigation-transfer-problem': 'Transfer {route1} - {route2} is no longer possible', 'navigation-wait': 'Wait at the stop', - 'navileg-arrive-at': 'TODO_{duration} min päästä klo {legTime}', 'navileg-at-ferrypier': 'ferry pier', 'navileg-at-station': 'station', 'navileg-at-stop': 'stop', 'navileg-bicycle': 'Cycle to', 'navileg-car': 'Drive to', + 'navileg-departing-at': 'TODO_{duration} min päästä klo {legTime}', + 'navileg-hop-on': 'TODO_ja nouse kyytiin', 'navileg-in-transit': 'TODO_{mode}matka', 'navileg-in-transit-interline': 'TODO_Matka jatkuu {stopOrStation} {stop} samassa kulkuneuvossa {duration} päästä klo {legTime}', @@ -2627,12 +2628,13 @@ const translations = { 'navigation-ticket': 'Lippu', 'navigation-transfer-problem': 'Vaihto {route1} - {route2} ei enää onnistu', 'navigation-wait': 'Odota pysäkillä', - 'navileg-arrive-at': '{duration} min päästä klo {legTime}', 'navileg-at-ferrypier': 'lauttalaiturilla', 'navileg-at-station': 'asemalla', 'navileg-at-stop': 'pysäkillä', 'navileg-bicycle': 'Pyöräile', 'navileg-car': 'Aja', + 'navileg-departing-at': '{duration} min päästä klo {legTime}', + 'navileg-hop-on': 'ja nouse kyytiin', 'navileg-in-transit': '{mode}matka', 'navileg-in-transit-interline': 'Matka jatkuu {stopOrStation} {stop} samassa kulkuneuvossa {duration} min päästä klo {legTime}', @@ -5571,12 +5573,13 @@ const translations = { 'navigation-transfer-problem': 'TODO_Vaihto {route1} - {route2} ei enää onnistu', 'navigation-wait': 'Vänta på hållplatsen', - 'navileg-arrive-at': 'TODO_{duration} min päästä klo {legTime}', 'navileg-at-ferrypier': 'färjekajen', 'navileg-at-station': 'station', 'navileg-at-stop': 'hållplats', 'navileg-bicycle': 'Cycla till', 'navileg-car': 'Kör till', + 'navileg-departing-at': 'TODO_{duration} min päästä klo {legTime}', + 'navileg-hop-on': 'TODO_ja nouse kyytiin', 'navileg-in-transit': 'TODO_{mode}matka', 'navileg-in-transit-interline': 'TODO_Matka jatkuu {stopOrStation} {stop} samassa kulkuneuvossa {duration} päästä klo {legTime}',