@asteby/storefront-react 0.19.0 → 0.21.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 +41 -0
- package/dist/account-server.cjs +405 -7
- package/dist/account-server.cjs.map +1 -1
- package/dist/account-server.d.cts +41 -1
- package/dist/account-server.d.ts +41 -1
- package/dist/account-server.js +73 -3
- package/dist/account-server.js.map +1 -1
- package/dist/account.cjs +1663 -4
- package/dist/account.cjs.map +1 -1
- package/dist/account.css +362 -0
- package/dist/account.d.cts +499 -3
- package/dist/account.d.ts +499 -3
- package/dist/account.js +1102 -2
- package/dist/account.js.map +1 -1
- package/dist/cart.cjs +125 -2
- package/dist/cart.cjs.map +1 -1
- package/dist/cart.css +145 -2
- package/dist/cart.js +4 -2
- package/dist/cart.js.map +1 -1
- package/dist/checkout.cjs +129 -1
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.css +142 -0
- package/dist/checkout.d.cts +3 -1
- package/dist/checkout.d.ts +3 -1
- package/dist/checkout.js +10 -3
- package/dist/checkout.js.map +1 -1
- package/dist/chunk-PPQEUTXJ.js +260 -0
- package/dist/chunk-PPQEUTXJ.js.map +1 -0
- package/dist/chunk-TXEYBDFG.js +182 -0
- package/dist/chunk-TXEYBDFG.js.map +1 -0
- package/dist/chunk-VHEY5AU3.js +152 -0
- package/dist/chunk-VHEY5AU3.js.map +1 -0
- package/dist/fiscal.d.cts +34 -34
- package/dist/fiscal.d.ts +34 -34
- package/dist/fiscal.js +4 -258
- package/dist/fiscal.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/payments.css +15 -14
- package/dist/payments.js +2 -2
- package/dist/product-preview-DRtLWGLS.d.cts +85 -0
- package/dist/product-preview-DaICr4f2.d.ts +85 -0
- package/dist/ui.cjs +202 -0
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.css +142 -0
- package/dist/ui.d.cts +3 -0
- package/dist/ui.d.ts +3 -0
- package/dist/ui.js +3 -1
- package/dist/ui.js.map +1 -1
- package/dist/voucher-document-SFWJCLTN.js +124 -0
- package/dist/voucher-document-SFWJCLTN.js.map +1 -0
- package/package.json +2 -2
package/dist/payments.css
CHANGED
|
@@ -89,16 +89,20 @@
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/*
|
|
92
|
-
* 3DS challenge (`<PaymentChallenge>`): a
|
|
92
|
+
* 3DS challenge (`<PaymentChallenge>`): a square-ish centered modal (36rem wide, max min(90dvh, 40rem) tall). Colors are fixed on purpose: the bank page inside
|
|
93
93
|
* the iframe is always white, and the dialog often lives in a themed (dark) panel. Override with a more specific rule.
|
|
94
94
|
*/
|
|
95
95
|
dialog[data-sf-challenge] {
|
|
96
96
|
margin: auto;
|
|
97
97
|
box-sizing: border-box;
|
|
98
|
-
|
|
98
|
+
inset: 0;
|
|
99
|
+
width: min(36rem, 100% - 2rem);
|
|
99
100
|
max-width: none;
|
|
100
|
-
max-height:
|
|
101
|
-
|
|
101
|
+
max-height: min(90dvh, 40rem);
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-direction: column;
|
|
104
|
+
/* Solo el cuerpo (el iframe) scrollea; el diálogo no. */
|
|
105
|
+
overflow: hidden;
|
|
102
106
|
border: 1px solid #c9d2ee;
|
|
103
107
|
border-radius: 0.75rem;
|
|
104
108
|
background: #ffffff;
|
|
@@ -120,7 +124,10 @@ dialog[data-sf-challenge] h2:focus {
|
|
|
120
124
|
}
|
|
121
125
|
dialog[data-sf-challenge] [data-sf-challenge-frame] {
|
|
122
126
|
display: block;
|
|
123
|
-
|
|
127
|
+
flex: 1 1 auto;
|
|
128
|
+
min-height: 16rem;
|
|
129
|
+
width: 100%;
|
|
130
|
+
overflow: auto;
|
|
124
131
|
background: #ffffff;
|
|
125
132
|
}
|
|
126
133
|
dialog[data-sf-challenge] [data-sf-challenge-escape] {
|
|
@@ -139,16 +146,10 @@ dialog[data-sf-challenge] [data-sf-challenge-escape] button {
|
|
|
139
146
|
border-radius: 0.5rem;
|
|
140
147
|
font-weight: 600;
|
|
141
148
|
}
|
|
142
|
-
@media (max-width:
|
|
149
|
+
@media (max-width: 30rem) {
|
|
143
150
|
dialog[data-sf-challenge] {
|
|
144
|
-
width: 100
|
|
145
|
-
height: 100dvh;
|
|
146
|
-
max-height: 100dvh;
|
|
147
|
-
border-radius: 0;
|
|
148
|
-
border: 0;
|
|
151
|
+
width: calc(100% - 2rem);
|
|
152
|
+
max-height: calc(100dvh - 2rem);
|
|
149
153
|
padding: 1rem;
|
|
150
154
|
}
|
|
151
|
-
dialog[data-sf-challenge] [data-sf-challenge-frame] {
|
|
152
|
-
height: calc(100dvh - 8rem);
|
|
153
|
-
}
|
|
154
155
|
}
|
package/dist/payments.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isSafeCssValue } from './chunk-672OTUM2.js';
|
|
2
2
|
import { useModalDialog } from './chunk-ZBH7JYU4.js';
|
|
3
3
|
import { findBrand, BrandMark } from './chunk-JNUJGPJC.js';
|
|
4
|
-
import { useResolvedLocale } from './chunk-T52H5HL3.js';
|
|
5
|
-
import './chunk-DQBL2DFJ.js';
|
|
6
4
|
import { paymentRecoveryMessages } from './chunk-MNLROALF.js';
|
|
7
5
|
export { buildPayChoices, cashProviderData, checkoutToAttempt, choiceValue, parseChoice, payErrorResponse, paymentCsp, paymentCspHeaderName, paymentCspHeaders, paymentCspMode, paymentNonce, paymentNonceFromHeaders, paymentRecoveryMessages, safeHttpStatus, voucherRows } from './chunk-MNLROALF.js';
|
|
6
|
+
import { useResolvedLocale } from './chunk-T52H5HL3.js';
|
|
7
|
+
import './chunk-DQBL2DFJ.js';
|
|
8
8
|
import { forwardRef, useImperativeHandle, useRef, useEffect, useMemo, useId, useState, useCallback, useSyncExternalStore } from 'react';
|
|
9
9
|
import { paymentMessages, formatMoney, generateIdempotencyKey, paymentOutcome, paymentErrorView, formatVoucherExpiry, classifyPaymentError } from '@asteby/storefront';
|
|
10
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { a as ImageLoader } from './components-B5XhSH-m.cjs';
|
|
4
|
+
|
|
5
|
+
/** First letter(s) of the name for the fallback: two initials from two words, one otherwise. Empty when nothing usable. */
|
|
6
|
+
declare function productInitials(name: string | null | undefined): string;
|
|
7
|
+
type ProductThumbClassNames = Partial<Record<'root' | 'img' | 'fallback' | 'badge', string>>;
|
|
8
|
+
type ProductThumbImage = {
|
|
9
|
+
url: string;
|
|
10
|
+
alt?: string | null;
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
};
|
|
14
|
+
type ProductThumbProps = {
|
|
15
|
+
/** Picture of the product or variant. `null`/missing: shows the fallback. */
|
|
16
|
+
image?: ProductThumbImage | null;
|
|
17
|
+
/** Name of the product: alt of the picture (unless `decorative`) and source of the fallback initial. */
|
|
18
|
+
name?: string | null;
|
|
19
|
+
/** Side in px. Default 56. */
|
|
20
|
+
size?: number;
|
|
21
|
+
/** Aspect ratio of the box, `w/h`. Default `1/1`. */
|
|
22
|
+
ratio?: string;
|
|
23
|
+
/** `contain` (default) never crops; `cover` fills the box. */
|
|
24
|
+
fit?: 'contain' | 'cover';
|
|
25
|
+
/** Quantity badge, shown when greater than 1. */
|
|
26
|
+
quantity?: number;
|
|
27
|
+
/** The name sits next to it as text: the picture has `alt=""` and the fallback is hidden from screen readers. Default false. */
|
|
28
|
+
decorative?: boolean;
|
|
29
|
+
/** Slight zoom on hover (honours `prefers-reduced-motion`). Default true. */
|
|
30
|
+
zoom?: boolean;
|
|
31
|
+
/** Above the fold: eager load. */
|
|
32
|
+
priority?: boolean;
|
|
33
|
+
sizes?: string;
|
|
34
|
+
loader?: ImageLoader | null;
|
|
35
|
+
className?: string;
|
|
36
|
+
classNames?: ProductThumbClassNames;
|
|
37
|
+
};
|
|
38
|
+
/** The picture of a product with fixed ratio, shimmer, lazy loading and a fallback that always looks designed. */
|
|
39
|
+
declare function ProductThumb({ image, name, size, ratio, fit, quantity, decorative, zoom, priority, sizes, loader, className, classNames: cx, }: ProductThumbProps): react.JSX.Element;
|
|
40
|
+
type ProductPreviewClassNames = Partial<Record<'root' | 'media' | 'body' | 'name' | 'variant' | 'meta' | 'sku' | 'qty' | 'price' | 'total' | 'unit', string>> & {
|
|
41
|
+
thumb?: ProductThumbClassNames;
|
|
42
|
+
};
|
|
43
|
+
type ProductPreviewProps = Pick<ProductThumbProps, 'image' | 'size' | 'ratio' | 'fit' | 'zoom' | 'priority' | 'sizes' | 'loader'> & {
|
|
44
|
+
name: string;
|
|
45
|
+
/** "Rojo / L". Omit for default variants. */
|
|
46
|
+
variant?: string | null;
|
|
47
|
+
sku?: string | null;
|
|
48
|
+
/** Quantity: a badge on the picture (> 1) and "Cantidad N" under the name. */
|
|
49
|
+
quantity?: number;
|
|
50
|
+
/** Unit price, already formatted. Shown next to the quantity when it is greater than 1. */
|
|
51
|
+
unitPrice?: ReactNode;
|
|
52
|
+
/** Line total, already formatted: aligned right, the strongest number. */
|
|
53
|
+
total?: ReactNode;
|
|
54
|
+
/** Link on the name (product page). */
|
|
55
|
+
href?: string | null;
|
|
56
|
+
/** Rendered under the text (status, note, actions). */
|
|
57
|
+
children?: ReactNode;
|
|
58
|
+
/** Text of the quantity for screen readers and the meta line. Default `Cantidad {n}`. */
|
|
59
|
+
quantityLabel?: string;
|
|
60
|
+
/** Name shown when the line has none (product deleted). Default `Producto no disponible`. */
|
|
61
|
+
unavailableLabel?: string;
|
|
62
|
+
className?: string;
|
|
63
|
+
classNames?: ProductPreviewClassNames;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A product line: picture, name (2 lines), variant, SKU, quantity and prices with a clear hierarchy. Degrades to a
|
|
67
|
+
* designed placeholder when the product has no image or no longer exists (empty name).
|
|
68
|
+
*/
|
|
69
|
+
declare function ProductPreview({ image, name, variant, sku, quantity, unitPrice, total, href, children, size, ratio, fit, zoom, priority, sizes, loader, quantityLabel, unavailableLabel, className, classNames: cx, }: ProductPreviewProps): react.JSX.Element;
|
|
70
|
+
type ProductThumbStackProps = {
|
|
71
|
+
items: ReadonlyArray<{
|
|
72
|
+
image?: ProductThumbImage | null;
|
|
73
|
+
name?: string | null;
|
|
74
|
+
}>;
|
|
75
|
+
/** Thumbs shown before "+N". Default 4. */
|
|
76
|
+
max?: number;
|
|
77
|
+
size?: number;
|
|
78
|
+
/** Screen reader text for the hidden remainder, `{n}` = how many. Default `y {n} más`. */
|
|
79
|
+
moreLabel?: string;
|
|
80
|
+
className?: string;
|
|
81
|
+
};
|
|
82
|
+
/** Overlapped thumbs with a "+N" counter: the products of an order at a glance. */
|
|
83
|
+
declare function ProductThumbStack({ items, max, size, moreLabel, className }: ProductThumbStackProps): react.JSX.Element | null;
|
|
84
|
+
|
|
85
|
+
export { ProductPreview as P, type ProductPreviewClassNames as a, type ProductPreviewProps as b, ProductThumb as c, type ProductThumbClassNames as d, type ProductThumbImage as e, type ProductThumbProps as f, ProductThumbStack as g, type ProductThumbStackProps as h, productInitials as p };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { a as ImageLoader } from './components-B5XhSH-m.js';
|
|
4
|
+
|
|
5
|
+
/** First letter(s) of the name for the fallback: two initials from two words, one otherwise. Empty when nothing usable. */
|
|
6
|
+
declare function productInitials(name: string | null | undefined): string;
|
|
7
|
+
type ProductThumbClassNames = Partial<Record<'root' | 'img' | 'fallback' | 'badge', string>>;
|
|
8
|
+
type ProductThumbImage = {
|
|
9
|
+
url: string;
|
|
10
|
+
alt?: string | null;
|
|
11
|
+
width?: number;
|
|
12
|
+
height?: number;
|
|
13
|
+
};
|
|
14
|
+
type ProductThumbProps = {
|
|
15
|
+
/** Picture of the product or variant. `null`/missing: shows the fallback. */
|
|
16
|
+
image?: ProductThumbImage | null;
|
|
17
|
+
/** Name of the product: alt of the picture (unless `decorative`) and source of the fallback initial. */
|
|
18
|
+
name?: string | null;
|
|
19
|
+
/** Side in px. Default 56. */
|
|
20
|
+
size?: number;
|
|
21
|
+
/** Aspect ratio of the box, `w/h`. Default `1/1`. */
|
|
22
|
+
ratio?: string;
|
|
23
|
+
/** `contain` (default) never crops; `cover` fills the box. */
|
|
24
|
+
fit?: 'contain' | 'cover';
|
|
25
|
+
/** Quantity badge, shown when greater than 1. */
|
|
26
|
+
quantity?: number;
|
|
27
|
+
/** The name sits next to it as text: the picture has `alt=""` and the fallback is hidden from screen readers. Default false. */
|
|
28
|
+
decorative?: boolean;
|
|
29
|
+
/** Slight zoom on hover (honours `prefers-reduced-motion`). Default true. */
|
|
30
|
+
zoom?: boolean;
|
|
31
|
+
/** Above the fold: eager load. */
|
|
32
|
+
priority?: boolean;
|
|
33
|
+
sizes?: string;
|
|
34
|
+
loader?: ImageLoader | null;
|
|
35
|
+
className?: string;
|
|
36
|
+
classNames?: ProductThumbClassNames;
|
|
37
|
+
};
|
|
38
|
+
/** The picture of a product with fixed ratio, shimmer, lazy loading and a fallback that always looks designed. */
|
|
39
|
+
declare function ProductThumb({ image, name, size, ratio, fit, quantity, decorative, zoom, priority, sizes, loader, className, classNames: cx, }: ProductThumbProps): react.JSX.Element;
|
|
40
|
+
type ProductPreviewClassNames = Partial<Record<'root' | 'media' | 'body' | 'name' | 'variant' | 'meta' | 'sku' | 'qty' | 'price' | 'total' | 'unit', string>> & {
|
|
41
|
+
thumb?: ProductThumbClassNames;
|
|
42
|
+
};
|
|
43
|
+
type ProductPreviewProps = Pick<ProductThumbProps, 'image' | 'size' | 'ratio' | 'fit' | 'zoom' | 'priority' | 'sizes' | 'loader'> & {
|
|
44
|
+
name: string;
|
|
45
|
+
/** "Rojo / L". Omit for default variants. */
|
|
46
|
+
variant?: string | null;
|
|
47
|
+
sku?: string | null;
|
|
48
|
+
/** Quantity: a badge on the picture (> 1) and "Cantidad N" under the name. */
|
|
49
|
+
quantity?: number;
|
|
50
|
+
/** Unit price, already formatted. Shown next to the quantity when it is greater than 1. */
|
|
51
|
+
unitPrice?: ReactNode;
|
|
52
|
+
/** Line total, already formatted: aligned right, the strongest number. */
|
|
53
|
+
total?: ReactNode;
|
|
54
|
+
/** Link on the name (product page). */
|
|
55
|
+
href?: string | null;
|
|
56
|
+
/** Rendered under the text (status, note, actions). */
|
|
57
|
+
children?: ReactNode;
|
|
58
|
+
/** Text of the quantity for screen readers and the meta line. Default `Cantidad {n}`. */
|
|
59
|
+
quantityLabel?: string;
|
|
60
|
+
/** Name shown when the line has none (product deleted). Default `Producto no disponible`. */
|
|
61
|
+
unavailableLabel?: string;
|
|
62
|
+
className?: string;
|
|
63
|
+
classNames?: ProductPreviewClassNames;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A product line: picture, name (2 lines), variant, SKU, quantity and prices with a clear hierarchy. Degrades to a
|
|
67
|
+
* designed placeholder when the product has no image or no longer exists (empty name).
|
|
68
|
+
*/
|
|
69
|
+
declare function ProductPreview({ image, name, variant, sku, quantity, unitPrice, total, href, children, size, ratio, fit, zoom, priority, sizes, loader, quantityLabel, unavailableLabel, className, classNames: cx, }: ProductPreviewProps): react.JSX.Element;
|
|
70
|
+
type ProductThumbStackProps = {
|
|
71
|
+
items: ReadonlyArray<{
|
|
72
|
+
image?: ProductThumbImage | null;
|
|
73
|
+
name?: string | null;
|
|
74
|
+
}>;
|
|
75
|
+
/** Thumbs shown before "+N". Default 4. */
|
|
76
|
+
max?: number;
|
|
77
|
+
size?: number;
|
|
78
|
+
/** Screen reader text for the hidden remainder, `{n}` = how many. Default `y {n} más`. */
|
|
79
|
+
moreLabel?: string;
|
|
80
|
+
className?: string;
|
|
81
|
+
};
|
|
82
|
+
/** Overlapped thumbs with a "+N" counter: the products of an order at a glance. */
|
|
83
|
+
declare function ProductThumbStack({ items, max, size, moreLabel, className }: ProductThumbStackProps): react.JSX.Element | null;
|
|
84
|
+
|
|
85
|
+
export { ProductPreview as P, type ProductPreviewClassNames as a, type ProductPreviewProps as b, ProductThumb as c, type ProductThumbClassNames as d, type ProductThumbImage as e, type ProductThumbProps as f, ProductThumbStack as g, type ProductThumbStackProps as h, productInitials as p };
|
package/dist/ui.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
require('@asteby/storefront');
|
|
5
6
|
|
|
6
7
|
// src/ui/select.tsx
|
|
7
8
|
var Chevron = ({ open = false, floating = false }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", "data-select-chevron": floating ? "floating" : "", "data-open": open || void 0, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 9 6 6 6-6" }) });
|
|
@@ -371,8 +372,208 @@ function InstallApp({ className = "", label = "Instalar app", iosHint = "Para in
|
|
|
371
372
|
}
|
|
372
373
|
return ios ? /* @__PURE__ */ jsxRuntime.jsx("p", { className, children: iosHint }) : null;
|
|
373
374
|
}
|
|
375
|
+
var DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600];
|
|
376
|
+
var defaultImageLoader = (src, width, height) => {
|
|
377
|
+
try {
|
|
378
|
+
const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src);
|
|
379
|
+
const url = new URL(src, "http://relative.invalid");
|
|
380
|
+
url.searchParams.set("width", String(width));
|
|
381
|
+
if (height) url.searchParams.set("height", String(height));
|
|
382
|
+
return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
|
|
383
|
+
} catch {
|
|
384
|
+
return src;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
function parseRatio(ratio) {
|
|
388
|
+
if (!ratio) return null;
|
|
389
|
+
const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n));
|
|
390
|
+
return w && h ? w / h : null;
|
|
391
|
+
}
|
|
392
|
+
function Image({
|
|
393
|
+
data,
|
|
394
|
+
sizes = "100vw",
|
|
395
|
+
widths = DEFAULT_IMAGE_WIDTHS,
|
|
396
|
+
loader = defaultImageLoader,
|
|
397
|
+
priority = false,
|
|
398
|
+
aspectRatio,
|
|
399
|
+
alt,
|
|
400
|
+
loading,
|
|
401
|
+
decoding,
|
|
402
|
+
...rest
|
|
403
|
+
}) {
|
|
404
|
+
const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null);
|
|
405
|
+
let candidates = [...widths].sort((a, b) => a - b);
|
|
406
|
+
if (data.width) {
|
|
407
|
+
const capped = candidates.filter((w) => w <= data.width);
|
|
408
|
+
candidates = capped.length ? capped : [data.width];
|
|
409
|
+
}
|
|
410
|
+
const srcSet = loader ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : void 0)} ${w}w`).join(", ") : void 0;
|
|
411
|
+
const width = data.width ?? candidates[candidates.length - 1];
|
|
412
|
+
const height = data.height ?? (ratio && width ? Math.round(width / ratio) : void 0);
|
|
413
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
414
|
+
"img",
|
|
415
|
+
{
|
|
416
|
+
src: data.url,
|
|
417
|
+
alt: alt ?? data.alt ?? "",
|
|
418
|
+
...srcSet ? { srcSet, sizes } : {},
|
|
419
|
+
...width ? { width } : {},
|
|
420
|
+
...height ? { height } : {},
|
|
421
|
+
loading: loading ?? (priority ? "eager" : "lazy"),
|
|
422
|
+
decoding: decoding ?? (priority ? "auto" : "async"),
|
|
423
|
+
...priority ? { fetchPriority: "high" } : {},
|
|
424
|
+
...rest
|
|
425
|
+
}
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
function productInitials(name) {
|
|
429
|
+
const words = (name ?? "").normalize("NFKC").split(/[\s\-_/.,()]+/).map((w) => Array.from(w).find((c) => /\p{L}|\p{N}/u.test(c)) ?? "").filter(Boolean);
|
|
430
|
+
if (words.length === 0) return "";
|
|
431
|
+
return (words.length > 1 ? words[0] + words[1] : words[0]).toLocaleUpperCase();
|
|
432
|
+
}
|
|
433
|
+
function ProductThumb({
|
|
434
|
+
image,
|
|
435
|
+
name,
|
|
436
|
+
size = 56,
|
|
437
|
+
ratio = "1/1",
|
|
438
|
+
fit = "contain",
|
|
439
|
+
quantity,
|
|
440
|
+
decorative = false,
|
|
441
|
+
zoom = true,
|
|
442
|
+
priority = false,
|
|
443
|
+
sizes,
|
|
444
|
+
loader,
|
|
445
|
+
className,
|
|
446
|
+
classNames: cx = {}
|
|
447
|
+
}) {
|
|
448
|
+
const url = image?.url || null;
|
|
449
|
+
const [state, setState] = react.useState(url ? "loading" : "fallback");
|
|
450
|
+
const box = react.useRef(null);
|
|
451
|
+
react.useEffect(() => {
|
|
452
|
+
const img = box.current?.querySelector("img");
|
|
453
|
+
setState(!url ? "fallback" : img?.complete ? img.naturalWidth > 0 ? "loaded" : "fallback" : "loading");
|
|
454
|
+
}, [url]);
|
|
455
|
+
const label = name?.trim() || "";
|
|
456
|
+
const initials = productInitials(label);
|
|
457
|
+
const showBadge = typeof quantity === "number" && quantity > 1;
|
|
458
|
+
const a11y = state === "fallback" && !decorative && label ? { role: "img", "aria-label": label } : { "aria-hidden": decorative || state === "fallback" ? true : void 0 };
|
|
459
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
460
|
+
"span",
|
|
461
|
+
{
|
|
462
|
+
ref: box,
|
|
463
|
+
className: [cx.root, className].filter(Boolean).join(" ") || void 0,
|
|
464
|
+
style: { ["--sf-preview-size"]: `${size}px`, ["--sf-preview-ratio"]: ratio.replace(":", "/") },
|
|
465
|
+
"data-sf-thumb": "",
|
|
466
|
+
"data-state": state,
|
|
467
|
+
"data-fit": fit,
|
|
468
|
+
"data-zoom": zoom || void 0,
|
|
469
|
+
...a11y,
|
|
470
|
+
children: [
|
|
471
|
+
url && state !== "fallback" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
472
|
+
Image,
|
|
473
|
+
{
|
|
474
|
+
data: { url, alt: image?.alt ?? null, ...image?.width ? { width: image.width } : {}, ...image?.height ? { height: image.height } : {} },
|
|
475
|
+
alt: decorative ? "" : image?.alt ?? label,
|
|
476
|
+
aspectRatio: ratio,
|
|
477
|
+
sizes: sizes ?? `${size * 2}px`,
|
|
478
|
+
widths: [Math.round(size), Math.round(size * 2), Math.round(size * 3)],
|
|
479
|
+
...loader !== void 0 ? { loader } : {},
|
|
480
|
+
priority,
|
|
481
|
+
className: cx.img,
|
|
482
|
+
onLoad: () => setState("loaded"),
|
|
483
|
+
onError: () => setState("fallback")
|
|
484
|
+
}
|
|
485
|
+
) : null,
|
|
486
|
+
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,
|
|
487
|
+
showBadge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.badge, "data-sf-thumb-qty": "", "aria-hidden": "true", children: quantity }) : null
|
|
488
|
+
]
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
function PackageGlyph() {
|
|
493
|
+
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: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 8 12 3 3 8v8l9 5 9-5V8Z" }),
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 8 9 5 9-5M12 13v8" })
|
|
496
|
+
] });
|
|
497
|
+
}
|
|
498
|
+
function ProductPreview({
|
|
499
|
+
image,
|
|
500
|
+
name,
|
|
501
|
+
variant,
|
|
502
|
+
sku,
|
|
503
|
+
quantity,
|
|
504
|
+
unitPrice,
|
|
505
|
+
total,
|
|
506
|
+
href,
|
|
507
|
+
children,
|
|
508
|
+
size = 64,
|
|
509
|
+
ratio,
|
|
510
|
+
fit,
|
|
511
|
+
zoom,
|
|
512
|
+
priority,
|
|
513
|
+
sizes,
|
|
514
|
+
loader,
|
|
515
|
+
quantityLabel,
|
|
516
|
+
unavailableLabel = "Producto no disponible",
|
|
517
|
+
className,
|
|
518
|
+
classNames: cx = {}
|
|
519
|
+
}) {
|
|
520
|
+
const missing = !name.trim();
|
|
521
|
+
const title = missing ? unavailableLabel : name;
|
|
522
|
+
const hasQty = typeof quantity === "number" && Number.isFinite(quantity);
|
|
523
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [cx.root, className].filter(Boolean).join(" ") || void 0, "data-sf-preview": "", "data-missing": missing || void 0, children: [
|
|
524
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.media, "data-sf-preview-media": "", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
525
|
+
ProductThumb,
|
|
526
|
+
{
|
|
527
|
+
image,
|
|
528
|
+
name: missing ? null : name,
|
|
529
|
+
size,
|
|
530
|
+
decorative: true,
|
|
531
|
+
...ratio ? { ratio } : {},
|
|
532
|
+
...fit ? { fit } : {},
|
|
533
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
534
|
+
...priority ? { priority } : {},
|
|
535
|
+
...sizes ? { sizes } : {},
|
|
536
|
+
...loader !== void 0 ? { loader } : {},
|
|
537
|
+
...hasQty ? { quantity } : {},
|
|
538
|
+
...cx.thumb ? { classNames: cx.thumb } : {}
|
|
539
|
+
}
|
|
540
|
+
) }),
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.body, "data-sf-preview-body": "", children: [
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.name, "data-sf-preview-name": "", children: href && !missing ? /* @__PURE__ */ jsxRuntime.jsx("a", { href, children: title }) : title }),
|
|
543
|
+
variant ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.variant, "data-sf-preview-variant": "", children: variant }) : null,
|
|
544
|
+
sku ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.sku, "data-sf-preview-sku": "", children: [
|
|
545
|
+
"SKU ",
|
|
546
|
+
sku
|
|
547
|
+
] }) : null,
|
|
548
|
+
hasQty ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.meta, "data-sf-preview-meta": "", children: [
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cx.qty, "data-sf-preview-qty": "", children: (quantityLabel ?? "Cantidad {n}").replace("{n}", String(quantity)) }),
|
|
550
|
+
unitPrice != null && quantity > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cx.unit, "data-sf-preview-unit": "", children: [
|
|
551
|
+
" \xB7 ",
|
|
552
|
+
unitPrice
|
|
553
|
+
] }) : null
|
|
554
|
+
] }) : null,
|
|
555
|
+
children
|
|
556
|
+
] }),
|
|
557
|
+
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
|
|
558
|
+
] });
|
|
559
|
+
}
|
|
560
|
+
function ProductThumbStack({ items, max = 4, size = 44, moreLabel = "y {n} m\xE1s", className }) {
|
|
561
|
+
if (items.length === 0) return null;
|
|
562
|
+
const shown = items.length > max ? items.slice(0, max - 1) : items;
|
|
563
|
+
const rest = items.length - shown.length;
|
|
564
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className, "data-sf-thumb-stack": "", style: { ["--sf-preview-size"]: `${size}px` }, children: [
|
|
565
|
+
shown.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx(ProductThumb, { image: it.image ?? null, name: it.name ?? null, size, zoom: false }, i)),
|
|
566
|
+
rest > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-sf-thumb-more": "", role: "img", "aria-label": moreLabel.replace("{n}", String(rest)), children: [
|
|
567
|
+
"+",
|
|
568
|
+
rest
|
|
569
|
+
] }) : null
|
|
570
|
+
] });
|
|
571
|
+
}
|
|
374
572
|
|
|
375
573
|
exports.InstallApp = InstallApp;
|
|
574
|
+
exports.ProductPreview = ProductPreview;
|
|
575
|
+
exports.ProductThumb = ProductThumb;
|
|
576
|
+
exports.ProductThumbStack = ProductThumbStack;
|
|
376
577
|
exports.Select = Select;
|
|
377
578
|
exports.Swatch = Swatch;
|
|
378
579
|
exports.THEME_INIT_SCRIPT = THEME_INIT_SCRIPT;
|
|
@@ -384,6 +585,7 @@ exports.normalizeSwatchName = normalize;
|
|
|
384
585
|
exports.optionHasSwatches = optionHasSwatches;
|
|
385
586
|
exports.parseSwatchAttribute = parseSwatchAttribute;
|
|
386
587
|
exports.pastelBackground = pastelBackground;
|
|
588
|
+
exports.productInitials = productInitials;
|
|
387
589
|
exports.resolveSwatch = resolveSwatch;
|
|
388
590
|
exports.resolveSwatchSpec = resolveSwatchSpec;
|
|
389
591
|
exports.swatchBackground = swatchBackground;
|