@tapcart/mobile-components 0.12.8 → 0.12.10

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.
@@ -1 +1 @@
1
- {"version":3,"file":"use-block-conditional-rendering.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-block-conditional-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAGb,MAAM,kBAAkB,CAAA;AAezB,eAAO,MAAM,4BAA4B,WAC/B;IACN,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,GAAG,CAAA;IACpB,iBAAiB,EAAE,GAAG,CAAA;CACvB,UACO,YAAY,6BACO,GAAG;;;CA4Q/B,CAAA"}
1
+ {"version":3,"file":"use-block-conditional-rendering.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-block-conditional-rendering.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAGb,MAAM,kBAAkB,CAAA;AAezB,eAAO,MAAM,4BAA4B,WAC/B;IACN,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,EAAE,GAAG,CAAA;IACpB,iBAAiB,EAAE,GAAG,CAAA;CACvB,UACO,YAAY,6BACO,GAAG;;;CAiT/B,CAAA"}
@@ -29,11 +29,7 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
29
29
  const { id: deviceId = "" } = deviceVariables || {};
30
30
  const productId = (_f = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("productId")) !== null && _f !== void 0 ? _f : undefined;
31
31
  const productHandle = (_g = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("productHandle")) !== null && _g !== void 0 ? _g : undefined;
32
- const location = {
33
- country,
34
- language,
35
- deviceId,
36
- };
32
+ const location = useMemo(() => ({ country, language, deviceId }), [country, language, deviceId]);
37
33
  const productMetafieldsQuery = useMemo(() => {
38
34
  var _a;
39
35
  if (!isConditionalsEnabled) {
@@ -67,7 +63,7 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
67
63
  ]);
68
64
  const shouldFetchProduct = isConditionalsEnabled && (productId || productHandle);
69
65
  const useProducts = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts) || mclUseProducts;
70
- const useProductsArguments = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
66
+ const useProductsArguments = useMemo(() => (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
71
67
  ? {
72
68
  productIds: productId ? [gidFromId(productId)] : [],
73
69
  productHandles: productHandle ? [productHandle] : [],
@@ -82,13 +78,20 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
82
78
  language,
83
79
  country,
84
80
  },
85
- };
86
- const useProductsSkipArguments = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
87
- ? {
88
- skip: true,
89
- }
90
- : null;
91
- const { products, error: useProductsError, isLoading: isProductsLoading, } = useProducts(shouldFetchProduct ? useProductsArguments : useProductsSkipArguments);
81
+ }, [
82
+ mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts,
83
+ productId,
84
+ productHandle,
85
+ apiUrl,
86
+ language,
87
+ country,
88
+ productMetafieldsQuery,
89
+ ]);
90
+ const useProductsSkipArguments = useMemo(() => (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts)
91
+ ? { skip: true }
92
+ : null, [mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useProducts]);
93
+ const useProductsInput = useMemo(() => shouldFetchProduct ? useProductsArguments : useProductsSkipArguments, [shouldFetchProduct, useProductsArguments, useProductsSkipArguments]);
94
+ const { products, error: useProductsError, isLoading: isProductsLoading, } = useProducts(useProductsInput);
92
95
  const blockCollectionMetafields = useMemo(() => {
93
96
  var _a;
94
97
  if (!isConditionalsEnabled) {
@@ -144,20 +147,30 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
144
147
  const collectionHandle = (_j = searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("collectionHandle")) !== null && _j !== void 0 ? _j : undefined;
145
148
  const shouldFetchCollection = isConditionalsEnabled && (collectionId || collectionHandle);
146
149
  const useCollection = (mobileComponentOverrides === null || mobileComponentOverrides === void 0 ? void 0 : mobileComponentOverrides.useCollection) || mclUseCollection;
147
- const { specificCollection = {}, error: useCollectionError, loading: isCollectionLoading, } = useCollection({
150
+ const useCollectionArguments = useMemo(() => ({
148
151
  appId,
149
152
  apiUrl,
150
153
  collectionId: shouldFetchCollection ? collectionId : undefined,
151
154
  collectionHandle: shouldFetchCollection ? collectionHandle : undefined,
152
155
  language: location === null || location === void 0 ? void 0 : location.language,
153
156
  metafields: shouldFetchCollection ? memoizedMetafields : undefined,
154
- });
157
+ }), [
158
+ appId,
159
+ apiUrl,
160
+ shouldFetchCollection,
161
+ collectionId,
162
+ collectionHandle,
163
+ location === null || location === void 0 ? void 0 : location.language,
164
+ memoizedMetafields,
165
+ ]);
166
+ const { specificCollection = {}, error: useCollectionError, loading: isCollectionLoading, } = useCollection(useCollectionArguments);
155
167
  const isLoading = isProductsLoading || isCollectionLoading;
156
168
  if (useProductsError || useCollectionError) {
157
169
  console.error("Unable to load products in conditional block rendering hook: ", useProductsError !== null && useProductsError !== void 0 ? useProductsError : useCollectionError);
158
170
  }
171
+ const disabledResult = useMemo(() => ({ shouldShow: true, isLoading: false }), []);
159
172
  if (!isConditionalsEnabled) {
160
- return { shouldShow: true, isLoading: false };
173
+ return disabledResult;
161
174
  }
162
175
  let shouldShow = true;
163
176
  const blockHasTags = countNumberOfTagsInState(blockState) > 0;
@@ -200,5 +213,5 @@ export const useBlockConditionalRendering = (_props, _block, mobileComponentOver
200
213
  console.error("Error evaluating block visibility conditions:", e);
201
214
  shouldShow = true; // Fail-safe to show block
202
215
  }
203
- return { shouldShow, isLoading };
216
+ return useMemo(() => ({ shouldShow, isLoading }), [shouldShow, isLoading]);
204
217
  };
@@ -1 +1 @@
1
- {"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBAwR3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"use-infinite-scroll.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-infinite-scroll.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,uBAAuB,EAAmB,MAAM,iBAAiB,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,UAAU,QAAQ;IAChB,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,GAAG,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;CACxC;AAED,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IAGxC,WAAW,CAAC,EAAE,QAAQ,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGpC,SAAS,CAAC,EAAE,UAAU,GAAG,YAAY,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAChD,YAAY,CAAC,EAAE,CACb,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,GAAG,GAAG,IAAI,EAC5B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,GAAG,CAAA;IACR,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAA;IAC5B,KAAK,EAAE,GAAG,CAAA;IACV,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,KAAK,IAAI,CAAA;IAChD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,oBAAoB,EAAE,GAAG,EAAE,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,sCAAsC,iBACnC,uBAAuB,OAsBtC,CAAA;AAED,QAAA,MAAM,YAAY,WAAY,MAAM,WAGnC,CAAA;AAED,QAAA,MAAM,iBAAiB,wLAgBpB,sBAAsB,KAAG,uBAqS3B,CAAA;AAED,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAA"}
@@ -49,7 +49,7 @@ initialData, queryVariables: queryVariableProps,
49
49
  // Common props
50
50
  direction = "vertical", productLimit = Infinity, threshold = 0.01, interval = 33, // ~2 frames
51
51
  customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
52
- var _a, _b, _c, _d, _e;
52
+ var _a, _b, _c, _d, _e, _f;
53
53
  const searchParams = useSearchParams();
54
54
  const productCount = useRef(0);
55
55
  const isFirstRender = useRef(true);
@@ -102,7 +102,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
102
102
  return Object.assign(Object.assign({}, queryVariables), { cursorBlob: previousPageData.pageData.cursorBlob });
103
103
  };
104
104
  const apiFetcher = (body) => __awaiter(void 0, void 0, void 0, function* () {
105
- var _f, _g;
105
+ var _g, _h;
106
106
  if (!(initialData === null || initialData === void 0 ? void 0 : initialData.apiURL)) {
107
107
  throw new Error("initialData.apiURL is required for API mode");
108
108
  }
@@ -111,7 +111,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
111
111
  body: JSON.stringify(body),
112
112
  });
113
113
  const data = yield res.json();
114
- productCount.current += (_g = (_f = data === null || data === void 0 ? void 0 : data.products) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0;
114
+ productCount.current += (_h = (_g = data === null || data === void 0 ? void 0 : data.products) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0;
115
115
  return data;
116
116
  });
117
117
  // Return null from getKey to conditionally skip fetching
@@ -134,20 +134,23 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
134
134
  };
135
135
  const fetcher = usingSearchClient
136
136
  ? (...args) => __awaiter(void 0, void 0, void 0, function* () {
137
- var _h, _j;
137
+ var _j, _k;
138
138
  if (!searchClientFetcher)
139
139
  return null;
140
140
  const result = yield searchClientFetcher(...args);
141
- productCount.current += (_j = (_h = result === null || result === void 0 ? void 0 : result.products) === null || _h === void 0 ? void 0 : _h.length) !== null && _j !== void 0 ? _j : 0;
141
+ productCount.current += (_k = (_j = result === null || result === void 0 ? void 0 : result.products) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : 0;
142
142
  return result;
143
143
  })
144
144
  : (...args) => __awaiter(void 0, void 0, void 0, function* () {
145
145
  const effectiveFetcher = customFetcher || apiFetcher;
146
146
  return effectiveFetcher(...args);
147
147
  });
148
+ // Check if caller provided pre-fetched products to display while SWR fetches
149
+ const hasInitialProducts = Boolean((_a = initialData === null || initialData === void 0 ? void 0 : initialData.products) === null || _a === void 0 ? void 0 : _a.length);
148
150
  const { data, error, size, setSize, isLoading, isValidating, mutate, } = useSWRInfinite(getKey, fetcher, {
149
151
  revalidateFirstPage: false,
150
152
  initialSize: 1,
153
+ keepPreviousData: true,
151
154
  });
152
155
  // Detect when params change and force cache invalidation
153
156
  useEffect(() => {
@@ -166,15 +169,19 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
166
169
  // Skip reset on first render, but force it for any subsequent transitions to valid params
167
170
  if (!isFirstRender.current && hasValidCurrentParams) {
168
171
  productCount.current = 0;
169
- mutate(undefined, { revalidate: true });
172
+ mutate();
170
173
  }
171
174
  }
172
175
  isFirstRender.current = false;
173
176
  }, [currentCollectionId, currentCollectionHandle, currentSearchQuery, mutate]);
174
- const isLoadingInitialData = !data && !error;
177
+ // When we have pre-fetched products, don't report as "loading initial data"
178
+ // so the grid renders immediately with those products
179
+ const isLoadingInitialData = !data && !error && !hasInitialProducts;
175
180
  const isLoadingMore = isLoadingInitialData ||
176
181
  (size > 0 && data && typeof data[size - 1] === "undefined");
177
- const isEmpty = ((_c = (_b = (_a = data === null || data === void 0 ? void 0 : data[0]) === null || _a === void 0 ? void 0 : _a.products) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0) === 0;
182
+ const isEmpty = data
183
+ ? ((_d = (_c = (_b = data === null || data === void 0 ? void 0 : data[0]) === null || _b === void 0 ? void 0 : _b.products) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) === 0
184
+ : !hasInitialProducts;
178
185
  let isEndPointer;
179
186
  if (!data) {
180
187
  isEndPointer = true;
@@ -183,7 +190,7 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
183
190
  isEndPointer = !(searchClient === null || searchClient === void 0 ? void 0 : searchClient.getHasMore());
184
191
  }
185
192
  else {
186
- isEndPointer = !((_e = (_d = data[data.length - 1]) === null || _d === void 0 ? void 0 : _d.pageData) === null || _e === void 0 ? void 0 : _e.cursorBlob);
193
+ isEndPointer = !((_f = (_e = data[data.length - 1]) === null || _e === void 0 ? void 0 : _e.pageData) === null || _f === void 0 ? void 0 : _f.cursorBlob);
187
194
  }
188
195
  const isReachingEnd = isEmpty || isEndPointer;
189
196
  const isRefreshing = isValidating && data && data.length === size;
@@ -214,15 +221,20 @@ customFetcher, customGetKey, shouldSkipFetch: shouldSkipFetchProp, }) => {
214
221
  return;
215
222
  const unsubscribeAllChanges = searchClient.onSearchStateChange(() => {
216
223
  productCount.current = 0;
217
- mutate(undefined, { revalidate: true });
224
+ mutate();
218
225
  });
219
226
  return unsubscribeAllChanges;
220
227
  }, [searchClient, mutate, usingSearchClient]);
221
228
  const products = useMemo(() => {
222
- return data
223
- ? data === null || data === void 0 ? void 0 : data.flatMap((page) => page === null || page === void 0 ? void 0 : page.products).slice(0, productLimit)
224
- : [];
225
- }, [data, productLimit]);
229
+ if (data) {
230
+ return data.flatMap((page) => page === null || page === void 0 ? void 0 : page.products).slice(0, productLimit);
231
+ }
232
+ // Use pre-fetched products while SWR fetches the real paginated data
233
+ if (hasInitialProducts) {
234
+ return initialData.products;
235
+ }
236
+ return [];
237
+ }, [data, productLimit, hasInitialProducts, initialData === null || initialData === void 0 ? void 0 : initialData.products]);
226
238
  // Collect all integration responses per page when using search client
227
239
  const integrationResponses = useMemo(() => {
228
240
  if (!usingSearchClient)
@@ -1,6 +1,12 @@
1
1
  import { ProductVariant } from "app-studio-types";
2
2
  type SelectedOptions = Record<string, string>;
3
- export declare function useProductOptions(variants: ProductVariant[] | undefined | null, selectedVariantId?: string | null): {
3
+ type VariantConnection = {
4
+ edges: {
5
+ node: ProductVariant;
6
+ }[];
7
+ };
8
+ type VariantsInput = ProductVariant[] | VariantConnection | undefined | null;
9
+ export declare function useProductOptions(variants: VariantsInput, selectedVariantId?: string | null): {
4
10
  selectedOptions: SelectedOptions;
5
11
  handleSelect: (optionName: string, optionValue: string) => void;
6
12
  selectedVariant: ProductVariant | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"use-product-options.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-product-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE7C,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,EAAE,GAAG,SAAS,GAAG,IAAI,EAC7C,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI;;+BA+DC,MAAM,eAAe,MAAM,KAAG,IAAI;;;;;;EA4CrE"}
1
+ {"version":3,"file":"use-product-options.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-product-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE7C,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE;QAAE,IAAI,EAAE,cAAc,CAAA;KAAE,EAAE,CAAA;CAClC,CAAA;AAED,KAAK,aAAa,GAAG,cAAc,EAAE,GAAG,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAAA;AAE5E,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI;;+BAwEC,MAAM,eAAe,MAAM,KAAG,IAAI;;;;;;EA4CrE"}
@@ -1,7 +1,13 @@
1
1
  "use client";
2
2
  import { useState, useEffect, useMemo, useCallback } from "react";
3
3
  export function useProductOptions(variants, selectedVariantId) {
4
- const safeVariants = useMemo(() => variants || [], [variants]);
4
+ const safeVariants = useMemo(() => {
5
+ var _a;
6
+ if (variants && "edges" in variants) {
7
+ return (((_a = variants.edges) === null || _a === void 0 ? void 0 : _a.map((e) => e === null || e === void 0 ? void 0 : e.node).filter((v) => Boolean(v))) || []);
8
+ }
9
+ return variants || [];
10
+ }, [variants]);
5
11
  const getCheapestProductFromVariants = useMemo(() => {
6
12
  return () => {
7
13
  if (!safeVariants.length)
@@ -1 +1 @@
1
- {"version":3,"file":"use-recommendations.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAOxE,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAUD,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,GAAG,CAAA;CACX;AAmBD,QAAA,MAAM,kBAAkB,yEASrB,sBAAsB,KAAG,uBAmH3B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"use-recommendations.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAOxE,UAAU,sBAAsB;IAE9B,YAAY,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAGxC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;CACf;AAUD,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,GAAG,CAAA;CACX;AAmBD,QAAA,MAAM,kBAAkB,yEASrB,sBAAsB,KAAG,uBA2H3B,CAAA;AAED,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -35,7 +35,13 @@ queryVariables, apiURL, }) => {
35
35
  ? query
36
36
  : (searchParams === null || searchParams === void 0 ? void 0 : searchParams.get("recommendation")) || "";
37
37
  // Check if resolved recommendation meets minimum length requirement
38
- const shouldFetch = recommendation.length >= MIN_QUERY_LENGTH;
38
+ // Allow fetching when:
39
+ // - query is empty → show trending searches
40
+ // - query meets minimum length (≥3 chars)
41
+ // - searchClient has a customSearchConfig (e.g., filter-only queries on PDP)
42
+ const shouldFetch = !recommendation.trim().length ||
43
+ recommendation.length >= MIN_QUERY_LENGTH ||
44
+ (usingSearchClient && Boolean(customSearchConfig));
39
45
  const [cachedRecommendation, setCachedRecommendations] = React.useState(recommendationsLocalStorage.getCacheItem({
40
46
  id: `${recommendation}-${queryVariables === null || queryVariables === void 0 ? void 0 : queryVariables.language}`,
41
47
  }));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-recommendations.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-recommendations.test.d.ts","sourceRoot":"","sources":["../../../components/hooks/use-recommendations.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { useRecommendations } from "./use-recommendations";
3
+ // Mock next/navigation
4
+ jest.mock("next/navigation", () => ({
5
+ useSearchParams: () => new URLSearchParams(),
6
+ }));
7
+ // Mock SWR to give us control over fetching behavior
8
+ jest.mock("swr", () => {
9
+ return {
10
+ __esModule: true,
11
+ default: (key, fetcher, _options) => {
12
+ // If key is null, SWR doesn't fetch
13
+ if (key === null) {
14
+ return { data: undefined, error: undefined, isLoading: false };
15
+ }
16
+ return { data: undefined, error: undefined, isLoading: true };
17
+ },
18
+ };
19
+ });
20
+ // Mock the local storage cache
21
+ jest.mock("../libs/cache/RecommendationsLocalStorage", () => {
22
+ return {
23
+ __esModule: true,
24
+ default: jest.fn().mockImplementation(() => ({
25
+ getCacheItem: jest.fn().mockReturnValue(null),
26
+ setCacheItem: jest.fn(),
27
+ })),
28
+ };
29
+ });
30
+ describe("useRecommendations", () => {
31
+ const mockSearchClient = {
32
+ getRecommendations: jest.fn().mockResolvedValue({
33
+ products: [{ id: "1", title: "Product 1" }],
34
+ searches: ["suggestion 1"],
35
+ facets: [],
36
+ totalResults: 1,
37
+ }),
38
+ };
39
+ const defaultProps = {
40
+ queryVariables: { appId: "test-app" },
41
+ apiURL: "http://test.com/recommendations",
42
+ };
43
+ beforeEach(() => {
44
+ jest.clearAllMocks();
45
+ });
46
+ it("should fetch trending recommendations when query is empty", () => {
47
+ const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "" }, defaultProps)));
48
+ // Empty query now triggers a trending recommendations fetch
49
+ expect(result.current.isLoading).toBe(true);
50
+ });
51
+ it("should not fetch when query is below MIN_QUERY_LENGTH and no customSearchConfig", () => {
52
+ const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "ab" }, defaultProps)));
53
+ expect(result.current.products).toEqual([]);
54
+ expect(result.current.isLoading).toBe(false);
55
+ });
56
+ it("should fetch trending when no searchClient and empty search params", () => {
57
+ const { result } = renderHook(() => useRecommendations(Object.assign({ query: "ab" }, defaultProps)));
58
+ // Without searchClient, recommendation comes from searchParams (empty string)
59
+ // Empty query now triggers a trending fetch
60
+ expect(result.current.isLoading).toBe(true);
61
+ });
62
+ it("should fetch when query meets MIN_QUERY_LENGTH", () => {
63
+ const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "abc" }, defaultProps)));
64
+ // SWR mock returns isLoading: true when key is not null
65
+ expect(result.current.isLoading).toBe(true);
66
+ });
67
+ it("should fetch when customSearchConfig is provided with searchClient even with empty query", () => {
68
+ const customSearchConfig = {
69
+ filter: 'attributes.mfield_bnb__seriesId = 584945 AND NOT id: ANY("9338273530096")',
70
+ pageSize: 6,
71
+ };
72
+ const { result } = renderHook(() => useRecommendations(Object.assign({ searchClient: mockSearchClient, query: "", customSearchConfig }, defaultProps)));
73
+ // shouldFetch should be true due to customSearchConfig bypass, so SWR gets a key and returns isLoading: true
74
+ expect(result.current.isLoading).toBe(true);
75
+ });
76
+ it("should fetch trending when customSearchConfig is provided without searchClient and empty query", () => {
77
+ const customSearchConfig = {
78
+ filter: "attributes.mfield_bnb__seriesId = 584945",
79
+ pageSize: 6,
80
+ };
81
+ const { result } = renderHook(() => useRecommendations(Object.assign({ query: "", customSearchConfig }, defaultProps)));
82
+ // Empty query now triggers a trending fetch regardless of searchClient
83
+ expect(result.current.isLoading).toBe(true);
84
+ });
85
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"variablesCart.util.d.ts","sourceRoot":"","sources":["../../lib/variablesCart.util.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,6BAA6B,EAO9B,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,2BAA2B;;cAM5B,MAAM;;0BAEI;YAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;YACzB,KAAK,EAAE,MAAM,CAAA;YACb,cAAc,CAAC,EAAE,MAAM,CAAA;SACxB,EAAE;;;;;;CAyBN,CAAA;AA4FD,eAAO,MAAM,6BAA6B,SAClC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAED,eAAO,MAAM,2BAA2B,SAChC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAoMD,MAAM,MAAM,uBAAuB,GAAG;IACpC,yBAAyB,EAAE,sBAAsB,EAAE,CAAA;IACnD,gBAAgB,EAAE,6BAA6B,EAAE,CAAA;IACjD,cAAc,EAAE,OAAO,CAAA;IACvB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,eAAO,MAAM,iCAAiC,EAAE,uBAU/C,CAAA;AAED,eAAO,MAAM,8BAA8B,SACnC,SAAS,GAAG,IAAI,KACrB,uBAmCF,CAAA"}
1
+ {"version":3,"file":"variablesCart.util.d.ts","sourceRoot":"","sources":["../../lib/variablesCart.util.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EACT,sBAAsB,EACtB,6BAA6B,EAO9B,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,2BAA2B;;cAM5B,MAAM;;0BAEI;YAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;YACzB,KAAK,EAAE,MAAM,CAAA;YACb,cAAc,CAAC,EAAE,MAAM,CAAA;SACxB,EAAE;;;;;;CAyBN,CAAA;AA8FD,eAAO,MAAM,6BAA6B,SAClC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAED,eAAO,MAAM,2BAA2B,SAChC,MAAM,QACN,SAAS,GAAG,IAAI,YAUvB,CAAA;AAoMD,MAAM,MAAM,uBAAuB,GAAG;IACpC,yBAAyB,EAAE,sBAAsB,EAAE,CAAA;IACnD,gBAAgB,EAAE,6BAA6B,EAAE,CAAA;IACjD,cAAc,EAAE,OAAO,CAAA;IACvB,oBAAoB,EAAE,MAAM,CAAA;IAC5B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,oBAAoB,EAAE,MAAM,CAAA;IAC5B,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,eAAO,MAAM,iCAAiC,EAAE,uBAU/C,CAAA;AAED,eAAO,MAAM,8BAA8B,SACnC,SAAS,GAAG,IAAI,KACrB,uBAmCF,CAAA"}
@@ -42,6 +42,9 @@ const getOrderLevelDiscounts = (cart) => {
42
42
  if (discount.code) {
43
43
  code = ((discount === null || discount === void 0 ? void 0 : discount.code) || "").toUpperCase();
44
44
  }
45
+ else if (discount.title) {
46
+ code = ((discount === null || discount === void 0 ? void 0 : discount.title) || "").toUpperCase();
47
+ }
45
48
  else if ((_b = (_a = discount.kind) === null || _a === void 0 ? void 0 : _a.manual) === null || _b === void 0 ? void 0 : _b.code) {
46
49
  code = (((_d = (_c = discount === null || discount === void 0 ? void 0 : discount.kind) === null || _c === void 0 ? void 0 : _c.manual) === null || _d === void 0 ? void 0 : _d.code) || "").toUpperCase();
47
50
  }
package/dist/styles.css CHANGED
@@ -1030,6 +1030,9 @@ video {
1030
1030
  .h-24 {
1031
1031
  height: 6rem;
1032
1032
  }
1033
+ .h-3 {
1034
+ height: 0.75rem;
1035
+ }
1033
1036
  .h-4 {
1034
1037
  height: 1rem;
1035
1038
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapcart/mobile-components",
3
- "version": "0.12.8",
3
+ "version": "0.12.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "style": "dist/styles.css",