@asteby/storefront-react 0.20.0 → 0.22.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/account.cjs CHANGED
@@ -207,7 +207,7 @@ __export(voucher_document_exports, {
207
207
  function esc(s) {
208
208
  return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
209
209
  }
210
- function formatMoney2(m, locale) {
210
+ function formatMoney3(m, locale) {
211
211
  if (!m) return "";
212
212
  try {
213
213
  return new Intl.NumberFormat(locale, { style: "currency", currency: m.currency_code }).format(Number(m.amount));
@@ -248,7 +248,7 @@ function renderVoucherDocument(v, opts = {}) {
248
248
  <meta name="robots" content="noindex"><title>${esc(t.title)}${shop ? ` - ${esc(shop)}` : ""}</title><style>${CSS}</style></head>
249
249
  <body><main>
250
250
  <div class="head"><div><h1>${esc(t.title)}</h1><div class="muted">${subtitle}</div></div>
251
- ${v.amount ? `<div><div class="label" style="text-align:right">${esc(t.total)}</div><div class="amount">${esc(formatMoney2(v.amount, locale))}</div></div>` : ""}</div>
251
+ ${v.amount ? `<div><div class="label" style="text-align:right">${esc(t.total)}</div><div class="amount">${esc(formatMoney3(v.amount, locale))}</div></div>` : ""}</div>
252
252
  ${field(t.reference, v.reference)}${field(t.clabe, v.clabe)}
253
253
  ${svg ? `<div class="bar">${svg}<div class="muted" style="font-family:ui-monospace,monospace">${esc(code)}</div></div>` : ""}
254
254
  ${expires ? `<p><strong>${esc(t.expires)}:</strong> ${esc(expires)}. ${esc(t.expiresNote)}</p>` : ""}
@@ -877,6 +877,11 @@ var ES4 = {
877
877
  "order.view": "Ver pedido",
878
878
  "order.items.one": "{count} art\xEDculo",
879
879
  "order.items.other": "{count} art\xEDculos",
880
+ "order.lines": "Art\xEDculos del pedido",
881
+ "order.line.quantity": "Cantidad {n}",
882
+ "order.line.each": "{price} c/u",
883
+ "order.line.unavailable": "Producto no disponible",
884
+ "order.productsMore": "y {n} m\xE1s",
880
885
  "order.stepOf": "Paso {n} de {total}: {step}",
881
886
  "order.listLabel": "Tus pedidos",
882
887
  "order.loading": "Cargando tus pedidos",
@@ -1039,6 +1044,11 @@ var EN4 = {
1039
1044
  "order.view": "View order",
1040
1045
  "order.items.one": "{count} item",
1041
1046
  "order.items.other": "{count} items",
1047
+ "order.lines": "Items in this order",
1048
+ "order.line.quantity": "Quantity {n}",
1049
+ "order.line.each": "{price} each",
1050
+ "order.line.unavailable": "Product unavailable",
1051
+ "order.productsMore": "and {n} more",
1042
1052
  "order.stepOf": "Step {n} of {total}: {step}",
1043
1053
  "order.listLabel": "Your orders",
1044
1054
  "order.loading": "Loading your orders",
@@ -1198,6 +1208,203 @@ var StorefrontContext = react.createContext(null);
1198
1208
  function useOptionalStorefront() {
1199
1209
  return react.useContext(StorefrontContext);
1200
1210
  }
1211
+ var DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600];
1212
+ var defaultImageLoader = (src, width, height) => {
1213
+ try {
1214
+ const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src);
1215
+ const url = new URL(src, "http://relative.invalid");
1216
+ url.searchParams.set("width", String(width));
1217
+ if (height) url.searchParams.set("height", String(height));
1218
+ return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
1219
+ } catch {
1220
+ return src;
1221
+ }
1222
+ };
1223
+ function parseRatio(ratio) {
1224
+ if (!ratio) return null;
1225
+ const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n));
1226
+ return w && h ? w / h : null;
1227
+ }
1228
+ function Image({
1229
+ data,
1230
+ sizes = "100vw",
1231
+ widths = DEFAULT_IMAGE_WIDTHS,
1232
+ loader = defaultImageLoader,
1233
+ priority = false,
1234
+ aspectRatio,
1235
+ alt,
1236
+ loading,
1237
+ decoding,
1238
+ ...rest
1239
+ }) {
1240
+ const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null);
1241
+ let candidates = [...widths].sort((a, b) => a - b);
1242
+ if (data.width) {
1243
+ const capped = candidates.filter((w) => w <= data.width);
1244
+ candidates = capped.length ? capped : [data.width];
1245
+ }
1246
+ const srcSet = loader ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : void 0)} ${w}w`).join(", ") : void 0;
1247
+ const width = data.width ?? candidates[candidates.length - 1];
1248
+ const height = data.height ?? (ratio && width ? Math.round(width / ratio) : void 0);
1249
+ return /* @__PURE__ */ jsxRuntime.jsx(
1250
+ "img",
1251
+ {
1252
+ src: data.url,
1253
+ alt: alt ?? data.alt ?? "",
1254
+ ...srcSet ? { srcSet, sizes } : {},
1255
+ ...width ? { width } : {},
1256
+ ...height ? { height } : {},
1257
+ loading: loading ?? (priority ? "eager" : "lazy"),
1258
+ decoding: decoding ?? (priority ? "auto" : "async"),
1259
+ ...priority ? { fetchPriority: "high" } : {},
1260
+ ...rest
1261
+ }
1262
+ );
1263
+ }
1264
+ function productInitials(name) {
1265
+ const words = (name ?? "").normalize("NFKC").split(/[\s\-_/.,()]+/).map((w) => Array.from(w).find((c) => /\p{L}|\p{N}/u.test(c)) ?? "").filter(Boolean);
1266
+ if (words.length === 0) return "";
1267
+ return (words.length > 1 ? words[0] + words[1] : words[0]).toLocaleUpperCase();
1268
+ }
1269
+ function ProductThumb({
1270
+ image,
1271
+ name,
1272
+ size = 56,
1273
+ ratio = "1/1",
1274
+ fit = "contain",
1275
+ quantity,
1276
+ decorative = false,
1277
+ zoom = true,
1278
+ priority = false,
1279
+ sizes,
1280
+ loader,
1281
+ className,
1282
+ classNames: cx = {}
1283
+ }) {
1284
+ const url = image?.url || null;
1285
+ const [state, setState] = react.useState(url ? "loading" : "fallback");
1286
+ const box = react.useRef(null);
1287
+ react.useEffect(() => {
1288
+ const img = box.current?.querySelector("img");
1289
+ setState(!url ? "fallback" : img?.complete ? img.naturalWidth > 0 ? "loaded" : "fallback" : "loading");
1290
+ }, [url]);
1291
+ const label = name?.trim() || "";
1292
+ const initials = productInitials(label);
1293
+ const showBadge = typeof quantity === "number" && quantity > 1;
1294
+ const a11y = state === "fallback" && !decorative && label ? { role: "img", "aria-label": label } : { "aria-hidden": decorative || state === "fallback" ? true : void 0 };
1295
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1296
+ "span",
1297
+ {
1298
+ ref: box,
1299
+ className: [cx.root, className].filter(Boolean).join(" ") || void 0,
1300
+ style: { ["--sf-preview-size"]: `${size}px`, ["--sf-preview-ratio"]: ratio.replace(":", "/") },
1301
+ "data-sf-thumb": "",
1302
+ "data-state": state,
1303
+ "data-fit": fit,
1304
+ "data-zoom": zoom || void 0,
1305
+ ...a11y,
1306
+ children: [
1307
+ url && state !== "fallback" ? /* @__PURE__ */ jsxRuntime.jsx(
1308
+ Image,
1309
+ {
1310
+ data: { url, alt: image?.alt ?? null, ...image?.width ? { width: image.width } : {}, ...image?.height ? { height: image.height } : {} },
1311
+ alt: decorative ? "" : image?.alt ?? label,
1312
+ aspectRatio: ratio,
1313
+ sizes: sizes ?? `${size * 2}px`,
1314
+ widths: [Math.round(size), Math.round(size * 2), Math.round(size * 3)],
1315
+ ...loader !== void 0 ? { loader } : {},
1316
+ priority,
1317
+ className: cx.img,
1318
+ onLoad: () => setState("loaded"),
1319
+ onError: () => setState("fallback")
1320
+ }
1321
+ ) : null,
1322
+ state === "fallback" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.fallback, "data-sf-thumb-fallback": "", "aria-hidden": "true", children: initials ? /* @__PURE__ */ jsxRuntime.jsx("span", { "data-sf-thumb-initial": "", children: initials }) : /* @__PURE__ */ jsxRuntime.jsx(PackageGlyph, {}) }) : null,
1323
+ showBadge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.badge, "data-sf-thumb-qty": "", "aria-hidden": "true", children: quantity }) : null
1324
+ ]
1325
+ }
1326
+ );
1327
+ }
1328
+ function PackageGlyph() {
1329
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", width: "45%", height: "45%", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", focusable: "false", children: [
1330
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 8 12 3 3 8v8l9 5 9-5V8Z" }),
1331
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 8 9 5 9-5M12 13v8" })
1332
+ ] });
1333
+ }
1334
+ function ProductPreview({
1335
+ image,
1336
+ name,
1337
+ variant,
1338
+ sku,
1339
+ quantity,
1340
+ unitPrice,
1341
+ total,
1342
+ href,
1343
+ children,
1344
+ size = 64,
1345
+ ratio,
1346
+ fit,
1347
+ zoom,
1348
+ priority,
1349
+ sizes,
1350
+ loader,
1351
+ quantityLabel,
1352
+ unavailableLabel = "Producto no disponible",
1353
+ className,
1354
+ classNames: cx = {}
1355
+ }) {
1356
+ const missing = !name.trim();
1357
+ const title = missing ? unavailableLabel : name;
1358
+ const hasQty = typeof quantity === "number" && Number.isFinite(quantity);
1359
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [cx.root, className].filter(Boolean).join(" ") || void 0, "data-sf-preview": "", "data-missing": missing || void 0, children: [
1360
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.media, "data-sf-preview-media": "", children: /* @__PURE__ */ jsxRuntime.jsx(
1361
+ ProductThumb,
1362
+ {
1363
+ image,
1364
+ name: missing ? null : name,
1365
+ size,
1366
+ decorative: true,
1367
+ ...ratio ? { ratio } : {},
1368
+ ...fit ? { fit } : {},
1369
+ ...zoom !== void 0 ? { zoom } : {},
1370
+ ...priority ? { priority } : {},
1371
+ ...sizes ? { sizes } : {},
1372
+ ...loader !== void 0 ? { loader } : {},
1373
+ ...hasQty ? { quantity } : {},
1374
+ ...cx.thumb ? { classNames: cx.thumb } : {}
1375
+ }
1376
+ ) }),
1377
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.body, "data-sf-preview-body": "", children: [
1378
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.name, "data-sf-preview-name": "", children: href && !missing ? /* @__PURE__ */ jsxRuntime.jsx("a", { href, children: title }) : title }),
1379
+ variant ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.variant, "data-sf-preview-variant": "", children: variant }) : null,
1380
+ sku ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.sku, "data-sf-preview-sku": "", children: [
1381
+ "SKU ",
1382
+ sku
1383
+ ] }) : null,
1384
+ hasQty ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.meta, "data-sf-preview-meta": "", children: [
1385
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.qty, "data-sf-preview-qty": "", children: (quantityLabel ?? "Cantidad {n}").replace("{n}", String(quantity)) }),
1386
+ unitPrice != null && quantity > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.unit, "data-sf-preview-unit": "", children: [
1387
+ " \xB7 ",
1388
+ unitPrice
1389
+ ] }) : null
1390
+ ] }) : null,
1391
+ children
1392
+ ] }),
1393
+ total != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.total, "data-sf-preview-total": "", children: total }) : unitPrice != null && !(hasQty && quantity > 1) ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.price, "data-sf-preview-total": "", children: unitPrice }) : null
1394
+ ] });
1395
+ }
1396
+ function ProductThumbStack({ items, max = 4, size = 44, moreLabel = "y {n} m\xE1s", className }) {
1397
+ if (items.length === 0) return null;
1398
+ const shown = items.length > max ? items.slice(0, max - 1) : items;
1399
+ const rest = items.length - shown.length;
1400
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, "data-sf-thumb-stack": "", style: { ["--sf-preview-size"]: `${size}px` }, children: [
1401
+ shown.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx(ProductThumb, { image: it.image ?? null, name: it.name ?? null, size, zoom: false }, i)),
1402
+ rest > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-sf-thumb-more": "", role: "img", "aria-label": moreLabel.replace("{n}", String(rest)), children: [
1403
+ "+",
1404
+ rest
1405
+ ] }) : null
1406
+ ] });
1407
+ }
1201
1408
 
1202
1409
  // src/locale.ts
1203
1410
  function useResolvedLocale(locale) {
@@ -1276,6 +1483,61 @@ function CopyButton({ value, label, copiedLabel, failedLabel, className, ariaLab
1276
1483
  /* @__PURE__ */ jsxRuntime.jsx("span", { role: "status", "aria-live": "polite", "data-sf-copy-status": true, style: state === "failed" ? void 0 : SR_ONLY, children: state === "copied" ? copiedLabel : state === "failed" ? failedLabel : "" })
1277
1484
  ] });
1278
1485
  }
1486
+ function lineImage(line) {
1487
+ const img = line?.image;
1488
+ if (!img || typeof img !== "object") return null;
1489
+ const url = str(img.url);
1490
+ if (!url) return null;
1491
+ const alt = str(img.alt);
1492
+ const w = Number(img.width);
1493
+ const h = Number(img.height);
1494
+ return { url, alt, ...Number.isFinite(w) && w > 0 ? { width: w } : {}, ...Number.isFinite(h) && h > 0 ? { height: h } : {} };
1495
+ }
1496
+ function validLines(order) {
1497
+ return (Array.isArray(order.lines) ? order.lines : []).filter((l) => !!l && typeof l === "object");
1498
+ }
1499
+ function OrderLines({ order, lineHref, size = 64, label, classNames: cx = {}, ...text2 }) {
1500
+ const { m, locale } = useAccountText(text2);
1501
+ const lines = validLines(order);
1502
+ if (lines.length === 0) return null;
1503
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx.root, "data-sf-order-lines": "", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: cx.list, "aria-label": label ?? m["order.lines"], "data-sf-order-lines-list": "", children: lines.map((l, i) => {
1504
+ const qty = Number.isFinite(l.quantity) && l.quantity > 0 ? l.quantity : 1;
1505
+ const unit = moneyText(l.unit, locale);
1506
+ const variant = str(l.variant_title);
1507
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { className: cx.item, "data-sf-order-line": "", children: /* @__PURE__ */ jsxRuntime.jsx(
1508
+ ProductPreview,
1509
+ {
1510
+ image: lineImage(l),
1511
+ name: str(l.title) ?? "",
1512
+ variant: variant && !/^default( title)?$/i.test(variant) ? variant : null,
1513
+ sku: str(l.sku),
1514
+ quantity: qty,
1515
+ quantityLabel: m["order.line.quantity"],
1516
+ unavailableLabel: m["order.line.unavailable"],
1517
+ unitPrice: unit ? fill(m["order.line.each"], { price: unit }) : null,
1518
+ total: moneyText(l.total, locale),
1519
+ href: lineHref?.(l) ?? null,
1520
+ size,
1521
+ ...cx.preview ? { classNames: cx.preview } : {}
1522
+ }
1523
+ ) }, `${str(l.sku) ?? ""}-${i}`);
1524
+ }) }) });
1525
+ }
1526
+ function OrderProductsStack({ order, size = 44, max = 4, className, ...text2 }) {
1527
+ const { m } = useAccountText(text2);
1528
+ const lines = validLines(order);
1529
+ if (lines.length === 0) return null;
1530
+ return /* @__PURE__ */ jsxRuntime.jsx(
1531
+ ProductThumbStack,
1532
+ {
1533
+ items: lines.map((l) => ({ image: lineImage(l), name: str(l.title) })),
1534
+ size,
1535
+ max,
1536
+ moreLabel: m["order.productsMore"],
1537
+ ...className ? { className } : {}
1538
+ }
1539
+ );
1540
+ }
1279
1541
  var STEP_STATES = /* @__PURE__ */ new Set(["done", "current", "pending", "problem"]);
1280
1542
  var stepState = (s) => typeof s === "string" && STEP_STATES.has(s) ? s : "pending";
1281
1543
  var GLYPH = { done: "\u2713", current: "\u25CF", pending: "\u25CB", problem: "!" };
@@ -1375,6 +1637,7 @@ function OrderCard({ order, orderHref, actionHref, onAction, headingLevel = 3, c
1375
1637
  plural(m, "order.items", items)
1376
1638
  ] }) : null
1377
1639
  ] }),
1640
+ Array.isArray(order.lines) && order.lines.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cx.products, "data-sf-order-products": true, children: /* @__PURE__ */ jsxRuntime.jsx(OrderProductsStack, { order, ...text2 }) }) : null,
1378
1641
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx.summary, "data-sf-order-summary": true, children: [
1379
1642
  /* @__PURE__ */ jsxRuntime.jsx(OrderProgress, { progress, variant: "compact", ...text2, ...cx.progress ? { classNames: cx.progress } : {} }),
1380
1643
  showShipment ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: cx.shipment, "data-sf-order-shipment": true, children: [
@@ -1912,13 +2175,18 @@ exports.Barcode = Barcode;
1912
2175
  exports.InvoiceList = InvoiceList;
1913
2176
  exports.NextActionBar = NextActionBar;
1914
2177
  exports.OrderCard = OrderCard;
2178
+ exports.OrderLines = OrderLines;
1915
2179
  exports.OrderList = OrderList;
2180
+ exports.OrderProductsStack = OrderProductsStack;
1916
2181
  exports.OrderProgress = OrderProgress;
1917
2182
  exports.OrderStatusBadge = OrderStatusBadge;
1918
2183
  exports.PaymentSummary = PaymentSummary;
1919
2184
  exports.PendingPaymentCard = PendingPaymentCard;
1920
2185
  exports.PendingPaymentList = PendingPaymentList;
1921
2186
  exports.PostPurchaseAccountPrompt = PostPurchaseAccountPrompt;
2187
+ exports.ProductPreview = ProductPreview;
2188
+ exports.ProductThumb = ProductThumb;
2189
+ exports.ProductThumbStack = ProductThumbStack;
1922
2190
  exports.RefundList = RefundList;
1923
2191
  exports.ShipmentCard = ShipmentCard;
1924
2192
  exports.accountAuthMessages = accountAuthMessages;
@@ -1927,7 +2195,9 @@ exports.barcodeGeometry = barcodeGeometry;
1927
2195
  exports.barcodeValue = barcodeValue;
1928
2196
  exports.code128Modules = code128Modules;
1929
2197
  exports.enumText = enumText;
2198
+ exports.lineImage = lineImage;
1930
2199
  exports.postPurchaseAccountPromptMessages = postPurchaseAccountPromptMessages;
2200
+ exports.productInitials = productInitials;
1931
2201
  exports.stageTone = stageTone;
1932
2202
  exports.useCustomerOrder = useCustomerOrder;
1933
2203
  exports.useCustomerOrders = useCustomerOrders;