Skip to content

Commit

Permalink
Merge pull request #137 from OneBusAway/arr-dep
Browse files Browse the repository at this point in the history
arrival and departure experience overhaul
  • Loading branch information
aaronbrethorst authored Nov 25, 2024
2 parents 607b394 + 39b46f4 commit 111fbd9
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 149 deletions.
65 changes: 21 additions & 44 deletions src/components/ArrivalDeparture.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
<script>
export let routeShortName;
export let tripHeadsign;
export let scheduledArrivalTime;
export let predictedArrivalTime;
export let tripId;
export let vehicleId;
export let serviceDate;
import { createEventDispatcher } from 'svelte';
export let arrivalDeparture;
let routeShortName = arrivalDeparture.routeShortName;
let tripHeadsign = arrivalDeparture.tripHeadsign;
let scheduledArrivalTime = arrivalDeparture.scheduledArrivalTime;
let predictedArrivalTime = arrivalDeparture.predictedArrivalTime;
import { t } from 'svelte-i18n';
const dispatch = createEventDispatcher();
function formatTime(time) {
const date = new Date(time);
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
Expand Down Expand Up @@ -62,39 +57,21 @@
return `${Math.floor((chosenTime - now) / 60000)}m`;
}
function handleTripDetail() {
dispatch('showTripDetails', {
tripId,
vehicleId,
serviceDate,
routeShortName,
tripHeadsign,
scheduledArrivalTime,
timeToReach: calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime),
arrivalStatus: getArrivalStatus(predictedArrivalTime, scheduledArrivalTime)
});
}
</script>

<button
on:click={handleTripDetail}
class="flex h-auto w-full items-center justify-between border-b-[1px] border-[#C6C6C8] bg-[#ffffff] p-4 hover:cursor-pointer hover:bg-[#e3e3e3] dark:border-[#313135] dark:bg-[#1c1c1c] hover:dark:bg-[#363636]"
>
<div class="flex flex-col gap-1">
<p class="text-left text-xl font-semibold text-black dark:text-white">
{routeShortName} - {tripHeadsign}
</p>
<p class="text-left font-semibold text-black dark:text-white">
<span class="text-md">{formatTime(scheduledArrivalTime)}</span> -
<span class={getArrivalStatus(predictedArrivalTime, scheduledArrivalTime).color}>
{getArrivalStatus(predictedArrivalTime, scheduledArrivalTime).text}
</span>
</p>
</div>
<div>
<p class="text-lg font-semibold text-black dark:text-white">
{calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime)}
</p>
</div>
</button>
<div class="flex flex-col gap-1">
<p class="text-left text-xl font-semibold text-black dark:text-white">
{routeShortName} - {tripHeadsign}
</p>
<p class="text-left font-semibold text-black dark:text-white">
<span class="text-md">{formatTime(scheduledArrivalTime)}</span> -
<span class={getArrivalStatus(predictedArrivalTime, scheduledArrivalTime).color}>
{getArrivalStatus(predictedArrivalTime, scheduledArrivalTime).text}
</span>
</p>
</div>
<div>
<p class="text-lg font-semibold text-black dark:text-white">
{calculateTimeToReach(predictedArrivalTime, scheduledArrivalTime)}
</p>
</div>
7 changes: 5 additions & 2 deletions src/components/containers/AccordionItem.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
<script>
import { getContext } from 'svelte';
import { slide } from 'svelte/transition';
export let data = null;
const id = crypto.randomUUID();
const { registerItem } = getContext('accordion');
const { isActive, skipAnimation, activate } = registerItem(id);
function toggle() {
activate();
activate(data);
}
</script>

<div class="relative">
<div class="sticky top-0 z-0 bg-white dark:bg-gray-800">
<div class="sticky top-0 z-10 bg-white dark:bg-gray-800">
<button
type="button"
class="flex w-full items-center justify-between py-3 text-left text-base font-medium text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white"
Expand Down
41 changes: 41 additions & 0 deletions src/components/containers/SingleSelectAccordion.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script>
import { setContext } from 'svelte';
import { writable, derived } from 'svelte/store';
// Create a store to track the active item and data.
const activeItem = writable(null);
const activeData = writable(null);
// Create dispatch for activeChanged event
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
// Watch for changes to activeItem and dispatch event
$: {
dispatch('activeChanged', {
activeItem: $activeItem,
activeData: $activeData
});
}
// Provide context for child AccordionItems
setContext('accordion', {
registerItem: (id) => {
const isActive = derived(activeItem, ($activeItem) => $activeItem === id);
return {
isActive,
activate: (data) => {
const newId = $activeItem === id ? null : id;
activeItem.set(newId);
activeData.set(newId ? data : null);
}
};
}
});
</script>

<div
class="divide-y divide-gray-200 border-y border-gray-200 dark:divide-gray-700 dark:border-gray-700"
>
<slot />
</div>
57 changes: 0 additions & 57 deletions src/components/navigation/TripDetailsModal.svelte

This file was deleted.

4 changes: 2 additions & 2 deletions src/components/oba/TripDetailsPane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
<div class="absolute bottom-0 left-3.5 top-0 w-[1px] bg-neutral-400"></div>
{#each tripDetails.schedule.stopTimes as tripStop, index}
<div class="relative mb-4 flex items-center">
<div class="mb-4 flex items-center">
<div
class="relative z-10 flex size-8 items-center justify-center rounded-md border border-neutral-400 bg-white dark:bg-neutral-800"
class="relative flex size-8 items-center justify-center rounded-md border border-neutral-400 bg-white dark:bg-neutral-800"
>
{#if index === busPosition}
<FontAwesomeIcon
Expand Down
63 changes: 20 additions & 43 deletions src/components/stops/StopPane.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script>
import ArrivalDeparture from '../ArrivalDeparture.svelte';
import TripDetailsModal from '../navigation/TripDetailsModal.svelte';
import ArrivalDeparture from '$components/ArrivalDeparture.svelte';
import TripDetailsPane from '$components/oba/TripDetailsPane.svelte';
import LoadingSpinner from '$components/LoadingSpinner.svelte';
import Accordion from '$components/containers/SingleSelectAccordion.svelte';
import AccordionItem from '$components/containers/AccordionItem.svelte';
import { onDestroy, onMount } from 'svelte';
import { createEventDispatcher } from 'svelte';
import '$lib/i18n.js';
import { t } from 'svelte-i18n';
import { isLoading } from 'svelte-i18n';
import { isLoading, t } from 'svelte-i18n';
export let stop;
export let arrivalsAndDeparturesResponse = null;
Expand All @@ -17,8 +17,6 @@
let loading = false;
let error;
let showTripDetails = false;
let selectedTripDetails = null;
let interval = null;
let initialDataLoaded = false;
Expand Down Expand Up @@ -72,22 +70,11 @@
return _routeShortNames;
}
function handleShowTripDetails(event) {
selectedTripDetails = {
...event.detail,
routeShortName: event.detail.routeShortName,
tripHeadsign: event.detail.tripHeadsign,
scheduledArrivalTime: event.detail.scheduledArrivalTime
};
showTripDetails = true;
dispatch('tripSelected', selectedTripDetails);
dispatch('updateRouteMap', { show: true });
}
function handleCloseTripDetailModal() {
showTripDetails = false;
dispatch('tripSelected', null);
dispatch('updateRouteMap', { show: false });
function handleAccordionSelectionChanged(event) {
const data = event.detail.activeData; // this is the ArrivalDeparture object plumbed into the AccordionItem
const show = !!data;
dispatch('tripSelected', data);
dispatch('updateRouteMap', { show });
}
</script>
Expand Down Expand Up @@ -128,28 +115,18 @@
<p>{$t('no_arrivals_or_departures_in_next_30_minutes')}</p>
</div>
{:else}
<div class="space-y-2 overflow-y-scroll rounded-lg">
<div>
{#each arrivalsAndDepartures.arrivalsAndDepartures as arrival}
<ArrivalDeparture
routeShortName={arrival.routeShortName}
tripHeadsign={arrival.tripHeadsign}
scheduledArrivalTime={arrival.scheduledArrivalTime}
predictedArrivalTime={arrival.predictedArrivalTime}
tripId={arrival.tripId}
vehicleId={arrival.vehicleId}
serviceDate={arrival.serviceDate}
on:showTripDetails={handleShowTripDetails}
/>
{/each}
</div>
</div>
<Accordion on:activeChanged={handleAccordionSelectionChanged}>
{#each arrivalsAndDepartures.arrivalsAndDepartures as arrival}
<AccordionItem data={arrival}>
<span slot="header">
<ArrivalDeparture arrivalDeparture={arrival} />
</span>
<TripDetailsPane {stop} tripId={arrival.tripId} serviceDate={arrival.serviceDate} />
</AccordionItem>
{/each}
</Accordion>
{/if}
</div>
{/if}
{#if showTripDetails}
<TripDetailsModal {stop} {selectedTripDetails} onClose={handleCloseTripDetailModal} />
{/if}
</div>
{/if}
6 changes: 5 additions & 1 deletion src/routes/stops/[stopID]/schedule/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@
$: if (selectedDate && selectedDate !== prevSelectedDate) {
const formattedDate = selectedDate.toISOString().split('T')[0];
prevSelectedDate = selectedDate;
fetchScheduleForStop(stopId, formattedDate);
// we get an error if we try to fetch data on the server
if (typeof window !== 'undefined') {
fetchScheduleForStop(stopId, formattedDate);
}
}
async function fetchScheduleForStop(stopId, date) {
Expand Down

0 comments on commit 111fbd9

Please sign in to comment.