@asteby/storefront-react 0.20.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/dist/account.cjs +272 -2
- package/dist/account.cjs.map +1 -1
- package/dist/account.css +159 -0
- package/dist/account.d.cts +35 -3
- package/dist/account.d.ts +35 -3
- package/dist/account.js +70 -1
- 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 +8 -1
- package/dist/checkout.js.map +1 -1
- package/dist/chunk-VHEY5AU3.js +152 -0
- package/dist/chunk-VHEY5AU3.js.map +1 -0
- package/dist/index.js +1 -1
- 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/package.json +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Image } from './chunk-6CVKW7F3.js';
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function productInitials(name) {
|
|
6
|
+
const words = (name ?? "").normalize("NFKC").split(/[\s\-_/.,()]+/).map((w) => Array.from(w).find((c) => /\p{L}|\p{N}/u.test(c)) ?? "").filter(Boolean);
|
|
7
|
+
if (words.length === 0) return "";
|
|
8
|
+
return (words.length > 1 ? words[0] + words[1] : words[0]).toLocaleUpperCase();
|
|
9
|
+
}
|
|
10
|
+
function ProductThumb({
|
|
11
|
+
image,
|
|
12
|
+
name,
|
|
13
|
+
size = 56,
|
|
14
|
+
ratio = "1/1",
|
|
15
|
+
fit = "contain",
|
|
16
|
+
quantity,
|
|
17
|
+
decorative = false,
|
|
18
|
+
zoom = true,
|
|
19
|
+
priority = false,
|
|
20
|
+
sizes,
|
|
21
|
+
loader,
|
|
22
|
+
className,
|
|
23
|
+
classNames: cx = {}
|
|
24
|
+
}) {
|
|
25
|
+
const url = image?.url || null;
|
|
26
|
+
const [state, setState] = useState(url ? "loading" : "fallback");
|
|
27
|
+
const box = useRef(null);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const img = box.current?.querySelector("img");
|
|
30
|
+
setState(!url ? "fallback" : img?.complete ? img.naturalWidth > 0 ? "loaded" : "fallback" : "loading");
|
|
31
|
+
}, [url]);
|
|
32
|
+
const label = name?.trim() || "";
|
|
33
|
+
const initials = productInitials(label);
|
|
34
|
+
const showBadge = typeof quantity === "number" && quantity > 1;
|
|
35
|
+
const a11y = state === "fallback" && !decorative && label ? { role: "img", "aria-label": label } : { "aria-hidden": decorative || state === "fallback" ? true : void 0 };
|
|
36
|
+
return /* @__PURE__ */ jsxs(
|
|
37
|
+
"span",
|
|
38
|
+
{
|
|
39
|
+
ref: box,
|
|
40
|
+
className: [cx.root, className].filter(Boolean).join(" ") || void 0,
|
|
41
|
+
style: { ["--sf-preview-size"]: `${size}px`, ["--sf-preview-ratio"]: ratio.replace(":", "/") },
|
|
42
|
+
"data-sf-thumb": "",
|
|
43
|
+
"data-state": state,
|
|
44
|
+
"data-fit": fit,
|
|
45
|
+
"data-zoom": zoom || void 0,
|
|
46
|
+
...a11y,
|
|
47
|
+
children: [
|
|
48
|
+
url && state !== "fallback" ? /* @__PURE__ */ jsx(
|
|
49
|
+
Image,
|
|
50
|
+
{
|
|
51
|
+
data: { url, alt: image?.alt ?? null, ...image?.width ? { width: image.width } : {}, ...image?.height ? { height: image.height } : {} },
|
|
52
|
+
alt: decorative ? "" : image?.alt ?? label,
|
|
53
|
+
aspectRatio: ratio,
|
|
54
|
+
sizes: sizes ?? `${size * 2}px`,
|
|
55
|
+
widths: [Math.round(size), Math.round(size * 2), Math.round(size * 3)],
|
|
56
|
+
...loader !== void 0 ? { loader } : {},
|
|
57
|
+
priority,
|
|
58
|
+
className: cx.img,
|
|
59
|
+
onLoad: () => setState("loaded"),
|
|
60
|
+
onError: () => setState("fallback")
|
|
61
|
+
}
|
|
62
|
+
) : null,
|
|
63
|
+
state === "fallback" ? /* @__PURE__ */ jsx("span", { className: cx.fallback, "data-sf-thumb-fallback": "", "aria-hidden": "true", children: initials ? /* @__PURE__ */ jsx("span", { "data-sf-thumb-initial": "", children: initials }) : /* @__PURE__ */ jsx(PackageGlyph, {}) }) : null,
|
|
64
|
+
showBadge ? /* @__PURE__ */ jsx("span", { className: cx.badge, "data-sf-thumb-qty": "", "aria-hidden": "true", children: quantity }) : null
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function PackageGlyph() {
|
|
70
|
+
return /* @__PURE__ */ 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: [
|
|
71
|
+
/* @__PURE__ */ jsx("path", { d: "M21 8 12 3 3 8v8l9 5 9-5V8Z" }),
|
|
72
|
+
/* @__PURE__ */ jsx("path", { d: "m3 8 9 5 9-5M12 13v8" })
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
function ProductPreview({
|
|
76
|
+
image,
|
|
77
|
+
name,
|
|
78
|
+
variant,
|
|
79
|
+
sku,
|
|
80
|
+
quantity,
|
|
81
|
+
unitPrice,
|
|
82
|
+
total,
|
|
83
|
+
href,
|
|
84
|
+
children,
|
|
85
|
+
size = 64,
|
|
86
|
+
ratio,
|
|
87
|
+
fit,
|
|
88
|
+
zoom,
|
|
89
|
+
priority,
|
|
90
|
+
sizes,
|
|
91
|
+
loader,
|
|
92
|
+
quantityLabel,
|
|
93
|
+
unavailableLabel = "Producto no disponible",
|
|
94
|
+
className,
|
|
95
|
+
classNames: cx = {}
|
|
96
|
+
}) {
|
|
97
|
+
const missing = !name.trim();
|
|
98
|
+
const title = missing ? unavailableLabel : name;
|
|
99
|
+
const hasQty = typeof quantity === "number" && Number.isFinite(quantity);
|
|
100
|
+
return /* @__PURE__ */ jsxs("div", { className: [cx.root, className].filter(Boolean).join(" ") || void 0, "data-sf-preview": "", "data-missing": missing || void 0, children: [
|
|
101
|
+
/* @__PURE__ */ jsx("span", { className: cx.media, "data-sf-preview-media": "", children: /* @__PURE__ */ jsx(
|
|
102
|
+
ProductThumb,
|
|
103
|
+
{
|
|
104
|
+
image,
|
|
105
|
+
name: missing ? null : name,
|
|
106
|
+
size,
|
|
107
|
+
decorative: true,
|
|
108
|
+
...ratio ? { ratio } : {},
|
|
109
|
+
...fit ? { fit } : {},
|
|
110
|
+
...zoom !== void 0 ? { zoom } : {},
|
|
111
|
+
...priority ? { priority } : {},
|
|
112
|
+
...sizes ? { sizes } : {},
|
|
113
|
+
...loader !== void 0 ? { loader } : {},
|
|
114
|
+
...hasQty ? { quantity } : {},
|
|
115
|
+
...cx.thumb ? { classNames: cx.thumb } : {}
|
|
116
|
+
}
|
|
117
|
+
) }),
|
|
118
|
+
/* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-preview-body": "", children: [
|
|
119
|
+
/* @__PURE__ */ jsx("span", { className: cx.name, "data-sf-preview-name": "", children: href && !missing ? /* @__PURE__ */ jsx("a", { href, children: title }) : title }),
|
|
120
|
+
variant ? /* @__PURE__ */ jsx("span", { className: cx.variant, "data-sf-preview-variant": "", children: variant }) : null,
|
|
121
|
+
sku ? /* @__PURE__ */ jsxs("span", { className: cx.sku, "data-sf-preview-sku": "", children: [
|
|
122
|
+
"SKU ",
|
|
123
|
+
sku
|
|
124
|
+
] }) : null,
|
|
125
|
+
hasQty ? /* @__PURE__ */ jsxs("span", { className: cx.meta, "data-sf-preview-meta": "", children: [
|
|
126
|
+
/* @__PURE__ */ jsx("span", { className: cx.qty, "data-sf-preview-qty": "", children: (quantityLabel ?? "Cantidad {n}").replace("{n}", String(quantity)) }),
|
|
127
|
+
unitPrice != null && quantity > 1 ? /* @__PURE__ */ jsxs("span", { className: cx.unit, "data-sf-preview-unit": "", children: [
|
|
128
|
+
" \xB7 ",
|
|
129
|
+
unitPrice
|
|
130
|
+
] }) : null
|
|
131
|
+
] }) : null,
|
|
132
|
+
children
|
|
133
|
+
] }),
|
|
134
|
+
total != null ? /* @__PURE__ */ jsx("span", { className: cx.total, "data-sf-preview-total": "", children: total }) : unitPrice != null && !(hasQty && quantity > 1) ? /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-preview-total": "", children: unitPrice }) : null
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
function ProductThumbStack({ items, max = 4, size = 44, moreLabel = "y {n} m\xE1s", className }) {
|
|
138
|
+
if (items.length === 0) return null;
|
|
139
|
+
const shown = items.length > max ? items.slice(0, max - 1) : items;
|
|
140
|
+
const rest = items.length - shown.length;
|
|
141
|
+
return /* @__PURE__ */ jsxs("span", { className, "data-sf-thumb-stack": "", style: { ["--sf-preview-size"]: `${size}px` }, children: [
|
|
142
|
+
shown.map((it, i) => /* @__PURE__ */ jsx(ProductThumb, { image: it.image ?? null, name: it.name ?? null, size, zoom: false }, i)),
|
|
143
|
+
rest > 0 ? /* @__PURE__ */ jsxs("span", { "data-sf-thumb-more": "", role: "img", "aria-label": moreLabel.replace("{n}", String(rest)), children: [
|
|
144
|
+
"+",
|
|
145
|
+
rest
|
|
146
|
+
] }) : null
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { ProductPreview, ProductThumb, ProductThumbStack, productInitials };
|
|
151
|
+
//# sourceMappingURL=chunk-VHEY5AU3.js.map
|
|
152
|
+
//# sourceMappingURL=chunk-VHEY5AU3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/product-preview.tsx"],"names":[],"mappings":"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","file":"chunk-VHEY5AU3.js","sourcesContent":["/**\n * Product preview of the storefront: ONE picture-plus-text pattern for every place a product is listed (cart drawer,\n * checkout summary, \"Mis pedidos\", order detail). Headless (`data-sf-preview*`, `classNames`), styled by\n * `product-preview.css` (plain CSS on the store tokens, dark mode included), SSR-safe.\n *\n * - `ProductThumb`: the picture alone. Fixed aspect ratio, `object-fit: contain` on a soft surface (parts photographed\n * on white are never cropped), responsive `srcset` through the shared `Image`, lazy by default, a shimmer while it\n * loads and an elegant fallback (initial of the name, or a package glyph) when there is no image, it is broken, or\n * the product no longer exists. Optional quantity badge.\n * - `ProductPreview`: thumb + name (2 lines) + variant + SKU + quantity x unit price, with the line total aligned right.\n * - `ProductThumbStack`: overlapped thumbs with a \"+N\" counter (order cards).\n */\nimport { useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { Image as ImageData } from '@asteby/storefront'\nimport { Image, type ImageLoader } from '../components.js'\n\ntype ThumbState = 'loading' | 'loaded' | 'fallback'\n\n/** First letter(s) of the name for the fallback: two initials from two words, one otherwise. Empty when nothing usable. */\nexport function productInitials(name: string | null | undefined): string {\n const words = (name ?? '')\n .normalize('NFKC')\n .split(/[\\s\\-_/.,()]+/)\n .map((w) => Array.from(w).find((c) => /\\p{L}|\\p{N}/u.test(c)) ?? '')\n .filter(Boolean)\n if (words.length === 0) return ''\n return (words.length > 1 ? words[0]! + words[1]! : words[0]!).toLocaleUpperCase()\n}\n\nexport type ProductThumbClassNames = Partial<Record<'root' | 'img' | 'fallback' | 'badge', string>>\n\nexport type ProductThumbImage = { url: string; alt?: string | null; width?: number; height?: number }\n\nexport type ProductThumbProps = {\n /** Picture of the product or variant. `null`/missing: shows the fallback. */\n image?: ProductThumbImage | null\n /** Name of the product: alt of the picture (unless `decorative`) and source of the fallback initial. */\n name?: string | null\n /** Side in px. Default 56. */\n size?: number\n /** Aspect ratio of the box, `w/h`. Default `1/1`. */\n ratio?: string\n /** `contain` (default) never crops; `cover` fills the box. */\n fit?: 'contain' | 'cover'\n /** Quantity badge, shown when greater than 1. */\n quantity?: number\n /** The name sits next to it as text: the picture has `alt=\"\"` and the fallback is hidden from screen readers. Default false. */\n decorative?: boolean\n /** Slight zoom on hover (honours `prefers-reduced-motion`). Default true. */\n zoom?: boolean\n /** Above the fold: eager load. */\n priority?: boolean\n sizes?: string\n loader?: ImageLoader | null\n className?: string\n classNames?: ProductThumbClassNames\n}\n\n/** The picture of a product with fixed ratio, shimmer, lazy loading and a fallback that always looks designed. */\nexport function ProductThumb({\n image,\n name,\n size = 56,\n ratio = '1/1',\n fit = 'contain',\n quantity,\n decorative = false,\n zoom = true,\n priority = false,\n sizes,\n loader,\n className,\n classNames: cx = {},\n}: ProductThumbProps) {\n const url = image?.url || null\n const [state, setState] = useState<ThumbState>(url ? 'loading' : 'fallback')\n const box = useRef<HTMLSpanElement>(null)\n // A new picture (another variant) starts over; a missing one goes straight to the fallback.\n useEffect(() => {\n const img = box.current?.querySelector('img')\n // The browser may have loaded the picture before hydration: no `load` event will come.\n setState(!url ? 'fallback' : img?.complete ? (img.naturalWidth > 0 ? 'loaded' : 'fallback') : 'loading')\n }, [url])\n\n const label = name?.trim() || ''\n const initials = productInitials(label)\n const showBadge = typeof quantity === 'number' && quantity > 1\n const a11y = state === 'fallback' && !decorative && label ? { role: 'img', 'aria-label': label } : { 'aria-hidden': decorative || state === 'fallback' ? true : undefined }\n return (\n <span\n ref={box}\n className={[cx.root, className].filter(Boolean).join(' ') || undefined}\n style={{ ['--sf-preview-size' as string]: `${size}px`, ['--sf-preview-ratio' as string]: ratio.replace(':', '/') }}\n data-sf-thumb=\"\"\n data-state={state}\n data-fit={fit}\n data-zoom={zoom || undefined}\n {...a11y}\n >\n {url && state !== 'fallback' ? (\n <Image\n data={{ url, alt: image?.alt ?? null, ...(image?.width ? { width: image.width } : {}), ...(image?.height ? { height: image.height } : {}) }}\n alt={decorative ? '' : (image?.alt ?? label)}\n aspectRatio={ratio}\n sizes={sizes ?? `${size * 2}px`}\n widths={[Math.round(size), Math.round(size * 2), Math.round(size * 3)]}\n {...(loader !== undefined ? { loader } : {})}\n priority={priority}\n className={cx.img}\n onLoad={() => setState('loaded')}\n onError={() => setState('fallback')}\n />\n ) : null}\n {state === 'fallback' ? (\n <span className={cx.fallback} data-sf-thumb-fallback=\"\" aria-hidden=\"true\">\n {initials ? <span data-sf-thumb-initial=\"\">{initials}</span> : <PackageGlyph />}\n </span>\n ) : null}\n {showBadge ? (\n <span className={cx.badge} data-sf-thumb-qty=\"\" aria-hidden=\"true\">\n {quantity}\n </span>\n ) : null}\n </span>\n )\n}\n\nfunction PackageGlyph() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"45%\" height=\"45%\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" focusable=\"false\">\n <path d=\"M21 8 12 3 3 8v8l9 5 9-5V8Z\" />\n <path d=\"m3 8 9 5 9-5M12 13v8\" />\n </svg>\n )\n}\n\nexport type ProductPreviewClassNames = Partial<Record<'root' | 'media' | 'body' | 'name' | 'variant' | 'meta' | 'sku' | 'qty' | 'price' | 'total' | 'unit', string>> & { thumb?: ProductThumbClassNames }\n\nexport type ProductPreviewProps = Pick<ProductThumbProps, 'image' | 'size' | 'ratio' | 'fit' | 'zoom' | 'priority' | 'sizes' | 'loader'> & {\n name: string\n /** \"Rojo / L\". Omit for default variants. */\n variant?: string | null\n sku?: string | null\n /** Quantity: a badge on the picture (> 1) and \"Cantidad N\" under the name. */\n quantity?: number\n /** Unit price, already formatted. Shown next to the quantity when it is greater than 1. */\n unitPrice?: ReactNode\n /** Line total, already formatted: aligned right, the strongest number. */\n total?: ReactNode\n /** Link on the name (product page). */\n href?: string | null\n /** Rendered under the text (status, note, actions). */\n children?: ReactNode\n /** Text of the quantity for screen readers and the meta line. Default `Cantidad {n}`. */\n quantityLabel?: string\n /** Name shown when the line has none (product deleted). Default `Producto no disponible`. */\n unavailableLabel?: string\n className?: string\n classNames?: ProductPreviewClassNames\n}\n\n/**\n * A product line: picture, name (2 lines), variant, SKU, quantity and prices with a clear hierarchy. Degrades to a\n * designed placeholder when the product has no image or no longer exists (empty name).\n */\nexport function ProductPreview({\n image,\n name,\n variant,\n sku,\n quantity,\n unitPrice,\n total,\n href,\n children,\n size = 64,\n ratio,\n fit,\n zoom,\n priority,\n sizes,\n loader,\n quantityLabel,\n unavailableLabel = 'Producto no disponible',\n className,\n classNames: cx = {},\n}: ProductPreviewProps) {\n const missing = !name.trim()\n const title = missing ? unavailableLabel : name\n const hasQty = typeof quantity === 'number' && Number.isFinite(quantity)\n return (\n <div className={[cx.root, className].filter(Boolean).join(' ') || undefined} data-sf-preview=\"\" data-missing={missing || undefined}>\n <span className={cx.media} data-sf-preview-media=\"\">\n <ProductThumb\n image={image}\n name={missing ? null : name}\n size={size}\n decorative\n {...(ratio ? { ratio } : {})}\n {...(fit ? { fit } : {})}\n {...(zoom !== undefined ? { zoom } : {})}\n {...(priority ? { priority } : {})}\n {...(sizes ? { sizes } : {})}\n {...(loader !== undefined ? { loader } : {})}\n {...(hasQty ? { quantity: quantity! } : {})}\n {...(cx.thumb ? { classNames: cx.thumb } : {})}\n />\n </span>\n <span className={cx.body} data-sf-preview-body=\"\">\n <span className={cx.name} data-sf-preview-name=\"\">\n {href && !missing ? <a href={href}>{title}</a> : title}\n </span>\n {variant ? (\n <span className={cx.variant} data-sf-preview-variant=\"\">\n {variant}\n </span>\n ) : null}\n {sku ? (\n <span className={cx.sku} data-sf-preview-sku=\"\">\n SKU {sku}\n </span>\n ) : null}\n {hasQty ? (\n <span className={cx.meta} data-sf-preview-meta=\"\">\n <span className={cx.qty} data-sf-preview-qty=\"\">\n {(quantityLabel ?? 'Cantidad {n}').replace('{n}', String(quantity))}\n </span>\n {unitPrice != null && quantity! > 1 ? (\n <span className={cx.unit} data-sf-preview-unit=\"\">\n {' · '}\n {unitPrice}\n </span>\n ) : null}\n </span>\n ) : null}\n {children}\n </span>\n {total != null ? (\n <span className={cx.total} data-sf-preview-total=\"\">\n {total}\n </span>\n ) : unitPrice != null && !(hasQty && quantity! > 1) ? (\n <span className={cx.price} data-sf-preview-total=\"\">\n {unitPrice}\n </span>\n ) : null}\n </div>\n )\n}\n\nexport type ProductThumbStackProps = {\n items: ReadonlyArray<{ image?: ProductThumbImage | null; name?: string | null }>\n /** Thumbs shown before \"+N\". Default 4. */\n max?: number\n size?: number\n /** Screen reader text for the hidden remainder, `{n}` = how many. Default `y {n} más`. */\n moreLabel?: string\n className?: string\n}\n\n/** Overlapped thumbs with a \"+N\" counter: the products of an order at a glance. */\nexport function ProductThumbStack({ items, max = 4, size = 44, moreLabel = 'y {n} más', className }: ProductThumbStackProps) {\n if (items.length === 0) return null\n const shown = items.length > max ? items.slice(0, max - 1) : items\n const rest = items.length - shown.length\n return (\n <span className={className} data-sf-thumb-stack=\"\" style={{ ['--sf-preview-size' as string]: `${size}px` }}>\n {shown.map((it, i) => (\n <ProductThumb key={i} image={it.image ?? null} name={it.name ?? null} size={size} zoom={false} />\n ))}\n {rest > 0 ? (\n <span data-sf-thumb-more=\"\" role=\"img\" aria-label={moreLabel.replace('{n}', String(rest))}>\n +{rest}\n </span>\n ) : null}\n </span>\n )\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { DEFAULT_IMAGE_WIDTHS, Image, Money, ProductPrice, defaultImageLoader } from './chunk-6CVKW7F3.js';
|
|
2
1
|
export { ThemeStyle, isSafeCssValue, themeFontsHref, themeToCss, themeToCssVariables, tokenName } from './chunk-672OTUM2.js';
|
|
2
|
+
export { DEFAULT_IMAGE_WIDTHS, Image, Money, ProductPrice, defaultImageLoader } from './chunk-6CVKW7F3.js';
|
|
3
3
|
import { useOptionalStorefront } from './chunk-DQBL2DFJ.js';
|
|
4
4
|
export { StorefrontProvider, useOptionalStorefront, useStorefront, useStorefrontClient } from './chunk-DQBL2DFJ.js';
|
|
5
5
|
import { createContext, useRef, useState, useCallback, useEffect, useMemo, useContext, Fragment as Fragment$1, useId } from 'react';
|
|
@@ -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;
|