@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,5 +1,5 @@
1
1
  import React from 'react';
2
- import { type AsChildProps } from '../utils/index.js';
2
+ import { AsChildChildren } from '@wix/headless-utils/react';
3
3
  /**
4
4
  * Choice data interface
5
5
  */
@@ -67,10 +67,16 @@ export declare const Root: React.ForwardRefExoticComponent<RootProps & React.Ref
67
67
  /**
68
68
  * Props for Choice Text component
69
69
  */
70
- export interface TextProps extends AsChildProps<{
71
- id: string;
72
- value: string;
73
- }> {
70
+ export interface TextProps {
71
+ /** Whether to render as a child component */
72
+ asChild?: boolean;
73
+ /** Custom render function when using asChild */
74
+ children?: AsChildChildren<{
75
+ id: string;
76
+ value: string;
77
+ }>;
78
+ /** CSS classes to apply to the default element */
79
+ className?: string;
74
80
  }
75
81
  /**
76
82
  * Text-based choice button.
@@ -105,11 +111,17 @@ export declare const Text: React.ForwardRefExoticComponent<TextProps & React.Ref
105
111
  /**
106
112
  * Props for Choice Color component
107
113
  */
108
- export interface ColorProps extends AsChildProps<{
109
- colorCode: string;
110
- name: string;
111
- id: string;
112
- }> {
114
+ export interface ColorProps {
115
+ /** Whether to render as a child component */
116
+ asChild?: boolean;
117
+ /** Custom render function when using asChild */
118
+ children?: AsChildChildren<{
119
+ colorCode: string;
120
+ name: string;
121
+ id: string;
122
+ }>;
123
+ /** CSS classes to apply to the default element */
124
+ className?: string;
113
125
  }
114
126
  /**
115
127
  * Color swatch choice.
@@ -148,14 +160,20 @@ export declare const Color: React.ForwardRefExoticComponent<ColorProps & React.R
148
160
  /**
149
161
  * Props for Choice FreeText component
150
162
  */
151
- export interface FreeTextProps extends AsChildProps<{
152
- minCharCount?: number;
153
- maxCharCount?: number;
154
- defaultAddedPrice?: string | null;
155
- title?: string;
156
- value?: string;
157
- onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
158
- }>, Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'children'> {
163
+ export interface FreeTextProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'children'> {
164
+ /** Whether to render as a child component */
165
+ asChild?: boolean;
166
+ /** Custom render function when using asChild */
167
+ children?: AsChildChildren<{
168
+ minCharCount?: number;
169
+ maxCharCount?: number;
170
+ defaultAddedPrice?: string | null;
171
+ title?: string;
172
+ value?: string;
173
+ onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
174
+ }>;
175
+ /** CSS classes to apply to the default element */
176
+ className?: string;
159
177
  }
160
178
  /**
161
179
  * Provides a free text input for variant selection.
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { renderAsChild } from '../utils/index.js';
4
3
  import { FreeText as FreeTextPrimitive } from './core/ProductModifiers.js';
4
+ import { AsChildSlot } from '@wix/headless-utils/react';
5
5
  var TestIds;
6
6
  (function (TestIds) {
7
7
  TestIds["choiceRoot"] = "choice-root";
@@ -91,24 +91,7 @@ export const Text = React.forwardRef((props, ref) => {
91
91
  if (!isVisible)
92
92
  return null;
93
93
  const choiceId = choice?.choiceId || '';
94
- const attributes = {
95
- 'data-testid': TestIds.choiceText,
96
- 'data-selected': isSelected ? 'true' : 'false',
97
- disabled: !isInStock && !isPreOrderEnabled,
98
- onClick: select,
99
- };
100
- if (asChild) {
101
- const rendered = renderAsChild({
102
- children,
103
- props: { id: choiceId, value },
104
- ref,
105
- content: value,
106
- attributes,
107
- });
108
- if (rendered)
109
- return rendered;
110
- }
111
- return (_jsx("button", { className: className, ...attributes, ref: ref, children: value }));
94
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.choiceText, "data-selected": isSelected ? 'true' : 'false', disabled: !isInStock && !isPreOrderEnabled, onClick: select, customElement: children, customElementProps: { id: choiceId, value }, content: value, children: _jsx("button", { children: value }) }));
112
95
  });
113
96
  /**
114
97
  * Color swatch choice.
@@ -157,28 +140,11 @@ export const Color = React.forwardRef((props, ref) => {
157
140
  if (!isVisible)
158
141
  return null;
159
142
  const { colorCode, choiceId } = choice;
160
- const attributes = {
161
- 'data-testid': TestIds.choiceColor,
162
- 'data-selected': isSelected ? 'true' : 'false',
163
- disabled: !isInStock && !isPreOrderEnabled,
164
- onClick: select,
165
- };
166
- if (asChild) {
167
- const rendered = renderAsChild({
168
- children,
169
- props: {
170
- colorCode: colorCode || '',
171
- name: value,
172
- id: choiceId || '',
173
- },
174
- ref,
175
- content: null,
176
- attributes,
177
- });
178
- if (rendered)
179
- return rendered;
180
- }
181
- return (_jsx("button", { className: className, ...attributes, ref: ref, style: { backgroundColor: colorCode }, title: value }));
143
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.choiceColor, "data-selected": isSelected ? 'true' : 'false', disabled: !isInStock && !isPreOrderEnabled, onClick: select, customElement: children, customElementProps: {
144
+ colorCode: colorCode || '',
145
+ name: value,
146
+ id: choiceId || '',
147
+ }, children: _jsx("button", { style: { backgroundColor: colorCode }, title: value }) }));
182
148
  });
183
149
  /**
184
150
  * Provides a free text input for variant selection.
@@ -226,10 +192,6 @@ export const FreeText = React.forwardRef((props, ref) => {
226
192
  // Don't render if not visible (handled by ProductVariantSelector in Root)
227
193
  if (!isVisible)
228
194
  return null;
229
- const attributes = {
230
- 'data-testid': TestIds.choiceFreetext,
231
- 'data-selected': isSelected ? 'true' : 'false',
232
- };
233
195
  return (_jsx(FreeTextPrimitive, { modifier: choice, children: ({ value, setText, placeholder, maxChars }) => {
234
196
  const handleChange = (e) => {
235
197
  setText(e.target.value);
@@ -237,23 +199,12 @@ export const FreeText = React.forwardRef((props, ref) => {
237
199
  onValueChange(e.target.value);
238
200
  }
239
201
  };
240
- if (asChild) {
241
- const rendered = renderAsChild({
242
- children,
243
- props: {
244
- minCharCount: choice?.minCharCount,
245
- maxCharCount: choice?.maxCharCount,
246
- defaultAddedPrice: choice?.addedPrice || undefined,
247
- title: choice?.name || undefined,
248
- onChange: handleChange,
249
- },
250
- ref,
251
- content: null,
252
- attributes,
253
- });
254
- if (rendered)
255
- return rendered;
256
- }
257
- return (_jsx("textarea", { ref: ref, className: className, placeholder: placeholder, rows: 3, value: value, maxLength: maxChars, ...attributes, onChange: handleChange }));
202
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.choiceFreetext, "data-selected": isSelected ? 'true' : 'false', customElement: children, customElementProps: {
203
+ minCharCount: choice?.minCharCount,
204
+ maxCharCount: choice?.maxCharCount,
205
+ defaultAddedPrice: choice?.addedPrice || undefined,
206
+ title: choice?.name || undefined,
207
+ onChange: handleChange,
208
+ }, children: _jsx("textarea", { placeholder: placeholder, rows: 3, value: value, maxLength: maxChars, onChange: handleChange }) }));
258
209
  } }));
259
210
  });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { type AsChildProps } from '../utils/index.js';
2
+ import { AsChildChildren } from '@wix/headless-utils/react';
3
3
  export interface Option {
4
4
  name: string;
5
5
  choices?: any[];
@@ -10,11 +10,17 @@ export interface Option {
10
10
  /**
11
11
  * Root props with asChild support
12
12
  */
13
- export interface RootProps extends AsChildProps<{
14
- option: Option;
15
- onValueChange?: (value: string) => void;
16
- allowedTypes?: ('color' | 'text' | 'free-text')[];
17
- }> {
13
+ export interface RootProps {
14
+ /** Whether to render as a child component */
15
+ asChild?: boolean;
16
+ /** Custom render function when using asChild */
17
+ children?: AsChildChildren<{
18
+ option: Option;
19
+ onValueChange?: (value: string) => void;
20
+ allowedTypes?: ('color' | 'text' | 'free-text')[];
21
+ }>;
22
+ /** CSS classes to apply to the default element */
23
+ className?: string;
18
24
  option: Option;
19
25
  onValueChange?: (value: string) => void;
20
26
  allowedTypes?: ('color' | 'text' | 'free-text')[];
@@ -105,9 +111,15 @@ export declare const OptionContext: React.Context<any>;
105
111
  /**
106
112
  * Props for Option Name component
107
113
  */
108
- export interface NameProps extends AsChildProps<{
109
- name: string;
110
- }> {
114
+ export interface NameProps {
115
+ /** Whether to render as a child component */
116
+ asChild?: boolean;
117
+ /** Custom render function when using asChild */
118
+ children?: AsChildChildren<{
119
+ name: string;
120
+ }>;
121
+ /** CSS classes to apply to the default element */
122
+ className?: string;
111
123
  }
112
124
  /**
113
125
  * Displays the option name.
@@ -136,9 +148,15 @@ export declare const Name: React.ForwardRefExoticComponent<NameProps & React.Ref
136
148
  /**
137
149
  * Props for Option MandatoryIndicator component
138
150
  */
139
- export interface MandatoryIndicatorProps extends AsChildProps<{
140
- mandatory: boolean;
141
- }> {
151
+ export interface MandatoryIndicatorProps {
152
+ /** Whether to render as a child component */
153
+ asChild?: boolean;
154
+ /** Custom render function when using asChild */
155
+ children?: AsChildChildren<{
156
+ mandatory: boolean;
157
+ }>;
158
+ /** CSS classes to apply to the default element */
159
+ className?: string;
142
160
  }
143
161
  /**
144
162
  * Displays the mandatory indicator (asterisk) when the option is required.
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { renderAsChild } from '../utils/index.js';
4
3
  import * as Choice from './Choice.js';
5
4
  import * as ProductModifiersPrimitive from './core/ProductModifiers.js';
6
5
  import * as ProductVariantSelectorPrimitive from './core/ProductVariantSelector.js';
6
+ import { AsChildSlot } from '@wix/headless-utils/react';
7
7
  var TestIds;
8
8
  (function (TestIds) {
9
9
  TestIds["optionRoot"] = "option-root";
@@ -93,7 +93,7 @@ var TestIds;
93
93
  * ```
94
94
  */
95
95
  export const Root = React.forwardRef((props, ref) => {
96
- const { asChild, children, option, onValueChange, allowedTypes } = props;
96
+ const { asChild, children, option, onValueChange, allowedTypes, className } = props;
97
97
  // Determine the option type based on the option name and available choices
98
98
  const getOptionType = () => {
99
99
  if (option.type === 'FREE_TEXT') {
@@ -113,23 +113,7 @@ export const Root = React.forwardRef((props, ref) => {
113
113
  allowedTypes,
114
114
  mandatory: option?.mandatory || false,
115
115
  };
116
- const attributes = {
117
- 'data-testid': TestIds.optionRoot,
118
- 'data-type': optionType,
119
- };
120
- const content = (_jsx(OptionContext.Provider, { value: contextValue, children: typeof children === 'function' ? null : children }));
121
- if (asChild) {
122
- const rendered = renderAsChild({
123
- children,
124
- props: { option, onValueChange, allowedTypes },
125
- ref,
126
- content,
127
- attributes,
128
- });
129
- if (rendered)
130
- return rendered;
131
- }
132
- return (_jsx("div", { ...attributes, ref: ref, children: content }));
116
+ return (_jsx(OptionContext.Provider, { value: contextValue, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.optionRoot, "data-type": optionType, customElement: children, customElementProps: { option, onValueChange, allowedTypes }, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
133
117
  });
134
118
  // Create a context to pass option data down
135
119
  export const OptionContext = React.createContext(null);
@@ -162,21 +146,7 @@ export const Name = React.forwardRef((props, ref) => {
162
146
  if (!optionData)
163
147
  return null;
164
148
  const name = optionData.name || '';
165
- const attributes = {
166
- 'data-testid': TestIds.optionName,
167
- };
168
- if (asChild) {
169
- const rendered = renderAsChild({
170
- children,
171
- props: { name },
172
- ref,
173
- content: name,
174
- attributes,
175
- });
176
- if (rendered)
177
- return rendered;
178
- }
179
- return (_jsx("div", { className: className, ...attributes, ref: ref, children: name }));
149
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.optionName, customElement: children, customElementProps: { name }, content: name, children: _jsx("div", { children: name }) }));
180
150
  });
181
151
  /**
182
152
  * Displays the mandatory indicator (asterisk) when the option is required.
@@ -205,21 +175,7 @@ export const MandatoryIndicator = React.forwardRef((props, ref) => {
205
175
  // Don't render anything if not mandatory
206
176
  if (!mandatory)
207
177
  return null;
208
- const attributes = {
209
- 'data-testid': TestIds.optionMandatoryIndicator,
210
- };
211
- if (asChild) {
212
- const rendered = renderAsChild({
213
- children,
214
- props: { mandatory },
215
- ref,
216
- content: '*',
217
- attributes,
218
- });
219
- if (rendered)
220
- return rendered;
221
- }
222
- return (_jsx("span", { className: className, ...attributes, ref: ref, children: "*" }));
178
+ return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.optionMandatoryIndicator, customElement: children, customElementProps: { mandatory }, content: '*', children: _jsx("span", { children: "*" }) }));
223
179
  });
224
180
  /**
225
181
  * Container for the list items with empty state support.
@@ -282,6 +282,113 @@ export interface RawProps {
282
282
  * ```
283
283
  */
284
284
  export declare const Raw: React.ForwardRefExoticComponent<RawProps & React.RefAttributes<HTMLElement>>;
285
+ /**
286
+ * Props for Product Ribbon component
287
+ */
288
+ export interface RibbonProps {
289
+ /** Whether to render as a child component */
290
+ asChild?: boolean;
291
+ /** Custom render function when using asChild */
292
+ children?: AsChildChildren<{
293
+ ribbon: string | null;
294
+ }>;
295
+ /** CSS classes to apply to the default element */
296
+ className?: string;
297
+ }
298
+ /**
299
+ * Displays the product ribbon with customizable rendering following the documented API.
300
+ *
301
+ * @component
302
+ * @example
303
+ * ```tsx
304
+ * // Default usage
305
+ * <Product.Ribbon className="absolute top-2 left-2 bg-red-500 text-white px-2 py-1 text-xs rounded" />
306
+ *
307
+ * // asChild with primitive
308
+ * <Product.Ribbon asChild>
309
+ * <span className="ribbon-badge" />
310
+ * </Product.Ribbon>
311
+ *
312
+ * // asChild with react component
313
+ * <Product.Ribbon asChild>
314
+ * {React.forwardRef(({ribbon, ...props}, ref) => (
315
+ * <div ref={ref} {...props} className="ribbon-badge">
316
+ * {ribbon}
317
+ * </div>
318
+ * ))}
319
+ * </Product.Ribbon>
320
+ * ```
321
+ */
322
+ export declare const Ribbon: React.ForwardRefExoticComponent<RibbonProps & React.RefAttributes<HTMLElement>>;
323
+ /**
324
+ * Props for Product Stock component
325
+ */
326
+ export interface StockProps {
327
+ /** Whether to render as a child component */
328
+ asChild?: boolean;
329
+ /** Custom render function when using asChild */
330
+ children?: AsChildChildren<{
331
+ status: 'in-stock' | 'limited-stock' | 'out-of-stock';
332
+ label: string;
333
+ }>;
334
+ /** CSS classes to apply to the default element */
335
+ className?: string;
336
+ /** Custom labels for different stock states */
337
+ labels?: {
338
+ /** Label for in stock state */
339
+ inStock?: string;
340
+ /** Label for limited stock state (when quantity is low) */
341
+ limitedStock?: string;
342
+ /** Label for out of stock state */
343
+ outOfStock?: string;
344
+ };
345
+ }
346
+ /**
347
+ * Displays the product stock status with customizable rendering and labels following the documented API.
348
+ *
349
+ * @component
350
+ * @example
351
+ * ```tsx
352
+ * // Default usage
353
+ * <Product.Stock
354
+ * className="stock-indicator"
355
+ * labels={{
356
+ * inStock: 'In Stock',
357
+ * limitedStock: 'Limited Stock',
358
+ * outOfStock: 'Out of Stock'
359
+ * }}
360
+ * />
361
+ *
362
+ * // asChild with primitive
363
+ * <Product.Stock asChild>
364
+ * <div className="stock-status" />
365
+ * </Product.Stock>
366
+ *
367
+ * // asChild with react component
368
+ * <Product.Stock
369
+ * labels={{
370
+ * inStock: 'Available',
371
+ * limitedStock: 'Low Stock',
372
+ * outOfStock: 'Sold Out'
373
+ * }}
374
+ * asChild
375
+ * >
376
+ * {React.forwardRef(({status, label, ...props}, ref) => (
377
+ * <div
378
+ * ref={ref}
379
+ * {...props}
380
+ * 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"
381
+ * >
382
+ * <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" />
383
+ * <span className="text-xs font-medium">
384
+ * {label}
385
+ * </span>
386
+ * </div>
387
+ * ))}
388
+ * </Product.Stock>
389
+ * ```
390
+ */
391
+ export declare const Stock: React.ForwardRefExoticComponent<StockProps & React.RefAttributes<HTMLElement>>;
285
392
  /**
286
393
  * Props for Product Variants container
287
394
  */
@@ -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;