@fanfare-io/fanfare-sdk-react 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,10 +50,22 @@ same `automaticEntry` option to `useExperienceJourney`; both reach the ready vie
50
50
  omitting it keeps the SDK default of entering on arrival. The contract is in the core package's
51
51
  `docs/UPGRADING-automatic-entry.md` in the SDK repository.
52
52
 
53
+ `loadFonts` is off by default. Turned on, the widget fetches the Google Fonts its theme and variant
54
+ name after mount, through a link that never blocks rendering and fails silently to the fallback
55
+ stack. A host that already loads its own fonts — or serves them from its own origin — leaves it off.
56
+
53
57
  Customization has four levels of increasing specificity: `theme`, `variant`, `slots`, and `children` or `useExperienceJourney`.
54
58
 
55
59
  `slots` override targeted states while keeping the default widget flow. `children` receives `{ journey, view, snapshot, error, start, isStarting }` when you want to render the full state yourself.
56
60
 
61
+ Mounted inside a shadow root, the widget's styleable elements export CSS parts named after their
62
+ `data-slot`, so a page styles them with `::part()` — `fanfare-host::part(auction-cta)`. The
63
+ attribution footer and every element that contains its "Powered by Fanfare" row are
64
+ deliberately not parts: the widget root and the panel cards that carry that row. The payment
65
+ card field is not a part either. A panel whose footer holds no attribution row keeps its card
66
+ as a part — `auction-module` is one; [`docs/STYLING.md`](./docs/STYLING.md#parts) lists every
67
+ part and every exception.
68
+
57
69
  ## Selection
58
70
 
59
71
  Some experiences capture which product and variant a consumer gets. `ExperienceWidget` renders that
@@ -0,0 +1,2 @@
1
+ /** The consumer-facing name of a wire card brand (`visa` → `Visa`, `cartes_bancaires` → `Cartes Bancaires`). */
2
+ export declare function cardBrandName(brand: string): string;
@@ -5,6 +5,7 @@ import * as React from "react";
5
5
  * confirmation.
6
6
  *
7
7
  * The consumer must not navigate away while the payment settles, so the wait is announced rather
8
- * than shown only as motion.
8
+ * than shown only as motion. It reads as the same callout the payment panel shows while a bank
9
+ * confirmation resumes, so the two waits on one payment look like one wait.
9
10
  */
10
11
  export declare function CheckoutProcessingPanel(_props: CheckoutProcessingSlotProps): React.ReactElement;
@@ -5,9 +5,9 @@ import * as React from "react";
5
5
  *
6
6
  * The hold amount is rendered from the view's own figure — the client never derives one.
7
7
  *
8
- * The configuration block reaches this panel on the entry mount only. A retry is offered from an
9
- * ended view, which carries none, so that mount renders payment as unavailable and a consumer with
10
- * a saved method submits without needing Stripe.js at all.
8
+ * An absent configuration block means its read failed. A saved method pays without Stripe.js, so
9
+ * with one the outage narrows to the new-card choice; with none, re-reading the configuration is
10
+ * the panel's only way forward.
11
11
  *
12
12
  * Entry has no server-side resume: a paused authorization is carried forward by submitting the
13
13
  * identical payment input again, held here from the first submit, so exactly one payment method is
@@ -0,0 +1,18 @@
1
+ import { PaymentMethodSummary } from '@fanfare-io/fanfare-sdk-core/experiences';
2
+ import type * as React from "react";
3
+ export interface PaymentMethodListProps {
4
+ methods: PaymentMethodSummary[];
5
+ /** Unique per panel: radios sharing a name form one group across the whole document. */
6
+ name: string;
7
+ selectedId: string | undefined;
8
+ newCardActive: boolean;
9
+ /** Offer the new-card row; absent when no card entry can be mounted. */
10
+ cardEntryAvailable: boolean;
11
+ onSelectSaved: (id: string) => void;
12
+ onSelectNew: () => void;
13
+ }
14
+ /**
15
+ * Saved payment methods and the new-card choice, as one radio group of selector rows. The row look
16
+ * lives in the theme stylesheet, keyed on `payment-method-row`.
17
+ */
18
+ export declare function PaymentMethodList({ methods, name, selectedId, newCardActive, cardEntryAvailable, onSelectSaved, onSelectNew, }: PaymentMethodListProps): React.ReactElement;
@@ -3,8 +3,9 @@ import * as React from "react";
3
3
  /**
4
4
  * Default panel for a completed Fanfare-managed payment.
5
5
  *
6
- * The order reference is the outcome. An admission credential appears only on the arm that carries
7
- * one, and stays subordinate to the purchase: payment is terminal, so a receipt must never read as
8
- * the thing that admits its holder.
6
+ * The purchase is the outcome: the panel is labelled as one and leads with what was paid, and the
7
+ * order reference follows. An admission credential appears only on the arm that carries one, and
8
+ * stays subordinate to the purchase: payment is terminal, so a receipt must never read as the thing
9
+ * that admits its holder.
9
10
  */
10
11
  export declare function ReceiptPanel(props: ReceiptSlotProps): React.ReactElement;
@@ -1,15 +1,18 @@
1
1
  /**
2
2
  * Countdown Component
3
3
  *
4
- * Displays time remaining until a target date.
5
- * Automatically updates every second.
4
+ * Displays time remaining until a target date, refreshed every second.
6
5
  *
7
- * Two layouts:
8
- * - "swiss" (default): Prominent display with animated digits and labels above
9
- * - "compact": Simple text-based countdown that inherits parent styling
6
+ * Two layouts share one `--ff-countdown-*` token set:
7
+ * - "cells" (default): one box per time unit
8
+ * - "inline": a single line that fits inside a sentence, a ledger row or a button
9
+ *
10
+ * The visual layouts are `aria-hidden`; one visually-hidden sibling carries
11
+ * `role="timer"`, so exactly one live region exists whatever the container
12
+ * width makes visible.
10
13
  */
11
14
  export type CountdownSize = "default" | "hero";
12
- export type CountdownTone = "calm" | "warning" | "critical";
15
+ export type CountdownTone = "calm" | "warning" | "critical" | "auto";
13
16
  export interface CountdownProps {
14
17
  /** Target date/time to count down to */
15
18
  targetDate: Date | string | number;
@@ -27,16 +30,52 @@ export interface CountdownProps {
27
30
  showMinutes?: boolean;
28
31
  /** Show seconds in countdown */
29
32
  showSeconds?: boolean;
30
- /** Layout: "swiss" for prominent animated display, "compact" for simple text */
31
- layout?: "swiss" | "compact";
32
- /** Size: "default" (compact display) or "hero" (52px monospace, urgency-driver). */
33
+ /** Layout: "cells" for a box per unit, "inline" for a single line */
34
+ layout?: "cells" | "inline";
35
+ /** Numeral granularity under `layout="cells"`: one text node per unit, or one box per digit. */
36
+ cells?: "unit" | "digit";
37
+ /**
38
+ * How the time is spelled wherever it is written as one line: the inline
39
+ * layout, the narrow fallback the cells layout falls back to, and — always in
40
+ * `clock` spelling — the string assistive technology is given.
41
+ * `labelled` renders `14m 57s`, with a literal space between units.
42
+ * `clock` renders `02:00` / `1:15:17`, and always carries minutes and seconds
43
+ * whatever `showMinutes` and `showSeconds` say.
44
+ */
45
+ format?: "labelled" | "clock";
46
+ /**
47
+ * Hold the last rendered value. A paused countdown runs no interval, never
48
+ * calls `onComplete` and stops announcing; on resume it re-reads the clock
49
+ * rather than continuing from the held value.
50
+ */
51
+ paused?: boolean;
52
+ /** Size: "default" (compact display) or "hero" (the larger urgency-driver); the two differ only in scale. */
33
53
  size?: CountdownSize;
34
54
  /**
35
- * Presentational urgency tone. Caller derives from phase + time-remaining and passes it.
36
- * - calm: foreground color (default)
37
- * - warning: warning token color
38
- * - critical: danger token color (in retro, also flips the unit container bg)
55
+ * Presentational urgency tone, published as `data-tone` on both layout roots
56
+ * and on each cell, and coloured from the `--ff-countdown-tone-*` tokens.
57
+ * - calm: numeral colour (default)
58
+ * - warning: warning tone colour
59
+ * - critical: critical tone colour (in retro, also flips the unit container bg)
60
+ * - auto: resolved from the time remaining on every tick
39
61
  */
40
62
  tone?: CountdownTone;
41
63
  }
42
- export declare function Countdown({ targetDate, onComplete, expiredLabel, className, showDays, showHours, showMinutes, showSeconds, layout, size, tone, }: CountdownProps): import("react/jsx-runtime").JSX.Element | null;
64
+ interface TimeRemaining {
65
+ days: number;
66
+ hours: number;
67
+ minutes: number;
68
+ seconds: number;
69
+ total: number;
70
+ }
71
+ /** Which units the caller allows on screen; a hidden unit rolls into the next shown one. */
72
+ interface ShownUnits {
73
+ days: boolean;
74
+ hours: boolean;
75
+ minutes: boolean;
76
+ seconds: boolean;
77
+ }
78
+ /** Time left on a target, decomposed across the units the caller shows. */
79
+ export declare function calculateTimeRemaining(targetDate: Date | string | number, shown: ShownUnits, nowMs?: number): TimeRemaining;
80
+ export declare function Countdown({ targetDate, onComplete, expiredLabel, className, showDays, showHours, showMinutes, showSeconds, layout, cells, format, paused, size, tone, }: CountdownProps): import("react/jsx-runtime").JSX.Element | null;
81
+ export {};
@@ -4,6 +4,11 @@ declare const spinnerVariants: (props?: ({
4
4
  size?: "sm" | "md" | "lg" | null | undefined;
5
5
  styleVariant?: "default" | "rounded" | "retro" | "clean" | null | undefined;
6
6
  } & import('class-variance-authority/types').ClassProp) | undefined) => string;
7
+ /**
8
+ * Whether `Spinner` and `Button` export their CSS `part` names. A subtree a page must never style
9
+ * from outside — the payment field — renders with it off.
10
+ */
11
+ export declare const ExportPartsContext: React.Context<boolean>;
7
12
  export interface SpinnerProps extends React.SVGAttributes<SVGSVGElement>, Omit<VariantProps<typeof spinnerVariants>, "styleVariant"> {
8
13
  }
9
14
  export declare const Spinner: React.ForwardRefExoticComponent<SpinnerProps & React.RefAttributes<SVGSVGElement>>;
@@ -86,6 +86,13 @@ interface PaymentCollectionBaseSlotProps extends SlotProps {
86
86
  export type PaymentCollectionSlotProps = PaymentCollectionBaseSlotProps & ({
87
87
  intent: "checkout";
88
88
  onResume: () => Promise<void>;
89
+ /** Grant expiry as a ms epoch: the deadline the claim window counts down to. */
90
+ expiresAt?: number;
91
+ /**
92
+ * True while the deadline is the server's alone, so the claim window holds its last value
93
+ * instead of counting a limit the SDK is no longer enforcing.
94
+ */
95
+ expiryPaused: boolean;
89
96
  } | {
90
97
  intent: "enter" | "reenter";
91
98
  preAuthAmount: string;
@@ -181,6 +188,12 @@ export interface ExperienceWidgetProps {
181
188
  * automatically. Omit to keep the default (`true`); pass `false` to keep every entry explicit.
182
189
  */
183
190
  automaticEntry?: boolean;
191
+ /**
192
+ * Whether the widget fetches the Google Fonts stylesheets for the families its theme names.
193
+ * Off unless asked: an embed's page owns its own font delivery and its own content policy.
194
+ * The fetch never blocks rendering and a failure is silent.
195
+ */
196
+ loadFonts?: boolean;
184
197
  /** Pre-fill access code */
185
198
  accessCode?: string;
186
199
  /** Auto-enter waitlist when available */
@@ -220,6 +233,16 @@ export interface ExperienceWidgetProps {
220
233
  onFanfareCheckout?: (result: FanfareCheckoutResult) => void;
221
234
  /** Called when an error occurs */
222
235
  onError?: (error: Error) => void;
236
+ /**
237
+ * Horizontal placement of the widget inside the host's container. The widget is start-aligned
238
+ * unless this is `"center"`.
239
+ */
240
+ align?: "start" | "center";
241
+ /**
242
+ * Explicit override of the widget's adaptive maximum width. Omit to keep the value of
243
+ * the `--ff-widget-max-width` custom property.
244
+ */
245
+ size?: "compact" | "regular" | "wide";
223
246
  /** Additional class name */
224
247
  className?: string;
225
248
  }
@@ -36,7 +36,7 @@ export declare namespace Ledger {
36
36
  var displayName: string;
37
37
  }
38
38
  /**
39
- * Discrete unit pips for a Dutch remaining count, at the wide adaptation only.
39
+ * Discrete unit pips for a Dutch remaining count, shown only at the wide container width.
40
40
  *
41
41
  * The pips are decorative; the grid states the count once in its own label so a screen reader
42
42
  * hears "7 of 20 units remaining" rather than twenty unlabelled cells.
@@ -49,14 +49,3 @@ export declare function UnitPips({ remaining, total, label }: {
49
49
  export declare namespace UnitPips {
50
50
  var displayName: string;
51
51
  }
52
- /**
53
- * Whether the panel's own rendered width has reached `threshold`.
54
- *
55
- * The adaptation is the panel's, not the viewport's: the widget is embedded at whatever width its
56
- * host gives it, and a viewport query would split the columns on a page that never made room.
57
- *
58
- * The measured node is attached through a callback ref rather than observed once after mount: the
59
- * panel swaps its skeleton for the live ledger, so the node the observer holds is replaced under
60
- * it and a width read after the swap would come from a detached element.
61
- */
62
- export declare function useIsWide(threshold: number): readonly [(element: HTMLDivElement | null) => void, boolean];
@@ -1,8 +1,6 @@
1
1
  import { SelectionAvailability, SelectionChoice, SelectionDraft, SelectionOptions } from '@fanfare-io/fanfare-sdk-core/selections';
2
2
  import { ProductSelectorHandle } from './selection';
3
3
  import * as React from "react";
4
- /** Widened so a selection-bearing panel stacks its rows instead of clipping them at the card cap. */
5
- export declare const SELECTION_PANEL_CLASS = "ff:max-h-none ff:overflow-visible";
6
4
  export interface EntrySelectionSurfaceProps {
7
5
  loadOptions: () => Promise<SelectionOptions>;
8
6
  /** Already-translated heading above the picker. */