@asteby/storefront-react 0.11.0 → 0.12.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/dist/checkout.js CHANGED
@@ -1,13 +1,571 @@
1
- import { brandName, BrandMark } from './chunk-3ZJTDFHE.js';
2
- export { BrandMark, brandKey, brandLogo, brandName, findBrand, listBrands } from './chunk-3ZJTDFHE.js';
1
+ import { brandName, BrandMark, brandKey } from './chunk-ODIQR6TG.js';
2
+ export { BrandMark, brandKey, brandLogo, brandName, findBrand, listBrands } from './chunk-ODIQR6TG.js';
3
3
  import { useResolvedLocale } from './chunk-T52H5HL3.js';
4
4
  import './chunk-DQBL2DFJ.js';
5
- import { useMemo, useSyncExternalStore } from 'react';
6
- import { formatMoney } from '@asteby/storefront';
5
+ import { useRef, useState, useEffect, useId, useMemo, useSyncExternalStore } from 'react';
7
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
+ import { formatMoney } from '@asteby/storefront';
8
8
 
9
- // src/checkout/delivery.ts
10
9
  var ES = {
10
+ valueMissing: "Completa este campo.",
11
+ choiceMissing: "Elige una opci\xF3n.",
12
+ email: "Escribe un correo v\xE1lido, por ejemplo nombre@correo.com.",
13
+ tel: "Escribe un tel\xE9fono v\xE1lido.",
14
+ pattern: "Revisa el formato.",
15
+ tooShort: "Escribe al menos {n} caracteres.",
16
+ server: "Revisa este campo.",
17
+ summaryOne: "Revisa 1 campo para continuar",
18
+ summaryMany: "Revisa {n} campos para continuar",
19
+ optional: "(opcional)"
20
+ };
21
+ var EN = {
22
+ valueMissing: "Fill in this field.",
23
+ choiceMissing: "Choose an option.",
24
+ email: "Enter a valid email, like name@example.com.",
25
+ tel: "Enter a valid phone number.",
26
+ pattern: "Check the format.",
27
+ tooShort: "Enter at least {n} characters.",
28
+ server: "Check this field.",
29
+ summaryOne: "Check 1 field to continue",
30
+ summaryMany: "Check {n} fields to continue",
31
+ optional: "(optional)"
32
+ };
33
+ function fieldMessages(locale, overrides) {
34
+ const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN : ES;
35
+ return overrides ? { ...base, ...overrides } : base;
36
+ }
37
+ var isControl = (el) => !!el && typeof el.tagName === "string" && ["INPUT", "SELECT", "TEXTAREA"].includes(el.tagName) && !!el.name;
38
+ function validationMessage(el, m = ES) {
39
+ const v = el.validity;
40
+ if (!v || v.valid) return null;
41
+ const own = el.getAttribute("data-sf-error");
42
+ if (own) return own;
43
+ if (v.valueMissing) return el instanceof HTMLInputElement && (el.type === "radio" || el.type === "checkbox") ? m.choiceMissing : m.valueMissing;
44
+ if (v.typeMismatch && el instanceof HTMLInputElement) return el.type === "email" ? m.email : el.type === "tel" ? m.tel : m.pattern;
45
+ if (v.patternMismatch) return el.getAttribute("title") || (el instanceof HTMLInputElement && el.type === "tel" ? m.tel : m.pattern);
46
+ if (v.tooShort && el instanceof HTMLInputElement) return m.tooShort.replace("{n}", String(el.minLength));
47
+ return el.validationMessage || m.pattern;
48
+ }
49
+ function labelOf(el) {
50
+ if (el instanceof HTMLInputElement && el.type === "radio") {
51
+ const legend = el.closest("fieldset")?.querySelector("legend");
52
+ if (legend?.textContent) return legend.textContent.trim();
53
+ }
54
+ const text = el.labels?.[0]?.getAttribute("data-sf-label") ?? el.labels?.[0]?.textContent ?? el.getAttribute("aria-label") ?? el.name;
55
+ return text.trim();
56
+ }
57
+ function useFieldErrors(formRef, options = {}) {
58
+ const m = fieldMessages(options.locale, options.messages);
59
+ const live = useRef(m);
60
+ live.current = m;
61
+ const server = [...options.serverFields ?? []];
62
+ const serverKey = server.join("|");
63
+ const [errors, setErrors] = useState(() => Object.fromEntries(server.map((n) => [n, options.serverMessages?.[n] ?? m.server])));
64
+ const [summary, setSummary] = useState(null);
65
+ const [summaryKey, setSummaryKey] = useState(0);
66
+ useEffect(() => {
67
+ if (!server.length) return;
68
+ const form = formRef.current;
69
+ const items = [];
70
+ const next = {};
71
+ for (const name of server) {
72
+ const msg = options.serverMessages?.[name] ?? live.current.server;
73
+ next[name] = msg;
74
+ const el = form?.elements.namedItem(name);
75
+ const ctl = el && "length" in el && !(el instanceof HTMLSelectElement) ? el[0] : el;
76
+ if (ctl) items.push({ name, id: ctl.id || name, label: labelOf(ctl), message: msg });
77
+ }
78
+ setErrors((e) => ({ ...e, ...next }));
79
+ if (items.length) {
80
+ setSummary(items);
81
+ setSummaryKey((k) => k + 1);
82
+ }
83
+ }, [serverKey]);
84
+ useEffect(() => {
85
+ const form = formRef.current;
86
+ if (!form) return;
87
+ let batch = null;
88
+ const set = (name, msg) => setErrors((e) => {
89
+ if (msg === null) {
90
+ if (!(name in e)) return e;
91
+ const { [name]: _gone, ...rest } = e;
92
+ return rest;
93
+ }
94
+ return e[name] === msg ? e : { ...e, [name]: msg };
95
+ });
96
+ const onBlur = (ev) => {
97
+ const el = ev.target;
98
+ if (!isControl(el) || el instanceof HTMLInputElement && (el.type === "radio" || el.type === "checkbox" || el.type === "hidden")) return;
99
+ if (!el.value) return;
100
+ set(el.name, validationMessage(el, live.current));
101
+ };
102
+ const onInput = (ev) => {
103
+ const el = ev.target;
104
+ if (!isControl(el)) return;
105
+ setErrors((e) => {
106
+ if (!(el.name in e)) return e;
107
+ const msg = el.validity.valid ? null : validationMessage(el, live.current);
108
+ if (msg !== null) return e;
109
+ const { [el.name]: _gone, ...rest } = e;
110
+ return rest;
111
+ });
112
+ };
113
+ const onInvalid = (ev) => {
114
+ const el = ev.target;
115
+ if (!isControl(el)) return;
116
+ ev.preventDefault();
117
+ if (!batch) {
118
+ batch = [];
119
+ queueMicrotask(() => {
120
+ const list = batch ?? [];
121
+ batch = null;
122
+ const seen = /* @__PURE__ */ new Set();
123
+ const items = [];
124
+ const next = {};
125
+ for (const c of list) {
126
+ if (seen.has(c.name)) continue;
127
+ seen.add(c.name);
128
+ const msg = validationMessage(c, live.current) ?? live.current.valueMissing;
129
+ next[c.name] = msg;
130
+ items.push({ name: c.name, id: c.id || c.name, label: labelOf(c), message: msg });
131
+ }
132
+ setErrors((e) => ({ ...e, ...next }));
133
+ setSummary(items);
134
+ setSummaryKey((k) => k + 1);
135
+ });
136
+ }
137
+ batch.push(el);
138
+ };
139
+ form.addEventListener("focusout", onBlur);
140
+ form.addEventListener("input", onInput);
141
+ form.addEventListener("change", onInput);
142
+ form.addEventListener("invalid", onInvalid, true);
143
+ return () => {
144
+ form.removeEventListener("focusout", onBlur);
145
+ form.removeEventListener("input", onInput);
146
+ form.removeEventListener("change", onInput);
147
+ form.removeEventListener("invalid", onInvalid, true);
148
+ };
149
+ }, [formRef]);
150
+ const shown = summary ? summary.filter((s) => s.name in errors).map((s) => ({ ...s, message: errors[s.name] })) : null;
151
+ return {
152
+ errors,
153
+ summary: shown && shown.length ? shown : null,
154
+ summaryKey,
155
+ clear: () => {
156
+ setErrors({});
157
+ setSummary(null);
158
+ }
159
+ };
160
+ }
161
+ function ErrorSummary({ items, focusKey, locale, messages, title, className, classNames: cx = {} }) {
162
+ const ref = useRef(null);
163
+ const m = fieldMessages(locale, messages);
164
+ const has = !!items && items.length > 0;
165
+ useEffect(() => {
166
+ if (has) ref.current?.focus();
167
+ }, [focusKey]);
168
+ if (!has) return null;
169
+ const n = items.length;
170
+ return /* @__PURE__ */ jsxs("div", { ref, role: "alert", tabIndex: -1, className, "data-sf-error-summary": true, "aria-labelledby": "sf-error-summary-title", children: [
171
+ /* @__PURE__ */ jsx("p", { id: "sf-error-summary-title", className: cx.title, "data-sf-error-summary-title": true, children: title ?? (n === 1 ? m.summaryOne : m.summaryMany.replace("{n}", String(n))) }),
172
+ /* @__PURE__ */ jsx("ul", { className: cx.list, children: items.map((it) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
173
+ "a",
174
+ {
175
+ href: `#${it.id}`,
176
+ className: cx.link,
177
+ onClick: (e) => {
178
+ const el = document.getElementById(it.id);
179
+ if (!el) return;
180
+ e.preventDefault();
181
+ el.focus();
182
+ el.scrollIntoView?.({ block: "center" });
183
+ },
184
+ children: [
185
+ it.label,
186
+ ": ",
187
+ it.message
188
+ ]
189
+ }
190
+ ) }, it.name)) })
191
+ ] });
192
+ }
193
+ function CheckoutField(props) {
194
+ const { name, label, id = name, error, hint, optional = false, classNames: cx = {}, locale, messages, ...rest } = props;
195
+ const m = fieldMessages(locale, messages);
196
+ const hintId = hint ? `${id}-hint` : void 0;
197
+ const errorId = `${id}-error`;
198
+ const describedBy = [hintId, error ? errorId : void 0].filter(Boolean).join(" ") || void 0;
199
+ const common = {
200
+ id,
201
+ name,
202
+ required: !optional,
203
+ "aria-invalid": error ? true : void 0,
204
+ "aria-describedby": describedBy,
205
+ className: cx.input,
206
+ "data-sf-field-control": ""
207
+ };
208
+ return /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-field": true, "data-invalid": error ? "" : void 0, children: [
209
+ /* @__PURE__ */ jsxs("label", { htmlFor: id, className: cx.label, "data-sf-label": label, children: [
210
+ label,
211
+ optional ? /* @__PURE__ */ jsxs("span", { className: cx.optional, "data-sf-field-optional": true, children: [
212
+ " ",
213
+ m.optional
214
+ ] }) : null
215
+ ] }),
216
+ hint ? /* @__PURE__ */ jsx("p", { id: hintId, className: cx.hint, "data-sf-field-hint": true, children: hint }) : null,
217
+ rest.as === "select" ? (() => {
218
+ const { as: _as, children, ...select } = rest;
219
+ return /* @__PURE__ */ jsx("select", { ...select, ...common, children });
220
+ })() : (() => {
221
+ const { as: _as, ...input } = rest;
222
+ return /* @__PURE__ */ jsx("input", { ...input, ...common });
223
+ })(),
224
+ error ? /* @__PURE__ */ jsx("p", { id: errorId, className: cx.error, "data-sf-field-error": true, children: error }) : null
225
+ ] });
226
+ }
227
+ var ES2 = {
228
+ email: "Correo",
229
+ phone: "Tel\xE9fono",
230
+ first_name: "Nombre",
231
+ last_name: "Apellidos",
232
+ zip: "C\xF3digo postal",
233
+ province: "Estado",
234
+ city: "Ciudad o municipio",
235
+ neighborhood: "Colonia",
236
+ address1: "Calle y n\xFAmero",
237
+ address2: "N\xFAmero interior o departamento",
238
+ reference: "Referencias para la entrega",
239
+ provincePlaceholder: "Elige tu estado",
240
+ address1Hint: "Por ejemplo: Av. Insurgentes Sur 1234",
241
+ referenceHint: "Entre qu\xE9 calles, color de la fachada\u2026",
242
+ emailHint: "Te enviamos aqu\xED la confirmaci\xF3n y el seguimiento.",
243
+ phoneHint: "Solo para la paqueter\xEDa, por si necesita contactarte.",
244
+ lookupFilled: "Completamos estado y ciudad con tu c\xF3digo postal."
245
+ };
246
+ var EN2 = {
247
+ email: "Email",
248
+ phone: "Phone",
249
+ first_name: "First name",
250
+ last_name: "Last name",
251
+ zip: "Postal code",
252
+ province: "State",
253
+ city: "City",
254
+ neighborhood: "Neighborhood",
255
+ address1: "Street and number",
256
+ address2: "Apartment, suite",
257
+ reference: "Delivery notes",
258
+ provincePlaceholder: "Choose your state",
259
+ address1Hint: "For example: 1234 Main St",
260
+ referenceHint: "Cross streets, building color\u2026",
261
+ emailHint: "We send the confirmation and tracking here.",
262
+ phoneHint: "Only for the carrier, in case they need to reach you.",
263
+ lookupFilled: "We filled in state and city from your postal code."
264
+ };
265
+ function addressFieldsMessages(locale, overrides) {
266
+ const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN2 : ES2;
267
+ return overrides ? { ...base, ...overrides } : base;
268
+ }
269
+ var join = (...c) => c.filter(Boolean).join(" ") || void 0;
270
+ var field = (cx, span) => ({ ...cx, root: join(cx.root, cx[span]) });
271
+ function ContactFields({ defaults = {}, names = {}, errors = {}, phone, hints = true, locale, messages, fieldMessages: fm, classNames: cx = {} }) {
272
+ const m = addressFieldsMessages(locale, messages);
273
+ const n = (k) => names[k] ?? k;
274
+ const common = { ...locale ? { locale } : {}, ...fm ? { messages: fm } : {} };
275
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
276
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("email"), label: m.email, type: "email", autoComplete: "email", inputMode: "email", autoCapitalize: "none", spellCheck: false, defaultValue: defaults.email ?? "", error: errors[n("email")] ?? null, hint: hints ? m.emailHint : void 0, classNames: field(cx, "wide") }),
277
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("phone"), label: m.phone, type: "tel", autoComplete: "tel", inputMode: "tel", defaultValue: defaults.phone ?? "", error: errors[n("phone")] ?? null, hint: hints ? m.phoneHint : void 0, classNames: field(cx, "wide"), ...phone?.pattern ? { pattern: phone.pattern } : {}, ...phone?.title ? { title: phone.title } : {}, ...phone?.minLength ? { minLength: phone.minLength } : {} }),
278
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("first_name"), label: m.first_name, autoComplete: "given-name", defaultValue: defaults.first_name ?? "", error: errors[n("first_name")] ?? null, classNames: field(cx, "half") }),
279
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("last_name"), label: m.last_name, autoComplete: "family-name", defaultValue: defaults.last_name ?? "", error: errors[n("last_name")] ?? null, classNames: field(cx, "half") })
280
+ ] });
281
+ }
282
+ function AddressFields(props) {
283
+ const { defaults, names = {}, errors = {}, countryCode, provinces, postalCode, lookupPostalCode, locale, messages, fieldMessages: fm, classNames: cx = {}, children } = props;
284
+ const showNeighborhood = props.neighborhood ?? true;
285
+ const showReference = props.reference ?? true;
286
+ const m = addressFieldsMessages(locale, messages);
287
+ const n = (k) => names[k] ?? k;
288
+ const common = { ...locale ? { locale } : {}, ...fm ? { messages: fm } : {} };
289
+ const listId = useId();
290
+ const [colonias, setColonias] = useState([]);
291
+ const [filled, setFilled] = useState(false);
292
+ const lastZip = useRef(null);
293
+ const zipRef = useRef(null);
294
+ const lookupRef = useRef(lookupPostalCode);
295
+ lookupRef.current = lookupPostalCode;
296
+ const runLookup = (zip) => {
297
+ const lookup = lookupRef.current;
298
+ if (!lookup || !zip || zip === lastZip.current) return;
299
+ if (postalCode?.pattern && !new RegExp(`^(?:${postalCode.pattern})$`).test(zip)) return;
300
+ lastZip.current = zip;
301
+ const form = zipRef.current?.closest("form");
302
+ void lookup(zip).then(
303
+ (info) => {
304
+ if (!info || lastZip.current !== zip) return;
305
+ const fill2 = (name, value) => {
306
+ const el = form?.elements.namedItem(name);
307
+ if (!el || !value || "length" in el && !(el instanceof HTMLSelectElement)) return false;
308
+ if (el.value && el.dataset.sfAutofilled !== "1") return false;
309
+ if (el instanceof HTMLSelectElement && ![...el.options].some((o) => o.value === value)) return false;
310
+ el.value = value;
311
+ el.dataset.sfAutofilled = "1";
312
+ el.dispatchEvent(new Event("input", { bubbles: true }));
313
+ return true;
314
+ };
315
+ const a = fill2(n("province"), info.province);
316
+ const b = fill2(n("city"), info.city);
317
+ setColonias(info.neighborhoods ?? []);
318
+ setFilled(a || b);
319
+ },
320
+ () => {
321
+ }
322
+ );
323
+ };
324
+ useEffect(() => {
325
+ const zip = defaults?.zip;
326
+ if (zip) runLookup(zip);
327
+ }, []);
328
+ const numericZip = postalCode?.inputMode === "numeric";
329
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
330
+ /* @__PURE__ */ jsx("div", { ref: zipRef, className: join(cx.half), "data-sf-zip": true, children: /* @__PURE__ */ jsx(
331
+ CheckoutField,
332
+ {
333
+ ...common,
334
+ name: n("zip"),
335
+ label: m.zip,
336
+ autoComplete: "postal-code",
337
+ inputMode: postalCode?.inputMode ?? "text",
338
+ ...postalCode?.pattern ? { pattern: postalCode.pattern } : {},
339
+ ...postalCode?.maxLength ? { maxLength: postalCode.maxLength } : {},
340
+ ...postalCode?.title ? { title: postalCode.title } : {},
341
+ ...numericZip ? { autoCapitalize: "none" } : {},
342
+ defaultValue: defaults?.zip ?? "",
343
+ error: errors[n("zip")] ?? null,
344
+ classNames: { ...cx, root: cx.root },
345
+ onInput: (e) => {
346
+ const v = e.currentTarget.value.trim();
347
+ if (postalCode?.maxLength && v.length === postalCode.maxLength) runLookup(v);
348
+ },
349
+ onBlur: (e) => runLookup(e.currentTarget.value.trim())
350
+ }
351
+ ) }),
352
+ provinces && provinces.length ? /* @__PURE__ */ jsxs(CheckoutField, { ...common, as: "select", name: n("province"), label: m.province, autoComplete: "address-level1", defaultValue: defaults?.province ?? "", error: errors[n("province")] ?? null, classNames: field(cx, "half"), children: [
353
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: m.provincePlaceholder }),
354
+ provinces.map((p) => /* @__PURE__ */ jsx("option", { value: p.name, children: p.name }, p.code ?? p.name))
355
+ ] }) : /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("province"), label: m.province, autoComplete: "address-level1", defaultValue: defaults?.province ?? "", error: errors[n("province")] ?? null, classNames: field(cx, "half") }),
356
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("city"), label: m.city, autoComplete: "address-level2", defaultValue: defaults?.city ?? "", error: errors[n("city")] ?? null, classNames: field(cx, "half") }),
357
+ showNeighborhood ? /* @__PURE__ */ jsx(
358
+ CheckoutField,
359
+ {
360
+ ...common,
361
+ name: n("neighborhood"),
362
+ label: m.neighborhood,
363
+ autoComplete: "address-level3",
364
+ optional: showNeighborhood !== "required",
365
+ defaultValue: defaults?.neighborhood ?? "",
366
+ error: errors[n("neighborhood")] ?? null,
367
+ classNames: field(cx, "half"),
368
+ ...colonias.length ? { list: listId } : {}
369
+ }
370
+ ) : null,
371
+ colonias.length ? /* @__PURE__ */ jsx("datalist", { id: listId, children: colonias.map((c) => /* @__PURE__ */ jsx("option", { value: c }, c)) }) : null,
372
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-live": "polite", "data-sf-zip-status": true, style: filled ? void 0 : { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, className: filled ? join(cx.hint, cx.wide) : void 0, children: filled ? m.lookupFilled : "" }),
373
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("address1"), label: m.address1, autoComplete: "address-line1", defaultValue: defaults?.address1 ?? "", error: errors[n("address1")] ?? null, hint: m.address1Hint, classNames: field(cx, "wide") }),
374
+ /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("address2"), label: m.address2, autoComplete: "address-line2", optional: true, defaultValue: defaults?.address2 ?? "", error: errors[n("address2")] ?? null, classNames: field(cx, "half") }),
375
+ showReference ? /* @__PURE__ */ jsx(CheckoutField, { ...common, name: n("reference"), label: m.reference, optional: true, defaultValue: defaults?.reference ?? "", error: errors[n("reference")] ?? null, hint: m.referenceHint, classNames: field(cx, "wide"), maxLength: 200 }) : null,
376
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "country_code", value: countryCode }),
377
+ children
378
+ ] });
379
+ }
380
+ var ES3 = {
381
+ title: "Tu pedido",
382
+ subtotal: "Subtotal",
383
+ discount: "Descuento",
384
+ shipping: "Env\xEDo",
385
+ shippingPending: "Al elegir la entrega",
386
+ updating: "Actualizando\u2026",
387
+ free: "Gratis",
388
+ taxes: "Impuestos",
389
+ taxesIncluded: "Incluye {amount} de impuestos",
390
+ total: "Total",
391
+ quantity: "Cantidad: {n}",
392
+ show: "Ver resumen del pedido",
393
+ hide: "Ocultar resumen del pedido"
394
+ };
395
+ var EN3 = {
396
+ title: "Your order",
397
+ subtotal: "Subtotal",
398
+ discount: "Discount",
399
+ shipping: "Shipping",
400
+ shippingPending: "Calculated at delivery",
401
+ updating: "Updating\u2026",
402
+ free: "Free",
403
+ taxes: "Taxes",
404
+ taxesIncluded: "Includes {amount} in taxes",
405
+ total: "Total",
406
+ quantity: "Quantity: {n}",
407
+ show: "Show order summary",
408
+ hide: "Hide order summary"
409
+ };
410
+ function orderSummaryMessages(locale, overrides) {
411
+ const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN3 : ES3;
412
+ return overrides ? { ...base, ...overrides } : base;
413
+ }
414
+ var isZero = (m) => !!m && Number(m.amount) === 0;
415
+ var positive = (m) => !!m && Number(m.amount) > 0;
416
+ function OrderSummary(props) {
417
+ const { lines, cost, updating = false, renderLineMedia, renderAmount, title, headingLevel = 2, classNames: cx = {}, children } = props;
418
+ const locale = useResolvedLocale(props.locale);
419
+ const m = orderSummaryMessages(locale, props.messages);
420
+ const money = (v) => renderAmount ? renderAmount(v) : formatMoney(v, locale);
421
+ const H = headingLevel === 3 ? "h3" : "h2";
422
+ const discounts = cost.discounts?.filter((d) => positive(d.amount)) ?? [];
423
+ return /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-summary": true, children: [
424
+ title === false ? null : /* @__PURE__ */ jsx(H, { className: cx.title, "data-sf-summary-title": true, children: title ?? m.title }),
425
+ /* @__PURE__ */ jsx("ul", { className: cx.lines, "data-sf-summary-lines": true, children: lines.map((l) => {
426
+ const variant = l.merchandise.title && !/^default( title)?$/i.test(l.merchandise.title) ? l.merchandise.title : null;
427
+ return /* @__PURE__ */ jsxs("li", { className: cx.line, "data-sf-summary-line": true, children: [
428
+ renderLineMedia ? /* @__PURE__ */ jsxs("span", { className: cx.media, "data-sf-summary-media": true, children: [
429
+ renderLineMedia(l),
430
+ l.quantity > 1 ? /* @__PURE__ */ jsx("span", { className: cx.qty, "data-sf-summary-qty": true, "aria-hidden": "true", children: l.quantity }) : null
431
+ ] }) : null,
432
+ /* @__PURE__ */ jsxs("span", { "data-sf-summary-line-body": true, children: [
433
+ /* @__PURE__ */ jsxs("span", { className: cx.lineTitle, "data-sf-summary-line-title": true, children: [
434
+ !renderLineMedia && l.quantity > 1 ? `${l.quantity} \xD7 ` : null,
435
+ l.merchandise.product.title
436
+ ] }),
437
+ variant ? /* @__PURE__ */ jsx("span", { className: cx.variant, "data-sf-summary-variant": true, children: variant }) : null,
438
+ renderLineMedia || l.quantity > 1 ? /* @__PURE__ */ jsx("span", { style: { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" }, children: m.quantity.replace("{n}", String(l.quantity)) }) : null
439
+ ] }),
440
+ /* @__PURE__ */ jsx("span", { className: cx.linePrice, "data-sf-summary-line-price": true, children: money(l.cost.total) })
441
+ ] }, l.id);
442
+ }) }),
443
+ children,
444
+ /* @__PURE__ */ jsxs("dl", { className: cx.totals, "data-sf-summary-totals": true, "aria-busy": updating || void 0, children: [
445
+ /* @__PURE__ */ jsxs("div", { className: cx.row, "data-sf-summary-row": "subtotal", children: [
446
+ /* @__PURE__ */ jsx("dt", { children: m.subtotal }),
447
+ /* @__PURE__ */ jsx("dd", { children: money(cost.subtotal) })
448
+ ] }),
449
+ discounts.length ? discounts.map((d) => /* @__PURE__ */ jsxs("div", { className: cx.row ? `${cx.row} ${cx.discount ?? ""}`.trim() : cx.discount, "data-sf-summary-row": "discount", children: [
450
+ /* @__PURE__ */ jsx("dt", { children: d.code ? `${m.discount} (${d.code})` : d.title || m.discount }),
451
+ /* @__PURE__ */ jsxs("dd", { children: [
452
+ "\u2212",
453
+ money(d.amount)
454
+ ] })
455
+ ] }, d.id)) : positive(cost.discount) ? /* @__PURE__ */ jsxs("div", { className: cx.row ? `${cx.row} ${cx.discount ?? ""}`.trim() : cx.discount, "data-sf-summary-row": "discount", children: [
456
+ /* @__PURE__ */ jsx("dt", { children: m.discount }),
457
+ /* @__PURE__ */ jsxs("dd", { children: [
458
+ "\u2212",
459
+ money(cost.discount)
460
+ ] })
461
+ ] }) : null,
462
+ /* @__PURE__ */ jsxs("div", { className: cx.row, "data-sf-summary-row": "shipping", children: [
463
+ /* @__PURE__ */ jsx("dt", { children: m.shipping }),
464
+ /* @__PURE__ */ jsx("dd", { className: updating || !cost.shipping ? cx.muted : void 0, children: updating ? m.updating : !cost.shipping ? m.shippingPending : isZero(cost.shipping) ? /* @__PURE__ */ jsx("span", { className: cx.free, children: m.free }) : money(cost.shipping) })
465
+ ] }),
466
+ positive(cost.tax) && !cost.tax_included ? /* @__PURE__ */ jsxs("div", { className: cx.row, "data-sf-summary-row": "tax", children: [
467
+ /* @__PURE__ */ jsx("dt", { children: m.taxes }),
468
+ /* @__PURE__ */ jsx("dd", { children: money(cost.tax) })
469
+ ] }) : null,
470
+ /* @__PURE__ */ jsxs("div", { className: cx.total, "data-sf-summary-row": "total", children: [
471
+ /* @__PURE__ */ jsx("dt", { children: m.total }),
472
+ /* @__PURE__ */ jsx("dd", { "aria-live": "polite", "data-updating": updating || void 0, children: money(cost.total) })
473
+ ] })
474
+ ] }),
475
+ positive(cost.tax) && cost.tax_included ? /* @__PURE__ */ jsx("p", { className: cx.note, "data-sf-summary-tax-note": true, children: m.taxesIncluded.split("{amount}").flatMap((part, i) => i === 0 ? [part] : [/* @__PURE__ */ jsx("span", { children: money(cost.tax) }, i), part]) }) : null
476
+ ] });
477
+ }
478
+ function OrderSummaryDisclosure({ total, updating, renderAmount, locale: loc, messages, defaultOpen = false, className, classNames: cx = {}, children }) {
479
+ const locale = useResolvedLocale(loc);
480
+ const m = orderSummaryMessages(locale, messages);
481
+ return /* @__PURE__ */ jsxs("details", { className, "data-sf-summary-disclosure": true, ...defaultOpen ? { open: true } : {}, children: [
482
+ /* @__PURE__ */ jsxs("summary", { className: cx.summary, "data-sf-summary-toggle": true, children: [
483
+ /* @__PURE__ */ jsxs("span", { className: cx.label, "data-sf-summary-toggle-label": true, children: [
484
+ /* @__PURE__ */ jsx("span", { "data-sf-summary-show": true, children: m.show }),
485
+ /* @__PURE__ */ jsx("span", { "data-sf-summary-hide": true, children: m.hide })
486
+ ] }),
487
+ /* @__PURE__ */ jsx("span", { className: cx.total, "data-sf-summary-toggle-total": true, "data-updating": updating || void 0, children: renderAmount ? renderAmount(total) : formatMoney(total, locale) })
488
+ ] }),
489
+ /* @__PURE__ */ jsx("div", { className: cx.panel, "data-sf-summary-panel": true, children })
490
+ ] });
491
+ }
492
+
493
+ // src/checkout/recommend.ts
494
+ var costOf = (r) => {
495
+ const n = Number(r.cost.amount);
496
+ return Number.isFinite(n) ? n : null;
497
+ };
498
+ var daysOf = (r) => r.eta_days_max ?? r.eta_days_min ?? null;
499
+ var norm = (s) => (s ?? "").trim().toLowerCase();
500
+ var carrierKey = (r) => r.carrier ? brandKey(r.carrier) : r.provider ? brandKey(r.provider) : "";
501
+ function dedupeRates(rates) {
502
+ const best = /* @__PURE__ */ new Map();
503
+ const keyOf = (r) => `${carrierKey(r)}|${norm(r.service_code) || norm(r.title)}|${daysOf(r) ?? ""}|${r.cost.currency_code}`;
504
+ for (const r of rates) {
505
+ const k = keyOf(r);
506
+ const prev = best.get(k);
507
+ const c = costOf(r);
508
+ if (!prev || c !== null && (costOf(prev) === null || c < costOf(prev))) best.set(k, r);
509
+ }
510
+ const keep = new Set(best.values());
511
+ return rates.filter((r) => keep.has(r));
512
+ }
513
+ function recommendRates(input, options = {}) {
514
+ const count = Math.max(1, options.count ?? 3);
515
+ const rates = options.dedupe === false ? [...input] : dedupeRates(input);
516
+ const collapseAfter = options.collapseAfter ?? count + 1;
517
+ const sameCurrency = new Set(rates.map((r) => r.cost.currency_code)).size <= 1;
518
+ const byCost = (a, b) => (costOf(a) ?? Infinity) - (costOf(b) ?? Infinity);
519
+ const byDays = (a, b) => (daysOf(a) ?? Infinity) - (daysOf(b) ?? Infinity);
520
+ const tags = /* @__PURE__ */ new Map();
521
+ const tag = (r, t) => tags.set(r, [...tags.get(r) ?? [], t]);
522
+ const cheapest = sameCurrency && rates.length > 1 ? [...rates].sort((a, b) => byCost(a, b) || byDays(a, b))[0] : void 0;
523
+ if (cheapest && costOf(cheapest) !== null) tag(cheapest, "cheapest");
524
+ const fastest = rates.length > 1 ? [...rates].sort((a, b) => byDays(a, b) || byCost(a, b))[0] : void 0;
525
+ if (fastest && daysOf(fastest) !== null && (!cheapest || fastest === cheapest || (daysOf(cheapest) ?? Infinity) > daysOf(fastest))) tag(fastest, "fastest");
526
+ const chosen = [];
527
+ for (const r of [cheapest, fastest]) if (r && tags.has(r) && !chosen.includes(r)) chosen.push(r);
528
+ if (rates.length > collapseAfter) {
529
+ const rank = (sorted, val) => {
530
+ const out = /* @__PURE__ */ new Map();
531
+ let pos = 0;
532
+ let last;
533
+ sorted.forEach((r, i) => {
534
+ const v = val(r);
535
+ if (v !== last) pos = i;
536
+ last = v;
537
+ out.set(r, v === null ? sorted.length : pos);
538
+ });
539
+ return out;
540
+ };
541
+ const priceRank = rank([...rates].sort(byCost), costOf);
542
+ const timeRank = rank([...rates].sort(byDays), daysOf);
543
+ const rest = rates.filter((r) => !chosen.includes(r)).sort((a, b) => priceRank.get(a) + timeRank.get(a) - (priceRank.get(b) + timeRank.get(b)) || byCost(a, b));
544
+ let first = true;
545
+ for (const r of rest) {
546
+ if (chosen.length >= count) break;
547
+ chosen.push(r);
548
+ if (first) tag(r, "best_value");
549
+ first = false;
550
+ }
551
+ } else {
552
+ for (const r of rates) if (!chosen.includes(r)) chosen.push(r);
553
+ }
554
+ const recommended = chosen.sort(byCost).map((rate) => ({ rate, tags: tags.get(rate) ?? [] }));
555
+ const groups = /* @__PURE__ */ new Map();
556
+ for (const r of [...rates].sort(byCost)) {
557
+ if (chosen.includes(r)) continue;
558
+ const key = carrierKey(r);
559
+ const g = groups.get(key) ?? { key, carrier: r.carrier ?? r.provider ?? null, rates: [] };
560
+ g.rates.push(r);
561
+ groups.set(key, g);
562
+ }
563
+ const more = [...groups.values()];
564
+ return { recommended, more, moreCount: more.reduce((n, g) => n + g.rates.length, 0) };
565
+ }
566
+
567
+ // src/checkout/delivery.ts
568
+ var ES4 = {
11
569
  etaOne: "Llega en 1 d\xEDa h\xE1bil",
12
570
  etaMany: "Llega en {n} d\xEDas h\xE1biles",
13
571
  etaRange: "Llega en {min} a {max} d\xEDas h\xE1biles",
@@ -21,11 +579,16 @@ var ES = {
21
579
  quoteTomorrow: "Precios de env\xEDo vigentes hasta ma\xF1ana a las {time}.",
22
580
  quoteLater: "Precios de env\xEDo vigentes hasta el {date}.",
23
581
  quoteSoon: "Los precios de env\xEDo vencen pronto ({time}).",
24
- partial: "No pudimos consultar todas las paqueter\xEDas. Estas son las opciones disponibles ahora.",
582
+ partial: "Algunas paqueter\xEDas no respondieron a tiempo; estas son las opciones disponibles.",
25
583
  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."
584
+ requoted: "Las tarifas de env\xEDo cambiaron y ya est\xE1n actualizadas. Revisa tu env\xEDo antes de pagar.",
585
+ bestValue: "Recomendado",
586
+ moreOptions: "Ver m\xE1s opciones ({n})",
587
+ otherCarriers: "Otras opciones",
588
+ error: "No pudimos consultar las opciones de env\xEDo en este momento.",
589
+ retry: "Intentar de nuevo"
27
590
  };
28
- var EN = {
591
+ var EN4 = {
29
592
  etaOne: "Arrives in 1 business day",
30
593
  etaMany: "Arrives in {n} business days",
31
594
  etaRange: "Arrives in {min} to {max} business days",
@@ -41,10 +604,15 @@ var EN = {
41
604
  quoteSoon: "Shipping prices expire soon ({time}).",
42
605
  partial: "We couldn't reach every carrier. These are the options available now.",
43
606
  loading: "Looking for delivery options\u2026",
44
- requoted: "Shipping rates changed and are now up to date. Check your delivery before paying."
607
+ requoted: "Shipping rates changed and are now up to date. Check your delivery before paying.",
608
+ bestValue: "Recommended",
609
+ moreOptions: "Show more options ({n})",
610
+ otherCarriers: "Other options",
611
+ error: "We couldn't get delivery options right now.",
612
+ retry: "Try again"
45
613
  };
46
614
  function deliveryMessages(locale, overrides) {
47
- const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN : ES;
615
+ const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN4 : ES4;
48
616
  return overrides ? { ...base, ...overrides } : base;
49
617
  }
50
618
  var fill = (tpl, vars) => tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? "")).replace(/\.\.$/, ".");
@@ -64,7 +632,7 @@ function rateService(rate) {
64
632
  }
65
633
  return title;
66
634
  }
67
- function rateEta(rate, messages = ES) {
635
+ function rateEta(rate, messages = ES4) {
68
636
  const { eta_days_min: min, eta_days_max: max } = rate;
69
637
  if (typeof min === "number" && typeof max === "number") {
70
638
  if (min === max) return min === 1 ? messages.etaOne : fill(messages.etaMany, { n: min });
@@ -74,11 +642,11 @@ function rateEta(rate, messages = ES) {
74
642
  if (typeof min === "number") return min === 1 ? messages.etaOne : fill(messages.etaFrom, { min });
75
643
  return rate.eta ?? null;
76
644
  }
77
- var costOf = (r) => {
645
+ var costOf2 = (r) => {
78
646
  const n = Number(r.cost.amount);
79
647
  return Number.isFinite(n) ? n : null;
80
648
  };
81
- var daysOf = (r) => r.eta_days_max ?? r.eta_days_min ?? null;
649
+ var daysOf2 = (r) => r.eta_days_max ?? r.eta_days_min ?? null;
82
650
  function rateBadges(rates) {
83
651
  const out = /* @__PURE__ */ new Map();
84
652
  if (rates.length < 2) return out;
@@ -92,10 +660,10 @@ function rateBadges(rates) {
92
660
  };
93
661
  const currencies = new Set(rates.map((r) => r.cost.currency_code));
94
662
  if (currencies.size === 1) {
95
- const id = unique(rates.map((r) => [r.id, costOf(r)]));
663
+ const id = unique(rates.map((r) => [r.id, costOf2(r)]));
96
664
  if (id) add(id, "cheapest");
97
665
  }
98
- const fast = unique(rates.map((r) => [r.id, daysOf(r)]));
666
+ const fast = unique(rates.map((r) => [r.id, daysOf2(r)]));
99
667
  if (fast) add(fast, "fastest");
100
668
  return out;
101
669
  }
@@ -114,7 +682,7 @@ function quoteNote(deadline, opts = {}) {
114
682
  const now = opts.now ?? /* @__PURE__ */ new Date();
115
683
  const left = deadline.getTime() - now.getTime();
116
684
  if (left <= 0) return null;
117
- const m = opts.messages ?? ES;
685
+ const m = opts.messages ?? ES4;
118
686
  const locale = opts.locale ?? "es-MX";
119
687
  const tz = opts.timeZone ? { timeZone: opts.timeZone } : {};
120
688
  const time = new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", ...tz }).format(deadline);
@@ -134,13 +702,13 @@ function isQuoteChange(err) {
134
702
  }
135
703
  function matchRate(previous, rates) {
136
704
  if (!previous) return null;
137
- const norm = (s) => (s ?? "").trim().toLowerCase();
138
- const sameCarrier = (r) => norm(r.carrier) === norm(previous.carrier);
705
+ const norm2 = (s) => (s ?? "").trim().toLowerCase();
706
+ const sameCarrier = (r) => norm2(r.carrier) === norm2(previous.carrier);
139
707
  if (previous.service_code) {
140
- const hit = rates.find((r) => sameCarrier(r) && norm(r.service_code) === norm(previous.service_code));
708
+ const hit = rates.find((r) => sameCarrier(r) && norm2(r.service_code) === norm2(previous.service_code));
141
709
  if (hit) return hit;
142
710
  }
143
- return rates.find((r) => sameCarrier(r) && norm(r.title) === norm(previous.title)) ?? null;
711
+ return rates.find((r) => sameCarrier(r) && norm2(r.title) === norm2(previous.title)) ?? null;
144
712
  }
145
713
  var SR_ONLY = { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" };
146
714
  var useHydrated = () => useSyncExternalStore(
@@ -174,7 +742,10 @@ function DeliveryOptions(props) {
174
742
  renderAmount,
175
743
  classNames: cx = {},
176
744
  testId,
177
- children
745
+ children,
746
+ layout = "list",
747
+ error,
748
+ onRetry
178
749
  } = props;
179
750
  const locale = useResolvedLocale(props.locale);
180
751
  const m = useMemo(() => deliveryMessages(locale, props.messages), [locale, props.messages]);
@@ -187,7 +758,13 @@ function DeliveryOptions(props) {
187
758
  const deadline = mode === "never" ? null : quoteDeadline(rates);
188
759
  const note = hydrated && deadline ? quoteNote(deadline, { locale, messages: m, ...timeZone ? { timeZone } : {} }) : null;
189
760
  const shownNote = note && (mode === "always" || note.soon) ? note : null;
190
- const radio = (v) => /* @__PURE__ */ jsx(
761
+ const current = value ?? defaultValue ?? null;
762
+ const plan = useMemo(
763
+ () => layout === "recommended" ? recommendRates(rates, { count: props.recommendedCount ?? 3 }) : null,
764
+ [layout, rates, props.recommendedCount]
765
+ );
766
+ const inMore = plan ? plan.more.some((g) => g.rates.some((r) => rateValue(r) === current)) : false;
767
+ const radio = (v, describedBy) => /* @__PURE__ */ jsx(
191
768
  "input",
192
769
  {
193
770
  type: "radio",
@@ -195,36 +772,66 @@ function DeliveryOptions(props) {
195
772
  value: v,
196
773
  required,
197
774
  className: cx.radio,
775
+ ...{},
198
776
  ...value !== void 0 ? { checked: value === v, onChange: () => onValueChange?.(v) } : { defaultChecked: v === defaultValue, ...onValueChange ? { onChange: () => onValueChange(v) } : {} },
199
777
  ...testId ? { "data-testid": testId } : {}
200
778
  }
201
779
  );
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,
780
+ const tagText = (t) => t === "cheapest" ? m.cheapest : t === "fastest" ? m.fastest : m.bestValue;
781
+ const rateRow = (rate, opts) => {
782
+ const carrier = rateCarrier(rate);
783
+ const service = rateService(rate);
784
+ const eta = rateEta(rate, m);
785
+ return /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-loading": loading || void 0, "data-grouped": opts.grouped || void 0, children: [
786
+ radio(rateValue(rate)),
787
+ marks && !opts.grouped ? (
788
+ // Fixed slot: logos of different widths do not push the text column out of line.
789
+ /* @__PURE__ */ jsx("span", { "data-sf-delivery-mark": true, "aria-hidden": "true", children: /* @__PURE__ */ jsx(BrandMark, { brand: rate.carrier ?? rate.provider ?? rate.title, size: 28, ...cx.mark ? { className: cx.mark } : {} }) })
790
+ ) : null,
791
+ /* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-delivery-body": true, children: [
792
+ /* @__PURE__ */ jsx("span", { className: cx.title, "data-sf-delivery-title": true, children: carrier ? opts.grouped ? /* @__PURE__ */ jsxs(Fragment, { children: [
793
+ /* @__PURE__ */ jsxs("span", { style: SR_ONLY, children: [
794
+ carrier,
795
+ " "
796
+ ] }),
797
+ /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, "data-sf-delivery-service-only": true, children: service })
798
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
799
+ /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: carrier }),
800
+ " ",
801
+ /* @__PURE__ */ jsx("span", { className: cx.service, "data-sf-delivery-service": true, children: service })
802
+ ] }) : /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: service }) }),
803
+ eta ? /* @__PURE__ */ jsx("span", { className: cx.eta, "data-sf-delivery-eta": true, children: eta }) : null,
804
+ opts.tags.length ? /* @__PURE__ */ jsx("span", { className: cx.badges, "data-sf-delivery-badges": true, children: opts.tags.map((b) => /* @__PURE__ */ jsx("span", { className: cx.badge, "data-sf-delivery-badge": b, children: tagText(b) }, b)) }) : null
805
+ ] }),
806
+ /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-delivery-price": true, children: amount(rate.cost) })
807
+ ] }, rate.id);
808
+ };
809
+ const errorText = error ? typeof error === "string" ? error : m.error : null;
810
+ return /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-delivery": true, "data-layout": layout, "aria-busy": loading || void 0, children: [
811
+ partial && !errorText ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-partial": true, children: m.partial }) : null,
204
812
  requoted ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-requoted": true, children: m.requoted }) : null,
205
813
  loading ? /* @__PURE__ */ jsx("span", { style: SR_ONLY, role: "status", "data-sf-delivery-loading": true, children: m.loading }) : null,
814
+ errorText && rates.length === 0 && !loading ? /* @__PURE__ */ jsxs("div", { role: "alert", className: cx.error, "data-sf-delivery-error": true, children: [
815
+ /* @__PURE__ */ jsx("p", { children: errorText }),
816
+ onRetry ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.retry, onClick: onRetry, "data-sf-delivery-retry": true, children: m.retry }) : null
817
+ ] }) : null,
206
818
  children,
207
819
  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
- }),
820
+ plan ? plan.recommended.map(({ rate, tags }) => rateRow(rate, { tags: showBadges ? tags : [] })) : rates.map((rate) => rateRow(rate, { tags: badges.get(rate.id) ?? [] })),
821
+ plan && plan.moreCount > 0 ? /* @__PURE__ */ jsxs("details", { className: cx.more, "data-sf-delivery-more": true, ...inMore ? { open: true } : {}, children: [
822
+ /* @__PURE__ */ jsx("summary", { className: cx.moreSummary, "data-sf-delivery-more-summary": true, children: m.moreOptions.replace("{n}", String(plan.moreCount)) }),
823
+ /* @__PURE__ */ jsx("div", { "data-sf-delivery-groups": true, children: plan.more.map((g) => {
824
+ const carrierName = g.carrier ? rateCarrier({ carrier: g.carrier }) : null;
825
+ const id = `${name}-group-${g.key || "other"}`;
826
+ return /* @__PURE__ */ jsxs("div", { role: "group", "aria-labelledby": id, className: cx.group, "data-sf-delivery-group": g.key || "other", children: [
827
+ /* @__PURE__ */ jsxs("p", { id, className: cx.groupTitle, "data-sf-delivery-group-title": true, children: [
828
+ marks && g.carrier ? /* @__PURE__ */ jsx(BrandMark, { brand: g.carrier, size: 22 }) : null,
829
+ /* @__PURE__ */ jsx("span", { children: carrierName ?? m.otherCarriers })
830
+ ] }),
831
+ g.rates.map((rate) => rateRow(rate, { tags: [], grouped: Boolean(carrierName) }))
832
+ ] }, g.key || "other");
833
+ }) })
834
+ ] }) : null,
228
835
  pickup.map((b) => /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-sf-delivery-pickup": true, children: [
229
836
  radio(pickupValue(b)),
230
837
  marks ? /* @__PURE__ */ jsx(PickupGlyph, { size: 28 }) : null,
@@ -238,6 +845,6 @@ function DeliveryOptions(props) {
238
845
  ] });
239
846
  }
240
847
 
241
- export { DeliveryOptions, deliveryMessages, isQuoteChange, matchRate, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService };
848
+ export { AddressFields, CheckoutField, ContactFields, DeliveryOptions, ErrorSummary, OrderSummary, OrderSummaryDisclosure, addressFieldsMessages, dedupeRates, deliveryMessages, fieldMessages, isQuoteChange, matchRate, orderSummaryMessages, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService, recommendRates, useFieldErrors, validationMessage };
242
849
  //# sourceMappingURL=checkout.js.map
243
850
  //# sourceMappingURL=checkout.js.map