npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

departureselection

v1.2.9

Published

A reusable departure selection component for travel booking applications

Readme

jetsetgo_departure-selection

A highly customizable React component for handling travel departure and return journey selections.

Installation

npm install jetsetgo_departure-selection

Quick Start

import { DepartureSelection } from 'jetsetgo_departure-selection';

function App() {
  return (
    <DepartureSelection
      initialData={{
        departure: outboundServices,
        return: returnServices
      }}
      uiConfig={{
        continueToReturnText: "Continue to Return Journey",
        finalContinueText: "Complete Booking",
        backToOutboundText: "Back to Outbound",
        backText: "Back",
        outboundTitle: "Select Outbound Journey",
        returnTitle: "Select Return Journey",
        pageTitle: "Choose your journey",
        pageSubtitle: "Select your preferred travel times",
        outboundButtonMode: "continue" // 'continue' | 'complete'
      }}
      onComplete={handleComplete}
      onDateSelect={handleDateSelect}
      onServiceSelect={handleServiceSelect}
      onContextChange={handleContextChange}
    />
  );
}

Event Communication

The component provides a comprehensive event system to keep the parent application informed about user interactions.

Date Selection Events

Triggered when the user selects a new date:

interface DateSelectEvent {
  date: string;            // The newly selected date (YYYY-MM-DD)
  previousDate: string;    // The previously selected date
  context: 'outbound' | 'return'; // Current selection context
}

// Example usage
const handleDateSelect = (event: DateSelectEvent) => {
  console.log('Date changed:', event.date);
  console.log('Previous date:', event.previousDate);
  console.log('Current context:', event.context);
};

Service Selection Events

Emitted when a user selects a service:

interface ServiceSelectEvent {
  serviceId: number;       // ID of the selected service
  context: 'outbound' | 'return'; // Whether this is outbound or return
  service: Service;        // Full service object
}

// Example usage
const handleServiceSelect = (event: ServiceSelectEvent) => {
  console.log('Selected service:', event.serviceId);
  console.log('Selection context:', event.context);
  console.log('Service details:', event.service);
};

Context Change Events

Triggered when switching between outbound and return selection:

interface ContextChangeEvent {
  previousContext: 'outbound' | 'return';
  nextContext: 'outbound' | 'return';
  selectedOutboundId: number | null;
  selectedReturnId: number | null;
}

// Example usage
const handleContextChange = (event: ContextChangeEvent) => {
  console.log('Switching from', event.previousContext, 'to', event.nextContext);
  console.log('Current selections:', {
    outbound: event.selectedOutboundId,
    return: event.selectedReturnId
  });
};

Completion Event

Triggered when the user completes their selection:

interface CompletionData {
  selectedDepartureId: number | null;
  selectedReturnId: number | null;
  departureService: Service | null;
  returnService: Service | null;
  date: string;
  totalCost: number;
}

// Example usage
const handleComplete = (data: CompletionData) => {
  console.log('Selection completed:', data);
};

Features

Dynamic Loading States

The component supports loading states for data updates, commonly used when fetching new data after date changes:

function BookingApp() {
  const [isLoading, setIsLoading] = useState(false);
  const [loadingContext, setLoadingContext] = useState<'outbound' | 'return' | 'both'>('both');
  const [services, setServices] = useState({
    departure: initialDepartureServices,
    return: initialReturnServices
  });

  const handleDateSelect = async (event: DateSelectEvent) => {
    // 1. Start loading state for the relevant section
    setIsLoading(true);
    setLoadingContext(event.context);
    
    try {
      // 2. Fetch new data for the selected date
      const newServices = await fetchServicesForDate(event.date);
      
      // 3. Update the services data
      setServices(newServices);
    } catch (error) {
      console.error('Failed to fetch services:', error);
    } finally {
      // 4. Stop loading state
      setIsLoading(false);
    }
  };

  return (
    <DepartureSelection
      initialData={services}
      isLoading={isLoading}
      loadingContext={loadingContext}
      onDateSelect={handleDateSelect}
      // ... other props
    />
  );
}

When isLoading is true:

  • A loading overlay appears over the specified section(s)
  • The UI remains interactive but selections are disabled
  • Smooth transitions handle the loading state changes
  • The date selector remains accessible

Data Persistence

The component includes built-in state persistence:

// Export current selections to JSON
const handleExport = () => {
  const data = exportSelections();
  // Save data to file/storage
};

// Import saved selections
const handleImport = (savedData: string) => {
  importSelections(savedData);
};

Props

Required Props

| Prop | Type | Description | |------|------|-------------| | initialData | { departure: Service[]; return: Service[]; } | The outbound and return services to display | | onComplete | (data: CompletionData) => void | Callback when selection is complete | | uiConfig | UIConfig | Configuration for UI text and labels |

Optional Props

| Prop | Type | Description | |------|------|-------------| | selectedOutboundId | number \| null | Pre-selected outbound service ID | | selectedReturnId | number \| null | Pre-selected return service ID | | currentContext | 'outbound' \| 'return' | Initial selection context | | onDateSelect | (event: DateSelectEvent) => void | Date selection callback | | onServiceSelect | (event: ServiceSelectEvent) => void | Service selection callback | | onContextChange | (event: ContextChangeEvent) => void | Context change callback | | onBack | () => void | Callback for back button click | | isLoading | boolean | Whether the component is loading new data | | loadingContext | 'outbound' \| 'return' \| 'both' | Which section is loading |

UI Configuration

The uiConfig prop allows customization of all text elements:

interface UIConfig {
  continueToReturnText: string;  // Text for continue to return button
  finalContinueText: string;     // Text for final continue button
  backToOutboundText: string;    // Text for back to outbound button
  backText: string;              // Text for back button
  outboundTitle: string;         // Title for outbound selection
  returnTitle: string;           // Title for return selection
  pageTitle: string;             // Main page title
  pageSubtitle: string;          // Page subtitle
  outboundButtonMode: 'continue' | 'complete'; // Controls which button appears in outbound context
}

Outbound Button Mode

The outboundButtonMode configuration controls which button appears after selecting an outbound service. Available options:

| Option | Description | |--------|-------------| | 'continue' | (Default) Shows the "Continue to Return Journey" button (continueToReturnText). Use this for return journeys where users need to select both outbound and return services. | | 'complete' | Shows the "Complete Booking" button (finalContinueText). Use this for one-way journeys where only an outbound service selection is needed. |

Example usage:

// For a one-way journey where return selection is not needed
<DepartureSelection
  uiConfig={{
    ...otherConfig,
    outboundButtonMode: "complete",  // Shows "Complete Booking" button
    finalContinueText: "Complete Booking"
  }}
  // ... other props
/>

// For a return journey where both selections are required
<DepartureSelection
  uiConfig={{
    ...otherConfig,
    outboundButtonMode: "continue",  // Shows "Continue to Return Journey" button
    continueToReturnText: "Continue to Return Journey"
  }}
  // ... other props
/>

Service Data Structure

Services should follow this structure:

interface Service {
  service_id: number;
  can_accept: string;
  resource_name: string;
  route_name: string;
  departing_from: string;
  travelling_to: string;
  departure_time: string;
  arrival_time: string;
  departure_date: string;
  total_cost: number;
  pats: Pat[];
  flags: Flag[];
}

Styling

The component uses Tailwind CSS classes by default. You can override styles by:

  1. Using the provided class names
  2. Using CSS modules (coming soon)
  3. Using styled-components (coming soon)

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Utility Functions

The component exports several utility functions for working with dates and services:

Date Utilities

import { getDefaultDate, generateDateRange, shiftDates } from 'jetsetgo_departure-selection';

// Get the default date from services
const defaultDate = getDefaultDate(services);

// Generate a range of dates (default: 1 day before and after)
const dateRange = generateDateRange('2024-01-15', 2);

// Shift dates forward or backward
const newDates = shiftDates(currentDates, 'forward');

Service Utilities

import { sortServices, filterServices } from 'jetsetgo_departure-selection';

// Sort services by departure time or price
const sortedServices = sortServices(services, 'departure');

// Filter services based on availability
const availableServices = filterServices(services, false);

Type Exports

The package exports TypeScript types for all major interfaces:

import type {
  Service,
  Pat,
  Flag,
  DepartureSelectionProps,
  UIConfig,
  DateSelectEvent,
  ServiceSelectEvent,
  ContextChangeEvent,
  DepartureSelections
} from 'jetsetgo_departure-selection';

// Example usage
const config: UIConfig = {
  continueToReturnText: "Next",
  finalContinueText: "Complete",
  backToOutboundText: "Back",
  backText: "Previous",
  outboundTitle: "Select Departure",
  returnTitle: "Select Return",
  pageTitle: "Travel Selection",
  pageSubtitle: "Choose your journey",
  outboundButtonMode: "continue"  // 'continue' | 'complete'
};

State Communication Overview The component communicates state changes through three main callback props:

onContextChange: Tracks the current view context and selections onServiceSelect: Reports individual service selections onDateSelect: Reports date selection changes Tracking Context State The onContextChange callback is triggered in these scenarios:

interface ContextChangeEvent { previousContext: 'outbound' | 'return'; nextContext: 'outbound' | 'return'; selectedOutboundId: number | null; selectedReturnId: number | null; } Triggered when:

Component mounts (initial load) User switches between outbound/return views Service selections change Example usage:

function ParentApp() { const handleContextChange = (event: ContextChangeEvent) => { console.log('Current view:', event.nextContext); console.log('Selected outbound:', event.selectedOutboundId); console.log('Selected return:', event.selectedReturnId); };

return ( <DepartureSelection onContextChange={handleContextChange} // ... other props /> ); } Tracking Service Selections The onServiceSelect callback provides detailed service information:

interface ServiceSelectEvent { serviceId: number; context: 'outbound' | 'return'; service: Service; // Full service object } Triggered when:

User selects an outbound service User selects a return service Example:

function ParentApp() { const handleServiceSelect = (event: ServiceSelectEvent) => { console.log('Service selected in context:', event.context); console.log('Selected service:', event.service); };

return ( <DepartureSelection onServiceSelect={handleServiceSelect} // ... other props /> ); } Tracking Date Changes The onDateSelect callback reports date changes:

interface DateSelectEvent { date: string; previousDate: string; context: 'outbound' | 'return'; } Triggered when:

User selects a new date User navigates dates using arrows Example:

function ParentApp() { const handleDateSelect = (event: DateSelectEvent) => { console.log('New date:', event.date); console.log('Previous date:', event.previousDate); console.log('Current context:', event.context); };

return ( <DepartureSelection onDateSelect={handleDateSelect} // ... other props /> ); } Complete Parent App Example Here's a complete example showing how to track all state changes:

function ParentApp() { const [currentContext, setCurrentContext] = useState<'outbound' | 'return'>('outbound'); const [selections, setSelections] = useState({ outboundId: null as number | null, returnId: null as number | null, currentDate: null as string | null });

const handleContextChange = (event: ContextChangeEvent) => { setCurrentContext(event.nextContext); setSelections(prev => ({ ...prev, outboundId: event.selectedOutboundId, returnId: event.selectedReturnId })); };

const handleServiceSelect = (event: ServiceSelectEvent) => { setSelections(prev => ({ ...prev, [event.context === 'outbound' ? 'outboundId' : 'returnId']: event.serviceId })); };

const handleDateSelect = (event: DateSelectEvent) => { setSelections(prev => ({ ...prev, currentDate: event.date })); };

return ( <DepartureSelection onContextChange={handleContextChange} onServiceSelect={handleServiceSelect} onDateSelect={handleDateSelect} currentContext={currentContext} selectedOutboundId={selections.outboundId} selectedReturnId={selections.returnId} // ... other required props /> ); } This setup provides complete visibility into the component's internal state and user interactions.