From a32d9ee9d6e08930cf31e4a47a632010c35a079a Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Tue, 3 Sep 2024 13:30:30 -0700 Subject: [PATCH 1/7] sync sort order with departarrive --- lib/components/form/date-time-modal.js | 76 ------------- lib/components/form/date-time-modal.tsx | 107 ++++++++++++++++++ .../narrative-itineraries-header.tsx | 10 +- lib/util/config-types.ts | 6 + lib/util/state-types.ts | 16 ++- 5 files changed, 125 insertions(+), 90 deletions(-) delete mode 100644 lib/components/form/date-time-modal.js create mode 100644 lib/components/form/date-time-modal.tsx diff --git a/lib/components/form/date-time-modal.js b/lib/components/form/date-time-modal.js deleted file mode 100644 index 0936ebbe9..000000000 --- a/lib/components/form/date-time-modal.js +++ /dev/null @@ -1,76 +0,0 @@ -// TODO: TypeScript with props. -/* eslint-disable react/prop-types */ -import { connect } from 'react-redux' -import coreUtils from '@opentripplanner/core-utils' -import PropTypes from 'prop-types' -import React, { Component } from 'react' - -import { setQueryParam } from '../../actions/form' - -import { StyledDateTimeSelector } from './styled' - -class DateTimeModal extends Component { - static propTypes = { - setQueryParam: PropTypes.func - } - - render() { - const { - config, - date, - dateFormatLegacy, - departArrive, - setQueryParam, - time, - timeFormatLegacy - } = this.props - const { homeTimezone, isTouchScreenOnDesktop } = config - const touchClassName = isTouchScreenOnDesktop - ? 'with-desktop-touchscreen' - : '' - - return ( -
-
- `. - // These props are not relevant in modern browsers, - // where `` already - // formats the time|date according to the OS settings. - // eslint-disable-next-line react/jsx-sort-props - timeFormatLegacy={timeFormatLegacy} - timeZone={homeTimezone} - /> -
-
- ) - } -} - -const mapStateToProps = (state) => { - const { date, departArrive, time } = state.otp.currentQuery - const config = state.otp.config - return { - config, - date, - // This prop is for legacy browsers (see render method above). - dateFormatLegacy: coreUtils.time.getDateFormat(config), - departArrive, - time, - // This prop is for legacy browsers (see render method above). - timeFormatLegacy: coreUtils.time.getTimeFormat(config) - } -} - -const mapDispatchToProps = { - setQueryParam -} - -export default connect(mapStateToProps, mapDispatchToProps)(DateTimeModal) diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx new file mode 100644 index 000000000..7f83decd9 --- /dev/null +++ b/lib/components/form/date-time-modal.tsx @@ -0,0 +1,107 @@ +// TODO: TypeScript with props. +/* eslint-disable react/prop-types */ +import { connect } from 'react-redux' +import coreUtils from '@opentripplanner/core-utils' +import React from 'react' + +import { AppConfig } from '../../util/config-types' +import { AppReduxState, FilterType, SortType } from '../../util/state-types' +import { setQueryParam } from '../../actions/form' + +import { StyledDateTimeSelector } from './styled' +import { updateItineraryFilter } from '../../actions/narrative' + +type Props = { + config: AppConfig + date: string + dateFormatLegacy?: string + departArrive: DepartArriveValue + setQueryParam: (params: any) => void + sort: SortType + time: string + timeFormatLegacy?: string + updateItineraryFilter: (payload: FilterType) => void +} + +type DepartArriveValue = 'NOW' | 'DEPART' | 'ARRIVE' + +function DateTimeModal(props: Props) { + const { + config, + date, + dateFormatLegacy, + departArrive, + setQueryParam, + sort, + time, + timeFormatLegacy, + updateItineraryFilter + } = props + const { homeTimezone, isTouchScreenOnDesktop } = config + const touchClassName = isTouchScreenOnDesktop + ? 'with-desktop-touchscreen' + : '' + + const setQueryParamMiddleware = (params: any) => { + switch (params.departArrive) { + case 'NOW': + updateItineraryFilter({ sort: { ...sort, type: 'DURATION' } }) + break + case 'DEPART': + updateItineraryFilter({ sort: { ...sort, type: 'DEPARTURETIME' } }) + break + case 'ARRIVE': + updateItineraryFilter({ sort: { ...sort, type: 'ARRIVALTIME' } }) + break + } + setQueryParam(params) + } + return ( +
+
+ `. + // These props are not relevant in modern browsers, + // where `` already + // formats the time|date according to the OS settings. + // eslint-disable-next-line react/jsx-sort-props + timeFormatLegacy={timeFormatLegacy} + timeZone={homeTimezone} + /> +
+
+ ) +} + +const mapStateToProps = (state: AppReduxState) => { + const { date, departArrive, time } = state.otp.currentQuery + const config = state.otp.config + const { sort } = state.otp.filter + return { + config, + date, + // This prop is for legacy browsers (see render method above). + // @ts-expect-error Msimatched config types + dateFormatLegacy: coreUtils.time.getDateFormat(config), + departArrive, + sort, + time, + // This prop is for legacy browsers (see render method above). + // @ts-expect-error Msimatched config types + timeFormatLegacy: coreUtils.time.getTimeFormat(config) + } +} + +const mapDispatchToProps = { + setQueryParam, + updateItineraryFilter +} + +export default connect(mapStateToProps, mapDispatchToProps)(DateTimeModal) diff --git a/lib/components/narrative/narrative-itineraries-header.tsx b/lib/components/narrative/narrative-itineraries-header.tsx index 46fe56dff..921140c09 100644 --- a/lib/components/narrative/narrative-itineraries-header.tsx +++ b/lib/components/narrative/narrative-itineraries-header.tsx @@ -46,7 +46,6 @@ export default function NarrativeItinerariesHeader({ enabledSortModes, errors, itineraries, - itinerary, itineraryIsExpanded, onSortChange, onSortDirChange, @@ -109,13 +108,6 @@ export default function NarrativeItinerariesHeader({ const sortOptionsArr = sortOptions(intl, enabledSortModes) const sortText = sortOptionsArr.find((x) => x.value === sort.type)?.text - const handleSortClick = useCallback( - (value) => { - onSortChange(value) - }, - [onSortChange] - ) - return (
handleSortClick(sortOption.value)} + onClick={() => onSortChange(sortOption.value)} role="option" > {sortOption.text} diff --git a/lib/util/config-types.ts b/lib/util/config-types.ts index 3995d0ba8..761daf45e 100644 --- a/lib/util/config-types.ts +++ b/lib/util/config-types.ts @@ -356,6 +356,11 @@ export interface StopScheduleViewerConfig { showBlockIds?: boolean } +export interface DateTimeConfig { + dateFormat: string + timeFormat: string +} + /** The main application configuration object */ export interface AppConfig { accessibilityScore?: AccessibilityScoreConfig @@ -368,6 +373,7 @@ export interface AppConfig { bugsnag?: BugsnagConfig co2?: CO2Config companies?: Company[] + dateTime?: DateTimeConfig elevationProfile?: boolean extraMenuItems?: AppMenuItemConfig[] geocoder: GeocoderConfig diff --git a/lib/util/state-types.ts b/lib/util/state-types.ts index 3daba23c6..05b89d3cc 100644 --- a/lib/util/state-types.ts +++ b/lib/util/state-types.ts @@ -12,11 +12,8 @@ export interface OtpState { // TODO: Add other OTP states activeSearchId?: string config: AppConfig - filter: { - sort: { - type: string - } - } + currentQuery: any + filter: FilterType location: any overlay: any serviceTimeRange?: { @@ -28,6 +25,15 @@ export interface OtpState { ui: any // TODO } +export interface SortType { + direction: string + type: string +} + +export interface FilterType { + sort: SortType +} + export interface UserState { itineraryExistence?: ItineraryExistence localUser?: any From cc0d7e5d955c475ddbc07aa20ee5511f20993eda Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Tue, 3 Sep 2024 13:30:48 -0700 Subject: [PATCH 2/7] add datetime to example config --- example-config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/example-config.yml b/example-config.yml index 77caef21f..143084509 100644 --- a/example-config.yml +++ b/example-config.yml @@ -111,6 +111,9 @@ persistence: # iconType: 'car' # iconUrl: '' # href: '' +dateTime: + timeFormat: h:mm a + dateFormat: MM/dd/yyyy map: initLat: 45.52 From 6c01a5e066be468121964f35742bdcd7c731c39e Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Tue, 3 Sep 2024 14:07:37 -0700 Subject: [PATCH 3/7] add configuration for syncing sort --- example-config.yml | 2 ++ lib/components/form/date-time-modal.tsx | 36 ++++++++++++++----------- lib/util/config-types.ts | 1 + 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/example-config.yml b/example-config.yml index 143084509..99b64e377 100644 --- a/example-config.yml +++ b/example-config.yml @@ -414,6 +414,8 @@ itinerary: displayA11yError: false # Whether to display itinerary info in the side of the preview or next to the departure times showInlineItinerarySummary: false + # Whether to sync the sort type with the depart/arrive time in the date/time modal + syncSortWithDepartArrive: true # The sort option to use by default # Available sort options: 'BEST', 'DURATION', 'ARRIVALTIME', 'WALKTIME', 'COST', 'DEPARTURETIME' # defaultSort: "BEST" # Default diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx index 7f83decd9..f97450ec2 100644 --- a/lib/components/form/date-time-modal.tsx +++ b/lib/components/form/date-time-modal.tsx @@ -2,7 +2,7 @@ /* eslint-disable react/prop-types */ import { connect } from 'react-redux' import coreUtils from '@opentripplanner/core-utils' -import React from 'react' +import React, { useCallback } from 'react' import { AppConfig } from '../../util/config-types' import { AppReduxState, FilterType, SortType } from '../../util/state-types' @@ -42,20 +42,26 @@ function DateTimeModal(props: Props) { ? 'with-desktop-touchscreen' : '' - const setQueryParamMiddleware = (params: any) => { - switch (params.departArrive) { - case 'NOW': - updateItineraryFilter({ sort: { ...sort, type: 'DURATION' } }) - break - case 'DEPART': - updateItineraryFilter({ sort: { ...sort, type: 'DEPARTURETIME' } }) - break - case 'ARRIVE': - updateItineraryFilter({ sort: { ...sort, type: 'ARRIVALTIME' } }) - break - } - setQueryParam(params) - } + const syncSortWithDepartArrive = config?.itinerary?.syncSortWithDepartArrive + const setQueryParamMiddleware = useCallback( + (params: any) => { + if (syncSortWithDepartArrive !== false) { + switch (params.departArrive) { + case 'NOW': + updateItineraryFilter({ sort: { ...sort, type: 'DURATION' } }) + break + case 'DEPART': + updateItineraryFilter({ sort: { ...sort, type: 'DEPARTURETIME' } }) + break + case 'ARRIVE': + updateItineraryFilter({ sort: { ...sort, type: 'ARRIVALTIME' } }) + break + } + } + setQueryParam(params) + }, + [setQueryParam, updateItineraryFilter, sort, syncSortWithDepartArrive] + ) return (
diff --git a/lib/util/config-types.ts b/lib/util/config-types.ts index 761daf45e..eced69936 100644 --- a/lib/util/config-types.ts +++ b/lib/util/config-types.ts @@ -289,6 +289,7 @@ export interface ItineraryConfig { showPlanFirstLastButtons?: boolean showRouteFares?: boolean sortModes?: ItinerarySortOption[] + syncSortWithDepartArrive?: boolean weights?: ItineraryCostWeights } From a9c432bddd6a500037141b69a0dd0ddfa55cb1d4 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Tue, 17 Sep 2024 17:12:47 -0700 Subject: [PATCH 4/7] change default to false --- lib/components/form/date-time-modal.tsx | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx index f97450ec2..bf11a0806 100644 --- a/lib/components/form/date-time-modal.tsx +++ b/lib/components/form/date-time-modal.tsx @@ -25,6 +25,15 @@ type Props = { type DepartArriveValue = 'NOW' | 'DEPART' | 'ARRIVE' +const DepartArriveTypeMap: Record< + DepartArriveValue, + FilterType['sort']['type'] +> = { + ARRIVE: 'ARRIVALTIME', + DEPART: 'DEPARTURETIME', + NOW: 'DURATION' +} + function DateTimeModal(props: Props) { const { config, @@ -45,18 +54,13 @@ function DateTimeModal(props: Props) { const syncSortWithDepartArrive = config?.itinerary?.syncSortWithDepartArrive const setQueryParamMiddleware = useCallback( (params: any) => { - if (syncSortWithDepartArrive !== false) { - switch (params.departArrive) { - case 'NOW': - updateItineraryFilter({ sort: { ...sort, type: 'DURATION' } }) - break - case 'DEPART': - updateItineraryFilter({ sort: { ...sort, type: 'DEPARTURETIME' } }) - break - case 'ARRIVE': - updateItineraryFilter({ sort: { ...sort, type: 'ARRIVALTIME' } }) - break - } + if (syncSortWithDepartArrive) { + updateItineraryFilter({ + sort: { + ...sort, + type: DepartArriveTypeMap[params.departArrive as DepartArriveValue] + } + }) } setQueryParam(params) }, From fb98775d5ece3b30d6ceea82a4a5ca2edbfcde8f Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 25 Sep 2024 12:21:52 -0700 Subject: [PATCH 5/7] address PR cleanup --- example-config.yml | 2 ++ lib/components/form/date-time-modal.tsx | 37 ++++++++++++------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/example-config.yml b/example-config.yml index 968e9fae2..7fc8ed198 100644 --- a/example-config.yml +++ b/example-config.yml @@ -111,6 +111,8 @@ persistence: # iconType: 'car' # iconUrl: '' # href: '' + +### These settings are only used for the field trip features. dateTime: timeFormat: h:mm a dateFormat: MM/dd/yyyy diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx index bf11a0806..1f0e13557 100644 --- a/lib/components/form/date-time-modal.tsx +++ b/lib/components/form/date-time-modal.tsx @@ -1,12 +1,10 @@ -// TODO: TypeScript with props. -/* eslint-disable react/prop-types */ import { connect } from 'react-redux' import coreUtils from '@opentripplanner/core-utils' import React, { useCallback } from 'react' +import * as formActions from '../../actions/form' import { AppConfig } from '../../util/config-types' import { AppReduxState, FilterType, SortType } from '../../util/state-types' -import { setQueryParam } from '../../actions/form' import { StyledDateTimeSelector } from './styled' import { updateItineraryFilter } from '../../actions/narrative' @@ -34,24 +32,25 @@ const DepartArriveTypeMap: Record< NOW: 'DURATION' } -function DateTimeModal(props: Props) { - const { - config, - date, - dateFormatLegacy, - departArrive, - setQueryParam, - sort, - time, - timeFormatLegacy, - updateItineraryFilter - } = props +function DateTimeModal({ + config, + date, + dateFormatLegacy, + departArrive, + setQueryParam, + sort, + time, + timeFormatLegacy, + updateItineraryFilter +}: Props) { const { homeTimezone, isTouchScreenOnDesktop } = config const touchClassName = isTouchScreenOnDesktop ? 'with-desktop-touchscreen' : '' const syncSortWithDepartArrive = config?.itinerary?.syncSortWithDepartArrive + // Note the side effect that this will resort the results of a previous query + // if the user changes the depart/arrive setting before the query is run. const setQueryParamMiddleware = useCallback( (params: any) => { if (syncSortWithDepartArrive) { @@ -76,7 +75,7 @@ function DateTimeModal(props: Props) { departArrive={departArrive} onQueryParamChange={setQueryParamMiddleware} time={time} - // These props below are for Safari on MacOS, and legacy browsers + // These props below are for legacy browsers // that don't support ``. // These props are not relevant in modern browsers, // where `` already @@ -98,19 +97,19 @@ const mapStateToProps = (state: AppReduxState) => { config, date, // This prop is for legacy browsers (see render method above). - // @ts-expect-error Msimatched config types + // @ts-expect-error Mismatched config types dateFormatLegacy: coreUtils.time.getDateFormat(config), departArrive, sort, time, // This prop is for legacy browsers (see render method above). - // @ts-expect-error Msimatched config types + // @ts-expect-error Mismatched config types timeFormatLegacy: coreUtils.time.getTimeFormat(config) } } const mapDispatchToProps = { - setQueryParam, + setQueryParam: formActions.setQueryParam, updateItineraryFilter } From 70c1925ea354297262b84ec0bbc8fef239f0adbe Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 25 Sep 2024 12:33:50 -0700 Subject: [PATCH 6/7] Merge branch 'dev' into sync-sort-departarrive --- .../viewers/__snapshots__/nearby-view.js.snap | 1407 ++++++++++------- .../stop-schedule-viewer.ts.snap | 6 +- example-config.yml | 3 + i18n/en-US.yml | 8 + i18n/fr.yml | 8 + lib/components/app/batch-routing-panel.tsx | 206 ++- .../form/advanced-settings-button.tsx | 29 + .../form/advanced-settings-panel.tsx | 285 ++++ lib/components/form/batch-settings.tsx | 222 +-- lib/components/form/batch-styled.ts | 2 + lib/components/form/form.css | 4 +- lib/components/form/styled.ts | 75 + lib/components/form/util.tsx | 115 ++ lib/components/map/default-map.tsx | 28 +- lib/components/map/point-popup.tsx | 2 +- lib/components/mobile/batch-search-screen.tsx | 155 +- lib/components/mobile/mobile.css | 13 +- lib/components/util/prefersReducedMotion.tsx | 9 + lib/components/util/types.ts | 2 + lib/components/viewers/pattern-row.tsx | 12 +- lib/components/viewers/viewers.css | 10 +- lib/util/config-types.ts | 5 + lib/util/state-types.ts | 2 + package.json | 44 +- percy/percy.test.js | 25 +- yarn.lock | 227 +-- 26 files changed, 1888 insertions(+), 1016 deletions(-) create mode 100644 lib/components/form/advanced-settings-button.tsx create mode 100644 lib/components/form/advanced-settings-panel.tsx create mode 100644 lib/components/form/util.tsx create mode 100644 lib/components/util/prefersReducedMotion.tsx diff --git a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap index 3d4a9501a..41b77e69d 100644 --- a/__tests__/components/viewers/__snapshots__/nearby-view.js.snap +++ b/__tests__/components/viewers/__snapshots__/nearby-view.js.snap @@ -46,7 +46,7 @@ exports[`components > viewers > nearby view renders nothing on a blank page 1`] className="nearby-view base-color-bg" >
viewers > nearby view renders nothing on a blank page 1`] } >
    viewers > nearby view renders proper scooter dates 1`] = ` className="nearby-view base-color-bg" >
    viewers > nearby view renders proper scooter dates 1`] = ` } >
      viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
      viewers > nearby view renders proper scooter dates 1`] = ` >

      Roosevelt Station - Bay 2 @@ -10036,7 +10036,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

      viewers > nearby view renders proper scooter dates 1`] = `

      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
      • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Loyal Heights Greenwood" > - Loyal Heights Greenwood + + components.NearbyView.headsign +

          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

        1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Downtown Seattle Fremont" > - Downtown Seattle Fremont + + components.NearbyView.headsign +

            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

          1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Roosevelt Station Sand Point" > - Roosevelt Station Sand Point + + components.NearbyView.headsign +

              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

              viewers > nearby view renders proper scooter dates 1`] = ` >

              Roosevelt @@ -16883,7 +16910,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

              viewers > nearby view renders proper scooter dates 1`] = `

              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
              • viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Angle Lake" > - Angle Lake + + components.NearbyView.headsign +

                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                1. viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Northgate" > - Northgate + + components.NearbyView.headsign +

                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Capitol Hill" > - Capitol Hill + + components.NearbyView.headsign +

                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                      viewers > nearby view renders proper scooter dates 1`] = ` >

                      Roosevelt Station - Bay 1 @@ -26397,7 +26451,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

                      viewers > nearby view renders proper scooter dates 1`] = `

                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                        viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                      • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` } title="University District Roosevelt Station" > - University District Roosevelt Station + + components.NearbyView.headsign +

                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                        1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Sand Point East Green Lake" > - Sand Point East Green Lake + + components.NearbyView.headsign +

                            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                          1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` } title="University District Sand Point" > - University District Sand Point + + components.NearbyView.headsign +

                              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                            1. viewers > nearby view renders proper scooter dates 1`] = ` title="988" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Madrona Special" > - Madrona Special + + components.NearbyView.headsign +

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                viewers > nearby view renders proper scooter dates 1`] = ` >

                                Roosevelt Station Bay 5 - Bay 5 @@ -35949,7 +36039,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

                                viewers > nearby view renders proper scooter dates 1`] = `

                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                  viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                • viewers > nearby view renders proper scooter dates 1`] = ` title="67" > viewers > nearby view renders proper scooter dates 1`] = ` } title="University District Roosevelt" > - University District Roosevelt + + components.NearbyView.headsign +

                                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="73" > viewers > nearby view renders proper scooter dates 1`] = ` } title="University District Maple Leaf" > - University District Maple Leaf + + components.NearbyView.headsign +

                                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                    1. viewers > nearby view renders proper scooter dates 1`] = ` title="984" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Downtown Seattle Special" > - Downtown Seattle Special + + components.NearbyView.headsign +

                                        viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = `

                                        viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                        viewers > nearby view renders proper scooter dates 1`] = ` >

                                        Roosevelt @@ -43780,7 +43897,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

                                        viewers > nearby view renders proper scooter dates 1`] = `

                                        viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                          viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                        • viewers > nearby view renders proper scooter dates 1`] = ` title="1 Line" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Northgate" > - Northgate + + components.NearbyView.headsign +

                                            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = ` >

                                            Roosevelt Station - Bay 3 @@ -51332,7 +51458,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

                                            viewers > nearby view renders proper scooter dates 1`] = `

                                            viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                              viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                            • viewers > nearby view renders proper scooter dates 1`] = ` title="522" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Bothell" > - Bothell + + components.NearbyView.headsign +

                                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                              1. viewers > nearby view renders proper scooter dates 1`] = ` title="67" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Northgate Station Roosevelt Station" > - Northgate Station Roosevelt Station + + components.NearbyView.headsign +

                                                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                1. viewers > nearby view renders proper scooter dates 1`] = ` title="522" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Woodinville" > - Woodinville + + components.NearbyView.headsign +

                                                    viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                  1. viewers > nearby view renders proper scooter dates 1`] = ` title="73" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Jackson Park Maple Leaf" > - Jackson Park Maple Leaf + + components.NearbyView.headsign +

                                                      viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                    1. viewers > nearby view renders proper scooter dates 1`] = ` title="322" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Kenmore P&R Roosevelt Station" > - Kenmore P&R Roosevelt Station + + components.NearbyView.headsign +

                                                        viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                      1. viewers > nearby view renders proper scooter dates 1`] = ` title="322" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Kenmore P&R" > - Kenmore P&R + + components.NearbyView.headsign +

                                                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = `

                                                          viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                                          viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          NE 65th St & 14th Ave NE @@ -62499,7 +62679,7 @@ exports[`components > viewers > nearby view renders proper scooter dates 1`] = ` >

                                                          viewers > nearby view renders proper scooter dates 1`] = `

                                                          viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` size="0.9em" > viewers > nearby view renders proper scooter dates 1`] = ` >
                                                            viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >
                                                          • viewers > nearby view renders proper scooter dates 1`] = ` title="45" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Loyal Heights Greenwood" > - Loyal Heights Greenwood + + components.NearbyView.headsign +

                                                              viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                            1. viewers > nearby view renders proper scooter dates 1`] = ` title="62" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Downtown Seattle Fremont" > - Downtown Seattle Fremont + + components.NearbyView.headsign +

                                                                viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` roundedTop={false} >

                                                              1. viewers > nearby view renders proper scooter dates 1`] = ` title="79" > viewers > nearby view renders proper scooter dates 1`] = ` } title="Roosevelt Station Sand Point" > - Roosevelt Station Sand Point + + components.NearbyView.headsign +

                                                                  viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.realtime" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` iconViewBox="0 0 448 512" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > nearby view renders proper scooter dates 1`] = ` title="components.StopTimeCell.scheduled" > viewers > nearby view renders proper scooter dates 1`] = ` > viewers > stop viewer should render with initial stop id a > viewers > stop viewer should render with initial stop id a

                                                          - @@ -357,6 +364,7 @@ components: distanceAway: "{localizedDistanceString} away" error: An error occurred loading nearby amenities. header: Nearby View + headsign: "{destination}" nearbyListIntro: List of {count} nearby entities. nothingNearby: There are no places nearby. spacesAvailable: "{spacesAvailable} empty spaces available" diff --git a/i18n/fr.yml b/i18n/fr.yml index 082b0e96a..741ad9f2c 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -209,8 +209,15 @@ components: BatchRoutingPanel: shortTitle: Planifier un trajet BatchSearchScreen: + advancedHeader: Préférences avancées header: Votre trajet + modeOptions: Choix du mode modeSelectorLabel: Sélectionnez un mode de déplacement + moreOptions: Plus d'options + saveAndReturn: Enregistrer et fermer + saved: Enregistré + submodeSelectorLabel: Sélectionnez vos modes et sous-modes de déplacement + tripOptions: Options concernant le trajet BatchSettings: destination: destination invalidModeSelection: >- @@ -376,6 +383,7 @@ components: distanceAway: à {localizedDistanceString} error: Erreur lors du chargement des services à proximité. header: À proximité + headsign: "{destination}" nearbyListIntro: Liste de {count} entités à proximité. nothingNearby: Aucun lieu à proximité. spacesAvailable: "{spacesAvailable} emplacements libres disponibles" diff --git a/lib/components/app/batch-routing-panel.tsx b/lib/components/app/batch-routing-panel.tsx index c1321091e..5c6a69c73 100644 --- a/lib/components/app/batch-routing-panel.tsx +++ b/lib/components/app/batch-routing-panel.tsx @@ -1,9 +1,17 @@ import { connect } from 'react-redux' +import { CSSTransition, TransitionGroup } from 'react-transition-group' import { FormattedMessage, injectIntl, IntlShape } from 'react-intl' import React, { Component, FormEvent } from 'react' +import { + advancedPanelClassName, + mainPanelClassName, + transitionDuration, + TransitionStyles +} from '../form/styled' import { getActiveSearch, getShowUserSettings } from '../../util/state' import { getPersistenceMode } from '../../util/user' +import AdvancedSettingsPanel from '../form/advanced-settings-panel' import BatchSettings from '../form/batch-settings' import InvisibleA11yLabel from '../util/invisible-a11y-label' import LocationField from '../form/connected-location-field' @@ -15,6 +23,7 @@ import ViewerContainer from '../viewers/viewer-container' interface Props { activeSearch: any intl: IntlShape + mainPanelContent: number mobile?: boolean showUserSettings: boolean } @@ -24,7 +33,43 @@ interface Props { */ class BatchRoutingPanel extends Component { state = { - planTripClicked: false + closeAdvancedSettingsWithDelay: false, + planTripClicked: false, + showAdvancedModeSettings: false + } + + _advancedSettingRef = React.createRef() + _mainPanelContentRef = React.createRef() + _itinerariesAndUserRef = React.createRef() + + componentDidUpdate(prevProps: Readonly): void { + // Close the advanced mode settings if we navigate to another page + if ( + prevProps.mainPanelContent === null && + this.props.mainPanelContent !== null && + this.state.showAdvancedModeSettings + ) { + this.setState({ + showAdvancedModeSettings: false + }) + } + } + + openAdvancedSettings = () => { + this.setState({ + closeAdvancedSettingsWithDelay: false, + showAdvancedModeSettings: true + }) + } + + closeAdvancedSettings = () => { + this.setState({ showAdvancedModeSettings: false }) + } + + setCloseAdvancedSettingsWithDelay = () => { + this.setState({ + closeAdvancedSettingsWithDelay: true + }) } handleSubmit = (e: FormEvent) => e.preventDefault() @@ -44,6 +89,11 @@ class BatchRoutingPanel extends Component { id: 'common.searchForms.click' }) + /* If there is a save button in advanced preferences, add a transition delay to allow + the saved state to be displayed to users */ + const transitionDelay = this.state.closeAdvancedSettingsWithDelay ? 300 : 0 + const transitionDurationWithDelay = transitionDuration + transitionDelay + return ( { height: '100%' }} > - -

                                                          - -

                                                          -
                                                          -
                                                          - - + {!this.state.showAdvancedModeSettings && ( + +

                                                          + +

                                                          +
                                                          + )} + + + {this.state.showAdvancedModeSettings && ( + + + )} - isRequired - locationType="from" - selfValidate={planTripClicked} - showClearButton={!mobile} - /> - +
                                                          + + + +
                                                          + +
                                                          +
                                                          + +
                                                          + )} - isRequired - locationType="to" - selfValidate={planTripClicked} - showClearButton={!mobile} - /> -
                                                          - -
                                                          -
                                                          - - - {!activeSearch && showUserSettings && ( - - )} -
                                                          - -
                                                          + + + + {!this.state.showAdvancedModeSettings && ( + +
                                                          + {!activeSearch && showUserSettings && ( + + )} +
                                                          + +
                                                          +
                                                          +
                                                          + )} +
                                                          +
                                                          ) } @@ -115,8 +222,11 @@ const mapStateToProps = (state: any) => { getShowUserSettings(state) && (state.user.loggedInUser?.hasConsentedToTerms || getPersistenceMode(state.otp.config.persistence).isLocalStorage) + const { mainPanelContent } = state.otp.ui + return { activeSearch: getActiveSearch(state), + mainPanelContent, showUserSettings } } diff --git a/lib/components/form/advanced-settings-button.tsx b/lib/components/form/advanced-settings-button.tsx new file mode 100644 index 000000000..0449dc85d --- /dev/null +++ b/lib/components/form/advanced-settings-button.tsx @@ -0,0 +1,29 @@ +import { ArrowRight } from '@styled-icons/fa-solid' +import { FormattedMessage } from 'react-intl' + +import { grey } from '../util/colors' +import React from 'react' +import styled from 'styled-components' + +interface Props { + onClick: () => void +} + +const StyledTransparentButton = styled.button` + align-items: center; + background: transparent; + border: none; + color: ${grey[800]}; + display: flex; + gap: 7px; + margin-bottom: 5px; +` + +const AdvancedSettingsButton = ({ onClick }: Props): JSX.Element => ( + + + + +) + +export default AdvancedSettingsButton diff --git a/lib/components/form/advanced-settings-panel.tsx b/lib/components/form/advanced-settings-panel.tsx new file mode 100644 index 000000000..ea085d017 --- /dev/null +++ b/lib/components/form/advanced-settings-panel.tsx @@ -0,0 +1,285 @@ +import { + addSettingsToButton, + AdvancedModeSubsettingsContainer, + ModeSettingRenderer, + populateSettingWithValue +} from '@opentripplanner/trip-form' +import { ArrowLeft } from '@styled-icons/fa-solid/ArrowLeft' +import { Check } from '@styled-icons/boxicons-regular' +import { connect } from 'react-redux' +import { decodeQueryParams, DelimitedArrayParam } from 'serialize-query-params' +import { FormattedMessage, useIntl } from 'react-intl' +import { invisibleCss } from '@opentripplanner/trip-form/lib/MetroModeSelector' +import { + ModeButtonDefinition, + ModeSetting, + ModeSettingValues +} from '@opentripplanner/types' +import React, { RefObject, useCallback, useContext, useState } from 'react' +import styled from 'styled-components' + +import * as formActions from '../../actions/form' +import { AppReduxState } from '../../util/state-types' +import { blue, getBaseColor } from '../util/colors' +import { ComponentContext } from '../../util/contexts' +import { generateModeSettingValues } from '../../util/api' + +import { + addCustomSettingLabels, + addModeButtonIcon, + onSettingsUpdate, + pipe, + populateSettingWithIcon, + setModeButton +} from './util' +import { setModeButtonEnabled } from './batch-settings' +import { styledCheckboxCss } from './styled' +import DateTimeModal from './date-time-modal' + +const PanelOverlay = styled.div` + height: 100%; + left: 0; + overflow-y: auto; + padding: 1.5em; + position: absolute; + top: 0; + width: 100%; + z-index: 100; +` + +const GlobalSettingsContainer = styled.div` + display: flex; + flex-direction: column; + gap: 13px; + margin-bottom: 2em; + + ${styledCheckboxCss} +` + +const CloseButton = styled.button` + background: transparent; + border: none; +` + +const HeaderContainer = styled.div` + align-items: center; + display: flex; + gap: 10px; + height: 30px; +` + +const Subheader = styled.h2` + ${invisibleCss} +` + +const ReturnToTripPlanButton = styled.button` + align-items: center; + background-color: var(--main-base-color, ${blue[900]}); + border: 0; + color: white; + display: flex; + font-weight: 700; + gap: 5px; + height: 51px; + justify-content: center; + margin-top: 2em; + width: 100%; + + svg { + margin-bottom: 7px; + } +` + +const DtSelectorContainer = styled.div` + margin: 2em 0; + + .date-time-modal { + padding: 0; + + .main-panel { + margin: 0; + + button { + padding: 6px 0; + } + + .date-time-selector { + margin: 15px 0; + } + } + } +` + +const AdvancedSettingsPanel = ({ + closeAdvancedSettings, + enabledModeButtons, + innerRef, + modeButtonOptions, + modeSettingDefinitions, + modeSettingValues, + saveAndReturnButton, + setCloseAdvancedSettingsWithDelay, + setQueryParam +}: { + closeAdvancedSettings: () => void + enabledModeButtons: string[] + innerRef: RefObject + modeButtonOptions: ModeButtonDefinition[] + modeSettingDefinitions: ModeSetting[] + modeSettingValues: ModeSettingValues + saveAndReturnButton?: boolean + setCloseAdvancedSettingsWithDelay: () => void + setQueryParam: (evt: any) => void +}): JSX.Element => { + const [closingBySave, setClosingBySave] = useState(false) + const baseColor = getBaseColor() + const accentColor = baseColor || blue[900] + + const intl = useIntl() + const closeButtonText = intl.formatMessage({ + id: 'components.BatchSearchScreen.saveAndReturn' + }) + const headerText = intl.formatMessage({ + id: 'components.BatchSearchScreen.advancedHeader' + }) + + // @ts-expect-error Context not typed + const { ModeIcon } = useContext(ComponentContext) + + const processSettings = (settings: ModeSetting[]) => + settings.map( + pipe( + populateSettingWithIcon(ModeIcon), + populateSettingWithValue(modeSettingValues), + addCustomSettingLabels(intl) + ) + ) + + const globalSettings = modeSettingDefinitions.filter((x) => !x.applicableMode) + const processedGlobalSettings = processSettings(globalSettings) + + const globalSettingsComponents = processedGlobalSettings.map( + (setting: ModeSetting) => ( + + ) + ) + + const processedModeSettings = processSettings(modeSettingDefinitions) + const processedModeButtons = modeButtonOptions.map( + pipe( + addModeButtonIcon(ModeIcon), + addSettingsToButton(processedModeSettings), + setModeButtonEnabled(enabledModeButtons) + ) + ) + + const onSaveAndReturnClick = useCallback(async () => { + await setCloseAdvancedSettingsWithDelay() + setClosingBySave(true) + closeAdvancedSettings() + }, [closeAdvancedSettings, setCloseAdvancedSettingsWithDelay]) + + return ( + + + { + closeAdvancedSettings() + }} + title={closeButtonText} + > + + +

                                                          {headerText}

                                                          +
                                                          + + + + {processedGlobalSettings.length > 0 && ( + <> + + + + + {globalSettingsComponents} + + + )} + + + + + {saveAndReturnButton && ( + + {closingBySave ? ( + <> + + + + ) : ( + + )} + + )} +
                                                          + ) +} + +const queryParamConfig = { modeButtons: DelimitedArrayParam } + +const mapStateToProps = (state: AppReduxState) => { + const urlSearchParams = new URLSearchParams(state.router.location.search) + const { modes } = state.otp.config + const modeSettingValues = generateModeSettingValues( + urlSearchParams, + state.otp.modeSettingDefinitions || [], + modes?.initialState?.modeSettingValues || {} + ) + const saveAndReturnButton = + state.otp.config?.advancedSettingsPanel?.saveAndReturnButton + return { + currentQuery: state.otp.currentQuery, + // TODO: Duplicated in apiv2.js + enabledModeButtons: + decodeQueryParams(queryParamConfig, { + modeButtons: urlSearchParams.get('modeButtons') + })?.modeButtons?.filter((mb): mb is string => mb !== null) || + modes?.initialState?.enabledModeButtons || + [], + modeButtonOptions: modes?.modeButtons || [], + modeSettingDefinitions: state.otp?.modeSettingDefinitions || [], + modeSettingValues, + saveAndReturnButton + } +} + +const mapDispatchToProps = { + setQueryParam: formActions.setQueryParam, + updateQueryTimeIfLeavingNow: formActions.updateQueryTimeIfLeavingNow +} + +export default connect( + mapStateToProps, + mapDispatchToProps +)(AdvancedSettingsPanel) diff --git a/lib/components/form/batch-settings.tsx b/lib/components/form/batch-settings.tsx index 63e748973..23bd13a6c 100644 --- a/lib/components/form/batch-settings.tsx +++ b/lib/components/form/batch-settings.tsx @@ -1,19 +1,7 @@ -import { - addSettingsToButton, - MetroModeSelector, - populateSettingWithValue -} from '@opentripplanner/trip-form' import { connect } from 'react-redux' -import { - decodeQueryParams, - DelimitedArrayParam, - encodeQueryParams -} from 'use-query-params' -import { - ModeButtonDefinition, - ModeSetting, - ModeSettingValues -} from '@opentripplanner/types' +import { decodeQueryParams } from 'use-query-params' +import { MetroModeSelector } from '@opentripplanner/trip-form' +import { ModeButtonDefinition } from '@opentripplanner/types' import { Search } from '@styled-icons/fa-solid/Search' import { SyncAlt } from '@styled-icons/fa-solid/SyncAlt' import { useIntl } from 'react-intl' @@ -22,22 +10,26 @@ import React, { useCallback, useContext, useState } from 'react' import * as apiActions from '../../actions/api' import * as formActions from '../../actions/form' import { ComponentContext } from '../../util/contexts' -import { generateModeSettingValues } from '../../util/api' import { getActiveSearch, hasValidLocation } from '../../util/state' import { getBaseColor, getDarkenedBaseColor } from '../util/colors' -import { getFormattedMode } from '../../util/i18n' -import { RoutingQueryCallResult } from '../../actions/api-constants' import { StyledIconWrapper } from '../util/styledIcon' +import { + addModeButtonIcon, + alertUserTripPlan, + modesQueryParamConfig, + onSettingsUpdate, + pipe, + setModeButton +} from './util' import { MainSettingsRow, ModeSelectorContainer, PlanTripButton } from './batch-styled' +import AdvancedSettingsButton from './advanced-settings-button' import DateTimeButton from './date-time-button' -const queryParamConfig = { modeButtons: DelimitedArrayParam } - // TYPESCRIPT TODO: better types type Props = { activeSearch: any @@ -45,18 +37,11 @@ type Props = { enabledModeButtons: string[] fillModeIcons?: boolean modeButtonOptions: ModeButtonDefinition[] - modeSettingDefinitions: ModeSetting[] - modeSettingValues: ModeSettingValues onPlanTripClick: () => void + openAdvancedSettings: () => void routingQuery: any setQueryParam: (evt: any) => void spacedOutModeSelector?: boolean - updateQueryTimeIfLeavingNow: () => void -} - -// This method is used to daisy-chain a series of functions together on a given value -function pipe(...fns: Array<(arg: T) => T>) { - return (value: T) => fns.reduce((acc, fn) => fn(acc), value) } export function setModeButtonEnabled(enabledKeys: string[]) { @@ -77,174 +62,37 @@ function BatchSettings({ enabledModeButtons, fillModeIcons, modeButtonOptions, - modeSettingDefinitions, - modeSettingValues, onPlanTripClick, + openAdvancedSettings, routingQuery, setQueryParam, - spacedOutModeSelector, - updateQueryTimeIfLeavingNow + spacedOutModeSelector }: Props) { const intl = useIntl() // Whether the date/time selector is open const [dateTimeOpen, setDateTimeOpen] = useState(false) - // Whether the mode selector has a popup open - const [modeSelectorPopup, setModeSelectorPopup] = useState(false) - // @ts-expect-error Context not typed const { ModeIcon } = useContext(ComponentContext) - const addModeButtonIcon = useCallback( - (def: ModeButtonDefinition) => ({ - ...def, - Icon: function ModeButtonIcon() { - return - } - }), - [ModeIcon] - ) - - const populateSettingWithIcon = useCallback( - (msd: ModeSetting) => ({ - ...msd, - icon: - }), - [ModeIcon] - ) - - const addCustomSettingLabels = useCallback( - (msd: ModeSetting) => { - let modeLabel - // If we're using route mode overrides, make sure we're using the custom mode name - if (msd.type === 'SUBMODE') { - modeLabel = msd.overrideMode || msd.addTransportMode.mode - return { - ...msd, - label: getFormattedMode(modeLabel, intl) - } - } - return msd - }, - [intl] - ) - - const processedModeSettings = modeSettingDefinitions.map( - pipe( - populateSettingWithIcon, - populateSettingWithValue(modeSettingValues), - addCustomSettingLabels - ) - ) - const processedModeButtons = modeButtonOptions.map( - pipe( - addModeButtonIcon, - addSettingsToButton(processedModeSettings), - setModeButtonEnabled(enabledModeButtons) - ) + pipe(addModeButtonIcon(ModeIcon), setModeButtonEnabled(enabledModeButtons)) ) const _planTrip = useCallback(() => { - // Check for any validation issues in query. - const issues = [] - if (!hasValidLocation(currentQuery, 'from')) { - issues.push(intl.formatMessage({ id: 'components.BatchSettings.origin' })) - } - if (!hasValidLocation(currentQuery, 'to')) { - issues.push( - intl.formatMessage({ id: 'components.BatchSettings.destination' }) - ) - } - onPlanTripClick && onPlanTripClick() - if (issues.length > 0) { - // TODO: replace with less obtrusive validation. - window.alert( - intl.formatMessage( - { id: 'components.BatchSettings.validationMessage' }, - { issues: intl.formatList(issues, { type: 'conjunction' }) } - ) - ) - return - } - - // Plan trip. - updateQueryTimeIfLeavingNow() - const routingQueryResult = routingQuery() - - // If mode combination is not valid (i.e. produced no query), alert the user. - if (routingQueryResult === RoutingQueryCallResult.INVALID_MODE_SELECTION) { - window.alert( - intl.formatMessage({ - id: 'components.BatchSettings.invalidModeSelection' - }) - ) - } - }, [ - currentQuery, - intl, - onPlanTripClick, - routingQuery, - updateQueryTimeIfLeavingNow - ]) - - /** - * Stores parameters in both the Redux `currentQuery` and URL - * @param params Params to store - */ - const _onSettingsUpdate = useCallback( - (params: any) => { - setQueryParam({ queryParamData: params, ...params }) - }, - [setQueryParam] - ) - - const _toggleModeButton = useCallback( - (buttonId: string, newState: boolean) => { - let newButtons - if (newState) { - newButtons = [...enabledModeButtons, buttonId] - } else { - newButtons = enabledModeButtons.filter((c) => c !== buttonId) - } - - // encodeQueryParams serializes the mode buttons for the URL - // to get nice looking URL params and consistency - _onSettingsUpdate( - encodeQueryParams(queryParamConfig, { modeButtons: newButtons }) - ) - }, - [enabledModeButtons, _onSettingsUpdate] - ) - - /** - * Check whether the mode selector is showing a popup. - */ - const checkModeSelectorPopup = useCallback(() => { - const modeSelectorPopup = document.querySelector( - '.metro-mode-selector div[role="dialog"]' - ) - setModeSelectorPopup(!!modeSelectorPopup) - }, [setModeSelectorPopup]) + alertUserTripPlan(intl, currentQuery, onPlanTripClick, routingQuery) + }, [currentQuery, intl, onPlanTripClick, routingQuery]) const baseColor = getBaseColor() const accentColor = getDarkenedBaseColor() return ( - - - + + + + { const urlSearchParams = new URLSearchParams(state.router.location.search) - const modeSettingValues = generateModeSettingValues( - urlSearchParams, - state.otp?.modeSettingDefinitions || [], - state.otp.config.modes?.initialState?.modeSettingValues - ) + const { modes } = state.otp.config return { activeSearch: getActiveSearch(state), currentQuery: state.otp.currentQuery, // TODO: Duplicated in apiv2.js enabledModeButtons: - decodeQueryParams(queryParamConfig, { + decodeQueryParams(modesQueryParamConfig, { modeButtons: urlSearchParams.get('modeButtons') })?.modeButtons || - state.otp.config?.modes?.initialState?.enabledModeButtons || + modes?.initialState?.enabledModeButtons || {}, fillModeIcons: state.otp.config.itinerary?.fillModeIcons, - modeButtonOptions: state.otp.config?.modes?.modeButtons || [], - modeSettingDefinitions: state.otp?.modeSettingDefinitions || [], - modeSettingValues, - spacedOutModeSelector: state.otp?.config?.modes?.spacedOut + modeButtonOptions: modes?.modeButtons || [], + spacedOutModeSelector: modes?.spacedOut } } const mapDispatchToProps = { routingQuery: apiActions.routingQuery, - setQueryParam: formActions.setQueryParam, - updateQueryTimeIfLeavingNow: formActions.updateQueryTimeIfLeavingNow + setQueryParam: formActions.setQueryParam } export default connect(mapStateToProps, mapDispatchToProps)(BatchSettings) diff --git a/lib/components/form/batch-styled.ts b/lib/components/form/batch-styled.ts index 2283f3b9e..4fe5d9da5 100644 --- a/lib/components/form/batch-styled.ts +++ b/lib/components/form/batch-styled.ts @@ -65,6 +65,8 @@ export const ModeSelectorContainer = styled.div<{ squashed?: boolean }>` align-items: flex-start; display: flex; float: right; + justify-content: space-between; + width: 100%; ${PlanTripButton} { border-bottom-left-radius: ${(props) => (props.squashed ? 0 : 'invalid')}; diff --git a/lib/components/form/form.css b/lib/components/form/form.css index 7ef399d67..9f694e155 100644 --- a/lib/components/form/form.css +++ b/lib/components/form/form.css @@ -93,14 +93,14 @@ position: absolute; top: 22px; right: 32px; - z-index: 100000; + z-index: 99; } .otp .switch-button-container-mobile { position: absolute; top: 32px; right: 45px; - z-index: 100000; + z-index: 99; } /* Settings Selector Panel */ diff --git a/lib/components/form/styled.ts b/lib/components/form/styled.ts index 06e9449f7..5033db42f 100644 --- a/lib/components/form/styled.ts +++ b/lib/components/form/styled.ts @@ -5,6 +5,7 @@ import { Styled as TripFormClasses } from '@opentripplanner/trip-form' import { Input, MenuItemLi } from '@opentripplanner/location-field/lib/styled' +import { prefersReducedMotion } from '../util/prefersReducedMotion' import LocationField from '@opentripplanner/location-field' import styled, { css } from 'styled-components' @@ -230,3 +231,77 @@ export const StyledLocationField = styled(LocationField)` } } ` + +export const advancedPanelClassName = 'advanced-panel' +export const mainPanelClassName = 'main-panel' +export const transitionDuration = prefersReducedMotion ? 0 : 175 + +const wipeOffset = 7 + +const transitionMixin = css` + transition: all ${transitionDuration}ms ease-in-out; +` + +const wipeOutMixin = (offset: number) => css` + opacity: 0; + transform: translateX(${offset}px); +` +const wipeInMixin = css` + opacity: 1; +` + +export const TransitionStyles = styled.div<{ transitionDelay: number }>` + display: contents; + .${advancedPanelClassName}-enter { + ${wipeOutMixin(wipeOffset)} + } + .${advancedPanelClassName}-enter-done { + ${wipeInMixin} + ${transitionMixin} + } + + .${advancedPanelClassName}-exit { + ${wipeInMixin} + } + + .${advancedPanelClassName}-exit-active { + ${wipeOutMixin(wipeOffset)} + ${transitionMixin} + transition-delay: ${(props) => props.transitionDelay}ms; + } + + .${mainPanelClassName}-enter { + ${wipeOutMixin(-wipeOffset)} + } + .${mainPanelClassName}-enter-done { + ${wipeInMixin} + ${transitionMixin} + } + + .${mainPanelClassName}-exit { + ${wipeInMixin} + } + + .${mainPanelClassName}-exit-active { + ${wipeOutMixin(-wipeOffset)} + ${transitionMixin} + } +` +export const styledCheckboxCss = css` + div { + align-items: center; + justify-content: space-between; + + label { + margin-bottom: 0; + } + input[type='checkbox'] { + margin-top: 0; + order: 2; + + &:focus-visible + label { + outline: 1px solid blue; + } + } + } +` diff --git a/lib/components/form/util.tsx b/lib/components/form/util.tsx new file mode 100644 index 000000000..85dd3107d --- /dev/null +++ b/lib/components/form/util.tsx @@ -0,0 +1,115 @@ +import { DelimitedArrayParam, encodeQueryParams } from 'serialize-query-params' +import { IntlShape } from 'react-intl' +import { ModeButtonDefinition, ModeSetting } from '@opentripplanner/types' +import React from 'react' + +import { getFormattedMode } from '../../util/i18n' +import { hasValidLocation } from '../../util/state' +import { QueryParamChangeHandler } from '../util/types' +import { RoutingQueryCallResult } from '../../actions/api-constants' +import { updateQueryTimeIfLeavingNow } from '../../actions/form' + +// This method is used to daisy-chain a series of functions together on a given value +export function pipe(...fns: Array<(arg: T) => T>) { + return (value: T) => fns.reduce((acc, fn) => fn(acc), value) +} + +export const modesQueryParamConfig = { modeButtons: DelimitedArrayParam } + +export const populateSettingWithIcon = + (ModeIcon: React.ComponentType<{ mode?: string; width?: number }>) => + // eslint-disable-next-line react/display-name + (modeSetting: ModeSetting): ModeSetting => ({ + ...modeSetting, + icon: + }) + +export const addModeButtonIcon = + (ModeIcon: React.ComponentType<{ mode?: string; width?: number }>) => + (def: ModeButtonDefinition): ModeButtonDefinition => ({ + ...def, + Icon: function ModeButtonIcon() { + return + } + }) + +export const addCustomSettingLabels = + (intl: IntlShape) => + (modeSetting: ModeSetting): ModeSetting => { + // If we're using route mode overrides, make sure we're using the custom mode name + if (modeSetting.type === 'SUBMODE') { + const modeLabel = + modeSetting.overrideMode || modeSetting.addTransportMode.mode + return { + ...modeSetting, + label: getFormattedMode(modeLabel, intl) + } + } + return modeSetting + } + +/** + * Stores parameters in both the Redux `currentQuery` and URL + * @param params Params to store + */ +export const onSettingsUpdate = + (setQueryParam: QueryParamChangeHandler) => + (params: any): void => { + setQueryParam({ queryParamData: params, ...params }) + } + +export const setModeButton = + (enabledModeButtons: string[], updateHandler: (params: any) => void) => + (buttonId: string, newState: boolean): void => { + const newButtons = newState + ? [...enabledModeButtons, buttonId] + : enabledModeButtons.filter((c) => c !== buttonId) + + // encodeQueryParams serializes the mode buttons for the URL + // to get nice looking URL params and consistency + updateHandler( + encodeQueryParams(modesQueryParamConfig, { modeButtons: newButtons }) + ) + } + +export const alertUserTripPlan = ( + intl: IntlShape, + currentQuery: any, + onPlanTripClick: () => void, + routingQuery: () => any +): void => { + // Check for any validation issues in query. + const issues: string[] = [] + if (!hasValidLocation(currentQuery, 'from')) { + issues.push(intl.formatMessage({ id: 'components.BatchSettings.origin' })) + } + if (!hasValidLocation(currentQuery, 'to')) { + issues.push( + intl.formatMessage({ id: 'components.BatchSettings.destination' }) + ) + } + onPlanTripClick() + if (issues.length > 0) { + // TODO: replace with less obtrusive validation. + window.alert( + intl.formatMessage( + { id: 'components.BatchSettings.validationMessage' }, + { issues: intl.formatList(issues, { type: 'conjunction' }) } + ) + ) + return + } + + // Plan trip. + updateQueryTimeIfLeavingNow() + const routingQueryResult = routingQuery() + + // If mode combination is not valid (i.e. produced no query), alert the user. + if (routingQueryResult === RoutingQueryCallResult.INVALID_MODE_SELECTION) { + window.alert( + intl.formatMessage({ + id: 'components.BatchSettings.invalidModeSelection' + }) + ) + } +} diff --git a/lib/components/map/default-map.tsx b/lib/components/map/default-map.tsx index 6c50d1899..38d7570e4 100644 --- a/lib/components/map/default-map.tsx +++ b/lib/components/map/default-map.tsx @@ -267,7 +267,8 @@ class DefaultMap extends Component { setLocation, setViewedStop, vehicleRentalQuery, - vehicleRentalStations + vehicleRentalStations, + viewedRouteStops } = this.props const { getCustomMapOverlays, getTransitiveRouteLabel, ModeIcon } = this.context @@ -405,7 +406,7 @@ class DefaultMap extends Component { vectorTilesEndpoint, setLocation, setViewedStop, - null, + viewedRouteStops, config.companies ) default: @@ -428,6 +429,26 @@ class DefaultMap extends Component { const mapStateToProps = (state) => { const activeSearch = getActiveSearch(state) + const viewedRoute = state.otp?.ui?.viewedRoute?.routeId + const nearbyViewerActive = + state.otp.ui.mainPanelContent === MainPanelContent.NEARBY_VIEW + + const viewedRoutePatterns = Object.entries( + state.otp?.transitIndex?.routes?.[viewedRoute]?.patterns || {} + ) + const viewedRouteStops = + viewedRoute && !nearbyViewerActive + ? // Ensure we don't have duplicates + Array.from( + new Set( + // Generate a list of every stop id the pattern stops at + viewedRoutePatterns.reduce((acc, cur) => { + // Convert pattern object to list of the pattern's stops + return [...cur?.[1]?.stops.map((s) => s.id), ...acc] + }, []) + ) + ) + : null return { bikeRentalStations: state.otp.overlay.bikeRental.stations, @@ -439,7 +460,8 @@ const mapStateToProps = (state) => { state.otp.ui.mainPanelContent === MainPanelContent.NEARBY_VIEW, pending: activeSearch ? Boolean(activeSearch.pending) : false, query: state.otp.currentQuery, - vehicleRentalStations: state.otp.overlay.vehicleRental.stations + vehicleRentalStations: state.otp.overlay.vehicleRental.stations, + viewedRouteStops } } diff --git a/lib/components/map/point-popup.tsx b/lib/components/map/point-popup.tsx index d7121b34b..071901434 100644 --- a/lib/components/map/point-popup.tsx +++ b/lib/components/map/point-popup.tsx @@ -82,7 +82,7 @@ function MapPopup({ // Override inline style supplied by react-map-gl to accommodate long "plan a trip" translations. style={{ maxWidth: '260px', width: '260px' }} > - + {typeof popupName === 'string' && popupName.split(',').length > 3 diff --git a/lib/components/mobile/batch-search-screen.tsx b/lib/components/mobile/batch-search-screen.tsx index 930112a88..c24b4dacb 100644 --- a/lib/components/mobile/batch-search-screen.tsx +++ b/lib/components/mobile/batch-search-screen.tsx @@ -1,9 +1,18 @@ import { connect } from 'react-redux' +import { CSSTransition, TransitionGroup } from 'react-transition-group' import { injectIntl, IntlShape } from 'react-intl' import React, { Component } from 'react' +import styled from 'styled-components' import * as uiActions from '../../actions/ui' +import { + advancedPanelClassName, + mainPanelClassName, + transitionDuration, + TransitionStyles +} from '../form/styled' import { MobileScreens } from '../../actions/ui-constants' +import AdvancedSettingsPanel from '../form/advanced-settings-panel' import BatchSettings from '../form/batch-settings' import DefaultMap from '../map/default-map' import LocationField from '../form/connected-location-field' @@ -14,6 +23,25 @@ import MobileNavigationBar from './navigation-bar' const { SET_FROM_LOCATION, SET_TO_LOCATION } = MobileScreens +const MobileSearchSettings = styled.div<{ + advancedPanelOpen: boolean + transitionDelay: number + transitionDuration: number +}>` + background: white; + box-shadow: 3px 0px 12px #00000052; + height: ${(props) => + props.advancedPanelOpen ? 'calc(100% - 50px)' : '230px'}; + left: 0; + position: fixed; + right: 0; + top: 50px; + transition: ${(props) => `all ${props.transitionDuration}ms ease`}; + transition-delay: ${(props) => props.transitionDelay}ms; + /* Must appear under the 'hamburger' dropdown which has z-index of 1000. */ + z-index: 999; +` + interface Props { intl: IntlShape map: React.ReactElement @@ -22,20 +50,45 @@ interface Props { class BatchSearchScreen extends Component { state = { - planTripClicked: false + closeAdvancedSettingsWithDelay: false, + planTripClicked: false, + showAdvancedModeSettings: false } _fromFieldClicked = () => this.props.setMobileScreen(SET_FROM_LOCATION) _toFieldClicked = () => this.props.setMobileScreen(SET_TO_LOCATION) + _mainPanelContentRef = React.createRef() + _advancedSettingRef = React.createRef() + handlePlanTripClick = () => { this.setState({ planTripClicked: true }) } + openAdvancedSettings = () => { + this.setState({ + closeAdvancedSettingsWithDelay: false, + showAdvancedModeSettings: true + }) + } + + closeAdvancedSettings = () => { + this.setState({ showAdvancedModeSettings: false }) + } + + setCloseAdvancedSettingsWithDelay = () => { + this.setState({ + closeAdvancedSettingsWithDelay: true + }) + } + render() { const { intl } = this.props - const { planTripClicked } = this.state + const { planTripClicked, showAdvancedModeSettings } = this.state + + const transitionDelay = this.state.closeAdvancedSettingsWithDelay ? 300 : 0 + const transitionDurationWithDelay = transitionDuration + transitionDelay return ( { })} />
                                                          -
                                                          - - -
                                                          - -
                                                          - -
                                                          + + + + {/* Unfortunately we can't use a ternary operator here because it is cancelling out the CSSTransition animations. */} + {!showAdvancedModeSettings && ( + +
                                                          + + +
                                                          + +
                                                          + +
                                                          +
                                                          + )} + {showAdvancedModeSettings && ( + + + + )} +
                                                          +
                                                          +
                                                          diff --git a/lib/components/mobile/mobile.css b/lib/components/mobile/mobile.css index b0b6fb3db..b1197cfbd 100644 --- a/lib/components/mobile/mobile.css +++ b/lib/components/mobile/mobile.css @@ -121,20 +121,9 @@ /* Batch routing search screen */ -.otp.mobile .batch-search-settings { - position: fixed; - top: 50px; - left: 0; - right: 0; - height: 216px; - /* Must appear under the 'hamburger' dropdown which has z-index of 1000. */ - z-index: 999; - box-shadow: 3px 0px 12px #00000052; -} - .otp.mobile .batch-search-map { position: fixed; - top: 266px; + top: 282px; left: 0; right: 0; bottom: 0; diff --git a/lib/components/util/prefersReducedMotion.tsx b/lib/components/util/prefersReducedMotion.tsx new file mode 100644 index 000000000..69e13b7d4 --- /dev/null +++ b/lib/components/util/prefersReducedMotion.tsx @@ -0,0 +1,9 @@ +/** + * Identifies whether the user's machine has "reduced motion" enabled + * in their local settings. If reduced motion is on, the app should + * show as few animations & transitions as possible. + * @returns boolean reflecting whether user prefers reduced motion + */ +export const prefersReducedMotion = window.matchMedia( + '(prefers-reduced-motion: reduce)' +).matches diff --git a/lib/components/util/types.ts b/lib/components/util/types.ts index 9b21030fa..83bcd7930 100644 --- a/lib/components/util/types.ts +++ b/lib/components/util/types.ts @@ -108,3 +108,5 @@ export type ZoomToPlaceHandler = ( place?: { lat: number; lon: number }, zoom?: number ) => void + +export type QueryParamChangeHandler = (event: any) => void diff --git a/lib/components/viewers/pattern-row.tsx b/lib/components/viewers/pattern-row.tsx index 73bc22006..4f87a8c7a 100644 --- a/lib/components/viewers/pattern-row.tsx +++ b/lib/components/viewers/pattern-row.tsx @@ -1,5 +1,6 @@ import { Calendar } from '@styled-icons/fa-regular' import { format, utcToZonedTime } from 'date-fns-tz' +import { FormattedMessage } from 'react-intl' import { getMostReadableTextColor } from '@opentripplanner/core-utils/lib/route' import { isSameDay } from 'date-fns' import React, { useContext } from 'react' @@ -108,8 +109,15 @@ const PatternRow = ({ {pattern.route.longName} )} - {extractHeadsignFromPattern(pattern) || - (pattern.route.longName !== routeName && pattern.route.longName)} +

                                                          {/* next departure preview (only shows up to 3 entries) */} diff --git a/lib/components/viewers/viewers.css b/lib/components/viewers/viewers.css index 31e260402..1ea4637a9 100644 --- a/lib/components/viewers/viewers.css +++ b/lib/components/viewers/viewers.css @@ -60,12 +60,18 @@ } .otp .trip-viewer .header-text, -.otp .route-viewer .header-text { - display: contents; +.otp .route-viewer .header-text, +.otp .advanced-settings .header-text { font-weight: 700; font-size: 24px; margin: 0; } + +.otp .trip-viewer .header-text, +.otp .route-viewer .header-text { + display: contents; +} + .otp .route-viewer .header-text.route-expanded { display: flex; align-items: center; diff --git a/lib/util/config-types.ts b/lib/util/config-types.ts index d44980640..3f71126cb 100644 --- a/lib/util/config-types.ts +++ b/lib/util/config-types.ts @@ -339,6 +339,10 @@ export interface TransitOperatorConfig extends TransitOperator { routeIcons?: boolean } +export interface AdvancedSettingsPanelConfig { + saveAndReturnButton?: boolean +} + /** Route Viewer config */ export interface RouteViewerConfig { /** Whether to hide the route linear shape inside a flex zone of that route. */ @@ -367,6 +371,7 @@ export interface DateTimeConfig { /** The main application configuration object */ export interface AppConfig { accessibilityScore?: AccessibilityScoreConfig + advancedSettingsPanel?: AdvancedSettingsPanelConfig api: ApiConfig // Optional on declaration, populated with defaults in reducer if not configured. autoPlan?: boolean | AutoPlanConfig diff --git a/lib/util/state-types.ts b/lib/util/state-types.ts index 05b89d3cc..4c5c70a3b 100644 --- a/lib/util/state-types.ts +++ b/lib/util/state-types.ts @@ -5,6 +5,7 @@ import { MonitoredTrip, User } from '../components/user/types' +import { ModeSetting } from '@opentripplanner/types' import { AppConfig } from './config-types' @@ -15,6 +16,7 @@ export interface OtpState { currentQuery: any filter: FilterType location: any + modeSettingDefinitions: ModeSetting[] overlay: any serviceTimeRange?: { end: number diff --git a/package.json b/package.json index 5b4a1881e..34e1e7c8f 100644 --- a/package.json +++ b/package.json @@ -42,34 +42,35 @@ "@bugsnag/js": "^7.17.0", "@bugsnag/plugin-react": "^7.17.0", "@floating-ui/react": "^0.19.2", - "@opentripplanner/base-map": "^3.2.2", - "@opentripplanner/building-blocks": "^1.2.3", - "@opentripplanner/core-utils": "^11.4.4", - "@opentripplanner/endpoints-overlay": "^2.1.4", - "@opentripplanner/from-to-location-picker": "^2.1.14", + "@opentripplanner/base-map": "4.0.0", + "@opentripplanner/building-blocks": "2.0.0", + "@opentripplanner/core-utils": "12.0.0", + "@opentripplanner/endpoints-overlay": "3.0.0", + "@opentripplanner/from-to-location-picker": "3.0.0", "@opentripplanner/geocoder": "^3.0.2", "@opentripplanner/humanize-distance": "^1.2.0", - "@opentripplanner/icons": "^2.0.13", - "@opentripplanner/itinerary-body": "^5.3.7", - "@opentripplanner/location-field": "^2.0.24", + "@opentripplanner/icons": "3.0.0", + "@opentripplanner/itinerary-body": "6.0.0", + "@opentripplanner/location-field": "3.0.0", "@opentripplanner/location-icon": "^1.4.1", - "@opentripplanner/map-popup": "^4.0.2", - "@opentripplanner/otp2-tile-overlay": "^1.0.17", - "@opentripplanner/park-and-ride-overlay": "^2.0.9", - "@opentripplanner/printable-itinerary": "^2.0.23", - "@opentripplanner/route-viewer-overlay": "^2.0.17", - "@opentripplanner/stop-viewer-overlay": "^2.0.10", - "@opentripplanner/stops-overlay": "^5.3.3", - "@opentripplanner/transit-vehicle-overlay": "^4.0.13", - "@opentripplanner/transitive-overlay": "3.0.22", - "@opentripplanner/trip-details": "^5.0.15", - "@opentripplanner/trip-form": "^3.6.4", - "@opentripplanner/trip-viewer-overlay": "^2.0.10", - "@opentripplanner/vehicle-rental-overlay": "^2.1.9", + "@opentripplanner/map-popup": "5.0.0", + "@opentripplanner/otp2-tile-overlay": "2.0.0", + "@opentripplanner/park-and-ride-overlay": "3.0.0", + "@opentripplanner/printable-itinerary": "3.0.0", + "@opentripplanner/route-viewer-overlay": "3.0.0", + "@opentripplanner/stop-viewer-overlay": "3.0.0", + "@opentripplanner/stops-overlay": "6.0.0", + "@opentripplanner/transit-vehicle-overlay": "5.0.0", + "@opentripplanner/transitive-overlay": "4.0.0", + "@opentripplanner/trip-details": "6.0.0", + "@opentripplanner/trip-form": "4.0.0", + "@opentripplanner/trip-viewer-overlay": "3.0.0", + "@opentripplanner/vehicle-rental-overlay": "3.0.0", "@styled-icons/fa-regular": "^10.34.0", "@styled-icons/fa-solid": "^10.34.0", "@turf/centroid": "^6.5.0", "@turf/helpers": "^6.5.0", + "@types/react-transition-group": "^4.4.10", "blob-stream": "^0.1.3", "bootstrap": "^3.3.7", "bowser": "^1.9.3", @@ -118,6 +119,7 @@ "react-router-dom": "^5.3.4", "react-select": "^3.1.0", "react-sliding-pane": "^7.0.0", + "react-transition-group": "^4.4.5", "redux": "^4.0.4", "redux-actions": "^1.2.1", "redux-logger": "^2.7.4", diff --git a/percy/percy.test.js b/percy/percy.test.js index c4ac7142e..6e8e8b1e6 100644 --- a/percy/percy.test.js +++ b/percy/percy.test.js @@ -133,16 +133,19 @@ async function executeTest(page, isMobile, isCallTaker) { await page.keyboard.press('Escape') await page.waitForTimeout(200) - // Check submode selector (this will have no effect on mock query) - await page.hover('label[title="Transit"]') + // Open advanced settings and wait for animation + await page.click('#open-advanced-settings-button') await page.waitForTimeout(500) + + // Check submode selector (this will have no effect on mock query) await page.click('#id-query-param-tram') // Enable accessible routing (this will have no effect on mock query) - await page.hover('label[title="Transit"]') - await page.waitForTimeout(500) await page.click('#id-query-param-wheelchair') + // Close advanced settings + await page.click('#close-advanced-settings-button') + await page.waitForTimeout(500) // Delete both origin and destination await page.click('.from-form-control') @@ -189,14 +192,12 @@ async function executeTest(page, isMobile, isCallTaker) { await page.waitForTimeout(1000) // wait extra time for all results to load if (!isMobile) { - await page.hover('label[title="Transit"]') - await page.waitForTimeout(200) - await percySnapshotWithWait( - page, - 'Metro Transit-Walk Itinerary Desktop with Mode Selector Expanded' - ) - // Hover something else to unhover the mode selector. - await page.hover('#plan-trip') + await page.click('#open-advanced-settings-button') + await page.waitForTimeout(500) + await percySnapshotWithWait(page, 'Metro Advanced Settings Open') + // Close advanced settings + await page.click('#close-advanced-settings-button') + await page.waitForTimeout(500) } else { await percySnapshotWithWait(page, 'Metro Transit-Walk Itinerary Mobile') } diff --git a/yarn.lock b/yarn.lock index 930fe34b0..599332b3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2383,12 +2383,12 @@ dependencies: "@octokit/openapi-types" "^10.0.0" -"@opentripplanner/base-map@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-3.2.0.tgz#db4410319d9614077ec925d739165a998c4a2485" - integrity sha512-d/yTKEnXqrw9pXhSvCERT+wLFa077Xr4wEFu4pYB+WYoZFflNxuTuAXXjm268HS/d0kjNndkjSMkxaKk6AjsvA== +"@opentripplanner/base-map@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-4.0.0.tgz#56ffa1d833673282cc3a0b7a17f388fc5dbd31e3" + integrity sha512-pWTKXxnzUQk43woPMc40uYfGIcGqHV8GoCvRwrIu2pqNw7QAV4rxjZfca0pm5hnbbJ+G83sRzYboILEbEUwMcw== dependencies: - "@opentripplanner/map-popup" "^3.1.0" + "@opentripplanner/building-blocks" "^1.2.2" mapbox-gl "npm:empty-npm-package@1.0.0" maplibre-gl "^2.1.9" react-map-gl "^7.0.15" @@ -2403,19 +2403,24 @@ maplibre-gl "^2.1.9" react-map-gl "^7.0.15" -"@opentripplanner/building-blocks@^1.2.2", "@opentripplanner/building-blocks@^1.2.3": +"@opentripplanner/building-blocks@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/building-blocks/-/building-blocks-2.0.0.tgz#8282c01dff7db5c7e809f6ea91cb52df559a2f9d" + integrity sha512-N07rDaZL8fp552eI9/0j1udKjc0uOpvO0Wv1P19Ge0a4roques463MJgWJ026fbopRCi3uwbc/gYTlh4/ske9A== + +"@opentripplanner/building-blocks@^1.0.3", "@opentripplanner/building-blocks@^1.2.2": version "1.2.3" resolved "https://registry.yarnpkg.com/@opentripplanner/building-blocks/-/building-blocks-1.2.3.tgz#404e8f9038867d66d55f51adf8855b1326c51ed5" integrity sha512-I0AxiZrTZu+e7+av4u0tHW2ijqpxH0AkLHrhf75BHf1Ep2FOGxaul/v+8UT18mNYiM5eHNstOX3XiXaDjtCUaw== -"@opentripplanner/core-utils@^11.4.0": - version "11.4.2" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.4.2.tgz#cc6034fb80ccda44e50f7f0a1e80a7bad8387f84" - integrity sha512-EVYVN73Cgf9IC+uya49843MFJnVkmv0nHAjsQwmPGSx/w5fY49X4fSpDprL7Bn+MTzk58U2udDsn6OzKmV0JdA== +"@opentripplanner/core-utils@12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-12.0.0.tgz#cc40af92620b207f4dce817d08f99def0cdaea7a" + integrity sha512-udLF8XU+k7gxZ+yyyw7ASz6/4D540zYIv8a9GbUL61TF8HmgGhcMk3XOgBnm5jdOukuaNNpOFE4J3oJc5QsSBQ== dependencies: "@conveyal/lonlat" "^1.4.1" "@mapbox/polyline" "^1.1.0" - "@opentripplanner/geocoder" "^3.0.0" + "@opentripplanner/geocoder" "^3.0.2" "@styled-icons/foundation" "^10.34.0" "@turf/along" "^6.0.1" chroma-js "^2.4.2" @@ -2444,10 +2449,10 @@ lodash.isequal "^4.5.0" qs "^6.9.1" -"@opentripplanner/endpoints-overlay@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-2.1.4.tgz#f81088bce83236344dfa4a51b2efe00092a5c87a" - integrity sha512-VLRZArhoRQ38aafc/w986Uv1lnq/WLJOgBqnpvuUbLhLR/qHU9h5X3wg3jgwf2GA0BIn03Z99VJbCkGfkyd0LA== +"@opentripplanner/endpoints-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-3.0.0.tgz#17bb943f5bf2b8571cb808b12b5a2185fd18196a" + integrity sha512-st6vfLRCBzVollYS4nIXghbjApDq73lcExo7hZh60DCVSGESSZA9jsNE6ff0HmvhYHdag9QV9zbZOFuNxPIrng== dependencies: "@opentripplanner/base-map" "^3.2.2" "@opentripplanner/building-blocks" "^1.2.2" @@ -2456,10 +2461,10 @@ "@styled-icons/fa-solid" "^10.34.0" flat "^5.0.2" -"@opentripplanner/from-to-location-picker@^2.1.12", "@opentripplanner/from-to-location-picker@^2.1.13": - version "2.1.13" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-2.1.13.tgz#d13acd582929175c676cd4303a6cdc6e1c289d99" - integrity sha512-6/7+wYQuuQhnGvxkDQcvoACdmuwUL1BlPqBIUFwyBpkdJ1VQGZiUSAAZTxXdY1Fv/p5mKR1vRsvZgtSPhcxgcg== +"@opentripplanner/from-to-location-picker@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-3.0.0.tgz#048a596c2f854825e0058e03dac67dcad7eb5864" + integrity sha512-jRXaY9jKg+PXUL7Z2SkHRyO88xG1t7iG3U449LPiCm/6flxsY+Wlxg+nyMsAP5gQMjOU0wsGLdH83lGgrpSF4A== dependencies: "@opentripplanner/location-icon" "^1.4.1" flat "^5.0.2" @@ -2472,17 +2477,6 @@ "@opentripplanner/location-icon" "^1.4.1" flat "^5.0.2" -"@opentripplanner/geocoder@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-3.0.1.tgz#834960bc52f515e1223346a8002fb847674d33bc" - integrity sha512-+LHTqY8pHmPE39IjVev5T5baa+BohEyvsLwVwFB2bYWzM+m/RgAJ188uBcDzXKdqk5y3dZR9ZODYVMtrvIiKzQ== - dependencies: - "@conveyal/geocoder-arcgis-geojson" "^0.0.3" - "@conveyal/lonlat" "^1.4.1" - "@leeoniya/ufuzzy" "^1.0.14" - isomorphic-mapzen-search "^1.6.1" - lodash.memoize "^4.1.2" - "@opentripplanner/geocoder@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-3.0.2.tgz#2c7618947d1d9b082bd39d037327c9cf23282782" @@ -2499,7 +2493,15 @@ resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-1.2.0.tgz#71cf5d5d1b756adef15300edbba0995ccd4b35ee" integrity sha512-x0QRXMDhypFeazZ6r6vzrdU8vhiV56nZ/WX6zUbxpgp6T9Oclw0gwR2Zdw6DZiiFpSYVNeVNxVzZwsu6NRGjcA== -"@opentripplanner/icons@^2.0.12", "@opentripplanner/icons@^2.0.13": +"@opentripplanner/icons@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-3.0.0.tgz#f7293fd4dd2625eace3a4c82ecd573d6000d85d3" + integrity sha512-naSCdCsPwSyEiP7Vf6oN6dpgwpFIkeQFXfTJG7lp1Dg9emLTAYzRx/f+45e9Bh0zP0aA4DsN4VgHBQllyu82qQ== + dependencies: + "@opentripplanner/core-utils" "^11.4.4" + prop-types "^15.7.2" + +"@opentripplanner/icons@^2.0.12": version "2.0.13" resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-2.0.13.tgz#45c4c16d8f208cff73811941f2def0fa23f87780" integrity sha512-1oEPCmFuyS88bJZ2U9eFlEw2kQ0ZZW+wOI1dggr0omJDD6L+nVNQJ6TUtosNHYL1S35Jpx4aSQEG3iwwlXOHMg== @@ -2507,7 +2509,25 @@ "@opentripplanner/core-utils" "^11.4.4" prop-types "^15.7.2" -"@opentripplanner/itinerary-body@^5.3.6", "@opentripplanner/itinerary-body@^5.3.7": +"@opentripplanner/itinerary-body@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-6.0.0.tgz#e261bcccd0d7a786f7f17be987a52ba1dc940229" + integrity sha512-j79byCefyEZuomvDlvBZJVZJ92+X6U4ivth3M62RKGmw1x8qW4nsbEKnzeQxXzGJcy3M+91eeIpcJnj98KHlRw== + dependencies: + "@opentripplanner/core-utils" "^11.4.4" + "@opentripplanner/humanize-distance" "^1.2.0" + "@opentripplanner/icons" "^2.0.12" + "@opentripplanner/location-icon" "^1.4.1" + "@styled-icons/fa-solid" "^10.34.0" + "@styled-icons/foundation" "^10.34.0" + date-fns "^2.28.0" + date-fns-tz "^1.2.2" + flat "^5.0.2" + react-animate-height "^3.0.4" + react-resize-detector "^4.2.1" + string-similarity "^4.0.4" + +"@opentripplanner/itinerary-body@^5.3.6": version "5.3.7" resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-5.3.7.tgz#e32437f804defc19aef23685e45c0a363ac8bc31" integrity sha512-ZnbxI78WsY6/Ynl3QY026JqimGG+gqXcN8ZEXY/BPMrNhSul27HgFFT2Flo37Fnc5gN2+jE8LsR1yxq05H3kvQ== @@ -2525,10 +2545,10 @@ react-resize-detector "^4.2.1" string-similarity "^4.0.4" -"@opentripplanner/location-field@^2.0.24": - version "2.0.24" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-2.0.24.tgz#32e7109142bd754d28bd28ebbdf68d4e5ef4c812" - integrity sha512-fOAyanDnLLHC39kHG6kMSY6i09n4l0KSVQACFoosGZgUcJmz5CUCMl0/x3RszIwh3g2wqxKh6fagh4V56YEpfQ== +"@opentripplanner/location-field@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-3.0.0.tgz#a6f8743290cf088bcd589cf8e6b0d07a651b704a" + integrity sha512-wPb9l5pvSeocZ45K1E3Zeb4hDjJtP9tw/S0MQ3HXYEQSf+700n4CWXVXYReYJXJYriOzge5/2whjUPGTlJckWw== dependencies: "@conveyal/geocoder-arcgis-geojson" "^0.0.3" "@opentripplanner/core-utils" "^11.4.4" @@ -2546,16 +2566,18 @@ "@styled-icons/fa-regular" "^10.34.0" "@styled-icons/fa-solid" "^10.34.0" -"@opentripplanner/map-popup@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-3.1.1.tgz#54f081162d328cc4bb0e89562f9ea200e29e01a1" - integrity sha512-yWBIPuYGw7biaRNIpglQm5+opZ+D5QQgXHLhKnYaCR0eNijjl9cx34lGXdyKPXt26S6MiyJZXL81uc6w6CnQ3A== +"@opentripplanner/map-popup@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-5.0.0.tgz#cf360845704ed54222c8bf19581ed1253d3dcf14" + integrity sha512-cuIzZm/cZbjY2tAVLSDpBK63efR+YsEsXVPBx4VAHlGhJoY1yooipFlq+3/51VSsFOJPp6gcsKaizfdICDJlYA== dependencies: - "@opentripplanner/core-utils" "^11.4.0" - "@opentripplanner/from-to-location-picker" "^2.1.12" + "@opentripplanner/base-map" "^3.2.2" + "@opentripplanner/building-blocks" "^1.2.2" + "@opentripplanner/core-utils" "^11.4.4" + "@opentripplanner/from-to-location-picker" "^2.1.14" flat "^5.0.2" -"@opentripplanner/map-popup@^4.0.0", "@opentripplanner/map-popup@^4.0.2": +"@opentripplanner/map-popup@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-4.0.2.tgz#367ab7ce69d16d319988bb48d8f6d2db762623c7" integrity sha512-RlHv9GE3Bk3++PwBcaPcALr6rZ+2AxY6Uj6W71AnLqz+wbeQO5rM3eEP99r0Sg1K3pAY0hXljBkVwKiUWhwxbQ== @@ -2566,33 +2588,33 @@ "@opentripplanner/from-to-location-picker" "^2.1.14" flat "^5.0.2" -"@opentripplanner/otp2-tile-overlay@^1.0.17": - version "1.0.17" - resolved "https://registry.yarnpkg.com/@opentripplanner/otp2-tile-overlay/-/otp2-tile-overlay-1.0.17.tgz#0e63bcb956778bbab6bd42d282aa9f5416881b74" - integrity sha512-mgMHprVVOXdzgU0D/50be57TeuEp1RP+b7xH/3Xt+rj0mF1PY+5GgLMuEul+1WqbzFwAefrmi9KRMs3MYJArrg== +"@opentripplanner/otp2-tile-overlay@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/otp2-tile-overlay/-/otp2-tile-overlay-2.0.0.tgz#6af1a9113a8baaebb226a5e747e8e523f6da249b" + integrity sha512-Yc0VsfxS6xIw4+1i9lpvQyCXobkTubZWYYu+6bDWkk77D4J4WaSoWE/qWT39vc2/h1ZY4afUZL59d7kc8V0PLg== dependencies: "@opentripplanner/map-popup" "^4.0.0" -"@opentripplanner/park-and-ride-overlay@^2.0.9": - version "2.0.9" - resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-2.0.9.tgz#0efe2bf8a7595b56c4da6396e89db5f04e4b3ec8" - integrity sha512-ekf6kcCgMVTzXDMY3Ed8qclaL3YY2/1BrArdpRY8DxciGWmE1HKOW90Vf1aP18aLrwcW9kpvv1Kdbl60tY6mCQ== +"@opentripplanner/park-and-ride-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/park-and-ride-overlay/-/park-and-ride-overlay-3.0.0.tgz#7139d5358bc8e5f68f1574e9225a3bcfb57993be" + integrity sha512-MLalzHhXSZ1yAwW9cyMVoUUQPMGktTTPFbhgEB/Ft4fX/TJAIH1sQHsKoaeGE0+48Kxg4M49zPoeNujr9RIeGA== dependencies: - "@opentripplanner/base-map" "^3.2.0" - "@opentripplanner/from-to-location-picker" "^2.1.13" + "@opentripplanner/base-map" "^3.2.2" + "@opentripplanner/from-to-location-picker" "^2.1.14" -"@opentripplanner/printable-itinerary@^2.0.23": - version "2.0.23" - resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-2.0.23.tgz#31f11e0e22bb9f223c4c1d8fa97c6eea18496cfa" - integrity sha512-Cb5SX7ts0rX0P1XSStjqbTITP5ueWlDe1b7U4KMaRNqEOaK2tELEyb/qZGo9kMpXTT/17jmEQZsKHQj43C5Eyg== +"@opentripplanner/printable-itinerary@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-3.0.0.tgz#7f4438b18cbd767ede74b2a3b7e3ad6ab1ec936b" + integrity sha512-/P7HtzXPu0yP6ZnfHl1DQFTVgEx+YplLxs43jd7rnXc0PUvsVyAsmp4oI3TDG8tStMD9pKsVLOCBNLUacoXo1Q== dependencies: "@opentripplanner/core-utils" "^11.4.4" "@opentripplanner/itinerary-body" "^5.3.6" -"@opentripplanner/route-viewer-overlay@^2.0.17": - version "2.0.17" - resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-2.0.17.tgz#f34686fd965cf39650a10f64df599e7aca468415" - integrity sha512-3UTTLxHhaMg4iKP4oJlobvUCbvC/TjCW6ss8PxxC3UurwiMeSFNVkaWGLElPc9YoKg0QqKxrIY7zq0WClIPa6g== +"@opentripplanner/route-viewer-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-3.0.0.tgz#d307af1fb734f113627a82f816bf4683a7d9a860" + integrity sha512-Kbgs+gkzOKNEoXG2ImoUBekET5olMc8sGo9ltb7tgTg8ZuBcq3woMID7b3+n3DDdvqdHLExg5T68gkip1Eu/QQ== dependencies: "@mapbox/polyline" "^1.1.0" "@opentripplanner/base-map" "^3.2.2" @@ -2610,38 +2632,38 @@ glob-promise "^4.2.2" js-yaml "^4.1.0" -"@opentripplanner/stop-viewer-overlay@^2.0.10": - version "2.0.10" - resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-2.0.10.tgz#0749fe7ffb28dac7a6925ed7c663e2fdd5156f3b" - integrity sha512-rFmaqQ7uJ+ZE80O6fveiNxlEVSJW5PwFSMh1B9pN0HaVTB1U27+yKbIMMuP7GtWve31mPy+PaWYXDW3hMlsi8A== +"@opentripplanner/stop-viewer-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-3.0.0.tgz#69dc8af797ab9a202954137b6ea68bcaa45cc4ff" + integrity sha512-1OnLhP9QcOnAe1qwSrjLXsFsMtqx7tBullcl/CgT5kYA/aXLKNiQ7zGeMrfbALOXhU51v74gtmMEAwW1EB1pcw== dependencies: "@opentripplanner/base-map" "^3.2.2" "@opentripplanner/core-utils" "^11.4.4" -"@opentripplanner/stops-overlay@^5.3.3": - version "5.3.3" - resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-5.3.3.tgz#711dd9316de93ac6c959dc3191d31884dc0a9589" - integrity sha512-GYADErZLIG3KPJ4OiigWkyHaGrWJ/wiCenH0RTbQ5J0XbYrenUIlWBsI5kpQNZPGBXocvF77sOTOUM8gex1AUA== +"@opentripplanner/stops-overlay@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-6.0.0.tgz#97186a2ce82284e2632967a4666294ad04d092db" + integrity sha512-zGpCc2PWpHDn1VGu9zdLfRIfde9sNAMN6ElHkLQq/67ylrQ7R1jIzi9K1O2X/ZPAEtfH5VOYa/y/A1drNlN3gw== dependencies: "@opentripplanner/base-map" "^3.2.2" "@opentripplanner/from-to-location-picker" "^2.1.14" "@opentripplanner/map-popup" "^4.0.0" flat "^5.0.2" -"@opentripplanner/transit-vehicle-overlay@^4.0.13": - version "4.0.13" - resolved "https://registry.yarnpkg.com/@opentripplanner/transit-vehicle-overlay/-/transit-vehicle-overlay-4.0.13.tgz#6f66c7c6a27d2473353b7c8c14acc8c540fb01ad" - integrity sha512-Sh3c3+q2dIhFZP5uyhPboyLSsTv8unVsPYKjFWVJaK19z6wN8KqFVmSqWysrcEnooRJd+D4+4erQiUbabomDLQ== +"@opentripplanner/transit-vehicle-overlay@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/transit-vehicle-overlay/-/transit-vehicle-overlay-5.0.0.tgz#1a3ea04abce38d65a21a9eaad8c5e0ac19a41100" + integrity sha512-N0vM89xozWTbbb63pBeZEZIbdlbgnP2K0qNJZ0vk87clJU0T7RxBtq/sdftBQ6CzB86AjXCCxfitCZH9HOD7/A== dependencies: "@opentripplanner/base-map" "^3.2.2" "@opentripplanner/core-utils" "^11.4.4" "@opentripplanner/icons" "^2.0.12" flat "^5.0.2" -"@opentripplanner/transitive-overlay@3.0.22": - version "3.0.22" - resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-3.0.22.tgz#631096b12e08671e5da05ffe1c0a0e01331750a4" - integrity sha512-Ix3+2qz1+iSbeLnMfd4tU+0AUU1LDjq4y8cTzfKHayqHe0pzHpYY9Ib2zrXbvog7Mav/Jozn2ycL27R4UgzQaA== +"@opentripplanner/transitive-overlay@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-4.0.0.tgz#1a0c41dd4a22704b74da105ef67c0ac63624c335" + integrity sha512-kFKRbKGeIKNDx2t21HaPXsOOkG+qlnHgXN8lbTZRxpfy+EIV3ZeoH72/ymWuiHdLnxUORHBAjhacQh+2fryc4A== dependencies: "@mapbox/polyline" "^1.1.1" "@opentripplanner/base-map" "^3.2.2" @@ -2655,22 +2677,23 @@ "@turf/midpoint" "^6.5.0" lodash.isequal "^4.5.0" -"@opentripplanner/trip-details@^5.0.15": - version "5.0.15" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-5.0.15.tgz#73cfd7427aed49af53fec4ded9de7d17b0ed5377" - integrity sha512-1OfCEju90PXGH9DVy2dbBk8Jz8/8zSJ35/OCgodenGT3FyokQPoJsQhPjr6MPIIYMTyAdUSDT9C372thA+rU2Q== +"@opentripplanner/trip-details@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-6.0.0.tgz#60958ca14910f865125d20db8ec2eeba6d2c7362" + integrity sha512-BzUAsSpuSWzVMRW6vhiZblrg0Sq7Tnar17jjgvTvIhzv0yUUyrW5jUxlGIPPk11UUJ5WrPZmkfQasF9z8xk5BQ== dependencies: "@opentripplanner/core-utils" "^11.4.4" "@styled-icons/fa-solid" "^10.34.0" flat "^5.0.2" react-animate-height "^3.0.4" -"@opentripplanner/trip-form@^3.6.4": - version "3.6.4" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-3.6.4.tgz#e741bb190dbea845c826d0021618daf568cc3df7" - integrity sha512-wnoJyI8jR3DbtTmg//FWWy+yhd6Yej843XxSnnGCuBhZjUnvC9aYffKa/FI1W9s8Xw+tJ7DsZ9wADiSOWEjmwA== +"@opentripplanner/trip-form@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-4.0.0.tgz#27b36a0504e46cfda1cdf50c971b5cea03de8ae2" + integrity sha512-Cg8SlAVN8M+qoWpz8jAkwuqllIPgrL2PVewTPuDPsIQ8i5B7xo5KKE3TPo7cQUM+jE6WEshpvv0FIdMF+NAlNg== dependencies: "@floating-ui/react" "^0.19.2" + "@opentripplanner/building-blocks" "^1.0.3" "@opentripplanner/core-utils" "^11.4.4" "@styled-icons/bootstrap" "^10.34.0" "@styled-icons/boxicons-regular" "^10.38.0" @@ -2678,13 +2701,14 @@ "@styled-icons/fa-solid" "^10.37.0" date-fns "^2.28.0" flat "^5.0.2" + react-animate-height "^3.0.4" react-indiana-drag-scroll "^2.0.1" react-inlinesvg "^2.3.0" -"@opentripplanner/trip-viewer-overlay@^2.0.10": - version "2.0.10" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-2.0.10.tgz#2c0809b2d54da4d57d0a0683a4739e29cb13a326" - integrity sha512-7M9l7fF8shtD/566bci+zEkPncf/L+ZWIYAl5gnIgrBxwLagN/+E2zkoDebYamGFGb236FXpvTS30i1BJzhcPA== +"@opentripplanner/trip-viewer-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-3.0.0.tgz#a56263863aba87a3608d4dbed0cf839333cf99d0" + integrity sha512-64qNLUxkL6ow8T1FghdKDp13KH96djjYAo6hHTAeZYHZuu7gxffRD1GmrDJVeyk0+DixjKdiNhAZ9mOCtAgK/w== dependencies: "@mapbox/polyline" "^1.1.0" "@opentripplanner/base-map" "^3.2.2" @@ -2695,10 +2719,10 @@ resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.5.2.tgz#1373d738479568d880a3b13670b0ec53a1a75bd5" integrity sha512-2qDcKOrsLoXdwjRAdi4xcdDUsZGTnwBM+vfEf8TTuuWSnA+WYav3ldlMB4sugxIdLaVKXlOfe3F5lCEh9jAHWA== -"@opentripplanner/vehicle-rental-overlay@^2.1.9": - version "2.1.9" - resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-2.1.9.tgz#c373e1400874a00f473be0f029b28e0944652c88" - integrity sha512-VYWqnuk5j1yHF/zH5NEqDIVjsSbIsIgiCK6SaYtQHOBwszWauIubpqviTBASAcY72JfKE36AQfGjPuYILd9oTw== +"@opentripplanner/vehicle-rental-overlay@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-3.0.0.tgz#68e2a6c117fa3af485f375685c24a557762c6bb6" + integrity sha512-Yt6l6fEX+5O1jJ4z2NpbA0rib2tARxKdE3nTUFaBORBSq/NFfa2vZMk3EBXIrtzC3/GRabqluJU8sDaMGfkvvw== dependencies: "@opentripplanner/base-map" "^3.2.2" "@opentripplanner/core-utils" "^11.4.4" @@ -3677,6 +3701,13 @@ "@types/history" "^4.7.11" "@types/react" "*" +"@types/react-transition-group@^4.4.10": + version "4.4.11" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.11.tgz#d963253a611d757de01ebb241143b1017d5d63d5" + integrity sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@17": version "17.0.38" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.38.tgz#f24249fefd89357d5fa71f739a686b8d7c7202bd" @@ -15526,6 +15557,16 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@<17.0.0: version "16.14.0" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" From d6f1a8cd2e12701265c4778f1015e8f55de70724 Mon Sep 17 00:00:00 2001 From: Daniel Heppner Date: Wed, 25 Sep 2024 12:36:59 -0700 Subject: [PATCH 7/7] more cleanup --- lib/components/form/date-time-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/components/form/date-time-modal.tsx b/lib/components/form/date-time-modal.tsx index 1f0e13557..e9f31407b 100644 --- a/lib/components/form/date-time-modal.tsx +++ b/lib/components/form/date-time-modal.tsx @@ -3,11 +3,11 @@ import coreUtils from '@opentripplanner/core-utils' import React, { useCallback } from 'react' import * as formActions from '../../actions/form' +import * as narrativeActions from '../../actions/narrative' import { AppConfig } from '../../util/config-types' import { AppReduxState, FilterType, SortType } from '../../util/state-types' import { StyledDateTimeSelector } from './styled' -import { updateItineraryFilter } from '../../actions/narrative' type Props = { config: AppConfig @@ -110,7 +110,7 @@ const mapStateToProps = (state: AppReduxState) => { const mapDispatchToProps = { setQueryParam: formActions.setQueryParam, - updateItineraryFilter + updateItineraryFilter: narrativeActions.updateItineraryFilter } export default connect(mapStateToProps, mapDispatchToProps)(DateTimeModal)