@wix/headless-stores 0.0.61 → 0.0.64

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,4 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { InventoryAvailabilityStatus } from '@wix/auto_sdk_stores_products-v-3';
2
3
  import React from 'react';
3
4
  import { AsChildSlot } from '@wix/headless-utils/react';
4
5
  import { MediaGallery } from '@wix/headless-media/react';
@@ -39,6 +40,8 @@ var TestIds;
39
40
  TestIds["productCompareAtPrice"] = "product-compare-at-price";
40
41
  TestIds["productSlug"] = "product-slug";
41
42
  TestIds["productRaw"] = "product-raw";
43
+ TestIds["productRibbon"] = "product-ribbon";
44
+ TestIds["productStock"] = "product-stock";
42
45
  TestIds["productVariants"] = "product-variants";
43
46
  TestIds["productVariantOptions"] = "product-variant-options";
44
47
  TestIds["productVariantOption"] = "product-variant-option";
@@ -68,9 +71,10 @@ var TestIds;
68
71
  * ```
69
72
  */
70
73
  export function Root(props) {
71
- return (_jsx(CoreProduct.Root, { productServiceConfig: { product: props.product }, "data-testid": TestIds.productRoot, children: _jsx(MediaGallery.Root, { mediaGalleryServiceConfig: {
74
+ const { children, product, ...attrs } = props;
75
+ return (_jsx(CoreProduct.Root, { productServiceConfig: { product: props.product }, children: _jsx(MediaGallery.Root, { mediaGalleryServiceConfig: {
72
76
  media: props.product.media?.itemsInfo?.items ?? [],
73
- }, children: _jsx(ProductVariantSelector.Root, { children: _jsx(ProductModifiers.Root, { children: _jsx(SelectedVariant.Root, { children: props.children }) }) }) }) }));
77
+ }, children: _jsx(ProductVariantSelector.Root, { children: _jsx(ProductModifiers.Root, { children: _jsx(SelectedVariant.Root, { children: _jsx(AsChildSlot, { ...attrs, children: children }) }) }) }) }) }));
74
78
  }
75
79
  /**
76
80
  * Displays the product name with customizable rendering following the documented API.
@@ -287,6 +291,120 @@ export const Raw = React.forwardRef((props, ref) => {
287
291
  return (_jsx(AsChildSlot, { ref: ref, className: className, "data-testid": TestIds.productRaw, customElement: children, customElementProps: { product } }));
288
292
  } }));
289
293
  });
294
+ /**
295
+ * Displays the product ribbon with customizable rendering following the documented API.
296
+ *
297
+ * @component
298
+ * @example
299
+ * ```tsx
300
+ * // Default usage
301
+ * <Product.Ribbon className="absolute top-2 left-2 bg-red-500 text-white px-2 py-1 text-xs rounded" />
302
+ *
303
+ * // asChild with primitive
304
+ * <Product.Ribbon asChild>
305
+ * <span className="ribbon-badge" />
306
+ * </Product.Ribbon>
307
+ *
308
+ * // asChild with react component
309
+ * <Product.Ribbon asChild>
310
+ * {React.forwardRef(({ribbon, ...props}, ref) => (
311
+ * <div ref={ref} {...props} className="ribbon-badge">
312
+ * {ribbon}
313
+ * </div>
314
+ * ))}
315
+ * </Product.Ribbon>
316
+ * ```
317
+ */
318
+ export const Ribbon = React.forwardRef((props, ref) => {
319
+ const { asChild, children, className } = props;
320
+ return (_jsx(CoreProduct.Ribbon, { children: ({ ribbon, hasRibbon }) => {
321
+ // Don't render anything if there's no ribbon
322
+ if (!hasRibbon) {
323
+ return null;
324
+ }
325
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productRibbon, customElement: children, customElementProps: { ribbon }, content: ribbon, children: _jsx("span", { children: ribbon }) }));
326
+ } }));
327
+ });
328
+ /**
329
+ * Displays the product stock status with customizable rendering and labels following the documented API.
330
+ *
331
+ * @component
332
+ * @example
333
+ * ```tsx
334
+ * // Default usage
335
+ * <Product.Stock
336
+ * className="stock-indicator"
337
+ * labels={{
338
+ * inStock: 'In Stock',
339
+ * limitedStock: 'Limited Stock',
340
+ * outOfStock: 'Out of Stock'
341
+ * }}
342
+ * />
343
+ *
344
+ * // asChild with primitive
345
+ * <Product.Stock asChild>
346
+ * <div className="stock-status" />
347
+ * </Product.Stock>
348
+ *
349
+ * // asChild with react component
350
+ * <Product.Stock
351
+ * labels={{
352
+ * inStock: 'Available',
353
+ * limitedStock: 'Low Stock',
354
+ * outOfStock: 'Sold Out'
355
+ * }}
356
+ * asChild
357
+ * >
358
+ * {React.forwardRef(({status, label, ...props}, ref) => (
359
+ * <div
360
+ * ref={ref}
361
+ * {...props}
362
+ * className="flex items-center gap-1 data-[state='in-stock']:text-green-600 data-[state='limited-stock']:text-yellow-600 data-[state='out-of-stock']:text-red-600"
363
+ * >
364
+ * <div className="w-2 h-2 rounded-full data-[state='in-stock']:bg-green-500 data-[state='limited-stock']:bg-yellow-500 data-[state='out-of-stock']:bg-red-500" />
365
+ * <span className="text-xs font-medium">
366
+ * {label}
367
+ * </span>
368
+ * </div>
369
+ * ))}
370
+ * </Product.Stock>
371
+ * ```
372
+ */
373
+ export const Stock = React.forwardRef((props, ref) => {
374
+ const { asChild, children, className, labels } = props;
375
+ return (_jsx(CoreProduct.Content, { children: ({ product }) => {
376
+ const availabilityStatus = product.inventory?.availabilityStatus;
377
+ // Default labels
378
+ const defaultLabels = {
379
+ inStock: 'In Stock',
380
+ limitedStock: 'Partially Out of Stock',
381
+ outOfStock: 'Out of Stock',
382
+ };
383
+ const finalLabels = { ...defaultLabels, ...labels };
384
+ // Determine status based on availabilityStatus
385
+ let status;
386
+ let label;
387
+ switch (availabilityStatus) {
388
+ case InventoryAvailabilityStatus.IN_STOCK:
389
+ status = 'in-stock';
390
+ label = finalLabels.inStock;
391
+ break;
392
+ case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
393
+ status = 'limited-stock';
394
+ label = finalLabels.limitedStock;
395
+ break;
396
+ case InventoryAvailabilityStatus.OUT_OF_STOCK:
397
+ default:
398
+ status = 'out-of-stock';
399
+ label = finalLabels.outOfStock;
400
+ break;
401
+ }
402
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productStock, "data-state": status, customElement: children, customElementProps: {
403
+ status,
404
+ label,
405
+ }, content: label, children: _jsx("span", { children: label }) }));
406
+ } }));
407
+ });
290
408
  /**
291
409
  * Container for product variant selection system.
292
410
  * Does not render when there are no variants.
@@ -183,3 +183,43 @@ export interface ProductSlugRenderProps {
183
183
  * ```
184
184
  */
185
185
  export declare function Slug(props: ProductSlugProps): import("react").ReactNode;
186
+ /**
187
+ * Props for ProductRibbon headless component
188
+ */
189
+ export interface ProductRibbonProps {
190
+ /** Render prop function that receives product ribbon data */
191
+ children: (props: ProductRibbonRenderProps) => React.ReactNode;
192
+ }
193
+ /**
194
+ * Render props for ProductRibbon component
195
+ */
196
+ export interface ProductRibbonRenderProps {
197
+ /** Product ribbon text */
198
+ ribbon: string | null;
199
+ /** Whether the product has a ribbon */
200
+ hasRibbon: boolean;
201
+ }
202
+ /**
203
+ * Headless component for product ribbon display
204
+ *
205
+ * @component
206
+ * @example
207
+ * ```tsx
208
+ * import { Product } from '@wix/stores/components';
209
+ *
210
+ * function ProductRibbonDisplay() {
211
+ * return (
212
+ * <Product.Ribbon>
213
+ * {({ ribbon, hasRibbon }) => (
214
+ * hasRibbon ? (
215
+ * <span className="ribbon-badge">
216
+ * {ribbon}
217
+ * </span>
218
+ * ) : null
219
+ * )}
220
+ * </Product.Ribbon>
221
+ * );
222
+ * }
223
+ * ```
224
+ */
225
+ export declare function Ribbon(props: ProductRibbonProps): import("react").ReactNode;
@@ -155,3 +155,36 @@ export function Slug(props) {
155
155
  slug,
156
156
  });
157
157
  }
158
+ /**
159
+ * Headless component for product ribbon display
160
+ *
161
+ * @component
162
+ * @example
163
+ * ```tsx
164
+ * import { Product } from '@wix/stores/components';
165
+ *
166
+ * function ProductRibbonDisplay() {
167
+ * return (
168
+ * <Product.Ribbon>
169
+ * {({ ribbon, hasRibbon }) => (
170
+ * hasRibbon ? (
171
+ * <span className="ribbon-badge">
172
+ * {ribbon}
173
+ * </span>
174
+ * ) : null
175
+ * )}
176
+ * </Product.Ribbon>
177
+ * );
178
+ * }
179
+ * ```
180
+ */
181
+ export function Ribbon(props) {
182
+ const service = useService(ProductServiceDefinition);
183
+ const product = service.product.get();
184
+ const ribbon = product.ribbon?.name || null;
185
+ const hasRibbon = !!ribbon;
186
+ return props.children({
187
+ ribbon,
188
+ hasRibbon,
189
+ });
190
+ }
@@ -52,6 +52,10 @@ function buildSearchFilterData(availableOptions, availableInventoryStatuses, ava
52
52
  ? name.toLowerCase()
53
53
  : name;
54
54
  },
55
+ valueBgColorFormatter: (value) => {
56
+ const choice = option.choices.find((c) => c.id === value);
57
+ return choice?.colorCode || null;
58
+ },
55
59
  })),
56
60
  // Inventory status - use actual search field name
57
61
  {
@@ -323,15 +323,7 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
323
323
  }
324
324
  try {
325
325
  isLoadingSignal.set(true);
326
- const affectiveSearchOptions = searchOptions.cursorPaging?.cursor
327
- ? {
328
- cursorPaging: {
329
- cursor: searchOptions.cursorPaging.cursor,
330
- limit: searchOptions.cursorPaging.limit,
331
- },
332
- }
333
- : searchOptions;
334
- const result = await fetchProducts(affectiveSearchOptions);
326
+ const result = await fetchProducts(searchOptions);
335
327
  productsSignal.set(result.products ?? []);
336
328
  pagingMetadataSignal.set(result.pagingMetadata);
337
329
  }
@@ -344,6 +336,29 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
344
336
  });
345
337
  }
346
338
  firstRun = false;
339
+ const loadMoreCursor = async (count) => {
340
+ const affectiveSearchOptions = {
341
+ cursorPaging: {
342
+ cursor: pagingMetadataSignal.get().cursors?.next,
343
+ limit: DEFAULT_QUERY_LIMIT || count,
344
+ },
345
+ };
346
+ try {
347
+ isLoadingSignal.set(true);
348
+ const result = await fetchProducts(affectiveSearchOptions);
349
+ productsSignal.set([
350
+ ...productsSignal.get(),
351
+ ...(result.products ?? []),
352
+ ]);
353
+ pagingMetadataSignal.set(result.pagingMetadata);
354
+ }
355
+ catch (error) {
356
+ errorSignal.set(error instanceof Error ? error.message : 'Unknown error');
357
+ }
358
+ finally {
359
+ isLoadingSignal.set(false);
360
+ }
361
+ };
347
362
  return {
348
363
  products: productsSignal,
349
364
  searchOptions: searchOptionsSignal,
@@ -391,14 +406,7 @@ export const ProductListService = implementService.withConfig()(ProductsListServ
391
406
  isLoading: isLoadingSignal,
392
407
  error: errorSignal,
393
408
  loadMore: (count) => {
394
- const currentOptions = searchOptionsSignal.peek();
395
- searchOptionsSignal.set({
396
- ...currentOptions,
397
- cursorPaging: {
398
- cursor: pagingMetadataSignal.get().cursors?.next,
399
- limit: currentOptions.cursorPaging?.limit ?? 0 + count,
400
- },
401
- });
409
+ loadMoreCursor(count);
402
410
  },
403
411
  hasMoreProducts: signalsService.computed(() => pagingMetadataSignal.get().hasNext ?? false),
404
412
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-stores",
3
- "version": "0.0.61",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "prebuild": "cd ../media && yarn build && cd ../ecom && yarn build",
@@ -62,10 +62,10 @@
62
62
  "@wix/auto_sdk_stores_read-only-variants-v-3": "^1.0.23",
63
63
  "@wix/ecom": "^1.0.1278",
64
64
  "@wix/essentials": "^0.1.24",
65
- "@wix/headless-components": "0.0.3",
66
- "@wix/headless-ecom": "0.0.17",
67
- "@wix/headless-media": "^0.0.11",
68
- "@wix/headless-utils": "^0.0.2",
65
+ "@wix/headless-components": "workspace:*",
66
+ "@wix/headless-ecom": "workspace:*",
67
+ "@wix/headless-media": "workspace:*",
68
+ "@wix/headless-utils": "workspace:*",
69
69
  "@wix/redirects": "^1.0.83",
70
70
  "@wix/services-definitions": "^0.1.4",
71
71
  "@wix/services-manager-react": "^0.1.26"