@decocms/apps 0.23.3 → 0.24.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.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/commerce/components/Image.tsx +129 -143
  3. package/commerce/components/JsonLd.tsx +192 -201
  4. package/commerce/components/Picture.tsx +65 -75
  5. package/commerce/sdk/analytics.ts +15 -15
  6. package/commerce/sdk/formatPrice.ts +13 -16
  7. package/commerce/sdk/url.ts +7 -7
  8. package/commerce/sdk/useOffer.ts +46 -57
  9. package/commerce/sdk/useVariantPossibilities.ts +25 -25
  10. package/commerce/types/commerce.ts +868 -875
  11. package/commerce/utils/canonical.ts +5 -8
  12. package/commerce/utils/constants.ts +5 -6
  13. package/commerce/utils/filters.ts +4 -4
  14. package/commerce/utils/productToAnalyticsItem.ts +52 -56
  15. package/commerce/utils/stateByZip.ts +42 -42
  16. package/package.json +23 -4
  17. package/shopify/actions/cart/addItems.ts +24 -25
  18. package/shopify/actions/cart/updateCoupons.ts +19 -20
  19. package/shopify/actions/cart/updateItems.ts +19 -20
  20. package/shopify/actions/user/signIn.ts +25 -30
  21. package/shopify/actions/user/signUp.ts +19 -24
  22. package/shopify/client.ts +24 -24
  23. package/shopify/index.ts +20 -18
  24. package/shopify/init.ts +18 -21
  25. package/shopify/loaders/ProductDetailsPage.ts +16 -20
  26. package/shopify/loaders/ProductList.ts +66 -69
  27. package/shopify/loaders/ProductListingPage.ts +150 -158
  28. package/shopify/loaders/RelatedProducts.ts +24 -27
  29. package/shopify/loaders/cart.ts +53 -52
  30. package/shopify/loaders/shop.ts +22 -27
  31. package/shopify/loaders/user.ts +27 -32
  32. package/shopify/utils/admin/admin.ts +33 -34
  33. package/shopify/utils/admin/queries.ts +2 -2
  34. package/shopify/utils/cart.ts +18 -14
  35. package/shopify/utils/cookies.ts +62 -65
  36. package/shopify/utils/enums.ts +424 -424
  37. package/shopify/utils/graphql.ts +44 -55
  38. package/shopify/utils/storefront/queries.ts +24 -29
  39. package/shopify/utils/storefront/storefront.graphql.gen.ts +55 -55
  40. package/shopify/utils/transform.ts +370 -376
  41. package/shopify/utils/types.ts +118 -118
  42. package/shopify/utils/user.ts +11 -11
  43. package/shopify/utils/utils.ts +135 -140
  44. package/vtex/actions/address.ts +86 -86
  45. package/vtex/actions/auth.ts +14 -27
  46. package/vtex/actions/checkout.ts +36 -49
  47. package/vtex/actions/masterData.ts +10 -27
  48. package/vtex/actions/misc.ts +101 -111
  49. package/vtex/actions/newsletter.ts +48 -52
  50. package/vtex/actions/orders.ts +13 -16
  51. package/vtex/actions/profile.ts +55 -55
  52. package/vtex/actions/session.ts +36 -35
  53. package/vtex/actions/trigger.ts +25 -25
  54. package/vtex/actions/wishlist.ts +51 -53
  55. package/vtex/client.ts +14 -42
  56. package/vtex/hooks/index.ts +4 -4
  57. package/vtex/hooks/useAutocomplete.ts +42 -48
  58. package/vtex/hooks/useCart.ts +153 -165
  59. package/vtex/hooks/useUser.ts +40 -40
  60. package/vtex/hooks/useWishlist.ts +70 -70
  61. package/vtex/inline-loaders/productDetailsPage.ts +1 -3
  62. package/vtex/inline-loaders/productList.ts +121 -127
  63. package/vtex/inline-loaders/productListingPage.ts +10 -34
  64. package/vtex/inline-loaders/relatedProducts.ts +1 -3
  65. package/vtex/inline-loaders/suggestions.ts +36 -39
  66. package/vtex/inline-loaders/workflowProducts.ts +45 -49
  67. package/vtex/invoke.ts +159 -194
  68. package/vtex/loaders/address.ts +49 -54
  69. package/vtex/loaders/brands.ts +19 -26
  70. package/vtex/loaders/cart.ts +24 -21
  71. package/vtex/loaders/catalog.ts +51 -53
  72. package/vtex/loaders/collections.ts +25 -27
  73. package/vtex/loaders/legacy.ts +487 -534
  74. package/vtex/loaders/logistics.ts +33 -37
  75. package/vtex/loaders/navbar.ts +5 -8
  76. package/vtex/loaders/orders.ts +28 -39
  77. package/vtex/loaders/pageType.ts +41 -35
  78. package/vtex/loaders/payment.ts +27 -37
  79. package/vtex/loaders/profile.ts +38 -38
  80. package/vtex/loaders/promotion.ts +5 -8
  81. package/vtex/loaders/search.ts +56 -59
  82. package/vtex/loaders/session.ts +22 -30
  83. package/vtex/loaders/user.ts +39 -41
  84. package/vtex/loaders/wishlist.ts +35 -35
  85. package/vtex/loaders/wishlistProducts.ts +3 -15
  86. package/vtex/loaders/workflow.ts +220 -227
  87. package/vtex/middleware.ts +116 -119
  88. package/vtex/types.ts +201 -201
  89. package/vtex/utils/batch.ts +13 -16
  90. package/vtex/utils/cookies.ts +76 -80
  91. package/vtex/utils/enrichment.ts +62 -42
  92. package/vtex/utils/fetchCache.ts +1 -4
  93. package/vtex/utils/index.ts +6 -6
  94. package/vtex/utils/intelligentSearch.ts +48 -57
  95. package/vtex/utils/legacy.ts +108 -124
  96. package/vtex/utils/pickAndOmit.ts +15 -20
  97. package/vtex/utils/proxy.ts +136 -146
  98. package/vtex/utils/resourceRange.ts +3 -3
  99. package/vtex/utils/segment.ts +100 -111
  100. package/vtex/utils/similars.ts +1 -2
  101. package/vtex/utils/sitemap.ts +91 -91
  102. package/vtex/utils/slugCache.ts +2 -6
  103. package/vtex/utils/slugify.ts +9 -9
  104. package/vtex/utils/transform.ts +1012 -1105
  105. package/vtex/utils/types.ts +1381 -1381
  106. package/vtex/utils/vtexId.ts +44 -47
  107. package/.github/workflows/release.yml +0 -34
  108. package/.releaserc.json +0 -28
  109. package/knip.json +0 -19
  110. package/tsconfig.json +0 -11
@@ -21,14 +21,14 @@
21
21
  */
22
22
 
23
23
  import type {
24
- Product,
25
- ProductListingPage,
26
- BreadcrumbList,
27
- ListItem,
28
- Offer,
29
- AggregateOffer,
30
- UnitPriceSpecification,
31
- AggregateRating,
24
+ AggregateOffer,
25
+ AggregateRating,
26
+ BreadcrumbList,
27
+ ListItem,
28
+ Offer,
29
+ Product,
30
+ ProductListingPage,
31
+ UnitPriceSpecification,
32
32
  } from "../types/commerce";
33
33
 
34
34
  // -------------------------------------------------------------------------
@@ -36,12 +36,9 @@ import type {
36
36
  // -------------------------------------------------------------------------
37
37
 
38
38
  function JsonLdScript({ data }: { data: unknown }) {
39
- return (
40
- <script
41
- type="application/ld+json"
42
- dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
43
- />
44
- );
39
+ return (
40
+ <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} />
41
+ );
45
42
  }
46
43
 
47
44
  // -------------------------------------------------------------------------
@@ -49,103 +46,96 @@ function JsonLdScript({ data }: { data: unknown }) {
49
46
  // -------------------------------------------------------------------------
50
47
 
51
48
  export interface ProductJsonLdProps {
52
- product: Product;
53
- /** Override the canonical URL. Defaults to product.url. */
54
- url?: string;
49
+ product: Product;
50
+ /** Override the canonical URL. Defaults to product.url. */
51
+ url?: string;
55
52
  }
56
53
 
57
54
  function getBestOffer(offers: Offer[] | AggregateOffer | undefined): {
58
- price?: number;
59
- priceCurrency?: string;
60
- availability?: string;
61
- seller?: string;
62
- priceValidUntil?: string;
55
+ price?: number;
56
+ priceCurrency?: string;
57
+ availability?: string;
58
+ seller?: string;
59
+ priceValidUntil?: string;
63
60
  } {
64
- if (!offers) return {};
65
-
66
- if ("@type" in offers && offers["@type"] === "AggregateOffer") {
67
- const agg = offers as AggregateOffer;
68
- return {
69
- price: agg.lowPrice,
70
- priceCurrency: agg.priceCurrency,
71
- };
72
- }
73
-
74
- if (Array.isArray(offers) && offers.length > 0) {
75
- const best = offers.reduce((a, b) => {
76
- const ap = a.price ?? Infinity;
77
- const bp = b.price ?? Infinity;
78
- return ap <= bp ? a : b;
79
- });
80
- return {
81
- price: best.price,
82
- priceCurrency: best.priceCurrency,
83
- availability: best.availability,
84
- seller: best.seller,
85
- priceValidUntil: best.priceValidUntil,
86
- };
87
- }
88
-
89
- return {};
61
+ if (!offers) return {};
62
+
63
+ if ("@type" in offers && offers["@type"] === "AggregateOffer") {
64
+ const agg = offers as AggregateOffer;
65
+ return {
66
+ price: agg.lowPrice,
67
+ priceCurrency: agg.priceCurrency,
68
+ };
69
+ }
70
+
71
+ if (Array.isArray(offers) && offers.length > 0) {
72
+ const best = offers.reduce((a, b) => {
73
+ const ap = a.price ?? Infinity;
74
+ const bp = b.price ?? Infinity;
75
+ return ap <= bp ? a : b;
76
+ });
77
+ return {
78
+ price: best.price,
79
+ priceCurrency: best.priceCurrency,
80
+ availability: best.availability,
81
+ seller: best.seller,
82
+ priceValidUntil: best.priceValidUntil,
83
+ };
84
+ }
85
+
86
+ return {};
90
87
  }
91
88
 
92
- function getListPrice(
93
- priceSpec: UnitPriceSpecification[] | undefined,
94
- ): number | undefined {
95
- if (!priceSpec) return undefined;
96
- const list = priceSpec.find(
97
- (p) =>
98
- p.priceType === "https://schema.org/ListPrice" ||
99
- p.priceType === "https://schema.org/SRP",
100
- );
101
- return list?.price;
89
+ function _getListPrice(priceSpec: UnitPriceSpecification[] | undefined): number | undefined {
90
+ if (!priceSpec) return undefined;
91
+ const list = priceSpec.find(
92
+ (p) =>
93
+ p.priceType === "https://schema.org/ListPrice" || p.priceType === "https://schema.org/SRP",
94
+ );
95
+ return list?.price;
102
96
  }
103
97
 
104
98
  export function ProductJsonLd({ product, url }: ProductJsonLdProps) {
105
- const offer = getBestOffer(product.offers as Offer[] | AggregateOffer | undefined);
106
- const images = product.image?.map((img) => img.url).filter(Boolean) ?? [];
107
- const rating = product.aggregateRating as AggregateRating | undefined;
108
-
109
- const data: Record<string, unknown> = {
110
- "@context": "https://schema.org",
111
- "@type": "Product",
112
- name: product.name,
113
- description: product.description,
114
- image: images.length === 1 ? images[0] : images,
115
- url: url ?? product.url,
116
- sku: product.sku,
117
- productID: product.productID,
118
- brand: product.brand
119
- ? { "@type": "Brand", name: product.brand.name }
120
- : undefined,
121
- gtin: product.gtin,
122
- };
123
-
124
- if (offer.price != null) {
125
- data.offers = {
126
- "@type": "Offer",
127
- price: offer.price,
128
- priceCurrency: offer.priceCurrency ?? "BRL",
129
- availability: offer.availability ?? "https://schema.org/InStock",
130
- seller: offer.seller
131
- ? { "@type": "Organization", name: offer.seller }
132
- : undefined,
133
- priceValidUntil: offer.priceValidUntil,
134
- url: url ?? product.url,
135
- };
136
- }
137
-
138
- if (rating && rating.ratingValue) {
139
- data.aggregateRating = {
140
- "@type": "AggregateRating",
141
- ratingValue: rating.ratingValue,
142
- reviewCount: rating.reviewCount ?? rating.ratingCount ?? 0,
143
- bestRating: rating.bestRating ?? 5,
144
- worstRating: rating.worstRating ?? 1,
145
- };
146
- }
147
-
148
- return <JsonLdScript data={data} />;
99
+ const offer = getBestOffer(product.offers as Offer[] | AggregateOffer | undefined);
100
+ const images = product.image?.map((img) => img.url).filter(Boolean) ?? [];
101
+ const rating = product.aggregateRating as AggregateRating | undefined;
102
+
103
+ const data: Record<string, unknown> = {
104
+ "@context": "https://schema.org",
105
+ "@type": "Product",
106
+ name: product.name,
107
+ description: product.description,
108
+ image: images.length === 1 ? images[0] : images,
109
+ url: url ?? product.url,
110
+ sku: product.sku,
111
+ productID: product.productID,
112
+ brand: product.brand ? { "@type": "Brand", name: product.brand.name } : undefined,
113
+ gtin: product.gtin,
114
+ };
115
+
116
+ if (offer.price != null) {
117
+ data.offers = {
118
+ "@type": "Offer",
119
+ price: offer.price,
120
+ priceCurrency: offer.priceCurrency ?? "BRL",
121
+ availability: offer.availability ?? "https://schema.org/InStock",
122
+ seller: offer.seller ? { "@type": "Organization", name: offer.seller } : undefined,
123
+ priceValidUntil: offer.priceValidUntil,
124
+ url: url ?? product.url,
125
+ };
126
+ }
127
+
128
+ if (rating?.ratingValue) {
129
+ data.aggregateRating = {
130
+ "@type": "AggregateRating",
131
+ ratingValue: rating.ratingValue,
132
+ reviewCount: rating.reviewCount ?? rating.ratingCount ?? 0,
133
+ bestRating: rating.bestRating ?? 5,
134
+ worstRating: rating.worstRating ?? 1,
135
+ };
136
+ }
137
+
138
+ return <JsonLdScript data={data} />;
149
139
  }
150
140
 
151
141
  // -------------------------------------------------------------------------
@@ -153,45 +143,46 @@ export function ProductJsonLd({ product, url }: ProductJsonLdProps) {
153
143
  // -------------------------------------------------------------------------
154
144
 
155
145
  export interface PLPJsonLdProps {
156
- page: ProductListingPage;
157
- /** Override the canonical URL. */
158
- url?: string;
146
+ page: ProductListingPage;
147
+ /** Override the canonical URL. */
148
+ url?: string;
159
149
  }
160
150
 
161
151
  export function PLPJsonLd({ page, url }: PLPJsonLdProps) {
162
- const items = (page.products ?? []).map((product, index) => {
163
- const offer = getBestOffer(product.offers as Offer[] | AggregateOffer | undefined);
164
- return {
165
- "@type": "ListItem" as const,
166
- position: index + 1,
167
- item: {
168
- "@type": "Product" as const,
169
- name: product.name,
170
- url: product.url,
171
- image: product.image?.[0]?.url,
172
- offers: offer.price != null
173
- ? {
174
- "@type": "Offer" as const,
175
- price: offer.price,
176
- priceCurrency: offer.priceCurrency ?? "BRL",
177
- availability: offer.availability ?? "https://schema.org/InStock",
178
- }
179
- : undefined,
180
- },
181
- };
182
- });
183
-
184
- const data = {
185
- "@context": "https://schema.org",
186
- "@type": "ItemList",
187
- url: url ?? page.seo?.canonical,
188
- name: page.seo?.title,
189
- description: page.seo?.description,
190
- numberOfItems: page.products?.length ?? 0,
191
- itemListElement: items,
192
- };
193
-
194
- return <JsonLdScript data={data} />;
152
+ const items = (page.products ?? []).map((product, index) => {
153
+ const offer = getBestOffer(product.offers as Offer[] | AggregateOffer | undefined);
154
+ return {
155
+ "@type": "ListItem" as const,
156
+ position: index + 1,
157
+ item: {
158
+ "@type": "Product" as const,
159
+ name: product.name,
160
+ url: product.url,
161
+ image: product.image?.[0]?.url,
162
+ offers:
163
+ offer.price != null
164
+ ? {
165
+ "@type": "Offer" as const,
166
+ price: offer.price,
167
+ priceCurrency: offer.priceCurrency ?? "BRL",
168
+ availability: offer.availability ?? "https://schema.org/InStock",
169
+ }
170
+ : undefined,
171
+ },
172
+ };
173
+ });
174
+
175
+ const data = {
176
+ "@context": "https://schema.org",
177
+ "@type": "ItemList",
178
+ url: url ?? page.seo?.canonical,
179
+ name: page.seo?.title,
180
+ description: page.seo?.description,
181
+ numberOfItems: page.products?.length ?? 0,
182
+ itemListElement: items,
183
+ };
184
+
185
+ return <JsonLdScript data={data} />;
195
186
  }
196
187
 
197
188
  // -------------------------------------------------------------------------
@@ -199,28 +190,28 @@ export function PLPJsonLd({ page, url }: PLPJsonLdProps) {
199
190
  // -------------------------------------------------------------------------
200
191
 
201
192
  export interface BreadcrumbJsonLdProps {
202
- breadcrumb: BreadcrumbList;
193
+ breadcrumb: BreadcrumbList;
203
194
  }
204
195
 
205
196
  export function BreadcrumbJsonLd({ breadcrumb }: BreadcrumbJsonLdProps) {
206
- const items = (breadcrumb.itemListElement ?? []).map((item, index) => {
207
- const listItem = item as ListItem;
208
- return {
209
- "@type": "ListItem" as const,
210
- position: listItem.position ?? index + 1,
211
- name: listItem.name,
212
- item: listItem.item ?? listItem.url,
213
- };
214
- });
215
-
216
- const data = {
217
- "@context": "https://schema.org",
218
- "@type": "BreadcrumbList",
219
- itemListElement: items,
220
- numberOfItems: items.length,
221
- };
222
-
223
- return <JsonLdScript data={data} />;
197
+ const items = (breadcrumb.itemListElement ?? []).map((item, index) => {
198
+ const listItem = item as ListItem;
199
+ return {
200
+ "@type": "ListItem" as const,
201
+ position: listItem.position ?? index + 1,
202
+ name: listItem.name,
203
+ item: listItem.item ?? listItem.url,
204
+ };
205
+ });
206
+
207
+ const data = {
208
+ "@context": "https://schema.org",
209
+ "@type": "BreadcrumbList",
210
+ itemListElement: items,
211
+ numberOfItems: items.length,
212
+ };
213
+
214
+ return <JsonLdScript data={data} />;
224
215
  }
225
216
 
226
217
  // -------------------------------------------------------------------------
@@ -228,13 +219,13 @@ export function BreadcrumbJsonLd({ breadcrumb }: BreadcrumbJsonLdProps) {
228
219
  // -------------------------------------------------------------------------
229
220
 
230
221
  export interface SeoMetaProps {
231
- title?: string;
232
- description?: string;
233
- canonical?: string;
234
- image?: string;
235
- noIndex?: boolean;
236
- type?: "website" | "article" | "product";
237
- siteName?: string;
222
+ title?: string;
223
+ description?: string;
224
+ canonical?: string;
225
+ image?: string;
226
+ noIndex?: boolean;
227
+ type?: "website" | "article" | "product";
228
+ siteName?: string;
238
229
  }
239
230
 
240
231
  /**
@@ -245,41 +236,41 @@ export interface SeoMetaProps {
245
236
  * by React's built-in behavior with TanStack Start).
246
237
  */
247
238
  export function seoMetaTags(props: SeoMetaProps): Array<Record<string, string>> {
248
- const tags: Array<Record<string, string>> = [];
249
-
250
- if (props.title) {
251
- tags.push({ title: props.title });
252
- tags.push({ property: "og:title", content: props.title });
253
- tags.push({ name: "twitter:title", content: props.title });
254
- }
255
-
256
- if (props.description) {
257
- tags.push({ name: "description", content: props.description });
258
- tags.push({ property: "og:description", content: props.description });
259
- tags.push({ name: "twitter:description", content: props.description });
260
- }
261
-
262
- if (props.canonical) {
263
- tags.push({ property: "og:url", content: props.canonical });
264
- }
265
-
266
- if (props.image) {
267
- tags.push({ property: "og:image", content: props.image });
268
- tags.push({ name: "twitter:image", content: props.image });
269
- tags.push({ name: "twitter:card", content: "summary_large_image" });
270
- }
271
-
272
- if (props.type) {
273
- tags.push({ property: "og:type", content: props.type });
274
- }
275
-
276
- if (props.siteName) {
277
- tags.push({ property: "og:site_name", content: props.siteName });
278
- }
279
-
280
- if (props.noIndex) {
281
- tags.push({ name: "robots", content: "noindex, nofollow" });
282
- }
283
-
284
- return tags;
239
+ const tags: Array<Record<string, string>> = [];
240
+
241
+ if (props.title) {
242
+ tags.push({ title: props.title });
243
+ tags.push({ property: "og:title", content: props.title });
244
+ tags.push({ name: "twitter:title", content: props.title });
245
+ }
246
+
247
+ if (props.description) {
248
+ tags.push({ name: "description", content: props.description });
249
+ tags.push({ property: "og:description", content: props.description });
250
+ tags.push({ name: "twitter:description", content: props.description });
251
+ }
252
+
253
+ if (props.canonical) {
254
+ tags.push({ property: "og:url", content: props.canonical });
255
+ }
256
+
257
+ if (props.image) {
258
+ tags.push({ property: "og:image", content: props.image });
259
+ tags.push({ name: "twitter:image", content: props.image });
260
+ tags.push({ name: "twitter:card", content: "summary_large_image" });
261
+ }
262
+
263
+ if (props.type) {
264
+ tags.push({ property: "og:type", content: props.type });
265
+ }
266
+
267
+ if (props.siteName) {
268
+ tags.push({ property: "og:site_name", content: props.siteName });
269
+ }
270
+
271
+ if (props.noIndex) {
272
+ tags.push({ name: "robots", content: "noindex, nofollow" });
273
+ }
274
+
275
+ return tags;
285
276
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
- createContext,
3
- forwardRef,
4
- useContext,
5
- useMemo,
6
- type ComponentPropsWithoutRef,
7
- type ReactNode,
2
+ type ComponentPropsWithoutRef,
3
+ createContext,
4
+ forwardRef,
5
+ type ReactNode,
6
+ useContext,
7
+ useMemo,
8
8
  } from "react";
9
- import { getOptimizedMediaUrl, getSrcSet, type FitOptions } from "./Image";
9
+ import { type FitOptions, getOptimizedMediaUrl, getSrcSet } from "./Image";
10
10
 
11
11
  // -------------------------------------------------------------------------
12
12
  // Preload context — flows from <Picture preload> to child <Source> elements
@@ -15,7 +15,7 @@ import { getOptimizedMediaUrl, getSrcSet, type FitOptions } from "./Image";
15
15
  // -------------------------------------------------------------------------
16
16
 
17
17
  interface PreloadContextValue {
18
- preload: boolean;
18
+ preload: boolean;
19
19
  }
20
20
 
21
21
  const PreloadContext = createContext<PreloadContextValue>({ preload: false });
@@ -25,59 +25,48 @@ const PreloadContext = createContext<PreloadContextValue>({ preload: false });
25
25
  // preload link injection when inside a <Picture preload>.
26
26
  // -------------------------------------------------------------------------
27
27
 
28
- export type SourceProps = Omit<
29
- ComponentPropsWithoutRef<"source">,
30
- "width" | "height"
31
- > & {
32
- src: string;
33
- /** @description Improves Web Vitals (CLS|LCP) */
34
- width: number;
35
- /** @description Improves Web Vitals (CLS|LCP) */
36
- height?: number;
37
- /** @description Improves Web Vitals (LCP). Use high for LCP image. */
38
- fetchPriority?: "high" | "low" | "auto";
39
- /** @description Object-fit */
40
- fit?: FitOptions;
28
+ export type SourceProps = Omit<ComponentPropsWithoutRef<"source">, "width" | "height"> & {
29
+ src: string;
30
+ /** @description Improves Web Vitals (CLS|LCP) */
31
+ width: number;
32
+ /** @description Improves Web Vitals (CLS|LCP) */
33
+ height?: number;
34
+ /** @description Improves Web Vitals (LCP). Use high for LCP image. */
35
+ fetchPriority?: "high" | "low" | "auto";
36
+ /** @description Object-fit */
37
+ fit?: FitOptions;
41
38
  };
42
39
 
43
- export const Source = forwardRef<HTMLSourceElement, SourceProps>(
44
- function Source(
45
- { src, width, height, fetchPriority, fit = "cover", ...rest },
46
- ref,
47
- ) {
48
- const { preload } = useContext(PreloadContext);
40
+ export const Source = forwardRef<HTMLSourceElement, SourceProps>(function Source(
41
+ { src, width, height, fetchPriority, fit = "cover", ...rest },
42
+ ref,
43
+ ) {
44
+ const { preload } = useContext(PreloadContext);
49
45
 
50
- const optimizedSrc = getOptimizedMediaUrl({
51
- originalSrc: src,
52
- width,
53
- height,
54
- fit,
55
- });
56
- const srcSet = rest.srcSet ?? getSrcSet(src, width, height, fit);
46
+ const optimizedSrc = getOptimizedMediaUrl({
47
+ originalSrc: src,
48
+ width,
49
+ height,
50
+ fit,
51
+ });
52
+ const srcSet = rest.srcSet ?? getSrcSet(src, width, height, fit);
57
53
 
58
- return (
59
- <>
60
- {preload && (
61
- <link
62
- as="image"
63
- rel="preload"
64
- href={optimizedSrc}
65
- imageSrcSet={srcSet}
66
- fetchPriority={fetchPriority ?? "high"}
67
- media={rest.media}
68
- />
69
- )}
70
- <source
71
- {...rest}
72
- srcSet={srcSet ?? optimizedSrc}
73
- width={width}
74
- height={height}
75
- ref={ref}
76
- />
77
- </>
78
- );
79
- },
80
- );
54
+ return (
55
+ <>
56
+ {preload && (
57
+ <link
58
+ as="image"
59
+ rel="preload"
60
+ href={optimizedSrc}
61
+ imageSrcSet={srcSet}
62
+ fetchPriority={fetchPriority ?? "high"}
63
+ media={rest.media}
64
+ />
65
+ )}
66
+ <source {...rest} srcSet={srcSet ?? optimizedSrc} width={width} height={height} ref={ref} />
67
+ </>
68
+ );
69
+ });
81
70
 
82
71
  // -------------------------------------------------------------------------
83
72
  // Picture — composable wrapper that provides preload context to children.
@@ -91,24 +80,25 @@ export const Source = forwardRef<HTMLSourceElement, SourceProps>(
91
80
  // -------------------------------------------------------------------------
92
81
 
93
82
  export type PictureProps = ComponentPropsWithoutRef<"picture"> & {
94
- children: ReactNode;
95
- /**
96
- * @description When true, child <Source> and <Image> elements inject
97
- * `<link rel="preload">` tags for their respective media queries.
98
- */
99
- preload?: boolean;
83
+ children: ReactNode;
84
+ /**
85
+ * @description When true, child <Source> and <Image> elements inject
86
+ * `<link rel="preload">` tags for their respective media queries.
87
+ */
88
+ preload?: boolean;
100
89
  };
101
90
 
102
- export const Picture = forwardRef<HTMLPictureElement, PictureProps>(
103
- function Picture({ children, preload = false, ...props }, ref) {
104
- const value = useMemo(() => ({ preload }), [preload]);
91
+ export const Picture = forwardRef<HTMLPictureElement, PictureProps>(function Picture(
92
+ { children, preload = false, ...props },
93
+ ref,
94
+ ) {
95
+ const value = useMemo(() => ({ preload }), [preload]);
105
96
 
106
- return (
107
- <PreloadContext.Provider value={value}>
108
- <picture {...props} ref={ref}>
109
- {children}
110
- </picture>
111
- </PreloadContext.Provider>
112
- );
113
- },
114
- );
97
+ return (
98
+ <PreloadContext.Provider value={value}>
99
+ <picture {...props} ref={ref}>
100
+ {children}
101
+ </picture>
102
+ </PreloadContext.Provider>
103
+ );
104
+ });
@@ -1,24 +1,24 @@
1
1
  import type { AnalyticsEvent } from "../types/commerce";
2
2
 
3
3
  declare global {
4
- interface Window {
5
- DECO: { events: { dispatch: (event: any) => void } };
6
- DECO_SITES_STD: {
7
- sendAnalyticsEvent: (args: AnalyticsEvent) => void;
8
- };
9
- }
4
+ interface Window {
5
+ DECO: { events: { dispatch: (event: any) => void } };
6
+ DECO_SITES_STD: {
7
+ sendAnalyticsEvent: (args: AnalyticsEvent) => void;
8
+ };
9
+ }
10
10
  }
11
11
 
12
12
  export const sendEvent = <E extends AnalyticsEvent>(event: E) => {
13
- if (typeof globalThis.window?.DECO?.events?.dispatch === "function") {
14
- globalThis.window.DECO.events.dispatch(event);
15
- return;
16
- }
13
+ if (typeof globalThis.window?.DECO?.events?.dispatch === "function") {
14
+ globalThis.window.DECO.events.dispatch(event);
15
+ return;
16
+ }
17
17
 
18
- if (typeof globalThis.window?.DECO_SITES_STD?.sendAnalyticsEvent === "function") {
19
- globalThis.window.DECO_SITES_STD.sendAnalyticsEvent(event);
20
- return;
21
- }
18
+ if (typeof globalThis.window?.DECO_SITES_STD?.sendAnalyticsEvent === "function") {
19
+ globalThis.window.DECO_SITES_STD.sendAnalyticsEvent(event);
20
+ return;
21
+ }
22
22
 
23
- console.info("[analytics] No event dispatcher found. Event not sent:", event.name);
23
+ console.info("[analytics] No event dispatcher found. Event not sent:", event.name);
24
24
  };