diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7206755e7..cfc31658b 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,4 +15,4 @@ jobs: # skip git, yarn, pixel test script and HAR file, and all i18n resources. # Also, the a11y test file has a false positive and the ignore list does not work # see https://github.com/opentripplanner/otp-react-redux/pull/436/checks?check_run_id=3369380014 - skip: ./.git,yarn.lock,./a11y/a11y.test.js,./a11y/mocks,./percy/percy.test.js,./percy/mock.har,./i18n + skip: ./.git,yarn.lock,./a11y/a11y.test.js,./a11y/mocks,./percy/percy.test.js,./percy/mock.har,./i18n,./__tests__/mocks diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 000000000..3337f3acb --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,21 @@ + +name: Auto-approve and enable auto-merge on dependabot PRs +on: pull_request_target + +jobs: + auto-approve-and-automerge: + # sources: + # https://github.com/hmarr/auto-approve-action + # https://github.com/marketplace/actions/enable-github-automerge + # https://github.com/marketplace/actions/enable-pull-request-automerge#dependabot-example + runs-on: ubuntu-latest + permissions: + pull-requests: write + if: github.actor == 'dependabot[bot]' + steps: + - name: Auto-approve PR + uses: hmarr/auto-approve-action@v3 + - name: Enable auto-merge + uses: alexwilson/enable-github-automerge-action@main + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/node-ci.yml b/.github/workflows/node-ci.yml index 79874aecd..60d823d55 100644 --- a/.github/workflows/node-ci.yml +++ b/.github/workflows/node-ci.yml @@ -21,8 +21,6 @@ jobs: node-version: 16.x - name: Install npm packages using cache uses: bahmutov/npm-install@v1 - - name: Copy example config - run: cp example-config.yml config.yml - name: Lint code # Move everything from latest commit back to staged run: git reset --soft HEAD^ && yarn lint @@ -36,8 +34,6 @@ jobs: run: yarn check:i18n-en-fr - name: Run tests run: yarn unit - - name: Build example project - run: yarn build - name: Run a11y tests run: yarn a11y-test diff --git a/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap b/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap index ef900b6da..4cf2c86dd 100644 --- a/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap +++ b/__tests__/components/viewers/__snapshots__/stop-viewer.js.snap @@ -101,6 +101,8 @@ exports[`components > viewers > stop viewer should render countdown times after > viewers > stop viewer should render countdown times after } /> + + +
@@ -392,7 +399,7 @@ exports[`components > viewers > stop viewer should render countdown times after > viewers > stop viewer should render countdown times after xmlns="http://www.w3.org/2000/svg" > @@ -518,7 +525,7 @@ exports[`components > viewers > stop viewer should render countdown times after } > viewers > stop viewer should render countdown times after xmlns="http://www.w3.org/2000/svg" > @@ -594,7 +601,7 @@ exports[`components > viewers > stop viewer should render countdown times after > viewers > stop viewer should render countdown times after xmlns="http://www.w3.org/2000/svg" > @@ -680,11 +687,11 @@ exports[`components > viewers > stop viewer should render countdown times after size="0.9em" > viewers > stop viewer should render countdown times after size="0.9em" > viewers > stop viewer should render countdown times after > @@ -757,11 +764,11 @@ exports[`components > viewers > stop viewer should render countdown times after size="0.9em" > viewers > stop viewer should render countdown times after size="0.9em" > viewers > stop viewer should render countdown times after > @@ -825,12 +832,97 @@ exports[`components > viewers > stop viewer should render countdown times after + + +
+ +
+ + + + + + + + + + + + + + components.StopViewer.noStopsFound + + +
+
+
+
+
viewers > stop viewer should render countdown times after
    - viewers > stop viewer should render countdown times after "timepoint": true, "tripId": "TriMet:9230377", }, - Object { - "arrivalDelay": 0, - "blockId": "2067", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 13980, - "realtimeDeparture": 13980, - "realtimeState": "SCHEDULED", - "scheduledArrival": 13980, - "scheduledDeparture": 13980, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230305", - }, - Object { - "arrivalDelay": 0, - "blockId": "2034", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 17580, - "realtimeDeparture": 17580, - "realtimeState": "SCHEDULED", - "scheduledArrival": 17580, - "scheduledDeparture": 17580, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230306", - }, - Object { - "arrivalDelay": 0, - "blockId": "2069", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 19020, - "realtimeDeparture": 19020, - "realtimeState": "SCHEDULED", - "scheduledArrival": 19020, - "scheduledDeparture": 19020, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230307", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render countdown times after } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render countdown times after } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render countdown times after iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - + + + + + + + + - - - common.time.tripDurationFormat - - - -
      - -
    2. -
    3. - + common.time.tripDurationFormat + + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render countdown times after } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render countdown times after iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 1:52 AM - - - -
    - -
  • -
  • - + + +
+ + +
  • + +
    -
    - viewers > stop viewer should render countdown times after } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render countdown times after iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 2:53 AM - - - -
    - -
  • - - - - - + 2:53 AM + + + + + + + + + +
    viewers > stop viewer should render countdown times after margin={0.25} > viewers > stop viewer should render countdown times after spin={true} > viewers > stop viewer should render countdown times after xmlns="http://www.w3.org/2000/svg" > @@ -2922,7 +2771,7 @@ exports[`components > viewers > stop viewer should render countdown times after >
    P
    @@ -3056,6 +2905,8 @@ exports[`components > viewers > stop viewer should render countdown times for st > viewers > stop viewer should render countdown times for st } /> + + +
    @@ -3248,7 +3104,7 @@ exports[`components > viewers > stop viewer should render countdown times for st > viewers > stop viewer should render countdown times for st xmlns="http://www.w3.org/2000/svg" > @@ -3374,7 +3230,7 @@ exports[`components > viewers > stop viewer should render countdown times for st } > viewers > stop viewer should render countdown times for st xmlns="http://www.w3.org/2000/svg" > @@ -3450,7 +3306,7 @@ exports[`components > viewers > stop viewer should render countdown times for st > viewers > stop viewer should render countdown times for st xmlns="http://www.w3.org/2000/svg" > @@ -3536,11 +3392,11 @@ exports[`components > viewers > stop viewer should render countdown times for st size="0.9em" > viewers > stop viewer should render countdown times for st size="0.9em" > viewers > stop viewer should render countdown times for st > @@ -3613,11 +3469,11 @@ exports[`components > viewers > stop viewer should render countdown times for st size="0.9em" > viewers > stop viewer should render countdown times for st size="0.9em" > viewers > stop viewer should render countdown times for st > @@ -3681,12 +3537,97 @@ exports[`components > viewers > stop viewer should render countdown times for st + + +
    + +
    + + + + + + + + + + + + + + components.StopViewer.noStopsFound + + +
    +
    +
    +
    +
    viewers > stop viewer should render countdown times for st

    - viewers > stop viewer should render countdown times for st }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render countdown times for st } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render countdown times for st } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render countdown times for st iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - + + + + + + + + - - - common.time.tripDurationFormat - - - -
      - -
    2. -
    -
  • - -
    -
    + } + > + common.time.tripDurationFormat + + + +
    + + + +
    + +
    viewers > stop viewer should render countdown times for st margin={0.25} > viewers > stop viewer should render countdown times for st spin={true} > viewers > stop viewer should render countdown times for st xmlns="http://www.w3.org/2000/svg" > @@ -4732,7 +4574,7 @@ exports[`components > viewers > stop viewer should render countdown times for st >
    P
    @@ -4866,6 +4708,8 @@ exports[`components > viewers > stop viewer should render times after midnight w > viewers > stop viewer should render times after midnight w } /> + + +
    @@ -5157,7 +5006,7 @@ exports[`components > viewers > stop viewer should render times after midnight w > viewers > stop viewer should render times after midnight w xmlns="http://www.w3.org/2000/svg" > @@ -5283,7 +5132,7 @@ exports[`components > viewers > stop viewer should render times after midnight w } > viewers > stop viewer should render times after midnight w xmlns="http://www.w3.org/2000/svg" > @@ -5359,7 +5208,7 @@ exports[`components > viewers > stop viewer should render times after midnight w > viewers > stop viewer should render times after midnight w xmlns="http://www.w3.org/2000/svg" > @@ -5445,11 +5294,11 @@ exports[`components > viewers > stop viewer should render times after midnight w size="0.9em" > viewers > stop viewer should render times after midnight w size="0.9em" > viewers > stop viewer should render times after midnight w > @@ -5522,11 +5371,11 @@ exports[`components > viewers > stop viewer should render times after midnight w size="0.9em" > viewers > stop viewer should render times after midnight w size="0.9em" > viewers > stop viewer should render times after midnight w > @@ -5590,12 +5439,97 @@ exports[`components > viewers > stop viewer should render times after midnight w + + +
    + +
    + + + + + + + + + + + + + + components.StopViewer.noStopsFound + + +
    +
    +
    +
    +
    viewers > stop viewer should render times after midnight w

    - viewers > stop viewer should render times after midnight w "timepoint": true, "tripId": "TriMet:9230377", }, - Object { - "arrivalDelay": 0, - "blockId": "2067", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 13980, - "realtimeDeparture": 13980, - "realtimeState": "SCHEDULED", - "scheduledArrival": 13980, - "scheduledDeparture": 13980, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230305", - }, - Object { - "arrivalDelay": 0, - "blockId": "2034", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 17580, - "realtimeDeparture": 17580, - "realtimeState": "SCHEDULED", - "scheduledArrival": 17580, - "scheduledDeparture": 17580, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230306", - }, - Object { - "arrivalDelay": 0, - "blockId": "2069", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 19020, - "realtimeDeparture": 19020, - "realtimeState": "SCHEDULED", - "scheduledArrival": 19020, - "scheduledDeparture": 19020, - "serviceDay": 1565247600, - "stopCount": 132, - "stopId": "TriMet:9860", - "stopIndex": 38, - "timepoint": true, - "tripId": "TriMet:9230307", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render times after midnight w } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render times after midnight w } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render times after midnight w iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.wednesday - - - - - - - - 12:51 AM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + + + + common.daysOfWeek.wednesday + + + + + + - - - + + +
      +
      +
    4. +
    5. + +
      + + + + + - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 1:52 AM - - - -
      -
      -
    6. -
    7. - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render times after midnight w } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render times after midnight w iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 2:53 AM - - - -
    - -
  • - -
    - - - + 2:53 AM + + +
    +
    + + + + + +
    viewers > stop viewer should render times after midnight w margin={0.25} > viewers > stop viewer should render times after midnight w spin={true} > viewers > stop viewer should render times after midnight w xmlns="http://www.w3.org/2000/svg" > @@ -7730,7 +7421,7 @@ exports[`components > viewers > stop viewer should render times after midnight w >
    P
    @@ -7864,6 +7555,8 @@ exports[`components > viewers > stop viewer should render with OTP transit index > viewers > stop viewer should render with OTP transit index } /> + + +
    @@ -8413,7 +8111,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index > viewers > stop viewer should render with OTP transit index xmlns="http://www.w3.org/2000/svg" > @@ -8539,7 +8237,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index } > viewers > stop viewer should render with OTP transit index xmlns="http://www.w3.org/2000/svg" > @@ -8615,7 +8313,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index > viewers > stop viewer should render with OTP transit index xmlns="http://www.w3.org/2000/svg" > @@ -8701,11 +8399,11 @@ exports[`components > viewers > stop viewer should render with OTP transit index size="0.9em" > viewers > stop viewer should render with OTP transit index size="0.9em" > viewers > stop viewer should render with OTP transit index > @@ -8778,11 +8476,11 @@ exports[`components > viewers > stop viewer should render with OTP transit index size="0.9em" > viewers > stop viewer should render with OTP transit index size="0.9em" > viewers > stop viewer should render with OTP transit index > @@ -8846,12 +8544,97 @@ exports[`components > viewers > stop viewer should render with OTP transit index + + +
    + +
    + + + + + + + + + + + + + + components.StopViewer.noStopsFound + + +
    +
    +
    +
    +
    viewers > stop viewer should render with OTP transit index

    - viewers > stop viewer should render with OTP transit index "timepoint": false, "tripId": "TriMet:9230360", }, - Object { - "arrivalDelay": 0, - "blockId": "2048", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 67628, - "realtimeDeparture": 67628, - "realtimeState": "SCHEDULED", - "scheduledArrival": 67628, - "scheduledDeparture": 67628, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230361", - }, - Object { - "arrivalDelay": 0, - "blockId": "2036", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 70028, - "realtimeDeparture": 70028, - "realtimeState": "SCHEDULED", - "scheduledArrival": 70028, - "scheduledDeparture": 70028, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230363", - }, - Object { - "arrivalDelay": 0, - "blockId": "2071", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 72436, - "realtimeDeparture": 72436, - "realtimeState": "SCHEDULED", - "scheduledArrival": 72436, - "scheduledDeparture": 72436, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230365", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render with OTP transit index } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.monday - - - - - - - - 6:00 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + + + + common.daysOfWeek.monday + + + + + + - - - - - - - - - - + 6:00 PM + + + +
      +
      +
    4. +
    5. + +
      + - - + - - - - - - - + + + + + + + + + - - 6:15 PM - - - -
      -
      -
    6. -
    7. - + common.daysOfWeek.monday + + + + + + + + 6:15 PM + + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:31 PM - - - -
    - -
  • - -
    - - - + 6:31 PM + + +
    +
    + + + + + +

    viewers > stop viewer should render with OTP transit index

    - viewers > stop viewer should render with OTP transit index "timepoint": true, "tripId": "TriMet:9238190", }, - Object { - "arrivalDelay": 0, - "blockId": "9474", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 56880, - "realtimeDeparture": 56880, - "realtimeState": "SCHEDULED", - "scheduledArrival": 56880, - "scheduledDeparture": 56880, - "serviceDay": 1565074800, - "stopCount": 40, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238194", - }, - Object { - "arrivalDelay": 0, - "blockId": "9470", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 54720, - "realtimeDeparture": 54720, - "realtimeState": "SCHEDULED", - "scheduledArrival": 54720, - "scheduledDeparture": 54720, - "serviceDay": 1565161200, - "stopCount": 34, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238190", - }, - Object { - "arrivalDelay": 0, - "blockId": "9470", - "departureDelay": 0, - "headsign": "Sherwood", - "realtime": false, - "realtimeArrival": 54720, - "realtimeDeparture": 54720, - "realtimeState": "SCHEDULED", - "scheduledArrival": 54720, - "scheduledDeparture": 54720, - "serviceDay": 1565247600, - "stopCount": 34, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238190", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    +
    + + + + + 94 + + + + + + Sherwood + +
    +
      +
    1. + +
      + + + + + + + + + + + + + + + + + common.daysOfWeek.tuesday + + + + + + + + 2:28 PM + + + +
      +
      +
    2. +
    3. + +
      + + + + + + + + + + + + + + + + + common.daysOfWeek.tuesday + + + + + + + + 3:02 PM + + + +
      +
      +
    4. +
    5. + +
      + + + + + + + + + + + + + + + + + common.daysOfWeek.tuesday + + + + + + + + 3:12 PM + + + +
      +
      +
    6. +
    +
    + +
    + +
  • +
    +
    + + + + + 94 + + + + + + King City + +
    +
      +
    1. + +
      + + + + + + + + + + + + + + + + + common.daysOfWeek.tuesday + + + + + + + + 3:22 PM + + + +
      +
      +
    2. +
    +
    +
  • +
    + +
  • -
  • -
    - - + - - - 94 - - - - - - Sherwood - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - - - 2:28 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 3:02 PM - - - -
      -
      -
    4. -
    5. - + + + + +
    6. +
    7. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 3:12 PM - - - -
      - -
    8. -
    - -
  • -
    -
    - + + + + + + + + + +

    + + + common.daysOfWeek.wednesday + + +

    + viewers > stop viewer should render with OTP transit index showOperatorLogo={false} stopTimes={ Array [ - Object { - "arrivalDelay": 0, - "blockId": "9472", - "departureDelay": 0, - "headsign": "King City", - "realtime": false, - "realtimeArrival": 55320, - "realtimeDeparture": 55320, - "realtimeState": "SCHEDULED", - "scheduledArrival": 55320, - "scheduledDeparture": 55320, - "serviceDay": 1565074800, - "stopCount": 23, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238192", - }, Object { "arrivalDelay": 0, "blockId": "9472", @@ -11524,199 +11506,59 @@ exports[`components > viewers > stop viewer should render with OTP transit index "timepoint": true, "tripId": "TriMet:9238192", }, - Object { - "arrivalDelay": 0, - "blockId": "9472", - "departureDelay": 0, - "headsign": "King City", - "realtime": false, - "realtimeArrival": 55320, - "realtimeDeparture": 55320, - "realtimeState": "SCHEDULED", - "scheduledArrival": 55320, - "scheduledDeparture": 55320, - "serviceDay": 1565247600, - "stopCount": 23, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9238192", - }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render with OTP transit index } title="94" > - - 94 - - - - - - King City - -
    -
      + + + + -
    1. - -
      - - - - - - - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - - - 3:22 PM - - - -
      -
      -
    2. -
    3. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.wednesday - - - - - - - - 3:22 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.thursday - - - - - - + + + + + + - - 3:22 PM - - - -
      -
      -
    4. -
    -
  • - -
    -
    - + + + + + + + + + + viewers > stop viewer should render with OTP transit index showOperatorLogo={false} stopTimes={ Array [ - Object { - "arrivalDelay": 0, - "blockId": "3668", - "departureDelay": 0, - "headsign": "Tualatin Park & Ride", - "realtime": false, - "realtimeArrival": 58260, - "realtimeDeparture": 58260, - "realtimeState": "SCHEDULED", - "scheduledArrival": 58260, - "scheduledDeparture": 58260, - "serviceDay": 1565074800, - "stopCount": 63, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9231858", - }, - Object { - "arrivalDelay": 0, - "blockId": "3670", - "departureDelay": 0, - "headsign": "Tualatin Park & Ride", - "realtime": false, - "realtimeArrival": 61740, - "realtimeDeparture": 61740, - "realtimeState": "SCHEDULED", - "scheduledArrival": 61740, - "scheduledDeparture": 61740, - "serviceDay": 1565074800, - "stopCount": 63, - "stopId": "TriMet:715", - "stopIndex": 0, - "timepoint": true, - "tripId": "TriMet:9231860", - }, Object { "arrivalDelay": 0, "blockId": "3668", @@ -12261,179 +11770,57 @@ exports[`components > viewers > stop viewer should render with OTP transit index }, ] } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } > - +
    -
  • -
    - - + - viewers > stop viewer should render with OTP transit index } title="36" > - - 36 - - - - - - Tualatin Park & Ride - -
    -
      -
    1. - + + + + + Tualatin Park & Ride + +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - - - 4:11 PM - - + /> + + + + -
      - -
    2. -
    3. - -
      + - + - - - - - - - - - - - - - - - common.daysOfWeek.tuesday - - - - - - + + + + + + - - 5:09 PM - - - -
      -
      -
    4. -
    5. - + + +
  • + + + + + +
    +

    + + + common.daysOfWeek.thursday + + +

    + +
  • +
    +
    + + + -
    + 94 + + + + + + King City + +
    +
      +
    1. + +
      + - viewers > stop viewer should render with OTP transit index } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with OTP transit index iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.wednesday - - - - - - + + + + + + - - 4:11 PM - - - -
      -
      -
    2. -
    -
    -
  • -
    -
    + 3:22 PM + + + + + + + + + +
    viewers > stop viewer should render with OTP transit index margin={0.25} > viewers > stop viewer should render with OTP transit index spin={true} > viewers > stop viewer should render with OTP transit index xmlns="http://www.w3.org/2000/svg" > @@ -14803,7 +14169,7 @@ exports[`components > viewers > stop viewer should render with OTP transit index >
    P
    @@ -14937,6 +14303,8 @@ exports[`components > viewers > stop viewer should render with TriMet transit in > viewers > stop viewer should render with TriMet transit in } /> + + +
    @@ -15481,7 +14854,7 @@ exports[`components > viewers > stop viewer should render with TriMet transit in > viewers > stop viewer should render with TriMet transit in xmlns="http://www.w3.org/2000/svg" > @@ -15607,7 +14980,7 @@ exports[`components > viewers > stop viewer should render with TriMet transit in } > viewers > stop viewer should render with TriMet transit in xmlns="http://www.w3.org/2000/svg" > @@ -15683,7 +15056,7 @@ exports[`components > viewers > stop viewer should render with TriMet transit in > viewers > stop viewer should render with TriMet transit in xmlns="http://www.w3.org/2000/svg" > @@ -15769,11 +15142,11 @@ exports[`components > viewers > stop viewer should render with TriMet transit in size="0.9em" > viewers > stop viewer should render with TriMet transit in size="0.9em" > viewers > stop viewer should render with TriMet transit in > @@ -15846,11 +15219,11 @@ exports[`components > viewers > stop viewer should render with TriMet transit in size="0.9em" > viewers > stop viewer should render with TriMet transit in size="0.9em" > viewers > stop viewer should render with TriMet transit in > @@ -15914,12 +15287,97 @@ exports[`components > viewers > stop viewer should render with TriMet transit in + + +
    + +
    + + + + + + + + + + + + + + components.StopViewer.noStopsFound + + +
    +
    +
    +
    +
    viewers > stop viewer should render with TriMet transit in

    - viewers > stop viewer should render with TriMet transit in "realtimeState": "SCHEDULED", "scheduledArrival": 65759, "scheduledDeparture": 65759, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230359", - }, - Object { - "arrivalDelay": 0, - "blockId": "2047", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 66668, - "realtimeDeparture": 66668, - "realtimeState": "SCHEDULED", - "scheduledArrival": 66668, - "scheduledDeparture": 66668, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230360", - }, - Object { - "arrivalDelay": 0, - "blockId": "2036", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 70028, - "realtimeDeparture": 70028, - "realtimeState": "SCHEDULED", - "scheduledArrival": 70028, - "scheduledDeparture": 70028, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230363", - }, - Object { - "arrivalDelay": 0, - "blockId": "2071", - "departureDelay": 0, - "headsign": "Gresham TC", - "realtime": false, - "realtimeArrival": 72436, - "realtimeDeparture": 72436, - "realtimeState": "SCHEDULED", - "scheduledArrival": 72436, - "scheduledDeparture": 72436, - "serviceDay": 1564988400, - "stopCount": 132, - "stopId": "TriMet:715", - "stopIndex": 42, - "timepoint": false, - "tripId": "TriMet:9230365", - }, - ] - } - stopViewerConfig={ - Object { - "nearbyRadius": 250, - "numberOfDepartures": 3, - "showBlockIds": false, - "timeRange": 172800, - } - } - > - +
  • -
  • -
    - - + - viewers > stop viewer should render with TriMet transit in } title="20" > - - 20 - - - - - - Gresham TC - -
    -
      + + + + -
    1. - +
    +
      +
    1. + +
      -
      - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 5:45 PM - - - -
      - -
    2. -
    3. - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:00 PM - - - -
    - -
  • -
  • - + + +
  • + + +
  • + +
    -
    - viewers > stop viewer should render with TriMet transit in } title="components.StopTimeCell.scheduled" > - + - - + viewers > stop viewer should render with TriMet transit in iconVerticalAlign="middle" iconViewBox="0 0 512 512" > - - - - - - - - + + + + + + + + + + - - - common.daysOfWeek.monday - - - - - - + + + + + + - - 6:15 PM - - - -
    - -
  • - - - - - + 6:15 PM + + + + + + + + + +
    viewers > stop viewer should render with TriMet transit in margin={0.25} > viewers > stop viewer should render with TriMet transit in spin={true} > viewers > stop viewer should render with TriMet transit in xmlns="http://www.w3.org/2000/svg" > @@ -19572,7 +18787,7 @@ exports[`components > viewers > stop viewer should render with TriMet transit in >
    P
    @@ -19706,6 +18921,8 @@ exports[`components > viewers > stop viewer should render with initial stop id a > viewers > stop viewer should render with initial stop id a } /> + + +
    @@ -19832,7 +19054,7 @@ exports[`components > viewers > stop viewer should render with initial stop id a > viewers > stop viewer should render with initial stop id a xmlns="http://www.w3.org/2000/svg" > diff --git a/__tests__/mocks/stop-data.json b/__tests__/mocks/stop-data.json new file mode 100644 index 000000000..653b2f66d --- /dev/null +++ b/__tests__/mocks/stop-data.json @@ -0,0 +1,286 @@ +{ + "id": "40:99256", + "code": "C15-T2", + "lat": 47.580589, + "lon": -122.327263, + "locationType": "STOP", + "name": "SODO Station", + "wheelchairBoarding": "POSSIBLE", + "zoneId": "73", + "geometries": { + "geoJson": { + "type": "Point", + "coordinates": [ + -122.3273, + 47.5806 + ] + } + }, + "routes": [ + { + "id": "40:100479", + "agency": { + "gtfsId": "40:40", + "name": "Sound Transit" + }, + "longName": "Northgate - Angle Lake", + "mode": "TRAM", + "color": "28813F", + "textColor": "FFFFFF", + "shortName": "1-Line" + } + ], + "stopTimes": [ + { + "pattern": { + "id": "40:100479:1:01", + "desc": "1-Line to Northgate Station (40:990006) from Angle Lake Station (40:99914)", + "routeId": "40:100479", + "headsign": "Northgate" + }, + "times": [ + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 19, + "scheduledArrival": 23850, + "scheduledDeparture": 23880, + "realtimeArrival": 23850, + "realtimeDeparture": 23880, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1016", + "blockId": "59906", + "headsign": "Northgate" + }, + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 19, + "scheduledArrival": 23370, + "scheduledDeparture": 23400, + "realtimeArrival": 23370, + "realtimeDeparture": 23400, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1014", + "blockId": "59905", + "headsign": "Northgate" + }, + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 19, + "scheduledArrival": 22890, + "scheduledDeparture": 22920, + "realtimeArrival": 22890, + "realtimeDeparture": 22920, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1013", + "blockId": "59904", + "headsign": "Northgate" + } + ] + }, + { + "pattern": { + "id": "40:100479:1:06", + "desc": "1-Line University of Washington", + "routeId": "40:100479", + "headsign": "University of Washington" + }, + "times": [ + { + "stopId": "40:99256", + "stopIndex": 0, + "stopCount": 8, + "scheduledArrival": 15810, + "scheduledDeparture": 15840, + "realtimeArrival": 15810, + "realtimeDeparture": 15840, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695366000, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1001", + "blockId": "59904", + "headsign": "University of Washington" + }, + { + "stopId": "40:99256", + "stopIndex": 0, + "stopCount": 8, + "scheduledArrival": 15810, + "scheduledDeparture": 15840, + "realtimeArrival": 15810, + "realtimeDeparture": 15840, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695279600, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1001", + "blockId": "59904", + "headsign": "University of Washington" + } + ] + }, + { + "pattern": { + "id": "40:100479:1:07", + "desc": "1-Line Stadium", + "routeId": "40:100479", + "headsign": "Stadium" + }, + "times": [ + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 10, + "scheduledArrival": 90090, + "scheduledDeparture": 90120, + "realtimeArrival": 90090, + "realtimeDeparture": 90120, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1132", + "blockId": "59904", + "headsign": "Stadium" + }, + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 10, + "scheduledArrival": 89190, + "scheduledDeparture": 89220, + "realtimeArrival": 89190, + "realtimeDeparture": 89220, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1131", + "blockId": "59924", + "headsign": "Stadium" + }, + { + "stopId": "40:99256", + "stopIndex": 8, + "stopCount": 10, + "scheduledArrival": 88290, + "scheduledDeparture": 88320, + "realtimeArrival": 88290, + "realtimeDeparture": 88320, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1130", + "blockId": "59903", + "headsign": "Stadium" + } + ] + }, + { + "pattern": { + "id": "40:100479:1:05", + "desc": "1-Line to Northgate Station (40:990006) from SODO Station (40:99256)", + "routeId": "40:100479", + "headsign": "Northgate" + }, + "times": [ + { + "stopId": "40:99256", + "stopIndex": 0, + "stopCount": 11, + "scheduledArrival": 17250, + "scheduledDeparture": 17280, + "realtimeArrival": 17250, + "realtimeDeparture": 17280, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695279600, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1003", + "blockId": "59908", + "headsign": "Northgate" + }, + { + "stopId": "40:99256", + "stopIndex": 0, + "stopCount": 11, + "scheduledArrival": 16530, + "scheduledDeparture": 16560, + "realtimeArrival": 16530, + "realtimeDeparture": 16560, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695279600, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1002", + "blockId": "59922", + "headsign": "Northgate" + }, + { + "stopId": "40:99256", + "stopIndex": 0, + "stopCount": 11, + "scheduledArrival": 52470, + "scheduledDeparture": 52500, + "realtimeArrival": 52470, + "realtimeDeparture": 52500, + "arrivalDelay": 0, + "departureDelay": 0, + "timepoint": true, + "realtime": false, + "realtimeState": "SCHEDULED", + "serviceDay": 1695193200, + "tripId": "40:LLR_2023-09-18_Sept18_Weekday_100479_1072", + "blockId": "59925", + "headsign": "Northgate" + } + ] + } + ], + "agencyId": "40:40", + "agencyName": "Sound Transit", + "color": "#28813F", + "bikeRental": { + "stations": [] + }, + "parkAndRideLocations": [], + "vehicleRental": { + "stations": [] + }, + "nearbyStops": [], + "fetchStatus": 2, + "stopTimesLastUpdated": 1695215706401 +} \ No newline at end of file diff --git a/__tests__/util/stop-times.ts b/__tests__/util/stop-times.ts new file mode 100644 index 000000000..92bbc0170 --- /dev/null +++ b/__tests__/util/stop-times.ts @@ -0,0 +1,63 @@ +import { utcToZonedTime } from 'date-fns-tz' + +import '../test-utils/mock-window-url' +import { groupAndSortStopTimesByPatternByDay } from '../../lib/util/stop-times' + +const stopData = require('../mocks/stop-data.json') +const now = utcToZonedTime( + new Date(stopData.stopTimesLastUpdated), + 'America/Los_Angeles' +) +const daysAhead = 2 + +describe('util > stop-times', () => { + describe('sortAndGroupStopTimesByDay', () => { + it('should sort and group stop times by day', () => { + const stopTimesByPatternByDay = groupAndSortStopTimesByPatternByDay( + stopData, + now, + daysAhead, + 3 + ) + + function getPatternDays(id: string) { + return stopTimesByPatternByDay + .filter((p) => p.id === id) + .map((p) => p.day) + } + + // Stop time data has 4 patterns aggregating to 3 final destinations. + // (This method assumes that the routing is roughly the same for patterns + // that have the same destination.) + const headsigns = new Set( + stopTimesByPatternByDay.map((st) => st.pattern.headsign) + ) + expect(headsigns).toEqual( + new Set(['Northgate', 'Stadium', 'University of Washington']) + ) + + // The pattern to UWA should be included twice, once per service day outside of today. + expect(getPatternDays('40:100479-University of Washington')).toEqual([ + 1695279600, 1695366000 + ]) + + // Patterns to Stadium (last trips in the evening) should be for "today"'s service day. + expect(getPatternDays('40:100479-Stadium')).toEqual([1695193200]) + + // No next-day pattern to Northgate should be returned (same-day depatures exist). + expect(getPatternDays('40:100479-Northgate')).toEqual([1695193200]) + + // Patterns should be sorted by day, then by departure time. + for (let i = 1; i < stopTimesByPatternByDay.length; i++) { + const prevPattern = stopTimesByPatternByDay[i - 1] + const thisPattern = stopTimesByPatternByDay[i] + expect( + prevPattern.day < thisPattern.day || + (prevPattern.day === thisPattern.day && + prevPattern.times[0].realtimeDeparture <= + thisPattern.times[0].realtimeDeparture) + ).toBe(true) + } + }) + }) +}) diff --git a/__tests__/util/ui.ts b/__tests__/util/ui.ts index 0dfadfbf1..314e604e4 100644 --- a/__tests__/util/ui.ts +++ b/__tests__/util/ui.ts @@ -1,4 +1,8 @@ -import { getItineraryView, ItineraryView } from '../../lib/util/ui' +import { + getItineraryView, + getMapToggleNewItineraryView, + ItineraryView +} from '../../lib/util/ui' describe('util > ui', () => { describe('getItineraryView', () => { @@ -11,15 +15,48 @@ describe('util > ui', () => { ItineraryView.LIST ) }) - it('returns a full itinerary view if URL contains ui_activeItinerary', () => { + it('returns a full itinerary view if URL contains ui_activeItinerary that is not -1', () => { expect(getItineraryView({ ui_activeItinerary: 2 })).toBe( ItineraryView.FULL ) }) + it('returns an itinerary list view if URL contains ui_activeItinerary=-1 regardless of ui_itineraryView', () => { + const expectedValues = { + [ItineraryView.FULL]: ItineraryView.LIST, + [ItineraryView.LEG]: ItineraryView.LIST, + [ItineraryView.LEG_HIDDEN]: ItineraryView.LIST, + [ItineraryView.LIST]: ItineraryView.LIST, + [ItineraryView.LIST_HIDDEN]: ItineraryView.LIST_HIDDEN + } + Object.entries(expectedValues).forEach(([k, v]) => { + expect( + getItineraryView({ + ui_activeItinerary: -1, + ui_itineraryView: k + }) + ).toBe(v) + }) + }) it('returns the specified view mode when set in URL', () => { - expect(getItineraryView({ ui_itineraryView: 'leg' })).toBe( - ItineraryView.LEG - ) + expect( + getItineraryView({ + ui_activeItinerary: 0, + ui_itineraryView: ItineraryView.LEG + }) + ).toBe(ItineraryView.LEG) + }) + }) + describe('getMapToggleNewItineraryView', () => { + it('should obtain the new itinerary view value', () => { + const expectedValues = { + [ItineraryView.LEG]: ItineraryView.LEG_HIDDEN, + [ItineraryView.LEG_HIDDEN]: ItineraryView.LEG, + [ItineraryView.LIST]: ItineraryView.LIST_HIDDEN, + [ItineraryView.LIST_HIDDEN]: ItineraryView.LIST + } + Object.entries(expectedValues).forEach(([k, v]) => { + expect(getMapToggleNewItineraryView(k)).toBe(v) + }) }) }) }) diff --git a/example-config.yml b/example-config.yml index 63fbf78cc..2bae9c943 100644 --- a/example-config.yml +++ b/example-config.yml @@ -310,6 +310,9 @@ itinerary: # Whether the plan first/previous/next/last buttons should be shown along with # plan trip itineraries. showPlanFirstLastButtons: false + # Filters out trips returned by OTP by default, unless specifically requested. + # e.g. filters out walk-only itineraries if user has not explicitly asked for them. + strictItineraryFiltering: false # Whether to render route names and colors in the blocks inside # the batch ui rows renderRouteNamesInBlocks: true @@ -331,6 +334,8 @@ itinerary: # Whether to show the "# itineraries found" text above the itinerary results showHeaderText: false + # Whether to show the mode descriptions in the batch UI results. Defaults to true + showBatchUiItineraryHeaders: false # Whether to hide the gray loading bars that appear while results are loading hideSkeletons: true # Whether the itinerary listing background should match the header @@ -349,8 +354,6 @@ itinerary: disableMetroSeperatorDot: false # Shows the duration of a leg below the leg in the metro itinerary summary showLegDurations: false - # Allows calorie counts to be hidden - displayCalories: true # The sort option to use by default # Available sort options: 'BEST', 'DURATION', 'ARRIVALTIME', 'WALKTIME', 'COST', 'DEPARTURETIME' # defaultSort: "BEST" # Default diff --git a/i18n/es.yml b/i18n/es.yml index 8ddaa7901..4df1c29df 100644 --- a/i18n/es.yml +++ b/i18n/es.yml @@ -30,8 +30,7 @@ actions: No se puede guardar el plan: este plan no se pudo guardar debido a la falta de capacidad en uno o más vehículos. Por favor, vuelva a planificar su viaje. - maxTripRequestsExceeded: Número de solicitudes de viaje superadas sin resultados - válidos + maxTripRequestsExceeded: Número de solicitudes de viaje superadas sin resultados válidos saveItinerariesError: "No se pudieron guardar los itinerarios: {err}" setDateError: "Error al establecer la fecha:" setGroupSizeError: "No se pudo establecer el tamaño del grupo:" @@ -53,11 +52,9 @@ actions: authTokenError: Error al obtener un token de autorización. confirmDeleteMonitoredTrip: ¿Desea eliminar este viaje? confirmDeletePlace: ¿Quiere eliminar este lugar? - emailVerificationResent: El mensaje de verificación de correo electrónico ha sido - reenviado. + emailVerificationResent: El mensaje de verificación de correo electrónico ha sido reenviado. genericError: "Se ha encontrado un error: {err}" - itineraryExistenceCheckFailed: Comprobación de errores para ver si el viaje seleccionado - es posible. + itineraryExistenceCheckFailed: Comprobación de errores para ver si el viaje seleccionado es posible. preferencesSaved: Sus preferencias se han guardado. smsInvalidCode: El código introducido no es válido. Por favor, inténtelo de nuevo. smsResendThrottled: >- @@ -66,6 +63,8 @@ actions: smsVerificationFailed: >- Su teléfono no ha podido ser verificado. Quizás el código que has introducido ha caducado. Solicita un nuevo código e inténtalo de nuevo. + mustBeLoggedInToSavePlace: Por favor, inicia la sesión para guardar las ubicaciones. + placeRemembered: La configuración de este lugar se ha guardado. common: coordinates: "{lat}; {lon}" dateExpressions: @@ -241,12 +240,12 @@ components: modeSelectorLabel: Seleccione un modo de transporte BatchSettings: destination: destino + invalidModeSelection: >- + No se puede planificar un viaje utilizando los modos seleccionados. Prueba + a incluir el transporte publico en la selección de modos. origin: origen planTripTooltip: Planificar viaje - validationMessage: "Por favor, defina los siguientes campos para planificar un - viaje: {issues}" - invalidModeSelection: No se puede planificar un viaje utilizando los modos seleccionados. - Prueba a incluir el transporte publico en la selección de modos. + validationMessage: "Por favor, defina los siguientes campos para planificar un viaje: {issues}" BeforeSignInScreen: mainTitle: Iniciando sesión message: > @@ -492,6 +491,7 @@ components: operatedBy: Servicio operado por {agencyName} selectADirection: Seleccione una dirección… stopsTo: Hacia + headsignTo: '{headsign} ({lastStop})' RouteViewer: agencyFilter: Filtro de agencia allAgencies: Todas las agencias @@ -538,6 +538,8 @@ components: Otro viaje guardado ya utiliza este nombre. Por favor, elija un nombre diferente. tripNameRequired: Por favor, introduzca el nombre del viaje. + SequentialPaneDisplay: + stepNumber: Paso {step} de {total} SessionTimeout: body: >- Su sesión expirará en un minuto. Pulse 'Continuar sesión' para mantener su @@ -545,8 +547,7 @@ components: header: ¡La sesión está a punto de terminar! keepSession: Continuar sesión SimpleRealtimeAnnotation: - usingRealtimeInfo: Este viaje utiliza información de tráfico y retrasos en tiempo - real + usingRealtimeInfo: Este viaje utiliza información de tráfico y retrasos en tiempo real StackedPaneDisplay: savePreferences: Guardar preferencias StopScheduleTable: @@ -609,19 +610,16 @@ components: travelingAt: Viajando a {milesPerHour} vehicleName: Vehículo {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Comprobación de la existencia de itinerarios para - cada día de la semana… + checkingItineraryExistence: Comprobación de la existencia de itinerarios para cada día de la semana… selectAtLeastOneDay: Por favor, seleccione al menos un día para el seguimiento. tripDaysPrompt: ¿Qué días hace este viaje? - tripIsAvailableOnDaysIndicated: Su viaje está disponible en los días de la semana - indicados anteriormente. + tripIsAvailableOnDaysIndicated: Su viaje está disponible en los días de la semana indicados anteriormente. tripNamePrompt: "Por favor, indique un nombre para este viaje:" tripNotAvailableOnDay: El viaje no está disponible el {repeatedDay} unsavedChangesExistingTrip: >- Todavía no ha guardado su viaje. Si abandona la página, los cambios se perderán. - unsavedChangesNewTrip: Todavía no ha guardado su nuevo viaje. Si abandona la página, - se perderá. + unsavedChangesNewTrip: Todavía no ha guardado su nuevo viaje. Si abandona la página, se perderá. TripNotificationsPane: advancedSettings: Configuración avanzada altRouteRecommended: Se recomienda una ruta alternativa o un punto de transferencia @@ -781,8 +779,6 @@ components: switcher: Botón de cambio WelcomeScreen: prompt: ¿A donde quiere ir? - SequentialPaneDisplay: - stepNumber: Paso {step} de {total} config: accessModes: bicycle: Tránsito + Bicicleta Personal diff --git a/i18n/tr.yml b/i18n/tr.yml new file mode 100644 index 000000000..d3363dd05 --- /dev/null +++ b/i18n/tr.yml @@ -0,0 +1,135 @@ +_id: en-US +actions: + fieldTrip: + saveItinerariesError: 'Seyahat planları kaydedilemedi: {err}' + setDateError: 'Tarih ayarlanırken hata oluştu:' + fetchFieldTripError: '' + setGroupSizeError: 'Grup boyutu ayarlanırken hata oluştu:' + maxTripRequestsExceeded: Geçerli sonuçlar olmadan yolculuk isteklerinin sayısı + aşıldı + setPaymentError: 'Ödeme bilgileri ayarlanırken hata oluştu:' + setRequestStatusError: 'İstek durumu ayarlanırken hata oluştu:' + editSubmitterNotesError: 'Gönderenin notları düzenlenirken hata oluştu:' + user: + emailVerificationResent: E-posta doğrulama mesajı yeniden gönderildi. + genericError: 'Bir hata ile karşılaşıldı: {err}' + smsVerificationFailed: Telefonunuz doğrulanamadı. Girdiğiniz kodun süresi dolmuş + olabilir. Lütfen yeni bir kod isteyin ve tekrar deneyin. + authTokenError: Yetkilendirme anahtarı alınırken hata oluştu. + mustBeLoggedInToSavePlace: Konumları kaydetmek için lütfen giriş yapın. + itineraryExistenceCheckFailed: Seçtiğiniz seyahatin mümkün olup olmadığı kontrol + edilirken hata oluştu. + smsInvalidCode: Girdiğiniz kod geçersiz. Lütfen tekrar deneyin. + accountDeleted: Kullanıcı hesabınız ({email}) silindi. + preferencesSaved: Tercihleriniz kaydedilmiştir. + placeRemembered: Bu yerin ayarları kaydedildi. + confirmDeletePlace: Bu yeri kaldırmak ister misiniz? + smsResendThrottled: Belirtilen telefon numarasına bir dakikadan kısa süre önce + bir doğrulama SMS'i gönderildi. Lütfen birkaç dakika sonra tekrar deneyin. + location: + userDeniedPermission: Kullanıcı izni reddetti + deniedAccessAlert: "Konumunuza erişim engellendi.\nMevcut konumunuzu kullanmak + için tarayıcınızdan konum izinlerini etkinleştirin ve sayfayı yeniden yükleyin. + \n" + unknownPositionError: Konum alınırken bilinmeyen hata + geolocationNotSupportedError: Coğrafi konum tarayıcınız tarafından desteklenmiyor + callTaker: + fetchCallsError: 'Çağrılar alınırken hata oluştu: {err}' + queryFetchError: 'Sorgular alınırken hata oluştu: {err}' + map: + currentLocation: (Mevcut Konum) +common: + daysOfWeekPlural: + thursday: Perşembeler + tuesday: Salılar + saturday: Cumartesileri + friday: Cumalar + monday: Pazartesileri + wednesday: Çarşambalar + sunday: Pazarları + modes: + car: Araba + micromobility_rent: E-Skutır + subway: Metro + micromobility: E-Skutır + rent: Kiralama seçenekleri + walk: Yürümek + funicular: Füniküler + car_park: Park Et Devam Et + flex: Esnek Rotalar + ferry: Feribot + bike: Bisiklet + tram: Tramvay + gondola: Gondol + drive: Sürücü + cable_car: Teleferik + rail: Demiryolu + bus: Otobüs + bicycle_rent: Bisiklet paylaşımı + notifications: + email: eposta + push: bildirimler + sms: SMS + daysOfWeekCompact: + wednesday: Çar + thursday: Per + sunday: Paz + tuesday: Sal + saturday: Cmt + friday: Cum + monday: Pzt + itineraryDescriptions: + fareUnknown: Ücret bilgisi yok + forms: + "yes": Evet + print: Yazdır + "no": Hayır + finish: Bitiş + submitting: Gönderiliyor… + startOver: Baştan Başla + close: Kapat + edit: Düzenle + delete: Sil + save: Kaydet + cancel: İptal + back: Geri + error: hata! + next: Sonraki + defaultValue: '{value} (varsayılan)' + daysOfWeek: + sunday: Pazar + wednesday: Çarşamba + saturday: Cumartesi + friday: Cuma + monday: Pazartesi + tuesday: Salı + thursday: Perşembe + dateExpressions: + tomorrow: Yarın + yesterday: Dün + today: Bugün + coordinates: '{lat}, {lon}' + linkOpensNewWindow: (Yeni pencere açar) + places: + home: ev + dining: yemek + work: iş + searchForms: + click: tıkla + time: + duration: + aFewSeconds: birkaç saniye +components: + AddPlaceButton: + addPlace: Yer ekle + tooManyPlaces: Maksimum ara yerlere ulaşıldı + needOriginDestination: Ara yerler eklemek için başlangıç/bitiş noktasını tanımlayın + AfterSignInScreen: + mainTitle: Yönlendiriliyor... + A11yPrefs: + accessibilityRoutingByDefault: Varsayılan olarak erişilebilir seyahatleri tercih + et + AdvancedOptions: + preferredRoutes: Tercih edilen rotaları seçin... + bannedRoutes: Yasaklanan rotaları seç… +_name: İngilizce diff --git a/i18n/vi.yml b/i18n/vi.yml index ba852fde4..e31f08271 100644 --- a/i18n/vi.yml +++ b/i18n/vi.yml @@ -5,8 +5,7 @@ actions: callQuerySaveError: "Lỗi khi lưu trữ các truy vấn cuộc gọi: {err}" callSaveError: "Không thể lưu cuộc gọi: {err}" checkSessionError: "Lỗi khi thiết lập phiên ủy quyền: {err}" - couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy - vấn. + couldNotFindCallError: Không thể tìm thấy cuộc gọi. Đang hủy yêu cầu lưu truy vấn. fetchCallsError: "Lỗi khi tìm nạp cuộc gọi: {err}" queryFetchError: "Lỗi khi tìm nạp các truy vấn: {err}" fieldTrip: @@ -29,16 +28,14 @@ actions: Không thể lưu kế hoạch chuyến đi: Không thể lưu kế hoạch chuyến đi này do thiếu sức chứa trên một hoặc nhiều xe. Vui lòng lên kế hoạch lại chuyến đi của bạn. - maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết - quả hợp lệ + maxTripRequestsExceeded: Đã vượt quá số lượng yêu cầu chuyến đi mà không có kết quả hợp lệ saveItinerariesError: "Không lưu được hành trình: {err}" setDateError: "Lỗi khi cài đặt ngày:" setGroupSizeError: "Lỗi khi cài đặt kích thước nhóm:" setPaymentError: "Lỗi khi cài đặt thông tin thanh toán:" setRequestStatusError: "Lỗi khi cài đặt trạng thái yêu cầu:" location: - geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt - của bạn + geolocationNotSupportedError: Định vị địa lý không được hỗ trợ bởi trình duyệt của bạn unknownPositionError: Lỗi không xác định khi tìm vị trí map: currentLocation: (Vị trí hiện tại) @@ -49,8 +46,7 @@ actions: confirmDeletePlace: Bạn có muốn loại bỏ nơi này không? emailVerificationResent: Thông báo xác minh email đã được gửi lại. genericError: "Phát sinh lỗi: {err}" - itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là - có thể. + itineraryExistenceCheckFailed: Lỗi kiểm tra xem chuyến đi được chọn của bạn là có thể. preferencesSaved: Những sở thích của bạn đã được lưu lại. smsInvalidCode: Mã bạn nhập không hợp lệ. Vui lòng thử lại. smsResendThrottled: >- @@ -160,14 +156,12 @@ common: {} other {# giây}} components: A11yPrefs: - accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc - định + accessibilityRoutingByDefault: Thích những chuyến đi có thể truy cập theo mặc định AccountSetupFinishPane: message: Bạn đã sẵn sàng để bắt đầu lên kế hoạch cho các chuyến đi của bạn. AddPlaceButton: addPlace: Thêm địa điểm - needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung - gian + needOriginDestination: Xác định nguồn gốc hoặc đích đến để thêm các địa điểm trung gian tooManyPlaces: Địa điểm trung gian tối đa đạt được AdvancedOptions: bannedRoutes: Chọn các tuyến đường bị cấm… @@ -278,8 +272,7 @@ components: editPlaceGeneric: Chỉnh sửa vị trí invalidAddress: Vui lòng cài đặt một vị trí cho nơi này. invalidName: Vui lòng nhập tên cho nơi này. - nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên - khác. + nameAlreadyUsed: Bạn đã sử dụng tên này cho một nơi khác. Vui lòng nhập một tên khác. placeNotFound: Không tìm thấy địa điểm placeNotFoundDescription: Xin lỗi, địa điểm được yêu cầu không được tìm thấy. FormNavigationButtons: @@ -521,22 +514,17 @@ components: travelingAt: di chuyển với tốc độ {milesPerHour} vehicleName: Phương tiện giao thông {vehicleNumber} TripBasicsPane: - checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong - tuần… + checkingItineraryExistence: Kiểm tra sự tồn tại của hành trình cho mỗi ngày trong tuần… selectAtLeastOneDay: Vui lòng chọn ít nhất một ngày để theo dõi. tripDaysPrompt: Bạn thực hiện chuyến đi này vào những ngày nào? - tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong - tuần như đã nêu ở trên. + tripIsAvailableOnDaysIndicated: Chuyến đi của bạn có sẵn vào những ngày trong tuần như đã nêu ở trên. tripNamePrompt: "Vui lòng cung cấp tên cho chuyến đi này:" tripNotAvailableOnDay: Chuyến đi không có sẵn vào {repeatedDay} - unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những - thay đổi sẽ bị mất. - unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó - sẽ bị mất. + unsavedChangesExistingTrip: Bạn chưa lưu chuyến đi của mình. Nếu bạn rời đi, những thay đổi sẽ bị mất. + unsavedChangesNewTrip: Bạn chưa lưu chuyến đi mới của mình. Nếu bạn rời đi, nó sẽ bị mất. TripNotificationsPane: advancedSettings: Cài đặt nâng cao - altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến - nghị + altRouteRecommended: Một tuyến đường hoặc điểm trung chuyển thay thế được khuyến nghị delaysAboveThreshold: Có sự chậm trễ hoặc gián đoạn của hơn howToReceiveAlerts: > Để nhận thông báo cho các chuyến đi đã lưu của bạn, bật thông báo trong @@ -545,8 +533,7 @@ components: notificationsTurnedOff: Thông báo được tắt cho tài khoản của bạn. notifyViaChannelWhen: "Thông báo cho tôi qua {channel} khi:" oneHour: 1 tiếng - realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình - của tôi + realtimeAlertFlagged: Có một cảnh báo thời gian thực được gắn cờ trên hành trình của tôi timeBefore: "{time} trước" TripStatus: alerts: "{alerts, plural, one {# cảnh báo!} other {# cảnh báo!}}" @@ -559,8 +546,7 @@ components: earlyHeading: >- Chuyến đi đang diễn ra và sẽ đến sớm hơn {formattedDuration} so với dự kiến! - noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực - có sẵn). + noDataHeading: Chuyến đi đang được tiến hành (không có cập nhật thời gian thực có sẵn). onTimeHeading: Chuyến đi đang được tiến hành và đúng giờ. base: lastCheckedDefaultText: Thời gian được kiểm tra lần cuối không xác định @@ -603,8 +589,7 @@ components: tripStartIsEarly: >- Thời gian bắt đầu chuyến đi đang diễn ra sớm hơn ${duration} so với dự kiến! - tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời - gian thực). + tripStartsSoonNoUpdates: Chuyến đi đang bắt đầu sớm (không có cập nhật về thời gian thực). tripStartsSoonOnTime: Chuyến đi đang bắt đầu sớm và sắp đúng giờ. TripSummary: arriveAt: "Đến nơi " diff --git a/lib/actions/api.js b/lib/actions/api.js index c4b0aad28..7a1b8b433 100644 --- a/lib/actions/api.js +++ b/lib/actions/api.js @@ -137,6 +137,8 @@ export function updateOtpUrlParams(state, searchId) { params.ui_activeSearch = searchId // Assumes this is a new search and the active itinerary should be reset. params.ui_activeItinerary = -1 + // At the same time, reset/delete the ui_itineraryView param. + params.ui_itineraryView = undefined if (config.itinerary?.showFirstResultByDefault) { dispatch(setVisibleItinerary({ index: 0 })) } diff --git a/lib/actions/apiV2.js b/lib/actions/apiV2.js index ae64d539c..55bacbcc8 100644 --- a/lib/actions/apiV2.js +++ b/lib/actions/apiV2.js @@ -2,6 +2,7 @@ import { aggregateModes, populateSettingWithValue } from '@opentripplanner/trip-form' +import { createAction } from 'redux-actions' import { decodeQueryParams, DelimitedArrayParam } from 'use-query-params' import clone from 'clone' import coreUtils from '@opentripplanner/core-utils' @@ -45,7 +46,8 @@ import { RoutingQueryCallResult } from './api-constants' import { setItineraryView } from './ui' import { zoomToPlace } from './map' -const { generateCombinations, generateOtp2Query } = coreUtils.queryGen +const { generateCombinations, generateOtp2Query, SIMPLIFICATIONS } = + coreUtils.queryGen const { getTripOptionsFromQuery, getUrlParams } = coreUtils.query const { convertGraphQLResponseToLegacy } = coreUtils.itinerary const { randId } = coreUtils.storage @@ -824,6 +826,8 @@ export function routingQuery(searchId = null, updateSearchInReducer) { config?.modes?.initialState?.enabledModeButtons || {} + const strictModes = config?.itinerary?.strictItineraryFiltering + // Filter mode definitions based on active mode keys const activeModeButtons = config.modes?.modeButtons.filter((mb) => activeModeKeys.includes(mb.key) @@ -878,8 +882,6 @@ export function routingQuery(searchId = null, updateSearchInReducer) { }) ) - dispatch(setItineraryView(ItineraryView.LIST)) - combinations.forEach((combo, index) => { const query = generateOtp2Query(combo) dispatch( @@ -920,11 +922,31 @@ export function routingQuery(searchId = null, updateSearchInReducer) { routingError, { rewritePayload: (response, dispatch, getState) => { - const withCollapsedShortNames = - response.data?.plan?.itineraries?.map((itin) => ({ + const itineraries = response.data?.plan?.itineraries + + // Convert user-selected transit modes from mode selector into modes recognized by OTP. + const activeModeStrings = activeModes.map( + (am) => SIMPLIFICATIONS[am.mode] + ) + + let filteredItineraries = itineraries + // If "strictItineraryFiltering" is enabled, only return itineraries that contain at least one explicitly requested mode... + if (strictModes) { + filteredItineraries = itineraries.filter((itin) => + itin.legs.some((leg) => + activeModeStrings.includes(SIMPLIFICATIONS[leg.mode]) + ) + ) + // ... Otherwise return all itineraries. + } + + // Filter itineraries to collapse short names and hide unnecessary errors. + const withCollapsedShortNames = filteredItineraries.map( + (itin) => ({ ...itin, legs: itin.legs?.map(convertGraphQLResponseToLegacy) - })) + }) + ) /* It is possible for a NO_TRANSIT_CONNECTION error to be returned even if trips were returned, since it is on a mode-by-mode basis. @@ -976,6 +998,31 @@ export function routingQuery(searchId = null, updateSearchInReducer) { } } +const requestingServiceTimeRange = createAction('SERVICE_TIME_RANGE_REQUEST') +const receivedServiceTimeRange = createAction('SERVICE_TIME_RANGE_RESPONSE') +const receivedServiceTimeRangeError = createAction('SERVICE_TIME_RANGE_ERROR') + +/** Queries for service time range. */ +const retrieveServiceTimeRangeIfNeeded = () => + function (dispatch, getState) { + if (getState().otp.serviceTimeRange) return + dispatch(requestingServiceTimeRange) + return dispatch( + createGraphQLQueryAction( + `{ + serviceTimeRange { + start + end + } + }`, + {}, + receivedServiceTimeRange, + receivedServiceTimeRangeError, + {} + ) + ) + } + export default { fetchStopInfo, findPatternsForRoute, @@ -983,6 +1030,7 @@ export default { findRoutes, findTrip, getVehiclePositionsForRoute, + retrieveServiceTimeRangeIfNeeded, routingQuery, vehicleRentalQuery } diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 3b254fa61..21856fd9b 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -9,12 +9,14 @@ import { getMatchingLocaleString, loadLocaleData } from '../util/i18n' -import { getModesForActiveAgencyFilter, getUiUrlParams } from '../util/state' import { + getItineraryView, + getMapToggleNewItineraryView, getPathFromParts, isDefinedAndNotEqual, ItineraryView } from '../util/ui' +import { getModesForActiveAgencyFilter, getUiUrlParams } from '../util/state' import { clearActiveSearch, @@ -39,7 +41,7 @@ export const setHoveredStop = createAction('SET_HOVERED_STOP') const viewTrip = createAction('SET_VIEWED_TRIP') const viewRoute = createAction('SET_VIEWED_ROUTE') export const toggleAutoRefresh = createAction('TOGGLE_AUTO_REFRESH') -const setPreviousItineraryView = createAction('SET_PREVIOUS_ITINERARY_VIEW') +const settingItineraryView = createAction('SET_ITINERARY_VIEW') export const setPopupContent = createAction('SET_POPUP_CONTENT') // This code-less action calls the reducer code @@ -314,21 +316,15 @@ export function handleBackButtonPress(e) { export function setItineraryView(value) { return function (dispatch, getState) { const urlParams = coreUtils.query.getUrlParams() - const prevItineraryView = urlParams.ui_itineraryView || ItineraryView.LIST // If the itinerary value is changed, - // set the desired ui query param + // set the desired ui query param (even if LIST, so it replaces the current value) // and store the current view as previousItineraryView. - if (value !== urlParams.ui_itineraryView) { - if (value !== ItineraryView.LIST) { - urlParams.ui_itineraryView = value - } else if (urlParams.ui_itineraryView) { - // Remove the ui_itineraryView param if it is set to LIST (default). - delete urlParams.ui_itineraryView - } + if (value !== getItineraryView(urlParams)) { + urlParams.ui_itineraryView = value dispatch(setUrlSearch(urlParams)) - dispatch(setPreviousItineraryView(prevItineraryView)) + dispatch(settingItineraryView(value)) } } } @@ -340,16 +336,10 @@ export function setItineraryView(value) { export function toggleBatchResultsMap() { return function (dispatch, getState) { const urlParams = coreUtils.query.getUrlParams() - const itineraryView = urlParams.ui_itineraryView || ItineraryView.LIST + const itineraryView = getItineraryView(urlParams) - if (itineraryView === ItineraryView.LEG) { - dispatch(setItineraryView(ItineraryView.LEG_HIDDEN)) - } else if (itineraryView === ItineraryView.LIST) { - dispatch(setItineraryView(ItineraryView.LIST_HIDDEN)) - } else { - const { previousItineraryView } = getState().otp.ui - dispatch(setItineraryView(previousItineraryView)) - } + const newView = getMapToggleNewItineraryView(itineraryView) + dispatch(setItineraryView(newView)) } } diff --git a/lib/components/app/app-menu-item.tsx b/lib/components/app/app-menu-item.tsx index f3a8e533b..3a9baebc7 100644 --- a/lib/components/app/app-menu-item.tsx +++ b/lib/components/app/app-menu-item.tsx @@ -68,7 +68,9 @@ export default class AppMenuItem extends Component { render(): JSX.Element { const { icon, id, onClick, subItems, text, ...otherProps } = this.props const { isExpanded } = this.state - const Element = otherProps.href ? 'a' : 'button' + const hasHref = !!otherProps.href + const isAbsolute = otherProps.href?.startsWith('http') + const Element = hasHref ? 'a' : 'button' const containerId = `${id}-container` return ( <> @@ -80,6 +82,7 @@ export default class AppMenuItem extends Component { onClick={subItems ? this._toggleSubmenu : onClick} onKeyDown={this._handleKeyDown} {...otherProps} + target={hasHref && isAbsolute ? '_blank' : undefined} > {icon} {text} diff --git a/lib/components/app/app-menu.tsx b/lib/components/app/app-menu.tsx index 4afc484f2..580ff803f 100644 --- a/lib/components/app/app-menu.tsx +++ b/lib/components/app/app-menu.tsx @@ -16,6 +16,8 @@ import type { WrappedComponentProps } from 'react-intl' import * as callTakerActions from '../../actions/call-taker' import * as fieldTripActions from '../../actions/field-trip' import * as uiActions from '../../actions/ui' +import { AppMenuItemConfig, LanguageConfig } from '../../util/config-types' +import { AppReduxState } from '../../util/state-types' import { ComponentContext } from '../../util/contexts' import { getLanguageOptions } from '../../util/i18n' import { isModuleEnabled, Modules } from '../../util/config' @@ -29,28 +31,27 @@ import PopupTriggerText from './popup-trigger-text' type MenuItem = { children?: MenuItem[] href?: string - iconType: string | JSX.Element + iconType?: string | JSX.Element iconUrl?: string id: string isSelected?: boolean - label: string | JSX.Element + label?: string | JSX.Element lang?: string onClick?: () => void skipLocales?: boolean - subMenuDivider: boolean + subMenuDivider?: boolean } type AppMenuProps = { activeLocale: string callTakerEnabled?: boolean - extraMenuItems?: MenuItem[] + extraMenuItems?: AppMenuItemConfig[] fieldTripEnabled?: boolean - // Typescript TODO language and language options based on configLanguage. - language: Record | null + language?: LanguageConfig languageOptions: Record | null location: { search: string } mailablesEnabled?: boolean - popupTarget: string + popupTarget?: string reactRouterConfig?: { basename: string } resetAndToggleCallHistory?: () => void resetAndToggleFieldTrips?: () => void @@ -89,7 +90,7 @@ class AppMenu extends Component< _triggerPopup = () => { const { popupTarget, setPopupContent } = this.props - setPopupContent(popupTarget) + if (popupTarget) setPopupContent(popupTarget) } _togglePane = () => { @@ -288,10 +289,8 @@ class AppMenu extends Component< // connect to the redux store -// FIXME: type otp config -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const mapStateToProps = (state: Record) => { - const { extraMenuItems, language } = state.otp.config +const mapStateToProps = (state: AppReduxState) => { + const { extraMenuItems, language, popups } = state.otp.config return { activeLocale: state.otp.ui.locale, callTakerEnabled: isModuleEnabled(state, Modules.CALL_TAKER), @@ -300,7 +299,7 @@ const mapStateToProps = (state: Record) => { language, languageOptions: getLanguageOptions(language), mailablesEnabled: isModuleEnabled(state, Modules.MAILABLES), - popupTarget: state.otp.config?.popups?.launchers?.sidebarLink + popupTarget: popups?.launchers?.sidebarLink } } @@ -324,7 +323,7 @@ export const Icon = ({ iconType, iconUrl }: { - iconType: string + iconType?: string iconUrl?: string }): JSX.Element => { // FIXME: add types to context diff --git a/lib/components/app/call-taker-panel.js b/lib/components/app/call-taker-panel.js index 5889c69d7..a0a1eafaf 100644 --- a/lib/components/app/call-taker-panel.js +++ b/lib/components/app/call-taker-panel.js @@ -139,7 +139,7 @@ class CallTakerPanel extends Component { if ( // Only enabled when field trip is enabled, as otherwise // this flashes a lot, possibly to an annoying extent - this.props.fieldTripEnabled && + this.props.fieldTripVisible && (prevProps?.currentQuery?.from?.name !== this.props?.currentQuery?.from?.name || prevProps?.currentQuery?.to?.name !== @@ -338,7 +338,7 @@ class CallTakerPanel extends Component { // connect to the redux store const mapStateToProps = (state) => { - const { activeId, requests } = state.callTaker.fieldTrip + const { activeId, groupSize, requests, visible } = state.callTaker.fieldTrip const request = requests.data.find((req) => req.id === activeId) const showUserSettings = getShowUserSettings(state) const moduleConfig = getModuleConfig(state, Modules.CALL_TAKER)?.options @@ -351,8 +351,8 @@ const mapStateToProps = (state) => { return { activeSearch: getActiveSearch(state), currentQuery: state.otp.currentQuery, - fieldTripEnabled: isModuleEnabled(state, Modules.FIELD_TRIP), - groupSize: state.callTaker.fieldTrip.groupSize, + fieldTripVisible: visible, + groupSize, mainPanelContent: state.otp.ui.mainPanelContent, maxGroupSize: getGroupSize(request), modeDropdownOptions: diff --git a/lib/components/app/desktop-nav.tsx b/lib/components/app/desktop-nav.tsx index bfe583c1f..e45a5027e 100644 --- a/lib/components/app/desktop-nav.tsx +++ b/lib/components/app/desktop-nav.tsx @@ -6,6 +6,8 @@ import styled from 'styled-components' import * as uiActions from '../../actions/ui' import { accountLinks, getAuth0Config } from '../../util/auth' +import { AppConfig } from '../../util/config-types' +import { AppReduxState } from '../../util/state-types' import { DEFAULT_APP_TITLE } from '../../util/constants' import InvisibleA11yLabel from '../util/invisible-a11y-label' import NavLoginButtonAuth0 from '../user/nav-login-button-auth0' @@ -40,7 +42,7 @@ const NavItemOnLargeScreens = styled(NavbarItem)` // Typscript TODO: otpConfig type export type Props = { locale: string - otpConfig: any + otpConfig: AppConfig popupTarget?: string setPopupContent: (url: string) => void } @@ -143,12 +145,12 @@ const DesktopNav = ({ } // connect to the redux store -// Typescript TODO: state type -const mapStateToProps = (state: any) => { +const mapStateToProps = (state: AppReduxState) => { + const { config: otpConfig } = state.otp return { locale: state.otp.ui.locale, - otpConfig: state.otp.config, - popupTarget: state.otp.config?.popups?.launchers?.toolbar + otpConfig, + popupTarget: otpConfig.popups?.launchers?.toolbar } } diff --git a/lib/components/app/popup.tsx b/lib/components/app/popup.tsx index d506ea5c1..8a67961cd 100644 --- a/lib/components/app/popup.tsx +++ b/lib/components/app/popup.tsx @@ -5,15 +5,13 @@ import coreUtils from '@opentripplanner/core-utils' import React, { useCallback, useEffect } from 'react' import styled from 'styled-components' +import { PopupLauncher, PopupTargetConfig } from '../../util/config-types' import { StyledIconWrapper } from '../util/styledIcon' import PageTitle from '../util/page-title' type Props = { - content?: { - appendLocale?: boolean - id?: string - modal?: boolean - url?: string + content?: PopupTargetConfig & { + id?: PopupLauncher } hideModal: () => void } diff --git a/lib/components/form/call-taker/advanced-options.js b/lib/components/form/call-taker/advanced-options.js index dcf81ee32..b755d11a7 100644 --- a/lib/components/form/call-taker/advanced-options.js +++ b/lib/components/form/call-taker/advanced-options.js @@ -39,6 +39,8 @@ export const StyledSubmodeSelector = styled(SubmodeSelector)` ${TripFormClasses.SubmodeSelector} { ${modeButtonButtonCss} } + + margin: 5px 0; ` const metersToMiles = (meters) => Math.round(meters * 0.000621371 * 100) / 100 @@ -207,7 +209,7 @@ class AdvancedOptions extends Component { const Select = lazy(() => import('react-select')) - const { bikeTolerance, modes: currentModes, walkReluctance } = currentQuery + const { modes: currentModes } = currentQuery const bannedRoutes = this.getRouteList('banned') const preferredRoutes = this.getRouteList('preferred') const transitModes = modes.transitModes.map((modeObj) => { @@ -223,7 +225,7 @@ class AdvancedOptions extends Component {
    diff --git a/lib/components/form/call-taker/date-time-picker.tsx b/lib/components/form/call-taker/date-time-picker.tsx index 83c52dcb6..7359d08fb 100644 --- a/lib/components/form/call-taker/date-time-picker.tsx +++ b/lib/components/form/call-taker/date-time-picker.tsx @@ -48,7 +48,8 @@ const SUPPORTED_TIME_FORMATS = [ 'HH:mm' ] -const safeFormat = (date: Date | string, time: string, options: any) => { +const safeFormat = (date: Date | '', time: string, options: any) => { + if (date === '') return '' try { return format(date, time, options) } catch (e) { diff --git a/lib/components/form/date-time-button.tsx b/lib/components/form/date-time-button.tsx index 3d6065c3e..26d554322 100644 --- a/lib/components/form/date-time-button.tsx +++ b/lib/components/form/date-time-button.tsx @@ -25,8 +25,10 @@ const ButtonWrapper = styled.span` position: relative; & > button { + background-color: var(--main-base-color, rgba(0, 0, 0, 0.5)); border-radius: 5px; border: none; + color: var(--main-color, white); cursor: pointer; font-size: 12px; height: ${buttonPixels}px; diff --git a/lib/components/form/styled.ts b/lib/components/form/styled.ts index 5056873f7..1067cfafa 100644 --- a/lib/components/form/styled.ts +++ b/lib/components/form/styled.ts @@ -3,7 +3,7 @@ import { SettingsSelectorPanel, Styled as TripFormClasses } from '@opentripplanner/trip-form' -import { Input, MenuItemA } from '@opentripplanner/location-field/lib/styled' +import { Input, MenuItemLi } from '@opentripplanner/location-field/lib/styled' import LocationField from '@opentripplanner/location-field' import styled, { css } from 'styled-components' @@ -24,9 +24,10 @@ const commonButtonCss = css` user-select: none; &.active { - background-color: rgb(173, 216, 230); + background-color: var(--main-base-color, rgb(173, 216, 230)); border: 2px solid rgb(0, 0, 0); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + color: var(--main-color, white); font-weight: 600; } ` @@ -222,8 +223,7 @@ export const StyledLocationField = styled(LocationField)` padding: 6px 12px; } - ${MenuItemA} { - text-decoration: none; + ${MenuItemLi} { &:hover { color: inherit; } diff --git a/lib/components/map/enhanced-stop-marker.tsx b/lib/components/map/enhanced-stop-marker.tsx index 82beaafa6..47c752ec5 100644 --- a/lib/components/map/enhanced-stop-marker.tsx +++ b/lib/components/map/enhanced-stop-marker.tsx @@ -13,13 +13,10 @@ import tinycolor from 'tinycolor2' import * as mapActions from '../../actions/map' import * as uiActions from '../../actions/ui' +import { AppReduxState } from '../../util/state-types' import { ComponentContext } from '../../util/contexts' -import { - ConfiguredTransitMode, - SetLocationHandler, - SetViewedStopHandler -} from '../util/types' import { getModeFromStop, getStopName } from '../../util/viewer' +import { SetLocationHandler, SetViewedStopHandler } from '../util/types' interface OwnProps { stop: Stop @@ -166,12 +163,11 @@ class EnhancedStopMarker extends Component { } } -const mapStateToProps = (state: any, ownProps: OwnProps) => { +const mapStateToProps = (state: AppReduxState, ownProps: OwnProps) => { const { highlightedStop } = state.otp.ui - const transitModes = state.otp.config.modes.transitModes const modeColors: ModeColors = {} - transitModes.forEach((mode: ConfiguredTransitMode) => { + state.otp.config.modes.transitModes.forEach((mode) => { modeColors[mode.mode] = mode.color }) diff --git a/lib/components/mobile/batch-results-screen.js b/lib/components/mobile/batch-results-screen.js index 2e88d01e9..69119f1c3 100644 --- a/lib/components/mobile/batch-results-screen.js +++ b/lib/components/mobile/batch-results-screen.js @@ -143,7 +143,7 @@ const mapStateToProps = (state) => { activeLeg: activeSearch ? activeSearch.activeLeg : null, errors: getResponsesWithErrors(state), itineraries: getActiveItineraries(state), - itineraryView: getItineraryView(urlParams) + itineraryView: getItineraryView(urlParams) || state.otp.ui.itineraryView } } diff --git a/lib/components/mobile/location-search.js b/lib/components/mobile/location-search.js index 2b7ca0390..b58b22536 100644 --- a/lib/components/mobile/location-search.js +++ b/lib/components/mobile/location-search.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux' import { injectIntl } from 'react-intl' import { - MenuItemA, MenuItemLi, MenuItemList } from '@opentripplanner/location-field/lib/styled' @@ -20,7 +19,7 @@ const MobileLocationField = styled(LocationField)` ${MenuItemList} { width: 100%; } - ${MenuItemA}, ${MenuItemLi} { + ${MenuItemLi} { overflow: hidden; padding-left: 5px; padding-right: 5px; diff --git a/lib/components/narrative/connected-trip-details.js b/lib/components/narrative/connected-trip-details.js index 8affe6ef8..b25d5009c 100644 --- a/lib/components/narrative/connected-trip-details.js +++ b/lib/components/narrative/connected-trip-details.js @@ -20,10 +20,6 @@ const mapStateToProps = (state) => { mediumId: null, riderCategoryId: null }, - displayCalories: - typeof itinerary?.displayCalories === 'boolean' - ? itinerary?.displayCalories - : undefined, fareDetailsLayout: itinerary?.fareDetailsLayout || undefined, fareKeyNameMap: itinerary?.fareKeyNameMap || {} } diff --git a/lib/components/narrative/default/default-itinerary.js b/lib/components/narrative/default/default-itinerary.js index bfbf25518..353cf9709 100644 --- a/lib/components/narrative/default/default-itinerary.js +++ b/lib/components/narrative/default/default-itinerary.js @@ -249,10 +249,10 @@ class DefaultItinerary extends NarrativeItinerary { return false } + // eslint-disable-next-line complexity render() { const { accessibilityScoreGradationMap, - active, co2Config, configCosts, defaultFareType, @@ -262,11 +262,11 @@ class DefaultItinerary extends NarrativeItinerary { LegIcon, setActiveLeg, showRealtimeAnnotation, + showResultHeaders, visible } = this.props const isFlexItinerary = itinerary.legs.some(isFlex) const isCallAhead = itinerary.legs.some(isAdvanceBookingRequired) - const isContDropoff = itinerary.legs.some(isCoordinationRequired) const { SvgIcon } = this.context const localizedGradationMapWithIcons = localizeGradationMap( @@ -321,9 +321,11 @@ class DefaultItinerary extends NarrativeItinerary { onClick={this._onHeaderClick} >
    -

    - -

    + {showResultHeaders && ( +

    + +

    + )} {itineraryHasAccessibilityScores(itinerary) && ( { defaultFareType: state.otp.config.itinerary?.defaultFareType || { mediumId: null, riderCategoryId: null - } + }, + showResultHeaders: + state.otp.config.itinerary?.showBatchUiItineraryHeaders !== false } } diff --git a/lib/components/narrative/line-itin/connected-itinerary-body.js b/lib/components/narrative/line-itin/connected-itinerary-body.js index 81cdb475a..12c51897a 100644 --- a/lib/components/narrative/line-itin/connected-itinerary-body.js +++ b/lib/components/narrative/line-itin/connected-itinerary-body.js @@ -5,7 +5,8 @@ import { LegDescriptionHeadsignPrefix, PlaceName as PlaceNameWrapper, PlaceRowWrapper, - PlaceSubheader + PlaceSubheader, + TimeColumn } from '@opentripplanner/itinerary-body/lib/styled' import { PlaceName } from '@opentripplanner/itinerary-body/lib/otp-react-redux' import clone from 'clone' @@ -53,6 +54,9 @@ const StyledItineraryBody = styled(ItineraryBody)` ${PlaceRowWrapper} { max-width: inherit; } + ${TimeColumn} { + white-space: nowrap; + } ` class ConnectedItineraryBody extends Component { diff --git a/lib/components/narrative/metro/metro-itinerary.tsx b/lib/components/narrative/metro/metro-itinerary.tsx index cbd0f7194..6eff07f40 100644 --- a/lib/components/narrative/metro/metro-itinerary.tsx +++ b/lib/components/narrative/metro/metro-itinerary.tsx @@ -75,7 +75,6 @@ const ItineraryDetails = styled.ul` margin: 0; overflow: hidden; padding: 0; - width: 90%; ` const PrimaryInfo = styled.li` color: #000000cc; diff --git a/lib/components/narrative/narrative-itineraries-header.tsx b/lib/components/narrative/narrative-itineraries-header.tsx index 5afc60308..6d9615510 100644 --- a/lib/components/narrative/narrative-itineraries-header.tsx +++ b/lib/components/narrative/narrative-itineraries-header.tsx @@ -9,7 +9,8 @@ import React, { useCallback } from 'react' import styled from 'styled-components' import { IconWithText, StyledIconWrapper } from '../util/styledIcon' -import { sortOptions, SortOptionsType } from '../util/sortOptions' +import { ItinerarySortOption } from '../../util/config-types' +import { sortOptions } from '../util/sortOptions' import { SortResultsDropdown } from '../util/dropdown' import { UnstyledButton } from '../util/unstyled-button' import InvisibleA11yLabel from '../util/invisible-a11y-label' @@ -53,7 +54,7 @@ export default function NarrativeItinerariesHeader({ sort }: { customBatchUiBackground?: boolean - enabledSortModes: SortOptionsType[] + enabledSortModes: ItinerarySortOption[] errors: unknown[] itineraries: unknown[] itinerary: Itinerary diff --git a/lib/components/narrative/narrative-itinerary.js b/lib/components/narrative/narrative-itinerary.js index a070a8f3a..196548e63 100644 --- a/lib/components/narrative/narrative-itinerary.js +++ b/lib/components/narrative/narrative-itinerary.js @@ -21,8 +21,9 @@ export default class NarrativeItinerary extends Component { } __setItinToActiveIndex = () => { - const { index, setActiveItinerary } = this.props + const { index, setActiveItinerary, setVisibleItinerary } = this.props setActiveItinerary && setActiveItinerary({ index }) + setVisibleItinerary && setVisibleItinerary({ index }) } _onDirectClick = () => { diff --git a/lib/components/user/notification-prefs-pane.tsx b/lib/components/user/notification-prefs-pane.tsx index 127fcb10c..67f3fb091 100644 --- a/lib/components/user/notification-prefs-pane.tsx +++ b/lib/components/user/notification-prefs-pane.tsx @@ -5,7 +5,9 @@ import { ListGroup, ListGroupItem } from 'react-bootstrap' import React from 'react' import styled from 'styled-components' +import { AppReduxState } from '../../util/state-types' import { GRAY_ON_WHITE } from '../util/colors' +import { PhoneFormatConfig } from '../../util/config-types' import { FieldSet } from './styled' import { PhoneVerificationSubmitHandler } from './phone-verification-form' @@ -19,9 +21,7 @@ interface Props extends FormikProps { loggedInUser: User onRequestPhoneVerificationCode: PhoneCodeRequestHandler onSendPhoneVerificationCode: PhoneVerificationSubmitHandler - phoneFormatOptions: { - countryCode: string - } + phoneFormatOptions: PhoneFormatConfig } const allNotificationChannels = ['email', 'sms', 'push'] @@ -121,14 +121,17 @@ const NotificationPrefsPane = ({ ) } -const mapStateToProps = (state: any) => { - const { supportsPushNotifications } = - state.otp.config.persistence?.otp_middleware || {} +const mapStateToProps = (state: AppReduxState) => { + const { persistence, phoneFormatOptions } = state.otp.config + const supportsPushNotifications = + persistence && 'otp_middleware' in persistence + ? persistence.otp_middleware?.supportsPushNotifications + : false return { allowedNotificationChannels: supportsPushNotifications ? allNotificationChannels : emailAndSms, - phoneFormatOptions: state.otp.config.phoneFormatOptions + phoneFormatOptions } } diff --git a/lib/components/user/phone-change-form.tsx b/lib/components/user/phone-change-form.tsx index 8e83ff2bd..7b34188ab 100644 --- a/lib/components/user/phone-change-form.tsx +++ b/lib/components/user/phone-change-form.tsx @@ -19,6 +19,7 @@ import React, { import styled from 'styled-components' import { InlineLoading } from '../narrative/loading' +import { PhoneFormatConfig } from '../../util/config-types' import InvisibleA11yLabel from '../util/invisible-a11y-label' import { ControlStrip, phoneFieldStyle } from './styled' @@ -59,9 +60,7 @@ interface Props { isSubmitting: boolean onCancel: () => void onSubmit: PhoneChangeSubmitHandler - phoneFormatOptions: { - countryCode: string - } + phoneFormatOptions: PhoneFormatConfig showCancel?: boolean } diff --git a/lib/components/user/phone-number-editor.tsx b/lib/components/user/phone-number-editor.tsx index b3dc52efc..fd5e8b972 100644 --- a/lib/components/user/phone-number-editor.tsx +++ b/lib/components/user/phone-number-editor.tsx @@ -8,6 +8,7 @@ import styled from 'styled-components' import { getAriaPhoneNumber } from '../../util/a11y' import { GRAY_ON_WHITE } from '../util/colors' import { isBlank } from '../../util/ui' +import { PhoneFormatConfig } from '../../util/config-types' import InvisibleA11yLabel from '../util/invisible-a11y-label' import { ControlStrip } from './styled' @@ -40,9 +41,7 @@ interface Props { intl: IntlShape onRequestCode: PhoneCodeRequestHandler onSubmitCode: PhoneVerificationSubmitHandler - phoneFormatOptions: { - countryCode: string - } + phoneFormatOptions: PhoneFormatConfig } interface State { diff --git a/lib/components/user/places/place-location-field.ts b/lib/components/user/places/place-location-field.ts index 711be8ae5..059033113 100644 --- a/lib/components/user/places/place-location-field.ts +++ b/lib/components/user/places/place-location-field.ts @@ -2,7 +2,6 @@ import { ClearButton, DropdownButton, Input, - MenuItemA, MenuItemLi, MenuItemList } from '@opentripplanner/location-field/lib/styled' @@ -42,18 +41,16 @@ const StyledLocationField = styled(LocationField)` position: absolute; ${(props) => (props.static ? 'width: 100%;' : '')} } - ${MenuItemA} { + ${MenuItemLi} { + overflow: hidden; + ${(props) => + props.static ? 'padding-left: 10px; padding-right: 5px; width: 100%' : ''} + &:focus, &:hover { color: inherit; - text-decoration: none; } } - ${MenuItemA}, ${MenuItemLi} { - overflow: hidden; - ${(props) => - props.static ? 'padding-left: 10px; padding-right: 5px; width: 100%' : ''} - } ` /** diff --git a/lib/components/user/user-account-screen.js b/lib/components/user/user-account-screen.js index ab20fcf01..b99cbf794 100644 --- a/lib/components/user/user-account-screen.js +++ b/lib/components/user/user-account-screen.js @@ -54,7 +54,7 @@ class UserAccountScreen extends Component { ) { passedUserData.notificationChannel = notificationChannel.join(',') } - const result = await createOrUpdateUser(userData, intl) + const result = await createOrUpdateUser(passedUserData, intl) // If needed, display a toast notification on success. if (result === userActions.UserActionResult.SUCCESS && !silentOnSucceed) { diff --git a/lib/components/util/service-time-range-retriever.ts b/lib/components/util/service-time-range-retriever.ts new file mode 100644 index 000000000..6a0625fb7 --- /dev/null +++ b/lib/components/util/service-time-range-retriever.ts @@ -0,0 +1,32 @@ +import { connect } from 'react-redux' +import { useEffect } from 'react' + +import apiActionsV2 from '../../actions/apiV2' + +interface Props { + retrieveServiceTimeRangeIfNeeded: () => void +} + +/** + * Invisible component that retrieves the date range available + * for OTP planning and schedule retrieval. + */ +const ServiceTimeRangeRetriever = ({ + retrieveServiceTimeRangeIfNeeded +}: Props): null => { + // If not already done, retrieve the OTP available date range on mount. + useEffect(() => { + retrieveServiceTimeRangeIfNeeded() + }, [retrieveServiceTimeRangeIfNeeded]) + + // Component renders nothing + return null +} + +// Connect to redux +const mapDispatchToProps = { + retrieveServiceTimeRangeIfNeeded: + apiActionsV2.retrieveServiceTimeRangeIfNeeded +} + +export default connect(null, mapDispatchToProps)(ServiceTimeRangeRetriever) diff --git a/lib/components/util/sortOptions.ts b/lib/components/util/sortOptions.ts index 1a721c8f0..54ac217d3 100644 --- a/lib/components/util/sortOptions.ts +++ b/lib/components/util/sortOptions.ts @@ -1,18 +1,12 @@ import { IntlShape } from 'react-intl' -export type SortOptionsType = - | 'BEST' - | 'DURATION' - | 'ARRIVALTIME' - | 'DEPARTURETIME' - | 'WALKTIME' - | 'COST' +import { ItinerarySortOption } from '../../util/config-types' -type SortOptionEntry = { text: string; value: SortOptionsType } +type SortOptionEntry = { text: string; value: ItinerarySortOption } export const sortOptions = ( intl: IntlShape, - enabledOptions: SortOptionsType[] = [ + enabledOptions: ItinerarySortOption[] = [ 'BEST', 'DURATION', 'ARRIVALTIME', @@ -20,10 +14,7 @@ export const sortOptions = ( 'COST', 'DEPARTURETIME' ] -): { - text: string - value: string -}[] => { +): SortOptionEntry[] => { const sortOptionsArray: SortOptionEntry[] = [ { text: intl.formatMessage({ diff --git a/lib/components/util/types.ts b/lib/components/util/types.ts index 8988c7126..0bb71dc00 100644 --- a/lib/components/util/types.ts +++ b/lib/components/util/types.ts @@ -24,13 +24,12 @@ export interface BikeRental { stations: any[] } -// FIXME: incomplete export interface StopTime { - departureDelay: number + departureDelay?: number headsign: string pattern: Pattern - realtimeDeparture: boolean - realtimeState: string + realtimeDeparture?: boolean + realtimeState?: string times: Time[] } @@ -66,6 +65,17 @@ export interface Time { tripId: string } +export interface PatternStopTimes { + id: string + pattern: Pattern + route: Route + times: Time[] +} + +export interface PatternDayStopTimes extends PatternStopTimes { + day: number +} + export interface VehicleRental { errorsByNetwork: { [key: string]: { message?: string; severity?: string } } systemInformationDataByNetwork: { @@ -95,10 +105,3 @@ export type SetViewedRouteHandler = (route?: ViewedRouteState) => void export type SetViewedStopHandler = (payload: { stopId: string } | null) => void export type SetLocationHandler = (payload: MapLocationActionArg) => void - -export interface ConfiguredTransitMode { - color?: string - label?: string - mode: string - showWheelchairSetting?: boolean -} diff --git a/lib/components/viewers/live-stop-times.tsx b/lib/components/viewers/live-stop-times.tsx index e8a772024..f07bc7662 100644 --- a/lib/components/viewers/live-stop-times.tsx +++ b/lib/components/viewers/live-stop-times.tsx @@ -1,4 +1,3 @@ -import { addDays, isBefore } from 'date-fns' import { format, utcToZonedTime } from 'date-fns-tz' import { FormattedMessage, @@ -12,14 +11,9 @@ import coreUtils from '@opentripplanner/core-utils' import isSameDay from 'date-fns/isSameDay' import React, { Component } from 'react' -import { - getRouteIdForPattern, - getStopTimesByPattern, - patternComparator, - routeIsValid, - stopTimeComparator -} from '../../util/viewer' +import { groupAndSortStopTimesByPatternByDay } from '../../util/stop-times' import { IconWithText } from '../util/styledIcon' +import { StopData } from '../util/types' import FormattedDayOfWeek from '../util/formatted-day-of-week' import SpanWithSpace from '../util/span-with-space' @@ -39,18 +33,16 @@ type Props = { findStopTimesForStop: ({ stopId }: { stopId: string }) => void homeTimezone: string intl: IntlShape - nearbyStops: any // TODO: shared types + nearbyStops: string[] setHoveredStop: (stopId: string) => void showNearbyStops: boolean showOperatorLogo?: boolean - // TODO: shared types - stopData: any + stopData: StopData stopViewerArriving: React.ReactNode // TODO: shared types stopViewerConfig: any toggleAutoRefresh: (enable: boolean) => void - // TODO: shared types - transitOperators: any + transitOperators: TransitOperator[] viewedStop: { stopId: string } } @@ -164,76 +156,45 @@ class LiveStopTimes extends Component { } = this.props const { spin } = this.state const userTimezone = getUserTimezone() - // construct a lookup table mapping pattern (e.g. 'ROUTE_ID-HEADSIGN') to - // an array of stoptimes - const stopTimesByPattern = getStopTimesByPattern(stopData) const now = utcToZonedTime(Date.now(), homeTimezone) // Time range is set in seconds, so convert to days - const timeRange = stopViewerConfig.timeRange / 86400 || 2 + const daysAhead = stopViewerConfig.timeRange / 86400 || 2 const refreshButtonText = intl.formatMessage({ id: 'components.LiveStopTimes.refresh' }) - const routeTimes = Object.values(stopTimesByPattern) - .filter( - ({ pattern, route, times }) => - times && - times.length !== 0 && - routeIsValid(route, getRouteIdForPattern(pattern)) - ) - .sort(patternComparator) - .map((route) => { - const sortedTimes = route.times - .concat() - ?.sort(stopTimeComparator) - // filter any times according to time range set in config. - .filter((time: any, i: number, array: Array) => { - const departureTime = time.serviceDay + time.realtimeDeparture - return isBefore(departureTime, addDays(now, timeRange)) - }) - const { serviceDay } = sortedTimes[0] - return { - ...route, - day: serviceDay || null, - times: sortedTimes - } - }) - // if the time range filter removes all times, remove route - .filter(({ times }) => times.length !== 0) + const routeTimes = groupAndSortStopTimesByPatternByDay( + stopData, + now, + daysAhead, + stopViewerConfig.numberOfDepartures + ) return ( <>
      - {routeTimes.map((time: any, index: number) => { - const { id, pattern, route, times } = time - return ( - - {((index > 0 && - !isSameDay( - time.day * 1000, - routeTimes[index - 1]?.day * 1000 - )) || - (index === 0 && !isSameDay(now, time.day * 1000))) && - this.renderDay(homeTimezone, time.day, now)} - o.agencyId === route.agencyId - ) - }} - showOperatorLogo={showOperatorLogo} - stopTimes={times} - stopViewerArriving={stopViewerArriving} - stopViewerConfig={stopViewerConfig} - /> - - ) - })} + {routeTimes.map(({ day, id, pattern, route, times }, index) => ( + + {((index > 0 && + !isSameDay(day * 1000, routeTimes[index - 1]?.day * 1000)) || + (index === 0 && !isSameDay(now, day * 1000))) && + this.renderDay(homeTimezone, day, now)} + o.agencyId === route.agencyId + ) + }} + showOperatorLogo={showOperatorLogo} + stopTimes={times} + /> + + ))}
    {/* Auto update controls for realtime arrivals */} diff --git a/lib/components/viewers/next-arrival-for-pattern.tsx b/lib/components/viewers/next-arrival-for-pattern.tsx index 2fceb4cf8..bed35582c 100644 --- a/lib/components/viewers/next-arrival-for-pattern.tsx +++ b/lib/components/viewers/next-arrival-for-pattern.tsx @@ -21,7 +21,7 @@ type Props = { pattern: Pattern // Not the true operator type, but the one that's used here // It is annoying to shoehorn the operator in here like this, but passing - // it in indvidually would cause a situation where a list of routes + // it in individually would cause a situation where a list of routes // needs to be matched up with a list of operators route: Route & { operator?: { colorMode?: string } } routeColor: string diff --git a/lib/components/viewers/pattern-row.tsx b/lib/components/viewers/pattern-row.tsx index 0b4084251..cc1765974 100644 --- a/lib/components/viewers/pattern-row.tsx +++ b/lib/components/viewers/pattern-row.tsx @@ -1,6 +1,5 @@ import { getMostReadableTextColor } from '@opentripplanner/core-utils/lib/route' -import { injectIntl, IntlShape } from 'react-intl' -import React, { Component } from 'react' +import React, { useContext } from 'react' import type { Route, TransitOperator } from '@opentripplanner/types' import { ComponentContext } from '../../util/contexts' @@ -16,107 +15,81 @@ import OperatorLogo from '../util/operator-logo' import StopTimeCell from './stop-time-cell' type Props = { - homeTimezone?: any - intl: IntlShape + homeTimezone?: string pattern: Pattern route: Route & { operator?: TransitOperator & { colorMode?: string } } showOperatorLogo?: boolean stopTimes: Time[] - stopViewerArriving: React.ReactNode - stopViewerConfig: { numberOfDepartures: number } } -type State = { expanded: boolean } + /** * Represents a single pattern row for displaying arrival times in the stop * viewer. */ -class PatternRow extends Component { - constructor(props: Props) { - super(props) - this.state = { expanded: false } - } - - static contextType = ComponentContext +const PatternRow = ({ + homeTimezone, + pattern, + route, + showOperatorLogo, + stopTimes +}: Props): JSX.Element | null => { + // @ts-expect-error FIXME: No type on ComponentContext + const { RouteRenderer: CustomRouteRenderer } = useContext(ComponentContext) + const RouteRenderer = CustomRouteRenderer || DefaultRouteRenderer - _toggleExpandedView = () => { - this.setState({ expanded: !this.state.expanded }) + const hasStopTimes = stopTimes && stopTimes.length > 0 + if (!hasStopTimes) { + return null } - render() { - const { RouteRenderer: CustomRouteRenderer } = this.context - const RouteRenderer = CustomRouteRenderer || DefaultRouteRenderer - const { - homeTimezone, - pattern, - route, - showOperatorLogo, - stopTimes, - stopViewerConfig - } = this.props + const routeName = route.shortName ? route.shortName : route.longName + const routeColor = getRouteColorBasedOnSettings(route.operator, route) - // sort stop times by next departure - let sortedStopTimes: Time[] = [] - const hasStopTimes = stopTimes && stopTimes.length > 0 - if (hasStopTimes) { - sortedStopTimes = stopTimes - // We request only x departures per pattern, but the patterns are merged - // according to shared headsigns, so we need to slice the stop times - // here as well to ensure only x times are shown per route/headsign combo. - .slice(0, stopViewerConfig.numberOfDepartures) - } else { - // Do not render pattern row if it has no stop times. - return null - } - - const routeName = route.shortName ? route.shortName : route.longName - const routeColor = getRouteColorBasedOnSettings(route.operator, route) - - return ( -
  • - {/* header row */} -
    - {/* route name */} -
    - - {showOperatorLogo && } - - - {pattern.headsign} -
    - {/* next departure preview */} - {hasStopTimes && ( -
      - {[0, 1, 2].map( - (index) => - sortedStopTimes?.[index] && ( -
    1. - -
    2. - ) + return ( +
    3. + {/* header row */} +
      + {/* route name */} +
      + + {showOperatorLogo && } + - )} + leg={generateFakeLegForRouteRenderer(route, true)} + style={{ fontSize: routeNameFontSize(routeName) }} + /> + + {pattern.headsign}
      -
    4. - ) - } + {/* next departure preview (only shows up to 3 entries) */} + {hasStopTimes && ( +
        + {[0, 1, 2].map( + (index) => + stopTimes?.[index] && ( +
      1. + +
      2. + ) + )} +
      + )} +
    +
  • + ) } -export default injectIntl(PatternRow) +export default PatternRow diff --git a/lib/components/viewers/stop-viewer.js b/lib/components/viewers/stop-viewer.js index f907f0d88..a580422a2 100644 --- a/lib/components/viewers/stop-viewer.js +++ b/lib/components/viewers/stop-viewer.js @@ -4,8 +4,10 @@ import { ArrowLeft } from '@styled-icons/fa-solid/ArrowLeft' import { Calendar } from '@styled-icons/fa-solid/Calendar' import { Clock } from '@styled-icons/fa-regular/Clock' import { connect } from 'react-redux' -import { format } from 'date-fns-tz' +import { ExclamationCircle } from '@styled-icons/fa-solid/ExclamationCircle' +import { format, parse } from 'date-fns' import { FormattedMessage, injectIntl } from 'react-intl' +import { format as formatTz, utcToZonedTime } from 'date-fns-tz' import { InfoCircle } from '@styled-icons/fa-solid/InfoCircle' import { Search } from '@styled-icons/fa-solid/Search' import { Star as StarRegular } from '@styled-icons/fa-regular/Star' @@ -24,10 +26,11 @@ import * as userActions from '../../actions/user' import { getPersistenceMode } from '../../util/user' import { getShowUserSettings, getStopViewerConfig } from '../../util/state' import { Icon, IconWithText, StyledIconWrapper } from '../util/styledIcon' -import { navigateBack } from '../../util/ui' +import { isBlank, navigateBack } from '../../util/ui' import { stopIsFlex } from '../../util/viewer' import OperatorLogo from '../util/operator-logo' import PageTitle from '../util/page-title' +import ServiceTimeRangeRetriever from '../util/service-time-range-retriever' import Strong from '../util/strong-text' import withMap from '../map/with-map' @@ -36,9 +39,13 @@ import StopScheduleTable from './stop-schedule-table' const { getCurrentDate, getUserTimezone } = coreUtils.time +/** The native date format used with elements */ +const inputDateFormat = 'yyyy-MM-dd' + function getDefaultState(timeZone) { return { // Compare dates/times in the stop viewer based on the agency's timezone. + // TODO: mock this date for percy tests. date: getCurrentDate(timeZone), isShowingSchedule: false } @@ -179,9 +186,18 @@ class StopViewer extends Component { } } + _isDateWithinRange = (date) => { + const { calendarMax, calendarMin } = this.props + return !isBlank(date) && date >= calendarMin && date <= calendarMax + } + handleDateChange = (evt) => { + // Check for non-empty date, and that date is within range before making request. + // (Users can enter a date outside of the range using the Up/Down arrow keys in Firefox and Safari.) const date = evt.target.value - this._findStopTimesForDate(date) + if (this._isDateWithinRange(date)) { + this._findStopTimesForDate(date) + } this.setState({ date }) } @@ -272,11 +288,11 @@ class StopViewer extends Component { /** * Plan trip from/to here buttons, plus the schedule/next arrivals toggle. - * TODO: Can this use SetFromToButtons? */ _renderControls = () => { - const { homeTimezone, intl, stopData } = this.props - const { isShowingSchedule } = this.state + const { calendarMax, calendarMin, homeTimezone, intl, stopData } = + this.props + const { date, isShowingSchedule } = this.state const inHomeTimezone = homeTimezone && homeTimezone === getUserTimezone() // Rewrite stop ID to not include Agency prefix, if present @@ -291,12 +307,22 @@ class StopViewer extends Component { const isFlex = stopIsFlex(stopData) let timezoneWarning - if (!inHomeTimezone) { - const timezoneCode = format(Date.now(), 'z', { - // To avoid ambiguities for now, use the English-US timezone abbreviations ("EST", "PDT", etc.) - locale: dateFnsUSLocale, - timeZone: homeTimezone - }) + if (!inHomeTimezone && this._isDateWithinRange(date)) { + // In schedule view, the time zone code should be that of the entered date, + // or the current day in the live view. + // This is to account for daylight time changes, especially when the live and + // schedule views are in different daylight saving periods. + const timezoneCode = formatTz( + isShowingSchedule && date + ? parse(date, inputDateFormat, new Date()) + : new Date(), // TODO: mock for percy tests, + 'z', + { + // To avoid ambiguities for now, use the English-US timezone abbreviations ("EST", "PDT", etc.) + locale: dateFnsUSLocale, + timeZone: homeTimezone + } + ) // Display a banner about the departure timezone if user's timezone is not the configured 'homeTimezone' // (e.g. cases where a user in New York looks at a schedule in Los Angeles). @@ -312,6 +338,16 @@ class StopViewer extends Component { ) } + if (!this._isDateWithinRange(date)) { + timezoneWarning = ( + + + + + + ) + } + return (
    - ) + if (this._isDateWithinRange(date)) { + contents = ( + + ) + } } else { contents = ( <> @@ -453,6 +493,7 @@ class StopViewer extends Component { return (
    + {/* Header Block */} {this._renderHeader(agencyCount)} @@ -475,7 +516,7 @@ class StopViewer extends Component { const mapStateToProps = (state) => { const showUserSettings = getShowUserSettings(state) const stopViewerConfig = getStopViewerConfig(state) - const { config, transitIndex, ui } = state.otp + const { config, serviceTimeRange = {}, transitIndex, ui } = state.otp const { homeTimezone, language, persistence, stopViewer, transitOperators } = config const { autoRefreshStopTimes = true, favoriteStops } = state.user.localUser @@ -484,8 +525,33 @@ const mapStateToProps = (state) => { const nearbyStops = Array.from(new Set(stopData?.nearbyStops))?.map( (stopId) => stopLookup[stopId] ) + const now = new Date() + const thisYear = now.getFullYear() + const { end = 0, start = 0 } = serviceTimeRange + // If start is not provided, default to the first day of the current calendar year in the user's timezone. + // (No timezone conversion is needed in this case.) + // If start is provided in OTP, convert that date in the agency's home time zone. + const calendarMin = format( + start + ? utcToZonedTime(start * 1000, homeTimezone) + : new Date(thisYear, 0, 1), + inputDateFormat + ) + // If end is not provided, default to the last day of the next calendar year in the user's timezone. + // (No timezone conversion is needed in this case.) + // If end date is provided and falls at midnight agency time, + // use the previous second to get the last service day available. + const calendarMax = format( + end + ? utcToZonedTime((end - 1) * 1000, homeTimezone) + : new Date(thisYear + 1, 11, 31), + inputDateFormat + ) + return { autoRefreshStopTimes, + calendarMax, + calendarMin, enableFavoriteStops: getPersistenceMode(persistence).isLocalStorage, favoriteStops, homeTimezone, diff --git a/lib/reducers/create-otp-reducer.js b/lib/reducers/create-otp-reducer.js index 0888474e9..472598ab7 100644 --- a/lib/reducers/create-otp-reducer.js +++ b/lib/reducers/create-otp-reducer.js @@ -1053,9 +1053,9 @@ function createOtpReducer(config) { }) case 'UPDATE_ITINERARY_FILTER': return update(state, { filter: { $set: action.payload } }) - case 'SET_PREVIOUS_ITINERARY_VIEW': + case 'SET_ITINERARY_VIEW': return update(state, { - ui: { previousItineraryView: { $set: action.payload } } + ui: { itineraryView: { $set: action.payload } } }) case 'UPDATE_LOCALE': return update(state, { @@ -1088,6 +1088,18 @@ function createOtpReducer(config) { } } }) + case 'SERVICE_TIME_RANGE_REQUEST': + return update(state, { + serviceTimeRange: { $set: { pending: true } } + }) + case 'SERVICE_TIME_RANGE_RESPONSE': + return update(state, { + serviceTimeRange: { $set: action.payload.data.serviceTimeRange } + }) + case 'SERVICE_TIME_RANGE_ERROR': + return update(state, { + serviceTimeRange: { $set: { error: true } } + }) default: return state } diff --git a/lib/util/arrays.ts b/lib/util/arrays.ts new file mode 100644 index 000000000..d5429ae6c --- /dev/null +++ b/lib/util/arrays.ts @@ -0,0 +1,12 @@ +/** Retrieves a key from a dictionary or adds it with the desired value if absent. */ +export function getOrPutEntry( + entries: Record, + key: K, + newValue: (newKey: K) => V +): V { + let entry = entries[key] + if (!entry) { + entries[key] = entry = newValue(key) + } + return entry +} diff --git a/lib/util/config-types.ts b/lib/util/config-types.ts new file mode 100644 index 000000000..734c39ed6 --- /dev/null +++ b/lib/util/config-types.ts @@ -0,0 +1,363 @@ +// This file is intended to contain configuration types, +// each suffixed with "Config", as in "MapConfig", "MenuItemConfig", etc. + +import { + CO2ConfigType, + FareTableLayout +} from '@opentripplanner/trip-details/lib/types' +import { + Company, + FareProductSelector, + MassUnitOption, + ModeButtonDefinition, + ModeSetting, + ModeSettingValues, + TransitOperator, + VehicleRentalMapOverlaySymbol +} from '@opentripplanner/types' +import { GeocoderConfig as GeocoderConfigOtpUI } from '@opentripplanner/geocoder' + +/** Accessibility threshold settings */ +export interface AccessibilityScoreThresholdConfig { + color: string + icon: string + text?: string +} + +/** All accessibility score settings */ +export interface AccessibilityScoreConfig { + gradationMap: Record +} + +/** OTP URL settings */ +export interface ApiConfig { + host: string + path: string + port: number + v2?: boolean +} + +/** Application side menu */ +export interface AppMenuItemConfig { + children?: AppMenuItemConfig[] + href?: string + iconType?: string + iconUrl?: string + id: string + label?: string + subMenuDivider?: boolean +} + +export type AutoPlanCondition = + | 'ONE_LOCATION_CHANGED' + | 'BOTH_LOCATIONS_CHANGED' + +export interface AutoPlanConfig { + default: AutoPlanCondition + mobile: AutoPlanCondition +} + +/** Generic API key config */ +interface ApiKeyConfig { + key: string +} + +export type BugsnagConfig = ApiKeyConfig +export type MapillaryConfig = ApiKeyConfig + +/** TODO: Language settings */ +export type LanguageConfig = Record + +export interface LocalizationConfig { + defaultLocale: string +} + +/** Auth0 settings */ +export interface Auth0Config { + audience: string + clientId: string + domain: string +} + +/** Local persistence setting */ +export interface LocalPersistenceConfig { + strategy: 'localStorage' + // eslint-disable-next-line camelcase + terms_of_storage?: boolean +} + +/** OTP Middleware (Personas) settings */ +export interface MiddlewarePersistenceConfig { + auth0: Auth0Config + // eslint-disable-next-line camelcase + otp_middleware: { + apiBaseUrl: string + apiKey?: string + supportsPushNotifications?: boolean + } + strategy: 'otp_middleware' +} + +/** General persistence settings */ +export type PersistenceConfig = ( + | LocalPersistenceConfig + | MiddlewarePersistenceConfig +) & { + enabled?: boolean +} + +/** Popup target settings */ +export interface PopupTargetConfig { + appendLocale?: boolean + modal?: boolean + url?: string +} + +/** Supported popup launchers */ +export type PopupLauncher = + /** This button is rendered at the bottom of a selected itinerary */ + | 'itineraryFooter' + /** This button is rendered to the left of the itinerary filter in the batch and metro UIs */ + | 'optionFilter' + /** This button is rendered in the sidebar */ + | 'sidebarLink' + /** This button is rendered in the top right of the toolbar (desktop view only!) */ + | 'toolbar' + +/** Popup settings */ +export interface PopupConfig { + launchers: Record + targets: Record +} + +/** Phone format options */ +export interface PhoneFormatConfig { + countryCode: string +} + +/** Map base layers (e.g. streets, satellite) */ +export interface BaseLayerConfig { + name?: string + type?: string + url: string +} + +/** Map views (e.g. default, stylized) */ +export interface MapViewConfig { + text: string + type: string // TODO use a list of values. +} + +export interface OverlayConfigBase { + modes?: string[] // TODO use allowed OTP mode list. + name?: string + visible?: boolean +} + +export interface ParkAndRideOverlayConfig extends OverlayConfigBase { + maxTransitDistance?: number + type: 'park-and-ride' +} + +export interface Otp1StopsOverlayConfig extends OverlayConfigBase { + type: 'stops' +} + +export interface Otp2TileLayer { + color?: string + initiallyVisible?: boolean + network?: string + type: 'stops' | 'stations' | 'rentalVehicles' | 'rentalStations' +} + +export interface Otp2TileLayerConfig { + layers: Otp2TileLayer[] + type: 'otp2' +} + +export interface MapTileLayerConfig extends OverlayConfigBase { + tileUrl: string + type: 'tile' +} + +export interface RentalOverlayConfig extends OverlayConfigBase { + companies?: string[] + mapSymbols: VehicleRentalMapOverlaySymbol[] + type: + | 'bike-rental' + | 'micromobility-rental' + | 'otp2-micromobility-rental' + | 'otp2-bike-rental' +} + +export type SupportedOverlays = + | ParkAndRideOverlayConfig + | RentalOverlayConfig + | Otp2TileLayerConfig + | Otp1StopsOverlayConfig + | MapTileLayerConfig + +export interface MapConfig { + baseLayers?: BaseLayerConfig[] + initLat?: number + initLon?: number + initZoom?: number + maxZoom?: number + overlays?: SupportedOverlays[] + views?: MapViewConfig[] +} + +/** Settings for reporting issues */ +export interface ReportIssueConfig { + mailto: string +} + +export interface ItineraryCostConfig { + bikeshareTripCostCents?: number + carParkingCostCents?: number + drivingCentsPerMile?: number +} + +export type ItinerarySortOption = + | 'BEST' + | 'DURATION' + | 'ARRIVALTIME' + | 'WALKTIME' + | 'COST' + | 'DEPARTURETIME' + +export interface ItineraryConfig { + costs?: ItineraryCostConfig + customBatchUiBackground?: boolean + defaultFareType?: FareProductSelector + defaultSort?: ItinerarySortOption + disableMetroSeperatorDot?: true + fareDetailsLayout?: FareTableLayout[] + fareKeyNameMap?: Record + fillModeIcons?: boolean + groupByMode?: boolean + groupTransitModes?: boolean + hideSkeletons?: boolean + mergeItineraries?: boolean + renderRouteNamesInBlocks?: boolean + showFirstResultByDefault?: boolean + showHeaderText?: boolean + showLegDurations?: boolean + showPlanFirstLastButtons?: boolean + showRouteFares?: boolean + sortModes?: ItinerarySortOption[] +} + +export interface CO2Config extends CO2ConfigType { + cutoffPercentage?: number + // FIXME: merge with the CO2ConfigType's unit field. + massUnit?: MassUnitOption + showIfHigher?: boolean +} + +export interface GeocoderConfig extends GeocoderConfigOtpUI { + maxNearbyStops?: number + resultColors?: Record + resultsCount?: number + type: string +} + +export interface TransitModeConfig { + color?: string + label?: string + mode: string + showWheelchairSetting?: boolean +} + +export interface ModesConfig { + accessModes: TransitModeConfig[] + initialState?: { + enabledModeButtons?: string[] + modeSettingValues?: ModeSettingValues + } + modeButtons?: ModeButtonDefinition[] + modeSettingDefinitions?: ModeSetting[] + transitModes: TransitModeConfig[] +} + +export interface ModeColorConfig { + color: string + textColor: string +} + +export interface TransitOperatorConfig extends TransitOperator { + colorMode?: 'gtfs' | 'gtfs-softened' | 'disabled' + modeColors?: Record +} + +/** Route Viewer config */ +export interface RouteViewerConfig { + /** Whether to hide the route linear shape inside a flex zone of that route. */ + hideRouteShapesWithinFlexZones?: boolean + /** Disable vehicle highlight if necessary (e.g. custom or inverted icons) */ + vehicleIconHighlight?: boolean + /** Customize vehicle icon padding (the default iconPadding is 2px in otp-ui) */ + vehicleIconPadding?: number + /** Interval for refreshing vehicle positions */ + vehiclePositionRefreshSeconds?: number +} + +/** Stop Viewer Config */ +export interface StopViewerConfig { + /** Radius (in meters) for searching nearby stops, rental vehicles, park and rides etc. */ + nearbyRadius?: number + /** The max. departures to show for each trip pattern in the Next Arrivals view */ + numberOfDepartures?: number + /** Whether to display block IDs with each departure in the schedule view. */ + showBlockIds?: boolean + /** + * Time window, in seconds, in which to search for next arrivals, + * so that, for example, if it is Friday and a route does + * not begin service again until Monday, we are showing its next + * departure and it is not entirely excluded from display. + */ + timeRange?: number +} + +/** The main application configuration object */ +export interface AppConfig { + accessibilityScore?: AccessibilityScoreConfig + api: ApiConfig + // Optional on declaration, populated with defaults in reducer if not configured. + autoPlan?: boolean | AutoPlanConfig + /** Whether the header brand should be clickable, and if so, reset the UI. */ + brandClickable?: boolean + branding?: string + bugsnag?: BugsnagConfig + co2?: CO2Config + companies?: Company[] + elevationProfile?: boolean + extraMenuItems?: AppMenuItemConfig[] + geocoder: GeocoderConfig + homeTimezone: string + /** Enable touch-friendly behavior on e.g. touch-screen kiosks that run a desktop OS. */ + isTouchScreenOnDesktop?: boolean + itinerary?: ItineraryConfig + language?: LanguageConfig + localization?: LocalizationConfig + map: MapConfig + mapillary?: MapillaryConfig + modes: ModesConfig + /** Interval in seconds past which a trip is no longer considered "on-time". */ + onTimeThresholdSeconds?: number + persistence?: PersistenceConfig + // Optional on declaration, populated with defaults in reducer if not configured. + phoneFormatOptions: PhoneFormatConfig + popups?: PopupConfig + reportIssue?: ReportIssueConfig + routeModeOverrides?: Record + routeViewer?: RouteViewerConfig + /** Approx delay in seconds to reset the UI to an initial URL if there is no user activity */ + sessionTimeoutSeconds?: number + stopViewer?: StopViewerConfig + /** App title shown in the browser title bar. */ + title?: string + transitOperators?: TransitOperatorConfig[] + + // TODO: add other config items. +} diff --git a/lib/util/i18n.js b/lib/util/i18n.js index 9dfe53126..2674a781e 100644 --- a/lib/util/i18n.js +++ b/lib/util/i18n.js @@ -117,6 +117,7 @@ async function loadOtpUiLocaleData(matchedLocale) { */ export async function loadLocaleData(matchedLocale, customMessages) { let messages + let otpUiLocale = matchedLocale switch (matchedLocale) { case 'es': // Spanish translation is not specific to a region messages = await import('../../i18n/es.yml') @@ -130,15 +131,18 @@ export async function loadLocaleData(matchedLocale, customMessages) { case 'vi': // Vietnamese translation is not specific to a region messages = await import('../../i18n/vi.yml') break - case 'zh': // Chinese translation is not specific to a region + case 'zh': // Chinese (Simplified) translation is not specific to a region messages = await import('../../i18n/zh.yml') + // The OTP-UI files for Chinese (Simplified) are (correctly) named `zh_Hans`. + // TODO: Rename this repo's zh files to zh_Hans + otpUiLocale = 'zh_Hans' break default: messages = await import('../../i18n/en-US.yml') break } - const otpUiMessages = await loadOtpUiLocaleData(matchedLocale) + const otpUiMessages = await loadOtpUiLocaleData(otpUiLocale) // Merge custom strings into the standard language strings. const mergedMessages = { diff --git a/lib/util/state-types.ts b/lib/util/state-types.ts new file mode 100644 index 000000000..18f7ef36b --- /dev/null +++ b/lib/util/state-types.ts @@ -0,0 +1,13 @@ +import { AppConfig } from './config-types' + +export interface OtpState { + config: AppConfig + // TODO: Add other OTP states + ui: any // TODO +} + +export interface AppReduxState { + calltaker?: any // TODO + otp: OtpState + user: any // TODO +} diff --git a/lib/util/stop-times.ts b/lib/util/stop-times.ts new file mode 100644 index 000000000..f58deae2f --- /dev/null +++ b/lib/util/stop-times.ts @@ -0,0 +1,82 @@ +import { addDays, isBefore } from 'date-fns' + +import { + PatternDayStopTimes, + PatternStopTimes, + StopData, + Time +} from '../components/util/types' + +import { getOrPutEntry } from './arrays' +import { + getRouteIdForPattern, + getStopTimesByPattern, + patternComparator, + routeIsValid, + stopTimeComparator +} from './viewer' + +function hasValidTimesAndRoute({ + pattern, + route, + times +}: PatternStopTimes): boolean { + return ( + times && + times.length !== 0 && + routeIsValid(route, getRouteIdForPattern(pattern)) + ) +} + +/** Filter any times according to time range set in config. */ +function isWithinDaysAhead(time: Time, now: Date, daysAhead: number) { + const departureTime = time.serviceDay + time.realtimeDeparture + return isBefore(departureTime, addDays(now, daysAhead)) +} + +/** Helper to sort and group stop times by pattern by service day */ +export function groupAndSortStopTimesByPatternByDay( + stopData: StopData, + now: Date, + daysAhead: number, + numberOfDepartures: number +): PatternDayStopTimes[] { + // construct a lookup table mapping pattern (e.g. 'ROUTE_ID-HEADSIGN') to + // an array of stoptimes + const stopTimesByPattern = getStopTimesByPattern(stopData) as Record< + string, + PatternStopTimes + > + + return ( + Object.values(stopTimesByPattern) + .filter(hasValidTimesAndRoute) + .map((pattern) => + pattern.times + .concat([]) + .sort(stopTimeComparator) + .filter((time) => isWithinDaysAhead(time, now, daysAhead)) + // remove excess departure times + .slice(0, numberOfDepartures) + // collect times by pattern by day + // (every pattern returned has stop times within the days ahead.) + .reduce>((days, t) => { + const { serviceDay } = t + const patternDay = getOrPutEntry(days, serviceDay, (day) => ({ + ...pattern, + day, + times: [] + })) + patternDay.times.push(t) + return days + }, {}) + ) + // Concatenate all resulting patterns + .reduce( + (result, cur) => result.concat(Object.values(cur)), + [] + ) + // Sort route times by service day then realtime departure + .sort(patternComparator) + ) +} diff --git a/lib/util/ui.ts b/lib/util/ui.ts index 60e1d4b0a..ed992e860 100644 --- a/lib/util/ui.ts +++ b/lib/util/ui.ts @@ -110,8 +110,32 @@ export function getItineraryView({ ui_itineraryView }: UrlParams): ItineraryView { return ( + ((ui_activeItinerary === null || + ui_activeItinerary === undefined || + `${ui_activeItinerary}` === '-1') && + (ui_itineraryView === ItineraryView.LIST_HIDDEN + ? ItineraryView.LIST_HIDDEN + : ItineraryView.LIST)) || ui_itineraryView || (isDefinedAndNotEqual(ui_activeItinerary, -1) && ItineraryView.FULL) || ItineraryView.LIST ) } + +/** + * Gets the new itinerary view to display based on current view. + */ +export function getMapToggleNewItineraryView( + currentView: ItineraryView +): ItineraryView { + switch (currentView) { + case ItineraryView.LEG: + return ItineraryView.LEG_HIDDEN + case ItineraryView.LIST: + return ItineraryView.LIST_HIDDEN + case ItineraryView.LEG_HIDDEN: + return ItineraryView.LEG + default: + return ItineraryView.LIST + } +} diff --git a/lib/util/viewer.js b/lib/util/viewer.js index bdb27b3cb..9f1f9ef9d 100644 --- a/lib/util/viewer.js +++ b/lib/util/viewer.js @@ -40,7 +40,7 @@ function excludeLastStop(stopTime) { * Checks that the given route object from an OTP pattern is valid. * If it is not, logs a warning message. * - * FIXME: there is currently a bug with the alernative transit index + * FIXME: there is currently a bug with the alternative transit index * where routes are not associated with the stop if the only stoptimes * for the stop are drop off only. See https://github.com/ibi-group/trimet-mod-otp/issues/217 * diff --git a/package.json b/package.json index 785bbf7e0..e25487124 100644 --- a/package.json +++ b/package.json @@ -38,29 +38,29 @@ "@bugsnag/js": "^7.17.0", "@bugsnag/plugin-react": "^7.17.0", "@floating-ui/react": "^0.19.2", - "@opentripplanner/base-map": "^3.0.14", - "@opentripplanner/core-utils": "^11.0.3", - "@opentripplanner/endpoints-overlay": "^2.0.8", - "@opentripplanner/from-to-location-picker": "^2.1.8", + "@opentripplanner/base-map": "^3.0.15", + "@opentripplanner/core-utils": "^11.0.6", + "@opentripplanner/endpoints-overlay": "^2.0.9", + "@opentripplanner/from-to-location-picker": "^2.1.9", "@opentripplanner/geocoder": "^1.4.2", "@opentripplanner/humanize-distance": "^1.2.0", - "@opentripplanner/icons": "^2.0.5", - "@opentripplanner/itinerary-body": "^5.0.6", - "@opentripplanner/location-field": "^2.0.8", + "@opentripplanner/icons": "^2.0.6", + "@opentripplanner/itinerary-body": "^5.1.0", + "@opentripplanner/location-field": "^2.0.11", "@opentripplanner/location-icon": "^1.4.1", - "@opentripplanner/map-popup": "^2.0.5", - "@opentripplanner/otp2-tile-overlay": "^1.0.5", + "@opentripplanner/map-popup": "^2.0.6", + "@opentripplanner/otp2-tile-overlay": "^1.0.6", "@opentripplanner/park-and-ride-overlay": "^2.0.6", - "@opentripplanner/printable-itinerary": "2.0.12", - "@opentripplanner/route-viewer-overlay": "^2.0.13", - "@opentripplanner/stop-viewer-overlay": "^2.0.6", - "@opentripplanner/stops-overlay": "^5.1.1", - "@opentripplanner/transit-vehicle-overlay": "^4.0.5", - "@opentripplanner/transitive-overlay": "^3.0.14", - "@opentripplanner/trip-details": "^5.0.2", - "@opentripplanner/trip-form": "^3.3.1", - "@opentripplanner/trip-viewer-overlay": "^2.0.6", - "@opentripplanner/vehicle-rental-overlay": "^2.1.2", + "@opentripplanner/printable-itinerary": "^2.0.14", + "@opentripplanner/route-viewer-overlay": "^2.0.14", + "@opentripplanner/stop-viewer-overlay": "^2.0.7", + "@opentripplanner/stops-overlay": "^5.1.2", + "@opentripplanner/transit-vehicle-overlay": "^4.0.6", + "@opentripplanner/transitive-overlay": "^3.0.16", + "@opentripplanner/trip-details": "^5.0.4", + "@opentripplanner/trip-form": "^3.3.4", + "@opentripplanner/trip-viewer-overlay": "^2.0.7", + "@opentripplanner/vehicle-rental-overlay": "^2.1.3", "@styled-icons/fa-regular": "^10.34.0", "@styled-icons/fa-solid": "^10.34.0", "blob-stream": "^0.1.3", diff --git a/percy/mock.har b/percy/mock.har index 168b2f6c5..5762498e1 100644 --- a/percy/mock.har +++ b/percy/mock.har @@ -327,6 +327,42 @@ "_blocked_queueing": 8.100999999442138 } }, + { + "request": { + "bodySize": 118, + "method": "POST", + "url": "http://localhost:9999/otp2/routers/default/index/graphql", + "httpVersion": "HTTP/2", + "queryString": [], + "postData": { + "mimeType": "application/json", + "text": "{\"query\":\"{\\n serviceTimeRange {\\n start\\n end\\n }\\n }\",\"variables\":{}}" + } + }, + "response": { + "status": 200, + "statusText": "", + "httpVersion": "HTTP/2", + "content": { + "mimeType": "application/json", + "size": 67, + "text": "{\"data\":{\"serviceTimeRange\":{\"start\":1661745600,\"end\":1735707600}}}" + }, + "headersSize": 502, + "bodySize": 569 + }, + "cache": {}, + "timings": { + "blocked": -1, + "dns": 0, + "connect": 0, + "ssl": 0, + "send": 0, + "wait": 204, + "receive": 0 + }, + "time": 204 + }, { "request": { "method": "GET", diff --git a/yarn.lock b/yarn.lock index 10b8965d3..61aeaa722 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,7 +43,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== @@ -51,13 +51,6 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - "@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.7", "@babel/compat-data@^7.15.0": version "7.15.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" @@ -311,12 +304,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== -"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.22.5": +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== @@ -345,25 +333,7 @@ "@babel/traverse" "^7.15.0" "@babel/types" "^7.15.0" -"@babel/highlight@^7.10.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.18.6": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.13": +"@babel/highlight@^7.10.4", "@babel/highlight@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== @@ -1125,34 +1095,13 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.3", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" - integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.10.5", "@babel/runtime@^7.14.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/runtime@^7.19.0", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.3", "@babel/runtime@^7.19.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.22.11" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.11.tgz#7a9ba3bbe406ad6f9e8dd4da2ece453eb23a77a4" integrity sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA== dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.20.7": - version "7.22.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" - integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== - dependencies: - regenerator-runtime "^0.13.11" - "@babel/template@^7.10.4", "@babel/template@^7.14.5", "@babel/template@^7.18.10", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" @@ -2093,20 +2042,13 @@ resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2" integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ== -"@mapbox/polyline@^1.1.0": +"@mapbox/polyline@^1.1.0", "@mapbox/polyline@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@mapbox/polyline/-/polyline-1.2.0.tgz#11f7481968a83bd9dde36273a50b8037af24a86b" integrity sha512-sIIi9clVZiTmXYqbXpSAoG+ZLsvQn7j9FJLqiNOG85KnXN8tz11MEhuW2M7NDEDIKi4hIMaSI1CKwH8oLuVxPQ== dependencies: meow "^6.1.1" -"@mapbox/polyline@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@mapbox/polyline/-/polyline-1.1.1.tgz#ab96e5e6936f4847a4894e14558daf43e40e3bd2" - integrity sha512-z9Sl7NYzsEIrAza658H92mc0OvpBjQwjp7Snv4xREKhsCMat7m1IKdWJMjQ5opiPYa0veMf7kCaSd1yx55AhmQ== - dependencies: - meow "^6.1.1" - "@mapbox/tiny-sdf@^2.0.4": version "2.0.5" resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz#cdba698d3d65087643130f9af43a2b622ce0b372" @@ -2402,92 +2344,25 @@ dependencies: "@octokit/openapi-types" "^10.0.0" -"@opentripplanner/base-map@^3.0.11", "@opentripplanner/base-map@^3.0.13": - version "3.0.13" - resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-3.0.13.tgz#8a6550f60414e268a53d3613c3839711976d3882" - integrity sha512-bzMa1ZrzqJ0Z+ZDf+M7XonvJeqpN4elcf1egZ/fPI6rDDHkIwGvPDBn5uqtIClYDYDmSuaS3nRmq6al+ZQ7Pwg== +"@opentripplanner/base-map@^3.0.13", "@opentripplanner/base-map@^3.0.14", "@opentripplanner/base-map@^3.0.15": + version "3.0.15" + resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-3.0.15.tgz#cf3e9ebd2aaed2bb2853c4969047a48fd86ba25c" + integrity sha512-LzoWKnnRwMMKsq7l1i8tKGwzU4mzKdtofpKAwxGap38jGqvd+Pn32yt5QcsMtTw0m5hWz9runxZAqfFO8ZYqGQ== dependencies: mapbox-gl "npm:empty-npm-package@1.0.0" maplibre-gl "^2.1.9" react-map-gl "^7.0.15" -"@opentripplanner/base-map@^3.0.14": - version "3.0.14" - resolved "https://registry.yarnpkg.com/@opentripplanner/base-map/-/base-map-3.0.14.tgz#538fc45fa5663695bab05452021125366b98e06a" - integrity sha512-vtTWlgZBta8nB9Imb2Bjpc5z6g8HamN7iXyGcNRRpOK01XRQsn5WA04VbeOhRpqRcN40vUvL0K1b3EBJFnTZqg== - dependencies: - mapbox-gl "npm:empty-npm-package@1.0.0" - maplibre-gl "^2.1.9" - react-map-gl "^7.0.15" - -"@opentripplanner/core-utils@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-10.0.0.tgz#ab5cba39097e2b15e634b1e70141076f3a5614d3" - integrity sha512-3T+P9GlBmeL8AHATUXbOouDtr3eYNv6VYMbVagFV9MBhYf3wSCJ3kAdBEWK+TQGfiWRfcxVJueuy8kvmVrkJtA== +"@opentripplanner/core-utils@^11.0.2", "@opentripplanner/core-utils@^11.0.6": + version "11.0.6" + resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.0.6.tgz#f8bd9796b4a9bc7490fb7fdca9ce661ed785bab1" + integrity sha512-ullRWOhvx4TzCmNk97Fk3FefX5jVlk0oYaLUsSfZNJJSiO0WKQadHBaxXBHQ6JHv7pk9SPuEP7xXfjz8YV6vRA== dependencies: "@conveyal/lonlat" "^1.4.1" "@mapbox/polyline" "^1.1.0" - "@opentripplanner/geocoder" "^1.4.1" - "@styled-icons/foundation" "^10.34.0" - "@turf/along" "^6.0.1" - bowser "^2.7.0" - chroma-js "^2.4.2" - date-fns "^2.28.0" - date-fns-tz "^1.2.2" - graphql "^16.6.0" - lodash.clonedeep "^4.5.0" - lodash.isequal "^4.5.0" - qs "^6.9.1" - -"@opentripplanner/core-utils@^11.0.3": - version "11.0.3" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-11.0.3.tgz#b8aa21f81be420c07128e78c20a74d32d0034476" - integrity sha512-hTLZwU8YyokTA86tIa+iy46g0lqcjBX/HDxyzwxvbm6lFnUvUhHgWRPKvr8fzUA5aE0vYzwlj16WygQ43n4ubQ== - dependencies: - "@conveyal/lonlat" "^1.4.1" - "@mapbox/polyline" "^1.1.0" - "@opentripplanner/geocoder" "^1.4.1" - "@styled-icons/foundation" "^10.34.0" - "@turf/along" "^6.0.1" - bowser "^2.7.0" - chroma-js "^2.4.2" - date-fns "^2.28.0" - date-fns-tz "^1.2.2" - graphql "^16.6.0" - lodash.clonedeep "^4.5.0" - lodash.isequal "^4.5.0" - qs "^6.9.1" - -"@opentripplanner/core-utils@^8.2.1": - version "8.3.2" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-8.3.2.tgz#5095dd3ae8493e478b3f599353c6804b35f1e1dd" - integrity sha512-Rij/vfnvB9+uoxfNoq/gPRr+T9YWiqrxnYHzzEbh+R4Jro7D8iwRbD08cHJF9RRMTv/s4Y2IQrzAnmZVXiLw6w== - dependencies: - "@conveyal/lonlat" "^1.4.1" - "@mapbox/polyline" "^1.1.0" - "@opentripplanner/geocoder" "^1.4.0" - "@styled-icons/foundation" "^10.34.0" - "@turf/along" "^6.0.1" - bowser "^2.7.0" - chroma-js "^2.4.2" - date-fns "^2.28.0" - date-fns-tz "^1.2.2" - graphql "^16.6.0" - lodash.clonedeep "^4.5.0" - lodash.isequal "^4.5.0" - qs "^6.9.1" - -"@opentripplanner/core-utils@^9.0.0", "@opentripplanner/core-utils@^9.0.2", "@opentripplanner/core-utils@^9.0.3": - version "9.0.3" - resolved "https://registry.yarnpkg.com/@opentripplanner/core-utils/-/core-utils-9.0.3.tgz#c1ebdcc3ad5999fb28427102c9be7d7268f6bd37" - integrity sha512-8P3Bi41jF7z18P/soo6lEw+nrqarsyGMAxivsF1/kMJdRo4wnakp0zcrVZjDXTxoR6LPtj6Kkuxv3JQFO9jKiw== - dependencies: - "@conveyal/lonlat" "^1.4.1" - "@mapbox/polyline" "^1.1.0" - "@opentripplanner/geocoder" "^1.4.1" + "@opentripplanner/geocoder" "^1.4.2" "@styled-icons/foundation" "^10.34.0" "@turf/along" "^6.0.1" - bowser "^2.7.0" chroma-js "^2.4.2" date-fns "^2.28.0" date-fns-tz "^1.2.2" @@ -2496,42 +2371,26 @@ lodash.isequal "^4.5.0" qs "^6.9.1" -"@opentripplanner/endpoints-overlay@^2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-2.0.8.tgz#18c8532a24d3d97cfc9e1208f7b5e0c1f8ae1341" - integrity sha512-ziIwLJxtFWEX1z/RiN60HqqLSt/GUovbWbWGkNdbHOZFT+jm7wrNbDSNWHfDJjwPMQaKO8SanVpIhmEcVgTOKQ== +"@opentripplanner/endpoints-overlay@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@opentripplanner/endpoints-overlay/-/endpoints-overlay-2.0.9.tgz#74a5bab257686130dea768dc921ac197dc0a7c0d" + integrity sha512-yILcYaxUYqcDG28eSmmaDUQsO0BrPerFl6UyTWhoxLUp/w4T3bC4/1wrU1ak8OfLeqkZK+SoR/7o8M2Z7nIigw== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" "@opentripplanner/location-icon" "^1.4.1" "@styled-icons/fa-solid" "^10.34.0" flat "^5.0.2" -"@opentripplanner/from-to-location-picker@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-2.1.6.tgz#ce918d3d06be7e44276892786ac572bdd6b8dba1" - integrity sha512-yv+ngCAlClV00FGY0K+tcCnFVp9M1VzMqqT5w3Ybc+ZxfuSFRLzTth+ZWRsz5Vo0Z9cswKHP5tPEuLE2jEBhow== - dependencies: - "@opentripplanner/location-icon" "^1.4.1" - flat "^5.0.2" - -"@opentripplanner/from-to-location-picker@^2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-2.1.7.tgz#ff21232b5211c1c826ddd25022320520b88d66c5" - integrity sha512-bCetjnEN9Er15TnoMGuSyBuqysJFsNVbdfl79CMBxScbMIFiFHmU+B45abejNVZEVFYeBzusba3s/W1EXHlvBQ== - dependencies: - "@opentripplanner/location-icon" "^1.4.1" - flat "^5.0.2" - -"@opentripplanner/from-to-location-picker@^2.1.8": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-2.1.8.tgz#4e4aaed7c1836f379ddfa77ee2802cb23fb3e428" - integrity sha512-qrf6w+8Q+Zord8epZeNKKEBhASOO8iemRagTtM/twNeukqaProCgi8yBaallQ18FHjm3pEjXnOARgKKAL4skWA== +"@opentripplanner/from-to-location-picker@^2.1.7", "@opentripplanner/from-to-location-picker@^2.1.8", "@opentripplanner/from-to-location-picker@^2.1.9": + version "2.1.9" + resolved "https://registry.yarnpkg.com/@opentripplanner/from-to-location-picker/-/from-to-location-picker-2.1.9.tgz#312bee9605c0e47233b659c947b65790e4316b39" + integrity sha512-+DXL6PIv239bygN0dX7BWdBGC2edCMA5Zp+cMrDze1i8D/iT9eXfBfX9d1g7Whm9Q7U2rc3qBXHlGLeoCupVbQ== dependencies: "@opentripplanner/location-icon" "^1.4.1" flat "^5.0.2" -"@opentripplanner/geocoder@^1.4.0", "@opentripplanner/geocoder@^1.4.1", "@opentripplanner/geocoder@^1.4.2": +"@opentripplanner/geocoder@^1.4.2": version "1.4.2" resolved "https://registry.yarnpkg.com/@opentripplanner/geocoder/-/geocoder-1.4.2.tgz#0f827dffca42c7f7a23063b54990a291dd028b80" integrity sha512-yzMVrKXEHO6Y50j9kntk1+odvHaqn9K9D4aKJAd+EabhiZckesfScLb0updmWRUloEWjN45nuDSFto8fbU7Uiw== @@ -2546,56 +2405,22 @@ resolved "https://registry.yarnpkg.com/@opentripplanner/humanize-distance/-/humanize-distance-1.2.0.tgz#71cf5d5d1b756adef15300edbba0995ccd4b35ee" integrity sha512-x0QRXMDhypFeazZ6r6vzrdU8vhiV56nZ/WX6zUbxpgp6T9Oclw0gwR2Zdw6DZiiFpSYVNeVNxVzZwsu6NRGjcA== -"@opentripplanner/icons@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-2.0.3.tgz#c7c587f5b3b8ea050f50af3537e2e409d7258829" - integrity sha512-IHoFtTozacXMXBOc87kTcd1V9hXAP4UQEq6RCzhTTCwOEFQ2/aWHSJ+aIyr+3XFIKYyfQ0b8JAdJnEoa1yJzew== - dependencies: - "@opentripplanner/core-utils" "^8.2.1" - prop-types "^15.7.2" - -"@opentripplanner/icons@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-2.0.4.tgz#6c5d7236bd63c73526000cc0c253ef9cd1057e03" - integrity sha512-9KYuC22iy8jw9drV9mKn+T4kfAW90mh5ltHgfhrP082TOlc8k1urCnm3KCG5C80s/Ov2JlMP09oOsLWVdmn97w== - dependencies: - "@opentripplanner/core-utils" "^9.0.0" - prop-types "^15.7.2" - -"@opentripplanner/icons@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-2.0.5.tgz#7274597e1d568b1d18861c36d9d4f77f4a6f82f3" - integrity sha512-0QIXsoGHK8c5Y2rFwKKfsDFTIL6NXuvTUtKDNH6qybkMDPc+IovswCdFjKLDvgR+7pmE5iEmhTvNBHvw/MZjig== +"@opentripplanner/icons@^2.0.5", "@opentripplanner/icons@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@opentripplanner/icons/-/icons-2.0.6.tgz#e1339f75a1c9cb52ad5bddf0b024a260e8f63725" + integrity sha512-bKwwtugFN9RPpKmqXcukXC4TcWHjmOqPWaMEvjtnnV5LdeuoaOBQ8dJeUUnEKd/dPeOJAF9l3oBVtvM3VAc8jg== dependencies: - "@opentripplanner/core-utils" "^9.0.2" + "@opentripplanner/core-utils" "^11.0.2" prop-types "^15.7.2" -"@opentripplanner/itinerary-body@^5.0.0": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-5.0.5.tgz#3038a910291e42d1a7ed3a03abcd8ecd32cd958e" - integrity sha512-58xn0cadRt7Vth8arjExZOt85KVESkqt9MKh2yqnMeXeSFMvP14/+jobgBRY+xsAh+EcLWPVBKq3ylas8npmEw== - dependencies: - "@opentripplanner/core-utils" "^9.0.2" - "@opentripplanner/humanize-distance" "^1.2.0" - "@opentripplanner/icons" "^2.0.4" - "@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.0.2", "@opentripplanner/itinerary-body@^5.0.6": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-5.0.6.tgz#33ad8c0dfa06702ed76ecbbd53974a1c0df8d21c" - integrity sha512-+9xDpo6HJrgOPyC2aOb6cw/Juimf0BBF9j3brSstJA9DIODp3cMsz39Iv35rETIory9R4wstEYFBwFzwE62I+g== +"@opentripplanner/itinerary-body@^5.0.2", "@opentripplanner/itinerary-body@^5.0.7", "@opentripplanner/itinerary-body@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@opentripplanner/itinerary-body/-/itinerary-body-5.1.0.tgz#29bba91db379bb4f63d13ec95c1cb058248d0683" + integrity sha512-DTS4KlbqokS/ZA+gL0QCuPFORoPq/GVcHlRv0s9YqKmr0aS5eBBLg4f4mOuv1CcNeHKyZc6lNb/Ro3epjshd5A== dependencies: - "@opentripplanner/core-utils" "^9.0.2" + "@opentripplanner/core-utils" "^11.0.2" "@opentripplanner/humanize-distance" "^1.2.0" - "@opentripplanner/icons" "^2.0.4" + "@opentripplanner/icons" "^2.0.5" "@opentripplanner/location-icon" "^1.4.1" "@styled-icons/fa-solid" "^10.34.0" "@styled-icons/foundation" "^10.34.0" @@ -2606,13 +2431,13 @@ react-resize-detector "^4.2.1" string-similarity "^4.0.4" -"@opentripplanner/location-field@^2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-2.0.8.tgz#cf684a5dd902410378924b06434783f35c758c85" - integrity sha512-xmJJgdRsKBaiWSHdh8XDtyq+deuvn/QWgSh0Cab9/BlNlQGlodl1IF6j+faf+93zfmHbwXcl8ZZ/rdG1Z5fqLw== +"@opentripplanner/location-field@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@opentripplanner/location-field/-/location-field-2.0.11.tgz#06a31ec2e62cf5b542d3392281bc28b9dd1ea416" + integrity sha512-viyC1vInANBwsd9w3KtE6Y4qYh/yuJAWDStlIx9Ce9z2TraoBHxYKlVhx6w+JxIgiBziEdvZ2staMjo0KKn13A== dependencies: "@conveyal/geocoder-arcgis-geojson" "^0.0.3" - "@opentripplanner/core-utils" "^9.0.2" + "@opentripplanner/core-utils" "^11.0.2" "@opentripplanner/geocoder" "^1.4.2" "@opentripplanner/humanize-distance" "^1.2.0" "@opentripplanner/location-icon" "^1.4.1" @@ -2627,30 +2452,20 @@ "@styled-icons/fa-regular" "^10.34.0" "@styled-icons/fa-solid" "^10.34.0" -"@opentripplanner/map-popup@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-2.0.4.tgz#268230e385f92c14f1526e079af037f74fc63d4c" - integrity sha512-pcfDpCpgnMImqnrAlvSppAznpdHr3uFERA0vpaY6aNg3ZgD9pUGq+2AEKiB0snF2pDSXIxAhtfboGzgpnIaUYg== - dependencies: - "@opentripplanner/base-map" "^3.0.11" - "@opentripplanner/core-utils" "^8.2.1" - "@opentripplanner/from-to-location-picker" "^2.1.6" - flat "^5.0.2" - -"@opentripplanner/map-popup@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-2.0.5.tgz#a6974712e841e522b3b2add520e7226dea797c0c" - integrity sha512-tpIWibuWjWbeNEK0URI9lRaZfMRZo+8iucWaSJ8uHTLbQyLj0htIRTDx2Jf+WYJVIjsiwjoAzfSSEbzub3lrvA== +"@opentripplanner/map-popup@^2.0.5", "@opentripplanner/map-popup@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@opentripplanner/map-popup/-/map-popup-2.0.6.tgz#c45617493452616dcc208cc7fcfe606b5e13a376" + integrity sha512-BRJRNaqRglK/WjjDvs2o7Vraa6yg7DfJMzUGczOjmdZsB/3Zo/7XHVv++bxjvEJR7Gds+hfseqODa1882lvy+Q== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" - "@opentripplanner/from-to-location-picker" "^2.1.7" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" + "@opentripplanner/from-to-location-picker" "^2.1.8" flat "^5.0.2" -"@opentripplanner/otp2-tile-overlay@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@opentripplanner/otp2-tile-overlay/-/otp2-tile-overlay-1.0.5.tgz#81f96d17f98bda9d354f11182c9a2b3f29923f48" - integrity sha512-TXC9h6T/fZALLqF1fkwxtNk6ZTLHYEHEXXHNjJNQ7tGTG4eA1sKTg2AWYGk9DyD2FjD4nIFgXROVhSXHQGf1mg== +"@opentripplanner/otp2-tile-overlay@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@opentripplanner/otp2-tile-overlay/-/otp2-tile-overlay-1.0.6.tgz#2358a8ec791b0fe4da48120f752bd55aa6ce6be7" + integrity sha512-MKPVz70AOqNP2g7jrfdSRbCp8RFXKdSSrTq6wPlBV/ajTmK6GTgsHR48koHYyI0425XxkLFw9TB/5Lmj4FwaBQ== dependencies: "@opentripplanner/map-popup" "^2.0.5" @@ -2662,22 +2477,22 @@ "@opentripplanner/base-map" "^3.0.13" "@opentripplanner/from-to-location-picker" "^2.1.7" -"@opentripplanner/printable-itinerary@2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-2.0.12.tgz#1eb42d76e7a359660116bad77937ed3c4ccb2f26" - integrity sha512-gdlU5uDtcJioM1JqnA2USuvahYGpw6HolJEaCfyTMBT5PiChGf5dEX8tWV8F8c/YnlOyQJ/M3EMEtlN2iNxP5g== +"@opentripplanner/printable-itinerary@^2.0.14": + version "2.0.14" + resolved "https://registry.yarnpkg.com/@opentripplanner/printable-itinerary/-/printable-itinerary-2.0.14.tgz#ab97cb3a522e999018da06ae342a8bf1255c7552" + integrity sha512-XOQh6sTlQGXdlASb4Db0WqyTzlGk/+TtYCl/JTBzVeURwvclG/NKoztErWB4ky9Q2kCdOS9n3SLA1dUNM3r0jA== dependencies: - "@opentripplanner/core-utils" "^9.0.2" - "@opentripplanner/itinerary-body" "^5.0.2" + "@opentripplanner/core-utils" "^11.0.2" + "@opentripplanner/itinerary-body" "^5.0.7" -"@opentripplanner/route-viewer-overlay@^2.0.13": - version "2.0.13" - resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-2.0.13.tgz#0772b074fd19dcc52ce56455710fa3fcbfc42271" - integrity sha512-FYaj9O3HTcnr78XpRaWIKIBiSWvNgeoibksHJCwvSmwVqgCHd3aA0WHKiNOui4pfiic2CdwbiQgY0ObJfHn/bg== +"@opentripplanner/route-viewer-overlay@^2.0.14": + version "2.0.14" + resolved "https://registry.yarnpkg.com/@opentripplanner/route-viewer-overlay/-/route-viewer-overlay-2.0.14.tgz#e69d3933465651ef1df5bd74c522ed5e2a83dbd4" + integrity sha512-w0RyJLo/XnSKugm7mylZytbMMOjGf8A0yJejCmlJeZpsiNlSAQvwHQ5m9uHIDt4ONTM93PRlzD8IcDFCsM8HUw== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" point-in-polygon "^1.1.0" "@opentripplanner/scripts@^1.2.0": @@ -2691,43 +2506,43 @@ glob-promise "^4.2.2" js-yaml "^4.1.0" -"@opentripplanner/stop-viewer-overlay@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-2.0.6.tgz#83e622651dd5e46b6766e180c1cdfb41aeb0d193" - integrity sha512-psrBNQwHveFf9Vtsj0VqJDjZUN/O8zYQbO9F3FYWhRKeKcwbjfJsf8BUHsy9w8qvBprUXTgU4SIzrB27Y2xLXw== +"@opentripplanner/stop-viewer-overlay@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@opentripplanner/stop-viewer-overlay/-/stop-viewer-overlay-2.0.7.tgz#f8c5b263b08dc08568f3f18ff30adcf4ff2e2934" + integrity sha512-z7BeKYmIvFPRv782wNuxpFxsZXKl1y0X7SH/c4O17m+8zJXmadcSEx1lQ0rD/fBK5P75hBmQhNMG3Sr3S/EhQQ== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" -"@opentripplanner/stops-overlay@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-5.1.1.tgz#c33ed904b42b8d4a92c51693538b82dca139c8fa" - integrity sha512-/Szhb4Sjry1w33OzI8xsYvUTc8jo1GZacz76ZAK/l0IkhkBHYomY4FH/NEkG4OKq7LjYqjRAKeElyjovpZLlpg== +"@opentripplanner/stops-overlay@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@opentripplanner/stops-overlay/-/stops-overlay-5.1.2.tgz#8a0bd94392ca792da7357635b6f25347e47f7e9f" + integrity sha512-arYArwgRLkQKBt6GwmFJ4GIcOgSiVl82SLPzJ5BgQjTVBTX0sfvjOe+5T0ZUotRKuFvnrgXSttlv5iCLmqVGIw== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/from-to-location-picker" "^2.1.7" - "@opentripplanner/map-popup" "^2.0.4" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/from-to-location-picker" "^2.1.8" + "@opentripplanner/map-popup" "^2.0.5" flat "^5.0.2" -"@opentripplanner/transit-vehicle-overlay@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@opentripplanner/transit-vehicle-overlay/-/transit-vehicle-overlay-4.0.5.tgz#4bd0d07e36ed8cb63724c387231ec47afbeaba23" - integrity sha512-jxSThu2KoGNQ9JHhDr1Ut89ULCekfZWFIHVWBGo1kcH0bocCizlm+0uLe95rmVif5R1iQjj0Zfx6Bx6FY1KT8Q== +"@opentripplanner/transit-vehicle-overlay@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@opentripplanner/transit-vehicle-overlay/-/transit-vehicle-overlay-4.0.6.tgz#01d1c3df08a4fce4b0aaf1bace04db5d75fc4d34" + integrity sha512-Y5vRna+xrhYprtJIHxE9EazFhMCr3+wl2I9YnfGh+n0NxMINqLE24rQP/dudyhR/0Jj8dOQLC2bjI144kq/Uzw== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" - "@opentripplanner/icons" "^2.0.3" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" + "@opentripplanner/icons" "^2.0.5" flat "^5.0.2" -"@opentripplanner/transitive-overlay@^3.0.14": - version "3.0.14" - resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-3.0.14.tgz#6a9a2ca676ddd7567d36b337c4ddee1591eed058" - integrity sha512-/CMhT56GrjMAK79edtVw7HjLt/jgXo/rWv48oYMaF8u8nZ/5BH7t2vH6Q6HN3POXghctJr38bm2ecWcp4n1baA== +"@opentripplanner/transitive-overlay@^3.0.16": + version "3.0.16" + resolved "https://registry.yarnpkg.com/@opentripplanner/transitive-overlay/-/transitive-overlay-3.0.16.tgz#36d51e345556d1c7b56047247762b10d3884f4b2" + integrity sha512-JfJ/5CG2Nhcjy9likuvgLUFK0nqgw6PRecflt1+wUiVTBJ/j/4l9l+QPpqrzFs1gtW0rtpiCUPvb7dXc/At61g== dependencies: "@mapbox/polyline" "^1.1.1" - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" - "@opentripplanner/itinerary-body" "^5.0.0" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" + "@opentripplanner/itinerary-body" "^5.0.2" "@turf/bbox" "^6.5.0" "@turf/bearing" "^6.5.0" "@turf/destination" "^6.5.0" @@ -2736,23 +2551,23 @@ "@turf/midpoint" "^6.5.0" lodash.isequal "^4.5.0" -"@opentripplanner/trip-details@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-5.0.2.tgz#46e5c27c4ac6b1f12b638b5c361882358aa6cdc0" - integrity sha512-yeWokJdnka0bW67yHxnvHg2w3L+qQJaaSmZjvnut8AOVAUc2wzUGMshwpU5hcUYpNkVCwNuSUjR1fIZ4rGaoGw== +"@opentripplanner/trip-details@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-details/-/trip-details-5.0.4.tgz#ca1c23e9e518ddd49ba15e019e4605eaa5096875" + integrity sha512-9eE4I3B2xYpNQskWU1QMDqnsiY5/LJV2A3PyzrFqbo+dJFv1ONuA9ZiXx/yxkbVWlH6B0B6FpifzPGfTa2CZ7g== dependencies: - "@opentripplanner/core-utils" "^9.0.3" + "@opentripplanner/core-utils" "^11.0.2" "@styled-icons/fa-solid" "^10.34.0" flat "^5.0.2" react-animate-height "^3.0.4" -"@opentripplanner/trip-form@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-3.3.1.tgz#3cf3212d1948ff28440600a6951847bf7a77467a" - integrity sha512-XDioTMNXSugz1iPWRVeP91xqJYbEOI/ZdEL9fHXllIT5c8dkg35mjCNtGtOJzaZZVtY+p3KxzD4lEWLVdAD45w== +"@opentripplanner/trip-form@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-form/-/trip-form-3.3.4.tgz#12847736515aa11e1c69c8db627a54c1ad5e7e89" + integrity sha512-adEjAJ+2ygkc6vptiD7tHI3O+1tEIvoDsM3+/DFIgvtKxkSKuRzzTNXn67CI2pmEev75aiiTyT97PiWbDKRTJw== dependencies: "@floating-ui/react" "^0.19.2" - "@opentripplanner/core-utils" "^10.0.0" + "@opentripplanner/core-utils" "^11.0.2" "@styled-icons/bootstrap" "^10.34.0" "@styled-icons/boxicons-regular" "^10.38.0" "@styled-icons/fa-regular" "^10.37.0" @@ -2762,29 +2577,29 @@ react-indiana-drag-scroll "^2.0.1" react-inlinesvg "^2.3.0" -"@opentripplanner/trip-viewer-overlay@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-2.0.6.tgz#96e8e6026812a7e64ccd431f67a01bb168acbe5d" - integrity sha512-Pp+pTykcnu7Udq8DDsVVyYVdENtzBtajXXM4W/BWgPzNAoXuKU0IA6HexK1WiR3IQiQV9sfZ0Gf27JdoNcDxhg== +"@opentripplanner/trip-viewer-overlay@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@opentripplanner/trip-viewer-overlay/-/trip-viewer-overlay-2.0.7.tgz#abc53da75de1e12a416a552976c2309e2b4023eb" + integrity sha512-H/QrV2dnNFQbjRErxneySObSp0kzpRWTpKyqC3daxMg7jk+zRyWQthE4cpcoJSVjaoAibWwI3DZqNjSFMgUfWw== dependencies: "@mapbox/polyline" "^1.1.0" - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" "@opentripplanner/types@^6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/@opentripplanner/types/-/types-6.1.1.tgz#ed28406d71b48d5299edc7ac884583d00917edc9" integrity sha512-VNqWQI8hQfQQZQZFHrUbxGZzldNODMls3wXM9aawUJ7OJRjjmNOhkgTjohMcpvA3Ed3wUAkzOXByBQO3MTVH6Q== -"@opentripplanner/vehicle-rental-overlay@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-2.1.2.tgz#5939e07cd15a25f92bfeb6229bd583de205cfee7" - integrity sha512-SCufc7d10ixE+82pfnalbZvEF4QBRw+9qmkTNW8lalArX8UbngUoZEaZ3QKTT0PIXuxcwqobZNtVyxH8iz5Vig== +"@opentripplanner/vehicle-rental-overlay@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@opentripplanner/vehicle-rental-overlay/-/vehicle-rental-overlay-2.1.3.tgz#0449e44cfeefb57df9432311ba2d26e2149daaf4" + integrity sha512-tvde7bkFzQouJgAPhRUpNLt5pIxtXEf+8e6bFu2mkXyuUMK9H0eFBoZFoBAmvzky9dGcYa9mXnMnzHnK0tu5LQ== dependencies: - "@opentripplanner/base-map" "^3.0.13" - "@opentripplanner/core-utils" "^9.0.0" - "@opentripplanner/from-to-location-picker" "^2.1.7" - "@opentripplanner/map-popup" "^2.0.4" + "@opentripplanner/base-map" "^3.0.14" + "@opentripplanner/core-utils" "^11.0.2" + "@opentripplanner/from-to-location-picker" "^2.1.8" + "@opentripplanner/map-popup" "^2.0.5" "@styled-icons/fa-solid" "^10.34.0" flat "^5.0.2" lodash.memoize "^4.1.2" @@ -3185,15 +3000,7 @@ "@babel/runtime" "^7.15.3" "@styled-icons/styled-icon" "^10.6.3" -"@styled-icons/fa-regular@^10.34.0": - version "10.34.0" - resolved "https://registry.yarnpkg.com/@styled-icons/fa-regular/-/fa-regular-10.34.0.tgz#b6761412c0b2985eae60d20bff77e76cecd2deb8" - integrity sha512-QUqljT+uIfowGurV5GzjnHA1qvq922H+6yGRzQYBxBZ5Vgaak4Q71jtCSMqZKXcOG3tngMyhrOuaNaPOw1Q1pQ== - dependencies: - "@babel/runtime" "^7.14.0" - "@styled-icons/styled-icon" "^10.6.3" - -"@styled-icons/fa-regular@^10.37.0": +"@styled-icons/fa-regular@^10.34.0", "@styled-icons/fa-regular@^10.37.0": version "10.47.0" resolved "https://registry.yarnpkg.com/@styled-icons/fa-regular/-/fa-regular-10.47.0.tgz#0c2e3196bec0706d2cb7fce5f7f49fdefc15e3b1" integrity sha512-UL/MwhwlJ5SbcioI+UBR8KocYt6i+20akKn1ZNj0Pjgm2kv2odEJSWWkHCxh8J7T7s0q/Wvs3CMsBTMrKdKDpQ== @@ -3201,15 +3008,7 @@ "@babel/runtime" "^7.20.7" "@styled-icons/styled-icon" "^10.7.0" -"@styled-icons/fa-solid@^10.34.0": - version "10.34.0" - resolved "https://registry.yarnpkg.com/@styled-icons/fa-solid/-/fa-solid-10.34.0.tgz#315a6f6f25d38202d3387928191731e4c7a3bb18" - integrity sha512-PnJMUPcbuPA7gswxl9FKd725qaqP5VSbmX7rk+ZZ7ivdA6Dbi1VoKYXqAc62OEisGDhzn5g56KbBvE14W1n7vw== - dependencies: - "@babel/runtime" "^7.14.0" - "@styled-icons/styled-icon" "^10.6.3" - -"@styled-icons/fa-solid@^10.37.0": +"@styled-icons/fa-solid@^10.34.0", "@styled-icons/fa-solid@^10.37.0": version "10.47.0" resolved "https://registry.yarnpkg.com/@styled-icons/fa-solid/-/fa-solid-10.47.0.tgz#011ffe890054f10957e247a798214c304e2f95c3" integrity sha512-8AJjbOAkvPlB/ypFzWo++CZxRDlFQfvWQSjWzVU/v/4kjGRWIiVB+rJrTB7joDUd5Oas/se4cSiDkSA+XR/Fqg== @@ -5296,11 +5095,6 @@ bowser@^1.9.3: resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== -bowser@^2.7.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" - integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== - boxen@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -5761,7 +5555,7 @@ chalk@2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -6945,22 +6739,12 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns-tz@^1.1.4: - version "1.3.6" - resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.3.6.tgz#4195a58a2f86eda55ea69fb477f3ed8a6e2188ac" - integrity sha512-C8q7mErvG4INw1ZwAFmPlGjEo5Sv4udjKVbTc03zpP9cu6cp5AemFzKhz0V68LGcWEtX5mJudzzg3G04emIxLA== - -date-fns-tz@^1.2.2: +date-fns-tz@^1.1.4, date-fns-tz@^1.2.2: version "1.3.8" resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.3.8.tgz#083e3a4e1f19b7857fa0c18deea6c2bc46ded7b9" integrity sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ== -date-fns@^2.23.0: - version "2.29.3" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" - integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== - -date-fns@^2.28.0: +date-fns@^2.23.0, date-fns@^2.28.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -9022,7 +8806,7 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-intrinsic@^1.0.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== @@ -9032,15 +8816,6 @@ get-intrinsic@^1.0.2: has-proto "^1.0.1" has-symbols "^1.0.3" -get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -9363,16 +9138,11 @@ has-proto@^1.0.1: resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== -has-symbols@^1.0.1, has-symbols@^1.0.3: +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -9601,9 +9371,9 @@ htmlparser2@^6.1.0: entities "^2.0.0" http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-deceiver@^1.2.7: version "1.2.7" @@ -10123,27 +9893,13 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" -is-core-module@^2.0.0, is-core-module@^2.5.0, is-core-module@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.6.0.tgz#d7553b2526fe59b92ba3e40c8df757ec8a709e19" - integrity sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.13.0: +is-core-module@^2.0.0, is-core-module@^2.13.0, is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.6.0: version "2.13.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== dependencies: has "^1.0.3" -is-core-module@^2.2.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== - dependencies: - has "^1.0.3" - is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -13317,12 +13073,7 @@ object-hash@^1.3.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== -object-inspect@^1.11.0, object-inspect@^1.6.0, object-inspect@^1.7.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - -object-inspect@^1.9.0: +object-inspect@^1.11.0, object-inspect@^1.6.0, object-inspect@^1.7.0, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -14874,16 +14625,7 @@ prop-types-extra@^1.0.1: react-is "^16.3.2" warning "^4.0.0" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - -prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -15029,14 +14771,7 @@ qs@6.9.7: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.10.3, qs@^6.5.1: - version "6.10.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" - integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== - dependencies: - side-channel "^1.0.4" - -qs@^6.3.0, qs@^6.9.1: +qs@^6.10.3, qs@^6.3.0, qs@^6.5.1, qs@^6.9.1: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== @@ -15324,7 +15059,7 @@ react-intl@^5.20.10: intl-messageformat "9.9.1" tslib "^2.1.0" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -15803,16 +15538,11 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regenerator-runtime@^0.13.7: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" @@ -16091,15 +15821,7 @@ resolve@1.18.1: is-core-module "^2.0.0" path-parse "^1.0.6" -resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.8.1: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -resolve@^1.10.0: +resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.8.1: version "1.22.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== @@ -16492,7 +16214,7 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807" integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA== -"semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -16514,11 +16236,6 @@ semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.1.3, semver@^7.2.1, semver@^ dependencies: lru-cache "^6.0.0" -semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -17957,16 +17674,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0: version "2.5.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.2.tgz#1b6f07185c881557b0ffa84b111a0106989e8338" integrity sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA== -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"