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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@swift-food-services/catering-widget

v0.2.17

Published

Swift Food catering widget for partner sites

Downloads

2,678

Readme

@swift-food-services/catering-widget

The Swift Food catering flow, embeddable in any React app. Restaurant browsing, multi-session order building, contact details, and submission — one component, one CSS file, one publishable key.

npm install @swift-food-services/catering-widget

Quick start

import { CateringWidget } from "@swift-food-services/catering-widget";

export default function CateringPage() {
  return (
    <CateringWidget
      publishableKey="pk_live_..."
      googleMapsApiKey="AIzaSy..."
      onOrderComplete={({ orderId }) => {
        // Fires after the success-screen countdown (default 15s,
        // configurable via `onOrderCompleteDelaySeconds`).
        window.location.href = `/thanks?order=${orderId}`;
      }}
    />
  );
}

Defaults: Swift-hosted backend, localStorage persistence, neutral theme.

Requirements

Props

| Prop | Type | Required | Description | |---|---|---|---| | publishableKey | string | yes | Your pk_live_... key from Swift. | | partnerSlug | string | no | First-party only. Renders and attributes the session to a different partner than the one owning the key. Requires a key Swift has explicitly permitted to delegate; any other key is rejected with slug_delegation_not_permitted. If you are integrating the widget on your own site, identify yourself with publishableKey alone and omit this. | | googleMapsApiKey | string | yes | Google Maps JavaScript API key with the Places library enabled. Used by the address-autocomplete field. | | theme | Theme | no | Primary color, border radius, and font overrides. | | initialData | InitialData | no | Pre-populate event window, address, and contact fields. Re-applied when its fingerprint changes between mounts; preserved on reload. See Pre-filling known event details. | | allowedCateringTimes | AllowedCateringTimes | no | Partner-level catering availability window ({ start: "HH:MM", end: "HH:MM" }). Restricts the session delivery-time picker to slots within this range. | | stickyTopOffset | number | no | Pixels to offset the widget's internal sticky date/session nav from the top of the viewport. Set this to the height of any sticky/fixed navbar in your host layout so the widget's nav doesn't slide underneath it. Defaults to 0. | | onReady | () => void | no | Fires when the widget has initialized. | | onOrderComplete | (result: OrderCompleteResult) => void | no | Fires after a successful order submission. See onOrderComplete behaviour for the timing. | | onOrderCompleteDelaySeconds | number | no | Seconds to keep the success screen visible before firing onOrderComplete. Defaults to 15. Ignored if onOrderComplete is not provided. | | onError | (error: WidgetError) => void | no | Fires on unrecoverable errors (bad key, network, submit failure). |

Examples

Next.js with router-based navigation

"use client";
import { CateringWidget } from "@swift-food-services/catering-widget";
import { useRouter } from "next/navigation";

export default function Page() {
  const router = useRouter();
  return (
    <CateringWidget
      publishableKey={process.env.NEXT_PUBLIC_SWIFT_PUBLISHABLE_KEY!}
      googleMapsApiKey={process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!}
      onOrderComplete={({ orderId, accessToken }) => {
        router.push(`/orders/${orderId}?token=${accessToken}`);
      }}
    />
  );
}

Theming

<CateringWidget
  publishableKey="pk_live_..."
  theme={{
    primary: "#0a7ea4",
    radius: "12px",
    font: "\"IBM Plex Mono\", monospace",
  }}
/>

Pre-filling known event details

<CateringWidget
  publishableKey="pk_live_..."
  initialData={{
    eventName: "Alice's Birthday",
    eventStartDate: "2026-07-12",
    eventStartTime: "18:00",
    eventEndDate: "2026-07-12",
    eventEndTime: "22:00",
    guestCount: 40,
    deliveryAddress: {
      line1: "1 Example Lane",
      city: "London",
      postcode: "E1 6AN",
      lat: 51.5074,
      lng: -0.1278,
    },
    contact: { name: "Alice", email: "[email protected]" },
  }}
  onOrderComplete={/* ... */}
/>

Every field in initialData is optional. Fields remain editable after pre-fill.

How initialData interacts with persisted in-progress orders: the widget fingerprints the supplied initialData (event window, guest count, address, and contact fields) and stores the fingerprint alongside the order. On every mount it compares the incoming fingerprint to the stored one:

  • Match (e.g. page reload) → the in-progress order is preserved; initialData is not re-applied.
  • Differ (e.g. customer navigated back to your booking page, edited details, then returned) → the stored order is cleared and initialData is applied fresh.
  • No fingerprint stored yet (first visit) → initialData is applied and the fingerprint is recorded.

For this to behave predictably, make sure the values you pass for the same booking are stable across renders — avoid recomputing dynamic values like new Date() inline in render.

The event window is a start–end pair (eventStartDate + eventStartTime to eventEndDate + eventEndTime). When set, the session editor restricts session dates to that range and filters the delivery-time picker so it stays within the window on the boundary days.

Address requires lat and lng. Delivery pricing depends on coordinates, so if you pass a deliveryAddress without both lat and lng, the widget ignores the entire address (no fields are pre-filled) and the guest is asked to pick one via the address-autocomplete field instead. The other initialData fields (event window, guest count, contact) still apply.

Restricting catering hours (allowedCateringTimes)

Pass a partner-level catering availability window to restrict the session delivery-time picker to a subset of the day. Times are 24-hour "HH:MM" strings.

<CateringWidget
  publishableKey="pk_live_..."
  allowedCateringTimes={{ start: "09:00", end: "19:00" }}
/>

Each slot is a 30-minute window. A slot is shown only if it fits entirely within [start, end] — its start must be ≥ start and its end must be ≤ end. With end: "19:00", for example, the latest visible slot is 6:30 PM – 7:00 PM. If initialData also defines an event window, the two filters compose: the partner window is the outer bound, and the event start/end further narrow the picker on boundary days.

Offsetting below a host navbar

If your page has its own sticky/fixed navbar, pass its height as stickyTopOffset so the widget's internal date/session nav pins just below it:

<CateringWidget
  publishableKey="pk_live_..."
  stickyTopOffset={64}
/>

Pass a live value if your navbar resizes (measure it with a ref + ResizeObserver and pass the current height). The prop is read on every render, so updates take effect immediately.

Types

import {
  CateringWidget,
  type CateringWidgetProps,
  type Theme,
  type InitialData,
  type AllowedCateringTimes,
  type OrderCompleteResult,
  type OrderSummary,
  type MealSession,
  type MealSessionItem,
  type WidgetError,
} from "@swift-food-services/catering-widget";

See the generated dist/index.d.ts for the full shapes — your IDE picks them up automatically.

Callbacks

onOrderComplete(result)

Fires after a successful order submission. result contains:

  • orderId: string — Swift's order ID.
  • accessToken: string — a token the customer can use to view the order on Swift's hosted view page.
  • summary: OrderSummary — structured breakdown of the submitted order.

Timing. When onOrderComplete is wired, the widget shows its built-in success screen with a "Continuing in N seconds…" countdown for onOrderCompleteDelaySeconds seconds (default 15), then fires the callback. This gives the guest a moment to read the confirmation before the host navigates away. A "Continue now" button under the countdown lets the guest skip the wait — clicking it cancels the timer and fires onOrderComplete immediately.

If onOrderComplete is not wired, the success screen stays up indefinitely. The success screen shows the event, customer, and pricing summary, and any navigation is the host's responsibility (do it from onOrderComplete).

Heads up: the widget does not navigate on its own. If your onOrderComplete runs without triggering a navigation (e.g. you only fire analytics or close a modal), the guest will stay on the widget's order-confirmation page even after the callback fires. To send them elsewhere, do it from inside onOrderCompleterouter.push(...), window.location.href = ..., hide the widget from your layout, etc.

<CateringWidget
  publishableKey="pk_live_..."
  googleMapsApiKey="AIzaSy..."
  onOrderCompleteDelaySeconds={5}
  onOrderComplete={({ orderId, accessToken }) => {
    router.push(`/orders/${orderId}?token=${accessToken}`);
  }}
/>

onError(error)

Fires on errors the widget can't recover from. error.code is one of:

  • invalid_publishable_key — the key was rejected by Swift's backend.
  • slug_delegation_not_permitted — a partnerSlug was supplied with a key that is not permitted to delegate.
  • unknown_partner_slug — the supplied partnerSlug does not match an active partner.
  • session_failed — the widget session handshake failed.
  • network_error — a backend request failed or returned a non-2xx.
  • submit_failed — the order submission itself failed.
  • unknown — anything else; see error.cause for details.

What the widget handles for you

  • Restaurant browsing and menu exploration
  • Multi-session meal building
  • Contact details and delivery address
  • Promo codes and pricing preview
  • Google Maps address autocomplete (requires the googleMapsApiKey you provide)
  • Order submission to Swift's API
  • State persistence in localStorage (namespaced)

What you're responsible for

  • Getting a publishable key from Swift.
  • Providing a Google Maps JavaScript API key (with the Places library enabled) via the googleMapsApiKey prop. Restrict the key to your own domain(s) in the Google Cloud Console.
  • Rendering <CateringWidget> wherever you want the flow to live.
  • Navigating after onOrderComplete.
  • Page chrome around the widget.

What you're not responsible for

  • Payment. Swift sends a payment link by email after reviewing the order.
  • Loading the Google Maps script — the widget injects the script using the key you provide.
  • Making API calls to Swift.
  • Session or auth management.

Getting a publishable key

Contact Swift at [email protected] with:

  1. Your company name.
  2. A technical contact for integration support.

Swift provisions a partner record and returns a publishable key (format: pk_live_...) out of band.

The key is public — it ships in the browser bundle.

Troubleshooting

Errors mentioning invalid_publishable_key or session_failed Either the key is wrong or the key is inactive. Contact Swift if you need a new one.

TypeScript errors importing types Ensure your tsconfig.json has "moduleResolution": "bundler" or "node16"/"nodenext".

Browser support

Modern evergreen browsers (Chrome, Firefox, Safari, Edge) on desktop and mobile.

Versioning

@swift-food-services/catering-widget follows semver. The public API is everything exported from the package entry — CateringWidget, its props, and the re-exported types.

Support

  • Integration questions: [email protected]
  • Bug reports: include your publishable key prefix (first 8 chars), the browser, and a reproducible example.

License

Proprietary. Distributed for use by authorized Swift Food partners only.