@asteby/storefront-react 0.2.0 → 0.3.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 CHANGED
@@ -196,6 +196,138 @@ const mine = useSelectedLocation({ initialId: urlSucursal }) // ?sucursal= + loc
196
196
  `OpenStatus` (calcula tras montar; `now` fija el instante), `DirectionsLink`,
197
197
  `PickupOption` (muestra lo que manda OPS: costo y días), `NearestLocationForm`.
198
198
 
199
+ ## Mockups de respaldo (`@asteby/storefront-react/mockups`)
200
+
201
+ Cuando un producto de OPS no trae imagen, `ProductImage` dibuja una ilustración
202
+ SVG según el tipo de producto en lugar de un hueco: **llanta** (medida en cifras,
203
+ marca, modelo, índice de carga/velocidad, LT, run flat), **refacción** (ficha con
204
+ número de parte, OEM, compatibilidad y posición), **suplemento** (bote, frasco o
205
+ bolsa con nombre, sabor y presentación) y **genérico** (caja con monograma).
206
+ SSR, sin JavaScript en el cliente, sin red, sin `id`s; ~1–2 KB gzip por mockup.
207
+
208
+ ```tsx
209
+ import { ProductImage } from '@asteby/storefront-react/mockups'
210
+
211
+ // Sustituye el `product.featured_image ? <Image …/> : null` de las tarjetas:
212
+ <ProductImage
213
+ product={product}
214
+ variant={selectedVariant} // opcional: su imagen gana; su sabor/presentación alimenta el mockup
215
+ widths={[240, 360, 480, 720]}
216
+ sizes="(min-width: 1024px) 25vw, 50vw"
217
+ priority={index < 4} // LCP (solo aplica a la imagen real)
218
+ className="h-full w-full object-cover"
219
+ />
220
+ ```
221
+
222
+ - **Con imagen** renderiza `<Image>` (srcset, `width`/`height`, `priority`); el
223
+ `alt` es el de OPS, o el título del producto si viene vacío.
224
+ - **Sin imagen** renderiza `<svg role="img" aria-label="Ilustración de referencia: …">`
225
+ con `<title>`: nunca se anuncia como foto. `alt=""` lo vuelve decorativo
226
+ (`aria-hidden`), útil cuando el nombre ya está junto a la imagen.
227
+ - **Nunca inventa ni muestra precios**: solo pinta lo que trae el producto
228
+ (atributos, `vendor`, título, opciones, variante). Sin medida no hay medida;
229
+ el sabor solo aparece con variante elegida o si la opción tiene un único valor.
230
+ Todo texto se limpia (controles/bidi), se recorta con `…` y React lo escapa.
231
+ - `image={null}` fuerza el mockup (p. ej. en una galería); `kind="tire"` fuerza el tipo.
232
+
233
+ ### Qué tipo se dibuja
234
+
235
+ `kind` → `resolveKind` (prop o `MockupProvider`) → `resolveMockupKind(product)`:
236
+
237
+ 1. Atributo `mockup_kind` (o `mockup`) en OPS, si ese tipo está registrado: el
238
+ dueño puede elegirlo sin tocar código.
239
+ 2. Atributos: `size_key`, `section_width_mm`, `rim_diameter_in`, `speed_rating`… → `tire`;
240
+ `part_number`, `oem_number`, `mpn` → `part`.
241
+ 3. Palabras en `product_type`, categoría y etiquetas (no en el título): llanta,
242
+ neumático, refacción, autoparte, proteína, creatina, colágeno, suplemento…
243
+ 4. `generic`.
244
+
245
+ Atributos que lee cada uno (los del pack del giro, más alias comunes):
246
+ llanta `size_key|size`, `section_width_mm|width`, `aspect_ratio|aspect`,
247
+ `rim_diameter_in|rim`, `load_index`/`load_service`, `speed_rating`, `model`,
248
+ `light_truck`, `runflat`; refacción `part_number|mpn`, `oem_number`,
249
+ `fitment|compatibility|compatibilidad`, `position`; suplemento opción
250
+ `Sabor`/`Presentación` o atributos `flavor|sabor`, `presentation|net_content`,
251
+ `servings` (número) y `form|envase` (`cápsulas` → frasco, `bolsa` → bolsa).
252
+
253
+ ### Personalizar sin tocar el SDK
254
+
255
+ **Colores y tipografía** con variables CSS. Por defecto toman el tema de la tienda
256
+ (`--color-muted`, `--color-foreground`, `--color-background`, `--color-primary`,
257
+ `--color-primary-foreground`, `--font-heading`/`--font-sans`, los que emite
258
+ `ThemeStyle`) y si no hay, grises neutros con `light-dark()` (modo oscuro cuando
259
+ la página declara `color-scheme: dark` o `light dark`).
260
+
261
+ ```css
262
+ /* app/app.css de la tienda */
263
+ :root {
264
+ --mockup-bg: var(--color-muted); /* fondo del cuadro */
265
+ --mockup-ink: var(--color-foreground); /* texto sobre el fondo */
266
+ --mockup-muted: #6b7280; /* texto secundario sobre el fondo */
267
+ --mockup-surface: #001963; /* hule, envase, caja */
268
+ --mockup-surface-ink: #ffffff; /* texto sobre el envase */
269
+ --mockup-label: #ffffff; /* etiqueta / ficha */
270
+ --mockup-label-ink: #000c3c; /* texto sobre la etiqueta */
271
+ --mockup-accent: #f66123; /* acento: R16, banda de la ficha, tapa */
272
+ --mockup-accent-ink: #ffffff;
273
+ --mockup-font: "Archivo", sans-serif;
274
+ --mockup-font-mono: ui-monospace, monospace; /* números de parte */
275
+ --mockup-text-transform: uppercase; /* p. ej. nombres en MAYÚSCULAS */
276
+ }
277
+ .dark { --mockup-surface: #0b1440; }
278
+ ```
279
+
280
+ **Tipo por tienda** (sin recompilar el SDK) y textos de la interfaz:
281
+
282
+ ```tsx
283
+ // app/root.tsx
284
+ import { MockupProvider } from '@asteby/storefront-react/mockups'
285
+
286
+ <MockupProvider
287
+ resolveKind={(product, guess) => (product.category?.handle === 'shakers' ? 'generic' : guess)}
288
+ labels={{ illustration: 'Imagen ilustrativa', servings: 'servicios' }}
289
+ >
290
+ <Outlet />
291
+ </MockupProvider>
292
+ ```
293
+
294
+ **Un mockup propio** (o reemplazar uno base). `MockupFrame` da el `<svg>`, el
295
+ fondo, las variables y la accesibilidad; `MockupText`, `wrapMockupText` y
296
+ `fitMockupFontSize` acomodan el texto sin medir el DOM.
297
+
298
+ ```tsx
299
+ import {
300
+ BUILTIN_MOCKUPS, MockupFrame, MockupText, MockupProvider, createMockupRegistry,
301
+ cleanMockupText, tone, wrapMockupText, type MockupComponentProps,
302
+ } from '@asteby/storefront-react/mockups'
303
+
304
+ type Helmet = { title: string; shell: string | null }
305
+
306
+ function HelmetMockup({ data, label, labels, ...svg }: MockupComponentProps<Helmet>) {
307
+ return (
308
+ <MockupFrame kind="helmet" label={label} {...svg}>
309
+ <path d="M90 230a110 110 0 0 1 220 0v40H90z" style={tone('surface')} />
310
+ <MockupText lines={wrapMockupText(data.title, 24, 2)} x={200} y={330} size={22} />
311
+ {data.shell ? <MockupText lines={[data.shell]} x={200} y={372} size={14} tone="muted" /> : null}
312
+ </MockupFrame>
313
+ )
314
+ }
315
+
316
+ const registry = createMockupRegistry(BUILTIN_MOCKUPS).register('helmet', {
317
+ component: HelmetMockup,
318
+ fromProduct: (p) => ({ title: cleanMockupText(p.title) ?? '', shell: cleanMockupText(p.attributes?.shell, 24) }),
319
+ // describe: (d, labels) => `${labels.illustration}: ${d.title}` (opcional)
320
+ })
321
+
322
+ <MockupProvider registry={registry} resolveKind={(p, guess) => (p.product_type === 'Casco' ? 'helmet' : guess)}>…</MockupProvider>
323
+ ```
324
+
325
+ `registerMockup(kind, renderer)` hace lo mismo sobre el registro por defecto
326
+ (llámalo una vez a nivel de módulo). Si registras solo un componente para un
327
+ tipo base (`registerMockup('tire', MiLlanta)`), recibe los datos del extractor
328
+ base (`TireMockupData`). Para dibujar sin producto completo:
329
+ `<ProductMockup kind="part" data={partMockupData(product)} />`.
330
+
199
331
  ## API
200
332
 
201
333
  | Export | |
@@ -209,3 +341,4 @@ const mine = useSelectedLocation({ initialId: urlSucursal }) // ?sucursal= + loc
209
341
  | `productJsonLd`, `organizationJsonLd`, `localBusinessJsonLd`, `breadcrumbJsonLd`, `faqJsonLd`, `collectionJsonLd`, `JsonLd`, `serializeJsonLd` | schema.org JSON-LD |
210
342
  | `defineSection`, `SectionRegistry`, `createSectionRegistry`, `Sections`, `resolveSectionProps`, `sectionJsonSchema` | Content sections |
211
343
  | `themeToCss`, `themeToCssVariables`, `ThemeStyle`, `themeFontsHref`, `tokenName` | SSR theme |
344
+ | `ProductImage`, `ProductMockup`, `MockupProvider`, `createMockupRegistry`, `registerMockup`, `BUILTIN_MOCKUPS`, `MockupFrame`, `MockupText`, `TireMockup`, `PartMockup`, `SupplementMockup`, `GenericMockup` | Mockups de respaldo (subpath `/mockups`) |
@@ -0,0 +1,162 @@
1
+ import { createContext, useMemo, useContext, createElement } from 'react';
2
+ import { createStorefrontClient, formatMoney, parseMoney } from '@asteby/storefront';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ // src/context.tsx
6
+ var StorefrontContext = createContext(null);
7
+ function StorefrontProvider({ client, config, shop = null, locale, currency, children }) {
8
+ const configKey = config ? `${config.baseUrl}|${config.token}|${config.locale ?? ""}|${config.customerToken ?? ""}` : "";
9
+ const resolvedClient = useMemo(
10
+ () => client ?? (config ? createStorefrontClient(config) : null),
11
+ [client, configKey]
12
+ );
13
+ const value = useMemo(
14
+ () => ({
15
+ client: resolvedClient,
16
+ shop,
17
+ locale: locale ?? config?.locale ?? shop?.locale ?? "es-MX",
18
+ currency: currency ?? shop?.currency_code ?? "MXN"
19
+ }),
20
+ [resolvedClient, shop, locale, currency, config?.locale]
21
+ );
22
+ return /* @__PURE__ */ jsx(StorefrontContext.Provider, { value, children });
23
+ }
24
+ function useStorefront() {
25
+ const ctx = useContext(StorefrontContext);
26
+ if (!ctx) throw new Error("useStorefront must be used inside <StorefrontProvider>");
27
+ return ctx;
28
+ }
29
+ function useOptionalStorefront() {
30
+ return useContext(StorefrontContext);
31
+ }
32
+ function useStorefrontClient() {
33
+ const { client } = useStorefront();
34
+ if (!client) throw new Error("<StorefrontProvider> needs a `client` or `config` for browser-side calls");
35
+ return client;
36
+ }
37
+ function Money({
38
+ data,
39
+ as,
40
+ locale,
41
+ withoutTrailingZeros,
42
+ withoutCurrency,
43
+ currencyDisplay,
44
+ ...rest
45
+ }) {
46
+ const sf = useOptionalStorefront();
47
+ const text = formatMoney(data, locale ?? sf?.locale, {
48
+ ...withoutTrailingZeros !== void 0 ? { withoutTrailingZeros } : {},
49
+ ...withoutCurrency !== void 0 ? { withoutCurrency } : {},
50
+ ...currencyDisplay !== void 0 ? { currencyDisplay } : {}
51
+ });
52
+ return createElement(as ?? "span", rest, text);
53
+ }
54
+ var DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600];
55
+ var defaultImageLoader = (src, width, height) => {
56
+ try {
57
+ const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src);
58
+ const url = new URL(src, "http://relative.invalid");
59
+ url.searchParams.set("width", String(width));
60
+ if (height) url.searchParams.set("height", String(height));
61
+ return isAbsolute ? url.toString() : url.pathname + url.search + url.hash;
62
+ } catch {
63
+ return src;
64
+ }
65
+ };
66
+ function parseRatio(ratio) {
67
+ if (!ratio) return null;
68
+ const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n));
69
+ return w && h ? w / h : null;
70
+ }
71
+ function Image({
72
+ data,
73
+ sizes = "100vw",
74
+ widths = DEFAULT_IMAGE_WIDTHS,
75
+ loader = defaultImageLoader,
76
+ priority = false,
77
+ aspectRatio,
78
+ alt,
79
+ loading,
80
+ decoding,
81
+ ...rest
82
+ }) {
83
+ const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null);
84
+ let candidates = [...widths].sort((a, b) => a - b);
85
+ if (data.width) {
86
+ const capped = candidates.filter((w) => w <= data.width);
87
+ candidates = capped.length ? capped : [data.width];
88
+ }
89
+ const srcSet = loader ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : void 0)} ${w}w`).join(", ") : void 0;
90
+ const width = data.width ?? candidates[candidates.length - 1];
91
+ const height = data.height ?? (ratio && width ? Math.round(width / ratio) : void 0);
92
+ return /* @__PURE__ */ jsx(
93
+ "img",
94
+ {
95
+ src: data.url,
96
+ alt: alt ?? data.alt ?? "",
97
+ ...srcSet ? { srcSet, sizes } : {},
98
+ ...width ? { width } : {},
99
+ ...height ? { height } : {},
100
+ loading: loading ?? (priority ? "eager" : "lazy"),
101
+ decoding: decoding ?? (priority ? "auto" : "async"),
102
+ ...priority ? { fetchPriority: "high" } : {},
103
+ ...rest
104
+ }
105
+ );
106
+ }
107
+ var VISUALLY_HIDDEN = {
108
+ position: "absolute",
109
+ width: 1,
110
+ height: 1,
111
+ padding: 0,
112
+ margin: -1,
113
+ overflow: "hidden",
114
+ clip: "rect(0, 0, 0, 0)",
115
+ whiteSpace: "nowrap",
116
+ border: 0
117
+ };
118
+ function ProductPrice({
119
+ product,
120
+ variant,
121
+ fromLabel = "Desde",
122
+ compareAtLabel = "Precio anterior",
123
+ locale,
124
+ withoutTrailingZeros,
125
+ ...rest
126
+ }) {
127
+ let price = null;
128
+ let compareAt = null;
129
+ let isRange = false;
130
+ if (variant) {
131
+ price = variant.price;
132
+ compareAt = variant.compare_at_price;
133
+ } else if (product) {
134
+ price = product.price_range.min;
135
+ compareAt = product.compare_at_price_range?.min ?? null;
136
+ isRange = product.price_range.min.amount !== product.price_range.max.amount;
137
+ }
138
+ if (!price) return null;
139
+ const onSale = compareAt !== null && parseMoney(compareAt) > parseMoney(price);
140
+ const opts = { ...locale ? { locale } : {}, ...withoutTrailingZeros !== void 0 ? { withoutTrailingZeros } : {} };
141
+ return /* @__PURE__ */ jsxs("div", { "data-on-sale": onSale || void 0, ...rest, children: [
142
+ isRange ? /* @__PURE__ */ jsxs("span", { "data-price-prefix": "", children: [
143
+ fromLabel,
144
+ " "
145
+ ] }) : null,
146
+ /* @__PURE__ */ jsx(Money, { data: price, "data-price": "", ...opts }),
147
+ onSale && compareAt ? /* @__PURE__ */ jsxs(Fragment, { children: [
148
+ " ",
149
+ /* @__PURE__ */ jsxs("s", { "data-compare-at-price": "", children: [
150
+ /* @__PURE__ */ jsxs("span", { style: VISUALLY_HIDDEN, children: [
151
+ compareAtLabel,
152
+ ": "
153
+ ] }),
154
+ /* @__PURE__ */ jsx(Money, { data: compareAt, ...opts })
155
+ ] })
156
+ ] }) : null
157
+ ] });
158
+ }
159
+
160
+ export { DEFAULT_IMAGE_WIDTHS, Image, Money, ProductPrice, StorefrontProvider, defaultImageLoader, useOptionalStorefront, useStorefront, useStorefrontClient };
161
+ //# sourceMappingURL=chunk-C6QZNKRE.js.map
162
+ //# sourceMappingURL=chunk-C6QZNKRE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context.tsx","../src/components.tsx"],"names":["jsx"],"mappings":";;;;;AAaA,IAAM,iBAAA,GAAoB,cAA6C,IAAI,CAAA;AAkBpE,SAAS,kBAAA,CAAmB,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAO,IAAA,EAAM,MAAA,EAAQ,QAAA,EAAU,QAAA,EAAS,EAA4B;AACvH,EAAA,MAAM,YAAY,MAAA,GAAS,CAAA,EAAG,MAAA,CAAO,OAAO,IAAI,MAAA,CAAO,KAAK,CAAA,CAAA,EAAI,MAAA,CAAO,UAAU,EAAE,CAAA,CAAA,EAAI,MAAA,CAAO,aAAA,IAAiB,EAAE,CAAA,CAAA,GAAK,EAAA;AACtH,EAAA,MAAM,cAAA,GAAiB,OAAA;AAAA,IACrB,MAAM,MAAA,KAAW,MAAA,GAAS,sBAAA,CAAuB,MAAM,CAAA,GAAI,IAAA,CAAA;AAAA,IAC3D,CAAC,QAAQ,SAAS;AAAA,GACpB;AACA,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,MAAA,EAAQ,cAAA;AAAA,MACR,IAAA;AAAA,MACA,MAAA,EAAQ,MAAA,IAAU,MAAA,EAAQ,MAAA,IAAU,MAAM,MAAA,IAAU,OAAA;AAAA,MACpD,QAAA,EAAU,QAAA,IAAY,IAAA,EAAM,aAAA,IAAiB;AAAA,KAC/C,CAAA;AAAA,IACA,CAAC,cAAA,EAAgB,IAAA,EAAM,MAAA,EAAQ,QAAA,EAAU,QAAQ,MAAM;AAAA,GACzD;AACA,EAAA,uBAAO,GAAA,CAAC,iBAAA,CAAkB,QAAA,EAAlB,EAA2B,OAAe,QAAA,EAAS,CAAA;AAC7D;AAGO,SAAS,aAAA,GAAwC;AACtD,EAAA,MAAM,GAAA,GAAM,WAAW,iBAAiB,CAAA;AACxC,EAAA,IAAI,CAAC,GAAA,EAAK,MAAM,IAAI,MAAM,wDAAwD,CAAA;AAClF,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,qBAAA,GAAuD;AACrE,EAAA,OAAO,WAAW,iBAAiB,CAAA;AACrC;AAGO,SAAS,mBAAA,GAAwC;AACtD,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,aAAA,EAAc;AACjC,EAAA,IAAI,CAAC,MAAA,EAAQ,MAAM,IAAI,MAAM,0EAA0E,CAAA;AACvG,EAAA,OAAO,MAAA;AACT;ACjDO,SAAS,KAAA,CAAuC;AAAA,EACrD,IAAA;AAAA,EACA,EAAA;AAAA,EACA,MAAA;AAAA,EACA,oBAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAmB;AACjB,EAAA,MAAM,KAAK,qBAAA,EAAsB;AACjC,EAAA,MAAM,IAAA,GAAO,WAAA,CAAY,IAAA,EAAM,MAAA,IAAU,IAAI,MAAA,EAAQ;AAAA,IACnD,GAAI,oBAAA,KAAyB,MAAA,GAAY,EAAE,oBAAA,KAAyB,EAAC;AAAA,IACrE,GAAI,eAAA,KAAoB,MAAA,GAAY,EAAE,eAAA,KAAoB,EAAC;AAAA,IAC3D,GAAI,eAAA,KAAoB,MAAA,GAAY,EAAE,eAAA,KAAoB;AAAC,GAC5D,CAAA;AACD,EAAA,OAAO,aAAA,CAAc,EAAA,IAAM,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAC/C;AAMO,IAAM,oBAAA,GAAuB,CAAC,GAAA,EAAK,GAAA,EAAK,KAAK,GAAA,EAAK,IAAA,EAAM,MAAM,IAAI;AASlE,IAAM,kBAAA,GAAkC,CAAC,GAAA,EAAK,KAAA,EAAO,MAAA,KAAW;AACrE,EAAA,IAAI;AACF,IAAA,MAAM,UAAA,GAAa,sBAAA,CAAuB,IAAA,CAAK,GAAG,CAAA;AAClD,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,GAAA,EAAK,yBAAyB,CAAA;AAClD,IAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,CAAA;AAC3C,IAAA,IAAI,QAAQ,GAAA,CAAI,YAAA,CAAa,IAAI,QAAA,EAAU,MAAA,CAAO,MAAM,CAAC,CAAA;AACzD,IAAA,OAAO,UAAA,GAAa,IAAI,QAAA,EAAS,GAAI,IAAI,QAAA,GAAW,GAAA,CAAI,SAAS,GAAA,CAAI,IAAA;AAAA,EACvE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAgBA,SAAS,WAAW,KAAA,EAA0C;AAC5D,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,MAAM,CAAC,CAAA,EAAG,CAAC,CAAA,GAAI,MAAM,KAAA,CAAM,MAAM,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,MAAA,CAAO,UAAA,CAAW,CAAC,CAAC,CAAA;AAClE,EAAA,OAAO,CAAA,IAAK,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,IAAA;AAC1B;AAGO,SAAS,KAAA,CAAM;AAAA,EACpB,IAAA;AAAA,EACA,KAAA,GAAQ,OAAA;AAAA,EACR,MAAA,GAAS,oBAAA;AAAA,EACT,MAAA,GAAS,kBAAA;AAAA,EACT,QAAA,GAAW,KAAA;AAAA,EACX,WAAA;AAAA,EACA,GAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,WAAW,CAAA,KAAM,IAAA,CAAK,KAAA,IAAS,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,MAAA,GAAS,IAAA,CAAA;AACjG,EAAA,IAAI,UAAA,GAAa,CAAC,GAAG,MAAM,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AACjD,EAAA,IAAI,KAAK,KAAA,EAAO;AACd,IAAA,MAAM,SAAS,UAAA,CAAW,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,IAAK,KAAK,KAAM,CAAA;AACxD,IAAA,UAAA,GAAa,MAAA,CAAO,MAAA,GAAS,MAAA,GAAS,CAAC,KAAK,KAAK,CAAA;AAAA,EACnD;AACA,EAAA,MAAM,MAAA,GAAS,MAAA,GACX,UAAA,CAAW,GAAA,CAAI,CAAC,MAAM,CAAA,EAAG,MAAA,CAAO,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG,KAAA,GAAQ,KAAK,KAAA,CAAM,CAAA,GAAI,KAAK,CAAA,GAAI,MAAS,CAAC,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,GAC1G,MAAA;AACJ,EAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,IAAS,UAAA,CAAW,UAAA,CAAW,SAAS,CAAC,CAAA;AAC5D,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,KAAW,KAAA,IAAS,QAAQ,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,KAAK,CAAA,GAAI,MAAA,CAAA;AAC5E,EAAA,uBACEA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAK,IAAA,CAAK,GAAA;AAAA,MACV,GAAA,EAAK,GAAA,IAAO,IAAA,CAAK,GAAA,IAAO,EAAA;AAAA,MACvB,GAAI,MAAA,GAAS,EAAE,MAAA,EAAQ,KAAA,KAAU,EAAC;AAAA,MAClC,GAAI,KAAA,GAAQ,EAAE,KAAA,KAAU,EAAC;AAAA,MACzB,GAAI,MAAA,GAAS,EAAE,MAAA,KAAW,EAAC;AAAA,MAC5B,OAAA,EAAS,OAAA,KAAY,QAAA,GAAW,OAAA,GAAU,MAAA,CAAA;AAAA,MAC1C,QAAA,EAAU,QAAA,KAAa,QAAA,GAAW,MAAA,GAAS,OAAA,CAAA;AAAA,MAC1C,GAAI,QAAA,GAAW,EAAE,aAAA,EAAe,MAAA,KAAoB,EAAC;AAAA,MACrD,GAAG;AAAA;AAAA,GACN;AAEJ;AAMA,IAAM,eAAA,GAAkB;AAAA,EACtB,QAAA,EAAU,UAAA;AAAA,EACV,KAAA,EAAO,CAAA;AAAA,EACP,MAAA,EAAQ,CAAA;AAAA,EACR,OAAA,EAAS,CAAA;AAAA,EACT,MAAA,EAAQ,EAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,IAAA,EAAM,kBAAA;AAAA,EACN,UAAA,EAAY,QAAA;AAAA,EACZ,MAAA,EAAQ;AACV,CAAA;AAkBO,SAAS,YAAA,CAAa;AAAA,EAC3B,OAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA,GAAY,OAAA;AAAA,EACZ,cAAA,GAAiB,iBAAA;AAAA,EACjB,MAAA;AAAA,EACA,oBAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAsB;AACpB,EAAA,IAAI,KAAA,GAA0B,IAAA;AAC9B,EAAA,IAAI,SAAA,GAA8B,IAAA;AAClC,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,KAAA,GAAQ,OAAA,CAAQ,KAAA;AAChB,IAAA,SAAA,GAAY,OAAA,CAAQ,gBAAA;AAAA,EACtB,WAAW,OAAA,EAAS;AAClB,IAAA,KAAA,GAAQ,QAAQ,WAAA,CAAY,GAAA;AAC5B,IAAA,SAAA,GAAY,OAAA,CAAQ,wBAAwB,GAAA,IAAO,IAAA;AACnD,IAAA,OAAA,GAAU,QAAQ,WAAA,CAAY,GAAA,CAAI,MAAA,KAAW,OAAA,CAAQ,YAAY,GAAA,CAAI,MAAA;AAAA,EACvE;AACA,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,MAAM,SAAS,SAAA,KAAc,IAAA,IAAQ,WAAW,SAAS,CAAA,GAAI,WAAW,KAAK,CAAA;AAC7E,EAAA,MAAM,OAAO,EAAE,GAAI,MAAA,GAAS,EAAE,QAAO,GAAI,EAAC,EAAI,GAAI,yBAAyB,MAAA,GAAY,EAAE,oBAAA,EAAqB,GAAI,EAAC,EAAG;AACtH,EAAA,4BACG,KAAA,EAAA,EAAI,cAAA,EAAc,MAAA,IAAU,MAAA,EAAY,GAAG,IAAA,EACzC,QAAA,EAAA;AAAA,IAAA,OAAA,mBAAU,IAAA,CAAC,MAAA,EAAA,EAAK,mBAAA,EAAkB,EAAA,EAAI,QAAA,EAAA;AAAA,MAAA,SAAA;AAAA,MAAU;AAAA,KAAA,EAAC,CAAA,GAAU,IAAA;AAAA,oBAC5DA,IAAC,KAAA,EAAA,EAAM,IAAA,EAAM,OAAO,YAAA,EAAW,EAAA,EAAI,GAAG,IAAA,EAAM,CAAA;AAAA,IAC3C,MAAA,IAAU,4BACT,IAAA,CAAA,QAAA,EAAA,EACG,QAAA,EAAA;AAAA,MAAA,GAAA;AAAA,sBACD,IAAA,CAAC,GAAA,EAAA,EAAE,uBAAA,EAAsB,EAAA,EACvB,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,MAAA,EAAA,EAAK,OAAO,eAAA,EAAkB,QAAA,EAAA;AAAA,UAAA,cAAA;AAAA,UAAe;AAAA,SAAA,EAAE,CAAA;AAAA,wBAChDA,GAAAA,CAAC,KAAA,EAAA,EAAM,IAAA,EAAM,SAAA,EAAY,GAAG,IAAA,EAAM;AAAA,OAAA,EACpC;AAAA,KAAA,EACF,CAAA,GACE;AAAA,GAAA,EACN,CAAA;AAEJ","file":"chunk-C6QZNKRE.js","sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { createStorefrontClient, type Shop, type StorefrontClient, type StorefrontClientOptions } from '@asteby/storefront'\n\nexport type StorefrontContextValue = {\n /** Client for browser-side calls (use a public `pk_` token). */\n client: StorefrontClient | null\n shop: Shop | null\n /** BCP 47 locale used for formatting (defaults to `shop.locale`, then `es-MX`). */\n locale: string\n /** ISO 4217 currency (defaults to `shop.currency_code`, then `MXN`). */\n currency: string\n}\n\nconst StorefrontContext = createContext<StorefrontContextValue | null>(null)\n\nexport type StorefrontProviderProps = {\n /** A ready client. Takes precedence over `config`. */\n client?: StorefrontClient\n /** Options to create the browser client (use a `pk_` token). */\n config?: StorefrontClientOptions\n /** Shop info loaded on the server (root loader). */\n shop?: Shop | null\n locale?: string\n currency?: string\n children?: ReactNode\n}\n\n/**\n * Makes the storefront client, shop, locale and currency available to the\n * tree. SSR-safe: creating the client does not touch `window`.\n */\nexport function StorefrontProvider({ client, config, shop = null, locale, currency, children }: StorefrontProviderProps) {\n const configKey = config ? `${config.baseUrl}|${config.token}|${config.locale ?? ''}|${config.customerToken ?? ''}` : ''\n const resolvedClient = useMemo(\n () => client ?? (config ? createStorefrontClient(config) : null),\n [client, configKey],\n )\n const value = useMemo<StorefrontContextValue>(\n () => ({\n client: resolvedClient,\n shop,\n locale: locale ?? config?.locale ?? shop?.locale ?? 'es-MX',\n currency: currency ?? shop?.currency_code ?? 'MXN',\n }),\n [resolvedClient, shop, locale, currency, config?.locale],\n )\n return <StorefrontContext.Provider value={value}>{children}</StorefrontContext.Provider>\n}\n\n/** Storefront context. Throws outside `<StorefrontProvider>`. */\nexport function useStorefront(): StorefrontContextValue {\n const ctx = useContext(StorefrontContext)\n if (!ctx) throw new Error('useStorefront must be used inside <StorefrontProvider>')\n return ctx\n}\n\n/** Storefront context or `null` outside a provider (for components that work standalone). */\nexport function useOptionalStorefront(): StorefrontContextValue | null {\n return useContext(StorefrontContext)\n}\n\n/** The client from context; throws when the provider has none. */\nexport function useStorefrontClient(): StorefrontClient {\n const { client } = useStorefront()\n if (!client) throw new Error('<StorefrontProvider> needs a `client` or `config` for browser-side calls')\n return client\n}\n","import { createElement, type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react'\nimport { formatMoney, parseMoney, type FormatMoneyOptions, type Image as ImageData, type Money as MoneyData, type Product, type ProductVariant } from '@asteby/storefront'\nimport { useOptionalStorefront } from './context.js'\n\n// ---------------------------------------------------------------------------\n// Money\n// ---------------------------------------------------------------------------\n\nexport type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {\n data: MoneyData\n /** Element to render (default `span`). */\n as?: As\n /** Overrides the provider locale. */\n locale?: string\n} & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>\n\n/** Formats `Money` with the provider locale (or `locale`). */\nexport function Money<As extends ElementType = 'span'>({\n data,\n as,\n locale,\n withoutTrailingZeros,\n withoutCurrency,\n currencyDisplay,\n ...rest\n}: MoneyProps<As>) {\n const sf = useOptionalStorefront()\n const text = formatMoney(data, locale ?? sf?.locale, {\n ...(withoutTrailingZeros !== undefined ? { withoutTrailingZeros } : {}),\n ...(withoutCurrency !== undefined ? { withoutCurrency } : {}),\n ...(currencyDisplay !== undefined ? { currencyDisplay } : {}),\n })\n return createElement(as ?? 'span', rest, text)\n}\n\n// ---------------------------------------------------------------------------\n// Image\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600] as const\n\n/** Builds the URL for one srcset width. */\nexport type ImageLoader = (src: string, width: number, height?: number) => string\n\n/**\n * Default loader: adds `?width=<w>` (the convention of most image CDNs and of\n * the OPS image proxy). URLs that cannot be parsed are returned unchanged.\n */\nexport const defaultImageLoader: ImageLoader = (src, width, height) => {\n try {\n const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src)\n const url = new URL(src, 'http://relative.invalid')\n url.searchParams.set('width', String(width))\n if (height) url.searchParams.set('height', String(height))\n return isAbsolute ? url.toString() : url.pathname + url.search + url.hash\n } catch {\n return src\n }\n}\n\nexport type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {\n data: ImageData\n /** `sizes` attribute (default `100vw`). */\n sizes?: string\n /** Candidate widths for `srcset` (capped at the intrinsic width when known). */\n widths?: readonly number[]\n /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */\n loader?: ImageLoader | null\n /** Above-the-fold image: eager loading and `fetchpriority=\"high\"`. */\n priority?: boolean\n /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */\n aspectRatio?: string\n}\n\nfunction parseRatio(ratio: string | undefined): number | null {\n if (!ratio) return null\n const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n))\n return w && h ? w / h : null\n}\n\n/** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */\nexport function Image({\n data,\n sizes = '100vw',\n widths = DEFAULT_IMAGE_WIDTHS,\n loader = defaultImageLoader,\n priority = false,\n aspectRatio,\n alt,\n loading,\n decoding,\n ...rest\n}: ImageProps) {\n const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null)\n let candidates = [...widths].sort((a, b) => a - b)\n if (data.width) {\n const capped = candidates.filter((w) => w <= data.width!)\n candidates = capped.length ? capped : [data.width]\n }\n const srcSet = loader\n ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : undefined)} ${w}w`).join(', ')\n : undefined\n const width = data.width ?? candidates[candidates.length - 1]\n const height = data.height ?? (ratio && width ? Math.round(width / ratio) : undefined)\n return (\n <img\n src={data.url}\n alt={alt ?? data.alt ?? ''}\n {...(srcSet ? { srcSet, sizes } : {})}\n {...(width ? { width } : {})}\n {...(height ? { height } : {})}\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? (priority ? 'auto' : 'async')}\n {...(priority ? { fetchPriority: 'high' as const } : {})}\n {...rest}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// ProductPrice\n// ---------------------------------------------------------------------------\n\nconst VISUALLY_HIDDEN = {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: 0,\n} as const\n\nexport type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {\n product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null\n variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null\n /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */\n fromLabel?: ReactNode\n /** Accessible label for the compare-at price (default `Precio anterior`). */\n compareAtLabel?: string\n locale?: string\n withoutTrailingZeros?: boolean\n}\n\n/**\n * Price with optional compare-at (strikethrough). With a `variant` it shows\n * that variant; otherwise the product's minimum price (prefixed with\n * `fromLabel` when prices vary).\n */\nexport function ProductPrice({\n product,\n variant,\n fromLabel = 'Desde',\n compareAtLabel = 'Precio anterior',\n locale,\n withoutTrailingZeros,\n ...rest\n}: ProductPriceProps) {\n let price: MoneyData | null = null\n let compareAt: MoneyData | null = null\n let isRange = false\n if (variant) {\n price = variant.price\n compareAt = variant.compare_at_price\n } else if (product) {\n price = product.price_range.min\n compareAt = product.compare_at_price_range?.min ?? null\n isRange = product.price_range.min.amount !== product.price_range.max.amount\n }\n if (!price) return null\n const onSale = compareAt !== null && parseMoney(compareAt) > parseMoney(price)\n const opts = { ...(locale ? { locale } : {}), ...(withoutTrailingZeros !== undefined ? { withoutTrailingZeros } : {}) }\n return (\n <div data-on-sale={onSale || undefined} {...rest}>\n {isRange ? <span data-price-prefix=\"\">{fromLabel} </span> : null}\n <Money data={price} data-price=\"\" {...opts} />\n {onSale && compareAt ? (\n <>\n {' '}\n <s data-compare-at-price=\"\">\n <span style={VISUALLY_HIDDEN}>{compareAtLabel}: </span>\n <Money data={compareAt} {...opts} />\n </s>\n </>\n ) : null}\n </div>\n )\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
3
+ import { Image as Image$1, FormatMoneyOptions, Money as Money$1, Product, ProductVariant } from '@asteby/storefront';
4
+
5
+ type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {
6
+ data: Money$1;
7
+ /** Element to render (default `span`). */
8
+ as?: As;
9
+ /** Overrides the provider locale. */
10
+ locale?: string;
11
+ } & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>;
12
+ /** Formats `Money` with the provider locale (or `locale`). */
13
+ declare function Money<As extends ElementType = 'span'>({ data, as, locale, withoutTrailingZeros, withoutCurrency, currencyDisplay, ...rest }: MoneyProps<As>): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
14
+ declare const DEFAULT_IMAGE_WIDTHS: readonly [320, 480, 640, 768, 1024, 1280, 1600];
15
+ /** Builds the URL for one srcset width. */
16
+ type ImageLoader = (src: string, width: number, height?: number) => string;
17
+ /**
18
+ * Default loader: adds `?width=<w>` (the convention of most image CDNs and of
19
+ * the OPS image proxy). URLs that cannot be parsed are returned unchanged.
20
+ */
21
+ declare const defaultImageLoader: ImageLoader;
22
+ type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {
23
+ data: Image$1;
24
+ /** `sizes` attribute (default `100vw`). */
25
+ sizes?: string;
26
+ /** Candidate widths for `srcset` (capped at the intrinsic width when known). */
27
+ widths?: readonly number[];
28
+ /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */
29
+ loader?: ImageLoader | null;
30
+ /** Above-the-fold image: eager loading and `fetchpriority="high"`. */
31
+ priority?: boolean;
32
+ /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */
33
+ aspectRatio?: string;
34
+ };
35
+ /** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */
36
+ declare function Image({ data, sizes, widths, loader, priority, aspectRatio, alt, loading, decoding, ...rest }: ImageProps): react.JSX.Element;
37
+ type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
38
+ product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null;
39
+ variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null;
40
+ /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */
41
+ fromLabel?: ReactNode;
42
+ /** Accessible label for the compare-at price (default `Precio anterior`). */
43
+ compareAtLabel?: string;
44
+ locale?: string;
45
+ withoutTrailingZeros?: boolean;
46
+ };
47
+ /**
48
+ * Price with optional compare-at (strikethrough). With a `variant` it shows
49
+ * that variant; otherwise the product's minimum price (prefixed with
50
+ * `fromLabel` when prices vary).
51
+ */
52
+ declare function ProductPrice({ product, variant, fromLabel, compareAtLabel, locale, withoutTrailingZeros, ...rest }: ProductPriceProps): react.JSX.Element | null;
53
+
54
+ export { DEFAULT_IMAGE_WIDTHS as D, Image as I, Money as M, ProductPrice as P, type ImageLoader as a, type ImageProps as b, type MoneyProps as c, type ProductPriceProps as d, defaultImageLoader as e };
@@ -0,0 +1,54 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';
3
+ import { Image as Image$1, FormatMoneyOptions, Money as Money$1, Product, ProductVariant } from '@asteby/storefront';
4
+
5
+ type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {
6
+ data: Money$1;
7
+ /** Element to render (default `span`). */
8
+ as?: As;
9
+ /** Overrides the provider locale. */
10
+ locale?: string;
11
+ } & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>;
12
+ /** Formats `Money` with the provider locale (or `locale`). */
13
+ declare function Money<As extends ElementType = 'span'>({ data, as, locale, withoutTrailingZeros, withoutCurrency, currencyDisplay, ...rest }: MoneyProps<As>): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
14
+ declare const DEFAULT_IMAGE_WIDTHS: readonly [320, 480, 640, 768, 1024, 1280, 1600];
15
+ /** Builds the URL for one srcset width. */
16
+ type ImageLoader = (src: string, width: number, height?: number) => string;
17
+ /**
18
+ * Default loader: adds `?width=<w>` (the convention of most image CDNs and of
19
+ * the OPS image proxy). URLs that cannot be parsed are returned unchanged.
20
+ */
21
+ declare const defaultImageLoader: ImageLoader;
22
+ type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {
23
+ data: Image$1;
24
+ /** `sizes` attribute (default `100vw`). */
25
+ sizes?: string;
26
+ /** Candidate widths for `srcset` (capped at the intrinsic width when known). */
27
+ widths?: readonly number[];
28
+ /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */
29
+ loader?: ImageLoader | null;
30
+ /** Above-the-fold image: eager loading and `fetchpriority="high"`. */
31
+ priority?: boolean;
32
+ /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */
33
+ aspectRatio?: string;
34
+ };
35
+ /** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */
36
+ declare function Image({ data, sizes, widths, loader, priority, aspectRatio, alt, loading, decoding, ...rest }: ImageProps): react.JSX.Element;
37
+ type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
38
+ product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null;
39
+ variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null;
40
+ /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */
41
+ fromLabel?: ReactNode;
42
+ /** Accessible label for the compare-at price (default `Precio anterior`). */
43
+ compareAtLabel?: string;
44
+ locale?: string;
45
+ withoutTrailingZeros?: boolean;
46
+ };
47
+ /**
48
+ * Price with optional compare-at (strikethrough). With a `variant` it shows
49
+ * that variant; otherwise the product's minimum price (prefixed with
50
+ * `fromLabel` when prices vary).
51
+ */
52
+ declare function ProductPrice({ product, variant, fromLabel, compareAtLabel, locale, withoutTrailingZeros, ...rest }: ProductPriceProps): react.JSX.Element | null;
53
+
54
+ export { DEFAULT_IMAGE_WIDTHS as D, Image as I, Money as M, ProductPrice as P, type ImageLoader as a, type ImageProps as b, type MoneyProps as c, type ProductPriceProps as d, defaultImageLoader as e };
package/dist/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ComponentPropsWithoutRef, ElementType, ComponentType, FormEvent } from 'react';
3
- import { StorefrontClient, Shop, StorefrontClientOptions, Cart, CartLineInput, CartLineMerchandise, CartLineUpdateInput, CartBuyerInput, AddressInput, DeliveryInput, DeliveryOptions, Image as Image$1, FormatMoneyOptions, Money as Money$1, Product, ProductVariant, Collection, Section, Theme, GeoPoint, ShopBranch } from '@asteby/storefront';
2
+ import { ReactNode, ComponentPropsWithoutRef, ComponentType, FormEvent } from 'react';
3
+ import { StorefrontClient, Shop, StorefrontClientOptions, Cart, CartLineInput, CartLineMerchandise, CartLineUpdateInput, CartBuyerInput, AddressInput, DeliveryInput, DeliveryOptions, ProductVariant, Product, Image, Collection, Section, Theme, GeoPoint, ShopBranch, Money } from '@asteby/storefront';
4
+ export { D as DEFAULT_IMAGE_WIDTHS, I as Image, a as ImageLoader, b as ImageProps, M as Money, c as MoneyProps, P as ProductPrice, d as ProductPriceProps, e as defaultImageLoader } from './components-B5XhSH-m.cjs';
4
5
  import { OpeningHours, GeoPoint as GeoPoint$1, OpenStatus as OpenStatus$1 } from '@asteby/storefront/geo';
5
6
 
6
7
  type StorefrontContextValue = {
@@ -124,55 +125,6 @@ declare function useCart(): CartContextValue;
124
125
  /** Cart context or `null` outside a provider. */
125
126
  declare function useOptionalCart(): CartContextValue | null;
126
127
 
127
- type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {
128
- data: Money$1;
129
- /** Element to render (default `span`). */
130
- as?: As;
131
- /** Overrides the provider locale. */
132
- locale?: string;
133
- } & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>;
134
- /** Formats `Money` with the provider locale (or `locale`). */
135
- declare function Money<As extends ElementType = 'span'>({ data, as, locale, withoutTrailingZeros, withoutCurrency, currencyDisplay, ...rest }: MoneyProps<As>): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
136
- declare const DEFAULT_IMAGE_WIDTHS: readonly [320, 480, 640, 768, 1024, 1280, 1600];
137
- /** Builds the URL for one srcset width. */
138
- type ImageLoader = (src: string, width: number, height?: number) => string;
139
- /**
140
- * Default loader: adds `?width=<w>` (the convention of most image CDNs and of
141
- * the OPS image proxy). URLs that cannot be parsed are returned unchanged.
142
- */
143
- declare const defaultImageLoader: ImageLoader;
144
- type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {
145
- data: Image$1;
146
- /** `sizes` attribute (default `100vw`). */
147
- sizes?: string;
148
- /** Candidate widths for `srcset` (capped at the intrinsic width when known). */
149
- widths?: readonly number[];
150
- /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */
151
- loader?: ImageLoader | null;
152
- /** Above-the-fold image: eager loading and `fetchpriority="high"`. */
153
- priority?: boolean;
154
- /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */
155
- aspectRatio?: string;
156
- };
157
- /** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */
158
- declare function Image({ data, sizes, widths, loader, priority, aspectRatio, alt, loading, decoding, ...rest }: ImageProps): react.JSX.Element;
159
- type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
160
- product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null;
161
- variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null;
162
- /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */
163
- fromLabel?: ReactNode;
164
- /** Accessible label for the compare-at price (default `Precio anterior`). */
165
- compareAtLabel?: string;
166
- locale?: string;
167
- withoutTrailingZeros?: boolean;
168
- };
169
- /**
170
- * Price with optional compare-at (strikethrough). With a `variant` it shows
171
- * that variant; otherwise the product's minimum price (prefixed with
172
- * `fromLabel` when prices vary).
173
- */
174
- declare function ProductPrice({ product, variant, fromLabel, compareAtLabel, locale, withoutTrailingZeros, ...rest }: ProductPriceProps): react.JSX.Element | null;
175
-
176
128
  type ProductOptionValueState = {
177
129
  value: string;
178
130
  /** Part of the current selection. */
@@ -285,7 +237,7 @@ type SeoMetaInput = {
285
237
  description?: string | null;
286
238
  /** Canonical absolute URL. */
287
239
  url?: string | null;
288
- image?: Image$1 | string | null;
240
+ image?: Image | string | null;
289
241
  /** `og:type` (default `website`). */
290
242
  type?: string;
291
243
  siteName?: string | null;
@@ -665,7 +617,7 @@ declare function DirectionsLink({ location, provider, children, ...rest }: Direc
665
617
  type PickupInfo<L extends ShopBranch = ShopBranch> = {
666
618
  location: L;
667
619
  /** `null` = free. Amount as sent by OPS; nothing is calculated here. */
668
- fee: Money$1 | null;
620
+ fee: Money | null;
669
621
  free: boolean;
670
622
  /** Formatted `fee` (`null` when free). */
671
623
  feeText: string | null;
@@ -703,4 +655,4 @@ type NearestLocationFormProps<L extends ShopBranch = ShopBranch> = {
703
655
  */
704
656
  declare function NearestLocationForm<L extends ShopBranch = ShopBranch>({ locations, action, name, defaultValue, onSubmitPostalCode, onLocate, labels, children, ...rest }: NearestLocationFormProps<L>): react.JSX.Element | null;
705
657
 
706
- export { AddToCartButton, type AddToCartButtonProps, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartProvider, type CartProviderProps, type CartStatus, DEFAULT_IMAGE_WIDTHS, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, Image, type ImageLoader, type ImageProps, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, Money, type MoneyProps, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, ProductPrice, type ProductPriceProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defaultImageLoader, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };
658
+ export { AddToCartButton, type AddToCartButtonProps, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ComponentPropsWithoutRef, ElementType, ComponentType, FormEvent } from 'react';
3
- import { StorefrontClient, Shop, StorefrontClientOptions, Cart, CartLineInput, CartLineMerchandise, CartLineUpdateInput, CartBuyerInput, AddressInput, DeliveryInput, DeliveryOptions, Image as Image$1, FormatMoneyOptions, Money as Money$1, Product, ProductVariant, Collection, Section, Theme, GeoPoint, ShopBranch } from '@asteby/storefront';
2
+ import { ReactNode, ComponentPropsWithoutRef, ComponentType, FormEvent } from 'react';
3
+ import { StorefrontClient, Shop, StorefrontClientOptions, Cart, CartLineInput, CartLineMerchandise, CartLineUpdateInput, CartBuyerInput, AddressInput, DeliveryInput, DeliveryOptions, ProductVariant, Product, Image, Collection, Section, Theme, GeoPoint, ShopBranch, Money } from '@asteby/storefront';
4
+ export { D as DEFAULT_IMAGE_WIDTHS, I as Image, a as ImageLoader, b as ImageProps, M as Money, c as MoneyProps, P as ProductPrice, d as ProductPriceProps, e as defaultImageLoader } from './components-B5XhSH-m.js';
4
5
  import { OpeningHours, GeoPoint as GeoPoint$1, OpenStatus as OpenStatus$1 } from '@asteby/storefront/geo';
5
6
 
6
7
  type StorefrontContextValue = {
@@ -124,55 +125,6 @@ declare function useCart(): CartContextValue;
124
125
  /** Cart context or `null` outside a provider. */
125
126
  declare function useOptionalCart(): CartContextValue | null;
126
127
 
127
- type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {
128
- data: Money$1;
129
- /** Element to render (default `span`). */
130
- as?: As;
131
- /** Overrides the provider locale. */
132
- locale?: string;
133
- } & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>;
134
- /** Formats `Money` with the provider locale (or `locale`). */
135
- declare function Money<As extends ElementType = 'span'>({ data, as, locale, withoutTrailingZeros, withoutCurrency, currencyDisplay, ...rest }: MoneyProps<As>): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
136
- declare const DEFAULT_IMAGE_WIDTHS: readonly [320, 480, 640, 768, 1024, 1280, 1600];
137
- /** Builds the URL for one srcset width. */
138
- type ImageLoader = (src: string, width: number, height?: number) => string;
139
- /**
140
- * Default loader: adds `?width=<w>` (the convention of most image CDNs and of
141
- * the OPS image proxy). URLs that cannot be parsed are returned unchanged.
142
- */
143
- declare const defaultImageLoader: ImageLoader;
144
- type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {
145
- data: Image$1;
146
- /** `sizes` attribute (default `100vw`). */
147
- sizes?: string;
148
- /** Candidate widths for `srcset` (capped at the intrinsic width when known). */
149
- widths?: readonly number[];
150
- /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */
151
- loader?: ImageLoader | null;
152
- /** Above-the-fold image: eager loading and `fetchpriority="high"`. */
153
- priority?: boolean;
154
- /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */
155
- aspectRatio?: string;
156
- };
157
- /** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */
158
- declare function Image({ data, sizes, widths, loader, priority, aspectRatio, alt, loading, decoding, ...rest }: ImageProps): react.JSX.Element;
159
- type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {
160
- product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null;
161
- variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null;
162
- /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */
163
- fromLabel?: ReactNode;
164
- /** Accessible label for the compare-at price (default `Precio anterior`). */
165
- compareAtLabel?: string;
166
- locale?: string;
167
- withoutTrailingZeros?: boolean;
168
- };
169
- /**
170
- * Price with optional compare-at (strikethrough). With a `variant` it shows
171
- * that variant; otherwise the product's minimum price (prefixed with
172
- * `fromLabel` when prices vary).
173
- */
174
- declare function ProductPrice({ product, variant, fromLabel, compareAtLabel, locale, withoutTrailingZeros, ...rest }: ProductPriceProps): react.JSX.Element | null;
175
-
176
128
  type ProductOptionValueState = {
177
129
  value: string;
178
130
  /** Part of the current selection. */
@@ -285,7 +237,7 @@ type SeoMetaInput = {
285
237
  description?: string | null;
286
238
  /** Canonical absolute URL. */
287
239
  url?: string | null;
288
- image?: Image$1 | string | null;
240
+ image?: Image | string | null;
289
241
  /** `og:type` (default `website`). */
290
242
  type?: string;
291
243
  siteName?: string | null;
@@ -665,7 +617,7 @@ declare function DirectionsLink({ location, provider, children, ...rest }: Direc
665
617
  type PickupInfo<L extends ShopBranch = ShopBranch> = {
666
618
  location: L;
667
619
  /** `null` = free. Amount as sent by OPS; nothing is calculated here. */
668
- fee: Money$1 | null;
620
+ fee: Money | null;
669
621
  free: boolean;
670
622
  /** Formatted `fee` (`null` when free). */
671
623
  feeText: string | null;
@@ -703,4 +655,4 @@ type NearestLocationFormProps<L extends ShopBranch = ShopBranch> = {
703
655
  */
704
656
  declare function NearestLocationForm<L extends ShopBranch = ShopBranch>({ locations, action, name, defaultValue, onSubmitPostalCode, onLocate, labels, children, ...rest }: NearestLocationFormProps<L>): react.JSX.Element | null;
705
657
 
706
- export { AddToCartButton, type AddToCartButtonProps, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartProvider, type CartProviderProps, type CartStatus, DEFAULT_IMAGE_WIDTHS, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, Image, type ImageLoader, type ImageProps, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, Money, type MoneyProps, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, ProductPrice, type ProductPriceProps, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defaultImageLoader, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };
658
+ export { AddToCartButton, type AddToCartButtonProps, type BreadcrumbItem, CART_COOKIE_NAME, type CartContextValue, type CartCookieOptions, type CartLineAddInput, CartProvider, type CartProviderProps, type CartStatus, DirectionsLink, type DirectionsLinkProps, type FaqItem, type GeolocationErrorCode, type GeolocationFailure, type GeolocationPermission, type InferSectionProps, JsonLd, type JsonLdObject, type JsonLdProps, type LocalBusinessOptions, type LocationItemState, LocationList, type LocationListProps, LocationPicker, type LocationPickerProps, type MetaDescriptor, type NearbyLocation, NearestLocationForm, type NearestLocationFormLabels, type NearestLocationFormProps, OpenStatus, type OpenStatusLabels, type OpenStatusProps, type PickupInfo, PickupOption, type PickupOptionProps, type ProductOptionState, type ProductOptionValueState, type ProductOptionsState, SELECTED_LOCATION_PARAM, SELECTED_LOCATION_STORAGE_KEY, type SectionComponentProps, type SectionDefinition, type SectionPropError, type SectionPropKind, type SectionPropSchema, SectionRegistry, type SectionSchema, Sections, type SectionsProps, type SeoMetaInput, type StorefrontContextValue, StorefrontProvider, type StorefrontProviderProps, type ThemeCssOptions, ThemeStyle, type ThemeStyleProps, type UseGeolocationOptions, type UseGeolocationResult, type UseNearestLocationsOptions, type UseNearestLocationsResult, type UseProductOptionsConfig, type UseSelectedLocationOptions, type UseSelectedLocationResult, VariantSelector, type VariantSelectorProps, type VariantSelectorRenderProps, breadcrumbJsonLd, collectionJsonLd, collectionMeta, createSectionRegistry, defineSection, faqJsonLd, getCartIdFromCookie, isSafeCssValue, localBusinessJsonLd, openingHoursJsonLd, optimisticCart, organizationJsonLd, productJsonLd, productMeta, readCartCookie, readSelectedLocationId, resolveSectionProps, sectionJsonSchema, seoMeta, serializeCartCookie, serializeJsonLd, themeFontsHref, themeToCss, themeToCssVariables, toMerchandise, tokenName, useCart, useGeolocation, useNearestLocations, useOptionalCart, useOptionalStorefront, useProductOptions, useSelectedLocation, useStorefront, useStorefrontClient, writeCartCookie };