@cimplify/sdk 0.79.0 → 0.80.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 (41) hide show
  1. package/dist/advanced.d.mts +1 -1
  2. package/dist/advanced.d.ts +1 -1
  3. package/dist/{chunk-FURKCWVG.mjs → chunk-FIC5S3KD.mjs} +1 -1
  4. package/dist/{chunk-T7ALPOK7.js → chunk-KMP4HVA2.js} +2 -2
  5. package/dist/{chunk-LBFTEXGD.mjs → chunk-NJGJCQWF.mjs} +9 -0
  6. package/dist/{chunk-5BPBDWAA.js → chunk-NV445GMS.js} +9 -0
  7. package/dist/{client-Byysg2IX.d.mts → client-6BNd11sq.d.mts} +1 -1
  8. package/dist/{client-BAzvXSLJ.d.ts → client-CuoWlAfH.d.ts} +1 -1
  9. package/dist/{client-D16qfHAB.d.mts → client-Czbt-mej.d.mts} +3 -0
  10. package/dist/{client-Cn5trGYV.d.ts → client-D22iSqke.d.ts} +3 -0
  11. package/dist/index.d.mts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +10 -10
  14. package/dist/index.mjs +1 -1
  15. package/dist/react.d.mts +33 -4
  16. package/dist/react.d.ts +33 -4
  17. package/dist/react.js +332 -51
  18. package/dist/react.mjs +312 -52
  19. package/dist/server.d.mts +2 -2
  20. package/dist/server.d.ts +2 -2
  21. package/dist/server.js +2 -2
  22. package/dist/server.mjs +1 -1
  23. package/dist/testing/suite.d.mts +2 -2
  24. package/dist/testing/suite.d.ts +2 -2
  25. package/dist/testing/suite.js +21 -21
  26. package/dist/testing/suite.mjs +2 -2
  27. package/dist/testing.d.mts +2 -2
  28. package/dist/testing.d.ts +2 -2
  29. package/dist/testing.js +77 -77
  30. package/dist/testing.mjs +3 -3
  31. package/package.json +7 -2
  32. package/registry/catalogue-page.json +1 -1
  33. package/registry/product-card.json +1 -1
  34. package/registry/product-grid.json +1 -1
  35. package/registry/product-image-gallery.json +1 -1
  36. package/registry/product-page.json +1 -1
  37. package/registry/recently-viewed.json +1 -1
  38. package/registry/recommendation-carousel.json +1 -1
  39. package/registry/search-input.json +1 -1
  40. package/dist/server-BXT62nBC.d.mts +0 -1186
  41. package/dist/server-BXT62nBC.d.ts +0 -1186
package/dist/react.js CHANGED
@@ -2,13 +2,14 @@
2
2
  'use strict';
3
3
 
4
4
  var chunkIQLJJW2H_js = require('./chunk-IQLJJW2H.js');
5
- var chunk5BPBDWAA_js = require('./chunk-5BPBDWAA.js');
5
+ var chunkNV445GMS_js = require('./chunk-NV445GMS.js');
6
6
  var chunkCV5QMDQS_js = require('./chunk-CV5QMDQS.js');
7
7
  var chunk7Y2O3E4D_js = require('./chunk-7Y2O3E4D.js');
8
8
  var chunkXA3ZNR75_js = require('./chunk-XA3ZNR75.js');
9
9
  require('./chunk-OWW5GUSB.js');
10
10
  var React14 = require('react');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
+ var ReactDOM = require('react-dom');
12
13
  var clsx = require('clsx');
13
14
  var tailwindMerge = require('tailwind-merge');
14
15
  var dateFns = require('date-fns');
@@ -27,7 +28,26 @@ var input = require('@base-ui/react/input');
27
28
 
28
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
30
 
31
+ function _interopNamespace(e) {
32
+ if (e && e.__esModule) return e;
33
+ var n = Object.create(null);
34
+ if (e) {
35
+ Object.keys(e).forEach(function (k) {
36
+ if (k !== 'default') {
37
+ var d = Object.getOwnPropertyDescriptor(e, k);
38
+ Object.defineProperty(n, k, d.get ? d : {
39
+ enumerable: true,
40
+ get: function () { return e[k]; }
41
+ });
42
+ }
43
+ });
44
+ }
45
+ n.default = e;
46
+ return Object.freeze(n);
47
+ }
48
+
30
49
  var React14__default = /*#__PURE__*/_interopDefault(React14);
50
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
31
51
 
32
52
  // src/ads/identity.ts
33
53
  var COOKIE_NAME = "_cimplify_uid";
@@ -1081,6 +1101,28 @@ function useCart(options = {}) {
1081
1101
  ]
1082
1102
  );
1083
1103
  }
1104
+ var reactPreconnect = typeof ReactDOM__namespace.preconnect === "function" ? ReactDOM__namespace.preconnect : void 0;
1105
+ var connected = /* @__PURE__ */ new Set();
1106
+ function preconnectOrigin(url, crossOrigin) {
1107
+ let origin;
1108
+ try {
1109
+ origin = new URL(url).origin;
1110
+ } catch {
1111
+ return;
1112
+ }
1113
+ if (reactPreconnect) {
1114
+ reactPreconnect(origin, crossOrigin ? { crossOrigin } : void 0);
1115
+ return;
1116
+ }
1117
+ if (typeof document === "undefined") return;
1118
+ if (origin === window.location.origin || connected.has(origin)) return;
1119
+ connected.add(origin);
1120
+ const link = document.createElement("link");
1121
+ link.rel = "preconnect";
1122
+ link.href = origin;
1123
+ if (crossOrigin) link.crossOrigin = crossOrigin;
1124
+ document.head.appendChild(link);
1125
+ }
1084
1126
  var LOCATION_STORAGE_KEY = "cimplify_location_id";
1085
1127
  var DISPLAY_CURRENCY_STORAGE_KEY = "cimplify_display_currency";
1086
1128
  var FX_REFRESH_INTERVAL = 12e4;
@@ -1089,7 +1131,7 @@ var DEFAULT_COUNTRY = "US";
1089
1131
  function createDefaultClient() {
1090
1132
  const processRef = globalThis.process;
1091
1133
  const envPublicKey = processRef?.env?.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || "";
1092
- return chunk5BPBDWAA_js.createCimplifyClient({ publicKey: envPublicKey });
1134
+ return chunkNV445GMS_js.createCimplifyClient({ publicKey: envPublicKey });
1093
1135
  }
1094
1136
  function getStoredLocationId() {
1095
1137
  if (typeof window === "undefined" || !window.localStorage) {
@@ -1146,6 +1188,11 @@ function resolveInitialLocation(locations) {
1146
1188
  }
1147
1189
  return locations[0];
1148
1190
  }
1191
+ var DEFAULT_PREFETCH_OPTIONS = {
1192
+ mode: "aggressive",
1193
+ rootMargin: "300px",
1194
+ images: true
1195
+ };
1149
1196
  var CimplifyClientContext = React14.createContext(null);
1150
1197
  var CimplifyStateContext = React14.createContext(null);
1151
1198
  function CimplifyProvider({
@@ -1154,9 +1201,19 @@ function CimplifyProvider({
1154
1201
  onLocationChange,
1155
1202
  initialBusiness,
1156
1203
  initialLocations,
1157
- optimistic = true
1204
+ optimistic = true,
1205
+ prefetch,
1206
+ linkComponent
1158
1207
  }) {
1159
1208
  const resolvedClient = React14.useMemo(() => client ?? createDefaultClient(), [client]);
1209
+ const resolvedPrefetch = React14.useMemo(
1210
+ () => ({
1211
+ mode: prefetch?.mode ?? DEFAULT_PREFETCH_OPTIONS.mode,
1212
+ rootMargin: prefetch?.rootMargin ?? DEFAULT_PREFETCH_OPTIONS.rootMargin,
1213
+ images: prefetch?.images ?? DEFAULT_PREFETCH_OPTIONS.images
1214
+ }),
1215
+ [prefetch?.mode, prefetch?.rootMargin, prefetch?.images]
1216
+ );
1160
1217
  const onLocationChangeRef = React14.useRef(onLocationChange);
1161
1218
  const seeded = initialBusiness !== void 0;
1162
1219
  const initialBootstrapDone = React14.useRef(seeded);
@@ -1223,6 +1280,12 @@ function CimplifyProvider({
1223
1280
  },
1224
1281
  [fxRate, displayCurrency, baseCurrency]
1225
1282
  );
1283
+ if (resolvedPrefetch.mode !== "off") {
1284
+ const apiCredentials = resolvedClient.getCredentialsMode() === "include" ? "use-credentials" : "anonymous";
1285
+ preconnectOrigin(resolvedClient.getBaseUrl(), apiCredentials);
1286
+ preconnectOrigin(resolvedClient.getLinkApiUrl(), apiCredentials);
1287
+ preconnectOrigin(chunkIQLJJW2H_js.DEFAULT_CDN_BASE_URL);
1288
+ }
1226
1289
  const setCurrentLocation = React14.useCallback(
1227
1290
  (location) => {
1228
1291
  const previousLocationId = resolvedClient.getLocationId();
@@ -1296,7 +1359,9 @@ function CimplifyProvider({
1296
1359
  locations,
1297
1360
  isReady,
1298
1361
  baseCurrency,
1299
- optimistic
1362
+ optimistic,
1363
+ prefetch: resolvedPrefetch,
1364
+ linkComponent: linkComponent ?? null
1300
1365
  }),
1301
1366
  [
1302
1367
  resolvedClient,
@@ -1304,7 +1369,9 @@ function CimplifyProvider({
1304
1369
  baseCurrency,
1305
1370
  locations,
1306
1371
  isReady,
1307
- optimistic
1372
+ optimistic,
1373
+ resolvedPrefetch,
1374
+ linkComponent
1308
1375
  ]
1309
1376
  );
1310
1377
  const stateContextValue = React14.useMemo(
@@ -4326,6 +4393,16 @@ function createCachedQuery(config) {
4326
4393
  }, [cacheKey2, load]);
4327
4394
  return { data, isLoading, error, refetch };
4328
4395
  }
4396
+ useCachedQuery.prefetch = (client, options) => {
4397
+ const key = config.key(client, options);
4398
+ if (cache.has(key) || inflight.has(key)) return void 0;
4399
+ const promise = config.fetch(client, options);
4400
+ inflight.set(key, promise);
4401
+ return promise.then((value) => {
4402
+ cache.set(key, { data: value, ts: Date.now() });
4403
+ }).catch(() => {
4404
+ }).finally(() => inflight.delete(key));
4405
+ };
4329
4406
  useCachedQuery.clear = () => {
4330
4407
  cache.clear();
4331
4408
  inflight.clear();
@@ -4382,6 +4459,9 @@ var productsQuery = createCachedQuery({
4382
4459
  };
4383
4460
  }
4384
4461
  });
4462
+ function prefetchProducts(client, options) {
4463
+ return productsQuery.prefetch(client, options);
4464
+ }
4385
4465
  function useProducts(options = {}) {
4386
4466
  const { data, isLoading, error, refetch } = productsQuery(options);
4387
4467
  return {
@@ -4406,14 +4486,27 @@ var productQuery = createCachedQuery({
4406
4486
  fetch: async (client, options) => {
4407
4487
  const result = isLikelySlug(options.slugOrId) ? await client.catalogue.getProductBySlug(options.slugOrId) : await client.catalogue.getProduct(options.slugOrId);
4408
4488
  if (!result.ok) throw result.error;
4409
- client.activity.trackProductView(result.value.id, {
4410
- productName: result.value.name,
4411
- categoryId: result.value.category_id,
4412
- price: String(result.value.default_price)
4413
- });
4414
4489
  return result.value;
4415
4490
  }
4416
4491
  });
4492
+ function prefetchProduct(client, slugOrId) {
4493
+ const normalized = (slugOrId || "").trim();
4494
+ if (!normalized) return void 0;
4495
+ return productQuery.prefetch(client, { slugOrId: normalized });
4496
+ }
4497
+ var VIEW_DEDUPE_WINDOW_MS = 3e5;
4498
+ var recentlyTracked = /* @__PURE__ */ new Map();
4499
+ function trackProductView(client, product) {
4500
+ const now = Date.now();
4501
+ const last = recentlyTracked.get(product.id);
4502
+ if (last !== void 0 && now - last < VIEW_DEDUPE_WINDOW_MS) return;
4503
+ recentlyTracked.set(product.id, now);
4504
+ client.activity.trackProductView(product.id, {
4505
+ productName: product.name,
4506
+ categoryId: product.category_id,
4507
+ price: String(product.default_price)
4508
+ });
4509
+ }
4417
4510
  function useProduct(slugOrId, options = {}) {
4418
4511
  const normalized = (slugOrId || "").trim();
4419
4512
  const { data, isLoading, error, refetch } = productQuery({
@@ -9626,6 +9719,39 @@ function ProductCustomizer({
9626
9719
  )
9627
9720
  ] });
9628
9721
  }
9722
+
9723
+ // src/react/utils/prefetch-policy.ts
9724
+ var SLOW_EFFECTIVE_TYPES = /* @__PURE__ */ new Set(["slow-2g", "2g"]);
9725
+ var MAX_CONCURRENT_SPECULATIVE = 4;
9726
+ var inFlight = 0;
9727
+ function connectionAllowsPrefetch() {
9728
+ if (typeof navigator === "undefined") return false;
9729
+ const connection = navigator.connection;
9730
+ if (!connection) return true;
9731
+ if (connection.saveData) return false;
9732
+ return !SLOW_EFFECTIVE_TYPES.has(connection.effectiveType ?? "");
9733
+ }
9734
+ function speculate(task) {
9735
+ if (!connectionAllowsPrefetch() || inFlight >= MAX_CONCURRENT_SPECULATIVE) return;
9736
+ const pending = task();
9737
+ if (!pending) return;
9738
+ inFlight++;
9739
+ pending.catch(() => {
9740
+ }).finally(() => {
9741
+ inFlight--;
9742
+ });
9743
+ }
9744
+
9745
+ // src/react/utils/preload-image.ts
9746
+ var preloaded = /* @__PURE__ */ new Set();
9747
+ function preloadImage(src) {
9748
+ if (!src || typeof window === "undefined") return;
9749
+ if (!connectionAllowsPrefetch() || preloaded.has(src)) return;
9750
+ preloaded.add(src);
9751
+ const image = new Image();
9752
+ image.decoding = "async";
9753
+ image.src = src;
9754
+ }
9629
9755
  var ASPECT_STYLES = {
9630
9756
  square: { aspectRatio: "1/1" },
9631
9757
  "4/3": { aspectRatio: "4/3" },
@@ -9658,6 +9784,8 @@ function ProductImageGallery({
9658
9784
  return null;
9659
9785
  }
9660
9786
  const activeImage = normalizedImages[selectedImage] || normalizedImages[0];
9787
+ preloadImage(normalizedImages[selectedImage + 1]);
9788
+ preloadImage(normalizedImages[selectedImage - 1]);
9661
9789
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-image-gallery": true, className, children: [
9662
9790
  /* @__PURE__ */ jsxRuntime.jsx(
9663
9791
  "div",
@@ -9669,6 +9797,7 @@ function ProductImageGallery({
9669
9797
  {
9670
9798
  src: activeImage,
9671
9799
  alt: productName,
9800
+ fetchPriority: "high",
9672
9801
  style: {
9673
9802
  width: "100%",
9674
9803
  height: "100%",
@@ -9710,6 +9839,8 @@ function ProductImageGallery({
9710
9839
  {
9711
9840
  src: image,
9712
9841
  alt: "",
9842
+ loading: "lazy",
9843
+ decoding: "async",
9713
9844
  style: { width: "100%", height: "100%", objectFit: "cover" }
9714
9845
  }
9715
9846
  )
@@ -10698,6 +10829,63 @@ function ProductSheet({
10698
10829
  }
10699
10830
  );
10700
10831
  }
10832
+ var observers = /* @__PURE__ */ new Map();
10833
+ function observe(node, rootMargin, onEnter) {
10834
+ let shared = observers.get(rootMargin);
10835
+ if (!shared) {
10836
+ const targets = /* @__PURE__ */ new Map();
10837
+ const observer = new IntersectionObserver(
10838
+ (entries) => {
10839
+ for (const entry of entries) {
10840
+ if (!entry.isIntersecting) continue;
10841
+ const callback = targets.get(entry.target);
10842
+ if (callback) {
10843
+ unobserve(entry.target, rootMargin);
10844
+ callback();
10845
+ }
10846
+ }
10847
+ },
10848
+ { rootMargin }
10849
+ );
10850
+ shared = { observer, targets };
10851
+ observers.set(rootMargin, shared);
10852
+ }
10853
+ shared.targets.set(node, onEnter);
10854
+ shared.observer.observe(node);
10855
+ }
10856
+ function unobserve(node, rootMargin) {
10857
+ const shared = observers.get(rootMargin);
10858
+ if (!shared || !shared.targets.delete(node)) return;
10859
+ shared.observer.unobserve(node);
10860
+ if (shared.targets.size === 0) {
10861
+ shared.observer.disconnect();
10862
+ observers.delete(rootMargin);
10863
+ }
10864
+ }
10865
+ function useInViewport(onEnter, { rootMargin = "300px", disabled = false } = {}) {
10866
+ const onEnterRef = React14.useRef(onEnter);
10867
+ onEnterRef.current = onEnter;
10868
+ const attachedRef = React14.useRef(null);
10869
+ const firedRef = React14.useRef(false);
10870
+ return React14.useCallback(
10871
+ (node) => {
10872
+ if (attachedRef.current) {
10873
+ unobserve(attachedRef.current.node, attachedRef.current.rootMargin);
10874
+ attachedRef.current = null;
10875
+ }
10876
+ if (!node || disabled || firedRef.current) return;
10877
+ if (typeof IntersectionObserver === "undefined") return;
10878
+ attachedRef.current = { node, rootMargin };
10879
+ observe(node, rootMargin, () => {
10880
+ firedRef.current = true;
10881
+ attachedRef.current = null;
10882
+ onEnterRef.current();
10883
+ });
10884
+ },
10885
+ [rootMargin, disabled]
10886
+ );
10887
+ }
10888
+ var ImagePriorityContext = React14__default.default.createContext(false);
10701
10889
  function CardImage({
10702
10890
  src,
10703
10891
  alt,
@@ -10706,6 +10894,7 @@ function CardImage({
10706
10894
  className,
10707
10895
  children
10708
10896
  }) {
10897
+ const priority = React14__default.default.useContext(ImagePriorityContext);
10709
10898
  const aspectClass = {
10710
10899
  square: "aspect-square",
10711
10900
  "3/4": "aspect-[3/4]",
@@ -10713,11 +10902,14 @@ function CardImage({
10713
10902
  "16/9": "aspect-[16/9]"
10714
10903
  }[aspectRatio];
10715
10904
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-card-image": true, className: cn("relative overflow-hidden bg-muted", aspectClass, className), children: [
10716
- renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]" }) : src ? /* @__PURE__ */ jsxRuntime.jsx(
10905
+ renderImage ? renderImage({ src, alt, className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]", priority }) : src ? /* @__PURE__ */ jsxRuntime.jsx(
10717
10906
  "img",
10718
10907
  {
10719
10908
  src,
10720
10909
  alt,
10910
+ loading: priority ? "eager" : "lazy",
10911
+ decoding: priority ? "auto" : "async",
10912
+ fetchPriority: priority ? "high" : void 0,
10721
10913
  className: "w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.04]"
10722
10914
  }
10723
10915
  ) : null,
@@ -11657,25 +11849,41 @@ function ProductCard({
11657
11849
  onQuickAdd,
11658
11850
  children,
11659
11851
  aspectRatio = "4/3",
11852
+ prefetch: prefetchProp,
11660
11853
  className,
11661
11854
  classNames
11662
11855
  }) {
11663
11856
  const mode = displayMode ?? product.display_mode ?? "card";
11664
11857
  const [isOpen, setIsOpen] = React14.useState(false);
11665
- const [shouldFetch, setShouldFetch] = React14.useState(false);
11666
- const { product: productDetails } = useProduct(
11667
- product.slug ?? product.id,
11668
- { enabled: shouldFetch || isOpen }
11669
- );
11670
- const handlePrefetch = React14.useCallback(() => {
11671
- setShouldFetch(true);
11672
- }, []);
11673
- const handleOpenChange = React14.useCallback((open) => {
11674
- setIsOpen(open);
11675
- if (open) {
11676
- setShouldFetch(true);
11858
+ const context = useOptionalCimplifyClient();
11859
+ const client = context?.client ?? null;
11860
+ const prefetchConfig = context?.prefetch ?? DEFAULT_PREFETCH_OPTIONS;
11861
+ const LinkComponent = context?.linkComponent ?? null;
11862
+ const prefetchMode = prefetchProp ?? prefetchConfig.mode;
11863
+ const imagePriority = React14.useContext(ImagePriorityContext);
11864
+ const { product: productDetails } = useProduct(product.slug ?? product.id, {
11865
+ enabled: isOpen
11866
+ });
11867
+ const warm = React14.useCallback(() => {
11868
+ if (!client || prefetchMode === "off") return;
11869
+ speculate(() => prefetchProduct(client, product.slug ?? product.id));
11870
+ if (prefetchConfig.images) {
11871
+ preloadImage(product.images?.[0] || product.image_url);
11677
11872
  }
11678
- }, []);
11873
+ }, [client, prefetchMode, prefetchConfig.images, product]);
11874
+ const viewportRef = useInViewport(warm, {
11875
+ rootMargin: prefetchConfig.rootMargin,
11876
+ disabled: prefetchMode !== "aggressive"
11877
+ });
11878
+ const handleOpenChange = React14.useCallback(
11879
+ (open) => {
11880
+ setIsOpen(open);
11881
+ if (open && client) {
11882
+ trackProductView(client, product);
11883
+ }
11884
+ },
11885
+ [client, product]
11886
+ );
11679
11887
  const handleClose = React14.useCallback(() => {
11680
11888
  setIsOpen(false);
11681
11889
  }, []);
@@ -11704,6 +11912,9 @@ function ProductCard({
11704
11912
  {
11705
11913
  src: imageUrl,
11706
11914
  alt: product.name,
11915
+ loading: imagePriority ? "eager" : "lazy",
11916
+ decoding: imagePriority ? "auto" : "async",
11917
+ fetchPriority: imagePriority ? "high" : void 0,
11707
11918
  className: cn("w-full h-full object-cover transition-transform duration-300 group-hover:scale-105", classNames?.image),
11708
11919
  "data-cimplify-product-card-image": true
11709
11920
  }
@@ -11721,12 +11932,33 @@ function ProductCard({
11721
11932
  const linkHref = href ?? `/menu/${product.slug}`;
11722
11933
  const linkClassName = cn("group block no-underline text-[inherit] rounded-xl border border-border bg-background transition-shadow duration-200 hover:shadow-md", className, classNames?.root);
11723
11934
  if (renderLink) {
11724
- return renderLink({ href: linkHref, className: linkClassName, children: cardBody });
11935
+ return renderLink({
11936
+ href: linkHref,
11937
+ className: linkClassName,
11938
+ children: cardBody,
11939
+ onPointerEnter: warm,
11940
+ onFocus: warm
11941
+ });
11942
+ }
11943
+ if (LinkComponent) {
11944
+ return /* @__PURE__ */ jsxRuntime.jsx(
11945
+ LinkComponent,
11946
+ {
11947
+ href: linkHref,
11948
+ className: linkClassName,
11949
+ onPointerEnter: warm,
11950
+ onFocus: warm,
11951
+ children: cardBody
11952
+ }
11953
+ );
11725
11954
  }
11726
11955
  return /* @__PURE__ */ jsxRuntime.jsx(
11727
11956
  "a",
11728
11957
  {
11729
11958
  href: linkHref,
11959
+ ref: viewportRef,
11960
+ onPointerEnter: warm,
11961
+ onFocus: warm,
11730
11962
  "data-cimplify-product-card": true,
11731
11963
  "data-display-mode": "page",
11732
11964
  className: linkClassName,
@@ -11738,7 +11970,9 @@ function ProductCard({
11738
11970
  /* @__PURE__ */ jsxRuntime.jsx(
11739
11971
  dialog.Dialog.Trigger,
11740
11972
  {
11741
- onPointerEnter: handlePrefetch,
11973
+ ref: viewportRef,
11974
+ onPointerEnter: warm,
11975
+ onFocus: warm,
11742
11976
  "data-cimplify-product-card": true,
11743
11977
  "data-display-mode": "card",
11744
11978
  className: cn(
@@ -11840,21 +12074,18 @@ function ProductGrid({
11840
12074
  id: gridId,
11841
12075
  "data-cimplify-product-grid": true,
11842
12076
  className: cn(className, classNames?.root),
11843
- children: products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
11844
- "div",
11845
- {
11846
- "data-cimplify-product-grid-item": true,
11847
- className: classNames?.item,
11848
- children: renderCard ? renderCard(product) : /* @__PURE__ */ jsxRuntime.jsx(
11849
- ProductCard,
11850
- {
11851
- product,
11852
- renderImage
11853
- }
11854
- )
11855
- },
11856
- product.id
11857
- ))
12077
+ children: products.map((product, index) => {
12078
+ const card = renderCard ? renderCard(product) : /* @__PURE__ */ jsxRuntime.jsx(ProductCard, { product, renderImage });
12079
+ return /* @__PURE__ */ jsxRuntime.jsx(
12080
+ "div",
12081
+ {
12082
+ "data-cimplify-product-grid-item": true,
12083
+ className: classNames?.item,
12084
+ children: index < xl ? /* @__PURE__ */ jsxRuntime.jsx(ImagePriorityContext.Provider, { value: true, children: card }) : card
12085
+ },
12086
+ product.id
12087
+ );
12088
+ })
11858
12089
  }
11859
12090
  )
11860
12091
  ] });
@@ -11895,6 +12126,15 @@ function SearchInput({
11895
12126
  [searchOptions, categoryId]
11896
12127
  );
11897
12128
  const { results, isLoading, query, setQuery, clear } = useSearch(mergedOptions);
12129
+ const context = useOptionalCimplifyClient();
12130
+ const warmResult = React14.useCallback(
12131
+ (product) => {
12132
+ const client = mergedOptions.client ?? context?.client;
12133
+ if (!client || context?.prefetch.mode === "off") return;
12134
+ speculate(() => prefetchProduct(client, product.slug ?? product.id));
12135
+ },
12136
+ [mergedOptions.client, context]
12137
+ );
11898
12138
  const handleValueChange = React14.useCallback(
11899
12139
  (value) => {
11900
12140
  setQuery(value);
@@ -11959,6 +12199,8 @@ function SearchInput({
11959
12199
  {
11960
12200
  type: "button",
11961
12201
  onClick: () => onResultClick?.(product),
12202
+ onPointerEnter: () => warmResult(product),
12203
+ onFocus: () => warmResult(product),
11962
12204
  "data-cimplify-search-result": true,
11963
12205
  className: classNames?.resultItem,
11964
12206
  style: !renderResult ? { display: "flex", alignItems: "center", gap: "0.5rem", width: "100%" } : void 0,
@@ -13668,18 +13910,24 @@ function CataloguePage({
13668
13910
  const [maxPrice, setMaxPrice] = React14.useState("");
13669
13911
  const [page, setPage] = React14.useState(1);
13670
13912
  const debouncedSearch = useDebounce(searchQuery, 300);
13913
+ const filterOptions = React14.useMemo(
13914
+ () => ({
13915
+ category: selectedCategory ?? void 0,
13916
+ search: debouncedSearch.length >= 2 ? debouncedSearch : void 0,
13917
+ sort_by: sortBy,
13918
+ sort_order: sortOrder,
13919
+ in_stock: inStockOnly || void 0,
13920
+ tags: selectedTags.length > 0 ? selectedTags : void 0,
13921
+ min_price: minPrice ? parseFloat(minPrice) : void 0,
13922
+ max_price: maxPrice ? parseFloat(maxPrice) : void 0,
13923
+ limit: pageSize
13924
+ }),
13925
+ [selectedCategory, debouncedSearch, sortBy, sortOrder, inStockOnly, selectedTags, minPrice, maxPrice, pageSize]
13926
+ );
13671
13927
  const { products: fetchedProducts, isLoading, pagination } = useProducts({
13928
+ ...filterOptions,
13672
13929
  enabled: productsProp === void 0,
13673
- category: selectedCategory ?? void 0,
13674
- search: debouncedSearch.length >= 2 ? debouncedSearch : void 0,
13675
- sort_by: sortBy,
13676
- sort_order: sortOrder,
13677
- in_stock: inStockOnly || void 0,
13678
- tags: selectedTags.length > 0 ? selectedTags : void 0,
13679
- min_price: minPrice ? parseFloat(minPrice) : void 0,
13680
- max_price: maxPrice ? parseFloat(maxPrice) : void 0,
13681
- page,
13682
- limit: pageSize
13930
+ page
13683
13931
  });
13684
13932
  const { categories: fetchedCategories } = useCategories({
13685
13933
  enabled: categoriesProp === void 0
@@ -13687,6 +13935,10 @@ function CataloguePage({
13687
13935
  const products = productsProp ?? fetchedProducts;
13688
13936
  const categories = categoriesProp ?? fetchedCategories;
13689
13937
  const totalPages = pagination?.total_pages ?? 1;
13938
+ const context = useOptionalCimplifyClient();
13939
+ if (context && context.prefetch.mode !== "off" && productsProp === void 0 && !isLoading && page < totalPages) {
13940
+ speculate(() => prefetchProducts(context.client, { ...filterOptions, page: page + 1 }));
13941
+ }
13690
13942
  const handleCategoryChange = React14.useCallback((id) => {
13691
13943
  setSelectedCategory(id);
13692
13944
  setPage(1);
@@ -14588,6 +14840,12 @@ function ProductPage({
14588
14840
  enabled: !productProp && resolvedId.length > 0
14589
14841
  });
14590
14842
  const product = productProp ?? fetched;
14843
+ const context = useOptionalCimplifyClient();
14844
+ React14.useEffect(() => {
14845
+ if (product && context) {
14846
+ trackProductView(context.client, product);
14847
+ }
14848
+ }, [context, product]);
14591
14849
  if (isLoading && !product) {
14592
14850
  return /* @__PURE__ */ jsxRuntime.jsx(
14593
14851
  "div",
@@ -16012,6 +16270,15 @@ function RecommendationCarousel({
16012
16270
  enabled: recommendationsProp === void 0
16013
16271
  });
16014
16272
  const recommendations = recommendationsProp ?? fetched;
16273
+ const context = useOptionalCimplifyClient();
16274
+ const warmRecommendation = React14.useCallback(
16275
+ (rec) => {
16276
+ const productId = rec.product?.id;
16277
+ if (!productId || !context || context.prefetch.mode === "off") return;
16278
+ speculate(() => prefetchProduct(context.client, productId));
16279
+ },
16280
+ [context]
16281
+ );
16015
16282
  if (isLoading && recommendations.length === 0) {
16016
16283
  return /* @__PURE__ */ jsxRuntime.jsx(
16017
16284
  "div",
@@ -16038,6 +16305,8 @@ function RecommendationCarousel({
16038
16305
  {
16039
16306
  type: "button",
16040
16307
  onClick: () => onProductClick?.(rec),
16308
+ onPointerEnter: () => warmRecommendation(rec),
16309
+ onFocus: () => warmRecommendation(rec),
16041
16310
  "data-cimplify-recommendation-item": true,
16042
16311
  className: classNames?.item,
16043
16312
  style: { flexShrink: 0 },
@@ -16116,6 +16385,14 @@ function RecentlyViewed({
16116
16385
  classNames
16117
16386
  }) {
16118
16387
  const { state, isLoading } = useActivityState();
16388
+ const context = useOptionalCimplifyClient();
16389
+ const warmViewed = React14.useCallback(
16390
+ (productId) => {
16391
+ if (!context || context.prefetch.mode === "off") return;
16392
+ speculate(() => prefetchProduct(context.client, productId));
16393
+ },
16394
+ [context]
16395
+ );
16119
16396
  const rawViewed = state?.activity?.viewed_products ?? [];
16120
16397
  const viewed = limit ? rawViewed.slice(0, limit) : rawViewed;
16121
16398
  if (isLoading && viewed.length === 0) {
@@ -16136,6 +16413,8 @@ function RecentlyViewed({
16136
16413
  {
16137
16414
  type: "button",
16138
16415
  onClick: () => onProductClick?.(product),
16416
+ onPointerEnter: () => warmViewed(product.product_id),
16417
+ onFocus: () => warmViewed(product.product_id),
16139
16418
  "data-cimplify-recently-viewed-item": true,
16140
16419
  className: classNames?.item,
16141
16420
  children: renderProduct ? renderProduct(product) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: product.product_name ?? product.product_id })
@@ -16956,6 +17235,8 @@ exports.WholesaleProductCard = WholesaleProductCard;
16956
17235
  exports.WholesaleProductLayout = WholesaleProductLayout;
16957
17236
  exports.WishlistButton = WishlistButton;
16958
17237
  exports.cn = cn;
17238
+ exports.prefetchProduct = prefetchProduct;
17239
+ exports.prefetchProducts = prefetchProducts;
16959
17240
  exports.roomToResource = roomToResource;
16960
17241
  exports.sanitizeRichTextHtml = sanitizeRichTextHtml;
16961
17242
  exports.stripHtmlToText = stripHtmlToText;