diff --git a/__tests__/reducers/__snapshots__/create-otp-reducer.js.snap b/__tests__/reducers/__snapshots__/create-otp-reducer.js.snap index 4bedf264f..36a8543f2 100644 --- a/__tests__/reducers/__snapshots__/create-otp-reducer.js.snap +++ b/__tests__/reducers/__snapshots__/create-otp-reducer.js.snap @@ -8,13 +8,13 @@ Object { "debouncePlanTimeMs": 0, "homeTimezone": "America/Los_Angeles", "language": Object {}, - "operators": Array [], "realtimeEffectsDisplayThreshold": 120, "routingTypes": Array [], "stopViewer": Object { "numberOfDepartures": 3, "timeRange": 345600, }, + "transitOperators": Array [], }, "currentQuery": Object { "bikeSpeed": 3.58, diff --git a/lib/components/app/print-layout.js b/lib/components/app/print-layout.js index 231c479ae..2fda1815f 100644 --- a/lib/components/app/print-layout.js +++ b/lib/components/app/print-layout.js @@ -1,8 +1,5 @@ -import BaseMap from '@opentripplanner/base-map' -import EndpointsOverlay from '@opentripplanner/endpoints-overlay' import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' import PrintableItinerary from '@opentripplanner/printable-itinerary' -import TransitiveOverlay from '@opentripplanner/transitive-overlay' import PropTypes from 'prop-types' import React, { Component } from 'react' import { Button } from 'react-bootstrap' @@ -10,8 +7,9 @@ import { connect } from 'react-redux' import { parseUrlQueryString } from '../../actions/form' import { routingQuery } from '../../actions/api' -import { getActiveItinerary } from '../../util/state' +import DefaultMap from '../map/default-map' import TripDetails from '../narrative/connected-trip-details' +import { getActiveItinerary } from '../../util/state' class PrintLayout extends Component { static propTypes = { @@ -91,10 +89,7 @@ class PrintLayout extends Component { {/* The map, if visible */} {this.state.mapVisible &&
- - - - +
} diff --git a/lib/components/narrative/default/access-leg.js b/lib/components/narrative/default/access-leg.js index 274282544..9a4258581 100644 --- a/lib/components/narrative/default/access-leg.js +++ b/lib/components/narrative/default/access-leg.js @@ -1,10 +1,9 @@ -import React, {Component} from 'react' +import { humanizeDistanceString } from '@opentripplanner/humanize-distance' import PropTypes from 'prop-types' +import React, {Component} from 'react' import Icon from '../icon' - import LegDiagramPreview from '../leg-diagram-preview' -import { distanceString } from '../../../util/distance' import { getStepInstructions } from '../../../util/itinerary' import { formatDuration } from '../../../util/time' @@ -50,7 +49,7 @@ export default class AccessLeg extends Component { {' '} {formatDuration(leg.duration)} {' '} - ({distanceString(leg.distance)}) + ({humanizeDistanceString(leg.distance)}) {active &&
@@ -62,7 +61,7 @@ export default class AccessLeg extends Component { key={stepIndex} className={`step ${stepIsActive ? 'active' : ''}`} onClick={(e) => this._onStepClick(e, step, stepIndex)}> - {distanceString(step.distance)} + {humanizeDistanceString(step.distance)} {getStepInstructions(step)} ) diff --git a/lib/components/narrative/line-itin/access-leg-body.js b/lib/components/narrative/line-itin/access-leg-body.js deleted file mode 100644 index 698651796..000000000 --- a/lib/components/narrative/line-itin/access-leg-body.js +++ /dev/null @@ -1,200 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { VelocityTransitionGroup } from 'velocity-react' -import currencyFormatter from 'currency-formatter' - -import LegDiagramPreview from '../leg-diagram-preview' - -import { distanceString } from '../../../util/distance' -import { - getLegModeLabel, - getLegIcon, - getPlaceName, - getStepDirection, - getStepStreetName -} from '../../../util/itinerary' -import { formatDuration, formatTime } from '../../../util/time' -import { isMobile } from '../../../util/ui' - -import DirectionIcon from '../../icons/direction-icon' - -/** - * Component for access (e.g. walk/bike/etc.) leg in narrative itinerary. This - * particular component is used in the line-itin (i.e., trimet-mod-otp) version - * of the narrative itinerary. - */ -export default class AccessLegBody extends Component { - static propTypes = { - leg: PropTypes.object, - routingType: PropTypes.string - } - - constructor (props) { - super(props) - this.state = { expanded: false } - } - - _onStepsHeaderClick = () => { - this.setState({ expanded: !this.state.expanded }) - } - - _onSummaryClick = () => { - this.props.setActiveLeg(this.props.legIndex, this.props.leg) - } - - render () { - const { config, customIcons, followsTransit, leg, timeOptions } = this.props - - if (leg.mode === 'CAR' && leg.hailedCar) { - return ( - - ) - } - - return ( -
- - -
- {formatDuration(leg.duration)} - {leg.steps && } -
- - {this.props.routingType === 'ITINERARY' && } - - {this.state.expanded && } - -
- ) - } -} - -class TNCLeg extends Component { - render () { - // TODO: ensure that client ID fields are populated - const { - config, - LYFT_CLIENT_ID, - UBER_CLIENT_ID, - customIcons, - followsTransit, - leg, - timeOptions - } = this.props - const universalLinks = { - 'UBER': `https://m.uber.com/${isMobile() ? 'ul/' : ''}?client_id=${UBER_CLIENT_ID}&action=setPickup&pickup[latitude]=${leg.from.lat}&pickup[longitude]=${leg.from.lon}&pickup[formatted_address]=${encodeURI(leg.from.name)}&dropoff[latitude]=${leg.to.lat}&dropoff[longitude]=${leg.to.lon}&dropoff[formatted_address]=${encodeURI(leg.to.name)}`, - 'LYFT': `https://lyft.com/ride?id=lyft&partner=${LYFT_CLIENT_ID}&pickup[latitude]=${leg.from.lat}&pickup[longitude]=${leg.from.lon}&destination[latitude]=${leg.to.lat}&destination[longitude]=${leg.to.lon}` - } - const { tncData } = leg - - if (!tncData || !tncData.estimatedArrival) return null - return ( -
-
- Wait {!followsTransit && {Math.round(tncData.estimatedArrival / 60)} minutes }for {tncData.displayName} pickup -
- -
- {/* The icon/summary row */} - - - {/* The "Book Ride" button */} -
- - Book Ride - - {followsTransit &&
} - {followsTransit && ( -
-
-
- Wait until {formatTime(leg.startTime - tncData.estimatedArrival * 1000, timeOptions)} to book -
-
-
- )} -
- - {/* The estimated travel time */} -
- Estimated travel time: {formatDuration(leg.duration)} (does not account for traffic) -
- - {/* The estimated travel cost */} - {tncData.minCost && -

Estimated cost: { - `${currencyFormatter.format(tncData.minCost, { code: tncData.currency })} - ${currencyFormatter.format(tncData.maxCost, { code: tncData.currency })}` - }

- } -
-
- ) - } -} - -class AccessLegSummary extends Component { - render () { - const { config, customIcons, leg } = this.props - return ( -
- {/* Mode-specific icon */} -
{getLegIcon(leg, customIcons)}
- - {/* Leg description, e.g. "Walk 0.5 mi to..." */} -
- {getLegModeLabel(leg)} - {' '} - {leg.distance > 0 && {distanceString(leg.distance)}} - {` to ${getPlaceName(leg.to, config.companies)}`} -
-
- ) - } -} - -class AccessLegSteps extends Component { - static propTypes = { - steps: PropTypes.array - } - - render () { - return ( -
- {this.props.steps.map((step, k) => { - return
-
- -
- -
- {getStepDirection(step)} - {step.relativeDirection === 'ELEVATOR' ? ' to ' : ' on '} - - {getStepStreetName(step)} - -
-
- })} -
- ) - } -} diff --git a/lib/components/narrative/line-itin/connected-itinerary-body.js b/lib/components/narrative/line-itin/connected-itinerary-body.js new file mode 100644 index 000000000..0c8774da3 --- /dev/null +++ b/lib/components/narrative/line-itin/connected-itinerary-body.js @@ -0,0 +1,85 @@ +import isEqual from 'lodash.isequal' +import TriMetLegIcon from '@opentripplanner/icons/lib/trimet-leg-icon' +import TransitLegSummary from '@opentripplanner/itinerary-body/lib/defaults/transit-leg-summary' +import ItineraryBody from '@opentripplanner/itinerary-body/lib/otp-react-redux/itinerary-body' +import LineColumnContent from '@opentripplanner/itinerary-body/lib/otp-react-redux/line-column-content' +import PlaceName from '@opentripplanner/itinerary-body/lib/otp-react-redux/place-name' +import RouteDescription from '@opentripplanner/itinerary-body/lib/otp-react-redux/route-description' +import React, { Component } from 'react' +import { connect } from 'react-redux' +import styled from 'styled-components' + +import { showLegDiagram } from '../../../actions/map' +import { setActiveLeg } from '../../../actions/narrative' +import { setViewedTrip } from '../../../actions/ui' +import TransitLegSubheader from './connected-transit-leg-subheader' +import TripDetails from '../connected-trip-details' +import TripTools from '../trip-tools' + +const noop = () => {} + +const ItineraryBodyContainer = styled.div` + padding: 20px 0px; +` + +class ConnectedItineraryBody extends Component { + /** avoid rerendering if the itinerary to display hasn't changed */ + shouldComponentUpdate (nextProps, nextState) { + return !isEqual(this.props.itinerary, nextProps.itinerary) + } + + render () { + const { + config, + diagramVisible, + itinerary, + setActiveLeg, + setViewedTrip, + showLegDiagram + } = this.props + + return ( + + + + + + ) + } +} + +const mapStateToProps = (state, ownProps) => { + return { + config: state.otp.config, + diagramVisible: state.otp.ui.diagramLeg + } +} + +const mapDispatchToProps = { + setActiveLeg, + setViewedTrip, + showLegDiagram +} + +export default connect(mapStateToProps, mapDispatchToProps)( + ConnectedItineraryBody +) diff --git a/lib/components/narrative/line-itin/connected-transit-leg-subheader.js b/lib/components/narrative/line-itin/connected-transit-leg-subheader.js new file mode 100644 index 000000000..130374022 --- /dev/null +++ b/lib/components/narrative/line-itin/connected-transit-leg-subheader.js @@ -0,0 +1,33 @@ +import TransitLegSubheader from '@opentripplanner/itinerary-body/lib/otp-react-redux/transit-leg-subheader' +import React, { Component } from 'react' +import { connect } from 'react-redux' + +import { setMainPanelContent, setViewedStop } from '../../../actions/ui' + +class ConnectedTransitLegSubheader extends Component { + onClick = (payload) => { + const { setMainPanelContent, setViewedStop } = this.props + setMainPanelContent(null) + setViewedStop(payload) + } + + render () { + const { languageConfig, leg } = this.props + return ( + + ) + } +} + +const mapDispatchToProps = { + setMainPanelContent, + setViewedStop +} + +export default connect(null, mapDispatchToProps)( + ConnectedTransitLegSubheader +) diff --git a/lib/components/narrative/line-itin/itin-body.js b/lib/components/narrative/line-itin/itin-body.js deleted file mode 100644 index 713e02b7b..000000000 --- a/lib/components/narrative/line-itin/itin-body.js +++ /dev/null @@ -1,65 +0,0 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import isEqual from 'lodash.isequal' - -import TripDetails from '../connected-trip-details' -import TripTools from '../trip-tools' - -import PlaceRow from './place-row' - -export default class ItineraryBody extends Component { - static propTypes = { - companies: PropTypes.string, - itinerary: PropTypes.object, - routingType: PropTypes.string - } - - constructor (props) { - super(props) - this.rowKey = 0 - } - - shouldComponentUpdate (nextProps, nextState) { - return !isEqual(this.props.companies, nextProps.companies) || - !isEqual(this.props.itinerary, nextProps.itinerary) - } - - render () { - const { itinerary, setActiveLeg, timeOptions } = this.props - - const rows = [] - let followsTransit = false - itinerary.legs.forEach((leg, i) => { - // Create a row containing this leg's start place and leg traversal details - rows.push( - - ) - // If this is the last leg, create a special PlaceRow for the destination only - if (i === itinerary.legs.length - 1) { - rows.push( - ) - } - if (leg.transitLeg) followsTransit = true - }) - - return ( -
- {rows} - - -
- ) - } -} diff --git a/lib/components/narrative/line-itin/itin-summary.js b/lib/components/narrative/line-itin/itin-summary.js index e8965fe35..2e74575a7 100644 --- a/lib/components/narrative/line-itin/itin-summary.js +++ b/lib/components/narrative/line-itin/itin-summary.js @@ -1,12 +1,82 @@ -import React, { Component } from 'react' +import coreUtils from '@opentripplanner/core-utils' import PropTypes from 'prop-types' - -import { calculateFares, calculatePhysicalActivity, getLegIcon, isTransit } from '../../../util/itinerary' +import React, { Component } from 'react' +import styled from 'styled-components' + +import { + calculateFares, + calculatePhysicalActivity, + getLegIcon, + isTransit +} from '../../../util/itinerary' import { formatDuration, formatTime } from '../../../util/time' // TODO: make this a prop const defaultRouteColor = '#008' +const Container = styled.div` + display: ${() => coreUtils.ui.isMobile() ? 'table' : 'none'}; + height: 60px; + margin-bottom: 15px; + padding-right: 5px; + width: 100%; +` + +const Detail = styled.div` + color: #999999; + font-size: 13px; +` + +const Details = styled.div` + display: table-cell; + vertical-align: top; +` + +const Header = styled.div` + font-size: 18px; + font-weight: bold; + margin-top: -3px; +` + +const ModeIcon = styled.div` + height: 30px; + width: 30px; +` + +const NonTransitSpacer = styled.div` + height: 30px; + overflow: hidden +` + +const RoutePreivew = styled.div` + display: inline-block; + margin-left: 8px; + vertical-align: top; +` + +const Routes = styled.div` + display: table-cell; + text-align: right; +` + +const ShortName = styled.div` + background-color: ${props => getRouteColorForBadge(props.leg)}; + border-radius: 15px; + border: 2px solid white; + box-shadow: 0 0 0.5em #000; + color: white; + font-size: 15px; + font-weight: 500; + height: 30px; + margin-top: 6px; + overflow: hidden; + padding-top: 4px; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + width: 30px; +` + export default class ItinerarySummary extends Component { static propTypes = { itinerary: PropTypes.object @@ -31,52 +101,54 @@ export default class ItinerarySummary extends Component { const { caloriesBurned } = calculatePhysicalActivity(itinerary) return ( -
-
+ +
{/* Travel time in hrs/mins */} -
{formatDuration(itinerary.duration)}
+
{formatDuration(itinerary.duration)}
{/* Duration as time range */} -
+ {formatTime(itinerary.startTime, timeOptions)} - {formatTime(itinerary.endTime, timeOptions)} -
+ {/* Fare / Calories */} -
+ {minTotalFare > 0 && {centsToString(minTotalFare)} {minTotalFare !== maxTotalFare && - {centsToString(maxTotalFare)}} } {Math.round(caloriesBurned)} Cals -
+ {/* Number of transfers, if applicable */} {itinerary.transfers > 0 && ( -
+ {itinerary.transfers} transfer{itinerary.transfers > 1 ? 's' : ''} -
+ )} -
-
+ + {itinerary.legs.filter(leg => { return !(leg.mode === 'WALK' && itinerary.transitTime > 0) }).map((leg, k) => { - return
-
{getLegIcon(leg, customIcons)}
- {isTransit(leg.mode) - ? ( -
- {getRouteNameForBadge(leg)} -
- ) - : (
) - } -
+ return ( + + {getLegIcon(leg, customIcons)} + {isTransit(leg.mode) + ? ( + + {getRouteNameForBadge(leg)} + + ) + : () + } + + ) })} -
-
+ + ) } } diff --git a/lib/components/narrative/line-itin/itinerary.css b/lib/components/narrative/line-itin/itinerary.css deleted file mode 100644 index 89df3acf0..000000000 --- a/lib/components/narrative/line-itin/itinerary.css +++ /dev/null @@ -1,375 +0,0 @@ -.otp .options.profile .itin-body .place-row { - margin-left: 55px; -} - -.otp .line-itin { - margin-bottom: 20px; -} - -/* Itinerary summary */ - -.otp .line-itin .itin-summary { - padding-right: 5px; - height: 60px; - display: table; - width: 100%; - margin-bottom: 15px; -} - -.otp .desktop-narrative-container .options.itinerary .line-itin .itin-summary { - display: none; -} - -.otp .line-itin .itin-summary .details { - display: table-cell; - vertical-align: top; -} - -.otp .line-itin .itin-summary .header { - font-weight: bold; - font-size: 18px; - margin-top: -3px; -} - -.otp .line-itin .itin-summary .detail { - font-size: 13px; - color: #999999; -} - -.otp .line-itin .itin-summary .routes { - display: table-cell; - text-align: right; -} - -.otp .line-itin .itin-summary .routes .route-preview { - display: inline-block; - margin-left: 8px; - vertical-align: top; -} - -.otp .line-itin .itin-summary .routes .route-preview .mode-icon { - height: 30px; - width: 30px; -} - -.otp .line-itin .itin-summary .routes .route-preview .short-name { - color: white; - font-weight: 500; - text-align: center; - margin-top: 6px; - font-size: 15px; - padding-top: 2px; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - width: 30px; - height: 30px; - border-radius: 15px; - border: 2px solid white; - box-shadow: 0 0 0.5em #000; -} - -/* Itinerary main body */ - -.otp .line-itin .itin-body { - padding: 20px 0px; -} - -.otp .line-itin .place-row { - display: table; - width: 100%; -} - - -/* Departure/arrival time (1st column in table) */ - -.otp .line-itin .time { - display: table-cell; - width: 60px; - font-size: 14px; - color: #999999; - text-align: right; - padding-right: 4px; - padding-top: 1px; - vertical-align: top; -} - -/* The place icon and line itself (2nd column in table) */ -.otp .line-itin .line-container { - position: relative; - display: table-cell; - width: 20px; - max-width: 20px; -} - -.otp .line-itin .place-icon-group { - position: absolute; - font-size: 18px; - left: -8px; - top: -7px; - z-index: 20; -} - -.otp .line-itin .leg-line { - position: absolute; - top: 11px; - bottom: -11px; - z-index: 10; -} - -// Internet explorer specific media query to apply the below styling to fix -// rendering issues with table cell display with undefined heights. -/*@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { - .otp .line-itin .line-container { - overflow: hidden; // hack for IE to render table cell correctly. - } - - .otp .line-itin .leg-line { - height: 1000px; // hack for IE to render table cell correctly. - } -}*/ - -.otp .line-itin .leg-line-walk { - left: 6px; - right: 6px; - background: radial-gradient(ellipse at center, #87cefa 40%, transparent 10%); - background-size: 12px 12px; - background-repeat: repeat-y; - background-position: center -5px; -} - -.otp .line-itin .leg-line-bicycle { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - red, - red 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-car { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - grey, - grey 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-micromobility { - left: 7.5px; - right: 7.5px; - background: repeating-linear-gradient( - 0deg, - #f5a729, - #f5a729 8px, - white 8px, - white 12.5px - ); -} - -.otp .line-itin .leg-line-transit { - left: 5px; - right: 5px; - background-color: gray; -} - -/* Place/Leg details (3rd column in table) */ - -.otp .line-itin .place-details { - font-size: 13px; - display: table-cell; - padding-top: 1px; -} - -.otp .line-itin .place-name { - font-size: 18px; - line-height: 20px; - padding-left: 4px; - font-weight: 500; - color: black; -} - -.otp .line-itin .place-subheader { - font-size: 12px; - padding-left: 4px; - padding-top: 1px; - font-weight: 300; - color: gray; -} - -.otp .line-itin .interline-dot { - position: relative; - float: left; - margin-left: -13.5px; - z-index: 25; - color: #fff; -} - -.otp .line-itin .interline-name { - font-size: 14px; - font-weight: 400; - line-height: 16px; -} - -/* Leg body general */ - -.otp .line-itin .leg-body { - padding: 12px 0px 18px 4px; - font-size: 13px; - color: #999999; -} - -.otp .line-itin .summary { - cursor: pointer; -} - -.otp .line-itin .leg-body .icon { - height: 24px; - width: 24px; - float: left; - margin-right: 6px; -} - -.otp .line-itin .leg-body .leg-description { - display: table; -} - -.otp .line-itin .leg-body .leg-description > div { - display: table-cell; - vertical-align: middle; -} - -/* Leg steps (for turn-by-turn) */ - -.otp .line-itin .leg-body .steps-header { - font-size: 13px; - margin-top: 10px; - color: #999999; - font-style: normal; - display: inline-block; -} - -.otp .line-itin .leg-body .step-row { - font-size: 13px; - margin-top: 8px; - color: #999999; - font-style: normal; -} - -/* Transit leg details */ - -.otp .line-itin .leg-body .route-name { - color: #999999; - margin-top: 5px; -} - -.otp .line-itin .leg-body .route-short-name { - display: inline-block; - background-color: #0f6aac; - padding-top: 1px; - color: white; - font-weight: 500; - font-size: 14px; - margin-right: 6px; - text-align: center; - width: 24px; - height: 24px; - border-radius: 12px; - border: 1px solid white; - box-shadow: 0 0 0.25em #000; - margin-right: 8px; -} - -.otp .line-itin .leg-body .route-long-name { - font-size: 13px; - line-height: 16px; - font-weight: 500; -} - -.otp .line-itin .leg-body .transit-leg-details { - margin-top: 5px; -} - -.otp .line-itin .leg-body .agency-info { - margin-top: 5px; - -} - -.otp .line-itin .leg-body .transit-leg-details .header { - cursor: pointer; - color: #999999; - font-size: 13px; -} - -/* Intermediate stops */ - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-row { - z-index: 30; - position: relative; -} - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-marker { - float: left; - margin-left: -17px; - color: white; -} - -.otp .line-itin .leg-body .transit-leg-details .intermediate-stops .stop-name { - color: #999999; - font-size: 14px; - margin-top: 3px; -} - -/* Transit alerts */ - -.otp .line-itin .leg-body .transit-alerts-toggle { - display: inline-block; - margin-top: 8px; - color: #D14727; - font-weight: 400; - cursor: pointer; -} - -.otp .line-itin .leg-body .transit-alerts { - margin-top: 3px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert { - margin-top: 5px; - background-color: #eee; - padding: 8px; - color: black; - border-radius: 4px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-icon { - float: left; - font-size: 18px; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-header { - font-size: 14px; - margin-left: 30px; - font-weight: 600; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .alert-body { - font-size: 12px; - margin-left: 30px; - /* white space pre-wrap is required to render line breaks correctly. */ - white-space: pre-wrap; -} - -.otp .line-itin .leg-body .transit-alerts .transit-alert .effective-date { - margin-top: 5px; - margin-left: 30px; - font-size: 12px; - font-style: italic; -} diff --git a/lib/components/narrative/line-itin/line-itinerary.js b/lib/components/narrative/line-itin/line-itinerary.js index 6c89ddae0..989d726c5 100644 --- a/lib/components/narrative/line-itin/line-itinerary.js +++ b/lib/components/narrative/line-itin/line-itinerary.js @@ -1,11 +1,15 @@ import React from 'react' +import styled from 'styled-components' +import ItineraryBody from './connected-itinerary-body' +import ItinerarySummary from './itin-summary' import NarrativeItinerary from '../narrative-itinerary' import SimpleRealtimeAnnotation from '../simple-realtime-annotation' import { getLegModeLabel, getTimeZoneOffset, isTransit } from '../../../util/itinerary' -import ItinerarySummary from './itin-summary' -import ItineraryBody from './itin-body' +export const LineItineraryContainer = styled.div` + margin-bottom: 20px; +` export default class LineItinerary extends NarrativeItinerary { _headerText () { @@ -59,7 +63,7 @@ export default class LineItinerary extends NarrativeItinerary { } return ( -
+ {showRealtimeAnnotation && } {active || expanded - ? + ? : null} {itineraryFooter} -
+ ) } } diff --git a/lib/components/narrative/line-itin/place-row.js b/lib/components/narrative/line-itin/place-row.js deleted file mode 100644 index 24090a6d2..000000000 --- a/lib/components/narrative/line-itin/place-row.js +++ /dev/null @@ -1,216 +0,0 @@ -import LocationIcon from '@opentripplanner/location-icon' -import React, { Component, PureComponent } from 'react' -import { connect } from 'react-redux' - -import ViewStopButton from '../../viewers/view-stop-button' -import { - getCompaniesLabelFromNetworks, - getModeForPlace, - getPlaceName -} from '../../../util/itinerary' -import { formatTime } from '../../../util/time' - -import TransitLegBody from './transit-leg-body' -import AccessLegBody from './access-leg-body' - -// TODO: make this a prop -const defaultRouteColor = '#008' - -class PlaceRow extends Component { - _createLegLine (leg) { - switch (leg.mode) { - case 'WALK': return
- case 'BICYCLE': - case 'BICYCLE_RENT': - return
- case 'CAR': return
- case 'MICROMOBILITY': - case 'MICROMOBILITY_RENT': - return
- default: - return
- } - } - - /* eslint-disable complexity */ - render () { - const { config, customIcons, leg, legIndex, place, time, timeOptions, followsTransit } = this.props - const stackIcon = (name, color, size) => - let icon - if (!leg) { // This is the itinerary destination - icon = ( - - {stackIcon('circle', 'white', 26)} - - - ) - } else if (legIndex === 0) { // The is the origin - icon = ( - - {stackIcon('circle', 'white', 26)} - - - ) - } else { // This is an intermediate place - icon = ( - - {stackIcon('circle', 'white', 22)} - {stackIcon('circle-o', 'black', 22)} - - ) - } - // NOTE: Previously there was a check for itineraries that changed vehicles - // at a single stop, which would render the stop place the same as the - // interline stop. However, this prevents the user from being able to click - // on the stop viewer in this case, which they may want to do in order to - // check the real-time arrival information for the next leg of their journey. - const interline = leg && leg.interlineWithPreviousLeg - return ( -
-
- {time && formatTime(time, timeOptions)} -
-
- {leg && this._createLegLine(leg) } -
{!interline && icon}
-
-
- {/* Dot separating interlined segments, if applicable */} - {interline &&
} - - {/* The place name */} -
- {interline - ?
Stay on Board at {place.name}
- :
{getPlaceName(place, config.companies)}
- } -
- - {/* Place subheading: Transit stop */} - {place.stopId && !interline && ( -
- Stop ID {place.stopId.split(':')[1]} - -
- )} - - {/* Place subheading: rented vehicle (e.g., scooter, bike, car) pickup */} - {leg && (leg.rentedVehicle || leg.rentedBike || leg.rentedCar) && ( - - )} - - {/* Show the leg, if present */} - {leg && ( - leg.transitLeg - ? (/* This is a transit leg */ - - ) - : (/* This is an access (e.g. walk/bike/etc.) leg */ - - ) - )} -
-
- ) - } -} - -// connect to the redux store - -const mapStateToProps = (state, ownProps) => { - return { - // Pass config in order to give access to companies definition (used to - // determine proper place names for rental vehicles). - config: state.otp.config - } -} - -const mapDispatchToProps = { } - -export default connect(mapStateToProps, mapDispatchToProps)(PlaceRow) - -/** - * A component to display vehicle rental data. The word "Vehicle" has been used - * because a future refactor is intended to combine car rental, bike rental - * and micromobility rental all within this component. The future refactor is - * assuming that the leg.rentedCar and leg.rentedBike response elements from OTP - * will eventually be merged into the leg.rentedVehicle element. - */ -class RentedVehicleLeg extends PureComponent { - render () { - const { config, leg } = this.props - const configCompanies = config.companies || [] - - // Sometimes rented vehicles can be walked over things like stairs or other - // ways that forbid the main mode of travel. - if (leg.mode === 'WALK') { - return ( -
- Walk vehicle along {leg.from.name} -
- ) - } - - let rentalDescription = 'Pick up' - if (leg.rentedBike) { - // TODO: Special case for TriMet may need to be refactored. - rentalDescription += ` shared bike` - } else { - // Add company and vehicle labels. - let vehicleName = '' - // TODO allow more flexibility in customizing these mode strings - let modeString = leg.rentedVehicle - ? 'E-scooter' - : leg.rentedBike - ? 'bike' - : 'car' - - // The networks attribute of the from data will only appear at the very - // beginning of the rental. It is possible that there will be some forced - // walking that occurs in the middle of the rental, so once the main mode - // resumes there won't be any network info. In that case we simply return - // that the rental is continuing. - if (leg.from.networks) { - const companiesLabel = getCompaniesLabelFromNetworks( - leg.from.networks, - configCompanies - ) - rentalDescription += ` ${companiesLabel}` - // Only show vehicle name for car rentals. For bikes and E-scooters, these - // IDs/names tend to be less relevant (or entirely useless) in this context. - if (leg.rentedCar && leg.from.name) { - vehicleName = leg.from.name - } - modeString = getModeForPlace(leg.from) - } else { - rentalDescription = 'Continue using rental' - } - - rentalDescription += ` ${modeString} ${vehicleName}` - } - // e.g., Pick up REACHNOW rented car XYZNDB OR - // Pick up SPIN E-scooter - // Pick up shared bike - return ( -
- {rentalDescription} -
- ) - } -} diff --git a/lib/components/narrative/line-itin/transit-leg-body.js b/lib/components/narrative/line-itin/transit-leg-body.js deleted file mode 100644 index a1fafdb1d..000000000 --- a/lib/components/narrative/line-itin/transit-leg-body.js +++ /dev/null @@ -1,237 +0,0 @@ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import PropTypes from 'prop-types' -import { VelocityTransitionGroup } from 'velocity-react' -import moment from 'moment' - -import ViewTripButton from '../../viewers/view-trip-button' -import { getIcon } from '../../../util/itinerary' -import { - formatDuration, - getLongDateFormat, - getTimeFormat -} from '../../../util/time' - -// TODO: support multi-route legs for profile routing - -class TransitLegBody extends Component { - static propTypes = { - leg: PropTypes.object, - legIndex: PropTypes.number, - setActiveLeg: PropTypes.func - } - - constructor (props) { - super(props) - this.state = { - alertsExpanded: false, - stopsExpanded: false - } - } - - _onToggleStopsClick = () => { - this.setState({ stopsExpanded: !this.state.stopsExpanded }) - } - - _onToggleAlertsClick = () => { - this.setState({ alertsExpanded: !this.state.alertsExpanded }) - } - - _onSummaryClick = () => { - this.props.setActiveLeg(this.props.legIndex, this.props.leg) - } - - render () { - const { customIcons, leg, longDateFormat, operator, timeFormat } = this.props - const { - agencyBrandingUrl, - agencyName, - agencyUrl, - alerts, - mode, - routeShortName, - routeLongName, - headsign - } = leg - const { alertsExpanded, stopsExpanded } = this.state - - // If the config contains an operator with a logo URL, prefer that over the - // one provided by OTP (which is derived from agency.txt#agency_branding_url) - const logoUrl = operator && operator.logo ? operator.logo : agencyBrandingUrl - - // get the iconKey for the leg's icon - let iconKey = mode - if (typeof customIcons.customIconForLeg === 'function') { - const customIcon = customIcons.customIconForLeg(leg) - if (customIcon) iconKey = customIcon - } - - return ( -
- {/* The Route Icon/Name Bar; clickable to set as active leg */} -
-
-
-
{getIcon(iconKey, customIcons)}
-
- {routeShortName && ( -
- {routeShortName} -
- )} -
- {routeLongName} - {headsign && to {headsign}} -
-
-
- - {/* Agency information */} - { -
- Service operated by{' '} - - {agencyName}{logoUrl && - - } - -
- } - - {/* Alerts toggle */} - {alerts && alerts.length > 0 && ( -
- {alerts.length} {pluralize('alert', alerts)} - {' '} - -
- )} - - {/* The Alerts body, if visible */} - - {alertsExpanded && - - } - - {/* The "Ride X Min / X Stops" Row, including IntermediateStops body */} - {leg.intermediateStops && leg.intermediateStops.length > 0 && ( -
- - {/* The header summary row, clickable to expand intermediate stops */} -
- {leg.duration && Ride {formatDuration(leg.duration)}} - {leg.intermediateStops && ( - - {' / '} - {leg.intermediateStops.length + 1} - {' stops '} - - - )} - - {/* The ViewTripButton. TODO: make configurable */} - -
- {/* IntermediateStops expanded body */} - - {stopsExpanded ? : null } - - - {/* Average wait details, if present */} - {leg.averageWait && Typical Wait: {formatDuration(leg.averageWait)}} -
- )} -
- ) - } -} - -// Connect to the redux store - -const mapStateToProps = (state, ownProps) => { - return { - longDateFormat: getLongDateFormat(state.otp.config), - operator: state.otp.config.operators.find(operator => operator.id === ownProps.leg.agencyId), - timeFormat: getTimeFormat(state.otp.config) - } -} - -const mapDispatchToProps = {} - -export default connect(mapStateToProps, mapDispatchToProps)(TransitLegBody) - -class IntermediateStops extends Component { - static propTypes = { - stops: PropTypes.array - } - - render () { - return ( -
- {this.props.stops.map((stop, k) => { - return
-
-
{stop.name}
-
- })} -
- ) - } -} - -class AlertsBody extends Component { - static propTypes = { - alerts: PropTypes.array - } - - render () { - const { longDateFormat, timeFormat } = this.props - return ( -
- {this.props.alerts - .sort((a, b) => b.effectiveStartDate - a.effectiveStartDate) - .map((alert, i) => { - // If alert is effective as of +/- one day, use today, tomorrow, or - // yesterday with time. Otherwise, use long date format. - const dateTimeString = moment(alert.effectiveStartDate) - .calendar(null, { - sameDay: `${timeFormat}, [Today]`, - nextDay: `${timeFormat}, [Tomorrow]`, - lastDay: `${timeFormat}, [Yesterday]`, - lastWeek: `${longDateFormat}`, - sameElse: `${longDateFormat}` - }) - const effectiveDateString = `Effective as of ${dateTimeString}` - return ( -
-
- {alert.alertHeaderText - ?
{alert.alertHeaderText}
- : null - } -
{alert.alertDescriptionText}
-
{effectiveDateString}
-
- ) - }) - } -
- ) - } -} - -// TODO use pluralize that for internationalization (and complex plurals, i.e., not just adding 's') -function pluralize (str, list) { - return `${str}${list.length > 1 ? 's' : ''}` -} diff --git a/lib/components/viewers/route-viewer.js b/lib/components/viewers/route-viewer.js index 60d8996dd..c298d5a50 100644 --- a/lib/components/viewers/route-viewer.js +++ b/lib/components/viewers/route-viewer.js @@ -10,11 +10,11 @@ import { setMainPanelContent, setViewedRoute } from '../../actions/ui' import { findRoutes, findRoute } from '../../actions/api' import { routeComparator } from '../../util/itinerary' -function operatorIndexForRoute (operators, route) { +function operatorIndexForRoute (transitOperators, route) { if (!route.agency) return 0 - const index = operators.findIndex(o => + const index = transitOperators.findIndex(o => o.id.toLowerCase() === route.agency.id.split(':')[0].toLowerCase()) - if (index !== -1 && typeof operators[index].order !== 'undefined') return operators[index].order + if (index !== -1 && typeof transitOperators[index].order !== 'undefined') return transitOperators[index].order else return 0 } @@ -52,7 +52,7 @@ class RouteViewer extends Component { findRoute, hideBackButton, languageConfig, - operators, + transitOperators, routes, setViewedRoute, viewedRoute @@ -60,9 +60,9 @@ class RouteViewer extends Component { const sortedRoutes = routes ? Object.values(routes).sort(routeComparator) : [] - const agencySortedRoutes = operators.length > 0 + const agencySortedRoutes = transitOperators.length > 0 ? sortedRoutes.sort((a, b) => { - return operatorIndexForRoute(operators, a) - operatorIndexForRoute(operators, b) + return operatorIndexForRoute(transitOperators, a) - operatorIndexForRoute(transitOperators, b) }) : sortedRoutes return ( @@ -94,7 +94,7 @@ class RouteViewer extends Component { .map(route => { // Find operator based on agency_id (extracted from OTP route ID). // TODO: re-implement multi-agency logos for route viewer. - // const operator = operatorForRoute(operators, route) || {} + // const operator = operatorForRoute(transitOperators, route) || {} return ( {// TODO: re-implement multi-agency logos for route viewer. // Currently, the agency object is not nested within the get all - // routes endpoint and causing this to only display operators for + // routes endpoint and causing this to only display transitOperators for // the selected route. // operator && } @@ -196,7 +196,7 @@ class RouteRow extends PureComponent { const mapStateToProps = (state, ownProps) => { return { - operators: state.otp.config.operators, + transitOperators: state.otp.config.transitOperators, routes: state.otp.transitIndex.routes, viewedRoute: state.otp.ui.viewedRoute, languageConfig: state.otp.config.language diff --git a/lib/index.css b/lib/index.css index 0fe0005bc..d768385b0 100644 --- a/lib/index.css +++ b/lib/index.css @@ -11,7 +11,6 @@ @import url(lib/components/form/form.css); @import url(lib/components/narrative/narrative.css); @import url(lib/components/narrative/default/itinerary.css); -@import url(lib/components/narrative/line-itin/itinerary.css); @import url(lib/components/mobile/mobile.css); @import url(lib/components/viewers/viewers.css); diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index d265283e4..2ea229618 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -64,7 +64,7 @@ export function getInitialState (userDefinedConfig, initialQuery) { autoPlan: false, debouncePlanTimeMs: 0, language: {}, - operators: [], + transitOperators: [], realtimeEffectsDisplayThreshold: 120, routingTypes: [], stopViewer: { @@ -251,7 +251,7 @@ function createOtpReducer (config, initialQuery) { } }, ui: { - diagramLeg: { $set: false } + diagramLeg: { $set: null } } }) case 'NON_REALTIME_ROUTING_RESPONSE': diff --git a/lib/util/distance.js b/lib/util/distance.js deleted file mode 100644 index e63c8a0fe..000000000 --- a/lib/util/distance.js +++ /dev/null @@ -1,26 +0,0 @@ -export function distanceStringImperial (meters, abbreviate) { - const feet = meters * 3.28084 - if (feet < 528) return Math.round(feet) + (abbreviate === true ? ' ft' : ' feet') - return (Math.round(feet / 528) / 10) + (abbreviate === true ? ' mi' : ' miles') -} - -export function distanceStringMetric (meters) { - let km = meters / 1000 - if (km > 100) { - // 100 km => 999999999 km - km = km.toFixed(0) - return km + ' km' - } else if (km > 1) { - // 1.1 km => 99.9 km - km = km.toFixed(1) - return km + ' km' - } else { - // 1m => 999m - meters = meters.toFixed(0) - return meters + ' m' - } -} - -export function distanceString (meters, outputMetricUntis = false) { - return (outputMetricUntis === true) ? distanceStringMetric(meters) : distanceStringImperial(meters) -} diff --git a/lib/util/index.js b/lib/util/index.js index 8d225a961..da1a44a7c 100644 --- a/lib/util/index.js +++ b/lib/util/index.js @@ -1,4 +1,3 @@ -import * as distance from './distance' import * as itinerary from './itinerary' import * as map from './map' import * as profile from './profile' @@ -9,7 +8,6 @@ import * as time from './time' import * as ui from './ui' const OtpUtils = { - distance, itinerary, map, profile, diff --git a/package.json b/package.json index 765b004ed..1bca03802 100644 --- a/package.json +++ b/package.json @@ -29,24 +29,26 @@ "dependencies": { "@conveyal/lonlat": "^1.1.0", "@mapbox/polyline": "^0.2.0", - "@opentripplanner/base-map": "^0.0.18", - "@opentripplanner/core-utils": "^0.0.18", - "@opentripplanner/endpoints-overlay": "^0.0.18", - "@opentripplanner/from-to-location-picker": "^0.0.18", - "@opentripplanner/geocoder": "^0.0.18", - "@opentripplanner/icons": "^0.0.18", - "@opentripplanner/location-field": "^0.0.18", - "@opentripplanner/location-icon": "^0.0.18", - "@opentripplanner/park-and-ride-overlay": "^0.0.18", - "@opentripplanner/route-viewer-overlay": "^0.0.18", - "@opentripplanner/stop-viewer-overlay": "^0.0.18", - "@opentripplanner/stops-overlay": "^0.0.18", - "@opentripplanner/printable-itinerary": "^0.0.18", - "@opentripplanner/transitive-overlay": "^0.0.18", - "@opentripplanner/trip-details": "^0.0.18", - "@opentripplanner/trip-form": "^0.0.18", - "@opentripplanner/trip-viewer-overlay": "^0.0.18", - "@opentripplanner/vehicle-rental-overlay": "^0.0.18", + "@opentripplanner/base-map": "^0.0.19", + "@opentripplanner/core-utils": "^0.0.19", + "@opentripplanner/endpoints-overlay": "^0.0.19", + "@opentripplanner/from-to-location-picker": "^0.0.19", + "@opentripplanner/geocoder": "^0.0.19", + "@opentripplanner/humanize-distance": "^0.0.19", + "@opentripplanner/icons": "^0.0.19", + "@opentripplanner/itinerary-body": "^0.0.19", + "@opentripplanner/location-field": "^0.0.19", + "@opentripplanner/location-icon": "^0.0.19", + "@opentripplanner/park-and-ride-overlay": "^0.0.19", + "@opentripplanner/printable-itinerary": "^0.0.19", + "@opentripplanner/route-viewer-overlay": "^0.0.19", + "@opentripplanner/stop-viewer-overlay": "^0.0.19", + "@opentripplanner/stops-overlay": "^0.0.19", + "@opentripplanner/transitive-overlay": "^0.0.19", + "@opentripplanner/trip-details": "^0.0.19", + "@opentripplanner/trip-form": "^0.0.19", + "@opentripplanner/trip-viewer-overlay": "^0.0.19", + "@opentripplanner/vehicle-rental-overlay": "^0.0.19", "@turf/along": "^6.0.1", "bootstrap": "^3.3.7", "clone": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 7b89dac62..53e41ada7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1331,18 +1331,18 @@ universal-user-agent "^3.0.0" url-template "^2.0.8" -"@opentripplanner/base-map@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-0.0.18.tgz#afd04eca83cbf972e0c4b0db5c8fdd42fd7ce040" - integrity sha512-6sUUL1WpFASxm8vhLSsCIER/VwsVPgAF27MEz5tatk5Fdl2KG3DD9+WeW41b1iUzkaK1S0b1z2y4Ozn52RqU0g== +"@opentripplanner/base-map@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-0.0.19.tgz#71b0d77c7e8265f742f5383c943663436eb3369b" + integrity sha512-X/OtKsCoVOfJ4GrkDfvW5zufDE2dTzdGsagojrrTk0mzJTh1Kv+x+Oe3iXJfllg5U61yt06PDunFdsZE5kHr2w== dependencies: - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/core-utils@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-0.0.18.tgz#5109a60e262802dca3b1e7a34091c9a394f5b290" - integrity sha512-6ej3MVTd/Kz4OYnIDJBm8fT4OHkchWq+sU9G+Z6fHJhGE5nh/ljCcps/8Vew1HXK6nhaeDMm099nkA/8YKRnug== +"@opentripplanner/core-utils@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-0.0.19.tgz#152991efb07d578abf40649ce0dde1c62a8ec4e3" + integrity sha512-9j0j8pGba+af5kz0K8+6bNLy/34BL7CEIHkLQyyLv3H30GpzJWQ+r9z6zIcwndVcoBqEcrn6gLBr1S2KrBscZA== dependencies: "@mapbox/polyline" "^1.1.0" "@turf/along" "^6.0.1" @@ -1353,157 +1353,172 @@ prop-types "^15.7.2" qs "^6.9.1" -"@opentripplanner/endpoints-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-0.0.18.tgz#688b83c756ad3c97679c417afbcd9ab86b7db1c6" - integrity sha512-6wLnCVO522IO/HZSp/mJDuCT04FoQWZpHJ/54h7ZQVWbowxwdXHz1f0PtFsxvMwZB2jN4W7h257p0g1CRiKunw== +"@opentripplanner/endpoints-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-0.0.19.tgz#667a70b8b0526cc3fd01e4f69e4839cf04a85ca5" + integrity sha512-0Cd4dd5yYXP42udDAKGNHj00WuCXwoWThj03IWagKOpbr7T5g7ag7B1RkbMJQICYsFvqg9stQJ3AnOmNrwBBPw== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/location-icon" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/location-icon" "^0.0.19" prop-types "^15.7.2" styled-icons "^9.1.0" -"@opentripplanner/from-to-location-picker@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-0.0.18.tgz#21d1164d09724becea640a95f97aadb479df8f24" - integrity sha512-y5GJxvf1La2Wk9ldEtKO5zzVnECi89HkrWdT5Tb2ZYuJrUVnPZZ28CmLEtQGSh+Qtj/kxVBMII/UOkoja3SkuQ== +"@opentripplanner/from-to-location-picker@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-0.0.19.tgz#5dd78fff068c84c256fa504dd163ca70e51b2cd9" + integrity sha512-ZNPJVRO2OIOKjdRLOX4fnOvWTX07ccTXEuUfXPpITOf8QBHnJZPTFBgBSMmfqxMfcuLf6NRD4+YzFcllIO4opQ== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/location-icon" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/location-icon" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/geocoder@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-0.0.18.tgz#fe1ecddd025b08e6b2bf51a9ddf8c65e10571b90" - integrity sha512-NvqZDJc6c+fKRXys5jkaFD5F2gtXag4gKov2L6z0d3+Sn8BTQvDTuE961J5Bb/xn8xE4IvmcIs5tQmE/B+tvBw== +"@opentripplanner/geocoder@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-0.0.19.tgz#4ff8b1baf3c322491f472db31c3fbd7ed576b2a1" + integrity sha512-BOkOY85fj6E2CAFemlMH3qu/e4t9xG3llKrvasm8Vh23PI3FwQRtDQoN8qz3vK4s/pjZsd0D/TpS8+bQGI9HQQ== dependencies: "@conveyal/geocoder-arcgis-geojson" "^0.0.2" "@conveyal/lonlat" "^1.4.0" isomorphic-mapzen-search "^1.4.1" lodash.memoize "^4.1.2" -"@opentripplanner/humanize-distance@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-0.0.18.tgz#eeb49b826754e9e04ca84813781fa47baeaebc7f" - integrity sha512-emn2TTVBk9DUnbLPh9OIP/ZlKdbDpJzVWq+tO5HIqCIL1TW0XFEQDbQLv3b3rGS2R2LHbhKlYYJKmP36ZFS20w== +"@opentripplanner/humanize-distance@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-0.0.19.tgz#30d445e9f586afcf0488a8fe27157d62b29fd563" + integrity sha512-gEDH3oB8XZoraPWfsmVfEZYqPN/aqh+Z3Pjc35asJmdOCVKuWWnSem/W4qyK6hBnpMV3RUl9TBKl3B5ubnqBLA== -"@opentripplanner/icons@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-0.0.18.tgz#4486286988801d374a31975aec80ac326ff17061" - integrity sha512-g0+8r9VL/6TPXlFak4mg8xOzAGzID5SGMurLayXvCuroFIbqwUD3/BOhGUD6+z25MvOv3Tov7V9wtN9l822eTg== +"@opentripplanner/icons@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-0.0.19.tgz#7681ad2b202e18f361cc4ae32b9c14e162500e50" + integrity sha512-yGNBamoCzvdzcc86RFfZGW5SsktOsXV2G+USPr9goWoP1Hw1IukSn2gEWP8s8qvBCulAMr7HZdLHSXAT9TXk9w== dependencies: - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/location-field@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-0.0.18.tgz#c01d5a876f7cd76ee1855ed4ee186abd3ff85ae1" - integrity sha512-RAJkkgCWTuZRcjOguzFWb+z4YDLv8Lrp6V2QZJhNV289SPjokuGOuDrARl7+FQ8Di0ZuH95Ca/PipJEdeF2wAQ== +"@opentripplanner/itinerary-body@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-0.0.19.tgz#44c7858f848a625c7327de38850adc2f0a086667" + integrity sha512-f7H8eXWUAArlqBUkvg9IqN+hbQgtVSqcLtwu+EMjNmO9/grU6qR1z9guyXudNi5BS+jsngIla8mNNC60+8l7tA== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/geocoder" "^0.0.18" - "@opentripplanner/humanize-distance" "^0.0.18" - "@opentripplanner/location-icon" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/humanize-distance" "^0.0.19" + "@opentripplanner/icons" "^0.0.19" + "@opentripplanner/location-icon" "^0.0.19" + currency-formatter "^1.5.5" + moment "^2.24.0" + prop-types "^15.7.2" + react-resize-detector "^4.2.1" + velocity-react "^1.4.3" + +"@opentripplanner/location-field@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-0.0.19.tgz#70db6c081139320cd29b58b2580a6e3178f5c844" + integrity sha512-yCq+VnwaJLPy9CC623I6sq08dEUxJHb/x0wWPXN6A7ZmiltAZdDIPmYnLJAkvRzEfLi1jv8pt1PO4nSYxctI0A== + dependencies: + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/geocoder" "^0.0.19" + "@opentripplanner/humanize-distance" "^0.0.19" + "@opentripplanner/location-icon" "^0.0.19" prop-types "^15.7.2" styled-icons "^9.1.0" throttle-debounce "^2.1.0" -"@opentripplanner/location-icon@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-icon/-/location-icon-0.0.18.tgz#8f207e1d7032ae04081cffa981ee3117c3647478" - integrity sha512-4Pv0RZZeHvDpc50XMOMUOS4E7dlYBNne/OvnOxHcbEmHSOUHGKNSiIq2EPO7Eyrae+SsKKhksv++okIiw6KOmw== +"@opentripplanner/location-icon@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-icon/-/location-icon-0.0.19.tgz#19d121cd7b0d0d1588321912fe0674ac6641046e" + integrity sha512-vtqTMORk0xLBVbBLFUzXqPf4acQSDIe0kxbA+VbpYc2NQD8vZDcnIVFL9e73zXAaKNj/uATAolubZjX6Jvyh3Q== dependencies: styled-icons "^9.1.0" -"@opentripplanner/printable-itinerary@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-0.0.18.tgz#4137fa0000f8c1164e5fa4226243e0b887412105" - integrity sha512-uxUm80WU4m8Pni3OzkBzMI/7kmtgRSR7e3wJqy6N9PpZRyEEzUzzSaDmAS8+/nswgZyrNpeenp5ZK5e+7AJA+g== +"@opentripplanner/park-and-ride-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-0.0.19.tgz#6f448eccb8ffb86bf9844ec778a6d9111f7575d7" + integrity sha512-9+ia0f2rl2Rkln46tmIGuyb19lYwUPTLGPSOSRRXr1/Qe9OiZD7dNrf4Zif/HnW6l+1Z4fexYA5nN6TN/clOaA== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/humanize-distance" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/from-to-location-picker" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/trip-details@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-0.0.18.tgz#69d8fa4e8a70977c9aaa71a777a34021aec0fa12" - integrity sha512-jCBgmPQhl9gsUniYSbPqDvpyFUHqsGNHUwHg+jqAplPHWu60Hn2F/P1oWf5PmjXlBV7ZFOsuTeH7HVmGfVrPrw== +"@opentripplanner/printable-itinerary@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-0.0.19.tgz#a224cae07e1459c455f7684eaebbac2eca66ea48" + integrity sha512-9Ow6YXKuxDJ5dnwlDBXXMEmyLriEtZtetk7IkRiAOXUNgo00un9uGbb0Zu6gYBbTVuLpjLMsZNpzUsnO27DbYw== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/humanize-distance" "^0.0.18" - moment "^2.24.0" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/humanize-distance" "^0.0.19" prop-types "^15.7.2" - styled-icons "^9.1.0" - velocity-react "^1.4.3" -"@opentripplanner/park-and-ride-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-0.0.18.tgz#2e466a4e0ab47531d595c58119371a5a2a11ec7a" - integrity sha512-/eJizVEd5FSB7QW8CiEIHnUi3kQ7ouL3uEdpIdwHtgUVP4sWYOPSoy/jKiY6W404U5Y/K/9B9ZgAAtMMpCuSEg== - dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/from-to-location-picker" "^0.0.18" - prop-types "^15.7.2" - -"@opentripplanner/route-viewer-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-0.0.18.tgz#1736e99d509579bacb67a15f07a517f1e37985ad" - integrity sha512-4cQlIqEeUfmVEJNcb/8ReQraTfb3t1+hl5hqOlpNmKUguHgHuKeeEuxBkPn2larMz0xGanNybRv+VnOuiG7DXw== +"@opentripplanner/route-viewer-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-0.0.19.tgz#d6f8080ec4106ae8d90cfebcd02edd39a7ae2d27" + integrity sha512-6DUumUOC33X4FauUVJNucaHoZBvCDVLizZLbU2hjDuS+ZrdCfckEy6wJz8j5x4lfi0pr8jUZEWK+pl3P+sFjaw== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/stop-viewer-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-0.0.18.tgz#2e81df67e49503749a955206c6200e816faff104" - integrity sha512-D9EHe37eP84h3bPJ0NR6KMNlS07vf2BJmMTtrKwYMPsRHj/+tmRneBzHGQ7Bk3tYE+sSzKekkGKjLLjs9z4XPQ== +"@opentripplanner/stop-viewer-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-0.0.19.tgz#1484864fcad81b9fa3ea9726845b3a7383e78db5" + integrity sha512-fdfBiUOrcKiDFDVo5upj7tl2jN6CPxIF7OoYtR/EoSf4A8pCSkCY4pxRL3VE0z7O103fJFP67BCu8axKptaEsA== dependencies: - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/stops-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-0.0.18.tgz#32f8d621b26cb59c0314bfab80b865bce0b64ab2" - integrity sha512-zRVWgn6qg0mqX6xlfLXRaiUkLixfkDCV7VUKpbHpbHKXbfaT2J5pOn81pDCNrtSF4zPkSIKX+24yMcq8i65yOQ== +"@opentripplanner/stops-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-0.0.19.tgz#b3cc17bbbb7d64fd44da17c01955b4069f0868a6" + integrity sha512-tlr/2S2E7zYjEHla3hIMQc0tpdgJMolY4jUqBPEFOohP1NCf4VQWyO8cY0D2CyjPI9eQkW96eU2be8WuUtwyXQ== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/from-to-location-picker" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/from-to-location-picker" "^0.0.19" -"@opentripplanner/transitive-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-0.0.18.tgz#d9835e2ac8f2754c79700e2042c1e9f112860f16" - integrity sha512-+Xc73WkMTTSjXGIorkskpSZXhfIkeqUuVofr8//rjdIG1EfhRDlJf0aOYEZpHnrB4AFDzHOIUzXAROnJqMy5EQ== +"@opentripplanner/transitive-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-0.0.19.tgz#096c30b08e94161cc8522f3a01ff147f473a40d7" + integrity sha512-+UYbDcGlODHxAZQuoX6WeiDVTZsCcrzCfphQmkoQERdd3xukJpPwzjSSjeIEbP+8gTGPtlM+i7qu00mu3C0ynw== dependencies: - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" lodash.isequal "^4.5.0" transitive-js "^0.13.0" -"@opentripplanner/trip-form@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-0.0.18.tgz#8322e2b3c554c2b020d46bbc61d3cd83be5cd60b" - integrity sha512-Yzhbwit1kWMq8FMRTUomX/OXPpmcnvUvEHNl/oA9w2gBEVpY72AcGYnJ9GPcaRGb2+hw9F+j/8PWsB/8ETA8/Q== +"@opentripplanner/trip-details@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-0.0.19.tgz#3379a13188b7fb12387be5abd0b0d071231b366a" + integrity sha512-ItRywhXMikvOv+13/qWwYueX1eC57IZnzukDy4Gukd/MtJheQOzwDU2W5pWwK1MmMEikxjdsSCw4vt2m6biTyw== + dependencies: + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/humanize-distance" "^0.0.19" + moment "^2.24.0" + prop-types "^15.7.2" + styled-icons "^9.1.0" + velocity-react "^1.4.3" + +"@opentripplanner/trip-form@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-0.0.19.tgz#f34995eee51401012d9fa81a1138de9627d2bc78" + integrity sha512-x2zRAUDYPZlZF1hsVH4wVEdjRspBNU+P1d5a5m0J4OYfS7prnWlVKQl5+b5ktcLiY3mbdGtDHtYv+2knQt0gnQ== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/icons" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/icons" "^0.0.19" moment "^2.17.1" -"@opentripplanner/trip-viewer-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-0.0.18.tgz#7e774378cc274caf7603aa14a08d4b57cd220b14" - integrity sha512-iz+KQBFRZORz36w10kXEUHhxS4CwTCXWb8xcLYAwh9LmWkHhbGXPnClwn6PJ+BPj0PcdvLukcQaCUpoxZggfqg== +"@opentripplanner/trip-viewer-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-0.0.19.tgz#ab608699720f3ab090f64aadae01a12434188f18" + integrity sha512-Nyta8qagZib/tQE/h57HB+O1nUzHi2ougDCEn6I4upqM6OUn6bYSC+V048VGXUHwa8rnnFT9jf8ryKUd6tVEfQ== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/core-utils" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" prop-types "^15.7.2" -"@opentripplanner/vehicle-rental-overlay@^0.0.18": - version "0.0.18" - resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-0.0.18.tgz#67da2f4ffa1fa89ccd4b336ba18cb4664e5a7419" - integrity sha512-maC+XNYEcs3w7Jv2zmuQlJRVL+QVF86yBK6bKG48FqgsPvhYz748qSyIao/tAeCWpUiomMKbSPPMbdbcdyVLDg== +"@opentripplanner/vehicle-rental-overlay@^0.0.19": + version "0.0.19" + resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-0.0.19.tgz#d0efc0d653c45da8e2fd8f835eb059f7173aa3fe" + integrity sha512-gZnqoZlZznGeCykfynVkC9pMq4AAotV0DJaNkX4z9sHVGUFgk+mcaiAkrIhPLWLyOiOTnLQnnuPw8T26yOqzng== dependencies: - "@opentripplanner/core-utils" "^0.0.18" - "@opentripplanner/from-to-location-picker" "^0.0.18" + "@opentripplanner/core-utils" "^0.0.19" + "@opentripplanner/from-to-location-picker" "^0.0.19" lodash.memoize "^4.1.2" prop-types "^15.7.2" styled-icons "^9.1.0" @@ -5151,6 +5166,15 @@ currency-formatter@^1.4.2: locale-currency "0.0.2" object-assign "^4.1.1" +currency-formatter@^1.5.5: + version "1.5.5" + resolved "https://registry.yarnpkg.com/currency-formatter/-/currency-formatter-1.5.5.tgz#907790bb0b7f129c4a64d2924e0d7fa36db0cf52" + integrity sha512-PEsZ9fK2AwPBYgzWTtqpSckam7hFDkT8ZKFAOrsooR0XbydZEKuFioUzcc3DoT2mCDkscjf1XdT6Qq53ababZQ== + dependencies: + accounting "^0.4.1" + locale-currency "0.0.2" + object-assign "^4.1.1" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -9396,6 +9420,11 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" +lodash-es@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -9608,7 +9637,7 @@ lodash@4.17.14: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -12947,6 +12976,11 @@ qw@~1.0.1: resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4" integrity sha1-77/cdA+a0FQwRCassYNBLMi5ltQ= +raf-schd@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" + integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== + raf@^3.4.0: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -13157,6 +13191,17 @@ react-resize-detector@^2.1.0: prop-types "^15.6.0" resize-observer-polyfill "^1.5.0" +react-resize-detector@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-4.2.1.tgz#8982b74c3e1cf949afaa3c41050458c87b033982" + integrity sha512-ZfPMBPxXi0o3xox42MIEtz84tPSVMW9GgwLHYvjVXlFM+OkNzbeEtpVSV+mSTJmk4Znwomolzt35zHN9LNBQMQ== + dependencies: + lodash "^4.17.15" + lodash-es "^4.17.15" + prop-types "^15.7.2" + raf-schd "^4.0.2" + resize-observer-polyfill "^1.5.1" + react-router@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" @@ -13928,7 +13973,7 @@ require-relative@^0.8.7: resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4= -resize-observer-polyfill@^1.5.0: +resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==