diff --git a/lib/components/app/print-layout.js b/lib/components/app/print-layout.js
index 0222b0f0c..231c479ae 100644
--- a/lib/components/app/print-layout.js
+++ b/lib/components/app/print-layout.js
@@ -1,16 +1,17 @@
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'
import { connect } from 'react-redux'
-import PrintableItinerary from '../narrative/printable/printable-itinerary'
import { parseUrlQueryString } from '../../actions/form'
import { routingQuery } from '../../actions/api'
import { getActiveItinerary } from '../../util/state'
-import { getTimeFormat } from '../../util/time'
+import TripDetails from '../narrative/connected-trip-details'
class PrintLayout extends Component {
static propTypes = {
@@ -34,14 +35,14 @@ class PrintLayout extends Component {
}
componentDidMount () {
- const { location } = this.props
+ const { location, parseUrlQueryString } = this.props
// Add print-view class to html tag to ensure that iOS scroll fix only applies
// to non-print views.
const root = document.getElementsByTagName('html')[0]
root.setAttribute('class', 'print-view')
// Parse the URL query parameters, if present
if (location && location.search) {
- this.props.parseUrlQueryString()
+ parseUrlQueryString()
}
}
@@ -53,8 +54,24 @@ class PrintLayout extends Component {
root.removeAttribute('class')
}
+ /**
+ * Use one of the customIcons if provided,
+ * (similar to @opentriplanner/trip-form/ModeIcon)
+ * otherwise, fall back on TriMetLegIcon.
+ * TODO: Combine all custom icon rendering in one place.
+ */
+ customLegIcon = icons => {
+ return function ({leg, props}) {
+ // Check if there is a custom icon (exact match required).
+ if (icons && leg.mode in icons) {
+ return icons[leg.mode]
+ }
+ return TriMetLegIcon({leg, props})
+ }
+ }
+
render () {
- const { configCompanies, customIcons, itinerary, timeFormat } = this.props
+ const { config, customIcons, itinerary } = this.props
return (
{/* The header bar, including the Toggle Map and Print buttons */}
@@ -82,13 +99,15 @@ class PrintLayout extends Component {
}
{/* The main itinerary body */}
- {itinerary
- ?
- : null
+ {itinerary &&
+ <>
+
+
+ >
}
)
@@ -99,9 +118,8 @@ class PrintLayout extends Component {
const mapStateToProps = (state, ownProps) => {
return {
- itinerary: getActiveItinerary(state.otp),
- configCompanies: state.otp.config.companies,
- timeFormat: getTimeFormat(state.otp.config)
+ config: state.otp.config,
+ itinerary: getActiveItinerary(state.otp)
}
}
diff --git a/lib/components/narrative/connected-trip-details.js b/lib/components/narrative/connected-trip-details.js
new file mode 100644
index 000000000..16183ea1e
--- /dev/null
+++ b/lib/components/narrative/connected-trip-details.js
@@ -0,0 +1,25 @@
+import { connect } from 'react-redux'
+import styled from 'styled-components'
+import TripDetailsBase from '@opentripplanner/trip-details'
+
+import { getTimeFormat, getLongDateFormat } from '../../util/time'
+
+const TripDetails = styled(TripDetailsBase)`
+ border: 2px solid gray;
+ border-radius: 0;
+ padding: 6px 10px;
+ margin: 16px 0 10px;
+`
+
+// Connect imported TripDetails class to redux store.
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ routingType: state.otp.currentQuery.routingType,
+ tnc: state.otp.tnc,
+ timeFormat: getTimeFormat(state.otp.config),
+ longDateFormat: getLongDateFormat(state.otp.config)
+ }
+}
+
+export default connect(mapStateToProps)(TripDetails)
diff --git a/lib/components/narrative/default/default-itinerary.js b/lib/components/narrative/default/default-itinerary.js
index b6b66a227..57f2717de 100644
--- a/lib/components/narrative/default/default-itinerary.js
+++ b/lib/components/narrative/default/default-itinerary.js
@@ -3,7 +3,7 @@ import React from 'react'
import NarrativeItinerary from '../narrative-itinerary'
import ItinerarySummary from './itinerary-summary'
import ItineraryDetails from './itinerary-details'
-import TripDetails from '../trip-details'
+import TripDetails from '../connected-trip-details'
import TripTools from '../trip-tools'
import { formatDuration, formatTime } from '../../../util/time'
diff --git a/lib/components/narrative/line-itin/itin-body.js b/lib/components/narrative/line-itin/itin-body.js
index c38ced30d..713e02b7b 100644
--- a/lib/components/narrative/line-itin/itin-body.js
+++ b/lib/components/narrative/line-itin/itin-body.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import isEqual from 'lodash.isequal'
-import TripDetails from '../trip-details'
+import TripDetails from '../connected-trip-details'
import TripTools from '../trip-tools'
import PlaceRow from './place-row'
diff --git a/lib/components/narrative/narrative.css b/lib/components/narrative/narrative.css
index 21c1ab760..ac8c4effa 100644
--- a/lib/components/narrative/narrative.css
+++ b/lib/components/narrative/narrative.css
@@ -116,58 +116,6 @@
fontSize: 16px;
}
-/* TRIP DETAILS */
-
-.otp .trip-details {
- border: 2px solid gray;
- padding: 6px 10px;
- margin-top: 16px;
-}
-
-.otp .trip-details .trip-details-header {
- font-size: 18px;
- font-weight: 600;
-}
-
-.otp .trip-details .trip-detail {
- margin-top: 6px;
-}
-
-.otp .trip-details .trip-detail .icon {
- float: left;
- font-size: 17px;
-}
-
-.otp .trip-details .trip-detail .summary {
- margin-left: 28px;
- padding-top: 2px;
-}
-
-.otp .trip-details .trip-detail .expand-button {
- margin-left: 6px;
- margin-top: -2px;
- font-size: 16px;
- color: blue;
-}
-
-.otp .trip-details .trip-detail .description {
- background-color: #fff;
- border: 1px solid #888;
- padding: 8px;
- margin-top: 2px;
- font-size: 12px;
-}
-
-.otp .trip-details .trip-detail b {
- font-weight: 600;
-}
-
-.otp .trip-details .trip-detail .hide-button {
- float: right;
- top: 5;
- right: 5;
-}
-
/* TRIP TOOLS ROW */
.otp .trip-tools {
margin-top: 10px;
diff --git a/lib/components/narrative/printable/itinerary.css b/lib/components/narrative/printable/itinerary.css
deleted file mode 100644
index 07b6c9dfe..000000000
--- a/lib/components/narrative/printable/itinerary.css
+++ /dev/null
@@ -1,33 +0,0 @@
-.otp .printable-itinerary .leg {
- margin-bottom: 10px;
- border-top: 1px solid gray;
- padding-top: 18px;
-}
-
-.otp .printable-itinerary .leg.collapse-top {
- border-top: none;
- padding-top: 0px;
-}
-
-.otp .printable-itinerary .mode-icon {
- float: left;
- width: 32px;
- height: 32px;
-}
-
-.otp .printable-itinerary .leg-body {
- margin-left: 40px;
-}
-
-.otp .printable-itinerary .leg-header {
- font-size: 18px;
-}
-
-.otp .printable-itinerary .leg-details {
- margin-top: 5px;
-}
-
-.otp .printable-itinerary .leg-detail {
- margin-top: 3px;
- font-size: 14px;
-}
diff --git a/lib/components/narrative/printable/printable-itinerary.js b/lib/components/narrative/printable/printable-itinerary.js
deleted file mode 100644
index ecb13c545..000000000
--- a/lib/components/narrative/printable/printable-itinerary.js
+++ /dev/null
@@ -1,221 +0,0 @@
-import React, { Component } from 'react'
-import PropTypes from 'prop-types'
-
-import TripDetails from '../trip-details'
-import { distanceString } from '../../../util/distance'
-import { formatTime, formatDuration } from '../../../util/time'
-import {
- getCompaniesLabelFromNetworks,
- getLegIcon,
- getLegModeLabel,
- getPlaceName,
- getStepDirection,
- getStepStreetName,
- getTimeZoneOffset
-} from '../../../util/itinerary'
-
-export default class PrintableItinerary extends Component {
- static propTypes = {
- itinerary: PropTypes.object
- }
-
- render () {
- const {
- configCompanies,
- customIcons,
- itinerary,
- timeFormat
- } = this.props
-
- const timeOptions = {
- format: timeFormat,
- offset: getTimeZoneOffset(itinerary)
- }
-
- return (
-
- {itinerary.legs.length > 0 && (
-
-
-
- Depart from {itinerary.legs[0].from.name}
-
-
-
- )}
- {itinerary.legs.map((leg, k) => leg.transitLeg
- ?
- : leg.hailedCar
- ?
- :
- )}
-
-
- )
- }
-}
-
-class TransitLeg extends Component {
- static propTypes = {
- leg: PropTypes.object
- }
-
- render () {
- const { customIcons, leg, interlineFollows, timeOptions } = this.props
-
- // Handle case of transit leg interlined w/ previous
- if (leg.interlineWithPreviousLeg) {
- return (
-
-
-
- Continues as{' '}
- {leg.routeShortName} {leg.routeLongName} {' '}
- to {leg.to.name}
-
-
-
- Get off at {leg.to.name} {' '}
- at {formatTime(leg.endTime, timeOptions)}
-
-
-
-
- )
- }
-
- return (
-
-
{getLegIcon(leg, customIcons)}
-
-
- {leg.routeShortName} {leg.routeLongName} to {leg.to.name}
-
-
-
- Board at {leg.from.name} {' '}
- at {formatTime(leg.startTime, timeOptions)}
-
-
- {interlineFollows
- ? Stay on board at {leg.to.name}
- :
- Get off at {leg.to.name} {' '}
- at {formatTime(leg.endTime, timeOptions)}
-
- }
-
-
-
-
- )
- }
-}
-
-class AccessLeg extends Component {
- static propTypes = {
- leg: PropTypes.object
- }
-
- render () {
- const { configCompanies, customIcons, leg } = this.props
-
- // calculate leg mode label in a special way for this component
- let legModeLabel = getLegModeLabel(leg)
-
- if (leg.rentedBike) {
- // FIXME: Special case for TriMet that needs to be refactored to
- // incorporate actual company.
- legModeLabel = 'Ride BIKETOWN bike'
- } else if (leg.rentedCar) {
- // Add extra information to printview that would otherwise clutter up
- // other places that use the getLegModeLabel function
- const companiesLabel = getCompaniesLabelFromNetworks(
- leg.from.networks,
- configCompanies
- )
- legModeLabel = `Drive ${companiesLabel} ${leg.from.name}`
- } else if (leg.rentedVehicle) {
- const companiesLabel = getCompaniesLabelFromNetworks(
- leg.from.networks,
- configCompanies
- )
- legModeLabel = `Ride ${companiesLabel} E-scooter`
- }
-
- return (
-
-
{getLegIcon(leg, customIcons)}
-
-
- {legModeLabel} {' '}
- {!leg.hailedCar &&
- leg.distance > 0 &&
- {distanceString(leg.distance)} }
- to {getPlaceName(leg.to, configCompanies)}
-
- {!leg.hailedCar && (
-
- {leg.steps.map((step, k) => {
- return (
-
- {getStepDirection(step)} on {getStepStreetName(step)}
-
- )
- })}
-
- )}
-
-
- )
- }
-}
-
-class TNCLeg extends Component {
- static propTypes = {
- leg: PropTypes.object
- }
-
- render () {
- const { customIcons, leg } = this.props
- const { tncData } = leg
- if (!tncData) return null
-
- return (
-
-
{getLegIcon(leg, customIcons)}
-
-
- Take {tncData.displayName} to {leg.to.name}
-
-
-
- Estimated wait time for pickup:{' '}
- {formatDuration(tncData.estimatedArrival)}
-
-
- Estimated travel time:{' '}
- {formatDuration(leg.duration)} (does not account for traffic)
-
-
-
-
- )
- }
-}
diff --git a/lib/components/narrative/trip-details.js b/lib/components/narrative/trip-details.js
deleted file mode 100644
index 0b7118d4f..000000000
--- a/lib/components/narrative/trip-details.js
+++ /dev/null
@@ -1,157 +0,0 @@
-import React, { Component } from 'react'
-import { connect } from 'react-redux'
-import { Button } from 'react-bootstrap'
-import { VelocityTransitionGroup } from 'velocity-react'
-import moment from 'moment'
-
-import { calculateFares, calculatePhysicalActivity, getTimeZoneOffset } from '../../util/itinerary'
-import { formatTime, getTimeFormat, getLongDateFormat } from '../../util/time'
-
-class TripDetails extends Component {
- render () {
- const { itinerary, timeFormat, longDateFormat } = this.props
- const date = moment(itinerary.startTime)
-
- // process the transit fare
- const { centsToString, dollarsToString, maxTNCFare, minTNCFare, transitFare } = calculateFares(itinerary)
- let companies
- itinerary.legs.forEach(leg => {
- if (leg.tncData) {
- companies = leg.tncData.company
- }
- })
- let fare
- if (transitFare || minTNCFare) {
- fare = (
-
- {transitFare && (
- Transit Fare: {centsToString(transitFare)}
- )}
- {minTNCFare !== 0 && (
-
-
-
- {companies.toLowerCase()}
- {' '}
- Fare: {dollarsToString(minTNCFare)} - {dollarsToString(maxTNCFare)}
-
- )}
-
- )
- }
-
- // Compute calories burned.
- const { bikeDuration, caloriesBurned, walkDuration } = calculatePhysicalActivity(itinerary)
-
- const timeOptions = {
- format: timeFormat,
- offset: getTimeZoneOffset(itinerary)
- }
-
- return (
-
-
Trip Details
-
-
}
- summary={
-
- Depart {date.format(longDateFormat)}
- {this.props.routingType === 'ITINERARY' && at {formatTime(itinerary.startTime, timeOptions)} }
-
- }
- />
- {fare && (
-
}
- summary={fare}
- />
- )}
- {caloriesBurned > 0 && (
-
}
- summary={
Calories Burned: {Math.round(caloriesBurned)} }
- description={
-
- Calories burned is based on {Math.round(walkDuration / 60)} minute(s) {' '}
- spent walking and {Math.round(bikeDuration / 60)} minute(s) {' '}
- spent biking during this trip. Adapted from{' '}
-
- Dietary Guidelines for Americans 2005, page 16, Table 4
- .
-
- }
- />
- )}
-
-
- )
- }
-}
-
-class TripDetail extends Component {
- constructor (props) {
- super(props)
- this.state = {
- expanded: false
- }
- }
-
- _toggle = () => this.state.expanded ? this._onHideClick() : this._onExpandClick()
-
- _onExpandClick = () => {
- this.setState({ expanded: true })
- }
-
- _onHideClick = () => {
- this.setState({ expanded: false })
- }
-
- render () {
- const { icon, summary, description } = this.props
- return (
-
-
{icon}
-
- {summary}
- {description && (
-
-
-
- )}
-
- {this.state.expanded && (
-
-
-
-
- {description}
-
- )}
-
-
-
- )
- }
-}
-
-// Connect main class to redux store
-
-const mapStateToProps = (state, ownProps) => {
- return {
- routingType: state.otp.currentQuery.routingType,
- tnc: state.otp.tnc,
- timeFormat: getTimeFormat(state.otp.config),
- longDateFormat: getLongDateFormat(state.otp.config)
- }
-}
-
-export default connect(mapStateToProps)(TripDetails)
diff --git a/lib/index.css b/lib/index.css
index c987003c1..0fe0005bc 100644
--- a/lib/index.css
+++ b/lib/index.css
@@ -12,7 +12,6 @@
@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/narrative/printable/itinerary.css);
@import url(lib/components/mobile/mobile.css);
@import url(lib/components/viewers/viewers.css);
diff --git a/lib/index.js b/lib/index.js
index 40a8df088..ad1bb1d41 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -22,7 +22,7 @@ import NarrativeRoutingResults from './components/narrative/narrative-routing-re
import RealtimeAnnotation from './components/narrative/realtime-annotation'
import SimpleRealtimeAnnotation from './components/narrative/simple-realtime-annotation'
import TransportationNetworkCompanyLeg from './components/narrative/default/tnc-leg'
-import TripDetails from './components/narrative/trip-details'
+import TripDetails from './components/narrative/connected-trip-details'
import TripTools from './components/narrative/trip-tools'
import LineItinerary from './components/narrative/line-itin/line-itinerary'
diff --git a/package.json b/package.json
index efce70e74..765b004ed 100644
--- a/package.json
+++ b/package.json
@@ -34,13 +34,16 @@
"@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",
diff --git a/yarn.lock b/yarn.lock
index 17d14b221..7b89dac62 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1415,6 +1415,27 @@
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==
+ dependencies:
+ "@opentripplanner/core-utils" "^0.0.18"
+ "@opentripplanner/humanize-distance" "^0.0.18"
+ 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==
+ dependencies:
+ "@opentripplanner/core-utils" "^0.0.18"
+ "@opentripplanner/humanize-distance" "^0.0.18"
+ moment "^2.24.0"
+ 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"
@@ -16016,7 +16037,7 @@ velocity-animate@^1.4.0:
resolved "https://registry.yarnpkg.com/velocity-animate/-/velocity-animate-1.5.2.tgz#5a351d75fca2a92756f5c3867548b873f6c32105"
integrity sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==
-velocity-react@^1.3.3:
+velocity-react@^1.3.3, velocity-react@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/velocity-react/-/velocity-react-1.4.3.tgz#63e41d92e334d5a3bea8b2fa02ee170f62ef4d36"
integrity sha512-zvefGm85A88S3KdF9/dz5vqyFLAiwKYlXGYkHH2EbXl+CZUD1OT0a0aS1tkX/WXWTa/FUYqjBaAzAEFYuSobBQ==