@asteby/storefront-react 0.9.0 → 0.11.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 +24 -0
- package/dist/cart.js +2 -1
- package/dist/cart.js.map +1 -1
- package/dist/checkout.cjs +526 -0
- package/dist/checkout.cjs.map +1 -0
- package/dist/checkout.css +100 -0
- package/dist/checkout.d.cts +212 -0
- package/dist/checkout.d.ts +212 -0
- package/dist/checkout.js +243 -0
- package/dist/checkout.js.map +1 -0
- package/dist/chunk-3ZJTDFHE.js +265 -0
- package/dist/chunk-3ZJTDFHE.js.map +1 -0
- package/dist/chunk-T52H5HL3.js +11 -0
- package/dist/chunk-T52H5HL3.js.map +1 -0
- package/dist/{chunk-2WZIWIBW.js → chunk-ZBH7JYU4.js} +4 -10
- package/dist/chunk-ZBH7JYU4.js.map +1 -0
- package/dist/payments.cjs +313 -13
- package/dist/payments.cjs.map +1 -1
- package/dist/payments.css +27 -0
- package/dist/payments.d.cts +19 -4
- package/dist/payments.d.ts +19 -4
- package/dist/payments.js +59 -14
- package/dist/payments.js.map +1 -1
- package/dist/promotions.js +2 -1
- package/dist/promotions.js.map +1 -1
- package/package.json +13 -2
- package/dist/chunk-2WZIWIBW.js.map +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ShippingRate, PickupOption, Money } from '@asteby/storefront';
|
|
4
|
+
|
|
5
|
+
type BrandMarkProps = {
|
|
6
|
+
/** Provider identifier (`fedex`, `oxxo`, `mercadopago`, `7eleven`…). Unknown keys get a neutral monogram. */
|
|
7
|
+
brand: string | null | undefined;
|
|
8
|
+
/** Licensed logo sent by the provider (`PaymentPoint.logo_url`…). Only `https:`; it wins over the bundled logo and the badge. */
|
|
9
|
+
logoUrl?: string | null;
|
|
10
|
+
/** Height in px of the box. Badges are 3:2 (width = 1.5 × height); real logos get a width that follows their shape. Default 24. */
|
|
11
|
+
size?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Accessible name. Omit it when the brand name is already written next to the mark (the usual case): the mark is then
|
|
14
|
+
* decorative (`aria-hidden`).
|
|
15
|
+
*/
|
|
16
|
+
title?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
};
|
|
19
|
+
/** What the mark ended up drawing: the provider's logo, the bundled real logo, or the typographic fallback. */
|
|
20
|
+
type BrandMarkSource = 'provider' | 'logo' | 'badge';
|
|
21
|
+
/**
|
|
22
|
+
* The mark of a carrier, card network, wallet or cash point. Precedence: the provider's `logoUrl` (https, lazy) > the
|
|
23
|
+
* real logo bundled with the package (inline data URI: no network request, same size on the server and in the browser)
|
|
24
|
+
* > a typographic badge in the brand color for the brands without an available logo.
|
|
25
|
+
*
|
|
26
|
+
* Logos are never recolored; they sit on a white plate with a hairline (`--sf-brand-mark-ring`, plate color
|
|
27
|
+
* `--sf-brand-mark-plate`) so they keep their contrast on light and dark themes. The element carries
|
|
28
|
+
* `data-sf-brand-mark="<key>"` and `data-sf-brand-source="provider|logo|badge"`.
|
|
29
|
+
*/
|
|
30
|
+
declare function BrandMark({ brand, logoUrl, size, title, className }: BrandMarkProps): react.JSX.Element;
|
|
31
|
+
|
|
32
|
+
type LogoAsset = {
|
|
33
|
+
src: string;
|
|
34
|
+
ar: number;
|
|
35
|
+
mono: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shared catalog of the brands a checkout names: parcel carriers, card networks, wallets and cash payment points.
|
|
40
|
+
*
|
|
41
|
+
* Keys are the stable identifiers the providers already send (`ShippingRate.carrier` of Skydropx / Envía, the
|
|
42
|
+
* `PaymentPoint.payment_point` or `id` of the payment addon, a `PaymentMethod.key`). Nothing here decides what a shop
|
|
43
|
+
* offers: the server lists the rates and methods; this only gives each one its commercial name and a mark.
|
|
44
|
+
*
|
|
45
|
+
* Marks are drawn by `<BrandMark>` with this precedence: the provider's `logo_url` > the real logo bundled in
|
|
46
|
+
* `brands/assets` (inline data URI, no request; provenance in `brands/LOGOS.md`) > a typographic badge (brand color +
|
|
47
|
+
* short wordmark) for the brands without an available logo. The `bg`/`fg` colors here are close approximations of each
|
|
48
|
+
* brand's public color and only paint that fallback badge.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
type BrandKind = 'carrier' | 'card' | 'wallet' | 'cash' | 'bank';
|
|
52
|
+
type Brand = {
|
|
53
|
+
key: string;
|
|
54
|
+
/** Commercial name, as the brand writes it. */
|
|
55
|
+
name: string;
|
|
56
|
+
kind: BrandKind;
|
|
57
|
+
/** Badge background. */
|
|
58
|
+
bg: string;
|
|
59
|
+
/** Badge text color (AA on `bg`). */
|
|
60
|
+
fg: string;
|
|
61
|
+
/** Short wordmark drawn in the badge (`\n` = two lines). Default: `name`. */
|
|
62
|
+
mark?: string;
|
|
63
|
+
/** Special drawings instead of a wordmark. */
|
|
64
|
+
shape?: 'mastercard';
|
|
65
|
+
};
|
|
66
|
+
/** `"FedEx México"`, `"fedex_mx"` and `"FEDEX"` all become `fedex`. Lowercase ASCII letters and digits only. */
|
|
67
|
+
declare function brandKey(raw: string | null | undefined): string;
|
|
68
|
+
/** Catalog entry for a provider identifier, or `null` when the brand is not in the catalog. */
|
|
69
|
+
declare function findBrand(raw: string | null | undefined): Brand | null;
|
|
70
|
+
/**
|
|
71
|
+
* Commercial name for a provider identifier: the catalog's (`fedex` → "FedEx") or, for an unknown one, the identifier
|
|
72
|
+
* in title case (`"envios_rapidos"` → "Envios Rapidos"). `null` for an empty input.
|
|
73
|
+
*/
|
|
74
|
+
declare function brandName(raw: string | null | undefined): string | null;
|
|
75
|
+
/** Every brand of the catalog (for docs, previews and tests). */
|
|
76
|
+
declare function listBrands(kind?: BrandKind): Brand[];
|
|
77
|
+
/** Real logo bundled for a provider identifier (inline data URI), or `null` when the brand only has the fallback badge. */
|
|
78
|
+
declare function brandLogo(raw: string | null | undefined): (LogoAsset & {
|
|
79
|
+
key: string;
|
|
80
|
+
}) | null;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Pure helpers for the "Delivery" step: how to name a carrier rate, its arrival time, which rate is the cheapest or the
|
|
84
|
+
* fastest, and one human note for the quote deadline. No amounts are computed: `cost` is shown as the server sends it;
|
|
85
|
+
* comparing two costs to flag the cheapest is the only arithmetic.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
type DeliveryMessages = {
|
|
89
|
+
/** `{n}` = business days. */
|
|
90
|
+
etaOne: string;
|
|
91
|
+
etaMany: string;
|
|
92
|
+
/** `{min}` / `{max}` = business days. */
|
|
93
|
+
etaRange: string;
|
|
94
|
+
etaUpTo: string;
|
|
95
|
+
etaFrom: string;
|
|
96
|
+
cheapest: string;
|
|
97
|
+
fastest: string;
|
|
98
|
+
free: string;
|
|
99
|
+
pickupTitle: string;
|
|
100
|
+
/** Quote deadline, `{time}` = local time. */
|
|
101
|
+
quoteToday: string;
|
|
102
|
+
quoteTomorrow: string;
|
|
103
|
+
/** `{date}` = short date + time. */
|
|
104
|
+
quoteLater: string;
|
|
105
|
+
quoteSoon: string;
|
|
106
|
+
partial: string;
|
|
107
|
+
loading: string;
|
|
108
|
+
/** The quote expired or changed and the rates were reloaded. */
|
|
109
|
+
requoted: string;
|
|
110
|
+
};
|
|
111
|
+
/** Messages for a locale (`es*` default, `en*`), with optional overrides. */
|
|
112
|
+
declare function deliveryMessages(locale?: string | null, overrides?: Partial<DeliveryMessages> | null): DeliveryMessages;
|
|
113
|
+
/** Commercial carrier name of a rate (`fedex` → "FedEx"); `null` without carrier. */
|
|
114
|
+
declare function rateCarrier(rate: Pick<ShippingRate, 'carrier'>): string | null;
|
|
115
|
+
/**
|
|
116
|
+
* Service name without the carrier repeated: "FedEx Express Saver" with carrier `fedex` → "Express Saver". Falls back
|
|
117
|
+
* to the title when stripping would leave nothing.
|
|
118
|
+
*/
|
|
119
|
+
declare function rateService(rate: Pick<ShippingRate, 'title' | 'carrier'>): string;
|
|
120
|
+
/** Arrival time of a rate: business days from the provider, else the server `eta` text as is. */
|
|
121
|
+
declare function rateEta(rate: Pick<ShippingRate, 'eta' | 'eta_days_min' | 'eta_days_max'>, messages?: DeliveryMessages): string | null;
|
|
122
|
+
type RateBadge = 'cheapest' | 'fastest';
|
|
123
|
+
/**
|
|
124
|
+
* Badges per rate id. Only with two or more rates, only when one rate is strictly the cheapest (same currency) or
|
|
125
|
+
* strictly the fastest: a tie gets no badge, so the badge always tells the buyer something.
|
|
126
|
+
*/
|
|
127
|
+
declare function rateBadges(rates: readonly ShippingRate[]): Map<string, RateBadge[]>;
|
|
128
|
+
/** Earliest `expires_at` of the rates (the deadline of the whole list), or `null`. */
|
|
129
|
+
declare function quoteDeadline(rates: readonly Pick<ShippingRate, 'expires_at'>[]): Date | null;
|
|
130
|
+
type QuoteNote = {
|
|
131
|
+
text: string;
|
|
132
|
+
soon: boolean;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* One human note for the quote deadline, in the buyer's time zone (or `timeZone`): "valid until 9:39 p.m.", "until
|
|
136
|
+
* tomorrow at…", "until Oct 24, …", or "expire soon" within `soonMinutes` (default 15). `null` when already past.
|
|
137
|
+
*/
|
|
138
|
+
declare function quoteNote(deadline: Date | null, opts?: {
|
|
139
|
+
locale?: string;
|
|
140
|
+
now?: Date;
|
|
141
|
+
timeZone?: string;
|
|
142
|
+
soonMinutes?: number;
|
|
143
|
+
messages?: DeliveryMessages;
|
|
144
|
+
}): QuoteNote | null;
|
|
145
|
+
/**
|
|
146
|
+
* `true` when saving a shipping rate failed because the quote expired or the rates changed, so the shop should reload
|
|
147
|
+
* the rates (and keep the address) instead of showing a generic failure. Recognizes the explicit codes
|
|
148
|
+
* (`quote_expired`, `rate_expired`…, any status), a `409`, and the generic `422` on the rate field that older servers
|
|
149
|
+
* answer when the provider re-quoted with new ids. 5xx and other 4xx stay real failures.
|
|
150
|
+
*/
|
|
151
|
+
declare function isQuoteChange(err: {
|
|
152
|
+
status?: number;
|
|
153
|
+
code?: string | null;
|
|
154
|
+
field?: string | null;
|
|
155
|
+
} | null | undefined): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The rate of a fresh quote that matches the one the buyer had chosen (same carrier and service), so a re-quote keeps
|
|
158
|
+
* the choice even though the provider issued new ids. `null` when that service is no longer offered.
|
|
159
|
+
*/
|
|
160
|
+
declare function matchRate<R extends Pick<ShippingRate, 'id' | 'carrier' | 'service_code' | 'title'>>(previous: Pick<ShippingRate, 'carrier' | 'service_code' | 'title'> | null | undefined, rates: readonly R[]): R | null;
|
|
161
|
+
|
|
162
|
+
type DeliveryOptionsClassNames = Partial<Record<'root' | 'option' | 'radio' | 'mark' | 'body' | 'title' | 'carrier' | 'service' | 'eta' | 'badges' | 'badge' | 'price' | 'free' | 'note' | 'partial' | 'skeleton', string>>;
|
|
163
|
+
type DeliveryOptionsProps = {
|
|
164
|
+
rates: readonly ShippingRate[];
|
|
165
|
+
pickup?: readonly PickupOption[];
|
|
166
|
+
/** Radio group name. Default `delivery_choice`. */
|
|
167
|
+
name?: string;
|
|
168
|
+
/** Form value of a rate. Default `rate:<id>`. */
|
|
169
|
+
rateValue?: (rate: ShippingRate) => string;
|
|
170
|
+
/** Form value of a pickup branch. Default `pickup:<branch_id>`. */
|
|
171
|
+
pickupValue?: (branch: PickupOption) => string;
|
|
172
|
+
/** Uncontrolled preselection. Default: the first rate. `null`: nothing preselected. */
|
|
173
|
+
defaultValue?: string | null;
|
|
174
|
+
/** Controlled value (use with `onValueChange`). */
|
|
175
|
+
value?: string;
|
|
176
|
+
onValueChange?: (value: string) => void;
|
|
177
|
+
/** Some provider did not answer (`DeliveryOptions.partial`): one neutral note above the list. */
|
|
178
|
+
partial?: boolean;
|
|
179
|
+
/** The previous quote expired or changed and these rates are fresh: one status note above the list. */
|
|
180
|
+
requoted?: boolean;
|
|
181
|
+
/** Rates are being quoted again: skeleton rows (no rates yet) or the current rows dimmed, `aria-busy`. */
|
|
182
|
+
loading?: boolean;
|
|
183
|
+
/** Quote deadline note: `always` (default), only when it is `soon`, or `never`. */
|
|
184
|
+
quoteNote?: 'always' | 'soon' | 'never';
|
|
185
|
+
/** Time zone of the note. Default: the buyer's browser. */
|
|
186
|
+
timeZone?: string;
|
|
187
|
+
/** Show the carrier marks. Default true. */
|
|
188
|
+
marks?: boolean;
|
|
189
|
+
/** Cheapest / fastest badges. Default true. */
|
|
190
|
+
badges?: boolean;
|
|
191
|
+
required?: boolean;
|
|
192
|
+
locale?: string;
|
|
193
|
+
messages?: Partial<DeliveryMessages>;
|
|
194
|
+
renderAmount?: (amount: Money) => ReactNode;
|
|
195
|
+
classNames?: DeliveryOptionsClassNames;
|
|
196
|
+
/** `data-testid` of every radio. */
|
|
197
|
+
testId?: string;
|
|
198
|
+
/** Rows the shop renders before the rates (e.g. "home delivery, rates after the address"). */
|
|
199
|
+
children?: ReactNode;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* The rates and pickup branches of "Delivery" as native radios: carrier mark and commercial name, readable service,
|
|
203
|
+
* arrival time, cheapest / fastest badges, price as the server sends it, and ONE note for the quote deadline in the
|
|
204
|
+
* buyer's time zone. Works without JavaScript (plain radios in the page form); the note appears after hydration in a
|
|
205
|
+
* reserved line, so it never shifts the layout.
|
|
206
|
+
*
|
|
207
|
+
* Wrap it in the shop's own `<fieldset><legend>`. Style it with `classNames` and/or the default sheet
|
|
208
|
+
* `@asteby/storefront-react/checkout.css`.
|
|
209
|
+
*/
|
|
210
|
+
declare function DeliveryOptions(props: DeliveryOptionsProps): react.JSX.Element;
|
|
211
|
+
|
|
212
|
+
export { type Brand, type BrandKind, BrandMark, type BrandMarkProps, type BrandMarkSource, type DeliveryMessages, DeliveryOptions, type DeliveryOptionsClassNames, type DeliveryOptionsProps, type QuoteNote, type RateBadge, brandKey, brandLogo, brandName, deliveryMessages, findBrand, isQuoteChange, listBrands, matchRate, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService };
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ShippingRate, PickupOption, Money } from '@asteby/storefront';
|
|
4
|
+
|
|
5
|
+
type BrandMarkProps = {
|
|
6
|
+
/** Provider identifier (`fedex`, `oxxo`, `mercadopago`, `7eleven`…). Unknown keys get a neutral monogram. */
|
|
7
|
+
brand: string | null | undefined;
|
|
8
|
+
/** Licensed logo sent by the provider (`PaymentPoint.logo_url`…). Only `https:`; it wins over the bundled logo and the badge. */
|
|
9
|
+
logoUrl?: string | null;
|
|
10
|
+
/** Height in px of the box. Badges are 3:2 (width = 1.5 × height); real logos get a width that follows their shape. Default 24. */
|
|
11
|
+
size?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Accessible name. Omit it when the brand name is already written next to the mark (the usual case): the mark is then
|
|
14
|
+
* decorative (`aria-hidden`).
|
|
15
|
+
*/
|
|
16
|
+
title?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
};
|
|
19
|
+
/** What the mark ended up drawing: the provider's logo, the bundled real logo, or the typographic fallback. */
|
|
20
|
+
type BrandMarkSource = 'provider' | 'logo' | 'badge';
|
|
21
|
+
/**
|
|
22
|
+
* The mark of a carrier, card network, wallet or cash point. Precedence: the provider's `logoUrl` (https, lazy) > the
|
|
23
|
+
* real logo bundled with the package (inline data URI: no network request, same size on the server and in the browser)
|
|
24
|
+
* > a typographic badge in the brand color for the brands without an available logo.
|
|
25
|
+
*
|
|
26
|
+
* Logos are never recolored; they sit on a white plate with a hairline (`--sf-brand-mark-ring`, plate color
|
|
27
|
+
* `--sf-brand-mark-plate`) so they keep their contrast on light and dark themes. The element carries
|
|
28
|
+
* `data-sf-brand-mark="<key>"` and `data-sf-brand-source="provider|logo|badge"`.
|
|
29
|
+
*/
|
|
30
|
+
declare function BrandMark({ brand, logoUrl, size, title, className }: BrandMarkProps): react.JSX.Element;
|
|
31
|
+
|
|
32
|
+
type LogoAsset = {
|
|
33
|
+
src: string;
|
|
34
|
+
ar: number;
|
|
35
|
+
mono: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Shared catalog of the brands a checkout names: parcel carriers, card networks, wallets and cash payment points.
|
|
40
|
+
*
|
|
41
|
+
* Keys are the stable identifiers the providers already send (`ShippingRate.carrier` of Skydropx / Envía, the
|
|
42
|
+
* `PaymentPoint.payment_point` or `id` of the payment addon, a `PaymentMethod.key`). Nothing here decides what a shop
|
|
43
|
+
* offers: the server lists the rates and methods; this only gives each one its commercial name and a mark.
|
|
44
|
+
*
|
|
45
|
+
* Marks are drawn by `<BrandMark>` with this precedence: the provider's `logo_url` > the real logo bundled in
|
|
46
|
+
* `brands/assets` (inline data URI, no request; provenance in `brands/LOGOS.md`) > a typographic badge (brand color +
|
|
47
|
+
* short wordmark) for the brands without an available logo. The `bg`/`fg` colors here are close approximations of each
|
|
48
|
+
* brand's public color and only paint that fallback badge.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
type BrandKind = 'carrier' | 'card' | 'wallet' | 'cash' | 'bank';
|
|
52
|
+
type Brand = {
|
|
53
|
+
key: string;
|
|
54
|
+
/** Commercial name, as the brand writes it. */
|
|
55
|
+
name: string;
|
|
56
|
+
kind: BrandKind;
|
|
57
|
+
/** Badge background. */
|
|
58
|
+
bg: string;
|
|
59
|
+
/** Badge text color (AA on `bg`). */
|
|
60
|
+
fg: string;
|
|
61
|
+
/** Short wordmark drawn in the badge (`\n` = two lines). Default: `name`. */
|
|
62
|
+
mark?: string;
|
|
63
|
+
/** Special drawings instead of a wordmark. */
|
|
64
|
+
shape?: 'mastercard';
|
|
65
|
+
};
|
|
66
|
+
/** `"FedEx México"`, `"fedex_mx"` and `"FEDEX"` all become `fedex`. Lowercase ASCII letters and digits only. */
|
|
67
|
+
declare function brandKey(raw: string | null | undefined): string;
|
|
68
|
+
/** Catalog entry for a provider identifier, or `null` when the brand is not in the catalog. */
|
|
69
|
+
declare function findBrand(raw: string | null | undefined): Brand | null;
|
|
70
|
+
/**
|
|
71
|
+
* Commercial name for a provider identifier: the catalog's (`fedex` → "FedEx") or, for an unknown one, the identifier
|
|
72
|
+
* in title case (`"envios_rapidos"` → "Envios Rapidos"). `null` for an empty input.
|
|
73
|
+
*/
|
|
74
|
+
declare function brandName(raw: string | null | undefined): string | null;
|
|
75
|
+
/** Every brand of the catalog (for docs, previews and tests). */
|
|
76
|
+
declare function listBrands(kind?: BrandKind): Brand[];
|
|
77
|
+
/** Real logo bundled for a provider identifier (inline data URI), or `null` when the brand only has the fallback badge. */
|
|
78
|
+
declare function brandLogo(raw: string | null | undefined): (LogoAsset & {
|
|
79
|
+
key: string;
|
|
80
|
+
}) | null;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Pure helpers for the "Delivery" step: how to name a carrier rate, its arrival time, which rate is the cheapest or the
|
|
84
|
+
* fastest, and one human note for the quote deadline. No amounts are computed: `cost` is shown as the server sends it;
|
|
85
|
+
* comparing two costs to flag the cheapest is the only arithmetic.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
type DeliveryMessages = {
|
|
89
|
+
/** `{n}` = business days. */
|
|
90
|
+
etaOne: string;
|
|
91
|
+
etaMany: string;
|
|
92
|
+
/** `{min}` / `{max}` = business days. */
|
|
93
|
+
etaRange: string;
|
|
94
|
+
etaUpTo: string;
|
|
95
|
+
etaFrom: string;
|
|
96
|
+
cheapest: string;
|
|
97
|
+
fastest: string;
|
|
98
|
+
free: string;
|
|
99
|
+
pickupTitle: string;
|
|
100
|
+
/** Quote deadline, `{time}` = local time. */
|
|
101
|
+
quoteToday: string;
|
|
102
|
+
quoteTomorrow: string;
|
|
103
|
+
/** `{date}` = short date + time. */
|
|
104
|
+
quoteLater: string;
|
|
105
|
+
quoteSoon: string;
|
|
106
|
+
partial: string;
|
|
107
|
+
loading: string;
|
|
108
|
+
/** The quote expired or changed and the rates were reloaded. */
|
|
109
|
+
requoted: string;
|
|
110
|
+
};
|
|
111
|
+
/** Messages for a locale (`es*` default, `en*`), with optional overrides. */
|
|
112
|
+
declare function deliveryMessages(locale?: string | null, overrides?: Partial<DeliveryMessages> | null): DeliveryMessages;
|
|
113
|
+
/** Commercial carrier name of a rate (`fedex` → "FedEx"); `null` without carrier. */
|
|
114
|
+
declare function rateCarrier(rate: Pick<ShippingRate, 'carrier'>): string | null;
|
|
115
|
+
/**
|
|
116
|
+
* Service name without the carrier repeated: "FedEx Express Saver" with carrier `fedex` → "Express Saver". Falls back
|
|
117
|
+
* to the title when stripping would leave nothing.
|
|
118
|
+
*/
|
|
119
|
+
declare function rateService(rate: Pick<ShippingRate, 'title' | 'carrier'>): string;
|
|
120
|
+
/** Arrival time of a rate: business days from the provider, else the server `eta` text as is. */
|
|
121
|
+
declare function rateEta(rate: Pick<ShippingRate, 'eta' | 'eta_days_min' | 'eta_days_max'>, messages?: DeliveryMessages): string | null;
|
|
122
|
+
type RateBadge = 'cheapest' | 'fastest';
|
|
123
|
+
/**
|
|
124
|
+
* Badges per rate id. Only with two or more rates, only when one rate is strictly the cheapest (same currency) or
|
|
125
|
+
* strictly the fastest: a tie gets no badge, so the badge always tells the buyer something.
|
|
126
|
+
*/
|
|
127
|
+
declare function rateBadges(rates: readonly ShippingRate[]): Map<string, RateBadge[]>;
|
|
128
|
+
/** Earliest `expires_at` of the rates (the deadline of the whole list), or `null`. */
|
|
129
|
+
declare function quoteDeadline(rates: readonly Pick<ShippingRate, 'expires_at'>[]): Date | null;
|
|
130
|
+
type QuoteNote = {
|
|
131
|
+
text: string;
|
|
132
|
+
soon: boolean;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* One human note for the quote deadline, in the buyer's time zone (or `timeZone`): "valid until 9:39 p.m.", "until
|
|
136
|
+
* tomorrow at…", "until Oct 24, …", or "expire soon" within `soonMinutes` (default 15). `null` when already past.
|
|
137
|
+
*/
|
|
138
|
+
declare function quoteNote(deadline: Date | null, opts?: {
|
|
139
|
+
locale?: string;
|
|
140
|
+
now?: Date;
|
|
141
|
+
timeZone?: string;
|
|
142
|
+
soonMinutes?: number;
|
|
143
|
+
messages?: DeliveryMessages;
|
|
144
|
+
}): QuoteNote | null;
|
|
145
|
+
/**
|
|
146
|
+
* `true` when saving a shipping rate failed because the quote expired or the rates changed, so the shop should reload
|
|
147
|
+
* the rates (and keep the address) instead of showing a generic failure. Recognizes the explicit codes
|
|
148
|
+
* (`quote_expired`, `rate_expired`…, any status), a `409`, and the generic `422` on the rate field that older servers
|
|
149
|
+
* answer when the provider re-quoted with new ids. 5xx and other 4xx stay real failures.
|
|
150
|
+
*/
|
|
151
|
+
declare function isQuoteChange(err: {
|
|
152
|
+
status?: number;
|
|
153
|
+
code?: string | null;
|
|
154
|
+
field?: string | null;
|
|
155
|
+
} | null | undefined): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The rate of a fresh quote that matches the one the buyer had chosen (same carrier and service), so a re-quote keeps
|
|
158
|
+
* the choice even though the provider issued new ids. `null` when that service is no longer offered.
|
|
159
|
+
*/
|
|
160
|
+
declare function matchRate<R extends Pick<ShippingRate, 'id' | 'carrier' | 'service_code' | 'title'>>(previous: Pick<ShippingRate, 'carrier' | 'service_code' | 'title'> | null | undefined, rates: readonly R[]): R | null;
|
|
161
|
+
|
|
162
|
+
type DeliveryOptionsClassNames = Partial<Record<'root' | 'option' | 'radio' | 'mark' | 'body' | 'title' | 'carrier' | 'service' | 'eta' | 'badges' | 'badge' | 'price' | 'free' | 'note' | 'partial' | 'skeleton', string>>;
|
|
163
|
+
type DeliveryOptionsProps = {
|
|
164
|
+
rates: readonly ShippingRate[];
|
|
165
|
+
pickup?: readonly PickupOption[];
|
|
166
|
+
/** Radio group name. Default `delivery_choice`. */
|
|
167
|
+
name?: string;
|
|
168
|
+
/** Form value of a rate. Default `rate:<id>`. */
|
|
169
|
+
rateValue?: (rate: ShippingRate) => string;
|
|
170
|
+
/** Form value of a pickup branch. Default `pickup:<branch_id>`. */
|
|
171
|
+
pickupValue?: (branch: PickupOption) => string;
|
|
172
|
+
/** Uncontrolled preselection. Default: the first rate. `null`: nothing preselected. */
|
|
173
|
+
defaultValue?: string | null;
|
|
174
|
+
/** Controlled value (use with `onValueChange`). */
|
|
175
|
+
value?: string;
|
|
176
|
+
onValueChange?: (value: string) => void;
|
|
177
|
+
/** Some provider did not answer (`DeliveryOptions.partial`): one neutral note above the list. */
|
|
178
|
+
partial?: boolean;
|
|
179
|
+
/** The previous quote expired or changed and these rates are fresh: one status note above the list. */
|
|
180
|
+
requoted?: boolean;
|
|
181
|
+
/** Rates are being quoted again: skeleton rows (no rates yet) or the current rows dimmed, `aria-busy`. */
|
|
182
|
+
loading?: boolean;
|
|
183
|
+
/** Quote deadline note: `always` (default), only when it is `soon`, or `never`. */
|
|
184
|
+
quoteNote?: 'always' | 'soon' | 'never';
|
|
185
|
+
/** Time zone of the note. Default: the buyer's browser. */
|
|
186
|
+
timeZone?: string;
|
|
187
|
+
/** Show the carrier marks. Default true. */
|
|
188
|
+
marks?: boolean;
|
|
189
|
+
/** Cheapest / fastest badges. Default true. */
|
|
190
|
+
badges?: boolean;
|
|
191
|
+
required?: boolean;
|
|
192
|
+
locale?: string;
|
|
193
|
+
messages?: Partial<DeliveryMessages>;
|
|
194
|
+
renderAmount?: (amount: Money) => ReactNode;
|
|
195
|
+
classNames?: DeliveryOptionsClassNames;
|
|
196
|
+
/** `data-testid` of every radio. */
|
|
197
|
+
testId?: string;
|
|
198
|
+
/** Rows the shop renders before the rates (e.g. "home delivery, rates after the address"). */
|
|
199
|
+
children?: ReactNode;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* The rates and pickup branches of "Delivery" as native radios: carrier mark and commercial name, readable service,
|
|
203
|
+
* arrival time, cheapest / fastest badges, price as the server sends it, and ONE note for the quote deadline in the
|
|
204
|
+
* buyer's time zone. Works without JavaScript (plain radios in the page form); the note appears after hydration in a
|
|
205
|
+
* reserved line, so it never shifts the layout.
|
|
206
|
+
*
|
|
207
|
+
* Wrap it in the shop's own `<fieldset><legend>`. Style it with `classNames` and/or the default sheet
|
|
208
|
+
* `@asteby/storefront-react/checkout.css`.
|
|
209
|
+
*/
|
|
210
|
+
declare function DeliveryOptions(props: DeliveryOptionsProps): react.JSX.Element;
|
|
211
|
+
|
|
212
|
+
export { type Brand, type BrandKind, BrandMark, type BrandMarkProps, type BrandMarkSource, type DeliveryMessages, DeliveryOptions, type DeliveryOptionsClassNames, type DeliveryOptionsProps, type QuoteNote, type RateBadge, brandKey, brandLogo, brandName, deliveryMessages, findBrand, isQuoteChange, listBrands, matchRate, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService };
|
package/dist/checkout.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { brandName, BrandMark } from './chunk-3ZJTDFHE.js';
|
|
2
|
+
export { BrandMark, brandKey, brandLogo, brandName, findBrand, listBrands } from './chunk-3ZJTDFHE.js';
|
|
3
|
+
import { useResolvedLocale } from './chunk-T52H5HL3.js';
|
|
4
|
+
import './chunk-DQBL2DFJ.js';
|
|
5
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
6
|
+
import { formatMoney } from '@asteby/storefront';
|
|
7
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
// src/checkout/delivery.ts
|
|
10
|
+
var ES = {
|
|
11
|
+
etaOne: "Llega en 1 d\xEDa h\xE1bil",
|
|
12
|
+
etaMany: "Llega en {n} d\xEDas h\xE1biles",
|
|
13
|
+
etaRange: "Llega en {min} a {max} d\xEDas h\xE1biles",
|
|
14
|
+
etaUpTo: "Llega en m\xE1ximo {max} d\xEDas h\xE1biles",
|
|
15
|
+
etaFrom: "Llega en {min} d\xEDas h\xE1biles o m\xE1s",
|
|
16
|
+
cheapest: "M\xE1s econ\xF3mico",
|
|
17
|
+
fastest: "M\xE1s r\xE1pido",
|
|
18
|
+
free: "Gratis",
|
|
19
|
+
pickupTitle: "Recoger en {name}",
|
|
20
|
+
quoteToday: "Precios de env\xEDo vigentes hasta las {time}.",
|
|
21
|
+
quoteTomorrow: "Precios de env\xEDo vigentes hasta ma\xF1ana a las {time}.",
|
|
22
|
+
quoteLater: "Precios de env\xEDo vigentes hasta el {date}.",
|
|
23
|
+
quoteSoon: "Los precios de env\xEDo vencen pronto ({time}).",
|
|
24
|
+
partial: "No pudimos consultar todas las paqueter\xEDas. Estas son las opciones disponibles ahora.",
|
|
25
|
+
loading: "Buscando opciones de env\xEDo\u2026",
|
|
26
|
+
requoted: "Las tarifas de env\xEDo cambiaron y ya est\xE1n actualizadas. Revisa tu env\xEDo antes de pagar."
|
|
27
|
+
};
|
|
28
|
+
var EN = {
|
|
29
|
+
etaOne: "Arrives in 1 business day",
|
|
30
|
+
etaMany: "Arrives in {n} business days",
|
|
31
|
+
etaRange: "Arrives in {min} to {max} business days",
|
|
32
|
+
etaUpTo: "Arrives in up to {max} business days",
|
|
33
|
+
etaFrom: "Arrives in {min}+ business days",
|
|
34
|
+
cheapest: "Cheapest",
|
|
35
|
+
fastest: "Fastest",
|
|
36
|
+
free: "Free",
|
|
37
|
+
pickupTitle: "Pick up at {name}",
|
|
38
|
+
quoteToday: "Shipping prices valid until {time}.",
|
|
39
|
+
quoteTomorrow: "Shipping prices valid until tomorrow at {time}.",
|
|
40
|
+
quoteLater: "Shipping prices valid until {date}.",
|
|
41
|
+
quoteSoon: "Shipping prices expire soon ({time}).",
|
|
42
|
+
partial: "We couldn't reach every carrier. These are the options available now.",
|
|
43
|
+
loading: "Looking for delivery options\u2026",
|
|
44
|
+
requoted: "Shipping rates changed and are now up to date. Check your delivery before paying."
|
|
45
|
+
};
|
|
46
|
+
function deliveryMessages(locale, overrides) {
|
|
47
|
+
const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN : ES;
|
|
48
|
+
return overrides ? { ...base, ...overrides } : base;
|
|
49
|
+
}
|
|
50
|
+
var fill = (tpl, vars) => tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? "")).replace(/\.\.$/, ".");
|
|
51
|
+
function rateCarrier(rate) {
|
|
52
|
+
return brandName(rate.carrier);
|
|
53
|
+
}
|
|
54
|
+
function rateService(rate) {
|
|
55
|
+
const title = rate.title.trim();
|
|
56
|
+
const carrier = rateCarrier(rate);
|
|
57
|
+
if (!carrier) return title;
|
|
58
|
+
for (const prefix of [carrier, rate.carrier ?? ""]) {
|
|
59
|
+
if (!prefix) continue;
|
|
60
|
+
if (title.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
61
|
+
const rest = title.slice(prefix.length).replace(/^[\s·:|-]+/, "");
|
|
62
|
+
if (rest) return rest;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return title;
|
|
66
|
+
}
|
|
67
|
+
function rateEta(rate, messages = ES) {
|
|
68
|
+
const { eta_days_min: min, eta_days_max: max } = rate;
|
|
69
|
+
if (typeof min === "number" && typeof max === "number") {
|
|
70
|
+
if (min === max) return min === 1 ? messages.etaOne : fill(messages.etaMany, { n: min });
|
|
71
|
+
return fill(messages.etaRange, { min, max });
|
|
72
|
+
}
|
|
73
|
+
if (typeof max === "number") return max === 1 ? messages.etaOne : fill(messages.etaUpTo, { max });
|
|
74
|
+
if (typeof min === "number") return min === 1 ? messages.etaOne : fill(messages.etaFrom, { min });
|
|
75
|
+
return rate.eta ?? null;
|
|
76
|
+
}
|
|
77
|
+
var costOf = (r) => {
|
|
78
|
+
const n = Number(r.cost.amount);
|
|
79
|
+
return Number.isFinite(n) ? n : null;
|
|
80
|
+
};
|
|
81
|
+
var daysOf = (r) => r.eta_days_max ?? r.eta_days_min ?? null;
|
|
82
|
+
function rateBadges(rates) {
|
|
83
|
+
const out = /* @__PURE__ */ new Map();
|
|
84
|
+
if (rates.length < 2) return out;
|
|
85
|
+
const add = (id, b) => out.set(id, [...out.get(id) ?? [], b]);
|
|
86
|
+
const unique = (values) => {
|
|
87
|
+
const known = values.filter((v) => v[1] !== null);
|
|
88
|
+
if (known.length < 2) return null;
|
|
89
|
+
const min = Math.min(...known.map((v) => v[1]));
|
|
90
|
+
const winners = known.filter((v) => v[1] === min);
|
|
91
|
+
return winners.length === 1 ? winners[0][0] : null;
|
|
92
|
+
};
|
|
93
|
+
const currencies = new Set(rates.map((r) => r.cost.currency_code));
|
|
94
|
+
if (currencies.size === 1) {
|
|
95
|
+
const id = unique(rates.map((r) => [r.id, costOf(r)]));
|
|
96
|
+
if (id) add(id, "cheapest");
|
|
97
|
+
}
|
|
98
|
+
const fast = unique(rates.map((r) => [r.id, daysOf(r)]));
|
|
99
|
+
if (fast) add(fast, "fastest");
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
function quoteDeadline(rates) {
|
|
103
|
+
let best = null;
|
|
104
|
+
for (const r of rates) {
|
|
105
|
+
if (!r.expires_at) continue;
|
|
106
|
+
const d = new Date(r.expires_at);
|
|
107
|
+
if (Number.isNaN(d.getTime())) continue;
|
|
108
|
+
if (!best || d < best) best = d;
|
|
109
|
+
}
|
|
110
|
+
return best;
|
|
111
|
+
}
|
|
112
|
+
function quoteNote(deadline, opts = {}) {
|
|
113
|
+
if (!deadline) return null;
|
|
114
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
115
|
+
const left = deadline.getTime() - now.getTime();
|
|
116
|
+
if (left <= 0) return null;
|
|
117
|
+
const m = opts.messages ?? ES;
|
|
118
|
+
const locale = opts.locale ?? "es-MX";
|
|
119
|
+
const tz = opts.timeZone ? { timeZone: opts.timeZone } : {};
|
|
120
|
+
const time = new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", ...tz }).format(deadline);
|
|
121
|
+
if (left <= (opts.soonMinutes ?? 15) * 6e4) return { text: fill(m.quoteSoon, { time }), soon: true };
|
|
122
|
+
const day = (d) => new Intl.DateTimeFormat("en-CA", { year: "numeric", month: "2-digit", day: "2-digit", ...tz }).format(d);
|
|
123
|
+
if (day(deadline) === day(now)) return { text: fill(m.quoteToday, { time }), soon: false };
|
|
124
|
+
if (day(deadline) === day(new Date(now.getTime() + 864e5))) return { text: fill(m.quoteTomorrow, { time }), soon: false };
|
|
125
|
+
const date = new Intl.DateTimeFormat(locale, { day: "numeric", month: "short", hour: "numeric", minute: "2-digit", ...tz }).format(deadline);
|
|
126
|
+
return { text: fill(m.quoteLater, { date }), soon: false };
|
|
127
|
+
}
|
|
128
|
+
var QUOTE_CODES = /* @__PURE__ */ new Set(["quote_expired", "rate_expired", "rate_changed", "rate_unavailable", "rate_not_found", "stale_rate", "invalid_rate"]);
|
|
129
|
+
function isQuoteChange(err) {
|
|
130
|
+
if (!err) return false;
|
|
131
|
+
if (err.code && QUOTE_CODES.has(err.code)) return true;
|
|
132
|
+
if (err.status === 409) return true;
|
|
133
|
+
return err.status === 422 && (err.field === "rate_id" || err.field === "delivery" || err.field === "delivery.rate_id" || !err.field);
|
|
134
|
+
}
|
|
135
|
+
function matchRate(previous, rates) {
|
|
136
|
+
if (!previous) return null;
|
|
137
|
+
const norm = (s) => (s ?? "").trim().toLowerCase();
|
|
138
|
+
const sameCarrier = (r) => norm(r.carrier) === norm(previous.carrier);
|
|
139
|
+
if (previous.service_code) {
|
|
140
|
+
const hit = rates.find((r) => sameCarrier(r) && norm(r.service_code) === norm(previous.service_code));
|
|
141
|
+
if (hit) return hit;
|
|
142
|
+
}
|
|
143
|
+
return rates.find((r) => sameCarrier(r) && norm(r.title) === norm(previous.title)) ?? null;
|
|
144
|
+
}
|
|
145
|
+
var SR_ONLY = { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" };
|
|
146
|
+
var useHydrated = () => useSyncExternalStore(
|
|
147
|
+
() => () => {
|
|
148
|
+
},
|
|
149
|
+
() => true,
|
|
150
|
+
() => false
|
|
151
|
+
);
|
|
152
|
+
function PickupGlyph({ size = 24 }) {
|
|
153
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 32", width: Math.round(size * 1.5), height: size, "aria-hidden": "true", focusable: "false", style: { flex: "none" }, "data-sf-brand-mark": "pickup", children: [
|
|
154
|
+
/* @__PURE__ */ jsx("rect", { x: "0.5", y: "0.5", width: "47", height: "31", rx: "5", fill: "var(--sf-brand-mark-bg, #E7E9EE)", stroke: "var(--sf-brand-mark-ring, rgb(127 127 127 / 0.35))" }),
|
|
155
|
+
/* @__PURE__ */ jsx("path", { d: "M15 13.5 17 9h14l2 4.5M15 13.5h18M15 13.5V23h18v-9.5M21 23v-5h6v5", fill: "none", stroke: "var(--sf-brand-mark-fg, #2B2F38)", strokeWidth: "1.8", strokeLinejoin: "round" })
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
function DeliveryOptions(props) {
|
|
159
|
+
const {
|
|
160
|
+
rates,
|
|
161
|
+
pickup = [],
|
|
162
|
+
name = "delivery_choice",
|
|
163
|
+
rateValue = (r) => `rate:${r.id}`,
|
|
164
|
+
pickupValue = (b) => `pickup:${b.branch_id}`,
|
|
165
|
+
value,
|
|
166
|
+
onValueChange,
|
|
167
|
+
partial = false,
|
|
168
|
+
requoted = false,
|
|
169
|
+
loading = false,
|
|
170
|
+
timeZone,
|
|
171
|
+
marks = true,
|
|
172
|
+
badges: showBadges = true,
|
|
173
|
+
required = true,
|
|
174
|
+
renderAmount,
|
|
175
|
+
classNames: cx = {},
|
|
176
|
+
testId,
|
|
177
|
+
children
|
|
178
|
+
} = props;
|
|
179
|
+
const locale = useResolvedLocale(props.locale);
|
|
180
|
+
const m = useMemo(() => deliveryMessages(locale, props.messages), [locale, props.messages]);
|
|
181
|
+
const hydrated = useHydrated();
|
|
182
|
+
const badges = useMemo(() => showBadges ? rateBadges(rates) : /* @__PURE__ */ new Map(), [rates, showBadges]);
|
|
183
|
+
const firstValue = rates[0] ? rateValue(rates[0]) : null;
|
|
184
|
+
const defaultValue = props.defaultValue === void 0 ? firstValue : props.defaultValue;
|
|
185
|
+
const amount = (money) => Number(money.amount) === 0 ? /* @__PURE__ */ jsx("span", { className: cx.free, "data-sf-delivery-free": true, children: m.free }) : renderAmount ? renderAmount(money) : formatMoney(money, locale);
|
|
186
|
+
const mode = props.quoteNote ?? "always";
|
|
187
|
+
const deadline = mode === "never" ? null : quoteDeadline(rates);
|
|
188
|
+
const note = hydrated && deadline ? quoteNote(deadline, { locale, messages: m, ...timeZone ? { timeZone } : {} }) : null;
|
|
189
|
+
const shownNote = note && (mode === "always" || note.soon) ? note : null;
|
|
190
|
+
const radio = (v) => /* @__PURE__ */ jsx(
|
|
191
|
+
"input",
|
|
192
|
+
{
|
|
193
|
+
type: "radio",
|
|
194
|
+
name,
|
|
195
|
+
value: v,
|
|
196
|
+
required,
|
|
197
|
+
className: cx.radio,
|
|
198
|
+
...value !== void 0 ? { checked: value === v, onChange: () => onValueChange?.(v) } : { defaultChecked: v === defaultValue, ...onValueChange ? { onChange: () => onValueChange(v) } : {} },
|
|
199
|
+
...testId ? { "data-testid": testId } : {}
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
return /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-delivery": true, "aria-busy": loading || void 0, children: [
|
|
203
|
+
partial ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-partial": true, children: m.partial }) : null,
|
|
204
|
+
requoted ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-requoted": true, children: m.requoted }) : null,
|
|
205
|
+
loading ? /* @__PURE__ */ jsx("span", { style: SR_ONLY, role: "status", "data-sf-delivery-loading": true, children: m.loading }) : null,
|
|
206
|
+
children,
|
|
207
|
+
loading && rates.length === 0 ? [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: cx.skeleton, "data-sf-delivery-skeleton": true, "aria-hidden": "true" }, i)) : null,
|
|
208
|
+
rates.map((rate) => {
|
|
209
|
+
const carrier = rateCarrier(rate);
|
|
210
|
+
const service = rateService(rate);
|
|
211
|
+
const eta = rateEta(rate, m);
|
|
212
|
+
const own = badges.get(rate.id) ?? [];
|
|
213
|
+
return /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-loading": loading || void 0, children: [
|
|
214
|
+
radio(rateValue(rate)),
|
|
215
|
+
marks ? /* @__PURE__ */ jsx(BrandMark, { brand: rate.carrier ?? rate.provider ?? rate.title, size: 28, ...cx.mark ? { className: cx.mark } : {} }) : null,
|
|
216
|
+
/* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-delivery-body": true, children: [
|
|
217
|
+
/* @__PURE__ */ jsx("span", { className: cx.title, "data-sf-delivery-title": true, children: carrier ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
218
|
+
/* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: carrier }),
|
|
219
|
+
" ",
|
|
220
|
+
/* @__PURE__ */ jsx("span", { className: cx.service, "data-sf-delivery-service": true, children: service })
|
|
221
|
+
] }) : /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: service }) }),
|
|
222
|
+
eta ? /* @__PURE__ */ jsx("span", { className: cx.eta, "data-sf-delivery-eta": true, children: eta }) : null,
|
|
223
|
+
own.length ? /* @__PURE__ */ jsx("span", { className: cx.badges, "data-sf-delivery-badges": true, children: own.map((b) => /* @__PURE__ */ jsx("span", { className: cx.badge, "data-sf-delivery-badge": b, children: b === "cheapest" ? m.cheapest : m.fastest }, b)) }) : null
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-delivery-price": true, children: amount(rate.cost) })
|
|
226
|
+
] }, rate.id);
|
|
227
|
+
}),
|
|
228
|
+
pickup.map((b) => /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-sf-delivery-pickup": true, children: [
|
|
229
|
+
radio(pickupValue(b)),
|
|
230
|
+
marks ? /* @__PURE__ */ jsx(PickupGlyph, { size: 28 }) : null,
|
|
231
|
+
/* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-delivery-body": true, children: [
|
|
232
|
+
/* @__PURE__ */ jsx("span", { className: cx.title, "data-sf-delivery-title": true, children: /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: m.pickupTitle.replace("{name}", b.name) }) }),
|
|
233
|
+
b.address || b.city ? /* @__PURE__ */ jsx("span", { className: cx.eta, "data-sf-delivery-eta": true, children: [b.address, b.city].filter(Boolean).join(", ") }) : null
|
|
234
|
+
] }),
|
|
235
|
+
/* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-delivery-price": true, children: /* @__PURE__ */ jsx("span", { className: cx.free, "data-sf-delivery-free": true, children: m.free }) })
|
|
236
|
+
] }, b.branch_id)),
|
|
237
|
+
deadline ? /* @__PURE__ */ jsx("p", { className: cx.note, "data-sf-delivery-note": true, "data-soon": shownNote?.soon || void 0, "aria-live": "polite", children: shownNote?.text ?? "" }) : null
|
|
238
|
+
] });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { DeliveryOptions, deliveryMessages, isQuoteChange, matchRate, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService };
|
|
242
|
+
//# sourceMappingURL=checkout.js.map
|
|
243
|
+
//# sourceMappingURL=checkout.js.map
|