diff --git a/src/components/ArrivalDeparture.svelte b/src/components/ArrivalDeparture.svelte index f393169..f337f1f 100644 --- a/src/components/ArrivalDeparture.svelte +++ b/src/components/ArrivalDeparture.svelte @@ -4,14 +4,8 @@ let tripHeadsign = arrivalDeparture.tripHeadsign; let scheduledArrivalTime = arrivalDeparture.scheduledArrivalTime; let predictedArrivalTime = arrivalDeparture.predictedArrivalTime; - let tripId = arrivalDeparture.tripId; - let vehicleId = arrivalDeparture.vehicleId; - let serviceDate = arrivalDeparture.serviceDate; - import { createEventDispatcher } from 'svelte'; 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' }); @@ -63,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> diff --git a/src/components/containers/Accordion.svelte b/src/components/containers/Accordion.svelte index 32c3548..2bebc25 100644 --- a/src/components/containers/Accordion.svelte +++ b/src/components/containers/Accordion.svelte @@ -49,7 +49,7 @@ return { isActive, skipAnimation, - activate: (_data) => { + activate: () => { activeItems.update((items) => { const newItems = new Set(items); if (newItems.has(id)) { diff --git a/src/components/containers/AccordionItem.svelte b/src/components/containers/AccordionItem.svelte index 6791e7e..64de63b 100644 --- a/src/components/containers/AccordionItem.svelte +++ b/src/components/containers/AccordionItem.svelte @@ -13,7 +13,7 @@ </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" diff --git a/src/components/containers/SingleSelectAccordion.svelte b/src/components/containers/SingleSelectAccordion.svelte index a82b212..05ba2f6 100644 --- a/src/components/containers/SingleSelectAccordion.svelte +++ b/src/components/containers/SingleSelectAccordion.svelte @@ -1,41 +1,41 @@ <script> - import { setContext } from 'svelte'; - import { writable, derived } from 'svelte/store'; + 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 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(); + // 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 - }); - } + // 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); - } - }; - } - }); + // 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" + class="divide-y divide-gray-200 border-y border-gray-200 dark:divide-gray-700 dark:border-gray-700" > - <slot /> -</div> \ No newline at end of file + <slot /> +</div> diff --git a/src/components/navigation/TripDetailsModal.svelte b/src/components/navigation/TripDetailsModal.svelte deleted file mode 100644 index 613cc75..0000000 --- a/src/components/navigation/TripDetailsModal.svelte +++ /dev/null @@ -1,57 +0,0 @@ -<script> - import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome'; - import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; - import TripDetailsPane from '../oba/TripDetailsPane.svelte'; - - export let stop; - export let selectedTripDetails; - export let onClose; -</script> - -<div class="trip-details-modal"> - <div class="py-1 text-left"> - <button type="button" on:click={onClose} class="close-button"> - <FontAwesomeIcon icon={faArrowLeft} class="font-black text-black dark:text-white" /> - <div class="font-semibold text-black dark:text-white">Back to {stop.name}</div> - </button> - </div> - <div - class="flex items-center justify-between rounded-lg bg-[#ffffff] p-4 hover:bg-[#e3e3e3] dark:bg-[#1c1c1c] hover:dark:bg-[#363636]" - > - <div> - <h2 class="text-lg font-semibold text-black dark:text-white"> - {selectedTripDetails.routeShortName} - {selectedTripDetails.tripHeadsign} - </h2> - <p class="text-sm font-semibold text-gray-600 dark:text-gray-400"> - {new Date(selectedTripDetails.scheduledArrivalTime).toLocaleTimeString([], { - hour: '2-digit', - minute: '2-digit' - })} - - <span class={selectedTripDetails.arrivalStatus.color}> - {selectedTripDetails.arrivalStatus.text} - </span> - </p> - </div> - <p class={`mt-1 text-sm font-semibold ${selectedTripDetails.arrivalStatus.color}`}> - {selectedTripDetails.timeToReach} - </p> - </div> - <div class="px-4 py-2"> - <TripDetailsPane - {stop} - tripId={selectedTripDetails.tripId} - serviceDate={selectedTripDetails.serviceDate} - /> - </div> -</div> - -<style lang="postcss"> - .trip-details-modal { - @apply absolute bottom-0 left-0 z-40 h-full w-full overflow-y-scroll rounded-lg bg-white px-2 shadow-lg md:max-w-prose; - @apply rounded-lg border-b-[1px] border-[#C6C6C8] bg-white p-4 shadow-lg dark:border-[1px] dark:border-[#C6C6C8] dark:border-opacity-15 dark:bg-black; - } - .close-button { - @apply flex items-center gap-2 rounded px-4 py-2; - @apply transition duration-300 ease-in-out hover:bg-neutral-200 dark:hover:bg-neutral-200/50; - } -</style> diff --git a/src/components/oba/TripDetailsPane.svelte b/src/components/oba/TripDetailsPane.svelte index 7703c69..a751601 100644 --- a/src/components/oba/TripDetailsPane.svelte +++ b/src/components/oba/TripDetailsPane.svelte @@ -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 diff --git a/src/components/stops/StopPane.svelte b/src/components/stops/StopPane.svelte index c402073..5c25ff1 100644 --- a/src/components/stops/StopPane.svelte +++ b/src/components/stops/StopPane.svelte @@ -1,7 +1,9 @@ <script> import ArrivalDeparture from '$components/ArrivalDeparture.svelte'; - import TripDetailsModal from '$components/navigation/TripDetailsModal.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'; @@ -15,8 +17,6 @@ let loading = false; let error; - let showTripDetails = false; - let selectedTripDetails = null; let interval = null; let initialDataLoaded = false; @@ -70,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> @@ -126,18 +115,18 @@ <p>{$t('no_arrivals_or_departures_in_next_30_minutes')}</p> </div> {:else} - {#each arrivalsAndDepartures.arrivalsAndDepartures as arrival} - <ArrivalDeparture - arrivalDeparture={arrival} - on:showTripDetails={handleShowTripDetails} - /> - {/each} + <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}